2018-08-12 11:31:27 +02:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2019-03-16 02:31:09 +02:00
|
|
|
"bufio"
|
2019-03-02 11:32:16 +02:00
|
|
|
"fmt"
|
2021-10-23 00:52:19 +02:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
"github.com/go-errors/errors"
|
2023-05-25 10:18:35 +02:00
|
|
|
"github.com/sirupsen/logrus"
|
2022-05-07 07:23:08 +02:00
|
|
|
|
2022-03-20 01:19:14 +02:00
|
|
|
"github.com/jesseduffield/generics/slices"
|
2022-08-09 13:27:12 +02:00
|
|
|
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
2018-08-12 11:31:27 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
2022-08-15 14:59:34 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
2020-09-29 11:10:57 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
2021-12-29 02:37:15 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/common"
|
2018-08-12 11:31:27 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
2022-05-07 07:23:08 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/constants"
|
2020-09-27 08:17:26 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/env"
|
2018-08-13 12:26:02 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
2018-08-14 15:26:25 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
2023-05-25 10:18:35 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/logs"
|
2018-08-19 15:28:29 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
2018-08-12 11:31:27 +02:00
|
|
|
)
|
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
// App is the struct that's instantiated from within main.go and it manages
|
|
|
|
// bootstrapping and running the application.
|
2018-08-12 11:31:27 +02:00
|
|
|
type App struct {
|
2021-12-29 02:37:15 +02:00
|
|
|
*common.Common
|
2022-05-07 07:23:08 +02:00
|
|
|
closers []io.Closer
|
|
|
|
Config config.AppConfigurer
|
|
|
|
OSCommand *oscommands.OSCommand
|
|
|
|
Gui *gui.Gui
|
2019-07-07 20:15:11 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 12:27:44 +02:00
|
|
|
func Run(
|
|
|
|
config config.AppConfigurer,
|
|
|
|
common *common.Common,
|
2022-08-09 13:27:12 +02:00
|
|
|
startArgs appTypes.StartArgs,
|
2022-08-09 12:27:44 +02:00
|
|
|
) {
|
2022-05-07 07:23:08 +02:00
|
|
|
app, err := NewApp(config, common)
|
2018-08-26 07:46:18 +02:00
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
if err == nil {
|
2022-06-09 11:52:08 +02:00
|
|
|
err = app.Run(startArgs)
|
2019-03-16 01:37:31 +02:00
|
|
|
}
|
|
|
|
|
2018-08-13 13:16:21 +02:00
|
|
|
if err != nil {
|
2022-05-07 07:23:08 +02:00
|
|
|
if errorMessage, known := knownError(common.Tr, err); known {
|
|
|
|
log.Fatal(errorMessage)
|
|
|
|
}
|
|
|
|
newErr := errors.Wrap(err, 0)
|
|
|
|
stackTrace := newErr.ErrorStack()
|
|
|
|
app.Log.Error(stackTrace)
|
2018-08-12 11:31:27 +02:00
|
|
|
|
2022-05-18 13:33:35 +02:00
|
|
|
log.Fatalf("%s: %s\n\n%s", common.Tr.ErrorOccurred, constants.Links.Issues, stackTrace)
|
2018-08-26 07:46:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
2021-12-29 03:03:35 +02:00
|
|
|
userConfig := config.GetUserConfig()
|
|
|
|
|
2018-08-12 11:31:27 +02:00
|
|
|
var err error
|
2021-12-29 02:37:15 +02:00
|
|
|
log := newLogger(config)
|
2021-12-29 03:03:35 +02:00
|
|
|
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
|
2021-08-27 09:00:53 +02:00
|
|
|
if err != nil {
|
2022-05-07 07:23:08 +02:00
|
|
|
return nil, err
|
2021-08-27 09:00:53 +02:00
|
|
|
}
|
2018-08-14 14:12:07 +02:00
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
return &common.Common{
|
2021-12-29 02:37:15 +02:00
|
|
|
Log: log,
|
|
|
|
Tr: tr,
|
2021-12-29 03:03:35 +02:00
|
|
|
UserConfig: userConfig,
|
2021-12-29 02:37:15 +02:00
|
|
|
Debug: config.GetDebug(),
|
2022-05-07 07:23:08 +02:00
|
|
|
}, nil
|
|
|
|
}
|
2021-12-29 02:37:15 +02:00
|
|
|
|
2023-05-25 10:18:35 +02:00
|
|
|
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
|
|
|
|
if cfg.GetDebug() {
|
|
|
|
logPath, err := config.LogPath()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
return logs.NewDevelopmentLogger(logPath)
|
|
|
|
} else {
|
|
|
|
return logs.NewProductionLogger()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
// NewApp bootstrap a new application
|
|
|
|
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
|
|
|
app := &App{
|
|
|
|
closers: []io.Closer{},
|
|
|
|
Config: config,
|
|
|
|
Common: common,
|
2019-02-18 12:29:43 +02:00
|
|
|
}
|
|
|
|
|
2022-05-07 07:23:08 +02:00
|
|
|
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
2019-02-18 12:29:43 +02:00
|
|
|
|
2022-12-30 14:24:24 +02:00
|
|
|
updater, err := updates.NewUpdater(common, config, app.OSCommand)
|
2018-08-12 11:31:27 +02:00
|
|
|
if err != nil {
|
2018-08-18 11:43:58 +02:00
|
|
|
return app, err
|
2018-08-12 11:31:27 +02:00
|
|
|
}
|
2019-03-16 02:31:09 +02:00
|
|
|
|
2022-03-15 15:12:26 +02:00
|
|
|
dirName, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return app, err
|
|
|
|
}
|
|
|
|
|
2022-12-26 16:43:08 +02:00
|
|
|
gitVersion, err := app.validateGitVersion()
|
|
|
|
if err != nil {
|
|
|
|
return app, err
|
|
|
|
}
|
|
|
|
|
2020-08-16 14:49:37 +02:00
|
|
|
showRecentRepos, err := app.setupRepo()
|
|
|
|
if err != nil {
|
2019-03-16 02:31:09 +02:00
|
|
|
return app, err
|
|
|
|
}
|
|
|
|
|
2023-06-07 09:41:17 +02:00
|
|
|
// used for testing purposes
|
|
|
|
if os.Getenv("SHOW_RECENT_REPOS") == "true" {
|
|
|
|
showRecentRepos = true
|
|
|
|
}
|
|
|
|
|
2022-12-30 14:24:24 +02:00
|
|
|
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
|
2018-08-13 12:26:02 +02:00
|
|
|
if err != nil {
|
2018-08-18 11:43:58 +02:00
|
|
|
return app, err
|
2018-08-13 12:26:02 +02:00
|
|
|
}
|
2018-08-12 11:31:27 +02:00
|
|
|
return app, nil
|
|
|
|
}
|
|
|
|
|
2022-12-26 16:43:08 +02:00
|
|
|
func (app *App) validateGitVersion() (*git_commands.GitVersion, error) {
|
|
|
|
version, err := git_commands.GetGitVersion(app.OSCommand)
|
2020-08-27 14:19:03 +02:00
|
|
|
// if we get an error anywhere here we'll show the same status
|
2020-10-04 02:00:48 +02:00
|
|
|
minVersionError := errors.New(app.Tr.MinGitVersionError)
|
2020-08-27 14:19:03 +02:00
|
|
|
if err != nil {
|
2022-12-26 16:43:08 +02:00
|
|
|
return nil, minVersionError
|
2020-08-27 14:19:03 +02:00
|
|
|
}
|
2020-09-18 13:00:03 +02:00
|
|
|
|
2023-02-19 11:20:05 +02:00
|
|
|
if version.IsOlderThan(2, 20, 0) {
|
2022-12-26 16:43:08 +02:00
|
|
|
return nil, minVersionError
|
2020-09-18 13:00:03 +02:00
|
|
|
}
|
|
|
|
|
2022-12-26 16:43:08 +02:00
|
|
|
return version, nil
|
2020-08-27 14:19:03 +02:00
|
|
|
}
|
|
|
|
|
2022-06-09 15:57:54 +02:00
|
|
|
func isDirectoryAGitRepository(dir string) (bool, error) {
|
|
|
|
info, err := os.Stat(filepath.Join(dir, ".git"))
|
2022-06-17 21:20:37 +02:00
|
|
|
return info != nil, err
|
2022-06-09 15:57:54 +02:00
|
|
|
}
|
|
|
|
|
2022-08-01 21:05:35 +02:00
|
|
|
func openRecentRepo(app *App) bool {
|
|
|
|
for _, repoDir := range app.Config.GetAppState().RecentRepos {
|
|
|
|
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
|
|
|
|
if err := os.Chdir(repoDir); err == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-08-16 14:49:37 +02:00
|
|
|
func (app *App) setupRepo() (bool, error) {
|
2020-09-27 08:17:26 +02:00
|
|
|
if env.GetGitDirEnv() != "" {
|
2022-01-08 05:10:01 +02:00
|
|
|
// we've been given the git dir directly. We'll verify this dir when initializing our Git object
|
2020-09-27 07:36:04 +02:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2019-03-16 02:31:09 +02:00
|
|
|
// if we are not in a git repo, we ask if we want to `git init`
|
2021-03-30 13:17:42 +02:00
|
|
|
if err := commands.VerifyInGitRepo(app.OSCommand); err != nil {
|
2020-05-28 06:20:13 +02:00
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
2020-08-16 14:49:37 +02:00
|
|
|
return false, err
|
2019-03-16 02:31:09 +02:00
|
|
|
}
|
2022-07-30 00:55:34 +02:00
|
|
|
|
2022-06-09 15:57:54 +02:00
|
|
|
if isRepo, err := isDirectoryAGitRepository(cwd); isRepo {
|
|
|
|
return false, err
|
2020-05-28 06:20:13 +02:00
|
|
|
}
|
|
|
|
|
2022-08-08 02:23:56 +02:00
|
|
|
var shouldInitRepo bool
|
|
|
|
initialBranchArg := ""
|
|
|
|
switch app.UserConfig.NotARepository {
|
|
|
|
case "prompt":
|
2020-11-24 13:21:11 +02:00
|
|
|
// Offer to initialize a new repository in current directory.
|
|
|
|
fmt.Print(app.Tr.CreateRepo)
|
|
|
|
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
2022-08-08 02:23:56 +02:00
|
|
|
shouldInitRepo = (strings.Trim(response, " \r\n") == "y")
|
|
|
|
if shouldInitRepo {
|
2022-06-30 13:53:58 +02:00
|
|
|
// Ask for the initial branch name
|
|
|
|
fmt.Print(app.Tr.InitialBranch)
|
|
|
|
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
|
|
|
if trimmedResponse := strings.Trim(response, " \r\n"); len(trimmedResponse) > 0 {
|
2023-07-11 09:18:58 +02:00
|
|
|
initialBranchArg += "--initial-branch=" + trimmedResponse
|
2022-06-30 13:53:58 +02:00
|
|
|
}
|
2020-11-24 13:21:11 +02:00
|
|
|
}
|
2022-08-08 02:23:56 +02:00
|
|
|
case "create":
|
|
|
|
shouldInitRepo = true
|
|
|
|
case "skip":
|
2020-11-24 13:21:11 +02:00
|
|
|
shouldInitRepo = false
|
2022-08-08 02:23:56 +02:00
|
|
|
case "quit":
|
|
|
|
fmt.Fprintln(os.Stderr, app.Tr.NotARepository)
|
|
|
|
os.Exit(1)
|
|
|
|
default:
|
|
|
|
fmt.Fprintln(os.Stderr, app.Tr.IncorrectNotARepository)
|
|
|
|
os.Exit(1)
|
2020-11-24 13:21:11 +02:00
|
|
|
}
|
|
|
|
|
2022-08-08 02:23:56 +02:00
|
|
|
if shouldInitRepo {
|
2023-07-11 09:18:58 +02:00
|
|
|
args := []string{"git", "init"}
|
|
|
|
if initialBranchArg != "" {
|
|
|
|
args = append(args, initialBranchArg)
|
|
|
|
}
|
|
|
|
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
|
2022-08-08 02:23:56 +02:00
|
|
|
return false, err
|
2020-08-16 14:49:37 +02:00
|
|
|
}
|
2022-08-08 02:23:56 +02:00
|
|
|
return false, nil
|
2019-03-16 02:31:09 +02:00
|
|
|
}
|
2022-08-08 02:23:56 +02:00
|
|
|
|
|
|
|
// check if we have a recent repo we can open
|
|
|
|
for _, repoDir := range app.Config.GetAppState().RecentRepos {
|
|
|
|
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
|
|
|
|
if err := os.Chdir(repoDir); err == nil {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
2019-03-16 02:31:09 +02:00
|
|
|
}
|
2022-08-08 02:23:56 +02:00
|
|
|
|
|
|
|
fmt.Fprintln(os.Stderr, app.Tr.NoRecentRepositories)
|
|
|
|
os.Exit(1)
|
2019-03-16 02:31:09 +02:00
|
|
|
}
|
2021-03-30 13:17:42 +02:00
|
|
|
|
2022-07-30 00:55:34 +02:00
|
|
|
// Run this afterward so that the previous repo creation steps can run without this interfering
|
2022-08-15 14:59:34 +02:00
|
|
|
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
|
2022-07-30 00:55:34 +02:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2022-08-01 20:39:39 +02:00
|
|
|
fmt.Print(app.Tr.BareRepo)
|
|
|
|
|
|
|
|
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
2022-08-18 19:26:34 +02:00
|
|
|
|
2022-08-01 22:54:54 +02:00
|
|
|
if shouldOpenRecent := strings.Trim(response, " \r\n") == "y"; !shouldOpenRecent {
|
2022-08-01 21:05:35 +02:00
|
|
|
os.Exit(0)
|
2022-07-30 00:55:34 +02:00
|
|
|
}
|
2022-08-01 22:54:54 +02:00
|
|
|
|
|
|
|
if didOpenRepo := openRecentRepo(app); didOpenRepo {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(app.Tr.NoRecentRepositories)
|
|
|
|
os.Exit(1)
|
2022-07-30 00:55:34 +02:00
|
|
|
}
|
|
|
|
|
2020-08-16 14:49:37 +02:00
|
|
|
return false, nil
|
2019-03-16 02:31:09 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 13:27:12 +02:00
|
|
|
func (app *App) Run(startArgs appTypes.StartArgs) error {
|
2022-06-09 11:52:08 +02:00
|
|
|
err := app.Gui.RunAndHandleError(startArgs)
|
2019-07-07 20:15:11 +02:00
|
|
|
return err
|
2019-02-18 10:42:23 +02:00
|
|
|
}
|
|
|
|
|
2018-08-12 11:31:27 +02:00
|
|
|
// Close closes any resources
|
|
|
|
func (app *App) Close() error {
|
2022-03-20 01:19:14 +02:00
|
|
|
return slices.TryForEach(app.closers, func(closer io.Closer) error {
|
|
|
|
return closer.Close()
|
|
|
|
})
|
2018-08-12 11:31:27 +02:00
|
|
|
}
|