fix(version): refresh the embedded git sha when HEAD moves on the same branch - #385
Open
x7c1 wants to merge 2 commits into
Open
Conversation
…e branch The footer of the navigator pane shows the server's version string, and on a debug build that string carries the short git sha of the checkout the binary was built from (`Delta v0.4.0+dev.<sha>`, see `backend/crates/apps/delta-server/src/version.rs`). The sha is produced by `backend/crates/apps/delta-server/build.rs`, which runs `git rev-parse --short HEAD` at build time and exports it as the `DELTA_GIT_SHA` env var. The sha goes stale. `build.rs` registers exactly one `cargo:rerun-if-changed` input: the repository's `HEAD` file (resolved with `git rev-parse --path-format=absolute --git-path HEAD`). That file holds `ref: refs/heads/main` and is rewritten only when the checkout switches branches. A commit or a fast-forward `git pull` on the same branch updates `refs/heads/main` (and the reflog at `logs/HEAD`) but leaves `HEAD` byte-identical, so cargo never re-runs the build script: it recompiles `delta-server` with the changed sources and reuses the build script's cached output, embedding whatever sha was recorded the last time the script actually ran. Observed on the development machine (clone always on `main`): - `HEAD` was last modified 2026-09-03 14:50 (a branch switch back to `main`). - The build script last ran 2026-09-03 21:56 and recorded `cc9d3d53` (`target/debug/build/delta-server-*/output`). - `refs/heads/main` and `logs/HEAD` were updated 2026-09-09 22:45 (pull to `7326b2e0`), and `target/debug/delta-server` was rebuilt at 22:47 from those sources — yet the footer still reads `v0.4.0+dev.cc9d3d53`. The module comment in `build.rs` says "cargo still won't rebuild for every commit in a working tree with no source changes, and that is fine". The real behavior is worse than that sentence admits: the sha does not refresh even when the sources changed and the binary was rebuilt. ### What to build Make the build script re-run whenever `HEAD` resolves to a different commit, not only when the `HEAD` file itself changes. The intended mechanism: in addition to `HEAD`, register the HEAD reflog file, resolved the same way (`git rev-parse --path-format=absolute --git-path logs/HEAD`). Every operation that moves `HEAD` — commit, pull, checkout, reset, merge — appends to that file, and `--git-path` returns the per-worktree file for a linked worktree (verified: `<repo>/.git/worktrees/<name>/logs/HEAD`). Constraints to keep, all already stated in the existing module comment: - Only register paths that exist. Registering a non-existent path makes cargo treat the crate as permanently stale and rebuild it on every invocation. The reflog can be absent (e.g. `core.logAllRefUpdates=false`, or a fresh worktree that has not moved yet), so check for existence before printing the `rerun-if-changed` line — the same defensive shape the `HEAD` registration already needs. - Keep the `unknown` fallback for a checkout without `git` / without `.git`. - Keep the "sha is a debugging hint, not a fingerprint" stance: it is fine that a dirty working tree with no commit does not change the sha. Rewrite the module comment so it describes the new trigger set and states plainly that the previous `HEAD`-only registration missed same-branch moves. Do not change `version.rs`, the wire type, or the frontend: the display format is unchanged, only the freshness of the sha. Prefer `logs/HEAD` over registering the ref file `HEAD` points to (`refs/heads/<branch>`): after `git pack-refs` the loose ref file can disappear, which would trip the "non-existent path → permanently stale" trap unless `packed-refs` were registered as well. The reflog is a single file that exists in every ordinary clone and worktree.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On a debug build the footer version string (
v<version>+dev.<sha>) now picks up a new short sha wheneverHEADmoves to a different commit — a commit, a fast-forwardgit pull, a reset or a merge on the same branch — instead of only on a branch switch. Before this change a clone that stayed onmainkept showing the sha recorded the last time the build script ran (on this development machine,v0.4.0+dev.cc9d3d53while the binary had been rebuilt from7326b2e0), becausebuild.rsregistered only theHEADfile as acargo:rerun-if-changedinput and that file is byte-identical across same-branch moves.Changes
build.rsnow registers both the repositoryHEADfile and its reflog (logs/HEAD) as rerun inputs. Each path is resolved withgit rev-parse --path-format=absolute --git-path, so a linked worktree gets its own per-worktree files, and each is registered only when it exists on disk — cargo treats a registered non-existent path as permanently stale and would rebuild on every invocation. The existence check is new forHEADas well.HEADpoints at (refs/heads/<branch>) becausegit pack-refscan fold that loose ref intopacked-refs, silently dropping the trigger; the reflog is never packed. Where the reflog is absent (core.logAllRefUpdates=false, or a fresh repository before its first commit) onlyHEADis registered and the sha can lag as before; the build never fails over it.unknownfallback for a checkout withoutgitis unchanged, as areversion.rs, the wire type and the frontend — only the freshness of the sha changes, not the format.git worktree addwrites the per-worktreelogs/HEADup front) and removed, and the wording was tightened so that "a dirty working tree" cannot be misread as "a repository with no commit yet".Review notes
The acceptance criteria live in the accompanying
docs/tasks/2026/0909-2300-fix-refresh-the-embedded-git-sha-when-head-moves-on-the-same-branch.md. The automated items are ticked (make checkplus grep gates onbuild.rs). The manual subsection asks for two checks on a real machine before merge: build from commit A, fast-forwardmainto commit B, rebuild and restart, and confirm the footer shows B's sha; and, on the same restarted server, the mid-turn branch-send check carried over from #380 (plain send, then a branch send while the turn runs: the branch prompt lands on the new thread, the plain prompt stays on the parent, nothing duplicated or dropped).