2018-08-12 11:31:27 +02:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2023-07-29 09:02:04 +02:00
|
|
|
"path"
|
2019-11-05 03:42:07 +02:00
|
|
|
"path/filepath"
|
2018-08-12 11:50:55 +02:00
|
|
|
"strings"
|
2019-02-18 12:29:43 +02:00
|
|
|
|
2019-02-11 12:30:27 +02:00
|
|
|
"github.com/go-errors/errors"
|
2022-08-07 01:44:50 +02:00
|
|
|
"github.com/sasha-s/go-deadlock"
|
2023-07-29 09:02:04 +02:00
|
|
|
"github.com/spf13/afero"
|
2019-02-11 12:30:27 +02:00
|
|
|
|
2020-10-06 11:50:54 +02:00
|
|
|
gogit "github.com/jesseduffield/go-git/v5"
|
2022-01-08 05:00:36 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
2021-10-23 00:52:19 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
2020-09-29 11:10:57 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
2020-08-15 03:18:40 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
2021-12-29 02:37:15 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/common"
|
2020-09-27 08:17:26 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/env"
|
2018-08-12 13:04:47 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
2018-08-12 11:31:27 +02:00
|
|
|
)
|
|
|
|
|
2022-01-02 01:34:33 +02:00
|
|
|
// GitCommand is our main git interface
|
|
|
|
type GitCommand struct {
|
2022-01-08 05:00:36 +02:00
|
|
|
Branch *git_commands.BranchCommands
|
|
|
|
Commit *git_commands.CommitCommands
|
|
|
|
Config *git_commands.ConfigCommands
|
|
|
|
Custom *git_commands.CustomCommands
|
2023-05-21 09:00:29 +02:00
|
|
|
Diff *git_commands.DiffCommands
|
2022-01-08 05:00:36 +02:00
|
|
|
File *git_commands.FileCommands
|
|
|
|
Flow *git_commands.FlowCommands
|
|
|
|
Patch *git_commands.PatchCommands
|
|
|
|
Rebase *git_commands.RebaseCommands
|
|
|
|
Remote *git_commands.RemoteCommands
|
|
|
|
Stash *git_commands.StashCommands
|
|
|
|
Status *git_commands.StatusCommands
|
|
|
|
Submodule *git_commands.SubmoduleCommands
|
|
|
|
Sync *git_commands.SyncCommands
|
|
|
|
Tag *git_commands.TagCommands
|
|
|
|
WorkingTree *git_commands.WorkingTreeCommands
|
2022-01-19 09:32:27 +02:00
|
|
|
Bisect *git_commands.BisectCommands
|
2022-09-02 02:58:36 +02:00
|
|
|
Worktree *git_commands.WorktreeCommands
|
2023-07-17 05:56:50 +02:00
|
|
|
Version *git_commands.GitVersion
|
2023-07-29 09:02:04 +02:00
|
|
|
RepoPaths *git_commands.RepoPaths
|
2022-01-07 11:36:11 +02:00
|
|
|
|
|
|
|
Loaders Loaders
|
2022-01-02 01:34:33 +02:00
|
|
|
}
|
2019-11-17 03:07:36 +02:00
|
|
|
|
2022-01-02 01:21:32 +02:00
|
|
|
type Loaders struct {
|
2022-11-11 04:19:29 +02:00
|
|
|
BranchLoader *git_commands.BranchLoader
|
|
|
|
CommitFileLoader *git_commands.CommitFileLoader
|
|
|
|
CommitLoader *git_commands.CommitLoader
|
|
|
|
FileLoader *git_commands.FileLoader
|
|
|
|
ReflogCommitLoader *git_commands.ReflogCommitLoader
|
|
|
|
RemoteLoader *git_commands.RemoteLoader
|
|
|
|
StashLoader *git_commands.StashLoader
|
|
|
|
TagLoader *git_commands.TagLoader
|
2022-09-01 20:25:41 +02:00
|
|
|
Worktrees *git_commands.WorktreeLoader
|
2022-01-02 01:21:32 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 00:52:19 +02:00
|
|
|
func NewGitCommand(
|
2021-12-29 02:37:15 +02:00
|
|
|
cmn *common.Common,
|
2022-12-26 16:43:08 +02:00
|
|
|
version *git_commands.GitVersion,
|
2021-10-23 00:52:19 +02:00
|
|
|
osCommand *oscommands.OSCommand,
|
|
|
|
gitConfig git_config.IGitConfig,
|
2022-08-07 01:44:50 +02:00
|
|
|
syncMutex *deadlock.Mutex,
|
2021-10-23 00:52:19 +02:00
|
|
|
) (*GitCommand, error) {
|
2023-07-29 09:02:04 +02:00
|
|
|
currentPath, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return nil, utils.WrapError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// converting to forward slashes for the sake of windows (which uses backwards slashes). We want everything
|
|
|
|
// to have forward slashes internally
|
|
|
|
currentPath = filepath.ToSlash(currentPath)
|
|
|
|
|
|
|
|
gitDir := env.GetGitDirEnv()
|
|
|
|
if gitDir != "" {
|
|
|
|
// we've been given the git directory explicitly so no need to navigate to it
|
|
|
|
_, err := cmn.Fs.Stat(gitDir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, utils.WrapError(err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// we haven't been given the git dir explicitly so we assume it's in the current working directory as `.git/` (or an ancestor directory)
|
|
|
|
|
|
|
|
rootDirectory, err := findWorktreeRoot(cmn.Fs, currentPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, utils.WrapError(err)
|
|
|
|
}
|
|
|
|
currentPath = rootDirectory
|
|
|
|
err = os.Chdir(rootDirectory)
|
|
|
|
if err != nil {
|
|
|
|
return nil, utils.WrapError(err)
|
|
|
|
}
|
2020-09-27 07:36:04 +02:00
|
|
|
}
|
|
|
|
|
2023-07-29 09:02:04 +02:00
|
|
|
repoPaths, err := git_commands.GetRepoPaths(cmn.Fs, currentPath)
|
2022-01-02 01:34:33 +02:00
|
|
|
if err != nil {
|
2023-07-28 10:27:14 +02:00
|
|
|
return nil, errors.Errorf("Error getting repo paths: %v", err)
|
2018-09-02 17:15:27 +02:00
|
|
|
}
|
|
|
|
|
2023-07-28 10:27:14 +02:00
|
|
|
repository, err := gogit.PlainOpenWithOptions(
|
|
|
|
repoPaths.WorktreeGitDirPath(),
|
|
|
|
&gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true},
|
|
|
|
)
|
2019-05-12 09:04:32 +02:00
|
|
|
if err != nil {
|
2023-07-27 13:52:24 +02:00
|
|
|
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
|
|
|
|
return nil, errors.New(cmn.Tr.GitconfigParseErr)
|
|
|
|
}
|
2019-05-12 09:04:32 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-01-02 01:34:33 +02:00
|
|
|
return NewGitCommandAux(
|
|
|
|
cmn,
|
2022-12-26 16:43:08 +02:00
|
|
|
version,
|
2022-01-02 01:34:33 +02:00
|
|
|
osCommand,
|
|
|
|
gitConfig,
|
2023-07-28 10:27:14 +02:00
|
|
|
repoPaths,
|
2023-07-27 13:52:24 +02:00
|
|
|
repository,
|
2022-01-16 05:46:53 +02:00
|
|
|
syncMutex,
|
2022-01-02 01:34:33 +02:00
|
|
|
), nil
|
|
|
|
}
|
2021-12-29 05:33:38 +02:00
|
|
|
|
2022-01-02 01:34:33 +02:00
|
|
|
func NewGitCommandAux(
|
|
|
|
cmn *common.Common,
|
2022-12-26 16:43:08 +02:00
|
|
|
version *git_commands.GitVersion,
|
2022-01-02 01:34:33 +02:00
|
|
|
osCommand *oscommands.OSCommand,
|
|
|
|
gitConfig git_config.IGitConfig,
|
2023-07-29 09:02:04 +02:00
|
|
|
repoPaths *git_commands.RepoPaths,
|
2022-01-02 01:34:33 +02:00
|
|
|
repo *gogit.Repository,
|
2022-08-07 01:44:50 +02:00
|
|
|
syncMutex *deadlock.Mutex,
|
2022-01-02 01:34:33 +02:00
|
|
|
) *GitCommand {
|
|
|
|
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
|
2019-11-05 09:10:47 +02:00
|
|
|
|
2022-01-07 05:45:18 +02:00
|
|
|
// here we're doing a bunch of dependency injection for each of our commands structs.
|
|
|
|
// This is admittedly messy, but allows us to test each command struct in isolation,
|
|
|
|
// and allows for better namespacing when compared to having every method living
|
|
|
|
// on the one struct.
|
2022-01-18 12:26:21 +02:00
|
|
|
// common ones are: cmn, osCommand, dotGitDir, configCommands
|
2022-01-08 05:44:07 +02:00
|
|
|
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)
|
2022-01-18 12:26:21 +02:00
|
|
|
|
2023-07-28 10:27:14 +02:00
|
|
|
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, syncMutex)
|
2022-11-11 04:19:29 +02:00
|
|
|
|
2023-07-28 10:27:14 +02:00
|
|
|
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
|
2022-01-18 12:26:21 +02:00
|
|
|
statusCommands := git_commands.NewStatusCommands(gitCommon)
|
|
|
|
flowCommands := git_commands.NewFlowCommands(gitCommon)
|
|
|
|
remoteCommands := git_commands.NewRemoteCommands(gitCommon)
|
|
|
|
branchCommands := git_commands.NewBranchCommands(gitCommon)
|
|
|
|
syncCommands := git_commands.NewSyncCommands(gitCommon)
|
|
|
|
tagCommands := git_commands.NewTagCommands(gitCommon)
|
|
|
|
commitCommands := git_commands.NewCommitCommands(gitCommon)
|
|
|
|
customCommands := git_commands.NewCustomCommands(gitCommon)
|
2023-05-21 09:00:29 +02:00
|
|
|
diffCommands := git_commands.NewDiffCommands(gitCommon)
|
2022-01-18 12:26:21 +02:00
|
|
|
fileCommands := git_commands.NewFileCommands(gitCommon)
|
|
|
|
submoduleCommands := git_commands.NewSubmoduleCommands(gitCommon)
|
|
|
|
workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
|
|
|
rebaseCommands := git_commands.NewRebaseCommands(gitCommon, commitCommands, workingTreeCommands)
|
|
|
|
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
|
2023-05-19 12:18:02 +02:00
|
|
|
patchBuilder := patch.NewPatchBuilder(cmn.Log,
|
2023-02-03 21:20:20 +02:00
|
|
|
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
2023-03-19 07:09:03 +02:00
|
|
|
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
|
2023-02-03 21:20:20 +02:00
|
|
|
// account. For now we just pass false.
|
|
|
|
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain, false)
|
|
|
|
})
|
2023-03-19 07:09:03 +02:00
|
|
|
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
2022-01-19 09:32:27 +02:00
|
|
|
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
2022-09-02 02:58:36 +02:00
|
|
|
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
2022-01-02 01:34:33 +02:00
|
|
|
|
2022-11-07 07:35:36 +02:00
|
|
|
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
2022-11-11 04:19:29 +02:00
|
|
|
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
2023-07-28 10:27:14 +02:00
|
|
|
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon)
|
2022-11-11 04:19:29 +02:00
|
|
|
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
|
|
|
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
|
2023-07-29 09:02:04 +02:00
|
|
|
worktreeLoader := git_commands.NewWorktreeLoader(gitCommon)
|
2022-11-11 04:19:29 +02:00
|
|
|
stashLoader := git_commands.NewStashLoader(cmn, cmd)
|
2023-02-19 12:36:06 +02:00
|
|
|
tagLoader := git_commands.NewTagLoader(cmn, cmd)
|
2022-11-11 04:19:29 +02:00
|
|
|
|
2022-01-02 01:34:33 +02:00
|
|
|
return &GitCommand{
|
|
|
|
Branch: branchCommands,
|
|
|
|
Commit: commitCommands,
|
|
|
|
Config: configCommands,
|
2022-01-07 11:36:11 +02:00
|
|
|
Custom: customCommands,
|
2023-05-21 09:00:29 +02:00
|
|
|
Diff: diffCommands,
|
2022-01-07 11:36:11 +02:00
|
|
|
File: fileCommands,
|
|
|
|
Flow: flowCommands,
|
2022-01-02 01:34:33 +02:00
|
|
|
Patch: patchCommands,
|
2022-01-07 11:36:11 +02:00
|
|
|
Rebase: rebaseCommands,
|
2022-01-02 01:34:33 +02:00
|
|
|
Remote: remoteCommands,
|
2022-01-07 11:36:11 +02:00
|
|
|
Stash: stashCommands,
|
|
|
|
Status: statusCommands,
|
|
|
|
Submodule: submoduleCommands,
|
2022-01-02 01:34:33 +02:00
|
|
|
Sync: syncCommands,
|
2022-01-07 11:36:11 +02:00
|
|
|
Tag: tagCommands,
|
2022-01-19 09:32:27 +02:00
|
|
|
Bisect: bisectCommands,
|
2022-01-07 11:36:11 +02:00
|
|
|
WorkingTree: workingTreeCommands,
|
2022-09-02 02:58:36 +02:00
|
|
|
Worktree: worktreeCommands,
|
2023-07-17 05:56:50 +02:00
|
|
|
Version: version,
|
2022-01-02 01:34:33 +02:00
|
|
|
Loaders: Loaders{
|
2022-11-11 04:19:29 +02:00
|
|
|
BranchLoader: branchLoader,
|
|
|
|
CommitFileLoader: commitFileLoader,
|
|
|
|
CommitLoader: commitLoader,
|
|
|
|
FileLoader: fileLoader,
|
|
|
|
ReflogCommitLoader: reflogCommitLoader,
|
|
|
|
RemoteLoader: remoteLoader,
|
2022-09-01 20:25:41 +02:00
|
|
|
Worktrees: worktreeLoader,
|
2022-11-11 04:19:29 +02:00
|
|
|
StashLoader: stashLoader,
|
|
|
|
TagLoader: tagLoader,
|
2022-01-02 01:34:33 +02:00
|
|
|
},
|
2023-07-28 10:27:14 +02:00
|
|
|
RepoPaths: repoPaths,
|
2022-01-02 01:21:32 +02:00
|
|
|
}
|
2018-09-02 17:15:27 +02:00
|
|
|
}
|
|
|
|
|
2023-07-29 09:02:04 +02:00
|
|
|
// this returns the root of the current worktree. So if you start lazygit from within
|
|
|
|
// a subdirectory of the worktree, it will start in the context of the root of that worktree
|
|
|
|
func findWorktreeRoot(fs afero.Fs, currentPath string) (string, error) {
|
2020-09-29 12:03:39 +02:00
|
|
|
for {
|
2023-07-29 09:02:04 +02:00
|
|
|
// we don't care if .git is a directory or a file: either is okay.
|
|
|
|
_, err := fs.Stat(path.Join(currentPath, ".git"))
|
2020-08-11 13:18:38 +02:00
|
|
|
|
2020-09-29 12:03:39 +02:00
|
|
|
if err == nil {
|
2023-07-29 09:02:04 +02:00
|
|
|
return currentPath, nil
|
2018-11-25 14:15:36 +02:00
|
|
|
}
|
2018-08-12 11:50:55 +02:00
|
|
|
|
2020-09-29 12:03:39 +02:00
|
|
|
if !os.IsNotExist(err) {
|
2023-07-29 09:02:04 +02:00
|
|
|
return "", utils.WrapError(err)
|
2019-02-20 10:47:01 +02:00
|
|
|
}
|
2021-03-30 13:17:42 +02:00
|
|
|
|
2023-07-29 09:02:04 +02:00
|
|
|
currentPath = path.Dir(currentPath)
|
2021-03-30 13:17:42 +02:00
|
|
|
|
2023-07-29 09:02:04 +02:00
|
|
|
atRoot := currentPath == path.Dir(currentPath)
|
2021-03-30 13:17:42 +02:00
|
|
|
if atRoot {
|
|
|
|
// we should never really land here: the code that creates GitCommand should
|
|
|
|
// verify we're in a git directory
|
2023-07-29 09:02:04 +02:00
|
|
|
return "", errors.New("Must open lazygit in a git repository")
|
2021-03-30 13:17:42 +02:00
|
|
|
}
|
2019-02-20 10:47:01 +02:00
|
|
|
}
|
2018-08-12 12:22:20 +02:00
|
|
|
}
|
|
|
|
|
2021-03-30 13:17:42 +02:00
|
|
|
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
2023-05-21 09:00:29 +02:00
|
|
|
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
|
2021-10-20 13:21:16 +02:00
|
|
|
}
|