mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Support creating worktrees from refs
This commit is contained in:
		| @@ -6,8 +6,6 @@ import ( | ||||
| 	"io/fs" | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| ) | ||||
|  | ||||
| type WorktreeCommands struct { | ||||
| @@ -20,10 +18,30 @@ func NewWorktreeCommands(gitCommon *GitCommon) *WorktreeCommands { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *WorktreeCommands) New(worktreePath string, committish string) error { | ||||
| 	cmdArgs := NewGitCmd("worktree").Arg("add", worktreePath, committish).ToArgv() | ||||
| type NewWorktreeOpts struct { | ||||
| 	// required. The path of the new worktree. | ||||
| 	Path string | ||||
| 	// required. The base branch/ref. | ||||
| 	Base string | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs).Run() | ||||
| 	// if true, ends up with a detached head | ||||
| 	Detach bool | ||||
|  | ||||
| 	// optional. if empty, and if detach is false, we will checkout the base | ||||
| 	Branch string | ||||
| } | ||||
|  | ||||
| func (self *WorktreeCommands) New(opts NewWorktreeOpts) error { | ||||
| 	if opts.Detach && opts.Branch != "" { | ||||
| 		panic("cannot specify branch when detaching") | ||||
| 	} | ||||
|  | ||||
| 	cmdArgs := NewGitCmd("worktree").Arg("add"). | ||||
| 		ArgIf(opts.Detach, "--detach"). | ||||
| 		ArgIf(opts.Branch != "", "-b", opts.Branch). | ||||
| 		Arg(opts.Path, opts.Base) | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs.ToArgv()).Run() | ||||
| } | ||||
|  | ||||
| func (self *WorktreeCommands) Delete(worktreePath string, force bool) error { | ||||
| @@ -38,25 +56,25 @@ func (self *WorktreeCommands) Detach(worktreePath string) error { | ||||
| 	return self.cmd.New(cmdArgs).SetWd(worktreePath).Run() | ||||
| } | ||||
|  | ||||
| func (self *WorktreeCommands) IsCurrentWorktree(w *models.Worktree) bool { | ||||
| 	return IsCurrentWorktree(w) | ||||
| func (self *WorktreeCommands) IsCurrentWorktree(path string) bool { | ||||
| 	return IsCurrentWorktree(path) | ||||
| } | ||||
|  | ||||
| func IsCurrentWorktree(w *models.Worktree) bool { | ||||
| func IsCurrentWorktree(path string) bool { | ||||
| 	pwd, err := os.Getwd() | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	return EqualPath(pwd, w.Path) | ||||
| 	return EqualPath(pwd, path) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeCommands) IsWorktreePathMissing(w *models.Worktree) bool { | ||||
| 	if _, err := os.Stat(w.Path); err != nil { | ||||
| func (self *WorktreeCommands) IsWorktreePathMissing(path string) bool { | ||||
| 	if _, err := os.Stat(path); err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return true | ||||
| 		} | ||||
| 		log.Fatalln(fmt.Errorf("failed to check if worktree path `%s` exists\n%w", w.Path, err).Error()) | ||||
| 		log.Fatalln(fmt.Errorf("failed to check if worktree path `%s` exists\n%w", path, err).Error()) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|   | ||||
| @@ -132,6 +132,7 @@ type KeybindingConfig struct { | ||||
| 	Status      KeybindingStatusConfig      `yaml:"status"` | ||||
| 	Files       KeybindingFilesConfig       `yaml:"files"` | ||||
| 	Branches    KeybindingBranchesConfig    `yaml:"branches"` | ||||
| 	Worktrees   KeybindingWorktreesConfig   `yaml:"worktrees"` | ||||
| 	Commits     KeybindingCommitsConfig     `yaml:"commits"` | ||||
| 	Stash       KeybindingStashConfig       `yaml:"stash"` | ||||
| 	CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"` | ||||
| @@ -246,6 +247,10 @@ type KeybindingBranchesConfig struct { | ||||
| 	FetchRemote            string `yaml:"fetchRemote"` | ||||
| } | ||||
|  | ||||
| type KeybindingWorktreesConfig struct { | ||||
| 	ViewWorktreeOptions string `yaml:"viewWorktreeOptions"` | ||||
| } | ||||
|  | ||||
| type KeybindingCommitsConfig struct { | ||||
| 	SquashDown                     string `yaml:"squashDown"` | ||||
| 	RenameCommit                   string `yaml:"renameCommit"` | ||||
| @@ -587,6 +592,9 @@ func GetDefaultConfig() *UserConfig { | ||||
| 				SetUpstream:            "u", | ||||
| 				FetchRemote:            "f", | ||||
| 			}, | ||||
| 			Worktrees: KeybindingWorktreesConfig{ | ||||
| 				ViewWorktreeOptions: "w", | ||||
| 			}, | ||||
| 			Commits: KeybindingCommitsConfig{ | ||||
| 				SquashDown:                     "s", | ||||
| 				RenameCommit:                   "r", | ||||
|   | ||||
| @@ -18,6 +18,9 @@ func (gui *Gui) Helpers() *helpers.Helpers { | ||||
|  | ||||
| func (gui *Gui) resetHelpersAndControllers() { | ||||
| 	helperCommon := gui.c | ||||
| 	recordDirectoryHelper := helpers.NewRecordDirectoryHelper(helperCommon) | ||||
| 	reposHelper := helpers.NewRecentReposHelper(helperCommon, recordDirectoryHelper, gui.onNewRepo) | ||||
| 	worktreeHelper := helpers.NewWorktreeHelper(helperCommon, reposHelper) | ||||
| 	refsHelper := helpers.NewRefsHelper(helperCommon) | ||||
|  | ||||
| 	rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon, refsHelper) | ||||
| @@ -41,12 +44,10 @@ func (gui *Gui) resetHelpersAndControllers() { | ||||
|  | ||||
| 	gpgHelper := helpers.NewGpgHelper(helperCommon) | ||||
| 	viewHelper := helpers.NewViewHelper(helperCommon, gui.State.Contexts) | ||||
| 	recordDirectoryHelper := helpers.NewRecordDirectoryHelper(helperCommon) | ||||
| 	patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon) | ||||
| 	stagingHelper := helpers.NewStagingHelper(helperCommon) | ||||
| 	mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon) | ||||
| 	reposHelper := helpers.NewRecentReposHelper(helperCommon, recordDirectoryHelper, gui.onNewRepo) | ||||
| 	worktreeHelper := helpers.NewWorktreeHelper(helperCommon, reposHelper) | ||||
|  | ||||
| 	refreshHelper := helpers.NewRefreshHelper( | ||||
| 		helperCommon, | ||||
| 		refsHelper, | ||||
| @@ -241,6 +242,18 @@ func (gui *Gui) resetHelpersAndControllers() { | ||||
| 		controllers.AttachControllers(context, controllers.NewBasicCommitsController(common, context)) | ||||
| 	} | ||||
|  | ||||
| 	for _, context := range []controllers.CanViewWorktreeOptions{ | ||||
| 		gui.State.Contexts.LocalCommits, | ||||
| 		gui.State.Contexts.ReflogCommits, | ||||
| 		gui.State.Contexts.SubCommits, | ||||
| 		gui.State.Contexts.Stash, | ||||
| 		gui.State.Contexts.Branches, | ||||
| 		gui.State.Contexts.RemoteBranches, | ||||
| 		gui.State.Contexts.Tags, | ||||
| 	} { | ||||
| 		controllers.AttachControllers(context, controllers.NewWorktreeOptionsController(common, context)) | ||||
| 	} | ||||
|  | ||||
| 	controllers.AttachControllers(gui.State.Contexts.ReflogCommits, | ||||
| 		reflogCommitsController, | ||||
| 	) | ||||
|   | ||||
| @@ -204,7 +204,7 @@ func (self *BranchesController) press(selectedBranch *models.Branch) error { | ||||
|  | ||||
| 	if selectedBranch.CheckedOutByOtherWorktree { | ||||
| 		worktreeForRef, ok := self.worktreeForBranch(selectedBranch) | ||||
| 		if ok && !self.c.Git().Worktree.IsCurrentWorktree(worktreeForRef) { | ||||
| 		if ok && !self.c.Git().Worktree.IsCurrentWorktree(worktreeForRef.Path) { | ||||
| 			return self.promptToCheckoutWorktree(worktreeForRef) | ||||
| 		} | ||||
| 	} | ||||
| @@ -228,7 +228,7 @@ func (self *BranchesController) promptToCheckoutWorktree(worktree *models.Worktr | ||||
| 		Title:  "Switch to worktree", | ||||
| 		Prompt: fmt.Sprintf("This branch is checked out by worktree %s. Do you want to switch to that worktree?", worktree.Name()), | ||||
| 		HandleConfirm: func() error { | ||||
| 			return self.c.Helpers().Worktree.Switch(worktree, context.LOCAL_BRANCHES_CONTEXT_KEY) | ||||
| 			return self.c.Helpers().Worktree.Switch(worktree.Path, context.LOCAL_BRANCHES_CONTEXT_KEY) | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
| @@ -346,7 +346,7 @@ func (self *BranchesController) promptWorktreeBranchDelete(selectedBranch *model | ||||
| 			{ | ||||
| 				Label: "Switch to worktree", | ||||
| 				OnPress: func() error { | ||||
| 					return self.c.Helpers().Worktree.Switch(worktree, context.LOCAL_BRANCHES_CONTEXT_KEY) | ||||
| 					return self.c.Helpers().Worktree.Switch(worktree.Path, context.LOCAL_BRANCHES_CONTEXT_KEY) | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
|   | ||||
| @@ -9,7 +9,9 @@ import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/git_commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
| @@ -66,10 +68,14 @@ func (self *WorktreeHelper) NewWorktree() error { | ||||
| 		HandleConfirm: func(path string) error { | ||||
| 			return self.c.Prompt(types.PromptOpts{ | ||||
| 				Title: self.c.Tr.NewWorktreeBranch, | ||||
| 				HandleConfirm: func(committish string) error { | ||||
| 				// TODO: suggestions | ||||
| 				HandleConfirm: func(base string) error { | ||||
| 					return self.c.WithWaitingStatus(self.c.Tr.AddingWorktree, func(gocui.Task) error { | ||||
| 						self.c.LogAction(self.c.Tr.Actions.AddWorktree) | ||||
| 						if err := self.c.Git().Worktree.New(sanitizedBranchName(path), committish); err != nil { | ||||
| 						if err := self.c.Git().Worktree.New(git_commands.NewWorktreeOpts{ | ||||
| 							Path: path, | ||||
| 							Base: base, | ||||
| 						}); err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| 						return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.WORKTREES, types.BRANCHES, types.FILES}}) | ||||
| @@ -80,14 +86,70 @@ func (self *WorktreeHelper) NewWorktree() error { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeHelper) Switch(worktree *models.Worktree, contextKey types.ContextKey) error { | ||||
| 	if self.c.Git().Worktree.IsCurrentWorktree(worktree) { | ||||
| func (self *WorktreeHelper) NewWorktreeCheckout(base string, isBranch bool, detached bool) error { | ||||
| 	opts := git_commands.NewWorktreeOpts{ | ||||
| 		Base:   base, | ||||
| 		Detach: detached, | ||||
| 	} | ||||
|  | ||||
| 	f := func() error { | ||||
| 		return self.c.WithWaitingStatus(self.c.Tr.AddingWorktree, func(gocui.Task) error { | ||||
| 			self.c.LogAction(self.c.Tr.Actions.AddWorktree) | ||||
| 			if err := self.c.Git().Worktree.New(opts); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			return self.Switch(opts.Path, context.LOCAL_BRANCHES_CONTEXT_KEY) | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return self.c.Prompt(types.PromptOpts{ | ||||
| 		Title: self.c.Tr.NewWorktreePath, | ||||
| 		HandleConfirm: func(path string) error { | ||||
| 			opts.Path = path | ||||
|  | ||||
| 			if detached { | ||||
| 				return f() | ||||
| 			} | ||||
|  | ||||
| 			if isBranch { | ||||
| 				// prompt for the new branch name where a blank means we just check out the branch | ||||
| 				return self.c.Prompt(types.PromptOpts{ | ||||
| 					Title: fmt.Sprintf("New branch name (leave blank to checkout %s)", base), | ||||
| 					// TODO: suggestions | ||||
| 					HandleConfirm: func(branchName string) error { | ||||
| 						opts.Branch = branchName | ||||
|  | ||||
| 						return f() | ||||
| 					}, | ||||
| 				}) | ||||
| 			} else { | ||||
| 				// prompt for the new branch name where a blank means we just check out the branch | ||||
| 				return self.c.Prompt(types.PromptOpts{ | ||||
| 					Title: "New branch name", | ||||
| 					// TODO: suggestions | ||||
| 					HandleConfirm: func(branchName string) error { | ||||
| 						if branchName == "" { | ||||
| 							return self.c.ErrorMsg("Branch name cannot be blank") | ||||
| 						} | ||||
|  | ||||
| 						opts.Branch = branchName | ||||
|  | ||||
| 						return f() | ||||
| 					}, | ||||
| 				}) | ||||
| 			} | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeHelper) Switch(path string, contextKey types.ContextKey) error { | ||||
| 	if self.c.Git().Worktree.IsCurrentWorktree(path) { | ||||
| 		return self.c.ErrorMsg(self.c.Tr.AlreadyInWorktree) | ||||
| 	} | ||||
|  | ||||
| 	self.c.LogAction(self.c.Tr.SwitchToWorktree) | ||||
|  | ||||
| 	return self.reposHelper.DispatchSwitchTo(worktree.Path, true, self.c.Tr.ErrWorktreeMovedOrRemoved, contextKey) | ||||
| 	return self.reposHelper.DispatchSwitchTo(path, true, self.c.Tr.ErrWorktreeMovedOrRemoved, contextKey) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeHelper) Remove(worktree *models.Worktree, force bool) error { | ||||
| @@ -139,3 +201,51 @@ func (self *WorktreeHelper) Detach(worktree *models.Worktree) error { | ||||
| 		return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.WORKTREES, types.BRANCHES, types.FILES}}) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeHelper) ViewWorktreeOptions(context types.IListContext, ref string) error { | ||||
| 	if context == self.c.Contexts().Branches { | ||||
| 		return self.ViewBranchWorktreeOptions(ref) | ||||
| 	} | ||||
|  | ||||
| 	return self.ViewRefWorktreeOptions(ref) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeHelper) ViewBranchWorktreeOptions(branchName string) error { | ||||
| 	return self.c.Menu(types.CreateMenuOptions{ | ||||
| 		Title: self.c.Tr.WorktreeTitle, | ||||
| 		Items: []*types.MenuItem{ | ||||
| 			{ | ||||
| 				LabelColumns: []string{"Create new worktree from branch"}, | ||||
| 				OnPress: func() error { | ||||
| 					return self.NewWorktreeCheckout(branchName, true, false) | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				LabelColumns: []string{"Create new worktree from branch (detached)"}, | ||||
| 				OnPress: func() error { | ||||
| 					return self.NewWorktreeCheckout(branchName, true, true) | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (self *WorktreeHelper) ViewRefWorktreeOptions(ref string) error { | ||||
| 	return self.c.Menu(types.CreateMenuOptions{ | ||||
| 		Title: self.c.Tr.WorktreeTitle, | ||||
| 		Items: []*types.MenuItem{ | ||||
| 			{ | ||||
| 				LabelColumns: []string{"Create new worktree from ref"}, | ||||
| 				OnPress: func() error { | ||||
| 					return self.NewWorktreeCheckout(ref, false, false) | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				LabelColumns: []string{"Create new worktree from ref (detached)"}, | ||||
| 				OnPress: func() error { | ||||
| 					return self.NewWorktreeCheckout(ref, false, true) | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|   | ||||
							
								
								
									
										59
									
								
								pkg/gui/controllers/worktree_options_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								pkg/gui/controllers/worktree_options_controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| package controllers | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| // This controller is for all contexts that have items you can create a worktree from | ||||
|  | ||||
| var _ types.IController = &WorktreeOptionsController{} | ||||
|  | ||||
| type CanViewWorktreeOptions interface { | ||||
| 	types.IListContext | ||||
| } | ||||
|  | ||||
| type WorktreeOptionsController struct { | ||||
| 	baseController | ||||
| 	c       *ControllerCommon | ||||
| 	context CanViewWorktreeOptions | ||||
| } | ||||
|  | ||||
| func NewWorktreeOptionsController(controllerCommon *ControllerCommon, context CanViewWorktreeOptions) *WorktreeOptionsController { | ||||
| 	return &WorktreeOptionsController{ | ||||
| 		baseController: baseController{}, | ||||
| 		c:              controllerCommon, | ||||
| 		context:        context, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *WorktreeOptionsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { | ||||
| 	bindings := []*types.Binding{ | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Worktrees.ViewWorktreeOptions), | ||||
| 			Handler:     self.checkSelected(self.viewWorktreeOptions), | ||||
| 			Description: self.c.Tr.ViewWorktreeOptions, | ||||
| 			OpensMenu:   true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return bindings | ||||
| } | ||||
|  | ||||
| func (self *WorktreeOptionsController) checkSelected(callback func(string) error) func() error { | ||||
| 	return func() error { | ||||
| 		ref := self.context.GetSelectedItemId() | ||||
| 		if ref == "" { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		return callback(ref) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *WorktreeOptionsController) Context() types.Context { | ||||
| 	return self.context | ||||
| } | ||||
|  | ||||
| func (self *WorktreeOptionsController) viewWorktreeOptions(ref string) error { | ||||
| 	return self.c.Helpers().Worktree.ViewWorktreeOptions(self.context, ref) | ||||
| } | ||||
| @@ -62,7 +62,7 @@ func (self *WorktreesController) GetOnRenderToMain() func() error { | ||||
| 			} | ||||
|  | ||||
| 			missing := "" | ||||
| 			if self.c.Git().Worktree.IsWorktreePathMissing(worktree) { | ||||
| 			if self.c.Git().Worktree.IsWorktreePathMissing(worktree.Path) { | ||||
| 				missing = style.FgRed.Sprintf(" %s", self.c.Tr.MissingWorktree) | ||||
| 			} | ||||
|  | ||||
| @@ -95,7 +95,7 @@ func (self *WorktreesController) remove(worktree *models.Worktree) error { | ||||
| 		return self.c.ErrorMsg(self.c.Tr.CantDeleteMainWorktree) | ||||
| 	} | ||||
|  | ||||
| 	if self.c.Git().Worktree.IsCurrentWorktree(worktree) { | ||||
| 	if self.c.Git().Worktree.IsCurrentWorktree(worktree.Path) { | ||||
| 		return self.c.ErrorMsg(self.c.Tr.CantDeleteCurrentWorktree) | ||||
| 	} | ||||
|  | ||||
| @@ -107,7 +107,7 @@ func (self *WorktreesController) GetOnClick() func() error { | ||||
| } | ||||
|  | ||||
| func (self *WorktreesController) enter(worktree *models.Worktree) error { | ||||
| 	return self.c.Helpers().Worktree.Switch(worktree, context.WORKTREES_CONTEXT_KEY) | ||||
| 	return self.c.Helpers().Worktree.Switch(worktree.Path, context.WORKTREES_CONTEXT_KEY) | ||||
| } | ||||
|  | ||||
| func (self *WorktreesController) checkSelected(callback func(worktree *models.Worktree) error) func() error { | ||||
|   | ||||
| @@ -8,11 +8,11 @@ import ( | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| func GetWorktreeDisplayStrings(worktrees []*models.Worktree, isCurrent func(*models.Worktree) bool, isMissing func(*models.Worktree) bool) [][]string { | ||||
| func GetWorktreeDisplayStrings(worktrees []*models.Worktree, isCurrent func(string) bool, isMissing func(string) bool) [][]string { | ||||
| 	return lo.Map(worktrees, func(worktree *models.Worktree, _ int) []string { | ||||
| 		return GetWorktreeDisplayString( | ||||
| 			isCurrent(worktree), | ||||
| 			isMissing(worktree), | ||||
| 			isCurrent(worktree.Path), | ||||
| 			isMissing(worktree.Path), | ||||
| 			worktree) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -562,6 +562,7 @@ type TranslationSet struct { | ||||
| 	CreateWorktree                      string | ||||
| 	NewWorktreePath                     string | ||||
| 	NewWorktreeBranch                   string | ||||
| 	ViewWorktreeOptions                 string | ||||
| 	Name                                string | ||||
| 	Branch                              string | ||||
| 	Path                                string | ||||
| @@ -1286,6 +1287,7 @@ func EnglishTranslationSet() TranslationSet { | ||||
| 		CreateWorktree:                      "Create worktree", | ||||
| 		NewWorktreePath:                     "New worktree path", | ||||
| 		NewWorktreeBranch:                   "New worktree branch (leave blank to use the current branch)", | ||||
| 		ViewWorktreeOptions:                 "View worktree options", | ||||
| 		Name:                                "Name", | ||||
| 		Branch:                              "Branch", | ||||
| 		Path:                                "Path", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user