]>
Commit | Line | Data |
---|---|---|
a2f15a81 JH |
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?) | |
f358c10f JH |
4 | Abstract: In this article, Tony Luck discusses how he uses GIT |
5 | as a Linux subsystem maintainer. | |
a2f15a81 JH |
6 | |
7 | Here's something that I've been putting together on how I'm using | |
8 | GIT as a Linux subsystem maintainer. | |
9 | ||
a2f15a81 JH |
10 | -Tony |
11 | ||
f37d0cc3 | 12 | Last updated w.r.t. GIT 0.99.9f |
23e86730 | 13 | |
a2f15a81 JH |
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 | ||
a4adf54d | 34 | Back in the BitKeeper days I achieved this by creating small forests of |
a2f15a81 JH |
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 | ||
3af849a3 | 45 | $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work |
a2f15a81 JH |
46 | |
47 | Change directory into the cloned tree you just created | |
48 | ||
49 | $ cd work | |
50 | ||
23e86730 TL |
51 | Set up a remotes file so that you can fetch the latest from Linus' master |
52 | branch into a local branch named "linus": | |
53 | ||
54 | $ cat > .git/remotes/linus | |
3af849a3 | 55 | URL: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git |
23e86730 TL |
56 | Pull: master:linus |
57 | ^D | |
a2f15a81 | 58 | |
23e86730 TL |
59 | and create the linus branch: |
60 | ||
61 | $ git branch linus | |
a2f15a81 JH |
62 | |
63 | The "linus" branch will be used to track the upstream kernel. To update it, | |
64 | you simply run: | |
65 | ||
23e86730 TL |
66 | $ git fetch linus |
67 | ||
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). | |
a2f15a81 | 70 | |
23e86730 TL |
71 | If you need to keep track of other public trees, you can add remote branches |
72 | for them too: | |
a2f15a81 | 73 | |
23e86730 TL |
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 | |
a2f15a81 | 79 | |
23e86730 TL |
80 | and run: |
81 | ||
82 | $ git fetch another | |
a2f15a81 JH |
83 | |
84 | Now create the branches in which you are going to work, these start | |
85 | out at the current tip of the linus branch. | |
86 | ||
accd952f TL |
87 | $ git branch test linus |
88 | $ git branch release linus | |
a2f15a81 JH |
89 | |
90 | These can be easily kept up to date by merging from the "linus" branch: | |
91 | ||
f37d0cc3 TL |
92 | $ git checkout test && git merge "Auto-update from upstream" test linus |
93 | $ git checkout release && git merge "Auto-update from upstream" release linus | |
a2f15a81 | 94 | |
23e86730 TL |
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). | |
a2f15a81 | 98 | |
23e86730 TL |
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 | |
a2f15a81 | 104 | |
23e86730 | 105 | and the push both the test and release trees using: |
a2f15a81 | 106 | |
23e86730 TL |
107 | $ git push mytree |
108 | ||
109 | or push just one of the test and release branches using: | |
110 | ||
111 | $ git push mytree test | |
112 | or | |
113 | $ git push mytree release | |
a2f15a81 JH |
114 | |
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: | |
119 | ||
120 | $ git checkout -b speed-up-spinlocks linus | |
121 | ||
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. | |
125 | ||
126 | $ ... patch ... test ... commit [ ... patch ... test ... commit ]* | |
127 | ||
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: | |
130 | ||
f37d0cc3 | 131 | $ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks |
a2f15a81 JH |
132 | |
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. | |
135 | ||
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. | |
140 | ||
f37d0cc3 | 141 | $ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks |
a2f15a81 JH |
142 | |
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: | |
147 | ||
148 | $ git-whatchanged branchname ^linus | git-shortlog | |
149 | ||
150 | To see whether it has already been merged into the test or release branches | |
151 | use: | |
152 | ||
153 | $ git-rev-list branchname ^test | |
154 | or | |
155 | $ git-rev-list branchname ^release | |
156 | ||
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] | |
159 | ||
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: | |
164 | ||
165 | $ git-rev-list branchname ^linus | |
166 | ||
167 | is empty. At this point the branch can be deleted: | |
168 | ||
e66ab03f | 169 | $ git branch -d branchname |
a2f15a81 | 170 | |
accd952f TL |
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. | |
175 | ||
a2f15a81 JH |
176 | To create diffstat and shortlog summaries of changes to include in a "please |
177 | pull" request to Linus you can use: | |
178 | ||
179 | $ git-whatchanged -p release ^linus | diffstat -p1 | |
180 | and | |
181 | $ git-whatchanged release ^linus | git-shortlog | |
182 | ||
accd952f TL |
183 | |
184 | Here are some of the scripts that I use to simplify all this even further. | |
185 | ||
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 | |
190 | ||
191 | case "$1" in | |
192 | test|release) | |
f37d0cc3 | 193 | git checkout $1 && git merge "Auto-update from upstream" $1 linus |
accd952f TL |
194 | ;; |
195 | linus) | |
23e86730 TL |
196 | before=$(cat .git/refs/heads/linus) |
197 | git fetch linus | |
198 | after=$(cat .git/refs/heads/linus) | |
accd952f TL |
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 | |
209 | ||
210 | ==== merge script ==== | |
211 | # Merge a branch into either the test or release branch | |
212 | ||
213 | pname=$0 | |
214 | ||
215 | usage() | |
216 | { | |
217 | echo "Usage: $pname branch test|release" 1>&2 | |
218 | exit 1 | |
219 | } | |
220 | ||
221 | if [ ! -f .git/refs/heads/"$1" ] | |
222 | then | |
223 | echo "Can't see branch <$1>" 1>&2 | |
224 | usage | |
225 | fi | |
226 | ||
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 | |
f37d0cc3 | 234 | git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1 |
accd952f TL |
235 | ;; |
236 | *) | |
237 | usage | |
238 | ;; | |
239 | esac | |
240 | ||
241 | ==== status script ==== | |
242 | # report on status of my ia64 GIT tree | |
243 | ||
244 | gb=$(tput setab 2) | |
245 | rb=$(tput setab 1) | |
246 | restore=$(tput setab 9) | |
247 | ||
9dcc829f | 248 | if [ `git-rev-list release ^test | wc -c` -gt 0 ] |
accd952f TL |
249 | then |
250 | echo $rb Warning: commits in release that are not in test $restore | |
251 | git-whatchanged release ^test | |
252 | fi | |
253 | ||
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 | |
260 | ||
261 | echo -n $gb ======= $branch ====== $restore " " | |
262 | status= | |
263 | for ref in test release linus | |
264 | do | |
9dcc829f | 265 | if [ `git-rev-list $branch ^$ref | wc -c` -gt 0 ] |
accd952f TL |
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 |