Skip to content

Commit 3ca164c

Browse files
petemooreclaude
andcommitted
fix: mirror script blob transplant clobbers modern files on stale dep branches
Replace direct blob transplant (update-index --cacheinfo) with diff-and-apply (git diff dep_base dep_tip | git apply --cached) for M/A files. The old approach set the index entry to the full blob from dep_tip — which is C's entire file state plus the bump — so for dep branches far behind main the mirrored PR diff regressed to ancient file content rather than containing just the bump change. The new approach applies the semantic diff onto _patched_main's version of each file: only the bump line changes, everything else stays modern. Falls back to the old blob transplant when the diff doesn't apply cleanly (e.g. lockfiles whose surrounding context has diverged; the sweeper's implementation agent handles lockfile regressions during CI-fix iterations). Add a prominent IMPORTANT comment block warning future agents not to revert to blob transplant, and explaining why it's wrong with the concrete observed example (mdi-react dep branch 1,544 commits behind main, package.json regressed to name=taskcluster-ui v86). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d448f9f commit 3ca164c

1 file changed

Lines changed: 32 additions & 9 deletions

File tree

utilities/mirror-upstream-dependabot.sh

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -220,22 +220,45 @@ mirror_one() {
220220
# Rebase the dependabot branch onto _patched_main. The dep branch may be
221221
# many commits behind upstream/main (dependabot doesn't always rebase), so
222222
# we must NOT start from its tree wholesale — that would make the fork PR diff
223-
# include all the upstream progress it missed. Instead:
224-
# 1. Find where the dep branch actually diverged from upstream/main.
225-
# 2. Compute only the dep-specific changes (dep_base → dep_tip).
226-
# 3. Start from _patched_main's tree and apply those changes on top.
223+
# include all the upstream progress it missed.
224+
#
225+
# Correct approach (implemented below):
226+
# 1. Find where the dep branch diverged from upstream/main (dep_base = C).
227+
# 2. For each file dependabot changed (C → dep_tip), apply ONLY that diff
228+
# onto _patched_main's version of the file via `git apply --cached`.
229+
# This carries just the bump line, not the full ancient file state.
230+
# 3. Fall back to blob transplant if the patch doesn't apply cleanly (e.g.
231+
# lockfiles whose surrounding context changed significantly since C).
232+
# The sweeper's implementation agent can repair lockfile regressions
233+
# during CI-fix iterations when this fallback is needed.
234+
#
227235
# Result: fork PR diff = same files as the upstream PR diff (just the bump).
236+
#
237+
# !! IMPORTANT — do not revert step 2 to a direct blob transplant !!
238+
# A blob transplant (update-index --cacheinfo with the blob from dep_tip)
239+
# looks equivalent but is NOT: the dep_tip blob is the full file at C + bump,
240+
# so transplanting it replaces _patched_main's modern content with the ancient
241+
# state. This was the original bug (observed on mdi-react, dep branch 1,544
242+
# commits behind main: package.json regressed to name=taskcluster-ui v86).
228243
dep_base=$(git -C "$CLONE_DIR" merge-base \
229244
"refs/remotes/origin/main" "refs/remotes/origin/$branch")
230245
git -C "$CLONE_DIR" read-tree "refs/heads/_patched_main"
231246
while IFS=$'\t' read -r status fpath; do
232247
case "$status" in
233248
M|A)
234-
entry=$(git -C "$CLONE_DIR" ls-tree "refs/remotes/origin/$branch" "$fpath")
235-
if [ -n "$entry" ]; then
236-
mode=$(printf '%s' "$entry" | awk '{print $1}')
237-
blob=$(printf '%s' "$entry" | awk '{print $3}')
238-
git -C "$CLONE_DIR" update-index --cacheinfo "$mode,$blob,$fpath"
249+
# Apply only the dep-specific diff (C → dep_tip) onto _patched_main's
250+
# content for this file. Falls back to blob transplant if the patch
251+
# context doesn't match (common for lockfiles; agent handles recovery).
252+
if ! git -C "$CLONE_DIR" diff "$dep_base" "refs/remotes/origin/$branch" \
253+
-- "$fpath" \
254+
| git -C "$CLONE_DIR" apply --cached --ignore-whitespace - 2>/dev/null
255+
then
256+
entry=$(git -C "$CLONE_DIR" ls-tree "refs/remotes/origin/$branch" "$fpath")
257+
if [ -n "$entry" ]; then
258+
mode=$(printf '%s' "$entry" | awk '{print $1}')
259+
blob=$(printf '%s' "$entry" | awk '{print $3}')
260+
git -C "$CLONE_DIR" update-index --cacheinfo "$mode,$blob,$fpath"
261+
fi
239262
fi
240263
;;
241264
D) git -C "$CLONE_DIR" update-index --remove "$fpath" ;;

0 commit comments

Comments
 (0)