1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-28 05:46:16 +02:00
lazygit/pkg/gui/controllers/helpers/working_tree_helper.go

212 lines
5.6 KiB
Go
Raw Normal View History

2022-02-06 15:54:26 +11:00
package helpers
import (
"fmt"
"regexp"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
2022-01-31 22:11:34 +11:00
type IWorkingTreeHelper interface {
AnyStagedFiles() bool
AnyTrackedFiles() bool
IsWorkingTreeDirty() bool
FileForSubmodule(submodule *models.SubmoduleConfig) *models.File
}
type WorkingTreeHelper struct {
c *HelperCommon
refHelper *RefsHelper
commitsHelper *CommitsHelper
gpgHelper *GpgHelper
}
func NewWorkingTreeHelper(
c *HelperCommon,
refHelper *RefsHelper,
commitsHelper *CommitsHelper,
gpgHelper *GpgHelper,
) *WorkingTreeHelper {
return &WorkingTreeHelper{
c: c,
refHelper: refHelper,
commitsHelper: commitsHelper,
gpgHelper: gpgHelper,
}
}
func (self *WorkingTreeHelper) AnyStagedFiles() bool {
2023-03-23 12:53:18 +11:00
for _, file := range self.c.Model().Files {
if file.HasStagedChanges {
return true
}
}
return false
}
func (self *WorkingTreeHelper) AnyTrackedFiles() bool {
2023-03-23 12:53:18 +11:00
for _, file := range self.c.Model().Files {
if file.Tracked {
return true
}
}
return false
}
func (self *WorkingTreeHelper) IsWorkingTreeDirty() bool {
return self.AnyStagedFiles() || self.AnyTrackedFiles()
}
func (self *WorkingTreeHelper) FileForSubmodule(submodule *models.SubmoduleConfig) *models.File {
2023-03-23 12:53:18 +11:00
for _, file := range self.c.Model().Files {
if file.IsSubmodule([]*models.SubmoduleConfig{submodule}) {
return file
}
}
return nil
}
2022-03-26 15:52:35 +11:00
func (self *WorkingTreeHelper) OpenMergeTool() error {
return self.c.Confirm(types.ConfirmOpts{
2022-03-26 15:52:35 +11:00
Title: self.c.Tr.MergeToolTitle,
Prompt: self.c.Tr.MergeToolPrompt,
HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.OpenMergeTool)
return self.c.RunSubprocessAndRefresh(
2023-03-23 12:53:18 +11:00
self.c.Git().WorkingTree.OpenMergeToolCmdObj(),
2022-03-26 15:52:35 +11:00
)
},
})
}
func (self *WorkingTreeHelper) HandleCommitPressWithMessage(initialMessage string) error {
if err := self.prepareFilesForCommit(); err != nil {
return self.c.Error(err)
}
2023-03-23 12:53:18 +11:00
if len(self.c.Model().Files) == 0 {
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
}
if !self.AnyStagedFiles() {
return self.PromptToStageAllAndRetry(self.HandleCommitPress)
}
return self.commitsHelper.OpenCommitMessagePanel(
&OpenCommitMessagePanelOpts{
CommitIndex: context.NoCommitIndex,
InitialMessage: initialMessage,
SummaryTitle: self.c.Tr.CommitSummaryTitle,
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
PreserveMessage: true,
OnConfirm: self.handleCommit,
},
)
}
func (self *WorkingTreeHelper) handleCommit(summary string, description string) error {
cmdObj := self.c.Git().Commit.CommitCmdObj(summary, description)
self.c.LogAction(self.c.Tr.Actions.Commit)
return self.gpgHelper.WithGpgHandling(cmdObj, self.c.Tr.CommittingStatus, func() error {
self.commitsHelper.OnCommitSuccess()
return nil
})
}
// HandleCommitEditorPress - handle when the user wants to commit changes via
// their editor rather than via the popup panel
func (self *WorkingTreeHelper) HandleCommitEditorPress() error {
2023-03-23 12:53:18 +11:00
if len(self.c.Model().Files) == 0 {
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
}
if !self.AnyStagedFiles() {
return self.PromptToStageAllAndRetry(self.HandleCommitEditorPress)
}
self.c.LogAction(self.c.Tr.Actions.Commit)
return self.c.RunSubprocessAndRefresh(
2023-03-23 12:53:18 +11:00
self.c.Git().Commit.CommitEditorCmdObj(),
)
}
func (self *WorkingTreeHelper) HandleWIPCommitPress() error {
skipHookPrefix := self.c.UserConfig.Git.SkipHookPrefix
if skipHookPrefix == "" {
return self.c.ErrorMsg(self.c.Tr.SkipHookPrefixNotConfigured)
}
return self.HandleCommitPressWithMessage(skipHookPrefix)
}
func (self *WorkingTreeHelper) HandleCommitPress() error {
message := self.c.Contexts().CommitMessage.GetPreservedMessage()
if message == "" {
commitPrefixConfig := self.commitPrefixConfigForRepo()
if commitPrefixConfig != nil {
prefixPattern := commitPrefixConfig.Pattern
prefixReplace := commitPrefixConfig.Replace
rgx, err := regexp.Compile(prefixPattern)
if err != nil {
return self.c.ErrorMsg(fmt.Sprintf("%s: %s", self.c.Tr.CommitPrefixPatternError, err.Error()))
}
prefix := rgx.ReplaceAllString(self.refHelper.GetCheckedOutRef().Name, prefixReplace)
message = prefix
}
}
return self.HandleCommitPressWithMessage(message)
}
func (self *WorkingTreeHelper) PromptToStageAllAndRetry(retry func() error) error {
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.NoFilesStagedTitle,
Prompt: self.c.Tr.NoFilesStagedPrompt,
HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
2023-03-23 12:53:18 +11:00
if err := self.c.Git().WorkingTree.StageAll(); err != nil {
return self.c.Error(err)
}
if err := self.syncRefresh(); err != nil {
return self.c.Error(err)
}
return retry()
},
})
}
// for when you need to refetch files before continuing an action. Runs synchronously.
func (self *WorkingTreeHelper) syncRefresh() error {
return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.FILES}})
}
func (self *WorkingTreeHelper) prepareFilesForCommit() error {
noStagedFiles := !self.AnyStagedFiles()
if noStagedFiles && self.c.UserConfig.Gui.SkipNoStagedFilesWarning {
self.c.LogAction(self.c.Tr.Actions.StageAllFiles)
2023-03-23 12:53:18 +11:00
err := self.c.Git().WorkingTree.StageAll()
if err != nil {
return err
}
return self.syncRefresh()
}
return nil
}
func (self *WorkingTreeHelper) commitPrefixConfigForRepo() *config.CommitPrefixConfig {
cfg, ok := self.c.UserConfig.Git.CommitPrefixes[self.c.Git().RepoPaths.RepoName()]
if !ok {
return nil
}
return &cfg
}