@@ -156,10 +156,56 @@ echo "Ungraftable candidate: ${ungraftable}"
156156# --- case 2: --dry-run prints candidates without rewriting ---
157157# As in case 1, use inode identity to detect the script's `mv`-based
158158# rewrite; mtime is unreliable here (see case 1 comment).
159+ #
160+ # INSTRUMENTED (temporary, for flake investigation): also capture sha256
161+ # and, when strace is available, the syscall trace around the dry-run
162+ # invocation. The two-part invariant is:
163+ # (a) the script's own `mv` must not fire on --dry-run (inode identity)
164+ # (b) no process (script or sub-git) must change the bytes of shallow
165+ # (sha256 identity)
166+ # Contents-changing without our `mv` would be a real regression; inode-
167+ # changing without contents-changing would indicate a sub-git internal
168+ # rewrite is crossing our invariant. Strace disambiguates which PID
169+ # and syscall did the work.
159170inode_before=" $( stat -f %i " ${shallow_file} " 2> /dev/null || stat -c %i " ${shallow_file} " ) "
171+ sha_before=" $( shasum -a 256 " ${shallow_file} " | awk ' {print $1}' ) "
160172contents_before=" $( cat " ${shallow_file} " ) "
161- dry_output=" $( bash " ${UNGRAFT} " --dry-run) "
173+ echo " [case 2 pre] inode=${inode_before} sha256=${sha_before} git=$( git --version) "
174+
175+ strace_log=" "
176+ if command -v strace > /dev/null 2>&1 ; then
177+ strace_log=" ${TMP} /case2.strace"
178+ # -f: follow forks into git subprocesses.
179+ # Trace the superset of syscalls any write to .git/shallow could use
180+ # (atomic tempfile-rename, in-place write, unlink+create). Swallow
181+ # strace failures (e.g. ptrace restricted) and fall back to a plain
182+ # run so we still exercise the assertion.
183+ if ! dry_output=" $( strace -f -o " ${strace_log} " \
184+ -e trace=openat,creat,rename,renameat,renameat2,unlink,unlinkat,write \
185+ bash " ${UNGRAFT} " --dry-run 2>&1 ) " ; then
186+ echo " [case 2] strace invocation failed; retrying without it"
187+ strace_log=" "
188+ dry_output=" $( bash " ${UNGRAFT} " --dry-run) "
189+ fi
190+ else
191+ echo " [case 2] strace not installed; running without syscall trace"
192+ dry_output=" $( bash " ${UNGRAFT} " --dry-run) "
193+ fi
194+
162195inode_after=" $( stat -f %i " ${shallow_file} " 2> /dev/null || stat -c %i " ${shallow_file} " ) "
196+ sha_after=" $( shasum -a 256 " ${shallow_file} " | awk ' {print $1}' ) "
197+ echo " [case 2 post] inode=${inode_after} sha256=${sha_after} "
198+
199+ # Always dump any syscall on the shallow file so we see the baseline
200+ # even on passing runs. Grep on the basename so relative-path and
201+ # absolute-path writes both surface.
202+ if [ -n " ${strace_log} " ] && [ -f " ${strace_log} " ]; then
203+ echo " [case 2 trace] strace entries mentioning 'shallow':"
204+ if ! grep -F shallow " ${strace_log} " | head -200; then
205+ echo " (no matches)"
206+ fi
207+ fi
208+
163209if ! grep -qxF " Would ungraft ${ungraftable} " <<< " ${dry_output}" ; then
164210 echo " FAIL (case 2): expected 'Would ungraft ${ungraftable} ' in dry-run output"
165211 echo " --- dry-run output ---"
@@ -169,6 +215,19 @@ if ! grep -qxF "Would ungraft ${ungraftable}" <<< "${dry_output}"; then
169215fi
170216if [ " ${inode_before} " != " ${inode_after} " ]; then
171217 echo " FAIL (case 2): --dry-run replaced .git/shallow (inode changed)"
218+ echo " inode_before=${inode_before} inode_after=${inode_after} "
219+ echo " sha256_before=${sha_before} "
220+ echo " sha256_after =${sha_after} "
221+ if [ " ${sha_before} " = " ${sha_after} " ]; then
222+ echo " (contents identical — atomic rewrite with same bytes)"
223+ else
224+ echo " (contents DIFFERED — real rewrite, not a same-bytes swap)"
225+ fi
226+ if [ -n " ${strace_log} " ] && [ -f " ${strace_log} " ]; then
227+ echo " --- strace entries touching 'shallow' (first 500) ---"
228+ grep -F shallow " ${strace_log} " | head -500
229+ echo " ------------------------------------------------------"
230+ fi
172231 exit 1
173232fi
174233if [ " $( cat " ${shallow_file} " ) " != " ${contents_before} " ]; then
0 commit comments