| 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 "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 <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 |
| >> > "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 |