Git clone: Redirect stderr to stdout but keep errors being written to stderr

前端 未结 7 603
广开言路
广开言路 2020-11-27 21:11

git clone writes its output to stderr as documented here. I can redirect this with the following command:

git clone https://myrepo          


        
7条回答
  •  醉话见心
    2020-11-27 21:38

    Here's yet another take which might serve as inspiration for some. As others pointed out redirecting and checking exit code works well. Things which are different from other answers:

    • Redirecting to a file is a bit annoying because as long as git is running you just don't see anything happening, so just return the output and use a separate error message. It's a tradeoff, but I like it more.
    • Use Write-Verbose instead of Write-Host since the former is configurable
    • Use Write-Error to produce an error instead of the harsh Throw. Makes things like ErrorAction, ErrorVariable work as expected.
    • Support setting the directory in which to run, like you'd use -C instead of having to cd into directories, but also support that for commands which normally do not have that functionality. So you can do igit -dir some/path stash. I use this mainly in automated scripts where it's otherwise annoying to have to cd into directories.
    • Use ValueFromRemainingArguments etc so commands can be passed as if you're writing a git command directly so not requiring a string, but still allowing it. So igit checkout master works like igit 'checkout master'. Almost, that is, because standard PS caveats apply: quoting so you still need an actual string if quotes need to be passed to the underlying command, i.e. igit log '--format="%h %d"'. And PS doesn't require you to type full parameter names meaning igit push -vwill be interpreted asigit push -Verboseinstead of passing-vi.e. verbose push to git. Use double dash to deal with thatigit -- push -v` or write quotes after all.

    Code:

    <#
    .SYNOPSIS
    Run git, Powershell-style.
    .DESCRIPTION
    By default some git commands (clone, checkout, ...) write a part of their
    output to stderr, resulting in PS treating that as an error.
    Here we work around that by redirecting stderr and using git's exit code
    to check if something was actually wrong, and use Write-Error if that's the case,
    i.e. standard PS error handling which works with -ErrorAction/-ErrorVariable etc.
    The command can be passed as a string or as separate strings.
    Additionally takes a $Directory argument which when used has the same effect as git -C,
    but also works for clone/stash/submodule/... commands making it easier to automate those.
    The $Git argument can be used to specify the executable.
    .EXAMPLE
    Invoke-Git status
    Invoke-Git -Directory some/path status
    Invoke-Git 'push -v'
    Invoke-Git -Verbose -- push -v  # Pass that last -v to git.
    #>
    function Invoke-Git {
      [CmdletBinding()]
      param(
        [Parameter()] [Alias('Dir')] [String] $Directory = $null,
        [Parameter()] [String] $Git = 'git',
        [Parameter(Mandatory, Position=0, ValueFromRemainingArguments=$true)] [string] $Command
      )
      try {
        $commandParts = $Command.Split(' ')
        $subCommand = $commandParts[0]
        if ($Directory -and $subCommand -eq 'clone') {
          # To make all commands look alike handle this one as well.
          $Command = ($commandParts + @($Directory)) -join ' '
        } elseif ($Directory -and @('submodule', 'stash', 'init') -eq $subCommand) {
          # These currently require one to be in the git directory so go there.
          $currentDir = Get-Location
          cd $Directory
        } elseif ($Directory) {
          if ($commandParts -eq '-C') {
            # Not an error, git will pick the last one, but unexpected.
            Write-Warning 'Better use either -Directory or -C, not both'
          }
          $Command = "-C $Directory " + $Command
        }
        Write-Verbose "Invoke-Git on '$Directory' with command '$Command'"
        $gitRedirection = $env:GIT_REDIRECT_STDERR
        $env:GIT_REDIRECT_STDERR = '2>&1'
        # Deliberately not getting output here: while this means we cannot pass the actual error to Write-Error,
        # it does result in all commands being shown 'live'. Otherwise when doing a clone for instance,
        # nothing gets displayed while git is doing it's thing which is unexepected and too different from normal usage.
        Invoke-Expression "$Git $Command"
        if ($LASTEXITCODE -ne 0) {
          Write-Error "git exited with code $LASTEXITCODE"
        }
      } finally {
        $env:GIT_REDIRECT_STDERR = $gitRedirection
        if ($currentDir) {
          cd $currentDir
        }
      }
    }
    
    New-Alias -Name IGit -Value Invoke-Git -ErrorAction SilentlyContinue
    

提交回复
热议问题