Documentation: topic branches
[git.git] / Documentation / howto / using-topic-branches.txt
1 Date: Mon, 15 Aug 2005 12:17:41 -0700
2 From: tony.luck@intel.com
3 Subject: Some tutorial text (was git/cogito workshop/bof at linuxconf au?)
4 Abstract: In this article, Tony Luck discusses how he uses GIT
5  as a Linux subsystem maintainer.
6
7 Here's something that I've been putting together on how I'm using
8 GIT as a Linux subsystem maintainer.
9
10 -Tony
11
12 Last updated w.r.t. GIT 0.99.9f
13
14 Linux subsystem maintenance using GIT
15 -------------------------------------
16
17 My requirements here are to be able to create two public trees:
18
19 1) A "test" tree into which patches are initially placed so that they
20 can get some exposure when integrated with other ongoing development.
21 This tree is available to Andrew for pulling into -mm whenever he wants.
22
23 2) A "release" tree into which tested patches are moved for final
24 sanity checking, and as a vehicle to send them upstream to Linus
25 (by sending him a "please pull" request.)
26
27 Note that the period of time that each patch spends in the "test" tree
28 is dependent on the complexity of the change.  Since GIT does not support
29 cherry picking, it is not practical to simply apply all patches to the
30 test tree and then pull to the release tree as that would leave trivial
31 patches blocked in the test tree waiting for complex changes to accumulate
32 enough test time to graduate.
33
34 Back in the BitKeeper days I achieved this by creating small forests of
35 temporary trees, one tree for each logical grouping of patches, and then
36 pulling changes from these trees first to the test tree, and then to the
37 release tree.  At first I replicated this in GIT, but then I realised
38 that I could so this far more efficiently using branches inside a single
39 GIT repository.
40
41 So here is the step-by-step guide how this all works for me.
42
43 First create your work tree by cloning Linus's public tree:
44
45  $ git clone \
46  master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
47
48 Change directory into the cloned tree you just created
49
50  $ cd work
51
52 Set up a remotes file so that you can fetch the latest from Linus' master
53 branch into a local branch named "linus":
54
55  $ cat > .git/remotes/linus
56  URL: master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
57  Pull: master:linus
58  ^D
59
60 and create the linus branch:
61
62  $ git branch linus
63
64 The "linus" branch will be used to track the upstream kernel.  To update it,
65 you simply run:
66
67  $ git fetch linus
68
69 you can do this frequently (and it should be safe to do so with pending
70 work in your tree, but perhaps not if you are in mid-merge).
71
72 If you need to keep track of other public trees, you can add remote branches
73 for them too:
74
75  $ git branch another
76  $ cat > .git/remotes/another
77  URL: ... insert URL here ...
78  Pull: name-of-branch-in-this-remote-tree:another
79  ^D
80
81 and run:
82
83  $ git fetch another
84
85 Now create the branches in which you are going to work, these start
86 out at the current tip of the linus branch.
87
88  $ git branch test linus
89  $ git branch release linus
90
91 These can be easily kept up to date by merging from the "linus" branch:
92
93  $ git checkout test && git merge "Auto-update from upstream" test linus
94  $ git checkout release && git merge "Auto-update from upstream" release linus
95
96 Set up so that you can push upstream to your public tree (you need to
97 log-in to the remote system and create an empty tree there before the
98 first push).
99
100  $ cat > .git/remotes/mytree
101  URL: master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
102  Push: release
103  Push: test
104  ^D
105
106 and the push both the test and release trees using:
107
108  $ git push mytree
109
110 or push just one of the test and release branches using:
111
112  $ git push mytree test
113 or
114  $ git push mytree release
115
116 Now to apply some patches from the community.  Think of a short
117 snappy name for a branch to hold this patch (or related group of
118 patches), and create a new branch from the current tip of the
119 linus branch:
120
121  $ git checkout -b speed-up-spinlocks linus
122
123 Now you apply the patch(es), run some tests, and commit the change(s).  If
124 the patch is a multi-part series, then you should apply each as a separate
125 commit to this branch.
126
127  $ ... patch ... test  ... commit [ ... patch ... test ... commit ]*
128
129 When you are happy with the state of this change, you can pull it into the
130 "test" branch in preparation to make it public:
131
132  $ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks
133
134 It is unlikely that you would have any conflicts here ... but you might if you
135 spent a while on this step and had also pulled new versions from upstream.
136
137 Some time later when enough time has passed and testing done, you can pull the
138 same branch into the "release" tree ready to go upstream.  This is where you
139 see the value of keeping each patch (or patch series) in its own branch.  It
140 means that the patches can be moved into the "release" tree in any order.
141
142  $ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks
143
144 After a while, you will have a number of branches, and despite the
145 well chosen names you picked for each of them, you may forget what
146 they are for, or what status they are in.  To get a reminder of what
147 changes are in a specific branch, use:
148
149  $ git-whatchanged branchname ^linus | git-shortlog
150
151 To see whether it has already been merged into the test or release branches
152 use:
153
154  $ git-rev-list branchname ^test
155 or
156  $ git-rev-list branchname ^release
157
158 [If this branch has not yet been merged you will see a set of SHA1 values
159 for the commits, if it has been merged, then there will be no output]
160
161 Once a patch completes the great cycle (moving from test to release, then
162 pulled by Linus, and finally coming back into your local "linus" branch)
163 the branch for this change is no longer needed.  You detect this when the
164 output from:
165
166  $ git-rev-list branchname ^linus
167
168 is empty.  At this point the branch can be deleted:
169
170  $ git branch -d branchname
171
172 Some changes are so trivial that it is not necessary to create a separate
173 branch and then merge into each of the test and release branches.  For
174 these changes, just apply directly to the "release" branch, and then
175 merge that into the "test" branch.
176
177 To create diffstat and shortlog summaries of changes to include in a "please
178 pull" request to Linus you can use:
179
180  $ git-whatchanged -p release ^linus | diffstat -p1
181 and
182  $ git-whatchanged release ^linus | git-shortlog
183
184
185 Here are some of the scripts that I use to simplify all this even further.
186
187 ==== update script ====
188 # Update a branch in my GIT tree.  If the branch to be updated
189 # is "linus", then pull from kernel.org.  Otherwise merge local
190 # linus branch into test|release branch
191
192 case "$1" in
193 test|release)
194         git checkout $1 && git merge "Auto-update from upstream" $1 linus
195         ;;
196 linus)
197         before=$(cat .git/refs/heads/linus)
198         git fetch linus
199         after=$(cat .git/refs/heads/linus)
200         if [ $before != $after ]
201         then
202                 git-whatchanged $after ^$before | git-shortlog
203         fi
204         ;;
205 *)
206         echo "Usage: $0 linus|test|release" 1>&2
207         exit 1
208         ;;
209 esac
210
211 ==== merge script ====
212 # Merge a branch into either the test or release branch
213
214 pname=$0
215
216 usage()
217 {
218         echo "Usage: $pname branch test|release" 1>&2
219         exit 1
220 }
221
222 if [ ! -f .git/refs/heads/"$1" ]
223 then
224         echo "Can't see branch <$1>" 1>&2
225         usage
226 fi
227
228 case "$2" in
229 test|release)
230         if [ $(git-rev-list $1 ^$2 | wc -c) -eq 0 ]
231         then
232                 echo $1 already merged into $2 1>&2
233                 exit 1
234         fi
235         git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1
236         ;;
237 *)
238         usage
239         ;;
240 esac
241
242 ==== status script ====
243 # report on status of my ia64 GIT tree
244
245 gb=$(tput setab 2)
246 rb=$(tput setab 1)
247 restore=$(tput setab 9)
248
249 if [ `git-rev-list release ^test | wc -c` -gt 0 ]
250 then
251         echo $rb Warning: commits in release that are not in test $restore
252         git-whatchanged release ^test
253 fi
254
255 for branch in `ls .git/refs/heads`
256 do
257         if [ $branch = linus -o $branch = test -o $branch = release ]
258         then
259                 continue
260         fi
261
262         echo -n $gb ======= $branch ====== $restore " "
263         status=
264         for ref in test release linus
265         do
266                 if [ `git-rev-list $branch ^$ref | wc -c` -gt 0 ]
267                 then
268                         status=$status${ref:0:1}
269                 fi
270         done
271         case $status in
272         trl)
273                 echo $rb Need to pull into test $restore
274                 ;;
275         rl)
276                 echo "In test"
277                 ;;
278         l)
279                 echo "Waiting for linus"
280                 ;;
281         "")
282                 echo $rb All done $restore
283                 ;;
284         *)
285                 echo $rb "<$status>" $restore
286                 ;;
287         esac
288         git-whatchanged $branch ^linus | git-shortlog
289 done