From 63dc07fdedec58ae5836a601d9c8839d0481eda6 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 21 May 2023 17:00:29 +1000 Subject: [PATCH] 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 --- pkg/app/app.go | 2 +- pkg/commands/git.go | 5 +- pkg/commands/git_cmd_obj_builder.go | 8 +- pkg/commands/git_commands/bisect.go | 22 +-- pkg/commands/git_commands/branch.go | 96 +++++------ pkg/commands/git_commands/branch_test.go | 47 +++--- pkg/commands/git_commands/commit.go | 110 ++++++------ .../git_commands/commit_file_loader.go | 6 +- pkg/commands/git_commands/commit_loader.go | 83 +++++----- .../git_commands/commit_loader_test.go | 48 +++--- pkg/commands/git_commands/commit_test.go | 63 +++---- pkg/commands/git_commands/custom.go | 4 +- pkg/commands/git_commands/diff.go | 19 +++ pkg/commands/git_commands/file.go | 2 +- pkg/commands/git_commands/file_loader.go | 6 +- pkg/commands/git_commands/file_loader_test.go | 13 +- pkg/commands/git_commands/file_test.go | 6 +- pkg/commands/git_commands/flow.go | 8 +- pkg/commands/git_commands/flow_test.go | 16 +- .../git_commands/git_command_builder.go | 8 +- .../git_commands/git_command_builder_test.go | 36 ++-- pkg/commands/git_commands/patch.go | 12 +- pkg/commands/git_commands/patch_test.go | 24 +-- pkg/commands/git_commands/rebase.go | 26 +-- pkg/commands/git_commands/rebase_test.go | 24 +-- .../git_commands/reflog_commit_loader.go | 14 +- .../git_commands/reflog_commit_loader_test.go | 10 +- pkg/commands/git_commands/remote.go | 48 +++--- pkg/commands/git_commands/remote_loader.go | 4 +- pkg/commands/git_commands/stash.go | 74 ++++----- pkg/commands/git_commands/stash_loader.go | 8 +- .../git_commands/stash_loader_test.go | 5 +- pkg/commands/git_commands/stash_test.go | 10 +- pkg/commands/git_commands/status.go | 2 +- pkg/commands/git_commands/submodule.go | 88 +++++----- pkg/commands/git_commands/sync.go | 48 +++--- pkg/commands/git_commands/sync_test.go | 10 +- pkg/commands/git_commands/tag.go | 30 ++-- pkg/commands/git_commands/tag_loader.go | 4 +- pkg/commands/git_commands/tag_loader_test.go | 4 +- pkg/commands/git_commands/version.go | 2 +- pkg/commands/git_commands/working_tree.go | 103 ++++++------ .../git_commands/working_tree_test.go | 54 +++--- pkg/commands/oscommands/cmd_obj.go | 26 ++- pkg/commands/oscommands/cmd_obj_builder.go | 30 ++-- pkg/commands/oscommands/cmd_obj_test.go | 33 ++++ .../oscommands/fake_cmd_obj_runner.go | 14 +- pkg/commands/oscommands/os.go | 15 +- pkg/commands/oscommands/os_default_test.go | 10 +- pkg/commands/oscommands/os_test.go | 8 +- pkg/gui/controllers/helpers/diff_helper.go | 22 ++- pkg/gui/controllers/helpers/gpg_helper.go | 5 +- pkg/gui/controllers/helpers/mode_helper.go | 3 +- .../controllers/local_commits_controller.go | 2 +- pkg/integration/components/git.go | 10 +- pkg/integration/components/runner.go | 15 +- pkg/integration/components/shell.go | 156 +++++++++++------- pkg/integration/components/test.go | 6 +- pkg/integration/tests/bisect/basic.go | 2 +- .../tests/bisect/from_other_branch.go | 4 +- .../tests/branch/checkout_by_name.go | 2 +- pkg/integration/tests/branch/create_tag.go | 2 +- pkg/integration/tests/branch/delete.go | 2 +- pkg/integration/tests/branch/detached_head.go | 2 +- .../tests/branch/open_with_cli_arg.go | 2 +- pkg/integration/tests/branch/rebase.go | 2 +- .../tests/branch/rebase_and_drop.go | 2 +- .../branch/rebase_does_not_autosquash.go | 2 +- pkg/integration/tests/branch/reset.go | 2 +- .../tests/branch/reset_upstream.go | 2 +- pkg/integration/tests/branch/set_upstream.go | 2 +- pkg/integration/tests/branch/suggestions.go | 2 +- .../tests/cherry_pick/cherry_pick.go | 2 +- .../cherry_pick/cherry_pick_conflicts.go | 2 +- pkg/integration/tests/commit/amend.go | 2 +- pkg/integration/tests/commit/commit.go | 2 +- .../tests/commit/commit_multiline.go | 2 +- .../tests/commit/commit_wip_with_prefix.go | 2 +- .../tests/commit/commit_with_prefix.go | 2 +- pkg/integration/tests/commit/create_tag.go | 2 +- .../tests/commit/discard_old_file_change.go | 2 +- pkg/integration/tests/commit/history.go | 2 +- .../tests/commit/history_complex.go | 2 +- pkg/integration/tests/commit/new_branch.go | 2 +- pkg/integration/tests/commit/reset_author.go | 2 +- pkg/integration/tests/commit/revert.go | 2 +- pkg/integration/tests/commit/revert_merge.go | 2 +- pkg/integration/tests/commit/reword.go | 2 +- pkg/integration/tests/commit/search.go | 2 +- pkg/integration/tests/commit/set_author.go | 2 +- .../tests/commit/stage_range_of_lines.go | 2 +- pkg/integration/tests/commit/staged.go | 2 +- .../tests/commit/staged_without_hooks.go | 2 +- pkg/integration/tests/commit/unstaged.go | 2 +- .../tests/config/remote_named_star.go | 2 +- pkg/integration/tests/conflicts/filter.go | 2 +- .../tests/conflicts/resolve_externally.go | 2 +- .../tests/conflicts/resolve_multiple_files.go | 2 +- .../tests/conflicts/undo_choose_hunk.go | 2 +- .../custom_commands/basic_cmd_at_runtime.go | 2 +- .../custom_commands/basic_cmd_from_config.go | 2 +- .../custom_commands/complex_cmd_at_runtime.go | 35 ++++ .../tests/custom_commands/form_prompts.go | 2 +- .../custom_commands/menu_from_command.go | 2 +- .../menu_from_commands_output.go | 2 +- .../tests/custom_commands/multiple_prompts.go | 2 +- .../custom_commands/omit_from_history.go | 2 +- pkg/integration/tests/diff/diff.go | 2 +- .../tests/diff/diff_and_apply_patch.go | 2 +- pkg/integration/tests/diff/diff_commits.go | 2 +- .../tests/diff/ignore_whitespace.go | 2 +- .../tests/file/dir_with_untracked_file.go | 2 +- pkg/integration/tests/file/discard_changes.go | 4 +- .../tests/file/discard_staged_changes.go | 2 +- pkg/integration/tests/file/gitignore.go | 2 +- .../remember_commit_message_after_fail.go | 4 +- .../tests/filter_by_path/cli_arg.go | 2 +- .../tests/filter_by_path/select_file.go | 2 +- .../tests/filter_by_path/type_file.go | 2 +- .../advanced_interactive_rebase.go | 2 +- .../interactive_rebase/amend_first_commit.go | 2 +- .../interactive_rebase/amend_fixup_commit.go | 2 +- .../amend_head_commit_during_rebase.go | 2 +- .../tests/interactive_rebase/amend_merge.go | 2 +- .../amend_non_head_commit_during_rebase.go | 2 +- .../drop_todo_commit_with_update_ref.go | 2 +- ...ommit_with_update_ref_show_branch_heads.go | 2 +- .../interactive_rebase/edit_first_commit.go | 2 +- .../edit_non_todo_commit_during_rebase.go | 2 +- .../interactive_rebase/fixup_first_commit.go | 2 +- .../interactive_rebase/fixup_second_commit.go | 2 +- .../tests/interactive_rebase/move.go | 2 +- .../interactive_rebase/move_in_rebase.go | 2 +- .../tests/interactive_rebase/rebase.go | 2 +- .../interactive_rebase/reword_first_commit.go | 2 +- .../interactive_rebase/reword_last_commit.go | 2 +- .../reword_you_are_here_commit.go | 2 +- .../reword_you_are_here_commit_with_editor.go | 2 +- .../squash_down_first_commit.go | 2 +- .../squash_down_second_commit.go | 2 +- .../squash_fixups_above_first_commit.go | 2 +- .../swap_in_rebase_with_conflict.go | 2 +- .../interactive_rebase/swap_with_conflict.go | 2 +- pkg/integration/tests/misc/confirm_on_quit.go | 2 +- pkg/integration/tests/misc/initial_open.go | 2 +- pkg/integration/tests/patch_building/apply.go | 2 +- .../tests/patch_building/apply_in_reverse.go | 2 +- .../apply_in_reverse_with_conflict.go | 2 +- .../patch_building/copy_patch_to_clipboard.go | 2 +- .../patch_building/move_to_earlier_commit.go | 2 +- .../move_to_earlier_commit_no_keep_empty.go | 2 +- .../tests/patch_building/move_to_index.go | 2 +- ...e_to_index_part_of_adjacent_added_lines.go | 2 +- .../patch_building/move_to_index_partial.go | 2 +- .../move_to_index_with_conflict.go | 2 +- .../patch_building/move_to_later_commit.go | 2 +- .../move_to_later_commit_partial_hunk.go | 2 +- .../patch_building/move_to_new_commit.go | 2 +- .../move_to_new_commit_partial_hunk.go | 2 +- .../patch_building/remove_from_commit.go | 2 +- .../tests/patch_building/reset_with_escape.go | 2 +- .../tests/patch_building/select_all_files.go | 2 +- .../patch_building/specific_selection.go | 2 +- .../tests/patch_building/start_new_patch.go | 2 +- pkg/integration/tests/reflog/checkout.go | 2 +- pkg/integration/tests/reflog/cherry_pick.go | 2 +- pkg/integration/tests/reflog/patch.go | 2 +- pkg/integration/tests/reflog/reset.go | 2 +- pkg/integration/tests/shared/conflicts.go | 6 +- .../tests/staging/diff_context_change.go | 2 +- .../tests/staging/discard_all_changes.go | 2 +- pkg/integration/tests/staging/search.go | 2 +- pkg/integration/tests/staging/stage_hunks.go | 2 +- pkg/integration/tests/staging/stage_lines.go | 2 +- pkg/integration/tests/staging/stage_ranges.go | 2 +- pkg/integration/tests/stash/apply.go | 2 +- pkg/integration/tests/stash/apply_patch.go | 2 +- pkg/integration/tests/stash/create_branch.go | 2 +- pkg/integration/tests/stash/drop.go | 2 +- pkg/integration/tests/stash/pop.go | 2 +- pkg/integration/tests/stash/rename.go | 2 +- pkg/integration/tests/stash/stash.go | 2 +- pkg/integration/tests/stash/stash_all.go | 2 +- .../tests/stash/stash_and_keep_index.go | 2 +- .../stash/stash_including_untracked_files.go | 2 +- pkg/integration/tests/stash/stash_staged.go | 2 +- pkg/integration/tests/stash/stash_unstaged.go | 2 +- pkg/integration/tests/submodule/add.go | 2 +- pkg/integration/tests/submodule/enter.go | 2 +- pkg/integration/tests/submodule/remove.go | 2 +- pkg/integration/tests/submodule/reset.go | 2 +- pkg/integration/tests/sync/fetch_prune.go | 2 +- pkg/integration/tests/sync/force_push.go | 2 +- .../sync/force_push_multiple_matching.go | 2 +- .../sync/force_push_multiple_upstream.go | 2 +- pkg/integration/tests/sync/pull.go | 2 +- .../tests/sync/pull_and_set_upstream.go | 2 +- pkg/integration/tests/sync/pull_merge.go | 2 +- .../tests/sync/pull_merge_conflict.go | 2 +- pkg/integration/tests/sync/pull_rebase.go | 2 +- .../tests/sync/pull_rebase_conflict.go | 2 +- .../sync/pull_rebase_interactive_conflict.go | 2 +- .../pull_rebase_interactive_conflict_drop.go | 2 +- pkg/integration/tests/sync/push.go | 2 +- .../tests/sync/push_and_auto_set_upstream.go | 2 +- .../tests/sync/push_and_set_upstream.go | 2 +- .../tests/sync/push_follow_tags.go | 2 +- .../tests/sync/push_no_follow_tags.go | 2 +- pkg/integration/tests/sync/push_tag.go | 2 +- .../tests/sync/push_with_credential_prompt.go | 2 +- .../tests/sync/rename_branch_and_pull.go | 2 +- pkg/integration/tests/tag/checkout.go | 2 +- pkg/integration/tests/tag/crud_annotated.go | 2 +- pkg/integration/tests/tag/crud_lightweight.go | 2 +- pkg/integration/tests/tag/reset.go | 2 +- pkg/integration/tests/test_list.go | 1 + pkg/integration/tests/ui/double_popup.go | 2 +- .../tests/ui/switch_tab_from_menu.go | 2 +- .../tests/undo/undo_checkout_and_drop.go | 2 +- pkg/integration/tests/undo/undo_drop.go | 2 +- pkg/updates/updates.go | 2 +- 221 files changed, 1050 insertions(+), 885 deletions(-) create mode 100644 pkg/commands/git_commands/diff.go create mode 100644 pkg/commands/oscommands/cmd_obj_test.go create mode 100644 pkg/integration/tests/custom_commands/complex_cmd_at_runtime.go diff --git a/pkg/app/app.go b/pkg/app/app.go index 5d0359d17..af34b7157 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -191,7 +191,7 @@ func (app *App) setupRepo() (bool, error) { } if shouldInitRepo { - if err := app.OSCommand.Cmd.New("git init " + initialBranchArg).Run(); err != nil { + if err := app.OSCommand.Cmd.New([]string{"git", "init", initialBranchArg}).Run(); err != nil { return false, err } return false, nil diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 95e46086f..2ae8694e7 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -24,6 +24,7 @@ type GitCommand struct { Commit *git_commands.CommitCommands Config *git_commands.ConfigCommands Custom *git_commands.CustomCommands + Diff *git_commands.DiffCommands File *git_commands.FileCommands Flow *git_commands.FlowCommands Patch *git_commands.PatchCommands @@ -112,6 +113,7 @@ func NewGitCommandAux( tagCommands := git_commands.NewTagCommands(gitCommon) commitCommands := git_commands.NewCommitCommands(gitCommon) customCommands := git_commands.NewCustomCommands(gitCommon) + diffCommands := git_commands.NewDiffCommands(gitCommon) fileCommands := git_commands.NewFileCommands(gitCommon) submoduleCommands := git_commands.NewSubmoduleCommands(gitCommon) workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader) @@ -139,6 +141,7 @@ func NewGitCommandAux( Commit: commitCommands, Config: configCommands, Custom: customCommands, + Diff: diffCommands, File: fileCommands, Flow: flowCommands, Patch: patchCommands, @@ -274,5 +277,5 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam } func VerifyInGitRepo(osCommand *oscommands.OSCommand) error { - return osCommand.Cmd.New("git rev-parse --git-dir").DontLog().Run() + return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run() } diff --git a/pkg/commands/git_cmd_obj_builder.go b/pkg/commands/git_cmd_obj_builder.go index 487dd2303..21f300bb3 100644 --- a/pkg/commands/git_cmd_obj_builder.go +++ b/pkg/commands/git_cmd_obj_builder.go @@ -30,12 +30,8 @@ func NewGitCmdObjBuilder(log *logrus.Entry, innerBuilder *oscommands.CmdObjBuild var defaultEnvVar = "GIT_OPTIONAL_LOCKS=0" -func (self *gitCmdObjBuilder) New(cmdStr string) oscommands.ICmdObj { - return self.innerBuilder.New(cmdStr).AddEnvVars(defaultEnvVar) -} - -func (self *gitCmdObjBuilder) NewFromArgs(args []string) oscommands.ICmdObj { - return self.innerBuilder.NewFromArgs(args).AddEnvVars(defaultEnvVar) +func (self *gitCmdObjBuilder) New(args []string) oscommands.ICmdObj { + return self.innerBuilder.New(args).AddEnvVars(defaultEnvVar) } func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj { diff --git a/pkg/commands/git_commands/bisect.go b/pkg/commands/git_commands/bisect.go index 898151d9c..101f2037b 100644 --- a/pkg/commands/git_commands/bisect.go +++ b/pkg/commands/git_commands/bisect.go @@ -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 } diff --git a/pkg/commands/git_commands/branch.go b/pkg/commands/git_commands/branch.go index 0952e59e1..3c4d97c82 100644 --- a/pkg/commands/git_commands/branch.go +++ b/pkg/commands/git_commands/branch.go @@ -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() } diff --git a/pkg/commands/git_commands/branch_test.go b/pkg/commands/git_commands/branch_test.go index 75c288203..dee2b03c8 100644 --- a/pkg/commands/git_commands/branch_test.go +++ b/pkg/commands/git_commands/branch_test.go @@ -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) diff --git a/pkg/commands/git_commands/commit.go b/pkg/commands/git_commands/commit.go index bca68c3ac..f6002bbfd 100644 --- a/pkg/commands/git_commands/commit.go +++ b/pkg/commands/git_commands/commit.go @@ -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 ' 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 diff --git a/pkg/commands/git_commands/commit_file_loader.go b/pkg/commands/git_commands/commit_file_loader.go index 53ca046ba..d89c3c578 100644 --- a/pkg/commands/git_commands/commit_file_loader.go +++ b/pkg/commands/git_commands/commit_file_loader.go @@ -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 } diff --git a/pkg/commands/git_commands/commit_loader.go b/pkg/commands/git_commands/commit_loader.go index 00a468ab2..777f13d5c 100644 --- a/pkg/commands/git_commands/commit_loader.go +++ b/pkg/commands/git_commands/commit_loader.go @@ -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` diff --git a/pkg/commands/git_commands/commit_loader_test.go b/pkg/commands/git_commands/commit_loader_test.go index 0b2df681c..a1a2c7e41 100644 --- a/pkg/commands/git_commands/commit_loader_test.go +++ b/pkg/commands/git_commands/commit_loader_test.go @@ -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, diff --git a/pkg/commands/git_commands/commit_test.go b/pkg/commands/git_commands/commit_test.go index 4cc8a8de2..7ce9b7a37 100644 --- a/pkg/commands/git_commands/commit_test.go +++ b/pkg/commands/git_commands/commit_test.go @@ -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) diff --git a/pkg/commands/git_commands/custom.go b/pkg/commands/git_commands/custom.go index 6dff6e771..ac8b0b1c9 100644 --- a/pkg/commands/git_commands/custom.go +++ b/pkg/commands/git_commands/custom.go @@ -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() } diff --git a/pkg/commands/git_commands/diff.go b/pkg/commands/git_commands/diff.go new file mode 100644 index 000000000..2f0e1b547 --- /dev/null +++ b/pkg/commands/git_commands/diff.go @@ -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(), + ) +} diff --git a/pkg/commands/git_commands/file.go b/pkg/commands/git_commands/file.go index 94563be65..6c90b91f2 100644 --- a/pkg/commands/git_commands/file.go +++ b/pkg/commands/git_commands/file.go @@ -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" } } diff --git a/pkg/commands/git_commands/file_loader.go b/pkg/commands/git_commands/file_loader.go index dbd625683..b33588475 100644 --- a/pkg/commands/git_commands/file_loader.go +++ b/pkg/commands/git_commands/file_loader.go @@ -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 } diff --git a/pkg/commands/git_commands/file_loader_test.go b/pkg/commands/git_commands/file_loader_test.go index fac93eb52..02ea390a7 100644 --- a/pkg/commands/git_commands/file_loader_test.go +++ b/pkg/commands/git_commands/file_loader_test.go @@ -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, ), diff --git a/pkg/commands/git_commands/file_test.go b/pkg/commands/git_commands/file_test.go index 2367e4fcb..1141a3577 100644 --- a/pkg/commands/git_commands/file_test.go +++ b/pkg/commands/git_commands/file_test.go @@ -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 "" }, diff --git a/pkg/commands/git_commands/flow.go b/pkg/commands/git_commands/flow.go index fab67d81b..725acbb30 100644 --- a/pkg/commands/git_commands/flow.go +++ b/pkg/commands/git_commands/flow.go @@ -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) } diff --git a/pkg/commands/git_commands/flow_test.go b/pkg/commands/git_commands/flow_test.go index 3ee2e91a5..21729b5a8 100644 --- a/pkg/commands/git_commands/flow_test.go +++ b/pkg/commands/git_commands/flow_test.go @@ -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) } }) } diff --git a/pkg/commands/git_commands/git_command_builder.go b/pkg/commands/git_commands/git_command_builder.go index da0859c2c..bb97f2dc6 100644 --- a/pkg/commands/git_commands/git_command_builder.go +++ b/pkg/commands/git_commands/git_command_builder.go @@ -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(), " ") } diff --git a/pkg/commands/git_commands/git_command_builder_test.go b/pkg/commands/git_commands/git_command_builder_test.go index d3791388c..becee087c 100644 --- a/pkg/commands/git_commands/git_command_builder_test.go +++ b/pkg/commands/git_commands/git_command_builder_test.go @@ -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"}, }, } diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index 3ee8fa362..942785314 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -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() } diff --git a/pkg/commands/git_commands/patch_test.go b/pkg/commands/git_commands/patch_test.go index d6ff28075..8acf471d4 100644 --- a/pkg/commands/git_commands/patch_test.go +++ b/pkg/commands/git_commands/patch_test.go @@ -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() }) } diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index 60386b2ed..48bf706e6 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -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 } diff --git a/pkg/commands/git_commands/rebase_test.go b/pkg/commands/git_commands/rebase_test.go index dd768d120..b616ec609 100644 --- a/pkg/commands/git_commands/rebase_test.go +++ b/pkg/commands/git_commands/rebase_test.go @@ -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) }, diff --git a/pkg/commands/git_commands/reflog_commit_loader.go b/pkg/commands/git_commands/reflog_commit_loader.go index c5c14fb72..5033b03c1 100644 --- a/pkg/commands/git_commands/reflog_commit_loader.go +++ b/pkg/commands/git_commands/reflog_commit_loader.go @@ -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) diff --git a/pkg/commands/git_commands/reflog_commit_loader_test.go b/pkg/commands/git_commands/reflog_commit_loader_test.go index a51a40e69..c17f7e2ba 100644 --- a/pkg/commands/git_commands/reflog_commit_loader_test.go +++ b/pkg/commands/git_commands/reflog_commit_loader_test.go @@ -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: "", diff --git a/pkg/commands/git_commands/remote.go b/pkg/commands/git_commands/remote.go index 4be4350ef..b594db28c 100644 --- a/pkg/commands/git_commands/remote.go +++ b/pkg/commands/git_commands/remote.go @@ -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 } diff --git a/pkg/commands/git_commands/remote_loader.go b/pkg/commands/git_commands/remote_loader.go index d8fffff13..553a8f0c0 100644 --- a/pkg/commands/git_commands/remote_loader.go +++ b/pkg/commands/git_commands/remote_loader.go @@ -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 } diff --git a/pkg/commands/git_commands/stash.go b/pkg/commands/git_commands/stash.go index 0033883a5..86018fd8d 100644 --- a/pkg/commands/git_commands/stash.go +++ b/pkg/commands/git_commands/stash.go @@ -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() } diff --git a/pkg/commands/git_commands/stash_loader.go b/pkg/commands/git_commands/stash_loader.go index 580287256..e5ea1da49 100644 --- a/pkg/commands/git_commands/stash_loader.go +++ b/pkg/commands/git_commands/stash_loader.go @@ -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) }) diff --git a/pkg/commands/git_commands/stash_loader_test.go b/pkg/commands/git_commands/stash_loader_test.go index 2e2180fba..d48b83be7 100644 --- a/pkg/commands/git_commands/stash_loader_test.go +++ b/pkg/commands/git_commands/stash_loader_test.go @@ -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, ), diff --git a/pkg/commands/git_commands/stash_test.go b/pkg/commands/git_commands/stash_test.go index 2fb03b90e..d19c0a519 100644 --- a/pkg/commands/git_commands/stash_test.go +++ b/pkg/commands/git_commands/stash_test.go @@ -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) }) } diff --git a/pkg/commands/git_commands/status.go b/pkg/commands/git_commands/status.go index 9a1c19b7e..7f03c698e 100644 --- a/pkg/commands/git_commands/status.go +++ b/pkg/commands/git_commands/status.go @@ -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 diff --git a/pkg/commands/git_commands/submodule.go b/pkg/commands/git_commands/submodule.go index a19e0ffe8..ca3a23ef5 100644 --- a/pkg/commands/git_commands/submodule.go +++ b/pkg/commands/git_commands/submodule.go @@ -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 { diff --git a/pkg/commands/git_commands/sync.go b/pkg/commands/git_commands/sync.go index 967fd8d9e..6b336d175 100644 --- a/pkg/commands/git_commands/sync.go +++ b/pkg/commands/git_commands/sync.go @@ -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() } diff --git a/pkg/commands/git_commands/sync_test.go b/pkg/commands/git_commands/sync_test.go index 9c33381fe..23058eb92 100644 --- a/pkg/commands/git_commands/sync_test.go +++ b/pkg/commands/git_commands/sync_test.go @@ -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) }, }, diff --git a/pkg/commands/git_commands/tag.go b/pkg/commands/git_commands/tag.go index ae6b816f5..f399a578a 100644 --- a/pkg/commands/git_commands/tag.go +++ b/pkg/commands/git_commands/tag.go @@ -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() } diff --git a/pkg/commands/git_commands/tag_loader.go b/pkg/commands/git_commands/tag_loader.go index 67d26f80b..f69ebf79a 100644 --- a/pkg/commands/git_commands/tag_loader.go +++ b/pkg/commands/git_commands/tag_loader.go @@ -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 } diff --git a/pkg/commands/git_commands/tag_loader_test.go b/pkg/commands/git_commands/tag_loader_test.go index 61b55d018..59c4d0337 100644 --- a/pkg/commands/git_commands/tag_loader_test.go +++ b/pkg/commands/git_commands/tag_loader_test.go @@ -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: ""}, diff --git a/pkg/commands/git_commands/version.go b/pkg/commands/git_commands/version.go index dcf624679..a089d7e06 100644 --- a/pkg/commands/git_commands/version.go +++ b/pkg/commands/git_commands/version.go @@ -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 } diff --git a/pkg/commands/git_commands/working_tree.go b/pkg/commands/git_commands/working_tree.go index e7ab69d6b..6269ce21a 100644 --- a/pkg/commands/git_commands/working_tree.go +++ b/pkg/commands/git_commands/working_tree.go @@ -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() } diff --git a/pkg/commands/git_commands/working_tree_test.go b/pkg/commands/git_commands/working_tree_test.go index 6d084bfbe..3a1f2a9da 100644 --- a/pkg/commands/git_commands/working_tree_test.go +++ b/pkg/commands/git_commands/working_tree_test.go @@ -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) }, diff --git a/pkg/commands/oscommands/cmd_obj.go b/pkg/commands/oscommands/cmd_obj.go index 47a227f17..b1223ea00 100644 --- a/pkg/commands/oscommands/cmd_obj.go +++ b/pkg/commands/oscommands/cmd_obj.go @@ -2,7 +2,9 @@ package oscommands import ( "os/exec" + "strings" + "github.com/samber/lo" "github.com/sasha-s/go-deadlock" ) @@ -15,6 +17,9 @@ type ICmdObj interface { // into a terminal e.g. 'sh -c git commit' as opposed to 'sh -c "git commit"' ToString() string + // outputs args vector e.g. ["git", "commit", "-m", "my message"] + Args() []string + AddEnvVars(...string) ICmdObj GetEnvVars() []string @@ -61,8 +66,11 @@ type ICmdObj interface { } type CmdObj struct { - cmdStr string - cmd *exec.Cmd + // the secureexec package will swap out the first arg with the full path to the binary, + // so we store these args separately so that ToString() will output the original + args []string + + cmd *exec.Cmd runner ICmdObjRunner @@ -104,7 +112,19 @@ func (self *CmdObj) GetCmd() *exec.Cmd { } func (self *CmdObj) ToString() string { - return self.cmdStr + // if a given arg contains a space, we need to wrap it in quotes + quotedArgs := lo.Map(self.args, func(arg string, _ int) string { + if strings.Contains(arg, " ") { + return `"` + arg + `"` + } + return arg + }) + + return strings.Join(quotedArgs, " ") +} + +func (self *CmdObj) Args() []string { + return self.args } func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj { diff --git a/pkg/commands/oscommands/cmd_obj_builder.go b/pkg/commands/oscommands/cmd_obj_builder.go index ef27bd4c0..40aadaf1d 100644 --- a/pkg/commands/oscommands/cmd_obj_builder.go +++ b/pkg/commands/oscommands/cmd_obj_builder.go @@ -10,12 +10,10 @@ import ( ) type ICmdObjBuilder interface { - // New returns a new command object based on the string provided - New(cmdStr string) ICmdObj + // NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object. + New(args []string) ICmdObj // NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'` NewShell(commandStr string) ICmdObj - // NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object. This can be useful when you don't want to worry about whitespace and quoting and stuff. - NewFromArgs(args []string) ICmdObj // Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects. Quote(str string) string } @@ -28,24 +26,12 @@ type CmdObjBuilder struct { // poor man's version of explicitly saying that struct X implements interface Y var _ ICmdObjBuilder = &CmdObjBuilder{} -func (self *CmdObjBuilder) New(cmdStr string) ICmdObj { - args := str.ToArgv(cmdStr) +func (self *CmdObjBuilder) New(args []string) ICmdObj { cmd := secureexec.Command(args[0], args[1:]...) cmd.Env = os.Environ() return &CmdObj{ - cmdStr: cmdStr, - cmd: cmd, - runner: self.runner, - } -} - -func (self *CmdObjBuilder) NewFromArgs(args []string) ICmdObj { - cmd := secureexec.Command(args[0], args[1:]...) - cmd.Env = os.Environ() - - return &CmdObj{ - cmdStr: strings.Join(args, " "), + args: args, cmd: cmd, runner: self.runner, } @@ -67,8 +53,9 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj { quotedCommand = self.Quote(commandStr) } - shellCommand := fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand) - return self.New(shellCommand) + cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand)) + + return self.New(cmdArgs) } func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder { @@ -80,6 +67,9 @@ func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdO } } +const CHARS_REQUIRING_QUOTES = "\"\\$` " + +// If you update this method, be sure to update CHARS_REQUIRING_QUOTES func (self *CmdObjBuilder) Quote(message string) string { var quote string if self.platform.OS == "windows" { diff --git a/pkg/commands/oscommands/cmd_obj_test.go b/pkg/commands/oscommands/cmd_obj_test.go new file mode 100644 index 000000000..dc04311a3 --- /dev/null +++ b/pkg/commands/oscommands/cmd_obj_test.go @@ -0,0 +1,33 @@ +package oscommands + +import ( + "testing" +) + +func TestCmdObjToString(t *testing.T) { + quote := func(s string) string { + return "\"" + s + "\"" + } + + scenarios := []struct { + cmdArgs []string + expected string + }{ + { + cmdArgs: []string{"git", "push", "myfile.txt"}, + expected: "git push myfile.txt", + }, + { + cmdArgs: []string{"git", "push", "my file.txt"}, + expected: "git push \"my file.txt\"", + }, + } + + for _, scenario := range scenarios { + cmdObj := &CmdObj{args: scenario.cmdArgs} + actual := cmdObj.ToString() + if actual != scenario.expected { + t.Errorf("Expected %s, got %s", quote(scenario.expected), quote(actual)) + } + } +} diff --git a/pkg/commands/oscommands/fake_cmd_obj_runner.go b/pkg/commands/oscommands/fake_cmd_obj_runner.go index f553d8c63..fa34f01be 100644 --- a/pkg/commands/oscommands/fake_cmd_obj_runner.go +++ b/pkg/commands/oscommands/fake_cmd_obj_runner.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "regexp" + "runtime" "strings" "testing" @@ -91,7 +92,18 @@ func (self *FakeCmdObjRunner) Expect(expectedCmdStr string, output string, err e func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner { self.ExpectFunc(func(cmdObj ICmdObj) (string, error) { args := cmdObj.GetCmd().Args - assert.EqualValues(self.t, expectedArgs, args, fmt.Sprintf("command %d did not match expectation", self.expectedCmdIndex+1)) + + if runtime.GOOS == "windows" { + // thanks to the secureexec package, the first arg is something like + // '"C:\\Program Files\\Git\\mingw64\\bin\\.exe" + // on windows so we'll just ensure it contains our program + assert.Contains(self.t, args[0], expectedArgs[0]) + } else { + // first arg is the program name + assert.Equal(self.t, expectedArgs[0], args[0]) + } + + assert.EqualValues(self.t, expectedArgs[1:], args[1:], fmt.Sprintf("command %d did not match expectation", self.expectedCmdIndex+1)) return output, err }) diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go index c8f38843b..78dad7a8e 100644 --- a/pkg/commands/oscommands/os.go +++ b/pkg/commands/oscommands/os.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/go-errors/errors" + "github.com/samber/lo" "github.com/atotto/clipboard" "github.com/jesseduffield/generics/slices" @@ -187,12 +188,18 @@ func (c *OSCommand) FileExists(path string) (bool, error) { } // PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C -func (c *OSCommand) PipeCommands(commandStrings ...string) error { - cmds := slices.Map(commandStrings, func(cmdString string) *exec.Cmd { - return c.Cmd.New(cmdString).GetCmd() +func (c *OSCommand) PipeCommands(cmdObjs ...ICmdObj) error { + cmds := slices.Map(cmdObjs, func(cmdObj ICmdObj) *exec.Cmd { + return cmdObj.GetCmd() }) - logCmdStr := strings.Join(commandStrings, " | ") + logCmdStr := strings.Join( + lo.Map(cmdObjs, func(cmdObj ICmdObj, _ int) string { + return cmdObj.ToString() + }), + " | ", + ) + c.LogCommand(logCmdStr, true) for i := 0; i < len(cmds)-1; i++ { diff --git a/pkg/commands/oscommands/os_default_test.go b/pkg/commands/oscommands/os_default_test.go index 1f534f6d3..7291b2d3a 100644 --- a/pkg/commands/oscommands/os_default_test.go +++ b/pkg/commands/oscommands/os_default_test.go @@ -12,20 +12,20 @@ import ( func TestOSCommandRunWithOutput(t *testing.T) { type scenario struct { - command string - test func(string, error) + args []string + test func(string, error) } scenarios := []scenario{ { - "echo -n '123'", + []string{"echo", "-n", "123"}, func(output string, err error) { assert.NoError(t, err) assert.EqualValues(t, "123", output) }, }, { - "rmdir unexisting-folder", + []string{"rmdir", "unexisting-folder"}, func(output string, err error) { assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error()) }, @@ -34,7 +34,7 @@ func TestOSCommandRunWithOutput(t *testing.T) { for _, s := range scenarios { c := NewDummyOSCommand() - s.test(c.Cmd.New(s.command).RunWithOutput()) + s.test(c.Cmd.New(s.args).RunWithOutput()) } } diff --git a/pkg/commands/oscommands/os_test.go b/pkg/commands/oscommands/os_test.go index e9fc91424..fbae7685e 100644 --- a/pkg/commands/oscommands/os_test.go +++ b/pkg/commands/oscommands/os_test.go @@ -10,13 +10,13 @@ import ( func TestOSCommandRun(t *testing.T) { type scenario struct { - command string - test func(error) + args []string + test func(error) } scenarios := []scenario{ { - "rmdir unexisting-folder", + []string{"rmdir", "unexisting-folder"}, func(err error) { assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error()) }, @@ -25,7 +25,7 @@ func TestOSCommandRun(t *testing.T) { for _, s := range scenarios { c := NewDummyOSCommand() - s.test(c.Cmd.New(s.command).Run()) + s.test(c.Cmd.New(s.args).Run()) } } diff --git a/pkg/gui/controllers/helpers/diff_helper.go b/pkg/gui/controllers/helpers/diff_helper.go index 8d18be2bf..3cf4f5735 100644 --- a/pkg/gui/controllers/helpers/diff_helper.go +++ b/pkg/gui/controllers/helpers/diff_helper.go @@ -1,8 +1,6 @@ package helpers import ( - "fmt" - "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/modes/diffing" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -19,27 +17,29 @@ func NewDiffHelper(c *HelperCommon) *DiffHelper { } } -func (self *DiffHelper) DiffStr() string { - output := self.c.Modes().Diffing.Ref +func (self *DiffHelper) DiffArgs() []string { + output := []string{self.c.Modes().Diffing.Ref} right := self.currentDiffTerminal() if right != "" { - output += " " + right + output = append(output, right) } if self.c.Modes().Diffing.Reverse { - output += " -R" + output = append(output, "-R") } if self.c.State().GetIgnoreWhitespaceInDiffView() { - output += " --ignore-all-space" + output = append(output, "--ignore-all-space") } + output = append(output, "--") + file := self.currentlySelectedFilename() if file != "" { - output += " -- " + file + output = append(output, file) } else if self.c.Modes().Filtering.Active() { - output += " -- " + self.c.Modes().Filtering.GetPath() + output = append(output, self.c.Modes().Filtering.GetPath()) } return output @@ -51,9 +51,7 @@ func (self *DiffHelper) ExitDiffMode() error { } func (self *DiffHelper) RenderDiff() error { - cmdObj := self.c.OS().Cmd.New( - fmt.Sprintf("git diff --submodule --no-ext-diff --color %s", self.DiffStr()), - ) + cmdObj := self.c.Git().Diff.DiffCmdObj(self.DiffArgs()) task := types.NewRunPtyTask(cmdObj.GetCmd()) return self.c.RenderToMainViews(types.RefreshMainOpts{ diff --git a/pkg/gui/controllers/helpers/gpg_helper.go b/pkg/gui/controllers/helpers/gpg_helper.go index 0cefc4208..45d67faaf 100644 --- a/pkg/gui/controllers/helpers/gpg_helper.go +++ b/pkg/gui/controllers/helpers/gpg_helper.go @@ -21,11 +21,10 @@ func NewGpgHelper(c *HelperCommon) *GpgHelper { // WithWaitingStatus we get stuck there and can't return to lazygit. We could // fix this bug, or just stop running subprocesses from within there, given that // we don't need to see a loading status if we're in a subprocess. -// TODO: we shouldn't need to use a shell here, but looks like that NewShell function contains some windows specific quoting stuff. We should centralise that. func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error { useSubprocess := self.c.Git().Config.UsingGpg() if useSubprocess { - success, err := self.c.RunSubprocess(self.c.OS().Cmd.NewShell(cmdObj.ToString())) + success, err := self.c.RunSubprocess(cmdObj) if success && onSuccess != nil { if err := onSuccess(); err != nil { return err @@ -42,8 +41,6 @@ func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus } func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error { - cmdObj = self.c.OS().Cmd.NewShell(cmdObj.ToString()) - return self.c.WithWaitingStatus(waitingStatus, func() error { if err := cmdObj.StreamOutput().Run(); err != nil { _ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC}) diff --git a/pkg/gui/controllers/helpers/mode_helper.go b/pkg/gui/controllers/helpers/mode_helper.go index a97625ac7..02672c6fc 100644 --- a/pkg/gui/controllers/helpers/mode_helper.go +++ b/pkg/gui/controllers/helpers/mode_helper.go @@ -2,6 +2,7 @@ package helpers import ( "fmt" + "strings" "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" @@ -53,7 +54,7 @@ func (self *ModeHelper) Statuses() []ModeStatus { fmt.Sprintf( "%s %s", self.c.Tr.LcShowingGitDiff, - "git diff "+self.diffHelper.DiffStr(), + "git diff "+strings.Join(self.diffHelper.DiffArgs(), " "), ), style.FgMagenta, ) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 76d9afc20..a4c013429 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -288,7 +288,7 @@ func (self *LocalCommitsController) doRewordEditor() error { self.c.LogAction(self.c.Tr.Actions.RewordCommit) if self.isHeadCommit() { - return self.c.RunSubprocessAndRefresh(self.c.OS().Cmd.New("git commit --allow-empty --amend --only")) + return self.c.RunSubprocessAndRefresh(self.c.Git().Commit.RewordLastCommitInEditorCmdObj()) } subProcess, err := self.c.Git().Rebase.RewordCommitInEditor( diff --git a/pkg/integration/components/git.go b/pkg/integration/components/git.go index 6f4d80f1a..f11c3342c 100644 --- a/pkg/integration/components/git.go +++ b/pkg/integration/components/git.go @@ -11,18 +11,18 @@ type Git struct { } func (self *Git) CurrentBranchName(expectedName string) *Git { - return self.assert("git rev-parse --abbrev-ref HEAD", expectedName) + return self.assert([]string{"git", "rev-parse", "--abbrev-ref", "HEAD"}, expectedName) } func (self *Git) TagNamesAt(ref string, expectedNames []string) *Git { - return self.assert(fmt.Sprintf(`git tag --sort=v:refname --points-at "%s"`, ref), strings.Join(expectedNames, "\n")) + return self.assert([]string{"git", "tag", "--sort=v:refname", "--points-at", ref}, strings.Join(expectedNames, "\n")) } -func (self *Git) assert(cmdStr string, expected string) *Git { +func (self *Git) assert(cmdArgs []string, expected string) *Git { self.assertWithRetries(func() (bool, string) { - output, err := self.shell.runCommandWithOutput(cmdStr) + output, err := self.shell.runCommandWithOutput(cmdArgs) if err != nil { - return false, fmt.Sprintf("Unexpected error running command: `%s`. Error: %s", cmdStr, err.Error()) + return false, fmt.Sprintf("Unexpected error running command: `%v`. Error: %s", cmdArgs, err.Error()) } actual := strings.TrimSpace(output) return actual == expected, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expected, actual) diff --git a/pkg/integration/components/runner.go b/pkg/integration/components/runner.go index 5c2bfca7e..f1f8e3c89 100644 --- a/pkg/integration/components/runner.go +++ b/pkg/integration/components/runner.go @@ -134,14 +134,14 @@ func buildLazygit() error { // return nil osCommand := oscommands.NewDummyOSCommand() - return osCommand.Cmd.New(fmt.Sprintf( - "go build -o %s pkg/integration/clients/injector/main.go", tempLazygitPath(), - )).Run() + return osCommand.Cmd.New([]string{ + "go", "build", "-o", tempLazygitPath(), filepath.FromSlash("pkg/integration/clients/injector/main.go"), + }).Run() } func createFixture(test *IntegrationTest, paths Paths, rootDir string) error { shell := NewShell(paths.ActualRepo(), func(errorMsg string) { panic(errorMsg) }) - shell.RunCommand("git init -b master") + shell.Init("master") os.Setenv(GIT_CONFIG_GLOBAL_ENV_VAR, globalGitConfigPath(rootDir)) @@ -156,7 +156,7 @@ func globalGitConfigPath(rootDir string) string { func getGitVersion() (*git_commands.GitVersion, error) { osCommand := oscommands.NewDummyOSCommand() - cmdObj := osCommand.Cmd.New("git --version") + cmdObj := osCommand.Cmd.New([]string{"git", "--version"}) versionStr, err := cmdObj.RunWithOutput() if err != nil { return nil, err @@ -178,9 +178,10 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandb return nil, err } - cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), paths.Config(), paths.ActualRepo(), test.ExtraCmdArgs()) + cmdArgs := []string{tempLazygitPath(), "-debug", "--use-config-dir=" + paths.Config(), "--path=" + paths.ActualRepo()} + cmdArgs = append(cmdArgs, test.ExtraCmdArgs()...) - cmdObj := osCommand.Cmd.New(cmdStr) + cmdObj := osCommand.Cmd.New(cmdArgs) cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", TEST_NAME_ENV_VAR, test.Name())) if sandbox { diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index 890f9efc4..03bd747e2 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -2,11 +2,12 @@ package components import ( "fmt" + "io" "os" "path/filepath" + "runtime" "github.com/jesseduffield/lazygit/pkg/secureexec" - "github.com/mgutz/str" ) // this is for running shell commands, mostly for the sake of setting up the repo @@ -24,31 +25,25 @@ func NewShell(dir string, fail func(string)) *Shell { return &Shell{dir: dir, fail: fail} } -func (self *Shell) RunCommand(cmdStr string) *Shell { - args := str.ToArgv(cmdStr) - cmd := secureexec.Command(args[0], args[1:]...) - cmd.Env = os.Environ() - cmd.Dir = self.dir - - output, err := cmd.CombinedOutput() +func (self *Shell) RunCommand(args []string) *Shell { + output, err := self.runCommandWithOutput(args) if err != nil { - self.fail(fmt.Sprintf("error running command: %s\n%s", cmdStr, string(output))) + self.fail(fmt.Sprintf("error running command: %v\n%s", args, output)) } return self } -// Help files are located at test/files from the root the lazygit repo. -// E.g. You may want to create a pre-commit hook file there, then call this -// function to copy it into your test repo. -func (self *Shell) CopyHelpFile(source string, destination string) *Shell { - self.RunCommand(fmt.Sprintf("cp ../../../../../files/%s %s", source, destination)) +func (self *Shell) RunCommandExpectError(args []string) *Shell { + output, err := self.runCommandWithOutput(args) + if err == nil { + self.fail(fmt.Sprintf("Expected error running shell command: %v\n%s", args, output)) + } return self } -func (self *Shell) runCommandWithOutput(cmdStr string) (string, error) { - args := str.ToArgv(cmdStr) +func (self *Shell) runCommandWithOutput(args []string) (string, error) { cmd := secureexec.Command(args[0], args[1:]...) cmd.Env = os.Environ() cmd.Dir = self.dir @@ -59,7 +54,14 @@ func (self *Shell) runCommandWithOutput(cmdStr string) (string, error) { } func (self *Shell) RunShellCommand(cmdStr string) *Shell { - cmd := secureexec.Command("sh", "-c", cmdStr) + shell := "sh" + shellArg := "-c" + if runtime.GOOS == "windows" { + shell = "cmd" + shellArg = "/C" + } + + cmd := secureexec.Command(shell, shellArg, cmdStr) cmd.Env = os.Environ() cmd.Dir = self.dir @@ -71,19 +73,6 @@ func (self *Shell) RunShellCommand(cmdStr string) *Shell { return self } -func (self *Shell) RunShellCommandExpectError(cmdStr string) *Shell { - cmd := secureexec.Command("sh", "-c", cmdStr) - cmd.Env = os.Environ() - cmd.Dir = self.dir - - output, err := cmd.CombinedOutput() - if err == nil { - self.fail(fmt.Sprintf("Expected error running shell command: %s\n%s", cmdStr, string(output))) - } - - return self -} - func (self *Shell) CreateFile(path string, content string) *Shell { fullPath := filepath.Join(self.dir, path) err := os.WriteFile(fullPath, []byte(content), 0o644) @@ -124,47 +113,47 @@ func (self *Shell) UpdateFile(path string, content string) *Shell { } func (self *Shell) NewBranch(name string) *Shell { - return self.RunCommand("git checkout -b " + name) + return self.RunCommand([]string{"git", "checkout", "-b", name}) } func (self *Shell) Checkout(name string) *Shell { - return self.RunCommand("git checkout " + name) + return self.RunCommand([]string{"git", "checkout", name}) } func (self *Shell) Merge(name string) *Shell { - return self.RunCommand("git merge --commit --no-ff " + name) + return self.RunCommand([]string{"git", "merge", "--commit", "--no-ff", name}) } func (self *Shell) ContinueMerge() *Shell { - return self.RunCommand("git -c core.editor=true merge --continue") + return self.RunCommand([]string{"git", "-c", "core.editor=true", "merge", "--continue"}) } func (self *Shell) GitAdd(path string) *Shell { - return self.RunCommand(fmt.Sprintf("git add \"%s\"", path)) + return self.RunCommand([]string{"git", "add", path}) } func (self *Shell) GitAddAll() *Shell { - return self.RunCommand("git add -A") + return self.RunCommand([]string{"git", "add", "-A"}) } func (self *Shell) Commit(message string) *Shell { - return self.RunCommand(fmt.Sprintf("git commit -m \"%s\"", message)) + return self.RunCommand([]string{"git", "commit", "-m", message}) } func (self *Shell) EmptyCommit(message string) *Shell { - return self.RunCommand(fmt.Sprintf("git commit --allow-empty -m \"%s\"", message)) + return self.RunCommand([]string{"git", "commit", "--allow-empty", "-m", message}) } func (self *Shell) Revert(ref string) *Shell { - return self.RunCommand(fmt.Sprintf("git revert %s", ref)) + return self.RunCommand([]string{"git", "revert", ref}) } func (self *Shell) CreateLightweightTag(name string, ref string) *Shell { - return self.RunCommand(fmt.Sprintf("git tag %s %s", name, ref)) + return self.RunCommand([]string{"git", "tag", name, ref}) } func (self *Shell) CreateAnnotatedTag(name string, message string, ref string) *Shell { - return self.RunCommand(fmt.Sprintf("git tag -a %s -m \"%s\" %s", name, message, ref)) + return self.RunCommand([]string{"git", "tag", "-a", name, "-m", message, ref}) } // convenience method for creating a file and adding it @@ -208,60 +197,115 @@ func (self *Shell) CreateNCommitsStartingAt(n, startIndex int) *Shell { } func (self *Shell) StashWithMessage(message string) *Shell { - self.RunCommand(fmt.Sprintf(`git stash -m "%s"`, message)) + self.RunCommand([]string{"git", "stash", "-m", message}) return self } func (self *Shell) SetConfig(key string, value string) *Shell { - self.RunCommand(fmt.Sprintf(`git config --local "%s" "%s"`, key, value)) + self.RunCommand([]string{"git", "config", "--local", key, value}) return self } -// creates a clone of the repo in a sibling directory and adds the clone -// as a remote, then fetches it. func (self *Shell) CloneIntoRemote(name string) *Shell { self.Clone(name) - self.RunCommand(fmt.Sprintf("git remote add %s ../%s", name, name)) - self.RunCommand(fmt.Sprintf("git fetch %s", name)) + self.RunCommand([]string{"git", "remote", "add", name, "../" + name}) + self.RunCommand([]string{"git", "fetch", name}) return self } func (self *Shell) CloneIntoSubmodule(submoduleName string) *Shell { self.Clone("other_repo") - self.RunCommand(fmt.Sprintf("git submodule add ../other_repo %s", submoduleName)) + self.RunCommand([]string{"git", "submodule", "add", "../other_repo", submoduleName}) return self } -// clones repo into a sibling directory func (self *Shell) Clone(repoName string) *Shell { - self.RunCommand(fmt.Sprintf("git clone --bare . ../%s", repoName)) + self.RunCommand([]string{"git", "clone", "--bare", ".", "../" + repoName}) return self } -// e.g. branch: 'master', upstream: 'origin/master' func (self *Shell) SetBranchUpstream(branch string, upstream string) *Shell { - self.RunCommand(fmt.Sprintf("git branch --set-upstream-to=%s %s", upstream, branch)) + self.RunCommand([]string{"git", "branch", "--set-upstream-to=" + upstream, branch}) return self } func (self *Shell) RemoveRemoteBranch(remoteName string, branch string) *Shell { - self.RunCommand(fmt.Sprintf("git -C ../%s branch -d %s", remoteName, branch)) + self.RunCommand([]string{"git", "-C", "../" + remoteName, "branch", "-d", branch}) return self } func (self *Shell) HardReset(ref string) *Shell { - self.RunCommand(fmt.Sprintf("git reset --hard %s", ref)) - + self.RunCommand([]string{"git", "reset", "--hard", ref}) return self } func (self *Shell) Stash(message string) *Shell { - self.RunCommand(fmt.Sprintf("git stash -m \"%s\"", message)) + self.RunCommand([]string{"git", "stash", "-m", message}) + return self +} + +func (self *Shell) StartBisect(good string, bad string) *Shell { + self.RunCommand([]string{"git", "bisect", "start", good, bad}) + return self +} + +func (self *Shell) Init(mainBranch string) *Shell { + self.RunCommand([]string{"git", "init", "-b", mainBranch}) + return self +} + +func (self *Shell) MakeExecutable(path string) *Shell { + // 0755 sets the executable permission for owner, and read/execute permissions for group and others + err := os.Chmod(filepath.Join(self.dir, path), 0o755) + if err != nil { + panic(err) + } + + return self +} + +// Help files are located at test/files from the root the lazygit repo. +// E.g. You may want to create a pre-commit hook file there, then call this +// function to copy it into your test repo. +func (self *Shell) CopyHelpFile(source string, destination string) *Shell { + return self.CopyFile(fmt.Sprintf("../../../../../files/%s", source), destination) +} + +func (self *Shell) CopyFile(source string, destination string) *Shell { + absSourcePath := filepath.Join(self.dir, source) + absDestPath := filepath.Join(self.dir, destination) + sourceFile, err := os.Open(absSourcePath) + if err != nil { + self.fail(err.Error()) + } + defer sourceFile.Close() + + destinationFile, err := os.Create(absDestPath) + if err != nil { + self.fail(err.Error()) + } + defer destinationFile.Close() + + _, err = io.Copy(destinationFile, sourceFile) + if err != nil { + self.fail(err.Error()) + } + + // copy permissions to destination file too + sourceFileInfo, err := os.Stat(absSourcePath) + if err != nil { + self.fail(err.Error()) + } + + err = os.Chmod(absDestPath, sourceFileInfo.Mode()) + if err != nil { + self.fail(err.Error()) + } return self } diff --git a/pkg/integration/components/test.go b/pkg/integration/components/test.go index f4838ad86..648c6ed41 100644 --- a/pkg/integration/components/test.go +++ b/pkg/integration/components/test.go @@ -22,7 +22,7 @@ const unitTestDescription = "test test" type IntegrationTest struct { name string description string - extraCmdArgs string + extraCmdArgs []string skip bool setupRepo func(shell *Shell) setupConfig func(config *config.AppConfig) @@ -45,7 +45,7 @@ type NewIntegrationTestArgs struct { // runs the test Run func(t *TestDriver, keys config.KeybindingConfig) // additional args passed to lazygit - ExtraCmdArgs string + ExtraCmdArgs []string // for when a test is flakey Skip bool // to run a test only on certain git versions @@ -128,7 +128,7 @@ func (self *IntegrationTest) Description() string { return self.description } -func (self *IntegrationTest) ExtraCmdArgs() string { +func (self *IntegrationTest) ExtraCmdArgs() []string { return self.extraCmdArgs } diff --git a/pkg/integration/tests/bisect/basic.go b/pkg/integration/tests/bisect/basic.go index 68009ca98..14e1e6b49 100644 --- a/pkg/integration/tests/bisect/basic.go +++ b/pkg/integration/tests/bisect/basic.go @@ -7,7 +7,7 @@ import ( var Basic = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Start a git bisect to find a bad commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell. diff --git a/pkg/integration/tests/bisect/from_other_branch.go b/pkg/integration/tests/bisect/from_other_branch.go index ecac6ea3f..a7c6b8358 100644 --- a/pkg/integration/tests/bisect/from_other_branch.go +++ b/pkg/integration/tests/bisect/from_other_branch.go @@ -7,7 +7,7 @@ import ( var FromOtherBranch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Opening lazygit when bisect has been started from another branch. There's an issue where we don't reselect the current branch if we mark the current branch as bad so this test side-steps that problem", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell. @@ -15,7 +15,7 @@ var FromOtherBranch = NewIntegrationTest(NewIntegrationTestArgs{ NewBranch("other"). CreateNCommits(10). Checkout("master"). - RunCommand("git bisect start other~2 other~5") + StartBisect("other~2", "other~5") }, SetupConfig: func(cfg *config.AppConfig) {}, Run: func(t *TestDriver, keys config.KeybindingConfig) { diff --git a/pkg/integration/tests/branch/checkout_by_name.go b/pkg/integration/tests/branch/checkout_by_name.go index 070a3a433..127ea10dd 100644 --- a/pkg/integration/tests/branch/checkout_by_name.go +++ b/pkg/integration/tests/branch/checkout_by_name.go @@ -7,7 +7,7 @@ import ( var CheckoutByName = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Try to checkout branch by name. Verify that it also works on the branch with the special name @.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/create_tag.go b/pkg/integration/tests/branch/create_tag.go index 36df00f60..289d04c0a 100644 --- a/pkg/integration/tests/branch/create_tag.go +++ b/pkg/integration/tests/branch/create_tag.go @@ -7,7 +7,7 @@ import ( var CreateTag = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create a new tag on branch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/delete.go b/pkg/integration/tests/branch/delete.go index 7d93513dc..c653e1cde 100644 --- a/pkg/integration/tests/branch/delete.go +++ b/pkg/integration/tests/branch/delete.go @@ -7,7 +7,7 @@ import ( var Delete = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Try to delete the checked out branch first (to no avail), and then delete another branch.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/detached_head.go b/pkg/integration/tests/branch/detached_head.go index 7eb7e18ae..494d164d6 100644 --- a/pkg/integration/tests/branch/detached_head.go +++ b/pkg/integration/tests/branch/detached_head.go @@ -7,7 +7,7 @@ import ( var DetachedHead = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create a new branch on detached head", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/open_with_cli_arg.go b/pkg/integration/tests/branch/open_with_cli_arg.go index 45029d603..8e08125fb 100644 --- a/pkg/integration/tests/branch/open_with_cli_arg.go +++ b/pkg/integration/tests/branch/open_with_cli_arg.go @@ -7,7 +7,7 @@ import ( var OpenWithCliArg = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Open straight to branches panel using a CLI arg", - ExtraCmdArgs: "branch", + ExtraCmdArgs: []string{"branch"}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/rebase.go b/pkg/integration/tests/branch/rebase.go index c34e73c2d..24462d1ea 100644 --- a/pkg/integration/tests/branch/rebase.go +++ b/pkg/integration/tests/branch/rebase.go @@ -8,7 +8,7 @@ import ( var Rebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rebase onto another branch, deal with the conflicts.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/rebase_and_drop.go b/pkg/integration/tests/branch/rebase_and_drop.go index 8a1ade2fe..c27a91091 100644 --- a/pkg/integration/tests/branch/rebase_and_drop.go +++ b/pkg/integration/tests/branch/rebase_and_drop.go @@ -8,7 +8,7 @@ import ( var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rebase onto another branch, deal with the conflicts. Also mark a commit to be dropped before continuing.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/rebase_does_not_autosquash.go b/pkg/integration/tests/branch/rebase_does_not_autosquash.go index 2638bec7b..11562c80f 100644 --- a/pkg/integration/tests/branch/rebase_does_not_autosquash.go +++ b/pkg/integration/tests/branch/rebase_does_not_autosquash.go @@ -7,7 +7,7 @@ import ( var RebaseDoesNotAutosquash = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rebase a branch that has fixups onto another branch, and verify that the fixups are not squashed even if rebase.autoSquash is enabled globally.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/reset.go b/pkg/integration/tests/branch/reset.go index 0a46ad2a1..b999f3af7 100644 --- a/pkg/integration/tests/branch/reset.go +++ b/pkg/integration/tests/branch/reset.go @@ -7,7 +7,7 @@ import ( var Reset = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Hard reset to another branch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/reset_upstream.go b/pkg/integration/tests/branch/reset_upstream.go index 126d2abf9..87101e19a 100644 --- a/pkg/integration/tests/branch/reset_upstream.go +++ b/pkg/integration/tests/branch/reset_upstream.go @@ -7,7 +7,7 @@ import ( var ResetUpstream = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Reset the upstream of a branch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/set_upstream.go b/pkg/integration/tests/branch/set_upstream.go index 3388a6086..ef759ca11 100644 --- a/pkg/integration/tests/branch/set_upstream.go +++ b/pkg/integration/tests/branch/set_upstream.go @@ -7,7 +7,7 @@ import ( var SetUpstream = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Set the upstream of a branch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/branch/suggestions.go b/pkg/integration/tests/branch/suggestions.go index 5b5b23403..e6215d13f 100644 --- a/pkg/integration/tests/branch/suggestions.go +++ b/pkg/integration/tests/branch/suggestions.go @@ -7,7 +7,7 @@ import ( var Suggestions = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Checking out a branch with name suggestions", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/cherry_pick/cherry_pick.go b/pkg/integration/tests/cherry_pick/cherry_pick.go index 7aaaf36f1..6b77f8cc0 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick.go @@ -7,7 +7,7 @@ import ( var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Cherry pick commits from the subcommits view, without conflicts", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go b/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go index a0030c2f9..54e69c044 100644 --- a/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go +++ b/pkg/integration/tests/cherry_pick/cherry_pick_conflicts.go @@ -8,7 +8,7 @@ import ( var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Cherry pick commits from the subcommits view, with conflicts", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/amend.go b/pkg/integration/tests/commit/amend.go index 55b690d27..123d62343 100644 --- a/pkg/integration/tests/commit/amend.go +++ b/pkg/integration/tests/commit/amend.go @@ -7,7 +7,7 @@ import ( var Amend = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Amends the last commit from the files panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/commit.go b/pkg/integration/tests/commit/commit.go index 741a1da99..3737f2e1c 100644 --- a/pkg/integration/tests/commit/commit.go +++ b/pkg/integration/tests/commit/commit.go @@ -7,7 +7,7 @@ import ( var Commit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Staging a couple files and committing", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/commit_multiline.go b/pkg/integration/tests/commit/commit_multiline.go index d36a5fdb4..576ec3ead 100644 --- a/pkg/integration/tests/commit/commit_multiline.go +++ b/pkg/integration/tests/commit/commit_multiline.go @@ -7,7 +7,7 @@ import ( var CommitMultiline = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Commit with a multi-line commit message", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/commit_wip_with_prefix.go b/pkg/integration/tests/commit/commit_wip_with_prefix.go index 4b3099abf..42696e190 100644 --- a/pkg/integration/tests/commit/commit_wip_with_prefix.go +++ b/pkg/integration/tests/commit/commit_wip_with_prefix.go @@ -7,7 +7,7 @@ import ( var CommitWipWithPrefix = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Commit with skip hook and config commitPrefix is defined. Prefix is ignored when creating WIP commits.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(testConfig *config.AppConfig) { testConfig.UserConfig.Git.CommitPrefixes = map[string]config.CommitPrefixConfig{"repo": {Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: "}} diff --git a/pkg/integration/tests/commit/commit_with_prefix.go b/pkg/integration/tests/commit/commit_with_prefix.go index 07c0f626e..fe53327e4 100644 --- a/pkg/integration/tests/commit/commit_with_prefix.go +++ b/pkg/integration/tests/commit/commit_with_prefix.go @@ -7,7 +7,7 @@ import ( var CommitWithPrefix = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Commit with defined config commitPrefix", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(testConfig *config.AppConfig) { testConfig.UserConfig.Git.CommitPrefixes = map[string]config.CommitPrefixConfig{"repo": {Pattern: "^\\w+\\/(\\w+-\\w+).*", Replace: "[$1]: "}} diff --git a/pkg/integration/tests/commit/create_tag.go b/pkg/integration/tests/commit/create_tag.go index 4b749b235..d081a8fa8 100644 --- a/pkg/integration/tests/commit/create_tag.go +++ b/pkg/integration/tests/commit/create_tag.go @@ -7,7 +7,7 @@ import ( var CreateTag = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create a new tag on a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/discard_old_file_change.go b/pkg/integration/tests/commit/discard_old_file_change.go index 735dcca87..c42a0f743 100644 --- a/pkg/integration/tests/commit/discard_old_file_change.go +++ b/pkg/integration/tests/commit/discard_old_file_change.go @@ -7,7 +7,7 @@ import ( var DiscardOldFileChange = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Discarding a single file from an old commit (does rebase in background to remove the file but retain the other one)", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/history.go b/pkg/integration/tests/commit/history.go index 9938ae35a..323dcb7f1 100644 --- a/pkg/integration/tests/commit/history.go +++ b/pkg/integration/tests/commit/history.go @@ -7,7 +7,7 @@ import ( var History = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Cycling through commit message history in the commit message panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/history_complex.go b/pkg/integration/tests/commit/history_complex.go index e88da4416..693082098 100644 --- a/pkg/integration/tests/commit/history_complex.go +++ b/pkg/integration/tests/commit/history_complex.go @@ -7,7 +7,7 @@ import ( var HistoryComplex = NewIntegrationTest(NewIntegrationTestArgs{ Description: "More complex flow for cycling commit message history", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/new_branch.go b/pkg/integration/tests/commit/new_branch.go index d3cd58f23..c73a2b88a 100644 --- a/pkg/integration/tests/commit/new_branch.go +++ b/pkg/integration/tests/commit/new_branch.go @@ -7,7 +7,7 @@ import ( var NewBranch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Creating a new branch from a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/reset_author.go b/pkg/integration/tests/commit/reset_author.go index f54b5b5aa..16b43da69 100644 --- a/pkg/integration/tests/commit/reset_author.go +++ b/pkg/integration/tests/commit/reset_author.go @@ -7,7 +7,7 @@ import ( var ResetAuthor = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Reset author on a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/revert.go b/pkg/integration/tests/commit/revert.go index 3b55aa65d..5bc81608f 100644 --- a/pkg/integration/tests/commit/revert.go +++ b/pkg/integration/tests/commit/revert.go @@ -7,7 +7,7 @@ import ( var Revert = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Reverts a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/revert_merge.go b/pkg/integration/tests/commit/revert_merge.go index 94b967916..1ddc1635a 100644 --- a/pkg/integration/tests/commit/revert_merge.go +++ b/pkg/integration/tests/commit/revert_merge.go @@ -8,7 +8,7 @@ import ( var RevertMerge = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Reverts a merge commit and chooses to revert to the parent commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/reword.go b/pkg/integration/tests/commit/reword.go index f488977ed..48941b7d2 100644 --- a/pkg/integration/tests/commit/reword.go +++ b/pkg/integration/tests/commit/reword.go @@ -7,7 +7,7 @@ import ( var Reword = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Staging a couple files and committing", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/search.go b/pkg/integration/tests/commit/search.go index 7159b11d1..1d9390dc7 100644 --- a/pkg/integration/tests/commit/search.go +++ b/pkg/integration/tests/commit/search.go @@ -7,7 +7,7 @@ import ( var Search = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Search for a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/set_author.go b/pkg/integration/tests/commit/set_author.go index 047ac167c..f9f1e42ca 100644 --- a/pkg/integration/tests/commit/set_author.go +++ b/pkg/integration/tests/commit/set_author.go @@ -7,7 +7,7 @@ import ( var SetAuthor = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Set author on a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/stage_range_of_lines.go b/pkg/integration/tests/commit/stage_range_of_lines.go index ef63b4838..1beef60a2 100644 --- a/pkg/integration/tests/commit/stage_range_of_lines.go +++ b/pkg/integration/tests/commit/stage_range_of_lines.go @@ -7,7 +7,7 @@ import ( var StageRangeOfLines = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Staging a range of lines", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/staged.go b/pkg/integration/tests/commit/staged.go index f5e45995d..36d9cec58 100644 --- a/pkg/integration/tests/commit/staged.go +++ b/pkg/integration/tests/commit/staged.go @@ -7,7 +7,7 @@ import ( var Staged = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Staging a couple files, going in the staged files menu, unstaging a line then committing", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/staged_without_hooks.go b/pkg/integration/tests/commit/staged_without_hooks.go index dcf20d08d..fcf53dd22 100644 --- a/pkg/integration/tests/commit/staged_without_hooks.go +++ b/pkg/integration/tests/commit/staged_without_hooks.go @@ -7,7 +7,7 @@ import ( var StagedWithoutHooks = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Staging a couple files, going in the staged files menu, unstaging a line then committing without pre-commit hooks", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/commit/unstaged.go b/pkg/integration/tests/commit/unstaged.go index 21e1e98d2..2cbd5e33c 100644 --- a/pkg/integration/tests/commit/unstaged.go +++ b/pkg/integration/tests/commit/unstaged.go @@ -7,7 +7,7 @@ import ( var Unstaged = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Staging a couple files, going in the unstaged files menu, staging a line and committing", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/config/remote_named_star.go b/pkg/integration/tests/config/remote_named_star.go index 735389667..94ff80b41 100644 --- a/pkg/integration/tests/config/remote_named_star.go +++ b/pkg/integration/tests/config/remote_named_star.go @@ -7,7 +7,7 @@ import ( var RemoteNamedStar = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Having a config remote.*", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell. diff --git a/pkg/integration/tests/conflicts/filter.go b/pkg/integration/tests/conflicts/filter.go index b5f4b4c81..32f5a8cd2 100644 --- a/pkg/integration/tests/conflicts/filter.go +++ b/pkg/integration/tests/conflicts/filter.go @@ -8,7 +8,7 @@ import ( var Filter = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Ensures that when there are merge conflicts, the files panel only shows conflicted files", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/conflicts/resolve_externally.go b/pkg/integration/tests/conflicts/resolve_externally.go index dec4ab7e2..a8d717351 100644 --- a/pkg/integration/tests/conflicts/resolve_externally.go +++ b/pkg/integration/tests/conflicts/resolve_externally.go @@ -8,7 +8,7 @@ import ( var ResolveExternally = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Ensures that when merge conflicts are resolved outside of lazygit, lazygit prompts you to continue", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/conflicts/resolve_multiple_files.go b/pkg/integration/tests/conflicts/resolve_multiple_files.go index f32743bef..f5f8db0bc 100644 --- a/pkg/integration/tests/conflicts/resolve_multiple_files.go +++ b/pkg/integration/tests/conflicts/resolve_multiple_files.go @@ -8,7 +8,7 @@ import ( var ResolveMultipleFiles = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Ensures that upon resolving conflicts for one file, the next file is selected", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/conflicts/undo_choose_hunk.go b/pkg/integration/tests/conflicts/undo_choose_hunk.go index b008ed762..c03f79cf6 100644 --- a/pkg/integration/tests/conflicts/undo_choose_hunk.go +++ b/pkg/integration/tests/conflicts/undo_choose_hunk.go @@ -8,7 +8,7 @@ import ( var UndoChooseHunk = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Chooses a hunk when resolving a merge conflict and then undoes the choice", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/custom_commands/basic_cmd_at_runtime.go b/pkg/integration/tests/custom_commands/basic_cmd_at_runtime.go index 96b9b9174..f83a5d514 100644 --- a/pkg/integration/tests/custom_commands/basic_cmd_at_runtime.go +++ b/pkg/integration/tests/custom_commands/basic_cmd_at_runtime.go @@ -7,7 +7,7 @@ import ( var BasicCmdAtRuntime = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Using a custom command provided at runtime to create a new file", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell.EmptyCommit("blah") diff --git a/pkg/integration/tests/custom_commands/basic_cmd_from_config.go b/pkg/integration/tests/custom_commands/basic_cmd_from_config.go index 0f15183d9..71b99c2de 100644 --- a/pkg/integration/tests/custom_commands/basic_cmd_from_config.go +++ b/pkg/integration/tests/custom_commands/basic_cmd_from_config.go @@ -7,7 +7,7 @@ import ( var BasicCmdFromConfig = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Using a custom command to create a new file", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell.EmptyCommit("blah") diff --git a/pkg/integration/tests/custom_commands/complex_cmd_at_runtime.go b/pkg/integration/tests/custom_commands/complex_cmd_at_runtime.go new file mode 100644 index 000000000..2b7075ecd --- /dev/null +++ b/pkg/integration/tests/custom_commands/complex_cmd_at_runtime.go @@ -0,0 +1,35 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var ComplexCmdAtRuntime = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Using a custom command provided at runtime to create a new file, via a shell command. We invoke custom commands through a shell already. This test proves that we can run a shell within a shell, which requires complex escaping.", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("blah") + }, + SetupConfig: func(cfg *config.AppConfig) {}, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsEmpty(). + IsFocused(). + Press(keys.Universal.ExecuteCustomCommand) + + t.ExpectPopup().Prompt(). + Title(Equals("Custom Command:")). + Type("sh -c \"touch file.txt\""). + Confirm() + + t.GlobalPress(keys.Files.RefreshFiles) + + t.Views().Files(). + IsFocused(). + Lines( + Contains("file.txt"), + ) + }, +}) diff --git a/pkg/integration/tests/custom_commands/form_prompts.go b/pkg/integration/tests/custom_commands/form_prompts.go index dcb41c83e..8ab54451d 100644 --- a/pkg/integration/tests/custom_commands/form_prompts.go +++ b/pkg/integration/tests/custom_commands/form_prompts.go @@ -7,7 +7,7 @@ import ( var FormPrompts = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Using a custom command reffering prompt responses by name", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell.EmptyCommit("blah") diff --git a/pkg/integration/tests/custom_commands/menu_from_command.go b/pkg/integration/tests/custom_commands/menu_from_command.go index db856c807..9e5f39615 100644 --- a/pkg/integration/tests/custom_commands/menu_from_command.go +++ b/pkg/integration/tests/custom_commands/menu_from_command.go @@ -9,7 +9,7 @@ import ( var MenuFromCommand = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Using menuFromCommand prompt type", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell. diff --git a/pkg/integration/tests/custom_commands/menu_from_commands_output.go b/pkg/integration/tests/custom_commands/menu_from_commands_output.go index 123e27695..7cbd16506 100644 --- a/pkg/integration/tests/custom_commands/menu_from_commands_output.go +++ b/pkg/integration/tests/custom_commands/menu_from_commands_output.go @@ -7,7 +7,7 @@ import ( var MenuFromCommandsOutput = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Using prompt response in menuFromCommand entries", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell. diff --git a/pkg/integration/tests/custom_commands/multiple_prompts.go b/pkg/integration/tests/custom_commands/multiple_prompts.go index b1592d03b..d36c40d65 100644 --- a/pkg/integration/tests/custom_commands/multiple_prompts.go +++ b/pkg/integration/tests/custom_commands/multiple_prompts.go @@ -7,7 +7,7 @@ import ( var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Using a custom command with multiple prompts", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell.EmptyCommit("blah") diff --git a/pkg/integration/tests/custom_commands/omit_from_history.go b/pkg/integration/tests/custom_commands/omit_from_history.go index 40cf5b328..b28ee08a1 100644 --- a/pkg/integration/tests/custom_commands/omit_from_history.go +++ b/pkg/integration/tests/custom_commands/omit_from_history.go @@ -7,7 +7,7 @@ import ( var OmitFromHistory = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Omitting a runtime custom command from history if it begins with space", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupRepo: func(shell *Shell) { shell.EmptyCommit("blah") diff --git a/pkg/integration/tests/diff/diff.go b/pkg/integration/tests/diff/diff.go index 8730fd881..3a856ab4d 100644 --- a/pkg/integration/tests/diff/diff.go +++ b/pkg/integration/tests/diff/diff.go @@ -7,7 +7,7 @@ import ( var Diff = NewIntegrationTest(NewIntegrationTestArgs{ Description: "View the diff of two branches, then view the reverse diff", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/diff/diff_and_apply_patch.go b/pkg/integration/tests/diff/diff_and_apply_patch.go index 8988b0493..8f5c55712 100644 --- a/pkg/integration/tests/diff/diff_and_apply_patch.go +++ b/pkg/integration/tests/diff/diff_and_apply_patch.go @@ -7,7 +7,7 @@ import ( var DiffAndApplyPatch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create a patch from the diff between two branches and apply the patch.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/diff/diff_commits.go b/pkg/integration/tests/diff/diff_commits.go index 6de643272..f502c034f 100644 --- a/pkg/integration/tests/diff/diff_commits.go +++ b/pkg/integration/tests/diff/diff_commits.go @@ -7,7 +7,7 @@ import ( var DiffCommits = NewIntegrationTest(NewIntegrationTestArgs{ Description: "View the diff between two commits", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/diff/ignore_whitespace.go b/pkg/integration/tests/diff/ignore_whitespace.go index 157a66da9..44e877139 100644 --- a/pkg/integration/tests/diff/ignore_whitespace.go +++ b/pkg/integration/tests/diff/ignore_whitespace.go @@ -13,7 +13,7 @@ var ( var IgnoreWhitespace = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Toggle whitespace in the diff", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/file/dir_with_untracked_file.go b/pkg/integration/tests/file/dir_with_untracked_file.go index 3b45e634c..1def7fa89 100644 --- a/pkg/integration/tests/file/dir_with_untracked_file.go +++ b/pkg/integration/tests/file/dir_with_untracked_file.go @@ -8,7 +8,7 @@ import ( var DirWithUntrackedFile = NewIntegrationTest(NewIntegrationTestArgs{ // notably, we currently _don't_ actually see the untracked file in the diff. Not sure how to get around that. Description: "When selecting a directory that contains an untracked file, we should not get an error", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/file/discard_changes.go b/pkg/integration/tests/file/discard_changes.go index 2c5d2b2ea..dcc63bd59 100644 --- a/pkg/integration/tests/file/discard_changes.go +++ b/pkg/integration/tests/file/discard_changes.go @@ -7,7 +7,7 @@ import ( var DiscardChanges = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Discarding all possible permutations of changed files", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: true, // failing due to index.lock file being created SetupConfig: func(config *config.AppConfig) { }, @@ -51,7 +51,7 @@ var DiscardChanges = NewIntegrationTest(NewIntegrationTestArgs{ shell.RunShellCommand(`rm deleted-us.txt && git add deleted-us.txt`) shell.RunShellCommand(`git commit -m "three"`) shell.RunShellCommand(`git reset --hard conflict_second`) - shell.RunShellCommandExpectError(`git merge conflict`) + shell.RunCommandExpectError([]string{"git", "merge", "conflict"}) shell.RunShellCommand(`echo "new" > new.txt`) shell.RunShellCommand(`echo "new staged" > new-staged.txt && git add new-staged.txt`) diff --git a/pkg/integration/tests/file/discard_staged_changes.go b/pkg/integration/tests/file/discard_staged_changes.go index 9b40ee72d..6e82f232e 100644 --- a/pkg/integration/tests/file/discard_staged_changes.go +++ b/pkg/integration/tests/file/discard_staged_changes.go @@ -7,7 +7,7 @@ import ( var DiscardStagedChanges = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Discarding staged changes", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/file/gitignore.go b/pkg/integration/tests/file/gitignore.go index 7cbd3bf1e..d23f218ad 100644 --- a/pkg/integration/tests/file/gitignore.go +++ b/pkg/integration/tests/file/gitignore.go @@ -7,7 +7,7 @@ import ( var Gitignore = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Verify that we can't ignore the .gitignore file, then ignore/exclude other files", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/file/remember_commit_message_after_fail.go b/pkg/integration/tests/file/remember_commit_message_after_fail.go index bb9b341b1..1f3b05689 100644 --- a/pkg/integration/tests/file/remember_commit_message_after_fail.go +++ b/pkg/integration/tests/file/remember_commit_message_after_fail.go @@ -14,13 +14,13 @@ fi var RememberCommitMessageAfterFail = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Verify that the commit message is remembered after a failed attempt at committing", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, SetupRepo: func(shell *Shell) { shell.CreateFile(".git/hooks/pre-commit", preCommitHook) - shell.RunCommand("chmod +x .git/hooks/pre-commit") + shell.MakeExecutable(".git/hooks/pre-commit") shell.CreateFileAndAdd("one", "one") diff --git a/pkg/integration/tests/filter_by_path/cli_arg.go b/pkg/integration/tests/filter_by_path/cli_arg.go index 0de95986e..de368c17d 100644 --- a/pkg/integration/tests/filter_by_path/cli_arg.go +++ b/pkg/integration/tests/filter_by_path/cli_arg.go @@ -7,7 +7,7 @@ import ( var CliArg = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Filter commits by file path, using CLI arg", - ExtraCmdArgs: "-f filterFile", + ExtraCmdArgs: []string{"-f", "filterFile"}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/filter_by_path/select_file.go b/pkg/integration/tests/filter_by_path/select_file.go index 80a69a2b4..a0875e319 100644 --- a/pkg/integration/tests/filter_by_path/select_file.go +++ b/pkg/integration/tests/filter_by_path/select_file.go @@ -7,7 +7,7 @@ import ( var SelectFile = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Filter commits by file path, by finding file in UI and filtering on it", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/filter_by_path/type_file.go b/pkg/integration/tests/filter_by_path/type_file.go index 23ec0ff1d..dad3068f0 100644 --- a/pkg/integration/tests/filter_by_path/type_file.go +++ b/pkg/integration/tests/filter_by_path/type_file.go @@ -7,7 +7,7 @@ import ( var TypeFile = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Filter commits by file path, by finding file in UI and filtering on it", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/interactive_rebase/advanced_interactive_rebase.go b/pkg/integration/tests/interactive_rebase/advanced_interactive_rebase.go index 5ebc86d52..25ddfc43f 100644 --- a/pkg/integration/tests/interactive_rebase/advanced_interactive_rebase.go +++ b/pkg/integration/tests/interactive_rebase/advanced_interactive_rebase.go @@ -16,7 +16,7 @@ const ( var AdvancedInteractiveRebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "It begins an interactive rebase and verifies to have the possibility of editing the commits of the branch before proceeding with the actual rebase", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { shell. diff --git a/pkg/integration/tests/interactive_rebase/amend_first_commit.go b/pkg/integration/tests/interactive_rebase/amend_first_commit.go index 483153e15..720be2176 100644 --- a/pkg/integration/tests/interactive_rebase/amend_first_commit.go +++ b/pkg/integration/tests/interactive_rebase/amend_first_commit.go @@ -7,7 +7,7 @@ import ( var AmendFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Amends a staged file to the first (initial) commit.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/amend_fixup_commit.go b/pkg/integration/tests/interactive_rebase/amend_fixup_commit.go index 0c39c756b..ebac45745 100644 --- a/pkg/integration/tests/interactive_rebase/amend_fixup_commit.go +++ b/pkg/integration/tests/interactive_rebase/amend_fixup_commit.go @@ -7,7 +7,7 @@ import ( var AmendFixupCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Amends a staged file to a fixup commit, and checks that other unrelated fixup commits are not auto-squashed.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/amend_head_commit_during_rebase.go b/pkg/integration/tests/interactive_rebase/amend_head_commit_during_rebase.go index 4f0a024c0..fbd72053d 100644 --- a/pkg/integration/tests/interactive_rebase/amend_head_commit_during_rebase.go +++ b/pkg/integration/tests/interactive_rebase/amend_head_commit_during_rebase.go @@ -7,7 +7,7 @@ import ( var AmendHeadCommitDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Amends the current head commit from the commits panel during a rebase.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/amend_merge.go b/pkg/integration/tests/interactive_rebase/amend_merge.go index b0def4f7a..c5bb036cd 100644 --- a/pkg/integration/tests/interactive_rebase/amend_merge.go +++ b/pkg/integration/tests/interactive_rebase/amend_merge.go @@ -12,7 +12,7 @@ var ( var AmendMerge = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Amends a staged file to a merge commit.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/amend_non_head_commit_during_rebase.go b/pkg/integration/tests/interactive_rebase/amend_non_head_commit_during_rebase.go index 946b1e455..e9c421297 100644 --- a/pkg/integration/tests/interactive_rebase/amend_non_head_commit_during_rebase.go +++ b/pkg/integration/tests/interactive_rebase/amend_non_head_commit_during_rebase.go @@ -7,7 +7,7 @@ import ( var AmendNonHeadCommitDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Tries to amend a commit that is not the head while already rebasing, resulting in an error message", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go index e8d1e170a..4bd738d6a 100644 --- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go +++ b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go @@ -7,7 +7,7 @@ import ( var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Drops a commit during interactive rebase when there is an update-ref in the git-rebase-todo file", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, GitVersion: AtLeast("2.38.0"), SetupConfig: func(config *config.AppConfig) {}, diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go index 6321891a7..b8cd41055 100644 --- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go +++ b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go @@ -7,7 +7,7 @@ import ( var DropTodoCommitWithUpdateRefShowBranchHeads = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Drops a commit during interactive rebase when there is an update-ref in the git-rebase-todo file (with experimentalShowBranchHeads on)", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, GitVersion: AtLeast("2.38.0"), SetupConfig: func(config *config.AppConfig) { diff --git a/pkg/integration/tests/interactive_rebase/edit_first_commit.go b/pkg/integration/tests/interactive_rebase/edit_first_commit.go index abf3f2be0..f6ea49ed1 100644 --- a/pkg/integration/tests/interactive_rebase/edit_first_commit.go +++ b/pkg/integration/tests/interactive_rebase/edit_first_commit.go @@ -7,7 +7,7 @@ import ( var EditFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Edits the first commit, just to show that it's possible", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/edit_non_todo_commit_during_rebase.go b/pkg/integration/tests/interactive_rebase/edit_non_todo_commit_during_rebase.go index 22b127d36..57f219227 100644 --- a/pkg/integration/tests/interactive_rebase/edit_non_todo_commit_during_rebase.go +++ b/pkg/integration/tests/interactive_rebase/edit_non_todo_commit_during_rebase.go @@ -7,7 +7,7 @@ import ( var EditNonTodoCommitDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Tries to edit a non-todo commit while already rebasing, resulting in an error message", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/fixup_first_commit.go b/pkg/integration/tests/interactive_rebase/fixup_first_commit.go index 7f66c24de..a45c2f050 100644 --- a/pkg/integration/tests/interactive_rebase/fixup_first_commit.go +++ b/pkg/integration/tests/interactive_rebase/fixup_first_commit.go @@ -7,7 +7,7 @@ import ( var FixupFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Tries to fixup the first commit, which results in an error message", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/fixup_second_commit.go b/pkg/integration/tests/interactive_rebase/fixup_second_commit.go index d4a77bdb6..57648035d 100644 --- a/pkg/integration/tests/interactive_rebase/fixup_second_commit.go +++ b/pkg/integration/tests/interactive_rebase/fixup_second_commit.go @@ -7,7 +7,7 @@ import ( var FixupSecondCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Fixup the second commit into the first (initial)", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/move.go b/pkg/integration/tests/interactive_rebase/move.go index e7679a793..8eca1073f 100644 --- a/pkg/integration/tests/interactive_rebase/move.go +++ b/pkg/integration/tests/interactive_rebase/move.go @@ -7,7 +7,7 @@ import ( var Move = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Directly move a commit all the way down and all the way back up", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/move_in_rebase.go b/pkg/integration/tests/interactive_rebase/move_in_rebase.go index 160a2c823..adce14409 100644 --- a/pkg/integration/tests/interactive_rebase/move_in_rebase.go +++ b/pkg/integration/tests/interactive_rebase/move_in_rebase.go @@ -7,7 +7,7 @@ import ( var MoveInRebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Via a single interactive rebase move a commit all the way up then back down then slightly back up again and apply the change", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/rebase.go b/pkg/integration/tests/interactive_rebase/rebase.go index 95fd185cb..2b279a22e 100644 --- a/pkg/integration/tests/interactive_rebase/rebase.go +++ b/pkg/integration/tests/interactive_rebase/rebase.go @@ -7,7 +7,7 @@ import ( var Rebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/reword_first_commit.go b/pkg/integration/tests/interactive_rebase/reword_first_commit.go index 50ca2fb0e..cb9afc3c4 100644 --- a/pkg/integration/tests/interactive_rebase/reword_first_commit.go +++ b/pkg/integration/tests/interactive_rebase/reword_first_commit.go @@ -10,7 +10,7 @@ import ( var RewordFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rewords the first commit, just to show that it's possible", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/reword_last_commit.go b/pkg/integration/tests/interactive_rebase/reword_last_commit.go index 742b250d4..5d3038feb 100644 --- a/pkg/integration/tests/interactive_rebase/reword_last_commit.go +++ b/pkg/integration/tests/interactive_rebase/reword_last_commit.go @@ -7,7 +7,7 @@ import ( var RewordLastCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rewords the last (HEAD) commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit.go b/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit.go index c7431d059..d7763c573 100644 --- a/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit.go +++ b/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit.go @@ -7,7 +7,7 @@ import ( var RewordYouAreHereCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rewords the current HEAD commit in an interactive rebase", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit_with_editor.go b/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit_with_editor.go index c6e57932a..4d654e1dd 100644 --- a/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit_with_editor.go +++ b/pkg/integration/tests/interactive_rebase/reword_you_are_here_commit_with_editor.go @@ -7,7 +7,7 @@ import ( var RewordYouAreHereCommitWithEditor = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rewords the current HEAD commit in an interactive rebase with editor", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/interactive_rebase/squash_down_first_commit.go b/pkg/integration/tests/interactive_rebase/squash_down_first_commit.go index 7d6182081..0f58419f9 100644 --- a/pkg/integration/tests/interactive_rebase/squash_down_first_commit.go +++ b/pkg/integration/tests/interactive_rebase/squash_down_first_commit.go @@ -7,7 +7,7 @@ import ( var SquashDownFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Tries to squash down the first commit, which results in an error message", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/squash_down_second_commit.go b/pkg/integration/tests/interactive_rebase/squash_down_second_commit.go index 385145743..931c52015 100644 --- a/pkg/integration/tests/interactive_rebase/squash_down_second_commit.go +++ b/pkg/integration/tests/interactive_rebase/squash_down_second_commit.go @@ -7,7 +7,7 @@ import ( var SquashDownSecondCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Squash down the second commit into the first (initial)", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go b/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go index 34d325726..8c9fed0a4 100644 --- a/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go +++ b/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go @@ -7,7 +7,7 @@ import ( var SquashFixupsAboveFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Squashes all fixups above the first (initial) commit.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/swap_in_rebase_with_conflict.go b/pkg/integration/tests/interactive_rebase/swap_in_rebase_with_conflict.go index 8c109fb51..d893da4bf 100644 --- a/pkg/integration/tests/interactive_rebase/swap_in_rebase_with_conflict.go +++ b/pkg/integration/tests/interactive_rebase/swap_in_rebase_with_conflict.go @@ -7,7 +7,7 @@ import ( var SwapInRebaseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Via an edit-triggered rebase, swap two commits, causing a conflict. Then resolve the conflict and continue", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/interactive_rebase/swap_with_conflict.go b/pkg/integration/tests/interactive_rebase/swap_with_conflict.go index 5f156a5c8..bd498c707 100644 --- a/pkg/integration/tests/interactive_rebase/swap_with_conflict.go +++ b/pkg/integration/tests/interactive_rebase/swap_with_conflict.go @@ -7,7 +7,7 @@ import ( var SwapWithConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Directly swap two commits, causing a conflict. Then resolve the conflict and continue", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/misc/confirm_on_quit.go b/pkg/integration/tests/misc/confirm_on_quit.go index 18b27a108..d3a8abaaa 100644 --- a/pkg/integration/tests/misc/confirm_on_quit.go +++ b/pkg/integration/tests/misc/confirm_on_quit.go @@ -7,7 +7,7 @@ import ( var ConfirmOnQuit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Quitting with a confirm prompt", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { config.UserConfig.ConfirmOnQuit = true diff --git a/pkg/integration/tests/misc/initial_open.go b/pkg/integration/tests/misc/initial_open.go index cc4146fc6..e2f085504 100644 --- a/pkg/integration/tests/misc/initial_open.go +++ b/pkg/integration/tests/misc/initial_open.go @@ -7,7 +7,7 @@ import ( var InitialOpen = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Confirms a popup appears on first opening Lazygit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { config.UserConfig.DisableStartupPopups = false diff --git a/pkg/integration/tests/patch_building/apply.go b/pkg/integration/tests/patch_building/apply.go index 3dd598a26..6cf28ecfc 100644 --- a/pkg/integration/tests/patch_building/apply.go +++ b/pkg/integration/tests/patch_building/apply.go @@ -7,7 +7,7 @@ import ( var Apply = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Apply a custom patch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/apply_in_reverse.go b/pkg/integration/tests/patch_building/apply_in_reverse.go index 1c6f56c30..e25911786 100644 --- a/pkg/integration/tests/patch_building/apply_in_reverse.go +++ b/pkg/integration/tests/patch_building/apply_in_reverse.go @@ -7,7 +7,7 @@ import ( var ApplyInReverse = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Apply a custom patch in reverse", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go b/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go index 04373622c..d98c95262 100644 --- a/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go +++ b/pkg/integration/tests/patch_building/apply_in_reverse_with_conflict.go @@ -7,7 +7,7 @@ import ( var ApplyInReverseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Apply a custom patch in reverse, resulting in a conflict", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/copy_patch_to_clipboard.go b/pkg/integration/tests/patch_building/copy_patch_to_clipboard.go index 0dea2c36b..d1d877a1d 100644 --- a/pkg/integration/tests/patch_building/copy_patch_to_clipboard.go +++ b/pkg/integration/tests/patch_building/copy_patch_to_clipboard.go @@ -7,7 +7,7 @@ import ( var CopyPatchToClipboard = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create a patch from the commits and copy the patch to clipbaord.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: true, // skipping because CI doesn't have clipboard functionality SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_earlier_commit.go b/pkg/integration/tests/patch_building/move_to_earlier_commit.go index 98bf6fa05..e1c89bc0b 100644 --- a/pkg/integration/tests/patch_building/move_to_earlier_commit.go +++ b/pkg/integration/tests/patch_building/move_to_earlier_commit.go @@ -7,7 +7,7 @@ import ( var MoveToEarlierCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to an earlier commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, GitVersion: AtLeast("2.26.0"), SetupConfig: func(config *config.AppConfig) {}, diff --git a/pkg/integration/tests/patch_building/move_to_earlier_commit_no_keep_empty.go b/pkg/integration/tests/patch_building/move_to_earlier_commit_no_keep_empty.go index a44ba3438..aefb85c73 100644 --- a/pkg/integration/tests/patch_building/move_to_earlier_commit_no_keep_empty.go +++ b/pkg/integration/tests/patch_building/move_to_earlier_commit_no_keep_empty.go @@ -7,7 +7,7 @@ import ( var MoveToEarlierCommitNoKeepEmpty = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to an earlier commit, for older git versions that don't keep the empty commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, GitVersion: Before("2.26.0"), SetupConfig: func(config *config.AppConfig) {}, diff --git a/pkg/integration/tests/patch_building/move_to_index.go b/pkg/integration/tests/patch_building/move_to_index.go index 2eea9f07b..f648b3e3f 100644 --- a/pkg/integration/tests/patch_building/move_to_index.go +++ b/pkg/integration/tests/patch_building/move_to_index.go @@ -7,7 +7,7 @@ import ( var MoveToIndex = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to the index", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go b/pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go index c307ae432..d18ce3178 100644 --- a/pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go +++ b/pkg/integration/tests/patch_building/move_to_index_part_of_adjacent_added_lines.go @@ -7,7 +7,7 @@ import ( var MoveToIndexPartOfAdjacentAddedLines = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to the index, with only some lines of a range of adjacent added lines in the patch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_index_partial.go b/pkg/integration/tests/patch_building/move_to_index_partial.go index ffe8f06ee..00eb97e17 100644 --- a/pkg/integration/tests/patch_building/move_to_index_partial.go +++ b/pkg/integration/tests/patch_building/move_to_index_partial.go @@ -7,7 +7,7 @@ import ( var MoveToIndexPartial = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to the index. This is different from the MoveToIndex test in that we're only selecting a partial patch from a file", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_index_with_conflict.go b/pkg/integration/tests/patch_building/move_to_index_with_conflict.go index 75ecff9a4..941f5a4d0 100644 --- a/pkg/integration/tests/patch_building/move_to_index_with_conflict.go +++ b/pkg/integration/tests/patch_building/move_to_index_with_conflict.go @@ -7,7 +7,7 @@ import ( var MoveToIndexWithConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to the index, causing a conflict", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_later_commit.go b/pkg/integration/tests/patch_building/move_to_later_commit.go index f80293e72..530dec1cb 100644 --- a/pkg/integration/tests/patch_building/move_to_later_commit.go +++ b/pkg/integration/tests/patch_building/move_to_later_commit.go @@ -7,7 +7,7 @@ import ( var MoveToLaterCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to a later commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go b/pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go index 3ebf4a886..e213a02fe 100644 --- a/pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go +++ b/pkg/integration/tests/patch_building/move_to_later_commit_partial_hunk.go @@ -7,7 +7,7 @@ import ( var MoveToLaterCommitPartialHunk = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to a later commit, with only parts of a hunk in the patch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_new_commit.go b/pkg/integration/tests/patch_building/move_to_new_commit.go index 491a8c9c5..9e32535dd 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit.go @@ -7,7 +7,7 @@ import ( var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to a new commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go b/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go index 2e3c59f77..55d7c2f65 100644 --- a/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go +++ b/pkg/integration/tests/patch_building/move_to_new_commit_partial_hunk.go @@ -7,7 +7,7 @@ import ( var MoveToNewCommitPartialHunk = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Move a patch from a commit to a new commit, with only parts of a hunk in the patch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/remove_from_commit.go b/pkg/integration/tests/patch_building/remove_from_commit.go index f2bc701a4..198b3a917 100644 --- a/pkg/integration/tests/patch_building/remove_from_commit.go +++ b/pkg/integration/tests/patch_building/remove_from_commit.go @@ -7,7 +7,7 @@ import ( var RemoveFromCommit = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Remove a custom patch from a commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/reset_with_escape.go b/pkg/integration/tests/patch_building/reset_with_escape.go index 714ea5f0b..559d689e6 100644 --- a/pkg/integration/tests/patch_building/reset_with_escape.go +++ b/pkg/integration/tests/patch_building/reset_with_escape.go @@ -7,7 +7,7 @@ import ( var ResetWithEscape = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Reset a custom patch with the escape keybinding", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/select_all_files.go b/pkg/integration/tests/patch_building/select_all_files.go index 17744786f..f7f340e63 100644 --- a/pkg/integration/tests/patch_building/select_all_files.go +++ b/pkg/integration/tests/patch_building/select_all_files.go @@ -7,7 +7,7 @@ import ( var SelectAllFiles = NewIntegrationTest(NewIntegrationTestArgs{ Description: "All all files of a commit to a custom patch with the 'a' keybinding", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/specific_selection.go b/pkg/integration/tests/patch_building/specific_selection.go index 1f5f56bc1..53131e9f0 100644 --- a/pkg/integration/tests/patch_building/specific_selection.go +++ b/pkg/integration/tests/patch_building/specific_selection.go @@ -7,7 +7,7 @@ import ( var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Build a custom patch with a specific selection of lines, adding individual lines, as well as a range and hunk, and adding a file directly", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/patch_building/start_new_patch.go b/pkg/integration/tests/patch_building/start_new_patch.go index 53d870b12..2b70feb8c 100644 --- a/pkg/integration/tests/patch_building/start_new_patch.go +++ b/pkg/integration/tests/patch_building/start_new_patch.go @@ -7,7 +7,7 @@ import ( var StartNewPatch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Attempt to add a file from another commit to a patch, then agree to start a new patch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/reflog/checkout.go b/pkg/integration/tests/reflog/checkout.go index 9307ab609..29af72449 100644 --- a/pkg/integration/tests/reflog/checkout.go +++ b/pkg/integration/tests/reflog/checkout.go @@ -7,7 +7,7 @@ import ( var Checkout = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Checkout a reflog commit as a detached head", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/reflog/cherry_pick.go b/pkg/integration/tests/reflog/cherry_pick.go index cc7f503e2..4167f06a9 100644 --- a/pkg/integration/tests/reflog/cherry_pick.go +++ b/pkg/integration/tests/reflog/cherry_pick.go @@ -7,7 +7,7 @@ import ( var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Cherry pick a reflog commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/reflog/patch.go b/pkg/integration/tests/reflog/patch.go index 568f36c43..cce33962c 100644 --- a/pkg/integration/tests/reflog/patch.go +++ b/pkg/integration/tests/reflog/patch.go @@ -7,7 +7,7 @@ import ( var Patch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Build a patch from a reflog commit and apply it", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/reflog/reset.go b/pkg/integration/tests/reflog/reset.go index 80c11cbb7..550be5607 100644 --- a/pkg/integration/tests/reflog/reset.go +++ b/pkg/integration/tests/reflog/reset.go @@ -7,7 +7,7 @@ import ( var Reset = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Hard reset to a reflog commit", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/shared/conflicts.go b/pkg/integration/tests/shared/conflicts.go index 76800a878..4f4a1954f 100644 --- a/pkg/integration/tests/shared/conflicts.go +++ b/pkg/integration/tests/shared/conflicts.go @@ -51,7 +51,7 @@ var MergeConflictsSetup = func(shell *Shell) { var CreateMergeConflictFile = func(shell *Shell) { MergeConflictsSetup(shell) - shell.RunShellCommandExpectError("git merge --no-edit second-change-branch") + shell.RunCommandExpectError([]string{"git", "merge", "--no-edit", "second-change-branch"}) } var CreateMergeCommit = func(shell *Shell) { @@ -84,7 +84,7 @@ var CreateMergeConflictFiles = func(shell *Shell) { EmptyCommit("second-change-branch unrelated change"). Checkout("first-change-branch") - shell.RunShellCommandExpectError("git merge --no-edit second-change-branch") + shell.RunCommandExpectError([]string{"git", "merge", "--no-edit", "second-change-branch"}) } // These 'multiple' variants are just like the short ones but with longer file contents and with multiple conflicts within the file. @@ -155,5 +155,5 @@ var CreateMergeConflictFileMultiple = func(shell *Shell) { EmptyCommit("second-change-branch unrelated change"). Checkout("first-change-branch") - shell.RunShellCommandExpectError("git merge --no-edit second-change-branch") + shell.RunCommandExpectError([]string{"git", "merge", "--no-edit", "second-change-branch"}) } diff --git a/pkg/integration/tests/staging/diff_context_change.go b/pkg/integration/tests/staging/diff_context_change.go index a88b99848..ca0aa0780 100644 --- a/pkg/integration/tests/staging/diff_context_change.go +++ b/pkg/integration/tests/staging/diff_context_change.go @@ -7,7 +7,7 @@ import ( var DiffContextChange = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Change the number of diff context lines while in the staging panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/staging/discard_all_changes.go b/pkg/integration/tests/staging/discard_all_changes.go index ce9f22aae..b869b2318 100644 --- a/pkg/integration/tests/staging/discard_all_changes.go +++ b/pkg/integration/tests/staging/discard_all_changes.go @@ -7,7 +7,7 @@ import ( var DiscardAllChanges = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Discard all changes of a file in the staging panel, then assert we land in the staging panel of the next file", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/staging/search.go b/pkg/integration/tests/staging/search.go index b7a42a276..a2c8edd69 100644 --- a/pkg/integration/tests/staging/search.go +++ b/pkg/integration/tests/staging/search.go @@ -7,7 +7,7 @@ import ( var Search = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Use the search feature in the staging panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/staging/stage_hunks.go b/pkg/integration/tests/staging/stage_hunks.go index 2dd07371c..1d05ed429 100644 --- a/pkg/integration/tests/staging/stage_hunks.go +++ b/pkg/integration/tests/staging/stage_hunks.go @@ -7,7 +7,7 @@ import ( var StageHunks = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stage and unstage various hunks of a file in the staging panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/staging/stage_lines.go b/pkg/integration/tests/staging/stage_lines.go index f517440b8..197e003ca 100644 --- a/pkg/integration/tests/staging/stage_lines.go +++ b/pkg/integration/tests/staging/stage_lines.go @@ -7,7 +7,7 @@ import ( var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stage and unstage various lines of a file in the staging panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/staging/stage_ranges.go b/pkg/integration/tests/staging/stage_ranges.go index 7458a5ea5..db5d7148f 100644 --- a/pkg/integration/tests/staging/stage_ranges.go +++ b/pkg/integration/tests/staging/stage_ranges.go @@ -7,7 +7,7 @@ import ( var StageRanges = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stage and unstage various ranges of a file in the staging panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/apply.go b/pkg/integration/tests/stash/apply.go index dfaa5130a..379f2fa2f 100644 --- a/pkg/integration/tests/stash/apply.go +++ b/pkg/integration/tests/stash/apply.go @@ -7,7 +7,7 @@ import ( var Apply = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Apply a stash entry", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/apply_patch.go b/pkg/integration/tests/stash/apply_patch.go index f8bf498ba..4c7a19841 100644 --- a/pkg/integration/tests/stash/apply_patch.go +++ b/pkg/integration/tests/stash/apply_patch.go @@ -7,7 +7,7 @@ import ( var ApplyPatch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Restore part of a stash entry via applying a custom patch", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/create_branch.go b/pkg/integration/tests/stash/create_branch.go index e6ea9c40c..995a0c27e 100644 --- a/pkg/integration/tests/stash/create_branch.go +++ b/pkg/integration/tests/stash/create_branch.go @@ -7,7 +7,7 @@ import ( var CreateBranch = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create a branch from a stash entry", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/drop.go b/pkg/integration/tests/stash/drop.go index 1450cfa30..af749bc3d 100644 --- a/pkg/integration/tests/stash/drop.go +++ b/pkg/integration/tests/stash/drop.go @@ -7,7 +7,7 @@ import ( var Drop = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Drop a stash entry", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/pop.go b/pkg/integration/tests/stash/pop.go index 369e3e50d..9eec96c55 100644 --- a/pkg/integration/tests/stash/pop.go +++ b/pkg/integration/tests/stash/pop.go @@ -7,7 +7,7 @@ import ( var Pop = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pop a stash entry", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/rename.go b/pkg/integration/tests/stash/rename.go index 0b47bcef3..9a079c9db 100644 --- a/pkg/integration/tests/stash/rename.go +++ b/pkg/integration/tests/stash/rename.go @@ -7,7 +7,7 @@ import ( var Rename = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Try to rename the stash.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/stash.go b/pkg/integration/tests/stash/stash.go index aa2393cd8..d5fc3e92e 100644 --- a/pkg/integration/tests/stash/stash.go +++ b/pkg/integration/tests/stash/stash.go @@ -7,7 +7,7 @@ import ( var Stash = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stashing files directly (not going through the stash menu)", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/stash_all.go b/pkg/integration/tests/stash/stash_all.go index 494617d02..74116fcee 100644 --- a/pkg/integration/tests/stash/stash_all.go +++ b/pkg/integration/tests/stash/stash_all.go @@ -7,7 +7,7 @@ import ( var StashAll = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stashing all changes (via the menu)", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/stash_and_keep_index.go b/pkg/integration/tests/stash/stash_and_keep_index.go index 1f1e91152..3201e70fb 100644 --- a/pkg/integration/tests/stash/stash_and_keep_index.go +++ b/pkg/integration/tests/stash/stash_and_keep_index.go @@ -7,7 +7,7 @@ import ( var StashAndKeepIndex = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stash staged changes", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/stash_including_untracked_files.go b/pkg/integration/tests/stash/stash_including_untracked_files.go index 770c87b9c..17283e3c0 100644 --- a/pkg/integration/tests/stash/stash_including_untracked_files.go +++ b/pkg/integration/tests/stash/stash_including_untracked_files.go @@ -7,7 +7,7 @@ import ( var StashIncludingUntrackedFiles = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stashing all files including untracked ones", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/stash_staged.go b/pkg/integration/tests/stash/stash_staged.go index 2fcd86c68..86ac6f327 100644 --- a/pkg/integration/tests/stash/stash_staged.go +++ b/pkg/integration/tests/stash/stash_staged.go @@ -7,7 +7,7 @@ import ( var StashStaged = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stash staged changes", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/stash/stash_unstaged.go b/pkg/integration/tests/stash/stash_unstaged.go index 6e8877681..5af7ac2bd 100644 --- a/pkg/integration/tests/stash/stash_unstaged.go +++ b/pkg/integration/tests/stash/stash_unstaged.go @@ -7,7 +7,7 @@ import ( var StashUnstaged = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Stash unstaged changes", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/submodule/add.go b/pkg/integration/tests/submodule/add.go index 1fd79a781..45babd186 100644 --- a/pkg/integration/tests/submodule/add.go +++ b/pkg/integration/tests/submodule/add.go @@ -7,7 +7,7 @@ import ( var Add = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Add a submodule", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/submodule/enter.go b/pkg/integration/tests/submodule/enter.go index d877a4e29..7b055c2b6 100644 --- a/pkg/integration/tests/submodule/enter.go +++ b/pkg/integration/tests/submodule/enter.go @@ -7,7 +7,7 @@ import ( var Enter = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Enter a submodule, add a commit, and then stage the change in the parent repo", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ diff --git a/pkg/integration/tests/submodule/remove.go b/pkg/integration/tests/submodule/remove.go index 5888aa1cf..3d85d4d5c 100644 --- a/pkg/integration/tests/submodule/remove.go +++ b/pkg/integration/tests/submodule/remove.go @@ -7,7 +7,7 @@ import ( var Remove = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Remove a submodule", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/submodule/reset.go b/pkg/integration/tests/submodule/reset.go index 98147b044..f9bf2afe2 100644 --- a/pkg/integration/tests/submodule/reset.go +++ b/pkg/integration/tests/submodule/reset.go @@ -7,7 +7,7 @@ import ( var Reset = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Enter a submodule, create a commit and stage some changes, then reset the submodule from back in the parent repo. This test captures functionality around getting a dirty submodule out of your files panel.", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ diff --git a/pkg/integration/tests/sync/fetch_prune.go b/pkg/integration/tests/sync/fetch_prune.go index c650947c1..ae34306a3 100644 --- a/pkg/integration/tests/sync/fetch_prune.go +++ b/pkg/integration/tests/sync/fetch_prune.go @@ -7,7 +7,7 @@ import ( var FetchPrune = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Fetch from the remote with the 'prune' option set in the git config", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/force_push.go b/pkg/integration/tests/sync/force_push.go index 32b094737..cf8ec60f1 100644 --- a/pkg/integration/tests/sync/force_push.go +++ b/pkg/integration/tests/sync/force_push.go @@ -7,7 +7,7 @@ import ( var ForcePush = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push to a remote with new commits, requiring a force push", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/force_push_multiple_matching.go b/pkg/integration/tests/sync/force_push_multiple_matching.go index 133554471..4f9998bb1 100644 --- a/pkg/integration/tests/sync/force_push_multiple_matching.go +++ b/pkg/integration/tests/sync/force_push_multiple_matching.go @@ -7,7 +7,7 @@ import ( var ForcePushMultipleMatching = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Force push to multiple branches because the user has push.default matching", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/force_push_multiple_upstream.go b/pkg/integration/tests/sync/force_push_multiple_upstream.go index c2b50c4fa..f94e33e22 100644 --- a/pkg/integration/tests/sync/force_push_multiple_upstream.go +++ b/pkg/integration/tests/sync/force_push_multiple_upstream.go @@ -7,7 +7,7 @@ import ( var ForcePushMultipleUpstream = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Force push to only the upstream branch of the current branch because the user has push.default upstream", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull.go b/pkg/integration/tests/sync/pull.go index 453740b96..7f5703791 100644 --- a/pkg/integration/tests/sync/pull.go +++ b/pkg/integration/tests/sync/pull.go @@ -7,7 +7,7 @@ import ( var Pull = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull a commit from the remote", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_and_set_upstream.go b/pkg/integration/tests/sync/pull_and_set_upstream.go index 93343492c..b2dbfddb4 100644 --- a/pkg/integration/tests/sync/pull_and_set_upstream.go +++ b/pkg/integration/tests/sync/pull_and_set_upstream.go @@ -7,7 +7,7 @@ import ( var PullAndSetUpstream = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull a commit from the remote, setting the upstream branch in the process", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_merge.go b/pkg/integration/tests/sync/pull_merge.go index 86f828570..d9c9e107d 100644 --- a/pkg/integration/tests/sync/pull_merge.go +++ b/pkg/integration/tests/sync/pull_merge.go @@ -7,7 +7,7 @@ import ( var PullMerge = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull with a merge strategy", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_merge_conflict.go b/pkg/integration/tests/sync/pull_merge_conflict.go index 2b4247584..3177cabe5 100644 --- a/pkg/integration/tests/sync/pull_merge_conflict.go +++ b/pkg/integration/tests/sync/pull_merge_conflict.go @@ -7,7 +7,7 @@ import ( var PullMergeConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull with a merge strategy, where a conflict occurs", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_rebase.go b/pkg/integration/tests/sync/pull_rebase.go index ca4c851d6..41ca82436 100644 --- a/pkg/integration/tests/sync/pull_rebase.go +++ b/pkg/integration/tests/sync/pull_rebase.go @@ -7,7 +7,7 @@ import ( var PullRebase = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull with a rebase strategy", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_rebase_conflict.go b/pkg/integration/tests/sync/pull_rebase_conflict.go index ebe7b3fd9..3b6c83b85 100644 --- a/pkg/integration/tests/sync/pull_rebase_conflict.go +++ b/pkg/integration/tests/sync/pull_rebase_conflict.go @@ -7,7 +7,7 @@ import ( var PullRebaseConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull with a rebase strategy, where a conflict occurs", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_rebase_interactive_conflict.go b/pkg/integration/tests/sync/pull_rebase_interactive_conflict.go index 37deb8a40..896c5cc45 100644 --- a/pkg/integration/tests/sync/pull_rebase_interactive_conflict.go +++ b/pkg/integration/tests/sync/pull_rebase_interactive_conflict.go @@ -7,7 +7,7 @@ import ( var PullRebaseInteractiveConflict = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull with an interactive rebase strategy, where a conflict occurs", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/pull_rebase_interactive_conflict_drop.go b/pkg/integration/tests/sync/pull_rebase_interactive_conflict_drop.go index a51d0d4b6..1950571b3 100644 --- a/pkg/integration/tests/sync/pull_rebase_interactive_conflict_drop.go +++ b/pkg/integration/tests/sync/pull_rebase_interactive_conflict_drop.go @@ -7,7 +7,7 @@ import ( var PullRebaseInteractiveConflictDrop = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Pull with an interactive rebase strategy, where a conflict occurs. Also drop a commit while rebasing", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/push.go b/pkg/integration/tests/sync/push.go index 92c0d0881..ea27b399c 100644 --- a/pkg/integration/tests/sync/push.go +++ b/pkg/integration/tests/sync/push.go @@ -7,7 +7,7 @@ import ( var Push = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push a commit to a pre-configured upstream", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/push_and_auto_set_upstream.go b/pkg/integration/tests/sync/push_and_auto_set_upstream.go index e8c8560e6..c3f58d644 100644 --- a/pkg/integration/tests/sync/push_and_auto_set_upstream.go +++ b/pkg/integration/tests/sync/push_and_auto_set_upstream.go @@ -7,7 +7,7 @@ import ( var PushAndAutoSetUpstream = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push a commit and set the upstream automatically as configured by git", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/push_and_set_upstream.go b/pkg/integration/tests/sync/push_and_set_upstream.go index a682832e1..0521c4b21 100644 --- a/pkg/integration/tests/sync/push_and_set_upstream.go +++ b/pkg/integration/tests/sync/push_and_set_upstream.go @@ -7,7 +7,7 @@ import ( var PushAndSetUpstream = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push a commit and set the upstream via a prompt", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/sync/push_follow_tags.go b/pkg/integration/tests/sync/push_follow_tags.go index 92a1074dc..463172abf 100644 --- a/pkg/integration/tests/sync/push_follow_tags.go +++ b/pkg/integration/tests/sync/push_follow_tags.go @@ -7,7 +7,7 @@ import ( var PushFollowTags = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push with --follow-tags configured in git config", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/push_no_follow_tags.go b/pkg/integration/tests/sync/push_no_follow_tags.go index eb7d04dff..599d05a64 100644 --- a/pkg/integration/tests/sync/push_no_follow_tags.go +++ b/pkg/integration/tests/sync/push_no_follow_tags.go @@ -7,7 +7,7 @@ import ( var PushNoFollowTags = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push with --follow-tags NOT configured in git config", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: true, // turns out this actually DOES push the tag. I have no idea why SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/push_tag.go b/pkg/integration/tests/sync/push_tag.go index 3a6739278..4016470a1 100644 --- a/pkg/integration/tests/sync/push_tag.go +++ b/pkg/integration/tests/sync/push_tag.go @@ -7,7 +7,7 @@ import ( var PushTag = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push a specific tag", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/push_with_credential_prompt.go b/pkg/integration/tests/sync/push_with_credential_prompt.go index ee45a03b0..79d7fcc1d 100644 --- a/pkg/integration/tests/sync/push_with_credential_prompt.go +++ b/pkg/integration/tests/sync/push_with_credential_prompt.go @@ -7,7 +7,7 @@ import ( var PushWithCredentialPrompt = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Push a commit to a pre-configured upstream, where credentials are required", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) { }, diff --git a/pkg/integration/tests/sync/rename_branch_and_pull.go b/pkg/integration/tests/sync/rename_branch_and_pull.go index 57968373d..99f7b7eea 100644 --- a/pkg/integration/tests/sync/rename_branch_and_pull.go +++ b/pkg/integration/tests/sync/rename_branch_and_pull.go @@ -7,7 +7,7 @@ import ( var RenameBranchAndPull = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Rename a branch to no longer match its upstream, then pull from the upstream", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/tag/checkout.go b/pkg/integration/tests/tag/checkout.go index 9ed88f980..10a2bd786 100644 --- a/pkg/integration/tests/tag/checkout.go +++ b/pkg/integration/tests/tag/checkout.go @@ -7,7 +7,7 @@ import ( var Checkout = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Checkout a tag", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/tag/crud_annotated.go b/pkg/integration/tests/tag/crud_annotated.go index f690fdce3..ca7b294b1 100644 --- a/pkg/integration/tests/tag/crud_annotated.go +++ b/pkg/integration/tests/tag/crud_annotated.go @@ -7,7 +7,7 @@ import ( var CrudAnnotated = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create and delete an annotated tag in the tags panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/tag/crud_lightweight.go b/pkg/integration/tests/tag/crud_lightweight.go index f76157038..e1a779595 100644 --- a/pkg/integration/tests/tag/crud_lightweight.go +++ b/pkg/integration/tests/tag/crud_lightweight.go @@ -7,7 +7,7 @@ import ( var CrudLightweight = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Create and delete a lightweight tag in the tags panel", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/tag/reset.go b/pkg/integration/tests/tag/reset.go index 1e2a9402b..749406d41 100644 --- a/pkg/integration/tests/tag/reset.go +++ b/pkg/integration/tests/tag/reset.go @@ -7,7 +7,7 @@ import ( var Reset = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Hard reset to a tag", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 563d19f80..1dd1a48e7 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -71,6 +71,7 @@ var tests = []*components.IntegrationTest{ conflicts.UndoChooseHunk, custom_commands.BasicCmdAtRuntime, custom_commands.BasicCmdFromConfig, + custom_commands.ComplexCmdAtRuntime, custom_commands.FormPrompts, custom_commands.MenuFromCommand, custom_commands.MenuFromCommandsOutput, diff --git a/pkg/integration/tests/ui/double_popup.go b/pkg/integration/tests/ui/double_popup.go index 85ba4309e..14a1edf7b 100644 --- a/pkg/integration/tests/ui/double_popup.go +++ b/pkg/integration/tests/ui/double_popup.go @@ -7,7 +7,7 @@ import ( var DoublePopup = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Open a popup from within another popup and assert you can escape back to the side panels", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/ui/switch_tab_from_menu.go b/pkg/integration/tests/ui/switch_tab_from_menu.go index 1a9067a28..88caf5d62 100644 --- a/pkg/integration/tests/ui/switch_tab_from_menu.go +++ b/pkg/integration/tests/ui/switch_tab_from_menu.go @@ -7,7 +7,7 @@ import ( var SwitchTabFromMenu = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Switch tab via the options menu", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/undo/undo_checkout_and_drop.go b/pkg/integration/tests/undo/undo_checkout_and_drop.go index b953c2528..a6dfc89c3 100644 --- a/pkg/integration/tests/undo/undo_checkout_and_drop.go +++ b/pkg/integration/tests/undo/undo_checkout_and_drop.go @@ -7,7 +7,7 @@ import ( var UndoCheckoutAndDrop = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Drop some commits and then undo/redo the actions", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/integration/tests/undo/undo_drop.go b/pkg/integration/tests/undo/undo_drop.go index bb3cbe1aa..6cf851ed3 100644 --- a/pkg/integration/tests/undo/undo_drop.go +++ b/pkg/integration/tests/undo/undo_drop.go @@ -7,7 +7,7 @@ import ( var UndoDrop = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Drop some commits and then undo/redo the actions", - ExtraCmdArgs: "", + ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { diff --git a/pkg/updates/updates.go b/pkg/updates/updates.go index d375c0eda..e98a995b1 100644 --- a/pkg/updates/updates.go +++ b/pkg/updates/updates.go @@ -283,7 +283,7 @@ func (u *Updater) downloadAndInstall(rawUrl string) error { } u.Log.Info("untarring tarball/unzipping zip file") - err = u.OSCommand.Cmd.New(fmt.Sprintf("tar -zxf %s %s", u.OSCommand.Quote(zipPath), "lazygit")).Run() + err = u.OSCommand.Cmd.New([]string{"tar", "-zxf", zipPath, "lazygit"}).Run() if err != nil { return err }