mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-12 21:05:48 +02:00
WIP
This commit is contained in:
parent
157dd309f7
commit
b4c078d565
48 changed files with 437 additions and 493 deletions
|
@ -146,7 +146,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) validateGitVersion() error {
|
func (app *App) validateGitVersion() error {
|
||||||
output, err := app.OSCommand.RunCommandWithOutput("git --version")
|
output, err := app.OSCommand.RunWithOutput(app.OSCommand.NewCmdObj("git --version"))
|
||||||
// if we get an error anywhere here we'll show the same status
|
// if we get an error anywhere here we'll show the same status
|
||||||
minVersionError := errors.New(app.Tr.MinGitVersionError)
|
minVersionError := errors.New(app.Tr.MinGitVersionError)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -231,7 +231,7 @@ func (app *App) setupRepo() (bool, error) {
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err := app.OSCommand.RunCommand("git init"); err != nil {
|
if err := app.OSCommand.Run(app.OSCommand.NewCmdObj("git init")); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,19 +11,19 @@ import (
|
||||||
|
|
||||||
// NewBranch create new branch
|
// NewBranch create new branch
|
||||||
func (c *GitCommand) NewBranch(name string, base string) error {
|
func (c *GitCommand) NewBranch(name string, base string) error {
|
||||||
return c.RunCommand("git checkout -b %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(base))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git checkout -b %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(base))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentBranchName get the current branch name and displayname.
|
// CurrentBranchName get the current branch name and displayname.
|
||||||
// the first returned string is the name and the second is the displayname
|
// the first returned string is the name and the second is the displayname
|
||||||
// e.g. name is 123asdf and displayname is '(HEAD detached at 123asdf)'
|
// e.g. name is 123asdf and displayname is '(HEAD detached at 123asdf)'
|
||||||
func (c *GitCommand) CurrentBranchName() (string, string, error) {
|
func (c *GitCommand) CurrentBranchName() (string, string, error) {
|
||||||
branchName, err := c.RunCommandWithOutput("git symbolic-ref --short HEAD")
|
branchName, err := c.RunWithOutput(c.NewCmdObj("git symbolic-ref --short HEAD"))
|
||||||
if err == nil && branchName != "HEAD\n" {
|
if err == nil && branchName != "HEAD\n" {
|
||||||
trimmedBranchName := strings.TrimSpace(branchName)
|
trimmedBranchName := strings.TrimSpace(branchName)
|
||||||
return trimmedBranchName, trimmedBranchName, nil
|
return trimmedBranchName, trimmedBranchName, nil
|
||||||
}
|
}
|
||||||
output, err := c.RunCommandWithOutput("git branch --contains")
|
output, err := c.RunWithOutput(c.NewCmdObj("git branch --contains"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ func (c *GitCommand) DeleteBranch(branch string, force bool) error {
|
||||||
command = "git branch -D"
|
command = "git branch -D"
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunCommand("%s %s", command, c.OSCommand.Quote(branch))
|
return c.OSCommand.Run(c.OSCommand.NewCmdObj(fmt.Sprintf("%s %s", command, c.OSCommand.Quote(branch))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
|
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
|
||||||
|
@ -61,36 +61,42 @@ func (c *GitCommand) Checkout(branch string, options CheckoutOptions) error {
|
||||||
if options.Force {
|
if options.Force {
|
||||||
forceArg = " --force"
|
forceArg = " --force"
|
||||||
}
|
}
|
||||||
return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git checkout%s %s", forceArg, c.OSCommand.Quote(branch)), oscommands.RunCommandOptions{EnvVars: options.EnvVars})
|
|
||||||
|
cmdObj := c.NewCmdObj(fmt.Sprintf("git checkout%s %s", forceArg, c.OSCommand.Quote(branch))).
|
||||||
|
// prevents git from prompting us for input which would freeze the program
|
||||||
|
// TODO: see if this is actually needed here
|
||||||
|
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
||||||
|
AddEnvVars(options.EnvVars...)
|
||||||
|
|
||||||
|
return c.OSCommand.Run(cmdObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBranchGraph gets the color-formatted graph of the log for the given branch
|
// GetBranchGraph gets the color-formatted graph of the log for the given branch
|
||||||
// Currently it limits the result to 100 commits, but when we get async stuff
|
// Currently it limits the result to 100 commits, but when we get async stuff
|
||||||
// working we can do lazy loading
|
// working we can do lazy loading
|
||||||
func (c *GitCommand) GetBranchGraph(branchName string) (string, error) {
|
func (c *GitCommand) GetBranchGraph(branchName string) (string, error) {
|
||||||
cmdStr := c.GetBranchGraphCmdStr(branchName)
|
return c.OSCommand.RunWithOutput(c.GetBranchGraphCmdObj(branchName))
|
||||||
return c.OSCommand.RunCommandWithOutput(cmdStr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GetUpstreamForBranch(branchName string) (string, error) {
|
func (c *GitCommand) GetUpstreamForBranch(branchName string) (string, error) {
|
||||||
output, err := c.RunCommandWithOutput("git rev-parse --abbrev-ref --symbolic-full-name %s@{u}", c.OSCommand.Quote(branchName))
|
output, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git rev-parse --abbrev-ref --symbolic-full-name %s@{u}", c.OSCommand.Quote(branchName))))
|
||||||
return strings.TrimSpace(output), err
|
return strings.TrimSpace(output), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GetBranchGraphCmdStr(branchName string) string {
|
func (c *GitCommand) GetBranchGraphCmdObj(branchName string) oscommands.ICmdObj {
|
||||||
branchLogCmdTemplate := c.Config.GetUserConfig().Git.BranchLogCmd
|
branchLogCmdTemplate := c.Config.GetUserConfig().Git.BranchLogCmd
|
||||||
templateValues := map[string]string{
|
templateValues := map[string]string{
|
||||||
"branchName": c.OSCommand.Quote(branchName),
|
"branchName": c.OSCommand.Quote(branchName),
|
||||||
}
|
}
|
||||||
return utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)
|
return c.NewCmdObj(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SetUpstreamBranch(upstream string) error {
|
func (c *GitCommand) SetUpstreamBranch(upstream string) error {
|
||||||
return c.RunCommand("git branch -u %s", c.OSCommand.Quote(upstream))
|
return c.Run(c.NewCmdObj("git branch -u " + c.OSCommand.Quote(upstream)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SetBranchUpstream(remoteName string, remoteBranchName string, branchName string) error {
|
func (c *GitCommand) SetBranchUpstream(remoteName string, remoteBranchName string, branchName string) error {
|
||||||
return c.RunCommand("git branch --set-upstream-to=%s/%s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git branch --set-upstream-to=%s/%s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
||||||
|
@ -105,11 +111,11 @@ func (c *GitCommand) GetBranchUpstreamDifferenceCount(branchName string) (string
|
||||||
// current branch
|
// current branch
|
||||||
func (c *GitCommand) GetCommitDifferences(from, to string) (string, string) {
|
func (c *GitCommand) GetCommitDifferences(from, to string) (string, string) {
|
||||||
command := "git rev-list %s..%s --count"
|
command := "git rev-list %s..%s --count"
|
||||||
pushableCount, err := c.OSCommand.RunCommandWithOutput(command, to, from)
|
pushableCount, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf(command, to, from)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "?", "?"
|
return "?", "?"
|
||||||
}
|
}
|
||||||
pullableCount, err := c.OSCommand.RunCommandWithOutput(command, from, to)
|
pullableCount, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf(command, from, to)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "?", "?"
|
return "?", "?"
|
||||||
}
|
}
|
||||||
|
@ -129,33 +135,33 @@ func (c *GitCommand) Merge(branchName string, opts MergeOpts) error {
|
||||||
command = fmt.Sprintf("%s --ff-only", command)
|
command = fmt.Sprintf("%s --ff-only", command)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunCommand(command)
|
return c.OSCommand.Run(c.OSCommand.NewCmdObj(command))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortMerge abort merge
|
// AbortMerge abort merge
|
||||||
func (c *GitCommand) AbortMerge() error {
|
func (c *GitCommand) AbortMerge() error {
|
||||||
return c.RunCommand("git merge --abort")
|
return c.Run(c.NewCmdObj("git merge --abort"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) IsHeadDetached() bool {
|
func (c *GitCommand) IsHeadDetached() bool {
|
||||||
err := c.RunCommand("git symbolic-ref -q HEAD")
|
err := c.Run(c.NewCmdObj("git symbolic-ref -q HEAD"))
|
||||||
return err != nil
|
return err != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetHardHead runs `git reset --hard`
|
// ResetHardHead runs `git reset --hard`
|
||||||
func (c *GitCommand) ResetHard(ref string) error {
|
func (c *GitCommand) ResetHard(ref string) error {
|
||||||
return c.RunCommand("git reset --hard " + c.OSCommand.Quote(ref))
|
return c.Run(c.NewCmdObj("git reset --hard " + c.OSCommand.Quote(ref)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetSoft runs `git reset --soft HEAD`
|
// ResetSoft runs `git reset --soft HEAD`
|
||||||
func (c *GitCommand) ResetSoft(ref string) error {
|
func (c *GitCommand) ResetSoft(ref string) error {
|
||||||
return c.RunCommand("git reset --soft " + c.OSCommand.Quote(ref))
|
return c.Run(c.NewCmdObj("git reset --soft " + c.OSCommand.Quote(ref)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) ResetMixed(ref string) error {
|
func (c *GitCommand) ResetMixed(ref string) error {
|
||||||
return c.RunCommand("git reset --mixed " + c.OSCommand.Quote(ref))
|
return c.Run(c.NewCmdObj("git reset --mixed " + c.OSCommand.Quote(ref)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) RenameBranch(oldName string, newName string) error {
|
func (c *GitCommand) RenameBranch(oldName string, newName string) error {
|
||||||
return c.RunCommand("git branch --move %s %s", c.OSCommand.Quote(oldName), c.OSCommand.Quote(newName))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git branch --move %s %s", c.OSCommand.Quote(oldName), c.OSCommand.Quote(newName))))
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ func TestGitCommandGetAllBranchGraph(t *testing.T) {
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
}
|
}
|
||||||
cmdStr := gitCmd.Config.GetUserConfig().Git.AllBranchesLogCmd
|
cmdStr := gitCmd.Config.GetUserConfig().Git.AllBranchesLogCmd
|
||||||
_, err := gitCmd.OSCommand.RunCommandWithOutput(cmdStr)
|
_, err := gitCmd.OSCommand.RunWithOutput(gitCmd.NewCmdObj(cmdStr))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,21 @@ import (
|
||||||
|
|
||||||
// RenameCommit renames the topmost commit with the given name
|
// RenameCommit renames the topmost commit with the given name
|
||||||
func (c *GitCommand) RenameCommit(name string) error {
|
func (c *GitCommand) RenameCommit(name string) error {
|
||||||
return c.RunCommand("git commit --allow-empty --amend --only -m %s", c.OSCommand.Quote(name))
|
return c.Run(c.NewCmdObj("git commit --allow-empty --amend --only -m " + c.OSCommand.Quote(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetToCommit reset to commit
|
// ResetToCommit reset to commit
|
||||||
func (c *GitCommand) ResetToCommit(sha string, strength string, options oscommands.RunCommandOptions) error {
|
func (c *GitCommand) ResetToCommit(sha string, strength string, envVars []string) error {
|
||||||
return c.OSCommand.RunCommandWithOptions(fmt.Sprintf("git reset --%s %s", strength, sha), options)
|
cmdObj := c.NewCmdObj(fmt.Sprintf("git reset --%s %s", strength, sha)).
|
||||||
|
// prevents git from prompting us for input which would freeze the program
|
||||||
|
// TODO: see if this is actually needed here
|
||||||
|
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
||||||
|
AddEnvVars(envVars...)
|
||||||
|
|
||||||
|
return c.OSCommand.Run(cmdObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) CommitCmdStr(message string, flags string) string {
|
func (c *GitCommand) CommitCmdObj(message string, flags string) oscommands.ICmdObj {
|
||||||
splitMessage := strings.Split(message, "\n")
|
splitMessage := strings.Split(message, "\n")
|
||||||
lineArgs := ""
|
lineArgs := ""
|
||||||
for _, line := range splitMessage {
|
for _, line := range splitMessage {
|
||||||
|
@ -30,52 +36,53 @@ func (c *GitCommand) CommitCmdStr(message string, flags string) string {
|
||||||
flagsStr = fmt.Sprintf(" %s", flags)
|
flagsStr = fmt.Sprintf(" %s", flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("git commit%s%s", flagsStr, lineArgs)
|
return c.NewCmdObj(fmt.Sprintf("git commit%s%s", flagsStr, lineArgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the subject of the HEAD commit
|
// Get the subject of the HEAD commit
|
||||||
func (c *GitCommand) GetHeadCommitMessage() (string, error) {
|
func (c *GitCommand) GetHeadCommitMessage() (string, error) {
|
||||||
cmdStr := "git log -1 --pretty=%s"
|
message, err := c.RunWithOutput(c.NewCmdObj("git log -1 --pretty=%s"))
|
||||||
message, err := c.OSCommand.RunCommandWithOutput(cmdStr)
|
|
||||||
return strings.TrimSpace(message), err
|
return strings.TrimSpace(message), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GetCommitMessage(commitSha string) (string, error) {
|
func (c *GitCommand) GetCommitMessage(commitSha string) (string, error) {
|
||||||
cmdStr := "git rev-list --format=%B --max-count=1 " + commitSha
|
cmdStr := "git rev-list --format=%B --max-count=1 " + commitSha
|
||||||
messageWithHeader, err := c.OSCommand.RunCommandWithOutput(cmdStr)
|
messageWithHeader, err := c.RunWithOutput(c.NewCmdObj(cmdStr))
|
||||||
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "\n")
|
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "\n")
|
||||||
return strings.TrimSpace(message), err
|
return strings.TrimSpace(message), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GetCommitMessageFirstLine(sha string) (string, error) {
|
func (c *GitCommand) GetCommitMessageFirstLine(sha string) (string, error) {
|
||||||
return c.RunCommandWithOutput("git show --no-patch --pretty=format:%%s %s", sha)
|
return c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git show --no-patch --pretty=format:%%s %s", sha)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AmendHead amends HEAD with whatever is staged in your working tree
|
// AmendHead amends HEAD with whatever is staged in your working tree
|
||||||
func (c *GitCommand) AmendHead() error {
|
func (c *GitCommand) AmendHead() error {
|
||||||
return c.OSCommand.RunCommand(c.AmendHeadCmdStr())
|
return c.OSCommand.Run(c.AmendHeadCmdObj())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) AmendHeadCmdStr() string {
|
func (c *GitCommand) AmendHeadCmdObj() oscommands.ICmdObj {
|
||||||
return "git commit --amend --no-edit --allow-empty"
|
return c.NewCmdObj("git commit --amend --no-edit --allow-empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) ShowCmdStr(sha string, filterPath string) string {
|
func (c *GitCommand) ShowCmdObj(sha string, filterPath string) oscommands.ICmdObj {
|
||||||
contextSize := c.Config.GetUserConfig().Git.DiffContextSize
|
contextSize := c.Config.GetUserConfig().Git.DiffContextSize
|
||||||
filterPathArg := ""
|
filterPathArg := ""
|
||||||
if filterPath != "" {
|
if filterPath != "" {
|
||||||
filterPathArg = fmt.Sprintf(" -- %s", c.OSCommand.Quote(filterPath))
|
filterPathArg = fmt.Sprintf(" -- %s", c.OSCommand.Quote(filterPath))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("git show --submodule --color=%s --unified=%d --no-renames --stat -p %s %s", c.colorArg(), contextSize, sha, filterPathArg)
|
|
||||||
|
cmdStr := fmt.Sprintf("git show --submodule --color=%s --unified=%d --no-renames --stat -p %s %s", c.colorArg(), contextSize, sha, filterPathArg)
|
||||||
|
return c.NewCmdObj(cmdStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert reverts the selected commit by sha
|
// Revert reverts the selected commit by sha
|
||||||
func (c *GitCommand) Revert(sha string) error {
|
func (c *GitCommand) Revert(sha string) error {
|
||||||
return c.RunCommand("git revert %s", sha)
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git revert %s", sha)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) RevertMerge(sha string, parentNumber int) error {
|
func (c *GitCommand) RevertMerge(sha string, parentNumber int) error {
|
||||||
return c.RunCommand("git revert %s -m %d", sha, parentNumber)
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git revert %s -m %d", sha, parentNumber)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD
|
// CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD
|
||||||
|
@ -90,10 +97,10 @@ func (c *GitCommand) CherryPickCommits(commits []*models.Commit) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunPreparedCommand(cmd)
|
return c.OSCommand.Run(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFixupCommit creates a commit that fixes up a previous commit
|
// CreateFixupCommit creates a commit that fixes up a previous commit
|
||||||
func (c *GitCommand) CreateFixupCommit(sha string) error {
|
func (c *GitCommand) CreateFixupCommit(sha string) error {
|
||||||
return c.RunCommand("git commit --fixup=%s", sha)
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git commit --fixup=%s", sha)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||||
"github.com/jesseduffield/lazygit/pkg/test"
|
"github.com/jesseduffield/lazygit/pkg/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -33,11 +32,11 @@ func TestGitCommandResetToCommit(t *testing.T) {
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard", oscommands.RunCommandOptions{}))
|
assert.NoError(t, gitCmd.ResetToCommit("78976bc", "hard", []string{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGitCommandCommitStr is a function.
|
// TestGitCommandCommitObj is a function.
|
||||||
func TestGitCommandCommitStr(t *testing.T) {
|
func TestGitCommandCommitObj(t *testing.T) {
|
||||||
gitCmd := NewDummyGitCommand()
|
gitCmd := NewDummyGitCommand()
|
||||||
|
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
|
@ -70,7 +69,7 @@ func TestGitCommandCommitStr(t *testing.T) {
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
cmdStr := gitCmd.CommitCmdStr(s.message, s.flags)
|
cmdStr := gitCmd.CommitCmdObj(s.message, s.flags).ToString()
|
||||||
assert.Equal(t, s.expected, cmdStr)
|
assert.Equal(t, s.expected, cmdStr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -111,8 +110,8 @@ func TestGitCommandCreateFixupCommit(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGitCommandShowCmdStr is a function.
|
// TestGitCommandShowCmdObj is a function.
|
||||||
func TestGitCommandShowCmdStr(t *testing.T) {
|
func TestGitCommandShowCmdObj(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
filterPath string
|
filterPath string
|
||||||
|
@ -146,7 +145,7 @@ func TestGitCommandShowCmdStr(t *testing.T) {
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
gitCmd.Config.GetUserConfig().Git.DiffContextSize = s.contextSize
|
gitCmd.Config.GetUserConfig().Git.DiffContextSize = s.contextSize
|
||||||
cmdStr := gitCmd.ShowCmdStr("1234567890", s.filterPath)
|
cmdStr := gitCmd.ShowCmdObj("1234567890", s.filterPath).ToString()
|
||||||
assert.Equal(t, s.expected, cmdStr)
|
assert.Equal(t, s.expected, cmdStr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
@ -23,27 +24,27 @@ func (c *GitCommand) CatFile(fileName string) (string, error) {
|
||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) OpenMergeToolCmd() string {
|
func (c *GitCommand) OpenMergeToolCmdObj() oscommands.ICmdObj {
|
||||||
return "git mergetool"
|
return c.NewCmdObj("git mergetool")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) OpenMergeTool() error {
|
func (c *GitCommand) OpenMergeTool() error {
|
||||||
return c.OSCommand.RunCommand("git mergetool")
|
return c.Run(c.OpenMergeToolCmdObj())
|
||||||
}
|
}
|
||||||
|
|
||||||
// StageFile stages a file
|
// StageFile stages a file
|
||||||
func (c *GitCommand) StageFile(fileName string) error {
|
func (c *GitCommand) StageFile(fileName string) error {
|
||||||
return c.RunCommand("git add -- %s", c.OSCommand.Quote(fileName))
|
return c.Run(c.NewCmdObj("git add -- " + c.OSCommand.Quote(fileName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// StageAll stages all files
|
// StageAll stages all files
|
||||||
func (c *GitCommand) StageAll() error {
|
func (c *GitCommand) StageAll() error {
|
||||||
return c.RunCommand("git add -A")
|
return c.Run(c.NewCmdObj("git add -A"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnstageAll unstages all files
|
// UnstageAll unstages all files
|
||||||
func (c *GitCommand) UnstageAll() error {
|
func (c *GitCommand) UnstageAll() error {
|
||||||
return c.RunCommand("git reset")
|
return c.Run(c.NewCmdObj("git reset"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnStageFile unstages a file
|
// UnStageFile unstages a file
|
||||||
|
@ -56,7 +57,8 @@ func (c *GitCommand) UnStageFile(fileNames []string, reset bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range fileNames {
|
for _, name := range fileNames {
|
||||||
if err := c.OSCommand.RunCommand(command, c.OSCommand.Quote(name)); err != nil {
|
cmdObj := c.NewCmdObj(fmt.Sprintf(command, c.OSCommand.Quote(name)))
|
||||||
|
if err := c.Run(cmdObj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,22 +122,22 @@ func (c *GitCommand) DiscardAllFileChanges(file *models.File) error {
|
||||||
quotedFileName := c.OSCommand.Quote(file.Name)
|
quotedFileName := c.OSCommand.Quote(file.Name)
|
||||||
|
|
||||||
if file.ShortStatus == "AA" {
|
if file.ShortStatus == "AA" {
|
||||||
if err := c.RunCommand("git checkout --ours -- %s", quotedFileName); err != nil {
|
if err := c.Run(c.NewCmdObj("git checkout --ours -- " + quotedFileName)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := c.RunCommand("git add -- %s", quotedFileName); err != nil {
|
if err := c.Run(c.NewCmdObj("git add -- " + quotedFileName)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.ShortStatus == "DU" {
|
if file.ShortStatus == "DU" {
|
||||||
return c.RunCommand("git rm -- %s", quotedFileName)
|
return c.Run(c.NewCmdObj("git rm -- " + quotedFileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the file isn't tracked, we assume you want to delete it
|
// if the file isn't tracked, we assume you want to delete it
|
||||||
if file.HasStagedChanges || file.HasMergeConflicts {
|
if file.HasStagedChanges || file.HasMergeConflicts {
|
||||||
if err := c.RunCommand("git reset -- %s", quotedFileName); err != nil {
|
if err := c.Run(c.NewCmdObj("git reset -- " + quotedFileName)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +163,7 @@ func (c *GitCommand) DiscardUnstagedDirChanges(node *filetree.FileNode) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
quotedPath := c.OSCommand.Quote(node.GetPath())
|
quotedPath := c.OSCommand.Quote(node.GetPath())
|
||||||
if err := c.RunCommand("git checkout -- %s", quotedPath); err != nil {
|
if err := c.Run(c.NewCmdObj("git checkout -- " + quotedPath)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +188,7 @@ func (c *GitCommand) RemoveUntrackedDirFiles(node *filetree.FileNode) error {
|
||||||
// DiscardUnstagedFileChanges directly
|
// DiscardUnstagedFileChanges directly
|
||||||
func (c *GitCommand) DiscardUnstagedFileChanges(file *models.File) error {
|
func (c *GitCommand) DiscardUnstagedFileChanges(file *models.File) error {
|
||||||
quotedFileName := c.OSCommand.Quote(file.Name)
|
quotedFileName := c.OSCommand.Quote(file.Name)
|
||||||
return c.RunCommand("git checkout -- %s", quotedFileName)
|
return c.Run(c.NewCmdObj("git checkout -- " + quotedFileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore adds a file to the gitignore for the repo
|
// Ignore adds a file to the gitignore for the repo
|
||||||
|
@ -197,11 +199,11 @@ func (c *GitCommand) Ignore(filename string) error {
|
||||||
// WorktreeFileDiff returns the diff of a file
|
// WorktreeFileDiff returns the diff of a file
|
||||||
func (c *GitCommand) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string {
|
func (c *GitCommand) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string {
|
||||||
// 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(c.WorktreeFileDiffCmdStr(file, plain, cached, ignoreWhitespace))
|
s, _ := c.OSCommand.RunWithOutput(c.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace))
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) string {
|
func (c *GitCommand) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||||
cachedArg := ""
|
cachedArg := ""
|
||||||
trackedArg := "--"
|
trackedArg := "--"
|
||||||
colorArg := c.colorArg()
|
colorArg := c.colorArg()
|
||||||
|
@ -221,7 +223,9 @@ func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cache
|
||||||
ignoreWhitespaceArg = "--ignore-all-space"
|
ignoreWhitespaceArg = "--ignore-all-space"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s %s %s %s %s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath)
|
cmdStr := fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s %s %s %s %s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath)
|
||||||
|
|
||||||
|
return c.NewCmdObj(cmdStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
|
func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
|
||||||
|
@ -236,17 +240,17 @@ func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
|
||||||
flagStr += " --" + flag
|
flagStr += " --" + flag
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.RunCommand("git apply %s %s", flagStr, c.OSCommand.Quote(filepath))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git apply %s %s", flagStr, c.OSCommand.Quote(filepath))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
||||||
// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
|
// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
|
||||||
func (c *GitCommand) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) {
|
func (c *GitCommand) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) {
|
||||||
cmdStr := c.ShowFileDiffCmdStr(from, to, reverse, fileName, plain)
|
cmdObj := c.ShowFileDiffCmdObj(from, to, reverse, fileName, plain)
|
||||||
return c.OSCommand.RunCommandWithOutput(cmdStr)
|
return c.RunWithOutput(cmdObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fileName string, plain bool) string {
|
func (c *GitCommand) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
|
||||||
colorArg := c.colorArg()
|
colorArg := c.colorArg()
|
||||||
contextSize := c.Config.GetUserConfig().Git.DiffContextSize
|
contextSize := c.Config.GetUserConfig().Git.DiffContextSize
|
||||||
if plain {
|
if plain {
|
||||||
|
@ -258,12 +262,12 @@ func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fi
|
||||||
reverseFlag = " -R "
|
reverseFlag = " -R "
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s %s %s %s -- %s", contextSize, colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName))
|
return c.NewCmdObj(fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s %s %s %s -- %s", contextSize, colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckoutFile checks out the file for the given commit
|
// CheckoutFile checks out the file for the given commit
|
||||||
func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
|
func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
|
||||||
return c.RunCommand("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscardOldFileChanges discards changes to a file from an old commit
|
// DiscardOldFileChanges discards changes to a file from an old commit
|
||||||
|
@ -273,7 +277,7 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*models.Commit, commitIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||||
if err := c.RunCommand("git cat-file -e HEAD^:%s", c.OSCommand.Quote(fileName)); err != nil {
|
if err := c.Run(c.NewCmdObj("git cat-file -e HEAD^:" + c.OSCommand.Quote(fileName))); err != nil {
|
||||||
if err := c.OSCommand.Remove(fileName); err != nil {
|
if err := c.OSCommand.Remove(fileName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -296,17 +300,17 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*models.Commit, commitIndex
|
||||||
|
|
||||||
// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
|
// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
|
||||||
func (c *GitCommand) DiscardAnyUnstagedFileChanges() error {
|
func (c *GitCommand) DiscardAnyUnstagedFileChanges() error {
|
||||||
return c.RunCommand("git checkout -- .")
|
return c.Run(c.NewCmdObj("git checkout -- ."))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
|
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
|
||||||
func (c *GitCommand) RemoveTrackedFiles(name string) error {
|
func (c *GitCommand) RemoveTrackedFiles(name string) error {
|
||||||
return c.RunCommand("git rm -r --cached -- %s", c.OSCommand.Quote(name))
|
return c.Run(c.NewCmdObj("git rm -r --cached -- " + c.OSCommand.Quote(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUntrackedFiles runs `git clean -fd`
|
// RemoveUntrackedFiles runs `git clean -fd`
|
||||||
func (c *GitCommand) RemoveUntrackedFiles() error {
|
func (c *GitCommand) RemoveUntrackedFiles() error {
|
||||||
return c.RunCommand("git clean -fd")
|
return c.Run(c.NewCmdObj("git clean -fd"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetAndClean removes all unstaged changes and removes all untracked files
|
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||||
|
@ -346,7 +350,7 @@ func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, er
|
||||||
editor = c.OSCommand.Getenv("EDITOR")
|
editor = c.OSCommand.Getenv("EDITOR")
|
||||||
}
|
}
|
||||||
if editor == "" {
|
if editor == "" {
|
||||||
if err := c.OSCommand.RunCommand("which vi"); err == nil {
|
if err := c.OSCommand.Run(c.NewCmdObj("which vi")); err == nil {
|
||||||
editor = "vi"
|
editor = "vi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,22 +223,22 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
||||||
return osCommand.RunCommand("git rev-parse --git-dir")
|
return osCommand.Run(osCommand.NewCmdObj("git rev-parse --git-dir"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) RunCommand(formatString string, formatArgs ...interface{}) error {
|
func (c *GitCommand) Run(cmdObj oscommands.ICmdObj) error {
|
||||||
_, err := c.RunCommandWithOutput(formatString, formatArgs...)
|
_, err := c.RunWithOutput(cmdObj)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) RunCommandWithOutput(formatString string, formatArgs ...interface{}) (string, error) {
|
func (c *GitCommand) RunWithOutput(cmdObj oscommands.ICmdObj) (string, error) {
|
||||||
// TODO: have this retry logic in other places we run the command
|
// TODO: have this retry logic in other places we run the command
|
||||||
waitTime := 50 * time.Millisecond
|
waitTime := 50 * time.Millisecond
|
||||||
retryCount := 5
|
retryCount := 5
|
||||||
attempt := 0
|
attempt := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
output, err := c.OSCommand.RunCommandWithOutput(formatString, formatArgs...)
|
output, err := c.OSCommand.RunWithOutput(cmdObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if we have an error based on the index lock, we should wait a bit and then retry
|
// if we have an error based on the index lock, we should wait a bit and then retry
|
||||||
if strings.Contains(output, ".git/index.lock") {
|
if strings.Contains(output, ".git/index.lock") {
|
||||||
|
@ -255,6 +255,12 @@ func (c *GitCommand) RunCommandWithOutput(formatString string, formatArgs ...int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) NewCmdObjFromStr(cmdStr string) oscommands.ICmdObj {
|
func (c *GitCommand) NewCmdObj(cmdStr string) oscommands.ICmdObj {
|
||||||
return c.OSCommand.NewCmdObjFromStr(cmdStr).AddEnvVars("GIT_OPTIONAL_LOCKS=0")
|
return c.OSCommand.NewCmdObj(cmdStr).AddEnvVars("GIT_OPTIONAL_LOCKS=0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) NewCmdObjWithLog(cmdStr string) oscommands.ICmdObj {
|
||||||
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
|
c.OSCommand.LogCmdObj(cmdObj)
|
||||||
|
return cmdObj
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand, reflogCommi
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainBranches() []*models.Branch {
|
func (b *BranchListBuilder) obtainBranches() []*models.Branch {
|
||||||
cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`
|
cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`
|
||||||
output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr)
|
output, err := b.GitCommand.RunWithOutput(b.GitCommand.NewCmdObj(cmdStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
@ -13,7 +14,7 @@ func (c *GitCommand) GetFilesInDiff(from string, to string, reverse bool) ([]*mo
|
||||||
reverseFlag = " -R "
|
reverseFlag = " -R "
|
||||||
}
|
}
|
||||||
|
|
||||||
filenames, err := c.RunCommandWithOutput("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)
|
filenames, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -153,9 +152,9 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
|
||||||
passedFirstPushedCommit = true
|
passedFirstPushedCommit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := c.getLogCmd(opts)
|
cmdObj := c.getLogCmd(opts)
|
||||||
|
|
||||||
err = oscommands.RunLineOutputCmd(cmd, func(line string) (bool, error) {
|
err = c.OSCommand.RunLineOutputCmd(cmdObj, func(line string) (bool, error) {
|
||||||
if canExtractCommit(line) {
|
if canExtractCommit(line) {
|
||||||
commit := c.extractCommitFromLine(line)
|
commit := c.extractCommitFromLine(line)
|
||||||
if commit.Sha == firstPushedCommit {
|
if commit.Sha == firstPushedCommit {
|
||||||
|
@ -201,7 +200,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
|
||||||
cmd := c.OSCommand.ExecutableFromString(
|
cmdObj := c.OSCommand.NewCmdObj(
|
||||||
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,7 +211,7 @@ 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 = oscommands.RunLineOutputCmd(cmd, func(line string) (bool, error) {
|
err = c.OSCommand.RunLineOutputCmd(cmdObj, func(line string) (bool, error) {
|
||||||
if canExtractCommit(line) {
|
if canExtractCommit(line) {
|
||||||
commit := c.extractCommitFromLine(line)
|
commit := c.extractCommitFromLine(line)
|
||||||
matchingCommit := commits[i]
|
matchingCommit := commits[i]
|
||||||
|
@ -374,7 +373,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.OSCommand.RunCommandWithOutput("git merge-base %s %s", c.OSCommand.Quote(refName), c.OSCommand.Quote(baseBranch))
|
output, _ := c.OSCommand.RunWithOutput(c.OSCommand.NewCmdObj(fmt.Sprintf("git merge-base %s %s", c.OSCommand.Quote(refName), c.OSCommand.Quote(baseBranch))))
|
||||||
return ignoringWarnings(output), nil
|
return ignoringWarnings(output), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +390,7 @@ 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 (c *CommitListBuilder) getFirstPushedCommit(refName string) (string, error) {
|
||||||
output, err := c.OSCommand.RunCommandWithOutput("git merge-base %s %s@{u}", c.OSCommand.Quote(refName), c.OSCommand.Quote(refName))
|
output, err := c.OSCommand.RunWithOutput(c.OSCommand.NewCmdObj(fmt.Sprintf("git merge-base %s %s@{u}", c.OSCommand.Quote(refName), c.OSCommand.Quote(refName))))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -400,7 +399,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) *exec.Cmd {
|
func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||||
limitFlag := ""
|
limitFlag := ""
|
||||||
if opts.Limit {
|
if opts.Limit {
|
||||||
limitFlag = "-300"
|
limitFlag = "-300"
|
||||||
|
@ -419,7 +418,7 @@ func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) *exec.Cmd {
|
||||||
allFlag = " --all"
|
allFlag = " --all"
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.ExecutableFromString(
|
return c.OSCommand.NewCmdObj(
|
||||||
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.OSCommand.Quote(opts.RefName),
|
c.OSCommand.Quote(opts.RefName),
|
||||||
|
|
|
@ -80,7 +80,7 @@ func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||||
noRenamesFlag = "--no-renames"
|
noRenamesFlag = "--no-renames"
|
||||||
}
|
}
|
||||||
|
|
||||||
statusLines, err := c.RunCommandWithOutput("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)
|
statusLines, err := c.RunWithOutput(c.NewCmdObj(fmt.Sprintf("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []FileStatus{}, err
|
return []FileStatus{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
||||||
|
@ -19,9 +18,9 @@ func (c *GitCommand) GetReflogCommits(lastReflogCommit *models.Commit, filterPat
|
||||||
filterPathArg = fmt.Sprintf(" --follow -- %s", c.OSCommand.Quote(filterPath))
|
filterPathArg = fmt.Sprintf(" --follow -- %s", c.OSCommand.Quote(filterPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := c.OSCommand.ExecutableFromString(fmt.Sprintf(`git log -g --abbrev=20 --format="%%h %%ct %%gs" %s`, filterPathArg))
|
cmdObj := c.OSCommand.NewCmdObj(fmt.Sprintf(`git log -g --abbrev=20 --format="%%h %%ct %%gs" %s`, filterPathArg))
|
||||||
onlyObtainedNewReflogCommits := false
|
onlyObtainedNewReflogCommits := false
|
||||||
err := oscommands.RunLineOutputCmd(cmd, func(line string) (bool, error) {
|
err := c.OSCommand.RunLineOutputCmd(cmdObj, 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
|
||||||
|
|
|
@ -10,9 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *GitCommand) GetRemotes() ([]*models.Remote, error) {
|
func (c *GitCommand) GetRemotes() ([]*models.Remote, error) {
|
||||||
// get remote branches
|
remoteBranchesStr, err := c.RunWithOutput(c.NewCmdObj("git branch -r"))
|
||||||
unescaped := "git branch -r"
|
|
||||||
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput(unescaped)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *GitCommand) getUnfilteredStashEntries() []*models.StashEntry {
|
func (c *GitCommand) getUnfilteredStashEntries() []*models.StashEntry {
|
||||||
unescaped := "git stash list --pretty='%gs'"
|
rawString, _ := c.RunWithOutput(c.NewCmdObj("git stash list --pretty='%gs'"))
|
||||||
rawString, _ := c.OSCommand.RunCommandWithOutput(unescaped)
|
|
||||||
stashEntries := []*models.StashEntry{}
|
stashEntries := []*models.StashEntry{}
|
||||||
for i, line := range utils.SplitLines(rawString) {
|
for i, line := range utils.SplitLines(rawString) {
|
||||||
stashEntries = append(stashEntries, stashEntryFromLine(line, i))
|
stashEntries = append(stashEntries, stashEntryFromLine(line, i))
|
||||||
|
@ -25,7 +24,7 @@ func (c *GitCommand) GetStashEntries(filterPath string) []*models.StashEntry {
|
||||||
return c.getUnfilteredStashEntries()
|
return c.getUnfilteredStashEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
rawString, err := c.RunCommandWithOutput("git stash list --name-only")
|
rawString, err := c.RunWithOutput(c.NewCmdObj("git stash list --name-only"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.getUnfilteredStashEntries()
|
return c.getUnfilteredStashEntries()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
func (c *GitCommand) GetTags() ([]*models.Tag, error) {
|
func (c *GitCommand) GetTags() ([]*models.Tag, error) {
|
||||||
// get remote branches, sorted by creation date (descending)
|
// get remote branches, sorted by creation date (descending)
|
||||||
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
|
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
|
||||||
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput(`git tag --list --sort=-creatordate`)
|
remoteBranchesStr, err := c.OSCommand.RunWithOutput(c.NewCmdObj(`git tag --list --sort=-creatordate`))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ type ICmdObj interface {
|
||||||
GetCmd() *exec.Cmd
|
GetCmd() *exec.Cmd
|
||||||
ToString() string
|
ToString() string
|
||||||
AddEnvVars(...string) ICmdObj
|
AddEnvVars(...string) ICmdObj
|
||||||
|
GetEnvVars() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CmdObj struct {
|
type CmdObj struct {
|
||||||
|
@ -31,3 +32,7 @@ func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CmdObj) GetEnvVars() []string {
|
||||||
|
return self.cmd.Env
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
|
||||||
|
@ -29,14 +30,25 @@ type Platform struct {
|
||||||
OpenLinkCommand string
|
OpenLinkCommand string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ICommander interface {
|
||||||
|
Run(ICmdObj) error
|
||||||
|
RunWithOutput(ICmdObj) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RealCommander struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RealCommander) Run(cmdObj ICmdObj) error {
|
||||||
|
return cmdObj.GetCmd().Run()
|
||||||
|
}
|
||||||
|
|
||||||
// OSCommand holds all the os commands
|
// OSCommand holds all the os commands
|
||||||
type OSCommand struct {
|
type OSCommand struct {
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
Platform *Platform
|
Platform *Platform
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
Command func(string, ...string) *exec.Cmd
|
Command func(string, ...string) *exec.Cmd
|
||||||
BeforeExecuteCmd func(*exec.Cmd)
|
Getenv func(string) string
|
||||||
Getenv func(string) string
|
|
||||||
|
|
||||||
// callback to run before running a command, i.e. for the purposes of logging
|
// callback to run before running a command, i.e. for the purposes of logging
|
||||||
onRunCommand func(CmdLogEntry)
|
onRunCommand func(CmdLogEntry)
|
||||||
|
@ -45,6 +57,8 @@ type OSCommand struct {
|
||||||
CmdLogSpan string
|
CmdLogSpan string
|
||||||
|
|
||||||
removeFile func(string) error
|
removeFile func(string) error
|
||||||
|
|
||||||
|
IRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make these fields private
|
// TODO: make these fields private
|
||||||
|
@ -79,15 +93,17 @@ func NewCmdLogEntry(cmdStr string, span string, commandLine bool) CmdLogEntry {
|
||||||
|
|
||||||
// NewOSCommand os command runner
|
// NewOSCommand os command runner
|
||||||
func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand {
|
func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand {
|
||||||
return &OSCommand{
|
c := &OSCommand{
|
||||||
Log: log,
|
Log: log,
|
||||||
Platform: getPlatform(),
|
Platform: getPlatform(),
|
||||||
Config: config,
|
Config: config,
|
||||||
Command: secureexec.Command,
|
Command: secureexec.Command,
|
||||||
BeforeExecuteCmd: func(*exec.Cmd) {},
|
Getenv: os.Getenv,
|
||||||
Getenv: os.Getenv,
|
removeFile: os.RemoveAll,
|
||||||
removeFile: os.RemoveAll,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.IRunner = &RealRunner{c: c}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) WithSpan(span string) *OSCommand {
|
func (c *OSCommand) WithSpan(span string) *OSCommand {
|
||||||
|
@ -104,8 +120,8 @@ func (c *OSCommand) WithSpan(span string) *OSCommand {
|
||||||
return newOSCommand
|
return newOSCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) LogExecCmd(cmd *exec.Cmd) {
|
func (c *OSCommand) LogCmdObj(cmdObj ICmdObj) {
|
||||||
c.LogCommand(strings.Join(cmd.Args, " "), true)
|
c.LogCommand(cmdObj.ToString(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) LogCommand(cmdStr string, commandLine bool) {
|
func (c *OSCommand) LogCommand(cmdStr string, commandLine bool) {
|
||||||
|
@ -131,108 +147,6 @@ func (c *OSCommand) SetRemoveFile(f func(string) error) {
|
||||||
c.removeFile = f
|
c.removeFile = f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) SetBeforeExecuteCmd(cmd func(*exec.Cmd)) {
|
|
||||||
c.BeforeExecuteCmd = cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunCommandOptions struct {
|
|
||||||
EnvVars []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OSCommand) RunCommandWithOutputWithOptions(command string, options RunCommandOptions) (string, error) {
|
|
||||||
c.LogCommand(command, true)
|
|
||||||
cmd := c.ExecutableFromString(command)
|
|
||||||
|
|
||||||
cmd.Env = append(cmd.Env, "GIT_TERMINAL_PROMPT=0") // prevents git from prompting us for input which would freeze the program
|
|
||||||
cmd.Env = append(cmd.Env, options.EnvVars...)
|
|
||||||
|
|
||||||
return sanitisedCommandOutput(cmd.CombinedOutput())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OSCommand) RunCommandWithOptions(command string, options RunCommandOptions) error {
|
|
||||||
_, err := c.RunCommandWithOutputWithOptions(command, options)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunCommandWithOutput wrapper around commands returning their output and error
|
|
||||||
// NOTE: If you don't pass any formatArgs we'll just use the command directly,
|
|
||||||
// however there's a bizarre compiler error/warning when you pass in a formatString
|
|
||||||
// with a percent sign because it thinks it's supposed to be a formatString when
|
|
||||||
// in that case it's not. To get around that error you'll need to define the string
|
|
||||||
// in a variable and pass the variable into RunCommandWithOutput.
|
|
||||||
func (c *OSCommand) RunCommandWithOutput(formatString string, formatArgs ...interface{}) (string, error) {
|
|
||||||
command := formatString
|
|
||||||
if formatArgs != nil {
|
|
||||||
command = fmt.Sprintf(formatString, formatArgs...)
|
|
||||||
}
|
|
||||||
cmd := c.ExecutableFromString(command)
|
|
||||||
c.LogExecCmd(cmd)
|
|
||||||
output, err := sanitisedCommandOutput(cmd.CombinedOutput())
|
|
||||||
if err != nil {
|
|
||||||
c.Log.WithField("command", command).Error(output)
|
|
||||||
}
|
|
||||||
return output, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunExecutableWithOutput runs an executable file and returns its output
|
|
||||||
func (c *OSCommand) RunExecutableWithOutput(cmd *exec.Cmd) (string, error) {
|
|
||||||
c.LogExecCmd(cmd)
|
|
||||||
c.BeforeExecuteCmd(cmd)
|
|
||||||
return sanitisedCommandOutput(cmd.CombinedOutput())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunExecutable runs an executable file and returns an error if there was one
|
|
||||||
func (c *OSCommand) RunExecutable(cmd *exec.Cmd) error {
|
|
||||||
_, err := c.RunExecutableWithOutput(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecutableFromString takes a string like `git status` and returns an executable command for it
|
|
||||||
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
|
|
||||||
splitCmd := str.ToArgv(commandStr)
|
|
||||||
cmd := c.Command(splitCmd[0], splitCmd[1:]...)
|
|
||||||
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShellCommandFromString takes a string like `git commit` and returns an executable shell command for it
|
|
||||||
func (c *OSCommand) ShellCommandFromString(commandStr string) *exec.Cmd {
|
|
||||||
quotedCommand := ""
|
|
||||||
// Windows does not seem to like quotes around the command
|
|
||||||
if c.Platform.OS == "windows" {
|
|
||||||
quotedCommand = strings.NewReplacer(
|
|
||||||
"^", "^^",
|
|
||||||
"&", "^&",
|
|
||||||
"|", "^|",
|
|
||||||
"<", "^<",
|
|
||||||
">", "^>",
|
|
||||||
"%", "^%",
|
|
||||||
).Replace(commandStr)
|
|
||||||
} else {
|
|
||||||
quotedCommand = c.Quote(commandStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
shellCommand := fmt.Sprintf("%s %s %s", c.Platform.Shell, c.Platform.ShellArg, quotedCommand)
|
|
||||||
return c.ExecutableFromString(shellCommand)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunCommand runs a command and just returns the error
|
|
||||||
func (c *OSCommand) RunCommand(formatString string, formatArgs ...interface{}) error {
|
|
||||||
_, err := c.RunCommandWithOutput(formatString, formatArgs...)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunShellCommand runs shell commands i.e. 'sh -c <command>'. Good for when you
|
|
||||||
// need access to the shell
|
|
||||||
func (c *OSCommand) RunShellCommand(command string) error {
|
|
||||||
cmd := c.ShellCommandFromString(command)
|
|
||||||
c.LogExecCmd(cmd)
|
|
||||||
|
|
||||||
_, err := sanitisedCommandOutput(cmd.CombinedOutput())
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileType tells us if the file is a file, directory or other
|
// FileType tells us if the file is a file, directory or other
|
||||||
func (c *OSCommand) FileType(path string) string {
|
func (c *OSCommand) FileType(path string) string {
|
||||||
fileInfo, err := os.Stat(path)
|
fileInfo, err := os.Stat(path)
|
||||||
|
@ -245,19 +159,6 @@ func (c *OSCommand) FileType(path string) string {
|
||||||
return "file"
|
return "file"
|
||||||
}
|
}
|
||||||
|
|
||||||
func sanitisedCommandOutput(output []byte, err error) (string, error) {
|
|
||||||
outputString := string(output)
|
|
||||||
if err != nil {
|
|
||||||
// errors like 'exit status 1' are not very useful so we'll create an error
|
|
||||||
// from the combined output
|
|
||||||
if outputString == "" {
|
|
||||||
return "", utils.WrapError(err)
|
|
||||||
}
|
|
||||||
return outputString, errors.New(outputString)
|
|
||||||
}
|
|
||||||
return outputString, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenFile opens a file with the given
|
// OpenFile opens a file with the given
|
||||||
func (c *OSCommand) OpenFile(filename string) error {
|
func (c *OSCommand) OpenFile(filename string) error {
|
||||||
commandTemplate := c.Config.GetUserConfig().OS.OpenCommand
|
commandTemplate := c.Config.GetUserConfig().OS.OpenCommand
|
||||||
|
@ -265,7 +166,7 @@ func (c *OSCommand) OpenFile(filename string) error {
|
||||||
"filename": c.Quote(filename),
|
"filename": c.Quote(filename),
|
||||||
}
|
}
|
||||||
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
||||||
err := c.RunShellCommand(command)
|
err := c.Run(c.NewShellCmdObjFromString(command))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,26 +179,10 @@ func (c *OSCommand) OpenLink(link string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
||||||
err := c.RunShellCommand(command)
|
err := c.Run(c.NewShellCmdObjFromString(command))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareSubProcess iniPrepareSubProcessrocess then tells the Gui to switch to it
|
|
||||||
// TODO: see if this needs to exist, given that ExecutableFromString does the same things
|
|
||||||
func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *exec.Cmd {
|
|
||||||
cmd := c.Command(cmdName, commandArgs...)
|
|
||||||
if cmd != nil {
|
|
||||||
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
|
|
||||||
}
|
|
||||||
c.LogExecCmd(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareShellSubProcess returns the pointer to a custom command
|
|
||||||
func (c *OSCommand) PrepareShellSubProcess(command string) *exec.Cmd {
|
|
||||||
return c.PrepareSubProcess(c.Platform.Shell, c.Platform.ShellArg, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quote wraps a message in platform-specific quotation marks
|
// Quote wraps a message in platform-specific quotation marks
|
||||||
func (c *OSCommand) Quote(message string) string {
|
func (c *OSCommand) Quote(message string) string {
|
||||||
var quote string
|
var quote string
|
||||||
|
@ -390,24 +275,6 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunPreparedCommand takes a pointer to an exec.Cmd and runs it
|
|
||||||
// this is useful if you need to give your command some environment variables
|
|
||||||
// before running it
|
|
||||||
func (c *OSCommand) RunPreparedCommand(cmd *exec.Cmd) error {
|
|
||||||
c.BeforeExecuteCmd(cmd)
|
|
||||||
c.LogExecCmd(cmd)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
outString := string(out)
|
|
||||||
c.Log.Info(outString)
|
|
||||||
if err != nil {
|
|
||||||
if len(outString) == 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.New(outString)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLazygitPath returns the path of the currently executed file
|
// GetLazygitPath returns the path of the currently executed file
|
||||||
func (c *OSCommand) GetLazygitPath() string {
|
func (c *OSCommand) GetLazygitPath() string {
|
||||||
ex, err := os.Executable() // get the executable path for git to use
|
ex, err := os.Executable() // get the executable path for git to use
|
||||||
|
@ -426,7 +293,7 @@ func (c *OSCommand) PipeCommands(commandStrings ...string) error {
|
||||||
logCmdStr += " | "
|
logCmdStr += " | "
|
||||||
}
|
}
|
||||||
logCmdStr += str
|
logCmdStr += str
|
||||||
cmds[i] = c.ExecutableFromString(str)
|
cmds[i] = c.NewCmdObj(str).GetCmd()
|
||||||
}
|
}
|
||||||
c.LogCommand(logCmdStr, true)
|
c.LogCommand(logCmdStr, true)
|
||||||
|
|
||||||
|
@ -489,7 +356,107 @@ func Kill(cmd *exec.Cmd) error {
|
||||||
return cmd.Process.Kill()
|
return cmd.Process.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunLineOutputCmd(cmd *exec.Cmd, onLine func(line string) (bool, error)) error {
|
func (c *OSCommand) CopyToClipboard(str string) error {
|
||||||
|
escaped := strings.Replace(str, "\n", "\\n", -1)
|
||||||
|
truncated := utils.TruncateWithEllipsis(escaped, 40)
|
||||||
|
c.LogCommand(fmt.Sprintf("Copying '%s' to clipboard", truncated), false)
|
||||||
|
return clipboard.WriteAll(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *OSCommand) RemoveFile(path string) error {
|
||||||
|
c.LogCommand(fmt.Sprintf("Deleting path '%s'", path), false)
|
||||||
|
|
||||||
|
return c.removeFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// builders
|
||||||
|
|
||||||
|
func (c *OSCommand) NewCmdObj(cmdStr string) ICmdObj {
|
||||||
|
args := str.ToArgv(cmdStr)
|
||||||
|
cmd := c.Command(args[0], args[1:]...)
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
|
||||||
|
return &CmdObj{
|
||||||
|
cmdStr: cmdStr,
|
||||||
|
cmd: cmd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *OSCommand) NewCmdObjFromArgs(args []string) ICmdObj {
|
||||||
|
cmd := c.Command(args[0], args[1:]...)
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
|
||||||
|
return &CmdObj{
|
||||||
|
cmdStr: strings.Join(args, " "),
|
||||||
|
cmd: cmd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewShellCmdObjFromString takes a string like `git commit` and returns an executable shell command for it
|
||||||
|
func (c *OSCommand) NewShellCmdObjFromString(commandStr string) ICmdObj {
|
||||||
|
quotedCommand := ""
|
||||||
|
// Windows does not seem to like quotes around the command
|
||||||
|
if c.Platform.OS == "windows" {
|
||||||
|
quotedCommand = strings.NewReplacer(
|
||||||
|
"^", "^^",
|
||||||
|
"&", "^&",
|
||||||
|
"|", "^|",
|
||||||
|
"<", "^<",
|
||||||
|
">", "^>",
|
||||||
|
"%", "^%",
|
||||||
|
).Replace(commandStr)
|
||||||
|
} else {
|
||||||
|
quotedCommand = c.Quote(commandStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
shellCommand := fmt.Sprintf("%s %s %s", c.Platform.Shell, c.Platform.ShellArg, quotedCommand)
|
||||||
|
return c.NewCmdObj(shellCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: pick one of NewShellCmdObjFromString2 and ShellCommandFromString to use. I'm not sure
|
||||||
|
// which one actually is better, but I suspect it's NewShellCmdObjFromString2
|
||||||
|
func (c *OSCommand) NewShellCmdObjFromString2(command string) ICmdObj {
|
||||||
|
return c.NewCmdObjFromArgs([]string{c.Platform.Shell, c.Platform.ShellArg, command})
|
||||||
|
}
|
||||||
|
|
||||||
|
// runners
|
||||||
|
|
||||||
|
type IRunner interface {
|
||||||
|
Run(cmdObj ICmdObj) error
|
||||||
|
RunWithOutput(cmdObj ICmdObj) (string, error)
|
||||||
|
RunLineOutputCmd(cmdObj ICmdObj, onLine func(line string) (bool, error)) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunExpectation func(ICmdObj) (string, error)
|
||||||
|
|
||||||
|
type FakeRunner struct {
|
||||||
|
expectations []RunExpectation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RealRunner) Run(cmdObj ICmdObj) error {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type RealRunner struct {
|
||||||
|
c *OSCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RealRunner) Run(cmdObj ICmdObj) error {
|
||||||
|
_, err := self.RunWithOutput(cmdObj)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RealRunner) RunWithOutput(cmdObj ICmdObj) (string, error) {
|
||||||
|
self.c.LogCmdObj(cmdObj)
|
||||||
|
output, err := sanitisedCommandOutput(cmdObj.GetCmd().CombinedOutput())
|
||||||
|
if err != nil {
|
||||||
|
self.c.Log.WithField("command", cmdObj.ToString()).Error(output)
|
||||||
|
}
|
||||||
|
return output, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RealRunner) RunLineOutputCmd(cmdObj ICmdObj, onLine func(line string) (bool, error)) error {
|
||||||
|
cmd := cmdObj.GetCmd()
|
||||||
stdoutPipe, err := cmd.StdoutPipe()
|
stdoutPipe, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -518,42 +485,15 @@ func RunLineOutputCmd(cmd *exec.Cmd, onLine func(line string) (bool, error)) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) CopyToClipboard(str string) error {
|
func sanitisedCommandOutput(output []byte, err error) (string, error) {
|
||||||
escaped := strings.Replace(str, "\n", "\\n", -1)
|
outputString := string(output)
|
||||||
truncated := utils.TruncateWithEllipsis(escaped, 40)
|
if err != nil {
|
||||||
c.LogCommand(fmt.Sprintf("Copying '%s' to clipboard", truncated), false)
|
// errors like 'exit status 1' are not very useful so we'll create an error
|
||||||
return clipboard.WriteAll(str)
|
// from the combined output
|
||||||
}
|
if outputString == "" {
|
||||||
|
return "", utils.WrapError(err)
|
||||||
func (c *OSCommand) RemoveFile(path string) error {
|
}
|
||||||
c.LogCommand(fmt.Sprintf("Deleting path '%s'", path), false)
|
return outputString, errors.New(outputString)
|
||||||
|
|
||||||
return c.removeFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OSCommand) NewCmdObjFromStr(cmdStr string) ICmdObj {
|
|
||||||
args := str.ToArgv(cmdStr)
|
|
||||||
cmd := c.Command(args[0], args[1:]...)
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
|
|
||||||
return &CmdObj{
|
|
||||||
cmdStr: cmdStr,
|
|
||||||
cmd: cmd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OSCommand) NewCmdObjFromArgs(args []string) ICmdObj {
|
|
||||||
cmd := c.Command(args[0], args[1:]...)
|
|
||||||
|
|
||||||
return &CmdObj{
|
|
||||||
cmdStr: strings.Join(args, " "),
|
|
||||||
cmd: cmd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OSCommand) NewCmdObj(cmd *exec.Cmd) ICmdObj {
|
|
||||||
return &CmdObj{
|
|
||||||
cmdStr: strings.Join(cmd.Args, " "),
|
|
||||||
cmd: cmd,
|
|
||||||
}
|
}
|
||||||
|
return outputString, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestOSCommandRunCommandWithOutput is a function.
|
// TestOSCommandRunWithOutput is a function.
|
||||||
func TestOSCommandRunCommandWithOutput(t *testing.T) {
|
func TestOSCommandRunWithOutput(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
command string
|
command string
|
||||||
test func(string, error)
|
test func(string, error)
|
||||||
|
@ -32,12 +32,13 @@ func TestOSCommandRunCommandWithOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
s.test(NewDummyOSCommand().RunCommandWithOutput(s.command))
|
c := NewDummyOSCommand()
|
||||||
|
s.test(NewDummyOSCommand().RunWithOutput(c.NewCmdObj(s.command)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestOSCommandRunCommand is a function.
|
// TestOSCommandRun is a function.
|
||||||
func TestOSCommandRunCommand(t *testing.T) {
|
func TestOSCommandRun(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
command string
|
command string
|
||||||
test func(error)
|
test func(error)
|
||||||
|
@ -53,7 +54,8 @@ func TestOSCommandRunCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
s.test(NewDummyOSCommand().RunCommand(s.command))
|
c := NewDummyOSCommand()
|
||||||
|
s.test(c.Run(c.NewCmdObj(s.command)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (c *GitCommand) MovePatchToSelectedCommit(commits []*models.Commit, sourceC
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.OSCommand.RunPreparedCommand(cmd); err != nil {
|
if err := c.OSCommand.Run(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ func (c *GitCommand) PullPatchIntoNewCommit(commits []*models.Commit, commitIdx
|
||||||
|
|
||||||
head_message, _ := c.GetHeadCommitMessage()
|
head_message, _ := c.GetHeadCommitMessage()
|
||||||
new_message := fmt.Sprintf("Split from \"%s\"", head_message)
|
new_message := fmt.Sprintf("Split from \"%s\"", head_message)
|
||||||
err := c.OSCommand.RunCommand(c.CommitCmdStr(new_message, ""))
|
err := c.OSCommand.Run(c.CommitCmdObj(new_message, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,15 @@ package commands
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/mgutz/str"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *GitCommand) RewordCommit(commits []*models.Commit, index int) (*exec.Cmd, error) {
|
func (c *GitCommand) RewordCommit(commits []*models.Commit, index int) (oscommands.ICmdObj, error) {
|
||||||
todo, sha, err := c.GenerateGenericRebaseTodo(commits, index, "reword")
|
todo, sha, err := c.GenerateGenericRebaseTodo(commits, index, "reword")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -40,7 +38,7 @@ func (c *GitCommand) MoveCommitDown(commits []*models.Commit, index int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunPreparedCommand(cmd)
|
return c.OSCommand.Run(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) InteractiveRebase(commits []*models.Commit, index int, action string) error {
|
func (c *GitCommand) InteractiveRebase(commits []*models.Commit, index int, action string) error {
|
||||||
|
@ -54,13 +52,13 @@ func (c *GitCommand) InteractiveRebase(commits []*models.Commit, index int, acti
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunPreparedCommand(cmd)
|
return c.OSCommand.Run(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
|
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
|
||||||
// we tell git to run lazygit to edit the todo list, and we pass the client
|
// we tell git to run lazygit to edit the todo list, and we pass the client
|
||||||
// lazygit a todo string to write to the todo file
|
// lazygit a todo string to write to the todo file
|
||||||
func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string, overrideEditor bool) (*exec.Cmd, error) {
|
func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string, overrideEditor bool) (oscommands.ICmdObj, error) {
|
||||||
ex := c.OSCommand.GetLazygitPath()
|
ex := c.OSCommand.GetLazygitPath()
|
||||||
|
|
||||||
debug := "FALSE"
|
debug := "FALSE"
|
||||||
|
@ -70,9 +68,8 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
|
||||||
|
|
||||||
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty %s", baseSha)
|
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty %s", baseSha)
|
||||||
c.Log.WithField("command", cmdStr).Info("RunCommand")
|
c.Log.WithField("command", cmdStr).Info("RunCommand")
|
||||||
splitCmd := str.ToArgv(cmdStr)
|
|
||||||
|
|
||||||
cmd := c.OSCommand.Command(splitCmd[0], splitCmd[1:]...)
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
|
|
||||||
gitSequenceEditor := ex
|
gitSequenceEditor := ex
|
||||||
if todo == "" {
|
if todo == "" {
|
||||||
|
@ -81,9 +78,7 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
|
||||||
c.OSCommand.LogCommand(fmt.Sprintf("Creating TODO file for interactive rebase: \n\n%s", todo), false)
|
c.OSCommand.LogCommand(fmt.Sprintf("Creating TODO file for interactive rebase: \n\n%s", todo), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Env = os.Environ()
|
cmdObj.AddEnvVars(
|
||||||
cmd.Env = append(
|
|
||||||
cmd.Env,
|
|
||||||
"LAZYGIT_CLIENT_COMMAND=INTERACTIVE_REBASE",
|
"LAZYGIT_CLIENT_COMMAND=INTERACTIVE_REBASE",
|
||||||
"LAZYGIT_REBASE_TODO="+todo,
|
"LAZYGIT_REBASE_TODO="+todo,
|
||||||
"DEBUG="+debug,
|
"DEBUG="+debug,
|
||||||
|
@ -93,10 +88,10 @@ func (c *GitCommand) PrepareInteractiveRebaseCommand(baseSha string, todo string
|
||||||
)
|
)
|
||||||
|
|
||||||
if overrideEditor {
|
if overrideEditor {
|
||||||
cmd.Env = append(cmd.Env, "GIT_EDITOR="+ex)
|
cmdObj.AddEnvVars("GIT_EDITOR=" + ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd, nil
|
return cmdObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GenerateGenericRebaseTodo(commits []*models.Commit, actionIndex int, action string) (string, string, error) {
|
func (c *GitCommand) GenerateGenericRebaseTodo(commits []*models.Commit, actionIndex int, action string) (string, string, error) {
|
||||||
|
@ -227,7 +222,7 @@ func (c *GitCommand) BeginInteractiveRebaseForCommit(commits []*models.Commit, c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.OSCommand.RunPreparedCommand(cmd); err != nil {
|
if err := c.OSCommand.Run(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +236,7 @@ func (c *GitCommand) RebaseBranch(branchName string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunPreparedCommand(cmd)
|
return c.OSCommand.Run(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenericMerge takes a commandType of "merge" or "rebase" and a command of "abort", "skip" or "continue"
|
// GenericMerge takes a commandType of "merge" or "rebase" and a command of "abort", "skip" or "continue"
|
||||||
|
@ -276,14 +271,13 @@ func (c *GitCommand) GenericMergeOrRebaseAction(commandType string, command stri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) runSkipEditorCommand(command string) error {
|
func (c *GitCommand) runSkipEditorCommand(command string) error {
|
||||||
cmd := c.OSCommand.ExecutableFromString(command)
|
cmdObj := c.OSCommand.NewCmdObj(command)
|
||||||
lazyGitPath := c.OSCommand.GetLazygitPath()
|
lazyGitPath := c.OSCommand.GetLazygitPath()
|
||||||
cmd.Env = append(
|
cmdObj.AddEnvVars(
|
||||||
cmd.Env,
|
|
||||||
"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY",
|
"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY",
|
||||||
"GIT_EDITOR="+lazyGitPath,
|
"GIT_EDITOR="+lazyGitPath,
|
||||||
"EDITOR="+lazyGitPath,
|
"EDITOR="+lazyGitPath,
|
||||||
"VISUAL="+lazyGitPath,
|
"VISUAL="+lazyGitPath,
|
||||||
)
|
)
|
||||||
return c.OSCommand.RunExecutable(cmd)
|
return c.OSCommand.Run(cmdObj)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/test"
|
"github.com/jesseduffield/lazygit/pkg/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -62,31 +63,31 @@ func TestGitCommandRebaseBranch(t *testing.T) {
|
||||||
func TestGitCommandSkipEditorCommand(t *testing.T) {
|
func TestGitCommandSkipEditorCommand(t *testing.T) {
|
||||||
cmd := NewDummyGitCommand()
|
cmd := NewDummyGitCommand()
|
||||||
|
|
||||||
cmd.OSCommand.SetBeforeExecuteCmd(func(cmd *exec.Cmd) {
|
cmd.OSCommand.SetBeforeExecuteCmd(func(cmdObj oscommands.ICmdObj) {
|
||||||
test.AssertContainsMatch(
|
test.AssertContainsMatch(
|
||||||
t,
|
t,
|
||||||
cmd.Env,
|
cmdObj.GetEnvVars(),
|
||||||
regexp.MustCompile("^VISUAL="),
|
regexp.MustCompile("^VISUAL="),
|
||||||
"expected VISUAL to be set for a non-interactive external command",
|
"expected VISUAL to be set for a non-interactive external command",
|
||||||
)
|
)
|
||||||
|
|
||||||
test.AssertContainsMatch(
|
test.AssertContainsMatch(
|
||||||
t,
|
t,
|
||||||
cmd.Env,
|
cmdObj.GetEnvVars(),
|
||||||
regexp.MustCompile("^EDITOR="),
|
regexp.MustCompile("^EDITOR="),
|
||||||
"expected EDITOR to be set for a non-interactive external command",
|
"expected EDITOR to be set for a non-interactive external command",
|
||||||
)
|
)
|
||||||
|
|
||||||
test.AssertContainsMatch(
|
test.AssertContainsMatch(
|
||||||
t,
|
t,
|
||||||
cmd.Env,
|
cmdObj.GetEnvVars(),
|
||||||
regexp.MustCompile("^GIT_EDITOR="),
|
regexp.MustCompile("^GIT_EDITOR="),
|
||||||
"expected GIT_EDITOR to be set for a non-interactive external command",
|
"expected GIT_EDITOR to be set for a non-interactive external command",
|
||||||
)
|
)
|
||||||
|
|
||||||
test.AssertContainsMatch(
|
test.AssertContainsMatch(
|
||||||
t,
|
t,
|
||||||
cmd.Env,
|
cmdObj.GetEnvVars(),
|
||||||
regexp.MustCompile("^LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY$"),
|
regexp.MustCompile("^LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY$"),
|
||||||
"expected LAZYGIT_CLIENT_COMMAND to be set for a non-interactive external command",
|
"expected LAZYGIT_CLIENT_COMMAND to be set for a non-interactive external command",
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,24 +7,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *GitCommand) AddRemote(name string, url string) error {
|
func (c *GitCommand) AddRemote(name string, url string) error {
|
||||||
return c.RunCommand("git remote add %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(url))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote add %s %s", c.OSCommand.Quote(name), c.OSCommand.Quote(url))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) RemoveRemote(name string) error {
|
func (c *GitCommand) RemoveRemote(name string) error {
|
||||||
return c.RunCommand("git remote remove %s", c.OSCommand.Quote(name))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote remove %s", c.OSCommand.Quote(name))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) RenameRemote(oldRemoteName string, newRemoteName string) error {
|
func (c *GitCommand) RenameRemote(oldRemoteName string, newRemoteName string) error {
|
||||||
return c.RunCommand("git remote rename %s %s", c.OSCommand.Quote(oldRemoteName), c.OSCommand.Quote(newRemoteName))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote rename %s %s", c.OSCommand.Quote(oldRemoteName), c.OSCommand.Quote(newRemoteName))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
|
func (c *GitCommand) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
|
||||||
return c.RunCommand("git remote set-url %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(updatedUrl))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git remote set-url %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(updatedUrl))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) DeleteRemoteBranch(remoteName string, branchName string, promptUserForCredential func(string) string) error {
|
func (c *GitCommand) DeleteRemoteBranch(remoteName string, branchName string, promptUserForCredential func(string) string) error {
|
||||||
command := fmt.Sprintf("git push %s --delete %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(branchName))
|
command := fmt.Sprintf("git push %s --delete %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(branchName))
|
||||||
cmdObj := c.NewCmdObjFromStr(command)
|
cmdObj := c.NewCmdObj(command)
|
||||||
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,10 +34,10 @@ func (c *GitCommand) DetectUnamePass(cmdObj oscommands.ICmdObj, promptUserForCre
|
||||||
|
|
||||||
// CheckRemoteBranchExists Returns remote branch
|
// CheckRemoteBranchExists Returns remote branch
|
||||||
func (c *GitCommand) CheckRemoteBranchExists(branchName string) bool {
|
func (c *GitCommand) CheckRemoteBranchExists(branchName string) bool {
|
||||||
_, err := c.OSCommand.RunCommandWithOutput(
|
_, err := c.RunWithOutput(c.NewCmdObj(
|
||||||
"git show-ref --verify -- refs/remotes/origin/%s",
|
fmt.Sprintf("git show-ref --verify -- refs/remotes/origin/%s",
|
||||||
c.OSCommand.Quote(branchName),
|
c.OSCommand.Quote(branchName),
|
||||||
)
|
)))
|
||||||
|
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ import "fmt"
|
||||||
|
|
||||||
// StashDo modify stash
|
// StashDo modify stash
|
||||||
func (c *GitCommand) StashDo(index int, method string) error {
|
func (c *GitCommand) StashDo(index int, method string) error {
|
||||||
return c.RunCommand("git stash %s stash@{%d}", method, index)
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git stash %s stash@{%d}", method, index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// StashSave save stash
|
// StashSave save stash
|
||||||
// TODO: before calling this, check if there is anything to save
|
// TODO: before calling this, check if there is anything to save
|
||||||
func (c *GitCommand) StashSave(message string) error {
|
func (c *GitCommand) StashSave(message string) error {
|
||||||
return c.RunCommand("git stash save %s", c.OSCommand.Quote(message))
|
return c.Run(c.NewCmdObj("git stash save " + c.OSCommand.Quote(message)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStashEntryDiff stash diff
|
// GetStashEntryDiff stash diff
|
||||||
|
@ -22,7 +22,7 @@ func (c *GitCommand) ShowStashEntryCmdStr(index int) string {
|
||||||
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
|
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
|
||||||
func (c *GitCommand) StashSaveStagedChanges(message string) error {
|
func (c *GitCommand) StashSaveStagedChanges(message string) error {
|
||||||
// wrap in 'writing', which uses a mutex
|
// wrap in 'writing', which uses a mutex
|
||||||
if err := c.RunCommand("git stash --keep-index"); err != nil {
|
if err := c.Run(c.NewCmdObj("git stash --keep-index")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ func (c *GitCommand) StashSaveStagedChanges(message string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.RunCommand("git stash apply stash@{1}"); err != nil {
|
if err := c.Run(c.NewCmdObj("git stash apply stash@{1}")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func (c *GitCommand) StashSaveStagedChanges(message string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.RunCommand("git stash drop stash@{1}"); err != nil {
|
if err := c.Run(c.NewCmdObj("git stash drop stash@{1}")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
)
|
)
|
||||||
|
|
||||||
// .gitmodules looks like this:
|
// .gitmodules looks like this:
|
||||||
|
@ -69,28 +71,28 @@ func (c *GitCommand) SubmoduleStash(submodule *models.SubmoduleConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.RunCommand("git -C %s stash --include-untracked", c.OSCommand.Quote(submodule.Path))
|
return c.Run(c.NewCmdObj("git -C " + c.OSCommand.Quote(submodule.Path) + " stash --include-untracked"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleReset(submodule *models.SubmoduleConfig) error {
|
func (c *GitCommand) SubmoduleReset(submodule *models.SubmoduleConfig) error {
|
||||||
return c.RunCommand("git submodule update --init --force -- %s", c.OSCommand.Quote(submodule.Path))
|
return c.Run(c.NewCmdObj("git submodule update --init --force -- " + c.OSCommand.Quote(submodule.Path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleUpdateAll() error {
|
func (c *GitCommand) SubmoduleUpdateAll() error {
|
||||||
// not doing an --init here because the user probably doesn't want that
|
// not doing an --init here because the user probably doesn't want that
|
||||||
return c.RunCommand("git submodule update --force")
|
return c.Run(c.NewCmdObj("git submodule update --force"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
|
func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
|
||||||
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
||||||
|
|
||||||
if err := c.RunCommand("git submodule deinit --force -- %s", c.OSCommand.Quote(submodule.Path)); err != nil {
|
if err := c.Run(c.NewCmdObj("git submodule deinit --force -- " + c.OSCommand.Quote(submodule.Path))); err != nil {
|
||||||
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
||||||
if err := c.RunCommand("git config --file .gitmodules --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
|
if err := c.Run(c.NewCmdObj("git config --file .gitmodules --remove-section submodule." + c.OSCommand.Quote(submodule.Name))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.RunCommand("git config --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
|
if err := c.Run(c.NewCmdObj("git config --remove-section submodule." + c.OSCommand.Quote(submodule.Name))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +102,7 @@ func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.RunCommand("git rm --force -r %s", submodule.Path); err != nil {
|
if err := c.Run(c.NewCmdObj("git rm --force -r " + submodule.Path)); err != nil {
|
||||||
// if the directory isn't there then that's fine
|
// if the directory isn't there then that's fine
|
||||||
c.Log.Error(err)
|
c.Log.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -109,21 +111,23 @@ func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleAdd(name string, path string, url string) error {
|
func (c *GitCommand) SubmoduleAdd(name string, path string, url string) error {
|
||||||
return c.OSCommand.RunCommand(
|
return c.OSCommand.Run(
|
||||||
"git submodule add --force --name %s -- %s %s ",
|
c.OSCommand.NewCmdObj(
|
||||||
c.OSCommand.Quote(name),
|
fmt.Sprintf(
|
||||||
c.OSCommand.Quote(url),
|
"git submodule add --force --name %s -- %s %s ",
|
||||||
c.OSCommand.Quote(path),
|
c.OSCommand.Quote(name),
|
||||||
)
|
c.OSCommand.Quote(url),
|
||||||
|
c.OSCommand.Quote(path),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string) error {
|
func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string) error {
|
||||||
// the set-url command is only for later git versions so we're doing it manually here
|
// the set-url command is only for later git versions so we're doing it manually here
|
||||||
if err := c.RunCommand("git config --file .gitmodules submodule.%s.url %s", c.OSCommand.Quote(name), c.OSCommand.Quote(newUrl)); err != nil {
|
if err := c.Run(c.NewCmdObj("git config --file .gitmodules submodule." + c.OSCommand.Quote(name) + ".url " + c.OSCommand.Quote(newUrl))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.RunCommand("git submodule sync -- %s", c.OSCommand.Quote(path)); err != nil {
|
if err := c.Run(c.NewCmdObj("git submodule sync -- " + c.OSCommand.Quote(path))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,27 +135,27 @@ func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleInit(path string) error {
|
func (c *GitCommand) SubmoduleInit(path string) error {
|
||||||
return c.RunCommand("git submodule init -- %s", c.OSCommand.Quote(path))
|
return c.Run(c.NewCmdObj("git submodule init -- " + c.OSCommand.Quote(path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleUpdate(path string) error {
|
func (c *GitCommand) SubmoduleUpdate(path string) error {
|
||||||
return c.RunCommand("git submodule update --init -- %s", c.OSCommand.Quote(path))
|
return c.Run(c.NewCmdObj("git submodule update --init -- " + c.OSCommand.Quote(path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleBulkInitCmdStr() string {
|
func (c *GitCommand) SubmoduleBulkInitCmdObj() oscommands.ICmdObj {
|
||||||
return "git submodule init"
|
return c.NewCmdObj("git submodule init")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleBulkUpdateCmdStr() string {
|
func (c *GitCommand) SubmoduleBulkUpdateCmdObj() oscommands.ICmdObj {
|
||||||
return "git submodule update"
|
return c.NewCmdObj("git submodule update")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleForceBulkUpdateCmdStr() string {
|
func (c *GitCommand) SubmoduleForceBulkUpdateCmdObj() oscommands.ICmdObj {
|
||||||
return "git submodule update --force"
|
return c.NewCmdObj("git submodule update --force")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) SubmoduleBulkDeinitCmdStr() string {
|
func (c *GitCommand) SubmoduleBulkDeinitCmdObj() oscommands.ICmdObj {
|
||||||
return "git submodule deinit --all --force"
|
return c.NewCmdObj("git submodule deinit --all --force")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) ResetSubmodules(submodules []*models.SubmoduleConfig) error {
|
func (c *GitCommand) ResetSubmodules(submodules []*models.SubmoduleConfig) error {
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (c *GitCommand) Push(opts PushOpts) error {
|
||||||
cmdStr += " " + c.OSCommand.Quote(opts.UpstreamBranch)
|
cmdStr += " " + c.OSCommand.Quote(opts.UpstreamBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdObj := c.NewCmdObjFromStr(cmdStr)
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
|
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ func (c *GitCommand) Fetch(opts FetchOptions) error {
|
||||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, c.OSCommand.Quote(opts.BranchName))
|
cmdStr = fmt.Sprintf("%s %s", cmdStr, c.OSCommand.Quote(opts.BranchName))
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdObj := c.NewCmdObjFromStr(cmdStr)
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
return c.DetectUnamePass(cmdObj, func(question string) string {
|
return c.DetectUnamePass(cmdObj, func(question string) string {
|
||||||
if opts.PromptUserForCredential != nil {
|
if opts.PromptUserForCredential != nil {
|
||||||
return opts.PromptUserForCredential(question)
|
return opts.PromptUserForCredential(question)
|
||||||
|
@ -94,18 +94,18 @@ func (c *GitCommand) Pull(opts PullOptions) error {
|
||||||
|
|
||||||
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
||||||
// has 'pull.rebase = interactive' configured.
|
// has 'pull.rebase = interactive' configured.
|
||||||
cmdObj := c.NewCmdObjFromStr(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:")
|
cmdObj := c.NewCmdObj(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:")
|
||||||
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
|
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) FastForward(branchName string, remoteName string, remoteBranchName string, promptUserForCredential func(string) string) error {
|
func (c *GitCommand) FastForward(branchName string, remoteName string, remoteBranchName string, promptUserForCredential func(string) string) error {
|
||||||
cmdStr := fmt.Sprintf("git fetch %s %s:%s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
|
cmdStr := fmt.Sprintf("git fetch %s %s:%s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
|
||||||
cmdObj := c.NewCmdObjFromStr(cmdStr)
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) FetchRemote(remoteName string, promptUserForCredential func(string) string) error {
|
func (c *GitCommand) FetchRemote(remoteName string, promptUserForCredential func(string) string) error {
|
||||||
cmdStr := fmt.Sprintf("git fetch %s", c.OSCommand.Quote(remoteName))
|
cmdStr := fmt.Sprintf("git fetch %s", c.OSCommand.Quote(remoteName))
|
||||||
cmdObj := c.NewCmdObjFromStr(cmdStr)
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *GitCommand) CreateLightweightTag(tagName string, commitSha string) error {
|
func (c *GitCommand) CreateLightweightTag(tagName string, commitSha string) error {
|
||||||
return c.RunCommand("git tag -- %s %s", c.OSCommand.Quote(tagName), commitSha)
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git tag -- %s %s", c.OSCommand.Quote(tagName), commitSha)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) CreateAnnotatedTag(tagName, commitSha, msg string) error {
|
func (c *GitCommand) CreateAnnotatedTag(tagName, commitSha, msg string) error {
|
||||||
|
@ -13,11 +13,11 @@ func (c *GitCommand) CreateAnnotatedTag(tagName, commitSha, msg string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) DeleteTag(tagName string) error {
|
func (c *GitCommand) DeleteTag(tagName string) error {
|
||||||
return c.RunCommand("git tag -d %s", c.OSCommand.Quote(tagName))
|
return c.Run(c.NewCmdObj(fmt.Sprintf("git tag -d %s", c.OSCommand.Quote(tagName))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) PushTag(remoteName string, tagName string, promptUserForCredential func(string) string) error {
|
func (c *GitCommand) PushTag(remoteName string, tagName string, promptUserForCredential func(string) string) error {
|
||||||
cmdStr := fmt.Sprintf("git push %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(tagName))
|
cmdStr := fmt.Sprintf("git push %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(tagName))
|
||||||
cmdObj := c.NewCmdObjFromStr(cmdStr)
|
cmdObj := c.NewCmdObj(cmdStr)
|
||||||
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
return c.DetectUnamePass(cmdObj, promptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,9 @@ func (gui *Gui) branchesRenderToMain() error {
|
||||||
if branch == nil {
|
if branch == nil {
|
||||||
task = NewRenderStringTask(gui.Tr.NoBranchesThisRepo)
|
task = NewRenderStringTask(gui.Tr.NoBranchesThisRepo)
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.GetBranchGraphCmdObj(branch.Name)
|
||||||
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
|
||||||
)
|
|
||||||
|
|
||||||
task = NewRunPtyTask(cmd)
|
task = NewRunPtyTask(cmdObj.GetCmd())
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -45,10 +45,8 @@ func (gui *Gui) commitFilesRenderToMain() error {
|
||||||
to := gui.State.CommitFileManager.GetParent()
|
to := gui.State.CommitFileManager.GetParent()
|
||||||
from, reverse := gui.getFromAndReverseArgsForDiff(to)
|
from, reverse := gui.getFromAndReverseArgsForDiff(to)
|
||||||
|
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false)
|
||||||
gui.GitCommand.ShowFileDiffCmdStr(from, to, reverse, node.GetPath(), false),
|
task := NewRunPtyTask(cmdObj.GetCmd())
|
||||||
)
|
|
||||||
task := NewRunPtyTask(cmd)
|
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
main: &viewUpdateOpts{
|
main: &viewUpdateOpts{
|
||||||
|
|
|
@ -24,10 +24,11 @@ func (gui *Gui) handleCommitConfirm() error {
|
||||||
flags = append(flags, "--signoff")
|
flags = append(flags, "--signoff")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdStr := gui.GitCommand.CommitCmdStr(message, strings.Join(flags, " "))
|
cmdObj := gui.GitCommand.CommitCmdObj(message, strings.Join(flags, " "))
|
||||||
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdStr, gui.Tr.Spans.Commit, true))
|
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdObj.ToString(), gui.Tr.Spans.Commit, true))
|
||||||
|
|
||||||
_ = gui.returnFromContext()
|
_ = gui.returnFromContext()
|
||||||
return gui.withGpgHandling(cmdStr, gui.Tr.CommittingStatus, func() error {
|
return gui.withGpgHandling(cmdObj, gui.Tr.CommittingStatus, func() error {
|
||||||
gui.Views.CommitMessage.ClearTextArea()
|
gui.Views.CommitMessage.ClearTextArea()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
@ -46,10 +46,8 @@ func (gui *Gui) branchCommitsRenderToMain() error {
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
task = NewRenderStringTask(gui.Tr.NoCommitsThisBranch)
|
task = NewRenderStringTask(gui.Tr.NoCommitsThisBranch)
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath())
|
||||||
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.GetPath()),
|
task = NewRunPtyTask(cmdObj.GetCmd())
|
||||||
)
|
|
||||||
task = NewRunPtyTask(cmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -203,7 +203,7 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run and save output
|
// Run and save output
|
||||||
message, err := gui.GitCommand.RunCommandWithOutput(cmdStr)
|
message, err := gui.GitCommand.RunWithOutput(gui.GitCommand.NewCmdObj(cmdStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
if customCommand.Subprocess {
|
if customCommand.Subprocess {
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(gui.OSCommand.PrepareShellSubProcess(cmdStr))
|
return gui.runSubprocessWithSuspenseAndRefresh(gui.OSCommand.NewShellCmdObjFromString2(cmdStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingText := customCommand.LoadingText
|
loadingText := customCommand.LoadingText
|
||||||
|
@ -252,7 +252,8 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||||
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
||||||
}
|
}
|
||||||
return gui.WithWaitingStatus(loadingText, func() error {
|
return gui.WithWaitingStatus(loadingText, func() error {
|
||||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.CustomCommand).RunShellCommand(cmdStr); err != nil {
|
cmdObj := gui.OSCommand.NewShellCmdObjFromString(cmdStr)
|
||||||
|
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.CustomCommand).Run(cmdObj); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(refreshOptions{})
|
return gui.refreshSidePanels(refreshOptions{})
|
||||||
|
|
|
@ -13,10 +13,10 @@ func (gui *Gui) exitDiffMode() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderDiff() error {
|
func (gui *Gui) renderDiff() error {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.OSCommand.NewCmdObj(
|
||||||
fmt.Sprintf("git diff --submodule --no-ext-diff --color %s", gui.diffStr()),
|
fmt.Sprintf("git diff --submodule --no-ext-diff --color %s", gui.diffStr()),
|
||||||
)
|
)
|
||||||
task := NewRunPtyTask(cmd)
|
task := NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
main: &viewUpdateOpts{
|
main: &viewUpdateOpts{
|
||||||
|
|
|
@ -58,22 +58,20 @@ func (gui *Gui) filesRenderToMain() error {
|
||||||
return gui.refreshMergePanelWithLock()
|
return gui.refreshMergePanelWithLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
cmdObj := gui.GitCommand.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
||||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
|
||||||
|
|
||||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||||
title: gui.Tr.UnstagedChanges,
|
title: gui.Tr.UnstagedChanges,
|
||||||
task: NewRunPtyTask(cmd),
|
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
if node.GetHasUnstagedChanges() {
|
if node.GetHasUnstagedChanges() {
|
||||||
if node.GetHasStagedChanges() {
|
if node.GetHasStagedChanges() {
|
||||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
cmdObj := gui.GitCommand.WorktreeFileDiffCmdObj(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
||||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
|
||||||
|
|
||||||
refreshOpts.secondary = &viewUpdateOpts{
|
refreshOpts.secondary = &viewUpdateOpts{
|
||||||
title: gui.Tr.StagedChanges,
|
title: gui.Tr.StagedChanges,
|
||||||
task: NewRunPtyTask(cmd),
|
task: NewRunPtyTask(cmdObj.GetCmd()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -440,9 +438,9 @@ func (gui *Gui) handleAmendCommitPress() error {
|
||||||
title: strings.Title(gui.Tr.AmendLastCommit),
|
title: strings.Title(gui.Tr.AmendLastCommit),
|
||||||
prompt: gui.Tr.SureToAmend,
|
prompt: gui.Tr.SureToAmend,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
cmdStr := gui.GitCommand.AmendHeadCmdStr()
|
cmdObj := gui.GitCommand.AmendHeadCmdObj()
|
||||||
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdStr, gui.Tr.Spans.AmendCommit, true))
|
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdObj.ToString(), gui.Tr.Spans.AmendCommit, true))
|
||||||
return gui.withGpgHandling(cmdStr, gui.Tr.AmendingStatus, nil)
|
return gui.withGpgHandling(cmdObj, gui.Tr.AmendingStatus, nil)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -464,8 +462,10 @@ func (gui *Gui) handleCommitEditorPress() error {
|
||||||
args = append(args, "--signoff")
|
args = append(args, "--signoff")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmdStr := "git " + strings.Join(args, " ")
|
||||||
|
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.WithSpan(gui.Tr.Spans.Commit).PrepareSubProcess("git", args...),
|
gui.GitCommand.WithSpan(gui.Tr.Spans.Commit).NewCmdObjWithLog(cmdStr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +511,7 @@ func (gui *Gui) editFileAtLine(filename string, lineNumber int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).ShellCommandFromString(cmdStr),
|
gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).NewShellCmdObjFromString(cmdStr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -923,7 +923,7 @@ func (gui *Gui) handleCustomCommand() error {
|
||||||
|
|
||||||
gui.OnRunCommand(oscommands.NewCmdLogEntry(command, gui.Tr.Spans.CustomCommand, true))
|
gui.OnRunCommand(oscommands.NewCmdLogEntry(command, gui.Tr.Spans.CustomCommand, true))
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.PrepareShellSubProcess(command),
|
gui.OSCommand.NewShellCmdObjFromString2(command),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1004,7 +1004,7 @@ func (gui *Gui) handleOpenMergeTool() error {
|
||||||
prompt: gui.Tr.MergeToolPrompt,
|
prompt: gui.Tr.MergeToolPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.ExecutableFromString(gui.GitCommand.OpenMergeToolCmd()),
|
gui.GitCommand.OpenMergeToolCmdObj(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,7 +32,7 @@ func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.WithSpan(gui.Tr.Spans.GitFlowFinish).PrepareSubProcess("git", "flow", branchType, "finish", suffix),
|
gui.GitCommand.WithSpan(gui.Tr.Spans.GitFlowFinish).NewCmdObjWithLog("git flow " + branchType + " finish " + suffix),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get config
|
// get config
|
||||||
gitFlowConfig, err := gui.GitCommand.RunCommandWithOutput("git config --local --get-regexp gitflow")
|
gitFlowConfig, err := gui.GitCommand.RunWithOutput(gui.GitCommand.NewCmdObj("git config --local --get-regexp gitflow"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel("You need to install git-flow and enable it in this repo to use git-flow features")
|
return gui.createErrorPanel("You need to install git-flow and enable it in this repo to use git-flow features")
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
|
||||||
title: title,
|
title: title,
|
||||||
handleConfirm: func(name string) error {
|
handleConfirm: func(name string) error {
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.WithSpan(gui.Tr.Spans.GitFlowStart).PrepareSubProcess("git", "flow", branchType, "start", name),
|
gui.GitCommand.WithSpan(gui.Tr.Spans.GitFlowStart).NewCmdObjWithLog("git flow " + branchType + " start " + name),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,6 +3,7 @@ package gui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,12 +11,11 @@ import (
|
||||||
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
|
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
|
||||||
// fix this bug, or just stop running subprocesses from within there, given that
|
// fix this bug, or just stop running subprocesses from within there, given that
|
||||||
// we don't need to see a loading status if we're in a subprocess.
|
// we don't need to see a loading status if we're in a subprocess.
|
||||||
func (gui *Gui) withGpgHandling(cmdStr string, waitingStatus string, onSuccess func() error) error {
|
// TODO: work out if we actually need to use a shell command here
|
||||||
|
func (gui *Gui) withGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||||
useSubprocess := gui.GitCommand.UsingGpg()
|
useSubprocess := gui.GitCommand.UsingGpg()
|
||||||
if useSubprocess {
|
if useSubprocess {
|
||||||
// Need to remember why we use the shell for the subprocess but not in the other case
|
success, err := gui.runSubprocessWithSuspense(gui.OSCommand.NewShellCmdObjFromString(cmdObj.ToString()))
|
||||||
// Maybe there's no good reason
|
|
||||||
success, err := gui.runSubprocessWithSuspense(gui.OSCommand.ShellCommandFromString(cmdStr))
|
|
||||||
if success && onSuccess != nil {
|
if success && onSuccess != nil {
|
||||||
if err := onSuccess(); err != nil {
|
if err := onSuccess(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -27,15 +27,16 @@ func (gui *Gui) withGpgHandling(cmdStr string, waitingStatus string, onSuccess f
|
||||||
|
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
return gui.RunAndStream(cmdStr, waitingStatus, onSuccess)
|
return gui.RunAndStream(cmdObj, waitingStatus, onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) RunAndStream(cmdStr string, waitingStatus string, onSuccess func() error) error {
|
func (gui *Gui) RunAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||||
return gui.WithWaitingStatus(waitingStatus, func() error {
|
return gui.WithWaitingStatus(waitingStatus, func() error {
|
||||||
cmd := gui.OSCommand.ShellCommandFromString(cmdStr)
|
cmdObj := gui.OSCommand.NewShellCmdObjFromString(cmdObj.ToString())
|
||||||
cmd.Env = append(cmd.Env, "TERM=dumb")
|
cmdObj.AddEnvVars("TERM=dumb")
|
||||||
cmdWriter := gui.getCmdWriter()
|
cmdWriter := gui.getCmdWriter()
|
||||||
|
cmd := cmdObj.GetCmd()
|
||||||
cmd.Stdout = cmdWriter
|
cmd.Stdout = cmdWriter
|
||||||
cmd.Stderr = cmdWriter
|
cmd.Stderr = cmdWriter
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -578,7 +577,7 @@ func (gui *Gui) RunAndHandleError() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns whether command exited without error or not
|
// returns whether command exited without error or not
|
||||||
func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess *exec.Cmd) error {
|
func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess oscommands.ICmdObj) error {
|
||||||
_, err := gui.runSubprocessWithSuspense(subprocess)
|
_, err := gui.runSubprocessWithSuspense(subprocess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -592,7 +591,7 @@ func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess *exec.Cmd) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns whether command exited without error or not
|
// returns whether command exited without error or not
|
||||||
func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) (bool, error) {
|
func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool, error) {
|
||||||
gui.Mutexes.SubprocessMutex.Lock()
|
gui.Mutexes.SubprocessMutex.Lock()
|
||||||
defer gui.Mutexes.SubprocessMutex.Unlock()
|
defer gui.Mutexes.SubprocessMutex.Unlock()
|
||||||
|
|
||||||
|
@ -621,7 +620,8 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) (bool, error) {
|
||||||
return cmdErr == nil, gui.surfaceError(cmdErr)
|
return cmdErr == nil, gui.surfaceError(cmdErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
|
func (gui *Gui) runSubprocess(cmdObj oscommands.ICmdObj) error {
|
||||||
|
subprocess := cmdObj.GetCmd()
|
||||||
subprocess.Stdout = os.Stdout
|
subprocess.Stdout = os.Stdout
|
||||||
subprocess.Stderr = os.Stdout
|
subprocess.Stderr = os.Stdout
|
||||||
subprocess.Stdin = os.Stdin
|
subprocess.Stdin = os.Stdin
|
||||||
|
|
|
@ -58,7 +58,7 @@ func (gui *Gui) genericMergeCommand(command string) error {
|
||||||
|
|
||||||
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
|
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
|
||||||
if status == commands.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && gui.Config.GetUserConfig().Git.Merging.ManualCommit {
|
if status == commands.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && gui.Config.GetUserConfig().Git.Merging.ManualCommit {
|
||||||
sub := gitCommand.OSCommand.PrepareSubProcess("git", commandType, fmt.Sprintf("--%s", command))
|
sub := gitCommand.NewCmdObj("git " + commandType + " --" + command)
|
||||||
if sub != nil {
|
if sub != nil {
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(sub)
|
return gui.runSubprocessWithSuspenseAndRefresh(sub)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,10 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleShowAllBranchLogs() error {
|
func (gui *Gui) handleShowAllBranchLogs() error {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.OSCommand.NewCmdObj(
|
||||||
gui.Config.GetUserConfig().Git.AllBranchesLogCmd,
|
gui.Config.GetUserConfig().Git.AllBranchesLogCmd,
|
||||||
)
|
)
|
||||||
task := NewRunPtyTask(cmd)
|
task := NewRunPtyTask(cmdObj.GetCmd())
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
main: &viewUpdateOpts{
|
main: &viewUpdateOpts{
|
||||||
|
|
|
@ -22,11 +22,9 @@ func (gui *Gui) reflogCommitsRenderToMain() error {
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
task = NewRenderStringTask("No reflog history")
|
task = NewRenderStringTask("No reflog history")
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath())
|
||||||
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.GetPath()),
|
|
||||||
)
|
|
||||||
|
|
||||||
task = NewRunPtyTask(cmd)
|
task = NewRunPtyTask(cmdObj.GetCmd())
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -24,10 +24,8 @@ func (gui *Gui) remoteBranchesRenderToMain() error {
|
||||||
if remoteBranch == nil {
|
if remoteBranch == nil {
|
||||||
task = NewRenderStringTask("No branches for this remote")
|
task = NewRenderStringTask("No branches for this remote")
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.GetBranchGraphCmdObj(remoteBranch.FullName())
|
||||||
gui.GitCommand.GetBranchGraphCmdStr(remoteBranch.FullName()),
|
task = NewRunCommandTask(cmdObj.GetCmd())
|
||||||
)
|
|
||||||
task = NewRunCommandTask(cmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -3,12 +3,11 @@ package gui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) resetToRef(ref string, strength string, span string, options oscommands.RunCommandOptions) error {
|
func (gui *Gui) resetToRef(ref string, strength string, span string, envVars []string) error {
|
||||||
if err := gui.GitCommand.WithSpan(span).ResetToCommit(ref, strength, options); err != nil {
|
if err := gui.GitCommand.WithSpan(span).ResetToCommit(ref, strength, envVars); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ func (gui *Gui) createResetMenu(ref string) error {
|
||||||
style.FgRed.Sprintf("reset --%s %s", strength, ref),
|
style.FgRed.Sprintf("reset --%s %s", strength, ref),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.resetToRef(ref, strength, "Reset", oscommands.RunCommandOptions{})
|
return gui.resetToRef(ref, strength, "Reset", []string{})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,10 @@ func (gui *Gui) stashRenderToMain() error {
|
||||||
if stashEntry == nil {
|
if stashEntry == nil {
|
||||||
task = NewRenderStringTask(gui.Tr.NoStashEntries)
|
task = NewRenderStringTask(gui.Tr.NoStashEntries)
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.OSCommand.NewCmdObj(
|
||||||
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
|
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
|
||||||
)
|
)
|
||||||
task = NewRunPtyTask(cmd)
|
task = NewRunPtyTask(cmdObj.GetCmd())
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -23,11 +23,9 @@ func (gui *Gui) subCommitsRenderToMain() error {
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
task = NewRenderStringTask("No commits")
|
task = NewRenderStringTask("No commits")
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath())
|
||||||
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.GetPath()),
|
|
||||||
)
|
|
||||||
|
|
||||||
task = NewRunPtyTask(cmd)
|
task = NewRunPtyTask(cmdObj.GetCmd())
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -36,9 +36,8 @@ func (gui *Gui) submodulesRenderToMain() error {
|
||||||
if file == nil {
|
if file == nil {
|
||||||
task = NewRenderStringTask(prefix)
|
task = NewRenderStringTask(prefix)
|
||||||
} else {
|
} else {
|
||||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.State.IgnoreWhitespaceInDiffView)
|
cmdObj := gui.GitCommand.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.State.IgnoreWhitespaceInDiffView)
|
||||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
task = NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
|
||||||
task = NewRunCommandTaskWithPrefix(cmd, prefix)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,10 +211,10 @@ func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) er
|
||||||
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||||
menuItems := []*menuItem{
|
menuItems := []*menuItem{
|
||||||
{
|
{
|
||||||
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.GitCommand.SubmoduleBulkInitCmdStr())},
|
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.GitCommand.SubmoduleBulkInitCmdObj().ToString())},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkInitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkInitCmdStr()); err != nil {
|
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkInitialiseSubmodules).Run(gui.GitCommand.SubmoduleBulkInitCmdObj()); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,10 +223,10 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.GitCommand.SubmoduleBulkUpdateCmdStr())},
|
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.GitCommand.SubmoduleBulkUpdateCmdObj().ToString())},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkUpdateSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkUpdateCmdStr()); err != nil {
|
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkUpdateSubmodules).Run(gui.GitCommand.SubmoduleBulkUpdateCmdObj()); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +235,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr())},
|
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdObj().ToString())},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||||
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.BulkStashAndResetSubmodules).ResetSubmodules(gui.State.Submodules); err != nil {
|
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.BulkStashAndResetSubmodules).ResetSubmodules(gui.State.Submodules); err != nil {
|
||||||
|
@ -248,10 +247,10 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.GitCommand.SubmoduleBulkDeinitCmdStr())},
|
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.GitCommand.SubmoduleBulkDeinitCmdObj().ToString())},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkDeinitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkDeinitCmdStr()); err != nil {
|
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkDeinitialiseSubmodules).Run(gui.GitCommand.SubmoduleBulkDeinitCmdObj()); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,8 @@ func (gui *Gui) tagsRenderToMain() error {
|
||||||
if tag == nil {
|
if tag == nil {
|
||||||
task = NewRenderStringTask("No tags")
|
task = NewRenderStringTask("No tags")
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmdObj := gui.GitCommand.GetBranchGraphCmdObj(tag.Name)
|
||||||
gui.GitCommand.GetBranchGraphCmdStr(tag.Name),
|
task = NewRunCommandTask(cmdObj.GetCmd())
|
||||||
)
|
|
||||||
task = NewRunCommandTask(cmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
|
|
|
@ -2,7 +2,6 @@ package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -169,7 +168,7 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
|
||||||
gitCommand := gui.GitCommand.WithSpan(options.span)
|
gitCommand := gui.GitCommand.WithSpan(options.span)
|
||||||
|
|
||||||
reset := func() error {
|
reset := func() error {
|
||||||
if err := gui.resetToRef(commitSha, "hard", options.span, oscommands.RunCommandOptions{EnvVars: options.EnvVars}); err != nil {
|
if err := gui.resetToRef(commitSha, "hard", options.span, options.EnvVars); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -45,7 +45,7 @@ func RunTests(
|
||||||
testDir := filepath.Join(rootDir, "test", "integration")
|
testDir := filepath.Join(rootDir, "test", "integration")
|
||||||
|
|
||||||
osCommand := oscommands.NewDummyOSCommand()
|
osCommand := oscommands.NewDummyOSCommand()
|
||||||
err = osCommand.RunCommand("go build -o %s", tempLazygitPath())
|
err = osCommand.Run(osCommand.NewCmdObj("go build -o " + tempLazygitPath()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -216,11 +216,10 @@ func GetRootDirectory() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFixture(testPath, actualDir string) error {
|
func createFixture(testPath, actualDir string) error {
|
||||||
osCommand := oscommands.NewDummyOSCommand()
|
|
||||||
bashScriptPath := filepath.Join(testPath, "setup.sh")
|
bashScriptPath := filepath.Join(testPath, "setup.sh")
|
||||||
cmd := secureexec.Command("bash", bashScriptPath, actualDir)
|
cmd := secureexec.Command("bash", bashScriptPath, actualDir)
|
||||||
|
|
||||||
if err := osCommand.RunExecutable(cmd); err != nil {
|
if _, err := cmd.CombinedOutput(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +319,7 @@ func generateSnapshot(dir string) (string, error) {
|
||||||
|
|
||||||
for _, cmdStr := range cmdStrs {
|
for _, cmdStr := range cmdStrs {
|
||||||
// ignoring error for now. If there's an error it could be that there are no results
|
// ignoring error for now. If there's an error it could be that there are no results
|
||||||
output, _ := osCommand.RunCommandWithOutput(cmdStr)
|
output, _ := osCommand.RunWithOutput(osCommand.NewCmdObj(cmdStr))
|
||||||
|
|
||||||
snapshot += output + "\n"
|
snapshot += output + "\n"
|
||||||
}
|
}
|
||||||
|
@ -429,22 +428,16 @@ func getLazygitCommand(testPath string, rootDir string, record bool, speed float
|
||||||
|
|
||||||
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualDir, extraCmdArgs)
|
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualDir, extraCmdArgs)
|
||||||
|
|
||||||
cmd := osCommand.ExecutableFromString(cmdStr)
|
cmdObj := osCommand.NewCmdObj(cmdStr)
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("SPEED=%f", speed))
|
cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed))
|
||||||
|
|
||||||
if record {
|
if record {
|
||||||
cmd.Env = append(
|
cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath))
|
||||||
cmd.Env,
|
|
||||||
fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
cmd.Env = append(
|
cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath))
|
||||||
cmd.Env,
|
|
||||||
fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd, nil
|
return cmdObj.GetCmd(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func folderExists(path string) bool {
|
func folderExists(path string) bool {
|
||||||
|
|
|
@ -300,7 +300,8 @@ func (u *Updater) downloadAndInstall(rawUrl string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Log.Info("untarring tarball/unzipping zip file")
|
u.Log.Info("untarring tarball/unzipping zip file")
|
||||||
if err := u.OSCommand.RunCommand("tar -zxf %s %s", u.OSCommand.Quote(zipPath), "lazygit"); err != nil {
|
cmdObj := u.OSCommand.NewCmdObj(fmt.Sprintf("tar -zxf %s %s", u.OSCommand.Quote(zipPath), "lazygit"))
|
||||||
|
if err := u.OSCommand.Run(cmdObj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue