Skip to content

Commit ffbfd0a

Browse files
unknwonclaude
andcommitted
refactor: address fourth round of PR review feedback
- Rename helpers: gitCmd -> cmd, gitRun -> exec, gitPipeline -> pipe - Remove stderr parameter from pipe (stderr is already in the error) - Remove extractStderr (no longer used after stderr removal) - Remove Blob.Pipeline stderr parameter and update all callers - Rename CreateArchive -> Archive and add --end-of-options Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b7f7b6f commit ffbfd0a

20 files changed

+88
-142
lines changed

blob.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ func (b *Blob) Bytes(ctx context.Context) ([]byte, error) {
2525
stdout.Grow(int(size))
2626
}
2727

28-
if err := b.Pipeline(ctx, stdout, nil); err != nil {
28+
if err := b.Pipeline(ctx, stdout); err != nil {
2929
return nil, err
3030
}
3131
return stdout.Bytes(), nil
3232
}
3333

34-
// Pipeline reads the content of the blob and pipes stdout and stderr to
35-
// supplied io.Writer.
36-
func (b *Blob) Pipeline(ctx context.Context, stdout, stderr io.Writer) error {
37-
return gitPipeline(ctx, b.parent.repo.path, []string{"show", b.id.String()}, nil, stdout, stderr)
34+
// Pipeline reads the content of the blob and pipes stdout to the supplied
35+
// io.Writer.
36+
func (b *Blob) Pipeline(ctx context.Context, stdout io.Writer) error {
37+
return pipe(ctx, b.parent.repo.path, []string{"show", b.id.String()}, nil, stdout)
3838
}

blob_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ This demo also includes an image with changes on a branch for examination of ima
5050

5151
t.Run("get data with pipeline", func(t *testing.T) {
5252
stdout := new(bytes.Buffer)
53-
err := blob.Pipeline(ctx, stdout, nil)
53+
err := blob.Pipeline(ctx, stdout)
5454
assert.Nil(t, err)
5555
assert.Equal(t, expOutput, stdout.String())
5656
})

command.go

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package git
77
import (
88
"bytes"
99
"context"
10-
"fmt"
1110
"io"
1211
"os"
1312
"strconv"
@@ -27,10 +26,10 @@ type CommandOptions struct {
2726
// applied when the context does not already have a deadline.
2827
const DefaultTimeout = time.Minute
2928

30-
// gitCmd builds a *run.Command for "git" with the given arguments, environment
29+
// cmd builds a *run.Command for "git" with the given arguments, environment
3130
// variables and working directory. If the context does not already have a
3231
// deadline, DefaultTimeout will be applied automatically.
33-
func gitCmd(ctx context.Context, dir string, args []string, envs []string) (*run.Command, context.CancelFunc) {
32+
func cmd(ctx context.Context, dir string, args []string, envs []string) (*run.Command, context.CancelFunc) {
3433
cancel := func() {}
3534

3635
// Apply default timeout if the context doesn't already have a deadline.
@@ -49,22 +48,22 @@ func gitCmd(ctx context.Context, dir string, args []string, envs []string) (*run
4948
parts = append(parts, run.Arg(arg))
5049
}
5150

52-
cmd := run.Cmd(ctx, parts...)
51+
c := run.Cmd(ctx, parts...)
5352
if dir != "" {
54-
cmd = cmd.Dir(dir)
53+
c = c.Dir(dir)
5554
}
5655
if len(envs) > 0 {
57-
cmd = cmd.Environ(append(os.Environ(), envs...))
56+
c = c.Environ(append(os.Environ(), envs...))
5857
}
59-
return cmd, cancel
58+
return c, cancel
6059
}
6160

62-
// gitRun executes a git command in the given directory and returns stdout as
61+
// exec executes a git command in the given directory and returns stdout as
6362
// bytes. Stderr is included in the error message on failure. If the command's
6463
// context does not have a deadline, DefaultTimeout will be applied
6564
// automatically. It returns an ErrExecTimeout if the execution was timed out.
66-
func gitRun(ctx context.Context, dir string, args []string, envs []string) ([]byte, error) {
67-
cmd, cancel := gitCmd(ctx, dir, args, envs)
65+
func exec(ctx context.Context, dir string, args []string, envs []string) ([]byte, error) {
66+
c, cancel := cmd(ctx, dir, args, envs)
6867
defer cancel()
6968

7069
var logBuf *bytes.Buffer
@@ -80,7 +79,7 @@ func gitRun(ctx context.Context, dir string, args []string, envs []string) ([]by
8079
// commands like "ls-tree -z"). The String/Lines methods process output
8180
// line-by-line which corrupts binary-ish output.
8281
stdout := new(bytes.Buffer)
83-
err := cmd.StdOut().Run().Stream(stdout)
82+
err := c.StdOut().Run().Stream(stdout)
8483

8584
// Capture (partial) stdout for logging even on error, so failed commands
8685
// produce a useful log entry rather than an empty one.
@@ -102,11 +101,10 @@ func gitRun(ctx context.Context, dir string, args []string, envs []string) ([]by
102101
return stdout.Bytes(), nil
103102
}
104103

105-
// gitPipeline executes a git command in the given directory, streaming stdout
106-
// to the given writer. If stderr writer is provided and the command fails,
107-
// stderr content extracted from the error is written to it.
108-
func gitPipeline(ctx context.Context, dir string, args []string, envs []string, stdout, stderr io.Writer) error {
109-
cmd, cancel := gitCmd(ctx, dir, args, envs)
104+
// pipe executes a git command in the given directory, streaming stdout to the
105+
// given writer.
106+
func pipe(ctx context.Context, dir string, args []string, envs []string, stdout io.Writer) error {
107+
c, cancel := cmd(ctx, dir, args, envs)
110108
defer cancel()
111109

112110
var buf *bytes.Buffer
@@ -125,11 +123,8 @@ func gitPipeline(ctx context.Context, dir string, args []string, envs []string,
125123
}()
126124
}
127125

128-
streamErr := cmd.StdOut().Run().Stream(w)
126+
streamErr := c.StdOut().Run().Stream(w)
129127
if streamErr != nil {
130-
if stderr != nil {
131-
_, _ = fmt.Fprint(stderr, extractStderr(streamErr))
132-
}
133128
return mapContextError(streamErr, ctx)
134129
}
135130
return nil
@@ -213,17 +208,3 @@ func isExitStatus(err error, code int) bool {
213208
exitCoder, ok := err.(run.ExitCoder)
214209
return ok && exitCoder.ExitCode() == code
215210
}
216-
217-
// extractStderr attempts to extract the stderr portion from a sourcegraph/run
218-
// error. The error format is typically "exit status N: <stderr content>".
219-
func extractStderr(err error) string {
220-
if err == nil {
221-
return ""
222-
}
223-
msg := err.Error()
224-
// sourcegraph/run error format: "exit status N: <stderr>"
225-
if idx := strings.Index(msg, ": "); idx >= 0 && strings.HasPrefix(msg, "exit status") {
226-
return msg[idx+2:]
227-
}
228-
return msg
229-
}

command_test.go

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,33 @@ import (
1313
"github.com/stretchr/testify/assert"
1414
)
1515

16-
func TestGitRun_ContextTimeout(t *testing.T) {
16+
func TestExec_ContextTimeout(t *testing.T) {
1717
t.Run("context already expired before start", func(t *testing.T) {
1818
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
1919
defer cancel()
2020
time.Sleep(time.Millisecond) // ensure deadline has passed
21-
_, err := gitRun(ctx, "", []string{"version"}, nil)
21+
_, err := exec(ctx, "", []string{"version"}, nil)
2222
assert.Equal(t, ErrExecTimeout, err)
2323
})
2424

2525
t.Run("context deadline fires mid-execution", func(t *testing.T) {
2626
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
2727
defer cancel()
2828

29-
// Use gitCmd directly with a blocking stdin so the command starts
29+
// Use cmd directly with a blocking stdin so the command starts
3030
// successfully and blocks reading until the context deadline fires.
31-
cmd, timeoutCancel := gitCmd(ctx, "", []string{"hash-object", "--stdin"}, nil)
31+
c, timeoutCancel := cmd(ctx, "", []string{"hash-object", "--stdin"}, nil)
3232
defer timeoutCancel()
3333

34-
err := cmd.Input(blockingReader{cancel: ctx.Done()}).StdOut().Run().Stream(io.Discard)
34+
err := c.Input(blockingReader{cancel: ctx.Done()}).StdOut().Run().Stream(io.Discard)
3535
err = mapContextError(err, ctx)
3636
assert.Equal(t, ErrExecTimeout, err)
3737
})
3838
}
3939

4040
// blockingReader is an io.Reader that blocks until its cancel channel is
4141
// closed, simulating a stdin that never provides data. When cancelled it
42-
// returns io.EOF so that exec's stdin copy goroutine can exit cleanly,
42+
// returns io.EOF so that the stdin copy goroutine can exit cleanly,
4343
// allowing cmd.Wait() to return.
4444
type blockingReader struct {
4545
cancel <-chan struct{}
@@ -50,7 +50,7 @@ func (r blockingReader) Read(p []byte) (int, error) {
5050
return 0, io.EOF
5151
}
5252

53-
func TestGitCmd_ContextCancellation(t *testing.T) {
53+
func TestCmd_ContextCancellation(t *testing.T) {
5454
ctx, cancel := context.WithCancel(context.Background())
5555

5656
// Cancel in the background after a short delay so the command is already
@@ -62,58 +62,22 @@ func TestGitCmd_ContextCancellation(t *testing.T) {
6262
close(done)
6363
}()
6464

65-
cmd, timeoutCancel := gitCmd(ctx, "", []string{"hash-object", "--stdin"}, nil)
65+
c, timeoutCancel := cmd(ctx, "", []string{"hash-object", "--stdin"}, nil)
6666
defer timeoutCancel()
6767

68-
err := cmd.Input(blockingReader{cancel: done}).StdOut().Run().Stream(io.Discard)
68+
err := c.Input(blockingReader{cancel: done}).StdOut().Run().Stream(io.Discard)
6969
err = mapContextError(err, ctx)
7070
assert.ErrorIs(t, err, context.Canceled)
7171
// Must NOT be ErrExecTimeout — cancellation is distinct from deadline.
7272
assert.NotEqual(t, ErrExecTimeout, err)
7373
}
7474

75-
func TestGitRun_DefaultTimeoutApplied(t *testing.T) {
75+
func TestExec_DefaultTimeoutApplied(t *testing.T) {
7676
// A plain context.Background() has no deadline. The command should still
7777
// succeed because DefaultTimeout (1 min) is applied automatically and
7878
// "git version" completes well within that.
7979
ctx := context.Background()
80-
stdout, err := gitRun(ctx, "", []string{"version"}, nil)
80+
stdout, err := exec(ctx, "", []string{"version"}, nil)
8181
assert.NoError(t, err)
8282
assert.Contains(t, string(stdout), "git version")
8383
}
84-
85-
func TestExtractStderr(t *testing.T) {
86-
tests := []struct {
87-
name string
88-
err error
89-
want string
90-
}{
91-
{
92-
name: "nil error",
93-
err: nil,
94-
want: "",
95-
},
96-
{
97-
name: "exit status with stderr",
98-
err: &exitStatusError{msg: "exit status 1: fatal: not a git repository"},
99-
want: "fatal: not a git repository",
100-
},
101-
{
102-
name: "other error",
103-
err: io.EOF,
104-
want: "EOF",
105-
},
106-
}
107-
for _, test := range tests {
108-
t.Run(test.name, func(t *testing.T) {
109-
assert.Equal(t, test.want, extractStderr(test.err))
110-
})
111-
}
112-
}
113-
114-
// exitStatusError is a simple error type for testing extractStderr.
115-
type exitStatusError struct {
116-
msg string
117-
}
118-
119-
func (e *exitStatusError) Error() string { return e.msg }

commit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func (c *Commit) isImageFile(ctx context.Context, blob *Blob, err error) (bool,
154154
N: int64(buf.Cap()),
155155
}
156156

157-
err = blob.Pipeline(ctx, stdout, io.Discard)
157+
err = blob.Pipeline(ctx, stdout)
158158
if err != nil {
159159
return false, err
160160
}

commit_archive.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ const (
1919
ArchiveTarGz ArchiveFormat = "tar.gz"
2020
)
2121

22-
// CreateArchive creates given format of archive to the destination.
23-
func (c *Commit) CreateArchive(ctx context.Context, format ArchiveFormat, dst string) error {
22+
// Archive creates given format of archive to the destination.
23+
func (c *Commit) Archive(ctx context.Context, format ArchiveFormat, dst string) error {
2424
prefix := filepath.Base(strings.TrimSuffix(c.repo.path, ".git")) + "/"
25-
_, err := gitRun(ctx, c.repo.path, []string{
25+
_, err := exec(ctx, c.repo.path, []string{
2626
"archive",
2727
"--prefix=" + prefix,
2828
"--format=" + string(format),
2929
"-o", dst,
30+
"--end-of-options",
3031
c.ID.String(),
3132
}, nil)
3233
return err

commit_archive_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func tempPath() string {
1919
return filepath.Join(os.TempDir(), strconv.Itoa(int(time.Now().UnixNano())))
2020
}
2121

22-
func TestCommit_CreateArchive(t *testing.T) {
22+
func TestCommit_Archive(t *testing.T) {
2323
ctx := context.Background()
2424
for _, format := range []ArchiveFormat{
2525
ArchiveZip,
@@ -36,7 +36,7 @@ func TestCommit_CreateArchive(t *testing.T) {
3636
_ = os.Remove(dst)
3737
}()
3838

39-
assert.Nil(t, c.CreateArchive(ctx, format, dst))
39+
assert.Nil(t, c.Archive(ctx, format, dst))
4040
})
4141
}
4242
}

git.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func BinVersion(ctx context.Context) (string, error) {
5858
return gitVersion, nil
5959
}
6060

61-
stdout, err := gitRun(ctx, "", []string{"version"}, nil)
61+
stdout, err := exec(ctx, "", []string{"version"}, nil)
6262
if err != nil {
6363
return "", err
6464
}

0 commit comments

Comments
 (0)