1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! The workspace's single entry point for constructing a `git` subprocess — #7171.
//!
//! Why: 41 detached `git maintenance run --auto` repacks hit the shared 21 GB
//! `.git` from ~25 worktrees in one incident (load 141) because every one of
//! the ~90 production `Command::new("git")` sites across trusty-common and
//! trusty-mpm ran with git's own auto-maintenance heuristics live. Git decides
//! whether to run background maintenance/gc **after any command that touches
//! the object store** (`fetch`, `commit`, `checkout`, …), so a fleet of
//! worktrees each running ordinary git commands independently triggers
//! independent maintenance runs against the ONE shared object store they all
//! point at (`.git/worktrees/*` share one `objects/` directory). This module
//! is the common-entry-point rule (`CLAUDE.md`, "Common entry point, clean
//! domain demarcation") applied to git spawns: every caller that wants a `git`
//! subprocess gets one through here, so the fix lands once instead of at each
//! of the ~90 sites.
//! What: [`command`] and [`command_in`] build a `std::process::Command`
//! (async callers: [`tokio_command`] / [`tokio_command_in`] build a
//! `tokio::process::Command`) that always carries
//! [`MAINTENANCE_DISABLE_ARGS`] as the FIRST argv entries. `-c key=value` is
//! passed on argv rather than written to a config file, so it outranks repo
//! config, global config, AND system config — a worktree cannot re-enable
//! maintenance for itself even if something writes `maintenance.auto = true`
//! into the shared `.git/config`, and no per-repo provisioning step is needed
//! for a git command that already runs through this module. Provisioning
//! (writing `maintenance.auto=false` / `gc.auto=0` into a managed checkout's
//! `.git/config`) is the separate, complementary fix in
//! `trusty-mpm::core::git_maintenance` for the ambient case — an operator
//! running git directly in a worktree, outside anything that calls this
//! module.
//! Test: `command_disables_maintenance_and_gc`,
//! `command_in_adds_dash_c_before_the_directory`,
//! `tokio_command_disables_maintenance_and_gc` (feature `unconditional-only`).
//!
//! [`command`]: crate::git::command
//! [`command_in`]: crate::git::command_in
//! [`tokio_command`]: crate::git::tokio_command
//! [`tokio_command_in`]: crate::git::tokio_command_in
//! [`MAINTENANCE_DISABLE_ARGS`]: crate::git::MAINTENANCE_DISABLE_ARGS
use Path;
use Command;
/// `-c` argv pairs that disable git's automatic background maintenance and
/// gc for the single invocation they are attached to (#7171).
///
/// Why: `git -c key=value` wins over every config file (repo, global,
/// system), so passing these on argv is the only form that cannot be
/// silently overridden by config a worktree does not control.
/// What: `maintenance.auto=false` stops git from scheduling a background
/// `git maintenance run --auto` after this command; `gc.auto=0` stops the
/// older, still-live `git gc --auto` heuristic some git subcommands check
/// independently of the maintenance scheduler. Both are pinned — one alone
/// leaves the other heuristic live.
/// Test: `command_disables_maintenance_and_gc`.
pub const MAINTENANCE_DISABLE_ARGS: & = &;
/// Build a bare `git` [`std::process::Command`] with automatic
/// maintenance/gc disabled.
///
/// Why: the single place every synchronous git spawn in the workspace
/// should originate from (#7171) — see the module doc.
/// What: `git -c maintenance.auto=false -c gc.auto=0`, with no subcommand or
/// `-C` yet — callers append their own args (and `-C`/`current_dir` where a
/// target directory applies) exactly as they would on a plain
/// `Command::new("git")`.
/// Test: `command_disables_maintenance_and_gc`.
/// [`command`] plus `-C <dir>`, for the common case of a git invocation
/// scoped to one repository or worktree.
///
/// Why: nearly every call site immediately does `.arg("-C").arg(dir)`;
/// folding it in here removes one more place a future site could copy a bare
/// `Command::new("git")` instead of this module's entry point.
/// What: `git -c maintenance.auto=false -c gc.auto=0 -C <dir>`. `-C` is a
/// top-level git option, so its position relative to the `-c` pairs above
/// does not matter — both must (and here do) precede the subcommand a caller
/// appends next.
/// Test: `command_in_adds_dash_c_before_the_directory`.
/// [`command`], as a `tokio::process::Command` for an async call site.
///
/// Why: some daemon call sites already run git under `tokio::process` (the
/// blocking cost of `std::process::Command::output()` inside an async
/// handler is what they avoid); those sites need the same maintenance-free
/// argv without going through `spawn_blocking` just to reach [`command`].
/// What: `git -c maintenance.auto=false -c gc.auto=0`, built on
/// `tokio::process::Command` — `tokio`'s `process` feature is already an
/// unconditional dependency of this crate.
/// Test: `tokio_command_disables_maintenance_and_gc`.
/// [`tokio_command`] plus `-C <dir>` — the async counterpart to [`command_in`].
///
/// Test: `tokio_command_in_adds_dash_c_before_the_directory`.