зеркало из https://github.com/microsoft/git.git
165 строки
6.2 KiB
Plaintext
165 строки
6.2 KiB
Plaintext
From: Junio C Hamano <gitster@pobox.com>
|
|
To: git@vger.kernel.org
|
|
Cc: Petr Baudis <pasky@suse.cz>, Linus Torvalds <torvalds@osdl.org>
|
|
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 "pu" 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 <pasky@suse.cz> writes:
|
|
|
|
> Dear diary, on Sun, Aug 14, 2005 at 09:57:13AM CEST, I got a letter
|
|
> where Junio C Hamano <junkio@cox.net> told me that...
|
|
>> Linus Torvalds <torvalds@osdl.org> writes:
|
|
>>
|
|
>> > Junio, maybe you want to talk about how you move patches from your "pu"
|
|
>> > 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 pu, and I
|
|
wanted to add some documentation bypassing my usual habit of
|
|
placing new things in pu first. At the beginning, the commit
|
|
ancestry graph looked like this:
|
|
|
|
*"pu" 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:
|
|
|
|
*"pu" 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 "pu" 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 "pu"
|
|
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
|
|
"pu". You _could_ merge master to pu 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) "pu" rather than merge. So I ran "git rebase":
|
|
|
|
$ git checkout pu
|
|
$ git rebase master pu
|
|
|
|
What this does is to pick all the commits since the current
|
|
branch (note that I now am on "pu" branch) forked from the
|
|
master branch, and forward port these changes.
|
|
|
|
master^ --> #1 --> #2 --> #3
|
|
\ *"pu" 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 "pu", 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
|