Let's break apart your question into two parts:
how can I go back in time, [to] produce a fix for an older version ?
Your question mark in your second diagram is exactly right for this. Simply find the tag of the release you wish to modify, branch off of it (perhaps naming it something like hotfix/1.1.1
), and commit your new changes. Then tag that "release".
For your second half of the question:
how can I ... merge it back in the trunk ?
And especially what if:
I do not want that fix to be brought back in the trunk ?
If you do wish to integrate that hotfix into trunk, it's easy- just merge it in (and if you have conflicts resolve them as you normally would).
If you don't wish to integrate that hotfix into trunk, you have some options:
- Keep your
hotfix/1.1.1
around indefinitely. Branches are super cheap and this is a valid strategy that many companies use. (In fact many companies do away with the master
branch altogether in a Git-Flow-Like strategy and just keep their release/
branches around indefinitely.) If you go this route you might someday have many remote branches, in which case I would recommend using the pseudo directory slash character (/
) in your branch name as suggested with hotfix/1.1.1
. Many Git clients enable you to roll up your branches by pseudo directory so you don't have to look at hundreds of branches that don't interest you (by collapsing the hotfix/
directory in the view.)
- My personal preference is not to keep lots of hotfix branches around forever, so instead you can get a little fancy and merge the hotfix branch back into trunk, but tell Git to ignore the change. After doing this you can delete the hotfix branch since all the information is contained in the new tagged commit in trunk.
Here's how to merge a branch without taking the changes:
git fetch
git branch -D main # in case you have an old copy of it
git switch main # now you should be up to date with origin/main
git merge --strategy=ours hotfix/1.1.1 # bring in commits without their changes!
Note the merge with strategy ours has no effect on the working copy. It's sole purpose is to bring the commit(s) from the hotfix branch in so that you don't need to maintain the hotfix branch anymore; you can simply delete it at this point.
Tip: Please be kind and use a descriptive commit message explaining that you did this and why!
You didn't ask this, but from my experience it's bound to come up eventually:
What if I want to integrate some of changes from the hotfix and not others?
That's a fantastic question! There are multiple ways to skin this cat, and before I learned about merging with the ours strategy, I would simply do the merge with the --no-commit
flag, manually undo the stuff I didn't want to keep, and then commit the merge. The downside of this is that you are at the mercy of trusting that the merge commit was done correctly, and it's potentially hard(er) to troubleshoot. If you go this route, a descriptive commit message explaining what you did and why is important here.
My current preferred way of handling this is to split up the hotfix changes into 2 parts (typically 2 commits but it could be more). Make sure all the commits you want to integrate are first on the hotfix branch, and then the commits you don't wish to integrate come later. (Most likely you can use rebase -i
to re-order them if needed after the fact, as long as it's before you tag it and release it.) The next steps are pretty straight forward:
git fetch
git switch -c merge-hotfix-into-main origin/main --no-track # create a temp branch
git merge <last-commit-you-want-to-integrate> # take in commits and changes
git merge --strategy=ours hotfix/1.1.1 # bring in remaining commits without changes
git branch -D main # in case you have an old copy of it
git switch main #
git merge merge-hotfix-into-main --no-ff
Note, the temporary branch merge-hotfix-into-main
isn't actually needed here, but to stay with the spirit of --no-ff
merges into main
as defined by Git Flow, every first parent commit into main
should represent a release, and that wouldn't be true unless the temp branch and it's 2 merge commits get brought in with a single merge commit on main
.
Side Note: This is a minor point, but your first diagram appears to be violating one Git Flow principle, mainly, that tagged releases aren't making their way back to develop
quick enough. Typically you'd have your blue dots pointing back to develop
right away. You can see 3 blue dot releases that aren't in develop
yet.