@@ -7,9 +7,11 @@ package git
77import (
88 "bytes"
99 "context"
10+ "errors"
1011 "fmt"
1112 "io"
1213 "os"
14+ "os/exec"
1315 "strings"
1416 "time"
1517
@@ -29,16 +31,17 @@ const DefaultTimeout = time.Minute
2931// gitCmd builds a *run.Command for "git" with the given arguments, environment
3032// variables and working directory. If the context does not already have a
3133// deadline, DefaultTimeout will be applied automatically.
32- func gitCmd (ctx context.Context , dir string , args []string , envs []string ) * run.Command {
34+ func gitCmd (ctx context.Context , dir string , args []string , envs []string ) ( * run.Command , context. CancelFunc ) {
3335 if ctx == nil {
3436 ctx = context .Background ()
3537 }
38+ cancel := func () {}
3639
3740 // Apply default timeout if the context doesn't already have a deadline.
3841 if _ , ok := ctx .Deadline (); ! ok {
39- var cancel context.CancelFunc
40- ctx , cancel = context .WithTimeout (ctx , DefaultTimeout )
41- _ = cancel
42+ var timeoutCancel context.CancelFunc
43+ ctx , timeoutCancel = context .WithTimeout (ctx , DefaultTimeout )
44+ cancel = timeoutCancel
4245 }
4346
4447 // run.Cmd joins all parts into a single string and then shell-parses it.
@@ -57,25 +60,26 @@ func gitCmd(ctx context.Context, dir string, args []string, envs []string) *run.
5760 if len (envs ) > 0 {
5861 cmd = cmd .Environ (append (os .Environ (), envs ... ))
5962 }
60- return cmd
63+ return cmd , cancel
6164}
6265
6366// gitRun executes a git command in the given directory and returns stdout as
6467// bytes. Stderr is included in the error message on failure. If the command's
6568// context does not have a deadline, DefaultTimeout will be applied
6669// automatically. It returns an ErrExecTimeout if the execution was timed out.
6770func gitRun (ctx context.Context , dir string , args []string , envs []string ) ([]byte , error ) {
68- cmd := gitCmd (ctx , dir , args , envs )
71+ cmd , cancel := gitCmd (ctx , dir , args , envs )
72+ defer cancel ()
6973
70- logBuf := new ( bytes.Buffer )
74+ var logBuf * bytes.Buffer
7175 if logOutput != nil {
76+ logBuf = new (bytes.Buffer )
7277 logBuf .Grow (512 )
78+ defer func () {
79+ logf (dir , args , logBuf .Bytes ())
80+ }()
7381 }
7482
75- defer func () {
76- logf (dir , args , logBuf .Bytes ())
77- }()
78-
7983 // Use Stream to a buffer to preserve raw bytes (including NUL bytes from
8084 // commands like "ls-tree -z"). The String/Lines methods process output
8185 // line-by-line which corrupts binary-ish output.
@@ -104,25 +108,27 @@ func gitRun(ctx context.Context, dir string, args []string, envs []string) ([]by
104108// to the given writer. If stderr writer is provided and the command fails,
105109// stderr content extracted from the error is written to it. stdin is optional.
106110func gitPipeline (ctx context.Context , dir string , args []string , envs []string , stdout , stderr io.Writer , stdin io.Reader ) error {
107- cmd := gitCmd (ctx , dir , args , envs )
111+ cmd , cancel := gitCmd (ctx , dir , args , envs )
112+ defer cancel ()
108113 if stdin != nil {
109114 cmd = cmd .Input (stdin )
110115 }
111116
112- buf := new ( bytes.Buffer )
117+ var buf * bytes.Buffer
113118 w := stdout
114119 if logOutput != nil {
120+ buf = new (bytes.Buffer )
115121 buf .Grow (512 )
116122 w = & limitDualWriter {
117123 W : buf ,
118124 N : int64 (buf .Cap ()),
119125 w : stdout ,
120126 }
121- }
122127
123- defer func () {
124- logf (dir , args , buf .Bytes ())
125- }()
128+ defer func () {
129+ logf (dir , args , buf .Bytes ())
130+ }()
131+ }
126132
127133 streamErr := cmd .StdOut ().Run ().Stream (w )
128134 if streamErr != nil {
@@ -195,12 +201,6 @@ func mapContextError(err error, ctx context.Context) error {
195201 }
196202 return ctxErr
197203 }
198- // Also check if the error itself wraps a context error.
199- if strings .Contains (err .Error (), "signal: killed" ) || strings .Contains (err .Error (), context .DeadlineExceeded .Error ()) {
200- if ctx .Err () == context .DeadlineExceeded {
201- return ErrExecTimeout
202- }
203- }
204204 return err
205205}
206206
@@ -210,6 +210,10 @@ func extractStderr(err error) string {
210210 if err == nil {
211211 return ""
212212 }
213+ var exitErr * exec.ExitError
214+ if errors .As (err , & exitErr ) && len (exitErr .Stderr ) > 0 {
215+ return string (exitErr .Stderr )
216+ }
213217 msg := err .Error ()
214218 // sourcegraph/run error format: "exit status N: <stderr>"
215219 if idx := strings .Index (msg , ": " ); idx >= 0 && strings .HasPrefix (msg , "exit status" ) {
0 commit comments