Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
257 views
in Technique[技术] by (71.8m points)

Go exec does not capture git log output

I'm translating our Cli tool written in Ruby (using Thor) to Go.

Up until now I've had no issues with passing git commands and capturing the output I want.

The general code I use when I want an output is:

func output(c string, args []string, printOut bool) (string, error) {
    cmd := exec.Command(c, args...)

    if verbose {
        text.Command(c, args)
    }

    if printOut {
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
    }

    output, err := cmd.Output()

    if err != nil {
        reportError()

        return "", err
    }

    reportOk()

    return string(output), nil
}

I'm having some issues with the following command though:

git --no-pager log --merges --grep='^merge.*(hotfix|release)|(.*from.*(hotfix|release))' --regexp-ignore-case --perl-regexp --pretty=format:%H -n 1

I'm calling the output like this:

    args := []string{
        "--no-pager", "log", "--merges", "--grep='^merge.*(hotfix|release)|(.*from.*(hotfix|release))'", "--regexp-ignore-case", "--perl-regexp", "--pretty=format:%H", "-n", "1",
    }

    out, err := output(cmd, args, false)

For some context, this command is used to find the hash of the last release/hotfix merge, in order to find all relevant commits to add to the changelog, from that commit until 'now'.

If I remove --grep='^merge.*(hotfix|release)|(.*from.*(hotfix|release))' --regexp-ignore-case --perl-regexp, I do get a commit hash output, but I need the regex to make sure its a merge I want.

The thing is cmd.Output() does not return an error when I pass the full command, it simply returns an empty string.

The full command works when executed on the terminal.

Does anyone has any idea what I might be doing wrong?

Thanks a lot!

question from:https://stackoverflow.com/questions/65928534/go-exec-does-not-capture-git-log-output

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You're invoking git grep without letting /bin/sh or /bin/bash or whatever shell1 get its grubby little paws on the command first.

When using --grep, we (by which I mean humans typing commands into shells) write thing like:

git log --grep='^merge|whatever' whatever-else

with the single quotes here to protect the pattern from the shell. The single quotes make sure that the vertical bar | character is not treated as a pipe-to, i.e., this is not the command:

git log --grep=^merge | whatever ...

which would pipe the output from git log --grep=^merge to whatever.

We need the quote here because the shell is interpreting our command, and treats some character specially; the single quotes defeat this, and get consumed by the shell in the process. That means Git sees --grep=^merge|whatever, for instance.

But you're not invoking the shell. There is nothing to protect. So you're instructing Git to look for '^merge, i.e., a single quote followed by a hat/caret ^ followed by an m followed by an e and so on. That doesn't exist in any of your commits, so you are not getting the output you expect.

The solution is trivial: stop quoting things against the non-existent shell, so that you aren't passing unexpected quotes to Git. If you do need to use the shell for some reason, keep the quotes, but pay very close attention to everything else, since careless invocation of shells causes 28.41% of all CVEs (this statistic, like 72.93% of all statistics, was made up on the spot).


1On some systems, /bin/sh is /bin/bash; on some it's dash; on some it's some other variant. This creates portability annoyances sometimes. Here it's not critical as all shells treat these the same.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...