Improve git log for debugging (#24095)

This commit is contained in:
wxiaoguang 2023-04-14 07:17:27 +08:00 committed by GitHub
parent 985f76dc4b
commit 334c899f7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 59 deletions

View file

@ -40,7 +40,7 @@ const DefaultLocale = "C"
// Command represents a command with its subcommands or arguments. // Command represents a command with its subcommands or arguments.
type Command struct { type Command struct {
name string prog string
args []string args []string
parentContext context.Context parentContext context.Context
desc string desc string
@ -49,10 +49,28 @@ type Command struct {
} }
func (c *Command) String() string { func (c *Command) String() string {
if len(c.args) == 0 { return c.toString(false)
return c.name }
func (c *Command) toString(sanitizing bool) string {
// WARNING: this function is for debugging purposes only. It's much better than old code (which only joins args with space),
// It's impossible to make a simple and 100% correct implementation of argument quoting for different platforms.
debugQuote := func(s string) string {
if strings.ContainsAny(s, " `'\"\t\r\n") {
return fmt.Sprintf("%q", s)
} }
return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " ")) return s
}
a := make([]string, 0, len(c.args)+1)
a = append(a, debugQuote(c.prog))
for _, arg := range c.args {
if sanitizing && (strings.Contains(arg, "://") && strings.Contains(arg, "@")) {
a = append(a, debugQuote(util.SanitizeCredentialURLs(arg)))
} else {
a = append(a, debugQuote(arg))
}
}
return strings.Join(a, " ")
} }
// NewCommand creates and returns a new Git Command based on given command and arguments. // NewCommand creates and returns a new Git Command based on given command and arguments.
@ -67,7 +85,7 @@ func NewCommand(ctx context.Context, args ...internal.CmdArg) *Command {
cargs = append(cargs, string(arg)) cargs = append(cargs, string(arg))
} }
return &Command{ return &Command{
name: GitExecutable, prog: GitExecutable,
args: cargs, args: cargs,
parentContext: ctx, parentContext: ctx,
globalArgsLength: len(globalCommandArgs), globalArgsLength: len(globalCommandArgs),
@ -82,7 +100,7 @@ func NewCommandContextNoGlobals(ctx context.Context, args ...internal.CmdArg) *C
cargs = append(cargs, string(arg)) cargs = append(cargs, string(arg))
} }
return &Command{ return &Command{
name: GitExecutable, prog: GitExecutable,
args: cargs, args: cargs,
parentContext: ctx, parentContext: ctx,
} }
@ -250,29 +268,19 @@ func (c *Command) Run(opts *RunOpts) error {
} }
if len(opts.Dir) == 0 { if len(opts.Dir) == 0 {
log.Debug("%s", c) log.Debug("git.Command.Run: %s", c)
} else { } else {
log.Debug("%s: %v", opts.Dir, c) log.Debug("git.Command.RunDir(%s): %s", opts.Dir, c)
} }
desc := c.desc desc := c.desc
if desc == "" { if desc == "" {
args := c.args[c.globalArgsLength:] if opts.Dir == "" {
var argSensitiveURLIndexes []int desc = fmt.Sprintf("git: %s", c.toString(true))
for i, arg := range c.args { } else {
if strings.Contains(arg, "://") && strings.Contains(arg, "@") { desc = fmt.Sprintf("git(dir:%s): %s", opts.Dir, c.toString(true))
argSensitiveURLIndexes = append(argSensitiveURLIndexes, i)
} }
} }
if len(argSensitiveURLIndexes) > 0 {
args = make([]string, len(c.args))
copy(args, c.args)
for _, urlArgIndex := range argSensitiveURLIndexes {
args[urlArgIndex] = util.SanitizeCredentialURLs(args[urlArgIndex])
}
}
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), opts.Dir)
}
var ctx context.Context var ctx context.Context
var cancel context.CancelFunc var cancel context.CancelFunc
@ -285,7 +293,7 @@ func (c *Command) Run(opts *RunOpts) error {
} }
defer finished() defer finished()
cmd := exec.CommandContext(ctx, c.name, c.args...) cmd := exec.CommandContext(ctx, c.prog, c.args...)
if opts.Env == nil { if opts.Env == nil {
cmd.Env = os.Environ() cmd.Env = os.Environ()
} else { } else {

View file

@ -52,3 +52,11 @@ func TestGitArgument(t *testing.T) {
assert.True(t, isSafeArgumentValue("x")) assert.True(t, isSafeArgumentValue("x"))
assert.False(t, isSafeArgumentValue("-x")) assert.False(t, isSafeArgumentValue("-x"))
} }
func TestCommandString(t *testing.T) {
cmd := NewCommandContextNoGlobals(context.Background(), "a", "-m msg", "it's a test", `say "hello"`)
assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.String())
cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/")
assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true))
}

View file

@ -209,49 +209,22 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
} else { } else {
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror)) cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror))
} }
var outbuf, errbuf strings.Builder
if opts.Timeout == 0 { stdout, stderr, err := cmd.RunStdString(&RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
opts.Timeout = -1
}
err := cmd.Run(&RunOpts{
Env: opts.Env,
Timeout: opts.Timeout,
Dir: repoPath,
Stdout: &outbuf,
Stderr: &errbuf,
})
if err != nil { if err != nil {
if strings.Contains(errbuf.String(), "non-fast-forward") { if strings.Contains(stderr, "non-fast-forward") {
return &ErrPushOutOfDate{ return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}
StdOut: outbuf.String(), } else if strings.Contains(stderr, "! [remote rejected]") {
StdErr: errbuf.String(), err := &ErrPushRejected{StdOut: stdout, StdErr: stderr, Err: err}
Err: err,
}
} else if strings.Contains(errbuf.String(), "! [remote rejected]") {
err := &ErrPushRejected{
StdOut: outbuf.String(),
StdErr: errbuf.String(),
Err: err,
}
err.GenerateMessage() err.GenerateMessage()
return err return err
} else if strings.Contains(errbuf.String(), "matches more than one") { } else if strings.Contains(stderr, "matches more than one") {
err := &ErrMoreThanOne{ return &ErrMoreThanOne{StdOut: stdout, StdErr: stderr, Err: err}
StdOut: outbuf.String(),
StdErr: errbuf.String(),
Err: err,
}
return err
} }
return fmt.Errorf("push failed: %w - %s\n%s", err, stderr, stdout)
} }
if errbuf.Len() > 0 && err != nil { return nil
return fmt.Errorf("%w - %s", err, errbuf.String())
}
return err
} }
// GetLatestCommitTime returns time for latest commit in repository (across all branches) // GetLatestCommitTime returns time for latest commit in repository (across all branches)