mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Support opening lazygit in a submodule
This commit is contained in:
		| @@ -25,13 +25,10 @@ func verifyInGitRepo(runCmd func(string) error) error { | ||||
|  | ||||
| func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error { | ||||
| 	for { | ||||
| 		f, err := stat(".git") | ||||
| 		_, err := stat(".git") | ||||
|  | ||||
| 		if err == nil { | ||||
| 			if f.IsDir() { | ||||
| 				return nil | ||||
| 			} | ||||
| 			return errors.New("expected .git to be a directory") | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		if !os.IsNotExist(err) { | ||||
| @@ -75,6 +72,7 @@ type GitCommand struct { | ||||
| 	getGlobalGitConfig func(string) (string, error) | ||||
| 	getLocalGitConfig  func(string) (string, error) | ||||
| 	removeFile         func(string) error | ||||
| 	DotGitDir          string | ||||
| } | ||||
|  | ||||
| // 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{ | ||||
| 		Log:                log, | ||||
| 		OSCommand:          osCommand, | ||||
| @@ -112,9 +115,31 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer, | ||||
| 		getGlobalGitConfig: gitconfig.Global, | ||||
| 		getLocalGitConfig:  gitconfig.Local, | ||||
| 		removeFile:         os.RemoveAll, | ||||
| 		DotGitDir:          dotGitDir, | ||||
| 	}, 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 | ||||
| func (c *GitCommand) GetStashEntries() []*StashEntry { | ||||
| 	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 | ||||
| // and "interactive" for interactive rebase | ||||
| 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 { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if exists { | ||||
| 		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 { | ||||
| 		return "interactive", err | ||||
| 	} 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 | ||||
| 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) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -775,7 +800,7 @@ func (c *GitCommand) getTodoCommitCount(content []string) int { | ||||
|  | ||||
| // MoveTodoDown moves a rebase todo item down by one position | ||||
| 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) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/go-errors/errors" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/i18n" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/test" | ||||
| 	"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) { | ||||
| 	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 { | ||||
| 		content := string(bytesContent) | ||||
| 		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 | ||||
| 	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 { | ||||
| 			rewrittenCount-- | ||||
| 			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 | ||||
| // in the rebase: | ||||
| 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 { | ||||
| 		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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user