summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/commands/files.go4
-rw-r--r--pkg/commands/files_test.go6
-rw-r--r--pkg/commands/loading_files.go65
-rw-r--r--pkg/commands/loading_files_test.go75
-rw-r--r--pkg/commands/models/file.go2
-rw-r--r--pkg/commands/submodules.go18
6 files changed, 126 insertions, 44 deletions
diff --git a/pkg/commands/files.go b/pkg/commands/files.go
index 51523ea5f..9fa5fb1bd 100644
--- a/pkg/commands/files.go
+++ b/pkg/commands/files.go
@@ -250,12 +250,12 @@ func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fi
reverseFlag = " -R "
}
- return fmt.Sprintf(`git diff --submodule --no-ext-diff --no-renames --color=%s %s %s %s -- "%s"`, colorArg, from, to, reverseFlag, fileName)
+ return fmt.Sprintf("git diff --submodule --no-ext-diff --no-renames --color=%s %s %s %s -- %s", colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName))
}
// CheckoutFile checks out the file for the given commit
func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
- return c.RunCommand("git checkout %s %s", commitSha, fileName)
+ return c.RunCommand("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))
}
// DiscardOldFileChanges discards changes to a file from an old commit
diff --git a/pkg/commands/files_test.go b/pkg/commands/files_test.go
index fd4b9f69f..01c1a6793 100644
--- a/pkg/commands/files_test.go
+++ b/pkg/commands/files_test.go
@@ -455,7 +455,7 @@ func TestGitCommandCheckoutFile(t *testing.T) {
"test999.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
- Expect: "git checkout 11af912 test999.txt",
+ Expect: "git checkout 11af912 -- test999.txt",
Replace: "echo",
},
}),
@@ -469,7 +469,7 @@ func TestGitCommandCheckoutFile(t *testing.T) {
"test999.txt",
test.CreateMockCommand(t, []*test.CommandSwapper{
{
- Expect: "git checkout 11af912 test999.txt",
+ Expect: "git checkout 11af912 -- test999.txt",
Replace: "test",
},
}),
@@ -606,7 +606,7 @@ func TestGitCommandDiscardOldFileChanges(t *testing.T) {
Replace: "echo",
},
{
- Expect: "git checkout HEAD^ test999.txt",
+ Expect: "git checkout HEAD^ -- test999.txt",
Replace: "echo",
},
{
diff --git a/pkg/commands/loading_files.go b/pkg/commands/loading_files.go
index 2b9bfc2be..d3638a724 100644
--- a/pkg/commands/loading_files.go
+++ b/pkg/commands/loading_files.go
@@ -8,8 +8,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
-const RENAME_SEPARATOR = " -> "
-
// GetStatusFiles git status files
type GetStatusFileOptions struct {
NoRenames bool
@@ -24,36 +22,29 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
}
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
- statusStrings, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
+ statuses, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
if err != nil {
c.Log.Error(err)
}
files := []*models.File{}
- for _, statusString := range statusStrings {
- if strings.HasPrefix(statusString, "warning") {
- c.Log.Warningf("warning when calling git status: %s", statusString)
+ for _, status := range statuses {
+ if strings.HasPrefix(status.StatusString, "warning") {
+ c.Log.Warningf("warning when calling git status: %s", status.StatusString)
continue
}
- change := statusString[0:2]
+ change := status.Change
stagedChange := change[0:1]
- unstagedChange := statusString[1:2]
- name := statusString[3:]
+ unstagedChange := change[1:2]
untracked := utils.IncludesString([]string{"??", "A ", "AM"}, change)
hasNoStagedChanges := utils.IncludesString([]string{" ", "U", "?"}, stagedChange)
hasMergeConflicts := utils.IncludesString([]string{"DD", "AA", "UU", "AU", "UA", "UD", "DU"}, change)
hasInlineMergeConflicts := utils.IncludesString([]string{"UU", "AA"}, change)
- previousName := ""
- if strings.Contains(name, RENAME_SEPARATOR) {
- split := strings.Split(name, RENAME_SEPARATOR)
- name = split[1]
- previousName = split[0]
- }
file := &models.File{
- Name: name,
- PreviousName: previousName,
- DisplayString: statusString,
+ Name: status.Name,
+ PreviousName: status.PreviousName,
+ DisplayString: status.StatusString,
HasStagedChanges: !hasNoStagedChanges,
HasUnstagedChanges: unstagedChange != " ",
Tracked: !untracked,
@@ -61,7 +52,7 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
Added: unstagedChange == "A" || untracked,
HasMergeConflicts: hasMergeConflicts,
HasInlineMergeConflicts: hasInlineMergeConflicts,
- Type: c.OSCommand.FileType(name),
+ Type: c.OSCommand.FileType(status.Name),
ShortStatus: change,
}
files = append(files, file)
@@ -70,13 +61,20 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
return files
}
-// GitStatus returns the plaintext short status of the repo
+// GitStatus returns the file status of the repo
type GitStatusOptions struct {
NoRenames bool
UntrackedFilesArg string
}
-func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]string, error) {
+type FileStatus struct {
+ StatusString string
+ Change string // ??, MM, AM, ...
+ Name string
+ PreviousName string
+}
+
+func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
noRenamesFlag := ""
if opts.NoRenames {
noRenamesFlag = "--no-renames"
@@ -84,23 +82,34 @@ func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]string, error) {
statusLines, err := c.RunCommandWithOutput("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)
if err != nil {
- return []string{}, err
+ return []FileStatus{}, err
}
splitLines := strings.Split(statusLines, "\x00")
- response := []string{}
+ response := []FileStatus{}
for i := 0; i < len(splitLines); i++ {
original := splitLines[i]
- if len(original) < 2 {
+
+ if len(original) < 3 {
continue
- } else if strings.HasPrefix(original, "R ") {
+ }
+
+ status := FileStatus{
+ StatusString: original,
+ Change: original[:2],
+ Name: original[3:],
+ PreviousName: "",
+ }
+
+ if strings.HasPrefix(status.Change, "R") {
// if a line starts with 'R' then the next line is the original file.
- next := strings.TrimSpace(splitLines[i+1])
- original = "R " + next + RENAME_SEPARATOR + strings.TrimPrefix(original, "R ")
+ status.PreviousName = strings.TrimSpace(splitLines[i+1])
+ status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousName, status.Name)
i++
}
- response = append(response, original)
+
+ response = append(response, status)
}
return response, nil
diff --git a/pkg/commands/loading_files_test.go b/pkg/commands/loading_files_test.go
index eefb8f924..456e4aca7 100644
--- a/pkg/commands/loading_files_test.go
+++ b/pkg/commands/loading_files_test.go
@@ -139,6 +139,81 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
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 {
diff --git a/pkg/commands/models/file.go b/pkg/commands/models/file.go
index d3aafb48e..c5e76949a 100644
--- a/pkg/commands/models/file.go
+++ b/pkg/commands/models/file.go
@@ -29,8 +29,6 @@ type IFile interface {
GetPath() string
}
-const RENAME_SEPARATOR = " -> "
-
func (f *File) IsRename() bool {
return f.PreviousName != ""
}
diff --git a/pkg/commands/submodules.go b/pkg/commands/submodules.go
index 856bc4cbe..9a5c49951 100644
--- a/pkg/commands/submodules.go
+++ b/pkg/commands/submodules.go
@@ -69,11 +69,11 @@ func (c *GitCommand) SubmoduleStash(submodule *models.SubmoduleConfig) error {
return nil
}
- return c.RunCommand("git -C %s stash --include-untracked", submodule.Path)
+ return c.RunCommand("git -C %s stash --include-untracked", c.OSCommand.Quote(submodule.Path))
}
func (c *GitCommand) SubmoduleReset(submodule *models.SubmoduleConfig) error {
- return c.RunCommand("git submodule update --init --force %s", submodule.Path)
+ return c.RunCommand("git submodule update --init --force -- %s", c.OSCommand.Quote(submodule.Path))
}
func (c *GitCommand) SubmoduleUpdateAll() error {
@@ -84,13 +84,13 @@ func (c *GitCommand) SubmoduleUpdateAll() error {
func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
- if err := c.RunCommand("git submodule deinit --force %s", submodule.Path); err != nil {
+ if err := c.RunCommand("git submodule deinit --force -- %s", c.OSCommand.Quote(submodule.Path)); err != nil {
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
- if err := c.RunCommand("git config --file .gitmodules --remove-section submodule.%s", submodule.Name); err != nil {
+ if err := c.RunCommand("git config --file .gitmodules --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
return err
}
- if err := c.RunCommand("git config --remove-section submodule.%s", submodule.Name); err != nil {
+ if err := c.RunCommand("git config --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
return err
}
@@ -119,11 +119,11 @@ func (c *GitCommand) SubmoduleAdd(name string, path string, url string) error {
func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string) error {
// the set-url command is only for later git versions so we're doing it manually here
- if err := c.RunCommand("git config --file .gitmodules submodule.%s.url %s", name, newUrl); err != nil {
+ if err := c.RunCommand("git config --file .gitmodules submodule.%s.url %s", c.OSCommand.Quote(name), newUrl); err != nil {
return err
}
- if err := c.RunCommand("git submodule sync %s", path); err != nil {
+ if err := c.RunCommand("git submodule sync -- %s", c.OSCommand.Quote(path)); err != nil {
return err
}
@@ -131,11 +131,11 @@ func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string)
}
func (c *GitCommand) SubmoduleInit(path string) error {
- return c.RunCommand("git submodule init %s", path)
+ return c.RunCommand("git submodule init -- %s", c.OSCommand.Quote(path))
}
func (c *GitCommand) SubmoduleUpdate(path string) error {
- return c.RunCommand("git submodule update --init %s", path)
+ return c.RunCommand("git submodule update --init -- %s", c.OSCommand.Quote(path))
}
func (c *GitCommand) SubmoduleBulkInitCmdStr() string {