mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 15:46:26 -04:00
The worktrees tab now displays the checked-out branch (or detached HEAD state) for each worktree, making it much easier to see which worktree holds which branch. This is useful when you need to "unlock" a branch that's occupied by another worktree: you can now clearly see which worktrees are on a branch vs detached, without having to select each one individually. Also rename the "(main)" label to "(main worktree)" to avoid confusion with the common "main" branch name, and move it after the branch column.
151 lines
4.3 KiB
Go
151 lines
4.3 KiB
Go
package controllers
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"text/tabwriter"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
)
|
|
|
|
type WorktreesController struct {
|
|
baseController
|
|
*ListControllerTrait[*models.Worktree]
|
|
c *ControllerCommon
|
|
}
|
|
|
|
var _ types.IController = &WorktreesController{}
|
|
|
|
func NewWorktreesController(
|
|
c *ControllerCommon,
|
|
) *WorktreesController {
|
|
return &WorktreesController{
|
|
baseController: baseController{},
|
|
ListControllerTrait: NewListControllerTrait(
|
|
c,
|
|
c.Contexts().Worktrees,
|
|
c.Contexts().Worktrees.GetSelected,
|
|
c.Contexts().Worktrees.GetSelectedItems,
|
|
),
|
|
c: c,
|
|
}
|
|
}
|
|
|
|
func (self *WorktreesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
|
bindings := []*types.Binding{
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.New),
|
|
Handler: self.add,
|
|
Description: self.c.Tr.NewWorktree,
|
|
DisplayOnScreen: true,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.Select),
|
|
Handler: self.withItem(self.enter),
|
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
|
Description: self.c.Tr.Switch,
|
|
Tooltip: self.c.Tr.SwitchToWorktreeTooltip,
|
|
DisplayOnScreen: true,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
|
Handler: self.withItem(self.enter),
|
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
|
Handler: self.withItem(self.open),
|
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
|
Description: self.c.Tr.OpenInEditor,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.Remove),
|
|
Handler: self.withItem(self.remove),
|
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
|
Description: self.c.Tr.Remove,
|
|
Tooltip: self.c.Tr.RemoveWorktreeTooltip,
|
|
DisplayOnScreen: true,
|
|
},
|
|
}
|
|
|
|
return bindings
|
|
}
|
|
|
|
func (self *WorktreesController) GetOnRenderToMain() func() {
|
|
return func() {
|
|
var task types.UpdateTask
|
|
worktree := self.context().GetSelected()
|
|
if worktree == nil {
|
|
task = types.NewRenderStringTask(self.c.Tr.NoWorktreesThisRepo)
|
|
} else {
|
|
main := ""
|
|
if worktree.IsMain {
|
|
main = style.FgDefault.Sprintf(" %s", self.c.Tr.MainWorktree)
|
|
}
|
|
|
|
missing := ""
|
|
if worktree.IsPathMissing {
|
|
missing = style.FgRed.Sprintf(" %s", self.c.Tr.MissingWorktree)
|
|
}
|
|
|
|
var builder strings.Builder
|
|
w := tabwriter.NewWriter(&builder, 0, 0, 2, ' ', 0)
|
|
_, _ = fmt.Fprintf(w, "%s:\t%s%s\n", self.c.Tr.Name, style.FgGreen.Sprint(worktree.Name), main)
|
|
branch := style.FgYellow.Sprint(worktree.Branch)
|
|
if worktree.Branch == "" && worktree.Head != "" {
|
|
branch = style.FgYellow.Sprintf("HEAD detached at %s", utils.ShortHash(worktree.Head))
|
|
}
|
|
_, _ = fmt.Fprintf(w, "%s:\t%s\n", self.c.Tr.Branch, branch)
|
|
_, _ = fmt.Fprintf(w, "%s:\t%s%s\n", self.c.Tr.Path, style.FgCyan.Sprint(worktree.Path), missing)
|
|
_ = w.Flush()
|
|
|
|
task = types.NewRenderStringTask(builder.String())
|
|
}
|
|
|
|
self.c.RenderToMainViews(types.RefreshMainOpts{
|
|
Pair: self.c.MainViewPairs().Normal,
|
|
Main: &types.ViewUpdateOpts{
|
|
Title: self.c.Tr.WorktreeTitle,
|
|
Task: task,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
func (self *WorktreesController) add() error {
|
|
return self.c.Helpers().Worktree.NewWorktree()
|
|
}
|
|
|
|
func (self *WorktreesController) remove(worktree *models.Worktree) error {
|
|
if worktree.IsMain {
|
|
return errors.New(self.c.Tr.CantDeleteMainWorktree)
|
|
}
|
|
|
|
if worktree.IsCurrent {
|
|
return errors.New(self.c.Tr.CantDeleteCurrentWorktree)
|
|
}
|
|
|
|
return self.c.Helpers().Worktree.Remove(worktree, false)
|
|
}
|
|
|
|
func (self *WorktreesController) GetOnClick() func() error {
|
|
return self.withItemGraceful(self.enter)
|
|
}
|
|
|
|
func (self *WorktreesController) enter(worktree *models.Worktree) error {
|
|
return self.c.Helpers().Worktree.Switch(worktree, context.WORKTREES_CONTEXT_KEY)
|
|
}
|
|
|
|
func (self *WorktreesController) open(worktree *models.Worktree) error {
|
|
return self.c.Helpers().Files.OpenDirInEditor(worktree.Path)
|
|
}
|
|
|
|
func (self *WorktreesController) context() *context.WorktreesContext {
|
|
return self.c.Contexts().Worktrees
|
|
}
|