Rebasing to new versions in upstream git (GitHub) with losing all local changes
I want to have the following version structure:
(my private changes to 0.1.0) - A - B - C - ...
/
(upstream repo) - 0.1.0 - 0.2.0 - ...
\
(my private changes to 0.2.0) - D - E - F - ...
I don't want to merge my changes with changes in upstream, because the upstream is developed rapidly and usually there are zillions of hard to resolve conflicts, because upstream code was significantly redesigned. When 0.3.0 is out, I'm going just to create an exact copy of it forgetting my private changes and then re-apply my private fixes one by one. But I don't want my old changes to disappear completely - I want them to sit somewhere still (on a separate branch?).
git rebase
is not what I want. According to Pro git book, rebasing will try to re-apply A-B-C patches to 0.2.0. Here is an example situation between upstream 0.1.0 and 0.2.0 release dates:
(upstream) - 0.1.0 - (my master) - A - B - C
Now 0.2.0 is out:
(my master) - A - B - C
/
(upstream) - 0.1.0 - 0.2.0
I don't want to rebase my A - B - C changes because of too many conflicts. I want to label my A - B - C branch as 'my-0.1.0' and start a 'new' master branch from 0.2.0:
(my-0.1.0) - A - B - C
/
(upstream) - 0.1.0 - 0.2.0 - (my new master)
I want my new master branch to be a clean copy of upstream 0.2.0, without any attempts to re-apply my old A-B-C changesets to it. So later I can cherry-pick A-B-C changes one by one if I need them in post-0.2.0 world.
How do I put the my-0.1.0 label? Is it a label, a tag, a branch? How do I start an empty master branch off 0.2.0? Note that there are two different repos: my repo and upstream repo. Do I need to copy upstream branch from upstream repo to my repo? How I ensure that 0.2.0 is pulled after 0.1.0, and not after C? If I just do 开发者_Go百科a pull request after
(upstream) - 0.1.0 - (my master) - A - B - C
I get
(upstream) - 0.1.0 - (my master) - A - B - C - "0.2.0 merged with ABC"
which is not what I want.
Also sometimes I want to push certain change (e.g. E) back to upstream. Is it possible to create a pull request for E change only? In darcs this is possible. If it's not possible with Git/GitHub, then I will need to create a separate branch, reapply the changes in E manually. How should I proceed?
git rebase
is your friend.
Tag the old version if you want and rebase your private branches on the latest upstream version.
To create a pull request for E only, you can either:
- Create new branch and
git cherry-pick E
to it (the old branch will have the old version of E). git rebase -i
the branch and reorder the commits to put E first. Than you can tag E and create a pull request from that tag. When the upstream applies it, rebasing should correctly apply the remaining commits
In either case if the rebase does not realize E is merged when upstream finally does it (which happens if they modified it), just use the interactive rebase to drop your obsolete version of it.
Darcs actually does the same thing, but darcs rebases automatically if it can do it without conflicts (and refuses to do it altogether if it can't), while in git you rebase explicitly and if there are conflicts (E turns out to depend on D), it spits out the conflict and lets you deal with it anyway you want.
Edit: Ah, you just want to put A, B and C aside and start with clean 0.2.0. You can use either branch (on master, do):
git branch -m my-0.1
git checkout -b master 0.2.0
or tag (on master, do):
git tag my-0.1
git reset --keep 0.2.0
The branch will keep it's reflog around, you will be able to check it out and modify it. The tag will not have reflog and you won't be able to check it out (checkout will put you in the "detached HEAD" state unless you give it a branch to create).
You might want to look at git rebase --onto
which will let you apply a set of changes onto a different base than they are currently on
Note that since git1.7.2rc2, the git reset --keep 0.2.0
option mentioned by Jan Hudec in his answer can also be written:
git checkout -B master 0.2.0
The announce mentions:
git checkout -B <current branch> <elsewhere>"
is a more intuitive way to spell "
git reset --keep <elsewhere>
".
精彩评论