mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-31 22:22:14 +02:00
Merge pull request #290 from jesseduffield/feature/informative-commit-colors
Color merged and unmerged commits differently
This commit is contained in:
commit
b8b59baa27
@ -9,17 +9,21 @@ type Commit struct {
|
|||||||
Sha string
|
Sha string
|
||||||
Name string
|
Name string
|
||||||
Pushed bool
|
Pushed bool
|
||||||
|
Merged bool
|
||||||
DisplayString string
|
DisplayString string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commit) GetDisplayStrings() []string {
|
func (c *Commit) GetDisplayStrings() []string {
|
||||||
red := color.New(color.FgRed)
|
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)
|
white := color.New(color.FgWhite)
|
||||||
|
|
||||||
shaColor := yellow
|
shaColor := yellow
|
||||||
if c.Pushed {
|
if c.Pushed {
|
||||||
shaColor = red
|
shaColor = red
|
||||||
|
} else if !c.Merged {
|
||||||
|
shaColor = green
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)}
|
return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)}
|
||||||
|
@ -267,6 +267,14 @@ func (c *GitCommand) NewBranch(name string) error {
|
|||||||
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -b %s", name))
|
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
|
// DeleteBranch delete branch
|
||||||
func (c *GitCommand) DeleteBranch(branch string, force bool) error {
|
func (c *GitCommand) DeleteBranch(branch string, force bool) error {
|
||||||
command := "git branch -d"
|
command := "git branch -d"
|
||||||
@ -460,24 +468,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))
|
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
|
// GetCommits obtains the commits of the current branch
|
||||||
func (c *GitCommand) GetCommits() []*Commit {
|
func (c *GitCommand) GetCommits() ([]*Commit, error) {
|
||||||
pushables := c.GetCommitsToPush()
|
pushables := c.GetCommitsToPush()
|
||||||
log := c.GetLog()
|
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
|
// 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, " ")
|
splitLine := strings.Split(line, " ")
|
||||||
sha := splitLine[0]
|
sha := splitLine[0]
|
||||||
_, pushed := pushables[sha]
|
_, pushed := pushables[sha]
|
||||||
commits = append(commits, &Commit{
|
commits[i] = &Commit{
|
||||||
Sha: sha,
|
Sha: sha,
|
||||||
Name: strings.Join(splitLine[1:], " "),
|
Name: strings.Join(splitLine[1:], " "),
|
||||||
Pushed: pushed,
|
Pushed: pushed,
|
||||||
DisplayString: strings.Join(splitLine, " "),
|
DisplayString: strings.Join(splitLine, " "),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
return commits
|
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, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLog gets the git log (currently limited to 30 commits for performance
|
// GetLog gets the git log (currently limited to 30 commits for performance
|
||||||
|
@ -1496,7 +1496,7 @@ func TestGitCommandGetCommits(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
test func([]*Commit)
|
test func([]*Commit, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
@ -1512,11 +1512,18 @@ func TestGitCommandGetCommits(t *testing.T) {
|
|||||||
case "log":
|
case "log":
|
||||||
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
|
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
|
||||||
return exec.Command("echo")
|
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
|
return nil
|
||||||
},
|
},
|
||||||
func(commits []*Commit) {
|
func(commits []*Commit, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Len(t, commits, 0)
|
assert.Len(t, commits, 0)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1532,28 +1539,65 @@ func TestGitCommandGetCommits(t *testing.T) {
|
|||||||
case "log":
|
case "log":
|
||||||
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
|
assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
|
||||||
return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2")
|
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
|
return nil
|
||||||
},
|
},
|
||||||
func(commits []*Commit) {
|
func(commits []*Commit, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Len(t, commits, 2)
|
assert.Len(t, commits, 2)
|
||||||
assert.EqualValues(t, []*Commit{
|
assert.EqualValues(t, []*Commit{
|
||||||
{
|
{
|
||||||
Sha: "8a2bb0e",
|
Sha: "8a2bb0e",
|
||||||
Name: "commit 1",
|
Name: "commit 1",
|
||||||
Pushed: true,
|
Pushed: true,
|
||||||
|
Merged: false,
|
||||||
DisplayString: "8a2bb0e commit 1",
|
DisplayString: "8a2bb0e commit 1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Sha: "78976bc",
|
Sha: "78976bc",
|
||||||
Name: "commit 2",
|
Name: "commit 2",
|
||||||
Pushed: false,
|
Pushed: false,
|
||||||
|
Merged: true,
|
||||||
DisplayString: "78976bc commit 2",
|
DisplayString: "78976bc commit 2",
|
||||||
},
|
},
|
||||||
}, commits)
|
}, 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 {
|
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())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,13 +11,19 @@ import (
|
|||||||
|
|
||||||
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||||
g.Update(func(*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")
|
v, err := g.View("commits")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
|
|
||||||
list, err := utils.RenderList(gui.State.Commits)
|
list, err := utils.RenderList(gui.State.Commits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
Loading…
x
Reference in New Issue
Block a user