I understand most of the basic Git/Github concepts by now, however I still have trouble understanding the bigger picture.

These are some things that I have managed to get working so far:

  • Push commits
  • Work with branches
  • Integrate Github with Travis CI, a continuous integration system
  • Via Travis CI, automatically build on every commit to master and put the release as a ZIP on Github under releases.

However I have only worked on alpha/beta versions of projects so far, so I've never seen versioned releases in practice yet.

Thus I want to learn more about versioning, maintaining separate versions, hotfixing the versions, etc.

How would I ensure that the following things happen:

  • Have different versions of my project, for example version 1.1.0 and 2.0.0
  • Have the ability to push hotfixes on the versions, sort of bumping the version to 1.1.1 or 2.0.1, etc.
  • Make a continuous integration system build that version automatically on commit and if succeeds, then publish a release for that specific version.

I am doubting between the following options:

  • Do I need to use tags for every version? If so, how can a continuous integration system build releases automatically?
  • Should I create branches for every version? If so, would that not create a whole ton of branches (like an 1.1 and a 2.0 branch, hotfixes go onto that branch of course)
  • How would I specify the version number? Is it okay to have a configuration file that specifies the version number, or are there smarter ways around it? In this case it would be a Java project, if that matters.

4 Answers 4


You should look at git-flow. It's an excellent (and popular) branching model.

Git Flow Summary


The main trunks that stay around forever are develop and master. master holds your latest release and develop holds your latest "stable" development copy.

Contributors create feature branches (prefixed with feature/ by convention) off of develop :

$ git checkout -b feature/my-feature develop

and hotfix branches (prefixed with hotfix/ by convention) off of master:

# hotfix the latest version of master
$ git checkout -b hotfix/hotfix-version-number master

# or hotfix from a specific version
$ git checkout -b hotfix/hotfix-version-number <starting-tag-name>

These branches are "disposable", meaning they have a short lifespan before they are merged back to the main trunks. They are meant to encapsulate small pieces of functionality.

Finishing Branches

When a contributor is done with a feature branch, they merge it back into develop:

$ git checkout develop
$ git merge --no-ff feature/my-feature
$ git branch -d feature/my-feature

When they're done with a hotfix branch, they merge it back into both master and develop so the hotfix carries forward:

$ git checkout master
$ git merge --no-ff hotfix/hotfix-version-number
$ git checkout develop
$ git merge --no-ff hotfix/hotfix-version-number
$ git branch -d hotfix/hotfix-version-number

This is the continuous integration aspect.


When you're ready to start packaging up a release, you create a release branch from your "stable" develop branch (same as creating feature branches). You then bump the version number in a tag (described below).

Using separate release branches allows you to continue developing new features on develop while you fix bugs and add finishing touches to the release branch.

When you're ready to finish the release, you merge the release branch into both master and develop (just like a hotfix) so that all your changes carry forward.


When you create a release branch or a hotfix branch, you bump the version number appropriately in a tag. With vanilla git, that looks like this:

$ git tag -a <tag-name> -m <tag-description>

You'll then also have to push the tags (separately) to your remote repository:

$ git push --tags

It's usually best to use semantic versioning in which your versions take the form major.minor.hotfix. Major bumps are backwards incompatible, whereas minor and hotfix bumps are not backwards incompatible (unless you're in beta, 0.x.x).


As you saw above, git-flow encourages you to merge branches with the following command:

$ git merge --no-ff <branch-name>

The --no-ff option allows you to maintain all of your branch history without leaving a bunch of branches lying around in the current commit of the repository (so no worries, you won't have a branch for every version).

You're also encouraged to pull with

$ git pull --rebase

So you don't add lots of useless merge commits.

You can configure git to do both of these things by default in your .gitconfig. I'll let you look that one up though ;)

Browsing versions

When someone is looking for a specific version of your codebase, they can checkout the tag by name:

# checkout in detached HEAD to browse
$ git checkout <tag-name>

# OR checkout and create a new local branch (as you might for a hotfix)
$ git checkout -b <new-branch-name> <tag-name>

Or, if someone is browsing on github, there is also a "tags" tab in the "branches" dropdown.

Using the git-flow extension (recommended)

My favorite way to use this model is with the git flow extension for git.

(Edit: Louis has recommended the AVH fork which works better with git describe and might be more active now. Thanks Louis. )

The extension automates all the messy parts (like using merge --no-ff and deleting branches after merging) so that you can get on with your life.

For example, with the extension, you can create a feature branch like so:

$ git flow feature start my-feature-name

and finish it like so

$ git flow feature finish my-feature-name

The commands for hotfixes and releases are similar, though they use the version number in place of a branch name, like so:

# Create hotfix number 14 for this minor version.
$ git flow hotfix start 2.4.14

# Create the next release
$ git flow release start 2.5.0

Git flow then creates the version tag for you and kindly reminds you to bump the version in any configuration or manifest files (which you could do with a task manager like grunt).

Hope that helps :) I'm not sure exactly how you'd integrate it all with your Travis CI setup, but I'm guessing githooks will get you there.

  • When starting a release branch, do you use the same literal string 'release' as the branch name for each release, or something version specific like 'v0.3.0'? These instructions are excellent and I'll be attempting to follow them to the letter, but I don't want to mess up this aspect of it.
    – Paul
    Commented Feb 10, 2017 at 3:48
  • 1
    Using the git flow plugin command, you would put the version identifier, like v0.3.0, in for <release> git flow release start <release> [<base>]. Under the hood, it will create a branch name including the version, like release/v0.3.0.
    – mxdubois
    Commented Feb 11, 2017 at 2:29

Do I need to use a tag for every version?

No, you don't need to use tags at all. If you want to tag every release, that's fine, or if you want to tag every single time your CI system builds, you could do that too. Tags are essentially just giving a user friendly name to the commit, so that you can easily pull it up and view it later.

Should I create branches for every version?

Sure! Branching is cheap/free in Git, so I take advantage of it every chance I get. You can also merge in and delete branches fairly quickly as well. If you feel that you have to many branches you can always pare them down later with some selective merging. There are a slew of Git branching schemes available as well if you want to use a tried and true scheme.

How would I specify the version number?

Tags are usually the way you specify version number, as it pertains to git. If you're talking about how to version a project, or the best way to do that, you're going to have to do some digging, as it's a fairly opinion based question. Some projects stay in Beta forever, others increment whole number versions like they're going out of style (Looking at you chrome)


Do I need to use tags for every version?

If by "version" you mean a set of files that make up a release or a release candidate, then I strongly recommend tagging every version. If you need to refer to version 1.2.7 down the road, do you want to hunt for a commit's hash or just use the version number?

Also if you use git describe to record build information somewhere (as I do), then using tags allows it to provide much nicer output.

If so, how can a continuous integration system build releases automatically?

A continuous integration system could build releases irrespective of how you use tags. You could tell it to build a release on the basis of a commit's hash. Tags make your life easier.

Should I create branches for every version? If so, would that not create a whole ton of branches (like an 1.1 and a 2.0 branch, hotfixes go onto that branch of course)

I don't see branching as being a "per-version" thing. I have a couple projects where my versions are all commits on the master branch. I don't need anything more complicated than this for now because neither project is at the stable stage and there is no need to support older versions long term. But let's say I release 1.0, 1.1, 1.2, then I release 2.0 and I still have to support the 1.0 series with security fixes, etc. Then I'd certainly have a branch to put the maintenance releases on for the 1.x series.

How would I specify the version number? Is it okay to have a configuration file that specifies the version number, or are there smarter ways around it? In this case it would be a Java project, if that matters.

Having a single source for your version number, like a configuration file, is the best way as it prevents fat finger errors that could otherwise occur if you have to update numbers in a number of places. I'm speaking from... hmm... embarrassing experience. You release 1.3 only to find that the software still reports that it is version 1.2. Oops!

In another answer, mxdubois recommended gitflow to you. If you do decide to use gitflow, I'd recommend using the AVH edition. The original version is no longer actively maintained. One notable difference is that the AVH edition performs release merges that allow git describe to work intelligently. The original version performs the merge in a way that trips git describe.


Scanning your list I see version as your focus, so...

One way to maintain versions is with branches and merging (or rebasing).

So you have:


then you create a branch


then you add more changes to


then you create a branch


then you add more changes to



To update Version 2 you now do

git checkout v2
git merge master  # for the changes you want to bring into version 2
# rebasing is also an option
# resolve any merge conflicts
# Done.

To update version 3

git checkout v3
git merge master

The above is for wholesale updates.

It's probably more likely though that you will want to pick out specific changes for that there is

git cherry-pick

More on cherry picking at http://git-scm.com/docs/git-cherry-pick

Not the answer you're looking for? Browse other questions tagged or ask your own question.