Skip to content

Commit 57fbe4f

Browse files
authored
Refactor to use new git client (cli#6447)
1 parent 0197b51 commit 57fbe4f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+459
-423
lines changed

context/context.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package context
33

44
import (
5+
"context"
56
"errors"
67
"sort"
78

@@ -138,7 +139,8 @@ func (r *ResolvedRemotes) BaseRepo(io *iostreams.IOStreams, p iprompter) (ghrepo
138139
}
139140

140141
// cache the result to git config
141-
err := git.SetRemoteResolution(remote.Name, resolution)
142+
c := &git.Client{}
143+
err := c.SetRemoteResolution(context.Background(), remote.Name, resolution)
142144
return selectedRepo, err
143145
}
144146

git/client.go

Lines changed: 133 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,16 @@ func (e *NotInstalled) Unwrap() error {
3838
}
3939

4040
type GitError struct {
41-
stderr string
42-
err error
41+
ExitCode int
42+
Stderr string
43+
err error
4344
}
4445

4546
func (ge *GitError) Error() string {
46-
stderr := ge.stderr
47-
if stderr == "" {
48-
var exitError *exec.ExitError
49-
if errors.As(ge.err, &exitError) {
50-
stderr = string(exitError.Stderr)
51-
}
52-
}
53-
if stderr == "" {
47+
if ge.Stderr == "" {
5448
return fmt.Sprintf("failed to run git: %v", ge.err)
5549
}
56-
return fmt.Sprintf("failed to run git: %s", stderr)
50+
return fmt.Sprintf("failed to run git: %s", ge.Stderr)
5751
}
5852

5953
func (ge *GitError) Unwrap() error {
@@ -64,16 +58,77 @@ type gitCommand struct {
6458
*exec.Cmd
6559
}
6660

67-
// This is a hack in order to not break the hundreds of
68-
// existing tests that rely on `run.PrepareCmd` to be invoked.
6961
func (gc *gitCommand) Run() error {
70-
return run.PrepareCmd(gc.Cmd).Run()
62+
// This is a hack in order to not break the hundreds of
63+
// existing tests that rely on `run.PrepareCmd` to be invoked.
64+
err := run.PrepareCmd(gc.Cmd).Run()
65+
if err != nil {
66+
ge := GitError{err: err}
67+
var exitError *exec.ExitError
68+
if errors.As(err, &exitError) {
69+
ge.Stderr = string(exitError.Stderr)
70+
ge.ExitCode = exitError.ExitCode()
71+
}
72+
return &ge
73+
}
74+
return nil
7175
}
7276

73-
// This is a hack in order to not break the hundreds of
74-
// existing tests that rely on `run.PrepareCmd` to be invoked.
7577
func (gc *gitCommand) Output() ([]byte, error) {
76-
return run.PrepareCmd(gc.Cmd).Output()
78+
gc.Stdout = nil
79+
gc.Stderr = nil
80+
// This is a hack in order to not break the hundreds of
81+
// existing tests that rely on `run.PrepareCmd` to be invoked.
82+
out, err := run.PrepareCmd(gc.Cmd).Output()
83+
if err != nil {
84+
ge := GitError{err: err}
85+
var exitError *exec.ExitError
86+
if errors.As(err, &exitError) {
87+
ge.Stderr = string(exitError.Stderr)
88+
ge.ExitCode = exitError.ExitCode()
89+
}
90+
return []byte{}, &ge
91+
}
92+
return out, nil
93+
}
94+
95+
func (gc *gitCommand) setRepoDir(repoDir string) {
96+
for i, arg := range gc.Args {
97+
if arg == "-C" {
98+
gc.Args[i+1] = repoDir
99+
return
100+
}
101+
}
102+
gc.Args = append(gc.Args[:3], gc.Args[1:]...)
103+
gc.Args[1] = "-C"
104+
gc.Args[2] = repoDir
105+
}
106+
107+
// Allow individual commands to be modified from the default client options.
108+
type CommandModifier func(*gitCommand)
109+
110+
func WithStderr(stderr io.Writer) CommandModifier {
111+
return func(gc *gitCommand) {
112+
gc.Stderr = stderr
113+
}
114+
}
115+
116+
func WithStdout(stdout io.Writer) CommandModifier {
117+
return func(gc *gitCommand) {
118+
gc.Stdout = stdout
119+
}
120+
}
121+
122+
func WithStdin(stdin io.Reader) CommandModifier {
123+
return func(gc *gitCommand) {
124+
gc.Stdin = stdin
125+
}
126+
}
127+
128+
func WithRepoDir(repoDir string) CommandModifier {
129+
return func(gc *gitCommand) {
130+
gc.setRepoDir(repoDir)
131+
}
77132
}
78133

79134
type Client struct {
@@ -133,8 +188,7 @@ func resolveGitPath() (string, error) {
133188
// AuthenticatedCommand is a wrapper around Command that included configuration to use gh
134189
// as the credential helper for git.
135190
func (c *Client) AuthenticatedCommand(ctx context.Context, args ...string) (*gitCommand, error) {
136-
preArgs := []string{}
137-
preArgs = append(preArgs, "-c", "credential.helper=")
191+
preArgs := []string{"-c", "credential.helper="}
138192
if c.GhPath == "" {
139193
// Assumes that gh is in PATH.
140194
c.GhPath = "gh"
@@ -153,7 +207,7 @@ func (c *Client) Remotes(ctx context.Context) (RemoteSet, error) {
153207
}
154208
remoteOut, remoteErr := remoteCmd.Output()
155209
if remoteErr != nil {
156-
return nil, &GitError{err: remoteErr}
210+
return nil, remoteErr
157211
}
158212

159213
configArgs := []string{"config", "--get-regexp", `^remote\..*\.gh-resolved$`}
@@ -164,9 +218,9 @@ func (c *Client) Remotes(ctx context.Context) (RemoteSet, error) {
164218
configOut, configErr := configCmd.Output()
165219
if configErr != nil {
166220
// Ignore exit code 1 as it means there are no resolved remotes.
167-
var exitErr *exec.ExitError
168-
if errors.As(configErr, &exitErr) && exitErr.ExitCode() != 1 {
169-
return nil, &GitError{err: configErr}
221+
var gitErr *GitError
222+
if ok := errors.As(configErr, &gitErr); ok && gitErr.ExitCode != 1 {
223+
return nil, gitErr
170224
}
171225
}
172226

@@ -176,18 +230,20 @@ func (c *Client) Remotes(ctx context.Context) (RemoteSet, error) {
176230
return remotes, nil
177231
}
178232

179-
func (c *Client) AddRemote(ctx context.Context, name, urlStr string, trackingBranches []string) (*Remote, error) {
233+
func (c *Client) AddRemote(ctx context.Context, name, urlStr string, trackingBranches []string, mods ...CommandModifier) (*Remote, error) {
180234
args := []string{"remote", "add"}
181235
for _, branch := range trackingBranches {
182236
args = append(args, "-t", branch)
183237
}
184238
args = append(args, "-f", name, urlStr)
185-
//TODO: Use AuthenticatedCommand
186239
cmd, err := c.Command(ctx, args...)
187240
if err != nil {
188241
return nil, err
189242
}
190-
if err := cmd.Run(); err != nil {
243+
for _, mod := range mods {
244+
mod(cmd)
245+
}
246+
if _, err := cmd.Output(); err != nil {
191247
return nil, err
192248
}
193249
var urlParsed *url.URL
@@ -216,7 +272,11 @@ func (c *Client) UpdateRemoteURL(ctx context.Context, name, url string) error {
216272
if err != nil {
217273
return err
218274
}
219-
return cmd.Run()
275+
_, err = cmd.Output()
276+
if err != nil {
277+
return err
278+
}
279+
return nil
220280
}
221281

222282
func (c *Client) SetRemoteResolution(ctx context.Context, name, resolution string) error {
@@ -225,7 +285,11 @@ func (c *Client) SetRemoteResolution(ctx context.Context, name, resolution strin
225285
if err != nil {
226286
return err
227287
}
228-
return cmd.Run()
288+
_, err = cmd.Output()
289+
if err != nil {
290+
return err
291+
}
292+
return nil
229293
}
230294

231295
// CurrentBranch reads the checked-out branch for the git repository.
@@ -235,14 +299,14 @@ func (c *Client) CurrentBranch(ctx context.Context) (string, error) {
235299
if err != nil {
236300
return "", err
237301
}
238-
errBuf := bytes.Buffer{}
239-
cmd.Stderr = &errBuf
240302
out, err := cmd.Output()
241303
if err != nil {
242-
if errBuf.Len() == 0 {
243-
return "", &GitError{err: err, stderr: "not on any branch"}
304+
var gitErr *GitError
305+
if ok := errors.As(err, &gitErr); ok && len(gitErr.Stderr) == 0 {
306+
gitErr.Stderr = "not on any branch"
307+
return "", gitErr
244308
}
245-
return "", &GitError{err: err, stderr: errBuf.String()}
309+
return "", err
246310
}
247311
branch := firstLine(out)
248312
return strings.TrimPrefix(branch, "refs/heads/"), nil
@@ -257,7 +321,7 @@ func (c *Client) ShowRefs(ctx context.Context, ref ...string) ([]Ref, error) {
257321
}
258322
out, err := cmd.Output()
259323
if err != nil {
260-
return nil, &GitError{err: err}
324+
return nil, err
261325
}
262326
var refs []Ref
263327
for _, line := range outputLines(out) {
@@ -279,15 +343,14 @@ func (c *Client) Config(ctx context.Context, name string) (string, error) {
279343
if err != nil {
280344
return "", err
281345
}
282-
errBuf := bytes.Buffer{}
283-
cmd.Stderr = &errBuf
284346
out, err := cmd.Output()
285347
if err != nil {
286-
var exitError *exec.ExitError
287-
if ok := errors.As(err, &exitError); ok && exitError.Error() == "1" {
288-
return "", &GitError{err: err, stderr: fmt.Sprintf("unknown config key %s", name)}
348+
var gitErr *GitError
349+
if ok := errors.As(err, &gitErr); ok && gitErr.ExitCode == 1 {
350+
gitErr.Stderr = fmt.Sprintf("unknown config key %s", name)
351+
return "", gitErr
289352
}
290-
return "", &GitError{err: err, stderr: errBuf.String()}
353+
return "", err
291354
}
292355
return firstLine(out), nil
293356
}
@@ -300,7 +363,7 @@ func (c *Client) UncommittedChangeCount(ctx context.Context) (int, error) {
300363
}
301364
out, err := cmd.Output()
302365
if err != nil {
303-
return 0, &GitError{err: err}
366+
return 0, err
304367
}
305368
lines := strings.Split(string(out), "\n")
306369
count := 0
@@ -320,7 +383,7 @@ func (c *Client) Commits(ctx context.Context, baseRef, headRef string) ([]*Commi
320383
}
321384
out, err := cmd.Output()
322385
if err != nil {
323-
return nil, &GitError{err: err}
386+
return nil, err
324387
}
325388
commits := []*Commit{}
326389
sha := 0
@@ -349,7 +412,7 @@ func (c *Client) lookupCommit(ctx context.Context, sha, format string) ([]byte,
349412
}
350413
out, err := cmd.Output()
351414
if err != nil {
352-
return nil, &GitError{err: err}
415+
return nil, err
353416
}
354417
return out, nil
355418
}
@@ -372,13 +435,16 @@ func (c *Client) CommitBody(ctx context.Context, sha string) (string, error) {
372435
}
373436

374437
// Push publishes a git ref to a remote and sets up upstream configuration.
375-
func (c *Client) Push(ctx context.Context, remote string, ref string) error {
438+
func (c *Client) Push(ctx context.Context, remote string, ref string, mods ...CommandModifier) error {
376439
args := []string{"push", "--set-upstream", remote, ref}
377440
//TODO: Use AuthenticatedCommand
378441
cmd, err := c.Command(ctx, args...)
379442
if err != nil {
380443
return err
381444
}
445+
for _, mod := range mods {
446+
mod(cmd)
447+
}
382448
return cmd.Run()
383449
}
384450

@@ -424,7 +490,11 @@ func (c *Client) DeleteLocalBranch(ctx context.Context, branch string) error {
424490
if err != nil {
425491
return err
426492
}
427-
return cmd.Run()
493+
_, err = cmd.Output()
494+
if err != nil {
495+
return err
496+
}
497+
return nil
428498
}
429499

430500
func (c *Client) HasLocalBranch(ctx context.Context, branch string) bool {
@@ -433,7 +503,7 @@ func (c *Client) HasLocalBranch(ctx context.Context, branch string) bool {
433503
if err != nil {
434504
return false
435505
}
436-
err = cmd.Run()
506+
_, err = cmd.Output()
437507
return err == nil
438508
}
439509

@@ -443,7 +513,11 @@ func (c *Client) CheckoutBranch(ctx context.Context, branch string) error {
443513
if err != nil {
444514
return err
445515
}
446-
return cmd.Run()
516+
_, err = cmd.Output()
517+
if err != nil {
518+
return err
519+
}
520+
return nil
447521
}
448522

449523
func (c *Client) CheckoutNewBranch(ctx context.Context, remoteName, branch string) error {
@@ -453,7 +527,11 @@ func (c *Client) CheckoutNewBranch(ctx context.Context, remoteName, branch strin
453527
if err != nil {
454528
return err
455529
}
456-
return cmd.Run()
530+
_, err = cmd.Output()
531+
if err != nil {
532+
return err
533+
}
534+
return nil
457535
}
458536

459537
func (c *Client) Pull(ctx context.Context, remote, branch string) error {
@@ -466,7 +544,7 @@ func (c *Client) Pull(ctx context.Context, remote, branch string) error {
466544
return cmd.Run()
467545
}
468546

469-
func (c *Client) Clone(ctx context.Context, cloneURL string, args []string) (target string, err error) {
547+
func (c *Client) Clone(ctx context.Context, cloneURL string, args []string) (string, error) {
470548
cloneArgs, target := parseCloneArgs(args)
471549
cloneArgs = append(cloneArgs, cloneURL)
472550
// If the args contain an explicit target, pass it to clone
@@ -483,7 +561,10 @@ func (c *Client) Clone(ctx context.Context, cloneURL string, args []string) (tar
483561
return "", err
484562
}
485563
err = cmd.Run()
486-
return
564+
if err != nil {
565+
return "", err
566+
}
567+
return target, nil
487568
}
488569

489570
// ToplevelDir returns the top-level directory path of the current repository.
@@ -495,7 +576,7 @@ func (c *Client) ToplevelDir(ctx context.Context) (string, error) {
495576
}
496577
out, err := cmd.Output()
497578
if err != nil {
498-
return "", &GitError{err: err}
579+
return "", err
499580
}
500581
return firstLine(out), nil
501582
}
@@ -508,7 +589,7 @@ func (c *Client) GitDir(ctx context.Context) (string, error) {
508589
}
509590
out, err := cmd.Output()
510591
if err != nil {
511-
return "", &GitError{err: err}
592+
return "", err
512593
}
513594
return firstLine(out), nil
514595
}

0 commit comments

Comments
 (0)