Documentation / howto / using-topic-branches.txton commit git(7): move gitk(1) to the list of porcelain commands (ef11862)
   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