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 1.1 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 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work 46 47Change directory into the cloned tree you just created 48 49 $ cd work 50 51Set up a remotes file so that you can fetch the latest from Linus' master 52branch into a local branch named "linus": 53 54 $ cat > .git/remotes/linus 55 URL: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git 56 Pull: master:linus 57 ^D 58 59and create the linus branch: 60 61 $ git branch linus 62 63The "linus" branch will be used to track the upstream kernel. To update it, 64you simply run: 65 66 $ git fetch linus 67 68you can do this frequently (and it should be safe to do so with pending 69work in your tree, but perhaps not if you are in mid-merge). 70 71If you need to keep track of other public trees, you can add remote branches 72for them too: 73 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 79 80and run: 81 82 $ git fetch another 83 84Now create the branches in which you are going to work, these start 85out at the current tip of the linus branch. 86 87 $ git branch test linus 88 $ git branch release linus 89 90These can be easily kept up to date by merging from the "linus" branch: 91 92 $ git checkout test && git merge "Auto-update from upstream" test linus 93 $ git checkout release && git merge "Auto-update from upstream" release linus 94 95Important note! If you have any local changes in these branches, then 96this merge will create a commit object in the history (with no local 97changes git will simply do a "Fast forward" merge). Many people dislike 98the "noise" that this creates in the Linux history, so you should avoid 99doing this capriciously in the "release" branch, as these noisy commits 100will become part of the permanent history when you ask Linus to pull 101from the release branch. 102 103Set up so that you can push upstream to your public tree (you need to 104log-in to the remote system and create an empty tree there before the 105first push). 106 107 $ cat > .git/remotes/mytree 108 URL: master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git 109 Push: release 110 Push: test 111 ^D 112 113and the push both the test and release trees using: 114 115 $ git push mytree 116 117or push just one of the test and release branches using: 118 119 $ git push mytree test 120or 121 $ git push mytree release 122 123Now to apply some patches from the community. Think of a short 124snappy name for a branch to hold this patch (or related group of 125patches), and create a new branch from the current tip of the 126linus branch: 127 128 $ git checkout -b speed-up-spinlocks linus 129 130Now you apply the patch(es), run some tests, and commit the change(s). If 131the patch is a multi-part series, then you should apply each as a separate 132commit to this branch. 133 134 $ ... patch ... test ... commit [ ... patch ... test ... commit ]* 135 136When you are happy with the state of this change, you can pull it into the 137"test" branch in preparation to make it public: 138 139 $ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks 140 141It is unlikely that you would have any conflicts here ... but you might if you 142spent a while on this step and had also pulled new versions from upstream. 143 144Some time later when enough time has passed and testing done, you can pull the 145same branch into the "release" tree ready to go upstream. This is where you 146see the value of keeping each patch (or patch series) in its own branch. It 147means that the patches can be moved into the "release" tree in any order. 148 149 $ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks 150 151After a while, you will have a number of branches, and despite the 152well chosen names you picked for each of them, you may forget what 153they are for, or what status they are in. To get a reminder of what 154changes are in a specific branch, use: 155 156 $ git-whatchanged branchname ^linus | git-shortlog 157 158To see whether it has already been merged into the test or release branches 159use: 160 161 $ git-rev-list branchname ^test 162or 163 $ git-rev-list branchname ^release 164 165[If this branch has not yet been merged you will see a set of SHA1 values 166for the commits, if it has been merged, then there will be no output] 167 168Once a patch completes the great cycle (moving from test to release, then 169pulled by Linus, and finally coming back into your local "linus" branch) 170the branch for this change is no longer needed. You detect this when the 171output from: 172 173 $ git-rev-list branchname ^linus 174 175is empty. At this point the branch can be deleted: 176 177 $ git branch -d branchname 178 179Some changes are so trivial that it is not necessary to create a separate 180branch and then merge into each of the test and release branches. For 181these changes, just apply directly to the "release" branch, and then 182merge that into the "test" branch. 183 184To create diffstat and shortlog summaries of changes to include in a "please 185pull" request to Linus you can use: 186 187 $ git-whatchanged -p release ^linus | diffstat -p1 188and 189 $ git-whatchanged release ^linus | git-shortlog 190 191 192Here are some of the scripts that I use to simplify all this even further. 193 194==== update script ==== 195# Update a branch in my GIT tree. If the branch to be updated 196# is "linus", then pull from kernel.org. Otherwise merge local 197# linus branch into test|release branch 198 199case "$1" in 200test|release) 201 git checkout $1 && git merge "Auto-update from upstream" $1 linus 202 ;; 203linus) 204 before=$(cat .git/refs/heads/linus) 205 git fetch linus 206 after=$(cat .git/refs/heads/linus) 207 if [ $before != $after ] 208 then 209 git-whatchanged $after ^$before | git-shortlog 210 fi 211 ;; 212*) 213 echo "Usage: $0 linus|test|release" 1>&2 214 exit 1 215 ;; 216esac 217 218==== merge script ==== 219# Merge a branch into either the test or release branch 220 221pname=$0 222 223usage() 224{ 225 echo "Usage: $pname branch test|release" 1>&2 226 exit 1 227} 228 229if [ ! -f .git/refs/heads/"$1" ] 230then 231 echo "Can't see branch <$1>" 1>&2 232 usage 233fi 234 235case "$2" in 236test|release) 237 if [ $(git-rev-list $1 ^$2 | wc -c) -eq 0 ] 238 then 239 echo $1 already merged into $2 1>&2 240 exit 1 241 fi 242 git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1 243 ;; 244*) 245 usage 246 ;; 247esac 248 249==== status script ==== 250# report on status of my ia64 GIT tree 251 252gb=$(tput setab 2) 253rb=$(tput setab 1) 254restore=$(tput setab 9) 255 256if [ `git-rev-list release ^test | wc -c` -gt 0 ] 257then 258 echo $rb Warning: commits in release that are not in test $restore 259 git-whatchanged release ^test 260fi 261 262for branch in `ls .git/refs/heads` 263do 264 if [ $branch = linus -o $branch = test -o $branch = release ] 265 then 266 continue 267 fi 268 269 echo -n $gb ======= $branch ====== $restore " " 270 status= 271 for ref in test release linus 272 do 273 if [ `git-rev-list $branch ^$ref | wc -c` -gt 0 ] 274 then 275 status=$status${ref:0:1} 276 fi 277 done 278 case $status in 279 trl) 280 echo $rb Need to pull into test $restore 281 ;; 282 rl) 283 echo "In test" 284 ;; 285 l) 286 echo "Waiting for linus" 287 ;; 288 "") 289 echo $rb All done $restore 290 ;; 291 *) 292 echo $rb "<$status>" $restore 293 ;; 294 esac 295 git-whatchanged $branch ^linus | git-shortlog 296done