summaryrefslogtreecommitdiffstats
path: root/pkg/commands/files.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/commands/files.go')
-rw-r--r--pkg/commands/files.go353
1 files changed, 31 insertions, 322 deletions
diff --git a/pkg/commands/files.go b/pkg/commands/files.go
index acb503d7f..9e17e60e0 100644
--- a/pkg/commands/files.go
+++ b/pkg/commands/files.go
@@ -1,359 +1,68 @@
package commands
import (
- "fmt"
"io/ioutil"
- "os"
- "path/filepath"
"strconv"
- "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"
+ "github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
)
-// CatFile obtains the content of a file
-func (c *GitCommand) CatFile(fileName string) (string, error) {
- buf, err := ioutil.ReadFile(fileName)
- if err != nil {
- return "", nil
- }
- return string(buf), nil
-}
-
-func (c *GitCommand) OpenMergeToolCmdObj() oscommands.ICmdObj {
- return c.Cmd.New("git mergetool")
-}
-
-func (c *GitCommand) OpenMergeTool() error {
- return c.OpenMergeToolCmdObj().Run()
-}
-
-// StageFile stages a file
-func (c *GitCommand) StageFile(fileName string) error {
- return c.Cmd.New("git add -- " + c.OSCommand.Quote(fileName)).Run()
-}
-
-// StageAll stages all files
-func (c *GitCommand) StageAll() error {
- return c.Cmd.New("git add -A").Run()
-}
-
-// UnstageAll unstages all files
-func (c *GitCommand) UnstageAll() error {
- return c.Cmd.New("git reset").Run()
-}
-
-// UnStageFile unstages a file
-// we accept an array of filenames for the cases where a file has been renamed i.e.
-// we accept the current name and the previous name
-func (c *GitCommand) UnStageFile(fileNames []string, reset bool) error {
- command := "git rm --cached --force -- %s"
- if reset {
- command = "git reset HEAD -- %s"
- }
-
- for _, name := range fileNames {
- err := c.Cmd.New(fmt.Sprintf(command, c.OSCommand.Quote(name))).Run()
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func (c *GitCommand) BeforeAndAfterFileForRename(file *models.File) (*models.File, *models.File, error) {
-
- if !file.IsRename() {
- return nil, nil, errors.New("Expected renamed file")
- }
-
- // we've got a file that represents a rename from one file to another. Here we will refetch
- // all files, passing the --no-renames flag and then recursively call the function
- // again for the before file and after file.
-
- 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 {
- if f.Name == file.PreviousName {
- beforeFile = f
- }
-
- if f.Name == file.Name {
- afterFile = f
- }
- }
-
- if beforeFile == nil || afterFile == nil {
- return nil, nil, errors.New("Could not find deleted file or new file for file rename")
- }
-
- if beforeFile.IsRename() || afterFile.IsRename() {
- // probably won't happen but we want to ensure we don't get an infinite loop
- return nil, nil, errors.New("Nested rename found")
- }
-
- return beforeFile, afterFile, nil
-}
-
-// DiscardAllFileChanges directly
-func (c *GitCommand) DiscardAllFileChanges(file *models.File) error {
- if file.IsRename() {
- beforeFile, afterFile, err := c.BeforeAndAfterFileForRename(file)
- if err != nil {
- return err
- }
-
- if err := c.DiscardAllFileChanges(beforeFile); err != nil {
- return err
- }
-
- if err := c.DiscardAllFileChanges(afterFile); err != nil {
- return err
- }
-
- return nil
- }
-
- quotedFileName := c.OSCommand.Quote(file.Name)
-
- if file.ShortStatus == "AA" {
- if err := c.Cmd.New("git checkout --ours -- " + quotedFileName).Run(); err != nil {
- return err
- }
- if err := c.Cmd.New("git add -- " + quotedFileName).Run(); err != nil {
- return err
- }
- return nil
- }
-
- if file.ShortStatus == "DU" {
- return c.Cmd.New("git rm -- " + quotedFileName).Run()
- }
-
- // if the file isn't tracked, we assume you want to delete it
- if file.HasStagedChanges || file.HasMergeConflicts {
- if err := c.Cmd.New("git reset -- " + quotedFileName).Run(); err != nil {
- return err
- }
- }
-
- if file.ShortStatus == "DD" || file.ShortStatus == "AU" {
- return nil
- }
-
- if file.Added {
- return c.OSCommand.RemoveFile(file.Name)
- }
- return c.DiscardUnstagedFileChanges(file)
-}
-
-func (c *GitCommand) DiscardAllDirChanges(node *filetree.FileNode) error {
- // this could be more efficient but we would need to handle all the edge cases
- return node.ForEachFile(c.DiscardAllFileChanges)
-}
-
-func (c *GitCommand) DiscardUnstagedDirChanges(node *filetree.FileNode) error {
- if err := c.RemoveUntrackedDirFiles(node); err != nil {
- return err
- }
-
- quotedPath := c.OSCommand.Quote(node.GetPath())
- if err := c.Cmd.New("git checkout -- " + quotedPath).Run(); err != nil {
- return err
- }
-
- return nil
-}
-
-func (c *GitCommand) RemoveUntrackedDirFiles(node *filetree.FileNode) error {
- untrackedFilePaths := node.GetPathsMatching(
- func(n *filetree.FileNode) bool { return n.File != nil && !n.File.GetIsTracked() },
- )
-
- for _, path := range untrackedFilePaths {
- err := os.Remove(path)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// DiscardUnstagedFileChanges directly
-func (c *GitCommand) DiscardUnstagedFileChanges(file *models.File) error {
- quotedFileName := c.OSCommand.Quote(file.Name)
- return c.Cmd.New("git checkout -- " + quotedFileName).Run()
-}
-
-// Ignore adds a file to the gitignore for the repo
-func (c *GitCommand) Ignore(filename string) error {
- return c.OSCommand.AppendLineToFile(".gitignore", filename)
-}
-
-// WorktreeFileDiff returns the diff of a file
-func (c *GitCommand) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string {
- // for now we assume an error means the file was deleted
- s, _ := c.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace).RunWithOutput()
- return s
-}
-
-func (c *GitCommand) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
- cachedArg := ""
- trackedArg := "--"
- colorArg := c.colorArg()
- quotedPath := c.OSCommand.Quote(node.GetPath())
- ignoreWhitespaceArg := ""
- contextSize := c.UserConfig.Git.DiffContextSize
- if cached {
- cachedArg = "--cached"
- }
- if !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached {
- trackedArg = "--no-index -- /dev/null"
- }
- if plain {
- colorArg = "never"
- }
- if ignoreWhitespace {
- ignoreWhitespaceArg = "--ignore-all-space"
- }
-
- cmdStr := fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s %s %s %s %s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath)
-
- return c.Cmd.New(cmdStr).DontLog()
-}
-
-func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
- filepath := filepath.Join(oscommands.GetTempDir(), utils.GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
- c.Log.Infof("saving temporary patch to %s", filepath)
- if err := c.OSCommand.CreateFileWithContent(filepath, patch); err != nil {
- return err
- }
-
- flagStr := ""
- for _, flag := range flags {
- flagStr += " --" + flag
- }
+type FileCommands struct {
+ *common.Common
- return c.Cmd.New(fmt.Sprintf("git apply%s %s", flagStr, c.OSCommand.Quote(filepath))).Run()
+ cmd oscommands.ICmdObjBuilder
+ config *ConfigCommands
+ os FileOSCommand
}
-// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
-// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
-func (c *GitCommand) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) {
- return c.ShowFileDiffCmdObj(from, to, reverse, fileName, plain).RunWithOutput()
+type FileOSCommand interface {
+ Getenv(string) string
}
-func (c *GitCommand) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
- colorArg := c.colorArg()
- contextSize := c.UserConfig.Git.DiffContextSize
- if plain {
- colorArg = "never"
+func NewFileCommands(
+ common *common.Common,
+ cmd oscommands.ICmdObjBuilder,
+ config *ConfigCommands,
+ osCommand FileOSCommand,
+) *FileCommands {
+ return &FileCommands{
+ Common: common,
+ cmd: cmd,
+ config: config,
+ os: osCommand,
}
-
- reverseFlag := ""
- if reverse {
- reverseFlag = " -R "
- }
-
- return c.Cmd.New(fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s %s %s %s -- %s", contextSize, colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName))).DontLog()
-}
-
-// CheckoutFile checks out the file for the given commit
-func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
- return c.Cmd.New(fmt.Sprintf("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))).Run()
-}
-
-// DiscardOldFileChanges discards changes to a file from an old commit
-func (c *GitCommand) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, fileName string) error {
- if err := c.BeginInteractiveRebaseForCommit(commits, commitIndex); err != nil {
- return err
- }
-
- // check if file exists in previous commit (this command returns an error if the file doesn't exist)
- if err := c.Cmd.New("git cat-file -e HEAD^:" + c.OSCommand.Quote(fileName)).DontLog().Run(); err != nil {
- if err := c.OSCommand.Remove(fileName); err != nil {
- return err
- }
- if err := c.StageFile(fileName); err != nil {
- return err
- }
- } else if err := c.CheckoutFile("HEAD^", fileName); err != nil {
- return err
- }
-
- // amend the commit
- err := c.AmendHead()
- if err != nil {
- return err
- }
-
- // continue
- return c.GenericMergeOrRebaseAction("rebase", "continue")
}
-// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
-func (c *GitCommand) DiscardAnyUnstagedFileChanges() error {
- return c.Cmd.New("git checkout -- .").Run()
-}
-
-// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
-func (c *GitCommand) RemoveTrackedFiles(name string) error {
- return c.Cmd.New("git rm -r --cached -- " + c.OSCommand.Quote(name)).Run()
-}
-
-// RemoveUntrackedFiles runs `git clean -fd`
-func (c *GitCommand) RemoveUntrackedFiles() error {
- return c.Cmd.New("git clean -fd").Run()
-}
-
-// ResetAndClean removes all unstaged changes and removes all untracked files
-func (c *GitCommand) ResetAndClean() error {
- submoduleConfigs, err := c.GetSubmoduleConfigs()
+// Cat obtains the content of a file
+func (self *FileCommands) Cat(fileName string) (string, error) {
+ buf, err := ioutil.ReadFile(fileName)
if err != nil {
- return err
- }
-
- if len(submoduleConfigs) > 0 {
- if err := c.ResetSubmodules(submoduleConfigs); err != nil {
- return err
- }
- }
-
- if err := c.ResetHard("HEAD"); err != nil {
- return err
+ return "", nil
}
-
- return c.RemoveUntrackedFiles()
+ return string(buf), nil
}
-func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, error) {
+func (c *FileCommands) GetEditCmdStr(filename string, lineNumber int) (string, error) {
editor := c.UserConfig.OS.EditCommand
if editor == "" {
- editor = c.GitConfig.Get("core.editor")
+ editor = c.config.GetCoreEditor()
}
if editor == "" {
- editor = c.OSCommand.Getenv("GIT_EDITOR")
+ editor = c.os.Getenv("GIT_EDITOR")
}
if editor == "" {
- editor = c.OSCommand.Getenv("VISUAL")
+ editor = c.os.Getenv("VISUAL")
}
if editor == "" {
- editor = c.OSCommand.Getenv("EDITOR")
+ editor = c.os.Getenv("EDITOR")
}
if editor == "" {
- if err := c.OSCommand.Cmd.New("which vi").DontLog().Run(); err == nil {
+ if err := c.cmd.New("which vi").DontLog().Run(); err == nil {
editor = "vi"
}
}
@@ -363,7 +72,7 @@ func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, er
templateValues := map[string]string{
"editor": editor,
- "filename": c.OSCommand.Quote(filename),
+ "filename": c.cmd.Quote(filename),
"line": strconv.Itoa(lineNumber),
}