State
Shared sync state must survive history rewrites and use by other VCS tools. The architecture separates that versioned state from local upstream data.
Derive state instead of storing redundant records
Every stored value creates work to keep it consistent. Storing both a
result and the information needed to derive it lets those records drift
apart after a partial update or history rewrite. Avoiding that redundant
bookkeeping was a motivation for departing from
git-subrepo.
B, R and the sync status are therefore derived from committed history and fetched refs. A push does not need a monorepo commit to record its result: a later deterministic rebuild recovers it. A proposed state field should explain why existing history and configuration cannot supply the value, rather than introduce another record that commands must keep in sync.
Sync state belongs in versioned files
Extra commit metadata ties the relationship to tools that understand and
preserve it. Keeping .splice in the worktree's versioned contents lets
other VCS tools carry that state as an ordinary file, even when they use
their own extra metadata. A rebase or squash likewise need not understand
a special sync record to preserve it. This avoids depending on
special handling of commit metadata, as
Sapling does, or sync records in
commit messages,
as git subtree does.
Committing the file also makes the relationship available in a fresh clone and lets ordinary Git switch and merge it between branches. Sync commands read its committed version so an unfinished edit cannot silently change the relationship they use.
Keep state inside the folder to avoid a redundant path
The minimum-state principle applies to paths too: .splice already
identifies its folder by living inside it. Recording that path separately,
as .gitmodules does for submodules, creates another value to keep in sync.
A move can leave that record stale. Keeping .splice inside the splice
lets ordinary folder moves carry the relationship without a second update.
Recorded references must survive history rewrites
A stored monorepo commit ID would become stale when history is rewritten, so the file records an upstream commit instead. The nesting principle also rules out storing a path or commit specific to the outermost monorepo: that reference would lose its meaning in a standalone clone of the enclosing upstream.
Reuse an available, established config format
Every git-splice user already needs Git installed, so its config reader
and writer are available without another dependency. Its established
format provides sections and values without a parser of our own to
maintain. Sourcing the file as a script would instead let committed
configuration execute commands.
Avoid interfering with build and packaging tools
Spliced files remain part of the monorepo's normal build and packaging
inputs. .splice was chosen to clash with no known configuration file,
so tools discovering files do not mistake the relationship for their own
configuration.
Identity must distinguish replacements and remain repeatable
A random ID would make otherwise identical initialisations differ. Path
alone cannot distinguish a moved splice from a replacement at its old
location. Deriving the ID from the path, HEAD, upstream URL and imported
commit makes the same operation repeatable while distinguishing different
upstreams introduced at the same path. Keeping that ID through moves lets
the rebuild recover the right history
(identity).
Conflicting sync points require a deliberate decision
Choosing either .splice mechanically can claim that a merged folder
contains an upstream state it does not actually contain. There is no
automatic merge driver today; adding one would need to establish that
relationship, not merely resolve the configuration text
(concurrent pulls).
An upstream with a root .splice is likewise refused because importing
it would overwrite the file defining the outer relationship.