summaryrefslogtreecommitdiffstats
path: root/Documentation/howto/rebase-from-internal-branch.txt
blob: f2e10a7ec88664be4c31db7ae9770281db343a74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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