1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-05-31 23:19:40 +02:00

Merge branch 'master' into feature/informative-commit-colors

This commit is contained in:
Jesse Duffield 2018-09-25 20:10:12 +10:00 committed by GitHub
commit 17d7bcdeaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 194 additions and 145 deletions

4
Gopkg.lock generated
View File

@ -189,11 +189,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:d6df25dee1274e63c25f84c257bc504359f975dedb791eec7a5b51992146bb76" digest = "1:66bb9b4a5abb704642fccba52a84a7f7feef2d9623f87b700e52a6695044723f"
name = "github.com/jesseduffield/gocui" name = "github.com/jesseduffield/gocui"
packages = ["."] packages = ["."]
pruneopts = "NUT" pruneopts = "NUT"
revision = "4fca348422d8b6136e801b222858204a35ee369a" revision = "03e26ff3f1de2c1bc2205113c3aba661312eee00"
[[projects]] [[projects]]
branch = "master" branch = "master"

View File

@ -121,6 +121,11 @@ For contributor discussion about things not better discussed here in the repo, j
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazygit/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M) [![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazygit/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M)
## Donate
If you would like to support the development of lazygit, please donate
[![Donate](https://d1iczxrky3cnb2.cloudfront.net/button-medium-blue.png)](https://donorbox.org/lazygit)
## Work in progress ## Work in progress
This is still a work in progress so there's still bugs to iron out and as this This is still a work in progress so there's still bugs to iron out and as this
is my first project in Go the code could no doubt use an increase in quality, is my first project in Go the code could no doubt use an increase in quality,

2
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63
github.com/jesseduffield/gocui v0.0.0-20180919095827-4fca348422d8 github.com/jesseduffield/gocui v0.0.0-20180921065632-03e26ff3f1de
github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1

View File

@ -527,26 +527,22 @@ func (c *GitCommand) Ignore(filename string) error {
} }
// Show shows the diff of a commit // Show shows the diff of a commit
func (c *GitCommand) Show(sha string) string { func (c *GitCommand) Show(sha string) (string, error) {
result, err := c.OSCommand.RunCommandWithOutput("git show --color " + sha) return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git show --color %s", sha))
if err != nil {
panic(err)
}
return result
} }
// Diff returns the diff of a file // Diff returns the diff of a file
func (c *GitCommand) Diff(file *File) string { func (c *GitCommand) Diff(file *File) string {
cachedArg := "" cachedArg := ""
trackedArg := "--"
fileName := c.OSCommand.Quote(file.Name) fileName := c.OSCommand.Quote(file.Name)
if file.HasStagedChanges && !file.HasUnstagedChanges { if file.HasStagedChanges && !file.HasUnstagedChanges {
cachedArg = "--cached" cachedArg = "--cached"
} }
trackedArg := "--"
if !file.Tracked && !file.HasStagedChanges { if !file.Tracked && !file.HasStagedChanges {
trackedArg = "--no-index /dev/null" trackedArg = "--no-index /dev/null"
} }
command := fmt.Sprintf("%s %s %s %s", "git diff --color ", cachedArg, trackedArg, fileName) command := fmt.Sprintf("git diff --color %s %s %s", cachedArg, trackedArg, fileName)
// for now we assume an error means the file was deleted // for now we assume an error means the file was deleted
s, _ := c.OSCommand.RunCommandWithOutput(command) s, _ := c.OSCommand.RunCommandWithOutput(command)

View File

@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/test"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
gogit "gopkg.in/src-d/go-git.v4" gogit "gopkg.in/src-d/go-git.v4"
@ -1421,6 +1420,19 @@ func TestGitCommandRemoveFile(t *testing.T) {
} }
} }
func TestGitCommandShow(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"show", "--color", "456abcde"}, args)
return exec.Command("echo")
}
_, err := gitCmd.Show("456abcde")
assert.NoError(t, err)
}
func TestGitCommandCheckout(t *testing.T) { func TestGitCommandCheckout(t *testing.T) {
type scenario struct { type scenario struct {
testName string testName string
@ -1553,97 +1565,106 @@ func TestGitCommandGetCommits(t *testing.T) {
} }
} }
func TestGitCommandDiff(t *testing.T) { func TestGitCommandGetLog(t *testing.T) {
gitCommand := newDummyGitCommand() type scenario struct {
assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) testName string
command func(string, ...string) *exec.Cmd
test func(string)
}
files := []*File{ scenarios := []scenario{
{ {
Name: "deleted_staged", "Retrieves logs",
HasStagedChanges: false, func(cmd string, args ...string) *exec.Cmd {
HasUnstagedChanges: true, assert.EqualValues(t, "git", cmd)
Tracked: true, assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
Deleted: true,
HasMergeConflicts: false, return exec.Command("echo", "6f0b32f commands/git : add GetCommits tests refactor\n9d9d775 circle : remove new line")
DisplayString: " D deleted_staged", },
func(output string) {
assert.EqualValues(t, "6f0b32f commands/git : add GetCommits tests refactor\n9d9d775 circle : remove new line\n", output)
},
}, },
{ {
Name: "file with space staged", "An error occurred when retrieving logs",
HasStagedChanges: true, func(cmd string, args ...string) *exec.Cmd {
HasUnstagedChanges: false, assert.EqualValues(t, "git", cmd)
Tracked: false, assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args)
Deleted: false, return exec.Command("test")
HasMergeConflicts: false, },
DisplayString: "A \"file with space staged\"", func(output string) {
}, assert.Empty(t, output)
{ },
Name: "file with space unstaged",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "?? file with space unstaged",
},
{
Name: "modified_unstaged",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: true,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "M modified_unstaged",
},
{
Name: "modified_staged",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: true,
Deleted: false,
HasMergeConflicts: false,
DisplayString: " M modified_staged",
},
{
Name: "renamed_before -> renamed_after",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: true,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "R renamed_before -> renamed_after",
},
{
Name: "untracked_unstaged",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "?? untracked_unstaged",
},
{
Name: "untracked_staged",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "A untracked_staged",
},
{
Name: "master",
HasStagedChanges: false,
HasUnstagedChanges: true,
Tracked: false,
Deleted: false,
HasMergeConflicts: false,
DisplayString: "?? master",
}, },
} }
for _, file := range files { for _, s := range scenarios {
t.Run(file.Name, func(t *testing.T) { t.Run(s.testName, func(t *testing.T) {
assert.NotContains(t, gitCommand.Diff(file), "error") gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = s.command
s.test(gitCmd.GetLog())
})
}
}
func TestGitCommandDiff(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
file *File
}
scenarios := []scenario{
{
"Default case",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color", "--", "test.txt"}, args)
return exec.Command("echo")
},
&File{
Name: "test.txt",
HasStagedChanges: false,
Tracked: true,
},
},
{
"All changes staged",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color", "--cached", "--", "test.txt"}, args)
return exec.Command("echo")
},
&File{
Name: "test.txt",
HasStagedChanges: true,
HasUnstagedChanges: false,
Tracked: true,
},
},
{
"File not tracked and file has no staged changes",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--color", "--no-index", "/dev/null", "test.txt"}, args)
return exec.Command("echo")
},
&File{
Name: "test.txt",
HasStagedChanges: false,
Tracked: false,
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = s.command
gitCmd.Diff(s.file)
}) })
} }
} }

View File

@ -37,6 +37,10 @@ func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
if _, err := g.SetViewOnTop("commitMessage"); err != nil {
return err
}
message := gui.Tr.TemplateLocalize( message := gui.Tr.TemplateLocalize(
"CloseConfirm", "CloseConfirm",
Teml{ Teml{

View File

@ -69,7 +69,10 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
} }
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch")) return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
} }
commitText := gui.GitCommand.Show(commit.Sha) commitText, err := gui.GitCommand.Show(commit.Sha)
if err != nil {
return err
}
return gui.renderString(g, "main", commitText) return gui.renderString(g, "main", commitText)
} }

View File

@ -2,6 +2,7 @@ package gui
import ( import (
"fmt" "fmt"
"strings"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
@ -51,14 +52,16 @@ func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
dashboardString := fmt.Sprintf( dashboardString := strings.Join(
"%s\n\n%s\n\n%s\n\n%s\n\n%s", []string{
lazygitTitle(), lazygitTitle(),
"Keybindings: https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md", "Copyright (c) 2018 Jesse Duffield",
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md", "Keybindings: https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md",
"Tutorial: https://www.youtube.com/watch?v=VDXvbHZYeKY", "Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues", "Tutorial: https://www.youtube.com/watch?v=VDXvbHZYeKY",
) "Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
"Buy Jesse a coffee: https://donorbox.org/lazygit",
}, "\n\n")
if err := gui.renderString(g, "main", dashboardString); err != nil { if err := gui.renderString(g, "main", dashboardString); err != nil {
return err return err

View File

@ -133,8 +133,15 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
}, },
) )
gui.Log.Info(message) gui.Log.Info(message)
gui.State.PreviousView = oldView.Name()
// second class panels should never have focus restored to them because
// once they lose focus they are effectively 'destroyed'
secondClassPanels := []string{"confirmation", "menu"}
if !utils.IncludesString(secondClassPanels, oldView.Name()) {
gui.State.PreviousView = oldView.Name()
}
} }
newView.Highlight = true newView.Highlight = true
message := gui.Tr.TemplateLocalize( message := gui.Tr.TemplateLocalize(
"newFocusedViewIs", "newFocusedViewIs",
@ -183,7 +190,7 @@ func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
} }
cx, cy := v.Cursor() cx, cy := v.Cursor()
ox, oy := v.Origin() ox, oy := v.Origin()
ly := len(v.BufferLines()) - 1 ly := v.LinesHeight() - 1
_, height := v.Size() _, height := v.Size()
maxY := height - 1 maxY := height - 1
@ -219,7 +226,7 @@ func (gui *Gui) correctCursor(v *gocui.View) error {
ox, oy := v.Origin() ox, oy := v.Origin()
_, height := v.Size() _, height := v.Size()
maxY := height - 1 maxY := height - 1
ly := len(v.BufferLines()) - 1 ly := v.LinesHeight() - 1
if oy+cy <= ly { if oy+cy <= ly {
return nil return nil
} }

View File

@ -204,3 +204,13 @@ func getDisplayStringArrays(displayables []Displayable) [][]string {
} }
return stringArrays return stringArrays
} }
// IncludesString if the list contains the string
func IncludesString(list []string, a string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}

View File

@ -376,3 +376,38 @@ func TestMin(t *testing.T) {
assert.EqualValues(t, s.expected, Min(s.a, s.b)) assert.EqualValues(t, s.expected, Min(s.a, s.b))
} }
} }
func TestIncludesString(t *testing.T) {
type scenario struct {
list []string
element string
expected bool
}
scenarios := []scenario{
{
[]string{"a", "b"},
"a",
true,
},
{
[]string{"a", "b"},
"c",
false,
},
{
[]string{"a", "b"},
"",
false,
},
{
[]string{""},
"",
true,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, IncludesString(s.list, s.element))
}
}

View File

@ -1,35 +0,0 @@
#!/bin/bash
set -ex; rm -rf repo; mkdir repo; cd repo
git init
git config user.email "test@example.com"
git config user.name "Lazygit Tester"
echo "deleted" > deleted_staged
echo "deleted_unstaged" > deleted_unstaged
echo "modified_staged" > modified_staged
echo "modified_unstaged" > modified_unstaged
echo "renamed" > renamed_before
git add .
git commit -m "files to delete"
rm deleted_staged
rm deleted_unstaged
rm renamed_before
echo "renamed" > renamed_after
echo "more" >> modified_staged
echo "more" >> modified_unstaged
echo "untracked_staged" > untracked_staged
echo "untracked_unstaged" > untracked_unstaged
echo "blah" > "file with space staged"
echo "blah" > "file with space unstaged"
echo "same name as branch" > master
git add deleted_staged
git add modified_staged
git add untracked_staged
git add "file with space staged"
git add renamed_before
git add renamed_after

View File

@ -447,8 +447,8 @@ func (v *View) ViewBufferLines() []string {
return lines return lines
} }
func (v *View) ViewLinesHeight() int { func (v *View) LinesHeight() int {
return len(v.viewLines) return len(v.lines)
} }
// ViewBuffer returns a string with the contents of the view's buffer that is // ViewBuffer returns a string with the contents of the view's buffer that is