Update documentation to warn users not to create noise in then Linux history by creating pointless "Auto-update from upstream" merge commits. Signed-off-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Junio C Hamano <junkio@cox.net>
		
			
				
	
	
		
			297 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
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
 |