Often when working on a feature branch, I'll make a commit or commits that are useful outside the immediate scope of work for that feature. And at the same time, that feature branch needs more time to get finish, but I need to get the commit(s) into master either to ship them or to help other feature branches.
Here's a flow I use to get those commits merged to master without trying to fast-track the original feature branch.
In this scenario I have a feature branch named new-feature-a
. I've made a handful of commits related to feature-a
and two that are useful for feature-a
, but also useful for other work. I want to get those two into master
now before I'm ready to merge feature-a
.
Make sure I have the latest master out of habit
git checkout master
git pull
Create a new branch from master
git checkout -b useful-commits
Now I use git cherry-pick
to grab the commits from the new-feature-a
branch and put them in the useful-commits
branch. First, I need to know the commit hashes of the commits I want. I find the commit hashes either using git log
or by looking at the commit history of the new-feature-a
branch. Both give me the same info, it just depends on how my brain is working.
To use git log
, I check out the feature branch and view the log:
git checkout new-feature-a
git log
In the log I find the two commits I want and copy paste their commit hashes somewhere for later, usually just textedit. I'm not fancy. For these purposes we'll pretend the hashes are:
123456
789012
Now, hop back to the useful-commits
branch to cherry pick the commits:
git checkout useful-commits
git cherry-pick 123456
git cherry-pick 789012
Note: git cherry-pick
does accept a range of commit hashes, but I still do them one at a time just out of habit.
After I cherry pick, I do a quick check of git log
on the useful-commits
branch to make sure the commits are in the log as expected.
I now have the useful-commits
branch where I want it, it's ready for merge. I follow a standard PR/review/merge to master
process. On merge, I promptly delete the useful-commits
branch using the Github UI because the branch is merged so no longer needed and omg please delete your branches when you merge to master.
The useful commits are now in master
. Anybody else with feature branches can rebase to master
or merge master
into their branches and they'll have access to those useful commits. new-feature-a
is in a slightly different state but we'll still use rebase
to reconcile.
Again, make sure I have latest master and clean up a bit:
git checkout master
git pull --prune
git branch -D useful-commits
Things may be a little odd here depending on your commit history. In master
we've now made the changes to the files we want with useful-commits
. Those are in master under whatever commit hash they ended up the the PR merge. But we also have those exact same file changes in new-feature-a
with different commit hashes. Notice I'm differentiating between "file changes" and "commit hashes". That's what makes this a little different. It may not cause any trouble, but it's good to understand in case you run into rebase conflicts or other git weirdness.
In this scenario, we've continued to work on new-feature-a
and at the same time master
has had a number of new merges made to it. We want to catch new-feature-a
branch up to master:
git checkout new-feature-a
git rebase master
Again, this will be different depending on commit history, but in my case, the rebase completed without conflict. With rebase
you will likely see a message like:
Your branch and 'origin/new-feature-a' have diverged,
and have 7 and 8 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
That's OK, but know that diverged commits mean you'll need to force push. Because the local new-feature-a
is now gospel. If multiple people are working on new-feature-a
let them know you're about to force push so you all can coordinate and nobody loses work/half a day with git conflict madness.
git push -f
A good thing to look at to understand what we've done with this cherry-pick and rebase is to: git log
. You should notice that the useful commits from before 123456
and 789012
are no longer in the log for this branch, but should be at an earlier state in the log under a different commit hash from where we merged them into master. Neat! We're rewritten history with rebase
.