From d58cfaa1a3a3bbaec47c4471f6fc1d8f00c449de Mon Sep 17 00:00:00 2001 From: stong <498327580@qq.com> Date: Tue, 23 Apr 2024 01:36:48 +0800 Subject: [PATCH] Optimize: Reduce full repo scans when look tag is set fix https://github.com/x-motemen/ghq/issues/379 --- cmd_get.go | 43 ++++++++++++++++++++++++++++--------------- getter.go | 50 +++++++++++++++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/cmd_get.go b/cmd_get.go index 82e17d0..5c7c3e6 100644 --- a/cmd_get.go +++ b/cmd_get.go @@ -40,8 +40,11 @@ func doGet(c *cli.Context) error { } var ( - firstArg string + firstArg string // Look at the first repo only, if there are more than one + argCnt int + getInfo getInfo // For fetching and looking a single repo scr scanner + err error ) if len(args) > 0 { scr = &sliceScanner{slice: args} @@ -52,6 +55,7 @@ func doGet(c *cli.Context) error { } scr = bufio.NewScanner(os.Stdin) } + eg := &errgroup.Group{} sem := make(chan struct{}, 6) for scr.Scan() { @@ -59,29 +63,35 @@ func doGet(c *cli.Context) error { if firstArg == "" { firstArg = target } + argCnt += 1 if parallel { sem <- struct{}{} eg.Go(func() error { defer func() { <-sem }() - if err := g.get(target); err != nil { + if getInfo, err = g.get(target); err != nil { logger.Logf("error", "failed to get %q: %s", target, err) } return nil }) } else { - if err := g.get(target); err != nil { + if getInfo, err = g.get(target); err != nil { return fmt.Errorf("failed to get %q: %w", target, err) } } } - if err := scr.Err(); err != nil { + if err = scr.Err(); err != nil { return fmt.Errorf("error occurred while reading input: %w", err) } - if err := eg.Wait(); err != nil { + if err = eg.Wait(); err != nil { return err } - if andLook && firstArg != "" { - return look(firstArg, g.bare) + if andLook { + if argCnt > 1 && firstArg != "" { + return look(firstArg, g.bare) + } + if argCnt == 1 && getInfo.localRepository != nil { + return lookByLocalRepository(getInfo.localRepository) + } } return nil } @@ -155,14 +165,7 @@ func look(name string, bare bool) error { case 0: return fmt.Errorf("no repository found") case 1: - repo := reposFound[0] - cmd := exec.Command(detectShell()) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Dir = repo.FullPath - cmd.Env = append(os.Environ(), "GHQ_LOOK="+filepath.ToSlash(repo.RelPath)) - return cmdutil.RunCommand(cmd, true) + return lookByLocalRepository(reposFound[0]) default: b := &strings.Builder{} b.WriteString("More than one repositories are found; Try more precise name\n") @@ -172,3 +175,13 @@ func look(name string, bare bool) error { return errors.New(b.String()) } } + +func lookByLocalRepository(repo *LocalRepository) error { + cmd := exec.Command(detectShell()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Dir = repo.FullPath + cmd.Env = append(os.Environ(), "GHQ_LOOK="+filepath.ToSlash(repo.RelPath)) + return cmdutil.RunCommand(cmd, true) +} diff --git a/getter.go b/getter.go index b04a429..2777fec 100644 --- a/getter.go +++ b/getter.go @@ -19,15 +19,19 @@ func getRepoLock(localRepoRoot string) bool { return !loaded } +type getInfo struct { + localRepository *LocalRepository +} + type getter struct { update, shallow, silent, ssh, recursive, bare bool vcs, branch string } -func (g *getter) get(argURL string) error { +func (g *getter) get(argURL string) (getInfo, error) { u, err := newURL(argURL, g.ssh, false) if err != nil { - return fmt.Errorf("could not parse URL %q: %w", argURL, err) + return getInfo{}, fmt.Errorf("could not parse URL %q: %w", argURL, err) } branch := g.branch if pos := strings.LastIndexByte(u.Path, '@'); pos >= 0 { @@ -35,7 +39,7 @@ func (g *getter) get(argURL string) error { } remote, err := NewRemoteRepository(u) if err != nil { - return err + return getInfo{}, err } return g.getRemoteRepository(remote, branch) @@ -44,11 +48,14 @@ func (g *getter) get(argURL string) error { // getRemoteRepository clones or updates a remote repository remote. // If doUpdate is true, updates the locally cloned repository. Otherwise does nothing. // If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn) -func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) error { +func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) (getInfo, error) { remoteURL := remote.URL() local, err := LocalRepositoryFromURL(remoteURL, g.bare) if err != nil { - return err + return getInfo{}, err + } + info := getInfo{ + localRepository: local, } var ( @@ -63,7 +70,7 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) err err = nil } if err != nil { - return err + return getInfo{}, err } } @@ -82,7 +89,7 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) err if !ok { vcs, repoURL, err = remote.VCS() if err != nil { - return err + return getInfo{}, err } } if l := detectLocalRepoRoot(remoteURL.Path, repoURL.Path); l != "" { @@ -97,29 +104,30 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) err repoURL, _ = url.Parse(remoteURL.Opaque) } if getRepoLock(localRepoRoot) { - return vcs.Clone(&vcsGetOption{ - url: repoURL, - dir: localRepoRoot, - shallow: g.shallow, - silent: g.silent, - branch: branch, - recursive: g.recursive, - bare: g.bare, - }) + return info, + vcs.Clone(&vcsGetOption{ + url: repoURL, + dir: localRepoRoot, + shallow: g.shallow, + silent: g.silent, + branch: branch, + recursive: g.recursive, + bare: g.bare, + }) } - return nil + return info, nil case g.update: logger.Log("update", fpath) vcs, localRepoRoot := local.VCS() if vcs == nil { - return fmt.Errorf("failed to detect VCS for %q", fpath) + return getInfo{}, fmt.Errorf("failed to detect VCS for %q", fpath) } repoURL := remoteURL if remoteURL.Scheme == "codecommit" { repoURL, _ = url.Parse(remoteURL.Opaque) } if getRepoLock(localRepoRoot) { - return vcs.Update(&vcsGetOption{ + return info, vcs.Update(&vcsGetOption{ url: repoURL, dir: localRepoRoot, silent: g.silent, @@ -127,10 +135,10 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) err bare: g.bare, }) } - return nil + return info, nil } logger.Log("exists", fpath) - return nil + return info, nil } func detectLocalRepoRoot(remotePath, repoPath string) string {