mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-12-01 22:52:01 +02:00
some more refactoring
This commit is contained in:
156
pkg/gui/controllers/cherry_pick_helper.go
Normal file
156
pkg/gui/controllers/cherry_pick_helper.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type CherryPickHelper struct {
|
||||
c *types.ControllerCommon
|
||||
|
||||
git *commands.GitCommand
|
||||
|
||||
getContexts func() context.ContextTree
|
||||
getData func() *cherrypicking.CherryPicking
|
||||
|
||||
rebaseHelper *RebaseHelper
|
||||
}
|
||||
|
||||
// I'm using the analogy of copy+paste in the terminology here because it's intuitively what's going on,
|
||||
// even if in truth we're running git cherry-pick
|
||||
|
||||
func NewCherryPickHelper(
|
||||
c *types.ControllerCommon,
|
||||
git *commands.GitCommand,
|
||||
getContexts func() context.ContextTree,
|
||||
getData func() *cherrypicking.CherryPicking,
|
||||
rebaseHelper *RebaseHelper,
|
||||
) *CherryPickHelper {
|
||||
return &CherryPickHelper{
|
||||
c: c,
|
||||
git: git,
|
||||
getContexts: getContexts,
|
||||
getData: getData,
|
||||
rebaseHelper: rebaseHelper,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) Copy(commit *models.Commit, commitsList []*models.Commit, context types.Context) error {
|
||||
if err := self.resetIfNecessary(context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we will un-copy it if it's already copied
|
||||
for index, cherryPickedCommit := range self.getData().CherryPickedCommits {
|
||||
if commit.Sha == cherryPickedCommit.Sha {
|
||||
self.getData().CherryPickedCommits = append(
|
||||
self.getData().CherryPickedCommits[0:index],
|
||||
self.getData().CherryPickedCommits[index+1:]...,
|
||||
)
|
||||
return self.rerender()
|
||||
}
|
||||
}
|
||||
|
||||
self.add(commit, commitsList)
|
||||
return self.rerender()
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) CopyRange(selectedIndex int, commitsList []*models.Commit, context types.Context) error {
|
||||
if err := self.resetIfNecessary(context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commitShaMap := self.CherryPickedCommitShaMap()
|
||||
|
||||
// find the last commit that is copied that's above our position
|
||||
// if there are none, startIndex = 0
|
||||
startIndex := 0
|
||||
for index, commit := range commitsList[0:selectedIndex] {
|
||||
if commitShaMap[commit.Sha] {
|
||||
startIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
for index := startIndex; index <= selectedIndex; index++ {
|
||||
commit := commitsList[index]
|
||||
self.add(commit, commitsList)
|
||||
}
|
||||
|
||||
return self.rerender()
|
||||
}
|
||||
|
||||
// HandlePasteCommits begins a cherry-pick rebase with the commits the user has copied.
|
||||
// Only to be called from the branch commits controller
|
||||
func (self *CherryPickHelper) Paste() error {
|
||||
return self.c.Ask(types.AskOpts{
|
||||
Title: self.c.Tr.CherryPick,
|
||||
Prompt: self.c.Tr.SureCherryPick,
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CherryPick)
|
||||
err := self.git.Rebase.CherryPickCommits(self.getData().CherryPickedCommits)
|
||||
return self.rebaseHelper.CheckMergeOrRebase(err)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) Reset() error {
|
||||
self.getData().ContextKey = ""
|
||||
self.getData().CherryPickedCommits = nil
|
||||
|
||||
return self.rerender()
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) CherryPickedCommitShaMap() map[string]bool {
|
||||
commitShaMap := map[string]bool{}
|
||||
for _, commit := range self.getData().CherryPickedCommits {
|
||||
commitShaMap[commit.Sha] = true
|
||||
}
|
||||
return commitShaMap
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) add(selectedCommit *models.Commit, commitsList []*models.Commit) {
|
||||
commitShaMap := self.CherryPickedCommitShaMap()
|
||||
commitShaMap[selectedCommit.Sha] = true
|
||||
|
||||
newCommits := []*models.Commit{}
|
||||
for _, commit := range commitsList {
|
||||
if commitShaMap[commit.Sha] {
|
||||
// duplicating just the things we need to put in the rebase TODO list
|
||||
newCommits = append(newCommits, &models.Commit{Name: commit.Name, Sha: commit.Sha})
|
||||
}
|
||||
}
|
||||
|
||||
self.getData().CherryPickedCommits = newCommits
|
||||
}
|
||||
|
||||
// you can only copy from one context at a time, because the order and position of commits matter
|
||||
func (self *CherryPickHelper) resetIfNecessary(context types.Context) error {
|
||||
oldContextKey := types.ContextKey(self.getData().ContextKey)
|
||||
|
||||
if oldContextKey != context.GetKey() {
|
||||
// need to reset the cherry picking mode
|
||||
self.getData().ContextKey = string(context.GetKey())
|
||||
self.getData().CherryPickedCommits = make([]*models.Commit, 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) rerender() error {
|
||||
for _, context := range []types.Context{
|
||||
self.getContexts().BranchCommits,
|
||||
self.getContexts().ReflogCommits,
|
||||
self.getContexts().SubCommits,
|
||||
} {
|
||||
if err := self.c.PostRefreshUpdate(context); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -24,17 +24,19 @@ type (
|
||||
)
|
||||
|
||||
type LocalCommitsController struct {
|
||||
c *types.ControllerCommon
|
||||
getContext func() types.IListContext
|
||||
os *oscommands.OSCommand
|
||||
git *commands.GitCommand
|
||||
tagsHelper *TagsHelper
|
||||
refsHelper IRefsHelper
|
||||
c *types.ControllerCommon
|
||||
getContext func() types.IListContext
|
||||
os *oscommands.OSCommand
|
||||
git *commands.GitCommand
|
||||
tagsHelper *TagsHelper
|
||||
refsHelper IRefsHelper
|
||||
cherryPickHelper *CherryPickHelper
|
||||
rebaseHelper *RebaseHelper
|
||||
|
||||
getSelectedLocalCommit func() *models.Commit
|
||||
getCommits func() []*models.Commit
|
||||
getSelectedLocalCommitIdx func() int
|
||||
checkMergeOrRebase CheckMergeOrRebase
|
||||
CheckMergeOrRebase CheckMergeOrRebase
|
||||
pullFiles PullFilesFn
|
||||
getHostingServiceMgr GetHostingServiceMgrFn
|
||||
switchToCommitFilesContext SwitchToCommitFilesContextFn
|
||||
@@ -54,10 +56,12 @@ func NewLocalCommitsController(
|
||||
git *commands.GitCommand,
|
||||
tagsHelper *TagsHelper,
|
||||
refsHelper IRefsHelper,
|
||||
cherryPickHelper *CherryPickHelper,
|
||||
rebaseHelper *RebaseHelper,
|
||||
getSelectedLocalCommit func() *models.Commit,
|
||||
getCommits func() []*models.Commit,
|
||||
getSelectedLocalCommitIdx func() int,
|
||||
checkMergeOrRebase CheckMergeOrRebase,
|
||||
CheckMergeOrRebase CheckMergeOrRebase,
|
||||
pullFiles PullFilesFn,
|
||||
getHostingServiceMgr GetHostingServiceMgrFn,
|
||||
switchToCommitFilesContext SwitchToCommitFilesContextFn,
|
||||
@@ -74,10 +78,12 @@ func NewLocalCommitsController(
|
||||
git: git,
|
||||
tagsHelper: tagsHelper,
|
||||
refsHelper: refsHelper,
|
||||
cherryPickHelper: cherryPickHelper,
|
||||
rebaseHelper: rebaseHelper,
|
||||
getSelectedLocalCommit: getSelectedLocalCommit,
|
||||
getCommits: getCommits,
|
||||
getSelectedLocalCommitIdx: getSelectedLocalCommitIdx,
|
||||
checkMergeOrRebase: checkMergeOrRebase,
|
||||
CheckMergeOrRebase: CheckMergeOrRebase,
|
||||
pullFiles: pullFiles,
|
||||
getHostingServiceMgr: getHostingServiceMgr,
|
||||
switchToCommitFilesContext: switchToCommitFilesContext,
|
||||
@@ -160,6 +166,27 @@ func (self *LocalCommitsController) Keybindings(
|
||||
Handler: self.checkSelected(self.handleCommitRevert),
|
||||
Description: self.c.Tr.LcRevertCommit,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Universal.New),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.checkSelected(self.newBranch),
|
||||
Description: self.c.Tr.LcCreateNewBranchFromCommit,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Commits.CherryPickCopy),
|
||||
Handler: self.checkSelected(self.copy),
|
||||
Description: self.c.Tr.LcCherryPickCopy,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Commits.CherryPickCopyRange),
|
||||
Handler: self.checkSelected(self.copyRange),
|
||||
Description: self.c.Tr.LcCherryPickCopyRange,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Commits.PasteCommits),
|
||||
Handler: guards.OutsideFilterMode(self.paste),
|
||||
Description: self.c.Tr.LcPasteCommits,
|
||||
},
|
||||
// overriding these navigation keybindings because we might need to load
|
||||
// more commits on demand
|
||||
{
|
||||
@@ -380,7 +407,7 @@ func (self *LocalCommitsController) pick() error {
|
||||
|
||||
func (self *LocalCommitsController) interactiveRebase(action string) error {
|
||||
err := self.git.Rebase.InteractiveRebase(self.getCommits(), self.getSelectedLocalCommitIdx(), action)
|
||||
return self.checkMergeOrRebase(err)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
}
|
||||
|
||||
// handleMidRebaseCommand sees if the selected commit is in fact a rebasing
|
||||
@@ -448,7 +475,7 @@ func (self *LocalCommitsController) handleCommitMoveDown() error {
|
||||
// TODO: use MoveSelectedLine
|
||||
_ = self.getContext().HandleNextLine()
|
||||
}
|
||||
return self.checkMergeOrRebase(err)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -483,7 +510,7 @@ func (self *LocalCommitsController) handleCommitMoveUp() error {
|
||||
if err == nil {
|
||||
_ = self.getContext().HandlePrevLine()
|
||||
}
|
||||
return self.checkMergeOrRebase(err)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -495,7 +522,7 @@ func (self *LocalCommitsController) handleCommitAmendTo() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
|
||||
err := self.git.Rebase.AmendTo(self.getSelectedLocalCommit().Sha)
|
||||
return self.checkMergeOrRebase(err)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -601,7 +628,7 @@ func (self *LocalCommitsController) handleSquashAllAboveFixupCommits(commit *mod
|
||||
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
|
||||
err := self.git.Rebase.SquashAllAboveFixupCommits(commit.Sha)
|
||||
return self.checkMergeOrRebase(err)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -781,3 +808,19 @@ func (self *LocalCommitsController) checkSelected(callback func(*models.Commit)
|
||||
func (self *LocalCommitsController) Context() types.Context {
|
||||
return self.getContext()
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) newBranch(commit *models.Commit) error {
|
||||
return self.refsHelper.NewBranch(commit.RefName(), commit.Description(), "")
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) copy(commit *models.Commit) error {
|
||||
return self.cherryPickHelper.Copy(commit, self.getCommits(), self.getContext())
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) copyRange(*models.Commit) error {
|
||||
return self.cherryPickHelper.CopyRange(self.getContext().GetPanelState().GetSelectedLineIdx(), self.getCommits(), self.getContext())
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) paste() error {
|
||||
return self.cherryPickHelper.Paste()
|
||||
}
|
||||
|
||||
192
pkg/gui/controllers/rebase_helper.go
Normal file
192
pkg/gui/controllers/rebase_helper.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type RebaseHelper struct {
|
||||
c *types.ControllerCommon
|
||||
getContexts func() context.ContextTree
|
||||
git *commands.GitCommand
|
||||
takeOverMergeConflictScrolling func()
|
||||
}
|
||||
|
||||
func NewRebaseHelper(
|
||||
c *types.ControllerCommon,
|
||||
getContexts func() context.ContextTree,
|
||||
git *commands.GitCommand,
|
||||
takeOverMergeConflictScrolling func(),
|
||||
) *RebaseHelper {
|
||||
return &RebaseHelper{
|
||||
c: c,
|
||||
getContexts: getContexts,
|
||||
git: git,
|
||||
takeOverMergeConflictScrolling: takeOverMergeConflictScrolling,
|
||||
}
|
||||
}
|
||||
|
||||
type RebaseOption string
|
||||
|
||||
const (
|
||||
REBASE_OPTION_CONTINUE string = "continue"
|
||||
REBASE_OPTION_ABORT string = "abort"
|
||||
REBASE_OPTION_SKIP string = "skip"
|
||||
)
|
||||
|
||||
func (self *RebaseHelper) CreateRebaseOptionsMenu() error {
|
||||
options := []string{REBASE_OPTION_CONTINUE, REBASE_OPTION_ABORT}
|
||||
|
||||
if self.git.Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||
options = append(options, REBASE_OPTION_SKIP)
|
||||
}
|
||||
|
||||
menuItems := make([]*types.MenuItem, len(options))
|
||||
for i, option := range options {
|
||||
// note to self. Never, EVER, close over loop variables in a function
|
||||
option := option
|
||||
menuItems[i] = &types.MenuItem{
|
||||
DisplayString: option,
|
||||
OnPress: func() error {
|
||||
return self.genericMergeCommand(option)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var title string
|
||||
if self.git.Status.WorkingTreeState() == enums.REBASE_MODE_MERGING {
|
||||
title = self.c.Tr.MergeOptionsTitle
|
||||
} else {
|
||||
title = self.c.Tr.RebaseOptionsTitle
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{Title: title, Items: menuItems})
|
||||
}
|
||||
|
||||
func (self *RebaseHelper) genericMergeCommand(command string) error {
|
||||
status := self.git.Status.WorkingTreeState()
|
||||
|
||||
if status != enums.REBASE_MODE_MERGING && status != enums.REBASE_MODE_REBASING {
|
||||
return self.c.ErrorMsg(self.c.Tr.NotMergingOrRebasing)
|
||||
}
|
||||
|
||||
self.c.LogAction(fmt.Sprintf("Merge/Rebase: %s", command))
|
||||
|
||||
commandType := ""
|
||||
switch status {
|
||||
case enums.REBASE_MODE_MERGING:
|
||||
commandType = "merge"
|
||||
case enums.REBASE_MODE_REBASING:
|
||||
commandType = "rebase"
|
||||
default:
|
||||
// shouldn't be possible to land here
|
||||
}
|
||||
|
||||
// we should end up with a command like 'git merge --continue'
|
||||
|
||||
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
|
||||
if status == enums.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && self.c.UserConfig.Git.Merging.ManualCommit {
|
||||
// TODO: see if we should be calling more of the code from self.Git.Rebase.GenericMergeOrRebaseAction
|
||||
return self.c.RunSubprocessAndRefresh(
|
||||
self.git.Rebase.GenericMergeOrRebaseActionCmdObj(commandType, command),
|
||||
)
|
||||
}
|
||||
result := self.git.Rebase.GenericMergeOrRebaseAction(commandType, command)
|
||||
if err := self.CheckMergeOrRebase(result); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var conflictStrings = []string{
|
||||
"Failed to merge in the changes",
|
||||
"When you have resolved this problem",
|
||||
"fix conflicts",
|
||||
"Resolve all conflicts manually",
|
||||
}
|
||||
|
||||
func isMergeConflictErr(errStr string) bool {
|
||||
for _, str := range conflictStrings {
|
||||
if strings.Contains(errStr, str) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *RebaseHelper) CheckMergeOrRebase(result error) error {
|
||||
if err := self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
if result == nil {
|
||||
return nil
|
||||
} else if strings.Contains(result.Error(), "No changes - did you forget to use") {
|
||||
return self.genericMergeCommand(REBASE_OPTION_SKIP)
|
||||
} else if strings.Contains(result.Error(), "The previous cherry-pick is now empty") {
|
||||
return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||
} else if strings.Contains(result.Error(), "No rebase in progress?") {
|
||||
// assume in this case that we're already done
|
||||
return nil
|
||||
} else if isMergeConflictErr(result.Error()) {
|
||||
return self.c.Ask(types.AskOpts{
|
||||
Title: self.c.Tr.FoundConflictsTitle,
|
||||
Prompt: self.c.Tr.FoundConflicts,
|
||||
HandlersManageFocus: true,
|
||||
HandleConfirm: func() error {
|
||||
return self.c.PushContext(self.getContexts().Files)
|
||||
},
|
||||
HandleClose: func() error {
|
||||
if err := self.c.PopContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return self.c.ErrorMsg(result.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RebaseHelper) AbortMergeOrRebaseWithConfirm() error {
|
||||
// prompt user to confirm that they want to abort, then do it
|
||||
mode := self.workingTreeStateNoun()
|
||||
return self.c.Ask(types.AskOpts{
|
||||
Title: fmt.Sprintf(self.c.Tr.AbortTitle, mode),
|
||||
Prompt: fmt.Sprintf(self.c.Tr.AbortPrompt, mode),
|
||||
HandleConfirm: func() error {
|
||||
return self.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RebaseHelper) workingTreeStateNoun() string {
|
||||
workingTreeState := self.git.Status.WorkingTreeState()
|
||||
switch workingTreeState {
|
||||
case enums.REBASE_MODE_NONE:
|
||||
return ""
|
||||
case enums.REBASE_MODE_MERGING:
|
||||
return "merge"
|
||||
default:
|
||||
return "rebase"
|
||||
}
|
||||
}
|
||||
|
||||
// PromptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress
|
||||
func (self *RebaseHelper) PromptToContinueRebase() error {
|
||||
self.takeOverMergeConflictScrolling()
|
||||
|
||||
return self.c.Ask(types.AskOpts{
|
||||
Title: "continue",
|
||||
Prompt: self.c.Tr.ConflictsResolved,
|
||||
HandleConfirm: func() error {
|
||||
return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -22,7 +22,7 @@ type SyncController struct {
|
||||
getCheckedOutBranch func() *models.Branch
|
||||
suggestionsHelper ISuggestionsHelper
|
||||
getSuggestedRemote func() string
|
||||
checkMergeOrRebase func(error) error
|
||||
CheckMergeOrRebase func(error) error
|
||||
}
|
||||
|
||||
var _ types.IController = &SyncController{}
|
||||
@@ -33,7 +33,7 @@ func NewSyncController(
|
||||
getCheckedOutBranch func() *models.Branch,
|
||||
suggestionsHelper ISuggestionsHelper,
|
||||
getSuggestedRemote func() string,
|
||||
checkMergeOrRebase func(error) error,
|
||||
CheckMergeOrRebase func(error) error,
|
||||
) *SyncController {
|
||||
return &SyncController{
|
||||
c: c,
|
||||
@@ -42,7 +42,7 @@ func NewSyncController(
|
||||
getCheckedOutBranch: getCheckedOutBranch,
|
||||
suggestionsHelper: suggestionsHelper,
|
||||
getSuggestedRemote: getSuggestedRemote,
|
||||
checkMergeOrRebase: checkMergeOrRebase,
|
||||
CheckMergeOrRebase: CheckMergeOrRebase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ func (self *SyncController) pullWithLock(opts PullFilesOptions) error {
|
||||
},
|
||||
)
|
||||
|
||||
return self.checkMergeOrRebase(err)
|
||||
return self.CheckMergeOrRebase(err)
|
||||
}
|
||||
|
||||
type pushOpts struct {
|
||||
|
||||
@@ -9,6 +9,7 @@ type IRefsHelper interface {
|
||||
CheckoutRef(ref string, options types.CheckoutRefOptions) error
|
||||
CreateGitResetMenu(ref string) error
|
||||
ResetToRef(ref string, strength string, envVars []string) error
|
||||
NewBranch(from string, fromDescription string, suggestedBranchname string) error
|
||||
}
|
||||
|
||||
type ISuggestionsHelper interface {
|
||||
|
||||
Reference in New Issue
Block a user