Skip to content
Open
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
7 changes: 7 additions & 0 deletions docs-master/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,13 @@ worktree:
# A leading "~" is expanded to your home directory, so "~/worktrees" works.
defaultPath: ""

# Config relating to multi-repo mode, which lazygit uses when you start it in a
# directory that is not a git repo but contains git repos
multiRepo:
# How many directory levels below the start directory lazygit searches for git
# repos. A value of 1 searches only the direct subdirectories.
maxDepth: 2

# Periodic update checks
update:
# One of: 'prompt' (default) | 'background' | 'never'
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` F `` | Add fork remote | Quickly add a fork remote by replacing the owner in the origin URL and optionally check out a branch from new remote. |
| `` / `` | Filter the current view by text | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Switch | Switch to the selected repository. |
| `` e `` | Edit config file | Open file in external editor. |
| `` u `` | Check for update | |
| `` / `` | Filter the current view by text | |

## Secondary

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` <enter> `` | 確認 | |
| `` <esc> `` | 閉じる/キャンセル | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | チェックアウト(切り替え) | Switch to the selected repository. |
| `` e `` | 設定ファイルを編集 | 外部エディタでファイルを開きます。 |
| `` u `` | 更新を確認 | |
| `` / `` | 現在のビューをテキストでフィルタリング | |

## コミット

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` <enter> `` | 커밋 보기 | |
| `` / `` | Filter the current view by text | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Switch | Switch to the selected repository. |
| `` e `` | 설정 파일 수정 | Open file in external editor. |
| `` u `` | 업데이트 확인 | |
| `` / `` | Filter the current view by text | |

## Secondary

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_nl.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` F `` | Add fork remote | Quickly add a fork remote by replacing the owner in the origin URL and optionally check out a branch from new remote. |
| `` / `` | Filter the current view by text | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Switch | Switch to the selected repository. |
| `` e `` | Verander config bestand | Open bestand in externe editor. |
| `` u `` | Check voor updates | |
| `` / `` | Filter the current view by text | |

## Secondary

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Przełącz | Switch to the selected repository. |
| `` e `` | Edytuj plik konfiguracyjny | Otwórz plik w zewnętrznym edytorze. |
| `` u `` | Sprawdź aktualizacje | |
| `` / `` | Filtruj bieżący widok po tekście | |

## Schowek

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_pt.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` F `` | Add fork remote | Quickly add a fork remote by replacing the owner in the origin URL and optionally check out a branch from new remote. |
| `` / `` | Filtrar a visualização atual por texto | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Switch | Switch to the selected repository. |
| `` e `` | Editar arquivo de configuração | Abrir arquivo no editor externo. |
| `` u `` | Verificar atualização | |
| `` / `` | Filtrar a visualização atual por texto | |

## Secundário

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` <enter> `` | Подтвердить | |
| `` <esc> `` | Закрыть/отменить | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Switch | Switch to the selected repository. |
| `` e `` | Редактировать файл конфигурации | Open file in external editor. |
| `` u `` | Проверить обновления | |
| `` / `` | Filter the current view by text | |

## Worktrees

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` ] `` | 下一个标签 | |
| `` [ `` | 上一个标签 | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 切换 | Switch to the selected repository. |
| `` e `` | 编辑配置文件 | 使用外部编辑器打开文件 |
| `` u `` | 检查更新 | |
| `` / `` | 通过文本过滤当前视图 | |

## 子提交

| Key | Action | Info |
Expand Down
9 changes: 9 additions & 0 deletions docs-master/keybindings/Keybindings_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` ] `` | 下一個索引標籤 | |
| `` [ `` | 上一個索引標籤 | |

## Repositories

| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 切換 | Switch to the selected repository. |
| `` e `` | 編輯設定檔案 | 使用外部編輯器開啟 |
| `` u `` | 檢查更新 | |
| `` / `` | 搜尋 | |

## 主面板(一般)

| Key | Action | Info |
Expand Down
74 changes: 51 additions & 23 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
common.Log.Infof("Error getting repo paths: %v", err)
}

showRecentRepos, err := app.setupRepo(repoPaths)
showRecentRepos, multiRepoRoot, err := app.setupRepo(repoPaths)
if err != nil {
return app, err
}
Expand All @@ -133,7 +133,7 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
showRecentRepos = true
}

app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName, test)
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName, multiRepoRoot, test)
if err != nil {
return app, err
}
Expand Down Expand Up @@ -170,18 +170,7 @@ func isDirectoryAGitRepository(dir string) (bool, error) {
func openRecentRepo(app *App) bool {
for _, repoDir := range app.Config.GetAppState().RecentRepos {
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
if err := os.Chdir(repoDir); err == nil {
// We're still in setup, before the gui exists, so we can't show the approval popup
// that DispatchSwitchTo offers for blocked .envrc files; just log and move on.
// Also, the logs only go to the debug log, not the Command Log, because that's not
// available yet, either.
result := direnv.Load(app.OSCommand.Cmd)
if result.Message != "" {
app.Log.WithField("message", result.Message).Info("direnv")
}
if result.Err != nil {
app.Log.WithError(result.Err).Warn("direnv load failed")
}
if enterRepo(app, repoDir) {
return true
}
}
Expand All @@ -190,23 +179,62 @@ func openRecentRepo(app *App) bool {
return false
}

// isUsableRepo runs the repo check of NewGitCommand. Discovery only sees that a
// .git exists, and a stale worktree link or an empty .git dir would make
// NewGitCommand fail and abort startup.
func isUsableRepo(app *App, dir string) bool {
repoPaths, err := git_commands.GetRepoPathsForDir(dir, app.OSCommand.Cmd)
return err == nil && !repoPaths.IsBareRepo()
}

func enterRepo(app *App, repoDir string) bool {
if err := os.Chdir(repoDir); err != nil {
return false
}

// We're still in setup, before the gui exists, so we can't show the approval popup
// that DispatchSwitchTo offers for blocked .envrc files; just log and move on.
// Also, the logs only go to the debug log, not the Command Log, because that's not
// available yet, either.
result := direnv.Load(app.OSCommand.Cmd)
if result.Message != "" {
app.Log.WithField("message", result.Message).Info("direnv")
}
if result.Err != nil {
app.Log.WithError(result.Err).Warn("direnv load failed")
}
return true
}

func (app *App) setupRepo(
repoPaths *git_commands.RepoPaths,
) (bool, error) {
) (bool, string, error) {
if env.GetGitDirEnv() != "" {
// we've been given the git dir directly. Skip setup
return false, nil
return false, "", nil
}

// if we are not in a git repo, we ask if we want to `git init`
if repoPaths == nil {
cwd, err := os.Getwd()
if err != nil {
return false, err
return false, "", err
}

if isRepo, err := isDirectoryAGitRepository(cwd); isRepo {
return false, err
return false, "", err
}

// git reports the current repo's path with symlinks resolved, and the
// repos list compares against it.
root := cwd
if resolved, err := filepath.EvalSymlinks(cwd); err == nil {
root = resolved
}
for _, repo := range git_commands.DiscoverRepos(root, app.UserConfig().MultiRepo.MaxDepth) {
if isUsableRepo(app, repo) && enterRepo(app, repo) {
return false, root, nil
}
}

var shouldInitRepo bool
Expand Down Expand Up @@ -243,15 +271,15 @@ func (app *App) setupRepo(
args = append(args, initialBranchArg)
}
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
return false, err
return false, "", err
}

return false, nil
return false, "", nil
}

// check if we have a recent repo we can open
if openRecentRepo(app) {
return true, nil
return true, "", nil
}

fmt.Fprintln(os.Stderr, app.Tr.NoRecentRepositories)
Expand All @@ -270,14 +298,14 @@ func (app *App) setupRepo(
}

if openRecentRepo(app) {
return true, nil
return true, "", nil
}

fmt.Println(app.Tr.NoRecentRepositories)
os.Exit(1)
}

return false, nil
return false, "", nil
}

func (app *App) Run(startArgs appTypes.StartArgs) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/cheatsheet/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
"localBranches": tr.LocalBranchesTitle,
"files": tr.FilesTitle,
"status": tr.StatusTitle,
"repos": tr.ReposTitle,
"submodules": tr.SubmodulesTitle,
"subCommits": tr.SubCommitsTitle,
"remoteBranches": tr.RemoteBranchesTitle,
Expand Down
41 changes: 41 additions & 0 deletions pkg/commands/git_commands/repo_discovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package git_commands

import (
"os"
"path/filepath"
"slices"
"strings"
)

// DiscoverRepos returns the absolute paths of the git repos below root, down to
// maxDepth directory levels, sorted by path. It does not look inside a repo it
// found, it skips hidden directories, and it does not follow symlinks, so a
// symlink loop can't trap it. It skips directories that it can't read.
func DiscoverRepos(root string, maxDepth int) []string {
var repos []string

var walk func(dir string, depth int)
walk = func(dir string, depth int) {
entries, err := os.ReadDir(dir)
if err != nil {
return
}
for _, entry := range entries {
if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
continue
}
path := filepath.Join(dir, entry.Name())
if _, err := os.Lstat(filepath.Join(path, ".git")); err == nil {
repos = append(repos, path)
continue
}
if depth < maxDepth {
walk(path, depth+1)
}
}
}
walk(root, 1)

slices.Sort(repos)
return repos
}
Loading