Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gh for openSUSE:Factory checked in 
at 2022-11-29 10:54:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gh (Old)
 and      /work/SRC/openSUSE:Factory/.gh.new.1597 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gh"

Tue Nov 29 10:54:09 2022 rev:24 rq:1038789 version:2.20.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/gh/gh.changes    2022-11-12 17:41:40.790350405 
+0100
+++ /work/SRC/openSUSE:Factory/.gh.new.1597/gh.changes  2022-11-29 
10:54:54.153287260 +0100
@@ -1,0 +2,10 @@
+Tue Nov 29 05:02:04 UTC 2022 - Emily Roberts <nopeinomi...@posteo.net>
+
+- Update to 2.20.2
+  https://github.com/cli/cli/releases
+  * Update security policy by @maclarel in #6587
+  * Fix listing repositories for a named GitHub user by @mislav in #6608
+  * Fix commands not properly refreshing the terminal screen by @samcoe in 
#6607
+
+
+-------------------------------------------------------------------

Old:
----
  cli-2.20.0.tar.gz

New:
----
  cli-2.20.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gh.spec ++++++
--- /var/tmp/diff_new_pack.1wFfit/_old  2022-11-29 10:54:55.973296821 +0100
+++ /var/tmp/diff_new_pack.1wFfit/_new  2022-11-29 10:54:55.977296842 +0100
@@ -19,7 +19,7 @@
 %define goflags "-buildmode=pie -trimpath -mod=vendor -modcacherw"
 %define sname cli
 Name:           gh
-Version:        2.20.0
+Version:        2.20.2
 Release:        0
 Summary:        The official CLI for GitHub
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.1wFfit/_old  2022-11-29 10:54:56.013297031 +0100
+++ /var/tmp/diff_new_pack.1wFfit/_new  2022-11-29 10:54:56.017297052 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/cli/cli</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v2.20.0</param>
+    <param name="revision">v2.20.2</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">disable</param>
     <param name="versionrewrite-pattern">v(.*)</param>
@@ -16,7 +16,7 @@
     <param name="compression">gz</param>
   </service>
   <service name="go_modules" mode="disabled">
-    <param name="archive">cli-2.20.0.tar.gz</param>
+    <param name="archive">cli-2.20.2.tar.gz</param>
   </service>
 </services>
 

++++++ cli-2.20.0.tar.gz -> cli-2.20.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/.github/SECURITY.md 
new/cli-2.20.2/.github/SECURITY.md
--- old/cli-2.20.0/.github/SECURITY.md  2022-11-08 21:54:07.000000000 +0100
+++ new/cli-2.20.2/.github/SECURITY.md  2022-11-15 10:40:30.000000000 +0100
@@ -1,3 +1,14 @@
-If you discover a security issue in this repository, please submit it through 
the [GitHub Security Bug Bounty](https://hackerone.com/github).
+GitHub takes the security of our software products and services seriously, 
including the open source code repositories managed through our GitHub 
organizations, such as [cli](https://github.com/cli).
+
+If you believe you have found a security vulnerability in GitHub CLI, you can 
report it to us in one of two ways:
+
+* Report it to this repository directly using [private vulnerability 
reporting][]. Such reports are not eligible for a bounty reward.
+
+* Submit the report through [HackerOne][] to be eligible for a bounty reward.
+
+**Please do not report security vulnerabilities through public GitHub issues, 
discussions, or pull requests.**
 
 Thanks for helping make GitHub safe for everyone.
+
+  [private vulnerability reporting]: 
https://github.com/cli/cli/security/advisories
+  [HackerOne]: https://hackerone.com/github
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/.github/workflows/releases.yml 
new/cli-2.20.2/.github/workflows/releases.yml
--- old/cli-2.20.0/.github/workflows/releases.yml       2022-11-08 
21:54:07.000000000 +0100
+++ new/cli-2.20.2/.github/workflows/releases.yml       2022-11-15 
10:40:30.000000000 +0100
@@ -15,6 +15,8 @@
     steps:
       - name: Checkout
         uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
       - name: Set up Go 1.18
         uses: actions/setup-go@v3
         with:
@@ -41,7 +43,7 @@
       - name: Run GoReleaser
         uses: goreleaser/goreleaser-action@v3
         with:
-          version: v0.174.1
+          version: v1.12.3
           args: release --release-notes=CHANGELOG.md
         env:
           GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/.goreleaser.yml 
new/cli-2.20.2/.goreleaser.yml
--- old/cli-2.20.0/.goreleaser.yml      2022-11-08 21:54:07.000000000 +0100
+++ new/cli-2.20.2/.goreleaser.yml      2022-11-15 10:40:30.000000000 +0100
@@ -60,7 +60,7 @@
   - license: MIT
     maintainer: GitHub
     homepage: https://github.com/cli/cli
-    bindir: /usr/bin
+    bindir: /usr
     dependencies:
       - git
     description: GitHub’s official command line tool.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/git/client.go new/cli-2.20.2/git/client.go
--- old/cli-2.20.0/git/client.go        2022-11-08 21:54:07.000000000 +0100
+++ new/cli-2.20.2/git/client.go        2022-11-15 10:40:30.000000000 +0100
@@ -32,7 +32,7 @@
        mu             sync.Mutex
 }
 
-func (c *Client) Command(ctx context.Context, args ...string) (*gitCommand, 
error) {
+func (c *Client) Command(ctx context.Context, args ...string) (*Command, 
error) {
        if c.RepoDir != "" {
                args = append([]string{"-C", c.RepoDir}, args...)
        }
@@ -53,12 +53,12 @@
        cmd.Stderr = c.Stderr
        cmd.Stdin = c.Stdin
        cmd.Stdout = c.Stdout
-       return &gitCommand{cmd}, nil
+       return &Command{cmd}, nil
 }
 
 // AuthenticatedCommand is a wrapper around Command that included 
configuration to use gh
 // as the credential helper for git.
-func (c *Client) AuthenticatedCommand(ctx context.Context, args ...string) 
(*gitCommand, error) {
+func (c *Client) AuthenticatedCommand(ctx context.Context, args ...string) 
(*Command, error) {
        preArgs := []string{"-c", "credential.helper="}
        if c.GhPath == "" {
                // Assumes that gh is in PATH.
@@ -414,7 +414,10 @@
 }
 
 func (c *Client) Pull(ctx context.Context, remote, branch string, mods 
...CommandModifier) error {
-       args := []string{"pull", "--ff-only", remote, branch}
+       args := []string{"pull", "--ff-only"}
+       if remote != "" && branch != "" {
+               args = append(args, remote, branch)
+       }
        // TODO: Use AuthenticatedCommand
        cmd, err := c.Command(ctx, args...)
        if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/git/command.go 
new/cli-2.20.2/git/command.go
--- old/cli-2.20.0/git/command.go       2022-11-08 21:54:07.000000000 +0100
+++ new/cli-2.20.2/git/command.go       2022-11-15 10:40:30.000000000 +0100
@@ -12,11 +12,11 @@
 
 type commandCtx = func(ctx context.Context, name string, args ...string) 
*exec.Cmd
 
-type gitCommand struct {
+type Command struct {
        *exec.Cmd
 }
 
-func (gc *gitCommand) Run() error {
+func (gc *Command) Run() error {
        stderr := &bytes.Buffer{}
        if gc.Cmd.Stderr == nil {
                gc.Cmd.Stderr = stderr
@@ -35,7 +35,7 @@
        return nil
 }
 
-func (gc *gitCommand) Output() ([]byte, error) {
+func (gc *Command) Output() ([]byte, error) {
        gc.Stdout = nil
        gc.Stderr = nil
        // This is a hack in order to not break the hundreds of
@@ -53,7 +53,7 @@
        return out, err
 }
 
-func (gc *gitCommand) setRepoDir(repoDir string) {
+func (gc *Command) setRepoDir(repoDir string) {
        for i, arg := range gc.Args {
                if arg == "-C" {
                        gc.Args[i+1] = repoDir
@@ -73,28 +73,28 @@
 }
 
 // Allow individual commands to be modified from the default client options.
-type CommandModifier func(*gitCommand)
+type CommandModifier func(*Command)
 
 func WithStderr(stderr io.Writer) CommandModifier {
-       return func(gc *gitCommand) {
+       return func(gc *Command) {
                gc.Stderr = stderr
        }
 }
 
 func WithStdout(stdout io.Writer) CommandModifier {
-       return func(gc *gitCommand) {
+       return func(gc *Command) {
                gc.Stdout = stdout
        }
 }
 
 func WithStdin(stdin io.Reader) CommandModifier {
-       return func(gc *gitCommand) {
+       return func(gc *Command) {
                gc.Stdin = stdin
        }
 }
 
 func WithRepoDir(repoDir string) CommandModifier {
-       return func(gc *gitCommand) {
+       return func(gc *Command) {
                gc.setRepoDir(repoDir)
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/cmd/extension/git.go 
new/cli-2.20.2/pkg/cmd/extension/git.go
--- old/cli-2.20.0/pkg/cmd/extension/git.go     1970-01-01 01:00:00.000000000 
+0100
+++ new/cli-2.20.2/pkg/cmd/extension/git.go     2022-11-15 10:40:30.000000000 
+0100
@@ -0,0 +1,67 @@
+package extension
+
+import (
+       "context"
+
+       "github.com/cli/cli/v2/git"
+)
+
+type gitClient interface {
+       CheckoutBranch(branch string) error
+       Clone(cloneURL string, args []string) (string, error)
+       CommandOutput(args []string) ([]byte, error)
+       Config(name string) (string, error)
+       Fetch(remote string, refspec string) error
+       ForRepo(repoDir string) gitClient
+       Pull(remote, branch string) error
+       Remotes() (git.RemoteSet, error)
+}
+
+type gitExecuter struct {
+       client *git.Client
+}
+
+func (g *gitExecuter) CheckoutBranch(branch string) error {
+       return g.client.CheckoutBranch(context.Background(), branch)
+}
+
+func (g *gitExecuter) Clone(cloneURL string, cloneArgs []string) (string, 
error) {
+       return g.client.Clone(context.Background(), cloneURL, cloneArgs)
+}
+
+func (g *gitExecuter) CommandOutput(args []string) ([]byte, error) {
+       cmd, err := g.client.Command(context.Background(), args...)
+       if err != nil {
+               return nil, err
+       }
+       return cmd.Output()
+}
+
+func (g *gitExecuter) Config(name string) (string, error) {
+       return g.client.Config(context.Background(), name)
+}
+
+func (g *gitExecuter) Fetch(remote string, refspec string) error {
+       return g.client.Fetch(context.Background(), remote, refspec)
+}
+
+func (g *gitExecuter) ForRepo(repoDir string) gitClient {
+       return &gitExecuter{
+               client: &git.Client{
+                       GhPath:  g.client.GhPath,
+                       RepoDir: repoDir,
+                       GitPath: g.client.GitPath,
+                       Stderr:  g.client.Stderr,
+                       Stdin:   g.client.Stdin,
+                       Stdout:  g.client.Stdout,
+               },
+       }
+}
+
+func (g *gitExecuter) Pull(remote, branch string) error {
+       return g.client.Pull(context.Background(), remote, branch)
+}
+
+func (g *gitExecuter) Remotes() (git.RemoteSet, error) {
+       return g.client.Remotes(context.Background())
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/cmd/extension/manager.go 
new/cli-2.20.2/pkg/cmd/extension/manager.go
--- old/cli-2.20.0/pkg/cmd/extension/manager.go 2022-11-08 21:54:07.000000000 
+0100
+++ new/cli-2.20.2/pkg/cmd/extension/manager.go 2022-11-15 10:40:30.000000000 
+0100
@@ -2,7 +2,6 @@
 
 import (
        "bytes"
-       "context"
        _ "embed"
        "errors"
        "fmt"
@@ -35,12 +34,13 @@
        newCommand func(string, ...string) *exec.Cmd
        platform   func() (string, string)
        client     *http.Client
+       gitClient  gitClient
        config     config.Config
        io         *iostreams.IOStreams
        dryRunMode bool
 }
 
-func NewManager(ios *iostreams.IOStreams) *Manager {
+func NewManager(ios *iostreams.IOStreams, gc *git.Client) *Manager {
        return &Manager{
                dataDir:    config.DataDir,
                lookPath:   safeexec.LookPath,
@@ -53,7 +53,8 @@
                        }
                        return fmt.Sprintf("%s-%s", runtime.GOOS, 
runtime.GOARCH), ext
                },
-               io: ios,
+               io:        ios,
+               gitClient: &gitExecuter{client: gc},
        }
 }
 
@@ -231,15 +232,9 @@
 
 // getCurrentVersion determines the current version for non-local git 
extensions.
 func (m *Manager) getCurrentVersion(extension string) string {
-       gitExe, err := m.lookPath("git")
-       if err != nil {
-               return ""
-       }
-       dir := m.installDir()
-       gitDir := "--git-dir=" + filepath.Join(dir, extension, ".git")
-       cmd := m.newCommand(gitExe, gitDir, "rev-parse", "HEAD")
-
-       localSha, err := cmd.Output()
+       dir := filepath.Join(m.installDir(), extension)
+       scopedClient := m.gitClient.ForRepo(dir)
+       localSha, err := scopedClient.CommandOutput([]string{"rev-parse", 
"HEAD"})
        if err != nil {
                return ""
        }
@@ -248,14 +243,9 @@
 
 // getRemoteUrl determines the remote URL for non-local git extensions.
 func (m *Manager) getRemoteUrl(extension string) string {
-       gitExe, err := m.lookPath("git")
-       if err != nil {
-               return ""
-       }
-       dir := m.installDir()
-       gitDir := "--git-dir=" + filepath.Join(dir, extension, ".git")
-       cmd := m.newCommand(gitExe, gitDir, "config", "remote.origin.url")
-       url, err := cmd.Output()
+       dir := filepath.Join(m.installDir(), extension)
+       scopedClient := m.gitClient.ForRepo(dir)
+       url, err := scopedClient.Config("remote.origin.url")
        if err != nil {
                return ""
        }
@@ -301,14 +291,9 @@
                }
                return r.Tag, nil
        } else {
-               gitExe, err := m.lookPath("git")
-               if err != nil {
-                       return "", err
-               }
                extDir := filepath.Dir(ext.path)
-               gitDir := "--git-dir=" + filepath.Join(extDir, ".git")
-               cmd := m.newCommand(gitExe, gitDir, "ls-remote", "origin", 
"HEAD")
-               lsRemote, err := cmd.Output()
+               scopedClient := m.gitClient.ForRepo(extDir)
+               lsRemote, err := 
scopedClient.CommandOutput([]string{"ls-remote", "origin", "HEAD"})
                if err != nil {
                        return "", err
                }
@@ -469,13 +454,9 @@
        protocol, _ := m.config.GetOrDefault(repo.RepoHost(), "git_protocol")
        cloneURL := ghrepo.FormatRemoteURL(repo, protocol)
 
-       exe, err := m.lookPath("git")
-       if err != nil {
-               return err
-       }
-
        var commitSHA string
        if target != "" {
+               var err error
                commitSHA, err = fetchCommitSHA(m.client, repo, target)
                if err != nil {
                        return err
@@ -485,20 +466,17 @@
        name := strings.TrimSuffix(path.Base(cloneURL), ".git")
        targetDir := filepath.Join(m.installDir(), name)
 
-       externalCmd := m.newCommand(exe, "clone", cloneURL, targetDir)
-       externalCmd.Stdout = stdout
-       externalCmd.Stderr = stderr
-       if err := externalCmd.Run(); err != nil {
+       _, err := m.gitClient.Clone(cloneURL, []string{targetDir})
+       if err != nil {
                return err
        }
        if commitSHA == "" {
                return nil
        }
 
-       checkoutCmd := m.newCommand(exe, "-C", targetDir, "checkout", commitSHA)
-       checkoutCmd.Stdout = stdout
-       checkoutCmd.Stderr = stderr
-       if err := checkoutCmd.Run(); err != nil {
+       scopedClient := m.gitClient.ForRepo(targetDir)
+       err = scopedClient.CheckoutBranch(commitSHA)
+       if err != nil {
                return err
        }
 
@@ -587,7 +565,7 @@
        } else {
                // Check if git extension has changed to a binary extension
                var isBin bool
-               repo, repoErr := repoFromPath(filepath.Join(ext.Path(), ".."))
+               repo, repoErr := repoFromPath(m.gitClient, 
filepath.Join(ext.Path(), ".."))
                if repoErr == nil {
                        isBin, _ = isBinExtension(m.client, repo)
                }
@@ -603,21 +581,22 @@
 }
 
 func (m *Manager) upgradeGitExtension(ext Extension, force bool) error {
-       exe, err := m.lookPath("git")
-       if err != nil {
-               return err
-       }
-       dir := filepath.Dir(ext.path)
        if m.dryRunMode {
                return nil
        }
+       dir := filepath.Dir(ext.path)
+       scopedClient := m.gitClient.ForRepo(dir)
        if force {
-               if err := m.newCommand(exe, "-C", dir, "fetch", "origin", 
"HEAD").Run(); err != nil {
+               err := scopedClient.Fetch("origin", "HEAD")
+               if err != nil {
                        return err
                }
-               return m.newCommand(exe, "-C", dir, "reset", "--hard", 
"origin/HEAD").Run()
+
+               _, err = scopedClient.CommandOutput([]string{"reset", "--hard", 
"origin/HEAD"})
+               return err
        }
-       return m.newCommand(exe, "-C", dir, "pull", "--ff-only").Run()
+
+       return scopedClient.Pull("", "")
 }
 
 func (m *Manager) upgradeBinExtension(ext Extension) error {
@@ -659,19 +638,14 @@
 var buildScript []byte
 
 func (m *Manager) Create(name string, tmplType extensions.ExtTemplateType) 
error {
-       exe, err := m.lookPath("git")
-       if err != nil {
-               return err
-       }
-
-       if err := m.newCommand(exe, "init", "--quiet", name).Run(); err != nil {
+       if _, err := m.gitClient.CommandOutput([]string{"init", "--quiet", 
name}); err != nil {
                return err
        }
 
        if tmplType == extensions.GoBinTemplateType {
-               return m.goBinScaffolding(exe, name)
+               return m.goBinScaffolding(name)
        } else if tmplType == extensions.OtherBinTemplateType {
-               return m.otherBinScaffolding(exe, name)
+               return m.otherBinScaffolding(name)
        }
 
        script := fmt.Sprintf(scriptTmpl, name)
@@ -679,10 +653,12 @@
                return err
        }
 
-       return m.newCommand(exe, "-C", name, "add", name, "--chmod=+x").Run()
+       scopedClient := m.gitClient.ForRepo(name)
+       _, err := scopedClient.CommandOutput([]string{"add", name, 
"--chmod=+x"})
+       return err
 }
 
-func (m *Manager) otherBinScaffolding(gitExe, name string) error {
+func (m *Manager) otherBinScaffolding(name string) error {
        if err := writeFile(filepath.Join(name, ".github", "workflows", 
"release.yml"), otherBinWorkflow, 0644); err != nil {
                return err
        }
@@ -690,13 +666,17 @@
        if err := writeFile(filepath.Join(name, buildScriptPath), buildScript, 
0755); err != nil {
                return err
        }
-       if err := m.newCommand(gitExe, "-C", name, "add", buildScriptPath, 
"--chmod=+x").Run(); err != nil {
+
+       scopedClient := m.gitClient.ForRepo(name)
+       if _, err := scopedClient.CommandOutput([]string{"add", 
buildScriptPath, "--chmod=+x"}); err != nil {
                return err
        }
-       return m.newCommand(gitExe, "-C", name, "add", ".").Run()
+
+       _, err := scopedClient.CommandOutput([]string{"add", "."})
+       return err
 }
 
-func (m *Manager) goBinScaffolding(gitExe, name string) error {
+func (m *Manager) goBinScaffolding(name string) error {
        goExe, err := m.lookPath("go")
        if err != nil {
                return fmt.Errorf("go is required for creating Go extensions: 
%w", err)
@@ -737,7 +717,9 @@
                }
        }
 
-       return m.newCommand(gitExe, "-C", name, "add", ".").Run()
+       scopedClient := m.gitClient.ForRepo(name)
+       _, err = scopedClient.CommandOutput([]string{"add", "."})
+       return err
 }
 
 func isSymlink(m os.FileMode) bool {
@@ -789,9 +771,9 @@
        return
 }
 
-func repoFromPath(path string) (ghrepo.Interface, error) {
-       gitClient := &git.Client{RepoDir: path}
-       remotes, err := gitClient.Remotes(context.Background())
+func repoFromPath(gitClient gitClient, path string) (ghrepo.Interface, error) {
+       scopedClient := gitClient.ForRepo(path)
+       remotes, err := scopedClient.Remotes()
        if err != nil {
                return nil, err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/cmd/extension/manager_test.go 
new/cli-2.20.2/pkg/cmd/extension/manager_test.go
--- old/cli-2.20.0/pkg/cmd/extension/manager_test.go    2022-11-08 
21:54:07.000000000 +0100
+++ new/cli-2.20.2/pkg/cmd/extension/manager_test.go    2022-11-15 
10:40:30.000000000 +0100
@@ -9,10 +9,10 @@
        "path/filepath"
        "runtime"
        "sort"
-       "strings"
        "testing"
 
        "github.com/MakeNowJust/heredoc"
+       "github.com/cli/cli/v2/git"
        "github.com/cli/cli/v2/internal/config"
        "github.com/cli/cli/v2/internal/ghrepo"
        "github.com/cli/cli/v2/internal/run"
@@ -29,15 +29,6 @@
                return
        }
        if err := func(args []string) error {
-               // git init should create the directory named by argument
-               if len(args) > 2 && strings.HasPrefix(strings.Join(args, " "), 
"git init") {
-                       dir := args[len(args)-1]
-                       if !strings.HasPrefix(dir, "-") {
-                               if err := os.MkdirAll(dir, 0755); err != nil {
-                                       return err
-                               }
-                       }
-               }
                fmt.Fprintf(os.Stdout, "%v\n", args)
                return nil
        }(os.Args[3:]); err != nil {
@@ -47,7 +38,7 @@
        os.Exit(0)
 }
 
-func newTestManager(dir string, client *http.Client, ios *iostreams.IOStreams) 
*Manager {
+func newTestManager(dir string, client *http.Client, gitClient gitClient, ios 
*iostreams.IOStreams) *Manager {
        return &Manager{
                dataDir:  func() string { return dir },
                lookPath: func(exe string) (string, error) { return exe, nil },
@@ -62,9 +53,10 @@
                        cmd.Env = []string{"GH_WANT_HELPER_PROCESS=1"}
                        return cmd
                },
-               config: config.NewBlankConfig(),
-               io:     ios,
-               client: client,
+               config:    config.NewBlankConfig(),
+               io:        ios,
+               client:    client,
+               gitClient: gitClient,
                platform: func() (string, string) {
                        return "windows-amd64", ".exe"
                },
@@ -85,12 +77,26 @@
                        Tag:   "v1.0.1",
                }))
 
-       m := newTestManager(tempDir, nil, nil)
+       dirOne := filepath.Join(tempDir, "extensions", "gh-hello")
+       dirTwo := filepath.Join(tempDir, "extensions", "gh-two")
+       gc, gcOne, gcTwo := &mockGitClient{}, &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", dirOne).Return(gcOne).Twice()
+       gc.On("ForRepo", dirTwo).Return(gcTwo).Twice()
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcTwo.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcTwo.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+
+       m := newTestManager(tempDir, nil, gc, nil)
        exts := m.List()
+
        assert.Equal(t, 3, len(exts))
        assert.Equal(t, "bin-ext", exts[0].Name())
        assert.Equal(t, "hello", exts[1].Name())
        assert.Equal(t, "two", exts[2].Name())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
+       gcTwo.AssertExpectations(t)
 }
 
 func TestManager_list_includeMetadata(t *testing.T) {
@@ -122,7 +128,7 @@
                                },
                        }))
 
-       m := newTestManager(tempDir, &client, nil)
+       m := newTestManager(tempDir, &client, nil, nil)
 
        exts, err := m.list(true)
        assert.NoError(t, err)
@@ -134,10 +140,16 @@
 
 func TestManager_Dispatch(t *testing.T) {
        tempDir := t.TempDir()
+       extDir := filepath.Join(tempDir, "extensions", "gh-hello")
        extPath := filepath.Join(tempDir, "extensions", "gh-hello", "gh-hello")
        assert.NoError(t, stubExtension(extPath))
 
-       m := newTestManager(tempDir, nil, nil)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extDir).Return(gcOne).Twice()
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+
+       m := newTestManager(tempDir, nil, gc, nil)
 
        stdout := &bytes.Buffer{}
        stderr := &bytes.Buffer{}
@@ -151,6 +163,9 @@
                assert.Equal(t, fmt.Sprintf("[%s one two]\n", extPath), 
stdout.String())
        }
        assert.Equal(t, "", stderr.String())
+
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_Dispatch_binary(t *testing.T) {
@@ -165,7 +180,7 @@
        }
        assert.NoError(t, stubBinaryExtension(extPath, bm))
 
-       m := newTestManager(tempDir, nil, nil)
+       m := newTestManager(tempDir, nil, nil, nil)
 
        stdout := &bytes.Buffer{}
        stderr := &bytes.Buffer{}
@@ -182,7 +197,7 @@
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-hello", "gh-hello")))
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-two", "gh-two")))
 
-       m := newTestManager(tempDir, nil, nil)
+       m := newTestManager(tempDir, nil, nil, nil)
        err := m.Remove("hello")
        assert.NoError(t, err)
 
@@ -195,7 +210,7 @@
 func TestManager_Upgrade_NoExtensions(t *testing.T) {
        tempDir := t.TempDir()
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       m := newTestManager(tempDir, nil, nil, ios)
        err := m.Upgrade("", false)
        assert.EqualError(t, err, "no extensions installed")
        assert.Equal(t, "", stdout.String())
@@ -204,22 +219,42 @@
 
 func TestManager_Upgrade_NoMatchingExtension(t *testing.T) {
        tempDir := t.TempDir()
+       extDir := filepath.Join(tempDir, "extensions", "gh-hello")
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-hello", "gh-hello")))
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extDir).Return(gcOne).Twice()
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       m := newTestManager(tempDir, nil, gc, ios)
        err := m.Upgrade("invalid", false)
        assert.EqualError(t, err, `no extension matched "invalid"`)
        assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_UpgradeExtensions(t *testing.T) {
        tempDir := t.TempDir()
+       dirOne := filepath.Join(tempDir, "extensions", "gh-hello")
+       dirTwo := filepath.Join(tempDir, "extensions", "gh-two")
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-hello", "gh-hello")))
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-two", "gh-two")))
        assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, 
"extensions", "gh-local", "gh-local")))
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       gc, gcOne, gcTwo := &mockGitClient{}, &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", dirOne).Return(gcOne).Times(4)
+       gc.On("ForRepo", dirTwo).Return(gcTwo).Times(4)
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcTwo.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcTwo.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcOne.On("Remotes").Return(nil, nil).Once()
+       gcTwo.On("Remotes").Return(nil, nil).Once()
+       gcOne.On("Pull", "", "").Return(nil).Once()
+       gcTwo.On("Pull", "", "").Return(nil).Once()
+       m := newTestManager(tempDir, nil, gc, ios)
        exts, err := m.list(false)
        assert.NoError(t, err)
        assert.Equal(t, 3, len(exts))
@@ -229,27 +264,37 @@
        }
        err = m.upgradeExtensions(exts, false)
        assert.NoError(t, err)
-       assert.Equal(t, heredoc.Docf(
+       assert.Equal(t, heredoc.Doc(
                `
-               [hello]: [git -C %s pull --ff-only]
-               upgraded from old vers to new vers
+               [hello]: upgraded from old vers to new vers
                [local]: local extensions can not be upgraded
-               [two]: [git -C %s pull --ff-only]
-               upgraded from old vers to new vers
+               [two]: upgraded from old vers to new vers
                `,
-               filepath.Join(tempDir, "extensions", "gh-hello"),
-               filepath.Join(tempDir, "extensions", "gh-two"),
        ), stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
+       gcTwo.AssertExpectations(t)
 }
 
 func TestManager_UpgradeExtensions_DryRun(t *testing.T) {
        tempDir := t.TempDir()
+       dirOne := filepath.Join(tempDir, "extensions", "gh-hello")
+       dirTwo := filepath.Join(tempDir, "extensions", "gh-two")
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-hello", "gh-hello")))
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-two", "gh-two")))
        assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, 
"extensions", "gh-local", "gh-local")))
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       gc, gcOne, gcTwo := &mockGitClient{}, &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", dirOne).Return(gcOne).Times(3)
+       gc.On("ForRepo", dirTwo).Return(gcTwo).Times(3)
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcTwo.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcTwo.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcOne.On("Remotes").Return(nil, nil).Once()
+       gcTwo.On("Remotes").Return(nil, nil).Once()
+       m := newTestManager(tempDir, nil, gc, ios)
        m.EnableDryRunMode()
        exts, err := m.list(false)
        assert.NoError(t, err)
@@ -268,6 +313,9 @@
                `,
        ), stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
+       gcTwo.AssertExpectations(t)
 }
 
 func TestManager_UpgradeExtension_LocalExtension(t *testing.T) {
@@ -275,7 +323,7 @@
        assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, 
"extensions", "gh-local", "gh-local")))
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       m := newTestManager(tempDir, nil, nil, ios)
        exts, err := m.list(false)
        assert.NoError(t, err)
        assert.Equal(t, 1, len(exts))
@@ -290,7 +338,7 @@
        assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, 
"extensions", "gh-local", "gh-local")))
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       m := newTestManager(tempDir, nil, nil, ios)
        m.EnableDryRunMode()
        exts, err := m.list(false)
        assert.NoError(t, err)
@@ -303,9 +351,16 @@
 
 func TestManager_UpgradeExtension_GitExtension(t *testing.T) {
        tempDir := t.TempDir()
+       extensionDir := filepath.Join(tempDir, "extensions", "gh-remote")
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-remote", "gh-remote")))
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extensionDir).Return(gcOne).Times(4)
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcOne.On("Remotes").Return(nil, nil).Once()
+       gcOne.On("Pull", "", "").Return(nil).Once()
+       m := newTestManager(tempDir, nil, gc, ios)
        exts, err := m.list(false)
        assert.NoError(t, err)
        assert.Equal(t, 1, len(exts))
@@ -314,20 +369,23 @@
        ext.latestVersion = "new version"
        err = m.upgradeExtension(ext, false)
        assert.NoError(t, err)
-       assert.Equal(t, heredoc.Docf(
-               `
-               [git -C %s pull --ff-only]
-               `,
-               filepath.Join(tempDir, "extensions", "gh-remote"),
-       ), stdout.String())
+       assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_UpgradeExtension_GitExtension_DryRun(t *testing.T) {
        tempDir := t.TempDir()
+       extDir := filepath.Join(tempDir, "extensions", "gh-remote")
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-remote", "gh-remote")))
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extDir).Return(gcOne).Times(3)
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcOne.On("Remotes").Return(nil, nil).Once()
+       m := newTestManager(tempDir, nil, gc, ios)
        m.EnableDryRunMode()
        exts, err := m.list(false)
        assert.NoError(t, err)
@@ -339,6 +397,8 @@
        assert.NoError(t, err)
        assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_UpgradeExtension_GitExtension_Force(t *testing.T) {
@@ -346,7 +406,14 @@
        extensionDir := filepath.Join(tempDir, "extensions", "gh-remote")
        assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", 
"gh-remote", "gh-remote")))
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extensionDir).Return(gcOne).Times(4)
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+       gcOne.On("Remotes").Return(nil, nil).Once()
+       gcOne.On("Fetch", "origin", "HEAD").Return(nil).Once()
+       gcOne.On("CommandOutput", []string{"reset", "--hard", 
"origin/HEAD"}).Return("", nil).Once()
+       m := newTestManager(tempDir, nil, gc, ios)
        exts, err := m.list(false)
        assert.NoError(t, err)
        assert.Equal(t, 1, len(exts))
@@ -355,14 +422,10 @@
        ext.latestVersion = "new version"
        err = m.upgradeExtension(ext, true)
        assert.NoError(t, err)
-       assert.Equal(t, heredoc.Docf(
-               `
-               [git -C %[1]s fetch origin HEAD]
-               [git -C %[1]s reset --hard origin/HEAD]
-               `,
-               extensionDir,
-       ), stdout.String())
+       assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_MigrateToBinaryExtension(t *testing.T) {
@@ -373,7 +436,8 @@
        reg := httpmock.Registry{}
        defer reg.Verify(t)
        client := http.Client{Transport: &reg}
-       m := newTestManager(tempDir, &client, ios)
+       gc := &gitExecuter{client: &git.Client{}}
+       m := newTestManager(tempDir, &client, gc, ios)
        exts, err := m.list(false)
        assert.NoError(t, err)
        assert.Equal(t, 1, len(exts))
@@ -458,7 +522,7 @@
                }))
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, &http.Client{Transport: &reg}, ios)
+       m := newTestManager(tempDir, &http.Client{Transport: &reg}, nil, ios)
        reg.Register(
                httpmock.REST("GET", 
"api/v3/repos/owner/gh-bin-ext/releases/latest"),
                httpmock.JSONResponse(
@@ -523,7 +587,7 @@
                }))
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, &http.Client{Transport: &reg}, ios)
+       m := newTestManager(tempDir, &http.Client{Transport: &reg}, nil, ios)
        reg.Register(
                httpmock.REST("GET", 
"api/v3/repos/owner/gh-bin-ext/releases/latest"),
                httpmock.JSONResponse(
@@ -585,7 +649,7 @@
                }))
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, &http.Client{Transport: &reg}, ios)
+       m := newTestManager(tempDir, &http.Client{Transport: &reg}, nil, ios)
        m.EnableDryRunMode()
        reg.Register(
                httpmock.REST("GET", 
"api/v3/repos/owner/gh-bin-ext/releases/latest"),
@@ -638,7 +702,7 @@
                }))
 
        ios, _, _, _ := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+       m := newTestManager(tempDir, nil, nil, ios)
        exts, err := m.list(false)
        assert.Nil(t, err)
        assert.Equal(t, 1, len(exts))
@@ -655,8 +719,16 @@
        assert.NoError(t, stubPinnedExtension(filepath.Join(extDir, 
"gh-remote"), "abcd1234"))
 
        ios, _, _, _ := iostreams.Test()
-       m := newTestManager(tempDir, nil, ios)
+
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extDir).Return(gcOne).Twice()
+       gcOne.On("Config", "remote.origin.url").Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"rev-parse", "HEAD"}).Return("", 
nil).Once()
+
+       m := newTestManager(tempDir, nil, gc, ios)
+
        exts, err := m.list(false)
+
        assert.NoError(t, err)
        assert.Equal(t, 1, len(exts))
        ext := exts[0]
@@ -666,6 +738,8 @@
        err = m.upgradeExtension(ext, false)
        assert.NotNil(t, err)
        assert.Equal(t, err, pinnedExtensionUpgradeError)
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_Install_git(t *testing.T) {
@@ -676,7 +750,12 @@
        client := http.Client{Transport: &reg}
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(tempDir, &client, ios)
+
+       extensionDir := filepath.Join(tempDir, "extensions", "gh-some-ext")
+       gc := &mockGitClient{}
+       gc.On("Clone", "https://github.com/owner/gh-some-ext.git";, 
[]string{extensionDir}).Return("", nil).Once()
+
+       m := newTestManager(tempDir, &client, gc, ios)
 
        reg.Register(
                httpmock.REST("GET", "repos/owner/gh-some-ext/releases/latest"),
@@ -697,8 +776,9 @@
 
        err := m.Install(repo, "")
        assert.NoError(t, err)
-       assert.Equal(t, fmt.Sprintf("[git clone 
https://github.com/owner/gh-some-ext.git %s]\n", filepath.Join(tempDir, 
"extensions", "gh-some-ext")), stdout.String())
+       assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
 }
 
 func TestManager_Install_git_pinned(t *testing.T) {
@@ -708,8 +788,15 @@
        defer reg.Verify(t)
        client := http.Client{Transport: &reg}
 
-       ios, _, _, stderr := iostreams.Test()
-       m := newTestManager(tempDir, &client, ios)
+       ios, _, stdout, stderr := iostreams.Test()
+
+       extensionDir := filepath.Join(tempDir, "extensions", "gh-cool-ext")
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", extensionDir).Return(gcOne).Once()
+       gc.On("Clone", "https://github.com/owner/gh-cool-ext.git";, 
[]string{extensionDir}).Return("", nil).Once()
+       gcOne.On("CheckoutBranch", "abcd1234").Return(nil).Once()
+
+       m := newTestManager(tempDir, &client, gc, ios)
 
        reg.Register(
                httpmock.REST("GET", "repos/owner/gh-cool-ext/releases/latest"),
@@ -734,6 +821,9 @@
        err := m.Install(repo, "some-ref")
        assert.NoError(t, err)
        assert.Equal(t, "", stderr.String())
+       assert.Equal(t, "", stdout.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_Install_binary_pinned(t *testing.T) {
@@ -772,7 +862,7 @@
        ios, _, stdout, stderr := iostreams.Test()
        tempDir := t.TempDir()
 
-       m := newTestManager(tempDir, &http.Client{Transport: &reg}, ios)
+       m := newTestManager(tempDir, &http.Client{Transport: &reg}, nil, ios)
 
        err := m.Install(repo, "v1.6.3-pre")
        assert.NoError(t, err)
@@ -836,7 +926,7 @@
        ios, _, stdout, stderr := iostreams.Test()
        tempDir := t.TempDir()
 
-       m := newTestManager(tempDir, &client, ios)
+       m := newTestManager(tempDir, &client, nil, ios)
 
        err := m.Install(repo, "")
        assert.EqualError(t, err, "gh-bin-ext unsupported for windows-amd64. 
Open an issue: `gh issue create -R owner/gh-bin-ext -t'Support windows-amd64'`")
@@ -881,7 +971,7 @@
        ios, _, stdout, stderr := iostreams.Test()
        tempDir := t.TempDir()
 
-       m := newTestManager(tempDir, &http.Client{Transport: &reg}, ios)
+       m := newTestManager(tempDir, &http.Client{Transport: &reg}, nil, ios)
 
        err := m.Install(repo, "")
        assert.NoError(t, err)
@@ -925,7 +1015,7 @@
        ios, _, stdout, stderr := iostreams.Test()
        tempDir := t.TempDir()
 
-       m := newTestManager(tempDir, &http.Client{Transport: &reg}, ios)
+       m := newTestManager(tempDir, &http.Client{Transport: &reg}, nil, ios)
 
        if err := m.Install(repo, ""); err != repositoryNotFoundErr {
                t.Errorf("expected repositoryNotFoundErr, got: %v", err)
@@ -937,24 +1027,35 @@
 
 func TestManager_Create(t *testing.T) {
        chdirTemp(t)
+       err := os.MkdirAll("gh-test", 0755)
+       assert.NoError(t, err)
+
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(".", nil, ios)
 
-       err := m.Create("gh-test", extensions.GitTemplateType)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", "gh-test").Return(gcOne).Once()
+       gc.On("CommandOutput", []string{"init", "--quiet", 
"gh-test"}).Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"add", "gh-test", 
"--chmod=+x"}).Return("", nil).Once()
+
+       m := newTestManager(".", nil, gc, ios)
+
+       err = m.Create("gh-test", extensions.GitTemplateType)
        assert.NoError(t, err)
        files, err := os.ReadDir("gh-test")
        assert.NoError(t, err)
        assert.Equal(t, []string{"gh-test"}, fileNames(files))
 
-       assert.Equal(t, heredoc.Doc(`
-               [git init --quiet gh-test]
-               [git -C gh-test add gh-test --chmod=+x]
-       `), stdout.String())
+       assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_Create_go_binary(t *testing.T) {
        chdirTemp(t)
+       err := os.MkdirAll("gh-test", 0755)
+       assert.NoError(t, err)
+
        reg := httpmock.Registry{}
        defer reg.Verify(t)
        reg.Register(
@@ -962,9 +1063,15 @@
                
httpmock.StringResponse(`{"data":{"viewer":{"login":"jillv"}}}`))
 
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(".", &http.Client{Transport: &reg}, ios)
 
-       err := m.Create("gh-test", extensions.GoBinTemplateType)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", "gh-test").Return(gcOne).Once()
+       gc.On("CommandOutput", []string{"init", "--quiet", 
"gh-test"}).Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"add", "."}).Return("", nil).Once()
+
+       m := newTestManager(".", &http.Client{Transport: &reg}, gc, ios)
+
+       err = m.Create("gh-test", extensions.GoBinTemplateType)
        require.NoError(t, err)
 
        files, err := os.ReadDir("gh-test")
@@ -983,21 +1090,31 @@
        assert.Equal(t, []string{"release.yml"}, fileNames(files))
 
        assert.Equal(t, heredoc.Doc(`
-               [git init --quiet gh-test]
                [go mod init github.com/jillv/gh-test]
                [go mod tidy]
                [go build]
-               [git -C gh-test add .]
        `), stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 func TestManager_Create_other_binary(t *testing.T) {
        chdirTemp(t)
+       err := os.MkdirAll("gh-test", 0755)
+       assert.NoError(t, err)
+
        ios, _, stdout, stderr := iostreams.Test()
-       m := newTestManager(".", nil, ios)
 
-       err := m.Create("gh-test", extensions.OtherBinTemplateType)
+       gc, gcOne := &mockGitClient{}, &mockGitClient{}
+       gc.On("ForRepo", "gh-test").Return(gcOne).Once()
+       gc.On("CommandOutput", []string{"init", "--quiet", 
"gh-test"}).Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"add", filepath.Join("script", 
"build.sh"), "--chmod=+x"}).Return("", nil).Once()
+       gcOne.On("CommandOutput", []string{"add", "."}).Return("", nil).Once()
+
+       m := newTestManager(".", nil, gc, ios)
+
+       err = m.Create("gh-test", extensions.OtherBinTemplateType)
        assert.NoError(t, err)
 
        files, err := os.ReadDir("gh-test")
@@ -1012,12 +1129,10 @@
        assert.NoError(t, err)
        assert.Equal(t, []string{"build.sh"}, fileNames(files))
 
-       assert.Equal(t, heredoc.Docf(`
-               [git init --quiet gh-test]
-               [git -C gh-test add %s --chmod=+x]
-               [git -C gh-test add .]
-       `, filepath.FromSlash("script/build.sh")), stdout.String())
+       assert.Equal(t, "", stdout.String())
        assert.Equal(t, "", stderr.String())
+       gc.AssertExpectations(t)
+       gcOne.AssertExpectations(t)
 }
 
 // chdirTemp changes the current working directory to a temporary directory 
for the duration of the test.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/cmd/extension/mocks.go 
new/cli-2.20.2/pkg/cmd/extension/mocks.go
--- old/cli-2.20.0/pkg/cmd/extension/mocks.go   1970-01-01 01:00:00.000000000 
+0100
+++ new/cli-2.20.2/pkg/cmd/extension/mocks.go   2022-11-15 10:40:30.000000000 
+0100
@@ -0,0 +1,53 @@
+package extension
+
+import (
+       "github.com/cli/cli/v2/git"
+       "github.com/stretchr/testify/mock"
+)
+
+type mockGitClient struct {
+       mock.Mock
+}
+
+func (g *mockGitClient) CheckoutBranch(branch string) error {
+       args := g.Called(branch)
+       return args.Error(0)
+}
+
+func (g *mockGitClient) Clone(cloneURL string, cloneArgs []string) (string, 
error) {
+       args := g.Called(cloneURL, cloneArgs)
+       return args.String(0), args.Error(1)
+}
+
+func (g *mockGitClient) CommandOutput(commandArgs []string) ([]byte, error) {
+       args := g.Called(commandArgs)
+       return []byte(args.String(0)), args.Error(1)
+}
+
+func (g *mockGitClient) Config(name string) (string, error) {
+       args := g.Called(name)
+       return args.String(0), args.Error(1)
+}
+
+func (g *mockGitClient) Fetch(remote string, refspec string) error {
+       args := g.Called(remote, refspec)
+       return args.Error(0)
+}
+
+func (g *mockGitClient) ForRepo(repoDir string) gitClient {
+       args := g.Called(repoDir)
+       if v, ok := args.Get(0).(*mockGitClient); ok {
+               return v
+       }
+       return nil
+}
+
+func (g *mockGitClient) Pull(remote, branch string) error {
+       args := g.Called(remote, branch)
+       return args.Error(0)
+}
+
+func (g *mockGitClient) Remotes() (git.RemoteSet, error) {
+       args := g.Called()
+       return nil, args.Error(1)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/cmd/factory/default.go 
new/cli-2.20.2/pkg/cmd/factory/default.go
--- old/cli-2.20.0/pkg/cmd/factory/default.go   2022-11-08 21:54:07.000000000 
+0100
+++ new/cli-2.20.2/pkg/cmd/factory/default.go   2022-11-15 10:40:30.000000000 
+0100
@@ -156,7 +156,7 @@
 }
 
 func extensionManager(f *cmdutil.Factory) *extension.Manager {
-       em := extension.NewManager(f.IOStreams)
+       em := extension.NewManager(f.IOStreams, f.GitClient)
 
        cfg, err := f.Config()
        if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/cmd/repo/list/http.go 
new/cli-2.20.2/pkg/cmd/repo/list/http.go
--- old/cli-2.20.0/pkg/cmd/repo/list/http.go    2022-11-08 21:54:07.000000000 
+0100
+++ new/cli-2.20.2/pkg/cmd/repo/list/http.go    2022-11-15 10:40:30.000000000 
+0100
@@ -80,7 +80,7 @@
        query := fmt.Sprintf(`query RepositoryList(%s) {
                %s {
                        login
-                       repositories(first: $perPage, after: $endCursor, 
privacy: $privacy, isFork: $fork, affiliations: OWNER, orderBy: { field: 
PUSHED_AT, direction: DESC }) {
+                       repositories(first: $perPage, after: $endCursor, 
privacy: $privacy, isFork: $fork, ownerAffiliations: OWNER, orderBy: { field: 
PUSHED_AT, direction: DESC }) {
                                nodes{%s}
                                totalCount
                                pageInfo{hasNextPage,endCursor}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cli-2.20.0/pkg/iostreams/iostreams.go 
new/cli-2.20.2/pkg/iostreams/iostreams.go
--- old/cli-2.20.0/pkg/iostreams/iostreams.go   2022-11-08 21:54:07.000000000 
+0100
+++ new/cli-2.20.2/pkg/iostreams/iostreams.go   2022-11-15 10:40:30.000000000 
+0100
@@ -341,7 +341,7 @@
 }
 
 func (s *IOStreams) RefreshScreen() {
-       if s.stdoutIsTTY {
+       if s.IsStdoutTTY() {
                // Move cursor to 0,0
                fmt.Fprint(s.Out, "\x1b[0;0H")
                // Clear from cursor to bottom of screen
@@ -407,8 +407,9 @@
        }
 
        stdoutIsTTY := io.IsStdoutTTY()
+       stderrIsTTY := io.IsStderrTTY()
 
-       if stdoutIsTTY && io.IsStderrTTY() {
+       if stdoutIsTTY && stderrIsTTY {
                io.progressIndicatorEnabled = true
        }
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/gh/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.gh.new.1597/vendor.tar.gz differ: char 5, line 1

Reply via email to