From c9b37f2477677e30c8115b944d11d8a6d55776e7 Mon Sep 17 00:00:00 2001 From: SBPro Date: Tue, 17 Mar 2026 14:41:04 +0300 Subject: [PATCH] Fix linearize fallback applying wrong diff (full tree instead of parent) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- lib/sync.sh | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/sync.sh b/lib/sync.sh index 596ae65..5c80de2 100644 --- a/lib/sync.sh +++ b/lib/sync.sh @@ -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