nowox
nowox

Reputation: 29066

Git rebase instead of merge, the correct way to do it?

I would like to avoid recurrent branches intersections on my remote repository by using a rebase instead of a merge.

To give you a better understanding of what I would like to achieve, please consider the following situation:

$ git lg
* 2345678 hotfix (HEAD -> master)
* 1234567 foo (origin/master, origin/HEAD)

$ git push 
! [rejected]  master -> master (fetch first)

$ git fetch
$ git lg 
* 2345678 hotfix (HEAD -> master)
| * 3456789 other change (origin/master, origin/HEAD)
|/
* 1234567 foo

Usually, the standard way of solving this issue is a merge followed by a push.

$ git merge origin/master
$ git lg
* 4567890 Merge remote-tracking branch 'origin/master'
|\
* |  2345678 hotfix (HEAD -> master)
| * 3456789 other change (origin/master, origin/HEAD)
|/
* 1234567 foo
$ git push

I don't like this solution because I could easily avoid the branching in this particular case. So, let's revert the change with git reset --hard head~1 and try another solution instead:

$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: hotfix

$ git lg
* 2345678 hotfix (master)
| * 5678901 hotfix (HEAD)
| * 3456789 other change (origin/master, origin/HEAD)
|/
* 1234567 foo

Now comes the unpleasant part where I have to move my master back on its HEAD:

$ git branch -D master
$ git checkout -b master
$ git push
$ git branch --set-upstram-to=origin/master master
Branch master set up to track remote branch master from origin.
$ git lg 
* 5678901 hotfix (HEAD -> master, origin/master, origin/HEAD)
* 3456789 other change
* 1234567 foo

My question is how can I simplify my rebase and avoid the unpleasant part?

Upvotes: 3

Views: 1716

Answers (3)

TTT
TTT

Reputation: 28869

I'd like to propose a slightly different approach to the accepted answer. I rarely use the pull command anyway, but when I do, my preference is not to automatically rebase without me specifying that I wish to rebase. Also, maybe it's even rarer, but what if I actually do wish to make a merge commit? Instead, my preference is to set my config such that a pull will fail if a fast-forward is not possible, using this config setting:

[pull]
  ff = only

This way I know I can always safely pull and it will either fast-forward, or I'll get an error because my branch has diverged. If I get an error I can then decide if I wish to rebase or merge (or even reset to the remote!), and do so accordingly.

Upvotes: 1

hlovdal
hlovdal

Reputation: 28180

The problem is that you work directly on the master branch locally. When you make some local changes to the master branch and there also are upstream changes from after you started you will get conflicts like you describe in the question. So the solution to avoid this is simply to not work directly on the master branch but on one or more other, local branches.

So you put your hotfix changes on a separate branch, say hotfix_branch and then fetch/pull the master branch normally (without any conflicts!). When you want to deliver your hotfix changes then you rebase the botfix branch to stay on top of a newly pulled master branch, merge the hotfix branch to master and push.

Example commands:

$ git pull master
$ git checkout -b hotfix_branch master
$ $EDITOR some.file
# Time passes and changes are made on origin/master
$ git add some.file
$ git commit -m "hotfix"
$ git pull master                           # No conflicts sine master is "clean"
$ git rebase master hotfix_branch      # This step might have merge conflicts but
                                       # if so those will come no matter what you
                                       # do with regards to branching
$ git checkout master
$ git merge hotfix_branch
$ git push

There is a tiny window where there might come new changes on origin/master between you pull and attempt to push master, but if so you only have to do

$ git checkout master                   # if not already on the master branch
$ git reset origin/master

and then start again at the second pull master command in the list above.

Upvotes: 3

Kevin Burdett
Kevin Burdett

Reputation: 2982

I think the easiest way is to change your pull workflow. There are a couple options here.

First, you can use the --rebase flag to pull

git pull --rebase

Per the docs, git pull executes a fetch followed by a merge in its default configuration. Using the --rebase flag will replace the merge with a rebase :)

Second, you can set a default to always do this with git pull

git config --global pull.rebase true

I would recommend the first approach, as setting a default of rebase makes me nervous. I created an alias git pr for git pull --rebase to make it easier. That way I can make the decision with each pull.

Upvotes: 6

Related Questions