summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/commands/commit.go6
-rw-r--r--pkg/commands/git.go59
-rw-r--r--pkg/commands/git_test.go182
-rw-r--r--pkg/gui/commits_panel.go10
4 files changed, 245 insertions, 12 deletions
diff --git a/pkg/commands/commit.go b/pkg/commands/commit.go
index 37c3e9525..54e94ef60 100644
--- a/pkg/commands/commit.go
+++ b/pkg/commands/commit.go
@@ -9,17 +9,21 @@ type Commit struct {
Sha string
Name string
Pushed bool
+ Merged bool
DisplayString string
}
func (c *Commit) GetDisplayStrings() []string {
red := color.New(color.FgRed)
- yellow := color.New(color.FgYellow)
+ yellow := color.New(color.FgGreen)
+ green := color.New(color.FgYellow)
white := color.New(color.FgWhite)
shaColor := yellow
if c.Pushed {
shaColor = red
+ } else if !c.Merged {
+ shaColor = green
}
return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)}
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 87ee69c8c..c7de19b3a 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -267,6 +267,14 @@ func (c *GitCommand) NewBranch(name string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -b %s", name))
}
+func (c *GitCommand) CurrentBranchName() (string, error) {
+ output, err := c.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
+ if err != nil {
+ return "", err
+ }
+ return utils.TrimTrailingNewline(output), nil
+}
+
// DeleteBranch delete branch
func (c *GitCommand) DeleteBranch(branch string, force bool) error {
command := "git branch -d"
@@ -464,24 +472,63 @@ 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
+ c.Log.Error(err)
+ }
+ return output, nil
+}
+
// GetCommits obtains the commits of the current branch
-func (c *GitCommand) GetCommits() []*Commit {
+func (c *GitCommand) GetCommits() ([]*Commit, error) {
pushables := c.GetCommitsToPush()
log := c.GetLog()
- commits := []*Commit{}
+
+ lines := utils.SplitLines(log)
+ commits := make([]*Commit, len(lines))
// now we can split it up and turn it into commits
- for _, line := range utils.SplitLines(log) {
+ for i, line := range lines {
splitLine := strings.Split(line, " ")
sha := splitLine[0]
_, pushed := pushables[sha]
- commits = append(commits, &Commit{
+ commits[i] = &Commit{
Sha: sha,
Name: strings.Join(splitLine[1:], " "),
Pushed: pushed,
DisplayString: strings.Join(splitLine, " "),
- })
+ }
+ }
+ 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
+ }
+ commits[i].Merged = passedAncestor
}
- return commits
+ return commits, nil
}
// GetLog gets the git log (currently limited to 30 commits for performance
diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go
index e5fd539b9..25820558a 100644
--- a/pkg/commands/git_test.go
+++ b/pkg/commands/git_test.go
@@ -1496,7 +1496,7 @@ func TestGitCommandGetCommits(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
- test func([]*Commit)
+ test func([]*Commit, error)
}
scenarios := []scenario{
@@ -1512,11 +1512,18 @@ func TestGitCommandGetCommits(t *testing.T) {
case "log":
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
return exec.Command("echo")
+ case "merge-base":
+ assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
+ return exec.Command("test")
+ case "symbolic-ref":
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("echo", "master")
}
return nil
},
- func(commits []*Commit) {
+ func(commits []*Commit, err error) {
+ assert.NoError(t, err)
assert.Len(t, commits, 0)
},
},
@@ -1532,28 +1539,65 @@ func TestGitCommandGetCommits(t *testing.T) {
case "log":
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2")
+ case "merge-base":
+ assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
+ return exec.Command("echo", "78976bc")
+ case "symbolic-ref":
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("echo", "master")
}
return nil
},
- func(commits []*Commit) {
+ func(commits []*Commit, err error) {
+ assert.NoError(t, err)
assert.Len(t, commits, 2)
assert.EqualValues(t, []*Commit{
{
Sha: "8a2bb0e",
Name: "commit 1",
Pushed: true,
+ Merged: false,
DisplayString: "8a2bb0e commit 1",
},
{
Sha: "78976bc",
Name: "commit 2",
Pushed: false,
+ Merged: true,
DisplayString: "78976bc commit 2",
},
}, commits)
},
},
+ {
+ "GetCommits bubbles up an error from setCommitMergedStatuses",
+ func(cmd string, args ...string) *exec.Cmd {
+ assert.EqualValues(t, "git", cmd)
+
+ switch args[0] {
+ case "rev-list":
+ assert.EqualValues(t, []string{"rev-list", "@{u}..head", "--abbrev-commit"}, args)
+ return exec.Command("echo", "8a2bb0e")
+ case "log":
+ assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
+ return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2")
+ case "merge-base":
+ assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
+ return exec.Command("echo", "78976bc")
+ case "symbolic-ref":
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ // here's where we are returning the error
+ return exec.Command("test")
+ }
+
+ return nil
+ },
+ func(commits []*Commit, err error) {
+ assert.Error(t, err)
+ assert.Len(t, commits, 0)
+ },
+ },
}
for _, s := range scenarios {
@@ -1668,3 +1712,135 @@ func TestGitCommandDiff(t *testing.T) {
})
}
}
+
+func TestGitCommandGetMergeBase(t *testing.T) {
+ type scenario struct {
+ testName string
+ command func(string, ...string) *exec.Cmd
+ test func(string, error)
+ }
+
+ scenarios := []scenario{
+ {
+ "swallows an error if the call to merge-base returns an error",
+ func(cmd string, args ...string) *exec.Cmd {
+ assert.EqualValues(t, "git", cmd)
+
+ switch args[0] {
+ case "symbolic-ref":
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("echo", "master")
+ case "merge-base":
+ assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
+ return exec.Command("test")
+ }
+ return nil
+ },
+ func(output string, err error) {
+ assert.NoError(t, err)
+ assert.EqualValues(t, "", output)
+ },
+ },
+ {
+ "returns the commit when master",
+ func(cmd string, args ...string) *exec.Cmd {
+ assert.EqualValues(t, "git", cmd)
+
+ switch args[0] {
+ case "symbolic-ref":
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("echo", "master")
+ case "merge-base":
+ assert.EqualValues(t, []string{"merge-base", "HEAD", "master"}, args)
+ return exec.Command("echo", "blah")
+ }
+ return nil
+ },
+ func(output string, err error) {
+ assert.NoError(t, err)
+ assert.Equal(t, "blah\n", output)
+ },
+ },
+ {
+ "checks against develop when a feature branch",
+ func(cmd string, args ...string) *exec.Cmd {
+ assert.EqualValues(t, "git", cmd)
+
+ switch args[0] {
+ case "symbolic-ref":
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("echo", "feature/test")
+ case "merge-base":
+ assert.EqualValues(t, []string{"merge-base", "HEAD", "develop"}, args)
+ return exec.Command("echo", "blah")
+ }
+ return nil
+ },
+ func(output string, err error) {
+ assert.NoError(t, err)
+ assert.Equal(t, "blah\n", output)
+ },
+ },
+ {
+ "bubbles up error if there is one",
+ func(cmd string, args ...string) *exec.Cmd {
+ return exec.Command("test")
+ },
+ func(output string, err error) {
+ assert.Error(t, err)
+ assert.Equal(t, "", output)
+ },
+ },
+ }
+
+ for _, s := range scenarios {
+ t.Run(s.testName, func(t *testing.T) {
+ gitCmd := newDummyGitCommand()
+ gitCmd.OSCommand.command = s.command
+ s.test(gitCmd.getMergeBase())
+ })
+ }
+}
+
+func TestGitCommandCurrentBranchName(t *testing.T) {
+ type scenario struct {
+ testName string
+ command func(string, ...string) *exec.Cmd
+ test func(string, error)
+ }
+
+ scenarios := []scenario{
+ {
+ "says we are on the master branch if we are",
+ func(cmd string, args ...string) *exec.Cmd {
+ assert.Equal(t, "git", cmd)
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("echo", "master")
+ },
+ func(output string, err error) {
+ assert.NoError(t, err)
+ assert.EqualValues(t, "master", output)
+ },
+ },
+ {
+ "bubbles up error if there is one",
+ func(cmd string, args ...string) *exec.Cmd {
+ assert.Equal(t, "git", cmd)
+ assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args)
+ return exec.Command("test")
+ },
+ func(output string, err error) {
+ assert.Error(t, err)
+ assert.EqualValues(t, "", output)
+ },
+ },
+ }
+
+ for _, s := range scenarios {
+ t.Run(s.testName, func(t *testing.T) {
+ gitCmd := newDummyGitCommand()
+ gitCmd.OSCommand.command = s.command
+ s.test(gitCmd.CurrentBranchName())
+ })
+ }
+}
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 91bb334ff..ee7f191a7 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -11,13 +11,19 @@ import (
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
g.Update(func(*gocui.Gui) error {
- gui.State.Commits = gui.GitCommand.GetCommits()
+ commits, err := gui.GitCommand.GetCommits()
+ if err != nil {
+ return err
+ }
+
+ gui.State.Commits = commits
v, err := g.View("commits")
if err != nil {
- panic(err)
+ return err
}
v.Clear()
+
list, err := utils.RenderList(gui.State.Commits)
if err != nil {
return err