Reputation: 29066
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
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
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
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