1Date: Mon, 15 Aug 2005 12:17:41 -0700 2From: tony.luck@intel.com 3Subject: Some tutorial text (was git/cogito workshop/bof at linuxconf au?) 4Abstract: In this article, Tony Luck discusses how he uses GIT 5 as a Linux subsystem maintainer. 6 7Here's something that I've been putting together on how I'm using 8GIT as a Linux subsystem maintainer. 9 10-Tony 11 12Last updated w.r.t. GIT 0.99.9f 13 14Linux subsystem maintenance using GIT 15------------------------------------- 16 17My requirements here are to be able to create two public trees: 18 191) A "test" tree into which patches are initially placed so that they 20can get some exposure when integrated with other ongoing development. 21This tree is available to Andrew for pulling into -mm whenever he wants. 22 232) A "release" tree into which tested patches are moved for final 24sanity checking, and as a vehicle to send them upstream to Linus 25(by sending him a "please pull" request.) 26 27Note that the period of time that each patch spends in the "test" tree 28is dependent on the complexity of the change. Since GIT does not support 29cherry picking, it is not practical to simply apply all patches to the 30test tree and then pull to the release tree as that would leave trivial 31patches blocked in the test tree waiting for complex changes to accumulate 32enough test time to graduate. 33 34Back in the BitKeeper days I achieved this by creating small forests of 35temporary trees, one tree for each logical grouping of patches, and then 36pulling changes from these trees first to the test tree, and then to the 37release tree. At first I replicated this in GIT, but then I realised 38that I could so this far more efficiently using branches inside a single 39GIT repository. 40 41So here is the step-by-step guide how this all works for me. 42 43First 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 48Change directory into the cloned tree you just created 49 50 $ cd work 51 52Set up a remotes file so that you can fetch the latest from Linus' master 53branch 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 60and create the linus branch: 61 62 $ git branch linus 63 64The "linus" branch will be used to track the upstream kernel. To update it, 65you simply run: 66 67 $ git fetch linus 68 69you can do this frequently (and it should be safe to do so with pending 70work in your tree, but perhaps not if you are in mid-merge). 71 72If you need to keep track of other public trees, you can add remote branches 73for 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 81and run: 82 83 $ git fetch another 84 85Now create the branches in which you are going to work, these start 86out at the current tip of the linus branch. 87 88 $ git branch test linus 89 $ git branch release linus 90 91These 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 96Set up so that you can push upstream to your public tree (you need to 97log-in to the remote system and create an empty tree there before the 98first 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 106and the push both the test and release trees using: 107 108 $ git push mytree 109 110or push just one of the test and release branches using: 111 112 $ git push mytree test 113or 114 $ git push mytree release 115 116Now to apply some patches from the community. Think of a short 117snappy name for a branch to hold this patch (or related group of 118patches), and create a new branch from the current tip of the 119linus branch: 120 121 $ git checkout -b speed-up-spinlocks linus 122 123Now you apply the patch(es), run some tests, and commit the change(s). If 124the patch is a multi-part series, then you should apply each as a separate 125commit to this branch. 126 127 $ ... patch ... test ... commit [ ... patch ... test ... commit ]* 128 129When 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 134It is unlikely that you would have any conflicts here ... but you might if you 135spent a while on this step and had also pulled new versions from upstream. 136 137Some time later when enough time has passed and testing done, you can pull the 138same branch into the "release" tree ready to go upstream. This is where you 139see the value of keeping each patch (or patch series) in its own branch. It 140means 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 144After a while, you will have a number of branches, and despite the 145well chosen names you picked for each of them, you may forget what 146they are for, or what status they are in. To get a reminder of what 147changes are in a specific branch, use: 148 149 $ git-whatchanged branchname ^linus | git-shortlog 150 151To see whether it has already been merged into the test or release branches 152use: 153 154 $ git-rev-list branchname ^test 155or 156 $ git-rev-list branchname ^release 157 158[If this branch has not yet been merged you will see a set of SHA1 values 159for the commits, if it has been merged, then there will be no output] 160 161Once a patch completes the great cycle (moving from test to release, then 162pulled by Linus, and finally coming back into your local "linus" branch) 163the branch for this change is no longer needed. You detect this when the 164output from: 165 166 $ git-rev-list branchname ^linus 167 168is empty. At this point the branch can be deleted: 169 170 $ git branch -d branchname 171 172Some changes are so trivial that it is not necessary to create a separate 173branch and then merge into each of the test and release branches. For 174these changes, just apply directly to the "release" branch, and then 175merge that into the "test" branch. 176 177To create diffstat and shortlog summaries of changes to include in a "please 178pull" request to Linus you can use: 179 180 $ git-whatchanged -p release ^linus | diffstat -p1 181and 182 $ git-whatchanged release ^linus | git-shortlog 183 184 185Here 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 192case "$1" in 193test|release) 194 git checkout $1 && git merge "Auto-update from upstream" $1 linus 195 ;; 196linus) 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 ;; 209esac 210 211==== merge script ==== 212# Merge a branch into either the test or release branch 213 214pname=$0 215 216usage() 217{ 218 echo "Usage: $pname branch test|release" 1>&2 219 exit 1 220} 221 222if [ ! -f .git/refs/heads/"$1" ] 223then 224 echo "Can't see branch <$1>" 1>&2 225 usage 226fi 227 228case "$2" in 229test|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 ;; 240esac 241 242==== status script ==== 243# report on status of my ia64 GIT tree 244 245gb=$(tput setab 2) 246rb=$(tput setab 1) 247restore=$(tput setab 9) 248 249if [ `git-rev-list release ^test | wc -c` -gt 0 ] 250then 251 echo $rb Warning: commits in release that are not in test $restore 252 git-whatchanged release ^test 253fi 254 255for branch in `ls .git/refs/heads` 256do 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 289done