1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-15 00:15:32 +02:00

Construct arg vector manually rather than parse string

By constructing an arg vector manually, we no longer need to quote arguments

Mandate that args must be passed when building a command

Now you need to provide an args array when building a command.
There are a handful of places where we need to deal with a string,
such as with user-defined custom commands, and for those we now require
that at the callsite they use str.ToArgv to do that. I don't want
to provide a method out of the box for it because I want to discourage its
use.

For some reason we were invoking a command through a shell when amending a
commit, and I don't believe we needed to do that as there was nothing user-
supplied about the command. So I've switched to using a regular command out-
side the shell there
This commit is contained in:
Jesse Duffield
2023-05-21 17:00:29 +10:00
parent 70e473b25d
commit 63dc07fded
221 changed files with 1050 additions and 885 deletions

View File

@ -97,15 +97,15 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
}
func (self *BisectCommands) Reset() error {
cmdStr := NewGitCmd("bisect").Arg("reset").ToString()
cmdArgs := NewGitCmd("bisect").Arg("reset").ToArgv()
return self.cmd.New(cmdStr).StreamOutput().Run()
return self.cmd.New(cmdArgs).StreamOutput().Run()
}
func (self *BisectCommands) Mark(ref string, term string) error {
cmdStr := NewGitCmd("bisect").Arg(term, ref).ToString()
cmdArgs := NewGitCmd("bisect").Arg(term, ref).ToArgv()
return self.cmd.New(cmdStr).
return self.cmd.New(cmdArgs).
IgnoreEmptyError().
StreamOutput().
Run()
@ -116,9 +116,9 @@ func (self *BisectCommands) Skip(ref string) error {
}
func (self *BisectCommands) Start() error {
cmdStr := NewGitCmd("bisect").Arg("start").ToString()
cmdArgs := NewGitCmd("bisect").Arg("start").ToArgv()
return self.cmd.New(cmdStr).StreamOutput().Run()
return self.cmd.New(cmdArgs).StreamOutput().Run()
}
// tells us whether we've found our problem commit(s). We return a string slice of
@ -140,8 +140,8 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
done := false
candidates := []string{}
cmdStr := NewGitCmd("rev-list").Arg(newSha).ToString()
err := self.cmd.New(cmdStr).RunAndProcessLines(func(line string) (bool, error) {
cmdArgs := NewGitCmd("rev-list").Arg(newSha).ToArgv()
err := self.cmd.New(cmdArgs).RunAndProcessLines(func(line string) (bool, error) {
sha := strings.TrimSpace(line)
if status, ok := info.statusMap[sha]; ok {
@ -171,11 +171,11 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
// bisecting is actually a descendant of our current bisect commit. If it's not, we need to
// render the commits from the bad commit.
func (self *BisectCommands) ReachableFromStart(bisectInfo *BisectInfo) bool {
cmdStr := NewGitCmd("merge-base").
cmdArgs := NewGitCmd("merge-base").
Arg("--is-ancestor", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()).
ToString()
ToArgv()
err := self.cmd.New(cmdStr).DontLog().Run()
err := self.cmd.New(cmdArgs).DontLog().Run()
return err == nil
}

View File

@ -6,6 +6,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/mgutz/str"
)
type BranchCommands struct {
@ -20,11 +21,11 @@ func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
// New creates a new branch
func (self *BranchCommands) New(name string, base string) error {
cmdStr := NewGitCmd("checkout").
Arg("-b", self.cmd.Quote(name), self.cmd.Quote(base)).
ToString()
cmdArgs := NewGitCmd("checkout").
Arg("-b", name, base).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// CurrentBranchInfo get the current branch information.
@ -32,7 +33,7 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
branchName, err := self.cmd.New(
NewGitCmd("symbolic-ref").
Arg("--short", "HEAD").
ToString(),
ToArgv(),
).DontLog().RunWithOutput()
if err == nil && branchName != "HEAD\n" {
trimmedBranchName := strings.TrimSpace(branchName)
@ -44,8 +45,8 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
}
output, err := self.cmd.New(
NewGitCmd("branch").
Arg("--points-at=HEAD", "--format=\"%(HEAD)%00%(objectname)%00%(refname)\"").
ToString(),
Arg("--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)").
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return BranchInfo{}, err
@ -71,12 +72,12 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
// Delete delete branch
func (self *BranchCommands) Delete(branch string, force bool) error {
cmdStr := NewGitCmd("branch").
cmdArgs := NewGitCmd("branch").
ArgIfElse(force, "-D", "-d").
Arg(self.cmd.Quote(branch)).
ToString()
Arg(branch).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
@ -86,12 +87,12 @@ type CheckoutOptions struct {
}
func (self *BranchCommands) Checkout(branch string, options CheckoutOptions) error {
cmdStr := NewGitCmd("checkout").
cmdArgs := NewGitCmd("checkout").
ArgIf(options.Force, "--force").
Arg(self.cmd.Quote(branch)).
ToString()
Arg(branch).
ToArgv()
return self.cmd.New(cmdStr).
return self.cmd.New(cmdArgs).
// 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").
@ -111,31 +112,34 @@ func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj
templateValues := map[string]string{
"branchName": self.cmd.Quote(branchName),
}
return self.cmd.New(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)).DontLog()
resolvedTemplate := utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)
return self.cmd.New(str.ToArgv(resolvedTemplate)).DontLog()
}
func (self *BranchCommands) SetCurrentBranchUpstream(remoteName string, remoteBranchName string) error {
cmdStr := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).
ToString()
cmdArgs := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", remoteName, remoteBranchName)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) SetUpstream(remoteName string, remoteBranchName string, branchName string) error {
cmdStr := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).
Arg(self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", remoteName, remoteBranchName)).
Arg(branchName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) UnsetUpstream(branchName string) error {
cmdStr := NewGitCmd("branch").Arg("--unset-upstream", self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("branch").Arg("--unset-upstream", branchName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
@ -161,37 +165,37 @@ func (self *BranchCommands) GetCommitDifferences(from, to string) (string, strin
}
func (self *BranchCommands) countDifferences(from, to string) (string, error) {
cmdStr := NewGitCmd("rev-list").
cmdArgs := NewGitCmd("rev-list").
Arg(fmt.Sprintf("%s..%s", from, to)).
Arg("--count").
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *BranchCommands) IsHeadDetached() bool {
cmdStr := NewGitCmd("symbolic-ref").Arg("-q", "HEAD").ToString()
cmdArgs := NewGitCmd("symbolic-ref").Arg("-q", "HEAD").ToArgv()
err := self.cmd.New(cmdStr).DontLog().Run()
err := self.cmd.New(cmdArgs).DontLog().Run()
return err != nil
}
func (self *BranchCommands) Rename(oldName string, newName string) error {
cmdStr := NewGitCmd("branch").
Arg("--move", self.cmd.Quote(oldName), self.cmd.Quote(newName)).
ToString()
cmdArgs := NewGitCmd("branch").
Arg("--move", oldName, newName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) GetRawBranches() (string, error) {
cmdStr := NewGitCmd("for-each-ref").
cmdArgs := NewGitCmd("for-each-ref").
Arg("--sort=-committerdate").
Arg(`--format="%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)"`).
Arg(`--format=%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)`).
Arg("refs/heads").
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
type MergeOpts struct {
@ -199,16 +203,16 @@ type MergeOpts struct {
}
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
command := NewGitCmd("merge").
cmdArgs := NewGitCmd("merge").
Arg("--no-edit").
ArgIf(self.UserConfig.Git.Merging.Args != "", self.UserConfig.Git.Merging.Args).
ArgIf(opts.FastForwardOnly, "--ff-only").
Arg(self.cmd.Quote(branchName)).
ToString()
Arg(branchName).
ToArgv()
return self.cmd.New(command).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
return self.cmd.New(self.UserConfig.Git.AllBranchesLogCmd).DontLog()
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
}

View File

@ -21,21 +21,21 @@ func TestBranchGetCommitDifferences(t *testing.T) {
{
"Can't retrieve pushable count",
oscommands.NewFakeRunner(t).
Expect("git rev-list @{u}..HEAD --count", "", errors.New("error")),
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "", errors.New("error")),
"?", "?",
},
{
"Can't retrieve pullable count",
oscommands.NewFakeRunner(t).
Expect("git rev-list @{u}..HEAD --count", "1\n", nil).
Expect("git rev-list HEAD..@{u} --count", "", errors.New("error")),
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "1\n", nil).
ExpectGitArgs([]string{"rev-list", "HEAD..@{u}", "--count"}, "", errors.New("error")),
"?", "?",
},
{
"Retrieve pullable and pushable count",
oscommands.NewFakeRunner(t).
Expect("git rev-list @{u}..HEAD --count", "1\n", nil).
Expect("git rev-list HEAD..@{u} --count", "2\n", nil),
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "1\n", nil).
ExpectGitArgs([]string{"rev-list", "HEAD..@{u}", "--count"}, "2\n", nil),
"1", "2",
},
}
@ -54,7 +54,7 @@ func TestBranchGetCommitDifferences(t *testing.T) {
func TestBranchNewBranch(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(`git checkout -b "test" "refs/heads/master"`, "", nil)
ExpectGitArgs([]string{"checkout", "-b", "test", "refs/heads/master"}, "", nil)
instance := buildBranchCommands(commonDeps{runner: runner})
assert.NoError(t, instance.New("test", "refs/heads/master"))
@ -73,7 +73,7 @@ func TestBranchDeleteBranch(t *testing.T) {
{
"Delete a branch",
false,
oscommands.NewFakeRunner(t).Expect(`git branch -d "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@ -81,7 +81,7 @@ func TestBranchDeleteBranch(t *testing.T) {
{
"Force delete a branch",
true,
oscommands.NewFakeRunner(t).Expect(`git branch -D "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@ -105,14 +105,14 @@ func TestBranchMerge(t *testing.T) {
userConfig *config.UserConfig
opts MergeOpts
branchName string
expected string
expected []string
}{
{
testName: "basic",
userConfig: &config.UserConfig{},
opts: MergeOpts{},
branchName: "mybranch",
expected: `git merge --no-edit "mybranch"`,
expected: []string{"merge", "--no-edit", "mybranch"},
},
{
testName: "merging args",
@ -125,14 +125,14 @@ func TestBranchMerge(t *testing.T) {
},
opts: MergeOpts{},
branchName: "mybranch",
expected: `git merge --no-edit --merging-args "mybranch"`,
expected: []string{"merge", "--no-edit", "--merging-args", "mybranch"},
},
{
testName: "fast forward only",
userConfig: &config.UserConfig{},
opts: MergeOpts{FastForwardOnly: true},
branchName: "mybranch",
expected: `git merge --no-edit --ff-only "mybranch"`,
expected: []string{"merge", "--no-edit", "--ff-only", "mybranch"},
},
}
@ -140,7 +140,7 @@ func TestBranchMerge(t *testing.T) {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(s.expected, "", nil)
ExpectGitArgs(s.expected, "", nil)
instance := buildBranchCommands(commonDeps{runner: runner, userConfig: s.userConfig})
assert.NoError(t, instance.Merge(s.branchName, s.opts))
@ -160,7 +160,7 @@ func TestBranchCheckout(t *testing.T) {
scenarios := []scenario{
{
"Checkout",
oscommands.NewFakeRunner(t).Expect(`git checkout "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"checkout", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@ -168,7 +168,7 @@ func TestBranchCheckout(t *testing.T) {
},
{
"Checkout forced",
oscommands.NewFakeRunner(t).Expect(`git checkout --force "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"checkout", "--force", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@ -214,7 +214,7 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
scenarios := []scenario{
{
"says we are on the master branch if we are",
oscommands.NewFakeRunner(t).Expect(`git symbolic-ref --short HEAD`, "master", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "master", nil),
func(info BranchInfo, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "master", info.RefName)
@ -225,8 +225,9 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
{
"falls back to git `git branch --points-at=HEAD` if symbolic-ref fails",
oscommands.NewFakeRunner(t).
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
ExpectGitArgs([]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"},
"*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
func(info BranchInfo, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "6f71c57a8d4bd6c11399c3f55f42c815527a73a4", info.RefName)
@ -237,9 +238,9 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
{
"handles a detached head (LANG=zh_CN.UTF-8)",
oscommands.NewFakeRunner(t).
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
Expect(
`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`,
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
ExpectGitArgs(
[]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"},
"*\x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00(头指针在 679b0456 分离)\n"+
" \x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00refs/heads/master\n",
nil),
@ -253,8 +254,8 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
{
"bubbles up error if there is one",
oscommands.NewFakeRunner(t).
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "", errors.New("error")),
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
ExpectGitArgs([]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"}, "", errors.New("error")),
func(info BranchInfo, err error) {
assert.Error(t, err)
assert.EqualValues(t, "", info.RefName)

View File

@ -22,27 +22,27 @@ func NewCommitCommands(gitCommon *GitCommon) *CommitCommands {
// ResetAuthor resets the author of the topmost commit
func (self *CommitCommands) ResetAuthor() error {
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--reset-author").
ToString()
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// Sets the commit's author to the supplied value. Value is expected to be of the form 'Name <Email>'
func (self *CommitCommands) SetAuthor(value string) error {
cmdStr := NewGitCmd("commit").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+self.cmd.Quote(value)).
ToString()
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+value).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// ResetToCommit reset to commit
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
cmdStr := NewGitCmd("reset").Arg("--"+strength, sha).ToString()
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
return self.cmd.New(cmdStr).
return self.cmd.New(cmdArgs).
// 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").
@ -55,33 +55,37 @@ func (self *CommitCommands) CommitCmdObj(message string) oscommands.ICmdObj {
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
ArgIf(skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix), "--no-verify").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
Arg(messageArgs...).
ToString()
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
}
// RewordLastCommit rewords the topmost commit with the given message
func (self *CommitCommands) RewordLastCommit(message string) error {
messageArgs := self.commitMessageArgs(message)
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only").
Arg(messageArgs...).
ToString()
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *CommitCommands) commitMessageArgs(message string) []string {
msg, description, _ := strings.Cut(message, "\n")
args := []string{"-m", self.cmd.Quote(msg)}
args := []string{"-m", msg}
if description != "" {
args = append(args, "-m", self.cmd.Quote(description))
args = append(args, "-m", description)
}
return args
@ -89,12 +93,12 @@ func (self *CommitCommands) commitMessageArgs(message string) []string {
// runs git commit without the -m argument meaning it will invoke the user's editor
func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
ArgIf(self.verboseFlag() != "", self.verboseFlag()).
ToString()
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) signoffFlag() string {
@ -118,26 +122,26 @@ func (self *CommitCommands) verboseFlag() string {
// Get the subject of the HEAD commit
func (self *CommitCommands) GetHeadCommitMessage() (string, error) {
cmdStr := NewGitCmd("log").Arg("-1", "--pretty=%s").ToString()
cmdArgs := NewGitCmd("log").Arg("-1", "--pretty=%s").ToArgv()
message, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.TrimSpace(message), err
}
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
cmdStr := NewGitCmd("rev-list").
cmdArgs := NewGitCmd("rev-list").
Arg("--format=%B", "--max-count=1", commitSha).
ToString()
ToArgv()
messageWithHeader, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
return strings.TrimSpace(message), err
}
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
cmdStr := NewGitCmd("show").Arg("--no-color", commitSha).ToString()
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
diff, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
diff, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return diff, err
}
@ -147,11 +151,11 @@ type Author struct {
}
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitSha).
ToString()
ToArgv()
output, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return Author{}, err
}
@ -170,21 +174,21 @@ func (self *CommitCommands) GetCommitMessageFirstLine(sha string) (string, error
}
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%s").
Arg(shas...).
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--oneline").
Arg(shas...).
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
// AmendHead amends HEAD with whatever is staged in your working tree
@ -193,17 +197,17 @@ func (self *CommitCommands) AmendHead() error {
}
func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
Arg("--amend", "--no-edit", "--allow-empty").
ToString()
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
contextSize := self.UserConfig.Git.DiffContextSize
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--submodule").
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
Arg(fmt.Sprintf("--unified=%d", contextSize)).
@ -211,39 +215,39 @@ func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhit
Arg("-p").
Arg(sha).
ArgIf(ignoreWhitespace, "--ignore-all-space").
ArgIf(filterPath != "", "--", self.cmd.Quote(filterPath)).
ToString()
ArgIf(filterPath != "", "--", filterPath).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
// Revert reverts the selected commit by sha
func (self *CommitCommands) Revert(sha string) error {
cmdStr := NewGitCmd("revert").Arg(sha).ToString()
cmdArgs := NewGitCmd("revert").Arg(sha).ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
cmdStr := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
ToString()
cmdArgs := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// CreateFixupCommit creates a commit that fixes up a previous commit
func (self *CommitCommands) CreateFixupCommit(sha string) error {
cmdStr := NewGitCmd("commit").Arg("--fixup=" + sha).ToString()
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + sha).ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// a value of 0 means the head commit, 1 is the parent commit, etc
func (self *CommitCommands) GetCommitMessageFromHistory(value int) (string, error) {
cmdStr := NewGitCmd("log").Arg("-1", fmt.Sprintf("--skip=%d", value), "--pretty=%H").
ToString()
cmdArgs := NewGitCmd("log").Arg("-1", fmt.Sprintf("--skip=%d", value), "--pretty=%H").
ToArgv()
hash, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
hash, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
formattedHash := strings.TrimSpace(hash)
if len(formattedHash) == 0 {
return "", ErrInvalidCommitIndex

View File

@ -24,7 +24,7 @@ func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *
// GetFilesInDiff get the specified commit files
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
cmdStr := NewGitCmd("diff").
cmdArgs := NewGitCmd("diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg("--name-status").
@ -33,9 +33,9 @@ func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse boo
ArgIf(reverse, "-R").
Arg(from).
Arg(to).
ToString()
ToArgv()
filenames, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
filenames, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return nil, err
}

View File

@ -33,10 +33,11 @@ type CommitLoader struct {
readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string
// List of main branches that exist in the repo, quoted for direct use in a git command.
// List of main branches that exist in the repo.
// We use these to obtain the merge base of the branch.
// When nil, we're yet to obtain the list of main branches.
quotedMainBranches *string
// When nil, we're yet to obtain the list of existing main branches.
// When an empty slice, we've obtained the list and it's empty.
mainBranches []string
}
// making our dependencies explicit for the sake of easier testing
@ -47,13 +48,13 @@ func NewCommitLoader(
getRebaseMode func() (enums.RebaseMode, error),
) *CommitLoader {
return &CommitLoader{
Common: cmn,
cmd: cmd,
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
dotGitDir: dotGitDir,
quotedMainBranches: nil,
Common: cmn,
cmd: cmd,
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
dotGitDir: dotGitDir,
mainBranches: nil,
}
}
@ -205,7 +206,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
Config("log.showSignature=false").
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
Arg(commitShas...).
ToString(),
ToArgv(),
).DontLog()
fullCommits := map[string]*models.Commit{}
@ -364,11 +365,11 @@ func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*mod
}
func (self *CommitLoader) getMergeBase(refName string) string {
if self.quotedMainBranches == nil {
self.quotedMainBranches = lo.ToPtr(self.getExistingMainBranches())
if self.mainBranches == nil {
self.mainBranches = self.getExistingMainBranches()
}
if *self.quotedMainBranches == "" {
if len(self.mainBranches) == 0 {
return ""
}
@ -376,30 +377,29 @@ func (self *CommitLoader) getMergeBase(refName string) string {
// return the base commit for the closest one.
output, err := self.cmd.New(
NewGitCmd("merge-base").Arg(self.cmd.Quote(refName), *self.quotedMainBranches).
ToString(),
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
// If there's an error, it must be because one of the main branches that
// used to exist when we called getExistingMainBranches() was deleted
// meanwhile. To fix this for next time, throw away our cache.
self.quotedMainBranches = nil
self.mainBranches = nil
}
return ignoringWarnings(output)
}
func (self *CommitLoader) getExistingMainBranches() string {
return strings.Join(
lo.FilterMap(self.UserConfig.Git.MainBranches,
func(branchName string, _ int) (string, bool) {
quotedRef := self.cmd.Quote("refs/heads/" + branchName)
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", quotedRef).ToString(),
).DontLog().Run(); err != nil {
return "", false
}
return quotedRef, true
}), " ")
func (self *CommitLoader) getExistingMainBranches() []string {
return lo.FilterMap(self.UserConfig.Git.MainBranches,
func(branchName string, _ int) (string, bool) {
ref := "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err != nil {
return "", false
}
return ref, true
})
}
func ignoringWarnings(commandOutput string) string {
@ -415,13 +415,12 @@ 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 (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
output, err := self.cmd.
New(
NewGitCmd("merge-base").
Arg(self.cmd.Quote(refName)).
Arg(self.cmd.Quote(strings.TrimPrefix(refName, "refs/heads/")) + "@{u}").
ToString(),
).
output, err := self.cmd.New(
NewGitCmd("merge-base").
Arg(refName).
Arg(strings.TrimPrefix(refName, "refs/heads/") + "@{u}").
ToArgv(),
).
DontLog().
RunWithOutput()
if err != nil {
@ -435,8 +434,8 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
config := self.UserConfig.Git.Log
cmdStr := NewGitCmd("log").
Arg(self.cmd.Quote(opts.RefName)).
cmdArgs := NewGitCmd("log").
Arg(opts.RefName).
ArgIf(config.Order != "default", "--"+config.Order).
ArgIf(opts.All, "--all").
Arg("--oneline").
@ -446,10 +445,10 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
ArgIf(opts.FilterPath != "", "--follow").
Arg("--no-show-signature").
Arg("--").
ArgIf(opts.FilterPath != "", self.cmd.Quote(opts.FilterPath)).
ToString()
ArgIf(opts.FilterPath != "", opts.FilterPath).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
const prettyFormat = `--pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s"`
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s`

View File

@ -43,8 +43,8 @@ func TestGetCommits(t *testing.T) {
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@ -55,8 +55,8 @@ func TestGetCommits(t *testing.T) {
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
Expect(`git merge-base "refs/heads/mybranch" "mybranch"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
Expect(`git log "refs/heads/mybranch" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@ -69,14 +69,14 @@ func TestGetCommits(t *testing.T) {
mainBranches: []string{"master", "main"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, commitsOutput, nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
// here it's testing which of the configured main branches exist
Expect(`git rev-parse --verify --quiet "refs/heads/master"`, "", nil). // this one does
Expect(`git rev-parse --verify --quiet "refs/heads/main"`, "", errors.New("error")). // this one doesn't
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", nil). // this one does
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")). // this one doesn't
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
Expect(`git merge-base "HEAD" "refs/heads/master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/heads/master"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
expectedCommits: []*models.Commit{
{
@ -202,12 +202,12 @@ func TestGetCommits(t *testing.T) {
mainBranches: []string{"master", "main"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, singleCommitOutput, nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
// here it's testing which of the configured main branches exist; neither does
Expect(`git rev-parse --verify --quiet "refs/heads/master"`, "", errors.New("error")).
Expect(`git rev-parse --verify --quiet "refs/heads/main"`, "", errors.New("error")),
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", errors.New("error")).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")),
expectedCommits: []*models.Commit{
{
@ -235,16 +235,16 @@ func TestGetCommits(t *testing.T) {
mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, singleCommitOutput, nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
// here it's testing which of the configured main branches exist
Expect(`git rev-parse --verify --quiet "refs/heads/master"`, "", nil).
Expect(`git rev-parse --verify --quiet "refs/heads/main"`, "", errors.New("error")).
Expect(`git rev-parse --verify --quiet "refs/heads/develop"`, "", nil).
Expect(`git rev-parse --verify --quiet "refs/heads/1.0-hotfixes"`, "", nil).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", nil).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", nil).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/1.0-hotfixes"}, "", nil).
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
Expect(`git merge-base "HEAD" "refs/heads/master" "refs/heads/develop" "refs/heads/1.0-hotfixes"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/heads/master", "refs/heads/develop", "refs/heads/1.0-hotfixes"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
expectedCommits: []*models.Commit{
{
@ -270,8 +270,8 @@ func TestGetCommits(t *testing.T) {
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
Expect(`git log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@ -282,8 +282,8 @@ func TestGetCommits(t *testing.T) {
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
runner: oscommands.NewFakeRunner(t).
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
Expect(`git log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --follow --no-show-signature -- "src"`, "", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,

View File

@ -53,7 +53,7 @@ func TestCommitCommitCmdObj(t *testing.T) {
message string
configSignoff bool
configSkipHookPrefix string
expected string
expectedArgs []string
}
scenarios := []scenario{
@ -62,35 +62,35 @@ func TestCommitCommitCmdObj(t *testing.T) {
message: "test",
configSignoff: false,
configSkipHookPrefix: "",
expected: `git commit -m "test"`,
expectedArgs: []string{"commit", "-m", "test"},
},
{
testName: "Commit with --no-verify flag",
message: "WIP: test",
configSignoff: false,
configSkipHookPrefix: "WIP",
expected: `git commit --no-verify -m "WIP: test"`,
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
},
{
testName: "Commit with multiline message",
message: "line1\nline2",
configSignoff: false,
configSkipHookPrefix: "",
expected: `git commit -m "line1" -m "line2"`,
expectedArgs: []string{"commit", "-m", "line1", "-m", "line2"},
},
{
testName: "Commit with signoff",
message: "test",
configSignoff: true,
configSkipHookPrefix: "",
expected: `git commit --signoff -m "test"`,
expectedArgs: []string{"commit", "--signoff", "-m", "test"},
},
{
testName: "Commit with signoff and no-verify",
message: "WIP: test",
configSignoff: true,
configSkipHookPrefix: "WIP",
expected: `git commit --no-verify --signoff -m "WIP: test"`,
expectedArgs: []string{"commit", "--no-verify", "--signoff", "-m", "WIP: test"},
},
}
@ -101,10 +101,11 @@ func TestCommitCommitCmdObj(t *testing.T) {
userConfig.Git.Commit.SignOff = s.configSignoff
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expectedArgs, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
cmdStr := instance.CommitCmdObj(s.message).ToString()
assert.Equal(t, s.expected, cmdStr)
assert.NoError(t, instance.CommitCmdObj(s.message).Run())
runner.CheckForMissingCalls()
})
}
}
@ -114,7 +115,7 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
testName string
configSignoff bool
configVerbose string
expected string
expected []string
}
scenarios := []scenario{
@ -122,31 +123,31 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
testName: "Commit using editor",
configSignoff: false,
configVerbose: "default",
expected: `git commit`,
expected: []string{"commit"},
},
{
testName: "Commit with --no-verbose flag",
configSignoff: false,
configVerbose: "never",
expected: `git commit --no-verbose`,
expected: []string{"commit", "--no-verbose"},
},
{
testName: "Commit with --verbose flag",
configSignoff: false,
configVerbose: "always",
expected: `git commit --verbose`,
expected: []string{"commit", "--verbose"},
},
{
testName: "Commit with --signoff",
configSignoff: true,
configVerbose: "default",
expected: `git commit --signoff`,
expected: []string{"commit", "--signoff"},
},
{
testName: "Commit with --signoff and --no-verbose",
configSignoff: true,
configVerbose: "never",
expected: `git commit --signoff --no-verbose`,
expected: []string{"commit", "--signoff", "--no-verbose"},
},
}
@ -157,10 +158,11 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
userConfig.Git.Commit.SignOff = s.configSignoff
userConfig.Git.Commit.Verbose = s.configVerbose
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
cmdStr := instance.CommitEditorCmdObj().ToString()
assert.Equal(t, s.expected, cmdStr)
assert.NoError(t, instance.CommitEditorCmdObj().Run())
runner.CheckForMissingCalls()
})
}
}
@ -178,7 +180,7 @@ func TestCommitCreateFixupCommit(t *testing.T) {
testName: "valid case",
sha: "12345",
runner: oscommands.NewFakeRunner(t).
Expect(`git commit --fixup=12345`, "", nil),
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -201,7 +203,7 @@ func TestCommitShowCmdObj(t *testing.T) {
filterPath string
contextSize int
ignoreWhitespace bool
expected string
expected []string
}
scenarios := []scenario{
@ -210,28 +212,28 @@ func TestCommitShowCmdObj(t *testing.T) {
filterPath: "",
contextSize: 3,
ignoreWhitespace: false,
expected: "git show --submodule --color=always --unified=3 --stat -p 1234567890",
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "-p", "1234567890"},
},
{
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
ignoreWhitespace: false,
expected: `git show --submodule --color=always --unified=3 --stat -p 1234567890 -- "file.txt"`,
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "-p", "1234567890", "--", "file.txt"},
},
{
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
ignoreWhitespace: false,
expected: "git show --submodule --color=always --unified=77 --stat -p 1234567890",
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "-p", "1234567890"},
},
{
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
ignoreWhitespace: true,
expected: "git show --submodule --color=always --unified=77 --stat -p 1234567890 --ignore-all-space",
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "-p", "1234567890", "--ignore-all-space"},
},
}
@ -241,10 +243,11 @@ func TestCommitShowCmdObj(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.DiffContextSize = s.contextSize
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
cmdStr := instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).ToString()
assert.Equal(t, s.expected, cmdStr)
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).Run())
runner.CheckForMissingCalls()
})
}
}
@ -283,7 +286,7 @@ Merge pull request #1750 from mark2185/fix-issue-template
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{
runner: oscommands.NewFakeRunner(t).Expect("git rev-list --format=%B --max-count=1 deadbeef", s.input, nil),
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
})
output, err := instance.GetCommitMessage("deadbeef")
@ -304,14 +307,14 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
scenarios := []scenario{
{
"Empty message",
oscommands.NewFakeRunner(t).Expect("git log -1 --skip=2 --pretty=%H", "", nil).Expect("git rev-list --format=%B --max-count=1 ", "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1"}, "", nil),
func(output string, err error) {
assert.Error(t, err)
},
},
{
"Default case to retrieve a commit in history",
oscommands.NewFakeRunner(t).Expect("git log -1 --skip=2 --pretty=%H", "sha3 \n", nil).Expect("git rev-list --format=%B --max-count=1 sha3", `commit sha3
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "sha3"}, `commit sha3
use generics to DRY up context code`, nil),
func(output string, err error) {
assert.NoError(t, err)

View File

@ -1,5 +1,7 @@
package git_commands
import "github.com/mgutz/str"
type CustomCommands struct {
*GitCommon
}
@ -14,5 +16,5 @@ func NewCustomCommands(gitCommon *GitCommon) *CustomCommands {
// If you want to run a new command, try finding a place for it in one of the neighbouring
// files, or creating a new BlahCommands struct to hold it.
func (self *CustomCommands) RunWithOutput(cmdStr string) (string, error) {
return self.cmd.New(cmdStr).RunWithOutput()
return self.cmd.New(str.ToArgv(cmdStr)).RunWithOutput()
}

View File

@ -0,0 +1,19 @@
package git_commands
import "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
type DiffCommands struct {
*GitCommon
}
func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
return &DiffCommands{
GitCommon: gitCommon,
}
}
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
return self.cmd.New(
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
)
}

View File

@ -45,7 +45,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
editor = self.os.Getenv("EDITOR")
}
if editor == "" {
if err := self.cmd.New("which vi").DontLog().Run(); err == nil {
if err := self.cmd.New([]string{"which", "vi"}).DontLog().Run(); err == nil {
editor = "vi"
}
}

View File

@ -82,14 +82,14 @@ type FileStatus struct {
}
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
cmdStr := NewGitCmd("status").
cmdArgs := NewGitCmd("status").
Arg(opts.UntrackedFilesArg).
Arg("--porcelain").
Arg("-z").
ArgIf(opts.NoRenames, "--no-renames").
ToString()
ToArgv()
statusLines, _, err := c.cmd.New(cmdStr).DontLog().RunWithOutputs()
statusLines, _, err := c.cmd.New(cmdArgs).DontLog().RunWithOutputs()
if err != nil {
return []FileStatus{}, err
}

View File

@ -20,14 +20,13 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"No files found",
oscommands.NewFakeRunner(t).
Expect(`git status --untracked-files=yes --porcelain -z`, "", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
[]*models.File{},
},
{
"Several files found",
oscommands.NewFakeRunner(t).
Expect(
`git status --untracked-files=yes --porcelain -z`,
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
nil,
),
@ -102,7 +101,7 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"File with new line char",
oscommands.NewFakeRunner(t).
Expect(`git status --untracked-files=yes --porcelain -z`, "MM a\nb.txt", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
[]*models.File{
{
Name: "a\nb.txt",
@ -122,8 +121,7 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"Renamed files",
oscommands.NewFakeRunner(t).
Expect(
`git status --untracked-files=yes --porcelain -z`,
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
nil,
),
@ -161,8 +159,7 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"File with arrow in name",
oscommands.NewFakeRunner(t).
Expect(
`git status --untracked-files=yes --porcelain -z`,
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
`?? a -> b.txt`,
nil,
),

View File

@ -27,7 +27,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
Expect(`which vi`, "", errors.New("error")),
ExpectArgs([]string{"which", "vi"}, "", errors.New("error")),
getenv: func(env string) string {
return ""
},
@ -105,7 +105,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
Expect(`which vi`, "/usr/bin/vi", nil),
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
getenv: func(env string) string {
return ""
},
@ -120,7 +120,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
Expect(`which vi`, "/usr/bin/vi", nil),
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
getenv: func(env string) string {
return ""
},

View File

@ -49,13 +49,13 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, e
return nil, errors.New(self.Tr.NotAGitFlowBranch)
}
cmdStr := NewGitCmd("flow").Arg(branchType, "finish", suffix).ToString()
cmdArgs := NewGitCmd("flow").Arg(branchType, "finish", suffix).ToArgv()
return self.cmd.New(cmdStr), nil
return self.cmd.New(cmdArgs), nil
}
func (self *FlowCommands) StartCmdObj(branchType string, name string) oscommands.ICmdObj {
cmdStr := NewGitCmd("flow").Arg(branchType, "start", name).ToString()
cmdArgs := NewGitCmd("flow").Arg(branchType, "start", name).ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}

View File

@ -12,13 +12,13 @@ func TestStartCmdObj(t *testing.T) {
testName string
branchType string
name string
expected string
expected []string
}{
{
testName: "basic",
branchType: "feature",
name: "test",
expected: "git flow feature start test",
expected: []string{"git", "flow", "feature", "start", "test"},
},
}
@ -28,7 +28,7 @@ func TestStartCmdObj(t *testing.T) {
instance := buildFlowCommands(commonDeps{})
assert.Equal(t,
instance.StartCmdObj(s.branchType, s.name).ToString(),
instance.StartCmdObj(s.branchType, s.name).Args(),
s.expected,
)
})
@ -39,28 +39,28 @@ func TestFinishCmdObj(t *testing.T) {
scenarios := []struct {
testName string
branchName string
expected string
expected []string
expectedError string
gitConfigMockResponses map[string]string
}{
{
testName: "not a git flow branch",
branchName: "mybranch",
expected: "",
expected: nil,
expectedError: "This does not seem to be a git flow branch",
gitConfigMockResponses: nil,
},
{
testName: "feature branch without config",
branchName: "feature/mybranch",
expected: "",
expected: nil,
expectedError: "This does not seem to be a git flow branch",
gitConfigMockResponses: nil,
},
{
testName: "feature branch with config",
branchName: "feature/mybranch",
expected: "git flow feature finish mybranch",
expected: []string{"git", "flow", "feature", "finish", "mybranch"},
expectedError: "",
gitConfigMockResponses: map[string]string{
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature/",
@ -85,7 +85,7 @@ func TestFinishCmdObj(t *testing.T) {
}
} else {
assert.NoError(t, err)
assert.Equal(t, cmd.ToString(), s.expected)
assert.Equal(t, cmd.Args(), s.expected)
}
})
}

View File

@ -49,6 +49,10 @@ func (self *GitCommandBuilder) RepoPath(value string) *GitCommandBuilder {
return self
}
func (self *GitCommandBuilder) ToString() string {
return "git " + strings.Join(self.args, " ")
func (self *GitCommandBuilder) ToArgv() []string {
return append([]string{"git"}, self.args...)
}
func (self *GitCommandBuilder) ToString() string {
return strings.Join(self.ToArgv(), " ")
}

View File

@ -8,8 +8,8 @@ import (
func TestGitCommandBuilder(t *testing.T) {
scenarios := []struct {
input string
expected string
input []string
expected []string
}{
{
input: NewGitCmd("push").
@ -17,36 +17,36 @@ func TestGitCommandBuilder(t *testing.T) {
Arg("--set-upstream").
Arg("origin").
Arg("master").
ToString(),
expected: "git push --force-with-lease --set-upstream origin master",
ToArgv(),
expected: []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"},
},
{
input: NewGitCmd("push").ArgIf(true, "--test").ToString(),
expected: "git push --test",
input: NewGitCmd("push").ArgIf(true, "--test").ToArgv(),
expected: []string{"git", "push", "--test"},
},
{
input: NewGitCmd("push").ArgIf(false, "--test").ToString(),
expected: "git push",
input: NewGitCmd("push").ArgIf(false, "--test").ToArgv(),
expected: []string{"git", "push"},
},
{
input: NewGitCmd("push").ArgIfElse(true, "-b", "-a").ToString(),
expected: "git push -b",
input: NewGitCmd("push").ArgIfElse(true, "-b", "-a").ToArgv(),
expected: []string{"git", "push", "-b"},
},
{
input: NewGitCmd("push").ArgIfElse(false, "-a", "-b").ToString(),
expected: "git push -b",
input: NewGitCmd("push").ArgIfElse(false, "-a", "-b").ToArgv(),
expected: []string{"git", "push", "-b"},
},
{
input: NewGitCmd("push").Arg("-a", "-b").ToString(),
expected: "git push -a -b",
input: NewGitCmd("push").Arg("-a", "-b").ToArgv(),
expected: []string{"git", "push", "-a", "-b"},
},
{
input: NewGitCmd("push").Config("user.name=foo").Config("user.email=bar").ToString(),
expected: "git -c user.email=bar -c user.name=foo push",
input: NewGitCmd("push").Config("user.name=foo").Config("user.email=bar").ToArgv(),
expected: []string{"git", "-c", "user.email=bar", "-c", "user.name=foo", "push"},
},
{
input: NewGitCmd("push").RepoPath("a/b/c").ToString(),
expected: "git -C a/b/c push",
input: NewGitCmd("push").RepoPath("a/b/c").ToArgv(),
expected: []string{"git", "-C", "a/b/c", "push"},
},
}

View File

@ -69,15 +69,15 @@ func (self *PatchCommands) ApplyPatch(patch string, opts ApplyPatchOpts) error {
}
func (self *PatchCommands) applyPatchFile(filepath string, opts ApplyPatchOpts) error {
cmdStr := NewGitCmd("apply").
cmdArgs := NewGitCmd("apply").
ArgIf(opts.ThreeWay, "--3way").
ArgIf(opts.Cached, "--cached").
ArgIf(opts.Index, "--index").
ArgIf(opts.Reverse, "--reverse").
Arg(self.cmd.Quote(filepath)).
ToString()
Arg(filepath).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) {
@ -320,7 +320,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm
// only some lines of a range of adjacent added lines. To solve this, we
// get the diff of HEAD and the original commit and then apply that.
func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) {
cmdStr := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToString()
cmdArgs := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToArgv()
return self.cmd.New(cmdStr).RunWithOutput()
return self.cmd.New(cmdArgs).RunWithOutput()
}

View File

@ -3,7 +3,6 @@ package git_commands
import (
"fmt"
"os"
"regexp"
"testing"
"github.com/go-errors/errors"
@ -11,21 +10,22 @@ import (
"github.com/stretchr/testify/assert"
)
func TestWorkingTreeApplyPatch(t *testing.T) {
func TestPatchApplyPatch(t *testing.T) {
type scenario struct {
testName string
opts ApplyPatchOpts
runner *oscommands.FakeCmdObjRunner
test func(error)
}
expectFn := func(regexStr string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
// expectedArgs excludes the last argument which is an indeterminate filename
expectFn := func(expectedArgs []string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
return func(cmdObj oscommands.ICmdObj) (string, error) {
re := regexp.MustCompile(regexStr)
cmdStr := cmdObj.ToString()
matches := re.FindStringSubmatch(cmdStr)
assert.Equal(t, 2, len(matches), fmt.Sprintf("unexpected command: %s", cmdStr))
args := cmdObj.Args()
filename := matches[1]
assert.Equal(t, len(args), len(expectedArgs)+1, fmt.Sprintf("unexpected command: %s", cmdObj.ToString()))
filename := args[len(args)-1]
content, err := os.ReadFile(filename)
assert.NoError(t, err)
@ -39,16 +39,18 @@ func TestWorkingTreeApplyPatch(t *testing.T) {
scenarios := []scenario{
{
testName: "valid case",
opts: ApplyPatchOpts{Cached: true},
runner: oscommands.NewFakeRunner(t).
ExpectFunc(expectFn(`git apply --cached "(.*)"`, nil)),
ExpectFunc(expectFn([]string{"git", "apply", "--cached"}, nil)),
test: func(err error) {
assert.NoError(t, err)
},
},
{
testName: "command returns error",
opts: ApplyPatchOpts{Cached: true},
runner: oscommands.NewFakeRunner(t).
ExpectFunc(expectFn(`git apply --cached "(.*)"`, errors.New("error"))),
ExpectFunc(expectFn([]string{"git", "apply", "--cached"}, errors.New("error"))),
test: func(err error) {
assert.Error(t, err)
},
@ -59,7 +61,7 @@ func TestWorkingTreeApplyPatch(t *testing.T) {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildPatchCommands(commonDeps{runner: s.runner})
s.test(instance.ApplyPatch("test", ApplyPatchOpts{Cached: true}))
s.test(instance.ApplyPatch("test", s.opts))
s.runner.CheckForMissingCalls()
})
}

View File

@ -177,7 +177,7 @@ type PrepareInteractiveRebaseCommandOpts struct {
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
ex := oscommands.GetLazygitPath()
cmdStr := NewGitCmd("rebase").
cmdArgs := NewGitCmd("rebase").
Arg("--interactive").
Arg("--autostash").
Arg("--keep-empty").
@ -185,16 +185,16 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
Arg("--no-autosquash").
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
Arg(opts.baseShaOrRoot).
ToString()
ToArgv()
debug := "FALSE"
if self.Debug {
debug = "TRUE"
}
self.Log.WithField("command", cmdStr).Debug("RunCommand")
self.Log.WithField("command", cmdArgs).Debug("RunCommand")
cmdObj := self.cmd.New(cmdStr)
cmdObj := self.cmd.New(cmdArgs)
gitSequenceEditor := ex
@ -227,8 +227,8 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
}
// Get the sha of the commit we just created
cmdStr := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToString()
fixupSha, err := self.cmd.New(cmdStr).RunWithOutput()
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
fixupSha, err := self.cmd.New(cmdArgs).RunWithOutput()
if err != nil {
return err
}
@ -265,11 +265,11 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
shaOrRoot = "--root"
}
cmdStr := NewGitCmd("rebase").
cmdArgs := NewGitCmd("rebase").
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", shaOrRoot).
ToString()
ToArgv()
return self.runSkipEditorCommand(self.cmd.New(cmdStr))
return self.runSkipEditorCommand(self.cmd.New(cmdArgs))
}
// BeginInteractiveRebaseForCommit starts an interactive rebase to edit the current
@ -308,9 +308,9 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error {
}
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
cmdStr := NewGitCmd(commandType).Arg("--" + command).ToString()
cmdArgs := NewGitCmd(commandType).Arg("--" + command).ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *RebaseCommands) ContinueRebase() error {
@ -367,9 +367,9 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm
}
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
cmdStr := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+self.cmd.Quote(fileName)).ToString()
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+fileName).ToArgv()
if err := self.cmd.New(cmdStr).Run(); err != nil {
if err := self.cmd.New(cmdArgs).Run(); err != nil {
if err := self.os.Remove(fileName); err != nil {
return err
}

View File

@ -29,7 +29,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 26, 0, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges master`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -39,7 +39,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 26, 0, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges master`, "", errors.New("error")),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", errors.New("error")),
test: func(err error) {
assert.Error(t, err)
},
@ -49,7 +49,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 25, 5, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges master`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -59,7 +59,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 21, 9, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash master`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "master"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -78,9 +78,9 @@ func TestRebaseRebaseBranch(t *testing.T) {
// TestRebaseSkipEditorCommand confirms that SkipEditorCommand injects
// environment variables that suppress an interactive editor
func TestRebaseSkipEditorCommand(t *testing.T) {
commandStr := "git blah"
cmdArgs := []string{"git", "blah"}
runner := oscommands.NewFakeRunner(t).ExpectFunc(func(cmdObj oscommands.ICmdObj) (string, error) {
assert.Equal(t, commandStr, cmdObj.ToString())
assert.EqualValues(t, cmdArgs, cmdObj.Args())
envVars := cmdObj.GetEnvVars()
for _, regexStr := range []string{
`^VISUAL=.*$`,
@ -100,7 +100,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) {
return "", nil
})
instance := buildRebaseCommands(commonDeps{runner: runner})
err := instance.runSkipEditorCommand(instance.cmd.New(commandStr))
err := instance.runSkipEditorCommand(instance.cmd.New(cmdArgs))
assert.NoError(t, err)
runner.CheckForMissingCalls()
}
@ -149,11 +149,11 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
commitIndex: 0,
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges abcdef`, "", nil).
Expect(`git cat-file -e HEAD^:"test999.txt"`, "", nil).
Expect(`git checkout HEAD^ -- "test999.txt"`, "", nil).
Expect(`git commit --amend --no-edit --allow-empty`, "", nil).
Expect(`git rebase --continue`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
ExpectGitArgs([]string{"checkout", "HEAD^", "--", "test999.txt"}, "", nil).
ExpectGitArgs([]string{"commit", "--amend", "--no-edit", "--allow-empty"}, "", nil).
ExpectGitArgs([]string{"rebase", "--continue"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},

View File

@ -1,7 +1,6 @@
package git_commands
import (
"fmt"
"strconv"
"strings"
@ -27,12 +26,15 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string) ([]*models.Commit, bool, error) {
commits := make([]*models.Commit, 0)
filterPathArg := ""
if filterPath != "" {
filterPathArg = fmt.Sprintf(" --follow -- %s", self.cmd.Quote(filterPath))
}
cmdArgs := NewGitCmd("log").
Config("log.showSignature=false").
Arg("-g").
Arg("--abbrev=40").
Arg("--format=%h%x00%ct%x00%gs%x00%p").
ArgIf(filterPath != "", "--follow", "--", filterPath).
ToArgv()
cmdObj := self.cmd.New(fmt.Sprintf(`git -c log.showSignature=false log -g --abbrev=40 --format="%s"%s`, "%h%x00%ct%x00%gs%x00%p", filterPathArg)).DontLog()
cmdObj := self.cmd.New(cmdArgs).DontLog()
onlyObtainedNewReflogCommits := false
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
fields := strings.SplitN(line, "\x00", 4)

View File

@ -34,7 +34,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "no reflog entries",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, "", nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, "", nil),
lastReflogCommit: nil,
expectedCommits: []*models.Commit{},
@ -44,7 +44,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "some reflog entries",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, reflogOutput, nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
lastReflogCommit: nil,
expectedCommits: []*models.Commit{
@ -90,7 +90,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "some reflog entries where last commit is given",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, reflogOutput, nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Sha: "c3c4b66b64c97ffeecde",
@ -114,7 +114,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "when passing filterPath",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p" --follow -- "path"`, reflogOutput, nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Sha: "c3c4b66b64c97ffeecde",
@ -139,7 +139,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "when command returns error",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, "", errors.New("haha")),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, "", errors.New("haha")),
lastReflogCommit: nil,
filterPath: "",

View File

@ -15,52 +15,52 @@ func NewRemoteCommands(gitCommon *GitCommon) *RemoteCommands {
}
func (self *RemoteCommands) AddRemote(name string, url string) error {
cmdStr := NewGitCmd("remote").
Arg("add", self.cmd.Quote(name), self.cmd.Quote(url)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("add", name, url).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) RemoveRemote(name string) error {
cmdStr := NewGitCmd("remote").
Arg("remove", self.cmd.Quote(name)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("remove", name).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) RenameRemote(oldRemoteName string, newRemoteName string) error {
cmdStr := NewGitCmd("remote").
Arg("rename", self.cmd.Quote(oldRemoteName), self.cmd.Quote(newRemoteName)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("rename", oldRemoteName, newRemoteName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
cmdStr := NewGitCmd("remote").
Arg("set-url", self.cmd.Quote(remoteName), self.cmd.Quote(updatedUrl)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("set-url", remoteName, updatedUrl).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) DeleteRemoteBranch(remoteName string, branchName string) error {
cmdStr := NewGitCmd("push").
Arg(self.cmd.Quote(remoteName), "--delete", self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("push").
Arg(remoteName, "--delete", branchName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
// CheckRemoteBranchExists Returns remote branch
func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
cmdStr := NewGitCmd("show-ref").
Arg("--verify", "--", fmt.Sprintf("refs/remotes/origin/%s", self.cmd.Quote(branchName))).
ToString()
cmdArgs := NewGitCmd("show-ref").
Arg("--verify", "--", fmt.Sprintf("refs/remotes/origin/%s", branchName)).
ToArgv()
_, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
_, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return err == nil
}

View File

@ -31,8 +31,8 @@ func NewRemoteLoader(
}
func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
cmdStr := NewGitCmd("branch").Arg("-r").ToString()
remoteBranchesStr, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv()
remoteBranchesStr, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return nil, err
}

View File

@ -26,84 +26,84 @@ func NewStashCommands(
}
func (self *StashCommands) DropNewest() error {
cmdStr := NewGitCmd("stash").Arg("drop").ToString()
cmdArgs := NewGitCmd("stash").Arg("drop").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Drop(index int) error {
cmdStr := NewGitCmd("stash").Arg("drop", fmt.Sprintf("stash@{%d}", index)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("drop", fmt.Sprintf("stash@{%d}", index)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Pop(index int) error {
cmdStr := NewGitCmd("stash").Arg("pop", fmt.Sprintf("stash@{%d}", index)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("pop", fmt.Sprintf("stash@{%d}", index)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Apply(index int) error {
cmdStr := NewGitCmd("stash").Arg("apply", fmt.Sprintf("stash@{%d}", index)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("apply", fmt.Sprintf("stash@{%d}", index)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// Save save stash
func (self *StashCommands) Save(message string) error {
cmdStr := NewGitCmd("stash").Arg("save", self.cmd.Quote(message)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("save", message).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Store(sha string, message string) error {
trimmedMessage := strings.Trim(message, " \t")
cmdStr := NewGitCmd("stash").Arg("store", self.cmd.Quote(sha)).
ArgIf(trimmedMessage != "", "-m", self.cmd.Quote(trimmedMessage)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("store", sha).
ArgIf(trimmedMessage != "", "-m", trimmedMessage).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Sha(index int) (string, error) {
cmdStr := NewGitCmd("rev-parse").
cmdArgs := NewGitCmd("rev-parse").
Arg(fmt.Sprintf("refs/stash@{%d}", index)).
ToString()
ToArgv()
sha, _, err := self.cmd.New(cmdStr).DontLog().RunWithOutputs()
sha, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
return strings.Trim(sha, "\r\n"), err
}
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreWhitespace bool) oscommands.ICmdObj {
cmdStr := NewGitCmd("stash").Arg("show").
cmdArgs := NewGitCmd("stash").Arg("show").
Arg("-p").
Arg("--stat").
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
Arg(fmt.Sprintf("--unified=%d", self.UserConfig.Git.DiffContextSize)).
ArgIf(ignoreWhitespace, "--ignore-all-space").
Arg(fmt.Sprintf("stash@{%d}", index)).
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
func (self *StashCommands) StashAndKeepIndex(message string) error {
cmdStr := NewGitCmd("stash").Arg("save", self.cmd.Quote(message), "--keep-index").
ToString()
cmdArgs := NewGitCmd("stash").Arg("save", message, "--keep-index").
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) StashUnstagedChanges(message string) error {
if err := self.cmd.New(
NewGitCmd("commit").
Arg("--no-verify", "-m", self.cmd.Quote("[lazygit] stashing unstaged changes")).
ToString(),
Arg("--no-verify", "-m", "[lazygit] stashing unstaged changes").
ToArgv(),
).Run(); err != nil {
return err
}
@ -112,7 +112,7 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
}
if err := self.cmd.New(
NewGitCmd("reset").Arg("--soft", "HEAD^").ToString(),
NewGitCmd("reset").Arg("--soft", "HEAD^").ToArgv(),
).Run(); err != nil {
return err
}
@ -124,7 +124,7 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
func (self *StashCommands) SaveStagedChanges(message string) error {
// wrap in 'writing', which uses a mutex
if err := self.cmd.New(
NewGitCmd("stash").Arg("--keep-index").ToString(),
NewGitCmd("stash").Arg("--keep-index").ToArgv(),
).Run(); err != nil {
return err
}
@ -134,20 +134,20 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
}
if err := self.cmd.New(
NewGitCmd("stash").Arg("apply", "stash@{1}").ToString(),
NewGitCmd("stash").Arg("apply", "stash@{1}").ToArgv(),
).Run(); err != nil {
return err
}
if err := self.os.PipeCommands(
NewGitCmd("stash").Arg("show", "-p").ToString(),
NewGitCmd("apply").Arg("-R").ToString(),
self.cmd.New(NewGitCmd("stash").Arg("show", "-p").ToArgv()),
self.cmd.New(NewGitCmd("apply").Arg("-R").ToArgv()),
); err != nil {
return err
}
if err := self.cmd.New(
NewGitCmd("stash").Arg("drop", "stash@{1}").ToString(),
NewGitCmd("stash").Arg("drop", "stash@{1}").ToArgv(),
).Run(); err != nil {
return err
}
@ -171,8 +171,8 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
return self.cmd.New(
NewGitCmd("stash").Arg("save", self.cmd.Quote(message), "--include-untracked").
ToString(),
NewGitCmd("stash").Arg("save", message, "--include-untracked").
ToArgv(),
).Run()
}

View File

@ -32,8 +32,8 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
return self.getUnfilteredStashEntries()
}
cmdStr := NewGitCmd("stash").Arg("list", "--name-only").ToString()
rawString, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return self.getUnfilteredStashEntries()
}
@ -66,9 +66,9 @@ outer:
}
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
cmdStr := NewGitCmd("stash").Arg("list", "-z", "--pretty='%gs'").ToString()
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
rawString, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
return self.stashEntryFromLine(line, index)
})

View File

@ -22,15 +22,14 @@ func TestGetStashEntries(t *testing.T) {
"No stash entries found",
"",
oscommands.NewFakeRunner(t).
Expect(`git stash list -z --pretty='%gs'`, "", nil),
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
[]*models.StashEntry{},
},
{
"Several stash entries found",
"",
oscommands.NewFakeRunner(t).
Expect(
`git stash list -z --pretty='%gs'`,
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
nil,
),

View File

@ -103,7 +103,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
index int
contextSize int
ignoreWhitespace bool
expected string
expected []string
}
scenarios := []scenario{
@ -112,21 +112,21 @@ func TestStashStashEntryCmdObj(t *testing.T) {
index: 5,
contextSize: 3,
ignoreWhitespace: false,
expected: "git stash show -p --stat --color=always --unified=3 stash@{5}",
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "stash@{5}"},
},
{
testName: "Show diff with custom context size",
index: 5,
contextSize: 77,
ignoreWhitespace: false,
expected: "git stash show -p --stat --color=always --unified=77 stash@{5}",
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "stash@{5}"},
},
{
testName: "Default case",
index: 5,
contextSize: 3,
ignoreWhitespace: true,
expected: "git stash show -p --stat --color=always --unified=3 --ignore-all-space stash@{5}",
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "stash@{5}"},
},
}
@ -137,7 +137,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
userConfig.Git.DiffContextSize = s.contextSize
instance := buildStashCommands(commonDeps{userConfig: userConfig})
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).ToString()
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).Args()
assert.Equal(t, s.expected, cmdStr)
})
}

View File

@ -57,7 +57,7 @@ func (self *StatusCommands) IsBareRepo() (bool, error) {
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
res, err := osCommand.Cmd.New(
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToString(),
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return false, err

View File

@ -81,27 +81,27 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
return nil
}
cmdStr := NewGitCmd("stash").
RepoPath(self.cmd.Quote(submodule.Path)).
cmdArgs := NewGitCmd("stash").
RepoPath(submodule.Path).
Arg("--include-untracked").
ToString()
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
cmdStr := NewGitCmd("submodule").
Arg("update", "--init", "--force", "--", self.cmd.Quote(submodule.Path)).
ToString()
cmdArgs := NewGitCmd("submodule").
Arg("update", "--init", "--force", "--", submodule.Path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) UpdateAll() error {
// not doing an --init here because the user probably doesn't want that
cmdStr := NewGitCmd("submodule").Arg("update", "--force").ToString()
cmdArgs := NewGitCmd("submodule").Arg("update", "--force").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
@ -109,7 +109,7 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
if err := self.cmd.New(
NewGitCmd("submodule").
Arg("deinit", "--force", "--", self.cmd.Quote(submodule.Path)).ToString(),
Arg("deinit", "--force", "--", submodule.Path).ToArgv(),
).Run(); err != nil {
if !strings.Contains(err.Error(), "did not match any file(s) known to git") {
return err
@ -117,23 +117,23 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
if err := self.cmd.New(
NewGitCmd("config").
Arg("--file", ".gitmodules", "--remove-section", "submodule."+self.cmd.Quote(submodule.Path)).
ToString(),
Arg("--file", ".gitmodules", "--remove-section", "submodule."+submodule.Path).
ToArgv(),
).Run(); err != nil {
return err
}
if err := self.cmd.New(
NewGitCmd("config").
Arg("--remove-section", "submodule."+self.cmd.Quote(submodule.Path)).
ToString(),
Arg("--remove-section", "submodule."+submodule.Path).
ToArgv(),
).Run(); err != nil {
return err
}
}
if err := self.cmd.New(
NewGitCmd("rm").Arg("--force", "-r", submodule.Path).ToString(),
NewGitCmd("rm").Arg("--force", "-r", submodule.Path).ToArgv(),
).Run(); err != nil {
// if the directory isn't there then that's fine
self.Log.Error(err)
@ -143,33 +143,33 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
}
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
cmdStr := NewGitCmd("submodule").
cmdArgs := NewGitCmd("submodule").
Arg("add").
Arg("--force").
Arg("--name").
Arg(self.cmd.Quote(name)).
Arg(name).
Arg("--").
Arg(self.cmd.Quote(url)).
Arg(self.cmd.Quote(path)).
ToString()
Arg(url).
Arg(path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
setUrlCmdStr := NewGitCmd("config").
Arg(
"--file", ".gitmodules", "submodule."+self.cmd.Quote(name)+".url", self.cmd.Quote(newUrl),
"--file", ".gitmodules", "submodule."+name+".url", newUrl,
).
ToString()
ToArgv()
// the set-url command is only for later git versions so we're doing it manually here
if err := self.cmd.New(setUrlCmdStr).Run(); err != nil {
return err
}
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", self.cmd.Quote(path)).
ToString()
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", path).
ToArgv()
if err := self.cmd.New(syncCmdStr).Run(); err != nil {
return err
@ -179,45 +179,45 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
}
func (self *SubmoduleCommands) Init(path string) error {
cmdStr := NewGitCmd("submodule").Arg("init", "--", self.cmd.Quote(path)).
ToString()
cmdArgs := NewGitCmd("submodule").Arg("init", "--", path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) Update(path string) error {
cmdStr := NewGitCmd("submodule").Arg("update", "--init", "--", self.cmd.Quote(path)).
ToString()
cmdArgs := NewGitCmd("submodule").Arg("update", "--init", "--", path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) BulkInitCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("init").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("init").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) BulkUpdateCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("update").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("update").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) ForceBulkUpdateCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("update", "--force").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("update", "--force").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) BulkDeinitCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("deinit", "--all", "--force").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("deinit", "--all", "--force").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) ResetSubmodules(submodules []*models.SubmoduleConfig) error {

View File

@ -28,14 +28,14 @@ func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error)
return nil, errors.New(self.Tr.MustSpecifyOriginError)
}
cmdStr := NewGitCmd("push").
cmdArgs := NewGitCmd("push").
ArgIf(opts.Force, "--force-with-lease").
ArgIf(opts.SetUpstream, "--set-upstream").
ArgIf(opts.UpstreamRemote != "", self.cmd.Quote(opts.UpstreamRemote)).
ArgIf(opts.UpstreamBranch != "", self.cmd.Quote(opts.UpstreamBranch)).
ToString()
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
ToArgv()
cmdObj := self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex)
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex)
return cmdObj, nil
}
@ -56,12 +56,12 @@ type FetchOptions struct {
// Fetch fetch git repo
func (self *SyncCommands) Fetch(opts FetchOptions) error {
cmdStr := NewGitCmd("fetch").
ArgIf(opts.RemoteName != "", self.cmd.Quote(opts.RemoteName)).
ArgIf(opts.BranchName != "", self.cmd.Quote(opts.BranchName)).
ToString()
cmdArgs := NewGitCmd("fetch").
ArgIf(opts.RemoteName != "", opts.RemoteName).
ArgIf(opts.BranchName != "", opts.BranchName).
ToArgv()
cmdObj := self.cmd.New(cmdStr)
cmdObj := self.cmd.New(cmdArgs)
if opts.Background {
cmdObj.DontLog().FailOnCredentialRequest()
} else {
@ -77,31 +77,31 @@ type PullOptions struct {
}
func (self *SyncCommands) Pull(opts PullOptions) error {
cmdStr := NewGitCmd("pull").
cmdArgs := NewGitCmd("pull").
Arg("--no-edit").
ArgIf(opts.FastForwardOnly, "--ff-only").
ArgIf(opts.RemoteName != "", self.cmd.Quote(opts.RemoteName)).
ArgIf(opts.BranchName != "", self.cmd.Quote(opts.BranchName)).
ToString()
ArgIf(opts.RemoteName != "", opts.RemoteName).
ArgIf(opts.BranchName != "", opts.BranchName).
ToArgv()
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
return self.cmd.New(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) FastForward(branchName string, remoteName string, remoteBranchName string) error {
cmdStr := NewGitCmd("fetch").
Arg(self.cmd.Quote(remoteName)).
Arg(self.cmd.Quote(remoteBranchName) + ":" + self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("fetch").
Arg(remoteName).
Arg(remoteBranchName + ":" + branchName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) FetchRemote(remoteName string) error {
cmdStr := NewGitCmd("fetch").
Arg(self.cmd.Quote(remoteName)).
ToString()
cmdArgs := NewGitCmd("fetch").
Arg(remoteName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}

View File

@ -19,7 +19,7 @@ func TestSyncPush(t *testing.T) {
testName: "Push with force disabled",
opts: PushOpts{Force: false},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), "git push")
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
assert.NoError(t, err)
},
},
@ -27,7 +27,7 @@ func TestSyncPush(t *testing.T) {
testName: "Push with force enabled",
opts: PushOpts{Force: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), "git push --force-with-lease")
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.NoError(t, err)
},
},
@ -39,7 +39,7 @@ func TestSyncPush(t *testing.T) {
UpstreamBranch: "master",
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), `git push "origin" "master"`)
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "master"})
assert.NoError(t, err)
},
},
@ -52,7 +52,7 @@ func TestSyncPush(t *testing.T) {
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), `git push --set-upstream "origin" "master"`)
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "master"})
assert.NoError(t, err)
},
},
@ -65,7 +65,7 @@ func TestSyncPush(t *testing.T) {
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), `git push --force-with-lease --set-upstream "origin" "master"`)
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"})
assert.NoError(t, err)
},
},

View File

@ -11,32 +11,32 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
}
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
cmdStr := NewGitCmd("tag").Arg("--", self.cmd.Quote(tagName)).
ArgIf(len(ref) > 0, self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("tag").Arg("--", tagName).
ArgIf(len(ref) > 0, ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
cmdStr := NewGitCmd("tag").Arg(self.cmd.Quote(tagName)).
ArgIf(len(ref) > 0, self.cmd.Quote(ref)).
Arg("-m", self.cmd.Quote(msg)).
ToString()
cmdArgs := NewGitCmd("tag").Arg(tagName).
ArgIf(len(ref) > 0, ref).
Arg("-m", msg).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) Delete(tagName string) error {
cmdStr := NewGitCmd("tag").Arg("-d", self.cmd.Quote(tagName)).
ToString()
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) Push(remoteName string, tagName string) error {
cmdStr := NewGitCmd("push").Arg(self.cmd.Quote(remoteName), "tag", self.cmd.Quote(tagName)).
ToString()
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}

View File

@ -28,8 +28,8 @@ func NewTagLoader(
func (self *TagLoader) 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
cmdStr := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToString()
tagsOutput, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
cmdArgs := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToArgv()
tagsOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return nil, err
}

View File

@ -26,14 +26,14 @@ func TestGetTags(t *testing.T) {
{
testName: "should return no tags if there are none",
runner: oscommands.NewFakeRunner(t).
Expect(`git tag --list -n --sort=-creatordate`, "", nil),
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, "", nil),
expectedTags: []*models.Tag{},
expectedError: nil,
},
{
testName: "should return tags if present",
runner: oscommands.NewFakeRunner(t).
Expect(`git tag --list -n --sort=-creatordate`, tagsOutput, nil),
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, tagsOutput, nil),
expectedTags: []*models.Tag{
{Name: "tag1", Message: "this is my message"},
{Name: "tag2", Message: ""},

View File

@ -15,7 +15,7 @@ type GitVersion struct {
}
func GetGitVersion(osCommand *oscommands.OSCommand) (*GitVersion, error) {
versionStr, _, err := osCommand.Cmd.New(NewGitCmd("--version").ToString()).RunWithOutputs()
versionStr, _, err := osCommand.Cmd.New(NewGitCmd("--version").ToArgv()).RunWithOutputs()
if err != nil {
return nil, err
}

View File

@ -5,7 +5,6 @@ import (
"os"
"github.com/go-errors/errors"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
@ -29,7 +28,7 @@ func NewWorkingTreeCommands(
}
func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("mergetool").ToString())
return self.cmd.New(NewGitCmd("mergetool").ToArgv())
}
func (self *WorkingTreeCommands) OpenMergeTool() error {
@ -42,25 +41,21 @@ func (self *WorkingTreeCommands) StageFile(path string) error {
}
func (self *WorkingTreeCommands) StageFiles(paths []string) error {
quotedPaths := slices.Map(paths, func(path string) string {
return self.cmd.Quote(path)
})
cmdArgs := NewGitCmd("add").Arg("--").Arg(paths...).ToArgv()
cmdStr := NewGitCmd("add").Arg("--").Arg(quotedPaths...).ToString()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// StageAll stages all files
func (self *WorkingTreeCommands) StageAll() error {
cmdStr := NewGitCmd("add").Arg("-A").ToString()
cmdArgs := NewGitCmd("add").Arg("-A").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// UnstageAll unstages all files
func (self *WorkingTreeCommands) UnstageAll() error {
return self.cmd.New(NewGitCmd("reset").ToString()).Run()
return self.cmd.New(NewGitCmd("reset").ToArgv()).Run()
}
// UnStageFile unstages a file
@ -68,14 +63,14 @@ func (self *WorkingTreeCommands) UnstageAll() error {
// we accept the current name and the previous name
func (self *WorkingTreeCommands) UnStageFile(fileNames []string, reset bool) error {
for _, name := range fileNames {
var cmdStr string
var cmdArgs []string
if reset {
cmdStr = NewGitCmd("reset").Arg("HEAD", "--", self.cmd.Quote(name)).ToString()
cmdArgs = NewGitCmd("reset").Arg("HEAD", "--", name).ToArgv()
} else {
cmdStr = NewGitCmd("rm").Arg("--cached", "--force", "--", self.cmd.Quote(name)).ToString()
cmdArgs = NewGitCmd("rm").Arg("--cached", "--force", "--", name).ToArgv()
}
err := self.cmd.New(cmdStr).Run()
err := self.cmd.New(cmdArgs).Run()
if err != nil {
return err
}
@ -137,17 +132,15 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
return nil
}
quotedFileName := self.cmd.Quote(file.Name)
if file.ShortStatus == "AA" {
if err := self.cmd.New(
NewGitCmd("checkout").Arg("--ours", "--", quotedFileName).ToString(),
NewGitCmd("checkout").Arg("--ours", "--", file.Name).ToArgv(),
).Run(); err != nil {
return err
}
if err := self.cmd.New(
NewGitCmd("add").Arg("--", quotedFileName).ToString(),
NewGitCmd("add").Arg("--", file.Name).ToArgv(),
).Run(); err != nil {
return err
}
@ -156,14 +149,14 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
if file.ShortStatus == "DU" {
return self.cmd.New(
NewGitCmd("rm").Arg("rm", "--", quotedFileName).ToString(),
NewGitCmd("rm").Arg("rm", "--", file.Name).ToArgv(),
).Run()
}
// if the file isn't tracked, we assume you want to delete it
if file.HasStagedChanges || file.HasMergeConflicts {
if err := self.cmd.New(
NewGitCmd("reset").Arg("--", quotedFileName).ToString(),
NewGitCmd("reset").Arg("--", file.Name).ToArgv(),
).Run(); err != nil {
return err
}
@ -195,9 +188,8 @@ func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error
return err
}
quotedPath := self.cmd.Quote(node.GetPath())
cmdStr := NewGitCmd("checkout").Arg("--", quotedPath).ToString()
if err := self.cmd.New(cmdStr).Run(); err != nil {
cmdArgs := NewGitCmd("checkout").Arg("--", node.GetPath()).ToArgv()
if err := self.cmd.New(cmdArgs).Run(); err != nil {
return err
}
@ -221,9 +213,8 @@ func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
// DiscardUnstagedFileChanges directly
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
quotedFileName := self.cmd.Quote(file.Name)
cmdStr := NewGitCmd("checkout").Arg("--", quotedFileName).ToString()
return self.cmd.New(cmdStr).Run()
cmdArgs := NewGitCmd("checkout").Arg("--", file.Name).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// Ignore adds a file to the gitignore for the repo
@ -253,7 +244,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
prevPath := node.GetPreviousPath()
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
cmdStr := NewGitCmd("diff").
cmdArgs := NewGitCmd("diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
@ -263,11 +254,11 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
ArgIf(noIndex, "--no-index").
Arg("--").
ArgIf(noIndex, "/dev/null").
Arg(self.cmd.Quote(node.GetPath())).
ArgIf(prevPath != "", self.cmd.Quote(prevPath)).
ToString()
Arg(node.GetPath()).
ArgIf(prevPath != "", prevPath).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
@ -288,7 +279,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
colorArg = "never"
}
cmdStr := NewGitCmd("diff").
cmdArgs := NewGitCmd("diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
@ -299,41 +290,41 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
ArgIf(reverse, "-R").
ArgIf(ignoreWhitespace, "--ignore-all-space").
Arg("--").
Arg(self.cmd.Quote(fileName)).
ToString()
Arg(fileName).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
// CheckoutFile checks out the file for the given commit
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
cmdStr := NewGitCmd("checkout").Arg(commitSha, "--", self.cmd.Quote(fileName)).
ToString()
cmdArgs := NewGitCmd("checkout").Arg(commitSha, "--", fileName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// DiscardAnyUnstagedFileChanges discards any unstaged file changes via `git checkout -- .`
func (self *WorkingTreeCommands) DiscardAnyUnstagedFileChanges() error {
cmdStr := NewGitCmd("checkout").Arg("--", ".").
ToString()
cmdArgs := NewGitCmd("checkout").Arg("--", ".").
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
func (self *WorkingTreeCommands) RemoveTrackedFiles(name string) error {
cmdStr := NewGitCmd("rm").Arg("-r", "--cached", "--", self.cmd.Quote(name)).
ToString()
cmdArgs := NewGitCmd("rm").Arg("-r", "--cached", "--", name).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// RemoveUntrackedFiles runs `git clean -fd`
func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
cmdStr := NewGitCmd("clean").Arg("-fd").ToString()
cmdArgs := NewGitCmd("clean").Arg("-fd").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// ResetAndClean removes all unstaged changes and removes all untracked files
@ -358,23 +349,23 @@ func (self *WorkingTreeCommands) ResetAndClean() error {
// ResetHardHead runs `git reset --hard`
func (self *WorkingTreeCommands) ResetHard(ref string) error {
cmdStr := NewGitCmd("reset").Arg("--hard", self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("reset").Arg("--hard", ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// ResetSoft runs `git reset --soft HEAD`
func (self *WorkingTreeCommands) ResetSoft(ref string) error {
cmdStr := NewGitCmd("reset").Arg("--soft", self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("reset").Arg("--soft", ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *WorkingTreeCommands) ResetMixed(ref string) error {
cmdStr := NewGitCmd("reset").Arg("--mixed", self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("reset").Arg("--mixed", ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}

View File

@ -13,7 +13,7 @@ import (
func TestWorkingTreeStageFile(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(`git add -- "test.txt"`, "", nil)
ExpectGitArgs([]string{"add", "--", "test.txt"}, "", nil)
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
@ -23,7 +23,7 @@ func TestWorkingTreeStageFile(t *testing.T) {
func TestWorkingTreeStageFiles(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(`git add -- "test.txt" "test2.txt"`, "", nil)
ExpectGitArgs([]string{"add", "--", "test.txt", "test2.txt"}, "", nil)
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
@ -44,7 +44,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
testName: "Remove an untracked file from staging",
reset: false,
runner: oscommands.NewFakeRunner(t).
Expect(`git rm --cached --force -- "test.txt"`, "", nil),
ExpectGitArgs([]string{"rm", "--cached", "--force", "--", "test.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -53,7 +53,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
testName: "Remove a tracked file from staging",
reset: true,
runner: oscommands.NewFakeRunner(t).
Expect(`git reset HEAD -- "test.txt"`, "", nil),
ExpectGitArgs([]string{"reset", "HEAD", "--", "test.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -90,7 +90,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", errors.New("error")),
ExpectGitArgs([]string{"reset", "--", "test"}, "", errors.New("error")),
expectedError: "error",
},
{
@ -115,7 +115,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- "test"`, "", errors.New("error")),
ExpectGitArgs([]string{"checkout", "--", "test"}, "", errors.New("error")),
expectedError: "error",
},
{
@ -127,7 +127,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- "test"`, "", nil),
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
expectedError: "",
},
{
@ -139,8 +139,8 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", nil).
Expect(`git checkout -- "test"`, "", nil),
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
expectedError: "",
},
{
@ -152,8 +152,8 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", nil).
Expect(`git checkout -- "test"`, "", nil),
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
expectedError: "",
},
{
@ -169,7 +169,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
return nil
},
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", nil),
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil),
expectedError: "",
},
{
@ -231,7 +231,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "cached",
@ -245,7 +245,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --cached -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "plain",
@ -259,7 +259,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=never -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "File not tracked and file has no staged changes",
@ -273,7 +273,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --no-index -- /dev/null "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@ -287,7 +287,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: true,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --ignore-all-space -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@ -301,7 +301,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 17,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=17 --color=always -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
},
}
@ -343,7 +343,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --no-renames --color=always 1234567890 0987654321 -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@ -354,7 +354,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 123,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=123 --no-renames --color=always 1234567890 0987654321 -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@ -365,7 +365,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: true,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --no-renames --color=always 1234567890 0987654321 --ignore-all-space -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
},
}
@ -400,7 +400,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
commitSha: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout 11af912 -- "test999.txt"`, "", nil),
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -410,7 +410,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
commitSha: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout 11af912 -- "test999.txt"`, "", errors.New("error")),
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
test: func(err error) {
assert.Error(t, err)
},
@ -441,7 +441,7 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
testName: "valid case",
file: &models.File{Name: "test.txt"},
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- "test.txt"`, "", nil),
ExpectGitArgs([]string{"checkout", "--", "test.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -469,7 +469,7 @@ func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
{
testName: "valid case",
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- .`, "", nil),
ExpectGitArgs([]string{"checkout", "--", "."}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -497,7 +497,7 @@ func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) {
{
testName: "valid case",
runner: oscommands.NewFakeRunner(t).
Expect(`git clean -fd`, "", nil),
ExpectGitArgs([]string{"clean", "-fd"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@ -527,7 +527,7 @@ func TestWorkingTreeResetHard(t *testing.T) {
"valid case",
"HEAD",
oscommands.NewFakeRunner(t).
Expect(`git reset --hard "HEAD"`, "", nil),
ExpectGitArgs([]string{"reset", "--hard", "HEAD"}, "", nil),
func(err error) {
assert.NoError(t, err)
},