mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Commit d94f2f05 dropped the GIT_OPTIONAL_LOCKS=0 env var that we used
to set on every git command, and re-added lock suppression only as a
--no-optional-locks flag on the background files refresh. The intent
was sound — a foreground `git status` should persist git's refreshed
stat-cache — but the change was too broad: it stopped suppressing
optional locks for every other command too.
The one that bites is the main-view diff. When a folder containing
submodules is selected, we render `git diff --submodule -- <dir>`, and
`--submodule` makes git run `git status` inside each submodule to
describe its "modified" state. That status now grabs the submodule's
index.lock. It runs as a PTY task on its own goroutine, so it races
any submodule-mutating action the user triggers — e.g. resetting a
submodule runs `git -C <submodule> stash`, which then fails with
"index.lock: File exists". This is what made submodule/reset_folder
flaky. `git status` is in fact the only command that takes the
optional lock, but the env var also covered its use inside `git diff
--submodule`, inside PTY-run commands, and inside git's own submodule
child processes — none of which a per-command flag reaches cleanly.
Invert the polarity to match how it worked before d94f2f05: the git
command builder disables optional locks on every command by default,
and the single command that benefits from taking the lock — the
foreground files refresh — opts back in. This restores the original
contention avoidance (including against the user's terminal git) while
keeping d94f2f05's stat-cache-persistence win for the foreground
refresh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
76 lines
1.6 KiB
Go
76 lines
1.6 KiB
Go
package oscommands
|
|
|
|
import (
|
|
"os/exec"
|
|
"testing"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/gocui"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestRemoveEnvVar(t *testing.T) {
|
|
cmd := exec.Command("git", "status")
|
|
cmd.Env = []string{
|
|
"PATH=/usr/bin",
|
|
"GIT_OPTIONAL_LOCKS=0",
|
|
"GIT_OPTIONAL_LOCKS_OTHER=1", // name is a prefix of ours but not the same var
|
|
"GIT_OPTIONAL_LOCKS=0", // duplicates must all be removed
|
|
"HOME=/home/me",
|
|
}
|
|
cmdObj := &CmdObj{cmd: cmd}
|
|
|
|
cmdObj.RemoveEnvVar("GIT_OPTIONAL_LOCKS")
|
|
|
|
assert.Equal(t, []string{
|
|
"PATH=/usr/bin",
|
|
"GIT_OPTIONAL_LOCKS_OTHER=1",
|
|
"HOME=/home/me",
|
|
}, cmdObj.GetEnvVars())
|
|
}
|
|
|
|
func TestCmdObjToString(t *testing.T) {
|
|
quote := func(s string) string {
|
|
return "\"" + s + "\""
|
|
}
|
|
|
|
scenarios := []struct {
|
|
cmdArgs []string
|
|
expected string
|
|
}{
|
|
{
|
|
cmdArgs: []string{"git", "push", "myfile.txt"},
|
|
expected: "git push myfile.txt",
|
|
},
|
|
{
|
|
cmdArgs: []string{"git", "push", "my file.txt"},
|
|
expected: "git push \"my file.txt\"",
|
|
},
|
|
}
|
|
|
|
for _, scenario := range scenarios {
|
|
cmd := exec.Command(scenario.cmdArgs[0], scenario.cmdArgs[1:]...)
|
|
cmdObj := &CmdObj{cmd: cmd}
|
|
actual := cmdObj.ToString()
|
|
if actual != scenario.expected {
|
|
t.Errorf("Expected %s, got %s", quote(scenario.expected), quote(actual))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClone(t *testing.T) {
|
|
task := gocui.NewFakeTask()
|
|
cmdObj := &CmdObj{task: task, cmd: &exec.Cmd{}}
|
|
clone := cmdObj.Clone()
|
|
if clone == cmdObj {
|
|
t.Errorf("Clone should not return the same object")
|
|
}
|
|
|
|
if clone.GetTask() == nil {
|
|
t.Errorf("Clone task should not be nil")
|
|
}
|
|
|
|
if clone.GetTask() != task {
|
|
t.Errorf("Clone should have the same task")
|
|
}
|
|
}
|