diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2021-12-29 14:33:38 +1100 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2022-01-04 09:07:15 +1100 |
commit | 43a4fa970dfceb7868959ff4da48d5507fa2f234 (patch) | |
tree | daff776e59aebeb9b814a7918a9db75d4443175f /pkg/commands | |
parent | 192a548c9957807d9d5c9c4700dffe02c1f55f03 (diff) |
WIP
Diffstat (limited to 'pkg/commands')
27 files changed, 509 insertions, 390 deletions
diff --git a/pkg/commands/branches.go b/pkg/commands/branches.go index 274168311..e3c40d343 100644 --- a/pkg/commands/branches.go +++ b/pkg/commands/branches.go @@ -11,19 +11,19 @@ import ( // NewBranch create new branch func (c *GitCommand) NewBranch(name string, base string) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git checkout -b %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(base)))) + return c.Cmd.New(fmt.Sprintf("git checkout -b %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(base))).Run() } // CurrentBranchName get the current branch name and displayname. // the first returned string is the name and the second is the displayname // e.g. name is 123asdf and displayname is '(HEAD detached at 123asdf)' func (c *GitCommand) CurrentBranchName() (string, string, error) { - branchName, err := c.RunWithOutput(c.NewCmdObj("git symbolic-ref --short HEAD")) + branchName, err := c.Cmd.New("git symbolic-ref --short HEAD").RunWithOutput() if err == nil && branchName != "HEAD\n" { trimmedBranchName := strings.TrimSpace(branchName) return trimmedBranchName, trimmedBranchName, nil } - output, err := c.RunWithOutput(c.NewCmdObj("git branch --contains")) + output, err := c.Cmd.New("git branch --contains").RunWithOutput() if err != nil { return "", "", err } @@ -47,7 +47,7 @@ func (c *GitCommand) DeleteBranch(branch string, force bool) error { command = "git branch -D" } - return c.OSCommand.Run(c.OSCommand.NewCmdObj(fmt.Sprintf("%s %s", command, c.OSCommand.Quote(branch)))) + return c.Cmd.New(fmt.Sprintf("%s %s", command, c.OSCommand.Quote(branch))).Run() } // Checkout checks out a branch (or commit), with --force if you set the force arg to true @@ -62,24 +62,23 @@ func (c *GitCommand) Checkout(branch string, options CheckoutOptions) error { forceArg = " --force" } - cmdObj := c.NewCmdObj(fmt.Sprintf("git checkout%s %s", forceArg, c.OSCommand.Quote(branch))). + return c.Cmd.New(fmt.Sprintf("git checkout%s %s", forceArg, c.OSCommand.Quote(branch))). // prevents git from prompting us for input which would freeze the program // TODO: see if this is actually needed here AddEnvVars("GIT_TERMINAL_PROMPT=0"). - AddEnvVars(options.EnvVars...) - - return c.OSCommand.Run(cmdObj) + AddEnvVars(options.EnvVars...). + Run() } // GetBranchGraph gets the color-formatted graph of the log for the given branch // Currently it limits the result to 100 commits, but when we get async stuff // working we can do lazy loading func (c *GitCommand) GetBranchGraph(branchName string) (string, error) { - return c.OSCommand.RunWithOutput(c.GetBranchGraphCmdObj(branchName)) + return c.GetBranchGraphCmdObj(branchName).RunWithOutput() } func (c *GitCommand) GetUpstreamForBranch(branchName string) (string, error) { - output, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git rev-parse --abbrev-ref --symbolic-full-name %s@{u}", c.OSCommand.Quote(branchName)))) + output, err := c.Cmd.New(fmt.Sprintf("git rev-parse --abbrev-ref --symbolic-full-name %s@{u}", c.OSCommand.Quote(branchName))).RunWithOutput() return strings.TrimSpace(output), err } @@ -88,15 +87,15 @@ func (c *GitCommand) GetBranchGraphCmdObj(branchName string) oscommands.ICmdObj templateValues := map[string]string{ "branchName": c.OSCommand.Quote(branchName), } - return c.NewCmdObj(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)) + return c.Cmd.New(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)) } func (c *GitCommand) SetUpstreamBranch(upstream string) error { - return c.Run(c.NewCmdObj("git branch -u " + c.OSCommand.Quote(upstream))) + return c.Cmd.New("git branch -u " + c.OSCommand.Quote(upstream)).Run() } func (c *GitCommand) SetBranchUpstream(remoteName string, remoteBranchName string, branchName string) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git branch --set-upstream-to=%s/%s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName)))) + return c.Cmd.New(fmt.Sprintf("git branch --set-upstream-to=%s/%s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))).Run() } func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) { @@ -111,11 +110,11 @@ func (c *GitCommand) GetBranchUpstreamDifferenceCount(branchName string) (string // current branch func (c *GitCommand) GetCommitDifferences(from, to string) (string, string) { command := "git rev-list %s..%s --count" - pushableCount, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf(command, to, from))) + pushableCount, err := c.Cmd.New(fmt.Sprintf(command, to, from)).RunWithOutput() if err != nil { return "?", "?" } - pullableCount, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf(command, from, to))) + pullableCount, err := c.Cmd.New(fmt.Sprintf(command, from, to)).RunWithOutput() if err != nil { return "?", "?" } @@ -135,37 +134,37 @@ func (c *GitCommand) Merge(branchName string, opts MergeOpts) error { command = fmt.Sprintf("%s --ff-only", command) } - return c.OSCommand.Run(c.OSCommand.NewCmdObj(command)) + return c.OSCommand.Cmd.New(command).Run() } // AbortMerge abort merge func (c *GitCommand) AbortMerge() error { - return c.Run(c.NewCmdObj("git merge --abort")) + return c.Cmd.New("git merge --abort").Run() } func (c *GitCommand) IsHeadDetached() bool { - err := c.Run(c.NewCmdObj("git symbolic-ref -q HEAD")) + err := c.Cmd.New("git symbolic-ref -q HEAD").Run() return err != nil } // ResetHardHead runs `git reset --hard` func (c *GitCommand) ResetHard(ref string) error { - return c.Run(c.NewCmdObj("git reset --hard " + c.OSCommand.Quote(ref))) + return c.Cmd.New("git reset --hard " + c.OSCommand.Quote(ref)).Run() } // ResetSoft runs `git reset --soft HEAD` func (c *GitCommand) ResetSoft(ref string) error { - return c.Run(c.NewCmdObj("git reset --soft " + c.OSCommand.Quote(ref))) + return c.Cmd.New("git reset --soft " + c.OSCommand.Quote(ref)).Run() } func (c *GitCommand) ResetMixed(ref string) error { - return c.Run(c.NewCmdObj("git reset --mixed " + c.OSCommand.Quote(ref))) + return c.Cmd.New("git reset --mixed " + c.OSCommand.Quote(ref)).Run() } func (c *GitCommand) RenameBranch(oldName string, newName string) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git branch --move %s %s", c.OSCommand.Quote(oldName), c.OSCommand.Quote(newName)))) + return c.Cmd.New(fmt.Sprintf("git branch --move %s %s", c.OSCommand.Quote(oldName), c.OSCommand.Quote(newName))).Run() } func (c *GitCommand) GetRawBranches() (string, error) { - return c.RunWithOutput(c.NewCmdObj(`git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`)) + return c.Cmd.New(`git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`).RunWithOutput() } diff --git a/pkg/commands/branches_test.go b/pkg/commands/branches_test.go index 7064f4968..45cfb8315 100644 --- a/pkg/commands/branches_test.go +++ b/pkg/commands/branches_test.go @@ -210,7 +210,7 @@ func TestGitCommandGetAllBranchGraph(t *testing.T) { return secureexec.Command("echo") } cmdStr := gitCmd.UserConfig.Git.AllBranchesLogCmd - _, err := gitCmd.OSCommand.RunWithOutput(gitCmd.NewCmdObj(cmdStr)) + _, err := gitCmd.Cmd.New(cmdStr).RunWithOutput() assert.NoError(t, err) } diff --git a/pkg/commands/commits.go b/pkg/commands/commits.go index 447e9adb8..689220529 100644 --- a/pkg/commands/commits.go +++ b/pkg/commands/commits.go @@ -10,18 +10,17 @@ import ( // RenameCommit renames the topmost commit with the given name func (c *GitCommand) RenameCommit(name string) error { - return c.Run(c.NewCmdObj("git commit --allow-empty --amend --only -m " + c.OSCommand.Quote(name))) + return c.Cmd.New("git commit --allow-empty --amend --only -m " + c.OSCommand.Quote(name)).Run() } // ResetToCommit reset to commit func (c *GitCommand) ResetToCommit(sha string, strength string, envVars []string) error { - cmdObj := c.NewCmdObj(fmt.Sprintf("git reset --%s %s", strength, sha)). + return c.Cmd.New(fmt.Sprintf("git reset --%s %s", strength, sha)). // prevents git from prompting us for input which would freeze the program // TODO: see if this is actually needed here AddEnvVars("GIT_TERMINAL_PROMPT=0"). - AddEnvVars(envVars...) - - return c.OSCommand.Run(cmdObj) + AddEnvVars(envVars...). + Run() } func (c *GitCommand) CommitCmdObj(message string, flags string) oscommands.ICmdObj { @@ -36,33 +35,33 @@ func (c *GitCommand) CommitCmdObj(message string, flags string) oscommands.ICmdO flagsStr = fmt.Sprintf(" %s", flags) } - return c.NewCmdObj(fmt.Sprintf("git commit%s%s", flagsStr, lineArgs)) + return c.Cmd.New(fmt.Sprintf("git commit%s%s", flagsStr, lineArgs)) } // Get the subject of the HEAD commit func (c *GitCommand) GetHeadCommitMessage() (string, error) { - message, err := c.RunWithOutput(c.NewCmdObj("git log -1 --pretty=%s")) + message, err := c.Cmd.New("git log -1 --pretty=%s").RunWithOutput() return strings.TrimSpace(message), err } func (c *GitCommand) GetCommitMessage(commitSha string) (string, error) { cmdStr := "git rev-list --format=%B --max-count=1 " + commitSha - messageWithHeader, err := c.RunWithOutput(c.NewCmdObj(cmdStr)) + messageWithHeader, err := c.Cmd.New(cmdStr).RunWithOutput() message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "\n") return strings.TrimSpace(message), err } func (c *GitCommand) GetCommitMessageFirstLine(sha string) (string, error) { - return c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git show --no-patch --pretty=format:%%s %s", sha))) + return c.Cmd.New(fmt.Sprintf("git show --no-patch --pretty=format:%%s %s", sha)).RunWithOutput() } // AmendHead amends HEAD with whatever is staged in your working tree func (c *GitCommand) AmendHead() error { - return c.OSCommand.Run(c.AmendHeadCmdObj()) + return c.AmendHeadCmdObj().Run() } func (c *GitCommand) AmendHeadCmdObj() oscommands.ICmdObj { - return c.NewCmdObj("git commit --amend --no-edit --allow-empty") + return c.Cmd.New("git commit --amend --no-edit --allow-empty") } func (c *GitCommand) ShowCmdObj(sha string, filterPath string) oscommands.ICmdObj { @@ -73,16 +72,16 @@ func (c *GitCommand) ShowCmdObj(sha string, filterPath string) oscommands.ICmdOb } cmdStr := fmt.Sprintf("git show --submodule --color=%s --unified=%d --no-renames --stat -p %s %s", c.colorArg(), contextSize, sha, filterPathArg) - return c.NewCmdObj(cmdStr) + return c.Cmd.New(cmdStr) } // Revert reverts the selected commit by sha func (c *GitCommand) Revert(sha string) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git revert %s", sha))) + return c.Cmd.New(fmt.Sprintf("git revert %s", sha)).Run() } func (c *GitCommand) RevertMerge(sha string, parentNumber int) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git revert %s -m %d", sha, parentNumber))) + return c.Cmd.New(fmt.Sprintf("git revert %s -m %d", sha, parentNumber)).Run() } // CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD @@ -92,15 +91,15 @@ func (c *GitCommand) CherryPickCommits(commits []*models.Commit) error { todo = "pick " + commit.Sha + " " + commit.Name + "\n" + todo } - cmd, err := c.PrepareInteractiveRebaseCommand("HEAD", todo, false) + cmdObj, err := c.PrepareInteractiveRebaseCommand("HEAD", todo, false) if err != nil { return err } - return c.OSCommand.Run(cmd) + return cmdObj.Run() } // CreateFixupCommit creates a commit that fixes up a previous commit func (c *GitCommand) CreateFixupCommit(sha string) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git commit --fixup=%s", sha))) + return c.Cmd.New(fmt.Sprintf("git commit --fixup=%s", sha)).Run() } diff --git a/pkg/commands/files.go b/pkg/commands/files.go index 28f37612f..da451e80c 100644 --- a/pkg/commands/files.go +++ b/pkg/commands/files.go @@ -25,26 +25,26 @@ func (c *GitCommand) CatFile(fileName string) (string, error) { } func (c *GitCommand) OpenMergeToolCmdObj() oscommands.ICmdObj { - return c.NewCmdObj("git mergetool") + return c.Cmd.New("git mergetool") } func (c *GitCommand) OpenMergeTool() error { - return c.Run(c.OpenMergeToolCmdObj()) + return c.OpenMergeToolCmdObj().Run() } // StageFile stages a file func (c *GitCommand) StageFile(fileName string) error { - return c.Run(c.NewCmdObj("git add -- " + c.OSCommand.Quote(fileName))) + return c.Cmd.New("git add -- " + c.OSCommand.Quote(fileName)).Run() } // StageAll stages all files func (c *GitCommand) StageAll() error { - return c.Run(c.NewCmdObj("git add -A")) + return c.Cmd.New("git add -A").Run() } // UnstageAll unstages all files func (c *GitCommand) UnstageAll() error { - return c.Run(c.NewCmdObj("git reset")) + return c.Cmd.New("git reset").Run() } // UnStageFile unstages a file @@ -57,8 +57,8 @@ func (c *GitCommand) UnStageFile(fileNames []string, reset bool) error { } for _, name := range fileNames { - cmdObj := c.NewCmdObj(fmt.Sprintf(command, c.OSCommand.Quote(name))) - if err := c.Run(cmdObj); err != nil { + err := c.Cmd.New(fmt.Sprintf(command, c.OSCommand.Quote(name))).Run() + if err != nil { return err } } @@ -122,22 +122,22 @@ func (c *GitCommand) DiscardAllFileChanges(file *models.File) error { quotedFileName := c.OSCommand.Quote(file.Name) if file.ShortStatus == "AA" { - if err := c.Run(c.NewCmdObj("git checkout --ours -- " + quotedFileName)); err != nil { + if err := c.Cmd.New("git checkout --ours -- " + quotedFileName).Run(); err != nil { return err } - if err := c.Run(c.NewCmdObj("git add -- " + quotedFileName)); err != nil { + if err := c.Cmd.New("git add -- " + quotedFileName).Run(); err != nil { return err } return nil } if file.ShortStatus == "DU" { - return c.Run(c.NewCmdObj("git rm -- " + quotedFileName)) + return c.Cmd.New("git rm -- " + quotedFileName).Run() } // if the file isn't tracked, we assume you want to delete it if file.HasStagedChanges || file.HasMergeConflicts { - if err := c.Run(c.NewCmdObj("git reset -- " + quotedFileName)); err != nil { + if err := c.Cmd.New("git reset -- " + quotedFileName).Run(); err != nil { return err } } @@ -163,7 +163,7 @@ func (c *GitCommand) DiscardUnstagedDirChanges(node *filetree.FileNode) error { } quotedPath := c.OSCommand.Quote(node.GetPath()) - if err := c.Run(c.NewCmdObj("git checkout -- " + quotedPath)); err != nil { + if err := c.Cmd.New("git checkout -- " + quotedPath).Run(); err != nil { return err } @@ -188,7 +188,7 @@ func (c *GitCommand) RemoveUntrackedDirFiles(node *filetree.FileNode) error { // DiscardUnstagedFileChanges directly func (c *GitCommand) DiscardUnstagedFileChanges(file *models.File) error { quotedFileName := c.OSCommand.Quote(file.Name) - return c.Run(c.NewCmdObj("git checkout -- " + quotedFileName)) + return c.Cmd.New("git checkout -- " + quotedFileName).Run() } // Ignore adds a file to the gitignore for the repo @@ -199,7 +199,7 @@ func (c *GitCommand) Ignore(filename string) error { // WorktreeFileDiff returns the diff of a file func (c *GitCommand) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string { // for now we assume an error means the file was deleted - s, _ := c.OSCommand.RunWithOutput(c.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace)) + s, _ := c.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace).RunWithOutput() return s } @@ -225,7 +225,7 @@ func (c *GitCommand) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cache cmdStr := fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s %s %s %s %s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath) - return c.NewCmdObj(cmdStr) + return c.Cmd.New(cmdStr) } func (c *GitCommand) ApplyPatch(patch string, flags ...string) error { @@ -240,14 +240,13 @@ func (c *GitCommand) ApplyPatch(patch string, flags ...string) error { flagStr += " --" + flag } - return c.Run(c.NewCmdObj(fmt.Sprintf("git apply %s %s", flagStr, c.OSCommand.Quote(filepath)))) + return c.Cmd.New(fmt.Sprintf("git apply %s %s", flagStr, c.OSCommand.Quote(filepath))).Run() } // ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc // but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode. func (c *GitCommand) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) { - cmdObj := c.ShowFileDiffCmdObj(from, to, reverse, fileName, plain) - return c.RunWithOutput(cmdObj) + return c.ShowFileDiffCmdObj(from, to, reverse, fileName, plain).RunWithOutput() } func (c *GitCommand) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj { @@ -262,12 +261,12 @@ func (c *GitCommand) ShowFileDiffCmdObj(from string, to string, reverse bool, fi reverseFlag = " -R " } - return c.NewCmdObj(fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s %s %s %s -- %s", contextSize, colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName))) + return c.Cmd.New(fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s %s %s %s -- %s", contextSize, colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName))) } // CheckoutFile checks out the file for the given commit func (c *GitCommand) CheckoutFile(commitSha, fileName string) error { - return c.Run(c.NewCmdObj(fmt.Sprintf("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName)))) + return c.Cmd.New(fmt.Sprintf("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))).Run() } // DiscardOldFileChanges discards changes to a file from an old commit @@ -277,7 +276,7 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*models.Commit, commitIndex } // check if file exists in previous commit (this command returns an error if the file doesn't exist) - if err := c.Run(c.NewCmdObj("git cat-file -e HEAD^:" + c.OSCommand.Quote(fileName))); err != nil { + if err := c.Cmd.New("git cat-file -e HEAD^:" + c.OSCommand.Quote(fileName)).Run(); err != nil { if err := c.OSCommand.Remove(fileName); err != nil { return err } @@ -300,17 +299,17 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*models.Commit, commitIndex // DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .` func (c *GitCommand) DiscardAnyUnstagedFileChanges() error { - return c.Run(c.NewCmdObj("git checkout -- .")) + return c.Cmd.New("git checkout -- .").Run() } // RemoveTrackedFiles will delete the given file(s) even if they are currently tracked func (c *GitCommand) RemoveTrackedFiles(name string) error { - return c.Run(c.NewCmdObj("git rm -r --cached -- " + c.OSCommand.Quote(name))) + return c.Cmd.New("git rm -r --cached -- " + c.OSCommand.Quote(name)).Run() } // RemoveUntrackedFiles runs `git clean -fd` func (c *GitCommand) RemoveUntrackedFiles() error { - return c.Run(c.NewCmdObj("git clean -fd")) + return c.Cmd.New("git clean -fd").Run() } // ResetAndClean removes all unstaged changes and removes all untracked files @@ -350,7 +349,7 @@ func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, er editor = c.OSCommand.Getenv("EDITOR") } if editor == "" { - if err := c.OSCommand.Run(c.NewCmdObj("which vi")); err == nil { + if err := c.OSCommand.Cmd.New("which vi").Run(); err == nil { editor = "vi" } } diff --git a/pkg/commands/git.go b/pkg/commands/git.go index dd3879b82..b83fa1b73 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" "strings" - "time" "github.com/go-errors/errors" @@ -42,6 +41,8 @@ type GitCommand struct { // Coincidentally at the moment it's the same view that OnRunCommand logs to // but that need not always be the case. GetCmdWriter func() io.Writer + + Cmd oscommands.ICmdObjBuilder } // NewGitCommand it runs git commands @@ -68,6 +69,8 @@ func NewGitCommand( return nil, err } + cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd) + gitCommand := &GitCommand{ Common: cmn, OSCommand: osCommand, @@ -76,6 +79,7 @@ func NewGitCommand( PushToCurrent: pushToCurrent, GitConfig: gitConfig, GetCmdWriter: func() io.Writer { return ioutil.Discard }, + Cmd: cmd, } gitCommand.PatchManager = patch.NewPatchManager(gitCommand.Log, gitCommand.ApplyPatch, gitCommand.ShowFileDiff) @@ -215,44 +219,5 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam } func VerifyInGitRepo(osCommand *oscommands.OSCommand) error { - return osCommand.Run(osCommand.NewCmdObj("git rev-parse --git-dir")) -} - -func (c *GitCommand) Run(cmdObj oscommands.ICmdObj) error { - _, err := c.RunWithOutput(cmdObj) - return err -} - -func (c *GitCommand) RunWithOutput(cmdObj oscommands.ICmdObj) (string, error) { - // TODO: have this retry logic in other places we run the command - waitTime := 50 * time.Millisecond - retryCount := 5 - attempt := 0 - - for { - output, err := c.OSCommand.RunWithOutput(cmdObj) - if err != nil { - // if we have an error based on the index lock, we should wait a bit and then retry - if strings.Contains(output, ".git/index.lock") { - c.Log.Error(output) - c.Log.Info("index.lock prevented command from running. Retrying command after a small wait") - attempt++ - time.Sleep(waitTime) - if attempt < retryCount { - continue - } - } - } - return output, err - } -} - -func (c *GitCommand) NewCmdObj(cmdStr string) oscommands.ICmdObj { - return c.OSCommand.NewCmdObj(cmdStr).AddEnvVars("GIT_OPTIONAL_LOCKS=0") -} - -func (c *GitCommand) NewCmdObjWithLog(cmdStr string) oscommands.ICmdObj { - cmdObj := c.NewCmdObj(cmdStr) - c.OSCommand.LogCmdObj(cmdObj) - return cmdObj + return osCommand.Cmd.New("git rev-parse --git-dir").Run() } diff --git a/pkg/commands/git_cmd_obj_builder.go b/pkg/commands/git_cmd_obj_builder.go new file mode 100644 index 000000000..487dd2303 --- /dev/null +++ b/pkg/commands/git_cmd_obj_builder.go @@ -0,0 +1,47 @@ +package commands + +import ( + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/sirupsen/logrus" +) + +// all we're doing here is wrapping the default command object builder with +// some git-specific stuff: e.g. adding a git-specific env var + +type gitCmdObjBuilder struct { + innerBuilder *oscommands.CmdObjBuilder +} + +var _ oscommands.ICmdObjBuilder = &gitCmdObjBuilder{} + +func NewGitCmdObjBuilder(log *logrus.Entry, innerBuilder *oscommands.CmdObjBuilder) *gitCmdObjBuilder { + // the price of having a convenient interface where we can say .New(...).Run() is that our builder now depends on our runner, so when we want to wrap the default builder/runner in new functionality we need to jump through some hoops. We could avoid the use of a decorator function here by just exporting the runner field on the default builder but that would be misleading because we don't want anybody using that to run commands (i.e. we want there to be a single API used across the codebase) + updatedBuilder := innerBuilder.CloneWithNewRunner(func(runner oscommands.ICmdObjRunner) oscommands.ICmdObjRunner { + return &gitCmdObjRunner{ + log: log, + innerRunner: runner, + } + }) + + return &gitCmdObjBuilder{ + innerBuilder: updatedBuilder, + } +} + +var defaultEnvVar = "GIT_OPTIONAL_LOCKS=0" + +func (self *gitCmdObjBuilder) New(cmdStr string) oscommands.ICmdObj { + return self.innerBuilder.New(cmdStr).AddEnvVars(defaultEnvVar) +} + +func (self *gitCmdObjBuilder) NewFromArgs(args []string) oscommands.ICmdObj { + return self.innerBuilder.NewFromArgs(args).AddEnvVars(defaultEnvVar) +} + +func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj { + return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar) +} + +func (self *gitCmdObjBuilder) Quote(str string) string { + return self.innerBuilder.Quote(str) +} diff --git a/pkg/commands/git_cmd_obj_runner.go b/pkg/commands/git_cmd_obj_runner.go new file mode 100644 index 000000000..7529403cd --- /dev/null +++ b/pkg/commands/git_cmd_obj_runner.go @@ -0,0 +1,49 @@ +package commands + +import ( + "strings" + "time" + + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/sirupsen/logrus" +) + +// here we're wrapping the default command runner in some git-specific stuff e.g. retry logic if we get an error due to the presence of .git/index.lock + +type gitCmdObjRunner struct { + log *logrus.Entry + innerRunner oscommands.ICmdObjRunner +} + +func (self *gitCmdObjRunner) Run(cmdObj oscommands.ICmdObj) error { + _, err := self.RunWithOutput(cmdObj) + return err +} + +func (self *gitCmdObjRunner) RunWithOutput(cmdObj oscommands.ICmdObj) (string, error) { + // TODO: have this retry logic in other places we run the command + waitTime := 50 * time.Millisecond + retryCount := 5 + attempt := 0 + + for { + output, err := self.innerRunner.RunWithOutput(cmdObj) + if err != nil { + // if we have an error based on the index lock, we should wait a bit and then retry + if strings.Contains(output, ".git/index.lock") { + self.log.Error(output) + self.log.Info("index.lock prevented command from running. Retrying command after a small wait") + attempt++ + time.Sleep(waitTime) + if attempt < retryCount { + continue + } + } + } + return output, err + } +} + +func (self *gitCmdObjRunner) RunLineOutputCmd(cmdObj oscommands.ICmdObj, onLine func(line string) (bool, error)) error { + return self.innerRunner.RunLineOutputCmd(cmdObj, onLine) +} |