A Git commit ID is a hash over the commit's author and committer lines. Whatever decides the email in those lines therefore decides the ID, and the ID of every descendant. zit's two Git export verbs, export-git and zit git export, have let three inputs decide it.
- The user table's contact field for the check-in's login.
zit git exportreads it andexport-gitdoes not, so the same check-in got a different ID from each verb. - A saved email domain. It lives in the local configuration tier, which is never synced, so two machines exporting one repository could use two domains.
--email-domain. It overrode the domain for a single run.
None of these is history. Two exports of the same repository could share no commit IDs at all, and anyone who had cloned the first would see the second as a force-push of the whole history.
The rule
A commit's identity is a function of the repository's synced history alone:
- the login is the check-in's U card;
- the email is that login at the repository's Git email domain, a synced setting that defaults to
zit.invalid; - the time is the D card truncated to the second, at
+0000.
Contacts are no longer an input. Both verbs share the one code path that applies the rule, and test vectors pin it: a fixed manifest goes in and a fixed commit ID comes out. The vectors cover a login with a contact, one without, a manifest with no U card, one with no D card, and a D card with a fractional second.
Existing mirrors keep their IDs
A mirror records the rule it was built with, and an incremental export keeps that rule. No mirror already published is rewritten. A fresh export or a --rebuild uses the repository's rule. --email-domain remains only for a mirror with no history yet. On one that has history, it refuses when it disagrees with the recorded rule rather than splitting the history.
Why not keep contacts
A contact is an address a user may change at any time. Kept as an input, changing it changes every commit ID that login ever made, and an incremental export would never notice until a rebuild or a second machine produced different IDs. The way to keep contacts would be a versioned, synced mapping, where every edit is an explicit migration of published history. That is more machinery than a mirror needs, for a cost nobody should pay by accident. An export that wants real addresses can rewrite authors on the Git side, where Git's own tools already do it.
Why now
Today one machine exports zit's own mirror incrementally, so its IDs are stable in practice; it is also built by fossil git export, so this change does not touch it. The rule matters the moment a second machine exports, a mirror is rebuilt, or something relies on commit IDs being derivable from the store, as a Git-protocol bridge over a zit store would.