You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(vcs/cache): mailmap is read twice, so an edit mid-walk can mis-stamp a cache entry #1409
src/vcs/git/history.rs — collect_events's own repo.open_mailmap(),
which builds the ParticipantResolver that canonicalises the identities
actually recorded in the event log.
A .mailmap edit landing between the two therefore persists an entry
stamped fingerprint(pre-edit mailmap) over events walked under the post-edit one.
Why it is narrow
It is self-healing in the common case: the next run sees the edited
mailmap, fingerprints differently, misses, and overwrites the entry with a
correct one. The wrong hit is only reachable if the mailmap is reverted
to its pre-edit bytes before any run under the edited version, at the
same HEAD. Then fingerprint matches and the mis-stamped entry is
served — the exact failure mode #1262 closed, through a much smaller door.
Found in review of #1262 rather than in the field; no reproducer beyond a
deliberately-timed edit.
Fix
Open the mailmap once in build_cached and thread the gix::mailmap::Snapshot into history::collect_events, so the digest and
the resolver are provably the same snapshot. That also removes the second
parse on every miss / splice.
It was deliberately not done in #1262 because it widens past that fix's
scope: collect_events gains a parameter (5 → 6), its third caller
(git::build, src/vcs/git/mod.rs) has to open the snapshot itself, incremental_events reaches 10 arguments, and build_cached — already
carrying a halstead.effort baseline entry — grows again. Worth doing as
its own change, where that restructuring can be judged on its merits.
Acceptance
One open_mailmap() per bca vcs invocation, shared by the digest and
the walk.
The digest a persisted entry carries is, by construction, the digest of
the mailmap its events were resolved under.
build_cached's --no-cache / no-cache-directory path still does not
pay for a digest it will not use.
Correction (from the #1424 review): the "Why it is narrow" section
understates the window. open_mailmap() swallows read errors, so a
transient partial read during a non-atomic save gives a wrong hit with no
edit and no revert, and it never self-heals. The single-snapshot fix below
closes that path too. Details are in the comments.
Correction: the "only a revert" window is wider — a transient read never heals
The Why it is narrow section above says the wrong hit is reachable
only if the mailmap is reverted to its pre-edit bytes before any run under
the edited version. The max-effort review of #1424 found a second path that
needs no edit and no revert, and that does not self-heal.
gix::Repository::open_mailmap() swallows read errors. So:
build_cached reads the full mailmap M for the digest, giving F(M).
While bca vcs runs, an editor re-saves an unchanged.mailmap
non-atomically (truncate, then write). The walk's own open_mailmap() call lands in the gap, reads an empty or partial file,
and gix returns that silently instead of an error.
The events are canonicalised with no mapping applied, and the entry
is persisted as F(M) over those unmapped events.
Every later run reads the full M, computes the same F(M), hits,
and serves the wrong author counts.
The mailmap bytes never actually changed, so no later edit forces a miss.
It persists until --clear-cache, exactly the failure #1262 closed.
The fix proposed here closes this path too
Opening the mailmap once and threading the Snapshot into collect_events makes the digest and the resolver the same read. A
transient read then gives a consistent wrong pair: F(partial) over
events resolved under the same partial map. The next clean read computes F(M) ≠ F(partial), misses, and rewrites the entry. So the proposed fix
closes the transient-read path as well as the edit race, which strengthens
the case for it over "narrow, worth doing someday".
#1424 corrects the matching doc comment in src/vcs/git/repo.rs, which
repeated the "only a revert" claim. It does not change the code; that is
still this issue's job.
Activity
dekobon commentedon Sep 11, 2026
Correction: the "only a revert" window is wider — a transient read never heals
The Why it is narrow section above says the wrong hit is reachable
only if the mailmap is reverted to its pre-edit bytes before any run under
the edited version. The max-effort review of #1424 found a second path that
needs no edit and no revert, and that does not self-heal.
gix::Repository::open_mailmap()swallows read errors. So:build_cachedreads the full mailmapMfor the digest, givingF(M).bca vcsruns, an editor re-saves an unchanged.mailmapnon-atomically (truncate, then write). The walk's own
open_mailmap()call lands in the gap, reads an empty or partial file,and gix returns that silently instead of an error.
is persisted as
F(M)over those unmapped events.M, computes the sameF(M), hits,and serves the wrong author counts.
The mailmap bytes never actually changed, so no later edit forces a miss.
It persists until
--clear-cache, exactly the failure #1262 closed.The fix proposed here closes this path too
Opening the mailmap once and threading the
Snapshotintocollect_eventsmakes the digest and the resolver the same read. Atransient read then gives a consistent wrong pair:
F(partial)overevents resolved under the same partial map. The next clean read computes
F(M) ≠ F(partial), misses, and rewrites the entry. So the proposed fixcloses the transient-read path as well as the edit race, which strengthens
the case for it over "narrow, worth doing someday".
#1424 corrects the matching doc comment in
src/vcs/git/repo.rs, whichrepeated the "only a revert" claim. It does not change the code; that is
still this issue's job.
fix(vcs/cache): invalidate the history cache on mailmap change