mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-19 00:28:03 +02:00
WIP
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:] {
|
||||
|
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"
|
||||
"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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -1,36 +1,54 @@
|
||||
package commands
|
||||
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"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
untrackedFilesSetting := c.GitConfig.Get("status.showUntrackedFiles")
|
||||
untrackedFilesSetting := self.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})
|
||||
statuses, err := self.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
||||
if err != nil {
|
||||
c.Log.Error(err)
|
||||
self.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)
|
||||
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
||||
continue
|
||||
}
|
||||
change := status.Change
|
||||
@ -52,7 +70,7 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
|
||||
Added: unstagedChange == "A" || untracked,
|
||||
HasMergeConflicts: hasMergeConflicts,
|
||||
HasInlineMergeConflicts: hasInlineMergeConflicts,
|
||||
Type: c.OSCommand.FileType(status.Name),
|
||||
Type: self.getFileType(status.Name),
|
||||
ShortStatus: change,
|
||||
}
|
||||
files = append(files, file)
|
||||
@ -74,13 +92,13 @@ type FileStatus struct {
|
||||
PreviousName string
|
||||
}
|
||||
|
||||
func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
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()
|
||||
statusLines, err := c.cmd.New(fmt.Sprintf("git status %s --porcelain -z%s", opts.UntrackedFilesArg, noRenamesFlag)).RunWithOutput()
|
||||
if err != nil {
|
||||
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())
|
||||
}
|
||||
|
||||
func NewCmdObjBuilderDummy(runner ICmdObjRunner) ICmdObjBuilder {
|
||||
func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder {
|
||||
return &CmdObjBuilder{
|
||||
runner: runner,
|
||||
logCmdObj: func(ICmdObj) {},
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
@ -105,7 +106,7 @@ func (gui *Gui) refreshCommitFilesView() error {
|
||||
to := gui.State.Panels.CommitFiles.refName
|
||||
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 {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func (gui *Gui) refreshCommitsWithLimit() error {
|
||||
gui.Mutexes.BranchCommitsMutex.Lock()
|
||||
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(
|
||||
loaders.GetCommitsOptions{
|
||||
@ -142,7 +142,7 @@ func (gui *Gui) refreshRebaseCommits() error {
|
||||
gui.Mutexes.BranchCommitsMutex.Lock()
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"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/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
@ -553,7 +554,9 @@ func (gui *Gui) refreshStateFiles() error {
|
||||
prevNodes := gui.State.FileManager.GetAllItems()
|
||||
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
|
||||
state.FileManager.RWMutex.Lock()
|
||||
|
@ -75,7 +75,7 @@ func (gui *Gui) handleViewSubCommitFiles() error {
|
||||
|
||||
func (gui *Gui) switchToSubCommitsContext(refName string) error {
|
||||
// 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(
|
||||
loaders.GetCommitsOptions{
|
||||
|
Reference in New Issue
Block a user