mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	WIP
This commit is contained in:
		| @@ -127,9 +127,12 @@ type MergeOpts struct { | |||||||
|  |  | ||||||
| // Merge merge | // Merge merge | ||||||
| func (c *GitCommand) Merge(branchName string, opts MergeOpts) error { | 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 { | 	if opts.FastForwardOnly { | ||||||
| 		command = fmt.Sprintf("%s --ff-only", command) | 		command = fmt.Sprintf("%s --ff-only", command) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,120 +1,85 @@ | |||||||
| package commands | package commands | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"os/exec" |  | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/secureexec" | 	"github.com/go-errors/errors" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/test" | 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // TestGitCommandGetCommitDifferences is a function. |  | ||||||
| func TestGitCommandGetCommitDifferences(t *testing.T) { | func TestGitCommandGetCommitDifferences(t *testing.T) { | ||||||
| 	type scenario struct { | 	type scenario struct { | ||||||
| 		testName string | 		testName          string | ||||||
| 		command  func(string, ...string) *exec.Cmd | 		runner            *oscommands.FakeCmdObjRunner | ||||||
| 		test     func(string, string) | 		expectedPushables string | ||||||
|  | 		expectedPullables string | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	scenarios := []scenario{ | 	scenarios := []scenario{ | ||||||
| 		{ | 		{ | ||||||
| 			"Can't retrieve pushable count", | 			"Can't retrieve pushable count", | ||||||
| 			func(string, ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t). | ||||||
| 				return secureexec.Command("test") | 				Expect("git rev-list @{u}..HEAD --count", "", errors.New("error")), | ||||||
| 			}, | 			"?", "?", | ||||||
| 			func(pushableCount string, pullableCount string) { |  | ||||||
| 				assert.EqualValues(t, "?", pushableCount) |  | ||||||
| 				assert.EqualValues(t, "?", pullableCount) |  | ||||||
| 			}, |  | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"Can't retrieve pullable count", | 			"Can't retrieve pullable count", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t). | ||||||
| 				if args[1] == "HEAD..@{u}" { | 				Expect("git rev-list @{u}..HEAD --count", "1\n", nil). | ||||||
| 					return secureexec.Command("test") | 				Expect("git rev-list HEAD..@{u} --count", "", errors.New("error")), | ||||||
| 				} | 			"?", "?", | ||||||
|  |  | ||||||
| 				return secureexec.Command("echo") |  | ||||||
| 			}, |  | ||||||
| 			func(pushableCount string, pullableCount string) { |  | ||||||
| 				assert.EqualValues(t, "?", pushableCount) |  | ||||||
| 				assert.EqualValues(t, "?", pullableCount) |  | ||||||
| 			}, |  | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"Retrieve pullable and pushable count", | 			"Retrieve pullable and pushable count", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t). | ||||||
| 				if args[1] == "HEAD..@{u}" { | 				Expect("git rev-list @{u}..HEAD --count", "1\n", nil). | ||||||
| 					return secureexec.Command("echo", "10") | 				Expect("git rev-list HEAD..@{u} --count", "2\n", nil), | ||||||
| 				} | 			"1", "2", | ||||||
|  |  | ||||||
| 				return secureexec.Command("echo", "11") |  | ||||||
| 			}, |  | ||||||
| 			func(pushableCount string, pullableCount string) { |  | ||||||
| 				assert.EqualValues(t, "11", pushableCount) |  | ||||||
| 				assert.EqualValues(t, "10", pullableCount) |  | ||||||
| 			}, |  | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, s := range scenarios { | 	for _, s := range scenarios { | ||||||
| 		t.Run(s.testName, func(t *testing.T) { | 		t.Run(s.testName, func(t *testing.T) { | ||||||
| 			gitCmd := NewDummyGitCommand() | 			gitCmd := NewDummyGitCommandWithRunner(s.runner) | ||||||
| 			gitCmd.OSCommand.Command = s.command | 			pushables, pullables := gitCmd.GetCommitDifferences("HEAD", "@{u}") | ||||||
| 			s.test(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) { | func TestGitCommandNewBranch(t *testing.T) { | ||||||
| 	gitCmd := NewDummyGitCommand() | 	runner := oscommands.NewFakeRunner(t). | ||||||
| 	gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { | 		Expect(`git checkout -b "test" "master"`, "", nil) | ||||||
| 		assert.EqualValues(t, "git", cmd) | 	gitCmd := NewDummyGitCommandWithRunner(runner) | ||||||
| 		assert.EqualValues(t, []string{"checkout", "-b", "test", "master"}, args) |  | ||||||
|  |  | ||||||
| 		return secureexec.Command("echo") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, gitCmd.NewBranch("test", "master")) | 	assert.NoError(t, gitCmd.NewBranch("test", "master")) | ||||||
|  | 	runner.CheckForMissingCalls() | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestGitCommandDeleteBranch is a function. |  | ||||||
| func TestGitCommandDeleteBranch(t *testing.T) { | func TestGitCommandDeleteBranch(t *testing.T) { | ||||||
| 	type scenario struct { | 	type scenario struct { | ||||||
| 		testName string | 		testName string | ||||||
| 		branch   string |  | ||||||
| 		force    bool | 		force    bool | ||||||
| 		command  func(string, ...string) *exec.Cmd | 		runner   *oscommands.FakeCmdObjRunner | ||||||
| 		test     func(error) | 		test     func(error) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	scenarios := []scenario{ | 	scenarios := []scenario{ | ||||||
| 		{ | 		{ | ||||||
| 			"Delete a branch", | 			"Delete a branch", | ||||||
| 			"test", |  | ||||||
| 			false, | 			false, | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t).Expect(`git branch -d "test"`, "", nil), | ||||||
| 				assert.EqualValues(t, "git", cmd) |  | ||||||
| 				assert.EqualValues(t, []string{"branch", "-d", "test"}, args) |  | ||||||
|  |  | ||||||
| 				return secureexec.Command("echo") |  | ||||||
| 			}, |  | ||||||
| 			func(err error) { | 			func(err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"Force delete a branch", | 			"Force delete a branch", | ||||||
| 			"test", |  | ||||||
| 			true, | 			true, | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t).Expect(`git branch -D "test"`, "", nil), | ||||||
| 				assert.EqualValues(t, "git", cmd) |  | ||||||
| 				assert.EqualValues(t, []string{"branch", "-D", "test"}, args) |  | ||||||
|  |  | ||||||
| 				return secureexec.Command("echo") |  | ||||||
| 			}, |  | ||||||
| 			func(err error) { | 			func(err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 			}, | 			}, | ||||||
| @@ -123,31 +88,27 @@ func TestGitCommandDeleteBranch(t *testing.T) { | |||||||
|  |  | ||||||
| 	for _, s := range scenarios { | 	for _, s := range scenarios { | ||||||
| 		t.Run(s.testName, func(t *testing.T) { | 		t.Run(s.testName, func(t *testing.T) { | ||||||
| 			gitCmd := NewDummyGitCommand() | 			gitCmd := NewDummyGitCommandWithRunner(s.runner) | ||||||
| 			gitCmd.OSCommand.Command = s.command |  | ||||||
| 			s.test(gitCmd.DeleteBranch(s.branch, s.force)) | 			s.test(gitCmd.DeleteBranch("test", s.force)) | ||||||
|  | 			s.runner.CheckForMissingCalls() | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestGitCommandMerge is a function. |  | ||||||
| func TestGitCommandMerge(t *testing.T) { | func TestGitCommandMerge(t *testing.T) { | ||||||
| 	gitCmd := NewDummyGitCommand() | 	runner := oscommands.NewFakeRunner(t). | ||||||
| 	gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { | 		Expect(`git merge --no-edit "test"`, "", nil) | ||||||
| 		assert.EqualValues(t, "git", cmd) | 	gitCmd := NewDummyGitCommandWithRunner(runner) | ||||||
| 		assert.EqualValues(t, []string{"merge", "--no-edit", "test"}, args) |  | ||||||
|  |  | ||||||
| 		return secureexec.Command("echo") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, gitCmd.Merge("test", MergeOpts{})) | 	assert.NoError(t, gitCmd.Merge("test", MergeOpts{})) | ||||||
|  | 	runner.CheckForMissingCalls() | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestGitCommandCheckout is a function. |  | ||||||
| func TestGitCommandCheckout(t *testing.T) { | func TestGitCommandCheckout(t *testing.T) { | ||||||
| 	type scenario struct { | 	type scenario struct { | ||||||
| 		testName string | 		testName string | ||||||
| 		command  func(string, ...string) *exec.Cmd | 		runner   *oscommands.FakeCmdObjRunner | ||||||
| 		test     func(error) | 		test     func(error) | ||||||
| 		force    bool | 		force    bool | ||||||
| 	} | 	} | ||||||
| @@ -155,12 +116,7 @@ func TestGitCommandCheckout(t *testing.T) { | |||||||
| 	scenarios := []scenario{ | 	scenarios := []scenario{ | ||||||
| 		{ | 		{ | ||||||
| 			"Checkout", | 			"Checkout", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t).Expect(`git checkout "test"`, "", nil), | ||||||
| 				assert.EqualValues(t, "git", cmd) |  | ||||||
| 				assert.EqualValues(t, []string{"checkout", "test"}, args) |  | ||||||
|  |  | ||||||
| 				return secureexec.Command("echo") |  | ||||||
| 			}, |  | ||||||
| 			func(err error) { | 			func(err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 			}, | 			}, | ||||||
| @@ -168,12 +124,7 @@ func TestGitCommandCheckout(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"Checkout forced", | 			"Checkout forced", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t).Expect(`git checkout --force "test"`, "", nil), | ||||||
| 				assert.EqualValues(t, "git", cmd) |  | ||||||
| 				assert.EqualValues(t, []string{"checkout", "--force", "test"}, args) |  | ||||||
|  |  | ||||||
| 				return secureexec.Command("echo") |  | ||||||
| 			}, |  | ||||||
| 			func(err error) { | 			func(err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 			}, | 			}, | ||||||
| @@ -183,52 +134,43 @@ func TestGitCommandCheckout(t *testing.T) { | |||||||
|  |  | ||||||
| 	for _, s := range scenarios { | 	for _, s := range scenarios { | ||||||
| 		t.Run(s.testName, func(t *testing.T) { | 		t.Run(s.testName, func(t *testing.T) { | ||||||
| 			gitCmd := NewDummyGitCommand() | 			gitCmd := NewDummyGitCommandWithRunner(s.runner) | ||||||
| 			gitCmd.OSCommand.Command = s.command |  | ||||||
| 			s.test(gitCmd.Checkout("test", CheckoutOptions{Force: s.force})) | 			s.test(gitCmd.Checkout("test", CheckoutOptions{Force: s.force})) | ||||||
|  | 			s.runner.CheckForMissingCalls() | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestGitCommandGetBranchGraph is a function. |  | ||||||
| func TestGitCommandGetBranchGraph(t *testing.T) { | func TestGitCommandGetBranchGraph(t *testing.T) { | ||||||
| 	gitCmd := NewDummyGitCommand() | 	runner := oscommands.NewFakeRunner(t).ExpectArgs([]string{ | ||||||
| 	gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { | 		"git", "log", "--graph", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test", "--", | ||||||
| 		assert.EqualValues(t, "git", cmd) | 	}, "", nil) | ||||||
| 		assert.EqualValues(t, []string{"log", "--graph", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test", "--"}, args) | 	gitCmd := NewDummyGitCommandWithRunner(runner) | ||||||
| 		return secureexec.Command("echo") |  | ||||||
| 	} |  | ||||||
| 	_, err := gitCmd.GetBranchGraph("test") | 	_, err := gitCmd.GetBranchGraph("test") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestGitCommandGetAllBranchGraph(t *testing.T) { | func TestGitCommandGetAllBranchGraph(t *testing.T) { | ||||||
| 	gitCmd := NewDummyGitCommand() | 	runner := oscommands.NewFakeRunner(t).ExpectArgs([]string{ | ||||||
| 	gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd { | 		"git", "log", "--graph", "--all", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", | ||||||
| 		assert.EqualValues(t, "git", cmd) | 	}, "", nil) | ||||||
| 		assert.EqualValues(t, []string{"log", "--graph", "--all", "--color=always", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium"}, args) | 	gitCmd := NewDummyGitCommandWithRunner(runner) | ||||||
| 		return secureexec.Command("echo") |  | ||||||
| 	} |  | ||||||
| 	cmdStr := gitCmd.UserConfig.Git.AllBranchesLogCmd | 	cmdStr := gitCmd.UserConfig.Git.AllBranchesLogCmd | ||||||
| 	_, err := gitCmd.Cmd.New(cmdStr).RunWithOutput() | 	_, err := gitCmd.Cmd.New(cmdStr).RunWithOutput() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestGitCommandCurrentBranchName is a function. |  | ||||||
| func TestGitCommandCurrentBranchName(t *testing.T) { | func TestGitCommandCurrentBranchName(t *testing.T) { | ||||||
| 	type scenario struct { | 	type scenario struct { | ||||||
| 		testName string | 		testName string | ||||||
| 		command  func(string, ...string) *exec.Cmd | 		runner   *oscommands.FakeCmdObjRunner | ||||||
| 		test     func(string, string, error) | 		test     func(string, string, error) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	scenarios := []scenario{ | 	scenarios := []scenario{ | ||||||
| 		{ | 		{ | ||||||
| 			"says we are on the master branch if we are", | 			"says we are on the master branch if we are", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t).Expect(`git symbolic-ref --short HEAD`, "master", nil), | ||||||
| 				assert.Equal(t, "git", cmd) |  | ||||||
| 				return secureexec.Command("echo", "master") |  | ||||||
| 			}, |  | ||||||
| 			func(name string, displayname string, err error) { | 			func(name string, displayname string, err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 				assert.EqualValues(t, "master", name) | 				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", | 			"falls back to git `git branch --contains` if symbolic-ref fails", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t). | ||||||
| 				assert.EqualValues(t, "git", cmd) | 				Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")). | ||||||
|  | 				Expect(`git branch --contains`, "* master", nil), | ||||||
| 				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) { | 			func(name string, displayname string, err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 				assert.EqualValues(t, "master", name) | 				assert.EqualValues(t, "master", name) | ||||||
| @@ -259,20 +190,9 @@ func TestGitCommandCurrentBranchName(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"handles a detached head", | 			"handles a detached head", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t). | ||||||
| 				assert.EqualValues(t, "git", cmd) | 				Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")). | ||||||
|  | 				Expect(`git branch --contains`, "* (HEAD detached at 123abcd)", nil), | ||||||
| 				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) { | 			func(name string, displayname string, err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 				assert.EqualValues(t, "123abcd", name) | 				assert.EqualValues(t, "123abcd", name) | ||||||
| @@ -281,10 +201,9 @@ func TestGitCommandCurrentBranchName(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"bubbles up error if there is one", | 			"bubbles up error if there is one", | ||||||
| 			func(cmd string, args ...string) *exec.Cmd { | 			oscommands.NewFakeRunner(t). | ||||||
| 				assert.Equal(t, "git", cmd) | 				Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")). | ||||||
| 				return secureexec.Command("test") | 				Expect(`git branch --contains`, "", errors.New("error")), | ||||||
| 			}, |  | ||||||
| 			func(name string, displayname string, err error) { | 			func(name string, displayname string, err error) { | ||||||
| 				assert.Error(t, err) | 				assert.Error(t, err) | ||||||
| 				assert.EqualValues(t, "", name) | 				assert.EqualValues(t, "", name) | ||||||
| @@ -295,19 +214,18 @@ func TestGitCommandCurrentBranchName(t *testing.T) { | |||||||
|  |  | ||||||
| 	for _, s := range scenarios { | 	for _, s := range scenarios { | ||||||
| 		t.Run(s.testName, func(t *testing.T) { | 		t.Run(s.testName, func(t *testing.T) { | ||||||
| 			gitCmd := NewDummyGitCommand() | 			gitCmd := NewDummyGitCommandWithRunner(s.runner) | ||||||
| 			gitCmd.OSCommand.Command = s.command |  | ||||||
| 			s.test(gitCmd.CurrentBranchName()) | 			s.test(gitCmd.CurrentBranchName()) | ||||||
|  | 			s.runner.CheckForMissingCalls() | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // TestGitCommandResetHard is a function. |  | ||||||
| func TestGitCommandResetHard(t *testing.T) { | func TestGitCommandResetHard(t *testing.T) { | ||||||
| 	type scenario struct { | 	type scenario struct { | ||||||
| 		testName string | 		testName string | ||||||
| 		ref      string | 		ref      string | ||||||
| 		command  func(string, ...string) *exec.Cmd | 		runner   *oscommands.FakeCmdObjRunner | ||||||
| 		test     func(error) | 		test     func(error) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -315,23 +233,17 @@ func TestGitCommandResetHard(t *testing.T) { | |||||||
| 		{ | 		{ | ||||||
| 			"valid case", | 			"valid case", | ||||||
| 			"HEAD", | 			"HEAD", | ||||||
| 			test.CreateMockCommand(t, []*test.CommandSwapper{ | 			oscommands.NewFakeRunner(t). | ||||||
| 				{ | 				Expect(`git reset --hard "HEAD"`, "", nil), | ||||||
| 					Expect:  `git reset --hard HEAD`, |  | ||||||
| 					Replace: "echo", |  | ||||||
| 				}, |  | ||||||
| 			}), |  | ||||||
| 			func(err error) { | 			func(err error) { | ||||||
| 				assert.NoError(t, err) | 				assert.NoError(t, err) | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	gitCmd := NewDummyGitCommand() |  | ||||||
|  |  | ||||||
| 	for _, s := range scenarios { | 	for _, s := range scenarios { | ||||||
| 		t.Run(s.testName, func(t *testing.T) { | 		t.Run(s.testName, func(t *testing.T) { | ||||||
| 			gitCmd.OSCommand.Command = s.command | 			gitCmd := NewDummyGitCommandWithRunner(s.runner) | ||||||
| 			s.test(gitCmd.ResetHard(s.ref)) | 			s.test(gitCmd.ResetHard(s.ref)) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -23,3 +23,12 @@ func NewDummyGitCommandWithOSCommand(osCommand *oscommands.OSCommand) *GitComman | |||||||
| 		GetCmdWriter: func() io.Writer { return ioutil.Discard }, | 		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 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/go-errors/errors" | 	"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/models" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/filetree" | 	"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 | 	// all files, passing the --no-renames flag and then recursively call the function | ||||||
| 	// again for the before file and after file. | 	// 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 beforeFile *models.File | ||||||
| 	var afterFile *models.File | 	var afterFile *models.File | ||||||
| 	for _, f := range filesWithoutRenames { | 	for _, f := range filesWithoutRenames { | ||||||
|   | |||||||
| @@ -223,3 +223,11 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam | |||||||
| func VerifyInGitRepo(osCommand *oscommands.OSCommand) error { | func VerifyInGitRepo(osCommand *oscommands.OSCommand) error { | ||||||
| 	return osCommand.Cmd.New("git rev-parse --git-dir").Run() | 	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 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import ( | |||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" |  | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/common" | 	"github.com/jesseduffield/lazygit/pkg/common" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||||
| @@ -29,9 +28,14 @@ type BranchLoader struct { | |||||||
| 	reflogCommits        []*models.Commit | 	reflogCommits        []*models.Commit | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type BranchLoaderGitCommand interface { | ||||||
|  | 	GetRawBranches() (string, error) | ||||||
|  | 	CurrentBranchName() (string, string, error) | ||||||
|  | } | ||||||
|  |  | ||||||
| func NewBranchLoader( | func NewBranchLoader( | ||||||
| 	cmn *common.Common, | 	cmn *common.Common, | ||||||
| 	gitCommand *commands.GitCommand, | 	gitCommand BranchLoaderGitCommand, | ||||||
| 	reflogCommits []*models.Commit, | 	reflogCommits []*models.Commit, | ||||||
| ) *BranchLoader { | ) *BranchLoader { | ||||||
| 	return &BranchLoader{ | 	return &BranchLoader{ | ||||||
| @@ -43,10 +47,10 @@ func NewBranchLoader( | |||||||
| } | } | ||||||
|  |  | ||||||
| // Load the list of branches for the current repo | // Load the list of branches for the current repo | ||||||
| func (b *BranchLoader) Load() []*models.Branch { | func (self *BranchLoader) Load() []*models.Branch { | ||||||
| 	branches := b.obtainBranches() | 	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 | 	// 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) | 	branchesWithRecency := make([]*models.Branch, 0) | ||||||
| @@ -78,7 +82,7 @@ outer: | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !foundHead { | 	if !foundHead { | ||||||
| 		currentBranchName, currentBranchDisplayName, err := b.getCurrentBranchName() | 		currentBranchName, currentBranchDisplayName, err := self.getCurrentBranchName() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			panic(err) | 			panic(err) | ||||||
| 		} | 		} | ||||||
| @@ -87,8 +91,8 @@ outer: | |||||||
| 	return branches | 	return branches | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *BranchLoader) obtainBranches() []*models.Branch { | func (self *BranchLoader) obtainBranches() []*models.Branch { | ||||||
| 	output, err := b.getRawBranches() | 	output, err := self.getRawBranches() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		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 | // TODO: only look at the new reflog commits, and otherwise store the recencies in | ||||||
| // int form against the branch to recalculate the time ago | // 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{} | 	foundBranchesMap := map[string]bool{} | ||||||
| 	re := regexp.MustCompile(`checkout: moving from ([\S]+) to ([\S]+)`) | 	re := regexp.MustCompile(`checkout: moving from ([\S]+) to ([\S]+)`) | ||||||
| 	reflogBranches := make([]*models.Branch, 0, len(b.reflogCommits)) | 	reflogBranches := make([]*models.Branch, 0, len(self.reflogCommits)) | ||||||
| 	for _, commit := range b.reflogCommits { | 	for _, commit := range self.reflogCommits { | ||||||
| 		if match := re.FindStringSubmatch(commit.Name); len(match) == 3 { | 		if match := re.FindStringSubmatch(commit.Name); len(match) == 3 { | ||||||
| 			recency := utils.UnixToTimeAgo(commit.UnixTimestamp) | 			recency := utils.UnixToTimeAgo(commit.UnixTimestamp) | ||||||
| 			for _, branchName := range match[1:] { | 			for _, branchName := range match[1:] { | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								pkg/commands/loaders/commit_files.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								pkg/commands/loaders/commit_files.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
| @@ -9,7 +9,6 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" |  | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/types/enums" | 	"github.com/jesseduffield/lazygit/pkg/commands/types/enums" | ||||||
| @@ -37,20 +36,26 @@ type CommitLoader struct { | |||||||
| 	dotGitDir            string | 	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 | // making our dependencies explicit for the sake of easier testing | ||||||
| func NewCommitLoader( | func NewCommitLoader( | ||||||
| 	cmn *common.Common, | 	cmn *common.Common, | ||||||
| 	gitCommand *commands.GitCommand, | 	gitCommand CommitLoaderGitCommand, | ||||||
| 	osCommand *oscommands.OSCommand, |  | ||||||
| ) *CommitLoader { | ) *CommitLoader { | ||||||
| 	return &CommitLoader{ | 	return &CommitLoader{ | ||||||
| 		Common:               cmn, | 		Common:               cmn, | ||||||
| 		cmd:                  gitCommand.Cmd, | 		cmd:                  gitCommand.GetCmd(), | ||||||
| 		getCurrentBranchName: gitCommand.CurrentBranchName, | 		getCurrentBranchName: gitCommand.CurrentBranchName, | ||||||
| 		getRebaseMode:        gitCommand.RebaseMode, | 		getRebaseMode:        gitCommand.RebaseMode, | ||||||
| 		readFile:             ioutil.ReadFile, | 		readFile:             ioutil.ReadFile, | ||||||
| 		walkFiles:            filepath.Walk, | 		walkFiles:            filepath.Walk, | ||||||
| 		dotGitDir:            gitCommand.DotGitDir, | 		dotGitDir:            gitCommand.GetDotGitDir(), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -189,7 +189,7 @@ func TestGetCommits(t *testing.T) { | |||||||
| 		t.Run(scenario.testName, func(t *testing.T) { | 		t.Run(scenario.testName, func(t *testing.T) { | ||||||
| 			builder := &CommitLoader{ | 			builder := &CommitLoader{ | ||||||
| 				Common: utils.NewDummyCommon(), | 				Common: utils.NewDummyCommon(), | ||||||
| 				cmd:    oscommands.NewCmdObjBuilderDummy(scenario.runner), | 				cmd:    oscommands.NewDummyCmdObjBuilder(scenario.runner), | ||||||
| 				getCurrentBranchName: func() (string, string, error) { | 				getCurrentBranchName: func() (string, string, error) { | ||||||
| 					return scenario.currentBranchName, scenario.currentBranchName, nil | 					return scenario.currentBranchName, scenario.currentBranchName, nil | ||||||
| 				}, | 				}, | ||||||
|   | |||||||
| @@ -1,36 +1,54 @@ | |||||||
| package commands | package loaders | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/jesseduffield/lazygit/pkg/commands/git_config" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | 	"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" | 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // GetStatusFiles git status files | 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 { | type GetStatusFileOptions struct { | ||||||
| 	NoRenames bool | 	NoRenames bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File { | func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File { | ||||||
| 	// check if config wants us ignoring untracked files | 	// check if config wants us ignoring untracked files | ||||||
| 	untrackedFilesSetting := c.GitConfig.Get("status.showUntrackedFiles") | 	untrackedFilesSetting := self.gitConfig.Get("status.showUntrackedFiles") | ||||||
| 
 | 
 | ||||||
| 	if untrackedFilesSetting == "" { | 	if untrackedFilesSetting == "" { | ||||||
| 		untrackedFilesSetting = "all" | 		untrackedFilesSetting = "all" | ||||||
| 	} | 	} | ||||||
| 	untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting) | 	untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting) | ||||||
| 
 | 
 | ||||||
| 	statuses, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg}) | 	statuses, err := self.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.Log.Error(err) | 		self.Log.Error(err) | ||||||
| 	} | 	} | ||||||
| 	files := []*models.File{} | 	files := []*models.File{} | ||||||
| 
 | 
 | ||||||
| 	for _, status := range statuses { | 	for _, status := range statuses { | ||||||
| 		if strings.HasPrefix(status.StatusString, "warning") { | 		if strings.HasPrefix(status.StatusString, "warning") { | ||||||
| 			c.Log.Warningf("warning when calling git status: %s", status.StatusString) | 			self.Log.Warningf("warning when calling git status: %s", status.StatusString) | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		change := status.Change | 		change := status.Change | ||||||
| @@ -52,7 +70,7 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File { | |||||||
| 			Added:                   unstagedChange == "A" || untracked, | 			Added:                   unstagedChange == "A" || untracked, | ||||||
| 			HasMergeConflicts:       hasMergeConflicts, | 			HasMergeConflicts:       hasMergeConflicts, | ||||||
| 			HasInlineMergeConflicts: hasInlineMergeConflicts, | 			HasInlineMergeConflicts: hasInlineMergeConflicts, | ||||||
| 			Type:                    c.OSCommand.FileType(status.Name), | 			Type:                    self.getFileType(status.Name), | ||||||
| 			ShortStatus:             change, | 			ShortStatus:             change, | ||||||
| 		} | 		} | ||||||
| 		files = append(files, file) | 		files = append(files, file) | ||||||
| @@ -74,13 +92,13 @@ type FileStatus struct { | |||||||
| 	PreviousName string | 	PreviousName string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) { | func (c *FileLoader) GitStatus(opts GitStatusOptions) ([]FileStatus, error) { | ||||||
| 	noRenamesFlag := "" | 	noRenamesFlag := "" | ||||||
| 	if opts.NoRenames { | 	if opts.NoRenames { | ||||||
| 		noRenamesFlag = "--no-renames" | 		noRenamesFlag = " --no-renames" | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	statusLines, err := c.Cmd.New(fmt.Sprintf("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)).RunWithOutput() | 	statusLines, err := c.cmd.New(fmt.Sprintf("git status %s --porcelain -z%s", opts.UntrackedFilesArg, noRenamesFlag)).RunWithOutput() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return []FileStatus{}, err | 		return []FileStatus{}, err | ||||||
| 	} | 	} | ||||||
							
								
								
									
										203
									
								
								pkg/commands/loaders/files_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								pkg/commands/loaders/files_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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{})) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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{})) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -10,7 +10,7 @@ func NewDummyOSCommand() *OSCommand { | |||||||
| 	return NewOSCommand(utils.NewDummyCommon()) | 	return NewOSCommand(utils.NewDummyCommon()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewCmdObjBuilderDummy(runner ICmdObjRunner) ICmdObjBuilder { | func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder { | ||||||
| 	return &CmdObjBuilder{ | 	return &CmdObjBuilder{ | ||||||
| 		runner:    runner, | 		runner:    runner, | ||||||
| 		logCmdObj: func(ICmdObj) {}, | 		logCmdObj: func(ICmdObj) {}, | ||||||
|   | |||||||
| @@ -36,9 +36,11 @@ func (self *FakeCmdObjRunner) RunWithOutput(cmdObj ICmdObj) (string, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	expectedCmd := self.expectedCmds[self.expectedCmdIndex] | 	expectedCmd := self.expectedCmds[self.expectedCmdIndex] | ||||||
|  | 	output, err := expectedCmd(cmdObj) | ||||||
|  |  | ||||||
| 	self.expectedCmdIndex++ | 	self.expectedCmdIndex++ | ||||||
|  |  | ||||||
| 	return expectedCmd(cmdObj) | 	return output, err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (self *FakeCmdObjRunner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error { | 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 { | func (self *FakeCmdObjRunner) Expect(expectedCmdStr string, output string, err error) *FakeCmdObjRunner { | ||||||
| 	self.ExpectFunc(func(cmdObj ICmdObj) (string, error) { | 	self.ExpectFunc(func(cmdObj ICmdObj) (string, error) { | ||||||
| 		cmdStr := cmdObj.ToString() | 		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)) | ||||||
| 			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") |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return output, err | 		return output, err | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	return self | 	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 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -137,7 +137,7 @@ func (c *OSCommand) SetRemoveFile(f func(string) error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // FileType tells us if the file is a file, directory or other | // 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) | 	fileInfo, err := os.Stat(path) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "other" | 		return "other" | ||||||
|   | |||||||
| @@ -164,7 +164,7 @@ func TestOSCommandFileType(t *testing.T) { | |||||||
|  |  | ||||||
| 	for _, s := range scenarios { | 	for _, s := range scenarios { | ||||||
| 		s.setup() | 		s.setup() | ||||||
| 		s.test(NewDummyOSCommand().FileType(s.path)) | 		s.test(FileType(s.path)) | ||||||
| 		_ = os.RemoveAll(s.path) | 		_ = os.RemoveAll(s.path) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,10 @@ | |||||||
| package commands | package commands | ||||||
|  |  | ||||||
| import "fmt" | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/jesseduffield/lazygit/pkg/commands/loaders" | ||||||
|  | ) | ||||||
|  |  | ||||||
| // StashDo modify stash | // StashDo modify stash | ||||||
| func (c *GitCommand) StashDo(index int, method string) error { | 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 | 	// 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 | 	// 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. | 	// 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 { | 	for _, file := range files { | ||||||
| 		if file.ShortStatus == "AD" { | 		if file.ShortStatus == "AD" { | ||||||
| 			if err := c.UnStageFile(file.Names(), false); err != nil { | 			if err := c.UnStageFile(file.Names(), false); err != nil { | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package gui | package gui | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"github.com/jesseduffield/lazygit/pkg/commands/loaders" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/patch" | 	"github.com/jesseduffield/lazygit/pkg/commands/patch" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/filetree" | 	"github.com/jesseduffield/lazygit/pkg/gui/filetree" | ||||||
| @@ -105,7 +106,7 @@ func (gui *Gui) refreshCommitFilesView() error { | |||||||
| 	to := gui.State.Panels.CommitFiles.refName | 	to := gui.State.Panels.CommitFiles.refName | ||||||
| 	from, reverse := gui.getFromAndReverseArgsForDiff(to) | 	from, reverse := gui.getFromAndReverseArgsForDiff(to) | ||||||
|  |  | ||||||
| 	files, err := gui.GitCommand.GetFilesInDiff(from, to, reverse) | 	files, err := loaders.NewCommitFileLoader(gui.Common, gui.GitCommand.Cmd).GetFilesInDiff(from, to, reverse) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return gui.surfaceError(err) | 		return gui.surfaceError(err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -119,7 +119,7 @@ func (gui *Gui) refreshCommitsWithLimit() error { | |||||||
| 	gui.Mutexes.BranchCommitsMutex.Lock() | 	gui.Mutexes.BranchCommitsMutex.Lock() | ||||||
| 	defer gui.Mutexes.BranchCommitsMutex.Unlock() | 	defer gui.Mutexes.BranchCommitsMutex.Unlock() | ||||||
|  |  | ||||||
| 	loader := loaders.NewCommitLoader(gui.Common, gui.GitCommand, gui.OSCommand) | 	loader := loaders.NewCommitLoader(gui.Common, gui.GitCommand) | ||||||
|  |  | ||||||
| 	commits, err := loader.GetCommits( | 	commits, err := loader.GetCommits( | ||||||
| 		loaders.GetCommitsOptions{ | 		loaders.GetCommitsOptions{ | ||||||
| @@ -142,7 +142,7 @@ func (gui *Gui) refreshRebaseCommits() error { | |||||||
| 	gui.Mutexes.BranchCommitsMutex.Lock() | 	gui.Mutexes.BranchCommitsMutex.Lock() | ||||||
| 	defer gui.Mutexes.BranchCommitsMutex.Unlock() | 	defer gui.Mutexes.BranchCommitsMutex.Unlock() | ||||||
|  |  | ||||||
| 	loader := loaders.NewCommitLoader(gui.Common, gui.GitCommand, gui.OSCommand) | 	loader := loaders.NewCommitLoader(gui.Common, gui.GitCommand) | ||||||
|  |  | ||||||
| 	updatedCommits, err := loader.MergeRebasingCommits(gui.State.Commits) | 	updatedCommits, err := loader.MergeRebasingCommits(gui.State.Commits) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/jesseduffield/gocui" | 	"github.com/jesseduffield/gocui" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" | 	"github.com/jesseduffield/lazygit/pkg/commands" | ||||||
|  | 	"github.com/jesseduffield/lazygit/pkg/commands/loaders" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | 	"github.com/jesseduffield/lazygit/pkg/config" | ||||||
| @@ -553,7 +554,9 @@ func (gui *Gui) refreshStateFiles() error { | |||||||
| 	prevNodes := gui.State.FileManager.GetAllItems() | 	prevNodes := gui.State.FileManager.GetAllItems() | ||||||
| 	prevSelectedLineIdx := gui.State.Panels.Files.SelectedLineIdx | 	prevSelectedLineIdx := gui.State.Panels.Files.SelectedLineIdx | ||||||
|  |  | ||||||
| 	files := gui.GitCommand.GetStatusFiles(commands.GetStatusFileOptions{}) | 	files := loaders. | ||||||
|  | 		NewFileLoader(gui.Common, gui.GitCommand.Cmd, gui.GitCommand.GitConfig). | ||||||
|  | 		GetStatusFiles(loaders.GetStatusFileOptions{}) | ||||||
|  |  | ||||||
| 	// for when you stage the old file of a rename and the new file is in a collapsed dir | 	// for when you stage the old file of a rename and the new file is in a collapsed dir | ||||||
| 	state.FileManager.RWMutex.Lock() | 	state.FileManager.RWMutex.Lock() | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ func (gui *Gui) handleViewSubCommitFiles() error { | |||||||
|  |  | ||||||
| func (gui *Gui) switchToSubCommitsContext(refName string) error { | func (gui *Gui) switchToSubCommitsContext(refName string) error { | ||||||
| 	// need to populate my sub commits | 	// need to populate my sub commits | ||||||
| 	loader := loaders.NewCommitLoader(gui.Common, gui.GitCommand, gui.OSCommand) | 	loader := loaders.NewCommitLoader(gui.Common, gui.GitCommand) | ||||||
|  |  | ||||||
| 	commits, err := loader.GetCommits( | 	commits, err := loader.GetCommits( | ||||||
| 		loaders.GetCommitsOptions{ | 		loaders.GetCommitsOptions{ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user