fix(git): split long path args to avoid Windows CreateProcess limit

Git operations that stage or restore many files could exceed the Windows
command line length limit, causing os error 206 failures. This change
splits file paths into size-bounded chunks and concatenates the output
from multiple git invocations to keep commands within safe limits.

- Chunk file path arguments for Windows compatibility
- Add a local dev command to list pkg-config packages
This commit is contained in:
Christoph Brandau
2026-07-06 08:22:41 +02:00
parent 0732f9ca6d
commit d592894f28
3 changed files with 40 additions and 8 deletions
+2 -1
View File
@@ -78,7 +78,8 @@
"Bash(rustfmt --edition 2024 --check src/badge.rs src/main.rs)", "Bash(rustfmt --edition 2024 --check src/badge.rs src/main.rs)",
"Bash(rustfmt --edition 2024 --check src/git.rs)", "Bash(rustfmt --edition 2024 --check src/git.rs)",
"Bash(rustfmt --edition 2024 --check src/badge.rs)", "Bash(rustfmt --edition 2024 --check src/badge.rs)",
"Bash(rustfmt --edition 2024 --check src/git.rs src/main.rs)" "Bash(rustfmt --edition 2024 --check src/git.rs src/main.rs)",
"Bash(pkg-config --list-all)"
] ]
} }
} }
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "git-lite", "name": "git-lite",
"version": "2026.7.12", "version": "2026.7.11",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "git-lite", "name": "git-lite",
"version": "2026.7.12", "version": "2026.7.11",
"dependencies": { "dependencies": {
"@lucide/svelte": "^1.21.0", "@lucide/svelte": "^1.21.0",
"@tailwindcss/vite": "^4.3.1", "@tailwindcss/vite": "^4.3.1",
+36 -5
View File
@@ -3415,16 +3415,47 @@ fn is_auth_error(details: &str) -> bool {
|| d.contains("authentication required") || d.contains("authentication required")
} }
// Windows' CreateProcess rejects command lines longer than ~32K chars with
// "os error 206" (filename or extension too long). Staging/restoring a large
// number of files can easily exceed that, so split the paths across multiple
// invocations and concatenate their output.
const MAX_PATH_ARGS_CHARS: usize = 8_000;
fn run_git_with_paths( fn run_git_with_paths(
repo: &Path, repo: &Path,
base_args: &[&str], base_args: &[&str],
files: &[String], files: &[String],
) -> Result<Vec<u8>, String> { ) -> Result<Vec<u8>, String> {
let mut args = Vec::with_capacity(base_args.len() + files.len() + 1); if files.is_empty() {
args.extend(base_args.iter().map(OsString::from)); let args: Vec<OsString> = base_args.iter().map(OsString::from).collect();
args.push(OsString::from("--")); return run_git(repo, args);
args.extend(files.iter().map(OsString::from)); }
run_git(repo, args)
let mut combined = Vec::new();
let mut start = 0;
while start < files.len() {
let mut end = start;
let mut chunk_chars = 0usize;
while end < files.len() {
let len = files[end].len() + 1;
if end > start && chunk_chars + len > MAX_PATH_ARGS_CHARS {
break;
}
chunk_chars += len;
end += 1;
}
let chunk = &files[start..end];
let mut args = Vec::with_capacity(base_args.len() + chunk.len() + 1);
args.extend(base_args.iter().map(OsString::from));
args.push(OsString::from("--"));
args.extend(chunk.iter().map(OsString::from));
combined.extend(run_git(repo, args)?);
start = end;
}
Ok(combined)
} }
fn run_git_with_paths_cancellable( fn run_git_with_paths_cancellable(