diff --git a/pkg/commands/git_commands/git_command_builder.go b/pkg/commands/git_commands/git_command_builder.go index 78e0236a3..27f2e4a6c 100644 --- a/pkg/commands/git_commands/git_command_builder.go +++ b/pkg/commands/git_commands/git_command_builder.go @@ -1,6 +1,8 @@ package git_commands -import "strings" +import ( + "strings" +) // convenience struct for building git commands. Especially useful when // including conditional args @@ -66,6 +68,14 @@ func (self *GitCommandBuilder) GitDir(path string) *GitCommandBuilder { return self } +func (self *GitCommandBuilder) GitDirIf(condition bool, path string) *GitCommandBuilder { + if condition { + return self.GitDir(path) + } + + return self +} + func (self *GitCommandBuilder) ToArgv() []string { return append([]string{"git"}, self.args...) } diff --git a/pkg/commands/git_commands/sync.go b/pkg/commands/git_commands/sync.go index d67c6aa79..c32286e6d 100644 --- a/pkg/commands/git_commands/sync.go +++ b/pkg/commands/git_commands/sync.go @@ -82,6 +82,7 @@ type PullOptions struct { RemoteName string BranchName string FastForwardOnly bool + WorktreeGitDir string } func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error { @@ -90,6 +91,7 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error { ArgIf(opts.FastForwardOnly, "--ff-only"). ArgIf(opts.RemoteName != "", opts.RemoteName). ArgIf(opts.BranchName != "", opts.BranchName). + GitDirIf(opts.WorktreeGitDir != "", opts.WorktreeGitDir). ToArgv() // setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user @@ -97,7 +99,12 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error { return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run() } -func (self *SyncCommands) FastForward(task gocui.Task, branchName string, remoteName string, remoteBranchName string) error { +func (self *SyncCommands) FastForward( + task gocui.Task, + branchName string, + remoteName string, + remoteBranchName string, +) error { cmdArgs := NewGitCmd("fetch"). Arg(remoteName). Arg(remoteBranchName + ":" + branchName). diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go index 623514638..4415fb012 100644 --- a/pkg/gui/controllers/branches_controller.go +++ b/pkg/gui/controllers/branches_controller.go @@ -3,6 +3,7 @@ package controllers import ( "errors" "fmt" + "os" "strings" "github.com/jesseduffield/gocui" @@ -426,15 +427,23 @@ func (self *BranchesController) fastForward(branch *models.Branch) error { ) return self.c.WithLoaderPanel(message, func(task gocui.Task) error { - if branch == self.c.Helpers().Refs.GetCheckedOutRef() { + worktree, ok := self.worktreeForBranch(branch) + if ok { self.c.LogAction(action) + worktreeGitDir := "" + // if it is the current worktree path, no need to specify the path + if !git_commands.IsCurrentWorktree(worktree.Path) { + worktreeGitDir = worktree.GitDir + } + err := self.c.Git().Sync.Pull( task, git_commands.PullOptions{ RemoteName: branch.UpstreamRemote, BranchName: branch.UpstreamBranch, FastForwardOnly: true, + WorktreeGitDir: worktreeGitDir, }, ) if err != nil { @@ -444,7 +453,10 @@ func (self *BranchesController) fastForward(branch *models.Branch) error { return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC}) } else { self.c.LogAction(action) - err := self.c.Git().Sync.FastForward(task, branch.Name, branch.UpstreamRemote, branch.UpstreamBranch) + + err := self.c.Git().Sync.FastForward( + task, branch.Name, branch.UpstreamRemote, branch.UpstreamBranch, + ) if err != nil { _ = self.c.Error(err) } @@ -455,6 +467,20 @@ func (self *BranchesController) fastForward(branch *models.Branch) error { }) } +func (self *BranchesController) worktreePathForBranch(branch *models.Branch) string { + worktreeForRef, ok := self.worktreeForBranch(branch) + if ok { + return worktreeForRef.Path + } + + dir, err := os.Getwd() + if err != nil { + // swallow for now + return "" + } + return dir +} + func (self *BranchesController) createTag(branch *models.Branch) error { return self.c.Helpers().Tags.OpenCreateTagPrompt(branch.FullRefName(), func() {}) } diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index bfa82c45e..741135e2f 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -261,6 +261,13 @@ func (self *Shell) AddWorktree(base string, path string, newBranchName string) * }) } +// add worktree and have it checkout the base branch +func (self *Shell) AddWorktreeCheckout(base string, path string) *Shell { + return self.RunCommand([]string{ + "git", "worktree", "add", path, base, + }) +} + func (self *Shell) AddFileInWorktree(worktreePath string) *Shell { self.CreateFile(filepath.Join(worktreePath, "content"), "content") diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 0e619f8b9..981f705e2 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -227,6 +227,7 @@ var tests = []*components.IntegrationTest{ worktree.Crud, worktree.CustomCommand, worktree.DetachWorktreeFromBranch, + worktree.FastForwardWorktreeBranch, worktree.ForceRemoveWorktree, worktree.Rebase, worktree.RemoveWorktreeFromBranch, diff --git a/pkg/integration/tests/worktree/fast_forward_worktree_branch.go b/pkg/integration/tests/worktree/fast_forward_worktree_branch.go new file mode 100644 index 000000000..99d32f40a --- /dev/null +++ b/pkg/integration/tests/worktree/fast_forward_worktree_branch.go @@ -0,0 +1,52 @@ +package worktree + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FastForwardWorktreeBranch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Fast-forward a linked worktree branch from another worktree", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + // both main and linked worktree will have changed to fast-foward + shell.NewBranch("mybranch") + shell.CreateFileAndAdd("README.md", "hello world") + shell.Commit("initial commit") + shell.EmptyCommit("two") + shell.EmptyCommit("three") + shell.NewBranch("newbranch") + + shell.CloneIntoRemote("origin") + shell.SetBranchUpstream("mybranch", "origin/mybranch") + shell.SetBranchUpstream("newbranch", "origin/newbranch") + + // remove the 'three' commit so that we have something to pull from the remote + shell.HardReset("HEAD^") + shell.Checkout("mybranch") + shell.HardReset("HEAD^") + + shell.AddWorktreeCheckout("newbranch", "../linked-worktree") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains("mybranch").Contains("↓1").IsSelected(), + Contains("newbranch (worktree)").Contains("↓1"), + ). + Press(keys.Branches.FastForward). + Lines( + Contains("mybranch").Contains("✓").IsSelected(), + Contains("newbranch (worktree)").Contains("↓1"), + ). + NavigateToLine(Contains("newbranch (worktree)")). + Press(keys.Branches.FastForward). + Lines( + Contains("mybranch").Contains("✓"), + Contains("newbranch (worktree)").Contains("✓").IsSelected(), + ) + }, +})