diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2021-04-10 11:40:42 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2021-04-10 11:54:38 +1000 |
commit | e42e7e5cbd9d075ee24ae8f91ba9e12bdd42fafc (patch) | |
tree | 61d65a544c056b3bf0384cf6954b81b292eb4b07 | |
parent | 93fac1f3124f87009091230f61cc13b5e5473cb5 (diff) |
fix commit amend
31 files changed, 1961 insertions, 1983 deletions
@@ -20,7 +20,7 @@ require ( github.com/imdario/mergo v0.3.11 github.com/integrii/flaggy v1.4.0 github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 - github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a + github.com/jesseduffield/gocui v0.3.1-0.20210410011117-a2bb4baca390 github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe // indirect github.com/jesseduffield/yaml v2.1.0+incompatible github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 @@ -108,6 +108,8 @@ github.com/jesseduffield/gocui v0.3.1-0.20210406065942-1b0c68414064 h1:Oe+QJuUIO github.com/jesseduffield/gocui v0.3.1-0.20210406065942-1b0c68414064/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a h1:ocrSuZxQIgWWt27b+rjiyIIPz6fzfFeoL5Q4cpa2cAo= github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= +github.com/jesseduffield/gocui v0.3.1-0.20210410011117-a2bb4baca390 h1:Es72JiUjt01TtvqCugdvOR91baB3DhuWF1DNuxA0frA= +github.com/jesseduffield/gocui v0.3.1-0.20210410011117-a2bb4baca390/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg= github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe h1:qsVhCf2RFyyKIUe/+gJblbCpXMUki9rZrHuEctg6M/E= github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4= github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE= diff --git a/pkg/commands/branches_test.go b/pkg/commands/branches_test.go new file mode 100644 index 000000000..85c477523 --- /dev/null +++ b/pkg/commands/branches_test.go @@ -0,0 +1,338 @@ +package commands + +import ( + "os/exec" + "testing" + + "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/jesseduffield/lazygit/pkg/test" + "github.com/stretchr/testify/assert" +) + +// TestGitCommandGetCommitDifferences is a function. +func TestGitCommandGetCommitDifferences(t *testing.T) { + type scenario struct { + testName string + command func(string, ...string) *exec.Cmd + test func(string, string) + } + + scenarios := []scenario{ + { + "Can't retrieve pushable count", + func(string, ...string) *exec.Cmd { + return secureexec.Command("test") + }, + func(pushableCount string, pullableCount string) { + assert.EqualValues(t, "?", pushableCount) + assert.EqualValues(t, "?", pullableCount) + }, + }, + { + "Can't retrieve pullable count", + func(cmd string, args ...string) *exec.Cmd { + if args[1] == "HEAD..@{u}" { + return secureexec.Command("test") + } + + return secureexec.Command("echo") + }, + func(pushableCount string, pullableCount string) { + assert.EqualValues(t, "?", pushableCount) + assert.EqualValues(t, "?", pullableCount) + }, + }, + { + "Retrieve pullable and pushable count", + func(cmd string, args ...string) *exec.Cmd { + if args[1] == "HEAD..@{u}" { + return secureexec.Command("echo", "10") + } + + return secureexec.Command("echo", "11") + }, + func(pushableCount string, pullableCount string) { + assert.EqualValues(t, "11", pushableCount) + assert.EqualValues(t, "10", pullableCount) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.GetCommitDifferences("HEAD", "@{u}")) + }) + } +} + +// TestGitCommandNewBranch is a function. +func TestGitCommandNewBranch(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"checkout", "-b", "test", "master"}, args) + + return secureexec.Command("echo") + } + + assert.NoError(t, gitCmd.NewBranch("test", "master")) +} + +// TestGitCommandDeleteBranch is a function. +func TestGitCommandDeleteBranch(t *testing.T) { + type scenario struct { + testName string + branch string + force bool + command func(string, ...string) *exec.Cmd + test func(error) + } + + scenarios := []scenario{ + { + "Delete a branch", + "test", + false, + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"branch", "-d", "test"}, args) + + return secureexec.Command("echo") + }, + func(err error) { + assert.NoError(t, err) + }, + }, + { + "Force delete a branch", + "test", + true, + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"branch", "-D", "test"}, args) + + return secureexec.Command("echo") + }, + func(err error) { + assert.NoError(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.DeleteBranch(s.branch, s.force)) + }) + } +} + +// TestGitCommandMerge is a function. +func TestGitCommandMerge(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"merge", "--no-edit", "test"}, args) + + return secureexec.Command("echo") + } + + assert.NoError(t, gitCmd.Merge("test", MergeOpts{})) +} + +// TestGitCommandCheckout is a function. +func TestGitCommandCheckout(t *testing.T) { + type scenario struct { + testName string + command func(string, ...string) *exec.Cmd + test func(error) + force bool + } + + scenarios := []scenario{ + { + "Checkout", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"checkout", "test"}, args) + + return secureexec.Command("echo") + }, + func(err error) { + assert.NoError(t, err) + }, + false, + }, + { + "Checkout forced", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"checkout", "--force", "test"}, args) + + return secureexec.Command("echo") + }, + func(err error) { + assert.NoError(t, err) + }, + true, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.Checkout("test", CheckoutOptions{Force: s.force})) + }) + } +} + +// TestGitCommandGetBranchGraph is a function. +func TestGitCommandGetBranchGraph(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"log", "--graph", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test", "--"}, args) + return secureexec.Command("echo") + } + _, err := gitCmd.GetBranchGraph("test") + assert.NoError(t, err) +} + +func TestGitCommandGetAllBranchGraph(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"log", "--graph", "--all", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium"}, args) + return secureexec.Command("echo") + } + cmdStr := gitCmd.Config.GetUserConfig().Git.AllBranchesLogCmd + _, err := gitCmd.OSCommand.RunCommandWithOutput(cmdStr) + assert.NoError(t, err) +} + +// TestGitCommandCurrentBranchName is a function. +func TestGitCommandCurrentBranchName(t *testing.T) { + type scenario struct { + testName string + command func(string, ...string) *exec.Cmd + test func(string, string, error) + } + + scenarios := []scenario{ + { + "says we are on the master branch if we are", + func(cmd string, args ...string) *exec.Cmd { + assert.Equal(t, "git", cmd) + return secureexec.Command("echo", "master") + }, + func(name string, displayname string, err error) { + assert.NoError(t, err) + assert.EqualValues(t, "master", name) + assert.EqualValues(t, "master", displayname) + }, + }, + { + "falls back to git `git branch --contains` if symbolic-ref fails", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + + switch args[0] { + case "symbolic-ref": + assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args) + return secureexec.Command("test") + case "branch": + assert.EqualValues(t, []string{"branch", "--contains"}, args) + return secureexec.Command("echo", "* master") + } + + return nil + }, + func(name string, displayname string, err error) { + assert.NoError(t, err) + assert.EqualValues(t, "master", name) + assert.EqualValues(t, "master", displayname) + }, + }, + { + "handles a detached head", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + + switch args[0] { + case "symbolic-ref": + assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args) + return secureexec.Command("test") + case "branch": + assert.EqualValues(t, []string{"branch", "--contains"}, args) + return secureexec.Command("echo", "* (HEAD detached at 123abcd)") + } + + return nil + }, + func(name string, displayname string, err error) { + assert.NoError(t, err) + assert.EqualValues(t, "123abcd", name) + assert.EqualValues(t, "(HEAD detached at 123abcd)", displayname) + }, + }, + { + "bubbles up error if there is one", + func(cmd string, args ...string) *exec.Cmd { + assert.Equal(t, "git", cmd) + return secureexec.Command("test") + }, + func(name string, displayname string, err error) { + assert.Error(t, err) + assert.EqualValues(t, "", name) + assert.EqualValues(t, "", displayname) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.CurrentBranchName()) + }) + } +} + +// TestGitCommandResetHard is a function. +func TestGitCommandResetHard(t *testing.T) { + type scenario struct { + testName string + ref string + command func(string, ...string) *exec.Cmd + test func(error) + } + + scenarios := []scenario{ + { + "valid case", + "HEAD", + test.CreateMockCommand(t, []*test.CommandSwapper{ + { + Expect: `git reset --hard HEAD`, + Replace: "echo", + }, + }), + func(err error) { + assert.NoError(t, err) + }, + }, + } + + gitCmd := NewDummyGitCommand() + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.ResetHard(s.ref)) + }) + } +} diff --git a/pkg/commands/commits.go b/pkg/commands/commits.go index 74043518d..3582131a6 100644 --- a/pkg/commands/commits.go +++ b/pkg/commands/commits.go @@ -19,20 +19,19 @@ func (c *GitCommand) ResetToCommit(sha string, strength string, options oscomman return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git reset --%s %s", strength, sha), options) } -// Commit commits to git -func (c *GitCommand) Commit(message string, flags string) (*exec.Cmd, error) { +func (c *GitCommand) CommitCmdStr(message string, flags string) string { splitMessage := strings.Split(message, "\n") lineArgs := "" for _, line := range splitMessage { lineArgs += fmt.Sprintf(" -m %s", c.OSCommand.Quote(line)) } - command := fmt.Sprintf("git commit %s%s", flags, lineArgs) - if c.usingGpg() { - return c.OSCommand.ShellCommandFromString(command), nil + flagsStr := "" + if flags != "" { + flagsStr = fmt.Sprintf(" %s", flags) } - return nil, c.OSCommand.RunCommand(command) + return fmt.Sprintf("git commit%s%s", flagsStr, lineArgs) } // Get the subject of the HEAD commit @@ -50,18 +49,17 @@ func (c *GitCommand) GetCommitMessage(commitSha string) (string, error) { } // AmendHead amends HEAD with whatever is staged in your working tree -func (c *GitCommand) AmendHead() (*exec.Cmd, error) { - command := "git commit --amend --no-edit --allow-empty" - if c.usingGpg() { - return c.OSCommand.ShellCommandFromString(command), nil - } +func (c *GitCommand) AmendHead() error { + return c.OSCommand.RunCommand(c.AmendHeadCmdStr()) +} - return nil, c.OSCommand.RunCommand(command) +// PrepareCommitAmendHeadSubProcess prepares a subprocess for `git commit --amend --allow-empty` +func (c *GitCommand) PrepareCommitAmendHeadSubProcess() *exec.Cmd { + return c.OSCommand.ShellCommandFromString(c.AmendHeadCmdStr()) } -// PrepareCommitAmendSubProcess prepares a subprocess for `git commit --amend --allow-empty` -func (c *GitCommand) PrepareCommitAmendSubProcess() *exec.Cmd { - return c.OSCommand.PrepareSubProcess("git", "commit", "--amend", "--allow-empty") +func (c *GitCommand) AmendHeadCmdStr() string { + return "git commit --amend --no-edit --allow-empty" } func (c *GitCommand) ShowCmdStr(sha string, filterPath string) string { diff --git a/pkg/commands/commits_test.go b/pkg/commands/commits_test.go new file mode 100644 index 000000000..f979e9507 --- /dev/null +++ b/pkg/commands/commits_test.go @@ -0,0 +1,111 @@ +package commands + +import ( + "os/exec" + "testing" + + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/jesseduffield/lazygit/pkg/test" + "github.com/stretchr/testify/assert" +) + +// TestGitCommandRenameCommit is a function. +func TestGitCommandRenameCommit(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"commit", "--allow-empty", "--amend", "--only", "-m", "test"}, args) + + return secureexec.Command("echo") + } + + assert.NoError(t, gitCmd.RenameCommit("test")) +} + +// TestGitCommandResetToCommit is a function. +func TestGitCommandResetToCommit(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"reset", "--hard", "78976bc"}, args) + + return secureexec.Command("echo") + } + + assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard", oscommands.RunCommandOptions{})) +} + +// TestGitCommandCommitStr is a function. +func TestGitCommandCommitStr(t *testing.T) { + type scenario struct { + testName string + message string + flags string + expected string + } + + scenarios := []scenario{ + { + testName: "Commit", + message: "test", + flags: "", + expected: "git commit -m \"test\"", + }, + { + testName: "Commit with --no-verify flag", + message: "test", + flags: "--no-verify", + expected: "git commit --no-verify -m \"test\"", + }, + { + testName: "Commit with multiline message", + message: "line1\nline2", + flags: "", + expected: "git commit -m \"line1\" -m \"line2\"", + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + cmdStr := gitCmd.CommitCmdStr(s.message, s.flags) + assert.Equal(t, s.expected, cmdStr) + }) + } +} + +// TestGitCommandCreateFixupCommit is a function. +func TestGitCommandCreateFixupCommit(t *testing.T) { + type scenario struct { + testName string + sha string + command func(string, ...string) *exec.Cmd + test func(error) + } + + scenarios := []scenario{ + { + "valid case", + "12345", + test.CreateMockCommand(t, []*test.CommandSwapper{ + { + Expect: `git commit --fixup=12345`, + Replace: "echo", + }, + }), + func(err error) { + assert.NoError(t, err) + }, + }, + } + + gitCmd := NewDummyGitCommand() + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.CreateFixupCommit(s.sha)) + }) + } +} diff --git a/pkg/commands/config.go b/pkg/commands/config.go index 0bb310e6d..9600c3403 100644 --- a/pkg/commands/config.go +++ b/pkg/commands/config.go @@ -46,3 +46,17 @@ func (c *GitCommand) GetConfigValue(key string) string { output, _ := c.getGitConfigValue(key) return output } + +// UsingGpg tells us whether the user has gpg enabled so that we can know +// whether we need to run a subprocess to allow them to enter their password +func (c *GitCommand) UsingGpg() bool { + overrideGpg := c.Config.GetUserConfig().Git.OverrideGpg + if overrideGpg { + return false + } + + gpgsign := c.GetConfigValue("commit.gpgsign") + value := strings.ToLower(gpgsign) + + return value == "true" || value == "1" || value == "yes" || value == "on" +} diff --git a/pkg/commands/config_test.go b/pkg/commands/config_test.go new file mode 100644 index 000000000..c16b53901 --- /dev/null +++ b/pkg/commands/config_test.go @@ -0,0 +1,70 @@ +package commands + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestGitCommandUsingGpg is a function. +func TestGitCommandUsingGpg(t *testing.T) { + type scenario struct { + testName string + getGitConfigValue func(string) (string, error) + test func(bool) + } + + scenarios := []scenario{ + { + "Option global and local config commit.gpgsign is not set", + func(string) (string, error) { return "", nil }, + func(gpgEnabled bool) { + assert.False(t, gpgEnabled) + }, + }, + { + "Option commit.gpgsign is true", + func(string) (string, error) { + return "True", nil + }, + func(gpgEnabled bool) { + assert.True(t, gpgEnabled) + }, + }, + { + "Option commit.gpgsign is on", + func(string) (string, error) { + return "ON", nil + }, + func(gpgEnabled bool) { + assert.True(t, gpgEnabled) + }, + }, + { + "Option commit.gpgsign is yes", + func(string) (string, error) { + return "YeS", nil + }, + func(gpgEnabled bool) { + assert.True(t, gpgEnabled) + }, + }, + { + "Option commit.gpgsign is 1", + func(string) (string, error) { + return "1", nil + }, + func(gpgEnabled bool) { + assert.True(t, gpgEnabled) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.getGitConfigValue = s.getGitConfigValue + s.test(gitCmd.UsingGpg()) + }) + } +} diff --git a/pkg/commands/files.go b/pkg/commands/files.go index fcf3ea51e..579a3a252 100644 --- a/pkg/commands/files.go +++ b/pkg/commands/files.go @@ -2,16 +2,20 @@ package commands import ( "fmt" + "io/ioutil" "os" "os/exec" "path/filepath" + "testing" "time" "github.com/go-errors/errors" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/gui/filetree" + "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/jesseduffield/lazygit/pkg/test" "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/mgutz/str" + "github.com/stretchr/testify/assert" ) // CatFile obtains the content of a file @@ -267,10 +271,7 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*models.Commit, commitIndex } // amend the commit - cmd, err := c.AmendHead() - if cmd != nil { - return errors.New("received unexpected pointer to cmd") - } + err := c.AmendHead() if err != nil { return err } @@ -314,9 +315,7 @@ func (c *GitCommand) ResetAndClean() error { return c.RemoveUntrackedFiles() } -// EditFile opens a file in a subprocess using whatever editor is available, -// falling back to core.editor, GIT_EDITOR, VISUAL, EDITOR, then vi -func (c *GitCommand) EditFile(filename string) (*exec.Cmd, error) { +func (c *GitCommand) EditFileCmdStr(filename string) (string, error) { editor := c.GetConfigValue("core.editor") if editor == "" { @@ -334,10 +333,386 @@ func (c *GitCommand) EditFile(filename string) (*exec.Cmd, error) { } } if editor == "" { - return nil, errors.New("No editor defined in $GIT_EDITOR, $VISUAL, $EDITOR, or git config") + return "", errors.New("No editor defined in $GIT_EDITOR, $VISUAL, $EDITOR, or git config") + } + + return fmt.Sprintf("%s %s", editor, c.OSCommand.Quote(filename)), nil +} + +func TestGitCommandApplyPatch(t *testing.T) { + type scenario struct { + testName string + command func(string, ...string) *exec.Cmd + test func(error) + } + + scenarios := []scenario{ + { + "valid case", + func(cmd string, args ...string) *exec.Cmd { + assert.Equal(t, "git", cmd) + assert.EqualValues(t, []string{"apply", "--cached"}, args[0:2]) + filename := args[2] + content, err := ioutil.ReadFile(filename) + assert.NoError(t, err) + + assert.Equal(t, "test", string(content)) + + return secureexec.Command("echo", "done") + }, + func(err error) { + assert.NoError(t, err) + }, + }, + { + "command returns error", + func(cmd string, args ...string) *exec.Cmd { + assert.Equal(t, "git", cmd) + assert.EqualValues(t, []string{"apply", "--cached"}, args[0:2]) + filename := args[2] + // TODO: Ideally we want to mock out OSCommand here so that we're not + // double handling testing it's CreateTempFile functionality, + // but it is going to take a bit of work to make a proper mock for it + // so I'm leaving it for another PR + content, err := ioutil.ReadFile(filename) + assert.NoError(t, err) + + assert.Equal(t, "test", string(content)) + + return secureexec.Command("test") + }, + func(err error) { + assert.Error(t, err) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := NewDummyGitCommand() + gitCmd.OSCommand.Command = s.command + s.test(gitCmd.ApplyPatch("test", "cached")) + }) + } +} + +// TestGitCommandDiscardOldFileChanges is a function. +func TestGitCommandDiscardOldFileChanges(t *testing.T) { + type scenario struct { + testName string + getGitConfigValue func(string) (string, error) + commits []*models.Commit + commitIndex int + fileName string + command func(string, ...string) *exec.Cmd + test func(error) + } + + scenarios := []scenario{ + { + "returns error when index outside of range of commits", + func(string) (string, error) { + return "", nil + }, + []*models.Commit{}, + 0, + "test999.txt", + nil, + func(err error) { + assert.Error(t, err) + }, + }, + { + "returns error when using gpg", + func(string) (string, error) { + return "true", nil + }, + []*models.Commit{{Name: "commit", Sha: "123456"}}, + 0, + "test999.txt", + nil, + func(err error) { + assert.Error(t, err) + }, + }, + { + "checks out file if it already existed", + func(string) (string, error) { + return "", nil + }, + []*models.Commit{ + {Name: "commit", Sha: "123456"}, + {Name: "commit2", Sha: "abcdef"}, + }, + 0, + "test999.txt", + test.CreateMockCommand(t, []*test.CommandSwapper{ + { + Expect: "git rebase --interactive --autostash --keep-empty abcdef", + Replace: "echo", + }, + { + Expect: "git cat-file -e HEAD^:test999.txt", + Replace: "echo", + }, + { + Expect: "git checkout HEAD^ test999.txt", + Replace: "echo", + }, + { + Expect: "git commit --amend --no-edit --allow-empty", + Replace: "echo", + }, + { + Expect: "git rebase --continue", + Replace: "echo", + }, + }), + func(err error) { + assert.NoError(t, err) + }, + }, + // test for when the file was created within the commit requires a refactor to support proper mocks + // currently we'd need to mock out the os.Remove function and that's gonna introduce tech debt + } + + gitCmd := NewDummyGitCommand() + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd.OSCommand.Command = s.command + gitCmd.getGitConfigValue = s.getGitConfigValue + s.test(gitCmd.DiscardOldFileChanges(s.commits, s.commitIndex, s.fileName)) + }) + } +} + +// TestGitCommandDiscardUnstagedFileChanges is a function. +func TestGitCommandDiscardUnstagedFileChanges(t *testing.T) { + type scenario struct { + testName string |