Expand description
What one invocation does from arguments to Git: the decision, real Git resolution, forwarding, startup and management words. What: What one invocation does from arguments to Git: the decision, real Git resolution, forwarding, startup and management words. Why: The thin executables call into this folder first.
In TS you’d write (pseudocode):
// invocation/index.ts: one `export * from './file'` per module below.Modules§
- action
- The decision one invocation ends in: become Git, or print and exit. What: The decision one wrapper invocation ends in. Why: Forwarding replaces the process and cannot run inside a unit test; returning the decision as data lets every path be checked directly.
- announced_
query - The four top-level spellings after which the wrapper announces itself,
and the text it prints once Git has answered.
What:
Recognize the four top-level spellings after which the wrapper says it is not plain Git
(
--version,-v,--help,-h), and build the text it prints once Git has answered. Why: A caller who typesgit --versionshould be able to tell that a policy wrapper sits in front of Git, without the wrapper changing a single byte of what Git itself printed. Every other query (--exec-path,--html-path, …) stays an exact process replacement. - child_
environment - Variables added to every real-Git child: lock PID injection and the forward-target marker. What: The environment variables cli-git adds for every real Git it starts. Why: Native Git must write lock-owner PID files, and a wrapper must be able to notice that another wrapper selected it as “real Git”.
- effective_
target - Worktree target classification for worktree-enforcing policies. What: Classify the worktree an invocation targets for worktree-enforcing policies. Why: Linked-worktree safeguards discipline a person’s repositories, not the disposable clones a tool keeps in its own cache.
- entry
- The per-invocation decision the thin executable performs.
What:
Decide what one
gitinvocation of the native wrapper does, and perform it. Why: The executable stays thin: it gathers process facts, asks this module for an action, and performs it. The decision itself is testable without replacing the test process. - escape_
hatch - Spellings of the wrapper-only escape hatches shared by several modules.
What:
The spellings of the wrapper-only escape hatches several modules share.
Why:
A command module puts its own hatch first in the flag list it hands the option
tokenizer,
and the control table lists the same spellings;
one constant per
spelling keeps both in step.
Removal is not done here:
wrapper_invocation.rsdeletes wrapper tokens by the positions the tokenizer reports. - forwarded_
command - The Git command a forwarded invocation runs,
after ordinary alias expansion.
What:
The Git command a forwarded invocation actually runs,
after ordinary alias expansion.
Why:
Git expands an alias only when no built-in has that name,
reads it from
alias.<name>oralias.<name>.command, splits it withsplit_cmdlinequoting, lets its leading words be global options, and expands again when the result is an alias. Index-writer coordination and the commit rules decide from the expanded command, sogit -c alias.c=commit cis a commit (src/forwarded-command.ts). - forwarding
- Starting real Git with unchanged arguments, streams and exit status. What: Start real Git with the caller’s exact arguments, streams and environment. Why: The wrapper adds policy around Git; it must never change what Git receives or what the caller observes from Git.
- git_
builtins - The names of the commands built into Git 2.56.0.
What:
The names of the commands built into Git 2.56.0,
and the test “is this word one
of them”.
Why:
Git never expands an alias whose name is a built-in command,
so a built-in word
is the command that runs.
Any other word may be an alias for anything,
including
worktree add, and resolving aliases is not ported; the caller needs to know which of the two it is looking at without starting Git. - git_
version - The Git version check run once before work that depends on Git’s behavior.
What:
The real Git version check,
run once per invocation before work that depends on Git’s
behavior:
a commit,
hooks and locks.
Why:
The native tables and protocols follow the Git release the test image runs the tests with,
and a Git older than that release is not refused:
the owner chose to warn once and go on
(“Raise minimum, drop the test, merely print a warning on older git and not refuse.”,
2026-11-04),
so the constant below is the release the wrapper was tested with.
Only a Git whose release cannot be read stops the command,
because nothing is known about it.
Read-only commands are never checked
(
doc/handover/cli-git-rust-implementation.md, “Adopted without a question”; that section moved on 2026-11-03 todoc/planning/cli-git-rust-open-decisions.md). - git_
version_ cache - The private per-user cache of the version text, keyed by the Git executable’s identity.
What:
A private, per-user cache of the text
git versionprints, keyed by the identity of the Git executable that printed it. Why: Every commit startsgit versionto learn whether Git is new enough. The text is fixed by the executable, so while the executable is byte-for-byte the same file the answer is the same. The identity is the canonical path, size, modification time, change time, device and inode: an upgrade replaces the file (new inode) or rewrites it (new size, times), so a replaced Git is noticed on the next start and a stale entry is never trusted. The cache is used only for a real compiled executable namedgit: a script, or a shim such as a version manager’sgitlink to a dispatcher, picks another Git by directory or environment, so its own file proves nothing about the version it will run. The cache lives in the user’s cache directory because the version check runs before any repository is known. - global_
arguments - Git 2.56.0 global-argument boundaries over unchanged operating-system strings. What: Locate Git 2.56.0 command boundaries without reconstructing native arguments. Why: Policy stages need the command index, but Git remains authoritative for option validation and -C semantics.
- hooks_
off - Every real-Git start of the wrapper carries
core.hooksPathpointing at a directory that never exists. What: Start the real Git with every hook switched off. Why: The wrapper supports no external hook program: not a traditional hook (an executable file in<git-common-dir>/hooksor acore.hooksPathdirectory;git lfswrites its four shims there again whenever it cleans a file) and not a hook defined in the configuration (hook.<name>.eventwithhook.<name>.command). Every real-Git start of the wrapper therefore carries-c core.hooksPath=<a directory that never exists>, which finds no hook file, and-c hook.<event>.enabled=falsefor every hook event Git knows, which switches every configured hook off whatever its name.core.fsmonitoris left as configured: it is a program Git starts for its own feature, and the event switches do not reach it (measured on Git 2.56.0, doc/planning/cli-git-no-self-invocation-dispatcher.md). They follow every global option of the caller and precede the subcommand, so no-cor--config-envof the caller can switch a hook or the hooks path back on. The options are part of the argument vector, so they add no system call and no process start. The wrapper injects no hook of its own, sopre-pushis switched off like every other event. - management
- The
git cli-gitdecision: help, retired commands, and directcheck/fix. What: Decide what onegit cli-git ...invocation does. Why: Help, refusals and retired commands need no repository;checkandfixvalidate their policy selection and configuration, then run one policy pass over the selected repository and report its events on standard output. - management_
arguments - The
git cli-gitargument grammar. What: The grammar ofgit cli-git <command>, the wrapper’s own management namespace. Why:checkandfixrun policies over an explicit scope;doctorlists the traditional hooks a repository holds;trust,untrustandstatusare retired with executable configuration and only explain that. - management_
selection - The refusal of a direct command whose
--policyselects a policy that is off. What: The--policyselection of a directgit cli-git checkorfix: the names turned into shipped policies, and the refusal of a selection that names a policy which is off. Why: A mistyped policy ID must stop the command instead of selecting nothing. A policy that is off runs nothing, so a run limited to it would exit 0 with no output and read as “checked and clean”. A CI step or a person who selected the policy by name must learn that it did not run, and whichcli-git.config.jsonckey turns it on. The commit, add and push lifecycles are not direct runs and keep skipping policies that are off. - panic_
notice - The executable’s panic hook: where an internal error happened, never its message. What: The panic hook of the executable: one line naming where an internal error happened, never its message. Why: The linked scanner catches a matcher panic and reports it as a failed scan, but Rust runs the process’s panic hook before the panic can be caught, and the default hook prints the panic’s message, which can hold bytes of the file being scanned. The scanner’s own executable installs a hook that prints nothing; this one keeps the source location, which holds no repository content, so a wrapper defect is still visible.
- real_
git - PATH resolution of the real Git executable.
What:
Find the real Git executable on PATH without ever selecting this wrapper.
Why:
The wrapper is installed as
gitahead of real Git; forwarding needs the first usable candidate that is not a cli-git wrapper. - real_
git_ candidate - Wrapper self-exclusion for one PATH candidate.
What:
Decide whether one PATH candidate is real Git,
a cli-git wrapper,
or unusable.
Why:
The wrapper shadows
giton PATH and must never select itself, a copy of itself, a toolchain launcher that replaces itself with the wrapper, or a script that starts the TypeScript wrapper. - real_
git_ record - The real Git that the commit service’s unit file records, read as the fallback when
PATHoffers none. What: Read the real Git that the wrapper’s commit service was set up with, from the unit file the wrapper wrote for it. Why: Nothing the wrapper installs records the real Git it shadows. The one file that names a real Git is the unit of the commit service (service_provision.rs): itsExecStartline holds--git <path>, the Git the wrapper resolved when it last provisioned the service. When no real Git follows the wrapper onPATH, that path is the only recorded one, so the resolver (real_git.rs) falls back to it, after the same checks aPATHcandidate passes. This is the coordinator’s pick under the owner’s delegation, open to the owner’s veto (doc/planning/cli-git-no-self-invocation.md, section “Falling back to the Git the commit service was set up with”). The file lives in the runtime directory, so it is gone after the account’s last logout, and no record then means no fallback. - real_
git_ resolution - The one resolution of the real Git every start uses:
PATH, then the Git the commit service’s unit records. What: The one resolution of the real Git that every start uses: the first real Git onPATH, otherwise the Git the commit service’s unit records, with a notice. Why: The coordinator picked, under the owner’s delegation and open to the owner’s veto (doc/planning/cli-git-no-self-invocation.md, section “Falling back to the Git the commit service was set up with”), that a wrapper which is found but has no real Git after it onPATHuses the Git it was set up with instead of stopping. The search ofPATHstays inreal_git.rs; this module adds the fallback after it and the types that carry its result, so no caller can take the path and drop the notice. - started_
as_ git - The marker every real-Git start carries and the check that stops a wrapper which finds it was started as Git.
What:
The marker that every start of the real Git carries,
and the check that stops a cli-git executable which finds it was
started as that Git.
Why:
Which file the wrapper selected as Git can be a launcher,
a link,
a copy or another release of the wrapper,
and no test
of that file can recognise every one of them.
What no spelling hides is the start itself:
the wrapper that starts
“Git” puts its own process number,
that process’s birth identity and the way it starts the program (replacing itself or
as a child) in the environment of the start.
A cli-git executable that finds itself in the very process,
or the child,
that a wrapper started as Git has been started as Git,
whatever stood in between,
and stops at once.
Anything started further down (a hook,
an alias,
an editor a real Git started) has another process in between and
passes:
running
gitthere through an installed wrapper stays legitimate. - startup_
gate - The startup step of a command: isolated merges whose process died, and what an earlier release left behind. What: The startup step of a wrapped, read-only or direct command: settle an isolated merge whose process died, stop on a commit journal of the previous executable’s format, and mention commit records an earlier release left behind. Why: A commit is landed by the resident service, which recovers its own journal, so no commit transaction exists to recover. What stays is state other code still writes (the intent record of an isolated merge) and state an earlier release wrote (the single-journal directory and the registry of commit transactions): the first is settled before anything reads the checkout, the others are named to the person, because nothing here reads them and an interrupted commit of that release is for the person to look at. An empty registry costs one directory read and no Git process.
- wrapped_
command - The lifecycle of one wrapped Git command, from refusals to forwarding. What: Decide what one wrapped Git command does once real Git is known: forward it, possibly rewritten, or stop with policy events or a refusal. Why: This is the whole lifecycle of a forwarded command in one place and in the installed wrapper’s order: unported work is refused, configuration is loaded only for commands that need it, the policy pass runs, and a real push is stopped until its own lifecycle exists. A read-only command never reads configuration and asks Git at most where it runs.
- wrapper_
controls - Wrapper-only control spellings, their meanings, and removal before the subcommand. What: The wrapper-only control tokens, what each one means, and their removal from the arguments that come before the Git subcommand. Why: Git refuses options it does not know, so every control must be gone before Git runs, and every policy decision must read the arguments without them. A control is recognized only where Git itself would look for an option, and it is removed by its position, never by its spelling, so an equal-looking value or path is kept.
- wrapper_
invocation - Removal of every wrapper control from one invocation, by position. What: Remove every wrapper control from one invocation, before and after the subcommand, and say how the command’s own arguments were read. Why: Detecting a control and removing it must be one act. This module is the only place that removes wrapper tokens: positions come from Git’s own option table of the command where one is ported, and are deleted by position, so a control never reaches Git and a message, value or path that spells a control is never removed.