Skip to content

Support bare repositories #1023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"

"github.com/go-errors/errors"
"github.com/integrii/flaggy"
"github.com/jesseduffield/lazygit/pkg/app"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/env"
)

var (
Expand All @@ -22,8 +24,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. (equivalent to --work-tree=<path> --git-dir=<path>/.git/)")

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")
Expand All @@ -44,8 +46,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 != "" {
env.SetGitWorkTreeEnv(workTree)
}

if gitDir != "" {
env.SetGitDirEnv(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)
Expand All @@ -61,8 +86,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())
}
}
Expand Down
17 changes: 16 additions & 1 deletion pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/aybabtme/humanlog"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/updates"
Expand Down Expand Up @@ -121,6 +122,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
Expand Down Expand Up @@ -169,6 +171,11 @@ func (app *App) setupRepo() (bool, error) {
return false, err
}

if env.GetGitDirEnv() != "" {
// 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()
Expand Down Expand Up @@ -219,6 +226,14 @@ func (app *App) Run() error {
return err
}

func gitDir() string {
dir := env.GetGitDirEnv()
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 {
Expand All @@ -230,7 +245,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 {
Expand Down
6 changes: 3 additions & 3 deletions pkg/commands/commit_list_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,15 @@ func (c *CommitListBuilder) getRebasingCommits(rebaseMode string) ([]*Commit, er

func (c *CommitListBuilder) getNormalRebasingCommits() ([]*Commit, error) {
rewrittenCount := 0
bytesContent, err := ioutil.ReadFile(fmt.Sprintf("%s/rebase-apply/rewritten", c.GitCommand.DotGitDir))
bytesContent, err := ioutil.ReadFile(filepath.Join(c.GitCommand.DotGitDir, "rebase-apply/rewritten"))
if err == nil {
content := string(bytesContent)
rewrittenCount = len(strings.Split(content, "\n"))
}

// we know we're rebasing, so lets get all the files whose names have numbers
commits := []*Commit{}
err = filepath.Walk(fmt.Sprintf("%s/rebase-apply", c.GitCommand.DotGitDir), func(path string, f os.FileInfo, err error) error {
err = filepath.Walk(filepath.Join(c.GitCommand.DotGitDir, "rebase-apply"), func(path string, f os.FileInfo, err error) error {
if rewrittenCount > 0 {
rewrittenCount--
return nil
Expand Down Expand Up @@ -246,7 +246,7 @@ func (c *CommitListBuilder) getNormalRebasingCommits() ([]*Commit, error) {
// and extracts out the sha and names of commits that we still have to go
// in the rebase:
func (c *CommitListBuilder) getInteractiveRebasingCommits() ([]*Commit, error) {
bytesContent, err := ioutil.ReadFile(fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.GitCommand.DotGitDir))
bytesContent, err := ioutil.ReadFile(filepath.Join(c.GitCommand.DotGitDir, "rebase-merge/git-rebase-todo"))
if err != nil {
c.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
// we assume an error means the file doesn't exist so we just return
Expand Down
104 changes: 66 additions & 38 deletions pkg/commands/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
gogit "github.com/go-git/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus"
Expand All @@ -35,6 +36,19 @@ func verifyInGitRepo(runCmd func(string, ...interface{}) error) error {
}

func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
gitDir := env.GetGitDirEnv()
if gitDir != "" {
// we've been given the git directory explicitly so no need to navigate to it
_, err := stat(gitDir)
if err != nil {
return WrapError(err)
}

return nil
}

// 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)

for {
_, err := stat(".git")

Expand All @@ -52,31 +66,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 := env.GetGitDirEnv()
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
Expand All @@ -93,7 +105,6 @@ 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 repo *gogit.Repository

// see what our default push behaviour is
Expand All @@ -105,24 +116,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)
Expand All @@ -134,7 +137,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,
Expand All @@ -150,6 +152,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 env.GetGitDirEnv() != "" {
return env.GetGitDirEnv(), nil
}

f, err := stat(".git")
if err != nil {
return "", err
Expand Down Expand Up @@ -236,8 +242,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)
}
Expand Down Expand Up @@ -582,33 +602,35 @@ 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) {
noRenamesFlag := ""
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
func (c *GitCommand) IsInMergeState() (bool, error) {
return c.OSCommand.FileExists(fmt.Sprintf("%s/MERGE_HEAD", c.DotGitDir))
return c.OSCommand.FileExists(filepath.Join(c.DotGitDir, "MERGE_HEAD"))
}

// 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(fmt.Sprintf("%s/rebase-apply", c.DotGitDir))
exists, err := c.OSCommand.FileExists(filepath.Join(c.DotGitDir, "rebase-apply"))
if err != nil {
return "", err
}
if exists {
return "normal", nil
}
exists, err = c.OSCommand.FileExists(fmt.Sprintf("%s/rebase-merge", c.DotGitDir))
exists, err = c.OSCommand.FileExists(filepath.Join(c.DotGitDir, "rebase-merge"))
if exists {
return "interactive", err
} else {
Expand Down Expand Up @@ -987,7 +1009,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 := fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.DotGitDir)
fileName := filepath.Join(c.DotGitDir, "rebase-merge/git-rebase-todo")
bytes, err := ioutil.ReadFile(fileName)
if err != nil {
return err
Expand Down Expand Up @@ -1019,7 +1041,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 := fmt.Sprintf("%s/rebase-merge/git-rebase-todo", c.DotGitDir)
fileName := filepath.Join(c.DotGitDir, "rebase-merge/git-rebase-todo")
bytes, err := ioutil.ReadFile(fileName)
if err != nil {
return err
Expand Down Expand Up @@ -1408,3 +1430,9 @@ func (c *GitCommand) WorkingTreeState() string {
}
return "normal"
}

func (c *GitCommand) IsBareRepo() bool {
// note: could use `git rev-parse --is-bare-repository` if we wanna drop go-git
_, err := c.Repo.Worktree()
return err == gogit.ErrIsBareRepository
}
Loading