From 9b2b0fc1226ebe0858fcbed59d87bcaae8c8a2e9 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 30 Dec 2021 17:19:01 +1100 Subject: WIP --- pkg/commands/branches.go | 7 +- pkg/commands/branches_test.go | 222 ++++++++---------------- pkg/commands/dummies.go | 9 + pkg/commands/files.go | 6 +- pkg/commands/git.go | 8 + pkg/commands/loaders/branches.go | 26 +-- pkg/commands/loaders/commit_files.go | 57 +++++++ pkg/commands/loaders/commits.go | 15 +- pkg/commands/loaders/commits_test.go | 2 +- pkg/commands/loaders/files.go | 134 +++++++++++++++ pkg/commands/loaders/files_test.go | 203 ++++++++++++++++++++++ pkg/commands/loading_commit_files.go | 43 ----- pkg/commands/loading_files.go | 116 ------------- pkg/commands/loading_files_test.go | 227 ------------------------- pkg/commands/oscommands/dummies.go | 2 +- pkg/commands/oscommands/fake_cmd_obj_runner.go | 28 ++- pkg/commands/oscommands/os.go | 2 +- pkg/commands/oscommands/os_test.go | 2 +- pkg/commands/stash_entries.go | 11 +- 19 files changed, 549 insertions(+), 571 deletions(-) create mode 100644 pkg/commands/loaders/commit_files.go create mode 100644 pkg/commands/loaders/files.go create mode 100644 pkg/commands/loaders/files_test.go delete mode 100644 pkg/commands/loading_commit_files.go delete mode 100644 pkg/commands/loading_files.go delete mode 100644 pkg/commands/loading_files_test.go (limited to 'pkg/commands') diff --git a/pkg/commands/branches.go b/pkg/commands/branches.go index e3c40d343..ef1838e9e 100644 --- a/pkg/commands/branches.go +++ b/pkg/commands/branches.go @@ -127,9 +127,12 @@ type MergeOpts struct { // Merge merge func (c *GitCommand) Merge(branchName string, opts MergeOpts) error { - mergeArgs := c.UserConfig.Git.Merging.Args + mergeArg := "" + if c.UserConfig.Git.Merging.Args != "" { + mergeArg = " " + c.UserConfig.Git.Merging.Args + } - command := fmt.Sprintf("git merge --no-edit %s %s", mergeArgs, c.OSCommand.Quote(branchName)) + command := fmt.Sprintf("git merge --no-edit%s %s", mergeArg, c.OSCommand.Quote(branchName)) if opts.FastForwardOnly { command = fmt.Sprintf("%s --ff-only", command) } diff --git a/pkg/commands/branches_test.go b/pkg/commands/branches_test.go index 45cfb8315..58a3e1cc4 100644 --- a/pkg/commands/branches_test.go +++ b/pkg/commands/branches_test.go @@ -1,120 +1,85 @@ package commands import ( - "os/exec" "testing" - "github.com/jesseduffield/lazygit/pkg/secureexec" - "github.com/jesseduffield/lazygit/pkg/test" + "github.com/go-errors/errors" + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "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) + testName string + runner *oscommands.FakeCmdObjRunner + expectedPushables string + expectedPullables 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) - }, + oscommands.NewFakeRunner(t). + Expect("git rev-list @{u}..HEAD --count", "", errors.New("error")), + "?", "?", }, { "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) - }, + oscommands.NewFakeRunner(t). + Expect("git rev-list @{u}..HEAD --count", "1\n", nil). + Expect("git rev-list HEAD..@{u} --count", "", errors.New("error")), + "?", "?", }, { "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) - }, + oscommands.NewFakeRunner(t). + Expect("git rev-list @{u}..HEAD --count", "1\n", nil). + Expect("git rev-list HEAD..@{u} --count", "2\n", nil), + "1", "2", }, } 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}")) + gitCmd := NewDummyGitCommandWithRunner(s.runner) + pushables, pullables := gitCmd.GetCommitDifferences("HEAD", "@{u}") + assert.EqualValues(t, s.expectedPushables, pushables) + assert.EqualValues(t, s.expectedPullables, pullables) + s.runner.CheckForMissingCalls() }) } } -// 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") - } + runner := oscommands.NewFakeRunner(t). + Expect(`git checkout -b "test" "master"`, "", nil) + gitCmd := NewDummyGitCommandWithRunner(runner) assert.NoError(t, gitCmd.NewBranch("test", "master")) + runner.CheckForMissingCalls() } -// TestGitCommandDeleteBranch is a function. func TestGitCommandDeleteBranch(t *testing.T) { type scenario struct { testName string - branch string force bool - command func(string, ...string) *exec.Cmd + runner *oscommands.FakeCmdObjRunner 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") - }, + oscommands.NewFakeRunner(t).Expect(`git branch -d "test"`, "", nil), 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") - }, + oscommands.NewFakeRunner(t).Expect(`git branch -D "test"`, "", nil), func(err error) { assert.NoError(t, err) }, @@ -123,31 +88,27 @@ func TestGitCommandDeleteBranch(t *testing.T) { 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)) + gitCmd := NewDummyGitCommandWithRunner(s.runner) + + s.test(gitCmd.DeleteBranch("test", s.force)) + s.runner.CheckForMissingCalls() }) } } -// 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") - } + runner := oscommands.NewFakeRunner(t). + Expect(`git merge --no-edit "test"`, "", nil) + gitCmd := NewDummyGitCommandWithRunner(runner) assert.NoError(t, gitCmd.Merge("test", MergeOpts{})) + runner.CheckForMissingCalls() } -// TestGitCommandCheckout is a function. func TestGitCommandCheckout(t *testing.T) { type scenario struct { testName string - command func(string, ...string) *exec.Cmd + runner *oscommands.FakeCmdObjRunner test func(error) force bool } @@ -155,12 +116,7 @@ func TestGitCommandCheckout(t *testing.T) { 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") - }, + oscommands.NewFakeRunner(t).Expect(`git checkout "test"`, "", nil), func(err error) { assert.NoError(t, err) }, @@ -168,12 +124,7 @@ func TestGitCommandCheckout(t *testing.T) { }, { "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") - }, + oscommands.NewFakeRunner(t).Expect(`git checkout --force "test"`, "", nil), func(err error) { assert.NoError(t, err) }, @@ -183,52 +134,43 @@ func TestGitCommandCheckout(t *testing.T) { for _, s := range scenarios { t.Run(s.testName, func(t *testing.T) { - gitCmd := NewDummyGitCommand() - gitCmd.OSCommand.Command = s.command + gitCmd := NewDummyGitCommandWithRunner(s.runner) s.test(gitCmd.Checkout("test", CheckoutOptions{Force: s.force})) + s.runner.CheckForMissingCalls() }) } } -// 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") - } + runner := oscommands.NewFakeRunner(t).ExpectArgs([]string{ + "git", "log", "--graph", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test", "--", + }, "", nil) + gitCmd := NewDummyGitCommandWithRunner(runner) _, 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") - } + runner := oscommands.NewFakeRunner(t).ExpectArgs([]string{ + "git", "log", "--graph", "--all", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", + }, "", nil) + gitCmd := NewDummyGitCommandWithRunner(runner) cmdStr := gitCmd.UserConfig.Git.AllBranchesLogCmd _, err := gitCmd.Cmd.New(cmdStr).RunWithOutput() assert.NoError(t, err) } -// TestGitCommandCurrentBranchName is a function. func TestGitCommandCurrentBranchName(t *testing.T) { type scenario struct { testName string - command func(string, ...string) *exec.Cmd + runner *oscommands.FakeCmdObjRunner 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") - }, + oscommands.NewFakeRunner(t).Expect(`git symbolic-ref --short HEAD`, "master", nil), func(name string, displayname string, err error) { assert.NoError(t, err) assert.EqualValues(t, "master", name) @@ -237,20 +179,9 @@ func TestGitCommandCurrentBranchName(t *testing.T) { }, { "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 - }, + oscommands.NewFakeRunner(t). + Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")). + Expect(`git branch --contains`, "* master", nil), func(name string, displayname string, err error) { assert.NoError(t, err) assert.EqualValues(t, "master", name) @@ -259,20 +190,9 @@ func TestGitCommandCurrentBranchName(t *testing.T) { }, { "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 - }, + oscommands.NewFakeRunner(t). + Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")). + Expect(`git branch --contains`, "* (HEAD detached at 123abcd)", nil), func(name string, displayname string, err error) { assert.NoError(t, err) assert.EqualValues(t, "123abcd", name) @@ -281,10 +201,9 @@ func TestGitCommandCurrentBranchName(t *testing.T) { }, { "bubbles up error if there is one", - func(cmd string, args ...string) *exec.Cmd { - assert.Equal(t, "git", cmd) - return secureexec.Command("test") - }, + oscommands.NewFakeRunner(t). + Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")). + Expect(`git branch --contains`, "", errors.New("error")), func(name string, displayname string, err error) { assert.Error(t, err) assert.EqualValues(t, "", name) @@ -295,19 +214,18 @@ func TestGitCommandCurrentBranchName(t *testing.T) { for _, s := range scenarios { t.Run(s.testName, func(t *testing.T) { - gitCmd := NewDummyGitCommand() - gitCmd.OSCommand.Command = s.command + gitCmd := NewDummyGitCommandWithRunner(s.runner) s.test(gitCmd.CurrentBranchName()) + s.runner.CheckForMissingCalls() }) } } -// TestGitCommandResetHard is a function. func TestGitCommandResetHard(t *testing.T) { type scenario struct { testName string ref string - command func(string, ...string) *exec.Cmd + runner *oscommands.FakeCmdObjRunner test func(error) } @@ -315,23 +233,17 @@ func TestGitCommandResetHard(t *testing.T) { { "valid case", "HEAD", - test.CreateMockCommand(t, []*test.CommandSwapper{ - { - Expect: `git reset --hard HEAD`, - Replace: "echo", - }, - }), + oscommands.NewFakeRunner(t). + Expect(`git reset --hard "HEAD"`, "", nil), 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 + gitCmd := NewDummyGitCommandWithRunner(s.runner) s.test(gitCmd.ResetHard(s.ref)) }) } diff --git a/pkg/commands/dummies.go b/pkg/commands/dummies.go index c17179c8e..126007baa 100644 --- a/pkg/commands/dummies.go +++ b/pkg/commands/dummies.go @@ -23,3 +23,12 @@ func NewDummyGitCommandWithOSCommand(osCommand *oscommands.OSCommand) *GitComman GetCmdWriter: func() io.Writer { return ioutil.Discard }, } } + +func NewDummyGitCommandWithRunner(runner oscommands.ICmdObjRunner) *GitCommand { + builder := oscommands.NewDummyCmdObjBuilder(runner) + gitCommand := NewDummyGitCommand() + gitCommand.Cmd = builder + gitCommand.OSCommand.Cmd = builder + + return gitCommand +} diff --git a/pkg/commands/files.go b/pkg/commands/files.go index da451e80c..2d118c749 100644 --- a/pkg/commands/files.go +++ b/pkg/commands/files.go @@ -9,6 +9,7 @@ import ( "time" "github.com/go-errors/errors" + "github.com/jesseduffield/lazygit/pkg/commands/loaders" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/gui/filetree" @@ -75,7 +76,10 @@ func (c *GitCommand) BeforeAndAfterFileForRename(file *models.File) (*models.Fil // all files, passing the --no-renames flag and then recursively call the function // again for the before file and after file. - filesWithoutRenames := c.GetStatusFiles(GetStatusFileOptions{NoRenames: true}) + filesWithoutRenames := loaders. + NewFileLoader(c.Common, c.Cmd, c.GitConfig). + GetStatusFiles(loaders.GetStatusFileOptions{NoRenames: true}) + var beforeFile *models.File var afterFile *models.File for _, f := range filesWithoutRenames { diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 950042780..90fd7b262 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -223,3 +223,11 @@ 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").Run() } + +func (c *GitCommand) GetDotGitDir() string { + return c.DotGitDir +} + +func (c *GitCommand) GetCmd() oscommands.ICmdObjBuilder { + return c.Cmd +} diff --git a/pkg/commands/loaders/branches.go b/pkg/commands/loaders/branches.go index fc3c15fc9..60e4f7aac 100644 --- a/pkg/commands/loaders/branches.go +++ b/pkg/commands/loaders/branches.go @@ -4,7 +4,6 @@ import ( "regexp" "strings" - "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/utils" @@ -29,9 +28,14 @@ type BranchLoader struct { reflogCommits []*models.Commit } +type BranchLoaderGitCommand interface { + GetRawBranches() (string, error) + CurrentBranchName() (string, string, error) +} + func NewBranchLoader( cmn *common.Common, - gitCommand *commands.GitCommand, + gitCommand BranchLoaderGitCommand, reflogCommits []*models.Commit, ) *BranchLoader { return &BranchLoader{ @@ -43,10 +47,10 @@ func NewBranchLoader( } // Load the list of branches for the current repo -func (b *BranchLoader) Load() []*models.Branch { - branches := b.obtainBranches() +func (self *BranchLoader) Load() []*models.Branch { + branches := self.obtainBranches() - reflogBranches := b.obtainReflogBranches() + reflogBranches := self.obtainReflogBranches() // loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches branchesWithRecency := make([]*models.Branch, 0) @@ -78,7 +82,7 @@ outer: } } if !foundHead { - currentBranchName, currentBranchDisplayName, err := b.getCurrentBranchName() + currentBranchName, currentBranchDisplayName, err := self.getCurrentBranchName() if err != nil { panic(err) } @@ -87,8 +91,8 @@ outer: return branches } -func (b *BranchLoader) obtainBranches() []*models.Branch { - output, err := b.getRawBranches() +func (self *BranchLoader) obtainBranches() []*models.Branch { + output, err := self.getRawBranches() if err != nil { panic(err) } @@ -150,11 +154,11 @@ func (b *BranchLoader) obtainBranches() []*models.Branch { // TODO: only look at the new reflog commits, and otherwise store the recencies in // int form against the branch to recalculate the time ago -func (b *BranchLoader) obtainReflogBranches() []*models.Branch { +func (self *BranchLoader) obtainReflogBranches() []*models.Branch { foundBranchesMap := map[string]bool{} re := regexp.MustCompile(`checkout: moving from ([\S]+) to ([\S]+)`) - reflogBranches := make([]*models.Branch, 0, len(b.reflogCommits)) - for _, commit := range b.reflogCommits { + reflogBranches := make([]*models.Branch, 0, len(self.reflogCommits)) + for _, commit := range self.reflogCommits { if match := re.FindStringSubmatch(commit.Name); len(match) == 3 { recency := utils.UnixToTimeAgo(commit.UnixTimestamp) for _, branchName := range match[1:] { diff --git a/pkg/commands/loaders/commit_files.go b/pkg/commands/loaders/commit_files.go new file mode 100644 index 000000000..1b65737ec --- /dev/null +++ b/pkg/commands/loaders/commit_files.go @@ -0,0 +1,57 @@ +package loaders + +import ( + "fmt" + "strings" + + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/common" +) + +type CommitFileLoader struct { + *common.Common + cmd oscommands.ICmdObjBuilder +} + +func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *CommitFileLoader { + return &CommitFileLoader{ + Common: common, + cmd: cmd, + } +} + +// GetFilesInDiff get the specified commit files +func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) { + reverseFlag := "" + if reverse { + reverseFlag = " -R " + } + + filenames, err := self.cmd.New(fmt.Sprintf("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)).RunWithOutput() + if err != nil { + return nil, err + } + + return self.getCommitFilesFromFilenames(filenames), nil +} + +// filenames string is something like "file1\nfile2\nfile3" +func (self *CommitFileLoader) getCommitFilesFromFilenames(filenames string) []*models.CommitFile { + commitFiles := make([]*models.CommitFile, 0) + + lines := strings.Split(strings.TrimRight(filenames, "\x00"), "\x00") + n := len(lines) + for i := 0; i < n-1; i += 2 { + // typical result looks like 'A my_file' meaning my_file was added + changeStatus := lines[i] + name := lines[i+1] + + commitFiles = append(commitFiles, &models.CommitFile{ + Name: name, + ChangeStatus: changeStatus, + }) + } + + return commitFiles +} diff --git a/pkg/commands/loaders/commits.go b/pkg/commands/loaders/commits.go index bbd520c81..6f2336e3a 100644 --- a/pkg/commands/loaders/commits.go +++ b/pkg/commands/loaders/commits.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" - "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" @@ -37,20 +36,26 @@ type CommitLoader struct { dotGitDir string } +type CommitLoaderGitCommand interface { + CurrentBranchName() (string, string, error) + RebaseMode() (enums.RebaseMode, error) + GetCmd() oscommands.ICmdObjBuilder + GetDotGitDir() string +} + // making our dependencies explicit for the sake of easier testing func NewCommitLoader( cmn *common.Common, - gitCommand *commands.GitCommand, - osCommand *oscommands.OSCommand, + gitCommand CommitLoaderGitCommand, ) *CommitLoader { return &CommitLoader{ Common: cmn, - cmd: gitCommand.Cmd, + cmd: gitCommand.GetCmd(), getCurrentBranchName: gitCommand.CurrentBranchName, getRebaseMode: gitCommand.RebaseMode, readFile: ioutil.ReadFile, walkFiles: filepath.Walk, - dotGitDir: gitCommand.DotGitDir, + dotGitDir: gitCommand.GetDotGitDir(), } } diff --git a/pkg/commands/loaders/commits_test.go b/pkg/commands/loaders/commits_test.go index f793567f7..5491fb4a5 100644 --- a/pkg/commands/loaders/commits_test.go +++ b/pkg/commands/loaders/commits_test.go @@ -189,7 +189,7 @@ func TestGetCommits(t *testing.T) { t.Run(scenario.testName, func(t *testing.T) { builder := &CommitLoader{ Common: utils.NewDummyCommon(), - cmd: oscommands.NewCmdObjBuilderDummy(scenario.runner), + cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner), getCurrentBranchName: func() (string, string, error) { return scenario.currentBranchName, scenario.currentBranchName, nil }, diff --git a/pkg/commands/loaders/files.go b/pkg/commands/loaders/files.go new file mode 100644 index 000000000..a1964a2b9 --- /dev/null +++ b/pkg/commands/loaders/files.go @@ -0,0 +1,134 @@ +package loaders + +import ( + "fmt" + "strings" + + "github.com/jesseduffield/lazygit/pkg/commands/git_config" + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/common" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +type FileLoader struct { + *common.Common + cmd oscommands.ICmdObjBuilder + gitConfig git_config.IGitConfig + getFileType func(string) string +} + +func NewFileLoader(cmn *common.Common, cmd oscommands.ICmdObjBuilder, gitConfig git_config.IGitConfig) *FileLoader { + return &FileLoader{ + Common: cmn, + cmd: cmd, + gitConfig: gitConfig, + getFileType: oscommands.FileType, + } +} + +type GetStatusFileOptions struct { + NoRenames bool +} + +func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File { + // check if config wants us ignoring untracked files + untrackedFilesSetting := self.gitConfig.Get("status.showUntrackedFiles") + + if untrackedFilesSetting == "" { + untrackedFilesSetting = "all" + } + untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting) + + statuses, err := self.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg}) + if err != nil { + self.Log.Error(err) + } + files := []*models.File{} + + for _, status := range statuses { + if strings.HasPrefix(status.StatusString, "warning") { + self.Log.Warningf("warning when calling git status: %s", status.StatusString) + continue + } + change := status.Change + stagedChange := change[0:1] + unstagedChange := change[1:2] + untracked := utils.IncludesString([]string{"??", "A ", "AM"}, change) + hasNoStagedChanges := utils.IncludesString([]string{" ", "U", "?"}, stagedChange) + hasMergeConflicts := utils.IncludesString([]string{"DD", "AA", "UU", "AU", "UA", "UD", "DU"}, change) + hasInlineMergeConflicts := utils.IncludesString([]string{"UU", "AA"}, change) + + file := &models.File{ + Name: status.Name, + PreviousName: status.PreviousName, + DisplayString: status.StatusString, + HasStagedChanges: !hasNoStagedChanges, + HasUnstagedChanges: unstagedChange != " ", + Tracked: !untracked, + Deleted: unstagedChange == "D" || stagedChange == "D", + Added: unstagedChange == "A" || untracked, + HasMergeConflicts: hasMergeConflicts, + HasInlineMergeConflicts: hasInlineMergeConflicts, + Type: self.getFileType(status.Name), + ShortStatus: change, + } + files = append(files, file) + } + + return files +} + +// GitStatus returns the file status of the repo +type GitStatusOptions struct { + NoRenames bool + UntrackedFilesArg string +} + +type FileStatus struct { + StatusString string + Change string // ??, MM, AM, ... + Name string + PreviousName string +} + +func (c *FileLoader) GitStatus(opts GitStatusOptions) ([]FileStatus, error) { + noRenamesFlag := "" + if opts.NoRenames { + noRenamesFlag = " --no-renames" + } + + statusLines, err := c.cmd.New(fmt.Sprintf("git status %s --porcelain -z%s", opts.UntrackedFilesArg, noRenamesFlag)).RunWithOutput() + if err != nil { + return []FileStatus{}, err + } + + splitLines := strings.Split(statusLines, "\x00") + response := []FileStatus{} + + for i := 0; i < len(splitLines); i++ { + original := splitLines[i] + + if len(original) < 3 { + continue + } + + status := FileStatus{ + StatusString: original, + Change: original[:2], + Name: original[3:], + PreviousName: "", + } + + if strings.HasPrefix(status.Change, "R") { + // if a line starts with 'R' then the next line is the original file. + status.PreviousName = strings.TrimSpace(splitLines[i+1]) + status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousName, status.Name) + i++ + } + + response = append(response, status) + } + + return response, nil +} diff --git a/pkg/commands/loaders/files_test.go b/pkg/commands/loaders/files_test.go new file mode 100644 index 000000000..1613290bb --- /dev/null +++ b/pkg/commands/loaders/files_test.go @@ -0,0 +1,203 @@ +package loaders + +import ( + "testing" + + "github.com/jesseduffield/lazygit/pkg/commands/git_config" + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestGitCommandGetStatusFiles(t *testing.T) { + type scenario struct { + testName string + runner oscommands.ICmdObjRunner + expectedFiles []*models.File + } + + scenarios := []scenario{ + { + "No files found", + oscommands.NewFakeRunner(t). + Expect(`git status --untracked-files=yes --porcelain -z`, "", nil), + []*models.File{}, + }, + { + "Several files found", + oscommands.NewFakeRunner(t). + Expect( + `git status --untracked-files=yes --porcelain -z`, + "MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt", + nil, + ), + []*models.File{ + { + Name: "file1.txt", + HasStagedChanges: true, + HasUnstagedChanges: true, + Tracked: true, + Added: false, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "MM file1.txt", + Type: "file", + ShortStatus: "MM", + }, + { + Name: "file3.txt", + HasStagedChanges: true, + HasUnstagedChanges: false, + Tracked: false, + Added: true, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "A file3.txt", + Type: "file", + ShortStatus: "A ", + }, + { + Name: "file2.txt", + HasStagedChanges: true, + HasUnstagedChanges: true, + Tracked: false, + Added: true, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "AM file2.txt", + Type: "file", + ShortStatus: "AM", + }, + { + Name: "file4.txt", + HasStagedChanges: false, + HasUnstagedChanges: true, + Tracked: false, + Added: true, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "?? file4.txt", + Type: "file", + ShortStatus: "??", + }, + { + Name: "file5.txt", + HasStagedChanges: false, + HasUnstagedChanges: true, + Tracked: true, + Added: false, + Deleted: false, + HasMergeConflicts: true, + HasInlineMergeConflicts: true, + DisplayString: "UU file5.txt", + Type: "file", + ShortStatus: "UU", + }, + }, + }, + { + "File with new line char", + oscommands.NewFakeRunner(t). + Expect(`git status --untracked-files=yes --porcelain -z`, "MM a\nb.txt", nil), + []*models.File{ + { + Name: "a\nb.txt", + HasStagedChanges: true, + HasUnstagedChanges: true, + Tracked: true, + Added: false, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "MM a\nb.txt", + Type: "file", + ShortStatus: "MM", + }, + }, + }, + { + "Renamed files", + oscommands.NewFakeRunner(t). + Expect( + `git status --untracked-files=yes --porcelain -z`, + "R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt", + nil, + ), + []*models.File{ + { + Name: "after1.txt", + PreviousName: "before1.txt", + HasStagedChanges: true, + HasUnstagedChanges: false, + Tracked: true, + Added: false, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "R before1.txt -> after1.txt", + Type: "file", + ShortStatus: "R ", + }, + { + Name: "after2.txt", + PreviousName: "before2.txt", + HasStagedChanges: true, + HasUnstagedChanges: true, + Tracked: true, + Added: false, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "RM before2.txt -> after2.txt", + Type: "file", + ShortStatus: "RM", + }, + }, + }, + { + "File with arrow in name", + oscommands.NewFakeRunner(t). + Expect( + `git status --untracked-files=yes --porcelain -z`, + `?? a -> b.txt`, + nil, + ), + []*models.File{ + { + Name: "a -> b.txt", + HasStagedChanges: false, + HasUnstagedChanges: true, + Tracked: false, + Added: true, + Deleted: false, + HasMergeConflicts: false, + HasInlineMergeConflicts: false, + DisplayString: "?? a -> b.txt", + Type: "file", + ShortStatus: "??", + }, + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + cmd := oscommands.NewDummyCmdObjBuilder(s.runner) + gitConfig := git_config.NewFakeGitConfig(map[string]string{"status.showUntrackedFiles": "yes"}) + + loader := &FileLoader{ + Common: utils.NewDummyCommon(), + cmd: cmd, + gitConfig: gitConfig, + getFileType: func(string) string { return "file" }, + } + + assert.EqualValues(t, s.expectedFiles, loader.GetStatusFiles(GetStatusFileOptions{})) + }) + } +} diff --git a/pkg/commands/loading_commit_files.go b/pkg/commands/loading_commit_files.go deleted file mode 100644 index fa6d3c8a2..000000000 --- a/pkg/commands/loading_commit_files.go +++ /dev/null @@ -1,43 +0,0 @@ -package commands - -import ( - "fmt" - "strings" - - "github.com/jesseduffield/lazygit/pkg/commands/models" -) - -// GetFilesInDiff get the specified commit files -func (c *GitCommand) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) { - reverseFlag := "" - if reverse { - reverseFlag = " -R " - } - - filenames, err := c.Cmd.New(fmt.Sprintf("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)).RunWithOutput() - if err != nil { - return nil, err - } - - return c.getCommitFilesFromFilenames(filenames), nil -} - -// filenames string is something like "file1\nfile2\nfile3" -func (c *GitCommand) getCommitFilesFromFilenames(filenames string) []*models.CommitFile { - commitFiles := make([]*models.CommitFile, 0) - - lines := strings.Split(strings.TrimRight(filenames, "\x00"), "\x00") - n := len(lines) - for i := 0; i < n-1; i += 2 { - // typical result looks like 'A my_file' meaning my_file was added - changeStatus := lines[i] - name := lines[i+1] - - commitFiles = append(commitFiles, &models.CommitFile{ - Name: name, - ChangeStatus: changeStatus, - }) - } - - return commitFiles -} diff --git a/pkg/commands/loading_files.go b/pkg/commands/loading_files.go deleted file mode 100644 index e28613c07..000000000 --- a/pkg/commands/loading_files.go +++ /dev/null @@ -1,116 +0,0 @@ -package commands - -import ( - "fmt" - "strings" - - "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/utils" -) - -// GetStatusFiles git status files -type GetStatusFileOptions struct { - NoRenames bool -} - -func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File { - // check if config wants us ignoring untracked files - untrackedFilesSetting := c.GitConfig.Get("status.showUntrackedFiles") - - if untrackedFilesSetting == "" { - untrackedFilesSetting = "all" - } - untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting) - - statuses, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg}) - if err != nil { - c.Log.Error(err) - } - files := []*models.File{} - - for _, status := range statuses { - if strings.HasPrefix(status.StatusString, "warning") { - c.Log.Warningf("warning when calling git status: %s", status.StatusString) - continue - } - change := status.Change - stagedChange := change[0:1] - unstagedChange := change[1:2] - untracked := utils.IncludesString([]string{"??", "A ", "AM"}, change) - hasNoStagedChanges := utils.IncludesString([]string{" ", "U", "?"}, stagedChange) - hasMergeConflicts := utils.IncludesString([]string{"DD", "AA", "UU", "AU", "UA", "UD", "DU"}, change) - hasInlineMergeConflicts := utils.IncludesString([]string{"UU", "AA"}, change) - - file := &models.File{ - Name: status.Name, - PreviousName: status.PreviousName, - DisplayString: status.StatusString, - HasStagedChanges: !hasNoStagedChanges, - HasUnstagedChanges: unstagedChange != " ", - Tracked: !untracked, - Deleted: unstagedChange == "D" || stagedChange == "D", - Added: unstagedChange == "A" || untracked, - HasMergeConflicts: hasMergeConflicts, - HasInlineMergeConflicts: hasInlineMergeConflicts, - Type: c.OSCommand.FileType(status.Name), - ShortStatus: change, - } - files = append(files, file) - } - - return files -} - -// GitStatus returns the file status of the repo -type GitStatusOptions struct { - NoRenames bool - UntrackedFilesArg string -} - -type FileStatus struct { - StatusString string - Change string // ??, MM, AM, ... - Name string - PreviousName string -} - -func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) { - noRenamesFlag := "" - if opts.NoRenames { - noRenamesFlag = "--no-renames" - } - - statusLines, err := c.Cmd.New(fmt.Sprintf("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)).RunWithOutput() - if err != nil { - return []FileStatus{}, err - } - - splitLines := strings.Split(statusLines, "\x00") - response := []FileStatus{} - - for i := 0; i < len(splitLines); i++ { - original := splitLines[i] - - if len(original) < 3 { - continue - } - - status := FileStatus{ - StatusString: original, - Change: original[:2], - Name: original[3:], - PreviousName: "", - } - - if strings.HasPrefix(status.Change, "R") { - // if a line starts with 'R' then the next line is the original file. - status.PreviousName = strings.TrimSpace(splitLines[i+1]) - status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousName, status.Name) - i++ - } - - response = append(response, status) - } - - return response, nil -} diff --git a/pkg/commands/loading_files_test.go b/pkg/commands/loading_files_test.go deleted file mode 100644 index 456e4aca7..000000000 --- a/pkg/commands/loading_files_test.go +++ /dev/null @@ -1,227 +0,0 @@ -package commands - -import ( - "os/exec" - "testing" - - "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/secureexec" - "github.com/stretchr/testify/assert" -) - -// TestGitCommandGetStatusFiles is a function. -func TestGitCommandGetStatusFiles(t *testing.T) { - type scenario struct { - testName string - command func(string, ...string) *exec.Cmd - test func([]*models.File) - } - - scenarios := []scenario{ - { - "No files found", - func(cmd string, args ...string) *exec.Cmd { - return secureexec.Command("echo") - }, - func(files []*models.File) { - assert.Len(t, files, 0) - }, - }, - { - "Several files found", - func(cmd string, args ...string) *exec.Cmd { - return secureexec.Command( - "printf", - `MM file1.txt\0A file3.txt\0AM file2.txt\0?? file4.txt\0UU file5.txt`, - ) - }, - func(files []*models.File) { - assert.Len(t, files, 5) - - expected := []*models.File{ - { - Name: "file1.txt", - HasStagedChanges: true, - HasUnstagedChanges: true, - Tracked: true, - Added: false, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "MM file1.txt", - Type: "other", - ShortStatus: "MM", - }, - { - Name: "file3.txt", - HasStagedChanges: true, - HasUnstagedChanges: false, - Tracked: false, - Added: true, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "A file3.txt", - Type: "other", - ShortStatus: "A ", - }, - { - Name: "file2.txt", - HasStagedChanges: true, - HasUnstagedChanges: true, - Tracked: false, - Added: true, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "AM file2.txt", - Type: "other", - ShortStatus: "AM", - }, - { - Name: "file4.txt", - HasStagedChanges: false, - HasUnstagedChanges: true, - Tracked: false, - Added: true, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "?? file4.txt", - Type: "other", - ShortStatus: "??", - }, - { - Name: "file5.txt", - HasStagedChanges: false, - HasUnstagedChanges: true, - Tracked: true, - Added: false, - Deleted: false, - HasMergeConflicts: true, - HasInlineMergeConflicts: true, - DisplayString: "UU file5.txt", - Type: "other", - ShortStatus: "UU", - }, - } - - assert.EqualValues(t, expected, files) - }, - }, - { - "File with new line char", - func(cmd string, args ...string) *exec.Cmd { - return secureexec.Command( - "printf", - `MM a\nb.txt`, - ) - }, - func(files []*models.File) { - assert.Len(t, files, 1) - - expected := []*models.File{ - { - Name: "a\nb.txt", - HasStagedChanges: true, - HasUnstagedChanges: true, - Tracked: true, - Added: false, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "MM a\nb.txt", - Type: "other", - ShortStatus: "MM", - }, - } - - assert.EqualValues(t, expected, files) - }, - }, - { - "Renamed files", - func(cmd string, args ...string) *exec.Cmd { - return secureexec.Command( - "printf", - `R after1.txt\0before1.txt\0RM after2.txt\0before2.txt`, - ) - }, - func(files []*models.File) { - assert.Len(t, files, 2) - - expected := []*models.File{ - { - Name: "after1.txt", - PreviousName: "before1.txt", - HasStagedChanges: true, - HasUnstagedChanges: false, - Tracked: true, - Added: false, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "R before1.txt -> after1.txt", - Type: "other", - ShortStatus: "R ", - }, - { - Name: "after2.txt", - PreviousName: "before2.txt", - HasStagedChanges: true, - HasUnstagedChanges: true, - Tracked: true, - Added: false, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "RM before2.txt -> after2.txt", - Type: "other", - ShortStatus: "RM", - }, - } - - assert.EqualValues(t, expected, files) - }, - }, - { - "File with arrow in name", - func(cmd string, args ...string) *exec.Cmd { - return secureexec.Command( - "printf", - `?? a -> b.txt`, - ) - }, - func(files []*models.File) { - assert.Len(t, files, 1) - - expected := []*models.File{ - { - Name: "a -> b.txt", - HasStagedChanges: false, - HasUnstagedChanges: true, - Tracked: false, - Added: true, - Deleted: false, - HasMergeConflicts: false, - HasInlineMergeConflicts: false, - DisplayString: "?? a -> b.txt", - Type: "other", - ShortStatus: "??", - }, - } - - assert.EqualValues(t, expected, files) - }, - }, - } - - for _, s := range scenarios { - t.Run(s.testName, func(t *testing.T) { - gitCmd := NewDummyGitCommand() - gitCmd.OSCommand.Command = s.command - - s.test(gitCmd.GetStatusFiles(GetStatusFileOptions{})) - }) - } -} diff --git a/pkg/commands/oscommands/dummies.go b/pkg/commands/oscommands/dummies.go index 3bb8429bd..ba60374f4 100644 --- a/pkg/commands/oscommands/dummies.go +++ b/pkg/commands/oscommands/dummies.go @@ -10,7 +10,7 @@ func NewDummyOSCommand() *OSCommand { return NewOSCommand(utils.NewDummyCommon()) } -func NewCmdObjBuilderDummy(runner ICmdObjRunner) ICmdObjBuilder { +func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder { return &CmdObjBuilder{ runner: runner, logCmdObj: func(ICmdObj) {}, diff --git a/pkg/commands/oscommands/fake_cmd_obj_runner.go b/pkg/commands/oscommands/fake_cmd_obj_runner.go index 09431bb20..9a8a1e798 100644 --- a/pkg/commands/oscommands/fake_cmd_obj_runner.go +++ b/pkg/commands/oscommands/fake_cmd_obj_runner.go @@ -36,9 +36,11 @@ func (self *FakeCmdObjRunner) RunWithOutput(cmdObj ICmdObj) (string, error) { } expectedCmd := self.expectedCmds[self.expectedCmdIndex] + output, err := expectedCmd(cmdObj) + self.expectedCmdIndex++ - return expectedCmd(cmdObj) + return output, err } func (self *FakeCmdObjRunner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error { @@ -72,13 +74,29 @@ func (self *FakeCmdObjRunner) ExpectFunc(fn func(cmdObj ICmdObj) (string, error) func (self *FakeCmdObjRunner) Expect(expectedCmdStr string, output string, err error) *FakeCmdObjRunner { self.ExpectFunc(func(cmdObj ICmdObj) (string, error) { cmdStr := cmdObj.ToString() - if cmdStr != expectedCmdStr { - assert.Equal(self.t, expectedCmdStr, cmdStr, fmt.Sprintf("expected command %d to be %s, but was %s", self.expectedCmdIndex+1, expectedCmdStr, cmdStr)) - return "", errors.New("expected cmd") - } + assert.Equal(self.t, expectedCmdStr, cmdStr, fmt.Sprintf("expected command %d to be %s, but was %s", self.expectedCmdIndex+1, expectedCmdStr, cmdStr)) + + return output, err + }) + + return self +} + +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)) return output, err }) return self } + +func (self *FakeCmdObjRunner) CheckForMissingCalls() { + if self.expectedCmdIndex < len(self.expectedCmds) { + self.t.Errorf("expected command %d to be called, but was not", self.expectedCmdIndex+1) + } + + return +} diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go index d906e584a..9695472ae 100644 --- a/pkg/commands/oscommands/os.go +++ b/pkg/commands/oscommands/os.go @@ -137,7 +137,7 @@ func (c *OSCommand) SetRemoveFile(f func(string) error) { } // FileType tells us if the file is a file, directory or other -func (c *OSCommand) FileType(path string) string { +func FileType(path string) string { fileInfo, err := os.Stat(path) if err != nil { return "other" diff --git a/pkg/commands/oscommands/os_test.go b/pkg/commands/oscommands/os_test.go index 73c846fb0..04e940f03 100644 --- a/pkg/commands/oscommands/os_test.go +++ b/pkg/commands/oscommands/os_test.go @@ -164,7 +164,7 @@ func TestOSCommandFileType(t *testing.T) { for _, s := range scenarios { s.setup() - s.test(NewDummyOSCommand().FileType(s.path)) + s.test(FileType(s.path)) _ = os.RemoveAll(s.path) } } diff --git a/pkg/commands/stash_entries.go b/pkg/commands/stash_entries.go index 16b0806bc..498431103 100644 --- a/pkg/commands/stash_entries.go +++ b/pkg/commands/stash_entries.go @@ -1,6 +1,10 @@ package commands -import "fmt" +import ( + "fmt" + + "github.com/jesseduffield/lazygit/pkg/commands/loaders" +) // StashDo modify stash func (c *GitCommand) StashDo(index int, method string) error { @@ -45,7 +49,10 @@ func (c *GitCommand) StashSaveStagedChanges(message string) error { // if you had staged an untracked file, that will now appear as 'AD' in git status // meaning it's deleted in your working tree but added in your index. Given that it's // now safely stashed, we need to remove it. - files := c.GetStatusFiles(GetStatusFileOptions{}) + files := loaders. + NewFileLoader(c.Common, c.Cmd, c.GitConfig). + GetStatusFiles(loaders.GetStatusFileOptions{}) + for _, file := range files { if file.ShortStatus == "AD" { if err := c.UnStageFile(file.Names(), false); err != nil { -- cgit v1.2.3