summaryrefslogtreecommitdiff
path: root/Documentation/howto
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/howto')
-rw-r--r--Documentation/howto/dangling-objects.txt109
-rw-r--r--Documentation/howto/isolate-bugs-with-bisect.txt65
-rw-r--r--Documentation/howto/make-dist.txt52
-rw-r--r--Documentation/howto/rebase-and-edit.txt81
-rw-r--r--Documentation/howto/rebase-from-internal-branch.txt165
-rw-r--r--Documentation/howto/rebuild-from-update-hook.txt87
-rw-r--r--Documentation/howto/revert-branch-rebase.txt200
-rw-r--r--Documentation/howto/separating-topic-branches.txt91
-rw-r--r--Documentation/howto/setup-git-server-over-http.txt256
-rw-r--r--Documentation/howto/update-hook-example.txt172
-rw-r--r--Documentation/howto/using-topic-branches.txt296
11 files changed, 1574 insertions, 0 deletions
diff --git a/Documentation/howto/dangling-objects.txt b/Documentation/howto/dangling-objects.txt
new file mode 100644
index 0000000..e82ddae
--- /dev/null
+++ b/Documentation/howto/dangling-objects.txt
@@ -0,0 +1,109 @@
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Subject: Re: Question about fsck-objects output
+Date: Thu, 25 Jan 2007 12:01:06 -0800 (PST)
+Message-ID: <Pine.LNX.4.64.0701251144290.25027@woody.linux-foundation.org>
+Archived-At: <http://permalink.gmane.org/gmane.comp.version-control.git/37754>
+Abstract: Linus describes what dangling objects are, when they
+ are left behind, and how to view their relationship with branch
+ heads in gitk
+
+On Thu, 25 Jan 2007, Larry Streepy wrote:
+
+> Sorry to ask such a basic question, but I can't quite decipher the output of
+> fsck-objects. When I run it, I get this:
+>
+> git fsck-objects
+> dangling commit 2213f6d4dd39ca8baebd0427723723e63208521b
+> dangling commit f0d4e00196bd5ee54463e9ea7a0f0e8303da767f
+> dangling blob 6a6d0b01b3e96d49a8f2c7addd4ef8c3bd1f5761
+>
+>
+> Even after a "repack -a -d" they still exist. The man page has a short
+> explanation, but, at least for me, it wasn't fully enlightening. :-)
+>
+> The man page says that dangling commits could be "root" commits, but since my
+> repo started as a clone of another repo, I don't see how I could have any root
+> commits. Also, the page doesn't really describe what a dangling blob is.
+>
+> So, can someone explain what these artifacts are and if they are a problem
+> that I should be worried about?
+
+The most common situation is that you've rebased a branch (or you have
+pulled from somebody else who rebased a branch, like the "pu" branch in
+the git.git archive itself).
+
+What happens is that the old head of the original branch still exists, as
+does obviously everything it pointed to. The branch pointer itself just
+doesn't, since you replaced it with another one.
+
+However, there are certainly other situations too that cause dangling
+objects. For example, the "dangling blob" situation you have tends to be
+because you did a "git add" of a file, but then, before you actually
+committed it and made it part of the bigger picture, you changed something
+else in that file and committed that *updated* thing - the old state that
+you added originally ends up not being pointed to by any commit/tree, so
+it's now a dangling blob object.
+
+Similarly, when the "recursive" merge strategy runs, and finds that there
+are criss-cross merges and thus more than one merge base (which is fairly
+unusual, but it does happen), it will generate one temporary midway tree
+(or possibly even more, if you had lots of criss-crossing merges and
+more than two merge bases) as a temporary internal merge base, and again,
+those are real objects, but the end result will not end up pointing to
+them, so they end up "dangling" in your repository.
+
+Generally, dangling objects aren't anything to worry about. They can even
+be very useful: if you screw something up, the dangling objects can be how
+you recover your old tree (say, you did a rebase, and realized that you
+really didn't want to - you can look at what dangling objects you have,
+and decide to reset your head to some old dangling state).
+
+For commits, the most useful thing to do with dangling objects tends to be
+to do a simple
+
+ gitk <dangling-commit-sha-goes-here> --not --all
+
+which means exactly what it sounds like: it says that you want to see the
+commit history that is described by the dangling commit(s), but you do NOT
+want to see the history that is described by all your branches and tags
+(which are the things you normally reach). That basically shows you in a
+nice way what the danglign commit was (and notice that it might not be
+just one commit: we only report the "tip of the line" as being dangling,
+but there might be a whole deep and complex commit history that has gotten
+dropped - rebasing will do that).
+
+For blobs and trees, you can't do the same, but you can examine them. You
+can just do
+
+ git show <dangling-blob/tree-sha-goes-here>
+
+to show what the contents of the blob were (or, for a tree, basically what
+the "ls" for that directory was), and that may give you some idea of what
+the operation was that left that dangling object.
+
+Usually, dangling blobs and trees aren't very interesting. They're almost
+always the result of either being a half-way mergebase (the blob will
+often even have the conflict markers from a merge in it, if you have had
+conflicting merges that you fixed up by hand), or simply because you
+interrupted a "git fetch" with ^C or something like that, leaving _some_
+of the new objects in the object database, but just dangling and useless.
+
+Anyway, once you are sure that you're not interested in any dangling
+state, you can just prune all unreachable objects:
+
+ git prune
+
+and they'll be gone. But you should only run "git prune" on a quiescent
+repository - it's kind of like doing a filesystem fsck recovery: you don't
+want to do that while the filesystem is mounted.
+
+(The same is true of "git-fsck-objects" itself, btw - but since
+git-fsck-objects never actually *changes* the repository, it just reports
+on what it found, git-fsck-objects itself is never "dangerous" to run.
+Running it while somebody is actually changing the repository can cause
+confusing and scary messages, but it won't actually do anything bad. In
+contrast, running "git prune" while somebody is actively changing the
+repository is a *BAD* idea).
+
+ Linus
+
diff --git a/Documentation/howto/isolate-bugs-with-bisect.txt b/Documentation/howto/isolate-bugs-with-bisect.txt
new file mode 100644
index 0000000..926bbdc
--- /dev/null
+++ b/Documentation/howto/isolate-bugs-with-bisect.txt
@@ -0,0 +1,65 @@
+From: Linus Torvalds <torvalds () osdl ! org>
+To: git@vger.kernel.org
+Date: 2005-11-08 1:31:34
+Subject: Real-life kernel debugging scenario
+Abstract: Short-n-sweet, Linus tells us how to leverage `git-bisect` to perform
+ bug isolation on a repository where "good" and "bad" revisions are known
+ in order to identify a suspect commit.
+
+
+How To Use git-bisect To Isolate a Bogus Commit
+===============================================
+
+The way to use "git bisect" couldn't be easier.
+
+Figure out what the oldest bad state you know about is (that's usually the
+head of "master", since that's what you just tried to boot and failed at).
+Also, figure out the most recent known-good commit (usually the _previous_
+kernel you ran: and if you've only done a single "pull" in between, it
+will be ORIG_HEAD).
+
+Then do
+
+ git bisect start
+ git bisect bad master <- mark "master" as the bad state
+ git bisect good ORIG_HEAD <- mark ORIG_HEAD as good (or
+ whatever other known-good
+ thing you booted last)
+
+and at this point "git bisect" will churn for a while, and tell you what
+the mid-point between those two commits are, and check that state out as
+the head of the new "bisect" branch.
+
+Compile and reboot.
+
+If it's good, just do
+
+ git bisect good <- mark current head as good
+
+otherwise, reboot into a good kernel instead, and do (surprise surprise,
+git really is very intuitive):
+
+ git bisect bad <- mark current head as bad
+
+and whatever you do, git will select a new half-way point. Do this for a
+while, until git tells you exactly which commit was the first bad commit.
+That's your culprit.
+
+It really works wonderfully well, except for the case where there was
+_another_ commit that broke something in between, like introduced some
+stupid compile error. In that case you should not mark that commit good or
+bad: you should try to find another commit close-by, and do a "git reset
+--hard <newcommit>" to try out _that_ commit instead, and then test that
+instead (and mark it good or bad).
+
+You can do "git bisect visualize" while you do all this to see what's
+going on by starting up gitk on the bisection range.
+
+Finally, once you've figured out exactly which commit was bad, you can
+then go back to the master branch, and try reverting just that commit:
+
+ git checkout master
+ git revert <bad-commit-id>
+
+to verify that the top-of-kernel works with that single commit reverted.
+
diff --git a/Documentation/howto/make-dist.txt b/Documentation/howto/make-dist.txt
new file mode 100644
index 0000000..00e330b
--- /dev/null
+++ b/Documentation/howto/make-dist.txt
@@ -0,0 +1,52 @@
+Date: Fri, 12 Aug 2005 22:39:48 -0700 (PDT)
+From: Linus Torvalds <torvalds@osdl.org>
+To: Dave Jones <davej@redhat.com>
+cc: git@vger.kernel.org
+Subject: Re: Fwd: Re: git checkout -f branch doesn't remove extra files
+Abstract: In this article, Linus talks about building a tarball,
+ incremental patch, and ChangeLog, given a base release and two
+ rc releases, following the convention of giving the patch from
+ the base release and the latest rc, with ChangeLog between the
+ last rc and the latest rc.
+
+On Sat, 13 Aug 2005, Dave Jones wrote:
+>
+> > Git actually has a _lot_ of nifty tools. I didn't realize that people
+> > didn't know about such basic stuff as "git-tar-tree" and "git-ls-files".
+>
+> Maybe its because things are moving so fast :) Or maybe I just wasn't
+> paying attention on that day. (I even read the git changes via RSS,
+> so I should have no excuse).
+
+Well, git-tar-tree has been there since late April - it's actually one of
+those really early commands. I'm pretty sure the RSS feed came later ;)
+
+I use it all the time in doing releases, it's a lot faster than creating a
+tar tree by reading the filesystem (even if you don't have to check things
+out). A hidden pearl.
+
+This is my crappy "release-script":
+
+ [torvalds@g5 ~]$ cat bin/release-script
+ #!/bin/sh
+ stable="$1"
+ last="$2"
+ new="$3"
+ echo "# git-tag v$new"
+ echo "git-tar-tree v$new linux-$new | gzip -9 > ../linux-$new.tar.gz"
+ echo "git-diff-tree -p v$stable v$new | gzip -9 > ../patch-$new.gz"
+ echo "git-rev-list --pretty v$new ^v$last > ../ChangeLog-$new"
+ echo "git-rev-list --pretty=short v$new ^v$last | git-shortlog > ../ShortLog"
+ echo "git-diff-tree -p v$last v$new | git-apply --stat > ../diffstat-$new"
+
+and when I want to do a new kernel release I literally first tag it, and
+then do
+
+ release-script 2.6.12 2.6.13-rc6 2.6.13-rc7
+
+and check that things look sane, and then just cut-and-paste the commands.
+
+Yeah, it's stupid.
+
+ Linus
+
diff --git a/Documentation/howto/rebase-and-edit.txt b/Documentation/howto/rebase-and-edit.txt
new file mode 100644
index 0000000..646c55c
--- /dev/null
+++ b/Documentation/howto/rebase-and-edit.txt
@@ -0,0 +1,81 @@
+Date: Sat, 13 Aug 2005 22:16:02 -0700 (PDT)
+From: Linus Torvalds <torvalds@osdl.org>
+To: Steve French <smfrench@austin.rr.com>
+cc: git@vger.kernel.org
+Subject: Re: sending changesets from the middle of a git tree
+Abstract: In this article, Linus demonstrates how a broken commit
+ in a sequence of commits can be removed by rewinding the head and
+ reapplying selected changes.
+
+On Sat, 13 Aug 2005, Linus Torvalds wrote:
+
+> That's correct. Same things apply: you can move a patch over, and create a
+> new one with a modified comment, but basically the _old_ commit will be
+> immutable.
+
+Let me clarify.
+
+You can entirely _drop_ old branches, so commits may be immutable, but
+nothing forces you to keep them. Of course, when you drop a commit, you'll
+always end up dropping all the commits that depended on it, and if you
+actually got somebody else to pull that commit you can't drop it from
+_their_ repository, but undoing things is not impossible.
+
+For example, let's say that you've made a mess of things: you've committed
+three commits "old->a->b->c", and you notice that "a" was broken, but you
+want to save "b" and "c". What you can do is
+
+ # Create a branch "broken" that is the current code
+ # for reference
+ git branch broken
+
+ # Reset the main branch to three parents back: this
+ # effectively undoes the three top commits
+ git reset HEAD^^^
+ git checkout -f
+
+ # Check the result visually to make sure you know what's
+ # going on
+ gitk --all
+
+ # Re-apply the two top ones from "broken"
+ #
+ # First "parent of broken" (aka b):
+ git-diff-tree -p broken^ | git-apply --index
+ git commit --reedit=broken^
+
+ # Then "top of broken" (aka c):
+ git-diff-tree -p broken | git-apply --index
+ git commit --reedit=broken
+
+and you've now re-applied (and possibly edited the comments) the two
+commits b/c, and commit "a" is basically gone (it still exists in the
+"broken" branch, of course).
+
+Finally, check out the end result again:
+
+ # Look at the new commit history
+ gitk --all
+
+to see that everything looks sensible.
+
+And then, you can just remove the broken branch if you decide you really
+don't want it:
+
+ # remove 'broken' branch
+ git branch -d broken
+
+ # Prune old objects if you're really really sure
+ git prune
+
+And yeah, I'm sure there are other ways of doing this. And as usual, the
+above is totally untested, and I just wrote it down in this email, so if
+I've done something wrong, you'll have to figure it out on your own ;)
+
+ Linus
+-
+To unsubscribe from this list: send the line "unsubscribe git" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+
+
diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt
new file mode 100644
index 0000000..3b3a5c2
--- /dev/null
+++ b/Documentation/howto/rebase-from-internal-branch.txt
@@ -0,0 +1,165 @@
+From: Junio C Hamano <junkio@cox.net>
+To: git@vger.kernel.org
+Cc: Petr Baudis <pasky@suse.cz>, Linus Torvalds <torvalds@osdl.org>
+Subject: Re: sending changesets from the middle of a git tree
+Date: Sun, 14 Aug 2005 18:37:39 -0700
+Abstract: In this article, JC talks about how he rebases the
+ public "pu" branch using the core GIT tools when he updates
+ the "master" branch, and how "rebase" works. Also discussed
+ is how this applies to individual developers who sends patches
+ upstream.
+
+Petr Baudis <pasky@suse.cz> writes:
+
+> Dear diary, on Sun, Aug 14, 2005 at 09:57:13AM CEST, I got a letter
+> where Junio C Hamano <junkio@cox.net> told me that...
+>> Linus Torvalds <torvalds@osdl.org> writes:
+>>
+>> > Junio, maybe you want to talk about how you move patches from your "pu"
+>> > branch to the real branches.
+>>
+> Actually, wouldn't this be also precisely for what StGIT is intended to?
+
+Exactly my feeling. I was sort of waiting for Catalin to speak
+up. With its basing philosophical ancestry on quilt, this is
+the kind of task StGIT is designed to do.
+
+I just have done a simpler one, this time using only the core
+GIT tools.
+
+I had a handful commits that were ahead of master in pu, and I
+wanted to add some documentation bypassing my usual habit of
+placing new things in pu first. At the beginning, the commit
+ancestry graph looked like this:
+
+ *"pu" head
+ master --> #1 --> #2 --> #3
+
+So I started from master, made a bunch of edits, and committed:
+
+ $ git checkout master
+ $ cd Documentation; ed git.txt ...
+ $ cd ..; git add Documentation/*.txt
+ $ git commit -s
+
+After the commit, the ancestry graph would look like this:
+
+ *"pu" head
+ master^ --> #1 --> #2 --> #3
+ \
+ \---> master
+
+The old master is now master^ (the first parent of the master).
+The new master commit holds my documentation updates.
+
+Now I have to deal with "pu" branch.
+
+This is the kind of situation I used to have all the time when
+Linus was the maintainer and I was a contributor, when you look
+at "master" branch being the "maintainer" branch, and "pu"
+branch being the "contributor" branch. Your work started at the
+tip of the "maintainer" branch some time ago, you made a lot of
+progress in the meantime, and now the maintainer branch has some
+other commits you do not have yet. And "git rebase" was written
+with the explicit purpose of helping to maintain branches like
+"pu". You _could_ merge master to pu and keep going, but if you
+eventually want to cherrypick and merge some but not necessarily
+all changes back to the master branch, it often makes later
+operations for _you_ easier if you rebase (i.e. carry forward
+your changes) "pu" rather than merge. So I ran "git rebase":
+
+ $ git checkout pu
+ $ git rebase master pu
+
+What this does is to pick all the commits since the current
+branch (note that I now am on "pu" branch) forked from the
+master branch, and forward port these changes.
+
+ master^ --> #1 --> #2 --> #3
+ \ *"pu" head
+ \---> master --> #1' --> #2' --> #3'
+
+The diff between master^ and #1 is applied to master and
+committed to create #1' commit with the commit information (log,
+author and date) taken from commit #1. On top of that #2' and #3'
+commits are made similarly out of #2 and #3 commits.
+
+Old #3 is not recorded in any of the .git/refs/heads/ file
+anymore, so after doing this you will have dangling commit if
+you ran fsck-cache, which is normal. After testing "pu", you
+can run "git prune" to get rid of those original three commits.
+
+While I am talking about "git rebase", I should talk about how
+to do cherrypicking using only the core GIT tools.
+
+Let's go back to the earlier picture, with different labels.
+
+You, as an individual developer, cloned upstream repository and
+made a couple of commits on top of it.
+
+ *your "master" head
+ upstream --> #1 --> #2 --> #3
+
+You would want changes #2 and #3 incorporated in the upstream,
+while you feel that #1 may need further improvements. So you
+prepare #2 and #3 for e-mail submission.
+
+ $ git format-patch master^^ master
+
+This creates two files, 0001-XXXX.patch and 0002-XXXX.patch. Send
+them out "To: " your project maintainer and "Cc: " your mailing
+list. You could use contributed script git-send-email if
+your host has necessary perl modules for this, but your usual
+MUA would do as long as it does not corrupt whitespaces in the
+patch.
+
+Then you would wait, and you find out that the upstream picked
+up your changes, along with other changes.
+
+ where *your "master" head
+ upstream --> #1 --> #2 --> #3
+ used \
+ to be \--> #A --> #2' --> #3' --> #B --> #C
+ *upstream head
+
+The two commits #2' and #3' in the above picture record the same
+changes your e-mail submission for #2 and #3 contained, but
+probably with the new sign-off line added by the upstream
+maintainer and definitely with different committer and ancestry
+information, they are different objects from #2 and #3 commits.
+
+You fetch from upstream, but not merge.
+
+ $ git fetch upstream
+
+This leaves the updated upstream head in .git/FETCH_HEAD but
+does not touch your .git/HEAD nor .git/refs/heads/master.
+You run "git rebase" now.
+
+ $ git rebase FETCH_HEAD master
+
+Earlier, I said that rebase applies all the commits from your
+branch on top of the upstream head. Well, I lied. "git rebase"
+is a bit smarter than that and notices that #2 and #3 need not
+be applied, so it only applies #1. The commit ancestry graph
+becomes something like this:
+
+ where *your old "master" head
+ upstream --> #1 --> #2 --> #3
+ used \ your new "master" head*
+ to be \--> #A --> #2' --> #3' --> #B --> #C --> #1'
+ *upstream
+ head
+
+Again, "git prune" would discard the disused commits #1-#3 and
+you continue on starting from the new "master" head, which is
+the #1' commit.
+
+-jc
+
+-
+To unsubscribe from this list: send the line "unsubscribe git" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+
+
diff --git a/Documentation/howto/rebuild-from-update-hook.txt b/Documentation/howto/rebuild-from-update-hook.txt
new file mode 100644
index 0000000..02621b5
--- /dev/null
+++ b/Documentation/howto/rebuild-from-update-hook.txt
@@ -0,0 +1,87 @@
+Subject: [HOWTO] Using post-update hook
+Message-ID: <7vy86o6usx.fsf@assigned-by-dhcp.cox.net>
+From: Junio C Hamano <junkio@cox.net>
+Date: Fri, 26 Aug 2005 18:19:10 -0700
+Abstract: In this how-to article, JC talks about how he
+ uses the post-update hook to automate git documentation page
+ shown at http://www.kernel.org/pub/software/scm/git/docs/.
+
+The pages under http://www.kernel.org/pub/software/scm/git/docs/
+are built from Documentation/ directory of the git.git project
+and needed to be kept up-to-date. The www.kernel.org/ servers
+are mirrored and I was told that the origin of the mirror is on
+the machine $some.kernel.org, on which I was given an account
+when I took over git maintainership from Linus.
+
+The directories relevant to this how-to are these two:
+
+ /pub/scm/git/git.git/ The public git repository.
+ /pub/software/scm/git/docs/ The HTML documentation page.
+
+So I made a repository to generate the documentation under my
+home directory over there.
+
+ $ cd
+ $ mkdir doc-git && cd doc-git
+ $ git clone /pub/scm/git/git.git/ docgen
+
+What needs to happen is to update the $HOME/doc-git/docgen/
+working tree, build HTML docs there and install the result in
+/pub/software/scm/git/docs/ directory. So I wrote a little
+script:
+
+ $ cat >dododoc.sh <<\EOF
+ #!/bin/sh
+ cd $HOME/doc-git/docgen || exit
+
+ unset GIT_DIR
+
+ git pull /pub/scm/git/git.git/ master &&
+ cd Documentation &&
+ make install-webdoc
+ EOF
+
+Initially I used to run this by hand whenever I push into the
+public git repository. Then I did a cron job that ran twice a
+day. The current round uses the post-update hook mechanism,
+like this:
+
+ $ cat >/pub/scm/git/git.git/hooks/post-update <<\EOF
+ #!/bin/sh
+ #
+ # An example hook script to prepare a packed repository for use over
+ # dumb transports.
+ #
+ # To enable this hook, make this file executable by "chmod +x post-update".
+
+ case " $* " in
+ *' refs/heads/master '*)
+ echo $HOME/doc-git/dododoc.sh | at now
+ ;;
+ esac
+ exec git-update-server-info
+ EOF
+ $ chmod +x /pub/scm/git/git.git/hooks/post-update
+
+There are four things worth mentioning:
+
+ - The update-hook is run after the repository accepts a "git
+ push", under my user privilege. It is given the full names
+ of refs that have been updated as arguments. My post-update
+ runs the dododoc.sh script only when the master head is
+ updated.
+
+ - When update-hook is run, GIT_DIR is set to '.' by the calling
+ receive-pack. This is inherited by the dododoc.sh run via
+ the "at" command, and needs to be unset; otherwise, "git
+ pull" it does into $HOME/doc-git/docgen/ repository would not
+ work correctly.
+
+ - The stdout of update hook script is not connected to git
+ push; I run the heavy part of the command inside "at", to
+ receive the execution report via e-mail.
+
+ - This is still crude and does not protect against simultaneous
+ make invocations stomping on each other. I would need to add
+ some locking mechanism for this.
+
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
new file mode 100644
index 0000000..d10476b
--- /dev/null
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -0,0 +1,200 @@
+From: Junio C Hamano <junkio@cox.net>
+To: git@vger.kernel.org
+Subject: [HOWTO] Reverting an existing commit
+Abstract: In this article, JC gives a small real-life example of using
+ 'git revert' command, and using a temporary branch and tag for safety
+ and easier sanity checking.
+Date: Mon, 29 Aug 2005 21:39:02 -0700
+Content-type: text/asciidoc
+Message-ID: <7voe7g3uop.fsf@assigned-by-dhcp.cox.net>
+
+Reverting an existing commit
+============================
+
+One of the changes I pulled into the 'master' branch turns out to
+break building GIT with GCC 2.95. While they were well intentioned
+portability fixes, keeping things working with gcc-2.95 was also
+important. Here is what I did to revert the change in the 'master'
+branch and to adjust the 'pu' branch, using core GIT tools and
+barebone Porcelain.
+
+First, prepare a throw-away branch in case I screw things up.
+
+------------------------------------------------
+$ git checkout -b revert-c99 master
+------------------------------------------------
+
+Now I am on the 'revert-c99' branch. Let's figure out which commit to
+revert. I happen to know that the top of the 'master' branch is a
+merge, and its second parent (i.e. foreign commit I merged from) has
+the change I would want to undo. Further I happen to know that that
+merge introduced 5 commits or so:
+
+------------------------------------------------
+$ git show-branch --more=4 master master^2 | head
+* [master] Merge refs/heads/portable from http://www.cs.berkeley....
+ ! [master^2] Replace C99 array initializers with code.
+--
+- [master] Merge refs/heads/portable from http://www.cs.berkeley....
+*+ [master^2] Replace C99 array initializers with code.
+*+ [master^2~1] Replace unsetenv() and setenv() with older putenv().
+*+ [master^2~2] Include sys/time.h in daemon.c.
+*+ [master^2~3] Fix ?: statements.
+*+ [master^2~4] Replace zero-length array decls with [].
+* [master~1] tutorial note about git branch
+------------------------------------------------
+
+The '--more=4' above means "after we reach the merge base of refs,
+show until we display four more common commits". That last commit
+would have been where the "portable" branch was forked from the main
+git.git repository, so this would show everything on both branches
+since then. I just limited the output to the first handful using
+'head'.
+
+Now I know 'master^2~4' (pronounce it as "find the second parent of
+the 'master', and then go four generations back following the first
+parent") is the one I would want to revert. Since I also want to say
+why I am reverting it, the '-n' flag is given to 'git revert'. This
+prevents it from actually making a commit, and instead 'git revert'
+leaves the commit log message it wanted to use in '.msg' file:
+
+------------------------------------------------
+$ git revert -n master^2~4
+$ cat .msg
+Revert "Replace zero-length array decls with []."
+
+This reverts 6c5f9baa3bc0d63e141e0afc23110205379905a4 commit.
+$ git diff HEAD ;# to make sure what we are reverting makes sense.
+$ make CC=gcc-2.95 clean test ;# make sure it fixed the breakage.
+$ make clean test ;# make sure it did not cause other breakage.
+------------------------------------------------
+
+The reverted change makes sense (from reading the 'diff' output), does
+fix the problem (from 'make CC=gcc-2.95' test), and does not cause new
+breakage (from the last 'make test'). I'm ready to commit:
+
+------------------------------------------------
+$ git commit -a -s ;# read .msg into the log,
+ # and explain why I am reverting.
+------------------------------------------------
+
+I could have screwed up in any of the above steps, but in the worst
+case I could just have done 'git checkout master' to start over.
+Fortunately I did not have to; what I have in the current branch
+'revert-c99' is what I want. So merge that back into 'master':
+
+------------------------------------------------
+$ git checkout master
+$ git resolve master revert-c99 fast ;# this should be a fast forward
+Updating from 10d781b9caa4f71495c7b34963bef137216f86a8 to e3a693c...
+ cache.h | 8 ++++----
+ commit.c | 2 +-
+ ls-files.c | 2 +-
+ receive-pack.c | 2 +-
+ server-info.c | 2 +-
+ 5 files changed, 8 insertions(+), 8 deletions(-)
+------------------------------------------------
+
+The 'fast' in the above 'git resolve' is not a magic. I knew this
+'resolve' would result in a fast forward merge, and if not, there is
+something very wrong (so I would do 'git reset' on the 'master' branch
+and examine the situation). When a fast forward merge is done, the
+message parameter to 'git resolve' is discarded, because no new commit
+is created. You could have said 'junk' or 'nothing' there as well.
+
+There is no need to redo the test at this point. We fast forwarded
+and we know 'master' matches 'revert-c99' exactly. In fact:
+
+------------------------------------------------
+$ git diff master..revert-c99
+------------------------------------------------
+
+says nothing.
+
+Then we rebase the 'pu' branch as usual.
+
+------------------------------------------------
+$ git checkout pu
+$ git tag pu-anchor pu
+$ git rebase master
+* Applying: Redo "revert" using three-way merge machinery.
+First trying simple merge strategy to cherry-pick.
+Finished one cherry-pick.
+* Applying: Remove git-apply-patch-script.
+First trying simple merge strategy to cherry-pick.
+Simple cherry-pick fails; trying Automatic cherry-pick.
+Removing Documentation/git-apply-patch-script.txt
+Removing git-apply-patch-script
+Finished one cherry-pick.
+* Applying: Document "git cherry-pick" and "git revert"
+First trying simple merge strategy to cherry-pick.
+Finished one cherry-pick.
+* Applying: mailinfo and applymbox updates
+First trying simple merge strategy to cherry-pick.
+Finished one cherry-pick.
+* Applying: Show commits in topo order and name all commits.
+First trying simple merge strategy to cherry-pick.
+Finished one cherry-pick.
+* Applying: More documentation updates.
+First trying simple merge strategy to cherry-pick.
+Finished one cherry-pick.
+------------------------------------------------
+
+The temporary tag 'pu-anchor' is me just being careful, in case 'git
+rebase' screws up. After this, I can do these for sanity check:
+
+------------------------------------------------
+$ git diff pu-anchor..pu ;# make sure we got the master fix.
+$ make CC=gcc-2.95 clean test ;# make sure it fixed the breakage.
+$ make clean test ;# make sure it did not cause other breakage.
+------------------------------------------------
+
+Everything is in the good order. I do not need the temporary branch
+nor tag anymore, so remove them:
+
+------------------------------------------------
+$ rm -f .git/refs/tags/pu-anchor
+$ git branch -d revert-c99
+------------------------------------------------
+
+It was an emergency fix, so we might as well merge it into the
+'release candidate' branch, although I expect the next release would
+be some days off:
+
+------------------------------------------------
+$ git checkout rc
+$ git pull . master
+Packing 0 objects
+Unpacking 0 objects
+
+* committish: e3a693c... refs/heads/master from .
+Trying to merge e3a693c... into 8c1f5f0... using 10d781b...
+Committed merge 7fb9b7262a1d1e0a47bbfdcbbcf50ce0635d3f8f
+ cache.h | 8 ++++----
+ commit.c | 2 +-
+ ls-files.c | 2 +-
+ receive-pack.c | 2 +-
+ server-info.c | 2 +-
+ 5 files changed, 8 insertions(+), 8 deletions(-)
+------------------------------------------------
+
+And the final repository status looks like this:
+
+------------------------------------------------
+$ git show-branch --more=1 master pu rc
+! [master] Revert "Replace zero-length array decls with []."
+ ! [pu] git-repack: Add option to repack all objects.
+ * [rc] Merge refs/heads/master from .
+---
+ + [pu] git-repack: Add option to repack all objects.
+ + [pu~1] More documentation updates.
+ + [pu~2] Show commits in topo order and name all commits.
+ + [pu~3] mailinfo and applymbox updates
+ + [pu~4] Document "git cherry-pick" and "git revert"
+ + [pu~5] Remove git-apply-patch-script.
+ + [pu~6] Redo "revert" using three-way merge machinery.
+ - [rc] Merge refs/heads/master from .
+++* [master] Revert "Replace zero-length array decls with []."
+ - [rc~1] Merge refs/heads/master from .
+... [master~1] Merge refs/heads/portable from http://www.cs.berkeley....
+------------------------------------------------
diff --git a/Documentation/howto/separating-topic-branches.txt b/Documentation/howto/separating-topic-branches.txt
new file mode 100644
index 0000000..090e2c9
--- /dev/null
+++ b/Documentation/howto/separating-topic-branches.txt
@@ -0,0 +1,91 @@
+From: Junio C Hamano <junkio@cox.net>
+Subject: Separating topic branches
+Abstract: In this article, JC describes how to separate topic branches.
+
+This text was originally a footnote to a discussion about the
+behaviour of the git diff commands.
+
+Often I find myself doing that [running diff against something other
+than HEAD] while rewriting messy development history. For example, I
+start doing some work without knowing exactly where it leads, and end
+up with a history like this:
+
+ "master"
+ o---o
+ \ "topic"
+ o---o---o---o---o---o
+
+At this point, "topic" contains something I know I want, but it
+contains two concepts that turned out to be completely independent.
+And often, one topic component is larger than the other. It may
+contain more than two topics.
+
+In order to rewrite this mess to be more manageable, I would first do
+"diff master..topic", to extract the changes into a single patch, start
+picking pieces from it to get logically self-contained units, and
+start building on top of "master":
+
+ $ git diff master..topic >P.diff
+ $ git checkout -b topicA master
+ ... pick and apply pieces from P.diff to build
+ ... commits on topicA branch.
+
+ o---o---o
+ / "topicA"
+ o---o"master"
+ \ "topic"
+ o---o---o---o---o---o
+
+Before doing each commit on "topicA" HEAD, I run "diff HEAD"
+before update-index the affected paths, or "diff --cached HEAD"
+after. Also I would run "diff --cached master" to make sure
+that the changes are only the ones related to "topicA". Usually
+I do this for smaller topics first.
+
+After that, I'd do the remainder of the original "topic", but
+for that, I do not start from the patchfile I extracted by
+comparing "master" and "topic" I used initially. Still on
+"topicA", I extract "diff topic", and use it to rebuild the
+other topic:
+
+ $ git diff -R topic >P.diff ;# --cached also would work fine
+ $ git checkout -b topicB master
+ ... pick and apply pieces from P.diff to build
+ ... commits on topicB branch.
+
+ "topicB"
+ o---o---o---o---o
+ /
+ /o---o---o
+ |/ "topicA"
+ o---o"master"
+ \ "topic"
+ o---o---o---o---o---o
+
+After I am done, I'd try a pretend-merge between "topicA" and
+"topicB" in order to make sure I have not missed anything:
+
+ $ git pull . topicA ;# merge it into current "topicB"
+ $ git diff topic
+ "topicB"
+ o---o---o---o---o---* (pretend merge)
+ / /
+ /o---o---o----------'
+ |/ "topicA"
+ o---o"master"
+ \ "topic"
+ o---o---o---o---o---o
+
+The last diff better not to show anything other than cleanups
+for crufts. Then I can finally clean things up:
+
+ $ git branch -D topic
+ $ git reset --hard HEAD^ ;# nuke pretend merge
+
+ "topicB"
+ o---o---o---o---o
+ /
+ /o---o---o
+ |/ "topicA"
+ o---o"master"
+
diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt
new file mode 100644
index 0000000..8eadc20
--- /dev/null
+++ b/Documentation/howto/setup-git-server-over-http.txt
@@ -0,0 +1,256 @@
+From: Rutger Nijlunsing <rutger@nospam.com>
+Subject: Setting up a git repository which can be pushed into and pulled from over HTTP.
+Date: Thu, 10 Aug 2006 22:00:26 +0200
+
+Since Apache is one of those packages people like to compile
+themselves while others prefer the bureaucrat's dream Debian, it is
+impossible to give guidelines which will work for everyone. Just send
+some feedback to the mailing list at git@vger.kernel.org to get this
+document tailored to your favorite distro.
+
+
+What's needed:
+
+- Have an Apache web-server
+
+ On Debian:
+ $ apt-get install apache2
+ To get apache2 by default started,
+ edit /etc/default/apache2 and set NO_START=0
+
+- can edit the configuration of it.
+
+ This could be found under /etc/httpd, or refer to your Apache documentation.
+
+ On Debian: this means being able to edit files under /etc/apache2
+
+- can restart it.
+
+ 'apachectl --graceful' might do. If it doesn't, just stop and
+ restart apache. Be warning that active connections to your server
+ might be aborted by this.
+
+ On Debian:
+ $ /etc/init.d/apache2 restart
+ or
+ $ /etc/init.d/apache2 force-reload
+ (which seems to do the same)
+ This adds symlinks from the /etc/apache2/mods-enabled to
+ /etc/apache2/mods-available.
+
+- have permissions to chown a directory
+
+- have git installed at the server _and_ client
+
+In effect, this probably means you're going to be root.
+
+
+Step 1: setup a bare GIT repository
+-----------------------------------
+
+At the time of writing, git-http-push cannot remotely create a GIT
+repository. So we have to do that at the server side with git. Another
+option would be to generate an empty repository at the client and copy
+it to the server with WebDAV. But then you're probably the first to
+try that out :)
+
+Create the directory under the DocumentRoot of the directories served
+by Apache. As an example we take /usr/local/apache2, but try "grep
+DocumentRoot /where/ever/httpd.conf" to find your root:
+
+ $ cd /usr/local/apache/htdocs
+ $ mkdir my-new-repo.git
+
+ On Debian:
+
+ $ cd /var/www
+ $ mkdir my-new-repo.git
+
+
+Initialize a bare repository
+
+ $ cd my-new-repo.git
+ $ git --bare init
+
+
+Change the ownership to your web-server's credentials. Use "grep ^User
+httpd.conf" and "grep ^Group httpd.conf" to find out:
+
+ $ chown -R www.www .
+
+ On Debian:
+
+ $ chown -R www-data.www-data .
+
+
+If you do not know which user Apache runs as, you can alternatively do
+a "chmod -R a+w .", inspect the files which are created later on, and
+set the permissions appropriately.
+
+Restart apache2, and check whether http://server/my-new-repo.git gives
+a directory listing. If not, check whether apache started up
+successfully.
+
+
+Step 2: enable DAV on this repository
+-------------------------------------
+
+First make sure the dav_module is loaded. For this, insert in httpd.conf:
+
+ LoadModule dav_module libexec/httpd/libdav.so
+ AddModule mod_dav.c
+
+Also make sure that this line exists which is the file used for
+locking DAV operations:
+
+ DAVLockDB "/usr/local/apache2/temp/DAV.lock"
+
+ On Debian these steps can be performed with:
+
+ Enable the dav and dav_fs modules of apache:
+ $ a2enmod dav_fs
+ (just to be sure. dav_fs might be unneeded, I don't know)
+ $ a2enmod dav
+ The DAV lock is located in /etc/apache2/mods-available/dav_fs.conf:
+ DAVLockDB /var/lock/apache2/DAVLock
+
+Of course, it can point somewhere else, but the string is actually just a
+prefix in some Apache configurations, and therefore the _directory_ has to
+be writable by the user Apache runs as.
+
+Then, add something like this to your httpd.conf
+
+ <Location /my-new-repo.git>
+ DAV on
+ AuthType Basic
+ AuthName "Git"
+ AuthUserFile /usr/local/apache2/conf/passwd.git
+ Require valid-user
+ </Location>
+
+ On Debian:
+ Create (or add to) /etc/apache2/conf.d/git.conf :
+
+ <Location /my-new-repo.git>
+ DAV on
+ AuthType Basic
+ AuthName "Git"
+ AuthUserFile /etc/apache2/passwd.git
+ Require valid-user
+ </Location>
+
+ Debian automatically reads all files under /etc/apach2/conf.d.
+
+The password file can be somewhere else, but it has to be readable by
+Apache and preferably not readable by the world.
+
+Create this file by
+ $ htpasswd -c /usr/local/apache2/conf/passwd.git <user>
+
+ On Debian:
+ $ htpasswd -c /etc/apache2/passwd.git <user>
+
+You will be asked a password, and the file is created. Subsequent calls
+to htpasswd should omit the '-c' option, since you want to append to the
+existing file.
+
+You need to restart Apache.
+
+Now go to http://<username>@<servername>/my-new-repo.git in your
+browser to check whether it asks for a password and accepts the right
+password.
+
+On Debian:
+
+ To test the WebDAV part, do:
+
+ $ apt-get install litmus
+ $ litmus http://<servername>/my-new-repo.git <username> <password>
+
+ Most tests should pass.
+
+A command line tool to test WebDAV is cadaver.
+
+If you're into Windows, from XP onwards Internet Explorer supports
+WebDAV. For this, do Internet Explorer -> Open Location ->
+http://<servername>/my-new-repo.git [x] Open as webfolder -> login .
+
+
+Step 3: setup the client
+------------------------
+
+Make sure that you have HTTP support, i.e. your git was built with curl.
+The easiest way to check is to look for the executable 'git-http-push'.
+
+Then, add the following to your $HOME/.netrc (you can do without, but will be
+asked to input your password a _lot_ of times):
+
+ machine <servername>
+ login <username>
+ password <password>
+
+...and set permissions:
+ chmod 600 ~/.netrc
+
+If you want to access the web-server by its IP, you have to type that in,
+instead of the server name.
+
+To check whether all is OK, do:
+
+ curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/
+
+...this should give a directory listing in HTML of /var/www/my-new-repo.git .
+
+
+Now, add the remote in your existing repository which contains the project
+you want to export:
+
+ $ git-config remote.upload.url \
+ http://<username>@<servername>/my-new-repo.git/
+
+It is important to put the last '/'; Without it, the server will send
+a redirect which git-http-push does not (yet) understand, and git-http-push
+will repeat the request infinitely.
+
+
+Step 4: make the initial push
+-----------------------------
+
+From your client repository, do
+
+ $ git push upload master
+
+This pushes branch 'master' (which is assumed to be the branch you
+want to export) to repository called 'upload', which we previously
+defined with git-config.
+
+
+Troubleshooting:
+----------------
+
+If git-http-push says
+
+ Error: no DAV locking support on remote repo http://...
+
+then it means the web-server did not accept your authentication. Make sure
+that the user name and password matches in httpd.conf, .netrc and the URL
+you are uploading to.
+
+If git-http-push shows you an error (22/502) when trying to MOVE a blob,
+it means that your web-server somehow does not recognize its name in the
+request; This can happen when you start Apache, but then disable the
+network interface. A simple restart of Apache helps.
+
+Errors like (22/502) are of format (curl error code/http error
+code). So (22/404) means something like 'not found' at the server.
+
+Reading /usr/local/apache2/logs/error_log is often helpful.
+
+ On Debian: Read /var/log/apache2/error.log instead.
+
+
+Debian References: http://www.debian-administration.org/articles/285
+
+Authors
+ Johannes Schindelin <Johannes.Schindelin@gmx.de>
+ Rutger Nijlunsing <git@wingding.demon.nl>
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
new file mode 100644
index 0000000..3a33696
--- /dev/null
+++ b/Documentation/howto/update-hook-example.txt
@@ -0,0 +1,172 @@
+From: Junio C Hamano <junkio@cox.net> and Carl Baldwin <cnb@fc.hp.com>
+Subject: control access to branches.
+Date: Thu, 17 Nov 2005 23:55:32 -0800
+Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net>
+Abstract: An example hooks/update script is presented to
+ implement repository maintenance policies, such as who can push
+ into which branch and who can make a tag.
+
+When your developer runs git-push into the repository,
+git-receive-pack is run (either locally or over ssh) as that
+developer, so is hooks/update script. Quoting from the relevant
+section of the documentation:
+
+ Before each ref is updated, if $GIT_DIR/hooks/update file exists
+ and executable, it is called with three parameters:
+
+ $GIT_DIR/hooks/update refname sha1-old sha1-new
+
+ The refname parameter is relative to $GIT_DIR; e.g. for the
+ master head this is "refs/heads/master". Two sha1 are the
+ object names for the refname before and after the update. Note
+ that the hook is called before the refname is updated, so either
+ sha1-old is 0{40} (meaning there is no such ref yet), or it
+ should match what is recorded in refname.
+
+So if your policy is (1) always require fast-forward push
+(i.e. never allow "git-push repo +branch:branch"), (2) you
+have a list of users allowed to update each branch, and (3) you
+do not let tags to be overwritten, then you can use something
+like this as your hooks/update script.
+
+[jc: editorial note. This is a much improved version by Carl
+since I posted the original outline]
+
+-- >8 -- beginning of script -- >8 --
+
+#!/bin/bash
+
+umask 002
+
+# If you are having trouble with this access control hook script
+# you can try setting this to true. It will tell you exactly
+# why a user is being allowed/denied access.
+
+verbose=false
+
+# Default shell globbing messes things up downstream
+GLOBIGNORE=*
+
+function grant {
+ $verbose && echo >&2 "-Grant- $1"
+ echo grant
+ exit 0
+}
+
+function deny {
+ $verbose && echo >&2 "-Deny- $1"
+ echo deny
+ exit 1
+}
+
+function info {
+ $verbose && echo >&2 "-Info- $1"
+}
+
+# Implement generic branch and tag policies.
+# - Tags should not be updated once created.
+# - Branches should only be fast-forwarded.
+case "$1" in
+ refs/tags/*)
+ [ -f "$GIT_DIR/$1" ] &&
+ deny >/dev/null "You can't overwrite an existing tag"
+ ;;
+ refs/heads/*)
+ # No rebasing or rewinding
+ if expr "$2" : '0*$' >/dev/null; then
+ info "The branch '$1' is new..."
+ else
+ # updating -- make sure it is a fast forward
+ mb=$(git-merge-base "$2" "$3")
+ case "$mb,$2" in
+ "$2,$mb") info "Update is fast-forward" ;;
+ *) deny >/dev/null "This is not a fast-forward update." ;;
+ esac
+ fi
+ ;;
+ *)
+ deny >/dev/null \
+ "Branch is not under refs/heads or refs/tags. What are you trying to do?"
+ ;;
+esac
+
+# Implement per-branch controls based on username
+allowed_users_file=$GIT_DIR/info/allowed-users
+username=$(id -u -n)
+info "The user is: '$username'"
+
+if [ -f "$allowed_users_file" ]; then
+ rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
+ while read head_pattern user_patterns; do
+ matchlen=$(expr "$1" : "$head_pattern")
+ if [ "$matchlen" == "${#1}" ]; then
+ info "Found matching head pattern: '$head_pattern'"
+ for user_pattern in $user_patterns; do
+ info "Checking user: '$username' against pattern: '$user_pattern'"
+ matchlen=$(expr "$username" : "$user_pattern")
+ if [ "$matchlen" == "${#username}" ]; then
+ grant "Allowing user: '$username' with pattern: '$user_pattern'"
+ fi
+ done
+ deny "The user is not in the access list for this branch"
+ fi
+ done
+ )
+ case "$rc" in
+ grant) grant >/dev/null "Granting access based on $allowed_users_file" ;;
+ deny) deny >/dev/null "Denying access based on $allowed_users_file" ;;
+ *) ;;
+ esac
+fi
+
+allowed_groups_file=$GIT_DIR/info/allowed-groups
+groups=$(id -G -n)
+info "The user belongs to the following groups:"
+info "'$groups'"
+
+if [ -f "$allowed_groups_file" ]; then
+ rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
+ while read head_pattern group_patterns; do
+ matchlen=$(expr "$1" : "$head_pattern")
+ if [ "$matchlen" == "${#1}" ]; then
+ info "Found matching head pattern: '$head_pattern'"
+ for group_pattern in $group_patterns; do
+ for groupname in $groups; do
+ info "Checking group: '$groupname' against pattern: '$group_pattern'"
+ matchlen=$(expr "$groupname" : "$group_pattern")
+ if [ "$matchlen" == "${#groupname}" ]; then
+ grant "Allowing group: '$groupname' with pattern: '$group_pattern'"
+ fi
+ done
+ done
+ deny "None of the user's groups are in the access list for this branch"
+ fi
+ done
+ )
+ case "$rc" in
+ grant) grant >/dev/null "Granting access based on $allowed_groups_file" ;;
+ deny) deny >/dev/null "Denying access based on $allowed_groups_file" ;;
+ *) ;;
+ esac
+fi
+
+deny >/dev/null "There are no more rules to check. Denying access"
+
+-- >8 -- end of script -- >8 --
+
+This uses two files, $GIT_DIR/info/allowed-users and
+allowed-groups, to describe which heads can be pushed into by
+whom. The format of each file would look like this:
+
+ refs/heads/master junio
+ refs/heads/cogito$ pasky
+ refs/heads/bw/ linus
+ refs/heads/tmp/ *
+ refs/tags/v[0-9]* junio
+
+With this, Linus can push or create "bw/penguin" or "bw/zebra"
+or "bw/panda" branches, Pasky can do only "cogito", and JC can
+do master branch and make versioned tags. And anybody can do
+tmp/blah branches.
+
+------------
diff --git a/Documentation/howto/using-topic-branches.txt b/Documentation/howto/using-topic-branches.txt
new file mode 100644
index 0000000..2c98194
--- /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