In my git workflow we have one main repository and one branch, master. Everyone pulls from remote master and everyone pushes to remote master. I want to work in my own branch while I prepare a feature. So far my history is something like this:
git pull --rebase
git checkout -b new_feature
<make some commits>
git checkout master
git pull --rebase
Now I want to merge the branch and here's what I need:
- No merge commits in my local master branch.
- All commits made into my new_feature branch merged into master as if I had made them in master.
- All merged commits to be merged somewhere on top of my local remote head pointer.
My biggest concern is item 3, when is needed so that I can safely push the changes. If the merged commits are intertwined with commits before head then I will have problems pushing, see related problem I had: git: Pushing Single Commits, Reordering with rebase, Duplicate Commits.
I've read the following:
- http://mettadore.com/2011/05/06/a-simple-git-rebase-workflow-explained/
- Keep commits history after a 'git merge'
- How do you rebase the current branch's changes on top of changes being merged in?
And I think I need to do:
git checkout master
git pull --rebase
git checkout new_feature
git rebase master
git checkout master
git rebase new_feature
git push
My understanding is that
git checkout new_feature
git rebase master
will make new_feature appear as if it was branched off from the new current head. Is that true? And that
git checkout master
git rebase new_feature
will place new_feature on top of master. Is that correct? If so, this is the main point of my confusion. If "git rebase master" places master commits at the bottom of new_feature, then why does "git rebase new_feature" place new_feature commits at the top of master, i.e. why doesn't it do the opposite?
Answer
Here is a workflow that you can use that does just what you need it to do.
git checkout master
git pull --rebase (1)
git checkout new_feature
<do a bunch of commits>
git rebase master (2)
git checkout master
git merge new_feature (3)
git branch -D new_feature (4)
Explanation
(1) git pull --rebase
will first fetch origin/master
and then replay your local master
on top of it. Note in the example log that your local commits are on top of your "local remote HEAD pointer."
> git log --oneline --all -10 --decorate
d34d34c (HEAD, master) Local commit message.
d3434r2 Local commit message.
d234d4c Local commit message.
er3ede3 (origin/master, origin/HEAD) Remote commit message.
sfe3fd3 Remote commit message.
You can now checkout
and work on your new_feature
branch for a while. When you're done...
(2) git rebase master
will replay new_feature
on top of master
. Again, your local commits remain on top of your "local remote HEAD pointer."
> git log --oneline --all -10 --decorate
fc5773d (new_feature) Local new_feature commit.
9282838 Local new_feature commit.
d34d34c (HEAD, master) Local commit.
d3434r2 Local commit.
d234d4c Local commit.
er3ede3 (origin/master, origin/HEAD) Remote commit.
sfe3fd3 Remote commit.
The rebase
command just put new_feature
ahead of master, and to align them you need to run...
(3) git merge new_feature
, which will do a fast-forward merge. Now HEAD
, new_feature
, and master
all point to the same commit.
> git log --oneline --all -10 --decorate
fc5773d (HEAD, new_feature, master) Local new_feature commit.
9282838 Local new_feature commit.
d34d34c Local commit.
d3434r2 Local commit.
d234d4c Local commit.
er3ede3 (origin/master, origin/HEAD) Remote commit.
sfe3fd3 Remote commit.
(4) After that, you can safely delete the new_feature
branch. Your final log before pushing will look like this:
> git log --oneline --all -10 --decorate
fc5773d (HEAD, master) Local new_feature commit 2
9282838 Local new_feature commit.
d34d34c Local commit.
d3434r2 Local commit.
d234d4c Local commit.
er3ede3 (origin/master, origin/HEAD) Remote commit.
sfe3fd3 Remote commit.
It's unnecessary to run git rebase new_feature
on the master branch after you've run git rebase master
on the new_feature branch. After you've run git rebase master
on the new_feature branch, you can then merge new_feature into master - it will be a fast-forward merge and won't introduce a merge commit.
The reason why git rebase new-feature
isn't playing all the new-feature commits on top of master is because git recognizes master already is at the base of new feature - we performed that step with git rebase master
- and that it would just be rebasing on itself. So instead it just fast-forwards to new-feature.
Also, you don't need to worry about pushing commits that reside below your remote/master tip -- the remote will reject your push should you try (unless you provide the -f option, which, don't). And, if your local master is tracking your remote master, git status
will tell if your local has diverged from you remote branch.