Git Configuration¶
[git] lets jarvy.toml codify Git settings the same way it codifies tools. New developers get a correctly configured Git on first jarvy setup — no more "I forgot to set my email" PRs.
Minimal Example¶
[git]
user_name = "Jane Doe"
user_email = { env = "GIT_EMAIL" }
default_branch = "main"
pull_rebase = true
user_email = { env = "GIT_EMAIL" } keeps personal email out of the shared config — each developer sets GIT_EMAIL in their shell.
Full Configuration¶
[git]
# Identity
user_name = "Jane Doe"
user_email = { env = "GIT_EMAIL", default = "[email protected]" }
# Commit signing
signing = true
signing_key = "~/.ssh/id_ed25519.pub"
signing_format = "ssh" # ssh | gpg, auto-detected from key extension
# Defaults
default_branch = "main"
pull_rebase = true
auto_stash = true
push_autosetup = true
editor = "vim"
# Line endings
autocrlf = "input" # true | false | input
eol = "lf"
# Credential helper (auto-detected per OS if omitted)
credential_helper = "osxkeychain"
# Scope
scope = "global" # global (~/.gitconfig) | local (.git/config)
# OS-aware defaults (enabled unless set to false)
os_defaults = true
# Aliases
[git.aliases]
co = "checkout"
br = "branch"
ci = "commit"
st = "status"
lg = "log --oneline --graph --decorate"
ConfigValue Resolution¶
Any string field accepts three forms:
| Form | Example | Behavior |
|---|---|---|
| Plain | user_name = "Jane" |
Used as-is |
| Env-only | user_email = { env = "GIT_EMAIL" } |
Reads env at runtime; errors if unset |
| Env + default | user_email = { env = "GIT_EMAIL", default = "[email protected]" } |
Reads env, falls back if unset |
Use the env+default form to keep secrets and personal info out of the shared jarvy.toml.
Signing¶
Commit signing is auto-detected from the key extension:
| Key | Format detected |
|---|---|
~/.ssh/id_ed25519.pub |
ssh |
~/.ssh/id_rsa.pub |
ssh |
| Any other path | gpg |
Override explicitly with signing_format:
When signing = true, Jarvy sets:
commit.gpgsign = truetag.gpgsign = truegpg.format = ssh|openpgpbased onsigning_formatuser.signingkey = <signing_key>- For SSH: configures
gpg.ssh.allowedSignersFileif present
Credential Helper Defaults¶
If credential_helper is omitted, Jarvy picks per OS:
| OS | Default |
|---|---|
| macOS | osxkeychain |
| Linux | cache |
| Windows | manager-core |
Override with any helper name accepted by git config credential.helper.
Scope¶
| Scope | File | Use |
|---|---|---|
global (default) |
~/.gitconfig |
Per-developer settings |
local |
.git/config |
Per-repo settings (e.g. work email for a work repo) |
A common pattern: keep user_name/user_email at scope local for a work repo, leave personal global config alone.
Aliases¶
These map directly to git config --<scope> alias.<name> "<value>". Existing aliases are overwritten.
OS-Aware Defaults¶
Like credential.helper, Jarvy fills in host-appropriate defaults for a few keys the user left unset. Enabled by default; set os_defaults = false to opt out.
| Key | Windows | macOS | Linux | Why |
|---|---|---|---|---|
core.autocrlf |
true |
input |
input |
CRLF↔LF conversion — Windows uses CRLF, Unix commits LF untouched |
core.longpaths |
true |
— | — | Allow paths beyond the 260-char MAX_PATH limit |
core.precomposeunicode |
— | true |
— | Recompose APFS/HFS+ NFD filenames to NFC for cross-platform matches |
Jarvy also applies a small set of cross-platform recommended defaults under the same os_defaults flag (unset keys only, [git.extra] still wins):
| Key | Value | Why |
|---|---|---|
fetch.prune |
true |
Drop local refs for branches deleted on the remote |
rerere.enabled |
true |
Reuse recorded conflict resolutions on re-merge/rebase |
merge.conflictStyle |
zdiff3 |
Show the common base in conflict markers (needs git ≥ 2.35; older git ignores it) |
These are only written when the corresponding value is unset. An explicit typed field (e.g. autocrlf = "false") or a [git.extra] entry for the same key always wins — Jarvy never overwrites an explicit value.
Extra Keys (escape hatch)¶
For git config keys Jarvy doesn't model as first-class fields, use [git.extra]. Keys are dotted git config keys; values are written verbatim via git config --<scope> <key> <value>.
[git.extra]
"core.fsmonitor" = "true"
"feature.manyFiles" = "true"
"diff.colorMoved" = "zebra"
"branch.main.rebase" = "true"
Every entry runs a layered gauntlet before it reaches git config:
- Key grammar / flag-injection. Keys must match the dotted grammar
section.key/section.subsection.keywith chars in[A-Za-z0-9._-], ≤ 256 bytes. Keys starting with-, missing a., or with empty segments are refused. Keys needing:or/(e.g.url.<base>.insteadOf) are not supported by this map. - Option-injection on the value. Values starting with
-(e.g.--unset) are refused — git would parse them as an option, not data. - Exec-capable keys (RCE guard) — keys whose value git executes are refused outright, for any value:
core.pager,core.editor,core.sshCommand,core.askPass,sequence.editor,diff.external,core.hooksPath,core.fsmonitor(except the builtintrue/falsetoggle),credential.helper,gpg[.<fmt>].program,uploadpack.packObjectsHook,init.templateDir, the per-commandpager.<cmd>family, and thefilter.<n>.{clean,smudge,process},<n>.textconv,mergetool.<n>.cmd,difftool.<n>.cmd,merge.<n>.driver,remote.<n>.{uploadpack,receivepack}families. The!-only filter cannot catch these (they need no marker), so they are denied by key. Override deliberately withJARVY_ALLOW_GIT_EXEC_KEYS=1. - Security-guardrail downgrades — values that weaken a git defense are refused:
core.protectNTFS/core.protectHFS= false (.git-path smuggling),safe.directory = *(CVE-2022-24765),safe.bareRepository = all(embedded bare-repo attack),fsck.*/fetch.fsck.*/receive.fsck.*=ignoreplustransfer/fetch/receive.fsckObjects= false (object-integrity checks), andhttp[.<url>].sslVerify = false(TLS MITM). Override withJARVY_ALLOW_GIT_PROTECT_DOWNGRADE=1. !-shell values are refused for every key (leading whitespace included — git trims it, so" !cmd"is caught too).
Also:
- Applied last, so an entry here overrides a modeled field targeting the same key.
- Prefer a typed field when one exists (
autocrlf,editor, etc.); reach for[git.extra]only when there's no first-party analogue.
Typed fields that map to exec-capable keys¶
[git.extra] refuses exec-capable keys outright, but the typed editor and credential_helper fields exist precisely to set core.editor / credential.helper. Those funnel through the same writer, which guards by value: a bare command plus flags is allowed (editor = "vim", editor = "code --wait", credential_helper = "osxkeychain", credential_helper = "cache --timeout=3600"), while a shell metacharacter (;, |, &, $, `, (), <>, newline), a leading !, or — for credential.helper — a program path (/tmp/x, ./x, ~/x) is refused. JARVY_ALLOW_GIT_EXEC_KEYS=1 overrides.
Remote configs (--from <url>)¶
A config fetched from a remote URL (ConfigOrigin::Remote) cannot apply [git] at all unless it sets allow_remote = true — mirroring [packages] allow_remote / [git_hooks] allow_remote. This closes the "remote broadens trust" hole: without the gate a remote config could write attacker-chosen keys (including exec-capable ones) to your shared ~/.gitconfig. Even with allow_remote = true, remote-sourced writes are forced to --local scope, so a remote can never touch the global config.
Preview before applying an untrusted config: jarvy setup --dry-run lists every OS-default and [git.extra] key that would be written. The [git.extra] preview runs the same guard gauntlet as a real apply, so a key that would be refused (e.g. core.pager, http.sslVerify = false) is shown as refused in the preview rather than as "would set" — preview matches apply.
What Runs¶
jarvy setup invokes git config --<scope> <key> <value> for each setting (after the remote-origin trust gate above). The order:
- Identity (
user.name,user.email) - Signing config (if enabled)
- Defaults (
init.defaultBranch,pull.rebase, etc.) - Line endings (
core.autocrlf,core.eol) - Credential helper
- OS-aware + recommended defaults (
core.autocrlf, Windowscore.longpaths, macOScore.precomposeunicode,fetch.prune,rerere.enabled,merge.conflictStyle— unset keys only; keys already matching are skipped so re-runs don't re-write) - Aliases
- Extra keys (
[git.extra], override-last)
If git itself is missing, the whole [git] section is skipped with a warning — install Git first.
CLI¶
jarvy setup # Applies [git] config
jarvy doctor # Verifies expected values are set
jarvy diff # Shows pending git config changes
Module¶
- Source:
src/git/ - Files:
config.rs,identity.rs,signing.rs,aliases.rs,setup.rs - Key types:
GitConfig,ConfigValue,ConfigScope,SigningFormat,AutoCrlf