Skip to main content

Module invocation

Module invocation 

Source
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 types git --version should 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 git invocation 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.rs deletes 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> or alias.<name>.command, splits it with split_cmdline quoting, 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, so git -c alias.c=commit c is 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 to doc/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 version prints, keyed by the identity of the Git executable that printed it. Why: Every commit starts git version to 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 named git: a script, or a shim such as a version manager’s git link 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.hooksPath pointing 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>/hooks or a core.hooksPath directory; git lfs writes its four shims there again whenever it cleans a file) and not a hook defined in the configuration (hook.<name>.event with hook.<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=false for every hook event Git knows, which switches every configured hook off whatever its name. core.fsmonitor is 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 -c or --config-env of 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, so pre-push is switched off like every other event.
management
The git cli-git decision: help, retired commands, and direct check/fix. What: Decide what one git cli-git ... invocation does. Why: Help, refusals and retired commands need no repository; check and fix validate 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-git argument grammar. What: The grammar of git cli-git <command>, the wrapper’s own management namespace. Why: check and fix run policies over an explicit scope; doctor lists the traditional hooks a repository holds; trust, untrust and status are retired with executable configuration and only explain that.
management_selection
The refusal of a direct command whose --policy selects a policy that is off. What: The --policy selection of a direct git cli-git check or fix: 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 which cli-git.config.jsonc key 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 git ahead 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 git on 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 PATH offers 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): its ExecStart line holds --git <path>, the Git the wrapper resolved when it last provisioned the service. When no real Git follows the wrapper on PATH, that path is the only recorded one, so the resolver (real_git.rs) falls back to it, after the same checks a PATH candidate 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 on PATH, 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 on PATH uses the Git it was set up with instead of stopping. The search of PATH stays in real_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 git there 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.