1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-27 12:32:37 +02:00
This commit is contained in:
Jesse Duffield 2021-12-07 21:59:36 +11:00
parent 157dd309f7
commit b4c078d565
48 changed files with 437 additions and 493 deletions

View File

@ -146,7 +146,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
}
func (app *App) validateGitVersion() error {
output, err := app.OSCommand.RunCommandWithOutput("git --version")
output, err := app.OSCommand.RunWithOutput(app.OSCommand.NewCmdObj("git --version"))
// if we get an error anywhere here we'll show the same status
minVersionError := errors.New(app.Tr.MinGitVersionError)
if err != nil {
@ -231,7 +231,7 @@ func (app *App) setupRepo() (bool, error) {
os.Exit(1)
}
if err := app.OSCommand.RunCommand("git init"); err != nil {
if err := app.OSCommand.Run(app.OSCommand.NewCmdObj("git init")); err != nil {
return false, err
}
}

View File

@ -11,19 +11,19 @@ import (
// NewBranch create new branch
func (c *GitCommand) NewBranch(name string, base string) error {
return c.RunCommand("git checkout -b %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(base))
return c.Run(c.NewCmdObj(fmt.Sprintf("git checkout -b %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(base))))
}
// 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.RunCommandWithOutput("git symbolic-ref --short HEAD")
branchName, err := c.RunWithOutput(c.NewCmdObj("git symbolic-ref --short HEAD"))
if err == nil && branchName != "HEAD\n" {
trimmedBranchName := strings.TrimSpace(branchName)
return trimmedBranchName, trimmedBranchName, nil
}
output, err := c.RunCommandWithOutput("git branch --contains")
output, err := c.RunWithOutput(c.NewCmdObj("git branch --contains"))
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.RunCommand("%s %s", command, c.OSCommand.Quote(branch))
return c.OSCommand.Run(c.OSCommand.NewCmdObj(fmt.Sprintf("%s %s", command, c.OSCommand.Quote(branch))))
}
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
@ -61,36 +61,42 @@ func (c *GitCommand) Checkout(branch string, options CheckoutOptions) error {
if options.Force {
forceArg = " --force"
}
return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git checkout%s %s", forceArg, c.OSCommand.Quote(branch)), oscommands.RunCommandOptions{EnvVars: options.EnvVars})
cmdObj := c.NewCmdObj(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)
}
// 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) {
cmdStr := c.GetBranchGraphCmdStr(branchName)
return c.OSCommand.RunCommandWithOutput(cmdStr)
return c.OSCommand.RunWithOutput(c.GetBranchGraphCmdObj(branchName))
}
func (c *GitCommand) GetUpstreamForBranch(branchName string) (string, error) {
output, err := c.RunCommandWithOutput("git rev-parse --abbrev-ref --symbolic-full-name %s@{u}", c.OSCommand.Quote(branchName))
output, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git rev-parse --abbrev-ref --symbolic-full-name %s@{u}", c.OSCommand.Quote(branchName))))
return strings.TrimSpace(output), err
}
func (c *GitCommand) GetBranchGraphCmdStr(branchName string) string {
func (c *GitCommand) GetBranchGraphCmdObj(branchName string) oscommands.ICmdObj {
branchLogCmdTemplate := c.Config.GetUserConfig().Git.BranchLogCmd
templateValues := map[string]string{
"branchName": c.OSCommand.Quote(branchName),
}
return utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)
return c.NewCmdObj(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues))
}
func (c *GitCommand) SetUpstreamBranch(upstream string) error {
return c.RunCommand("git branch -u %s", c.OSCommand.Quote(upstream))
return c.Run(c.NewCmdObj("git branch -u " + c.OSCommand.Quote(upstream)))
}
func (c *GitCommand) SetBranchUpstream(remoteName string, remoteBranchName string, branchName string) error {
return c.RunCommand("git branch --set-upstream-to=%s/%s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
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))))
}
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
@ -105,11 +111,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.OSCommand.RunCommandWithOutput(command, to, from)
pushableCount, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf(command, to, from)))
if err != nil {
return "?", "?"
}
pullableCount, err := c.OSCommand.RunCommandWithOutput(command, from, to)
pullableCount, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf(command, from, to)))
if err != nil {
return "?", "?"
}
@ -129,33 +135,33 @@ func (c *GitCommand) Merge(branchName string, opts MergeOpts) error {
command = fmt.Sprintf("%s --ff-only", command)
}
return c.OSCommand.RunCommand(command)
return c.OSCommand.Run(c.OSCommand.NewCmdObj(command))
}
// AbortMerge abort merge
func (c *GitCommand) AbortMerge() error {
return c.RunCommand("git merge --abort")
return c.Run(c.NewCmdObj("git merge --abort"))
}
func (c *GitCommand) IsHeadDetached() bool {
err := c.RunCommand("git symbolic-ref -q HEAD")
err := c.Run(c.NewCmdObj("git symbolic-ref -q HEAD"))
return err != nil
}
// ResetHardHead runs `git reset --hard`
func (c *GitCommand) ResetHard(ref string) error {
return c.RunCommand("git reset --hard " + c.OSCommand.Quote(ref))
return c.Run(c.NewCmdObj("git reset --hard " + c.OSCommand.Quote(ref)))
}
// ResetSoft runs `git reset --soft HEAD`
func (c *GitCommand) ResetSoft(ref string) error {
return c.RunCommand("git reset --soft " + c.OSCommand.Quote(ref))
return c.Run(c.NewCmdObj("git reset --soft " + c.OSCommand.Quote(ref)))
}
func (c *GitCommand) ResetMixed(ref string) error {
return c.RunCommand("git reset --mixed " + c.OSCommand.Quote(ref))
return c.Run(c.NewCmdObj("git reset --mixed " + c.OSCommand.Quote(ref)))
}
func (c *GitCommand) RenameBranch(oldName string, newName string) error {
return c.RunCommand("git branch --move %s %s", c.OSCommand.Quote(oldName), c.OSCommand.Quote(newName))
return c.Run(c.NewCmdObj(fmt.Sprintf("git branch --move %s %s", c.OSCommand.Quote(oldName), c.OSCommand.Quote(newName))))
}

View File

@ -210,7 +210,7 @@ func TestGitCommandGetAllBranchGraph(t *testing.T) {
return secureexec.Command("echo")
}
cmdStr := gitCmd.Config.GetUserConfig().Git.AllBranchesLogCmd
_, err := gitCmd.OSCommand.RunCommandWithOutput(cmdStr)
_, err := gitCmd.OSCommand.RunWithOutput(gitCmd.NewCmdObj(cmdStr))
assert.NoError(t, err)
}

View File

@ -10,15 +10,21 @@ import (
// RenameCommit renames the topmost commit with the given name
func (c *GitCommand) RenameCommit(name string) error {
return c.RunCommand("git commit --allow-empty --amend --only -m %s", c.OSCommand.Quote(name))
return c.Run(c.NewCmdObj("git commit --allow-empty --amend --only -m " + c.OSCommand.Quote(name)))
}
// ResetToCommit reset to commit
func (c *GitCommand) ResetToCommit(sha string, strength string, options oscommands.RunCommandOptions) error {
return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git reset --%s %s", strength, sha), options)
func (c *GitCommand) ResetToCommit(sha string, strength string, envVars []string) error {
cmdObj := c.NewCmdObj(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)
}
func (c *GitCommand) CommitCmdStr(message string, flags string) string {
func (c *GitCommand) CommitCmdObj(message string, flags string) oscommands.ICmdObj {
splitMessage := strings.Split(message, "\n")
lineArgs := ""
for _, line := range splitMessage {
@ -30,52 +36,53 @@ func (c *GitCommand) CommitCmdStr(message string, flags string) string {
flagsStr = fmt.Sprintf(" %s", flags)
}
return fmt.Sprintf("git commit%s%s", flagsStr, lineArgs)
return c.NewCmdObj(fmt.Sprintf("git commit%s%s", flagsStr, lineArgs))
}
// Get the subject of the HEAD commit
func (c *GitCommand) GetHeadCommitMessage() (string, error) {
cmdStr := "git log -1 --pretty=%s"
message, err := c.OSCommand.RunCommandWithOutput(cmdStr)
message, err := c.RunWithOutput(c.NewCmdObj("git log -1 --pretty=%s"))
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.OSCommand.RunCommandWithOutput(cmdStr)
messageWithHeader, err := c.RunWithOutput(c.NewCmdObj(cmdStr))
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "\n")
return strings.TrimSpace(message), err
}
func (c *GitCommand) GetCommitMessageFirstLine(sha string) (string, error) {
return c.RunCommandWithOutput("git show --no-patch --pretty=format:%%s %s", sha)
return c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git show --no-patch --pretty=format:%%s %s", sha)))
}
// AmendHead amends HEAD with whatever is staged in your working tree
func (c *GitCommand) AmendHead() error {
return c.OSCommand.RunCommand(c.AmendHeadCmdStr())
return c.OSCommand.Run(c.AmendHeadCmdObj())
}
func (c *GitCommand) AmendHeadCmdStr() string {
return "git commit --amend --no-edit --allow-empty"
func (c *GitCommand) AmendHeadCmdObj() oscommands.ICmdObj {
return c.NewCmdObj("git commit --amend --no-edit --allow-empty")
}
func (c *GitCommand) ShowCmdStr(sha string, filterPath string) string {
func (c *GitCommand) ShowCmdObj(sha string, filterPath string) oscommands.ICmdObj {
contextSize := c.Config.GetUserConfig().Git.DiffContextSize
filterPathArg := ""
if filterPath != "" {
filterPathArg = fmt.Sprintf(" -- %s", c.OSCommand.Quote(filterPath))
}
return fmt.Sprintf("git show --submodule --color=%s --unified=%d --no-renames --stat -p %s %s", c.colorArg(), contextSize, sha, filterPathArg)
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)
}
// Revert reverts the selected commit by sha
func (c *GitCommand) Revert(sha string) error {
return c.RunCommand("git revert %s", sha)
return c.Run(c.NewCmdObj(fmt.Sprintf("git revert %s", sha)))
}
func (c *GitCommand) RevertMerge(sha string, parentNumber int) error {
return c.RunCommand("git revert %s -m %d", sha, parentNumber)
return c.Run(c.NewCmdObj(fmt.Sprintf("git revert %s -m %d", sha, parentNumber)))
}
// CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD
@ -90,10 +97,10 @@ func (c *GitCommand) CherryPickCommits(commits []*models.Commit) error {
return err
}
return c.OSCommand.RunPreparedCommand(cmd)
return c.OSCommand.Run(cmd)
}
// CreateFixupCommit creates a commit that fixes up a previous commit
func (c *GitCommand) CreateFixupCommit(sha string) error {
return c.RunCommand("git commit --fixup=%s", sha)
return c.Run(c.NewCmdObj(fmt.Sprintf("git commit --fixup=%s", sha)))
}

View File

@ -4,7 +4,6 @@ import (
"os/exec"
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/test"
"github.com/stretchr/testify/assert"
@ -33,11 +32,11 @@ func TestGitCommandResetToCommit(t *testing.T) {
return secureexec.Command("echo")
}
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard", oscommands.RunCommandOptions{}))
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard", []string{}))
}
// TestGitCommandCommitStr is a function.
func TestGitCommandCommitStr(t *testing.T) {
// TestGitCommandCommitObj is a function.
func TestGitCommandCommitObj(t *testing.T) {
gitCmd := NewDummyGitCommand()
type scenario struct {
@ -70,7 +69,7 @@ func TestGitCommandCommitStr(t *testing.T) {
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
cmdStr := gitCmd.CommitCmdStr(s.message, s.flags)
cmdStr := gitCmd.CommitCmdObj(s.message, s.flags).ToString()
assert.Equal(t, s.expected, cmdStr)
})
}
@ -111,8 +110,8 @@ func TestGitCommandCreateFixupCommit(t *testing.T) {
}
}
// TestGitCommandShowCmdStr is a function.
func TestGitCommandShowCmdStr(t *testing.T) {
// TestGitCommandShowCmdObj is a function.
func TestGitCommandShowCmdObj(t *testing.T) {
type scenario struct {
testName string
filterPath string
@ -146,7 +145,7 @@ func TestGitCommandShowCmdStr(t *testing.T) {
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd.Config.GetUserConfig().Git.DiffContextSize = s.contextSize
cmdStr := gitCmd.ShowCmdStr("1234567890", s.filterPath)
cmdStr := gitCmd.ShowCmdObj("1234567890", s.filterPath).ToString()
assert.Equal(t, s.expected, cmdStr)
})
}

View File

@ -10,6 +10,7 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -23,27 +24,27 @@ func (c *GitCommand) CatFile(fileName string) (string, error) {
return string(buf), nil
}
func (c *GitCommand) OpenMergeToolCmd() string {
return "git mergetool"
func (c *GitCommand) OpenMergeToolCmdObj() oscommands.ICmdObj {
return c.NewCmdObj("git mergetool")
}
func (c *GitCommand) OpenMergeTool() error {
return c.OSCommand.RunCommand("git mergetool")
return c.Run(c.OpenMergeToolCmdObj())
}
// StageFile stages a file
func (c *GitCommand) StageFile(fileName string) error {
return c.RunCommand("git add -- %s", c.OSCommand.Quote(fileName))
return c.Run(c.NewCmdObj("git add -- " + c.OSCommand.Quote(fileName)))
}
// StageAll stages all files
func (c *GitCommand) StageAll() error {
return c.RunCommand("git add -A")
return c.Run(c.NewCmdObj("git add -A"))
}
// UnstageAll unstages all files
func (c *GitCommand) UnstageAll() error {
return c.RunCommand("git reset")
return c.Run(c.NewCmdObj("git reset"))
}
// UnStageFile unstages a file
@ -56,7 +57,8 @@ func (c *GitCommand) UnStageFile(fileNames []string, reset bool) error {
}
for _, name := range fileNames {
if err := c.OSCommand.RunCommand(command, c.OSCommand.Quote(name)); err != nil {
cmdObj := c.NewCmdObj(fmt.Sprintf(command, c.OSCommand.Quote(name)))
if err := c.Run(cmdObj); err != nil {
return err
}
}
@ -120,22 +122,22 @@ func (c *GitCommand) DiscardAllFileChanges(file *models.File) error {
quotedFileName := c.OSCommand.Quote(file.Name)
if file.ShortStatus == "AA" {
if err := c.RunCommand("git checkout --ours -- %s", quotedFileName); err != nil {
if err := c.Run(c.NewCmdObj("git checkout --ours -- " + quotedFileName)); err != nil {
return err
}
if err := c.RunCommand("git add -- %s", quotedFileName); err != nil {
if err := c.Run(c.NewCmdObj("git add -- " + quotedFileName)); err != nil {
return err
}
return nil
}
if file.ShortStatus == "DU" {
return c.RunCommand("git rm -- %s", quotedFileName)
return c.Run(c.NewCmdObj("git rm -- " + quotedFileName))
}
// if the file isn't tracked, we assume you want to delete it
if file.HasStagedChanges || file.HasMergeConflicts {
if err := c.RunCommand("git reset -- %s", quotedFileName); err != nil {
if err := c.Run(c.NewCmdObj("git reset -- " + quotedFileName)); err != nil {
return err
}
}
@ -161,7 +163,7 @@ func (c *GitCommand) DiscardUnstagedDirChanges(node *filetree.FileNode) error {
}
quotedPath := c.OSCommand.Quote(node.GetPath())
if err := c.RunCommand("git checkout -- %s", quotedPath); err != nil {
if err := c.Run(c.NewCmdObj("git checkout -- " + quotedPath)); err != nil {
return err
}
@ -186,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.RunCommand("git checkout -- %s", quotedFileName)
return c.Run(c.NewCmdObj("git checkout -- " + quotedFileName))
}
// Ignore adds a file to the gitignore for the repo
@ -197,11 +199,11 @@ 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.RunCommandWithOutput(c.WorktreeFileDiffCmdStr(file, plain, cached, ignoreWhitespace))
s, _ := c.OSCommand.RunWithOutput(c.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace))
return s
}
func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) string {
func (c *GitCommand) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
cachedArg := ""
trackedArg := "--"
colorArg := c.colorArg()
@ -221,7 +223,9 @@ func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cache
ignoreWhitespaceArg = "--ignore-all-space"
}
return fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s %s %s %s %s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath)
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)
}
func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
@ -236,17 +240,17 @@ func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
flagStr += " --" + flag
}
return c.RunCommand("git apply %s %s", flagStr, c.OSCommand.Quote(filepath))
return c.Run(c.NewCmdObj(fmt.Sprintf("git apply %s %s", flagStr, c.OSCommand.Quote(filepath))))
}
// 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) {
cmdStr := c.ShowFileDiffCmdStr(from, to, reverse, fileName, plain)
return c.OSCommand.RunCommandWithOutput(cmdStr)
cmdObj := c.ShowFileDiffCmdObj(from, to, reverse, fileName, plain)
return c.RunWithOutput(cmdObj)
}
func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fileName string, plain bool) string {
func (c *GitCommand) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
colorArg := c.colorArg()
contextSize := c.Config.GetUserConfig().Git.DiffContextSize
if plain {
@ -258,12 +262,12 @@ func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fi
reverseFlag = " -R "
}
return 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.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)))
}
// CheckoutFile checks out the file for the given commit
func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
return c.RunCommand("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))
return c.Run(c.NewCmdObj(fmt.Sprintf("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))))
}
// DiscardOldFileChanges discards changes to a file from an old commit
@ -273,7 +277,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.RunCommand("git cat-file -e HEAD^:%s", c.OSCommand.Quote(fileName)); err != nil {
if err := c.Run(c.NewCmdObj("git cat-file -e HEAD^:" + c.OSCommand.Quote(fileName))); err != nil {
if err := c.OSCommand.Remove(fileName); err != nil {
return err
}
@ -296,17 +300,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.RunCommand("git checkout -- .")
return c.Run(c.NewCmdObj("git checkout -- ."))
}
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
func (c *GitCommand) RemoveTrackedFiles(name string) error {
return c.RunCommand("git rm -r --cached -- %s", c.OSCommand.Quote(name))
return c.Run(c.NewCmdObj("git rm -r --cached -- " + c.OSCommand.Quote(name)))
}
// RemoveUntrackedFiles runs `git clean -fd`
func (c *GitCommand) RemoveUntrackedFiles() error {
return c.RunCommand("git clean -fd")
return c.Run(c.NewCmdObj("git clean -fd"))
}
// ResetAndClean removes all unstaged changes and removes all untracked files
@ -346,7 +350,7 @@ func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, er
editor = c.OSCommand.Getenv("EDITOR")
}
if editor == "" {
if err := c.OSCommand.RunCommand("which vi"); err == nil {
if err := c.OSCommand.Run(c.NewCmdObj("which vi")); err == nil {
editor = "vi"
}
}

View File

@ -223,22 +223,22 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam
}
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
return osCommand.RunCommand("git rev-parse --git-dir")
return osCommand.Run(osCommand.NewCmdObj("git rev-parse --git-dir"))
}
func (c *GitCommand) RunCommand(formatString string, formatArgs ...interface{}) error {
_, err := c.RunCommandWithOutput(formatString, formatArgs...)
func (c *GitCommand) Run(cmdObj oscommands.ICmdObj) error {
_, err := c.RunWithOutput(cmdObj)
return err
}
func (c *GitCommand) RunCommandWithOutput(formatString string, formatArgs ...interface{}) (string, error) {
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.RunCommandWithOutput(formatString, formatArgs...)
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") {
@ -255,6 +255,12 @@ func (c *GitCommand) RunCommandWithOutput(formatString string, formatArgs ...int
}
}
func (c *GitCommand) NewCmdObjFromStr(cmdStr string) oscommands.ICmdObj {
return c.OSCommand.NewCmdObjFromStr(cmdStr).AddEnvVars("GIT_OPTIONAL_LOCKS=0")
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
}

View File

@ -38,7 +38,7 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand, reflogCommi
func (b *BranchListBuilder) obtainBranches() []*models.Branch {
cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`
output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr)
output, err := b.GitCommand.RunWithOutput(b.GitCommand.NewCmdObj(cmdStr))
if err != nil {
panic(err)
}

View File

@ -1,6 +1,7 @@
package commands
import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@ -13,7 +14,7 @@ func (c *GitCommand) GetFilesInDiff(from string, to string, reverse bool) ([]*mo
reverseFlag = " -R "
}
filenames, err := c.RunCommandWithOutput("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)
filenames, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)))
if err != nil {
return nil, err
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
@ -153,9 +152,9 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
passedFirstPushedCommit = true
}
cmd := c.getLogCmd(opts)
cmdObj := c.getLogCmd(opts)
err = oscommands.RunLineOutputCmd(cmd, func(line string) (bool, error) {
err = c.OSCommand.RunLineOutputCmd(cmdObj, func(line string) (bool, error) {
if canExtractCommit(line) {
commit := c.extractCommitFromLine(line)
if commit.Sha == firstPushedCommit {
@ -201,7 +200,7 @@ func (c *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*mo
// note that we're not filtering these as we do non-rebasing commits just because
// I suspect that will cause some damage
cmd := c.OSCommand.ExecutableFromString(
cmdObj := c.OSCommand.NewCmdObj(
fmt.Sprintf(
"git show %s --no-patch --oneline %s --abbrev=%d",
strings.Join(commitShas, " "),
@ -212,7 +211,7 @@ func (c *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*mo
hydratedCommits := make([]*models.Commit, 0, len(commits))
i := 0
err = oscommands.RunLineOutputCmd(cmd, func(line string) (bool, error) {
err = c.OSCommand.RunLineOutputCmd(cmdObj, func(line string) (bool, error) {
if canExtractCommit(line) {
commit := c.extractCommitFromLine(line)
matchingCommit := commits[i]
@ -374,7 +373,7 @@ func (c *CommitListBuilder) getMergeBase(refName string) (string, error) {
}
// swallowing error because it's not a big deal; probably because there are no commits yet
output, _ := c.OSCommand.RunCommandWithOutput("git merge-base %s %s", c.OSCommand.Quote(refName), c.OSCommand.Quote(baseBranch))
output, _ := c.OSCommand.RunWithOutput(c.OSCommand.NewCmdObj(fmt.Sprintf("git merge-base %s %s", c.OSCommand.Quote(refName), c.OSCommand.Quote(baseBranch))))
return ignoringWarnings(output), nil
}
@ -391,7 +390,7 @@ func ignoringWarnings(commandOutput string) string {
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
// all commits above this are deemed unpushed and marked as such.
func (c *CommitListBuilder) getFirstPushedCommit(refName string) (string, error) {
output, err := c.OSCommand.RunCommandWithOutput("git merge-base %s %s@{u}", c.OSCommand.Quote(refName), c.OSCommand.Quote(refName))
output, err := c.OSCommand.RunWithOutput(c.OSCommand.NewCmdObj(fmt.Sprintf("git merge-base %s %s@{u}", c.OSCommand.Quote(refName), c.OSCommand.Quote(refName))))
if err != nil {
return "", err
}
@ -400,7 +399,7 @@ func (c *CommitListBuilder) getFirstPushedCommit(refName string) (string, error)
}
// getLog gets the git log.
func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) *exec.Cmd {
func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
limitFlag := ""
if opts.Limit {
limitFlag = "-300"
@ -419,7 +418,7 @@ func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) *exec.Cmd {
allFlag = " --all"
}
return c.OSCommand.ExecutableFromString(
return c.OSCommand.NewCmdObj(
fmt.Sprintf(
"git log %s %s %s --oneline %s %s --abbrev=%d %s",
c.OSCommand.Quote(opts.RefName),

View File

@ -80,7 +80,7 @@ func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
noRenamesFlag = "--no-renames"
}
statusLines, err := c.RunCommandWithOutput("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)
statusLines, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)))
if err != nil {
return []FileStatus{}, err
}

View File

@ -6,7 +6,6 @@ import (
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
@ -19,9 +18,9 @@ func (c *GitCommand) GetReflogCommits(lastReflogCommit *models.Commit, filterPat
filterPathArg = fmt.Sprintf(" --follow -- %s", c.OSCommand.Quote(filterPath))
}
cmd := c.OSCommand.ExecutableFromString(fmt.Sprintf(`git log -g --abbrev=20 --format="%%h %%ct %%gs" %s`, filterPathArg))
cmdObj := c.OSCommand.NewCmdObj(fmt.Sprintf(`git log -g --abbrev=20 --format="%%h %%ct %%gs" %s`, filterPathArg))
onlyObtainedNewReflogCommits := false
err := oscommands.RunLineOutputCmd(cmd, func(line string) (bool, error) {
err := c.OSCommand.RunLineOutputCmd(cmdObj, func(line string) (bool, error) {
fields := strings.SplitN(line, " ", 3)
if len(fields) <= 2 {
return false, nil

View File

@ -10,9 +10,7 @@ import (
)
func (c *GitCommand) GetRemotes() ([]*models.Remote, error) {
// get remote branches
unescaped := "git branch -r"
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput(unescaped)
remoteBranchesStr, err := c.RunWithOutput(c.NewCmdObj("git branch -r"))
if err != nil {
return nil, err
}

View File

@ -10,8 +10,7 @@ import (
)
func (c *GitCommand) getUnfilteredStashEntries() []*models.StashEntry {
unescaped := "git stash list --pretty='%gs'"
rawString, _ := c.OSCommand.RunCommandWithOutput(unescaped)
rawString, _ := c.RunWithOutput(c.NewCmdObj("git stash list --pretty='%gs'"))
stashEntries := []*models.StashEntry{}
for i, line := range utils.SplitLines(rawString) {
stashEntries = append(stashEntries, stashEntryFromLine(line, i))
@ -25,7 +24,7 @@ func (c *GitCommand) GetStashEntries(filterPath string) []*models.StashEntry {
return c.getUnfilteredStashEntries()
}
rawString, err := c.RunCommandWithOutput("git stash list --name-only")
rawString, err := c.RunWithOutput(c.NewCmdObj("git stash list --name-only"))
if err != nil {
return c.getUnfilteredStashEntries()
}

View File

@ -10,7 +10,7 @@ import (
func (c *GitCommand) GetTags() ([]*models.Tag, error) {
// get remote branches, sorted by creation date (descending)
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput(`git tag --list --sort=-creatordate`)
remoteBranchesStr, err := c.OSCommand.RunWithOutput(c.NewCmdObj(`git tag --list --sort=-creatordate`))
if err != nil {
return nil, err
}

View File

@ -11,6 +11,7 @@ type ICmdObj interface {
GetCmd() *exec.Cmd
ToString() string
AddEnvVars(...string) ICmdObj
GetEnvVars() []string
}
type CmdObj struct {
@ -31,3 +32,7 @@ func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
return self
}
func (self *CmdObj) GetEnvVars() []string {
return self.cmd.Env
}

View File

@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
"sync"
"testing"
"github.com/go-errors/errors"
@ -29,14 +30,25 @@ type Platform struct {
OpenLinkCommand string
}
type ICommander interface {
Run(ICmdObj) error
RunWithOutput(ICmdObj) (string, error)
}
type RealCommander struct {
}
func (self *RealCommander) Run(cmdObj ICmdObj) error {
return cmdObj.GetCmd().Run()
}
// OSCommand holds all the os commands
type OSCommand struct {
Log *logrus.Entry
Platform *Platform
Config config.AppConfigurer
Command func(string, ...string) *exec.Cmd
BeforeExecuteCmd func(*exec.Cmd)
Getenv func(string) string
Log *logrus.Entry
Platform *Platform
Config config.AppConfigurer
Command func(string, ...string) *exec.Cmd
Getenv func(string) string
// callback to run before running a command, i.e. for the purposes of logging
onRunCommand func(CmdLogEntry)
@ -45,6 +57,8 @@ type OSCommand struct {
CmdLogSpan string
removeFile func(string) error
IRunner
}
// TODO: make these fields private
@ -79,15 +93,17 @@ func NewCmdLogEntry(cmdStr string, span string, commandLine bool) CmdLogEntry {
// NewOSCommand os command runner
func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand {
return &OSCommand{
Log: log,
Platform: getPlatform(),
Config: config,
Command: secureexec.Command,
BeforeExecuteCmd: func(*exec.Cmd) {},
Getenv: os.Getenv,
removeFile: os.RemoveAll,
c := &OSCommand{
Log: log,
Platform: getPlatform(),
Config: config,
Command: secureexec.Command,
Getenv: os.Getenv,
removeFile: os.RemoveAll,
}
c.IRunner = &RealRunner{c: c}
return c
}
func (c *OSCommand) WithSpan(span string) *OSCommand {
@ -104,8 +120,8 @@ func (c *OSCommand) WithSpan(span string) *OSCommand {
return newOSCommand
}
func (c *OSCommand) LogExecCmd(cmd *exec.Cmd) {
c.LogCommand(strings.Join(cmd.Args, " "), true)
func (c *OSCommand) LogCmdObj(cmdObj ICmdObj) {
c.LogCommand(cmdObj.ToString(), true)
}
func (c *OSCommand) LogCommand(cmdStr string, commandLine bool) {
@ -131,108 +147,6 @@ func (c *OSCommand) SetRemoveFile(f func(string) error) {
c.removeFile = f
}
func (c *OSCommand) SetBeforeExecuteCmd(cmd func(*exec.Cmd)) {
c.BeforeExecuteCmd = cmd
}
type RunCommandOptions struct {
EnvVars []string
}
func (c *OSCommand) RunCommandWithOutputWithOptions(command string, options RunCommandOptions) (string, error) {
c.LogCommand(command, true)
cmd := c.ExecutableFromString(command)
cmd.Env = append(cmd.Env, "GIT_TERMINAL_PROMPT=0") // prevents git from prompting us for input which would freeze the program
cmd.Env = append(cmd.Env, options.EnvVars...)
return sanitisedCommandOutput(cmd.CombinedOutput())
}
func (c *OSCommand) RunCommandWithOptions(command string, options RunCommandOptions) error {
_, err := c.RunCommandWithOutputWithOptions(command, options)
return err
}
// RunCommandWithOutput wrapper around commands returning their output and error
// NOTE: If you don't pass any formatArgs we'll just use the command directly,
// however there's a bizarre compiler error/warning when you pass in a formatString
// with a percent sign because it thinks it's supposed to be a formatString when
// in that case it's not. To get around that error you'll need to define the string
// in a variable and pass the variable into RunCommandWithOutput.
func (c *OSCommand) RunCommandWithOutput(formatString string, formatArgs ...interface{}) (string, error) {
command := formatString
if formatArgs != nil {
command = fmt.Sprintf(formatString, formatArgs...)
}
cmd := c.ExecutableFromString(command)
c.LogExecCmd(cmd)
output, err := sanitisedCommandOutput(cmd.CombinedOutput())
if err != nil {
c.Log.WithField("command", command).Error(output)
}
return output, err
}
// RunExecutableWithOutput runs an executable file and returns its output
func (c *OSCommand) RunExecutableWithOutput(cmd *exec.Cmd) (string, error) {
c.LogExecCmd(cmd)
c.BeforeExecuteCmd(cmd)
return sanitisedCommandOutput(cmd.CombinedOutput())
}
// RunExecutable runs an executable file and returns an error if there was one
func (c *OSCommand) RunExecutable(cmd *exec.Cmd) error {
_, err := c.RunExecutableWithOutput(cmd)
return err
}
// ExecutableFromString takes a string like `git status` and returns an executable command for it
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
splitCmd := str.ToArgv(commandStr)
cmd := c.Command(splitCmd[0], splitCmd[1:]...)
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
return cmd
}
// ShellCommandFromString takes a string like `git commit` and returns an executable shell command for it
func (c *OSCommand) ShellCommandFromString(commandStr string) *exec.Cmd {
quotedCommand := ""
// Windows does not seem to like quotes around the command
if c.Platform.OS == "windows" {
quotedCommand = strings.NewReplacer(
"^", "^^",
"&", "^&",
"|", "^|",
"<", "^<",
">", "^>",
"%", "^%",
).Replace(commandStr)
} else {
quotedCommand = c.Quote(commandStr)
}
shellCommand := fmt.Sprintf("%s %s %s", c.Platform.Shell, c.Platform.ShellArg, quotedCommand)
return c.ExecutableFromString(shellCommand)
}
// RunCommand runs a command and just returns the error
func (c *OSCommand) RunCommand(formatString string, formatArgs ...interface{}) error {
_, err := c.RunCommandWithOutput(formatString, formatArgs...)
return err
}
// RunShellCommand runs shell commands i.e. 'sh -c <command>'. Good for when you
// need access to the shell
func (c *OSCommand) RunShellCommand(command string) error {
cmd := c.ShellCommandFromString(command)
c.LogExecCmd(cmd)
_, err := sanitisedCommandOutput(cmd.CombinedOutput())
return err
}
// FileType tells us if the file is a file, directory or other
func (c *OSCommand) FileType(path string) string {
fileInfo, err := os.Stat(path)
@ -245,19 +159,6 @@ func (c *OSCommand) FileType(path string) string {
return "file"
}
func sanitisedCommandOutput(output []byte, err error) (string, error) {
outputString := string(output)
if err != nil {
// errors like 'exit status 1' are not very useful so we'll create an error
// from the combined output
if outputString == "" {
return "", utils.WrapError(err)
}
return outputString, errors.New(outputString)
}
return outputString, nil
}
// OpenFile opens a file with the given
func (c *OSCommand) OpenFile(filename string) error {
commandTemplate := c.Config.GetUserConfig().OS.OpenCommand
@ -265,7 +166,7 @@ func (c *OSCommand) OpenFile(filename string) error {
"filename": c.Quote(filename),
}
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
err := c.RunShellCommand(command)
err := c.Run(c.NewShellCmdObjFromString(command))
return err
}
@ -278,26 +179,10 @@ func (c *OSCommand) OpenLink(link string) error {
}
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
err := c.RunShellCommand(command)
err := c.Run(c.NewShellCmdObjFromString(command))
return err
}
// PrepareSubProcess iniPrepareSubProcessrocess then tells the Gui to switch to it
// TODO: see if this needs to exist, given that ExecutableFromString does the same things
func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *exec.Cmd {
cmd := c.Command(cmdName, commandArgs...)
if cmd != nil {
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
}
c.LogExecCmd(cmd)
return cmd
}
// PrepareShellSubProcess returns the pointer to a custom command
func (c *OSCommand) PrepareShellSubProcess(command string) *exec.Cmd {
return c.PrepareSubProcess(c.Platform.Shell, c.Platform.ShellArg, command)
}
// Quote wraps a message in platform-specific quotation marks
func (c *OSCommand) Quote(message string) string {
var quote string
@ -390,24 +275,6 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
return true, nil
}
// RunPreparedCommand takes a pointer to an exec.Cmd and runs it
// this is useful if you need to give your command some environment variables
// before running it
func (c *OSCommand) RunPreparedCommand(cmd *exec.Cmd) error {
c.BeforeExecuteCmd(cmd)
c.LogExecCmd(cmd)
out, err := cmd.CombinedOutput()
outString := string(out)
c.Log.Info(outString)
if err != nil {
if len(outString) == 0 {
return err
}
return errors.New(outString)
}
return nil
}
// GetLazygitPath returns the path of the currently executed file
func (c *OSCommand) GetLazygitPath() string {
ex, err := os.Executable() // get the executable path for git to use
@ -426,7 +293,7 @@ func (c *OSCommand) PipeCommands(commandStrings ...string) error {
logCmdStr += " | "
}
logCmdStr += str
cmds[i] = c.ExecutableFromString(str)
cmds[i] = c.NewCmdObj(str).GetCmd()
}
c.LogCommand(logCmdStr, true)
@ -489,7 +356,107 @@ func Kill(cmd *exec.Cmd) error {
return cmd.Process.Kill()
}
func RunLineOutputCmd(cmd *exec.Cmd, onLine func(line string) (bool, error)) error {
func (c *OSCommand) CopyToClipboard(str string) error {
escaped := strings.Replace(str, "\n", "\\n", -1)
truncated := utils.TruncateWithEllipsis(escaped, 40)
c.LogCommand(fmt.Sprintf("Copying '%s' to clipboard", truncated), false)
return clipboard.WriteAll(str)
}
func (c *OSCommand) RemoveFile(path string) error {
c.LogCommand(fmt.Sprintf("Deleting path '%s'", path), false)
return c.removeFile(path)
}
// builders
func (c *OSCommand) NewCmdObj(cmdStr string) ICmdObj {
args := str.ToArgv(cmdStr)
cmd := c.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
return &CmdObj{
cmdStr: cmdStr,
cmd: cmd,
}
}
func (c *OSCommand) NewCmdObjFromArgs(args []string) ICmdObj {
cmd := c.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
return &CmdObj{
cmdStr: strings.Join(args, " "),
cmd: cmd,
}
}
// NewShellCmdObjFromString takes a string like `git commit` and returns an executable shell command for it
func (c *OSCommand) NewShellCmdObjFromString(commandStr string) ICmdObj {
quotedCommand := ""
// Windows does not seem to like quotes around the command
if c.Platform.OS == "windows" {
quotedCommand = strings.NewReplacer(
"^", "^^",
"&", "^&",
"|", "^|",
"<", "^<",
">", "^>",
"%", "^%",
).Replace(commandStr)
} else {
quotedCommand = c.Quote(commandStr)
}
shellCommand := fmt.Sprintf("%s %s %s", c.Platform.Shell, c.Platform.ShellArg, quotedCommand)
return c.NewCmdObj(shellCommand)
}
// TODO: pick one of NewShellCmdObjFromString2 and ShellCommandFromString to use. I'm not sure
// which one actually is better, but I suspect it's NewShellCmdObjFromString2
func (c *OSCommand) NewShellCmdObjFromString2(command string) ICmdObj {
return c.NewCmdObjFromArgs([]string{c.Platform.Shell, c.Platform.ShellArg, command})
}
// runners
type IRunner interface {
Run(cmdObj ICmdObj) error
RunWithOutput(cmdObj ICmdObj) (string, error)
RunLineOutputCmd(cmdObj ICmdObj, onLine func(line string) (bool, error)) error
}
type RunExpectation func(ICmdObj) (string, error)
type FakeRunner struct {
expectations []RunExpectation
}
func (self *RealRunner) Run(cmdObj ICmdObj) error {
}
type RealRunner struct {
c *OSCommand
}
func (self *RealRunner) Run(cmdObj ICmdObj) error {
_, err := self.RunWithOutput(cmdObj)
return err
}
func (self *RealRunner) RunWithOutput(cmdObj ICmdObj) (string, error) {
self.c.LogCmdObj(cmdObj)
output, err := sanitisedCommandOutput(cmdObj.GetCmd().CombinedOutput())
if err != nil {
self.c.Log.WithField("command", cmdObj.ToString()).Error(output)
}
return output, err
}
func (self *RealRunner) RunLineOutputCmd(cmdObj ICmdObj, onLine func(line string) (bool, error)) error {
cmd := cmdObj.GetCmd()
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return err
@ -518,42 +485,15 @@ func RunLineOutputCmd(cmd *exec.Cmd, onLine func(line string) (bool, error)) err
return nil
}
func (c *OSCommand) CopyToClipboard(str string) error {
escaped := strings.Replace(str, "\n", "\\n", -1)
truncated := utils.TruncateWithEllipsis(escaped, 40)
c.LogCommand(fmt.Sprintf("Copying '%s' to clipboard", truncated), false)
return clipboard.WriteAll(str)
}
func (c *OSCommand) RemoveFile(path string) error {
c.LogCommand(fmt.Sprintf("Deleting path '%s'", path), false)
return c.removeFile(path)
}
func (c *OSCommand) NewCmdObjFromStr(cmdStr string) ICmdObj {
args := str.ToArgv(cmdStr)
cmd := c.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
return &CmdObj{
cmdStr: cmdStr,
cmd: cmd,
}
}
func (c *OSCommand) NewCmdObjFromArgs(args []string) ICmdObj {
cmd := c.Command(args[0], args[1:]...)
return &CmdObj{
cmdStr: strings.Join(args, " "),
cmd: cmd,
}
}
func (c *OSCommand) NewCmdObj(cmd *exec.Cmd) ICmdObj {
return &CmdObj{
cmdStr: strings.Join(cmd.Args, " "),
cmd: cmd,
func sanitisedCommandOutput(output []byte, err error) (string, error) {
outputString := string(output)
if err != nil {
// errors like 'exit status 1' are not very useful so we'll create an error
// from the combined output
if outputString == "" {
return "", utils.WrapError(err)
}
return outputString, errors.New(outputString)
}
return outputString, nil
}

View File

@ -8,8 +8,8 @@ import (
"github.com/stretchr/testify/assert"
)
// TestOSCommandRunCommandWithOutput is a function.
func TestOSCommandRunCommandWithOutput(t *testing.T) {
// TestOSCommandRunWithOutput is a function.
func TestOSCommandRunWithOutput(t *testing.T) {
type scenario struct {
command string
test func(string, error)
@ -32,12 +32,13 @@ func TestOSCommandRunCommandWithOutput(t *testing.T) {
}
for _, s := range scenarios {
s.test(NewDummyOSCommand().RunCommandWithOutput(s.command))
c := NewDummyOSCommand()
s.test(NewDummyOSCommand().RunWithOutput(c.NewCmdObj(s.command)))
}
}
// TestOSCommandRunCommand is a function.
func TestOSCommandRunCommand(t *testing.T) {
// TestOSCommandRun is a function.
func TestOSCommandRun(t *testing.T) {
type scenario struct {
command string
test func(error)
@ -53,7 +54,8 @@ func TestOSCommandRunCommand(t *testing.T) {
}
for _, s := range scenarios {
s.test(NewDummyOSCommand().RunCommand(s.command))
c := NewDummyOSCommand()
s.test(c.Run(c.NewCmdObj(s.command)))
}
}

View File

@ -90,7 +90,7 @@ func (c *GitCommand) MovePatchToSelectedCommit(commits []*models.Commit, sourceC
return err
}
if err := c.OSCommand.RunPreparedCommand(cmd); err != nil {
if err := c.OSCommand.Run(cmd); err != nil {
return err
}
@ -217,7 +217,7 @@ func (c *GitCommand) PullPatchIntoNewCommit(commits []*models.Commit, commitIdx
head_message, _ := c.GetHeadCommitMessage()
new_message := fmt.Sprintf("Split from \"%s\"", head_message)
err := c.OSCommand.RunCommand(c.CommitCmdStr(new_message, ""))
err := c.OSCommand.Run(c.CommitCmdObj(new_message, ""))
if err != nil {
return err
}

View File

@ -3,17 +3,15 @@ package commands
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/mgutz/str"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
func (c *GitCommand) RewordCommit(commits []*models.Commit, index int) (*exec.Cmd, error) {
func (c *GitCommand) RewordCommit(commits []*models.Commit, index int) (oscommands.ICmdObj, error) {
todo, sha, err := c.GenerateGenericRebaseTodo(commits, index, "reword")
if err != nil {
return nil, err
@ -40,7 +38,7 @@ func (c *GitCommand) MoveCommitDown(commits []*models.Commit, index int) error {
return err
}
return c.OSCommand.RunPreparedCommand(cmd)
return c.OSCommand.Run(cmd)
}
func (c *GitCommand) InteractiveRebase(commits []*models.Commit, index int, action string) error {
@ -54,13 +52,13 @@ func (c *GitCommand) InteractiveRebase(commits []*models.Commit, index int, acti
return err
}
return c.OSCommand.RunPreparedCommand(cmd)
return c.OSCommand.Run(cmd)
}
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
// we tell git to run lazygit to edit the todo list, and we pass the client
// lazygit a todo string to write to the todo file
func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string, overrideEditor bool) (*exec.Cmd, error) {
func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string, overrideEditor bool) (oscommands.ICmdObj, error) {
ex := c.OSCommand.GetLazygitPath()
debug := "FALSE"
@ -70,9 +68,8 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty %s", baseSha)
c.Log.WithField("command", cmdStr).Info("RunCommand")
splitCmd := str.ToArgv(cmdStr)
cmd := c.OSCommand.Command(splitCmd[0], splitCmd[1:]...)
cmdObj := c.NewCmdObj(cmdStr)
gitSequenceEditor := ex
if todo == "" {
@ -81,9 +78,7 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
c.OSCommand.LogCommand(fmt.Sprintf("Creating TODO file for interactive rebase: \n\n%s", todo), false)
}
cmd.Env = os.Environ()
cmd.Env = append(
cmd.Env,
cmdObj.AddEnvVars(
"LAZYGIT_CLIENT_COMMAND=INTERACTIVE_REBASE",
"LAZYGIT_REBASE_TODO="+todo,
"DEBUG="+debug,
@ -93,10 +88,10 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
)
if overrideEditor {
cmd.Env = append(cmd.Env, "GIT_EDITOR="+ex)
cmdObj.AddEnvVars("GIT_EDITOR=" + ex)
}
return cmd, nil
return cmdObj, nil
}
func (c *GitCommand) GenerateGenericRebaseTodo(commits []*models.Commit, actionIndex int, action string) (string, string, error) {
@ -227,7 +222,7 @@ func (c *GitCommand) BeginInteractiveRebaseForCommit(commits []*models.Commit, c
return err
}
if err := c.OSCommand.RunPreparedCommand(cmd); err != nil {
if err := c.OSCommand.Run(cmd); err != nil {
return err
}
@ -241,7 +236,7 @@ func (c *GitCommand) RebaseBranch(branchName string) error {
return err
}
return c.OSCommand.RunPreparedCommand(cmd)
return c.OSCommand.Run(cmd)
}
// GenericMerge takes a commandType of "merge" or "rebase" and a command of "abort", "skip" or "continue"
@ -276,14 +271,13 @@ func (c *GitCommand) GenericMergeOrRebaseAction(commandType string, command stri
}
func (c *GitCommand) runSkipEditorCommand(command string) error {
cmd := c.OSCommand.ExecutableFromString(command)
cmdObj := c.OSCommand.NewCmdObj(command)
lazyGitPath := c.OSCommand.GetLazygitPath()
cmd.Env = append(
cmd.Env,
cmdObj.AddEnvVars(
"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY",
"GIT_EDITOR="+lazyGitPath,
"EDITOR="+lazyGitPath,
"VISUAL="+lazyGitPath,
)
return c.OSCommand.RunExecutable(cmd)
return c.OSCommand.Run(cmdObj)
}

View File

@ -5,6 +5,7 @@ import (
"regexp"
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/test"
"github.com/stretchr/testify/assert"
)
@ -62,31 +63,31 @@ func TestGitCommandRebaseBranch(t *testing.T) {
func TestGitCommandSkipEditorCommand(t *testing.T) {
cmd := NewDummyGitCommand()
cmd.OSCommand.SetBeforeExecuteCmd(func(cmd *exec.Cmd) {
cmd.OSCommand.SetBeforeExecuteCmd(func(cmdObj oscommands.ICmdObj) {
test.AssertContainsMatch(
t,
cmd.Env,
cmdObj.GetEnvVars(),
regexp.MustCompile("^VISUAL="),
"expected VISUAL to be set for a non-interactive external command",
)
test.AssertContainsMatch(
t,
cmd.Env,
cmdObj.GetEnvVars(),
regexp.MustCompile("^EDITOR="),
"expected EDITOR to be set for a non-interactive external command",
)
test.AssertContainsMatch(
t,
cmd.Env,
cmdObj.GetEnvVars(),
regexp.MustCompile("^GIT_EDITOR="),
"expected GIT_EDITOR to be set for a non-interactive external command",
)
test.AssertContainsMatch(
t,
cmd.Env,
cmdObj.GetEnvVars(),
regexp.MustCompile("^LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY$"),
"expected LAZYGIT_CLIENT_COMMAND to be set for a non-interactive external command",
)

View File

@ -7,24 +7,24 @@ import (
)
func (c *GitCommand) AddRemote(name string, url string) error {
return c.RunCommand("git remote add %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(url))
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote add %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(url))))
}
func (c *GitCommand) RemoveRemote(name string) error {
return c.RunCommand("git remote remove %s", c.OSCommand.Quote(name))
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote remove %s", c.OSCommand.Quote(name))))
}
func (c *GitCommand) RenameRemote(oldRemoteName string, newRemoteName string) error {
return c.RunCommand("git remote rename %s %s", c.OSCommand.Quote(oldRemoteName), c.OSCommand.Quote(newRemoteName))
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote rename %s %s", c.OSCommand.Quote(oldRemoteName), c.OSCommand.Quote(newRemoteName))))
}
func (c *GitCommand) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
return c.RunCommand("git remote set-url %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(updatedUrl))
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote set-url %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(updatedUrl))))
}
func (c *GitCommand) DeleteRemoteBranch(remoteName string, branchName string, promptUserForCredential func(string) string) error {
command := fmt.Sprintf("git push %s --delete %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(branchName))
cmdObj := c.NewCmdObjFromStr(command)
cmdObj := c.NewCmdObj(command)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
@ -34,10 +34,10 @@ func (c *GitCommand) DetectUnamePass(cmdObj oscommands.ICmdObj, promptUserForCre
// CheckRemoteBranchExists Returns remote branch
func (c *GitCommand) CheckRemoteBranchExists(branchName string) bool {
_, err := c.OSCommand.RunCommandWithOutput(
"git show-ref --verify -- refs/remotes/origin/%s",
c.OSCommand.Quote(branchName),
)
_, err := c.RunWithOutput(c.NewCmdObj(
fmt.Sprintf("git show-ref --verify -- refs/remotes/origin/%s",
c.OSCommand.Quote(branchName),
)))
return err == nil
}

View File

@ -4,13 +4,13 @@ import "fmt"
// StashDo modify stash
func (c *GitCommand) StashDo(index int, method string) error {
return c.RunCommand("git stash %s stash@{%d}", method, index)
return c.Run(c.NewCmdObj(fmt.Sprintf("git stash %s stash@{%d}", method, index)))
}
// StashSave save stash
// TODO: before calling this, check if there is anything to save
func (c *GitCommand) StashSave(message string) error {
return c.RunCommand("git stash save %s", c.OSCommand.Quote(message))
return c.Run(c.NewCmdObj("git stash save " + c.OSCommand.Quote(message)))
}
// GetStashEntryDiff stash diff
@ -22,7 +22,7 @@ func (c *GitCommand) ShowStashEntryCmdStr(index int) string {
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
func (c *GitCommand) StashSaveStagedChanges(message string) error {
// wrap in 'writing', which uses a mutex
if err := c.RunCommand("git stash --keep-index"); err != nil {
if err := c.Run(c.NewCmdObj("git stash --keep-index")); err != nil {
return err
}
@ -30,7 +30,7 @@ func (c *GitCommand) StashSaveStagedChanges(message string) error {
return err
}
if err := c.RunCommand("git stash apply stash@{1}"); err != nil {
if err := c.Run(c.NewCmdObj("git stash apply stash@{1}")); err != nil {
return err
}
@ -38,7 +38,7 @@ func (c *GitCommand) StashSaveStagedChanges(message string) error {
return err
}
if err := c.RunCommand("git stash drop stash@{1}"); err != nil {
if err := c.Run(c.NewCmdObj("git stash drop stash@{1}")); err != nil {
return err
}

View File

@ -2,12 +2,14 @@ package commands
import (
"bufio"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
// .gitmodules looks like this:
@ -69,28 +71,28 @@ func (c *GitCommand) SubmoduleStash(submodule *models.SubmoduleConfig) error {
return nil
}
return c.RunCommand("git -C %s stash --include-untracked", c.OSCommand.Quote(submodule.Path))
return c.Run(c.NewCmdObj("git -C " + c.OSCommand.Quote(submodule.Path) + " stash --include-untracked"))
}
func (c *GitCommand) SubmoduleReset(submodule *models.SubmoduleConfig) error {
return c.RunCommand("git submodule update --init --force -- %s", c.OSCommand.Quote(submodule.Path))
return c.Run(c.NewCmdObj("git submodule update --init --force -- " + c.OSCommand.Quote(submodule.Path)))
}
func (c *GitCommand) SubmoduleUpdateAll() error {
// not doing an --init here because the user probably doesn't want that
return c.RunCommand("git submodule update --force")
return c.Run(c.NewCmdObj("git submodule update --force"))
}
func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
if err := c.RunCommand("git submodule deinit --force -- %s", c.OSCommand.Quote(submodule.Path)); err != nil {
if err := c.Run(c.NewCmdObj("git submodule deinit --force -- " + c.OSCommand.Quote(submodule.Path))); err != nil {
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
if err := c.RunCommand("git config --file .gitmodules --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
if err := c.Run(c.NewCmdObj("git config --file .gitmodules --remove-section submodule." + c.OSCommand.Quote(submodule.Name))); err != nil {
return err
}
if err := c.RunCommand("git config --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
if err := c.Run(c.NewCmdObj("git config --remove-section submodule." + c.OSCommand.Quote(submodule.Name))); err != nil {
return err
}
@ -100,7 +102,7 @@ func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
}
}
if err := c.RunCommand("git rm --force -r %s", submodule.Path); err != nil {
if err := c.Run(c.NewCmdObj("git rm --force -r " + submodule.Path)); err != nil {
// if the directory isn't there then that's fine
c.Log.Error(err)
}
@ -109,21 +111,23 @@ func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
}
func (c *GitCommand) SubmoduleAdd(name string, path string, url string) error {
return c.OSCommand.RunCommand(
"git submodule add --force --name %s -- %s %s ",
c.OSCommand.Quote(name),
c.OSCommand.Quote(url),
c.OSCommand.Quote(path),
)
return c.OSCommand.Run(
c.OSCommand.NewCmdObj(
fmt.Sprintf(
"git submodule add --force --name %s -- %s %s ",
c.OSCommand.Quote(name),
c.OSCommand.Quote(url),
c.OSCommand.Quote(path),
)))
}
func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string) error {
// the set-url command is only for later git versions so we're doing it manually here
if err := c.RunCommand("git config --file .gitmodules submodule.%s.url %s", c.OSCommand.Quote(name), c.OSCommand.Quote(newUrl)); err != nil {
if err := c.Run(c.NewCmdObj("git config --file .gitmodules submodule." + c.OSCommand.Quote(name) + ".url " + c.OSCommand.Quote(newUrl))); err != nil {
return err
}
if err := c.RunCommand("git submodule sync -- %s", c.OSCommand.Quote(path)); err != nil {
if err := c.Run(c.NewCmdObj("git submodule sync -- " + c.OSCommand.Quote(path))); err != nil {
return err
}
@ -131,27 +135,27 @@ func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string)
}
func (c *GitCommand) SubmoduleInit(path string) error {
return c.RunCommand("git submodule init -- %s", c.OSCommand.Quote(path))
return c.Run(c.NewCmdObj("git submodule init -- " + c.OSCommand.Quote(path)))
}
func (c *GitCommand) SubmoduleUpdate(path string) error {
return c.RunCommand("git submodule update --init -- %s", c.OSCommand.Quote(path))
return c.Run(c.NewCmdObj("git submodule update --init -- " + c.OSCommand.Quote(path)))
}
func (c *GitCommand) SubmoduleBulkInitCmdStr() string {
return "git submodule init"
func (c *GitCommand) SubmoduleBulkInitCmdObj() oscommands.ICmdObj {
return c.NewCmdObj("git submodule init")
}
func (c *GitCommand) SubmoduleBulkUpdateCmdStr() string {
return "git submodule update"
func (c *GitCommand) SubmoduleBulkUpdateCmdObj() oscommands.ICmdObj {
return c.NewCmdObj("git submodule update")
}
func (c *GitCommand) SubmoduleForceBulkUpdateCmdStr() string {
return "git submodule update --force"
func (c *GitCommand) SubmoduleForceBulkUpdateCmdObj() oscommands.ICmdObj {
return c.NewCmdObj("git submodule update --force")
}
func (c *GitCommand) SubmoduleBulkDeinitCmdStr() string {
return "git submodule deinit --all --force"
func (c *GitCommand) SubmoduleBulkDeinitCmdObj() oscommands.ICmdObj {
return c.NewCmdObj("git submodule deinit --all --force")
}
func (c *GitCommand) ResetSubmodules(submodules []*models.SubmoduleConfig) error {

View File

@ -37,7 +37,7 @@ func (c *GitCommand) Push(opts PushOpts) error {
cmdStr += " " + c.OSCommand.Quote(opts.UpstreamBranch)
}
cmdObj := c.NewCmdObjFromStr(cmdStr)
cmdObj := c.NewCmdObj(cmdStr)
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
}
@ -58,7 +58,7 @@ func (c *GitCommand) Fetch(opts FetchOptions) error {
cmdStr = fmt.Sprintf("%s %s", cmdStr, c.OSCommand.Quote(opts.BranchName))
}
cmdObj := c.NewCmdObjFromStr(cmdStr)
cmdObj := c.NewCmdObj(cmdStr)
return c.DetectUnamePass(cmdObj, func(question string) string {
if opts.PromptUserForCredential != nil {
return opts.PromptUserForCredential(question)
@ -94,18 +94,18 @@ func (c *GitCommand) Pull(opts PullOptions) error {
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
cmdObj := c.NewCmdObjFromStr(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:")
cmdObj := c.NewCmdObj(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:")
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
}
func (c *GitCommand) FastForward(branchName string, remoteName string, remoteBranchName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git fetch %s %s:%s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
cmdObj := c.NewCmdObj(cmdStr)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
func (c *GitCommand) FetchRemote(remoteName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git fetch %s", c.OSCommand.Quote(remoteName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
cmdObj := c.NewCmdObj(cmdStr)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}

View File

@ -5,7 +5,7 @@ import (
)
func (c *GitCommand) CreateLightweightTag(tagName string, commitSha string) error {
return c.RunCommand("git tag -- %s %s", c.OSCommand.Quote(tagName), commitSha)
return c.Run(c.NewCmdObj(fmt.Sprintf("git tag -- %s %s", c.OSCommand.Quote(tagName), commitSha)))
}
func (c *GitCommand) CreateAnnotatedTag(tagName, commitSha, msg string) error {
@ -13,11 +13,11 @@ func (c *GitCommand) CreateAnnotatedTag(tagName, commitSha, msg string) error {
}
func (c *GitCommand) DeleteTag(tagName string) error {
return c.RunCommand("git tag -d %s", c.OSCommand.Quote(tagName))
return c.Run(c.NewCmdObj(fmt.Sprintf("git tag -d %s", c.OSCommand.Quote(tagName))))
}
func (c *GitCommand) PushTag(remoteName string, tagName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git push %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(tagName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
cmdObj := c.NewCmdObj(cmdStr)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}

View File

@ -32,11 +32,9 @@ func (gui *Gui) branchesRenderToMain() error {
if branch == nil {
task = NewRenderStringTask(gui.Tr.NoBranchesThisRepo)
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
)
cmdObj := gui.GitCommand.GetBranchGraphCmdObj(branch.Name)
task = NewRunPtyTask(cmd)
task = NewRunPtyTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -45,10 +45,8 @@ func (gui *Gui) commitFilesRenderToMain() error {
to := gui.State.CommitFileManager.GetParent()
from, reverse := gui.getFromAndReverseArgsForDiff(to)
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowFileDiffCmdStr(from, to, reverse, node.GetPath(), false),
)
task := NewRunPtyTask(cmd)
cmdObj := gui.GitCommand.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false)
task := NewRunPtyTask(cmdObj.GetCmd())
return gui.refreshMainViews(refreshMainOpts{
main: &viewUpdateOpts{

View File

@ -24,10 +24,11 @@ func (gui *Gui) handleCommitConfirm() error {
flags = append(flags, "--signoff")
}
cmdStr := gui.GitCommand.CommitCmdStr(message, strings.Join(flags, " "))
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdStr, gui.Tr.Spans.Commit, true))
cmdObj := gui.GitCommand.CommitCmdObj(message, strings.Join(flags, " "))
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdObj.ToString(), gui.Tr.Spans.Commit, true))
_ = gui.returnFromContext()
return gui.withGpgHandling(cmdStr, gui.Tr.CommittingStatus, func() error {
return gui.withGpgHandling(cmdObj, gui.Tr.CommittingStatus, func() error {
gui.Views.CommitMessage.ClearTextArea()
return nil
})

View File

@ -46,10 +46,8 @@ func (gui *Gui) branchCommitsRenderToMain() error {
if commit == nil {
task = NewRenderStringTask(gui.Tr.NoCommitsThisBranch)
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.GetPath()),
)
task = NewRunPtyTask(cmd)
cmdObj := gui.GitCommand.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath())
task = NewRunPtyTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -203,7 +203,7 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
}
// Run and save output
message, err := gui.GitCommand.RunCommandWithOutput(cmdStr)
message, err := gui.GitCommand.RunWithOutput(gui.GitCommand.NewCmdObj(cmdStr))
if err != nil {
return gui.surfaceError(err)
}
@ -244,7 +244,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
}
if customCommand.Subprocess {
return gui.runSubprocessWithSuspenseAndRefresh(gui.OSCommand.PrepareShellSubProcess(cmdStr))
return gui.runSubprocessWithSuspenseAndRefresh(gui.OSCommand.NewShellCmdObjFromString2(cmdStr))
}
loadingText := customCommand.LoadingText
@ -252,7 +252,8 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
loadingText = gui.Tr.LcRunningCustomCommandStatus
}
return gui.WithWaitingStatus(loadingText, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.CustomCommand).RunShellCommand(cmdStr); err != nil {
cmdObj := gui.OSCommand.NewShellCmdObjFromString(cmdStr)
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.CustomCommand).Run(cmdObj); err != nil {
return gui.surfaceError(err)
}
return gui.refreshSidePanels(refreshOptions{})

View File

@ -13,10 +13,10 @@ func (gui *Gui) exitDiffMode() error {
}
func (gui *Gui) renderDiff() error {
cmd := gui.OSCommand.ExecutableFromString(
cmdObj := gui.OSCommand.NewCmdObj(
fmt.Sprintf("git diff --submodule --no-ext-diff --color %s", gui.diffStr()),
)
task := NewRunPtyTask(cmd)
task := NewRunPtyTask(cmdObj.GetCmd())
return gui.refreshMainViews(refreshMainOpts{
main: &viewUpdateOpts{

View File

@ -58,22 +58,20 @@ func (gui *Gui) filesRenderToMain() error {
return gui.refreshMergePanelWithLock()
}
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
cmdObj := gui.GitCommand.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
title: gui.Tr.UnstagedChanges,
task: NewRunPtyTask(cmd),
task: NewRunPtyTask(cmdObj.GetCmd()),
}}
if node.GetHasUnstagedChanges() {
if node.GetHasStagedChanges() {
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
cmdObj := gui.GitCommand.WorktreeFileDiffCmdObj(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
refreshOpts.secondary = &viewUpdateOpts{
title: gui.Tr.StagedChanges,
task: NewRunPtyTask(cmd),
task: NewRunPtyTask(cmdObj.GetCmd()),
}
}
} else {
@ -440,9 +438,9 @@ func (gui *Gui) handleAmendCommitPress() error {
title: strings.Title(gui.Tr.AmendLastCommit),
prompt: gui.Tr.SureToAmend,
handleConfirm: func() error {
cmdStr := gui.GitCommand.AmendHeadCmdStr()
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdStr, gui.Tr.Spans.AmendCommit, true))
return gui.withGpgHandling(cmdStr, gui.Tr.AmendingStatus, nil)
cmdObj := gui.GitCommand.AmendHeadCmdObj()
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdObj.ToString(), gui.Tr.Spans.AmendCommit, true))
return gui.withGpgHandling(cmdObj, gui.Tr.AmendingStatus, nil)
},
})
}
@ -464,8 +462,10 @@ func (gui *Gui) handleCommitEditorPress() error {
args = append(args, "--signoff")
}
cmdStr := "git " + strings.Join(args, " ")
return gui.runSubprocessWithSuspenseAndRefresh(
gui.OSCommand.WithSpan(gui.Tr.Spans.Commit).PrepareSubProcess("git", args...),
gui.GitCommand.WithSpan(gui.Tr.Spans.Commit).NewCmdObjWithLog(cmdStr),
)
}
@ -511,7 +511,7 @@ func (gui *Gui) editFileAtLine(filename string, lineNumber int) error {
}
return gui.runSubprocessWithSuspenseAndRefresh(
gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).ShellCommandFromString(cmdStr),
gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).NewShellCmdObjFromString(cmdStr),
)
}
@ -923,7 +923,7 @@ func (gui *Gui) handleCustomCommand() error {
gui.OnRunCommand(oscommands.NewCmdLogEntry(command, gui.Tr.Spans.CustomCommand, true))
return gui.runSubprocessWithSuspenseAndRefresh(
gui.OSCommand.PrepareShellSubProcess(command),
gui.OSCommand.NewShellCmdObjFromString2(command),
)
},
})
@ -1004,7 +1004,7 @@ func (gui *Gui) handleOpenMergeTool() error {
prompt: gui.Tr.MergeToolPrompt,
handleConfirm: func() error {
return gui.runSubprocessWithSuspenseAndRefresh(
gui.OSCommand.ExecutableFromString(gui.GitCommand.OpenMergeToolCmd()),
gui.GitCommand.OpenMergeToolCmdObj(),
)
},
})

View File

@ -32,7 +32,7 @@ func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) err
}
return gui.runSubprocessWithSuspenseAndRefresh(
gui.OSCommand.WithSpan(gui.Tr.Spans.GitFlowFinish).PrepareSubProcess("git", "flow", branchType, "finish", suffix),
gui.GitCommand.WithSpan(gui.Tr.Spans.GitFlowFinish).NewCmdObjWithLog("git flow " + branchType + " finish " + suffix),
)
}
@ -43,7 +43,7 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
}
// get config
gitFlowConfig, err := gui.GitCommand.RunCommandWithOutput("git config --local --get-regexp gitflow")
gitFlowConfig, err := gui.GitCommand.RunWithOutput(gui.GitCommand.NewCmdObj("git config --local --get-regexp gitflow"))
if err != nil {
return gui.createErrorPanel("You need to install git-flow and enable it in this repo to use git-flow features")
}
@ -56,7 +56,7 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
title: title,
handleConfirm: func(name string) error {
return gui.runSubprocessWithSuspenseAndRefresh(
gui.OSCommand.WithSpan(gui.Tr.Spans.GitFlowStart).PrepareSubProcess("git", "flow", branchType, "start", name),
gui.GitCommand.WithSpan(gui.Tr.Spans.GitFlowStart).NewCmdObjWithLog("git flow " + branchType + " start " + name),
)
},
})

View File

@ -3,6 +3,7 @@ package gui
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
@ -10,12 +11,11 @@ import (
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
// fix this bug, or just stop running subprocesses from within there, given that
// we don't need to see a loading status if we're in a subprocess.
func (gui *Gui) withGpgHandling(cmdStr string, waitingStatus string, onSuccess func() error) error {
// TODO: work out if we actually need to use a shell command here
func (gui *Gui) withGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
useSubprocess := gui.GitCommand.UsingGpg()
if useSubprocess {
// Need to remember why we use the shell for the subprocess but not in the other case
// Maybe there's no good reason
success, err := gui.runSubprocessWithSuspense(gui.OSCommand.ShellCommandFromString(cmdStr))
success, err := gui.runSubprocessWithSuspense(gui.OSCommand.NewShellCmdObjFromString(cmdObj.ToString()))
if success && onSuccess != nil {
if err := onSuccess(); err != nil {
return err
@ -27,15 +27,16 @@ func (gui *Gui) withGpgHandling(cmdStr string, waitingStatus string, onSuccess f
return err
} else {
return gui.RunAndStream(cmdStr, waitingStatus, onSuccess)
return gui.RunAndStream(cmdObj, waitingStatus, onSuccess)
}
}
func (gui *Gui) RunAndStream(cmdStr string, waitingStatus string, onSuccess func() error) error {
func (gui *Gui) RunAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
return gui.WithWaitingStatus(waitingStatus, func() error {
cmd := gui.OSCommand.ShellCommandFromString(cmdStr)
cmd.Env = append(cmd.Env, "TERM=dumb")
cmdObj := gui.OSCommand.NewShellCmdObjFromString(cmdObj.ToString())
cmdObj.AddEnvVars("TERM=dumb")
cmdWriter := gui.getCmdWriter()
cmd := cmdObj.GetCmd()
cmd.Stdout = cmdWriter
cmd.Stderr = cmdWriter

View File

@ -7,7 +7,6 @@ import (
"os"
"sync"
"os/exec"
"strings"
"time"
@ -578,7 +577,7 @@ func (gui *Gui) RunAndHandleError() error {
}
// returns whether command exited without error or not
func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess *exec.Cmd) error {
func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess oscommands.ICmdObj) error {
_, err := gui.runSubprocessWithSuspense(subprocess)
if err != nil {
return err
@ -592,7 +591,7 @@ func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess *exec.Cmd) error
}
// returns whether command exited without error or not
func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) (bool, error) {
func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool, error) {
gui.Mutexes.SubprocessMutex.Lock()
defer gui.Mutexes.SubprocessMutex.Unlock()
@ -621,7 +620,8 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) (bool, error) {
return cmdErr == nil, gui.surfaceError(cmdErr)
}
func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
func (gui *Gui) runSubprocess(cmdObj oscommands.ICmdObj) error {
subprocess := cmdObj.GetCmd()
subprocess.Stdout = os.Stdout
subprocess.Stderr = os.Stdout
subprocess.Stdin = os.Stdin

View File

@ -58,7 +58,7 @@ func (gui *Gui) genericMergeCommand(command string) error {
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
if status == commands.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && gui.Config.GetUserConfig().Git.Merging.ManualCommit {
sub := gitCommand.OSCommand.PrepareSubProcess("git", commandType, fmt.Sprintf("--%s", command))
sub := gitCommand.NewCmdObj("git " + commandType + " --" + command)
if sub != nil {
return gui.runSubprocessWithSuspenseAndRefresh(sub)
}

View File

@ -38,10 +38,10 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
}
func (gui *Gui) handleShowAllBranchLogs() error {
cmd := gui.OSCommand.ExecutableFromString(
cmdObj := gui.OSCommand.NewCmdObj(
gui.Config.GetUserConfig().Git.AllBranchesLogCmd,
)
task := NewRunPtyTask(cmd)
task := NewRunPtyTask(cmdObj.GetCmd())
return gui.refreshMainViews(refreshMainOpts{
main: &viewUpdateOpts{

View File

@ -22,11 +22,9 @@ func (gui *Gui) reflogCommitsRenderToMain() error {
if commit == nil {
task = NewRenderStringTask("No reflog history")
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.GetPath()),
)
cmdObj := gui.GitCommand.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath())
task = NewRunPtyTask(cmd)
task = NewRunPtyTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -24,10 +24,8 @@ func (gui *Gui) remoteBranchesRenderToMain() error {
if remoteBranch == nil {
task = NewRenderStringTask("No branches for this remote")
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(remoteBranch.FullName()),
)
task = NewRunCommandTask(cmd)
cmdObj := gui.GitCommand.GetBranchGraphCmdObj(remoteBranch.FullName())
task = NewRunCommandTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -3,12 +3,11 @@ package gui
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) resetToRef(ref string, strength string, span string, options oscommands.RunCommandOptions) error {
if err := gui.GitCommand.WithSpan(span).ResetToCommit(ref, strength, options); err != nil {
func (gui *Gui) resetToRef(ref string, strength string, span string, envVars []string) error {
if err := gui.GitCommand.WithSpan(span).ResetToCommit(ref, strength, envVars); err != nil {
return gui.surfaceError(err)
}
@ -39,7 +38,7 @@ func (gui *Gui) createResetMenu(ref string) error {
style.FgRed.Sprintf("reset --%s %s", strength, ref),
},
onPress: func() error {
return gui.resetToRef(ref, strength, "Reset", oscommands.RunCommandOptions{})
return gui.resetToRef(ref, strength, "Reset", []string{})
},
}
}

View File

@ -22,10 +22,10 @@ func (gui *Gui) stashRenderToMain() error {
if stashEntry == nil {
task = NewRenderStringTask(gui.Tr.NoStashEntries)
} else {
cmd := gui.OSCommand.ExecutableFromString(
cmdObj := gui.OSCommand.NewCmdObj(
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
)
task = NewRunPtyTask(cmd)
task = NewRunPtyTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -23,11 +23,9 @@ func (gui *Gui) subCommitsRenderToMain() error {
if commit == nil {
task = NewRenderStringTask("No commits")
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.GetPath()),
)
cmdObj := gui.GitCommand.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath())
task = NewRunPtyTask(cmd)
task = NewRunPtyTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -36,9 +36,8 @@ func (gui *Gui) submodulesRenderToMain() error {
if file == nil {
task = NewRenderStringTask(prefix)
} else {
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.State.IgnoreWhitespaceInDiffView)
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
task = NewRunCommandTaskWithPrefix(cmd, prefix)
cmdObj := gui.GitCommand.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.State.IgnoreWhitespaceInDiffView)
task = NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
}
}
@ -212,10 +211,10 @@ func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) er
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
menuItems := []*menuItem{
{
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.GitCommand.SubmoduleBulkInitCmdStr())},
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.GitCommand.SubmoduleBulkInitCmdObj().ToString())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkInitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkInitCmdStr()); err != nil {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkInitialiseSubmodules).Run(gui.GitCommand.SubmoduleBulkInitCmdObj()); err != nil {
return gui.surfaceError(err)
}
@ -224,10 +223,10 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
},
},
{
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.GitCommand.SubmoduleBulkUpdateCmdStr())},
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.GitCommand.SubmoduleBulkUpdateCmdObj().ToString())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkUpdateSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkUpdateCmdStr()); err != nil {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkUpdateSubmodules).Run(gui.GitCommand.SubmoduleBulkUpdateCmdObj()); err != nil {
return gui.surfaceError(err)
}
@ -236,7 +235,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
},
},
{
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr())},
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdObj().ToString())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.BulkStashAndResetSubmodules).ResetSubmodules(gui.State.Submodules); err != nil {
@ -248,10 +247,10 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
},
},
{
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.GitCommand.SubmoduleBulkDeinitCmdStr())},
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.GitCommand.SubmoduleBulkDeinitCmdObj().ToString())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkDeinitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkDeinitCmdStr()); err != nil {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkDeinitialiseSubmodules).Run(gui.GitCommand.SubmoduleBulkDeinitCmdObj()); err != nil {
return gui.surfaceError(err)
}

View File

@ -25,10 +25,8 @@ func (gui *Gui) tagsRenderToMain() error {
if tag == nil {
task = NewRenderStringTask("No tags")
} else {
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(tag.Name),
)
task = NewRunCommandTask(cmd)
cmdObj := gui.GitCommand.GetBranchGraphCmdObj(tag.Name)
task = NewRunCommandTask(cmdObj.GetCmd())
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -2,7 +2,6 @@ package gui
import (
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -169,7 +168,7 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
gitCommand := gui.GitCommand.WithSpan(options.span)
reset := func() error {
if err := gui.resetToRef(commitSha, "hard", options.span, oscommands.RunCommandOptions{EnvVars: options.EnvVars}); err != nil {
if err := gui.resetToRef(commitSha, "hard", options.span, options.EnvVars); err != nil {
return gui.surfaceError(err)
}
return nil

View File

@ -45,7 +45,7 @@ func RunTests(
testDir := filepath.Join(rootDir, "test", "integration")
osCommand := oscommands.NewDummyOSCommand()
err = osCommand.RunCommand("go build -o %s", tempLazygitPath())
err = osCommand.Run(osCommand.NewCmdObj("go build -o " + tempLazygitPath()))
if err != nil {
return err
}
@ -216,11 +216,10 @@ func GetRootDirectory() string {
}
func createFixture(testPath, actualDir string) error {
osCommand := oscommands.NewDummyOSCommand()
bashScriptPath := filepath.Join(testPath, "setup.sh")
cmd := secureexec.Command("bash", bashScriptPath, actualDir)
if err := osCommand.RunExecutable(cmd); err != nil {
if _, err := cmd.CombinedOutput(); err != nil {
return err
}
@ -320,7 +319,7 @@ func generateSnapshot(dir string) (string, error) {
for _, cmdStr := range cmdStrs {
// ignoring error for now. If there's an error it could be that there are no results
output, _ := osCommand.RunCommandWithOutput(cmdStr)
output, _ := osCommand.RunWithOutput(osCommand.NewCmdObj(cmdStr))
snapshot += output + "\n"
}
@ -429,22 +428,16 @@ func getLazygitCommand(testPath string, rootDir string, record bool, speed float
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualDir, extraCmdArgs)
cmd := osCommand.ExecutableFromString(cmdStr)
cmd.Env = append(cmd.Env, fmt.Sprintf("SPEED=%f", speed))
cmdObj := osCommand.NewCmdObj(cmdStr)
cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed))
if record {
cmd.Env = append(
cmd.Env,
fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath),
)
cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath))
} else {
cmd.Env = append(
cmd.Env,
fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath),
)
cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath))
}
return cmd, nil
return cmdObj.GetCmd(), nil
}
func folderExists(path string) bool {

View File

@ -300,7 +300,8 @@ func (u *Updater) downloadAndInstall(rawUrl string) error {
}
u.Log.Info("untarring tarball/unzipping zip file")
if err := u.OSCommand.RunCommand("tar -zxf %s %s", u.OSCommand.Quote(zipPath), "lazygit"); err != nil {
cmdObj := u.OSCommand.NewCmdObj(fmt.Sprintf("tar -zxf %s %s", u.OSCommand.Quote(zipPath), "lazygit"))
if err := u.OSCommand.Run(cmdObj); err != nil {
return err
}