1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-08-06 22:33:07 +02:00

Merge branch 'master' into https-ask-for-username-password

This commit is contained in:
Mark Kopenga
2018-12-06 08:31:12 +01:00
committed by GitHub
33 changed files with 1283 additions and 65 deletions

View File

@ -13,6 +13,7 @@ type Commit struct {
DisplayString string
}
// GetDisplayStrings is a function.
func (c *Commit) GetDisplayStrings() []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgGreen)

View File

@ -267,6 +267,7 @@ func (c *GitCommand) NewBranch(name string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -b %s", name))
}
// CurrentBranchName is a function.
func (c *GitCommand) CurrentBranchName() (string, error) {
branchName, err := c.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
if err != nil {
@ -490,7 +491,6 @@ func (c *GitCommand) getMergeBase() (string, error) {
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
}
@ -576,9 +576,10 @@ func (c *GitCommand) CheckRemoteBranchExists(branch *Branch) bool {
}
// Diff returns the diff of a file
func (c *GitCommand) Diff(file *File) string {
func (c *GitCommand) Diff(file *File, plain bool) string {
cachedArg := ""
trackedArg := "--"
colorArg := "--color"
fileName := c.OSCommand.Quote(file.Name)
if file.HasStagedChanges && !file.HasUnstagedChanges {
cachedArg = "--cached"
@ -586,9 +587,25 @@ func (c *GitCommand) Diff(file *File) string {
if !file.Tracked && !file.HasStagedChanges {
trackedArg = "--no-index /dev/null"
}
command := fmt.Sprintf("git diff --color %s %s %s", cachedArg, trackedArg, fileName)
if plain {
colorArg = ""
}
command := fmt.Sprintf("git diff %s %s %s %s", colorArg, cachedArg, trackedArg, fileName)
// for now we assume an error means the file was deleted
s, _ := c.OSCommand.RunCommandWithOutput(command)
return s
}
func (c *GitCommand) ApplyPatch(patch string) (string, error) {
filename, err := c.OSCommand.CreateTempFile("patch", patch)
if err != nil {
c.Log.Error(err)
return "", err
}
defer func() { _ = c.OSCommand.RemoveFile(filename) }()
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", filename))
}

View File

@ -23,26 +23,32 @@ type fileInfoMock struct {
sys interface{}
}
// Name is a function.
func (f fileInfoMock) Name() string {
return f.name
}
// Size is a function.
func (f fileInfoMock) Size() int64 {
return f.size
}
// Mode is a function.
func (f fileInfoMock) Mode() os.FileMode {
return f.fileMode
}
// ModTime is a function.
func (f fileInfoMock) ModTime() time.Time {
return f.fileModTime
}
// IsDir is a function.
func (f fileInfoMock) IsDir() bool {
return f.isDir
}
// Sys is a function.
func (f fileInfoMock) Sys() interface{} {
return f.sys
}
@ -64,6 +70,7 @@ func newDummyGitCommand() *GitCommand {
}
}
// TestVerifyInGitRepo is a function.
func TestVerifyInGitRepo(t *testing.T) {
type scenario struct {
testName string
@ -100,6 +107,7 @@ func TestVerifyInGitRepo(t *testing.T) {
}
}
// TestNavigateToRepoRootDirectory is a function.
func TestNavigateToRepoRootDirectory(t *testing.T) {
type scenario struct {
testName string
@ -156,6 +164,7 @@ func TestNavigateToRepoRootDirectory(t *testing.T) {
}
}
// TestSetupRepositoryAndWorktree is a function.
func TestSetupRepositoryAndWorktree(t *testing.T) {
type scenario struct {
testName string
@ -224,6 +233,7 @@ func TestSetupRepositoryAndWorktree(t *testing.T) {
}
}
// TestNewGitCommand is a function.
func TestNewGitCommand(t *testing.T) {
actual, err := os.Getwd()
assert.NoError(t, err)
@ -271,6 +281,7 @@ func TestNewGitCommand(t *testing.T) {
}
}
// TestGitCommandGetStashEntries is a function.
func TestGitCommandGetStashEntries(t *testing.T) {
type scenario struct {
testName string
@ -323,6 +334,7 @@ func TestGitCommandGetStashEntries(t *testing.T) {
}
}
// TestGitCommandGetStashEntryDiff is a function.
func TestGitCommandGetStashEntryDiff(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -337,6 +349,7 @@ func TestGitCommandGetStashEntryDiff(t *testing.T) {
assert.NoError(t, err)
}
// TestGitCommandGetStatusFiles is a function.
func TestGitCommandGetStatusFiles(t *testing.T) {
type scenario struct {
testName string
@ -423,6 +436,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
}
}
// TestGitCommandStashDo is a function.
func TestGitCommandStashDo(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -435,6 +449,7 @@ func TestGitCommandStashDo(t *testing.T) {
assert.NoError(t, gitCmd.StashDo(1, "drop"))
}
// TestGitCommandStashSave is a function.
func TestGitCommandStashSave(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -447,6 +462,7 @@ func TestGitCommandStashSave(t *testing.T) {
assert.NoError(t, gitCmd.StashSave("A stash message"))
}
// TestGitCommandCommitAmend is a function.
func TestGitCommandCommitAmend(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -460,6 +476,7 @@ func TestGitCommandCommitAmend(t *testing.T) {
assert.NoError(t, err)
}
// TestGitCommandMergeStatusFiles is a function.
func TestGitCommandMergeStatusFiles(t *testing.T) {
type scenario struct {
testName string
@ -540,6 +557,7 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
}
}
// TestGitCommandUpstreamDifferentCount is a function.
func TestGitCommandUpstreamDifferentCount(t *testing.T) {
type scenario struct {
testName string
@ -597,6 +615,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) {
}
}
// TestGitCommandGetCommitsToPush is a function.
func TestGitCommandGetCommitsToPush(t *testing.T) {
type scenario struct {
testName string
@ -635,6 +654,7 @@ func TestGitCommandGetCommitsToPush(t *testing.T) {
}
}
// TestGitCommandRenameCommit is a function.
func TestGitCommandRenameCommit(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -647,6 +667,7 @@ func TestGitCommandRenameCommit(t *testing.T) {
assert.NoError(t, gitCmd.RenameCommit("test"))
}
// TestGitCommandResetToCommit is a function.
func TestGitCommandResetToCommit(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -659,6 +680,7 @@ func TestGitCommandResetToCommit(t *testing.T) {
assert.NoError(t, gitCmd.ResetToCommit("78976bc"))
}
// TestGitCommandNewBranch is a function.
func TestGitCommandNewBranch(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -671,6 +693,7 @@ func TestGitCommandNewBranch(t *testing.T) {
assert.NoError(t, gitCmd.NewBranch("test"))
}
// TestGitCommandDeleteBranch is a function.
func TestGitCommandDeleteBranch(t *testing.T) {
type scenario struct {
testName string
@ -720,6 +743,7 @@ func TestGitCommandDeleteBranch(t *testing.T) {
}
}
// TestGitCommandMerge is a function.
func TestGitCommandMerge(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -732,6 +756,7 @@ func TestGitCommandMerge(t *testing.T) {
assert.NoError(t, gitCmd.Merge("test"))
}
// TestGitCommandUsingGpg is a function.
func TestGitCommandUsingGpg(t *testing.T) {
type scenario struct {
testName string
@ -825,6 +850,7 @@ func TestGitCommandUsingGpg(t *testing.T) {
}
}
// TestGitCommandCommit is a function.
func TestGitCommandCommit(t *testing.T) {
type scenario struct {
testName string
@ -894,6 +920,7 @@ func TestGitCommandCommit(t *testing.T) {
}
}
// TestGitCommandCommitAmendFromFiles is a function.
func TestGitCommandCommitAmendFromFiles(t *testing.T) {
type scenario struct {
testName string
@ -963,6 +990,7 @@ func TestGitCommandCommitAmendFromFiles(t *testing.T) {
}
}
// TestGitCommandPush is a function.
func TestGitCommandPush(t *testing.T) {
type scenario struct {
testName string
@ -1025,6 +1053,7 @@ func TestGitCommandPush(t *testing.T) {
}
}
// TestGitCommandSquashPreviousTwoCommits is a function.
func TestGitCommandSquashPreviousTwoCommits(t *testing.T) {
type scenario struct {
testName string
@ -1088,6 +1117,7 @@ func TestGitCommandSquashPreviousTwoCommits(t *testing.T) {
}
}
// TestGitCommandSquashFixupCommit is a function.
func TestGitCommandSquashFixupCommit(t *testing.T) {
type scenario struct {
testName string
@ -1151,6 +1181,7 @@ func TestGitCommandSquashFixupCommit(t *testing.T) {
}
}
// TestGitCommandCatFile is a function.
func TestGitCommandCatFile(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -1165,6 +1196,7 @@ func TestGitCommandCatFile(t *testing.T) {
assert.Equal(t, "test", o)
}
// TestGitCommandStageFile is a function.
func TestGitCommandStageFile(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -1177,6 +1209,7 @@ func TestGitCommandStageFile(t *testing.T) {
assert.NoError(t, gitCmd.StageFile("test.txt"))
}
// TestGitCommandUnstageFile is a function.
func TestGitCommandUnstageFile(t *testing.T) {
type scenario struct {
testName string
@ -1223,6 +1256,7 @@ func TestGitCommandUnstageFile(t *testing.T) {
}
}
// TestGitCommandIsInMergeState is a function.
func TestGitCommandIsInMergeState(t *testing.T) {
type scenario struct {
testName string
@ -1291,6 +1325,7 @@ func TestGitCommandIsInMergeState(t *testing.T) {
}
}
// TestGitCommandRemoveFile is a function.
func TestGitCommandRemoveFile(t *testing.T) {
type scenario struct {
testName string
@ -1492,6 +1527,7 @@ func TestGitCommandRemoveFile(t *testing.T) {
}
}
// TestGitCommandShow is a function.
func TestGitCommandShow(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -1505,6 +1541,7 @@ func TestGitCommandShow(t *testing.T) {
assert.NoError(t, err)
}
// TestGitCommandCheckout is a function.
func TestGitCommandCheckout(t *testing.T) {
type scenario struct {
testName string
@ -1551,6 +1588,7 @@ func TestGitCommandCheckout(t *testing.T) {
}
}
// TestGitCommandGetBranchGraph is a function.
func TestGitCommandGetBranchGraph(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
@ -1564,6 +1602,7 @@ func TestGitCommandGetBranchGraph(t *testing.T) {
assert.NoError(t, err)
}
// TestGitCommandGetCommits is a function.
func TestGitCommandGetCommits(t *testing.T) {
type scenario struct {
testName string
@ -1685,6 +1724,7 @@ func TestGitCommandGetCommits(t *testing.T) {
}
}
// TestGitCommandGetLog is a function.
func TestGitCommandGetLog(t *testing.T) {
type scenario struct {
testName string
@ -1727,11 +1767,13 @@ func TestGitCommandGetLog(t *testing.T) {
}
}
// TestGitCommandDiff is a function.
func TestGitCommandDiff(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
file *File
plain bool
}
scenarios := []scenario{
@ -1748,6 +1790,22 @@ func TestGitCommandDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
false,
},
{
"Default case",
func(cmd string, args ...string) *exec.Cmd {
assert.EqualValues(t, "git", cmd)
assert.EqualValues(t, []string{"diff", "--", "test.txt"}, args)
return exec.Command("echo")
},
&File{
Name: "test.txt",
HasStagedChanges: false,
Tracked: true,
},
true,
},
{
"All changes staged",
@ -1763,6 +1821,7 @@ func TestGitCommandDiff(t *testing.T) {
HasUnstagedChanges: false,
Tracked: true,
},
false,
},
{
"File not tracked and file has no staged changes",
@ -1777,6 +1836,7 @@ func TestGitCommandDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: false,
},
false,
},
}
@ -1784,11 +1844,12 @@ func TestGitCommandDiff(t *testing.T) {
t.Run(s.testName, func(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = s.command
gitCmd.Diff(s.file)
gitCmd.Diff(s.file, s.plain)
})
}
}
// TestGitCommandGetMergeBase is a function.
func TestGitCommandGetMergeBase(t *testing.T) {
type scenario struct {
testName string
@ -1878,6 +1939,7 @@ func TestGitCommandGetMergeBase(t *testing.T) {
}
}
// TestGitCommandCurrentBranchName is a function.
func TestGitCommandCurrentBranchName(t *testing.T) {
type scenario struct {
testName string
@ -1939,3 +2001,61 @@ func TestGitCommandCurrentBranchName(t *testing.T) {
})
}
}
func TestGitCommandApplyPatch(t *testing.T) {
type scenario struct {
testName string
command func(string, ...string) *exec.Cmd
test func(string, error)
}
scenarios := []scenario{
{
"valid case",
func(cmd string, args ...string) *exec.Cmd {
assert.Equal(t, "git", cmd)
assert.EqualValues(t, []string{"apply", "--cached"}, args[0:2])
filename := args[2]
content, err := ioutil.ReadFile(filename)
assert.NoError(t, err)
assert.Equal(t, "test", string(content))
return exec.Command("echo", "done")
},
func(output string, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "done\n", output)
},
},
{
"command returns error",
func(cmd string, args ...string) *exec.Cmd {
assert.Equal(t, "git", cmd)
assert.EqualValues(t, []string{"apply", "--cached"}, args[0:2])
filename := args[2]
// TODO: Ideally we want to mock out OSCommand here so that we're not
// double handling testing it's CreateTempFile functionality,
// but it is going to take a bit of work to make a proper mock for it
// so I'm leaving it for another PR
content, err := ioutil.ReadFile(filename)
assert.NoError(t, err)
assert.Equal(t, "test", string(content))
return exec.Command("test")
},
func(output string, err error) {
assert.Error(t, err)
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
gitCmd := newDummyGitCommand()
gitCmd.OSCommand.command = s.command
s.test(gitCmd.ApplyPatch("test"))
})
}
}

View File

@ -2,6 +2,7 @@ package commands
import (
"errors"
"io/ioutil"
"os"
"os/exec"
"regexp"
@ -157,7 +158,7 @@ func (c *OSCommand) OpenFile(filename string) error {
return err
}
// OpenFile opens a file with the given
// OpenLink opens a file with the given
func (c *OSCommand) OpenLink(link string) error {
commandTemplate := c.Config.GetUserConfig().GetString("os.openLinkCommand")
templateValues := map[string]string{
@ -224,3 +225,28 @@ func (c *OSCommand) AppendLineToFile(filename, line string) error {
_, err = f.WriteString("\n" + line)
return err
}
// CreateTempFile writes a string to a new temp file and returns the file's name
func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
tmpfile, err := ioutil.TempFile("", filename)
if err != nil {
c.Log.Error(err)
return "", err
}
if _, err := tmpfile.Write([]byte(content)); err != nil {
c.Log.Error(err)
return "", err
}
if err := tmpfile.Close(); err != nil {
c.Log.Error(err)
return "", err
}
return tmpfile.Name(), nil
}
// RemoveFile removes a file at the specified path
func (c *OSCommand) RemoveFile(filename string) error {
return os.Remove(filename)
}

View File

@ -1,6 +1,7 @@
package commands
import (
"io/ioutil"
"os"
"os/exec"
"testing"
@ -29,6 +30,7 @@ func newDummyAppConfig() *config.AppConfig {
return appConfig
}
// TestOSCommandRunCommandWithOutput is a function.
func TestOSCommandRunCommandWithOutput(t *testing.T) {
type scenario struct {
command string
@ -56,6 +58,7 @@ func TestOSCommandRunCommandWithOutput(t *testing.T) {
}
}
// TestOSCommandRunCommand is a function.
func TestOSCommandRunCommand(t *testing.T) {
type scenario struct {
command string
@ -76,6 +79,7 @@ func TestOSCommandRunCommand(t *testing.T) {
}
}
// TestOSCommandOpenFile is a function.
func TestOSCommandOpenFile(t *testing.T) {
type scenario struct {
filename string
@ -126,6 +130,7 @@ func TestOSCommandOpenFile(t *testing.T) {
}
}
// TestOSCommandEditFile is a function.
func TestOSCommandEditFile(t *testing.T) {
type scenario struct {
filename string
@ -255,6 +260,7 @@ func TestOSCommandEditFile(t *testing.T) {
}
}
// TestOSCommandQuote is a function.
func TestOSCommandQuote(t *testing.T) {
osCommand := newDummyOSCommand()
@ -278,7 +284,7 @@ func TestOSCommandQuoteSingleQuote(t *testing.T) {
assert.EqualValues(t, expected, actual)
}
// TestOSCommandQuoteSingleQuote tests the quote function with " quotes explicitly for Linux
// TestOSCommandQuoteDoubleQuote tests the quote function with " quotes explicitly for Linux
func TestOSCommandQuoteDoubleQuote(t *testing.T) {
osCommand := newDummyOSCommand()
@ -291,6 +297,7 @@ func TestOSCommandQuoteDoubleQuote(t *testing.T) {
assert.EqualValues(t, expected, actual)
}
// TestOSCommandUnquote is a function.
func TestOSCommandUnquote(t *testing.T) {
osCommand := newDummyOSCommand()
@ -301,6 +308,7 @@ func TestOSCommandUnquote(t *testing.T) {
assert.EqualValues(t, expected, actual)
}
// TestOSCommandFileType is a function.
func TestOSCommandFileType(t *testing.T) {
type scenario struct {
path string
@ -357,3 +365,34 @@ func TestOSCommandFileType(t *testing.T) {
_ = os.RemoveAll(s.path)
}
}
func TestOSCommandCreateTempFile(t *testing.T) {
type scenario struct {
testName string
filename string
content string
test func(string, error)
}
scenarios := []scenario{
{
"valid case",
"filename",
"content",
func(path string, err error) {
assert.NoError(t, err)
content, err := ioutil.ReadFile(path)
assert.NoError(t, err)
assert.Equal(t, "content", string(content))
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
s.test(newDummyOSCommand().CreateTempFile(s.filename, s.content))
})
}
}

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/assert"
)
// TestGetRepoInfoFromURL is a function.
func TestGetRepoInfoFromURL(t *testing.T) {
type scenario struct {
testName string
@ -41,6 +42,7 @@ func TestGetRepoInfoFromURL(t *testing.T) {
}
}
// TestCreatePullRequest is a function.
func TestCreatePullRequest(t *testing.T) {
type scenario struct {
testName string