[[command]]
name = "git"
description = "Distributed version control system. git has a huge surface area — hundreds of subcommands and flags, with new ones added regularly. Read-only operations (log, diff, status, blame, show, branch listing) are safe. Write operations (push, commit, merge, rebase, reset, checkout) modify the repo or communicate with remotes. fetch contacts the network but only downloads; it does not modify the working tree. diff and show have 50+ display flags that are all safe but easy to miss. git releases roughly every 2-3 months (e.g. 2.48 through 2.53 shipped between Jan 2025 and Feb 2026), with git 3.0 expected late 2026; flag coverage needs periodic review."
url = "https://git-scm.com/docs"
bare_flags = ["--help", "--version", "-V", "-h"]
[command.wrapper]
valued = ["-C"]
# push — Phase-1 pilot for per-flag escalation. Base is a vcs-sync (egress to the configured remote
# → network-admin, not auto-approved). Escalating flags ADD a capability: --force/--mirror can
# irreversibly destroy remote refs → remote-destroy-irreversible (yolo). All Denied in the 3-value
# projection today; the point is that the verdict is DERIVED and the flags carry their own research.
# (A future refinement: `-c core.sshCommand=…` is an execution escalator, but it is a valued flag
# matched by VALUE — the simple present/absent mechanism here doesn't cover it yet; the git handler's
# `-c` filter still catches `git -c … push`.)
[[command.sub]]
name = "push"
profile = "vcs-sync"
network_destination = true
destination_flag = "--repo"
fact = "Transfers local commits to a remote repository over the network, updating the remote's refs; sends the committed content to the configured remote. The target may be a configured remote name, an inline URL, or an `ext::<cmd>` transport that runs a local command."
source = "https://git-scm.com/docs/git-push"
judgment = "The send target's provenance is classified onto locus.provenance: a bare remote name is `established`, an inline URL/scp-path is `literal`, a `$VAR` is `opaque`; `ext::<cmd>` runs a local command and worst-cases as RCE."
standalone = ["--help", "-h"]
[[command.sub.flag]]
name = "--force"
classifies = "remote-destroy-irreversible"
fact = "Overwrites the remote ref with no merge check, discarding commits present only on the remote — can irreversibly destroy work not held locally."
source = "https://git-scm.com/docs/git-push — `--force`"
[[command.sub.flag]]
name = "-f"
classifies = "remote-destroy-irreversible"
fact = "Short form of --force."
source = "https://git-scm.com/docs/git-push — `-f`"
[[command.sub.flag]]
name = "--mirror"
classifies = "remote-destroy-irreversible"
fact = "Makes the remote match the local repo exactly, DELETING remote refs absent locally — mass irreversible remote deletion."
source = "https://git-scm.com/docs/git-push — `--mirror`"
# `--receive-pack`/`--exec` name the program git runs on the RECEIVING side — an arbitrary command
# execution vector. Any value is out-of-bounds, so `classifies = "unclassified"` (fail-closed worst).
[[command.sub.flag]]
name = "--receive-pack"
classifies = "unclassified"
fact = "Names the git-receive-pack program run on the remote to receive the push — an arbitrary-program execution surface."
source = "https://git-scm.com/docs/git-push — `--receive-pack`"
[[command.sub.flag]]
name = "--exec"
classifies = "unclassified"
fact = "Alias of --receive-pack: names the program run on the remote."
source = "https://git-scm.com/docs/git-push — `--exec`"
[[command.sub]]
name = "blame"
bare = false
standalone = [
"--color-by-age", "--color-lines",
"--help", "--incremental",
"--line-porcelain",
"--minimal",
"--porcelain", "--progress",
"--root",
"--show-email", "--show-name", "--show-number",
"--show-stats",
"-b", "-c", "-e", "-f", "-h", "-k", "-l", "-n", "-p", "-s", "-t", "-w",
]
valued = [
"--abbrev",
"--contents",
"--date",
"--ignore-rev", "--ignore-revs-file",
"-C", "-L", "-M", "-S",
]
[[command.sub]]
name = "branch"
standalone = [
"--all", "--help", "--ignore-case", "--list",
"--no-abbrev", "--no-color", "--no-column",
"--omit-empty", "--remotes", "--show-current",
"--verbose",
"-a", "-h", "-i", "-l", "-r", "-v", "-vv",
]
valued = [
"--abbrev", "--color", "--column", "--contains",
"--format", "--merged", "--no-contains", "--no-merged",
"--points-at", "--sort",
]
[[command.sub]]
name = "cat-file"
bare = false
standalone = [
"--batch-all-objects", "--buffer",
"--filters", "--follow-symlinks",
"--help", "--mailmap",
"--textconv", "--unordered", "--use-mailmap",
"-Z", "-e", "-h", "-p", "-s", "-t",
]
valued = [
"--batch", "--batch-check", "--batch-command",
"--filter", "--path",
]
[[command.sub]]
name = "check-ignore"
bare = false
standalone = [
"--help", "--no-index", "--non-matching",
"--quiet", "--stdin", "--verbose",
"-h", "-n", "-q", "-v", "-z",
]
[[command.sub]]
name = "config"
standalone = [
"--global", "--local", "--name-only", "--show-origin", "--show-scope",
"--system", "--worktree",
"-z",
]
valued = ["--blob", "--file", "-f"]
[[command.sub.sub]]
name = "--get"
[[command.sub.sub]]
name = "--get-all"
[[command.sub.sub]]
name = "--get-regexp"
[[command.sub.sub]]
name = "--help"
[[command.sub.sub]]
name = "--list"
[[command.sub.sub]]
name = "-h"
[[command.sub.sub]]
name = "-l"
[[command.sub]]
name = "count-objects"
standalone = [
"--help", "--human-readable", "--verbose",
"-H", "-h", "-v",
]
[[command.sub]]
name = "describe"
standalone = [
"--all", "--always", "--contains",
"--debug",
"--exact-match", "--first-parent",
"--help", "--long",
"--tags",
"-h",
]
valued = [
"--abbrev", "--broken",
"--candidates", "--dirty",
"--exclude", "--match",
]
[[command.sub]]
name = "diff"
standalone = [
"--cached", "--cc", "--check", "--color-words",
"--combined-all-paths", "--compact-summary", "--cumulative",
"--dirstat-by-file",
"--exit-code",
"--find-copies", "--find-copies-harder", "--find-renames",
"--first-parent", "--follow", "--full-index",
"--help", "--histogram",
"--ignore-all-space", "--ignore-blank-lines",
"--ignore-cr-at-eol", "--ignore-space-at-eol",
"--ignore-space-change", "--ignore-submodules",
"--merge-base", "--minimal",
"--name-only", "--name-status", "--no-color",
"--no-ext-diff", "--no-index", "--no-patch",
"--no-prefix", "--no-renames", "--no-textconv",
"--numstat",
"--ours",
"--patch", "--patch-with-raw", "--patch-with-stat",
"--patience", "--pickaxe-all",
"--quiet",
"--raw",
"--shortstat", "--staged", "--stat", "--summary",
"--text", "--textconv", "--theirs",
"-B", "-C", "-M", "-R",
"-a", "-b", "-h", "-p", "-q", "-u", "-w", "-z",
]
valued = [
"--abbrev", "--color", "--color-moved",
"--diff-algorithm", "--diff-filter",
"--diff-merges", "--dirstat", "--dst-prefix",
"--inter-hunk-context",
"--line-prefix",
"--output-indicator-new", "--output-indicator-old",
"--relative", "--src-prefix",
"--stat-count", "--stat-graph-width", "--stat-name-width", "--stat-width",
"--submodule",
"--unified", "--word-diff", "--word-diff-regex",
"-G", "-O", "-S", "-U",
]
[[command.sub]]
name = "diff-tree"
bare = false
standalone = [
"--cc", "--combined-all-paths",
"--find-copies-harder", "--full-index",
"--help", "--ignore-all-space", "--ignore-space-at-eol",
"--ignore-space-change",
"--merge-base", "--minimal",
"--name-only", "--name-status", "--no-commit-id",
"--no-ext-diff", "--no-patch", "--no-renames",
"--numstat",
"--patch", "--patch-with-raw", "--patch-with-stat",
"--pickaxe-all",
"--raw", "--root",
"--shortstat", "--stat", "--stdin", "--summary",
"--text",
"-B", "-C", "-M", "-R",
"-a", "-c", "-h", "-m", "-p", "-r", "-s", "-t", "-u", "-v", "-z",
]
valued = [
"--abbrev", "--diff-algorithm", "--diff-filter",
"--pretty",
"-O", "-S",
]
[[command.sub]]
name = "fetch"
standalone = [
"--all", "--append", "--atomic",
"--dry-run",
"--force",
"--help", "--ipv4", "--ipv6",
"--keep",
"--multiple",
"--negotiate-only", "--no-auto-gc", "--no-auto-maintenance",
"--no-show-forced-updates", "--no-tags", "--no-write-fetch-head",
"--porcelain", "--prefetch", "--progress",
"--prune", "--prune-tags",
"--quiet",
"--refetch",
"--set-upstream", "--show-forced-updates", "--stdin",
"--tags",
"--unshallow", "--update-head-ok", "--update-shallow",
"--verbose", "--write-commit-graph", "--write-fetch-head",
"-4", "-6",
"-P", "-a", "-f", "-h", "-k", "-m", "-n", "-p", "-q", "-t", "-u", "-v",
]
valued = [
"--deepen", "--depth",
"--filter",
"--jobs", "--negotiation-tip",
"--recurse-submodules", "--refmap",
"--server-option",
"--shallow-exclude", "--shallow-since",
"-j", "-o",
]
[[command.sub]]
name = "for-each-ref"
standalone = [
"--help", "--ignore-case", "--include-root-refs",
"--omit-empty",
"--perl", "--python",
"--shell", "--stdin",
"--tcl",
"-h", "-p", "-s",
]
valued = [
"--color", "--contains", "--count",
"--exclude", "--format",
"--merged", "--no-contains", "--no-merged",
"--points-at", "--sort",
]
[[command.sub]]
name = "grep"
bare = false
standalone = [
"--all-match", "--and",
"--basic-regexp", "--break",
"--cached", "--column", "--count",
"--exclude-standard", "--extended-regexp",
"--files-with-matches", "--files-without-match",
"--fixed-strings", "--full-name", "--function-context",
"--heading", "--help",
"--ignore-case", "--index", "--invert-match",
"--line-number",
"--name-only", "--no-color", "--no-index", "--null",
"--only-matching",
"--perl-regexp",
"--quiet",
"--recurse-submodules", "--recursive",
"--show-function",
"--text", "--textconv",
"--untracked",
"--word-regexp",
"-E", "-F", "-G", "-H", "-I", "-L", "-P", "-W",
"-a", "-c", "-h", "-i", "-l", "-n", "-o",
"-p", "-q", "-r", "-v", "-w", "-z",
]
valued = [
"--after-context", "--before-context",
"--color", "--context",
"--max-count", "--max-depth",
"--open-files-in-pager", "--threads",
"-A", "-B", "-C", "-O",
"-e", "-f", "-m",
]
[[command.sub]]
name = "help"
tolerate_unknown_short = true
tolerate_unknown_long = true
[[command.sub]]
name = "log"
standalone = [
"--abbrev-commit", "--all", "--ancestry-path",
"--author-date-order", "--bisect", "--boundary",
"--branches", "--cherry", "--cherry-mark", "--cherry-pick",
"--children", "--clear-decorations",
"--compact-summary", "--cumulative",
"--date-order",
"--dense", "--do-walk",
"--early-output",
"--first-parent", "--follow", "--full-diff", "--full-history",
"--graph",
"--help", "--ignore-missing", "--invert-grep",
"--left-only", "--left-right", "--log-size",
"--mailmap",
"--merges", "--minimal",
"--name-only", "--name-status",
"--no-abbrev-commit",
"--no-color", "--no-decorate",
"--no-expand-tabs", "--no-ext-diff", "--no-merges",
"--no-notes", "--no-patch", "--no-prefix",
"--no-renames", "--no-walk",
"--numstat",
"--oneline",
"--parents", "--patch", "--patch-with-raw",
"--patch-with-stat", "--patience", "--pickaxe-all", "--pickaxe-regex",
"--raw", "--reflog", "--regexp-ignore-case", "--relative-date", "--remotes",
"--reverse",
"--shortstat", "--show-linear-break", "--show-notes",
"--show-pulls", "--show-signature", "--simplify-by-decoration",
"--simplify-merges", "--source", "--sparse", "--stat",
"--stdin", "--summary",
"--tags", "--text", "--topo-order",
"--use-mailmap",
"-0", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9",
"-h", "-i", "-p", "-q", "-s", "-u",
]
valued = [
"--abbrev", "--after", "--author", "--before",
"--color", "--committer", "--date",
"--decorate", "--decorate-refs", "--decorate-refs-exclude",
"--diff-algorithm", "--diff-filter", "--diff-merges",
"--encoding", "--exclude",
"--format", "--glob", "--grep",
"--max-count", "--max-parents", "--min-parents",
"--pretty",
"--since", "--skip", "--until",
"-G", "-L", "-S", "-n",
]
[[command.sub]]
name = "ls-files"
standalone = [
"--cached", "--debug", "--deduplicate", "--deleted",
"--directory", "--empty-directory", "--eol",
"--error-unmatch", "--exclude-standard",
"--full-name",
"--help", "--ignored",
"--killed",
"--modified",
"--no-empty-directory",
"--others",
"--recurse-submodules", "--resolve-undo",
"--sparse", "--stage",
"--unmerged",
"-c", "-d", "-f", "-h", "-i", "-k", "-m", "-o", "-r", "-s", "-t", "-u", "-v", "-z",
]
valued = [
"--abbrev",
"--exclude", "--exclude-from", "--exclude-per-directory",
"--format",
"--with-tree",
"-X", "-x",
]
[[command.sub]]
name = "ls-remote"
standalone = [
"--branches",
"--exit-code",
"--get-url", "--help",
"--quiet",
"--refs",
"--symref",
"--tags",
"-b", "-h", "-q", "-t",
]
valued = [
"--server-option", "--sort",
"-o",
]
[[command.sub]]
name = "ls-tree"
bare = false
standalone = [
"--full-name", "--full-tree",
"--help", "--long",
"--name-only", "--name-status",
"--object-only",
"-d", "-h", "-l", "-r", "-t", "-z",
]
valued = [
"--abbrev", "--format",
]
[[command.sub]]
name = "merge-base"
bare = false
standalone = [
"--all", "--fork-point",
"--help", "--independent", "--is-ancestor",
"--octopus",
"-a", "-h",
]
[[command.sub]]
name = "merge-tree"
bare = false
standalone = [
"--allow-unrelated-histories",
"--help", "--messages", "--name-only",
"--quiet",
"--stdin", "--trivial-merge", "--write-tree",
"-h", "-z",
]
valued = [
"--merge-base",
"-X",
]
[[command.sub]]
name = "name-rev"
bare = false
standalone = [
"--all", "--always", "--annotate-stdin",
"--help", "--name-only", "--tags", "--undefined",
"-h",
]
valued = [
"--exclude", "--refs",
]
[[command.sub]]
name = "notes"
[[command.sub.sub]]
name = "--help"
[[command.sub.sub]]
name = "-h"
[[command.sub.sub]]
name = "list"
[[command.sub.sub]]
name = "show"
[[command.sub]]
name = "pull"
level = "SafeWrite"
standalone = [
"--all", "--allow-unrelated-histories", "--append", "--autostash",
"--commit", "--compact-summary", "--dry-run",
"--edit",
"--ff", "--ff-only", "--force",
"--help",
"--ipv4", "--ipv6",
"--keep",
"--no-autostash", "--no-commit", "--no-edit",
"--no-ff", "--no-gpg-sign", "--no-log",
"--no-rebase", "--no-recurse-submodules",
"--no-show-forced-updates", "--no-signoff",
"--no-squash", "--no-stat", "--no-tags",
"--no-verify", "--no-verify-signatures",
"--progress", "--prune",
"--quiet",
"--set-upstream", "--show-forced-updates",
"--signoff", "--squash", "--stat",
"--tags",
"--verbose", "--verify", "--verify-signatures",
"-4", "-6",
"-a", "-e", "-f", "-h", "-k", "-n", "-p", "-q", "-t", "-v",
]
valued = [
"--cleanup", "--depth", "--deepen",
"--filter",
"--gpg-sign", "--jobs",
"--log",
"--rebase", "--recurse-submodules", "--refmap",
"--server-option", "--shallow-exclude", "--shallow-since",
"--strategy", "--strategy-option",
"--upload-pack",
"-S", "-X", "-j", "-o", "-r", "-s",
]
[[command.sub]]
name = "reflog"
standalone = [
"--abbrev-commit", "--all", "--ancestry-path",
"--author-date-order", "--bisect", "--boundary",
"--branches", "--cherry", "--cherry-mark", "--cherry-pick",
"--children", "--clear-decorations",
"--compact-summary", "--cumulative",
"--date-order",
"--dense", "--do-walk",
"--early-output",
"--first-parent", "--follow", "--full-diff", "--full-history",
"--graph",
"--help", "--ignore-missing", "--invert-grep",
"--left-only", "--left-right", "--log-size",
"--mailmap",
"--merges", "--minimal",
"--name-only", "--name-status",
"--no-abbrev-commit",
"--no-color", "--no-decorate",
"--no-expand-tabs", "--no-ext-diff", "--no-merges",
"--no-notes", "--no-patch", "--no-prefix",
"--no-renames", "--no-walk",
"--numstat",
"--oneline",
"--parents", "--patch", "--patch-with-raw",
"--patch-with-stat", "--patience", "--pickaxe-all", "--pickaxe-regex",
"--raw", "--reflog", "--regexp-ignore-case", "--relative-date", "--remotes",
"--reverse",
"--shortstat", "--show-linear-break", "--show-notes",
"--show-pulls", "--show-signature", "--simplify-by-decoration",
"--simplify-merges", "--source", "--sparse", "--stat",
"--stdin", "--summary",
"--tags", "--text", "--topo-order",
"--use-mailmap",
"-0", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9",
"-h", "-i", "-p", "-q", "-s", "-u",
]
valued = [
"--abbrev", "--after", "--author", "--before",
"--color", "--committer", "--date",
"--decorate", "--decorate-refs", "--decorate-refs-exclude",
"--diff-algorithm", "--diff-filter", "--diff-merges",
"--encoding", "--exclude",
"--format", "--glob", "--grep",
"--max-count", "--max-parents", "--min-parents",
"--pretty",
"--since", "--skip", "--until",
"-G", "-L", "-S", "-n",
]
[[command.sub]]
# `git remote` — READ-ONLY forms only (migrated from the `git_remote` Rust handler to declarative
# subs; it was a pure read/mutate allowlist, no logic). bare / `-v` list configured remotes;
# `get-url` / `show` query them. Mutating subs (add/remove/rename/set-url/set-head/prune/update) deny
# by OMISSION — fail-closed, so a new or aliased mutating sub (`rm`) never slips through.
name = "remote"
nested_bare = true
standalone = ["-v", "--verbose", "-h", "--help"]
[[command.sub.sub]]
name = "get-url"
level = "SafeRead"
max_positional = 1
standalone = ["--push", "--all", "-h", "--help"]
# `git remote show [-n] <name>…` takes MULTIPLE remote names — positionals are harmless remote
# names on a read-only sub, so they are left unbounded (matches the retired handler's any-args form).
[[command.sub.sub]]
name = "show"
level = "SafeRead"
standalone = ["-n", "-h", "--help"]
[[command.sub]]
name = "rev-parse"
bare = false
tolerate_unknown_short = true
standalone = [
"--absolute-git-dir",
"--all",
"--branches",
"--git-common-dir", "--git-dir", "--git-path",
"--help", "--is-bare-repository", "--is-inside-git-dir",
"--is-inside-work-tree", "--is-shallow-repository",
"--local-env-vars",
"--quiet",
"--remotes",
"--shared-index-path", "--show-cdup", "--show-prefix",
"--show-superproject-working-tree", "--show-toplevel",
"--symbolic", "--symbolic-full-name",
"--tags", "--verify",
"-h", "-q",
]
valued = [
"--abbrev-ref", "--after", "--before",
"--default", "--exclude",
"--glob", "--prefix",
"--resolve-git-dir", "--short",
"--since", "--until",
]
[[command.sub]]
name = "shortlog"
standalone = [
"--committer", "--email", "--help", "--numbered", "--summary",
"-c", "-e", "-h", "-n", "-s",
]
valued = [
"--format", "--group",
]
[[command.sub]]
name = "show"
bare = false
standalone = [
"--abbrev-commit", "--cc", "--color-words",
"--combined-all-paths", "--compact-summary", "--cumulative",
"--expand-tabs", "--find-copies", "--find-renames", "--full-index",
"--help", "--histogram",
"--ignore-all-space", "--ignore-blank-lines",
"--ignore-space-at-eol", "--ignore-space-change",
"--mailmap", "--minimal",
"--name-only", "--name-status", "--no-color",
"--no-ext-diff", "--no-notes", "--no-patch",
"--no-prefix", "--no-renames", "--no-textconv",
"--numstat",
"--oneline",
"--patch", "--patch-with-raw", "--patch-with-stat",
"--patience", "--pickaxe-all", "--pickaxe-regex", "--raw",
"--shortstat", "--show-notes", "--show-signature",
"--source", "--stat", "--summary",
"--text", "--textconv", "--use-mailmap",
"-h", "-p", "-q", "-s", "-u", "-w",
]
valued = [
"--abbrev", "--color", "--color-moved",
"--decorate", "--decorate-refs", "--decorate-refs-exclude",
"--diff-algorithm", "--diff-filter", "--diff-merges",
"--encoding", "--format",
"--notes", "--pretty",
"--stat-count", "--stat-graph-width", "--stat-name-width",
"--submodule", "--word-diff", "--word-diff-regex",
"-G", "-O", "-S",
]
[[command.sub]]
name = "stash"
[[command.sub.sub]]
name = "--help"
tolerate_unknown_short = true
tolerate_unknown_long = true
[[command.sub.sub]]
name = "-h"
tolerate_unknown_short = true
tolerate_unknown_long = true
[[command.sub.sub]]
name = "list"
tolerate_unknown_short = true
tolerate_unknown_long = true
[[command.sub.sub]]
name = "show"
tolerate_unknown_short = true
standalone = ["--help", "--patch", "--stat", "-h", "-p", "-u"]
[[command.sub.sub]]
name = "push"
candidate = true
[[command.sub.sub]]
name = "pop"
candidate = true
[[command.sub.sub]]
name = "apply"
candidate = true
[[command.sub.sub]]
name = "drop"
candidate = true
[[command.sub.sub]]
name = "clear"
candidate = true
[[command.sub]]
name = "status"
standalone = [
"--ahead-behind", "--branch",
"--help", "--ignore-submodules",
"--long", "--no-ahead-behind",
"--no-renames", "--null",
"--renames",
"--short", "--show-stash",
"--verbose",
"-b", "-h", "-s", "-v", "-z",
]
valued = [
"--column", "--find-renames",
"--ignored",
"--porcelain",
"--untracked-files",
"-M", "-u",
]
[[command.sub]]
name = "symbolic-ref"
bare = false
max_positional = 1
standalone = [
"--help", "--no-recurse", "--quiet", "--recurse", "--short",
"-h", "-q",
]
[[command.sub]]
name = "tag"
standalone = [
"--help", "--list", "--no-color", "--no-column",
"--verify",
"-h", "-l", "-v",
]
valued = [
"--color", "--column", "--contains",
"--format", "--merged", "--no-contains", "--no-merged",
"--points-at", "--sort", "-n",
]
[[command.sub]]
name = "verify-commit"
bare = false
standalone = [
"--help", "--raw", "--verbose",
"-h", "-v",
]
[[command.sub]]
name = "verify-tag"
bare = false
standalone = [
"--help", "--raw", "--verbose",
"-h", "-v",
]
valued = [
"--format",
]
[[command.sub]]
name = "worktree"
[[command.sub.sub]]
name = "--help"
[[command.sub.sub]]
name = "-h"
[[command.sub.sub]]
name = "list"
standalone = ["--porcelain", "--verbose", "-v", "-z"]
max_positional = 0
[[command.sub]]
name = "cliff"
require_any = ["--no-exec"]
standalone = [
"--bumped-version", "--current",
"--help", "--latest",
"--no-exec",
"--offline",
"--topo-order",
"--unreleased", "--use-branch-tags",
"--verbose", "--version",
"-V", "-h", "-l", "-u", "-v",
]
valued = [
"--body", "--bump",
"--config", "--count-tags",
"--exclude-path",
"--from-context",
"--ignore-tags", "--include-path",
"--repository",
"--skip-commit", "--skip-tags", "--sort", "--strip",
"--tag", "--tag-pattern",
"--with-commit", "--with-tag-message",
"--workdir",
"-b", "-c", "-r", "-s", "-t", "-w",
]
# ═══ Local working-copy operations ═══════════════════════════════════════════════════════════════
# Re-researched 2026-07 against git 2.x under the facet taxonomy. Each declares a `profile`
# (archetypes.toml) rather than a legacy `level`, so the tier is DERIVED from the facets and the
# three-layer provenance below records what upstream documents (`fact`), our inference (`profile`),
# and our stance where the docs don't decide (`judgment`).
#
# On HOOKS: commit/merge/rebase run .git/hooks/* — an `execution` capability, not part of the write
# profile. It is contained by the region model, which freezes writes to `.git` (verified: a write to
# .git/hooks/pre-commit denies), so an agent cannot author what it would run. The hook file itself
# sits at `worktree-trusted`, inside developer's executor band.
[[command.sub]]
name = "switch"
profile = "local-mutate-trivial"
fact = "Switches HEAD to an existing branch, updating tracked files to match. git REFUSES to proceed when the switch would overwrite local modifications unless --force/--discard-changes/--merge is given; `git switch -` returns to the previous branch. -c/--create makes a new branch from the current HEAD."
source = "https://git-scm.com/docs/git-switch"
judgment = "The mildest possible worktree mutate: every resulting state is already in the object store, the inverse is one command, and git's own refusal makes data loss impossible — hence `trivial` rather than `recoverable`. The force family is deliberately omitted: --force/--discard-changes exist precisely to defeat that refusal and discard uncommitted work, which is a different (irreversible) capability."
max_positional = 1
standalone = ["--create", "--detach", "--guess", "--help", "--no-guess", "--no-track", "--track", "-c", "-d", "-h", "-t"]
valued = ["--orphan"]
[[command.sub]]
name = "add"
profile = "local-mutate-recoverable"
fact = "Stages worktree content into the index. Does not alter working-tree files; the index entry it replaces is recoverable while the prior commit exists, and `git restore --staged` unstages."
source = "https://git-scm.com/docs/git-add"
judgment = "-f/--force only overrides ignore rules (stages a file .gitignore would skip) — it neither deletes nor overwrites working-tree data, so it stays inside the same recoverable profile rather than escalating."
standalone = ["--all", "--dry-run", "--force", "--help", "--ignore-errors", "--intent-to-add", "--no-all", "--refresh", "--renormalize", "--update", "--verbose", "-A", "-N", "-f", "-h", "-n", "-u", "-v"]
valued = ["--chmod", "--pathspec-from-file"]
[[command.sub]]
name = "mv"
profile = "local-mutate-recoverable"
fact = "Moves or renames a tracked file and stages the change; refuses to overwrite an existing destination unless -f/--force is given."
source = "https://git-scm.com/docs/git-mv"
judgment = "The moved content is tracked, so the pre-move state is in the object store — recoverable. -f can clobber an existing destination file, but a TRACKED destination is likewise recoverable from HEAD; the operands are path-gated, so an out-of-worktree destination is caught by locus, not by this profile."
standalone = ["--dry-run", "--force", "--help", "--verbose", "-f", "-h", "-k", "-n", "-v"]
[[command.sub]]
name = "commit"
profile = "local-mutate-recoverable"
fact = "Records the staged index as a new commit on the current branch, running the pre-commit, prepare-commit-msg, commit-msg and post-commit hooks. --amend replaces the tip commit; the replaced commit stays reachable through the reflog for the gc grace period."
source = "https://git-scm.com/docs/git-commit"
judgment = "Editor-tier by the level model's own reckoning — its clause names `git commit` as the shape it intends. --amend is a rewrite rather than a plain append, but the superseded commit remains reflog-reachable, so it is recoverable, not destructive. --no-verify is admitted deliberately: it SKIPS hook execution, which narrows rather than widens what runs."
standalone = ["--all", "--allow-empty", "--allow-empty-message", "--amend", "--dry-run", "--help", "--no-edit", "--no-post-rewrite", "--no-verify", "--quiet", "--short", "--signoff", "--verbose", "-a", "-h", "-n", "-q", "-s", "-v"]
valued = ["--author", "--date", "--file", "--message", "--reuse-message", "--squash", "-C", "-F", "-m"]
[[command.sub]]
name = "merge"
profile = "local-mutate-recoverable"
fact = "Joins another history into the current branch, creating a merge commit (or fast-forwarding). Runs the pre-merge-commit and post-merge hooks. A conflicted merge leaves markers in the worktree; `git merge --abort` restores the pre-merge state."
source = "https://git-scm.com/docs/git-merge"
judgment = "An ADDITIVE history operation — it creates a commit rather than making one unreachable — so it is a mutate, not a destroy, and lands alongside commit. The pre-merge state is recoverable via --abort or ORIG_HEAD. Left at the same tier as commit deliberately: `git pull` is fetch+merge, and it would be incoherent for the composite to sit below its own component."
standalone = ["--abort", "--continue", "--ff", "--ff-only", "--help", "--no-commit", "--no-edit", "--no-ff", "--no-verify", "--quiet", "--squash", "--stat", "--verbose", "-h", "-q", "-v"]
valued = ["--into-name", "--log", "--message", "--strategy", "--strategy-option", "-X", "-m", "-s"]
[[command.sub]]
name = "rebase"
profile = "local-destroy-recoverable"
fact = "Replays commits onto a new base, producing NEW commit objects; the original commits become unreachable from any branch but remain in the reflog (and thus recoverable via ORIG_HEAD / `git rebase --abort`) until gc prunes them. Runs the pre-rebase and post-rewrite hooks."
source = "https://git-scm.com/docs/git-rebase"
judgment = "Classified as a DESTROY rather than a mutate: unlike merge it makes the previous commits unreachable, which is the facet that separates it from commit/merge and lifts it a tier. It is `recoverable` — not irreversible — because the reflog retains the originals, which is exactly the distinction from filter-branch (unbounded scale, and used precisely to make history unrecoverable). --exec/-x is omitted: it runs an arbitrary command per commit and needs delegation, not a blanket allow."
standalone = ["--abort", "--autosquash", "--autostash", "--continue", "--edit-todo", "--fork-point", "--help", "--interactive", "--keep-base", "--no-autosquash", "--no-autostash", "--quiet", "--quit", "--root", "--skip", "--verbose", "-h", "-i", "-q", "-v"]
valued = ["--onto", "--strategy", "--strategy-option", "-X", "-s"]