summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main.go32
-rw-r--r--pkg/app/app.go16
-rw-r--r--pkg/commands/git.go85
-rw-r--r--pkg/commands/git_test.go26
4 files changed, 102 insertions, 57 deletions
diff --git a/main.go b/main.go
index 697129f80..534285afa 100644
--- a/main.go
+++ b/main.go
@@ -4,6 +4,7 @@ import (
"fmt"
"log"
"os"
+ "path/filepath"
"runtime"
"github.com/go-errors/errors"
@@ -22,8 +23,8 @@ var (
func main() {
flaggy.DefaultParser.ShowVersionWithVersionFlag = false
- repoPath := "."
- flaggy.String(&repoPath, "p", "path", "Path of git repo")
+ repoPath := ""
+ flaggy.String(&repoPath, "p", "path", "Path of git repo. (Deprecated: use --git-dir for git directory and --work-tree for work tree directory)")
filterPath := ""
flaggy.String(&filterPath, "f", "filter", "Path to filter on in `git log -- <path>`. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted")
@@ -44,8 +45,31 @@ func main() {
configFlag := false
flaggy.Bool(&configFlag, "c", "config", "Print the default config")
+ workTree := ""
+ flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
+
+ gitDir := ""
+ flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
+
flaggy.Parse()
+ if repoPath != "" {
+ if workTree != "" || gitDir != "" {
+ log.Fatal("--path option is incompatible with the --work-tree and --git-dir options")
+ }
+
+ workTree = repoPath
+ gitDir = filepath.Join(repoPath, ".git")
+ }
+
+ if workTree != "" {
+ os.Setenv("GIT_WORK_TREE", workTree)
+ }
+
+ if gitDir != "" {
+ os.Setenv("GIT_DIR", gitDir)
+ }
+
if versionFlag {
fmt.Printf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", commit, date, buildSource, version, runtime.GOOS, runtime.GOARCH)
os.Exit(0)
@@ -61,8 +85,8 @@ func main() {
os.Exit(0)
}
- if repoPath != "." {
- if err := os.Chdir(repoPath); err != nil {
+ if workTree != "" {
+ if err := os.Chdir(workTree); err != nil {
log.Fatal(err.Error())
}
}
diff --git a/pkg/app/app.go b/pkg/app/app.go
index 31144cc11..6446d8acd 100644
--- a/pkg/app/app.go
+++ b/pkg/app/app.go
@@ -121,6 +121,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
if err != nil {
return app, err
}
+
app.Gui, err = gui.NewGui(app.Log, app.GitCommand, app.OSCommand, app.Tr, config, app.Updater, filterPath, showRecentRepos)
if err != nil {
return app, err
@@ -169,6 +170,11 @@ func (app *App) setupRepo() (bool, error) {
return false, err
}
+ if os.Getenv("GIT_DIR") != "" {
+ // we've been given the git dir directly. We'll verify this dir when initializing our GitCommand object
+ return false, nil
+ }
+
// if we are not in a git repo, we ask if we want to `git init`
if err := app.OSCommand.RunCommand("git status"); err != nil {
cwd, err := os.Getwd()
@@ -219,6 +225,14 @@ func (app *App) Run() error {
return err
}
+func gitDir() string {
+ dir := os.Getenv("GIT_DIR")
+ if dir == "" {
+ return ".git"
+ }
+ return dir
+}
+
// Rebase contains logic for when we've been run in demon mode, meaning we've
// given lazygit as a command for git to call e.g. to edit a file
func (app *App) Rebase() error {
@@ -230,7 +244,7 @@ func (app *App) Rebase() error {
return err
}
- } else if strings.HasSuffix(os.Args[1], ".git/COMMIT_EDITMSG") {
+ } else if strings.HasSuffix(os.Args[1], filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
// but in this case we don't need to edit it, so we'll just return
} else {
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index 8a7abb55b..da4324d4d 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -35,6 +35,16 @@ func verifyInGitRepo(runCmd func(string, ...interface{}) error) error {
}
func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
+ if os.Getenv("GIT_DIR") != "" {
+ // we've been given the git directory explicitly so no need to navigate to it
+ _, err := stat(os.Getenv("GIT_DIR"))
+ if err != nil {
+ return WrapError(err)
+ }
+
+ return nil
+ }
+
for {
_, err := stat(".git")
@@ -52,31 +62,29 @@ func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir f
}
}
-func setupRepositoryAndWorktree(openGitRepository func(string) (*gogit.Repository, error), sLocalize func(string) string) (repository *gogit.Repository, worktree *gogit.Worktree, err error) {
- repository, err = openGitRepository(".")
+func setupRepository(openGitRepository func(string) (*gogit.Repository, error), sLocalize func(string) string) (*gogit.Repository, error) {
+ path := os.Getenv("GIT_DIR")
+ if path == "" {
+ path = "."
+ }
+
+ repository, err := openGitRepository(path)
if err != nil {
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
- return nil, nil, errors.New(sLocalize("GitconfigParseErr"))
+ return nil, errors.New(sLocalize("GitconfigParseErr"))
}
- return
- }
-
- worktree, err = repository.Worktree()
-
- if err != nil {
- return
+ return nil, err
}
- return
+ return repository, err
}
// GitCommand is our main git interface
type GitCommand struct {
Log *logrus.Entry
OSCommand *OSCommand
- Worktree *gogit.Worktree
Repo *gogit.Repository
Tr *i18n.Localizer
Config config.AppConfigurer
@@ -93,7 +101,7 @@ type GitCommand struct {
// NewGitCommand it runs git commands
func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer, config config.AppConfigurer) (*GitCommand, error) {
- var worktree *gogit.Worktree
+ // var worktree *gogit.Worktree
var repo *gogit.Repository
// see what our default push behaviour is
@@ -105,24 +113,16 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
pushToCurrent = strings.TrimSpace(output) == "current"
}
- fs := []func() error{
- func() error {
- return verifyInGitRepo(osCommand.RunCommand)
- },
- func() error {
- return navigateToRepoRootDirectory(os.Stat, os.Chdir)
- },
- func() error {
- var err error
- repo, worktree, err = setupRepositoryAndWorktree(gogit.PlainOpen, tr.SLocalize)
- return err
- },
+ if err := verifyInGitRepo(osCommand.RunCommand); err != nil {
+ return nil, err
}
- for _, f := range fs {
- if err := f(); err != nil {
- return nil, err
- }
+ if err := navigateToRepoRootDirectory(os.Stat, os.Chdir); err != nil {
+ return nil, err
+ }
+
+ if repo, err = setupRepository(gogit.PlainOpen, tr.SLocalize); err != nil {
+ return nil, err
}
dotGitDir, err := findDotGitDir(os.Stat, ioutil.ReadFile)
@@ -134,7 +134,6 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
Log: log,
OSCommand: osCommand,
Tr: tr,
- Worktree: worktree,
Repo: repo,
Config: config,
getGlobalGitConfig: gitconfig.Global,
@@ -150,6 +149,10 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
}
func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) {
+ if os.Getenv("GIT_DIR") != "" {
+ return os.Getenv("GIT_DIR"), nil
+ }
+
f, err := stat(".git")
if err != nil {
return "", err
@@ -236,8 +239,22 @@ type GetStatusFileOptions struct {
NoRenames bool
}
+func (c *GitCommand) GetConfigValue(key string) string {
+ output, _ := c.OSCommand.RunCommandWithOutput("git config --get %s", key)
+ // looks like this returns an error if there is no matching value which we're okay with
+ return strings.TrimSpace(output)
+}
+
func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*File {
- statusOutput, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames})
+ // check if config wants us ignoring untracked files
+ untrackedFilesSetting := c.GetConfigValue("status.showUntrackedFiles")
+
+ if untrackedFilesSetting == "" {
+ untrackedFilesSetting = "all"
+ }
+ untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
+
+ statusOutput, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
if err != nil {
c.Log.Error(err)
}
@@ -582,7 +599,8 @@ func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
// GitStatus returns the plaintext short status of the repo
type GitStatusOptions struct {
- NoRenames bool
+ NoRenames bool
+ UntrackedFilesArg string
}
func (c *GitCommand) GitStatus(opts GitStatusOptions) (string, error) {
@@ -590,7 +608,8 @@ func (c *GitCommand) GitStatus(opts GitStatusOptions) (string, error) {
if opts.NoRenames {
noRenamesFlag = "--no-renames"
}
- return c.OSCommand.RunCommandWithOutput("git status --untracked-files=all --porcelain %s", noRenamesFlag)
+
+ return c.OSCommand.RunCommandWithOutput("git status %s --porcelain %s", opts.UntrackedFilesArg, noRenamesFlag)
}
// IsInMergeState states whether we are still mid-merge
diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go
index d6761148b..b2a20fe08 100644
--- a/pkg/commands/git_test.go
+++ b/pkg/commands/git_test.go
@@ -150,13 +150,13 @@ func TestNavigateToRepoRootDirectory(t *testing.T) {
}
}
-// TestSetupRepositoryAndWorktree is a function.
-func TestSetupRepositoryAndWorktree(t *testing.T) {
+// TestSetupRepository is a function.
+func TestSetupRepository(t *testing.T) {
type scenario struct {
testName string
openGitRepository func(string) (*gogit.Repository, error)
sLocalize func(string) string
- test func(*gogit.Repository, *gogit.Worktree, error)
+ test func(*gogit.Repository, error)
}
scenarios := []scenario{
@@ -168,7 +168,7 @@ func TestSetupRepositoryAndWorktree(t *testing.T) {
func(string) string {
return "error translated"
},
- func(r *gogit.Repository, w *gogit.Worktree, err error) {
+ func(r *gogit.Repository, err error) {
assert.Error(t, err)
assert.EqualError(t, err, "error translated")
},
@@ -179,23 +179,12 @@ func TestSetupRepositoryAndWorktree(t *testing.T) {
return nil, fmt.Errorf("Error from inside gogit")
},
func(string) string { return "" },
- func(r *gogit.Repository, w *gogit.Worktree, err error) {
+ func(r *gogit.Repository, err error) {
assert.Error(t, err)
assert.EqualError(t, err, "Error from inside gogit")
},
},
{
- "An error occurred cause git repository is a bare repository",
- func(string) (*gogit.Repository, error) {
- return &gogit.Repository{}, nil
- },
- func(string) string { return "" },
- func(r *gogit.Repository, w *gogit.Worktree, err error) {
- assert.Error(t, err)
- assert.Equal(t, gogit.ErrIsBareRepository, err)
- },
- },
- {
"Setup done properly",
func(string) (*gogit.Repository, error) {
assert.NoError(t, os.RemoveAll("/tmp/lazygit-test"))
@@ -204,9 +193,8 @@ func TestSetupRepositoryAndWorktree(t *testing.T) {
return r, nil
},
func(string) string { return "" },
- func(r *gogit.Repository, w *gogit.Worktree, err error) {
+ func(r *gogit.Repository, err error) {
assert.NoError(t, err)
- assert.NotNil(t, w)
assert.NotNil(t, r)
},
},
@@ -214,7 +202,7 @@ func TestSetupRepositoryAndWorktree(t *testing.T) {
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
- s.test(setupRepositoryAndWorktree(s.openGitRepository, s.sLocalize))
+ s.test(setupRepository(s.openGitRepository, s.sLocalize))
})
}
}