mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-06 14:06:32 -04:00
When somebody else rebases a branch and force-pushes it, our branch shows up as diverged, say ↓5↑3, and that looks exactly like a branch that carries three commits of our own. The two want different treatment. The first one only has to be moved to its upstream, while the second one needs a decision about what to do with those commits. The panel doesn't tell them apart, so users have to look at each branch in turn to find out which one they are dealing with. Determine for every diverged branch whether it has commits of its own, and dim its divergence when it doesn't. This runs on a worker, as it takes a few git commands per diverged branch, and a branch keeps the answer from the previous load while a new one is in flight, so that the display doesn't flicker. A later commit teaches the fast-forward command to act on such a branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
198 lines
7.9 KiB
Go
198 lines
7.9 KiB
Go
package commands
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/go-errors/errors"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
|
"github.com/jesseduffield/lazygit/pkg/common"
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
|
"github.com/jesseduffield/lazygit/pkg/env"
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
)
|
|
|
|
// GitCommand is our main git interface
|
|
type GitCommand struct {
|
|
Blame *git_commands.BlameCommands
|
|
Branch *git_commands.BranchCommands
|
|
Commit *git_commands.CommitCommands
|
|
Config *git_commands.ConfigCommands
|
|
Custom *git_commands.CustomCommands
|
|
Diff *git_commands.DiffCommands
|
|
File *git_commands.FileCommands
|
|
Flow *git_commands.FlowCommands
|
|
Patch *git_commands.PatchCommands
|
|
Rebase *git_commands.RebaseCommands
|
|
Remote *git_commands.RemoteCommands
|
|
Stash *git_commands.StashCommands
|
|
Status *git_commands.StatusCommands
|
|
Submodule *git_commands.SubmoduleCommands
|
|
Sync *git_commands.SyncCommands
|
|
Tag *git_commands.TagCommands
|
|
WorkingTree *git_commands.WorkingTreeCommands
|
|
Bisect *git_commands.BisectCommands
|
|
Worktree *git_commands.WorktreeCommands
|
|
Version *git_commands.GitVersion
|
|
RepoPaths *git_commands.RepoPaths
|
|
GitHub *git_commands.GitHubCommands
|
|
HostingService *git_commands.HostingService
|
|
|
|
Loaders Loaders
|
|
}
|
|
|
|
type Loaders struct {
|
|
BranchLoader *git_commands.BranchLoader
|
|
CommitFileLoader *git_commands.CommitFileLoader
|
|
CommitLoader *git_commands.CommitLoader
|
|
FileLoader *git_commands.FileLoader
|
|
ReflogCommitLoader *git_commands.ReflogCommitLoader
|
|
RemoteLoader *git_commands.RemoteLoader
|
|
StashLoader *git_commands.StashLoader
|
|
TagLoader *git_commands.TagLoader
|
|
Worktrees *git_commands.WorktreeLoader
|
|
}
|
|
|
|
func NewGitCommand(
|
|
cmn *common.Common,
|
|
version *git_commands.GitVersion,
|
|
osCommand *oscommands.OSCommand,
|
|
gitConfig git_config.IGitConfig,
|
|
diffRendererConfigManager *config.DiffRendererConfigManager,
|
|
) (*GitCommand, error) {
|
|
repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version)
|
|
if err != nil {
|
|
return nil, errors.Errorf("Error getting repo paths: %v", err)
|
|
}
|
|
|
|
// A bare repo has no worktree for us to work in. Callers that can offer the
|
|
// user something better (app.setupRepo) check for this first; getting here
|
|
// means nobody could, e.g. because --git-dir was pointed at a bare repo.
|
|
if repoPaths.IsBareRepo() {
|
|
return nil, errors.New(cmn.Tr.BareRepoNotSupported)
|
|
}
|
|
|
|
err = os.Chdir(repoPaths.WorktreePath())
|
|
if err != nil {
|
|
return nil, utils.WrapError(err)
|
|
}
|
|
|
|
// Everything we run through the command builder gets told where the repo is
|
|
// by the builder itself, but subprocesses don't go through it: user-defined
|
|
// custom commands, an editor, and the lazygit we re-enter as git's sequence
|
|
// editor during a rebase. Put it in the process env for those.
|
|
env.SetGitLocationEnvVars(repoPaths.GitLocationEnvVars())
|
|
|
|
// Pin the config reads to the repo directory like all other git commands
|
|
// (see NewGitCmdObjBuilder); the config commands run outside that builder.
|
|
gitConfig.SetDir(repoPaths.WorktreePath())
|
|
|
|
return NewGitCommandAux(
|
|
cmn,
|
|
version,
|
|
osCommand,
|
|
gitConfig,
|
|
repoPaths,
|
|
diffRendererConfigManager,
|
|
), nil
|
|
}
|
|
|
|
func NewGitCommandAux(
|
|
cmn *common.Common,
|
|
version *git_commands.GitVersion,
|
|
osCommand *oscommands.OSCommand,
|
|
gitConfig git_config.IGitConfig,
|
|
repoPaths *git_commands.RepoPaths,
|
|
diffRendererConfigManager *config.DiffRendererConfigManager,
|
|
) *GitCommand {
|
|
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd, repoPaths.WorktreePath(), repoPaths.GitLocationEnvVars())
|
|
|
|
// here we're doing a bunch of dependency injection for each of our commands structs.
|
|
// This is admittedly messy, but allows us to test each command struct in isolation,
|
|
// and allows for better namespacing when compared to having every method living
|
|
// on the one struct.
|
|
// common ones are: cmn, osCommand, dotGitDir, configCommands
|
|
configCommands := git_commands.NewConfigCommands(cmn, gitConfig)
|
|
|
|
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, configCommands, diffRendererConfigManager)
|
|
|
|
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
|
|
statusCommands := git_commands.NewStatusCommands(gitCommon)
|
|
flowCommands := git_commands.NewFlowCommands(gitCommon)
|
|
remoteCommands := git_commands.NewRemoteCommands(gitCommon)
|
|
branchCommands := git_commands.NewBranchCommands(gitCommon)
|
|
syncCommands := git_commands.NewSyncCommands(gitCommon)
|
|
tagCommands := git_commands.NewTagCommands(gitCommon)
|
|
commitCommands := git_commands.NewCommitCommands(gitCommon)
|
|
customCommands := git_commands.NewCustomCommands(gitCommon)
|
|
diffCommands := git_commands.NewDiffCommands(gitCommon)
|
|
fileCommands := git_commands.NewFileCommands(gitCommon)
|
|
submoduleCommands := git_commands.NewSubmoduleCommands(gitCommon)
|
|
workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
|
rebaseCommands := git_commands.NewRebaseCommands(gitCommon, commitCommands, workingTreeCommands)
|
|
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
|
|
patchBuilder := patch.NewPatchBuilder(cmn.Log,
|
|
func(from string, to string, reverse bool, filename string, previousPath string, plain bool) (string, error) {
|
|
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, previousPath, plain)
|
|
})
|
|
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
|
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
|
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
|
blameCommands := git_commands.NewBlameCommands(gitCommon)
|
|
gitHubCommands := git_commands.NewGitHubCommands(gitCommon)
|
|
hostingServiceCommands := git_commands.NewHostingServiceCommand(gitCommon)
|
|
|
|
branchLoader := git_commands.NewBranchLoader(cmn, gitCommon, cmd, branchCommands.CurrentBranchInfo, branchCommands.HasLocalOnlyCommits, configCommands)
|
|
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
|
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.WorkingTreeState, gitCommon)
|
|
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
|
remoteLoader := git_commands.NewRemoteLoader(gitCommon)
|
|
worktreeLoader := git_commands.NewWorktreeLoader(gitCommon)
|
|
stashLoader := git_commands.NewStashLoader(cmn, cmd)
|
|
tagLoader := git_commands.NewTagLoader(cmn, cmd)
|
|
|
|
return &GitCommand{
|
|
Blame: blameCommands,
|
|
Branch: branchCommands,
|
|
Commit: commitCommands,
|
|
Config: configCommands,
|
|
Custom: customCommands,
|
|
Diff: diffCommands,
|
|
File: fileCommands,
|
|
Flow: flowCommands,
|
|
Patch: patchCommands,
|
|
Rebase: rebaseCommands,
|
|
Remote: remoteCommands,
|
|
Stash: stashCommands,
|
|
Status: statusCommands,
|
|
Submodule: submoduleCommands,
|
|
Sync: syncCommands,
|
|
Tag: tagCommands,
|
|
Bisect: bisectCommands,
|
|
WorkingTree: workingTreeCommands,
|
|
Worktree: worktreeCommands,
|
|
Version: version,
|
|
GitHub: gitHubCommands,
|
|
HostingService: hostingServiceCommands,
|
|
Loaders: Loaders{
|
|
BranchLoader: branchLoader,
|
|
CommitFileLoader: commitFileLoader,
|
|
CommitLoader: commitLoader,
|
|
FileLoader: fileLoader,
|
|
ReflogCommitLoader: reflogCommitLoader,
|
|
RemoteLoader: remoteLoader,
|
|
Worktrees: worktreeLoader,
|
|
StashLoader: stashLoader,
|
|
TagLoader: tagLoader,
|
|
},
|
|
RepoPaths: repoPaths,
|
|
}
|
|
}
|
|
|
|
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
|
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
|
|
}
|