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