summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2019-02-19 09:18:30 +1100
committerJesse Duffield <jessedduffield@gmail.com>2019-02-19 09:18:30 +1100
commitdcc7855fd0f04d68fa809d586ac7f45a534f7b25 (patch)
treed2da3fbae5460f220dd5ff9b204d28704fade1ec
parenta8e22ed82f39f514a4166675cb9dde0d3cb49f5c (diff)
pull commit list builder functions into their own builder struct
-rw-r--r--pkg/commands/git.go239
-rw-r--r--pkg/git/branch_list_builder.go3
-rw-r--r--pkg/git/commit_list_builder.go275
-rw-r--r--pkg/gui/commits_panel.go7
4 files changed, 284 insertions, 240 deletions
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 5047ab114..e89551fe3 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -2,16 +2,12 @@ package commands
import (
"fmt"
- "io/ioutil"
"os"
"os/exec"
- "path/filepath"
- "regexp"
"strings"
"github.com/mgutz/str"
- "github.com/fatih/color"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/config"
@@ -250,21 +246,6 @@ func (c *GitCommand) GetCommitDifferences(from, to string) (string, string) {
return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount)
}
-// GetUnpushedCommits Returns the sha's of the commits that have not yet been pushed
-// to the remote branch of the current branch, a map is returned to ease look up
-func (c *GitCommand) GetUnpushedCommits() map[string]bool {
- pushables := map[string]bool{}
- o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --abbrev-commit")
- if err != nil {
- return pushables
- }
- for _, p := range utils.SplitLines(o) {
- pushables[p] = true
- }
-
- return pushables
-}
-
// RenameCommit renames the topmost commit with the given name
func (c *GitCommand) RenameCommit(name string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git commit --allow-empty --amend -m %s", c.OSCommand.Quote(name)))
@@ -527,226 +508,6 @@ func (c *GitCommand) GetBranchGraph(branchName string) (string, error) {
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 %s", branchName))
}
-func (c *GitCommand) getMergeBase() (string, error) {
- currentBranch, err := c.CurrentBranchName()
- if err != nil {
- return "", err
- }
-
- baseBranch := "master"
- if strings.HasPrefix(currentBranch, "feature/") {
- baseBranch = "develop"
- }
-
- output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git merge-base HEAD %s", baseBranch))
- if err != nil {
- // swallowing error because it's not a big deal; probably because there are no commits yet
- }
- return output, nil
-}
-
-// GetRebasingCommits obtains the commits that we're in the process of rebasing
-func (c *GitCommand) GetRebasingCommits() ([]*Commit, error) {
- rebaseMode, err := c.RebaseMode()
- if err != nil {
- return nil, err
- }
- switch rebaseMode {
- case "normal":
- return c.GetNormalRebasingCommits()
- case "interactive":
- return c.GetInteractiveRebasingCommits()
- default:
- return nil, nil
- }
-}
-
-func (c *GitCommand) GetNormalRebasingCommits() ([]*Commit, error) {
- rewrittenCount := 0
- bytesContent, err := ioutil.ReadFile(".git/rebase-apply/rewritten")
- if err == nil {
- content := string(bytesContent)
- rewrittenCount = len(strings.Split(content, "\n"))
- }
-
- // we know we're rebasing, so lets get all the files whose names have numbers
- commits := []*Commit{}
- err = filepath.Walk(".git/rebase-apply", func(path string, f os.FileInfo, err error) error {
- if rewrittenCount > 0 {
- rewrittenCount -= 1
- return nil
- }
- if err != nil {
- return err
- }
- re := regexp.MustCompile(`^\d+$`)
- if !re.MatchString(f.Name()) {
- return nil
- }
- bytesContent, err := ioutil.ReadFile(path)
- if err != nil {
- return err
- }
- content := string(bytesContent)
- commit, err := c.CommitFromPatch(content)
- if err != nil {
- return err
- }
- commits = append([]*Commit{commit}, commits...)
- return nil
- })
- if err != nil {
- return nil, err
- }
-
- return commits, nil
-}
-
-// git-rebase-todo example:
-// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
-// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
-
-// git-rebase-todo.backup example:
-// pick 49cbba374296938ea86bbd4bf4fee2f6ba5cccf6 third commit on master
-// pick ac446ae94ee560bdb8d1d057278657b251aaef17 blah commit on master
-// pick afb893148791a2fbd8091aeb81deba4930c73031 fourth commit on master
-
-// GetInteractiveRebasingCommits takes our git-rebase-todo and our git-rebase-todo.backup files
-// and extracts out the sha and names of commits that we still have to go
-// in the rebase:
-func (c *GitCommand) GetInteractiveRebasingCommits() ([]*Commit, error) {
- bytesContent, err := ioutil.ReadFile(".git/rebase-merge/git-rebase-todo")
- var content []string
- if err == nil {
- content = strings.Split(string(bytesContent), "\n")
- if len(content) > 0 && content[len(content)-1] == "" {
- content = content[0 : len(content)-1]
- }
- }
-
- // for each of them, grab the matching commit name in the backup
- bytesContent, err = ioutil.ReadFile(".git/rebase-merge/git-rebase-todo.backup")
- var backupContent []string
- if err == nil {
- backupContent = strings.Split(string(bytesContent), "\n")
- }
-
- commits := []*Commit{}
- for _, todoLine := range content {
- commit := c.extractCommit(todoLine, backupContent)
- if commit != nil {
- commits = append([]*Commit{commit}, commits...)
- }
- }
-
- return commits, nil
-}
-
-func (c *GitCommand) extractCommit(todoLine string, backupContent []string) *Commit {
- for _, backupLine := range backupContent {
- split := strings.Split(todoLine, " ")
- prefix := strings.Join(split[0:2], " ")
- if strings.HasPrefix(backupLine, prefix) {
- return &Commit{
- Sha: split[2],
- Name: strings.TrimPrefix(backupLine, prefix+" "),
- Status: "rebasing",
- }
- }
- }
- return nil
-}
-
-// assuming the file starts like this:
-// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
-// From: Lazygit Tester <test@example.com>
-// Date: Wed, 5 Dec 2018 21:03:23 +1100
-// Subject: second commit on master
-func (c *GitCommand) CommitFromPatch(content string) (*Commit, error) {
- lines := strings.Split(content, "\n")
- sha := strings.Split(lines[0], " ")[1][0:7]
- name := strings.TrimPrefix(lines[3], "Subject: ")
- return &Commit{
- Sha: sha,
- Name: name,
- Status: "rebasing",
- }, nil
-}
-
-// GetCommits obtains the commits of the current branch
-func (c *GitCommand) GetCommits() ([]*Commit, error) {
- commits := []*Commit{}
- // here we want to also prepend the commits that we're in the process of rebasing
- rebasingCommits, err := c.GetRebasingCommits()
- if err != nil {
- return nil, err
- }
- if len(rebasingCommits) > 0 {
- commits = append(commits, rebasingCommits...)
- }
-
- unpushedCommits := c.GetUnpushedCommits()
- log := c.GetLog()
-
- // now we can split it up and turn it into commits
- for _, line := range utils.SplitLines(log) {
- splitLine := strings.Split(line, " ")
- sha := splitLine[0]
- _, unpushed := unpushedCommits[sha]
- status := map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
- commits = append(commits, &Commit{
- Sha: sha,
- Name: strings.Join(splitLine[1:], " "),
- Status: status,
- DisplayString: strings.Join(splitLine, " "),
- })
- }
- if len(rebasingCommits) > 0 {
- currentCommit := commits[len(rebasingCommits)]
- blue := color.New(color.FgYellow)
- youAreHere := blue.Sprint("<-- YOU ARE HERE ---")
- currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
- }
- return c.setCommitMergedStatuses(commits)
-}
-
-func (c *GitCommand) setCommitMergedStatuses(commits []*Commit) ([]*Commit, error) {
- ancestor, err := c.getMergeBase()
- if err != nil {
- return nil, err
- }
- if ancestor == "" {
- return commits, nil
- }
- passedAncestor := false
- for i, commit := range commits {
- if strings.HasPrefix(ancestor, commit.Sha) {
- passedAncestor = true
- }
- if commit.Status != "pushed" {
- continue
- }
- if passedAncestor {
- commits[i].Status = "merged"
- }
- }
- return commits, nil
-}
-
-// GetLog gets the git log (currently limited to 30 commits for performance
-// until we work out lazy loading
-func (c *GitCommand) GetLog() string {
- // currently limiting to 30 for performance reasons
- // TODO: add lazyloading when you scroll down
- result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30")
- if err != nil {
- // assume if there is an error there are no commits yet for this branch
- return ""
- }
-
- return result
-}
-
// Ignore adds a file to the gitignore for the repo
func (c *GitCommand) Ignore(filename string) error {
return c.OSCommand.AppendLineToFile(".gitignore", filename)
diff --git a/pkg/git/branch_list_builder.go b/pkg/git/branch_list_builder.go
index 60ec4e060..b96a8ec67 100644
--- a/pkg/git/branch_list_builder.go
+++ b/pkg/git/branch_list_builder.go
@@ -20,6 +20,9 @@ import (
// our safe branches, then add the remaining safe branches, ensuring uniqueness
// along the way
+// if we find out we need to use one of these functions in the git.go file, we
+// can just pull them out of here and put them there and then call them from in here
+
// BranchListBuilder returns a list of Branch objects for the current repo
type BranchListBuilder struct {
Log *logrus.Entry
diff --git a/pkg/git/commit_list_builder.go b/pkg/git/commit_list_builder.go
new file mode 100644
index 000000000..703dbd990
--- /dev/null
+++ b/pkg/git/commit_list_builder.go
@@ -0,0 +1,275 @@
+package git
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "github.com/fatih/color"
+ "github.com/jesseduffield/lazygit/pkg/commands"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+ "github.com/sirupsen/logrus"
+)
+
+// context:
+// here we get the commits from git log but format them to show whether they're
+// unpushed/pushed/merged into the base branch or not, or if they're yet to
+// be processed as part of a rebase (these won't appear in git log but we
+// grab them from the rebase-related files in the .git directory to show them
+
+// if we find out we need to use one of these functions in the git.go file, we
+// can just pull them out of here and put them there and then call them from in here
+
+// CommitListBuilder returns a list of Branch objects for the current repo
+type CommitListBuilder struct {
+ Log *logrus.Entry
+ GitCommand *commands.GitCommand
+ OSCommand *commands.OSCommand
+}
+
+// NewCommitListBuilder builds a new commit list builder
+func NewCommitListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand, osCommand *commands.OSCommand) (*CommitListBuilder, error) {
+ return &CommitListBuilder{
+ Log: log,
+ GitCommand: gitCommand,
+ OSCommand: osCommand,
+ }, nil
+}
+
+// GetCommits obtains the commits of the current branch
+func (c *CommitListBuilder) GetCommits() ([]*commands.Commit, error) {
+ commits := []*commands.Commit{}
+ // here we want to also prepend the commits that we're in the process of rebasing
+ rebasingCommits, err := c.getRebasingCommits()
+ if err != nil {
+ return nil, err
+ }
+ if len(rebasingCommits) > 0 {
+ commits = append(commits, rebasingCommits...)
+ }
+
+ unpushedCommits := c.getUnpushedCommits()
+ log := c.getLog()
+
+ // now we can split it up and turn it into commits
+ for _, line := range utils.SplitLines(log) {
+ splitLine := strings.Split(line, " ")
+ sha := splitLine[0]
+ _, unpushed := unpushedCommits[sha]
+ status := map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
+ commits = append(commits, &commands.Commit{
+ Sha: sha,
+ Name: strings.Join(splitLine[1:], " "),
+ Status: status,
+ DisplayString: strings.Join(splitLine, " "),
+ })
+ }
+ if len(rebasingCommits) > 0 {
+ currentCommit := commits[len(rebasingCommits)]
+ blue := color.New(color.FgYellow)
+ youAreHere := blue.Sprint("<-- YOU ARE HERE ---")
+ currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
+ }
+ return c.setCommitMergedStatuses(commits)
+}
+
+// getRebasingCommits obtains the commits that we're in the process of rebasing
+func (c *CommitListBuilder) getRebasingCommits() ([]*commands.Commit, error) {
+ rebaseMode, err := c.GitCommand.RebaseMode()
+ if err != nil {
+ return nil, err
+ }
+ switch rebaseMode {
+ case "normal":
+ return c.getNormalRebasingCommits()
+ case "interactive":
+ return c.getInteractiveRebasingCommits()
+ default:
+ return nil, nil
+ }
+}
+
+func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, error) {
+ rewrittenCount := 0
+ bytesContent, err := ioutil.ReadFile(".git/rebase-apply/rewritten")
+ if err == nil {
+ content := string(bytesContent)
+ rewrittenCount = len(strings.Split(content, "\n"))
+ }
+
+ // we know we're rebasing, so lets get all the files whose names have numbers
+ commits := []*commands.Commit{}
+ err = filepath.Walk(".git/rebase-apply", func(path string, f os.FileInfo, err error) error {
+ if rewrittenCount > 0 {
+ rewrittenCount--
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ re := regexp.MustCompile(`^\d+$`)
+ if !re.MatchString(f.Name()) {
+ return nil
+ }
+ bytesContent, err := ioutil.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ content := string(bytesContent)
+ commit, err := c.commitFromPatch(content)
+ if err != nil {
+ return err
+ }
+ commits = append([]*commands.Commit{commit}, commits...)
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return commits, nil
+}
+
+// git-rebase-todo example:
+// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
+// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
+
+// git-rebase-todo.backup example:
+// pick 49cbba374296938ea86bbd4bf4fee2f6ba5cccf6 third commit on master
+// pick ac446ae94ee560bdb8d1d057278657b251aaef17 blah commit on master
+// pick afb893148791a2fbd8091aeb81deba4930c73031 fourth commit on master
+
+// getInteractiveRebasingCommits takes our git-rebase-todo and our git-rebase-todo.backup files
+// and extracts out the sha and names of commits that we still have to go
+// in the rebase:
+func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*commands.Commit, error) {
+ bytesContent, err := ioutil.ReadFile(".git/rebase-merge/git-rebase-todo")
+ var content []string
+ if err == nil {
+ content = strings.Split(string(bytesContent), "\n")
+ if len(content) > 0 && content[len(content)-1] == "" {
+ content = content[0 : len(content)-1]
+ }
+ }
+
+ // for each of them, grab the matching commit name in the backup
+ bytesContent, err = ioutil.ReadFile(".git/rebase-merge/git-rebase-todo.backup")
+ var backupContent []string
+ if err == nil {
+ backupContent = strings.Split(string(bytesContent), "\n")
+ }
+
+ commits := []*commands.Commit{}
+ for _, todoLine := range content {
+ commit := c.extractCommit(todoLine, backupContent)
+ if commit != nil {
+ commits = append([]*commands.Commit{commit}, commits...)
+ }
+ }
+
+ return commits, nil
+}
+
+func (c *CommitListBuilder) extractCommit(todoLine string, backupContent []string) *commands.Commit {
+ for _, backupLine := range backupContent {
+ split := strings.Split(todoLine, " ")
+ prefix := strings.Join(split[0:2], " ")
+ if strings.HasPrefix(backupLine, prefix) {
+ return &commands.Commit{
+ Sha: split[2],
+ Name: strings.TrimPrefix(backupLine, prefix+" "),
+ Status: "rebasing",
+ }
+ }
+ }
+ return nil
+}
+
+// assuming the file starts like this:
+// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
+// From: Lazygit Tester <test@example.com>
+// Date: Wed, 5 Dec 2018 21:03:23 +1100
+// Subject: second commit on master
+func (c *CommitListBuilder) commitFromPatch(content string) (*commands.Commit, error) {
+ lines := strings.Split(content, "\n")
+ sha := strings.Split(lines[0], " ")[1][0:7]
+ name := strings.TrimPrefix(lines[3], "Subject: ")
+ return &commands.Commit{
+ Sha: sha,
+ Name: name,
+ Status: "rebasing",
+ }, nil
+}
+
+func (c *CommitListBuilder) setCommitMergedStatuses(commits []*commands.Commit) ([]*commands.Commit, error) {
+ ancestor, err := c.getMergeBase()
+ if err != nil {
+ return nil, err
+ }
+ if ancestor == "" {
+ return commits, nil
+ }
+ passedAncestor := false
+ for i, commit := range commits {
+ if strings.HasPrefix(ancestor, commit.Sha) {
+ passedAncestor = true
+ }
+ if commit.Status != "pushed" {
+ continue
+ }
+ if passedAncestor {
+ commits[i].Status = "merged"
+ }
+ }
+ return commits, nil
+}
+
+func (c *CommitListBuilder) getMergeBase() (string, error) {
+ currentBranch, err := c.GitCommand.CurrentBranchName()
+ if err != nil {
+ return "", err
+ }
+
+ baseBranch := "master"
+ if strings.HasPrefix(currentBranch, "feature/") {
+ baseBranch = "develop"
+ }
+
+ output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git merge-base HEAD %s", baseBranch))
+ if err != nil {
+ // swallowing error because it's not a big deal; probably because there are no commits yet
+ }
+ return output, nil
+}
+
+// getUnpushedCommits Returns the sha's of the commits that have not yet been pushed
+// to the remote branch of the current branch, a map is returned to ease look up
+func (c *CommitListBuilder) getUnpushedCommits() map[string]bool {
+ pushables := map[string]bool{}
+ o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --abbrev-commit")
+ if err != nil {
+ return pushables
+ }
+ for _, p := range utils.SplitLines(o) {
+ pushables[p] = true
+ }
+
+ return pushables
+}
+
+// getLog gets the git log (currently limited to 30 commits for performance
+// until we work out lazy loading
+func (c *CommitListBuilder) getLog() string {
+ // currently limiting to 30 for performance reasons
+ // TODO: add lazyloading when you scroll down
+ result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30")
+ if err != nil {
+ // assume if there is an error there are no commits yet for this branch
+ return ""
+ }
+
+ return result
+}
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 374f7ccf0..f1d443731 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -7,6 +7,7 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
+ "github.com/jesseduffield/lazygit/pkg/git"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -39,7 +40,11 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
g.Update(func(*gocui.Gui) error {
- commits, err := gui.GitCommand.GetCommits()
+ builder, err := git.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand)
+ if err != nil {
+ return err
+ }
+ commits, err := builder.GetCommits()
if err != nil {
return err
}