Skip to content

Commit

Permalink
Change RepoPaths to be acquired via RepoPathCache
Browse files Browse the repository at this point in the history
In order to optimize the number of git calls made, particularly at
startup time, this change implements a RepoPathCache as the API by which
RepoPaths instances are acquired.
  • Loading branch information
jwhitley committed Jan 30, 2024
1 parent 761c77f commit b70a140
Show file tree
Hide file tree
Showing 28 changed files with 2,025 additions and 83 deletions.
3 changes: 3 additions & 0 deletions go.mod
Expand Up @@ -23,6 +23,7 @@ require (
github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
github.com/kofalt/go-memoize v0.0.0-20220914132407-0b5d6a304579
github.com/kyokomi/emoji/v2 v2.2.8
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-runewidth v0.0.15
Expand Down Expand Up @@ -66,6 +67,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/onsi/ginkgo v1.10.3 // indirect
github.com/onsi/gomega v1.7.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.6 // indirect
Expand All @@ -74,6 +76,7 @@ require (
github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Expand Up @@ -209,6 +209,8 @@ github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3/go.mod h1:
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kofalt/go-memoize v0.0.0-20220914132407-0b5d6a304579 h1:RbY+urZu3ri7Medi8pY3ovt1+XQxxv7zSkgmEZ5E0CU=
github.com/kofalt/go-memoize v0.0.0-20220914132407-0b5d6a304579/go.mod h1:PifxINf6wYU0USPBk0z1Z8Pka1AqeyCJAp9ecCcNL5Q=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down Expand Up @@ -252,6 +254,8 @@ github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -422,6 +426,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down
32 changes: 22 additions & 10 deletions pkg/app/app.go
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/spf13/afero"

appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
Expand Down Expand Up @@ -119,7 +118,13 @@ func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest,
return app, err
}

showRecentRepos, err := app.setupRepo()
repoPathCache := git_commands.NewRepoPathCache(app.OSCommand.Cmd, gitVersion)

// If we're not in a repo, repoPaths will be nil. The error is moot for us
// at this stage, since we'll try to init a new repo in setupRepo(), below
repoPaths, _ := repoPathCache.GetRepoPaths()

showRecentRepos, err := app.setupRepo(&repoPathCache, repoPaths)
if err != nil {
return app, err
}
Expand All @@ -129,7 +134,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, &repoPathCache, updater, showRecentRepos, dirName, test)
if err != nil {
return app, err
}
Expand Down Expand Up @@ -168,14 +173,17 @@ func openRecentRepo(app *App) bool {
return false
}

func (app *App) setupRepo() (bool, error) {
func (app *App) setupRepo(
repoPathCache *git_commands.RepoPathCache,
repoPaths *git_commands.RepoPaths,
) (bool, error) {
if env.GetGitDirEnv() != "" {
// we've been given the git dir directly. We'll verify this dir when initializing our Git object
// we've been given the git dir directly. Skip setup
return false, nil
}

// if we are not in a git repo, we ask if we want to `git init`
if err := commands.VerifyInGitRepo(app.OSCommand); err != nil {
if repoPaths == nil {
cwd, err := os.Getwd()
if err != nil {
return false, err
Expand Down Expand Up @@ -221,6 +229,13 @@ func (app *App) setupRepo() (bool, error) {
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
return false, err
}

// Add the new repo to repoPathCache
_, err := repoPathCache.GetRepoPaths()
if err != nil {
return false, err
}

return false, nil
}

Expand All @@ -238,10 +253,7 @@ func (app *App) setupRepo() (bool, error) {
}

// Run this afterward so that the previous repo creation steps can run without this interfering
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
if err != nil {
return false, err
}
if repoPaths.IsBareRepo() {

fmt.Print(app.Tr.BareRepo)

Expand Down
12 changes: 6 additions & 6 deletions pkg/commands/git.go
Expand Up @@ -56,11 +56,11 @@ type Loaders struct {

func NewGitCommand(
cmn *common.Common,
version *git_commands.GitVersion,
repoPathCache *git_commands.RepoPathCache,
osCommand *oscommands.OSCommand,
gitConfig git_config.IGitConfig,
) (*GitCommand, error) {
repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version)
repoPaths, err := repoPathCache.GetRepoPaths()
if err != nil {
return nil, errors.Errorf("Error getting repo paths: %v", err)
}
Expand All @@ -83,7 +83,7 @@ func NewGitCommand(

return NewGitCommandAux(
cmn,
version,
repoPathCache,
osCommand,
gitConfig,
repoPaths,
Expand All @@ -93,7 +93,7 @@ func NewGitCommand(

func NewGitCommandAux(
cmn *common.Common,
version *git_commands.GitVersion,
repoPathCache *git_commands.RepoPathCache,
osCommand *oscommands.OSCommand,
gitConfig git_config.IGitConfig,
repoPaths *git_commands.RepoPaths,
Expand All @@ -108,7 +108,7 @@ func NewGitCommandAux(
// common ones are: cmn, osCommand, dotGitDir, configCommands
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)

gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands)
gitCommon := git_commands.NewGitCommon(cmn, cmd, osCommand, repoPathCache, repoPaths, repo, configCommands)

fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
statusCommands := git_commands.NewStatusCommands(gitCommon)
Expand Down Expand Up @@ -163,7 +163,7 @@ func NewGitCommandAux(
Bisect: bisectCommands,
WorkingTree: workingTreeCommands,
Worktree: worktreeCommands,
Version: version,
Version: repoPathCache.GetGitVersion(),
Loaders: Loaders{
BranchLoader: branchLoader,
CommitFileLoader: commitFileLoader,
Expand Down
30 changes: 16 additions & 14 deletions pkg/commands/git_commands/common.go
Expand Up @@ -8,30 +8,32 @@ import (

type GitCommon struct {
*common.Common
version *GitVersion
cmd oscommands.ICmdObjBuilder
os *oscommands.OSCommand
repoPaths *RepoPaths
repo *gogit.Repository
config *ConfigCommands
version *GitVersion
cmd oscommands.ICmdObjBuilder
os *oscommands.OSCommand
repoPathCache *RepoPathCache
repoPaths *RepoPaths
repo *gogit.Repository
config *ConfigCommands
}

func NewGitCommon(
cmn *common.Common,
version *GitVersion,
cmd oscommands.ICmdObjBuilder,
osCommand *oscommands.OSCommand,
repoPathCache *RepoPathCache,
repoPaths *RepoPaths,
repo *gogit.Repository,
config *ConfigCommands,
) *GitCommon {
return &GitCommon{
Common: cmn,
version: version,
cmd: cmd,
os: osCommand,
repoPaths: repoPaths,
repo: repo,
config: config,
Common: cmn,
version: repoPathCache.GetGitVersion(),
cmd: cmd,
os: osCommand,
repoPathCache: repoPathCache,
repoPaths: repoPaths,
repo: repo,
config: config,
}
}
3 changes: 3 additions & 0 deletions pkg/commands/git_commands/deps_test.go
Expand Up @@ -68,6 +68,9 @@ func buildGitCommon(deps commonDeps) *GitCommon {
gitCommon.version = &GitVersion{2, 0, 0, ""}
}

repoPathCache := NewRepoPathCache(cmd, gitCommon.version)
gitCommon.repoPathCache = &repoPathCache

gitConfig := deps.gitConfig
if gitConfig == nil {
gitConfig = git_config.NewFakeGitConfig(nil)
Expand Down
48 changes: 48 additions & 0 deletions pkg/commands/git_commands/repo_path_cache.go
@@ -0,0 +1,48 @@
package git_commands

import (
"os"

"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/kofalt/go-memoize"
"github.com/patrickmn/go-cache"
)

type RepoPathCache struct {
cmd oscommands.ICmdObjBuilder
gitVersion *GitVersion
cache memoize.Memoizer
}

func NewRepoPathCache(cmd oscommands.ICmdObjBuilder, gitVersion *GitVersion) RepoPathCache {
return RepoPathCache{
cmd: cmd,
gitVersion: gitVersion,
cache: *memoize.NewMemoizer(cache.NoExpiration, cache.NoExpiration),
}
}

func (self *RepoPathCache) GetRepoPathsForDir(dir string) (*RepoPaths, error) {
getter := func() (interface{}, error) {
return getRepoPaths(dir, self.cmd, self.gitVersion)
}

repoPaths, err, _ := self.cache.Memoize(dir, getter)
if err != nil {
return nil, err
}

return repoPaths.(*RepoPaths), nil
}

func (self *RepoPathCache) GetRepoPaths() (*RepoPaths, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
return self.GetRepoPathsForDir(cwd)
}

func (self *RepoPathCache) GetGitVersion() *GitVersion {
return self.gitVersion
}
23 changes: 12 additions & 11 deletions pkg/commands/git_commands/repo_paths.go
Expand Up @@ -18,6 +18,7 @@ type RepoPaths struct {
repoPath string
repoGitDirPath string
repoName string
isBareRepo bool
}

var gitPathFormatVersion GitVersion = GitVersion{2, 31, 0, ""}
Expand Down Expand Up @@ -54,6 +55,10 @@ func (self *RepoPaths) RepoName() string {
return self.repoName
}

func (self *RepoPaths) IsBareRepo() bool {
return self.isBareRepo
}

// Returns the repo paths for a typical repo
func MockRepoPaths(currentPath string) *RepoPaths {
return &RepoPaths{
Expand All @@ -62,14 +67,16 @@ func MockRepoPaths(currentPath string) *RepoPaths {
repoPath: currentPath,
repoGitDirPath: path.Join(currentPath, ".git"),
repoName: "lazygit",
isBareRepo: false,
}
}

func GetRepoPaths(
func getRepoPaths(
dir string,
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
) (*RepoPaths, error) {
gitDirOutput, err := callGitRevParse(cmd, version, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree")
gitDirOutput, err := callGitRevParseWithDir(cmd, version, dir, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree")
if err != nil {
return nil, err
}
Expand All @@ -84,13 +91,14 @@ func GetRepoPaths(
return nil, err
}
}
isBareRepo := gitDirResults[3] == "true"

// If we're in a submodule, --show-superproject-working-tree will return
// a value, meaning gitDirResults will be length 4. In that case
// return the worktree path as the repoPath. Otherwise we're in a
// normal repo or a worktree so return the parent of the git common
// dir (repoGitDirPath)
isSubmodule := len(gitDirResults) == 4
isSubmodule := len(gitDirResults) == 5

var repoPath string
if isSubmodule {
Expand All @@ -106,17 +114,10 @@ func GetRepoPaths(
repoPath: repoPath,
repoGitDirPath: repoGitDirPath,
repoName: repoName,
isBareRepo: isBareRepo,
}, nil
}

func callGitRevParse(
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
gitRevArgs ...string,
) (string, error) {
return callGitRevParseWithDir(cmd, version, "", gitRevArgs...)
}

func callGitRevParseWithDir(
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
Expand Down

0 comments on commit b70a140

Please sign in to comment.