1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-17 01:42:45 +02:00
This commit is contained in:
Jesse Duffield
2021-12-30 11:22:29 +11:00
parent 43a4fa970d
commit 95f4ceea34
7 changed files with 75 additions and 60 deletions

View File

@ -44,6 +44,6 @@ func (self *gitCmdObjRunner) RunWithOutput(cmdObj oscommands.ICmdObj) (string, e
} }
} }
func (self *gitCmdObjRunner) RunLineOutputCmd(cmdObj oscommands.ICmdObj, onLine func(line string) (bool, error)) error { func (self *gitCmdObjRunner) RunAndProcessLines(cmdObj oscommands.ICmdObj, onLine func(line string) (bool, error)) error {
return self.innerRunner.RunLineOutputCmd(cmdObj, onLine) return self.innerRunner.RunAndProcessLines(cmdObj, onLine)
} }

View File

@ -34,6 +34,7 @@ type CommitListBuilder struct {
getCurrentBranchName func() (string, string, error) getCurrentBranchName func() (string, string, error)
getRebaseMode func() (string, error) getRebaseMode func() (string, error)
readFile func(filename string) ([]byte, error) readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string dotGitDir string
} }
@ -50,6 +51,7 @@ func NewCommitListBuilder(
getRebaseMode: gitCommand.RebaseMode, getRebaseMode: gitCommand.RebaseMode,
dotGitDir: gitCommand.DotGitDir, dotGitDir: gitCommand.DotGitDir,
readFile: ioutil.ReadFile, readFile: ioutil.ReadFile,
walkFiles: filepath.Walk,
} }
} }
@ -57,7 +59,7 @@ func NewCommitListBuilder(
// then puts them into a commit object // then puts them into a commit object
// example input: // example input:
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag // 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
func (c *CommitListBuilder) extractCommitFromLine(line string) *models.Commit { func (self *CommitListBuilder) extractCommitFromLine(line string) *models.Commit {
split := strings.Split(line, SEPARATION_CHAR) split := strings.Split(line, SEPARATION_CHAR)
sha := split[0] sha := split[0]
@ -99,7 +101,7 @@ type GetCommitsOptions struct {
All bool All bool
} }
func (c *CommitListBuilder) MergeRebasingCommits(commits []*models.Commit) ([]*models.Commit, error) { func (self *CommitListBuilder) MergeRebasingCommits(commits []*models.Commit) ([]*models.Commit, error) {
// chances are we have as many commits as last time so we'll set the capacity to be the old length // chances are we have as many commits as last time so we'll set the capacity to be the old length
result := make([]*models.Commit, 0, len(commits)) result := make([]*models.Commit, 0, len(commits))
for i, commit := range commits { for i, commit := range commits {
@ -109,7 +111,7 @@ func (c *CommitListBuilder) MergeRebasingCommits(commits []*models.Commit) ([]*m
} }
} }
rebaseMode, err := c.getRebaseMode() rebaseMode, err := self.getRebaseMode()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,7 +121,7 @@ func (c *CommitListBuilder) MergeRebasingCommits(commits []*models.Commit) ([]*m
return result, nil return result, nil
} }
rebasingCommits, err := c.getHydratedRebasingCommits(rebaseMode) rebasingCommits, err := self.getHydratedRebasingCommits(rebaseMode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -131,17 +133,17 @@ func (c *CommitListBuilder) MergeRebasingCommits(commits []*models.Commit) ([]*m
} }
// GetCommits obtains the commits of the current branch // GetCommits obtains the commits of the current branch
func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit, error) { func (self *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit, error) {
commits := []*models.Commit{} commits := []*models.Commit{}
var rebasingCommits []*models.Commit var rebasingCommits []*models.Commit
rebaseMode, err := c.getRebaseMode() rebaseMode, err := self.getRebaseMode()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if opts.IncludeRebaseCommits && opts.FilterPath == "" { if opts.IncludeRebaseCommits && opts.FilterPath == "" {
var err error var err error
rebasingCommits, err = c.MergeRebasingCommits(commits) rebasingCommits, err = self.MergeRebasingCommits(commits)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,15 +151,15 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
} }
passedFirstPushedCommit := false passedFirstPushedCommit := false
firstPushedCommit, err := c.getFirstPushedCommit(opts.RefName) firstPushedCommit, err := self.getFirstPushedCommit(opts.RefName)
if err != nil { if err != nil {
// must have no upstream branch so we'll consider everything as pushed // must have no upstream branch so we'll consider everything as pushed
passedFirstPushedCommit = true passedFirstPushedCommit = true
} }
err = c.getLogCmd(opts).RunLineOutputCmd(func(line string) (bool, error) { err = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
if canExtractCommit(line) { if canExtractCommit(line) {
commit := c.extractCommitFromLine(line) commit := self.extractCommitFromLine(line)
if commit.Sha == firstPushedCommit { if commit.Sha == firstPushedCommit {
passedFirstPushedCommit = true passedFirstPushedCommit = true
} }
@ -172,11 +174,11 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
if rebaseMode != "" { if rebaseMode != "" {
currentCommit := commits[len(rebasingCommits)] currentCommit := commits[len(rebasingCommits)]
youAreHere := style.FgYellow.Sprintf("<-- %s ---", c.Tr.YouAreHere) youAreHere := style.FgYellow.Sprintf("<-- %s ---", self.Tr.YouAreHere)
currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name) currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
} }
commits, err = c.setCommitMergedStatuses(opts.RefName, commits) commits, err = self.setCommitMergedStatuses(opts.RefName, commits)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -184,8 +186,8 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
return commits, nil return commits, nil
} }
func (c *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*models.Commit, error) { func (self *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*models.Commit, error) {
commits, err := c.getRebasingCommits(rebaseMode) commits, err := self.getRebasingCommits(rebaseMode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -201,7 +203,7 @@ func (c *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*mo
// note that we're not filtering these as we do non-rebasing commits just because // note that we're not filtering these as we do non-rebasing commits just because
// I suspect that will cause some damage // I suspect that will cause some damage
cmdObj := c.cmd.New( cmdObj := self.cmd.New(
fmt.Sprintf( fmt.Sprintf(
"git show %s --no-patch --oneline %s --abbrev=%d", "git show %s --no-patch --oneline %s --abbrev=%d",
strings.Join(commitShas, " "), strings.Join(commitShas, " "),
@ -212,9 +214,9 @@ func (c *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*mo
hydratedCommits := make([]*models.Commit, 0, len(commits)) hydratedCommits := make([]*models.Commit, 0, len(commits))
i := 0 i := 0
err = cmdObj.RunLineOutputCmd(func(line string) (bool, error) { err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
if canExtractCommit(line) { if canExtractCommit(line) {
commit := c.extractCommitFromLine(line) commit := self.extractCommitFromLine(line)
matchingCommit := commits[i] matchingCommit := commits[i]
commit.Action = matchingCommit.Action commit.Action = matchingCommit.Action
commit.Status = matchingCommit.Status commit.Status = matchingCommit.Status
@ -230,20 +232,20 @@ func (c *CommitListBuilder) getHydratedRebasingCommits(rebaseMode string) ([]*mo
} }
// getRebasingCommits obtains the commits that we're in the process of rebasing // getRebasingCommits obtains the commits that we're in the process of rebasing
func (c *CommitListBuilder) getRebasingCommits(rebaseMode string) ([]*models.Commit, error) { func (self *CommitListBuilder) getRebasingCommits(rebaseMode string) ([]*models.Commit, error) {
switch rebaseMode { switch rebaseMode {
case REBASE_MODE_MERGING: case REBASE_MODE_MERGING:
return c.getNormalRebasingCommits() return self.getNormalRebasingCommits()
case REBASE_MODE_INTERACTIVE: case REBASE_MODE_INTERACTIVE:
return c.getInteractiveRebasingCommits() return self.getInteractiveRebasingCommits()
default: default:
return nil, nil return nil, nil
} }
} }
func (c *CommitListBuilder) getNormalRebasingCommits() ([]*models.Commit, error) { func (self *CommitListBuilder) getNormalRebasingCommits() ([]*models.Commit, error) {
rewrittenCount := 0 rewrittenCount := 0
bytesContent, err := c.readFile(filepath.Join(c.dotGitDir, "rebase-apply/rewritten")) bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-apply/rewritten"))
if err == nil { if err == nil {
content := string(bytesContent) content := string(bytesContent)
rewrittenCount = len(strings.Split(content, "\n")) rewrittenCount = len(strings.Split(content, "\n"))
@ -251,7 +253,7 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*models.Commit, error)
// we know we're rebasing, so lets get all the files whose names have numbers // we know we're rebasing, so lets get all the files whose names have numbers
commits := []*models.Commit{} commits := []*models.Commit{}
err = filepath.Walk(filepath.Join(c.dotGitDir, "rebase-apply"), func(path string, f os.FileInfo, err error) error { err = self.walkFiles(filepath.Join(self.dotGitDir, "rebase-apply"), func(path string, f os.FileInfo, err error) error {
if rewrittenCount > 0 { if rewrittenCount > 0 {
rewrittenCount-- rewrittenCount--
return nil return nil
@ -263,12 +265,12 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*models.Commit, error)
if !re.MatchString(f.Name()) { if !re.MatchString(f.Name()) {
return nil return nil
} }
bytesContent, err := c.readFile(path) bytesContent, err := self.readFile(path)
if err != nil { if err != nil {
return err return err
} }
content := string(bytesContent) content := string(bytesContent)
commit, err := c.commitFromPatch(content) commit, err := self.commitFromPatch(content)
if err != nil { if err != nil {
return err return err
} }
@ -294,10 +296,10 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*models.Commit, error)
// getInteractiveRebasingCommits takes our git-rebase-todo and our git-rebase-todo.backup files // 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 // and extracts out the sha and names of commits that we still have to go
// in the rebase: // in the rebase:
func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*models.Commit, error) { func (self *CommitListBuilder) getInteractiveRebasingCommits() ([]*models.Commit, error) {
bytesContent, err := c.readFile(filepath.Join(c.dotGitDir, "rebase-merge/git-rebase-todo")) bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"))
if err != nil { if err != nil {
c.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error())) self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
// we assume an error means the file doesn't exist so we just return // we assume an error means the file doesn't exist so we just return
return nil, nil return nil, nil
} }
@ -328,7 +330,7 @@ func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*models.Commit, e
// From: Lazygit Tester <test@example.com> // From: Lazygit Tester <test@example.com>
// Date: Wed, 5 Dec 2018 21:03:23 +1100 // Date: Wed, 5 Dec 2018 21:03:23 +1100
// Subject: second commit on master // Subject: second commit on master
func (c *CommitListBuilder) commitFromPatch(content string) (*models.Commit, error) { func (self *CommitListBuilder) commitFromPatch(content string) (*models.Commit, error) {
lines := strings.Split(content, "\n") lines := strings.Split(content, "\n")
sha := strings.Split(lines[0], " ")[1] sha := strings.Split(lines[0], " ")[1]
name := strings.TrimPrefix(lines[3], "Subject: ") name := strings.TrimPrefix(lines[3], "Subject: ")
@ -339,8 +341,8 @@ func (c *CommitListBuilder) commitFromPatch(content string) (*models.Commit, err
}, nil }, nil
} }
func (c *CommitListBuilder) setCommitMergedStatuses(refName string, commits []*models.Commit) ([]*models.Commit, error) { func (self *CommitListBuilder) setCommitMergedStatuses(refName string, commits []*models.Commit) ([]*models.Commit, error) {
ancestor, err := c.getMergeBase(refName) ancestor, err := self.getMergeBase(refName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -362,8 +364,8 @@ func (c *CommitListBuilder) setCommitMergedStatuses(refName string, commits []*m
return commits, nil return commits, nil
} }
func (c *CommitListBuilder) getMergeBase(refName string) (string, error) { func (self *CommitListBuilder) getMergeBase(refName string) (string, error) {
currentBranch, _, err := c.getCurrentBranchName() currentBranch, _, err := self.getCurrentBranchName()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -374,7 +376,7 @@ func (c *CommitListBuilder) getMergeBase(refName string) (string, error) {
} }
// swallowing error because it's not a big deal; probably because there are no commits yet // swallowing error because it's not a big deal; probably because there are no commits yet
output, _ := c.cmd.New(fmt.Sprintf("git merge-base %s %s", c.cmd.Quote(refName), c.cmd.Quote(baseBranch))).RunWithOutput() output, _ := self.cmd.New(fmt.Sprintf("git merge-base %s %s", self.cmd.Quote(refName), self.cmd.Quote(baseBranch))).RunWithOutput()
return ignoringWarnings(output), nil return ignoringWarnings(output), nil
} }
@ -390,8 +392,12 @@ func ignoringWarnings(commandOutput string) string {
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream. // getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
// all commits above this are deemed unpushed and marked as such. // all commits above this are deemed unpushed and marked as such.
func (c *CommitListBuilder) getFirstPushedCommit(refName string) (string, error) { func (self *CommitListBuilder) getFirstPushedCommit(refName string) (string, error) {
output, err := c.cmd.New(fmt.Sprintf("git merge-base %s %s@{u}", c.cmd.Quote(refName), c.cmd.Quote(refName))).RunWithOutput() output, err := self.cmd.
New(
fmt.Sprintf("git merge-base %s %s@{u}", self.cmd.Quote(refName), self.cmd.Quote(refName)),
).
RunWithOutput()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -400,7 +406,7 @@ func (c *CommitListBuilder) getFirstPushedCommit(refName string) (string, error)
} }
// getLog gets the git log. // getLog gets the git log.
func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj { func (self *CommitListBuilder) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
limitFlag := "" limitFlag := ""
if opts.Limit { if opts.Limit {
limitFlag = "-300" limitFlag = "-300"
@ -408,10 +414,10 @@ func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj
filterFlag := "" filterFlag := ""
if opts.FilterPath != "" { if opts.FilterPath != "" {
filterFlag = fmt.Sprintf(" --follow -- %s", c.cmd.Quote(opts.FilterPath)) filterFlag = fmt.Sprintf(" --follow -- %s", self.cmd.Quote(opts.FilterPath))
} }
config := c.UserConfig.Git.Log config := self.UserConfig.Git.Log
orderFlag := "--" + config.Order orderFlag := "--" + config.Order
allFlag := "" allFlag := ""
@ -419,10 +425,10 @@ func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj
allFlag = " --all" allFlag = " --all"
} }
return c.cmd.New( return self.cmd.New(
fmt.Sprintf( fmt.Sprintf(
"git log %s %s %s --oneline %s %s --abbrev=%d %s", "git log %s %s %s --oneline %s %s --abbrev=%d %s",
c.cmd.Quote(opts.RefName), self.cmd.Quote(opts.RefName),
orderFlag, orderFlag,
allFlag, allFlag,
prettyFormat, prettyFormat,

View File

@ -2,10 +2,9 @@ package commands
import ( import (
"os/exec" "os/exec"
"path/filepath"
"testing" "testing"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/secureexec" "github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -13,13 +12,20 @@ import (
// NewDummyCommitListBuilder creates a new dummy CommitListBuilder for testing // NewDummyCommitListBuilder creates a new dummy CommitListBuilder for testing
func NewDummyCommitListBuilder() *CommitListBuilder { func NewDummyCommitListBuilder() *CommitListBuilder {
osCommand := oscommands.NewDummyOSCommand() cmn := utils.NewDummyCommon()
return &CommitListBuilder{ return &CommitListBuilder{
Log: utils.NewDummyLog(), Common: cmn,
GitCommand: NewDummyGitCommandWithOSCommand(osCommand), cmd: nil,
OSCommand: osCommand, getCurrentBranchName: func() (string, string, error) { return "master", "master", nil },
Tr: i18n.NewTranslationSet(utils.NewDummyLog(), "auto"), getRebaseMode: func() (string, error) { return REBASE_MODE_NORMAL, nil },
dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) {
return []byte(""), nil
},
walkFiles: func(root string, fn filepath.WalkFunc) error {
return nil
},
} }
} }

View File

@ -20,7 +20,7 @@ func (c *GitCommand) GetReflogCommits(lastReflogCommit *models.Commit, filterPat
cmdObj := c.OSCommand.Cmd.New(fmt.Sprintf(`git log -g --abbrev=20 --format="%%h %%ct %%gs" %s`, filterPathArg)) cmdObj := c.OSCommand.Cmd.New(fmt.Sprintf(`git log -g --abbrev=20 --format="%%h %%ct %%gs" %s`, filterPathArg))
onlyObtainedNewReflogCommits := false onlyObtainedNewReflogCommits := false
err := cmdObj.RunLineOutputCmd(func(line string) (bool, error) { err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
fields := strings.SplitN(line, " ", 3) fields := strings.SplitN(line, " ", 3)
if len(fields) <= 2 { if len(fields) <= 2 {
return false, nil return false, nil

View File

@ -12,9 +12,12 @@ type ICmdObj interface {
AddEnvVars(...string) ICmdObj AddEnvVars(...string) ICmdObj
GetEnvVars() []string GetEnvVars() []string
// runs the command and returns an error if any
Run() error Run() error
// runs the command and returns the output as a string, and an error if any
RunWithOutput() (string, error) RunWithOutput() (string, error)
RunLineOutputCmd(onLine func(line string) (bool, error)) error // runs the command and runs a callback function on each line of the output. If the callback returns true for the boolean value, we kill the process and return.
RunAndProcessLines(onLine func(line string) (bool, error)) error
// logs command // logs command
Log() ICmdObj Log() ICmdObj
@ -60,6 +63,6 @@ func (self *CmdObj) RunWithOutput() (string, error) {
return self.runner.RunWithOutput(self) return self.runner.RunWithOutput(self)
} }
func (self *CmdObj) RunLineOutputCmd(onLine func(line string) (bool, error)) error { func (self *CmdObj) RunAndProcessLines(onLine func(line string) (bool, error)) error {
return self.runner.RunLineOutputCmd(self, onLine) return self.runner.RunAndProcessLines(self, onLine)
} }

View File

@ -11,22 +11,22 @@ import (
type ICmdObjRunner interface { type ICmdObjRunner interface {
Run(cmdObj ICmdObj) error Run(cmdObj ICmdObj) error
RunWithOutput(cmdObj ICmdObj) (string, error) RunWithOutput(cmdObj ICmdObj) (string, error)
RunLineOutputCmd(cmdObj ICmdObj, onLine func(line string) (bool, error)) error RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error
} }
type RunExpectation func(ICmdObj) (string, error) type RunExpectation func(ICmdObj) (string, error)
type RealRunner struct { type Runner struct {
log *logrus.Entry log *logrus.Entry
logCmdObj func(ICmdObj) logCmdObj func(ICmdObj)
} }
func (self *RealRunner) Run(cmdObj ICmdObj) error { func (self *Runner) Run(cmdObj ICmdObj) error {
_, err := self.RunWithOutput(cmdObj) _, err := self.RunWithOutput(cmdObj)
return err return err
} }
func (self *RealRunner) RunWithOutput(cmdObj ICmdObj) (string, error) { func (self *Runner) RunWithOutput(cmdObj ICmdObj) (string, error) {
self.logCmdObj(cmdObj) self.logCmdObj(cmdObj)
output, err := sanitisedCommandOutput(cmdObj.GetCmd().CombinedOutput()) output, err := sanitisedCommandOutput(cmdObj.GetCmd().CombinedOutput())
if err != nil { if err != nil {
@ -35,7 +35,7 @@ func (self *RealRunner) RunWithOutput(cmdObj ICmdObj) (string, error) {
return output, err return output, err
} }
func (self *RealRunner) RunLineOutputCmd(cmdObj ICmdObj, onLine func(line string) (bool, error)) error { func (self *Runner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error {
cmd := cmdObj.GetCmd() cmd := cmdObj.GetCmd()
stdoutPipe, err := cmd.StdoutPipe() stdoutPipe, err := cmd.StdoutPipe()
if err != nil { if err != nil {

View File

@ -99,7 +99,7 @@ func NewOSCommand(common *common.Common) *OSCommand {
removeFile: os.RemoveAll, removeFile: os.RemoveAll,
} }
runner := &RealRunner{log: common.Log, logCmdObj: c.LogCmdObj} runner := &Runner{log: common.Log, logCmdObj: c.LogCmdObj}
c.Cmd = &CmdObjBuilder{runner: runner, command: command, logCmdObj: c.LogCmdObj, platform: platform} c.Cmd = &CmdObjBuilder{runner: runner, command: command, logCmdObj: c.LogCmdObj, platform: platform}
return c return c