diff --git a/main.go b/main.go index c1de86e30..3a0f07148 100644 --- a/main.go +++ b/main.go @@ -7,12 +7,14 @@ import ( "os" "path/filepath" "runtime" + "strings" "github.com/integrii/flaggy" "github.com/jesseduffield/lazygit/pkg/app" "github.com/jesseduffield/lazygit/pkg/app/daemon" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/env" + "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/logs" yaml "github.com/jesseduffield/yaml" ) @@ -33,9 +35,8 @@ func main() { filterPath := "" flaggy.String(&filterPath, "f", "filter", "Path to filter on in `git log -- `. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted") - dump := "" - flaggy.AddPositionalValue(&dump, "gitargs", 1, false, "Todo file") - flaggy.DefaultParser.PositionalFlags[0].Hidden = true + gitArg := "" + flaggy.AddPositionalValue(&gitArg, "git-arg", 1, false, "Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.") versionFlag := false flaggy.Bool(&versionFlag, "v", "version", "Print the current version") @@ -148,5 +149,31 @@ func main() { return } - app.Run(appConfig, common, filterPath) + parsedGitArg := parseGitArg(gitArg) + + app.Run(appConfig, common, types.NewStartArgs(filterPath, parsedGitArg)) +} + +func parseGitArg(gitArg string) types.GitArg { + typedArg := types.GitArg(gitArg) + + // using switch so that linter catches when a new git arg value is defined but not handled here + switch typedArg { + case types.GitArgNone, types.GitArgStatus, types.GitArgBranch, types.GitArgLog, types.GitArgStash: + return typedArg + } + + permittedValues := []string{ + string(types.GitArgStatus), + string(types.GitArgBranch), + string(types.GitArgLog), + string(types.GitArgStash), + } + + log.Fatalf("Invalid git arg value: '%s'. Must be one of the following values: %s. e.g. 'lazygit status'. See 'lazygit --help'.", + gitArg, + strings.Join(permittedValues, ", "), + ) + + panic("unreachable") } diff --git a/pkg/app/app.go b/pkg/app/app.go index f12c0f08a..e02c88339 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -22,6 +22,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/constants" "github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/gui" + "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/updates" ) @@ -37,11 +38,11 @@ type App struct { Updater *updates.Updater // may only need this on the Gui } -func Run(config config.AppConfigurer, common *common.Common, filterPath string) { +func Run(config config.AppConfigurer, common *common.Common, startArgs types.StartArgs) { app, err := NewApp(config, common) if err == nil { - err = app.Run(filterPath) + err = app.Run(startArgs) } if err != nil { @@ -203,8 +204,8 @@ func (app *App) setupRepo() (bool, error) { return false, nil } -func (app *App) Run(filterPath string) error { - err := app.Gui.RunAndHandleError(filterPath) +func (app *App) Run(startArgs types.StartArgs) error { + err := app.Gui.RunAndHandleError(startArgs) return err } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 3f07fb986..531a43035 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -249,7 +249,7 @@ type guiMutexes struct { PopupMutex *sync.Mutex } -func (gui *Gui) onNewRepo(filterPath string, reuseState bool) error { +func (gui *Gui) onNewRepo(startArgs types.StartArgs, reuseState bool) error { var err error gui.git, err = commands.NewGitCommand( gui.Common, @@ -261,7 +261,7 @@ func (gui *Gui) onNewRepo(filterPath string, reuseState bool) error { return err } - gui.resetState(filterPath, reuseState) + gui.resetState(startArgs, reuseState) gui.resetControllers() @@ -281,7 +281,7 @@ func (gui *Gui) onNewRepo(filterPath string, reuseState bool) error { // it gets a bit confusing to land back in the status panel when visiting a repo // you've already switched from. There's no doubt some easy way to make the UX // optimal for all cases but I'm too lazy to think about what that is right now -func (gui *Gui) resetState(filterPath string, reuseState bool) { +func (gui *Gui) resetState(startArgs types.StartArgs, reuseState bool) { currentDir, err := os.Getwd() if reuseState { @@ -306,12 +306,8 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) { contextTree := gui.contextTree() - screenMode := SCREEN_NORMAL - var initialContext types.IListContext = contextTree.Files - if filterPath != "" { - screenMode = SCREEN_HALF - initialContext = contextTree.LocalCommits - } + initialContext := initialContext(contextTree, startArgs) + initialScreenMode := initialScreenMode(startArgs) viewContextMap := context.NewViewContextMap() for viewName, context := range initialViewContextMapping(contextTree) { @@ -338,13 +334,13 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) { }, Ptmx: nil, Modes: &types.Modes{ - Filtering: filtering.New(filterPath), + Filtering: filtering.New(startArgs.FilterPath), CherryPicking: cherrypicking.New(), Diffing: diffing.New(), }, ViewContextMap: viewContextMap, ViewTabContextMap: gui.initialViewTabContextMap(contextTree), - ScreenMode: screenMode, + ScreenMode: initialScreenMode, // TODO: put contexts in the context manager ContextManager: NewContextManager(initialContext), Contexts: contextTree, @@ -355,6 +351,37 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) { gui.RepoStateMap[Repo(currentDir)] = gui.State } +func initialScreenMode(startArgs types.StartArgs) WindowMaximisation { + if startArgs.FilterPath != "" || startArgs.GitArg != types.GitArgNone { + return SCREEN_HALF + } else { + return SCREEN_NORMAL + } +} + +func initialContext(contextTree *context.ContextTree, startArgs types.StartArgs) types.IListContext { + var initialContext types.IListContext = contextTree.Files + + if startArgs.FilterPath != "" { + initialContext = contextTree.LocalCommits + } else if startArgs.GitArg != types.GitArgNone { + switch startArgs.GitArg { + case types.GitArgStatus: + initialContext = contextTree.Files + case types.GitArgBranch: + initialContext = contextTree.Branches + case types.GitArgLog: + initialContext = contextTree.LocalCommits + case types.GitArgStash: + initialContext = contextTree.Stash + default: + panic("unhandled git arg") + } + } + + return initialContext +} + func (gui *Gui) syncViewContexts() { for viewName, context := range gui.State.ViewContextMap.Entries() { view, err := gui.g.View(viewName) @@ -506,7 +533,7 @@ func (gui *Gui) initialViewTabContextMap(contextTree *context.ContextTree) map[s } // Run: setup the gui with keybindings and start the mainloop -func (gui *Gui) Run(filterPath string) error { +func (gui *Gui) Run(startArgs types.StartArgs) error { g, err := gui.initGocui() if err != nil { return err @@ -559,7 +586,7 @@ func (gui *Gui) Run(filterPath string) error { } // onNewRepo must be called after g.SetManager because SetManager deletes keybindings - if err := gui.onNewRepo(filterPath, false); err != nil { + if err := gui.onNewRepo(startArgs, false); err != nil { return err } @@ -592,10 +619,10 @@ func (gui *Gui) Run(filterPath string) error { return gui.g.MainLoop() } -func (gui *Gui) RunAndHandleError(filterPath string) error { +func (gui *Gui) RunAndHandleError(startArgs types.StartArgs) error { gui.stopChan = make(chan struct{}) return utils.SafeWithError(func() error { - if err := gui.Run(filterPath); err != nil { + if err := gui.Run(startArgs); err != nil { for _, manager := range gui.viewBufferManagerMap { manager.Close() } diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index dc2f9f17f..6b5a0b73d 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -79,7 +79,7 @@ func (gui *Gui) dispatchSwitchToRepo(path string, reuse bool) error { gui.Mutexes.RefreshingFilesMutex.Lock() defer gui.Mutexes.RefreshingFilesMutex.Unlock() - return gui.onNewRepo("", reuse) + return gui.onNewRepo(types.StartArgs{}, reuse) } // updateRecentRepoList registers the fact that we opened lazygit in this repo, diff --git a/pkg/gui/types/main_args.go b/pkg/gui/types/main_args.go new file mode 100644 index 000000000..b055b3736 --- /dev/null +++ b/pkg/gui/types/main_args.go @@ -0,0 +1,26 @@ +package types + +// StartArgs is the struct that represents some things we want to do on program start +type StartArgs struct { + // FilterPath determines which path we're going to filter on so that we only see commits from that file. + FilterPath string + // GitArg determines what context we open in + GitArg GitArg +} + +type GitArg string + +const ( + GitArgNone GitArg = "" + GitArgStatus GitArg = "status" + GitArgBranch GitArg = "branch" + GitArgLog GitArg = "log" + GitArgStash GitArg = "stash" +) + +func NewStartArgs(filterPath string, gitArg GitArg) StartArgs { + return StartArgs{ + FilterPath: filterPath, + GitArg: gitArg, + } +} diff --git a/test/integration/gitArg/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration/gitArg/expected/repo/.git_keep/COMMIT_EDITMSG new file mode 100644 index 000000000..907b30816 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/COMMIT_EDITMSG @@ -0,0 +1 @@ +blah diff --git a/test/integration/gitArg/expected/repo/.git_keep/FETCH_HEAD b/test/integration/gitArg/expected/repo/.git_keep/FETCH_HEAD new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/gitArg/expected/repo/.git_keep/HEAD b/test/integration/gitArg/expected/repo/.git_keep/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/integration/gitArg/expected/repo/.git_keep/config b/test/integration/gitArg/expected/repo/.git_keep/config new file mode 100644 index 000000000..8ae104545 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/config @@ -0,0 +1,10 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[user] + email = CI@example.com + name = CI diff --git a/test/integration/gitArg/expected/repo/.git_keep/description b/test/integration/gitArg/expected/repo/.git_keep/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/integration/gitArg/expected/repo/.git_keep/index b/test/integration/gitArg/expected/repo/.git_keep/index new file mode 100644 index 000000000..65d675154 Binary files /dev/null and b/test/integration/gitArg/expected/repo/.git_keep/index differ diff --git a/test/integration/gitArg/expected/repo/.git_keep/info/exclude b/test/integration/gitArg/expected/repo/.git_keep/info/exclude new file mode 100644 index 000000000..8e9f2071f --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/info/exclude @@ -0,0 +1,7 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ +.DS_Store diff --git a/test/integration/gitArg/expected/repo/.git_keep/logs/HEAD b/test/integration/gitArg/expected/repo/.git_keep/logs/HEAD new file mode 100644 index 000000000..eaac3a34d --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/logs/HEAD @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 CI 1654768290 +1000 commit (initial): blah +45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 CI 1654768290 +1000 checkout: moving from master to other +45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 CI 1654768291 +1000 checkout: moving from other to master diff --git a/test/integration/gitArg/expected/repo/.git_keep/logs/refs/heads/master b/test/integration/gitArg/expected/repo/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..12b2c7ee4 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 CI 1654768290 +1000 commit (initial): blah diff --git a/test/integration/gitArg/expected/repo/.git_keep/logs/refs/heads/other b/test/integration/gitArg/expected/repo/.git_keep/logs/refs/heads/other new file mode 100644 index 000000000..d1b9aa145 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/logs/refs/heads/other @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 CI 1654768290 +1000 branch: Created from HEAD diff --git a/test/integration/gitArg/expected/repo/.git_keep/objects/45/fe0608335366a31a1ad6dacbdcc6b17d31a5b6 b/test/integration/gitArg/expected/repo/.git_keep/objects/45/fe0608335366a31a1ad6dacbdcc6b17d31a5b6 new file mode 100644 index 000000000..3171fbc5a Binary files /dev/null and b/test/integration/gitArg/expected/repo/.git_keep/objects/45/fe0608335366a31a1ad6dacbdcc6b17d31a5b6 differ diff --git a/test/integration/gitArg/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/test/integration/gitArg/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 new file mode 100644 index 000000000..adf64119a Binary files /dev/null and b/test/integration/gitArg/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 differ diff --git a/test/integration/gitArg/expected/repo/.git_keep/refs/heads/master b/test/integration/gitArg/expected/repo/.git_keep/refs/heads/master new file mode 100644 index 000000000..86a280f52 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/refs/heads/master @@ -0,0 +1 @@ +45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 diff --git a/test/integration/gitArg/expected/repo/.git_keep/refs/heads/other b/test/integration/gitArg/expected/repo/.git_keep/refs/heads/other new file mode 100644 index 000000000..86a280f52 --- /dev/null +++ b/test/integration/gitArg/expected/repo/.git_keep/refs/heads/other @@ -0,0 +1 @@ +45fe0608335366a31a1ad6dacbdcc6b17d31a5b6 diff --git a/test/integration/gitArg/recording.json b/test/integration/gitArg/recording.json new file mode 100644 index 000000000..0a42da3cf --- /dev/null +++ b/test/integration/gitArg/recording.json @@ -0,0 +1 @@ +{"KeyEvents":[{"Timestamp":620,"Mod":0,"Key":258,"Ch":0},{"Timestamp":995,"Mod":0,"Key":256,"Ch":32},{"Timestamp":1865,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]} \ No newline at end of file diff --git a/test/integration/gitArg/setup.sh b/test/integration/gitArg/setup.sh new file mode 100644 index 000000000..d71cc83e4 --- /dev/null +++ b/test/integration/gitArg/setup.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +cd $1 + +git init + +git config user.email "CI@example.com" +git config user.name "CI" + +git commit --allow-empty -m "blah" + +git checkout -b other diff --git a/test/integration/gitArg/test.json b/test/integration/gitArg/test.json new file mode 100644 index 000000000..0261df3ea --- /dev/null +++ b/test/integration/gitArg/test.json @@ -0,0 +1,5 @@ +{ + "description": "Open lazygit to the branches panel", + "speed": 10, + "extraCmdArgs": "branch" +}