GIT_DIR and GIT_WORK_TREE tell git where our repo is, and every command
we run inherits them — including the ones we point at a submodule or
another worktree. git resolves those against our repo instead, and says
nothing about it: with GIT_DIR set, `git -C mysub log -1` reports the
superproject's commit. So opening lazygit with --git-dir/--work-tree
quietly broke resolving submodule conflicts, stashing and resetting a
submodule, and detaching another worktree; the worktree list came back
claiming every worktree shared our git dir.
Drop the two variables from the commands that address another repo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reset told git to change directory with -C while runInParentModule does
it by setting the command's working directory, but they were computing
the same directory for the same reason. Use the helper, so that there is
one place that knows what running in a nested submodule's parent means.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Its working directory resolves against the process rather than against
the repo the command builder pins commands to, which is only safe
because nothing but foreground commands come through here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The refresh workers read a few files at paths relative to the process
working directory: the submodule config read of .gitmodules, the files
refresh's check for conflict markers, and the submodule stash's
existence check. Git commands are pinned to the repo their instance was
created for, but these Go file reads still followed the cwd, so a
background refresh crossing a repo switch would read the new repo's
files while computing data for the old one. Join them with the worktree
root of the instance they belong to. (Most git-state file reads —
working tree state, rebase todos, bisect info — already resolve
against RepoPaths and need no change.)
This also fixes the submodule stash's existence check for nested
submodules: it stat'ed submodule.Path, which is relative to the parent
module, against the repo root — now it uses the submodule's full path,
matching the stash command right below it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deleting a nested submodule (and updating its URL) chdir'd the whole
process into the parent module, ran its git commands there, and chdir'd
back. Only those commands need to run there, and a process-wide chdir
leaks the parent module's directory into any command another goroutine
spawns during that window (e.g. a background refresh's). Set the
directory on the commands themselves instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When a conflicted submodule is selected, the main view shows the commits
each side added relative to their common ancestor as two indented logs,
labelled current and incoming, so it's clear which commit each side would
resolve to.
The logs aren't truncated (the view scrolls). If a side added no commits
of its own (e.g. it was rewound to an ancestor of the other), its head
commit is shown instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When both sides of a merge moved a submodule's gitlink, git reports it as "UU".
Pressing space used to fall into the submodule no-op guard and pop the confusing
"Nothing to stage..." error, and enter just entered the submodule, which does
nothing to resolve the superproject conflict.
Treat a conflicted submodule like the other non-textual conflicts: both space
and enter now open a picker offering the two candidate commits, "current" and
"incoming", each labelled with its summary. `git checkout --ours/--theirs` is a
no-op on gitlinks, so we resolve by checking the submodule out at the chosen
commit and staging it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A submodule that only has dirty or untracked content (no new commit) can't
be staged from the parent repo, but it still shows up as having unstaged
changes. Pressing stage on it therefore briefly flashed as staged and then
reverted, without explaining why nothing was staged.
Detect this case (via `git submodule status`, where a '+' prefix marks a
stageable commit change) in the shared stage/unstage decision: if the only
thing that looks stageable is such a submodule, don't try to stage it.
Instead unstage if there's anything staged to unstage, so the toggle stays
symmetric; otherwise show an error explaining that there's nothing to stage.
Because the decision is shared, this covers both the stage (space) and
stage-all (a) keybindings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
I took the set of enabled checks from revive's recommended configuration [1],
and removed some that I didn't like. There might be other useful checks in
revive that we might want to enable, but this is a nice improvement already.
The bulk of the changes here are removing unnecessary else statements after
returns, but there are a few others too.
[1] https://github.com/mgechev/revive?tab=readme-ov-file#recommended-configuration
There are quite a few paths you might want to get e.g. the repo's path, the worktree's path,
the repo's git dir path, the worktree's git dir path. I want these all obtained once and
then used when needed rather than having to have IO whenever we need them. This is not so
much about reducing time spent on IO as it is about not having to care about errors every time
we want a path.
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