Moving changes on a Git branch
This page describes what to do if you are developing changes on a branch and need to move them to a different branch using Git
Contents
Scenario
Take the following example: I was working on some changes to the dist/makerelease.xml file and doing this on my local copy of the master branch. But in the end, these changes actually need to go into all of our active branches. Which means, if I'm going to follow our Git Workflow I need to first commit them to the lowest supported branch first. But... that's not where I committed them! Oh no!
[Never fear]
Starting Out
Here's a snapshot from gitk showing the tree where I started. Note that my master is ahead of the upstream origin/master. It is important you don't do anything on this page with changes that have been published! It's important. So I'll repeat it. In bold. It is important you don't do anything on this page with changes that have been published!
First we made a common mistake
We didn't do our changes in the right place (V5-4-patches) or, even better, in a new special branch for the new work (maybe "really-cool-new-release-script"). Ehh... Who cares. We can do this now by creating the new branch:
# git checkout -b really-cool-new-release-script Switched to a new branch 'really-cool-new-release-script'
Results in:
Now we need to reset the master to get rid of all the changes to it (yeah, they'll just be merged in again later and this is silly, but...)
# git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 7 commits. # git reset --hard remotes/origin/master HEAD is now at 8e08fc5 Tweak RPM handling to work with library version 4.9
This results in removing all the changes from the master branch (--hard means 'don't even save them; discard them entirely):
Rebasing changes somewhere else
Now that we have the changes properly placed into a new branch, we can move the whole branch to rebase it (relocate it) somewhere else. Like where we want the branch to move to. We do this with git rebase and the --onto flag:
# git checkout really-cool-new-release-script Switched to branch 'really-cool-new-release-script' # git rebase --onto V5-4-patches master really-cool-new-release-script First, rewinding head to replay your work on top of it... Applying: pull the branch info from git Applying: make the branch echo output and svn update converted to git equivalents Applying: remaining command line changes for svn -> git Applying: A script to invoke the correct version of the autoconf tool Applying: use the new run-autoconf script Applying: use git2cl to generate the ChangeLog file; needs much more work. Applying: xml fixes
Which actually moves all the changes (one at a time) to the V5-4-patches tree (it would have stopped us in the middle if it failed because of a conflict, but it didn't):
Now we can "properly" apply them
Now that they're hanging off the right branch, we can apply them directly to the branch:
# git checkout V5-4-patches Switched to branch 'V5-4-patches' # 'git merge --ff really-cool-new-release-script Updating af8b235..df9cf9a Fast-forward dist/makerelease.xml | 76 +++++++++++++++++++++++-------------------------- dist/run-autoconf | 32 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 40 deletions(-) create mode 100755 dist/run-autoconf
Deleting the unused branch
# git branch -d really-cool-new-release-script Deleted branch really-cool-new-release-script (was df9cf9a).
Rolling Upward
Now that we have the changes applied on the right branch, we can apply them to all the active branches using the nsrollup shell macro:
# nsrollup [Long merging output deleted]]
Resulting in:
And Pushing the Whole Mess Forward
# 'git push --all Counting objects: 50, done. Delta compression using up to 2 threads. Compressing objects: 100% (39/39), done. Writing objects: 100% (39/39), 5.22 KiB, done. Total 39 (delta 29), reused 0 (delta 0) To ssh://hardaker@net-snmp.git.sourceforge.net/gitroot/net-snmp/net-snmp af8b235..df9cf9a V5-4-patches -> V5-4-patches d588ec8..d39194b V5-5-patches -> V5-5-patches 127eec2..64b6c68 V5-6-patches -> V5-6-patches 8e08fc5..a308656 master -> master
But but but...
You made that too complex! You didn't actually need to create a new branch!!! You cheater!'
Yeah, I did. But you learned more this way, like how to move changes to a branch, etc. And it's conceptually a bit easier than dealing with change sets.
And what's with the moving them and then just reapplying them later?
Well, technically it wasn't necessary. But it showed good practice by applying them where they should go and then merging upward. Either way, the resulting files were the same. You're right.
Exercises left to the reader
- Do the above without creating a new branch
- Do the above, but with only a subset of the changes, in the middle of course (hint: git cherry-pick)