Skip to content

fix(version): refresh the embedded git sha when HEAD moves on the same branch - #385

Open
x7c1 wants to merge 2 commits into
mainfrom
task/0909-2300-fix-refresh-the-embedded-git-sha-when-head-moves-on-the-same-branch
Open

fix(version): refresh the embedded git sha when HEAD moves on the same branch#385
x7c1 wants to merge 2 commits into
mainfrom
task/0909-2300-fix-refresh-the-embedded-git-sha-when-head-moves-on-the-same-branch

Conversation

@x7c1

@x7c1 x7c1 commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Summary

On a debug build the footer version string (v<version>+dev.<sha>) now picks up a new short sha whenever HEAD moves to a different commit — a commit, a fast-forward git pull, a reset or a merge on the same branch — instead of only on a branch switch. Before this change a clone that stayed on main kept showing the sha recorded the last time the build script ran (on this development machine, v0.4.0+dev.cc9d3d53 while the binary had been rebuilt from 7326b2e0), because build.rs registered only the HEAD file as a cargo:rerun-if-changed input and that file is byte-identical across same-branch moves.

Changes

  • build.rs now registers both the repository HEAD file and its reflog (logs/HEAD) as rerun inputs. Each path is resolved with git 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 for HEAD as well.
  • The reflog was chosen over the ref file HEAD points at (refs/heads/<branch>) because git pack-refs can fold that loose ref into packed-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) only HEAD is registered and the sha can lag as before; the build never fails over it.
  • The unknown fallback for a checkout without git is unchanged, as are version.rs, the wire type and the frontend — only the freshness of the sha changes, not the format.
  • The module comment was rewritten around the new trigger set. During review a claim that the reflog is missing "in a worktree whose HEAD has never moved" was verified to be false (git worktree add writes the per-worktree logs/HEAD up 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 check plus grep gates on build.rs). The manual subsection asks for two checks on a real machine before merge: build from commit A, fast-forward main to 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).

x7c1 added 2 commits September 9, 2026 23:22
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant