A portable and magic-free way to open Pull Requests from the Command Line

Posted on | 817 words | ~4 mins
Bash Git Linux CodeProject

This little bash snippet will let you open a GitHub or GitLab pull request from the command line on most Unix-like systems (OSX, Ubuntu, etc), without using any magic libraries, ZSH tricks or other dependencies.

tl;dr download the gpr.sh gist.

gpr

Here’s how it looks in action OSX:

gpr

And Ubuntu:

gpr-ubuntu

The script is available as the gpr.sh gist. You can also find it in my dotfiles, in the git.sh file.

The Script

Here’s the script in its entirety:

# Colour constants for nicer output.
GREEN='\033[0;32m'
RESET='\033[0m'

# Push the current branch to origin, set upstream, open the PR page if possible.
gpr() {
    # Get the current branch name, or use 'HEAD' if we cannot get it.
    branch=$(git symbolic-ref -q HEAD)
    branch=${branch##refs/heads/}
    branch=${branch:-HEAD}

    # Pushing take a little while, so let the user know we're working.
    echo "Opening pull request for ${GREEN}${branch}${RESET}..."

    # Push to origin, grabbing the output but then echoing it back.
    push_output=`git push origin -u ${branch} 2>&1`
    echo ""
    echo ${push_output}

    # If there's anything which starts with http, it's a good guess it'll be a
    # link to GitHub/GitLab/Whatever. So open it.
    link=$(echo ${push_output} | grep -o 'http.*' | sed -e 's/[[:space:]]*$//')
    if [ ${link} ]; then
        echo ""
        echo "Opening: ${GREEN}${link}${RESET}..."
        python -mwebbrowser ${link}
    fi
}

How It Works

Blow-by-blow, let’s take a look.

# Colour constants for nicer output.
GREEN='\033[0;32m'
RESET='\033[0m'

To make colouring console output easier, we create strings with the escape code required to set the ‘green’ colour, and reset the text colour.

gpr() {
    # Get the current branch name, or use 'HEAD' if we cannot get it.
    branch=$(git symbolic-ref -q HEAD)
    branch=${branch##refs/heads/}
    branch=${branch:-HEAD}

Now we define the gpr (Git Pull Request) function. We’ll need to push the current branch, so we need to get the current branch name. There’s plenty of discussion on how this works on Stack Overflow: How to get the current branch name in Git. Essentially we just get the symbolic name for the head of our current branch, which will be something like this:

refs/heads/my-new-branch

We then use Bash substring removal to rip out the ref/heads/ part. If we have no branch (for example, we are detached) we just use HEAD a the branch name.

Next we have this:

    # Pushing take a little while, so let the user know we're working.
    echo "Opening pull request for ${GREEN}${branch}${RESET}..."

    # Push to origin, grabbing the output but then echoing it back.
    push_output=`git push origin -u ${branch} 2>&1`
    echo ""
    echo ${push_output}

We’ve previously defined some strings which include the escape codes to colour terminal output. Now we just show the user the branch we’re going to push, push it and then store all of the output in the push_output variable.

The 2>&1 idiom is a common one. This simply makes sure we put all stderr output (which is always file descriptor 2) into stdout (which is always file descriptor 1). This means whether the program writes output to stdout or stderr, we capture it. There’s a nice write-up on this in the blog post ‘Understanding Shell Script’s idiom: 2>&1 ’.

The output from Git push will be dependent on the Git server being used. For GitHub it’ll look like this:

remote:
remote: Create a pull request for 'feat/doc-cleanup' on GitHub by visiting:
remote:      https://github.com/dwmkerr/dotfiles/pull/new/feat/doc-cleanup
remote:
To github.com:dwmkerr/dotfiles
 * [new branch]      feat/doc-cleanup -> feat/doc-cleanup
Branch feat/doc-cleanup set up to track remote branch feat/doc-cleanup from origin.

Now all we want to do is see if there is any text which starts with http and if there is, then open it. Here’s how we do that:

    # If there's anything which starts with http, it's a good guess it'll be a
    # link to GitHub/GitLab/Whatever. So open it.
    link=$(echo ${push_output} | grep -o 'http.*' | sed -e 's/[[:space:]]*$//')
    if [ ${link} ]; then
        echo ""
        echo "Opening: ${GREEN}${link}${RESET}..."
        python -mwebbrowser ${link}
    fi

This uses grep to rip out everything from http onwards, and the sed to remove any trailing whitespace. If we have found a link, we use python to open it (which is a fairly safe cross-platform solution).

That’s it! When you have a branch ready which you want to push and create a pull request from, just run:

gpr

And the branch will be pushed to origin, and if there is a Pull Request webpage, it’ll be opened.

Prior Art

My colleague Tobias recently shared a nice trick we worked out to open a GitLab merge request - which also now works for GitHub:

I wanted to be able to use the same trick in Ubuntu and other Linux distros, but realised it relied on oh-my-zsh and assumed OSX with Chrome as the browser, so tweaked it to the above. Thanks Tobi!