mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-11 16:16:28 -04:00
We have integration tests for this functionality, but those only test the behavior, not the performance. In these unit tests you can see that we make individual calls to git checkout and git reset for each file, which is very slow when there are lots of files.
745 lines
22 KiB
Go
745 lines
22 KiB
Go
package git_commands
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-errors/errors"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
|
"github.com/samber/lo"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestWorkingTreeStageFile(t *testing.T) {
|
|
runner := oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"add", "--", "test.txt"}, "", nil)
|
|
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
|
|
|
|
assert.NoError(t, instance.StageFile("test.txt"))
|
|
runner.CheckForMissingCalls()
|
|
}
|
|
|
|
func TestWorkingTreeStageFiles(t *testing.T) {
|
|
runner := oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"add", "--", "test.txt", "test2.txt"}, "", nil)
|
|
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
|
|
|
|
assert.NoError(t, instance.StageFiles([]string{"test.txt", "test2.txt"}, nil))
|
|
runner.CheckForMissingCalls()
|
|
}
|
|
|
|
func TestWorkingTreeUnstageFile(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
reset bool
|
|
runner *oscommands.FakeCmdObjRunner
|
|
test func(error)
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "Remove an untracked file from staging",
|
|
reset: false,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"rm", "--cached", "--force", "--", "test.txt"}, "", nil),
|
|
test: func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
testName: "Remove a tracked file from staging",
|
|
reset: true,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "HEAD", "--", "test.txt"}, "", nil),
|
|
test: func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
s.test(instance.UnStageFile([]string{"test.txt"}, s.reset))
|
|
})
|
|
}
|
|
}
|
|
|
|
// these tests don't cover everything, in part because we already have an integration
|
|
// test which does cover everything. I don't want to unnecessarily assert on the 'how'
|
|
// when the 'what' is what matters
|
|
func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
file *models.File
|
|
removedFileErr error
|
|
runner *oscommands.FakeCmdObjRunner
|
|
expectedError string
|
|
expectedRemovedFiles []string
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "An error occurred when resetting",
|
|
file: &models.File{
|
|
Path: "test",
|
|
HasStagedChanges: true,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", errors.New("error")),
|
|
expectedError: "error",
|
|
},
|
|
{
|
|
testName: "An error occurred when removing file",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: false,
|
|
Added: true,
|
|
},
|
|
removedFileErr: errors.New("an error occurred when removing file"),
|
|
runner: oscommands.NewFakeRunner(t),
|
|
expectedError: "an error occurred when removing file",
|
|
expectedRemovedFiles: []string{"test"},
|
|
},
|
|
{
|
|
testName: "An error occurred with checkout",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: true,
|
|
HasStagedChanges: false,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", errors.New("error")),
|
|
expectedError: "error",
|
|
},
|
|
{
|
|
testName: "Checkout only",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: true,
|
|
HasStagedChanges: false,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
|
},
|
|
{
|
|
testName: "Reset and checkout staged changes",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: true,
|
|
HasStagedChanges: true,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
|
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
|
},
|
|
{
|
|
testName: "Reset and checkout merge conflicts",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: true,
|
|
HasMergeConflicts: true,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
|
|
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
|
|
},
|
|
{
|
|
testName: "Reset and remove",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: false,
|
|
Added: true,
|
|
HasStagedChanges: true,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil),
|
|
expectedRemovedFiles: []string{"test"},
|
|
},
|
|
{
|
|
testName: "Remove only",
|
|
file: &models.File{
|
|
Path: "test",
|
|
Tracked: false,
|
|
Added: true,
|
|
HasStagedChanges: false,
|
|
},
|
|
runner: oscommands.NewFakeRunner(t),
|
|
expectedRemovedFiles: []string{"test"},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
var removedFiles []string
|
|
removeFile := func(path string) error {
|
|
removedFiles = append(removedFiles, path)
|
|
return s.removedFileErr
|
|
}
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, removeFile: removeFile})
|
|
err := instance.DiscardAllFileChanges(s.file)
|
|
|
|
if s.expectedError == "" {
|
|
assert.Nil(t, err)
|
|
} else {
|
|
assert.Equal(t, s.expectedError, err.Error())
|
|
}
|
|
assert.Equal(t, s.expectedRemovedFiles, removedFiles)
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeDiff(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
file *models.File
|
|
plain bool
|
|
cached bool
|
|
ignoreWhitespace bool
|
|
contextSize uint64
|
|
similarityThreshold int
|
|
runner *oscommands.FakeCmdObjRunner
|
|
}
|
|
|
|
const expectedResult = "pretend this is an actual git diff"
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "Default case",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: true,
|
|
},
|
|
plain: false,
|
|
cached: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 3,
|
|
similarityThreshold: 50,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "cached",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: true,
|
|
},
|
|
plain: false,
|
|
cached: true,
|
|
ignoreWhitespace: false,
|
|
contextSize: 3,
|
|
similarityThreshold: 50,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--cached", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "plain",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: true,
|
|
},
|
|
plain: true,
|
|
cached: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 3,
|
|
similarityThreshold: 50,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "File not tracked and file has no staged changes",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: false,
|
|
},
|
|
plain: false,
|
|
cached: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 3,
|
|
similarityThreshold: 50,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "Default case (ignore whitespace)",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: true,
|
|
},
|
|
plain: false,
|
|
cached: false,
|
|
ignoreWhitespace: true,
|
|
contextSize: 3,
|
|
similarityThreshold: 50,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "Show diff with custom context size",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: true,
|
|
},
|
|
plain: false,
|
|
cached: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 17,
|
|
similarityThreshold: 50,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "Show diff with custom similarity threshold",
|
|
file: &models.File{
|
|
Path: "test.txt",
|
|
HasStagedChanges: false,
|
|
Tracked: true,
|
|
},
|
|
plain: false,
|
|
cached: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 3,
|
|
similarityThreshold: 33,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=33%", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
userConfig := config.GetDefaultConfig()
|
|
userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
|
userConfig.Git.DiffContextSize = s.contextSize
|
|
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
|
|
repoPaths := RepoPaths{
|
|
worktreePath: "/path/to/worktree",
|
|
}
|
|
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig, appState: &config.AppState{}, repoPaths: &repoPaths})
|
|
result := instance.WorktreeFileDiff(s.file, s.plain, s.cached)
|
|
assert.Equal(t, expectedResult, result)
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeShowFileDiff(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
from string
|
|
to string
|
|
reverse bool
|
|
plain bool
|
|
ignoreWhitespace bool
|
|
contextSize uint64
|
|
runner *oscommands.FakeCmdObjRunner
|
|
}
|
|
|
|
const expectedResult = "pretend this is an actual git diff"
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "Default case",
|
|
from: "1234567890",
|
|
to: "0987654321",
|
|
reverse: false,
|
|
plain: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 3,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "Show diff with custom context size",
|
|
from: "1234567890",
|
|
to: "0987654321",
|
|
reverse: false,
|
|
plain: false,
|
|
ignoreWhitespace: false,
|
|
contextSize: 123,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
{
|
|
testName: "Default case (ignore whitespace)",
|
|
from: "1234567890",
|
|
to: "0987654321",
|
|
reverse: false,
|
|
plain: false,
|
|
ignoreWhitespace: true,
|
|
contextSize: 3,
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
userConfig := config.GetDefaultConfig()
|
|
userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
|
userConfig.Git.DiffContextSize = s.contextSize
|
|
repoPaths := RepoPaths{
|
|
worktreePath: "/path/to/worktree",
|
|
}
|
|
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig, appState: &config.AppState{}, repoPaths: &repoPaths})
|
|
|
|
result, err := instance.ShowFileDiff(s.from, s.to, s.reverse, "test.txt", s.plain)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, expectedResult, result)
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeCheckoutFile(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
commitHash string
|
|
fileName string
|
|
runner *oscommands.FakeCmdObjRunner
|
|
test func(error)
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "typical case",
|
|
commitHash: "11af912",
|
|
fileName: "test999.txt",
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
|
|
test: func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
testName: "returns error if there is one",
|
|
commitHash: "11af912",
|
|
fileName: "test999.txt",
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
|
|
test: func(err error) {
|
|
assert.Error(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
|
|
s.test(instance.CheckoutFile(s.commitHash, s.fileName))
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
file *models.File
|
|
runner *oscommands.FakeCmdObjRunner
|
|
test func(error)
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "valid case",
|
|
file: &models.File{Path: "test.txt"},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "test.txt"}, "", nil),
|
|
test: func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
s.test(instance.DiscardUnstagedFileChanges(s.file))
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
// testNode implements IFileNode for unit tests.
|
|
type testNode struct {
|
|
files []*models.File // all leaf files under this node
|
|
path string
|
|
file *models.File // non-nil only for file nodes
|
|
}
|
|
|
|
func (n *testNode) ForEachFile(cb func(*models.File) error) error {
|
|
for _, f := range n.files {
|
|
if err := cb(f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *testNode) GetFilePathsMatching(test func(*models.File) bool) []string {
|
|
return lo.FilterMap(n.files, func(f *models.File, _ int) (string, bool) {
|
|
return f.Path, test(f)
|
|
})
|
|
}
|
|
|
|
func (n *testNode) GetPath() string { return n.path }
|
|
func (n *testNode) GetFile() *models.File { return n.file }
|
|
|
|
func TestWorkingTreeDiscardAllDirChanges(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
node *testNode
|
|
runner *oscommands.FakeCmdObjRunner
|
|
expectedRemovedFiles []string
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "multiple tracked files make individual checkout calls",
|
|
node: &testNode{
|
|
files: []*models.File{
|
|
{Path: "a.txt", Tracked: true},
|
|
{Path: "b.txt", Tracked: true},
|
|
{Path: "c.txt", Tracked: true},
|
|
},
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "a.txt"}, "", nil).
|
|
ExpectGitArgs([]string{"checkout", "--", "b.txt"}, "", nil).
|
|
ExpectGitArgs([]string{"checkout", "--", "c.txt"}, "", nil),
|
|
},
|
|
{
|
|
testName: "staged files each make an individual reset then checkout",
|
|
node: &testNode{
|
|
files: []*models.File{
|
|
{Path: "a.txt", Tracked: true, HasStagedChanges: true},
|
|
{Path: "b.txt", Tracked: true, HasStagedChanges: true},
|
|
},
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "--", "a.txt"}, "", nil).
|
|
ExpectGitArgs([]string{"checkout", "--", "a.txt"}, "", nil).
|
|
ExpectGitArgs([]string{"reset", "--", "b.txt"}, "", nil).
|
|
ExpectGitArgs([]string{"checkout", "--", "b.txt"}, "", nil),
|
|
},
|
|
{
|
|
testName: "added files with no staged changes are removed from disk without any git call",
|
|
node: &testNode{
|
|
files: []*models.File{
|
|
{Path: "new1.txt", Added: true},
|
|
{Path: "new2.txt", Added: true},
|
|
},
|
|
},
|
|
runner: oscommands.NewFakeRunner(t),
|
|
expectedRemovedFiles: []string{"new1.txt", "new2.txt"},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
var removedFiles []string
|
|
removeFile := func(path string) error {
|
|
removedFiles = append(removedFiles, path)
|
|
return nil
|
|
}
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, removeFile: removeFile})
|
|
err := instance.DiscardAllDirChanges(s.node)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, s.expectedRemovedFiles, removedFiles)
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeDiscardUnstagedDirChanges(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
node *testNode
|
|
runner *oscommands.FakeCmdObjRunner
|
|
expectedRemovedFiles []string
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "directory node: uses directory path for checkout",
|
|
node: &testNode{
|
|
path: "dir",
|
|
files: []*models.File{
|
|
{Path: "dir/tracked1.txt", Tracked: true},
|
|
{Path: "dir/tracked2.txt", Tracked: true},
|
|
{Path: "dir/new.txt", Tracked: false},
|
|
},
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "dir"}, "", nil),
|
|
expectedRemovedFiles: []string{"dir/new.txt"},
|
|
},
|
|
{
|
|
testName: "directory node: staged-but-not-committed file (Tracked=false, HasStagedChanges=true) is removed along with untracked files",
|
|
node: &testNode{
|
|
path: "dir",
|
|
files: []*models.File{
|
|
{Path: "dir/staged-new1.txt", Tracked: false, Added: true, HasStagedChanges: true},
|
|
{Path: "dir/staged-new2.txt", Tracked: false, Added: true, HasStagedChanges: true},
|
|
{Path: "dir/untracked.txt", Tracked: false, Added: true, HasStagedChanges: false},
|
|
},
|
|
},
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "dir"}, "", nil),
|
|
// All files are removed because the predicate of GetFilePathsMatching in
|
|
// RemoveUntrackedDirFiles is just !Tracked. git checkout -- dir then restores the
|
|
// staged file from the index. This is a bit wasteful, and we'll improve it at the end
|
|
// of this branch.
|
|
expectedRemovedFiles: []string{"dir/staged-new1.txt", "dir/staged-new2.txt", "dir/untracked.txt"},
|
|
},
|
|
{
|
|
testName: "file node: added and unstaged file is removed from disk",
|
|
node: &testNode{
|
|
path: "new.txt",
|
|
files: []*models.File{{Path: "new.txt", Added: true}},
|
|
file: &models.File{Path: "new.txt", Added: true, HasStagedChanges: false},
|
|
},
|
|
runner: oscommands.NewFakeRunner(t),
|
|
expectedRemovedFiles: []string{"new.txt"},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
var removedFiles []string
|
|
removeFile := func(path string) error {
|
|
removedFiles = append(removedFiles, path)
|
|
return nil
|
|
}
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, removeFile: removeFile})
|
|
assert.NoError(t, instance.DiscardUnstagedDirChanges(s.node))
|
|
s.runner.CheckForMissingCalls()
|
|
assert.Equal(t, s.expectedRemovedFiles, removedFiles)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
runner *oscommands.FakeCmdObjRunner
|
|
test func(error)
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "valid case",
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"checkout", "--", "."}, "", nil),
|
|
test: func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
s.test(instance.DiscardAnyUnstagedFileChanges())
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
runner *oscommands.FakeCmdObjRunner
|
|
test func(error)
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
testName: "valid case",
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"clean", "-fd"}, "", nil),
|
|
test: func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
s.test(instance.RemoveUntrackedFiles())
|
|
s.runner.CheckForMissingCalls()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeResetHard(t *testing.T) {
|
|
type scenario struct {
|
|
testName string
|
|
ref string
|
|
runner *oscommands.FakeCmdObjRunner
|
|
test func(error)
|
|
}
|
|
|
|
scenarios := []scenario{
|
|
{
|
|
"valid case",
|
|
"HEAD",
|
|
oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"reset", "--hard", "HEAD"}, "", nil),
|
|
func(err error) {
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.testName, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
s.test(instance.ResetHard(s.ref))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWorkingTreeCommands_AllRepoFiles(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
runner *oscommands.FakeCmdObjRunner
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "no files",
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"ls-files", "-z"}, "", nil),
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "two files",
|
|
runner: oscommands.NewFakeRunner(t).
|
|
ExpectGitArgs([]string{"ls-files", "-z"}, "dir/file1.txt\x00dir2/file2.go\x00", nil),
|
|
expected: []string{"dir/file1.txt", "dir2/file2.go"},
|
|
},
|
|
}
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
|
result, err := instance.AllRepoFiles()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, s.expected, result)
|
|
})
|
|
}
|
|
}
|