fix(hardlink): content freshness moved switches, it was not withdrawn
CI / shellcheck + selftests (pull_request) Successful in 1m17s

`unshare_mutable_paths`' comment recorded that upstream had stopped
rewriting `dep-<target>` in place, on a measurement taken against
1.100.0-nightly. It had not. Two unrelated cargo changes landed within
four days of each other and between them moved the switch that turns the
behaviour on and the path it writes to:

  - cargo PR #17382 (2026-08-22) demoted `-Z checksum-freshness` to a
    gate and gave `build.fingerprint` the choice, defaulting to `mtime`.
    Setting only the gate is accepted and does nothing, which is exactly
    the result that was read as a withdrawal.
  - build-dir layout v2 (cargo PR #17354, stable 1.100.0 on 2026-11-12,
    nightly default since 1.99) moved the file from
    `.fingerprint/<unit>/dep-*` to `build/<pkg>/<hash>/fingerprint/dep-*`.

Measured 2026-08-26 on 1.100.0-nightly (e8cb624d5): same toolchain, same
clone procedure, one env var apart — with the gate alone a `cp -al` clone
mutates only the build/ and *.d families; add
`CARGO_BUILD_FINGERPRINT=content` and the source's dep-info file is
mutated through the shared inode again. The hazard is intact.

So the suite now exports both switches, and its strongest scenario runs
again on current nightlies — verified passing against both layouts. Its
control note learned the v2 path too: it looked for the v1 path only, and
so printed "does NOT rewrite ... in place" three lines beneath a listing
that showed the rewrite.

Every claim these comments make is now dated and cited, because the
defect being fixed is a comment that cited one measurement and silently
stopped reproducing.

Layout v2 also drags the artifacts under `build/`, which collapses this
function's real-copy set from 21.3% of a target dir to 99.998%. That is a
live cost, not a correctness problem, and it is filed as gitdan-actions#14
rather than fixed here.

Part of daniel/gitdan#62.
This commit is contained in:
2026-08-26 19:48:07 -05:00
parent 50e430f4de
commit 554310186f
5 changed files with 156 additions and 61 deletions
+65 -19
View File
@@ -329,21 +329,18 @@ _unshare_files() {
# the source) the following files in the SOURCE were mutated through the
# shared inode:
#
# <profile>/.fingerprint/<unit>/dep-<target> (only under
# CARGO_UNSTABLE_CHECKSUM_FRESHNESS,
# where this file carries the
# per-source blake3 checksums.
# NOT reproduced on
# 1.100.0-nightly (2026-08-25),
# measured by this repo's own CI
# — upstream appears to have
# stopped writing it in place.
# Kept in the unshared set
# anyway: it costs 22 MB of a
# 6.9 GB tree, and the failure
# it guards is a wrong answer,
# not a slow one.)
# <profile>/build/<pkg>/output, root-output (Cargo build-script metadata)
# <profile>/.fingerprint/<unit>/dep-<target> (build-dir layout v1) — or,
# <profile>/build/<pkg>/<hash>/fingerprint/dep-<target>
# (build-dir layout v2; see the
# dated note below for which
# Cargo writes which). Only
# when Cargo resolves freshness
# by CONTENT, where this file
# carries the per-source blake3
# checksums.
# <profile>/build/<pkg>/output, root-output (Cargo build-script metadata;
# `<pkg>/<hash>/run/root-output`
# under layout v2)
# <profile>/build/<pkg>/out/** (whatever the build script
# writes into OUT_DIR — build
# scripts overwhelmingly use a
@@ -359,10 +356,59 @@ _unshare_files() {
# sources, reports `Fresh`, and reuses a binary built from the PRE-merge code.
# That is silent stale-artifact reuse — a wrong answer, not a slow one.
#
# So: hardlink the artifacts (the GB), real-copy the metadata (the MB).
# Measured on a 6.9 GB Bevy workspace target dir, the unshared set is
# .fingerprint 22 MB + build/ 237 MB + a handful of dep-info files — about
# 3.7% of the tree, against 100% for a plain `cp -a`.
# WHAT UPSTREAM CHANGED, AND WHAT IT DID NOT (measured 2026-08-26, daniel/gitdan#62).
#
# An earlier revision of this comment recorded that the dep-info write was
# "NOT reproduced on 1.100.0-nightly (2026-08-25) — upstream appears to have
# stopped writing it in place". That reading was wrong, and the way it was
# wrong is the reason this paragraph is dated. Two unrelated upstream changes
# landed within days of each other, and between them they moved both the
# switch that turns the behaviour on and the path it writes to:
#
# 1. The ON-SWITCH MOVED. cargo PR #17382 `feat(config): Add build.fingerprint`
# (merged 2026-08-22) demoted `-Z checksum-freshness` to a gate: it now
# only UNLOCKS the feature, and `build.fingerprint` SELECTS it, defaulting
# to `"mtime"`. So `CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true` on its own is
# accepted and does nothing, which is exactly the "flag accepted, mtime
# anyway" result that was mistaken for a withdrawal. Content freshness
# needs BOTH, and with both it is entirely intact:
#
# CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true CARGO_BUILD_FINGERPRINT=content
#
# Measured on cargo 1.100.0-nightly (e8cb624d5 2026-08-22): with the gate
# alone a `cp -al` clone mutates only the build/ and *.d families; add
# `CARGO_BUILD_FINGERPRINT=content` and the source's dep-info file is
# mutated through the shared inode again. Same toolchain, same clone, one
# env var apart. The hazard was never removed — it was switched off.
#
# 2. THE PATH MOVED. Build-dir layout v2 (`-Z build-dir-new-layout`, cargo
# 1.91) became the nightly default in cargo 1.99 (PR #17258) and was
# stabilised by PR #17354, merged 2026-08-18, shipping in cargo 1.100.0
# stable on 2026-11-12. Under v2 there is no `<profile>/.fingerprint` and
# no `<profile>/deps` at all: everything is regrouped per build unit under
# `<profile>/build/<pkg>/<hash>/{fingerprint,out,run}/`, artifacts
# included. Bracketed locally: cargo 1.97.1 and 1.98.0-nightly write v1,
# 1.100.0-nightly writes v2.
#
# The `-name .fingerprint` clause below therefore matches nothing under a v2
# Cargo, and the dep-info file is covered only because the `-name build` clause
# happens to swallow its new home. That is belt-and-braces by accident, not by
# design — and the same accident makes this function real-copy essentially the
# whole tree, because the artifacts moved under `build/` too. Measured on one
# scratch crate (serde + serde_json + regex), same sources both ways:
#
# cargo 1.97.1 (layout v1) 27.0 MB unshared of 126.7 MB — 21.3%
# 1.100.0-nightly (layout v2) 105.9 MB unshared of 105.9 MB — 99.998%
#
# So the guard still holds and the saving does not. Deliberately NOT fixed
# here: adjusting the selection is a change to what gets hardlinked on every
# consumer, which wants its own change and its own review, and the deadline is
# cargo 1.100.0 stable on 2026-11-12. Tracked as gitdan-actions#14.
#
# The historical v1 figure this block used to quote stands as measured: on a
# 6.9 GB Bevy workspace target dir the unshared set was .fingerprint 22 MB +
# build/ 237 MB + a handful of dep-info files — about 3.7% of the tree, against
# 100% for a plain `cp -a`. It describes layout v1 only.
#
# `incremental/` is deliberately left shared: rustc writes each incremental
# session to a fresh `s-*-working` directory and finalises it with a rename,