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
//! What:
//! Whether the checkout of a new worktree may be given Git's parallel checkout, and the option that does it.
//! Why:
//! Git's `checkout.workers` defaults to one worker, and a checkout of twenty thousand files measured several times faster with
//! `checkout.workers=0` (one worker per logical CPU). The owner decided on 2026-11-03 "Pass only if unset": a value the person
//! set anywhere Git reads (a configuration file of any scope, `GIT_CONFIG_*`, `GIT_CONFIG_PARAMETERS`, a `-c` or
//! `--config-env` they typed) is theirs and stands alone; only where nothing sets the key does the wrapper put
//! `-c checkout.workers=0` before the subcommand of its own checkout run. The files are read through the one configuration
//! read the wrapper already makes, so no extra Git start is added.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! if (!workersSetAnywhere(args, env, configSnapshot)) args.splice(subcommandIndex, 0, '-c', 'checkout.workers=0');
//! ```
use super::config_snapshot::{ConfigSnapshot, read_config_snapshot};
use super::diagnostic_log::debug;
use super::global_arguments::{GlobalOutcome, global_layout};
use super::transaction_git::{GitContext, subcommand_of};
use std::ffi::{OsStr, OsString};
use std::path::Path;
/// The configuration key, in the canonical form Git lists it.
pub const KEY: &str = "checkout.workers";
/// The option the wrapper adds: one worker per logical CPU.
const PARALLEL: &str = "checkout.workers=0";
/// What:
/// How many leading words of the arguments are global options, which is where the subcommand starts.
/// Why:
/// The wrapper reads global options with one reader (`--git-dir <path>` and `--work-tree <path>` take a separate value, `-c`
/// and `-C` too, and `--option=value` forms are one word), so the position agrees with where the wrapper's own hook options go.
/// Arguments with no subcommand have no position, and the whole list is global options.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const prefixLength = layout.outcome === 'command' ? layout.prefixLen : args.length;
/// ```
fn global_prefix_length(arguments: &[OsString]) -> usize {
let layout = global_layout(arguments);
return if layout.outcome == GlobalOutcome::Command { layout.prefix_len } else { arguments.len() };
}
/// What:
/// Whether a global option the person typed before the subcommand names the key: `-c checkout.workers=<n>`,
/// `-ccheckout.workers=<n>` or `--config-env=checkout.workers=<variable>`.
/// Why:
/// A typed value is the person's, and the options come before the subcommand, so reading the words up to it finds them all.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// args.slice(0, subcommandIndex).some((word) => word.toLowerCase().includes('checkout.workers'))
/// ```
pub fn typed_before_subcommand(arguments: &[OsString]) -> bool {
let before: &[OsString] = &arguments[..global_prefix_length(arguments)];
return before.iter().any(|word: &OsString| { return word.to_string_lossy().to_lowercase().contains(KEY); });
}
/// What:
/// Whether the environment sets the key: a `GIT_CONFIG_KEY_<n>` variable naming it, or `GIT_CONFIG_PARAMETERS` mentioning it.
/// Why:
/// Git reads these after every file, so a value there is the person's whatever the files say; the check is on the text, so a
/// spelling the reader would decline is still found.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// Object.entries(env).some(([name, value]) => (name.startsWith('GIT_CONFIG_KEY_') || name === 'GIT_CONFIG_PARAMETERS') && value.toLowerCase().includes('checkout.workers'))
/// ```
pub fn environment_sets<'a>(variables: impl IntoIterator<Item = (&'a OsStr, &'a OsStr)>) -> bool {
return variables.into_iter().any(|(name, value): (&OsStr, &OsStr)| {
let variable: String = name.to_string_lossy().into_owned();
return (variable.starts_with("GIT_CONFIG_KEY_") || variable == "GIT_CONFIG_PARAMETERS") && value.to_string_lossy().to_lowercase().contains(KEY);
});
}
/// What:
/// Whether any entry of the configuration read has the key, in any scope.
/// Why:
/// The listing holds every file Git reads, in precedence order; one entry of any value is the person's.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// snapshot.entries.some((entry) => entry.key === 'checkout.workers')
/// ```
pub fn configured(snapshot: &ConfigSnapshot) -> bool {
return snapshot.entries().iter().any(|entry| { return entry.key == KEY; });
}
/// What:
/// Whether the wrapper may give the checkout parallel workers: nothing the person typed, exported or configured sets the key.
/// Why:
/// A configuration that cannot be read leaves the answer unknown, and unknown means the person's setting may exist, so
/// nothing is added.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const wanted = await parallelCheckoutWanted({ context, cwd, arguments });
/// ```
pub fn parallel_checkout_wanted(context: &GitContext, cwd: &Path, arguments: &[OsString]) -> bool {
if typed_before_subcommand(arguments) {
debug("parallelCheckoutWanted", "the person typed the key; nothing is added");
return false;
}
let process: Vec<(OsString, OsString)> = std::env::vars_os().collect();
let exported = process.iter().chain(context.overlay.iter()).map(|(name, value): &(OsString, OsString)| { return (name.as_os_str(), value.as_os_str()); });
if environment_sets(exported) {
debug("parallelCheckoutWanted", "the environment sets the key; nothing is added");
return false;
}
let Some(snapshot) = read_config_snapshot(context, cwd) else {
debug("parallelCheckoutWanted", "the configuration could not be read; nothing is added");
return false;
};
let wanted: bool = !configured(&snapshot);
debug("parallelCheckoutWanted", if wanted { "no configuration file sets the key; the option is added" } else { "a configuration file sets the key; nothing is added" });
return wanted;
}
/// What:
/// Put `-c checkout.workers=0` in front of the subcommand of the arguments.
/// Why:
/// Git takes `-c` only as a global option, before the subcommand, after the options the person and the wrapper put there.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// args.splice(subcommandIndex, 0, '-c', 'checkout.workers=0');
/// ```
pub fn insert_parallel_checkout(arguments: &mut Vec<OsString>) {
let layout = global_layout(arguments.as_slice());
if layout.outcome != GlobalOutcome::Command {
return;
}
arguments.splice(layout.prefix_len..layout.prefix_len, [OsString::from("-c"), OsString::from(PARALLEL)]);
}
/// What:
/// Whether the arguments are the wrapper's own checkout of a new worktree after a snapshot or clone attempt failed:
/// `reset --hard` as the subcommand.
/// Why:
/// That run is the checkout the person's `git worktree add` would have made, so it takes the same option; every other step
/// of the ladder is a bookkeeping or difference step and stays as it is.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// subcommand(args) === 'reset' && args.includes('--hard')
/// ```
pub fn is_fallback_checkout(arguments: &[OsString]) -> bool {
return matches!(subcommand_of(arguments), Some((_index, name)) if name == "reset") && arguments.iter().any(|word: &OsString| { return word == "--hard"; });
}
/// Parallel-checkout controls stay out of the release executable.
#[cfg(test)]
#[path = "checkout_workers_tests.rs"]
mod tests;