diff options
-rw-r--r-- | pkg/commands/git.go | 158 | ||||
-rw-r--r-- | pkg/commands/gitcommands.go | 74 | ||||
-rw-r--r-- | pkg/gui/panels/branches_panel.go | 2 | ||||
-rw-r--r-- | pkg/gui/panels/commits_panel.go | 2 | ||||
-rw-r--r-- | pkg/gui/panels/files_panel.go | 2 | ||||
-rw-r--r-- | pkg/gui/panels/status_panel.go | 2 |
6 files changed, 159 insertions, 81 deletions
diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 17855b98e..23a7d90f9 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -3,17 +3,23 @@ package commands import ( "fmt" "os" + "strings" + "time" "github.com/Sirupsen/logrus" - git "gopkg.in/src-d/go-git.v4" + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/git" + gitconfig "github.com/tcnksm/go-gitconfig" + gogit "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing/object" ) // GitCommand is our main git interface type GitCommand struct { Log *logrus.Logger OSCommand *OSCommand - Worktree *git.Worktree - Repo *git.Repository + Worktree *gogit.Worktree + Repo *gogit.Repository } // NewGitCommand it runs git commands @@ -32,6 +38,7 @@ func (c *GitCommand) SetupGit() { c.setupWorktree() } +// GitIgnore adds a file to the .gitignore of the repo func (c *GitCommand) GitIgnore(filename string) { if _, err := c.OSCommand.RunDirectCommand("echo '" + filename + "' >> .gitignore"); err != nil { panic(err) @@ -55,7 +62,6 @@ func (c *GitCommand) navigateToRepoRootDirectory() { } func (c *GitCommand) setupWorktree() { - var err error r, err := git.PlainOpen(".") if err != nil { panic(err) @@ -63,9 +69,151 @@ func (c *GitCommand) setupWorktree() { c.Repo = r w, err := r.Worktree() - c.Worktree = w if err != nil { panic(err) } c.Worktree = w } + +// ResetHard does the equivalent of `git reset --hard HEAD` +func (c *GitCommand) ResetHard() error { + return c.Worktree.Reset(&gogit.ResetOptions{Mode: git.HardReset}) +} + +// UpstreamDifferenceCount checks how many pushables/pullables there are for the +// current branch +func (c *GitCommand) UpstreamDifferenceCount() (string, string) { + pushableCount, err := c.OSCommand.runDirectCommand("git rev-list @{u}..head --count") + if err != nil { + return "?", "?" + } + pullableCount, err := c.OSCommand.runDirectCommand("git rev-list head..@{u} --count") + if err != nil { + return "?", "?" + } + return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount) +} + +// 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.runDirectCommand("git rev-list @{u}..head --abbrev-commit") + if err != nil { + return make([]string, 0) + } + return splitLines(pushables) +} + +// GetGitBranches returns a list of branches for the current repo, with recency +// values stored against those that are in the reflog +func (c *GitCommand) GetGitBranches() []Branch { + builder := git.newBranchListBuilder() + return builder.build() +} + +// BranchIncluded states whether a branch is included in a list of branches, +// with a case insensitive comparison on name +func (c *GitCommand) BranchIncluded(branchName string, branches []Branch) bool { + for _, existingBranch := range branches { + if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) { + return true + } + } + return false +} + +// RenameCommit renames the topmost commit with the given name +func (c *GitCommand) RenameCommit(name string) (string, error) { + return c.OSCommand.runDirectCommand("git commit --allow-empty --amend -m \"" + name + "\"") +} + +func (c *GitCommand) Fetch() (string, error) { + return c.OSCommand.runDirectCommand("git fetch") +} + +func (c *GitCommand) ResetToCommit(sha string) (string, error) { + return c.OSCommand.runDirectCommand("git reset " + sha) +} + +func (c *GitCommand) NewBranch(name string) (string, error) { + return c.OSCommand.runDirectCommand("git checkout -b " + name) +} + +func (c *GitCommand) DeleteBranch(branch string) (string, error) { + return runCommand("git branch -d " + branch) +} + +func (c *GitCommand) ListStash() (string, error) { + return c.OSCommand.runDirectCommand("git stash list") +} + +func (c *GitCommand) Merge(branchName string) (string, error) { + return c.OSCommand.runDirectCommand("git merge --no-edit " + branchName) +} + +func (c *GitCommand) AbortMerge() (string, error) { + return c.OSCommand.runDirectCommand("git merge --abort") +} + +func gitCommit(g *gocui.Gui, message string) (string, error) { + gpgsign, _ := gitconfig.Global("commit.gpgsign") + if gpgsign != "" { + runSubProcess(g, "git", "commit") + return "", nil + } + userName, err := gitconfig.Username() + if userName == "" { + return "", errNoUsername + } + userEmail, err := gitconfig.Email() + _, err = w.Commit(message, &git.CommitOptions{ + Author: &object.Signature{ + Name: userName, + Email: userEmail, + When: time.Now(), + }, + }) + if err != nil { + return err.Error(), err + } + return "", nil +} + +func gitPull() (string, error) { + return runDirectCommand("git pull --no-edit") +} + +func gitPush() (string, error) { + return runDirectCommand("git push -u origin " + state.Branches[0].Name) +} + +func gitSquashPreviousTwoCommits(message string) (string, error) { + return runDirectCommand("git reset --soft HEAD^ && git commit --amend -m \"" + message + "\"") +} + +func gitSquashFixupCommit(branchName string, shaValue string) (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, + } + ret := "" + for _, command := range commands { + devLog(command) + output, err := runDirectCommand(command) + ret += output + if err != nil { + devLog(ret) + break + } + } + if err != nil { + // We are already in an error state here so we're just going to append + // the output of these commands + ret += runDirectCommandIgnoringError("git branch -d " + shaValue) + ret += runDirectCommandIgnoringError("git checkout " + branchName) + } + return ret, err +} diff --git a/pkg/commands/gitcommands.go b/pkg/commands/gitcommands.go index bdc23fef2..f6eb4a357 100644 --- a/pkg/commands/gitcommands.go +++ b/pkg/commands/gitcommands.go @@ -1,4 +1,4 @@ -package main +package commands import ( @@ -279,7 +279,7 @@ func verifyInGitRepo() { } func getCommits() []Commit { - pushables := gitCommitsToPush() + pushables := git.GetCommitsToPush() log := getLog() commits := make([]Commit, 0) // now we can split it up and turn it into commits @@ -447,73 +447,3 @@ func gitSquashFixupCommit(branchName string, shaValue string) (string, error) { } return ret, err } - -func gitRenameCommit(message string) (string, error) { - return runDirectCommand("git commit --allow-empty --amend -m \"" + message + "\"") -} - -func gitFetch() (string, error) { - return runDirectCommand("git fetch") -} - -func gitResetToCommit(sha string) (string, error) { - return runDirectCommand("git reset " + sha) -} - -func gitNewBranch(name string) (string, error) { - return runDirectCommand("git checkout -b " + name) -} - -func gitDeleteBranch(branch string) (string, error) { - return runCommand("git branch -d " + branch) -} - -func gitListStash() (string, error) { - return runDirectCommand("git stash list") -} - -func gitMerge(branchName string) (string, error) { - return runDirectCommand("git merge --no-edit " + branchName) -} - -func gitAbortMerge() (string, error) { - return runDirectCommand("git merge --abort") -} - -func gitUpstreamDifferenceCount() (string, string) { - pushableCount, err := runDirectCommand("git rev-list @{u}..head --count") - if err != nil { - return "?", "?" - } - pullableCount, err := runDirectCommand("git rev-list head..@{u} --count") - if err != nil { - return "?", "?" - } - return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount) -} - -func gitCommitsToPush() []string { - pushables, err := runDirectCommand("git rev-list @{u}..head --abbrev-commit") - if err != nil { - return make([]string, 0) - } - return splitLines(pushables) -} - -func getGitBranches() []Branch { - builder := newBranchListBuilder() - return builder.build() -} - -func branchIncluded(branchName string, branches []Branch) bool { - for _, existingBranch := range branches { - if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) { - return true - } - } - return false -} - -func gitResetHard() error { - return w.Reset(&git.ResetOptions{Mode: git.HardReset}) -} diff --git a/pkg/gui/panels/branches_panel.go b/pkg/gui/panels/branches_panel.go index a73d28bb3..82b8542ad 100644 --- a/pkg/gui/panels/branches_panel.go +++ b/pkg/gui/panels/branches_panel.go @@ -123,7 +123,7 @@ func refreshBranches(g *gocui.Gui) error { if err != nil { panic(err) } - state.Branches = getGitBranches() + state.Branches = git.GetGitBranches() v.Clear() for _, branch := range state.Branches { fmt.Fprintln(v, branch.getDisplayString()) diff --git a/pkg/gui/panels/commits_panel.go b/pkg/gui/panels/commits_panel.go index 1a9976ffe..5b02ae115 100644 --- a/pkg/gui/panels/commits_panel.go +++ b/pkg/gui/panels/commits_panel.go @@ -148,7 +148,7 @@ func handleRenameCommit(g *gocui.Gui, v *gocui.View) error { return createErrorPanel(g, "Can only rename topmost commit") } createPromptPanel(g, v, "Rename Commit", func(g *gocui.Gui, v *gocui.View) error { - if output, err := gitRenameCommit(v.Buffer()); err != nil { + if output, err := git.RenameCommit(v.Buffer()); err != nil { return createErrorPanel(g, output) } if err := refreshCommits(g); err != nil { diff --git a/pkg/gui/panels/files_panel.go b/pkg/gui/panels/files_panel.go index 4de8caca7..fb4fc8196 100644 --- a/pkg/gui/panels/files_panel.go +++ b/pkg/gui/panels/files_panel.go @@ -354,7 +354,7 @@ func handleAbortMerge(g *gocui.Gui, v *gocui.View) error { func handleResetHard(g *gocui.Gui, v *gocui.View) error { return createConfirmationPanel(g, v, "Clear file panel", "Are you sure you want `reset --hard HEAD`? You may lose changes", func(g *gocui.Gui, v *gocui.View) error { - if err := gitResetHard(); err != nil { + if err := git.ResetHard(); err != nil { createErrorPanel(g, err.Error()) } return refreshFiles(g) diff --git a/pkg/gui/panels/status_panel.go b/pkg/gui/panels/status_panel.go index f3fcb8078..a58375ce0 100644 --- a/pkg/gui/panels/status_panel.go +++ b/pkg/gui/panels/status_panel.go @@ -17,7 +17,7 @@ func refreshStatus(g *gocui.Gui) error { // contents end up cleared g.Update(func(*gocui.Gui) error { v.Clear() - pushables, pullables := gitUpstreamDifferenceCount() + pushables, pullables := git.UpstreamDifferenceCount() fmt.Fprint(v, "↑"+pushables+"↓"+pullables) branches := state.Branches if err := updateHasMergeConflictStatus(); err != nil { |