lazygit builds a shell command by interpolating Quote'd arguments into a
template and running the result via `cmd /c`. Several things were wrong
on Windows:
- Quote emitted bash-style `\"…\"` quoting, which cmd.exe doesn't
understand. Making it usable at all previously required a fragile
round-trip through str.ToArgv and re-escaping.
- The assembled command line was handed to `cmd /c` without `/s`, so
cmd's default rules stripped the wrong quotes once the line contained
more than two of them (e.g. a quoted editor path at a location with
spaces, plus a quoted filename that also contains spaces).
- Shell metacharacters were escaped with `^` (`&` → `^&`, etc.), which
neutralised command chaining, pipes, redirection and `%VAR%` expansion
in custom commands.
Quote now emits the standard Windows convention directly, and NewShell
hands cmd.exe the fully-assembled line verbatim via SysProcAttr.CmdLine,
wrapped as `cmd /s /c "<command>"`. The /s flag strips exactly the outer
quote pair we add, leaving each argument's own quoting intact. With the
`^` escaping gone, metacharacters in a custom command reach cmd as the
author intended; this also removes the spurious `^` reported in #3092.
Fixes#5560Fixes#2427Fixes#4147
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On Windows, NewShell escapes shell metacharacters (`&`, `|`, `<`, `>`,
`%`) with `^` and splits the command into separate arguments. The
operators in a custom command therefore never reach cmd as operators,
so command chaining (`&&`), pipes, redirection and `%VAR%` expansion all
silently break (#2427, #4147, #5113; the stray `^` is also what #3092
reports).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On Windows, Quote wraps arguments in bash-style `\"…\"` and rewrites
embedded double quotes as `"'"'"`. Neither convention is understood by
cmd.exe or CommandLineToArgvW, so commands built from quoted arguments
are mis-parsed once they contain quotes or spaces (#5560).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Windows cannot remove files while handles are still open. In
TestOSCommandFileType we created files and immediately called RemoveAll without
closing handles, which left untracked artifacts (e.g. "testFile" and "file with
spaces") in the working tree. Close the handles and assert RemoveAll succeeds so
cleanup failures are visible.
By constructing an arg vector manually, we no longer need to quote arguments
Mandate that args must be passed when building a command
Now you need to provide an args array when building a command.
There are a handful of places where we need to deal with a string,
such as with user-defined custom commands, and for those we now require
that at the callsite they use str.ToArgv to do that. I don't want
to provide a method out of the box for it because I want to discourage its
use.
For some reason we were invoking a command through a shell when amending a
commit, and I don't believe we needed to do that as there was nothing user-
supplied about the command. So I've switched to using a regular command out-
side the shell there