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 49df908 commit 18f6fa9
Show file tree
Hide file tree
Showing 28 changed files with 2,088 additions and 88 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ 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
github.com/mgutz/str v1.2.0
github.com/mitchellh/go-ps v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/sahilm/fuzzy v0.1.0
github.com/samber/lo v1.31.0
github.com/sanity-io/litter v1.5.2
Expand Down Expand Up @@ -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
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
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 @@ -279,6 +283,8 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/gunit v1.4.2 h1:tyWYZffdPhQPfK5VsMQXfauwnJkqg7Tv5DLuQVYxq3Q=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
Expand Down Expand Up @@ -422,6 +428,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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 18f6fa9

Please sign in to comment.