Spaces:
Running
Running
text-generation-webui
/
installer_files
/env
/share
/doc
/git
/howto
/rebase-from-internal-branch.txt
From: Junio C Hamano <[email protected]> | |
To: [email protected] | |
Cc: Petr Baudis <[email protected]>, Linus Torvalds <[email protected]> | |
Subject: Re: sending changesets from the middle of a git tree | |
Date: Sun, 14 Aug 2005 18:37:39 -0700 | |
Abstract: In this article, JC talks about how he rebases the | |
public "seen" branch using the core Git tools when he updates | |
the "master" branch, and how "rebase" works. Also discussed | |
is how this applies to individual developers who sends patches | |
upstream. | |
Content-type: text/asciidoc | |
How to rebase from an internal branch | |
===================================== | |
-------------------------------------- | |
Petr Baudis <[email protected]> writes: | |
> Dear diary, on Sun, Aug 14, 2005 at 09:57:13AM CEST, I got a letter | |
> where Junio C Hamano <[email protected]> told me that... | |
>> Linus Torvalds <[email protected]> writes: | |
>> | |
>> > Junio, maybe you want to talk about how you move patches from your | |
>> > "seen" branch to the real branches. | |
>> | |
> Actually, wouldn't this be also precisely for what StGIT is intended to? | |
-------------------------------------- | |
Exactly my feeling. I was sort of waiting for Catalin to speak | |
up. With its basing philosophical ancestry on quilt, this is | |
the kind of task StGIT is designed to do. | |
I just have done a simpler one, this time using only the core | |
Git tools. | |
I had a handful of commits that were ahead of master in 'seen', and I | |
wanted to add some documentation bypassing my usual habit of | |
placing new things in 'seen' first. At the beginning, the commit | |
ancestry graph looked like this: | |
*"seen" head | |
master --> #1 --> #2 --> #3 | |
So I started from master, made a bunch of edits, and committed: | |
$ git checkout master | |
$ cd Documentation; ed git.txt ... | |
$ cd ..; git add Documentation/*.txt | |
$ git commit -s | |
After the commit, the ancestry graph would look like this: | |
*"seen" head | |
master^ --> #1 --> #2 --> #3 | |
\ | |
\---> master | |
The old master is now master^ (the first parent of the master). | |
The new master commit holds my documentation updates. | |
Now I have to deal with "seen" branch. | |
This is the kind of situation I used to have all the time when | |
Linus was the maintainer and I was a contributor, when you look | |
at "master" branch being the "maintainer" branch, and "seen" | |
branch being the "contributor" branch. Your work started at the | |
tip of the "maintainer" branch some time ago, you made a lot of | |
progress in the meantime, and now the maintainer branch has some | |
other commits you do not have yet. And "git rebase" was written | |
with the explicit purpose of helping to maintain branches like | |
"seen". You _could_ merge master to 'seen' and keep going, but if you | |
eventually want to cherrypick and merge some but not necessarily | |
all changes back to the master branch, it often makes later | |
operations for _you_ easier if you rebase (i.e. carry forward | |
your changes) "seen" rather than merge. So I ran "git rebase": | |
$ git checkout seen | |
$ git rebase master seen | |
What this does is to pick all the commits since the current | |
branch (note that I now am on "seen" branch) forked from the | |
master branch, and forward port these changes. | |
master^ --> #1 --> #2 --> #3 | |
\ *"seen" head | |
\---> master --> #1' --> #2' --> #3' | |
The diff between master^ and #1 is applied to master and | |
committed to create #1' commit with the commit information (log, | |
author and date) taken from commit #1. On top of that #2' and #3' | |
commits are made similarly out of #2 and #3 commits. | |
Old #3 is not recorded in any of the .git/refs/heads/ file | |
anymore, so after doing this you will have dangling commit if | |
you ran fsck-cache, which is normal. After testing "seen", you | |
can run "git prune" to get rid of those original three commits. | |
While I am talking about "git rebase", I should talk about how | |
to do cherrypicking using only the core Git tools. | |
Let's go back to the earlier picture, with different labels. | |
You, as an individual developer, cloned upstream repository and | |
made a couple of commits on top of it. | |
*your "master" head | |
upstream --> #1 --> #2 --> #3 | |
You would want changes #2 and #3 incorporated in the upstream, | |
while you feel that #1 may need further improvements. So you | |
prepare #2 and #3 for e-mail submission. | |
$ git format-patch master^^ master | |
This creates two files, 0001-XXXX.patch and 0002-XXXX.patch. Send | |
them out "To: " your project maintainer and "Cc: " your mailing | |
list. You could use contributed script git-send-email if | |
your host has necessary perl modules for this, but your usual | |
MUA would do as long as it does not corrupt whitespaces in the | |
patch. | |
Then you would wait, and you find out that the upstream picked | |
up your changes, along with other changes. | |
where *your "master" head | |
upstream --> #1 --> #2 --> #3 | |
used \ | |
to be \--> #A --> #2' --> #3' --> #B --> #C | |
*upstream head | |
The two commits #2' and #3' in the above picture record the same | |
changes your e-mail submission for #2 and #3 contained, but | |
probably with the new sign-off line added by the upstream | |
maintainer and definitely with different committer and ancestry | |
information, they are different objects from #2 and #3 commits. | |
You fetch from upstream, but not merge. | |
$ git fetch upstream | |
This leaves the updated upstream head in .git/FETCH_HEAD but | |
does not touch your .git/HEAD or .git/refs/heads/master. | |
You run "git rebase" now. | |
$ git rebase FETCH_HEAD master | |
Earlier, I said that rebase applies all the commits from your | |
branch on top of the upstream head. Well, I lied. "git rebase" | |
is a bit smarter than that and notices that #2 and #3 need not | |
be applied, so it only applies #1. The commit ancestry graph | |
becomes something like this: | |
where *your old "master" head | |
upstream --> #1 --> #2 --> #3 | |
used \ your new "master" head* | |
to be \--> #A --> #2' --> #3' --> #B --> #C --> #1' | |
*upstream | |
head | |
Again, "git prune" would discard the disused commits #1-#3 and | |
you continue on starting from the new "master" head, which is | |
the #1' commit. | |
-jc | |