I've never had a very good grasp on how I should branch my open source libraries. There are a lot of resources out there detailing how to branch and tag projects (git-flow, GitHub flow, Trunk based development, ...), but these don't map very well to distributed libraries.
This is the blog post I wish I had when I was searching for a good workflow for branching and tagging open source packages/libraries.
This workflow is similar to what I've observed other packages/libraries/frameworks follow. It is not meant to be a definitive guide to how you should do it, but serves mainly as a reminder and guide for myself. Ok, let's dive in.
First off, every repository has a
main branch. This is always the next major version of the library, and therefore
also where all the development happens before a version is tagged.
Tagging the first version
When you're ready to tag your first major version (
v1.0.0), first create a branch called
1.x. This is where you'll
then tag your first version from.
$ git switch main $ git pull origin main $ git switch -c 1.x $ git push origin 1.x $ git tag -s v1.0.0 -m 'Version 1.0.0' $ git push origin v1.0.0
Be sure to also make
1.x the default branch on GitHub.
From this point on,
main is for
v2.0.0 development. I would now also stop directly committing to any of these
branches and work via pull requests instead. These pull requests most often target
1.x, and are ported over to
For instance, say you have a new feature without breaking changes in the works. The pull request for this feature will
1.x so it lands in
v1.1.0. Once approved and merged into
1.x, you'd merge that change into
$ git switch 1.x $ git pull origin 1.x $ git switch main $ git pull origin main $ git merge 1.x --ff-only $ git push origin main
You could also facilitate this merge from the GitHub UI by using the "compare" page. Append
/compare/main...1.xto your repository's URL, and you'll see a button to create a pull request to merge the changes from
Tagging new minor and patch versions
v1.1.0 is ready to be released, tag it from the
$ git switch 1.x $ git pull origin 1.x $ git tag -s v1.1.0 -m 'Version 1.1.0' $ git push origin v1.1.0
Two point oh and beyond
When you introduce a breaking change in one of your pull requests, you should target it to the
main branch. The moment
you're ready to tag
v2.0.0, you create a new
2.x branch and tag the next major version from there. This is pretty
much the same as tagging the first version.
$ git switch main $ git pull origin main $ git switch -c 2.x $ git tag -s v2.0.0 -m 'Version 2.0.0' $ git push origin v2.0.0
Remember to also update the default branch to
2.xat this point.
Bug fixes for previous versions
If a bug fix comes in that should be applied to
1.x (or other previous versions), it should be targeted to
the latest branch where the bug is present. I will assume
2.x in this case. Once the bug fix is merged into
can cherry-pick the commit(s) into the other affected version(s):
$ git switch 2.x $ git pull origin 2.x $ git switch 1.x $ git cherry-pick <commit sha> # Or, if you want to cherry-pick multiple commits: # git cherry-pick <first commit>^..<last commit> $ git push origin 1.x $ git tag -s v1.x.y -m 'Version 1.x.y' # Where 'y' is incremented $ git push origin v1.x.y $ git switch main
I considered a strategy where the
main branch is always the current latest version, but that resulted in more
forgettable administrative work. Besides, what I outlined above is actually what Laravel does (as far as I could infer),
and it's similar to what Symfony follows.
So we're in good company.
I hope now that I've written this down, I can actually follow this in my own projects. This should ease the process of contributing to my packages, and help me in being less confused when I need to tag a new release.