1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-28 16:02:01 +03:00

Merge branch 'master' into feature/commit-amend

This commit is contained in:
Jesse Duffield
2018-09-25 19:05:24 +10:00
committed by GitHub
55 changed files with 1484 additions and 396 deletions

View File

@ -65,6 +65,7 @@ type GitCommand struct {
Tr *i18n.Localizer
getGlobalGitConfig func(string) (string, error)
getLocalGitConfig func(string) (string, error)
removeFile func(string) error
}
// NewGitCommand it runs git commands
@ -100,21 +101,22 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer)
Repo: repo,
getGlobalGitConfig: gitconfig.Global,
getLocalGitConfig: gitconfig.Local,
removeFile: os.RemoveAll,
}, nil
}
// GetStashEntries stash entryies
func (c *GitCommand) GetStashEntries() []StashEntry {
func (c *GitCommand) GetStashEntries() []*StashEntry {
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
stashEntries := []StashEntry{}
stashEntries := []*StashEntry{}
for i, line := range utils.SplitLines(rawString) {
stashEntries = append(stashEntries, stashEntryFromLine(line, i))
}
return stashEntries
}
func stashEntryFromLine(line string, index int) StashEntry {
return StashEntry{
func stashEntryFromLine(line string, index int) *StashEntry {
return &StashEntry{
Name: line,
Index: index,
DisplayString: line,
@ -127,10 +129,10 @@ func (c *GitCommand) GetStashEntryDiff(index int) (string, error) {
}
// GetStatusFiles git status files
func (c *GitCommand) GetStatusFiles() []File {
func (c *GitCommand) GetStatusFiles() []*File {
statusOutput, _ := c.GitStatus()
statusStrings := utils.SplitLines(statusOutput)
files := []File{}
files := []*File{}
for _, statusString := range statusStrings {
change := statusString[0:2]
@ -140,7 +142,7 @@ func (c *GitCommand) GetStatusFiles() []File {
_, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change]
_, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange]
file := File{
file := &File{
Name: filename,
DisplayString: statusString,
HasStagedChanges: !hasNoStagedChanges,
@ -168,7 +170,7 @@ func (c *GitCommand) StashSave(message string) error {
}
// MergeStatusFiles merge status files
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File {
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File) []*File {
if len(oldFiles) == 0 {
return newFiles
}
@ -176,7 +178,7 @@ func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File {
appendedIndexes := []int{}
// retain position of files we already could see
result := []File{}
result := []*File{}
for _, oldFile := range oldFiles {
for newIndex, newFile := range newFiles {
if oldFile.Name == newFile.Name {
@ -231,13 +233,18 @@ func (c *GitCommand) UpstreamDifferenceCount() (string, string) {
}
// GetCommitsToPush Returns the sha's of the commits that have not yet been pushed
// to the remote branch of the current branch
func (c *GitCommand) GetCommitsToPush() []string {
pushables, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit")
// to the remote branch of the current branch, a map is returned to ease look up
func (c *GitCommand) GetCommitsToPush() map[string]bool {
pushables := map[string]bool{}
o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit")
if err != nil {
return []string{}
return pushables
}
return utils.SplitLines(pushables)
for _, p := range utils.SplitLines(o) {
pushables[p] = true
}
return pushables
}
// RenameCommit renames the topmost commit with the given name
@ -331,56 +338,50 @@ func (c *GitCommand) Push(branchName string, force bool) error {
// retaining the message of the higher commit
func (c *GitCommand) SquashPreviousTwoCommits(message string) error {
// TODO: test this
err := c.OSCommand.RunCommand("git reset --soft HEAD^")
if err != nil {
if err := c.OSCommand.RunCommand("git reset --soft HEAD^"); err != nil {
return err
}
// TODO: if password is required, we need to return a subprocess
return c.OSCommand.RunCommand("git commit --amend -m " + c.OSCommand.Quote(message))
return c.OSCommand.RunCommand(fmt.Sprintf("git commit --amend -m %s", c.OSCommand.Quote(message)))
}
// SquashFixupCommit squashes a 'FIXUP' commit into the commit beneath it,
// retaining the commit message of the lower commit
func (c *GitCommand) SquashFixupCommit(branchName string, shaValue string) error {
var err error
commands := []string{
"git checkout -q " + shaValue,
"git reset --soft " + shaValue + "^",
"git commit --amend -C " + shaValue + "^",
"git rebase --onto HEAD " + shaValue + " " + branchName,
fmt.Sprintf("git checkout -q %s", shaValue),
fmt.Sprintf("git reset --soft %s^", shaValue),
fmt.Sprintf("git commit --amend -C %s^", shaValue),
fmt.Sprintf("git rebase --onto HEAD %s %s", shaValue, branchName),
}
ret := ""
for _, command := range commands {
c.Log.Info(command)
output, err := c.OSCommand.RunCommandWithOutput(command)
ret += output
if err != nil {
if output, err := c.OSCommand.RunCommandWithOutput(command); err != nil {
ret := output
// We are already in an error state here so we're just going to append
// the output of these commands
output, _ := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git branch -d %s", shaValue))
ret += output
output, _ = c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git checkout %s", branchName))
ret += output
c.Log.Info(ret)
break
return errors.New(ret)
}
}
if err != nil {
// We are already in an error state here so we're just going to append
// the output of these commands
output, _ := c.OSCommand.RunCommandWithOutput("git branch -d " + shaValue)
ret += output
output, _ = c.OSCommand.RunCommandWithOutput("git checkout " + branchName)
ret += output
}
if err != nil {
return errors.New(ret)
}
return nil
}
// CatFile obtain the contents of a file
// CatFile obtains the content of a file
func (c *GitCommand) CatFile(fileName string) (string, error) {
return c.OSCommand.RunCommandWithOutput("cat " + c.OSCommand.Quote(fileName))
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("cat %s", c.OSCommand.Quote(fileName)))
}
// StageFile stages a file
func (c *GitCommand) StageFile(fileName string) error {
return c.OSCommand.RunCommand("git add " + c.OSCommand.Quote(fileName))
return c.OSCommand.RunCommand(fmt.Sprintf("git add %s", c.OSCommand.Quote(fileName)))
}
// StageAll stages all files
@ -395,13 +396,11 @@ func (c *GitCommand) UnstageAll() error {
// UnStageFile unstages a file
func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
var command string
command := "git rm --cached %s"
if tracked {
command = "git reset HEAD "
} else {
command = "git rm --cached "
command = "git reset HEAD %s"
}
return c.OSCommand.RunCommand(command + c.OSCommand.Quote(fileName))
return c.OSCommand.RunCommand(fmt.Sprintf(command, c.OSCommand.Quote(fileName)))
}
// GitStatus returns the plaintext short status of the repo
@ -419,18 +418,18 @@ func (c *GitCommand) IsInMergeState() (bool, error) {
}
// RemoveFile directly
func (c *GitCommand) RemoveFile(file File) error {
func (c *GitCommand) RemoveFile(file *File) error {
// if the file isn't tracked, we assume you want to delete it
if file.HasStagedChanges {
if err := c.OSCommand.RunCommand("git reset -- " + file.Name); err != nil {
if err := c.OSCommand.RunCommand(fmt.Sprintf("git reset -- %s", file.Name)); err != nil {
return err
}
}
if !file.Tracked {
return os.RemoveAll(file.Name)
return c.removeFile(file.Name)
}
// if the file is tracked, we assume you want to just check it out
return c.OSCommand.RunCommand("git checkout -- " + file.Name)
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -- %s", file.Name))
}
// Checkout checks out a branch, with --force if you set the force arg to true
@ -439,7 +438,7 @@ func (c *GitCommand) Checkout(branch string, force bool) error {
if force {
forceArg = "--force "
}
return c.OSCommand.RunCommand("git checkout " + forceArg + branch)
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout %s %s", forceArg, branch))
}
// AddPatch prepares a subprocess for adding a patch by patch
@ -462,30 +461,20 @@ func (c *GitCommand) PrepareCommitAmendSubProcess() *exec.Cmd {
// Currently it limits the result to 100 commits, but when we get async stuff
// working we can do lazy loading
func (c *GitCommand) GetBranchGraph(branchName string) (string, error) {
return c.OSCommand.RunCommandWithOutput("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branchName)
}
func includesString(list []string, a string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 %s", branchName))
}
// GetCommits obtains the commits of the current branch
func (c *GitCommand) GetCommits() []Commit {
func (c *GitCommand) GetCommits() []*Commit {
pushables := c.GetCommitsToPush()
log := c.GetLog()
commits := []Commit{}
commits := []*Commit{}
// now we can split it up and turn it into commits
lines := utils.SplitLines(log)
for _, line := range lines {
for _, line := range utils.SplitLines(log) {
splitLine := strings.Split(line, " ")
sha := splitLine[0]
pushed := includesString(pushables, sha)
commits = append(commits, Commit{
_, pushed := pushables[sha]
commits = append(commits, &Commit{
Sha: sha,
Name: strings.Join(splitLine[1:], " "),
Pushed: pushed,
@ -505,6 +494,7 @@ func (c *GitCommand) GetLog() string {
// assume if there is an error there are no commits yet for this branch
return ""
}
return result
}
@ -523,7 +513,7 @@ func (c *GitCommand) Show(sha string) string {
}
// Diff returns the diff of a file
func (c *GitCommand) Diff(file File) string {
func (c *GitCommand) Diff(file *File) string {
cachedArg := ""
fileName := c.OSCommand.Quote(file.Name)
if file.HasStagedChanges && !file.HasUnstagedChanges {