Fix linearize fallback applying wrong diff (full tree instead of parent)

The fallback for failed cherry-picks used `git diff HEAD $sha` which
computes the diff between the linearize branch and the commit's full
tree. This captures ALL tree differences, not just the changes the
commit introduced — causing unrelated file deletions.

Fix: diff from the commit's own parent (`${sha}^` for regular commits,
`${sha}^1` for merge commits). Also detect and skip empty merges
(where cherry-pick -m 1 produces nothing because the changes were
already applied by a prior cherry-pick).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-17 14:41:04 +03:00
parent d3799187a1
commit c9b37f2477

View File

@@ -339,14 +339,16 @@ reverse_sync() {
if git cherry-pick "$sha" >/dev/null 2>&1; then
log "INFO" " cherry-pick ${sha:0:7}: ${short_msg}"
else
log "WARN" " conflict ${sha:0:7} — applying tree directly"
log "WARN" " conflict ${sha:0:7} — applying patch from parent"
git cherry-pick --abort 2>/dev/null
local msg author_name author_email author_date
msg=$(git log -1 --format="%B" "$sha")
author_name=$(git log -1 --format="%an" "$sha")
author_email=$(git log -1 --format="%ae" "$sha")
author_date=$(git log -1 --format="%aI" "$sha")
git diff HEAD "$sha" | git apply --index >/dev/null 2>&1 \
# Diff from the commit's own parent — NOT from HEAD (which is
# the linearize branch and may have a different base).
git diff "${sha}^" "$sha" | git apply --index >/dev/null 2>&1 \
|| die "Failed to apply diff for ${sha:0:7}"
GIT_AUTHOR_NAME="$author_name" GIT_AUTHOR_EMAIL="$author_email" \
GIT_AUTHOR_DATE="$author_date" \
@@ -362,16 +364,26 @@ reverse_sync() {
author_date=$(git log -1 --format="%aI" "$sha")
if git cherry-pick -m 1 "$sha" >/dev/null 2>&1; then
: # success
log "INFO" " squash ${sha:0:7}: ${short_msg}"
else
git cherry-pick --abort 2>/dev/null
git diff HEAD "$sha" | git apply --index >/dev/null 2>&1 \
|| die "Failed to apply merge diff for ${sha:0:7}"
GIT_AUTHOR_NAME="$author_name" GIT_AUTHOR_EMAIL="$author_email" \
GIT_AUTHOR_DATE="$author_date" \
git commit -m "$msg" >/dev/null 2>&1 || die "Failed to commit merge ${sha:0:7}"
# Diff from first parent — what the merge actually introduced.
# NOT from HEAD (which would capture all tree differences, not
# just this merge's changes).
if git diff "${sha}^1" "$sha" --quiet 2>/dev/null; then
# Empty diff from first parent — the merge introduced no new
# changes (e.g., a fast-forward merge or changes already applied
# by a prior cherry-pick). Skip silently.
log "INFO" " skip ${sha:0:7}: ${short_msg} (empty — already applied)"
else
git diff "${sha}^1" "$sha" | git apply --index >/dev/null 2>&1 \
|| die "Failed to apply merge diff for ${sha:0:7}"
GIT_AUTHOR_NAME="$author_name" GIT_AUTHOR_EMAIL="$author_email" \
GIT_AUTHOR_DATE="$author_date" \
git commit -m "$msg" >/dev/null 2>&1 || die "Failed to commit merge ${sha:0:7}"
log "INFO" " squash ${sha:0:7}: ${short_msg}"
fi
fi
log "INFO" " squash ${sha:0:7}: ${short_msg}"
fi
done