Video

Want to see the full-length video right now for free?

Sign In with GitHub for Free Access

Notes

In this video, we'll be talking about using Git with remotes like GitHub and Heroku. While Git is perfectly serviceable when used alone, its real power comes from working with others. Git is designed to work well in a distributed environment, and with platforms like GitHub and Heroku we can can really take advantage of that.

Hub

The first topic we want to cover is a command line utility called Hub. Hub is maintained by the folks at GitHub, and allows us to introduce Git and GitHub to each other. With Hub we can run a whole range of commands from the command line, just as we would any other Git command, and have them interact with the GitHub API for us.

The hub readme has the full listing of commands that hub provides, but we'll just be looking at a few in this video.

Hub Browse

We'll start with Hub's browse command, which opens the GitHub remote repo for the current local repo. Even better, it opens the GitHub URL for the current branch.

Hub Compare

Similar to browse, the compare command in hub will open the GitHub repo associated with the current local repo, but on the GitHub Compare Page. The compare page displays the range of commits specified (you can compare branches, specific commit ranges, or even time) and will show the complete diff.

Likewise, from the compare view we can create a new pull request or navigate to the PR if one already exists.

Hub Pull Request

The pull-request command allows us to compose our pull request title and description locally, using our favorite editor (I hear good things about Vim), just like we would with a commit message. Once we finish writing, hub will take our newly composed document and use the GitHub API to build a pull request using the provided text.

Hub CI Status

If we're using continuous integration (CI) with our pull project, we can use the ci-status command to see the current status for our CI build. This will mirror the pull request status on the PR page, aggregating any checks from services like TravisCI, CircleCI, Hound, Code Climate, etc.

The command will provide both a text output ("success", "failure", or "pending"), as well as providing a relevant exit code for use in scripts.

Integrating Hub

While we certainly can use Hub directly with the hub command, the recommended way to use Hub is to alias Hub as Git. It provides a simple code snippet that you can put into your zshrc file. This will then load on startup, and commands will be intelligently handled by Hub, or passed through to Git.

eval "$(hub alias -s)"

If you'd prefer not to fully wrap hub around git, but still want the ease of use and continuity of using git for all your Git- related fun, an alternative approach is to create specific aliases in your ~/.gitconfig file.

[alias]
  browse = !hub browse
  compare = !hub compare
  issues = !hub browse --issues

Canonical URLs and Highlighting Lines

GitHub does a wonderful job of hosting our code and making it possible to share links to specific files and directories. Even better, if we click on a line number (or shift-click to select a range of lines), then those lines will be highlighted and the URL will be updated to include a reference to those lines. We can then share that URL to highlight specific lines in specific files, which is pretty great.

Unfortunately, by default these URLs will include the current branch that we're looking at, typically master. If we share this URL and then make changes to that file on the master branch, our line numbers and the code we reference will likely be incorrect.

Instead, we want to provide a URL for the code exactly as it looks right now, which we can do by specifying a commit that will always point at the same version of the code, rather than a branch which will change over time.

Thankfully, GitHub has thought this through and provides canonical URLs for all content in our repos. If we press y, GitHub will reload the page and replace the branch reference with the relevant commit, making the link robust and safe to share.

PR Command

pr is a custom subcommand that allows us to easily open the pull request associated with the current local branch. As we iterate on a PR, respond to feedback, and refactor our branch, we often find ourselves needing to return to the PR to review comments or CI build status. Automating and streamlining this iteration is a big win.

Check out the full implementation of the git-pr script to set this up, but here's a brief explanation. The script is implemented as a sub command that looks at the branch name, then builds and opens the relevant URL for us when the command is run. GitHub then redirects this request to either the PR (if one exists), or to the compare URL to open the PR.

Parity

The final tool we'll discuss for working with remotes is Parity. Parity wraps the heroku command, providing distinct executables for:

  • development
  • staging
  • production

Parity works under the assumption that you have the Git remotes staging and production, which point at the relevant Heroku instances. This allows the slightly wordy Heroku command:

$ heroku run console -r production

to be replaced with the more concise

$ production console

In this example, not only does Parity provide the production command to manage the -r production flag, but it also allows us to use the direct console as an alias for run console.

Parity also exposes a set of subcomamnds such as backup, restore, migrate, deploy, etc. that make working with our Heroku apps much easier.

Parity isn't specifically a Git utility, so we won't go into too much detail in this video, but do check out the Parity usage guide as well as the Heroku episode of The Weekly Information for more info.

Understanding Remotes

Tracking

Tracking branches, also known as "upstream branches", are branches that have a direct relationship to a branch on a remote such as origin. When working with these tracking branches, we can run the bare git pull or git push, and Git will automatically use the configured remote branch as the target.

If we check out our local branch based on a remote branch, then the upstream tracking will automatically be configured.

$ git branch --remote origin/remote-branch-name
$ git checkout remote-branch-name
$ git push
Everything up to date.

However, if you have a newly created local branch that does not yet exist on your remote server then you cannot simply push:

$ git co -b upstream-test
$ git push
fatal: The current branch upstream-test has no upstream branch.

Instead (as Git kindly points out in the error message for the above push command), you need to explicitly specify where to push, and instruct Git to set up tracking:

$ git push -u origin local-branch

The -u flag here is short for --set-upstream.

Lastly, if we want to push our local branch with an alternate name on our remote, we can do this using a slightly altered form:

$ git push -u origin local-branch-name:upstream-name

This is, of course, a prime candidate for automation, and my git-publish subcommand does just that.

Verbose Branch List

One way that we can see the data about our local and remote branches, as well as the relationships between them, is by using a specific form of the branch command:

$ git branch -vv

Running this causes Git to list out each of the local branches and the current commit they point at, as well as listing the upstream remote branch (if there is one).

Upstream Alias

Focusing on the current branch, we can ask Git specifically for the remote tracking branch. Unfortunately, the command needed to do this is a bit verbose, but luckily we have aliases to clean it up:

  # in the [alias] section of the ~/.gitconfig file
  upstream = rev-parse --abbrev-ref --symbolic-full-name @{upstream}

With this alias in place, we can run:

$ git checkout master
$ git upstream
origin/master

and Git will kindly display the name of the remote branch we are tracking.

Pushing Non-Master Branches To Heroku

In some cases, we'll want to deploy a feature branch to our Heroku staging instance without merging it into master. The complication here is that Heroku will only deploy the master branch. In order to deploy our non-master branch, we have to essentially push it up to staging, setting staging's master branch to point at our feature branch:

$ git push --force staging local-branch:master

Note: The use of --force pushing onto master here can be destructive. It should only be used on a throwaway environment like staging, and never with origin or production.

Fetchall Command

A typical application will have at least three remotes: origin (GitHub), staging (Heroku), and production (Heroku). If we're working with a team, we will quickly fall behind and need to fetch these remotes. The following alias automates fetching all of the remotes in one single command:

  # in the [alias] section of the ~/.gitconfig file
  fall = !for remote in $(git remote); do echo Fetching $remote; git fetch $remote; done

This uses a "bang alias" to loop over each of the remotes, running git fetch in turn.

Note git fetch actually accepts an --all argument that provides identical behavior to the above alias. That said, the above is still a useful example of a complex alias.