Back in 2010, Vincent Driessen wrote a post called
A successful Git branching model.
Besides being extremely well drafted and clear in it's intention, the post goes
into great detail about Git branching strategies and release management. The
model that Vincent (sobriquet "nvie") came up with was popularized as git-flow
.
Since then, nvie has published a set of Git extensions to provide high-level
repository operations for his branching model used on the CLI via an executable
named, unsurprisingly, git-flow
. It creates
abstractions for the creation and completion and publication of everything he
talks about in the blog post, namely, feature, release, and hotfix branches.
You can read a full list of the command line arguments for git-flow
here. While this
extension does create a nice abstraction to the principles of Git flow, the DSL
may stifle some people's ability to handle a complicated situation which may
require basic git
commands.
Thankfully, git-flow
does not interfere with vanilla git
to accomplish
the same thing, which is what we're going to talk about here.
This post is aimed at explaining how to work on a feature branch within this company. For release and feature branches, please consult the blog post mentioned above, as it does a fantastic job of explaining their purpose and implementation, which we are following as it is laid out in the post.
May branch from: develop
Must merge back into: develop
Branch naming convention: feature/GLOB-<JIRA-ticket-number>
Feature branches will make up the large majority of what developers work on. They are also the most understood branch type, and are used to develop new features for a future release.
So you're about to start on your new feature. Most likely this is associated to
a JIRA ticket for a bug or story. For this example, let's say that ticket is
GLOB-1234
.
NOTE: If you have any unsaved local changes you'd like to move to the feature
branch at this time, you can prepend the following steps with a stash of your
local changes by running git stash
git checkout develop
Switched to branch "develop"
git pull origin develop
Pulled changes from remote branch "develop"
git checkout -b feature/GLOB-1234
Switched to a new branch "feature/GLOB-1234"
NOTE: If you had stashed your changes before executing the list of commands
above, you can now properly bring those back into existence with git stash pop
.
Work on your feature, make your commits. The convention is to prepend your commit message(s) with the JIRA ticket number for the feature you are working on.
git commit -m "GLOB-1234 - <commit message here>"
Then you need to do a few things before creating your pull request.
Firstly, try to squash your commits down to a reasonable number. This number may vary depending on the needs of the team, but for a feature it would be advisable to have no more than two commits; a bug really shouldn't be more than a single commit.
You want to make sure you are up to date with the current develop
branch. Since
you started work on your feature, other bug fixes and features from other
developers may have been merged back into develop
that you do not have locally.
git checkout develop
Switched to branch "develop"
git pull origin develop
Pulled changes from remote branch "develop"
Now that your local develop
mirrors the remote develop
, you will want to
rebase your feature changes on top of develop
. This will create a cleaner,
more linear history.
git checkout feature/GLOB-1234
Switched to branch "feature/GLOB-1234"
git rebase develop
First, rewinding head to replay your work on top of it...
Applying: added staged command
You can condense the above two commands into a one-liner if you'd like:
git rebase develop feature/GLOB-1234
If you happen to run into merge conflicts when rebasing, it will "pause" the rebase at that point in history until you clean up the conflicting changes. After you have examined and fixed the files that had conflicting code, you can add that file(s) back into the ("paused") rebase and tell Git to continue rebasing.
git add <some-file-that-had-conflicts>
git rebase --continue
This cleanup may take multiple continuation steps depending on how many conflicts there are and how many commits are being rebased on top of.
NOTE: I am assuming that our release and hotfix branches work as they are
described in the git-flow
blog post, which uses normal git
commands itself.
If we are tweaking the process for release or hotfix branches, please explain in
which ways our management is different by either leaving a comment in this file
or editing it yourself.
nvie's post is remote agnostic: Git flow can be used regardless of the team's choice of remote storage. Our team uses GitHub to manage this.
Since Git and GitHub are bffs, GitHub has developed hub
, a command line
tool that wraps git
in order to extend it with extra features and commands
that make working with GitHub easier. We will be using this throughout the
tutorial.
To install hub
on OS X:
brew install hub
Follow hub
's installation page if you are running
on another platform.
Some people advocate for making hub
and git
aliases in your terminal. I would
advise against this. While I am a huge proponent of using GitHub for all your Git
remote storage needs, aliasing it creates an unnecessary bind between the two
commands. It also gives git
more power than it normally has, which can get you
into trouble if you don't use it carefully or forget you've aliased it. In some
sense, it's like traversing around your shell as root
, negating the requirement
to prepend sudo
when running high-level or dangerous commands. Since hub
is
a wrapper around git
, you could just use hub
if you really wanted to
stick with only one executable.
hub
adds a lot of shiny new features and extensions to normal git
commands,
from cloning and forking remote GitHub repositories to creating pull requests to
browsing the repository on the web to creating a remote repository; for a complete
list of documentation just run man hub
. For the purposes of this tutorial, We
are only going to run through the creation of a pull request and browsing it on
the web.
hub pull request
- This is the most popular reason for people downloading hub
.
It allows you to create a GitHub pull request directly from the command line.
It comes with some flag options that we want to take advantage of since we are
following the Git flow model, namely, we should be creating our pull requests
against develop
instead of master
. To accomplish this we can use the -b
flag, which stands for BASE
. We can also open up the browser upon our creation
of the pull request with -o
or --browse
. In its entirety, our command would
look something like this.
hub pull-request -b develop -o
The default head of the pull request is the current branch. You can specify a
different head with the -h
flag and then passing in the branch you would like
to use as head.
You can also use the -m
flag which is similar to the git
-m
flag in that
it allows you to include a title for your pull request from the command line.
hub pull-request -b develop -o -m "<PR title here>"
Without the -m
flag, a text editor will open in which title and body of the pull
request can be entered in the same manner as git commit message. By default, Git
uses whatever you've set as your default text editor ($VISUAL
or $EDITOR
) or
else falls back to the vi editor to create and edit your commit and tag messages.
To change that default to something else, you can use the core.editor
setting:
git config --global core.editor atom
Whichever implementation of hub pull-request
you choose, the end result will
be the same. You will have created a new pull request against develop (-b develop
), with a PR title (-m "<PR title here>"
), and opened that pull
request in the browser (-o
).
This may seem like a lot of overhead to create a feature and get it merged, but
understanding the commands being used in DSLs like git-flow
is a very useful
skill to have.