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.
7 Here's something that I've been putting together on how I'm using
8 GIT as a Linux subsystem maintainer.
10 -Tony
12 Last updated w.r.t. GIT 0.99.9f
14 Linux subsystem maintenance using GIT
15 -------------------------------------
17 My requirements here are to be able to create two public trees:
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.
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.)
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.
34 Back in the BitKeeper days I achieved this my 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.
41 So here is the step-by-step guide how this all works for me.
43 First create your work tree by cloning Linus's public tree:
45 $ git clone rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
47 Change directory into the cloned tree you just created
49 $ cd work
51 Set up a remotes file so that you can fetch the latest from Linus' master
52 branch into a local branch named "linus":
54 $ cat > .git/remotes/linus
55 URL: rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
56 Pull: master:linus
57 ^D
59 and create the linus branch:
61 $ git branch linus
63 The "linus" branch will be used to track the upstream kernel. To update it,
64 you simply run:
66 $ git fetch linus
68 you can do this frequently (and it should be safe to do so with pending
69 work in your tree, but perhaps not if you are in mid-merge).
71 If you need to keep track of other public trees, you can add remote branches
72 for them too:
74 $ git branch another
75 $ cat > .git/remotes/another
76 URL: ... insert URL here ...
77 Pull: name-of-branch-in-this-remote-tree:another
78 ^D
80 and run:
82 $ git fetch another
84 Now create the branches in which you are going to work, these start
85 out at the current tip of the linus branch.
87 $ git branch test linus
88 $ git branch release linus
90 These can be easily kept up to date by merging from the "linus" branch:
92 $ git checkout test && git merge "Auto-update from upstream" test linus
93 $ git checkout release && git merge "Auto-update from upstream" release linus
95 Set up so that you can push upstream to your public tree (you need to
96 log-in to the remote system and create an empty tree there before the
97 first push).
99 $ cat > .git/remotes/mytree
100 URL: master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
101 Push: release
102 Push: test
103 ^D
105 and the push both the test and release trees using:
107 $ git push mytree
109 or push just one of the test and release branches using:
111 $ git push mytree test
112 or
113 $ git push mytree release
115 Now to apply some patches from the community. Think of a short
116 snappy name for a branch to hold this patch (or related group of
117 patches), and create a new branch from the current tip of the
118 linus branch:
120 $ git checkout -b speed-up-spinlocks linus
122 Now you apply the patch(es), run some tests, and commit the change(s). If
123 the patch is a multi-part series, then you should apply each as a separate
124 commit to this branch.
126 $ ... patch ... test ... commit [ ... patch ... test ... commit ]*
128 When you are happy with the state of this change, you can pull it into the
129 "test" branch in preparation to make it public:
131 $ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks
133 It is unlikely that you would have any conflicts here ... but you might if you
134 spent a while on this step and had also pulled new versions from upstream.
136 Some time later when enough time has passed and testing done, you can pull the
137 same branch into the "release" tree ready to go upstream. This is where you
138 see the value of keeping each patch (or patch series) in its own branch. It
139 means that the patches can be moved into the "release" tree in any order.
141 $ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks
143 After a while, you will have a number of branches, and despite the
144 well chosen names you picked for each of them, you may forget what
145 they are for, or what status they are in. To get a reminder of what
146 changes are in a specific branch, use:
148 $ git-whatchanged branchname ^linus | git-shortlog
150 To see whether it has already been merged into the test or release branches
151 use:
153 $ git-rev-list branchname ^test
154 or
155 $ git-rev-list branchname ^release
157 [If this branch has not yet been merged you will see a set of SHA1 values
158 for the commits, if it has been merged, then there will be no output]
160 Once a patch completes the great cycle (moving from test to release, then
161 pulled by Linus, and finally coming back into your local "linus" branch)
162 the branch for this change is no longer needed. You detect this when the
163 output from:
165 $ git-rev-list branchname ^linus
167 is empty. At this point the branch can be deleted:
169 $ git branch -d branchname
171 Some changes are so trivial that it is not necessary to create a separate
172 branch and then merge into each of the test and release branches. For
173 these changes, just apply directly to the "release" branch, and then
174 merge that into the "test" branch.
176 To create diffstat and shortlog summaries of changes to include in a "please
177 pull" request to Linus you can use:
179 $ git-whatchanged -p release ^linus | diffstat -p1
180 and
181 $ git-whatchanged release ^linus | git-shortlog
184 Here are some of the scripts that I use to simplify all this even further.
186 ==== update script ====
187 # Update a branch in my GIT tree. If the branch to be updated
188 # is "linus", then pull from kernel.org. Otherwise merge local
189 # linus branch into test|release branch
191 case "$1" in
192 test|release)
193 git checkout $1 && git merge "Auto-update from upstream" $1 linus
194 ;;
195 linus)
196 before=$(cat .git/refs/heads/linus)
197 git fetch linus
198 after=$(cat .git/refs/heads/linus)
199 if [ $before != $after ]
200 then
201 git-whatchanged $after ^$before | git-shortlog
202 fi
203 ;;
204 *)
205 echo "Usage: $0 linus|test|release" 1>&2
206 exit 1
207 ;;
208 esac
210 ==== merge script ====
211 # Merge a branch into either the test or release branch
213 pname=$0
215 usage()
216 {
217 echo "Usage: $pname branch test|release" 1>&2
218 exit 1
219 }
221 if [ ! -f .git/refs/heads/"$1" ]
222 then
223 echo "Can't see branch <$1>" 1>&2
224 usage
225 fi
227 case "$2" in
228 test|release)
229 if [ $(git-rev-list $1 ^$2 | wc -c) -eq 0 ]
230 then
231 echo $1 already merged into $2 1>&2
232 exit 1
233 fi
234 git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1
235 ;;
236 *)
237 usage
238 ;;
239 esac
241 ==== status script ====
242 # report on status of my ia64 GIT tree
244 gb=$(tput setab 2)
245 rb=$(tput setab 1)
246 restore=$(tput setab 9)
248 if [ `git-rev-list release ^test | wc -c` -gt 0 ]
249 then
250 echo $rb Warning: commits in release that are not in test $restore
251 git-whatchanged release ^test
252 fi
254 for branch in `ls .git/refs/heads`
255 do
256 if [ $branch = linus -o $branch = test -o $branch = release ]
257 then
258 continue
259 fi
261 echo -n $gb ======= $branch ====== $restore " "
262 status=
263 for ref in test release linus
264 do
265 if [ `git-rev-list $branch ^$ref | wc -c` -gt 0 ]
266 then
267 status=$status${ref:0:1}
268 fi
269 done
270 case $status in
271 trl)
272 echo $rb Need to pull into test $restore
273 ;;
274 rl)
275 echo "In test"
276 ;;
277 l)
278 echo "Waiting for linus"
279 ;;
280 "")
281 echo $rb All done $restore
282 ;;
283 *)
284 echo $rb "<$status>" $restore
285 ;;
286 esac
287 git-whatchanged $branch ^linus | git-shortlog
288 done