diff options
Diffstat (limited to 'Documentation/howto/using-topic-branches.txt')
-rw-r--r-- | Documentation/howto/using-topic-branches.txt | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/Documentation/howto/using-topic-branches.txt b/Documentation/howto/using-topic-branches.txt new file mode 100644 index 0000000000..2c98194cb8 --- /dev/null +++ b/Documentation/howto/using-topic-branches.txt @@ -0,0 +1,296 @@ +Date: Mon, 15 Aug 2005 12:17:41 -0700 +From: tony.luck@intel.com +Subject: Some tutorial text (was git/cogito workshop/bof at linuxconf au?) +Abstract: In this article, Tony Luck discusses how he uses GIT + as a Linux subsystem maintainer. + +Here's something that I've been putting together on how I'm using +GIT as a Linux subsystem maintainer. + +-Tony + +Last updated w.r.t. GIT 1.1 + +Linux subsystem maintenance using GIT +------------------------------------- + +My requirements here are to be able to create two public trees: + +1) A "test" tree into which patches are initially placed so that they +can get some exposure when integrated with other ongoing development. +This tree is available to Andrew for pulling into -mm whenever he wants. + +2) A "release" tree into which tested patches are moved for final +sanity checking, and as a vehicle to send them upstream to Linus +(by sending him a "please pull" request.) + +Note that the period of time that each patch spends in the "test" tree +is dependent on the complexity of the change. Since GIT does not support +cherry picking, it is not practical to simply apply all patches to the +test tree and then pull to the release tree as that would leave trivial +patches blocked in the test tree waiting for complex changes to accumulate +enough test time to graduate. + +Back in the BitKeeper days I achieved this by creating small forests of +temporary trees, one tree for each logical grouping of patches, and then +pulling changes from these trees first to the test tree, and then to the +release tree. At first I replicated this in GIT, but then I realised +that I could so this far more efficiently using branches inside a single +GIT repository. + +So here is the step-by-step guide how this all works for me. + +First create your work tree by cloning Linus's public tree: + + $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work + +Change directory into the cloned tree you just created + + $ cd work + +Set up a remotes file so that you can fetch the latest from Linus' master +branch into a local branch named "linus": + + $ cat > .git/remotes/linus + URL: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + Pull: master:linus + ^D + +and create the linus branch: + + $ git branch linus + +The "linus" branch will be used to track the upstream kernel. To update it, +you simply run: + + $ git fetch linus + +you can do this frequently (and it should be safe to do so with pending +work in your tree, but perhaps not if you are in mid-merge). + +If you need to keep track of other public trees, you can add remote branches +for them too: + + $ git branch another + $ cat > .git/remotes/another + URL: ... insert URL here ... + Pull: name-of-branch-in-this-remote-tree:another + ^D + +and run: + + $ git fetch another + +Now create the branches in which you are going to work, these start +out at the current tip of the linus branch. + + $ git branch test linus + $ git branch release linus + +These can be easily kept up to date by merging from the "linus" branch: + + $ git checkout test && git merge "Auto-update from upstream" test linus + $ git checkout release && git merge "Auto-update from upstream" release linus + +Important note! If you have any local changes in these branches, then +this merge will create a commit object in the history (with no local +changes git will simply do a "Fast forward" merge). Many people dislike +the "noise" that this creates in the Linux history, so you should avoid +doing this capriciously in the "release" branch, as these noisy commits +will become part of the permanent history when you ask Linus to pull +from the release branch. + +Set up so that you can push upstream to your public tree (you need to +log-in to the remote system and create an empty tree there before the +first push). + + $ cat > .git/remotes/mytree + URL: master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git + Push: release + Push: test + ^D + +and the push both the test and release trees using: + + $ git push mytree + +or push just one of the test and release branches using: + + $ git push mytree test +or + $ git push mytree release + +Now to apply some patches from the community. Think of a short +snappy name for a branch to hold this patch (or related group of +patches), and create a new branch from the current tip of the +linus branch: + + $ git checkout -b speed-up-spinlocks linus + +Now you apply the patch(es), run some tests, and commit the change(s). If +the patch is a multi-part series, then you should apply each as a separate +commit to this branch. + + $ ... patch ... test ... commit [ ... patch ... test ... commit ]* + +When you are happy with the state of this change, you can pull it into the +"test" branch in preparation to make it public: + + $ git checkout test && git merge "Pull speed-up-spinlock changes" test speed-up-spinlocks + +It is unlikely that you would have any conflicts here ... but you might if you +spent a while on this step and had also pulled new versions from upstream. + +Some time later when enough time has passed and testing done, you can pull the +same branch into the "release" tree ready to go upstream. This is where you +see the value of keeping each patch (or patch series) in its own branch. It +means that the patches can be moved into the "release" tree in any order. + + $ git checkout release && git merge "Pull speed-up-spinlock changes" release speed-up-spinlocks + +After a while, you will have a number of branches, and despite the +well chosen names you picked for each of them, you may forget what +they are for, or what status they are in. To get a reminder of what +changes are in a specific branch, use: + + $ git-whatchanged branchname ^linus | git-shortlog + +To see whether it has already been merged into the test or release branches +use: + + $ git-rev-list branchname ^test +or + $ git-rev-list branchname ^release + +[If this branch has not yet been merged you will see a set of SHA1 values +for the commits, if it has been merged, then there will be no output] + +Once a patch completes the great cycle (moving from test to release, then +pulled by Linus, and finally coming back into your local "linus" branch) +the branch for this change is no longer needed. You detect this when the +output from: + + $ git-rev-list branchname ^linus + +is empty. At this point the branch can be deleted: + + $ git branch -d branchname + +Some changes are so trivial that it is not necessary to create a separate +branch and then merge into each of the test and release branches. For +these changes, just apply directly to the "release" branch, and then +merge that into the "test" branch. + +To create diffstat and shortlog summaries of changes to include in a "please +pull" request to Linus you can use: + + $ git-whatchanged -p release ^linus | diffstat -p1 +and + $ git-whatchanged release ^linus | git-shortlog + + +Here are some of the scripts that I use to simplify all this even further. + +==== update script ==== +# Update a branch in my GIT tree. If the branch to be updated +# is "linus", then pull from kernel.org. Otherwise merge local +# linus branch into test|release branch + +case "$1" in +test|release) + git checkout $1 && git merge "Auto-update from upstream" $1 linus + ;; +linus) + before=$(cat .git/refs/heads/linus) + git fetch linus + after=$(cat .git/refs/heads/linus) + if [ $before != $after ] + then + git-whatchanged $after ^$before | git-shortlog + fi + ;; +*) + echo "Usage: $0 linus|test|release" 1>&2 + exit 1 + ;; +esac + +==== merge script ==== +# Merge a branch into either the test or release branch + +pname=$0 + +usage() +{ + echo "Usage: $pname branch test|release" 1>&2 + exit 1 +} + +if [ ! -f .git/refs/heads/"$1" ] +then + echo "Can't see branch <$1>" 1>&2 + usage +fi + +case "$2" in +test|release) + if [ $(git-rev-list $1 ^$2 | wc -c) -eq 0 ] + then + echo $1 already merged into $2 1>&2 + exit 1 + fi + git checkout $2 && git merge "Pull $1 into $2 branch" $2 $1 + ;; +*) + usage + ;; +esac + +==== status script ==== +# report on status of my ia64 GIT tree + +gb=$(tput setab 2) +rb=$(tput setab 1) +restore=$(tput setab 9) + +if [ `git-rev-list release ^test | wc -c` -gt 0 ] +then + echo $rb Warning: commits in release that are not in test $restore + git-whatchanged release ^test +fi + +for branch in `ls .git/refs/heads` +do + if [ $branch = linus -o $branch = test -o $branch = release ] + then + continue + fi + + echo -n $gb ======= $branch ====== $restore " " + status= + for ref in test release linus + do + if [ `git-rev-list $branch ^$ref | wc -c` -gt 0 ] + then + status=$status${ref:0:1} + fi + done + case $status in + trl) + echo $rb Need to pull into test $restore + ;; + rl) + echo "In test" + ;; + l) + echo "Waiting for linus" + ;; + "") + echo $rb All done $restore + ;; + *) + echo $rb "<$status>" $restore + ;; + esac + git-whatchanged $branch ^linus | git-shortlog +done |