---
title: GitHub Workflow
date: 2014-12-27
description: A team workflow for GitHub PRs using interactive rebase, squash commits, and cherry-pick merges.
tags: [git, github]
---

## Introduction

This is a quick post to cover a GitHub workflow that is utilised by our specific team (Frameworks) here at BBC News. The basis of our workflow is this:

- Open a GitHub PR (Pull Request) by creating a new feature branch from `master`
- Make feature specific changes and request a code review
- If given a "thumbs up", this means the PR author is allowed to handle merging the PR
- The merge process requires a set of sub steps (see below)

## Rebase before merge

At this point the PR author has been given a "thumbs up" and is preparing their PR to be merged back into the `master` branch. The steps (in summary) are:

- Interactively rebase `master` onto the feature branch
- Squash all feature commits into a single commit:
  - we'll see an example of this later (as this isn't *exactly* a `squash`)
  - the first line of the commit message should be the same as the GitHub PR title
- As part of the rebase ensure:
  - the author(s) is mentioned in the commit message
  - the PR is mentioned (and any associated issues as well)
- Move back to the `master` branch and `cherry-pick` in the newly squashed feature commit

## Example

Below is an example set of commits we'll be working from. I create a master branch and then branch off from that with a new `feature` branch:

```
git init
touch test.txt
git commit -am "Initial file"

[master (root-commit) 85919e1] Initial file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test.txt

git checkout -b feature
Switched to a new branch 'feature'

echo foo >> test.txt
git commit -am "Foo"

echo bar >> test.txt
git commit -am "Bar"

echo baz >> test.txt
git commit -am "Baz"

# Check commits we now have in this feature branch
# Note: this is a custom shell alias
git lg

* 62d4c80 - Baz (HEAD, feature)
* a5827db - Bar
* ae1a4a5 - Foo
```

At this point, let's imagine our `feature` PR has been approved to be merged:

```
# Make sure master is up to date
git checkout master
git pull --rebase origin master

# Carry out the interactive rebase
git checkout feature
git rebase -i master
```

Now at this point you should see something like the following in your terminal:

```
pick ae1a4a5 Foo
pick a5827db Bar
pick 62d4c80 Baz

# Rebase 85919e1..62d4c80 onto 85919e1
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```

We're now ready to modify our git history. So let's squash all our commits down into a single commit:

```
reword ae1a4a5 Foo
fixup a5827db Bar
fixup 62d4c80 Baz
```

> [!NOTE]
> we don't use `squash` as that automatically uses the existing commit message from the commit we're squashing other commits into (but our requirements mean we wish to modify that commit message); so we use `reword` and `fixup` instead

Let's apply these changes by executing `:wq`. Once you do this, Git will carry out the rebase and then drop you back to the `COMMIT_EDITMSG` screen. You can now modify the commit message so it is the same as the title of your GitHub PR (and you can inform GitHub of what PR to automatically close when this commit arrives in `master` by using the keyword: [closes](https://help.github.com/articles/closing-issues-via-commit-messages/); you'll notice there is the keyword `fixes` which indicates a GitHub issue to close).

```
New Feature X

Closes #1 and Fixes #11
Authors @integralist @stevenjack
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sat Dec 27 16:19:54 2014 +0000
#
# rebase in progress; onto 85919e1
# You are currently editing a commit while rebasing branch 'feature' on '85919e1'.
#
# Changes to be committed:
#    modified:   test.txt
#
```

We can now see (when executing `git log`) that the three individual feature commits are now a single commit with a commit hash of `68f5bee`. We can move back to `master` (i.e. `git checkout master`) and then `cherry-pick` the squashed commit into it:

```
git checkout master
git cherry-pick 68f5bee
git branch -D feature
git push origin master
```

## (Bonus) Modifying content within an interactive rebase

One aspect of carrying out an interactive rebase that seems to confuse a lot of users is the ability to edit the content of a specific commit.

I think the reason being is that when you select a commit to `edit`, the interactive rebase drops you at that specific commit, so you'll find there aren't any files in the staging area.

To make edits at that point you need to undo the commit, so the files end up back in the staging area, ready to be modified and a new commit made.

Let's take a look at an example, using our earlier example:

```
git lg

* 76c99a2 - New Feature X (HEAD, master)
* 85919e1 - Initial file
```

Let's say we want to make an edit to the commit `76c99a2`. To do that we'll need to start up an interactive rebase like so (we specify the commit before the one we want to edit):

```
git rebase -i 85919e1

Stopped at 76c99a27e64b5f749ac8e3d3c7032f53954c760a... New Feature X
You can amend the commit now, with

        git commit --amend 

Once you are satisfied with your changes, run

        git rebase --continue
```

At this point we want to execute the following command, which will undo the commit (but keeps the changes from that commit) and place the files back into the staging area:

```
git reset --mixed 85919e1

Unstaged changes after reset:
M       test.txt
```

Now if you check the diff on the files you'll see the changes from that commit have been made and are waiting to be committed again:

```
git diff

diff --git a/test.txt b/test.txt
index e69de29..86e041d 100644
--- a/test.txt
+++ b/test.txt
@@ -0,0 +1,3 @@
+foo
+bar
+baz
```

So you can now modify the `test.txt` file as necessary and create a new commit. When you create the new commit you finish the rebase by using the `--continue` feature (as seen in the above output when initially starting the rebase):

```
echo qux >> test.txt
git commit -am "New Feature X"
git rebase --continue
```

> [!NOTE]
> if you forget where you are then running `git status` should give you the information you need to help you either continue rebasing or to abort the rebase
