mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-05 15:15:49 +02:00
Support opening lazygit in a submodule
This commit is contained in:
parent
e38d9d5f22
commit
c61bfbdd4c
@ -25,14 +25,11 @@ func verifyInGitRepo(runCmd func(string) error) error {
|
|||||||
|
|
||||||
func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
|
func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
|
||||||
for {
|
for {
|
||||||
f, err := stat(".git")
|
_, err := stat(".git")
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if f.IsDir() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("expected .git to be a directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
return WrapError(err)
|
return WrapError(err)
|
||||||
@ -75,6 +72,7 @@ type GitCommand struct {
|
|||||||
getGlobalGitConfig func(string) (string, error)
|
getGlobalGitConfig func(string) (string, error)
|
||||||
getLocalGitConfig func(string) (string, error)
|
getLocalGitConfig func(string) (string, error)
|
||||||
removeFile func(string) error
|
removeFile func(string) error
|
||||||
|
DotGitDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGitCommand it runs git commands
|
// NewGitCommand it runs git commands
|
||||||
@ -102,6 +100,11 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dotGitDir, err := findDotGitDir(os.Stat, ioutil.ReadFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &GitCommand{
|
return &GitCommand{
|
||||||
Log: log,
|
Log: log,
|
||||||
OSCommand: osCommand,
|
OSCommand: osCommand,
|
||||||
@ -112,9 +115,31 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
|
|||||||
getGlobalGitConfig: gitconfig.Global,
|
getGlobalGitConfig: gitconfig.Global,
|
||||||
getLocalGitConfig: gitconfig.Local,
|
getLocalGitConfig: gitconfig.Local,
|
||||||
removeFile: os.RemoveAll,
|
removeFile: os.RemoveAll,
|
||||||
|
DotGitDir: dotGitDir,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) {
|
||||||
|
f, err := stat(".git")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.IsDir() {
|
||||||
|
return ".git", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fileBytes, err := readFile(".git")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fileContent := string(fileBytes)
|
||||||
|
if !strings.HasPrefix(fileContent, "gitdir: ") {
|
||||||
|
return "", errors.New(".git is a file which suggests we are in a submodule but the file's contents do not contain a gitdir pointing to the actual .git directory")
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(strings.TrimPrefix(fileContent, "gitdir: ")), nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetStashEntries stash entryies
|
// GetStashEntries stash entryies
|
||||||
func (c *GitCommand) GetStashEntries() []*StashEntry {
|
func (c *GitCommand) GetStashEntries() []*StashEntry {
|
||||||
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
|
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
|
||||||
@ -426,14 +451,14 @@ func (c *GitCommand) IsInMergeState() (bool, error) {
|
|||||||
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase
|
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase
|
||||||
// and "interactive" for interactive rebase
|
// and "interactive" for interactive rebase
|
||||||
func (c *GitCommand) RebaseMode() (string, error) {
|
func (c *GitCommand) RebaseMode() (string, error) {
|
||||||
exists, err := c.OSCommand.FileExists(".git/rebase-apply")
|
exists, err := c.OSCommand.FileExists(fmt.Sprintf("%s/rebase-apply", c.DotGitDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if exists {
|
if exists {
|
||||||
return "normal", nil
|
return "normal", nil
|
||||||
}
|
}
|
||||||
exists, err = c.OSCommand.FileExists(".git/rebase-merge")
|
exists, err = c.OSCommand.FileExists(fmt.Sprintf("%s/rebase-merge", c.DotGitDir))
|
||||||
if exists {
|
if exists {
|
||||||
return "interactive", err
|
return "interactive", err
|
||||||
} else {
|
} else {
|
||||||
@ -743,7 +768,7 @@ func (c *GitCommand) AmendTo(sha string) error {
|
|||||||
|
|
||||||
// EditRebaseTodo sets the action at a given index in the git-rebase-todo file
|
// EditRebaseTodo sets the action at a given index in the git-rebase-todo file
|
||||||
func (c *GitCommand) EditRebaseTodo(index int, action string) error {
|
func (c *GitCommand) EditRebaseTodo(index int, action string) error {
|
||||||
fileName := ".git/rebase-merge/git-rebase-todo"
|
fileName := fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.DotGitDir)
|
||||||
bytes, err := ioutil.ReadFile(fileName)
|
bytes, err := ioutil.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -775,7 +800,7 @@ func (c *GitCommand) getTodoCommitCount(content []string) int {
|
|||||||
|
|
||||||
// MoveTodoDown moves a rebase todo item down by one position
|
// MoveTodoDown moves a rebase todo item down by one position
|
||||||
func (c *GitCommand) MoveTodoDown(index int) error {
|
func (c *GitCommand) MoveTodoDown(index int) error {
|
||||||
fileName := ".git/rebase-merge/git-rebase-todo"
|
fileName := fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.DotGitDir)
|
||||||
bytes, err := ioutil.ReadFile(fileName)
|
bytes, err := ioutil.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/test"
|
"github.com/jesseduffield/lazygit/pkg/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -2122,3 +2123,78 @@ func TestGitCommandCreateFixupCommit(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFindDotGitDir(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
stat func(string) (os.FileInfo, error)
|
||||||
|
readFile func(filename string) ([]byte, error)
|
||||||
|
test func(string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
".git is a directory",
|
||||||
|
func(dotGit string) (os.FileInfo, error) {
|
||||||
|
assert.Equal(t, ".git", dotGit)
|
||||||
|
return os.Stat("testdata/a_dir")
|
||||||
|
},
|
||||||
|
func(dotGit string) ([]byte, error) {
|
||||||
|
assert.Fail(t, "readFile should not be called if .git is a directory")
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
func(gitDir string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, ".git", gitDir)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
".git is a file",
|
||||||
|
func(dotGit string) (os.FileInfo, error) {
|
||||||
|
assert.Equal(t, ".git", dotGit)
|
||||||
|
return os.Stat("testdata/a_file")
|
||||||
|
},
|
||||||
|
func(dotGit string) ([]byte, error) {
|
||||||
|
assert.Equal(t, ".git", dotGit)
|
||||||
|
return []byte("gitdir: blah\n"), nil
|
||||||
|
},
|
||||||
|
func(gitDir string, err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "blah", gitDir)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"os.Stat returns an error",
|
||||||
|
func(dotGit string) (os.FileInfo, error) {
|
||||||
|
assert.Equal(t, ".git", dotGit)
|
||||||
|
return nil, errors.New("error")
|
||||||
|
},
|
||||||
|
func(dotGit string) ([]byte, error) {
|
||||||
|
assert.Fail(t, "readFile should not be called os.Stat returns an error")
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
func(gitDir string, err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"readFile returns an error",
|
||||||
|
func(dotGit string) (os.FileInfo, error) {
|
||||||
|
assert.Equal(t, ".git", dotGit)
|
||||||
|
return os.Stat("testdata/a_file")
|
||||||
|
},
|
||||||
|
func(dotGit string) ([]byte, error) {
|
||||||
|
return nil, errors.New("error")
|
||||||
|
},
|
||||||
|
func(gitDir string, err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
s.test(findDotGitDir(s.stat, s.readFile))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
pkg/commands/testdata/a_dir/file
vendored
Normal file
0
pkg/commands/testdata/a_dir/file
vendored
Normal file
0
pkg/commands/testdata/a_file
vendored
Normal file
0
pkg/commands/testdata/a_file
vendored
Normal file
@ -123,7 +123,7 @@ func (c *CommitListBuilder) getRebasingCommits(rebaseMode string) ([]*commands.C
|
|||||||
|
|
||||||
func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, error) {
|
func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, error) {
|
||||||
rewrittenCount := 0
|
rewrittenCount := 0
|
||||||
bytesContent, err := ioutil.ReadFile(".git/rebase-apply/rewritten")
|
bytesContent, err := ioutil.ReadFile(fmt.Sprintf("%s/rebase-apply/rewritten", c.GitCommand.DotGitDir))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
content := string(bytesContent)
|
content := string(bytesContent)
|
||||||
rewrittenCount = len(strings.Split(content, "\n"))
|
rewrittenCount = len(strings.Split(content, "\n"))
|
||||||
@ -131,7 +131,7 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, erro
|
|||||||
|
|
||||||
// we know we're rebasing, so lets get all the files whose names have numbers
|
// we know we're rebasing, so lets get all the files whose names have numbers
|
||||||
commits := []*commands.Commit{}
|
commits := []*commands.Commit{}
|
||||||
err = filepath.Walk(".git/rebase-apply", func(path string, f os.FileInfo, err error) error {
|
err = filepath.Walk(fmt.Sprintf("%s/rebase-apply", c.GitCommand.DotGitDir), func(path string, f os.FileInfo, err error) error {
|
||||||
if rewrittenCount > 0 {
|
if rewrittenCount > 0 {
|
||||||
rewrittenCount--
|
rewrittenCount--
|
||||||
return nil
|
return nil
|
||||||
@ -175,7 +175,7 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*commands.Commit, erro
|
|||||||
// and extracts out the sha and names of commits that we still have to go
|
// and extracts out the sha and names of commits that we still have to go
|
||||||
// in the rebase:
|
// in the rebase:
|
||||||
func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*commands.Commit, error) {
|
func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*commands.Commit, error) {
|
||||||
bytesContent, err := ioutil.ReadFile(".git/rebase-merge/git-rebase-todo")
|
bytesContent, err := ioutil.ReadFile(fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.GitCommand.DotGitDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Info(fmt.Sprintf("error occured reading git-rebase-todo: %s", err.Error()))
|
c.Log.Info(fmt.Sprintf("error occured reading git-rebase-todo: %s", err.Error()))
|
||||||
// we assume an error means the file doesn't exist so we just return
|
// we assume an error means the file doesn't exist so we just return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user