Merge branch 'maint'

* maint:
  git-cvsserver: read from git with -z to get non-ASCII pathnames.
diff --git a/.gitignore b/.gitignore
index 25eb463..4c8c8e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@
 git-fetch-pack
 git-findtags
 git-fmt-merge-msg
+git-for-each-ref
 git-format-patch
 git-fsck-objects
 git-get-tar-commit-id
@@ -74,6 +75,7 @@
 git-mv
 git-pack-redundant
 git-pack-objects
+git-pack-refs
 git-parse-remote
 git-patch-id
 git-peek-remote
@@ -105,6 +107,7 @@
 git-show
 git-show-branch
 git-show-index
+git-show-ref
 git-ssh-fetch
 git-ssh-pull
 git-ssh-push
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ee51fe3..9d3c71c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -71,12 +71,16 @@
 	expect HEAD to be a symbolic link.
 
 core.logAllRefUpdates::
-	If true, `git-update-ref` will append a line to
-	"$GIT_DIR/logs/<ref>" listing the new SHA1 and the date/time
-	of the update.	If the file does not exist it will be
-	created automatically.	This information can be used to
-	determine what commit was the tip of a branch "2 days ago".
-	This value is false by default (no logging).
+	Updates to a ref <ref> is logged to the file
+	"$GIT_DIR/logs/<ref>", by appending the new and old
+	SHA1, the date/time and the reason of the update, but
+	only when the file exists.  If this configuration
+	variable is set to true, missing "$GIT_DIR/logs/<ref>"
+	file is automatically created for branch heads.
+
+	This information can be used to determine what commit
+	was the tip of a branch "2 days ago".  This value is
+	false by default (no automated creation of log files).
 
 core.repositoryFormatVersion::
 	Internal variable identifying the repository format and layout
@@ -242,6 +246,10 @@
 	The default set of "refspec" for gitlink:git-push[1]. See
 	gitlink:git-push[1].
 
+repack.usedeltabaseoffset::
+	Allow gitlink:git-repack[1] to create packs that uses
+	delta-base offset.  Defaults to false.
+
 show.difftree::
 	The default gitlink:git-diff-tree[1] arguments to be used
 	for gitlink:git-show[1].
@@ -293,7 +301,16 @@
 	The configuration variables in the 'imap' section are described
 	in gitlink:git-imap-send[1].
 
-receive.denyNonFastforwads::
+receive.unpackLimit::
+	If the number of objects received in a push is below this
+	limit then the objects will be unpacked into loose object
+	files. However if the number of received objects equals or
+	exceeds this limit then the received pack will be stored as
+	a pack, after adding any missing delta bases.  Storing the
+	pack from a push can make the push operation complete faster,
+	especially on slow filesystems.
+
+receive.denyNonFastForwards::
 	If set to true, git-receive-pack will deny a ref update which is
 	not a fast forward. Use this to prevent such an update via a push,
 	even if that push is forced. This configuration variable is
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 7b7b9e8..e112172 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -16,6 +16,11 @@
 	The width of the filename part can be controlled by
 	giving another width to it separated by a comma.
 
+--numstat::
+	Similar to \--stat, but shows number of added and
+	deleted lines in decimal notation and pathname without
+	abbreviation, to make it more machine friendly.
+
 --summary::
 	Output a condensed summary of extended header information
 	such as creations, renames and mode changes.
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index e1f8944..bdfc666 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -7,7 +7,9 @@
 
 SYNOPSIS
 --------
-'git-blame' [-c] [-l] [-t] [-S <revs-file>] [--] <file> [<rev>]
+[verse]
+'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>]
+            [-M] [-C] [-C] [--since=<date>] [<rev>] [--] <file>
 
 DESCRIPTION
 -----------
@@ -15,6 +17,8 @@
 Annotates each line in the given file with information from the revision which
 last modified the line. Optionally, start annotating from the given revision.
 
+Also it can limit the range of lines annotated.
+
 This report doesn't tell you anything about lines which have been deleted or
 replaced; you need to use a tool such as gitlink:git-diff[1] or the "pickaxe"
 interface briefly mentioned in the following paragraph.
@@ -36,6 +40,9 @@
 -c, --compatibility::
 	Use the same output mode as gitlink:git-annotate[1] (Default: off).
 
+-L n,m::
+	Annotate only the specified line range (lines count from 1).
+
 -l, --long::
 	Show long rev (Default: off).
 
@@ -45,17 +52,116 @@
 -S, --rev-file <revs-file>::
 	Use revs from revs-file instead of calling gitlink:git-rev-list[1].
 
+-f, --show-name::
+	Show filename in the original commit.  By default
+	filename is shown if there is any line that came from a
+	file with different name, due to rename detection.
+
+-n, --show-number::
+	Show line number in the original commit (Default: off).
+
+-p, --porcelain::
+	Show in a format designed for machine consumption.
+
+-M::
+	Detect moving lines in the file as well.  When a commit
+	moves a block of lines in a file (e.g. the original file
+	has A and then B, and the commit changes it to B and
+	then A), traditional 'blame' algorithm typically blames
+	the lines that were moved up (i.e. B) to the parent and
+	assigns blame to the lines that were moved down (i.e. A)
+	to the child commit.  With this option, both groups of
+	lines are blamed on the parent.
+
+-C::
+	In addition to `-M`, detect lines copied from other
+	files that were modified in the same commit.  This is
+	useful when you reorganize your program and move code
+	around across files.  When this option is given twice,
+	the command looks for copies from all other files in the
+	parent for the commit that creates the file in addition.
+
 -h, --help::
 	Show help message.
 
 
+THE PORCELAIN FORMAT
+--------------------
+
+In this format, each line is output after a header; the
+header at the minumum has the first line which has:
+
+- 40-byte SHA-1 of the commit the line is attributed to;
+- the line number of the line in the original file;
+- the line number of the line in the final file;
+- on a line that starts a group of line from a different
+  commit than the previous one, the number of lines in this
+  group.  On subsequent lines this field is absent.
+
+This header line is followed by the following information
+at least once for each commit:
+
+- author name ("author"), email ("author-mail"), time
+  ("author-time"), and timezone ("author-tz"); similarly
+  for committer.
+- filename in the commit the line is attributed to.
+- the first line of the commit log message ("summary").
+
+The contents of the actual line is output after the above
+header, prefixed by a TAB. This is to allow adding more
+header elements later.
+
+
+SPECIFIYING RANGES
+------------------
+
+Unlike `git-blame` and `git-annotate` in older git, the extent
+of annotation can be limited to both line ranges and revision
+ranges.  When you are interested in finding the origin for
+ll. 40-60 for file `foo`, you can use `-L` option like this:
+
+	git blame -L 40,60 foo
+
+Also you can use regular expression to specify the line range.
+
+	git blame -L '/^sub hello {/,/^}$/' foo
+
+would limit the annotation to the body of `hello` subroutine.
+
+When you are not interested in changes older than the version
+v2.6.18, or changes older than 3 weeks, you can use revision
+range specifiers  similar to `git-rev-list`:
+
+	git blame v2.6.18.. -- foo
+	git blame --since=3.weeks -- foo
+
+When revision range specifiers are used to limit the annotation,
+lines that have not changed since the range boundary (either the
+commit v2.6.18 or the most recent commit that is more than 3
+weeks old in the above example) are blamed for that range
+boundary commit.
+
+A particularly useful way is to see if an added file have lines
+created by copy-and-paste from existing files.  Sometimes this
+indicates that the developer was being sloppy and did not
+refactor the code properly.  You can first find the commit that
+introduced the file with:
+
+	git log --diff-filter=A --pretty=short -- foo
+
+and then annotate the change between the commit and its
+parents, using `commit{caret}!` notation:
+
+	git blame -C -C -f $commit^! -- foo
+
+
 SEE ALSO
 --------
 gitlink:git-annotate[1]
 
 AUTHOR
 ------
-Written by Fredrik Kuivinen <freku045@student.liu.se>.
+Written by Junio C Hamano <junkio@cox.net>
 
 GIT
 ---
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index bff9aa6..3e6cd88 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -32,7 +32,8 @@
 -k::
 	Do not invoke 'git-unpack-objects' on received data, but
 	create a single packfile out of it instead, and store it
-	in the object database.
+	in the object database. If provided twice then the pack is
+	locked against repacking.
 
 --exec=<git-upload-pack>::
 	Use this to specify the path to 'git-upload-pack' on the
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
new file mode 100644
index 0000000..2bf6aef
--- /dev/null
+++ b/Documentation/git-for-each-ref.txt
@@ -0,0 +1,185 @@
+git-for-each-ref(1)
+===================
+
+NAME
+----
+git-for-each-ref - Output information on each ref
+
+SYNOPSIS
+--------
+'git-for-each-ref' [--count=<count>]\* [--shell|--perl|--python] [--sort=<key>]\* [--format=<format>] [<pattern>]
+
+DESCRIPTION
+-----------
+
+Iterate over all refs that match `<pattern>` and show them
+according to the given `<format>`, after sorting them according
+to the given set of `<key>`.  If `<max>` is given, stop after
+showing that many refs.  The interporated values in `<format>`
+can optionally be quoted as string literals in the specified
+host language allowing their direct evaluation in that language.
+
+OPTIONS
+-------
+<count>::
+	By default the command shows all refs that match
+	`<pattern>`.  This option makes it stop after showing
+	that many refs.
+
+<key>::
+	A field name to sort on.  Prefix `-` to sort in
+	descending order of the value.  When unspecified,
+	`refname` is used.  More than one sort keys can be
+	given.
+
+<format>::
+	A string that interpolates `%(fieldname)` from the
+	object pointed at by a ref being shown.  If `fieldname`
+	is prefixed with an asterisk (`*`) and the ref points
+	at a tag object, the value for the field in the object
+	tag refers is used.  When unspecified, defaults to
+	`%(objectname) SPC %(objecttype) TAB %(refname)`.
+	It also interpolates `%%` to `%`, and `%xx` where `xx`
+	are hex digits interpolates to character with hex code
+	`xx`; for example `%00` interpolates to `\0` (NUL),
+	`%09` to `\t` (TAB) and `%0a` to `\n` (LF).
+
+<pattern>::
+	If given, the name of the ref is matched against this
+	using fnmatch(3).  Refs that do not match the pattern
+	are not shown.
+
+--shell, --perl, --python::
+	If given, strings that substitute `%(fieldname)`
+	placeholders are quoted as string literals suitable for
+	the specified host language.  This is meant to produce
+	a scriptlet that can directly be `eval`ed.
+
+
+FIELD NAMES
+-----------
+
+Various values from structured fields in referenced objects can
+be used to interpolate into the resulting output, or as sort
+keys.
+
+For all objects, the following names can be used:
+
+refname::
+	The name of the ref (the part after $GIT_DIR/refs/).
+
+objecttype::
+	The type of the object (`blob`, `tree`, `commit`, `tag`).
+
+objectsize::
+	The size of the object (the same as `git-cat-file -s` reports).
+
+objectname::
+	The object name (aka SHA-1).
+
+In addition to the above, for commit and tag objects, the header
+field names (`tree`, `parent`, `object`, `type`, and `tag`) can
+be used to specify the value in the header field.
+
+Fields that have name-email-date tuple as its value (`author`,
+`committer`, and `tagger`) can be suffixed with `name`, `email`,
+and `date` to extract the named component.
+
+The first line of the message in a commit and tag object is
+`subject`, the remaining lines are `body`.  The whole message
+is `contents`.
+
+For sorting purposes, fields with numeric values sort in numeric
+order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
+All other fields are used to sort in their byte-value order.
+
+In any case, a field name that refers to a field inapplicable to
+the object referred by the ref does not cause an error.  It
+returns an empty string instead.
+
+
+EXAMPLES
+--------
+
+An example directly producing formatted text.  Show the most recent
+3 tagged commits::
+
+------------
+#!/bin/sh
+
+git-for-each-ref --count=3 --sort='-*authordate' \
+--format='From: %(*authorname) %(*authoremail)
+Subject: %(*subject)
+Date: %(*authordate)
+Ref: %(*refname)
+
+%(*body)
+' 'refs/tags'
+------------
+
+
+A simple example showing the use of shell eval on the output,
+demonstrating the use of --shell.  List the prefixes of all heads::
+------------
+#!/bin/sh
+
+git-for-each-ref --shell --format="ref=%(refname)" refs/heads | \
+while read entry
+do
+	eval "$entry"
+	echo `dirname $ref`
+done
+------------
+
+
+A bit more elaborate report on tags, demonstrating that the format
+may be an entire script::
+------------
+#!/bin/sh
+
+fmt='
+	r=%(refname)
+	t=%(*objecttype)
+	T=${r#refs/tags/}
+
+	o=%(*objectname)
+	n=%(*authorname)
+	e=%(*authoremail)
+	s=%(*subject)
+	d=%(*authordate)
+	b=%(*body)
+
+	kind=Tag
+	if test "z$t" = z
+	then
+		# could be a lightweight tag
+		t=%(objecttype)
+		kind="Lightweight tag"
+		o=%(objectname)
+		n=%(authorname)
+		e=%(authoremail)
+		s=%(subject)
+		d=%(authordate)
+		b=%(body)
+	fi
+	echo "$kind $T points at a $t object $o"
+	if test "z$t" = zcommit
+	then
+		echo "The commit was authored by $n $e
+at $d, and titled
+
+    $s
+
+Its message reads as:
+"
+		echo "$b" | sed -e "s/^/    /"
+		echo
+	fi
+'
+
+eval=`git-for-each-ref --shell --format="$fmt" \
+	--sort='*objecttype' \
+	--sort=-taggerdate \
+	refs/tags`
+eval "$eval"
+------------
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index d8af4d9..bfbece9 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -14,7 +14,7 @@
 	   [-v | --invert-match] [-h|-H] [--full-name]
 	   [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
 	   [-n] [-l | --files-with-matches] [-L | --files-without-match]
-	   [-c | --count]
+	   [-c | --count] [--all-match]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
 	   [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
 	   [<tree>...]
@@ -96,6 +96,11 @@
 	higher precedence than `--or`.  `-e` has to be used for all
 	patterns.
 
+--all-match::
+	When giving multiple pattern expressions combined with `--or`,
+	this flag is specified to limit the match to files that
+	have lines to match all of them.
+
 `<tree>...`::
 	Search blobs in the trees for specified patterns.
 
@@ -111,6 +116,10 @@
 	Looks for a line that has `#define` and either `MAX_PATH` or
 	`PATH_MAX`.
 
+git grep --all-match -e NODE -e Unexpected::
+	Looks for a line that has `NODE` or `Unexpected` in
+	files that have lines that match both.
+
 Author
 ------
 Originally written by Linus Torvalds <torvalds@osdl.org>, later
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 71ce557..2229ee8 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git-index-pack' [-o <index-file>] <pack-file>
+'git-index-pack' [-v] [-o <index-file>] <pack-file>
+'git-index-pack' --stdin [--fix-thin] [--keep] [-v] [-o <index-file>] [<pack-file>]
 
 
 DESCRIPTION
@@ -21,6 +22,9 @@
 
 OPTIONS
 -------
+-v::
+	Be verbose about what is going on, including progress status.
+
 -o <index-file>::
 	Write the generated pack index into the specified
 	file.  Without this option the name of pack index
@@ -29,6 +33,52 @@
 	fails if the name of packed archive does not end
 	with .pack).
 
+--stdin::
+	When this flag is provided, the pack is read from stdin
+	instead and a copy is then written to <pack-file>. If
+	<pack-file> is not specified, the pack is written to
+	objects/pack/ directory of the current git repository with
+	a default name determined from the pack content.  If
+	<pack-file> is not specified consider using --keep to
+	prevent a race condition between this process and
+	gitlink::git-repack[1] .
+
+--fix-thin::
+	It is possible for gitlink:git-pack-objects[1] to build
+	"thin" pack, which records objects in deltified form based on
+	objects not included in the pack to reduce network traffic.
+	Those objects are expected to be present on the receiving end
+	and they must be included in the pack for that pack to be self
+	contained and indexable. Without this option any attempt to
+	index a thin pack will fail. This option only makes sense in
+	conjunction with --stdin.
+
+--keep::
+	Before moving the index into its final destination
+	create an empty .keep file for the associated pack file.
+	This option is usually necessary with --stdin to prevent a
+	simultaneous gitlink:git-repack[1] process from deleting
+	the newly constructed pack and index before refs can be
+	updated to use objects contained in the pack.
+
+--keep='why'::
+	Like --keep create a .keep file before moving the index into
+	its final destination, but rather than creating an empty file
+	place 'why' followed by an LF into the .keep file.  The 'why'
+	message can later be searched for within all .keep files to
+	locate any which have outlived their usefulness.
+
+
+Note
+----
+
+Once the index has been created, the list of object names is sorted
+and the SHA1 hash of that list is printed to stdout. If --stdin was
+also used then this is prefixed by either "pack\t", or "keep\t" if a
+new .keep file was successfully created. This is useful to remove a
+.keep file used as a lock to prevent the race with gitlink:git-repack[1]
+mentioned above.
+
 
 Author
 ------
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 944c9e1..fdc6f97 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -9,8 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
-	[--local] [--incremental] [--window=N] [--depth=N]
+'git-pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
+	[--local] [--incremental] [--window=N] [--depth=N] [--all-progress]
 	[--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
 
 
@@ -99,6 +99,23 @@
         Only create a packed archive if it would contain at
         least one object.
 
+--progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless -q
+	is specified. This flag forces progress status even if
+	the standard error stream is not directed to a terminal.
+
+--all-progress::
+	When --stdout is specified then progress report is
+	displayed during the object count and deltification phases
+	but inhibited during the write-out phase. The reason is
+	that in some cases the output stream is directly linked
+	to another command which may wish to display progress
+	status of its own as it processes incoming pack data.
+	This flag is like --progress except that it forces progress
+	report for the write-out phase as well even if --stdout is
+	used.
+
 -q::
 	This flag makes the command not to report its progress
 	on the standard error stream.
@@ -110,6 +127,17 @@
 	This flag tells the command not to reuse existing deltas
 	but compute them from scratch.
 
+--delta-base-offset::
+	A packed archive can express base object of a delta as
+	either 20-byte object name or as an offset in the
+	stream, but older version of git does not understand the
+	latter.  By default, git-pack-objects only uses the
+	former format for better compatibility.  This option
+	allows the command to use the latter format for
+	compactness.  Depending on the average delta chain
+	length, this option typically shrinks the resulting
+	packfile by 3-5 per-cent.
+
 
 Author
 ------
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
new file mode 100644
index 0000000..5da5105
--- /dev/null
+++ b/Documentation/git-pack-refs.txt
@@ -0,0 +1,54 @@
+git-pack-refs(1)
+================
+
+NAME
+----
+git-pack-refs - Pack heads and tags for efficient repository access
+
+SYNOPSIS
+--------
+'git-pack-refs' [--all] [--prune]
+
+DESCRIPTION
+-----------
+
+Traditionally, tips of branches and tags (collectively known as
+'refs') were stored one file per ref under `$GIT_DIR/refs`
+directory.  While many branch tips tend to be updated often,
+most tags and some branch tips are never updated.  When a
+repository has hundreds or thousands of tags, this
+one-file-per-ref format both wastes storage and hurts
+performance.
+
+This command is used to solve the storage and performance
+problem by stashing the refs in a single file,
+`$GIT_DIR/packed-refs`.  When a ref is missing from the
+traditional `$GIT_DIR/refs` hierarchy, it is looked up in this
+file and used if found.
+
+Subsequent updates to branches always creates new file under
+`$GIT_DIR/refs` hierarchy.
+
+OPTIONS
+-------
+
+\--all::
+
+The command by default packs all tags and leaves branch tips
+alone.  This is because branches are expected to be actively
+developed and packing their tips does not help performance.
+This option causes branch tips to be packed as well.  Useful for
+a repository with many branches of historical interests.
+
+\--prune::
+
+After packing the refs, remove loose refs under `$GIT_DIR/refs`
+hierarchy.  This should probably become default.
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 51577fc..2a5aea7 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-pull - Pull and merge from another repository
+git-pull - Pull and merge from another repository or a local branch
 
 
 SYNOPSIS
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 878eb6f..03e867a 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -7,7 +7,7 @@
 
 SYNOPSIS
 --------
-'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-v] [--merge] [--onto <newbase>] <upstream> [<branch>]
 
 'git-rebase' --continue | --skip | --abort
 
@@ -170,6 +170,9 @@
 	is used instead (`git-merge-recursive` when merging a single
 	head, `git-merge-octopus` otherwise).  This implies --merge.
 
+-v, \--verbose::
+	Display a diffstat of what changed upstream since the last rebase.
+
 include::merge-strategies.txt[]
 
 NOTES
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index d2eaa09..0fa47e3 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -67,6 +67,20 @@
 	The default value for both --window and --depth is 10.
 
 
+Configuration
+-------------
+
+When configuration variable `repack.UseDeltaBaseOffset` is set
+for the repository, the command passes `--delta-base-offset`
+option to `git-pack-objects`; this typically results in slightly
+smaller packs, but the generated packs are incompatible with
+versions of git older than (and including) v1.4.3; do not set
+the variable in a repository that older version of git needs to
+be able to read (this includes repositories from which packs can
+be copied out over http or rsync, and people who obtained packs
+that way can try to use older git with it).
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index 8a1ab61..8199615 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -3,19 +3,19 @@
 
 NAME
 ----
-git-repo-config - Get and set options in .git/config
+git-repo-config - Get and set repository or global options.
 
 
 SYNOPSIS
 --------
 [verse]
-'git-repo-config' [type] name [value [value_regex]]
-'git-repo-config' [type] --replace-all name [value [value_regex]]
-'git-repo-config' [type] --get name [value_regex]
-'git-repo-config' [type] --get-all name [value_regex]
-'git-repo-config' [type] --unset name [value_regex]
-'git-repo-config' [type] --unset-all name [value_regex]
-'git-repo-config' -l | --list
+'git-repo-config' [--global] [type] name [value [value_regex]]
+'git-repo-config' [--global] [type] --replace-all name [value [value_regex]]
+'git-repo-config' [--global] [type] --get name [value_regex]
+'git-repo-config' [--global] [type] --get-all name [value_regex]
+'git-repo-config' [--global] [type] --unset name [value_regex]
+'git-repo-config' [--global] [type] --unset-all name [value_regex]
+'git-repo-config' [--global] -l | --list
 
 DESCRIPTION
 -----------
@@ -41,8 +41,9 @@
 . Can not write to .git/config,
 . no section was provided,
 . the section or key is invalid,
-. you try to unset an option which does not exist, or
-. you try to unset/set an option for which multiple lines match.
+. you try to unset an option which does not exist,
+. you try to unset/set an option for which multiple lines match, or
+. you use --global option without $HOME being properly set.
 
 
 OPTIONS
@@ -64,14 +65,17 @@
 --get-regexp::
 	Like --get-all, but interprets the name as a regular expression.
 
+--global::
+	Use global ~/.gitconfig file rather than the repository .git/config.
+
 --unset::
-	Remove the line matching the key from .git/config.
+	Remove the line matching the key from config file.
 
 --unset-all::
-	Remove all matching lines from .git/config.
+	Remove all matching lines from config file.
 
 -l, --list::
-	List all variables set in .git/config.
+	List all variables set in config file.
 
 
 ENVIRONMENT
@@ -79,6 +83,7 @@
 
 GIT_CONFIG::
 	Take the configuration from the given file instead of .git/config.
+	Using the "--global" option forces this to ~/.gitconfig.
 
 GIT_CONFIG_LOCAL::
 	Currently the same as $GIT_CONFIG; when Git will support global
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index ed938aa..4eaf5a0 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -239,14 +239,21 @@
 It it the set of commits that are reachable from either one of
 `r1` or `r2` but not from both.
 
-Here are a few examples:
+Two other shorthands for naming a set that is formed by a commit
+and its parent commits exists.  `r1{caret}@` notation means all
+parents of `r1`.  `r1{caret}!` includes commit `r1` but excludes
+its all parents.
+
+Here are a handful examples:
 
    D                A B D
    D F              A B C D F
-   ^A G		    B D
+   ^A G             B D
    ^A F             B C F
    G...I            C D F G I
-   ^B G I	    C D F G I
+   ^B G I           C D F G I
+   F^@              A B C
+   F^! H            D F H
 
 Author
 ------
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 481b3f5..4c8d907 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -66,8 +66,13 @@
 	all that is output.
 
 --smtp-server::
-	If set, specifies the outgoing SMTP server to use.  Defaults to
-	localhost.
+	If set, specifies the outgoing SMTP server to use.  A full
+	pathname of a sendmail-like program can be specified instead;
+	the program must support the `-i` option.  Default value can
+	be specified by the 'sendemail.smtpserver' configuration
+	option; the built-in default is `/usr/sbin/sendmail` or
+	`/usr/lib/sendmail` if such program is available, or
+	`localhost` otherwise.
 
 --subject::
    	Specify the initial subject of the email thread.
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
new file mode 100644
index 0000000..5973a82
--- /dev/null
+++ b/Documentation/git-show-ref.txt
@@ -0,0 +1,156 @@
+git-show-ref(1)
+===============
+
+NAME
+----
+git-show-ref - List references in a local repository
+
+SYNOPSIS
+--------
+[verse]
+'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
+	     [-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
+
+DESCRIPTION
+-----------
+
+Displays references available in a local repository along with the associated
+commit IDs. Results can be filtered using a pattern and tags can be
+dereferenced into object IDs. Additionally, it can be used to test whether a
+particular ref exists.
+
+Use of this utility is encouraged in favor of directly accessing files under
+in the `.git` directory.
+
+OPTIONS
+-------
+
+-h, --head::
+
+	Show the HEAD reference.
+
+--tags, --heads::
+
+	Limit to only "refs/heads" and "refs/tags", respectively.  These
+	options are not mutually exclusive; when given both, references stored
+	in "refs/heads" and "refs/tags" are displayed.
+
+-d, --dereference::
+
+	Dereference tags into object IDs as well. They will be shown with "^{}"
+	appended.
+
+-s, --hash::
+
+	Only show the SHA1 hash, not the reference name. When also using
+	--dereference the dereferenced tag will still be shown after the SHA1.
+
+--verify::
+
+	Enable stricter reference checking by requiring an exact ref path.
+	Aside from returning an error code of 1, it will also print an error
+	message if '--quiet' was not specified.
+
+--abbrev, --abbrev=len::
+
+	Abbreviate the object name.  When using `--hash`, you do
+	not have to say `--hash --abbrev`; `--hash=len` would do.
+
+-q, --quiet::
+
+	Do not print any results to stdout. When combined with '--verify' this
+	can be used to silently check if a reference exists.
+
+<pattern>::
+
+	Show references matching one or more patterns.
+
+OUTPUT
+------
+
+The output is in the format: '<SHA-1 ID>' '<space>' '<reference name>'.
+
+-----------------------------------------------------------------------------
+$ git show-ref --head --dereference
+832e76a9899f560a90ffd62ae2ce83bbeff58f54 HEAD
+832e76a9899f560a90ffd62ae2ce83bbeff58f54 refs/heads/master
+832e76a9899f560a90ffd62ae2ce83bbeff58f54 refs/heads/origin
+3521017556c5de4159da4615a39fa4d5d2c279b5 refs/tags/v0.99.9c
+6ddc0964034342519a87fe013781abf31c6db6ad refs/tags/v0.99.9c^{}
+055e4ae3ae6eb344cbabf2a5256a49ea66040131 refs/tags/v1.0rc4
+423325a2d24638ddcc82ce47be5e40be550f4507 refs/tags/v1.0rc4^{}
+...
+-----------------------------------------------------------------------------
+
+When using --hash (and not --dereference) the output format is: '<SHA-1 ID>'
+
+-----------------------------------------------------------------------------
+$ git show-ref --heads --hash
+2e3ba0114a1f52b47df29743d6915d056be13278
+185008ae97960c8d551adcd9e23565194651b5d1
+03adf42c988195b50e1a1935ba5fcbc39b2b029b
+...
+-----------------------------------------------------------------------------
+
+EXAMPLE
+-------
+
+To show all references called "master", whether tags or heads or anything
+else, and regardless of how deep in the reference naming hierarchy they are,
+use:
+
+-----------------------------------------------------------------------------
+	git show-ref master
+-----------------------------------------------------------------------------
+
+This will show "refs/heads/master" but also "refs/remote/other-repo/master",
+if such references exists.
+
+When using the '--verify' flag, the command requires an exact path:
+
+-----------------------------------------------------------------------------
+	git show-ref --verify refs/heads/master
+-----------------------------------------------------------------------------
+
+will only match the exact branch called "master".
+
+If nothing matches, gitlink:git-show-ref[1] will return an error code of 1,
+and in the case of verification, it will show an error message.
+
+For scripting, you can ask it to be quiet with the "--quiet" flag, which
+allows you to do things like
+
+-----------------------------------------------------------------------------
+	git-show-ref --quiet --verify -- "refs/heads/$headname" ||
+		echo "$headname is not a valid branch"
+-----------------------------------------------------------------------------
+
+to check whether a particular branch exists or not (notice how we don't
+actually want to show any results, and we want to use the full refname for it
+in order to not trigger the problem with ambiguous partial matches).
+
+To show only tags, or only proper branch heads, use "--tags" and/or "--heads"
+respectively (using both means that it shows tags and heads, but not other
+random references under the refs/ subdirectory).
+
+To do automatic tag object dereferencing, use the "-d" or "--dereference"
+flag, so you can do
+
+-----------------------------------------------------------------------------
+	git show-ref --tags --dereference
+-----------------------------------------------------------------------------
+
+to get a listing of all tags together with what they dereference.
+
+SEE ALSO
+--------
+gitlink:git-ls-remote[1], gitlink:git-peek-remote[1]
+
+AUTHORS
+-------
+Written by Linus Torvalds <torvalds@osdl.org>.
+Man page by Jonas Fonseca <fonseca@diku.dk>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index e062030..71bcb79 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -7,7 +7,7 @@
 
 SYNOPSIS
 --------
-'git-update-ref' [-m <reason>] <ref> <newvalue> [<oldvalue>]
+'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
 
 DESCRIPTION
 -----------
@@ -20,7 +20,9 @@
 the current value of the <ref> matches <oldvalue>.
 E.g. `git-update-ref refs/heads/master <newvalue> <oldvalue>`
 updates the master branch head to <newvalue> only if its current
-value is <oldvalue>.
+value is <oldvalue>.  You can specify 40 "0" or an empty string
+as <oldvalue> to make sure that the ref you are creating does
+not exist.
 
 It also allows a "ref" file to be a symbolic pointer to another
 ref file by starting with the four-byte header sequence of
@@ -49,6 +51,10 @@
 ref symlink to some other tree, if you have copied a whole
 archive by creating a symlink tree).
 
+With `-d` flag, it deletes the named <ref> after verifying it
+still contains <oldvalue>.
+
+
 Logging Updates
 ---------------
 If config parameter "core.logAllRefUpdates" is true or the file
diff --git a/Documentation/git.txt b/Documentation/git.txt
index b00607e..52bc05a 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -72,185 +72,6 @@
 We divide git into high level ("porcelain") commands and low level
 ("plumbing") commands.
 
-Low-level commands (plumbing)
------------------------------
-
-Although git includes its
-own porcelain layer, its low-level commands are sufficient to support
-development of alternative porcelains.  Developers of such porcelains
-might start by reading about gitlink:git-update-index[1] and
-gitlink:git-read-tree[1].
-
-We divide the low-level commands into commands that manipulate objects (in
-the repository, index, and working tree), commands that interrogate and
-compare objects, and commands that move objects and references between
-repositories.
-
-Manipulation commands
-~~~~~~~~~~~~~~~~~~~~~
-gitlink:git-apply[1]::
-	Reads a "diff -up1" or git generated patch file and
-	applies it to the working tree.
-
-gitlink:git-checkout-index[1]::
-	Copy files from the index to the working tree.
-
-gitlink:git-commit-tree[1]::
-	Creates a new commit object.
-
-gitlink:git-hash-object[1]::
-	Computes the object ID from a file.
-
-gitlink:git-index-pack[1]::
-	Build pack idx file for an existing packed archive.
-
-gitlink:git-init-db[1]::
-	Creates an empty git object database, or reinitialize an
-	existing one.
-
-gitlink:git-merge-index[1]::
-	Runs a merge for files needing merging.
-
-gitlink:git-mktag[1]::
-	Creates a tag object.
-
-gitlink:git-mktree[1]::
-	Build a tree-object from ls-tree formatted text.
-
-gitlink:git-pack-objects[1]::
-	Creates a packed archive of objects.
-
-gitlink:git-prune-packed[1]::
-	Remove extra objects that are already in pack files.
-
-gitlink:git-read-tree[1]::
-	Reads tree information into the index.
-
-gitlink:git-repo-config[1]::
-	Get and set options in .git/config.
-
-gitlink:git-unpack-objects[1]::
-	Unpacks objects out of a packed archive.
-
-gitlink:git-update-index[1]::
-	Registers files in the working tree to the index.
-
-gitlink:git-write-tree[1]::
-	Creates a tree from the index.
-
-
-Interrogation commands
-~~~~~~~~~~~~~~~~~~~~~~
-
-gitlink:git-cat-file[1]::
-	Provide content or type/size information for repository objects.
-
-gitlink:git-describe[1]::
-	Show the most recent tag that is reachable from a commit.
-
-gitlink:git-diff-index[1]::
-	Compares content and mode of blobs between the index and repository.
-
-gitlink:git-diff-files[1]::
-	Compares files in the working tree and the index.
-
-gitlink:git-diff-stages[1]::
-	Compares two "merge stages" in the index.
-
-gitlink:git-diff-tree[1]::
-	Compares the content and mode of blobs found via two tree objects.
-
-gitlink:git-fsck-objects[1]::
-	Verifies the connectivity and validity of the objects in the database.
-
-gitlink:git-ls-files[1]::
-	Information about files in the index and the working tree.
-
-gitlink:git-ls-tree[1]::
-	Displays a tree object in human readable form.
-
-gitlink:git-merge-base[1]::
-	Finds as good common ancestors as possible for a merge.
-
-gitlink:git-name-rev[1]::
-	Find symbolic names for given revs.
-
-gitlink:git-pack-redundant[1]::
-	Find redundant pack files.
-
-gitlink:git-rev-list[1]::
-	Lists commit objects in reverse chronological order.
-
-gitlink:git-show-index[1]::
-	Displays contents of a pack idx file.
-
-gitlink:git-tar-tree[1]::
-	Creates a tar archive of the files in the named tree object.
-
-gitlink:git-unpack-file[1]::
-	Creates a temporary file with a blob's contents.
-
-gitlink:git-var[1]::
-	Displays a git logical variable.
-
-gitlink:git-verify-pack[1]::
-	Validates packed git archive files.
-
-In general, the interrogate commands do not touch the files in
-the working tree.
-
-
-Synching repositories
-~~~~~~~~~~~~~~~~~~~~~
-
-gitlink:git-fetch-pack[1]::
-	Updates from a remote repository (engine for ssh and
-	local transport).
-
-gitlink:git-http-fetch[1]::
-	Downloads a remote git repository via HTTP by walking
-	commit chain.
-
-gitlink:git-local-fetch[1]::
-	Duplicates another git repository on a local system by
-	walking commit chain.
-
-gitlink:git-peek-remote[1]::
-	Lists references on a remote repository using
-	upload-pack protocol (engine for ssh and local
-	transport).
-
-gitlink:git-receive-pack[1]::
-	Invoked by 'git-send-pack' to receive what is pushed to it.
-
-gitlink:git-send-pack[1]::
-	Pushes to a remote repository, intelligently.
-
-gitlink:git-http-push[1]::
-	Push missing objects using HTTP/DAV.
-
-gitlink:git-shell[1]::
-	Restricted shell for GIT-only SSH access.
-
-gitlink:git-ssh-fetch[1]::
-	Pulls from a remote repository over ssh connection by
-	walking commit chain.
-
-gitlink:git-ssh-upload[1]::
-	Helper "server-side" program used by git-ssh-fetch.
-
-gitlink:git-update-server-info[1]::
-	Updates auxiliary information on a dumb server to help
-	clients discover references and packs on it.
-
-gitlink:git-upload-archive[1]::
-	Invoked by 'git-archive' to send a generated archive.
-
-gitlink:git-upload-pack[1]::
-	Invoked by 'git-fetch-pack' to push
-	what are asked for.
-
-
 High-level commands (porcelain)
 -------------------------------
 
@@ -320,8 +141,11 @@
 gitlink:git-mv[1]::
 	Move or rename a file, a directory, or a symlink.
 
+gitlink:git-pack-refs[1]::
+	Pack heads and tags for efficient repository access.
+
 gitlink:git-pull[1]::
-	Fetch from and merge with a remote repository.
+	Fetch from and merge with a remote repository or a local branch.
 
 gitlink:git-push[1]::
 	Update remote refs along with associated objects.
@@ -428,7 +252,7 @@
 	Annotate file lines with commit info.
 
 gitlink:git-blame[1]::
-	Blame file lines on commits.
+	Find out where each line in a file came from.
 
 gitlink:git-check-ref-format[1]::
 	Make sure ref name is well formed.
@@ -488,6 +312,191 @@
 	Filter out empty lines.
 
 
+Low-level commands (plumbing)
+-----------------------------
+
+Although git includes its
+own porcelain layer, its low-level commands are sufficient to support
+development of alternative porcelains.  Developers of such porcelains
+might start by reading about gitlink:git-update-index[1] and
+gitlink:git-read-tree[1].
+
+We divide the low-level commands into commands that manipulate objects (in
+the repository, index, and working tree), commands that interrogate and
+compare objects, and commands that move objects and references between
+repositories.
+
+Manipulation commands
+~~~~~~~~~~~~~~~~~~~~~
+gitlink:git-apply[1]::
+	Reads a "diff -up1" or git generated patch file and
+	applies it to the working tree.
+
+gitlink:git-checkout-index[1]::
+	Copy files from the index to the working tree.
+
+gitlink:git-commit-tree[1]::
+	Creates a new commit object.
+
+gitlink:git-hash-object[1]::
+	Computes the object ID from a file.
+
+gitlink:git-index-pack[1]::
+	Build pack idx file for an existing packed archive.
+
+gitlink:git-init-db[1]::
+	Creates an empty git object database, or reinitialize an
+	existing one.
+
+gitlink:git-merge-index[1]::
+	Runs a merge for files needing merging.
+
+gitlink:git-mktag[1]::
+	Creates a tag object.
+
+gitlink:git-mktree[1]::
+	Build a tree-object from ls-tree formatted text.
+
+gitlink:git-pack-objects[1]::
+	Creates a packed archive of objects.
+
+gitlink:git-prune-packed[1]::
+	Remove extra objects that are already in pack files.
+
+gitlink:git-read-tree[1]::
+	Reads tree information into the index.
+
+gitlink:git-repo-config[1]::
+	Get and set options in .git/config.
+
+gitlink:git-unpack-objects[1]::
+	Unpacks objects out of a packed archive.
+
+gitlink:git-update-index[1]::
+	Registers files in the working tree to the index.
+
+gitlink:git-write-tree[1]::
+	Creates a tree from the index.
+
+
+Interrogation commands
+~~~~~~~~~~~~~~~~~~~~~~
+
+gitlink:git-cat-file[1]::
+	Provide content or type/size information for repository objects.
+
+gitlink:git-describe[1]::
+	Show the most recent tag that is reachable from a commit.
+
+gitlink:git-diff-index[1]::
+	Compares content and mode of blobs between the index and repository.
+
+gitlink:git-diff-files[1]::
+	Compares files in the working tree and the index.
+
+gitlink:git-diff-stages[1]::
+	Compares two "merge stages" in the index.
+
+gitlink:git-diff-tree[1]::
+	Compares the content and mode of blobs found via two tree objects.
+
+gitlink:git-for-each-ref[1]::
+	Output information on each ref.
+
+gitlink:git-fsck-objects[1]::
+	Verifies the connectivity and validity of the objects in the database.
+
+gitlink:git-ls-files[1]::
+	Information about files in the index and the working tree.
+
+gitlink:git-ls-tree[1]::
+	Displays a tree object in human readable form.
+
+gitlink:git-merge-base[1]::
+	Finds as good common ancestors as possible for a merge.
+
+gitlink:git-name-rev[1]::
+	Find symbolic names for given revs.
+
+gitlink:git-pack-redundant[1]::
+	Find redundant pack files.
+
+gitlink:git-rev-list[1]::
+	Lists commit objects in reverse chronological order.
+
+gitlink:git-show-index[1]::
+	Displays contents of a pack idx file.
+
+gitlink:git-show-ref[1]::
+	List references in a local repository.
+
+gitlink:git-tar-tree[1]::
+	Creates a tar archive of the files in the named tree object.
+
+gitlink:git-unpack-file[1]::
+	Creates a temporary file with a blob's contents.
+
+gitlink:git-var[1]::
+	Displays a git logical variable.
+
+gitlink:git-verify-pack[1]::
+	Validates packed git archive files.
+
+In general, the interrogate commands do not touch the files in
+the working tree.
+
+
+Synching repositories
+~~~~~~~~~~~~~~~~~~~~~
+
+gitlink:git-fetch-pack[1]::
+	Updates from a remote repository (engine for ssh and
+	local transport).
+
+gitlink:git-http-fetch[1]::
+	Downloads a remote git repository via HTTP by walking
+	commit chain.
+
+gitlink:git-local-fetch[1]::
+	Duplicates another git repository on a local system by
+	walking commit chain.
+
+gitlink:git-peek-remote[1]::
+	Lists references on a remote repository using
+	upload-pack protocol (engine for ssh and local
+	transport).
+
+gitlink:git-receive-pack[1]::
+	Invoked by 'git-send-pack' to receive what is pushed to it.
+
+gitlink:git-send-pack[1]::
+	Pushes to a remote repository, intelligently.
+
+gitlink:git-http-push[1]::
+	Push missing objects using HTTP/DAV.
+
+gitlink:git-shell[1]::
+	Restricted shell for GIT-only SSH access.
+
+gitlink:git-ssh-fetch[1]::
+	Pulls from a remote repository over ssh connection by
+	walking commit chain.
+
+gitlink:git-ssh-upload[1]::
+	Helper "server-side" program used by git-ssh-fetch.
+
+gitlink:git-update-server-info[1]::
+	Updates auxiliary information on a dumb server to help
+	clients discover references and packs on it.
+
+gitlink:git-upload-archive[1]::
+	Invoked by 'git-archive' to send a generated archive.
+
+gitlink:git-upload-pack[1]::
+	Invoked by 'git-fetch-pack' to push
+	what are asked for.
+
+
 Configuration Mechanism
 -----------------------
 
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 0cacac3..9796e91 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.3.GIT
+DEF_VER=v1.4.4-rc1.GIT
 
 LF='
 '
diff --git a/Makefile b/Makefile
index b52dd57..36ce8cd 100644
--- a/Makefile
+++ b/Makefile
@@ -132,6 +132,8 @@
 GITWEB_CSS = gitweb.css
 GITWEB_LOGO = git-logo.png
 GITWEB_FAVICON = git-favicon.png
+GITWEB_SITE_HEADER =
+GITWEB_SITE_FOOTER =
 
 export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
 
@@ -156,8 +158,8 @@
 BASIC_LDFLAGS =
 
 SCRIPT_SH = \
-	git-bisect.sh git-branch.sh git-checkout.sh \
-	git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
+	git-bisect.sh git-checkout.sh \
+	git-clean.sh git-clone.sh git-commit.sh \
 	git-fetch.sh \
 	git-ls-remote.sh \
 	git-merge-one-file.sh git-parse-remote.sh \
@@ -173,7 +175,7 @@
 SCRIPT_PERL = \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
 	git-shortlog.perl git-rerere.perl \
-	git-annotate.perl git-cvsserver.perl \
+	git-cvsserver.perl \
 	git-svnimport.perl git-cvsexportcommit.perl \
 	git-send-email.perl git-svn.perl
 
@@ -199,7 +201,7 @@
 	git-update-server-info$X \
 	git-upload-pack$X git-verify-pack$X \
 	git-pack-redundant$X git-var$X \
-	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+	git-describe$X git-merge-tree$X git-imap-send$X \
 	git-merge-recursive$X \
 	$(EXTRA_PROGRAMS)
 
@@ -207,7 +209,7 @@
 EXTRA_PROGRAMS =
 
 BUILT_INS = \
-	git-format-patch$X git-show$X git-whatchanged$X \
+	git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
 	git-get-tar-commit-id$X \
 	$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
@@ -255,15 +257,18 @@
 	quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
-	fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
+	revision.o pager.o tree-walk.o xdiff-interface.o \
 	write_or_die.o trace.o list-objects.o grep.o \
 	alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
 	color.o wt-status.o archive-zip.o archive-tar.o
 
 BUILTIN_OBJS = \
 	builtin-add.o \
+	builtin-annotate.o \
 	builtin-apply.o \
 	builtin-archive.o \
+	builtin-blame.o \
+	builtin-branch.o \
 	builtin-cat-file.o \
 	builtin-checkout-index.o \
 	builtin-check-ref-format.o \
@@ -275,6 +280,7 @@
 	builtin-diff-stages.o \
 	builtin-diff-tree.o \
 	builtin-fmt-merge-msg.o \
+	builtin-for-each-ref.o \
 	builtin-grep.o \
 	builtin-init-db.o \
 	builtin-log.o \
@@ -303,7 +309,9 @@
 	builtin-update-ref.o \
 	builtin-upload-archive.o \
 	builtin-verify-pack.o \
-	builtin-write-tree.o
+	builtin-write-tree.o \
+	builtin-show-ref.o \
+	builtin-pack-refs.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 EXTLIBS = -lz
@@ -670,6 +678,8 @@
 	    -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
 	    -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
 	    -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
+	    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
+	    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
 	    $< >$@+
 	chmod +x $@+
 	mv $@+ $@
@@ -923,3 +933,8 @@
 		*) echo "no link: $$v";; \
 		esac ; \
 	done | sort
+
+### Make sure built-ins do not have dups and listed in git.c
+#
+check-builtins::
+	./check-builtins.sh
diff --git a/archive-zip.c b/archive-zip.c
index 3ffdad6..28e7352 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -145,6 +145,7 @@
 {
 	struct zip_local_header header;
 	struct zip_dir_header dirent;
+	unsigned long attr2;
 	unsigned long compressed_size;
 	unsigned long uncompressed_size;
 	unsigned long crc;
@@ -172,12 +173,16 @@
 
 	if (S_ISDIR(mode)) {
 		method = 0;
+		attr2 = 16;
 		result = READ_TREE_RECURSIVE;
 		out = NULL;
 		uncompressed_size = 0;
 		compressed_size = 0;
-	} else if (S_ISREG(mode)) {
-		method = zlib_compression_level == 0 ? 0 : 8;
+	} else if (S_ISREG(mode) || S_ISLNK(mode)) {
+		method = 0;
+		attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
+		if (S_ISREG(mode) && zlib_compression_level != 0)
+			method = 8;
 		result = 0;
 		buffer = read_sha1_file(sha1, type, &size);
 		if (!buffer)
@@ -213,8 +218,8 @@
 	}
 
 	copy_le32(dirent.magic, 0x02014b50);
-	copy_le16(dirent.creator_version, 0);
-	copy_le16(dirent.version, 20);
+	copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
+	copy_le16(dirent.version, 10);
 	copy_le16(dirent.flags, 0);
 	copy_le16(dirent.compression_method, method);
 	copy_le16(dirent.mtime, zip_time);
@@ -227,7 +232,7 @@
 	copy_le16(dirent.comment_length, 0);
 	copy_le16(dirent.disk, 0);
 	copy_le16(dirent.attr1, 0);
-	copy_le32(dirent.attr2, 0);
+	copy_le32(dirent.attr2, attr2);
 	copy_le32(dirent.offset, zip_offset);
 	memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
 	zip_dir_offset += sizeof(struct zip_dir_header);
@@ -236,7 +241,7 @@
 	zip_dir_entries++;
 
 	copy_le32(header.magic, 0x04034b50);
-	copy_le16(header.version, 20);
+	copy_le16(header.version, 10);
 	copy_le16(header.flags, 0);
 	copy_le16(header.compression_method, method);
 	copy_le16(header.mtime, zip_time);
diff --git a/blame.c b/blame.c
deleted file mode 100644
index 3e227d2..0000000
--- a/blame.c
+++ /dev/null
@@ -1,925 +0,0 @@
-/*
- * Copyright (C) 2006, Fredrik Kuivinen <freku045@student.liu.se>
- */
-
-#include <assert.h>
-#include <time.h>
-#include <sys/time.h>
-#include <math.h>
-
-#include "cache.h"
-#include "refs.h"
-#include "tag.h"
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "revision.h"
-#include "xdiff-interface.h"
-
-#ifndef DEBUG
-#define DEBUG 0
-#endif
-
-static const char blame_usage[] = "git-blame [-c] [-l] [-t] [-S <revs-file>] [--] file [commit]\n"
-	"  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
-	"  -l, --long          Show long commit SHA1 (Default: off)\n"
-	"  -t, --time          Show raw timestamp (Default: off)\n"
-	"  -S, --revs-file     Use revisions from revs-file instead of calling git-rev-list\n"
-	"  -h, --help          This message";
-
-static struct commit **blame_lines;
-static int num_blame_lines;
-static char* blame_contents;
-static int blame_len;
-
-struct util_info {
-	int *line_map;
-	unsigned char sha1[20];	/* blob sha, not commit! */
-	char *buf;
-	unsigned long size;
-	int num_lines;
-	const char* pathname;
-
-	void* topo_data;
-};
-
-struct chunk {
-	int off1, len1;	/* --- */
-	int off2, len2;	/* +++ */
-};
-
-struct patch {
-	struct chunk *chunks;
-	int num;
-};
-
-static void get_blob(struct commit *commit);
-
-/* Only used for statistics */
-static int num_get_patch;
-static int num_commits;
-static int patch_time;
-
-struct blame_diff_state {
-	struct xdiff_emit_state xm;
-	struct patch *ret;
-};
-
-static void process_u0_diff(void *state_, char *line, unsigned long len)
-{
-	struct blame_diff_state *state = state_;
-	struct chunk *chunk;
-
-	if (len < 4 || line[0] != '@' || line[1] != '@')
-		return;
-
-	if (DEBUG)
-		printf("chunk line: %.*s", (int)len, line);
-	state->ret->num++;
-	state->ret->chunks = xrealloc(state->ret->chunks,
-				      sizeof(struct chunk) * state->ret->num);
-	chunk = &state->ret->chunks[state->ret->num - 1];
-
-	assert(!strncmp(line, "@@ -", 4));
-
-	if (parse_hunk_header(line, len,
-			      &chunk->off1, &chunk->len1,
-			      &chunk->off2, &chunk->len2)) {
-		state->ret->num--;
-		return;
-	}
-
-	if (chunk->len1 == 0)
-		chunk->off1++;
-	if (chunk->len2 == 0)
-		chunk->off2++;
-
-	if (chunk->off1 > 0)
-		chunk->off1--;
-	if (chunk->off2 > 0)
-		chunk->off2--;
-
-	assert(chunk->off1 >= 0);
-	assert(chunk->off2 >= 0);
-}
-
-static struct patch *get_patch(struct commit *commit, struct commit *other)
-{
-	struct blame_diff_state state;
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-	mmfile_t file_c, file_o;
-	xdemitcb_t ecb;
-	struct util_info *info_c = (struct util_info *)commit->util;
-	struct util_info *info_o = (struct util_info *)other->util;
-	struct timeval tv_start, tv_end;
-
-	get_blob(commit);
-	file_c.ptr = info_c->buf;
-	file_c.size = info_c->size;
-
-	get_blob(other);
-	file_o.ptr = info_o->buf;
-	file_o.size = info_o->size;
-
-	gettimeofday(&tv_start, NULL);
-
-	xpp.flags = XDF_NEED_MINIMAL;
-	xecfg.ctxlen = 0;
-	xecfg.flags = 0;
-	ecb.outf = xdiff_outf;
-	ecb.priv = &state;
-	memset(&state, 0, sizeof(state));
-	state.xm.consume = process_u0_diff;
-	state.ret = xmalloc(sizeof(struct patch));
-	state.ret->chunks = NULL;
-	state.ret->num = 0;
-
-	xdl_diff(&file_c, &file_o, &xpp, &xecfg, &ecb);
-
-	gettimeofday(&tv_end, NULL);
-	patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) +
-		tv_end.tv_usec - tv_start.tv_usec;
-
-	num_get_patch++;
-	return state.ret;
-}
-
-static void free_patch(struct patch *p)
-{
-	free(p->chunks);
-	free(p);
-}
-
-static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
-				  int baselen, const char *pathname,
-				  unsigned mode, int stage);
-
-static unsigned char blob_sha1[20];
-static const char* blame_file;
-static int get_blob_sha1(struct tree *t, const char *pathname,
-			 unsigned char *sha1)
-{
-	int i;
-	const char *pathspec[2];
-	blame_file = pathname;
-	pathspec[0] = pathname;
-	pathspec[1] = NULL;
-	hashclr(blob_sha1);
-	read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);
-
-	for (i = 0; i < 20; i++) {
-		if (blob_sha1[i] != 0)
-			break;
-	}
-
-	if (i == 20)
-		return -1;
-
-	hashcpy(sha1, blob_sha1);
-	return 0;
-}
-
-static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
-				  int baselen, const char *pathname,
-				  unsigned mode, int stage)
-{
-	if (S_ISDIR(mode))
-		return READ_TREE_RECURSIVE;
-
-	if (strncmp(blame_file, base, baselen) ||
-	    strcmp(blame_file + baselen, pathname))
-		return -1;
-
-	hashcpy(blob_sha1, sha1);
-	return -1;
-}
-
-static void get_blob(struct commit *commit)
-{
-	struct util_info *info = commit->util;
-	char type[20];
-
-	if (info->buf)
-		return;
-
-	info->buf = read_sha1_file(info->sha1, type, &info->size);
-
-	assert(!strcmp(type, blob_type));
-}
-
-/* For debugging only */
-static void print_patch(struct patch *p)
-{
-	int i;
-	printf("Num chunks: %d\n", p->num);
-	for (i = 0; i < p->num; i++) {
-		printf("%d,%d %d,%d\n", p->chunks[i].off1, p->chunks[i].len1,
-		       p->chunks[i].off2, p->chunks[i].len2);
-	}
-}
-
-#if DEBUG
-/* For debugging only */
-static void print_map(struct commit *cmit, struct commit *other)
-{
-	struct util_info *util = cmit->util;
-	struct util_info *util2 = other->util;
-
-	int i;
-	int max =
-	    util->num_lines >
-	    util2->num_lines ? util->num_lines : util2->num_lines;
-	int num;
-
-	if (print_map == NULL)
-		; /* to avoid "unused function" warning */
-
-	for (i = 0; i < max; i++) {
-		printf("i: %d ", i);
-		num = -1;
-
-		if (i < util->num_lines) {
-			num = util->line_map[i];
-			printf("%d\t", num);
-		} else
-			printf("\t");
-
-		if (i < util2->num_lines) {
-			int num2 = util2->line_map[i];
-			printf("%d\t", num2);
-			if (num != -1 && num2 != num)
-				printf("---");
-		} else
-			printf("\t");
-
-		printf("\n");
-	}
-}
-#endif
-
-/* p is a patch from commit to other. */
-static void fill_line_map(struct commit *commit, struct commit *other,
-			  struct patch *p)
-{
-	struct util_info *util = commit->util;
-	struct util_info *util2 = other->util;
-	int *map = util->line_map;
-	int *map2 = util2->line_map;
-	int cur_chunk = 0;
-	int i1, i2;
-
-	if (p->num && DEBUG)
-		print_patch(p);
-
-	if (DEBUG)
-		printf("num lines 1: %d num lines 2: %d\n", util->num_lines,
-		       util2->num_lines);
-
-	for (i1 = 0, i2 = 0; i1 < util->num_lines; i1++, i2++) {
-		struct chunk *chunk = NULL;
-		if (cur_chunk < p->num)
-			chunk = &p->chunks[cur_chunk];
-
-		if (chunk && chunk->off1 == i1) {
-			if (DEBUG && i2 != chunk->off2)
-				printf("i2: %d off2: %d\n", i2, chunk->off2);
-
-			assert(i2 == chunk->off2);
-
-			i1--;
-			i2--;
-			if (chunk->len1 > 0)
-				i1 += chunk->len1;
-
-			if (chunk->len2 > 0)
-				i2 += chunk->len2;
-
-			cur_chunk++;
-		} else {
-			if (i2 >= util2->num_lines)
-				break;
-
-			if (map[i1] != map2[i2] && map[i1] != -1) {
-				if (DEBUG)
-					printf("map: i1: %d %d %p i2: %d %d %p\n",
-					       i1, map[i1],
-					       (void *) (i1 != -1 ? blame_lines[map[i1]] : NULL),
-					       i2, map2[i2],
-					       (void *) (i2 != -1 ? blame_lines[map2[i2]] : NULL));
-				if (map2[i2] != -1 &&
-				    blame_lines[map[i1]] &&
-				    !blame_lines[map2[i2]])
-					map[i1] = map2[i2];
-			}
-
-			if (map[i1] == -1 && map2[i2] != -1)
-				map[i1] = map2[i2];
-		}
-
-		if (DEBUG > 1)
-			printf("l1: %d l2: %d i1: %d i2: %d\n",
-			       map[i1], map2[i2], i1, i2);
-	}
-}
-
-static int map_line(struct commit *commit, int line)
-{
-	struct util_info *info = commit->util;
-	assert(line >= 0 && line < info->num_lines);
-	return info->line_map[line];
-}
-
-static struct util_info* get_util(struct commit *commit)
-{
-	struct util_info *util = commit->util;
-
-	if (util)
-		return util;
-
-	util = xmalloc(sizeof(struct util_info));
-	util->buf = NULL;
-	util->size = 0;
-	util->line_map = NULL;
-	util->num_lines = -1;
-	util->pathname = NULL;
-	commit->util = util;
-	return util;
-}
-
-static int fill_util_info(struct commit *commit)
-{
-	struct util_info *util = commit->util;
-
-	assert(util);
-	assert(util->pathname);
-
-	return !!get_blob_sha1(commit->tree, util->pathname, util->sha1);
-}
-
-static void alloc_line_map(struct commit *commit)
-{
-	struct util_info *util = commit->util;
-	int i;
-
-	if (util->line_map)
-		return;
-
-	get_blob(commit);
-
-	util->num_lines = 0;
-	for (i = 0; i < util->size; i++) {
-		if (util->buf[i] == '\n')
-			util->num_lines++;
-	}
-	if(util->buf[util->size - 1] != '\n')
-		util->num_lines++;
-
-	util->line_map = xmalloc(sizeof(int) * util->num_lines);
-
-	for (i = 0; i < util->num_lines; i++)
-		util->line_map[i] = -1;
-}
-
-static void init_first_commit(struct commit* commit, const char* filename)
-{
-	struct util_info* util = commit->util;
-	int i;
-
-	util->pathname = filename;
-	if (fill_util_info(commit))
-		die("fill_util_info failed");
-
-	alloc_line_map(commit);
-
-	util = commit->util;
-
-	for (i = 0; i < util->num_lines; i++)
-		util->line_map[i] = i;
-}
-
-
-static void process_commits(struct rev_info *rev, const char *path,
-			    struct commit** initial)
-{
-	int i;
-	struct util_info* util;
-	int lines_left;
-	int *blame_p;
-	int *new_lines;
-	int new_lines_len;
-
-	struct commit* commit = get_revision(rev);
-	assert(commit);
-	init_first_commit(commit, path);
-
-	util = commit->util;
-	num_blame_lines = util->num_lines;
-	blame_lines = xmalloc(sizeof(struct commit *) * num_blame_lines);
-	blame_contents = util->buf;
-	blame_len = util->size;
-
-	for (i = 0; i < num_blame_lines; i++)
-		blame_lines[i] = NULL;
-
-	lines_left = num_blame_lines;
-	blame_p = xmalloc(sizeof(int) * num_blame_lines);
-	new_lines = xmalloc(sizeof(int) * num_blame_lines);
-	do {
-		struct commit_list *parents;
-		int num_parents;
-		struct util_info *util;
-
-		if (DEBUG)
-			printf("\nProcessing commit: %d %s\n", num_commits,
-			       sha1_to_hex(commit->object.sha1));
-
-		if (lines_left == 0)
-			return;
-
-		num_commits++;
-		memset(blame_p, 0, sizeof(int) * num_blame_lines);
-		new_lines_len = 0;
-		num_parents = 0;
-		for (parents = commit->parents;
-		     parents != NULL; parents = parents->next)
-			num_parents++;
-
-		if(num_parents == 0)
-			*initial = commit;
-
-		if (fill_util_info(commit))
-			continue;
-
-		alloc_line_map(commit);
-		util = commit->util;
-
-		for (parents = commit->parents;
-		     parents != NULL; parents = parents->next) {
-			struct commit *parent = parents->item;
-			struct patch *patch;
-
-			if (parse_commit(parent) < 0)
-				die("parse_commit error");
-
-			if (DEBUG)
-				printf("parent: %s\n",
-				       sha1_to_hex(parent->object.sha1));
-
-			if (fill_util_info(parent)) {
-				num_parents--;
-				continue;
-			}
-
-			patch = get_patch(parent, commit);
-                        alloc_line_map(parent);
-                        fill_line_map(parent, commit, patch);
-
-                        for (i = 0; i < patch->num; i++) {
-                            int l;
-                            for (l = 0; l < patch->chunks[i].len2; l++) {
-                                int mapped_line =
-                                    map_line(commit, patch->chunks[i].off2 + l);
-                                if (mapped_line != -1) {
-                                    blame_p[mapped_line]++;
-                                    if (blame_p[mapped_line] == num_parents)
-                                        new_lines[new_lines_len++] = mapped_line;
-                                }
-                            }
-			}
-                        free_patch(patch);
-		}
-
-		if (DEBUG)
-			printf("parents: %d\n", num_parents);
-
-		for (i = 0; i < new_lines_len; i++) {
-			int mapped_line = new_lines[i];
-			if (blame_lines[mapped_line] == NULL) {
-				blame_lines[mapped_line] = commit;
-				lines_left--;
-				if (DEBUG)
-					printf("blame: mapped: %d i: %d\n",
-					       mapped_line, i);
-			}
-		}
-	} while ((commit = get_revision(rev)) != NULL);
-}
-
-
-static int compare_tree_path(struct rev_info* revs,
-			     struct commit* c1, struct commit* c2)
-{
-	int ret;
-	const char* paths[2];
-	struct util_info* util = c2->util;
-	paths[0] = util->pathname;
-	paths[1] = NULL;
-
-	diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
-			      &revs->pruning);
-	ret = rev_compare_tree(revs, c1->tree, c2->tree);
-	diff_tree_release_paths(&revs->pruning);
-	return ret;
-}
-
-
-static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,
-				   const char* path)
-{
-	int ret;
-	const char* paths[2];
-	paths[0] = path;
-	paths[1] = NULL;
-
-	diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
-			      &revs->pruning);
-	ret = rev_same_tree_as_empty(revs, t1);
-	diff_tree_release_paths(&revs->pruning);
-	return ret;
-}
-
-static const char* find_rename(struct commit* commit, struct commit* parent)
-{
-	struct util_info* cutil = commit->util;
-	struct diff_options diff_opts;
-	const char *paths[1];
-	int i;
-
-	if (DEBUG) {
-		printf("find_rename commit: %s ",
-		       sha1_to_hex(commit->object.sha1));
-		puts(sha1_to_hex(parent->object.sha1));
-	}
-
-	diff_setup(&diff_opts);
-	diff_opts.recursive = 1;
-	diff_opts.detect_rename = DIFF_DETECT_RENAME;
-	paths[0] = NULL;
-	diff_tree_setup_paths(paths, &diff_opts);
-	if (diff_setup_done(&diff_opts) < 0)
-		die("diff_setup_done failed");
-
-	diff_tree_sha1(commit->tree->object.sha1, parent->tree->object.sha1,
-		       "", &diff_opts);
-	diffcore_std(&diff_opts);
-
-	for (i = 0; i < diff_queued_diff.nr; i++) {
-		struct diff_filepair *p = diff_queued_diff.queue[i];
-
-		if (p->status == 'R' && !strcmp(p->one->path, cutil->pathname)) {
-			if (DEBUG)
-				printf("rename %s -> %s\n", p->one->path, p->two->path);
-			return p->two->path;
-		}
-	}
-
-	return 0;
-}
-
-static void simplify_commit(struct rev_info *revs, struct commit *commit)
-{
-	struct commit_list **pp, *parent;
-
-	if (!commit->tree)
-		return;
-
-	if (!commit->parents) {
-		struct util_info* util = commit->util;
-		if (!same_tree_as_empty_path(revs, commit->tree,
-					     util->pathname))
-			commit->object.flags |= TREECHANGE;
-		return;
-	}
-
-	pp = &commit->parents;
-	while ((parent = *pp) != NULL) {
-		struct commit *p = parent->item;
-
-		if (p->object.flags & UNINTERESTING) {
-			pp = &parent->next;
-			continue;
-		}
-
-		parse_commit(p);
-		switch (compare_tree_path(revs, p, commit)) {
-		case REV_TREE_SAME:
-			parent->next = NULL;
-			commit->parents = parent;
-			get_util(p)->pathname = get_util(commit)->pathname;
-			return;
-
-		case REV_TREE_NEW:
-		{
-
-			struct util_info* util = commit->util;
-			if (revs->remove_empty_trees &&
-			    same_tree_as_empty_path(revs, p->tree,
-						    util->pathname)) {
-				const char* new_name = find_rename(commit, p);
-				if (new_name) {
-					struct util_info* putil = get_util(p);
-					if (!putil->pathname)
-						putil->pathname = xstrdup(new_name);
-				} else {
-					*pp = parent->next;
-					continue;
-				}
-			}
-		}
-
-		/* fallthrough */
-		case REV_TREE_DIFFERENT:
-			pp = &parent->next;
-			if (!get_util(p)->pathname)
-				get_util(p)->pathname =
-					get_util(commit)->pathname;
-			continue;
-		}
-		die("bad tree compare for commit %s",
-		    sha1_to_hex(commit->object.sha1));
-	}
-	commit->object.flags |= TREECHANGE;
-}
-
-
-struct commit_info
-{
-	char* author;
-	char* author_mail;
-	unsigned long author_time;
-	char* author_tz;
-};
-
-static void get_commit_info(struct commit* commit, struct commit_info* ret)
-{
-	int len;
-	char* tmp;
-	static char author_buf[1024];
-
-	tmp = strstr(commit->buffer, "\nauthor ") + 8;
-	len = strchr(tmp, '\n') - tmp;
-	ret->author = author_buf;
-	memcpy(ret->author, tmp, len);
-
-	tmp = ret->author;
-	tmp += len;
-	*tmp = 0;
-	while(*tmp != ' ')
-		tmp--;
-	ret->author_tz = tmp+1;
-
-	*tmp = 0;
-	while(*tmp != ' ')
-		tmp--;
-	ret->author_time = strtoul(tmp, NULL, 10);
-
-	*tmp = 0;
-	while(*tmp != ' ')
-		tmp--;
-	ret->author_mail = tmp + 1;
-
-	*tmp = 0;
-}
-
-static const char* format_time(unsigned long time, const char* tz_str,
-			       int show_raw_time)
-{
-	static char time_buf[128];
-	time_t t = time;
-	int minutes, tz;
-	struct tm *tm;
-
-	if (show_raw_time) {
-		sprintf(time_buf, "%lu %s", time, tz_str);
-		return time_buf;
-	}
-
-	tz = atoi(tz_str);
-	minutes = tz < 0 ? -tz : tz;
-	minutes = (minutes / 100)*60 + (minutes % 100);
-	minutes = tz < 0 ? -minutes : minutes;
-	t = time + minutes * 60;
-	tm = gmtime(&t);
-
-	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
-	strcat(time_buf, tz_str);
-	return time_buf;
-}
-
-static void topo_setter(struct commit* c, void* data)
-{
-	struct util_info* util = c->util;
-	util->topo_data = data;
-}
-
-static void* topo_getter(struct commit* c)
-{
-	struct util_info* util = c->util;
-	return util->topo_data;
-}
-
-static int read_ancestry(const char *graft_file,
-			 unsigned char **start_sha1)
-{
-	FILE *fp = fopen(graft_file, "r");
-	char buf[1024];
-	if (!fp)
-		return -1;
-	while (fgets(buf, sizeof(buf), fp)) {
-		/* The format is just "Commit Parent1 Parent2 ...\n" */
-		int len = strlen(buf);
-		struct commit_graft *graft = read_graft_line(buf, len);
-		register_commit_graft(graft, 0);
-		if (!*start_sha1)
-			*start_sha1 = graft->sha1;
-	}
-	fclose(fp);
-	return 0;
-}
-
-int main(int argc, const char **argv)
-{
-	int i;
-	struct commit *initial = NULL;
-	unsigned char sha1[20], *sha1_p = NULL;
-
-	const char *filename = NULL, *commit = NULL;
-	char filename_buf[256];
-	int sha1_len = 8;
-	int compatibility = 0;
-	int show_raw_time = 0;
-	int options = 1;
-	struct commit* start_commit;
-
-	const char* args[10];
-	struct rev_info rev;
-
-	struct commit_info ci;
-	const char *buf;
-	int max_digits;
-	int longest_file, longest_author;
-	int found_rename;
-
-	const char* prefix = setup_git_directory();
-	git_config(git_default_config);
-
-	for(i = 1; i < argc; i++) {
-		if(options) {
-			if(!strcmp(argv[i], "-h") ||
-			   !strcmp(argv[i], "--help"))
-				usage(blame_usage);
-			else if(!strcmp(argv[i], "-l") ||
-				!strcmp(argv[i], "--long")) {
-				sha1_len = 40;
-				continue;
-			} else if(!strcmp(argv[i], "-c") ||
-				  !strcmp(argv[i], "--compatibility")) {
-				compatibility = 1;
-				continue;
-			} else if(!strcmp(argv[i], "-t") ||
-				  !strcmp(argv[i], "--time")) {
-				show_raw_time = 1;
-				continue;
-			} else if(!strcmp(argv[i], "-S")) {
-				if (i + 1 < argc &&
-				    !read_ancestry(argv[i + 1], &sha1_p)) {
-					compatibility = 1;
-					i++;
-					continue;
-				}
-				usage(blame_usage);
-			} else if(!strcmp(argv[i], "--")) {
-				options = 0;
-				continue;
-			} else if(argv[i][0] == '-')
-				usage(blame_usage);
-			else
-				options = 0;
-		}
-
-		if(!options) {
-			if(!filename)
-				filename = argv[i];
-			else if(!commit)
-				commit = argv[i];
-			else
-				usage(blame_usage);
-		}
-	}
-
-	if(!filename)
-		usage(blame_usage);
-	if (commit && sha1_p)
-		usage(blame_usage);
-	else if(!commit)
-		commit = "HEAD";
-
-	if(prefix)
-		sprintf(filename_buf, "%s%s", prefix, filename);
-	else
-		strcpy(filename_buf, filename);
-	filename = filename_buf;
-
-	if (!sha1_p) {
-		if (get_sha1(commit, sha1))
-			die("get_sha1 failed, commit '%s' not found", commit);
-		sha1_p = sha1;
-	}
-	start_commit = lookup_commit_reference(sha1_p);
-	get_util(start_commit)->pathname = filename;
-	if (fill_util_info(start_commit)) {
-		printf("%s not found in %s\n", filename, commit);
-		return 1;
-	}
-
-
-	init_revisions(&rev, setup_git_directory());
-	rev.remove_empty_trees = 1;
-	rev.topo_order = 1;
-	rev.prune_fn = simplify_commit;
-	rev.topo_setter = topo_setter;
-	rev.topo_getter = topo_getter;
-	rev.parents = 1;
-	rev.limited = 1;
-
-	commit_list_insert(start_commit, &rev.commits);
-
-	args[0] = filename;
-	args[1] = NULL;
-	diff_tree_setup_paths(args, &rev.pruning);
-	prepare_revision_walk(&rev);
-	process_commits(&rev, filename, &initial);
-
-	buf = blame_contents;
-	for (max_digits = 1, i = 10; i <= num_blame_lines + 1; max_digits++)
-		i *= 10;
-
-	longest_file = 0;
-	longest_author = 0;
-	found_rename = 0;
-	for (i = 0; i < num_blame_lines; i++) {
-		struct commit *c = blame_lines[i];
-		struct util_info* u;
-		if (!c)
-			c = initial;
-		u = c->util;
-
-		if (!found_rename && strcmp(filename, u->pathname))
-			found_rename = 1;
-		if (longest_file < strlen(u->pathname))
-			longest_file = strlen(u->pathname);
-		get_commit_info(c, &ci);
-		if (longest_author < strlen(ci.author))
-			longest_author = strlen(ci.author);
-	}
-
-	for (i = 0; i < num_blame_lines; i++) {
-		struct commit *c = blame_lines[i];
-		struct util_info* u;
-
-		if (!c)
-			c = initial;
-
-		u = c->util;
-		get_commit_info(c, &ci);
-		fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
-		if(compatibility) {
-			printf("\t(%10s\t%10s\t%d)", ci.author,
-			       format_time(ci.author_time, ci.author_tz,
-					   show_raw_time),
-			       i+1);
-		} else {
-			if (found_rename)
-				printf(" %-*.*s", longest_file, longest_file,
-				       u->pathname);
-			printf(" (%-*.*s %10s %*d) ",
-			       longest_author, longest_author, ci.author,
-			       format_time(ci.author_time, ci.author_tz,
-					   show_raw_time),
-			       max_digits, i+1);
-		}
-
-		if(i == num_blame_lines - 1) {
-			fwrite(buf, blame_len - (buf - blame_contents),
-			       1, stdout);
-			if(blame_contents[blame_len-1] != '\n')
-				putc('\n', stdout);
-		} else {
-			char* next_buf = strchr(buf, '\n') + 1;
-			fwrite(buf, next_buf - buf, 1, stdout);
-			buf = next_buf;
-		}
-	}
-
-	if (DEBUG) {
-		printf("num get patch: %d\n", num_get_patch);
-		printf("num commits: %d\n", num_commits);
-		printf("patch time: %f\n", patch_time / 1000000.0);
-		printf("initial: %s\n", sha1_to_hex(initial->object.sha1));
-	}
-
-	return 0;
-}
diff --git a/builtin-annotate.c b/builtin-annotate.c
new file mode 100644
index 0000000..57c4684
--- /dev/null
+++ b/builtin-annotate.c
@@ -0,0 +1,25 @@
+/*
+ * "git annotate" builtin alias
+ *
+ * Copyright (C) 2006 Ryan Anderson
+ */
+#include "git-compat-util.h"
+#include "builtin.h"
+
+int cmd_annotate(int argc, const char **argv, const char *prefix)
+{
+	const char **nargv;
+	int i;
+	nargv = xmalloc(sizeof(char *) * (argc + 2));
+
+	nargv[0] = "blame";
+	nargv[1] = "-c";
+
+	for (i = 1; i < argc; i++) {
+		nargv[i+1] = argv[i];
+	}
+	nargv[argc + 1] = NULL;
+
+	return cmd_blame(argc + 1, nargv, prefix);
+}
+
diff --git a/builtin-apply.c b/builtin-apply.c
index 6ec22b8..aad5526 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -360,7 +360,7 @@
 static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
 {
 	if (!orig_name && !isnull)
-		return find_name(line, NULL, 1, 0);
+		return find_name(line, NULL, 1, TERM_TAB);
 
 	if (orig_name) {
 		int len;
@@ -370,7 +370,7 @@
 		len = strlen(name);
 		if (isnull)
 			die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
-		another = find_name(line, NULL, 1, 0);
+		another = find_name(line, NULL, 1, TERM_TAB);
 		if (!another || memcmp(another, name, len))
 			die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
 		free(another);
diff --git a/builtin-blame.c b/builtin-blame.c
new file mode 100644
index 0000000..066dee7
--- /dev/null
+++ b/builtin-blame.c
@@ -0,0 +1,1890 @@
+/*
+ * Pickaxe
+ *
+ * Copyright (c) 2006, Junio C Hamano
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "xdiff-interface.h"
+
+#include <time.h>
+#include <sys/time.h>
+#include <regex.h>
+
+static char blame_usage[] =
+"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [commit] [--] file\n"
+"  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
+"  -l, --long          Show long commit SHA1 (Default: off)\n"
+"  -t, --time          Show raw timestamp (Default: off)\n"
+"  -f, --show-name     Show original filename (Default: auto)\n"
+"  -n, --show-number   Show original linenumber (Default: off)\n"
+"  -p, --porcelain     Show in a format designed for machine consumption\n"
+"  -L n,m              Process only line range n,m, counting from 1\n"
+"  -M, -C              Find line movements within and across files\n"
+"  -S revs-file        Use revisions from revs-file instead of calling git-rev-list\n";
+
+static int longest_file;
+static int longest_author;
+static int max_orig_digits;
+static int max_digits;
+static int max_score_digits;
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+/* stats */
+static int num_read_blob;
+static int num_get_patch;
+static int num_commits;
+
+#define PICKAXE_BLAME_MOVE		01
+#define PICKAXE_BLAME_COPY		02
+#define PICKAXE_BLAME_COPY_HARDER	04
+
+/*
+ * blame for a blame_entry with score lower than these thresholds
+ * is not passed to the parent using move/copy logic.
+ */
+static unsigned blame_move_score;
+static unsigned blame_copy_score;
+#define BLAME_DEFAULT_MOVE_SCORE	20
+#define BLAME_DEFAULT_COPY_SCORE	40
+
+/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */
+#define METAINFO_SHOWN		(1u<<12)
+#define MORE_THAN_ONE_PATH	(1u<<13)
+
+/*
+ * One blob in a commit that is being suspected
+ */
+struct origin {
+	int refcnt;
+	struct commit *commit;
+	mmfile_t file;
+	unsigned char blob_sha1[20];
+	char path[FLEX_ARRAY];
+};
+
+static char *fill_origin_blob(struct origin *o, mmfile_t *file)
+{
+	if (!o->file.ptr) {
+		char type[10];
+		num_read_blob++;
+		file->ptr = read_sha1_file(o->blob_sha1, type,
+					   (unsigned long *)(&(file->size)));
+		o->file = *file;
+	}
+	else
+		*file = o->file;
+	return file->ptr;
+}
+
+static inline struct origin *origin_incref(struct origin *o)
+{
+	if (o)
+		o->refcnt++;
+	return o;
+}
+
+static void origin_decref(struct origin *o)
+{
+	if (o && --o->refcnt <= 0) {
+		if (o->file.ptr)
+			free(o->file.ptr);
+		memset(o, 0, sizeof(*o));
+		free(o);
+	}
+}
+
+struct blame_entry {
+	struct blame_entry *prev;
+	struct blame_entry *next;
+
+	/* the first line of this group in the final image;
+	 * internally all line numbers are 0 based.
+	 */
+	int lno;
+
+	/* how many lines this group has */
+	int num_lines;
+
+	/* the commit that introduced this group into the final image */
+	struct origin *suspect;
+
+	/* true if the suspect is truly guilty; false while we have not
+	 * checked if the group came from one of its parents.
+	 */
+	char guilty;
+
+	/* the line number of the first line of this group in the
+	 * suspect's file; internally all line numbers are 0 based.
+	 */
+	int s_lno;
+
+	/* how significant this entry is -- cached to avoid
+	 * scanning the lines over and over
+	 */
+	unsigned score;
+};
+
+struct scoreboard {
+	/* the final commit (i.e. where we started digging from) */
+	struct commit *final;
+
+	const char *path;
+
+	/* the contents in the final; pointed into by buf pointers of
+	 * blame_entries
+	 */
+	const char *final_buf;
+	unsigned long final_buf_size;
+
+	/* linked list of blames */
+	struct blame_entry *ent;
+
+	/* look-up a line in the final buffer */
+	int num_lines;
+	int *lineno;
+};
+
+static int cmp_suspect(struct origin *a, struct origin *b)
+{
+	int cmp = hashcmp(a->commit->object.sha1, b->commit->object.sha1);
+	if (cmp)
+		return cmp;
+	return strcmp(a->path, b->path);
+}
+
+#define cmp_suspect(a, b) ( ((a)==(b)) ? 0 : cmp_suspect(a,b) )
+
+static void sanity_check_refcnt(struct scoreboard *);
+
+static void coalesce(struct scoreboard *sb)
+{
+	struct blame_entry *ent, *next;
+
+	for (ent = sb->ent; ent && (next = ent->next); ent = next) {
+		if (!cmp_suspect(ent->suspect, next->suspect) &&
+		    ent->guilty == next->guilty &&
+		    ent->s_lno + ent->num_lines == next->s_lno) {
+			ent->num_lines += next->num_lines;
+			ent->next = next->next;
+			if (ent->next)
+				ent->next->prev = ent;
+			origin_decref(next->suspect);
+			free(next);
+			ent->score = 0;
+			next = ent; /* again */
+		}
+	}
+
+	if (DEBUG) /* sanity */
+		sanity_check_refcnt(sb);
+}
+
+static struct origin *make_origin(struct commit *commit, const char *path)
+{
+	struct origin *o;
+	o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
+	o->commit = commit;
+	o->refcnt = 1;
+	strcpy(o->path, path);
+	return o;
+}
+
+static struct origin *get_origin(struct scoreboard *sb,
+				 struct commit *commit,
+				 const char *path)
+{
+	struct blame_entry *e;
+
+	for (e = sb->ent; e; e = e->next) {
+		if (e->suspect->commit == commit &&
+		    !strcmp(e->suspect->path, path))
+			return origin_incref(e->suspect);
+	}
+	return make_origin(commit, path);
+}
+
+static int fill_blob_sha1(struct origin *origin)
+{
+	unsigned mode;
+	char type[10];
+
+	if (!is_null_sha1(origin->blob_sha1))
+		return 0;
+	if (get_tree_entry(origin->commit->object.sha1,
+			   origin->path,
+			   origin->blob_sha1, &mode))
+		goto error_out;
+	if (sha1_object_info(origin->blob_sha1, type, NULL) ||
+	    strcmp(type, blob_type))
+		goto error_out;
+	return 0;
+ error_out:
+	hashclr(origin->blob_sha1);
+	return -1;
+}
+
+static struct origin *find_origin(struct scoreboard *sb,
+				  struct commit *parent,
+				  struct origin *origin)
+{
+	struct origin *porigin = NULL;
+	struct diff_options diff_opts;
+	const char *paths[2];
+
+	if (parent->util) {
+		/* This is a freestanding copy of origin and not
+		 * refcounted.
+		 */
+		struct origin *cached = parent->util;
+		if (!strcmp(cached->path, origin->path)) {
+			porigin = get_origin(sb, parent, cached->path);
+			if (porigin->refcnt == 1)
+				hashcpy(porigin->blob_sha1, cached->blob_sha1);
+			return porigin;
+		}
+		/* otherwise it was not very useful; free it */
+		free(parent->util);
+		parent->util = NULL;
+	}
+
+	/* See if the origin->path is different between parent
+	 * and origin first.  Most of the time they are the
+	 * same and diff-tree is fairly efficient about this.
+	 */
+	diff_setup(&diff_opts);
+	diff_opts.recursive = 1;
+	diff_opts.detect_rename = 0;
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	paths[0] = origin->path;
+	paths[1] = NULL;
+
+	diff_tree_setup_paths(paths, &diff_opts);
+	if (diff_setup_done(&diff_opts) < 0)
+		die("diff-setup");
+	diff_tree_sha1(parent->tree->object.sha1,
+		       origin->commit->tree->object.sha1,
+		       "", &diff_opts);
+	diffcore_std(&diff_opts);
+
+	/* It is either one entry that says "modified", or "created",
+	 * or nothing.
+	 */
+	if (!diff_queued_diff.nr) {
+		/* The path is the same as parent */
+		porigin = get_origin(sb, parent, origin->path);
+		hashcpy(porigin->blob_sha1, origin->blob_sha1);
+	}
+	else if (diff_queued_diff.nr != 1)
+		die("internal error in blame::find_origin");
+	else {
+		struct diff_filepair *p = diff_queued_diff.queue[0];
+		switch (p->status) {
+		default:
+			die("internal error in blame::find_origin (%c)",
+			    p->status);
+		case 'M':
+			porigin = get_origin(sb, parent, origin->path);
+			hashcpy(porigin->blob_sha1, p->one->sha1);
+			break;
+		case 'A':
+		case 'T':
+			/* Did not exist in parent, or type changed */
+			break;
+		}
+	}
+	diff_flush(&diff_opts);
+	if (porigin) {
+		struct origin *cached;
+		cached = make_origin(porigin->commit, porigin->path);
+		hashcpy(cached->blob_sha1, porigin->blob_sha1);
+		parent->util = cached;
+	}
+	return porigin;
+}
+
+static struct origin *find_rename(struct scoreboard *sb,
+				  struct commit *parent,
+				  struct origin *origin)
+{
+	struct origin *porigin = NULL;
+	struct diff_options diff_opts;
+	int i;
+	const char *paths[2];
+
+	diff_setup(&diff_opts);
+	diff_opts.recursive = 1;
+	diff_opts.detect_rename = DIFF_DETECT_RENAME;
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	diff_opts.single_follow = origin->path;
+	paths[0] = NULL;
+	diff_tree_setup_paths(paths, &diff_opts);
+	if (diff_setup_done(&diff_opts) < 0)
+		die("diff-setup");
+	diff_tree_sha1(parent->tree->object.sha1,
+		       origin->commit->tree->object.sha1,
+		       "", &diff_opts);
+	diffcore_std(&diff_opts);
+
+	for (i = 0; i < diff_queued_diff.nr; i++) {
+		struct diff_filepair *p = diff_queued_diff.queue[i];
+		if ((p->status == 'R' || p->status == 'C') &&
+		    !strcmp(p->two->path, origin->path)) {
+			porigin = get_origin(sb, parent, p->one->path);
+			hashcpy(porigin->blob_sha1, p->one->sha1);
+			break;
+		}
+	}
+	diff_flush(&diff_opts);
+	return porigin;
+}
+
+struct chunk {
+	/* line number in postimage; up to but not including this
+	 * line is the same as preimage
+	 */
+	int same;
+
+	/* preimage line number after this chunk */
+	int p_next;
+
+	/* postimage line number after this chunk */
+	int t_next;
+};
+
+struct patch {
+	struct chunk *chunks;
+	int num;
+};
+
+struct blame_diff_state {
+	struct xdiff_emit_state xm;
+	struct patch *ret;
+	unsigned hunk_post_context;
+	unsigned hunk_in_pre_context : 1;
+};
+
+static void process_u_diff(void *state_, char *line, unsigned long len)
+{
+	struct blame_diff_state *state = state_;
+	struct chunk *chunk;
+	int off1, off2, len1, len2, num;
+
+	num = state->ret->num;
+	if (len < 4 || line[0] != '@' || line[1] != '@') {
+		if (state->hunk_in_pre_context && line[0] == ' ')
+			state->ret->chunks[num - 1].same++;
+		else {
+			state->hunk_in_pre_context = 0;
+			if (line[0] == ' ')
+				state->hunk_post_context++;
+			else
+				state->hunk_post_context = 0;
+		}
+		return;
+	}
+
+	if (num && state->hunk_post_context) {
+		chunk = &state->ret->chunks[num - 1];
+		chunk->p_next -= state->hunk_post_context;
+		chunk->t_next -= state->hunk_post_context;
+	}
+	state->ret->num = ++num;
+	state->ret->chunks = xrealloc(state->ret->chunks,
+				      sizeof(struct chunk) * num);
+	chunk = &state->ret->chunks[num - 1];
+	if (parse_hunk_header(line, len, &off1, &len1, &off2, &len2)) {
+		state->ret->num--;
+		return;
+	}
+
+	/* Line numbers in patch output are one based. */
+	off1--;
+	off2--;
+
+	chunk->same = len2 ? off2 : (off2 + 1);
+
+	chunk->p_next = off1 + (len1 ? len1 : 1);
+	chunk->t_next = chunk->same + len2;
+	state->hunk_in_pre_context = 1;
+	state->hunk_post_context = 0;
+}
+
+static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
+				    int context)
+{
+	struct blame_diff_state state;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = context;
+	xecfg.flags = 0;
+	ecb.outf = xdiff_outf;
+	ecb.priv = &state;
+	memset(&state, 0, sizeof(state));
+	state.xm.consume = process_u_diff;
+	state.ret = xmalloc(sizeof(struct patch));
+	state.ret->chunks = NULL;
+	state.ret->num = 0;
+
+	xdl_diff(file_p, file_o, &xpp, &xecfg, &ecb);
+
+	if (state.ret->num) {
+		struct chunk *chunk;
+		chunk = &state.ret->chunks[state.ret->num - 1];
+		chunk->p_next -= state.hunk_post_context;
+		chunk->t_next -= state.hunk_post_context;
+	}
+	return state.ret;
+}
+
+static struct patch *get_patch(struct origin *parent, struct origin *origin)
+{
+	mmfile_t file_p, file_o;
+	struct patch *patch;
+
+	fill_origin_blob(parent, &file_p);
+	fill_origin_blob(origin, &file_o);
+	if (!file_p.ptr || !file_o.ptr)
+		return NULL;
+	patch = compare_buffer(&file_p, &file_o, 0);
+	num_get_patch++;
+	return patch;
+}
+
+static void free_patch(struct patch *p)
+{
+	free(p->chunks);
+	free(p);
+}
+
+static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
+{
+	struct blame_entry *ent, *prev = NULL;
+
+	origin_incref(e->suspect);
+
+	for (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)
+		prev = ent;
+
+	/* prev, if not NULL, is the last one that is below e */
+	e->prev = prev;
+	if (prev) {
+		e->next = prev->next;
+		prev->next = e;
+	}
+	else {
+		e->next = sb->ent;
+		sb->ent = e;
+	}
+	if (e->next)
+		e->next->prev = e;
+}
+
+static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
+{
+	struct blame_entry *p, *n;
+
+	p = dst->prev;
+	n = dst->next;
+	origin_incref(src->suspect);
+	origin_decref(dst->suspect);
+	memcpy(dst, src, sizeof(*src));
+	dst->prev = p;
+	dst->next = n;
+	dst->score = 0;
+}
+
+static const char *nth_line(struct scoreboard *sb, int lno)
+{
+	return sb->final_buf + sb->lineno[lno];
+}
+
+static void split_overlap(struct blame_entry *split,
+			  struct blame_entry *e,
+			  int tlno, int plno, int same,
+			  struct origin *parent)
+{
+	/* it is known that lines between tlno to same came from
+	 * parent, and e has an overlap with that range.  it also is
+	 * known that parent's line plno corresponds to e's line tlno.
+	 *
+	 *                <---- e ----->
+	 *                   <------>
+	 *                   <------------>
+	 *             <------------>
+	 *             <------------------>
+	 *
+	 * Potentially we need to split e into three parts; before
+	 * this chunk, the chunk to be blamed for parent, and after
+	 * that portion.
+	 */
+	int chunk_end_lno;
+	memset(split, 0, sizeof(struct blame_entry [3]));
+
+	if (e->s_lno < tlno) {
+		/* there is a pre-chunk part not blamed on parent */
+		split[0].suspect = origin_incref(e->suspect);
+		split[0].lno = e->lno;
+		split[0].s_lno = e->s_lno;
+		split[0].num_lines = tlno - e->s_lno;
+		split[1].lno = e->lno + tlno - e->s_lno;
+		split[1].s_lno = plno;
+	}
+	else {
+		split[1].lno = e->lno;
+		split[1].s_lno = plno + (e->s_lno - tlno);
+	}
+
+	if (same < e->s_lno + e->num_lines) {
+		/* there is a post-chunk part not blamed on parent */
+		split[2].suspect = origin_incref(e->suspect);
+		split[2].lno = e->lno + (same - e->s_lno);
+		split[2].s_lno = e->s_lno + (same - e->s_lno);
+		split[2].num_lines = e->s_lno + e->num_lines - same;
+		chunk_end_lno = split[2].lno;
+	}
+	else
+		chunk_end_lno = e->lno + e->num_lines;
+	split[1].num_lines = chunk_end_lno - split[1].lno;
+
+	if (split[1].num_lines < 1)
+		return;
+	split[1].suspect = origin_incref(parent);
+}
+
+static void split_blame(struct scoreboard *sb,
+			struct blame_entry *split,
+			struct blame_entry *e)
+{
+	struct blame_entry *new_entry;
+
+	if (split[0].suspect && split[2].suspect) {
+		/* we need to split e into two and add another for parent */
+		dup_entry(e, &split[0]);
+
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+	}
+	else if (!split[0].suspect && !split[2].suspect)
+		/* parent covers the entire area */
+		dup_entry(e, &split[1]);
+	else if (split[0].suspect) {
+		dup_entry(e, &split[0]);
+
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+	}
+	else {
+		dup_entry(e, &split[1]);
+
+		new_entry = xmalloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
+		add_blame_entry(sb, new_entry);
+	}
+
+	if (DEBUG) { /* sanity */
+		struct blame_entry *ent;
+		int lno = sb->ent->lno, corrupt = 0;
+
+		for (ent = sb->ent; ent; ent = ent->next) {
+			if (lno != ent->lno)
+				corrupt = 1;
+			if (ent->s_lno < 0)
+				corrupt = 1;
+			lno += ent->num_lines;
+		}
+		if (corrupt) {
+			lno = sb->ent->lno;
+			for (ent = sb->ent; ent; ent = ent->next) {
+				printf("L %8d l %8d n %8d\n",
+				       lno, ent->lno, ent->num_lines);
+				lno = ent->lno + ent->num_lines;
+			}
+			die("oops");
+		}
+	}
+}
+
+static void decref_split(struct blame_entry *split)
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		origin_decref(split[i].suspect);
+}
+
+static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
+			  int tlno, int plno, int same,
+			  struct origin *parent)
+{
+	struct blame_entry split[3];
+
+	split_overlap(split, e, tlno, plno, same, parent);
+	if (split[1].suspect)
+		split_blame(sb, split, e);
+	decref_split(split);
+}
+
+static int find_last_in_target(struct scoreboard *sb, struct origin *target)
+{
+	struct blame_entry *e;
+	int last_in_target = -1;
+
+	for (e = sb->ent; e; e = e->next) {
+		if (e->guilty || cmp_suspect(e->suspect, target))
+			continue;
+		if (last_in_target < e->s_lno + e->num_lines)
+			last_in_target = e->s_lno + e->num_lines;
+	}
+	return last_in_target;
+}
+
+static void blame_chunk(struct scoreboard *sb,
+			int tlno, int plno, int same,
+			struct origin *target, struct origin *parent)
+{
+	struct blame_entry *e;
+
+	for (e = sb->ent; e; e = e->next) {
+		if (e->guilty || cmp_suspect(e->suspect, target))
+			continue;
+		if (same <= e->s_lno)
+			continue;
+		if (tlno < e->s_lno + e->num_lines)
+			blame_overlap(sb, e, tlno, plno, same, parent);
+	}
+}
+
+static int pass_blame_to_parent(struct scoreboard *sb,
+				struct origin *target,
+				struct origin *parent)
+{
+	int i, last_in_target, plno, tlno;
+	struct patch *patch;
+
+	last_in_target = find_last_in_target(sb, target);
+	if (last_in_target < 0)
+		return 1; /* nothing remains for this target */
+
+	patch = get_patch(parent, target);
+	plno = tlno = 0;
+	for (i = 0; i < patch->num; i++) {
+		struct chunk *chunk = &patch->chunks[i];
+
+		blame_chunk(sb, tlno, plno, chunk->same, target, parent);
+		plno = chunk->p_next;
+		tlno = chunk->t_next;
+	}
+	/* rest (i.e. anything above tlno) are the same as parent */
+	blame_chunk(sb, tlno, plno, last_in_target, target, parent);
+
+	free_patch(patch);
+	return 0;
+}
+
+static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
+{
+	unsigned score;
+	const char *cp, *ep;
+
+	if (e->score)
+		return e->score;
+
+	score = 1;
+	cp = nth_line(sb, e->lno);
+	ep = nth_line(sb, e->lno + e->num_lines);
+	while (cp < ep) {
+		unsigned ch = *((unsigned char *)cp);
+		if (isalnum(ch))
+			score++;
+		cp++;
+	}
+	e->score = score;
+	return score;
+}
+
+static void copy_split_if_better(struct scoreboard *sb,
+				 struct blame_entry *best_so_far,
+				 struct blame_entry *this)
+{
+	int i;
+
+	if (!this[1].suspect)
+		return;
+	if (best_so_far[1].suspect) {
+		if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
+			return;
+	}
+
+	for (i = 0; i < 3; i++)
+		origin_incref(this[i].suspect);
+	decref_split(best_so_far);
+	memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
+}
+
+static void find_copy_in_blob(struct scoreboard *sb,
+			      struct blame_entry *ent,
+			      struct origin *parent,
+			      struct blame_entry *split,
+			      mmfile_t *file_p)
+{
+	const char *cp;
+	int cnt;
+	mmfile_t file_o;
+	struct patch *patch;
+	int i, plno, tlno;
+
+	cp = nth_line(sb, ent->lno);
+	file_o.ptr = (char*) cp;
+	cnt = ent->num_lines;
+
+	while (cnt && cp < sb->final_buf + sb->final_buf_size) {
+		if (*cp++ == '\n')
+			cnt--;
+	}
+	file_o.size = cp - file_o.ptr;
+
+	patch = compare_buffer(file_p, &file_o, 1);
+
+	memset(split, 0, sizeof(struct blame_entry [3]));
+	plno = tlno = 0;
+	for (i = 0; i < patch->num; i++) {
+		struct chunk *chunk = &patch->chunks[i];
+
+		/* tlno to chunk->same are the same as ent */
+		if (ent->num_lines <= tlno)
+			break;
+		if (tlno < chunk->same) {
+			struct blame_entry this[3];
+			split_overlap(this, ent,
+				      tlno + ent->s_lno, plno,
+				      chunk->same + ent->s_lno,
+				      parent);
+			copy_split_if_better(sb, split, this);
+			decref_split(this);
+		}
+		plno = chunk->p_next;
+		tlno = chunk->t_next;
+	}
+	free_patch(patch);
+}
+
+static int find_move_in_parent(struct scoreboard *sb,
+			       struct origin *target,
+			       struct origin *parent)
+{
+	int last_in_target, made_progress;
+	struct blame_entry *e, split[3];
+	mmfile_t file_p;
+
+	last_in_target = find_last_in_target(sb, target);
+	if (last_in_target < 0)
+		return 1; /* nothing remains for this target */
+
+	fill_origin_blob(parent, &file_p);
+	if (!file_p.ptr)
+		return 0;
+
+	made_progress = 1;
+	while (made_progress) {
+		made_progress = 0;
+		for (e = sb->ent; e; e = e->next) {
+			if (e->guilty || cmp_suspect(e->suspect, target))
+				continue;
+			find_copy_in_blob(sb, e, parent, split, &file_p);
+			if (split[1].suspect &&
+			    blame_move_score < ent_score(sb, &split[1])) {
+				split_blame(sb, split, e);
+				made_progress = 1;
+			}
+			decref_split(split);
+		}
+	}
+	return 0;
+}
+
+
+struct blame_list {
+	struct blame_entry *ent;
+	struct blame_entry split[3];
+};
+
+static struct blame_list *setup_blame_list(struct scoreboard *sb,
+					   struct origin *target,
+					   int *num_ents_p)
+{
+	struct blame_entry *e;
+	int num_ents, i;
+	struct blame_list *blame_list = NULL;
+
+	/* Count the number of entries the target is suspected for,
+	 * and prepare a list of entry and the best split.
+	 */
+	for (e = sb->ent, num_ents = 0; e; e = e->next)
+		if (!e->guilty && !cmp_suspect(e->suspect, target))
+			num_ents++;
+	if (num_ents) {
+		blame_list = xcalloc(num_ents, sizeof(struct blame_list));
+		for (e = sb->ent, i = 0; e; e = e->next)
+			if (!e->guilty && !cmp_suspect(e->suspect, target))
+				blame_list[i++].ent = e;
+	}
+	*num_ents_p = num_ents;
+	return blame_list;
+}
+
+static int find_copy_in_parent(struct scoreboard *sb,
+			       struct origin *target,
+			       struct commit *parent,
+			       struct origin *porigin,
+			       int opt)
+{
+	struct diff_options diff_opts;
+	const char *paths[1];
+	int i, j;
+	int retval;
+	struct blame_list *blame_list;
+	int num_ents;
+
+	blame_list = setup_blame_list(sb, target, &num_ents);
+	if (!blame_list)
+		return 1; /* nothing remains for this target */
+
+	diff_setup(&diff_opts);
+	diff_opts.recursive = 1;
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+
+	paths[0] = NULL;
+	diff_tree_setup_paths(paths, &diff_opts);
+	if (diff_setup_done(&diff_opts) < 0)
+		die("diff-setup");
+
+	/* Try "find copies harder" on new path if requested;
+	 * we do not want to use diffcore_rename() actually to
+	 * match things up; find_copies_harder is set only to
+	 * force diff_tree_sha1() to feed all filepairs to diff_queue,
+	 * and this code needs to be after diff_setup_done(), which
+	 * usually makes find-copies-harder imply copy detection.
+	 */
+	if ((opt & PICKAXE_BLAME_COPY_HARDER) &&
+	    (!porigin || strcmp(target->path, porigin->path)))
+		diff_opts.find_copies_harder = 1;
+
+	diff_tree_sha1(parent->tree->object.sha1,
+		       target->commit->tree->object.sha1,
+		       "", &diff_opts);
+
+	if (!diff_opts.find_copies_harder)
+		diffcore_std(&diff_opts);
+
+	retval = 0;
+	while (1) {
+		int made_progress = 0;
+
+		for (i = 0; i < diff_queued_diff.nr; i++) {
+			struct diff_filepair *p = diff_queued_diff.queue[i];
+			struct origin *norigin;
+			mmfile_t file_p;
+			struct blame_entry this[3];
+
+			if (!DIFF_FILE_VALID(p->one))
+				continue; /* does not exist in parent */
+			if (porigin && !strcmp(p->one->path, porigin->path))
+				/* find_move already dealt with this path */
+				continue;
+
+			norigin = get_origin(sb, parent, p->one->path);
+			hashcpy(norigin->blob_sha1, p->one->sha1);
+			fill_origin_blob(norigin, &file_p);
+			if (!file_p.ptr)
+				continue;
+
+			for (j = 0; j < num_ents; j++) {
+				find_copy_in_blob(sb, blame_list[j].ent,
+						  norigin, this, &file_p);
+				copy_split_if_better(sb, blame_list[j].split,
+						     this);
+				decref_split(this);
+			}
+			origin_decref(norigin);
+		}
+
+		for (j = 0; j < num_ents; j++) {
+			struct blame_entry *split = blame_list[j].split;
+			if (split[1].suspect &&
+			    blame_copy_score < ent_score(sb, &split[1])) {
+				split_blame(sb, split, blame_list[j].ent);
+				made_progress = 1;
+			}
+			decref_split(split);
+		}
+		free(blame_list);
+
+		if (!made_progress)
+			break;
+		blame_list = setup_blame_list(sb, target, &num_ents);
+		if (!blame_list) {
+			retval = 1;
+			break;
+		}
+	}
+	diff_flush(&diff_opts);
+
+	return retval;
+}
+
+/* The blobs of origin and porigin exactly match, so everything
+ * origin is suspected for can be blamed on the parent.
+ */
+static void pass_whole_blame(struct scoreboard *sb,
+			     struct origin *origin, struct origin *porigin)
+{
+	struct blame_entry *e;
+
+	if (!porigin->file.ptr && origin->file.ptr) {
+		/* Steal its file */
+		porigin->file = origin->file;
+		origin->file.ptr = NULL;
+	}
+	for (e = sb->ent; e; e = e->next) {
+		if (cmp_suspect(e->suspect, origin))
+			continue;
+		origin_incref(porigin);
+		origin_decref(e->suspect);
+		e->suspect = porigin;
+	}
+}
+
+#define MAXPARENT 16
+
+static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
+{
+	int i, pass;
+	struct commit *commit = origin->commit;
+	struct commit_list *parent;
+	struct origin *parent_origin[MAXPARENT], *porigin;
+
+	memset(parent_origin, 0, sizeof(parent_origin));
+
+	/* The first pass looks for unrenamed path to optimize for
+	 * common cases, then we look for renames in the second pass.
+	 */
+	for (pass = 0; pass < 2; pass++) {
+		struct origin *(*find)(struct scoreboard *,
+				       struct commit *, struct origin *);
+		find = pass ? find_rename : find_origin;
+
+		for (i = 0, parent = commit->parents;
+		     i < MAXPARENT && parent;
+		     parent = parent->next, i++) {
+			struct commit *p = parent->item;
+			int j, same;
+
+			if (parent_origin[i])
+				continue;
+			if (parse_commit(p))
+				continue;
+			porigin = find(sb, p, origin);
+			if (!porigin)
+				continue;
+			if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
+				pass_whole_blame(sb, origin, porigin);
+				origin_decref(porigin);
+				goto finish;
+			}
+			for (j = same = 0; j < i; j++)
+				if (parent_origin[j] &&
+				    !hashcmp(parent_origin[j]->blob_sha1,
+					     porigin->blob_sha1)) {
+					same = 1;
+					break;
+				}
+			if (!same)
+				parent_origin[i] = porigin;
+			else
+				origin_decref(porigin);
+		}
+	}
+
+	num_commits++;
+	for (i = 0, parent = commit->parents;
+	     i < MAXPARENT && parent;
+	     parent = parent->next, i++) {
+		struct origin *porigin = parent_origin[i];
+		if (!porigin)
+			continue;
+		if (pass_blame_to_parent(sb, origin, porigin))
+			goto finish;
+	}
+
+	/*
+	 * Optionally run "miff" to find moves in parents' files here.
+	 */
+	if (opt & PICKAXE_BLAME_MOVE)
+		for (i = 0, parent = commit->parents;
+		     i < MAXPARENT && parent;
+		     parent = parent->next, i++) {
+			struct origin *porigin = parent_origin[i];
+			if (!porigin)
+				continue;
+			if (find_move_in_parent(sb, origin, porigin))
+				goto finish;
+		}
+
+	/*
+	 * Optionally run "ciff" to find copies from parents' files here.
+	 */
+	if (opt & PICKAXE_BLAME_COPY)
+		for (i = 0, parent = commit->parents;
+		     i < MAXPARENT && parent;
+		     parent = parent->next, i++) {
+			struct origin *porigin = parent_origin[i];
+			if (find_copy_in_parent(sb, origin, parent->item,
+						porigin, opt))
+				goto finish;
+		}
+
+ finish:
+	for (i = 0; i < MAXPARENT; i++)
+		origin_decref(parent_origin[i]);
+}
+
+static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
+{
+	while (1) {
+		struct blame_entry *ent;
+		struct commit *commit;
+		struct origin *suspect = NULL;
+
+		/* find one suspect to break down */
+		for (ent = sb->ent; !suspect && ent; ent = ent->next)
+			if (!ent->guilty)
+				suspect = ent->suspect;
+		if (!suspect)
+			return; /* all done */
+
+		origin_incref(suspect);
+		commit = suspect->commit;
+		if (!commit->object.parsed)
+			parse_commit(commit);
+		if (!(commit->object.flags & UNINTERESTING) &&
+		    !(revs->max_age != -1 && commit->date  < revs->max_age))
+			pass_blame(sb, suspect, opt);
+
+		/* Take responsibility for the remaining entries */
+		for (ent = sb->ent; ent; ent = ent->next)
+			if (!cmp_suspect(ent->suspect, suspect))
+				ent->guilty = 1;
+		origin_decref(suspect);
+
+		if (DEBUG) /* sanity */
+			sanity_check_refcnt(sb);
+	}
+}
+
+static const char *format_time(unsigned long time, const char *tz_str,
+			       int show_raw_time)
+{
+	static char time_buf[128];
+	time_t t = time;
+	int minutes, tz;
+	struct tm *tm;
+
+	if (show_raw_time) {
+		sprintf(time_buf, "%lu %s", time, tz_str);
+		return time_buf;
+	}
+
+	tz = atoi(tz_str);
+	minutes = tz < 0 ? -tz : tz;
+	minutes = (minutes / 100)*60 + (minutes % 100);
+	minutes = tz < 0 ? -minutes : minutes;
+	t = time + minutes * 60;
+	tm = gmtime(&t);
+
+	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
+	strcat(time_buf, tz_str);
+	return time_buf;
+}
+
+struct commit_info
+{
+	char *author;
+	char *author_mail;
+	unsigned long author_time;
+	char *author_tz;
+
+	/* filled only when asked for details */
+	char *committer;
+	char *committer_mail;
+	unsigned long committer_time;
+	char *committer_tz;
+
+	char *summary;
+};
+
+static void get_ac_line(const char *inbuf, const char *what,
+			int bufsz, char *person, char **mail,
+			unsigned long *time, char **tz)
+{
+	int len;
+	char *tmp, *endp;
+
+	tmp = strstr(inbuf, what);
+	if (!tmp)
+		goto error_out;
+	tmp += strlen(what);
+	endp = strchr(tmp, '\n');
+	if (!endp)
+		len = strlen(tmp);
+	else
+		len = endp - tmp;
+	if (bufsz <= len) {
+	error_out:
+		/* Ugh */
+		person = *mail = *tz = "(unknown)";
+		*time = 0;
+		return;
+	}
+	memcpy(person, tmp, len);
+
+	tmp = person;
+	tmp += len;
+	*tmp = 0;
+	while (*tmp != ' ')
+		tmp--;
+	*tz = tmp+1;
+
+	*tmp = 0;
+	while (*tmp != ' ')
+		tmp--;
+	*time = strtoul(tmp, NULL, 10);
+
+	*tmp = 0;
+	while (*tmp != ' ')
+		tmp--;
+	*mail = tmp + 1;
+	*tmp = 0;
+}
+
+static void get_commit_info(struct commit *commit,
+			    struct commit_info *ret,
+			    int detailed)
+{
+	int len;
+	char *tmp, *endp;
+	static char author_buf[1024];
+	static char committer_buf[1024];
+	static char summary_buf[1024];
+
+	/* We've operated without save_commit_buffer, so
+	 * we now need to populate them for output.
+	 */
+	if (!commit->buffer) {
+		char type[20];
+		unsigned long size;
+		commit->buffer =
+			read_sha1_file(commit->object.sha1, type, &size);
+	}
+	ret->author = author_buf;
+	get_ac_line(commit->buffer, "\nauthor ",
+		    sizeof(author_buf), author_buf, &ret->author_mail,
+		    &ret->author_time, &ret->author_tz);
+
+	if (!detailed)
+		return;
+
+	ret->committer = committer_buf;
+	get_ac_line(commit->buffer, "\ncommitter ",
+		    sizeof(committer_buf), committer_buf, &ret->committer_mail,
+		    &ret->committer_time, &ret->committer_tz);
+
+	ret->summary = summary_buf;
+	tmp = strstr(commit->buffer, "\n\n");
+	if (!tmp) {
+	error_out:
+		sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
+		return;
+	}
+	tmp += 2;
+	endp = strchr(tmp, '\n');
+	if (!endp)
+		goto error_out;
+	len = endp - tmp;
+	if (len >= sizeof(summary_buf))
+		goto error_out;
+	memcpy(summary_buf, tmp, len);
+	summary_buf[len] = 0;
+}
+
+#define OUTPUT_ANNOTATE_COMPAT	001
+#define OUTPUT_LONG_OBJECT_NAME	002
+#define OUTPUT_RAW_TIMESTAMP	004
+#define OUTPUT_PORCELAIN	010
+#define OUTPUT_SHOW_NAME	020
+#define OUTPUT_SHOW_NUMBER	040
+#define OUTPUT_SHOW_SCORE      0100
+
+static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
+{
+	int cnt;
+	const char *cp;
+	struct origin *suspect = ent->suspect;
+	char hex[41];
+
+	strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+	printf("%s%c%d %d %d\n",
+	       hex,
+	       ent->guilty ? ' ' : '*', // purely for debugging
+	       ent->s_lno + 1,
+	       ent->lno + 1,
+	       ent->num_lines);
+	if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
+		struct commit_info ci;
+		suspect->commit->object.flags |= METAINFO_SHOWN;
+		get_commit_info(suspect->commit, &ci, 1);
+		printf("author %s\n", ci.author);
+		printf("author-mail %s\n", ci.author_mail);
+		printf("author-time %lu\n", ci.author_time);
+		printf("author-tz %s\n", ci.author_tz);
+		printf("committer %s\n", ci.committer);
+		printf("committer-mail %s\n", ci.committer_mail);
+		printf("committer-time %lu\n", ci.committer_time);
+		printf("committer-tz %s\n", ci.committer_tz);
+		printf("filename %s\n", suspect->path);
+		printf("summary %s\n", ci.summary);
+	}
+	else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH)
+		printf("filename %s\n", suspect->path);
+
+	cp = nth_line(sb, ent->lno);
+	for (cnt = 0; cnt < ent->num_lines; cnt++) {
+		char ch;
+		if (cnt)
+			printf("%s %d %d\n", hex,
+			       ent->s_lno + 1 + cnt,
+			       ent->lno + 1 + cnt);
+		putchar('\t');
+		do {
+			ch = *cp++;
+			putchar(ch);
+		} while (ch != '\n' &&
+			 cp < sb->final_buf + sb->final_buf_size);
+	}
+}
+
+static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
+{
+	int cnt;
+	const char *cp;
+	struct origin *suspect = ent->suspect;
+	struct commit_info ci;
+	char hex[41];
+	int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
+
+	get_commit_info(suspect->commit, &ci, 1);
+	strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+
+	cp = nth_line(sb, ent->lno);
+	for (cnt = 0; cnt < ent->num_lines; cnt++) {
+		char ch;
+
+		printf("%.*s", (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8, hex);
+		if (opt & OUTPUT_ANNOTATE_COMPAT)
+			printf("\t(%10s\t%10s\t%d)", ci.author,
+			       format_time(ci.author_time, ci.author_tz,
+					   show_raw_time),
+			       ent->lno + 1 + cnt);
+		else {
+			if (opt & OUTPUT_SHOW_SCORE)
+				printf(" %*d %02d",
+				       max_score_digits, ent->score,
+				       ent->suspect->refcnt);
+			if (opt & OUTPUT_SHOW_NAME)
+				printf(" %-*.*s", longest_file, longest_file,
+				       suspect->path);
+			if (opt & OUTPUT_SHOW_NUMBER)
+				printf(" %*d", max_orig_digits,
+				       ent->s_lno + 1 + cnt);
+			printf(" (%-*.*s %10s %*d) ",
+			       longest_author, longest_author, ci.author,
+			       format_time(ci.author_time, ci.author_tz,
+					   show_raw_time),
+			       max_digits, ent->lno + 1 + cnt);
+		}
+		do {
+			ch = *cp++;
+			putchar(ch);
+		} while (ch != '\n' &&
+			 cp < sb->final_buf + sb->final_buf_size);
+	}
+}
+
+static void output(struct scoreboard *sb, int option)
+{
+	struct blame_entry *ent;
+
+	if (option & OUTPUT_PORCELAIN) {
+		for (ent = sb->ent; ent; ent = ent->next) {
+			struct blame_entry *oth;
+			struct origin *suspect = ent->suspect;
+			struct commit *commit = suspect->commit;
+			if (commit->object.flags & MORE_THAN_ONE_PATH)
+				continue;
+			for (oth = ent->next; oth; oth = oth->next) {
+				if ((oth->suspect->commit != commit) ||
+				    !strcmp(oth->suspect->path, suspect->path))
+					continue;
+				commit->object.flags |= MORE_THAN_ONE_PATH;
+				break;
+			}
+		}
+	}
+
+	for (ent = sb->ent; ent; ent = ent->next) {
+		if (option & OUTPUT_PORCELAIN)
+			emit_porcelain(sb, ent);
+		else {
+			emit_other(sb, ent, option);
+		}
+	}
+}
+
+static int prepare_lines(struct scoreboard *sb)
+{
+	const char *buf = sb->final_buf;
+	unsigned long len = sb->final_buf_size;
+	int num = 0, incomplete = 0, bol = 1;
+
+	if (len && buf[len-1] != '\n')
+		incomplete++; /* incomplete line at the end */
+	while (len--) {
+		if (bol) {
+			sb->lineno = xrealloc(sb->lineno,
+					      sizeof(int* ) * (num + 1));
+			sb->lineno[num] = buf - sb->final_buf;
+			bol = 0;
+		}
+		if (*buf++ == '\n') {
+			num++;
+			bol = 1;
+		}
+	}
+	sb->lineno = xrealloc(sb->lineno,
+			      sizeof(int* ) * (num + incomplete + 1));
+	sb->lineno[num + incomplete] = buf - sb->final_buf;
+	sb->num_lines = num + incomplete;
+	return sb->num_lines;
+}
+
+static int read_ancestry(const char *graft_file)
+{
+	FILE *fp = fopen(graft_file, "r");
+	char buf[1024];
+	if (!fp)
+		return -1;
+	while (fgets(buf, sizeof(buf), fp)) {
+		/* The format is just "Commit Parent1 Parent2 ...\n" */
+		int len = strlen(buf);
+		struct commit_graft *graft = read_graft_line(buf, len);
+		if (graft)
+			register_commit_graft(graft, 0);
+	}
+	fclose(fp);
+	return 0;
+}
+
+static int lineno_width(int lines)
+{
+        int i, width;
+
+        for (width = 1, i = 10; i <= lines + 1; width++)
+                i *= 10;
+        return width;
+}
+
+static void find_alignment(struct scoreboard *sb, int *option)
+{
+	int longest_src_lines = 0;
+	int longest_dst_lines = 0;
+	unsigned largest_score = 0;
+	struct blame_entry *e;
+
+	for (e = sb->ent; e; e = e->next) {
+		struct origin *suspect = e->suspect;
+		struct commit_info ci;
+		int num;
+
+		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
+			suspect->commit->object.flags |= METAINFO_SHOWN;
+			get_commit_info(suspect->commit, &ci, 1);
+			if (strcmp(suspect->path, sb->path))
+				*option |= OUTPUT_SHOW_NAME;
+			num = strlen(suspect->path);
+			if (longest_file < num)
+				longest_file = num;
+			num = strlen(ci.author);
+			if (longest_author < num)
+				longest_author = num;
+		}
+		num = e->s_lno + e->num_lines;
+		if (longest_src_lines < num)
+			longest_src_lines = num;
+		num = e->lno + e->num_lines;
+		if (longest_dst_lines < num)
+			longest_dst_lines = num;
+		if (largest_score < ent_score(sb, e))
+			largest_score = ent_score(sb, e);
+	}
+	max_orig_digits = lineno_width(longest_src_lines);
+	max_digits = lineno_width(longest_dst_lines);
+	max_score_digits = lineno_width(largest_score);
+}
+
+static void sanity_check_refcnt(struct scoreboard *sb)
+{
+	int baa = 0;
+	struct blame_entry *ent;
+
+	for (ent = sb->ent; ent; ent = ent->next) {
+		/* Nobody should have zero or negative refcnt */
+		if (ent->suspect->refcnt <= 0) {
+			fprintf(stderr, "%s in %s has negative refcnt %d\n",
+				ent->suspect->path,
+				sha1_to_hex(ent->suspect->commit->object.sha1),
+				ent->suspect->refcnt);
+			baa = 1;
+		}
+	}
+	for (ent = sb->ent; ent; ent = ent->next) {
+		/* Mark the ones that haven't been checked */
+		if (0 < ent->suspect->refcnt)
+			ent->suspect->refcnt = -ent->suspect->refcnt;
+	}
+	for (ent = sb->ent; ent; ent = ent->next) {
+		/* then pick each and see if they have the the correct
+		 * refcnt.
+		 */
+		int found;
+		struct blame_entry *e;
+		struct origin *suspect = ent->suspect;
+
+		if (0 < suspect->refcnt)
+			continue;
+		suspect->refcnt = -suspect->refcnt; /* Unmark */
+		for (found = 0, e = sb->ent; e; e = e->next) {
+			if (e->suspect != suspect)
+				continue;
+			found++;
+		}
+		if (suspect->refcnt != found) {
+			fprintf(stderr, "%s in %s has refcnt %d, not %d\n",
+				ent->suspect->path,
+				sha1_to_hex(ent->suspect->commit->object.sha1),
+				ent->suspect->refcnt, found);
+			baa = 2;
+		}
+	}
+	if (baa) {
+		int opt = 0160;
+		find_alignment(sb, &opt);
+		output(sb, opt);
+		die("Baa %d!", baa);
+	}
+}
+
+static int has_path_in_work_tree(const char *path)
+{
+	struct stat st;
+	return !lstat(path, &st);
+}
+
+static unsigned parse_score(const char *arg)
+{
+	char *end;
+	unsigned long score = strtoul(arg, &end, 10);
+	if (*end)
+		return 0;
+	return score;
+}
+
+static const char *add_prefix(const char *prefix, const char *path)
+{
+	if (!prefix || !prefix[0])
+		return path;
+	return prefix_path(prefix, strlen(prefix), path);
+}
+
+static const char *parse_loc(const char *spec,
+			     struct scoreboard *sb, long lno,
+			     long begin, long *ret)
+{
+	char *term;
+	const char *line;
+	long num;
+	int reg_error;
+	regex_t regexp;
+	regmatch_t match[1];
+
+	/* Allow "-L <something>,+20" to mean starting at <something>
+	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
+	 * <something>.
+	 */
+	if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
+		num = strtol(spec + 1, &term, 10);
+		if (term != spec + 1) {
+			if (spec[0] == '-')
+				num = 0 - num;
+			if (0 < num)
+				*ret = begin + num - 2;
+			else if (!num)
+				*ret = begin;
+			else
+				*ret = begin + num;
+			return term;
+		}
+		return spec;
+	}
+	num = strtol(spec, &term, 10);
+	if (term != spec) {
+		*ret = num;
+		return term;
+	}
+	if (spec[0] != '/')
+		return spec;
+
+	/* it could be a regexp of form /.../ */
+	for (term = (char*) spec + 1; *term && *term != '/'; term++) {
+		if (*term == '\\')
+			term++;
+	}
+	if (*term != '/')
+		return spec;
+
+	/* try [spec+1 .. term-1] as regexp */
+	*term = 0;
+	begin--; /* input is in human terms */
+	line = nth_line(sb, begin);
+
+	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
+	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
+		const char *cp = line + match[0].rm_so;
+		const char *nline;
+
+		while (begin++ < lno) {
+			nline = nth_line(sb, begin);
+			if (line <= cp && cp < nline)
+				break;
+			line = nline;
+		}
+		*ret = begin;
+		regfree(&regexp);
+		*term++ = '/';
+		return term;
+	}
+	else {
+		char errbuf[1024];
+		regerror(reg_error, &regexp, errbuf, 1024);
+		die("-L parameter '%s': %s", spec + 1, errbuf);
+	}
+}
+
+static void prepare_blame_range(struct scoreboard *sb,
+				const char *bottomtop,
+				long lno,
+				long *bottom, long *top)
+{
+	const char *term;
+
+	term = parse_loc(bottomtop, sb, lno, 1, bottom);
+	if (*term == ',') {
+		term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
+		if (*term)
+			usage(blame_usage);
+	}
+	if (*term)
+		usage(blame_usage);
+}
+
+int cmd_blame(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	const char *path;
+	struct scoreboard sb;
+	struct origin *o;
+	struct blame_entry *ent;
+	int i, seen_dashdash, unk, opt;
+	long bottom, top, lno;
+	int output_option = 0;
+	const char *revs_file = NULL;
+	const char *final_commit_name = NULL;
+	char type[10];
+	const char *bottomtop = NULL;
+
+	save_commit_buffer = 0;
+
+	opt = 0;
+	seen_dashdash = 0;
+	for (unk = i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (*arg != '-')
+			break;
+		else if (!strcmp("-c", arg))
+			output_option |= OUTPUT_ANNOTATE_COMPAT;
+		else if (!strcmp("-t", arg))
+			output_option |= OUTPUT_RAW_TIMESTAMP;
+		else if (!strcmp("-l", arg))
+			output_option |= OUTPUT_LONG_OBJECT_NAME;
+		else if (!strcmp("-S", arg) && ++i < argc)
+			revs_file = argv[i];
+		else if (!strncmp("-M", arg, 2)) {
+			opt |= PICKAXE_BLAME_MOVE;
+			blame_move_score = parse_score(arg+2);
+		}
+		else if (!strncmp("-C", arg, 2)) {
+			if (opt & PICKAXE_BLAME_COPY)
+				opt |= PICKAXE_BLAME_COPY_HARDER;
+			opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
+			blame_copy_score = parse_score(arg+2);
+		}
+		else if (!strncmp("-L", arg, 2)) {
+			if (!arg[2]) {
+				if (++i >= argc)
+					usage(blame_usage);
+				arg = argv[i];
+			}
+			else
+				arg += 2;
+			if (bottomtop)
+				die("More than one '-L n,m' option given");
+			bottomtop = arg;
+		}
+		else if (!strcmp("--score-debug", arg))
+			output_option |= OUTPUT_SHOW_SCORE;
+		else if (!strcmp("-f", arg) ||
+			 !strcmp("--show-name", arg))
+			output_option |= OUTPUT_SHOW_NAME;
+		else if (!strcmp("-n", arg) ||
+			 !strcmp("--show-number", arg))
+			output_option |= OUTPUT_SHOW_NUMBER;
+		else if (!strcmp("-p", arg) ||
+			 !strcmp("--porcelain", arg))
+			output_option |= OUTPUT_PORCELAIN;
+		else if (!strcmp("--", arg)) {
+			seen_dashdash = 1;
+			i++;
+			break;
+		}
+		else
+			argv[unk++] = arg;
+	}
+
+	if (!blame_move_score)
+		blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
+	if (!blame_copy_score)
+		blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
+
+	/* We have collected options unknown to us in argv[1..unk]
+	 * which are to be passed to revision machinery if we are
+	 * going to do the "bottom" procesing.
+	 *
+	 * The remaining are:
+	 *
+	 * (1) if seen_dashdash, its either
+	 *     "-options -- <path>" or
+	 *     "-options -- <path> <rev>".
+	 *     but the latter is allowed only if there is no
+	 *     options that we passed to revision machinery.
+	 *
+	 * (2) otherwise, we may have "--" somewhere later and
+	 *     might be looking at the first one of multiple 'rev'
+	 *     parameters (e.g. " master ^next ^maint -- path").
+	 *     See if there is a dashdash first, and give the
+	 *     arguments before that to revision machinery.
+	 *     After that there must be one 'path'.
+	 *
+	 * (3) otherwise, its one of the three:
+	 *     "-options <path> <rev>"
+	 *     "-options <rev> <path>"
+	 *     "-options <path>"
+	 *     but again the first one is allowed only if
+	 *     there is no options that we passed to revision
+	 *     machinery.
+	 */
+
+	if (seen_dashdash) {
+		/* (1) */
+		if (argc <= i)
+			usage(blame_usage);
+		path = add_prefix(prefix, argv[i]);
+		if (i + 1 == argc - 1) {
+			if (unk != 1)
+				usage(blame_usage);
+			argv[unk++] = argv[i + 1];
+		}
+		else if (i + 1 != argc)
+			/* garbage at end */
+			usage(blame_usage);
+	}
+	else {
+		int j;
+		for (j = i; !seen_dashdash && j < argc; j++)
+			if (!strcmp(argv[j], "--"))
+				seen_dashdash = j;
+		if (seen_dashdash) {
+			if (seen_dashdash + 1 != argc - 1)
+				usage(blame_usage);
+			path = add_prefix(prefix, argv[seen_dashdash + 1]);
+			for (j = i; j < seen_dashdash; j++)
+				argv[unk++] = argv[j];
+		}
+		else {
+			/* (3) */
+			path = add_prefix(prefix, argv[i]);
+			if (i + 1 == argc - 1) {
+				final_commit_name = argv[i + 1];
+
+				/* if (unk == 1) we could be getting
+				 * old-style
+				 */
+				if (unk == 1 && !has_path_in_work_tree(path)) {
+					path = add_prefix(prefix, argv[i + 1]);
+					final_commit_name = argv[i];
+				}
+			}
+			else if (i != argc - 1)
+				usage(blame_usage); /* garbage at end */
+
+			if (!has_path_in_work_tree(path))
+				die("cannot stat path %s: %s",
+				    path, strerror(errno));
+		}
+	}
+
+	if (final_commit_name)
+		argv[unk++] = final_commit_name;
+
+	/* Now we got rev and path.  We do not want the path pruning
+	 * but we may want "bottom" processing.
+	 */
+	argv[unk] = NULL;
+
+	init_revisions(&revs, NULL);
+	setup_revisions(unk, argv, &revs, "HEAD");
+	memset(&sb, 0, sizeof(sb));
+
+	/* There must be one and only one positive commit in the
+	 * revs->pending array.
+	 */
+	for (i = 0; i < revs.pending.nr; i++) {
+		struct object *obj = revs.pending.objects[i].item;
+		if (obj->flags & UNINTERESTING)
+			continue;
+		while (obj->type == OBJ_TAG)
+			obj = deref_tag(obj, NULL, 0);
+		if (obj->type != OBJ_COMMIT)
+			die("Non commit %s?",
+			    revs.pending.objects[i].name);
+		if (sb.final)
+			die("More than one commit to dig from %s and %s?",
+			    revs.pending.objects[i].name,
+			    final_commit_name);
+		sb.final = (struct commit *) obj;
+		final_commit_name = revs.pending.objects[i].name;
+	}
+
+	if (!sb.final) {
+		/* "--not A B -- path" without anything positive */
+		unsigned char head_sha1[20];
+
+		final_commit_name = "HEAD";
+		if (get_sha1(final_commit_name, head_sha1))
+			die("No such ref: HEAD");
+		sb.final = lookup_commit_reference(head_sha1);
+		add_pending_object(&revs, &(sb.final->object), "HEAD");
+	}
+
+	/* If we have bottom, this will mark the ancestors of the
+	 * bottom commits we would reach while traversing as
+	 * uninteresting.
+	 */
+	prepare_revision_walk(&revs);
+
+	o = get_origin(&sb, sb.final, path);
+	if (fill_blob_sha1(o))
+		die("no such path %s in %s", path, final_commit_name);
+
+	sb.final_buf = read_sha1_file(o->blob_sha1, type, &sb.final_buf_size);
+	num_read_blob++;
+	lno = prepare_lines(&sb);
+
+	bottom = top = 0;
+	if (bottomtop)
+		prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
+	if (bottom && top && top < bottom) {
+		long tmp;
+		tmp = top; top = bottom; bottom = tmp;
+	}
+	if (bottom < 1)
+		bottom = 1;
+	if (top < 1)
+		top = lno;
+	bottom--;
+	if (lno < top)
+		die("file %s has only %lu lines", path, lno);
+
+	ent = xcalloc(1, sizeof(*ent));
+	ent->lno = bottom;
+	ent->num_lines = top - bottom;
+	ent->suspect = o;
+	ent->s_lno = bottom;
+
+	sb.ent = ent;
+	sb.path = path;
+
+	if (revs_file && read_ancestry(revs_file))
+		die("reading graft file %s failed: %s",
+		    revs_file, strerror(errno));
+
+	assign_blame(&sb, &revs, opt);
+
+	coalesce(&sb);
+
+	if (!(output_option & OUTPUT_PORCELAIN))
+		find_alignment(&sb, &output_option);
+
+	output(&sb, output_option);
+	free((void *)sb.final_buf);
+	for (ent = sb.ent; ent; ) {
+		struct blame_entry *e = ent->next;
+		free(ent);
+		ent = e;
+	}
+
+	if (DEBUG) {
+		printf("num read blob: %d\n", num_read_blob);
+		printf("num get patch: %d\n", num_get_patch);
+		printf("num commits: %d\n", num_commits);
+	}
+	return 0;
+}
diff --git a/builtin-branch.c b/builtin-branch.c
new file mode 100644
index 0000000..368b68e
--- /dev/null
+++ b/builtin-branch.c
@@ -0,0 +1,221 @@
+/*
+ * Builtin "git branch"
+ *
+ * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-branch.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "refs.h"
+#include "commit.h"
+#include "builtin.h"
+
+static const char builtin_branch_usage[] =
+"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r]";
+
+
+static const char *head;
+static unsigned char head_sha1[20];
+
+static int in_merge_bases(const unsigned char *sha1,
+			  struct commit *rev1,
+			  struct commit *rev2)
+{
+	struct commit_list *bases, *b;
+	int ret = 0;
+
+	bases = get_merge_bases(rev1, rev2, 1);
+	for (b = bases; b; b = b->next) {
+		if (!hashcmp(sha1, b->item->object.sha1)) {
+			ret = 1;
+			break;
+		}
+	}
+
+	free_commit_list(bases);
+	return ret;
+}
+
+static void delete_branches(int argc, const char **argv, int force)
+{
+	struct commit *rev, *head_rev;
+	unsigned char sha1[20];
+	char *name;
+	int i;
+
+	head_rev = lookup_commit_reference(head_sha1);
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(head, argv[i]))
+			die("Cannot delete the branch you are currently on.");
+
+		name = xstrdup(mkpath("refs/heads/%s", argv[i]));
+		if (!resolve_ref(name, sha1, 1, NULL))
+			die("Branch '%s' not found.", argv[i]);
+
+		rev = lookup_commit_reference(sha1);
+		if (!rev || !head_rev)
+			die("Couldn't look up commit objects.");
+
+		/* This checks whether the merge bases of branch and
+		 * HEAD contains branch -- which means that the HEAD
+		 * contains everything in both.
+		 */
+
+		if (!force &&
+		    !in_merge_bases(sha1, rev, head_rev)) {
+			fprintf(stderr,
+				"The branch '%s' is not a strict subset of your current HEAD.\n"
+				"If you are sure you want to delete it, run 'git branch -D %s'.\n",
+				argv[i], argv[i]);
+			exit(1);
+		}
+
+		if (delete_ref(name, sha1))
+			printf("Error deleting branch '%s'\n", argv[i]);
+		else
+			printf("Deleted branch %s.\n", argv[i]);
+
+		free(name);
+	}
+}
+
+static int ref_index, ref_alloc;
+static char **ref_list;
+
+static int append_ref(const char *refname, const unsigned char *sha1, int flags,
+		void *cb_data)
+{
+	if (ref_index >= ref_alloc) {
+		ref_alloc = alloc_nr(ref_alloc);
+		ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *));
+	}
+
+	ref_list[ref_index++] = xstrdup(refname);
+
+	return 0;
+}
+
+static int ref_cmp(const void *r1, const void *r2)
+{
+	return strcmp(*(char **)r1, *(char **)r2);
+}
+
+static void print_ref_list(int remote_only)
+{
+	int i;
+	char c;
+
+	if (remote_only)
+		for_each_remote_ref(append_ref, NULL);
+	else
+		for_each_branch_ref(append_ref, NULL);
+
+	qsort(ref_list, ref_index, sizeof(char *), ref_cmp);
+
+	for (i = 0; i < ref_index; i++) {
+		c = ' ';
+		if (!strcmp(ref_list[i], head))
+			c = '*';
+
+		printf("%c %s\n", c, ref_list[i]);
+	}
+}
+
+static void create_branch(const char *name, const char *start,
+			  int force, int reflog)
+{
+	struct ref_lock *lock;
+	struct commit *commit;
+	unsigned char sha1[20];
+	char ref[PATH_MAX], msg[PATH_MAX + 20];
+
+	snprintf(ref, sizeof ref, "refs/heads/%s", name);
+	if (check_ref_format(ref))
+		die("'%s' is not a valid branch name.", name);
+
+	if (resolve_ref(ref, sha1, 1, NULL)) {
+		if (!force)
+			die("A branch named '%s' already exists.", name);
+		else if (!strcmp(head, name))
+			die("Cannot force update the current branch.");
+	}
+
+	if (get_sha1(start, sha1) ||
+	    (commit = lookup_commit_reference(sha1)) == NULL)
+		die("Not a valid branch point: '%s'.", start);
+	hashcpy(sha1, commit->object.sha1);
+
+	lock = lock_any_ref_for_update(ref, NULL);
+	if (!lock)
+		die("Failed to lock ref for update: %s.", strerror(errno));
+
+	if (reflog) {
+		log_all_ref_updates = 1;
+		snprintf(msg, sizeof msg, "branch: Created from %s", start);
+	}
+
+	if (write_ref_sha1(lock, sha1, msg) < 0)
+		die("Failed to write ref: %s.", strerror(errno));
+}
+
+int cmd_branch(int argc, const char **argv, const char *prefix)
+{
+	int delete = 0, force_delete = 0, force_create = 0, remote_only = 0;
+	int reflog = 0;
+	int i;
+
+	git_config(git_default_config);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (arg[0] != '-')
+			break;
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		if (!strcmp(arg, "-d")) {
+			delete = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-D")) {
+			delete = 1;
+			force_delete = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-f")) {
+			force_create = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-r")) {
+			remote_only = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-l")) {
+			reflog = 1;
+			continue;
+		}
+		usage(builtin_branch_usage);
+	}
+
+	head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
+	if (!head)
+		die("Failed to resolve HEAD as a valid ref.");
+	if (strncmp(head, "refs/heads/", 11))
+		die("HEAD not found below refs/heads!");
+	head += 11;
+
+	if (delete)
+		delete_branches(argc - i, argv + i, force_delete);
+	else if (i == argc)
+		print_ref_list(remote_only);
+	else if (i == argc - 1)
+		create_branch(argv[i], head, force_create, reflog);
+	else if (i == argc - 2)
+		create_branch(argv[i], argv[i + 1], force_create, reflog);
+	else
+		usage(builtin_branch_usage);
+
+	return 0;
+}
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index c407c03..3d3097d 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -249,7 +249,7 @@
 	FILE *in = stdin;
 	const char *sep = "";
 	unsigned char head_sha1[20];
-	const char *head, *current_branch;
+	const char *current_branch;
 
 	git_config(fmt_merge_msg_config);
 
@@ -277,10 +277,7 @@
 		usage(fmt_merge_msg_usage);
 
 	/* get current branch */
-	head = xstrdup(git_path("HEAD"));
-	current_branch = resolve_ref(head, head_sha1, 1);
-	current_branch += strlen(head) - 4;
-	free((char *)head);
+	current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
 	if (!strncmp(current_branch, "refs/heads/", 11))
 		current_branch += 11;
 
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
new file mode 100644
index 0000000..173bf38
--- /dev/null
+++ b/builtin-for-each-ref.c
@@ -0,0 +1,896 @@
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "quote.h"
+#include <fnmatch.h>
+
+/* Quoting styles */
+#define QUOTE_NONE 0
+#define QUOTE_SHELL 1
+#define QUOTE_PERL 2
+#define QUOTE_PYTHON 3
+
+typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
+
+struct atom_value {
+	const char *s;
+	unsigned long ul; /* used for sorting when not FIELD_STR */
+};
+
+struct ref_sort {
+	struct ref_sort *next;
+	int atom; /* index into used_atom array */
+	unsigned reverse : 1;
+};
+
+struct refinfo {
+	char *refname;
+	unsigned char objectname[20];
+	struct atom_value *value;
+};
+
+static struct {
+	const char *name;
+	cmp_type cmp_type;
+} valid_atom[] = {
+	{ "refname" },
+	{ "objecttype" },
+	{ "objectsize", FIELD_ULONG },
+	{ "objectname" },
+	{ "tree" },
+	{ "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */
+	{ "numparent", FIELD_ULONG },
+	{ "object" },
+	{ "type" },
+	{ "tag" },
+	{ "author" },
+	{ "authorname" },
+	{ "authoremail" },
+	{ "authordate", FIELD_TIME },
+	{ "committer" },
+	{ "committername" },
+	{ "committeremail" },
+	{ "committerdate", FIELD_TIME },
+	{ "tagger" },
+	{ "taggername" },
+	{ "taggeremail" },
+	{ "taggerdate", FIELD_TIME },
+	{ "creator" },
+	{ "creatordate", FIELD_TIME },
+	{ "subject" },
+	{ "body" },
+	{ "contents" },
+};
+
+/*
+ * An atom is a valid field atom listed above, possibly prefixed with
+ * a "*" to denote deref_tag().
+ *
+ * We parse given format string and sort specifiers, and make a list
+ * of properties that we need to extract out of objects.  refinfo
+ * structure will hold an array of values extracted that can be
+ * indexed with the "atom number", which is an index into this
+ * array.
+ */
+static const char **used_atom;
+static cmp_type *used_atom_type;
+static int used_atom_cnt, sort_atom_limit, need_tagged;
+
+/*
+ * Used to parse format string and sort specifiers
+ */
+static int parse_atom(const char *atom, const char *ep)
+{
+	const char *sp;
+	char *n;
+	int i, at;
+
+	sp = atom;
+	if (*sp == '*' && sp < ep)
+		sp++; /* deref */
+	if (ep <= sp)
+		die("malformed field name: %.*s", (int)(ep-atom), atom);
+
+	/* Do we have the atom already used elsewhere? */
+	for (i = 0; i < used_atom_cnt; i++) {
+		int len = strlen(used_atom[i]);
+		if (len == ep - atom && !memcmp(used_atom[i], atom, len))
+			return i;
+	}
+
+	/* Is the atom a valid one? */
+	for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
+		int len = strlen(valid_atom[i].name);
+		if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
+			break;
+	}
+
+	if (ARRAY_SIZE(valid_atom) <= i)
+		die("unknown field name: %.*s", (int)(ep-atom), atom);
+
+	/* Add it in, including the deref prefix */
+	at = used_atom_cnt;
+	used_atom_cnt++;
+	used_atom = xrealloc(used_atom,
+			     (sizeof *used_atom) * used_atom_cnt);
+	used_atom_type = xrealloc(used_atom_type,
+				  (sizeof(*used_atom_type) * used_atom_cnt));
+	n = xmalloc(ep - atom + 1);
+	memcpy(n, atom, ep - atom);
+	n[ep-atom] = 0;
+	used_atom[at] = n;
+	used_atom_type[at] = valid_atom[i].cmp_type;
+	return at;
+}
+
+/*
+ * In a format string, find the next occurrence of %(atom).
+ */
+static const char *find_next(const char *cp)
+{
+	while (*cp) {
+		if (*cp == '%') {
+			/* %( is the start of an atom;
+			 * %% is a quoteed per-cent.
+			 */
+			if (cp[1] == '(')
+				return cp;
+			else if (cp[1] == '%')
+				cp++; /* skip over two % */
+			/* otherwise this is a singleton, literal % */
+		}
+		cp++;
+	}
+	return NULL;
+}
+
+/*
+ * Make sure the format string is well formed, and parse out
+ * the used atoms.
+ */
+static void verify_format(const char *format)
+{
+	const char *cp, *sp;
+	for (cp = format; *cp && (sp = find_next(cp)); ) {
+		const char *ep = strchr(sp, ')');
+		if (!ep)
+			die("malformatted format string %s", sp);
+		/* sp points at "%(" and ep points at the closing ")" */
+		parse_atom(sp + 2, ep);
+		cp = ep + 1;
+	}
+}
+
+/*
+ * Given an object name, read the object data and size, and return a
+ * "struct object".  If the object data we are returning is also borrowed
+ * by the "struct object" representation, set *eaten as well---it is a
+ * signal from parse_object_buffer to us not to free the buffer.
+ */
+static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
+{
+	char type[20];
+	void *buf = read_sha1_file(sha1, type, sz);
+
+	if (buf)
+		*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
+	else
+		*obj = NULL;
+	return buf;
+}
+
+/* See grab_values */
+static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (!strcmp(name, "objecttype"))
+			v->s = type_names[obj->type];
+		else if (!strcmp(name, "objectsize")) {
+			char *s = xmalloc(40);
+			sprintf(s, "%lu", sz);
+			v->ul = sz;
+			v->s = s;
+		}
+		else if (!strcmp(name, "objectname")) {
+			char *s = xmalloc(41);
+			strcpy(s, sha1_to_hex(obj->sha1));
+			v->s = s;
+		}
+	}
+}
+
+/* See grab_values */
+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	struct tag *tag = (struct tag *) obj;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (!strcmp(name, "tag"))
+			v->s = tag->tag;
+	}
+}
+
+static int num_parents(struct commit *commit)
+{
+	struct commit_list *parents;
+	int i;
+
+	for (i = 0, parents = commit->parents;
+	     parents;
+	     parents = parents->next)
+		i++;
+	return i;
+}
+
+/* See grab_values */
+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	struct commit *commit = (struct commit *) obj;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (!strcmp(name, "tree")) {
+			char *s = xmalloc(41);
+			strcpy(s, sha1_to_hex(commit->tree->object.sha1));
+			v->s = s;
+		}
+		if (!strcmp(name, "numparent")) {
+			char *s = xmalloc(40);
+			sprintf(s, "%lu", v->ul);
+			v->s = s;
+			v->ul = num_parents(commit);
+		}
+		else if (!strcmp(name, "parent")) {
+			int num = num_parents(commit);
+			int i;
+			struct commit_list *parents;
+			char *s = xmalloc(42 * num);
+			v->s = s;
+			for (i = 0, parents = commit->parents;
+			     parents;
+			     parents = parents->next, i = i + 42) {
+				struct commit *parent = parents->item;
+				strcpy(s+i, sha1_to_hex(parent->object.sha1));
+				if (parents->next)
+					s[i+40] = ' ';
+			}
+		}
+	}
+}
+
+static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
+{
+	const char *eol;
+	while (*buf) {
+		if (!strncmp(buf, who, wholen) &&
+		    buf[wholen] == ' ')
+			return buf + wholen + 1;
+		eol = strchr(buf, '\n');
+		if (!eol)
+			return "";
+		eol++;
+		if (eol[1] == '\n')
+			return ""; /* end of header */
+		buf = eol;
+	}
+	return "";
+}
+
+static char *copy_line(const char *buf)
+{
+	const char *eol = strchr(buf, '\n');
+	char *line;
+	int len;
+	if (!eol)
+		return "";
+	len = eol - buf;
+	line = xmalloc(len + 1);
+	memcpy(line, buf, len);
+	line[len] = 0;
+	return line;
+}
+
+static char *copy_name(const char *buf)
+{
+	const char *eol = strchr(buf, '\n');
+	const char *eoname = strstr(buf, " <");
+	char *line;
+	int len;
+	if (!(eoname && eol && eoname < eol))
+		return "";
+	len = eoname - buf;
+	line = xmalloc(len + 1);
+	memcpy(line, buf, len);
+	line[len] = 0;
+	return line;
+}
+
+static char *copy_email(const char *buf)
+{
+	const char *email = strchr(buf, '<');
+	const char *eoemail = strchr(email, '>');
+	char *line;
+	int len;
+	if (!email || !eoemail)
+		return "";
+	eoemail++;
+	len = eoemail - email;
+	line = xmalloc(len + 1);
+	memcpy(line, email, len);
+	line[len] = 0;
+	return line;
+}
+
+static void grab_date(const char *buf, struct atom_value *v)
+{
+	const char *eoemail = strstr(buf, "> ");
+	char *zone;
+	unsigned long timestamp;
+	long tz;
+
+	if (!eoemail)
+		goto bad;
+	timestamp = strtoul(eoemail + 2, &zone, 10);
+	if (timestamp == ULONG_MAX)
+		goto bad;
+	tz = strtol(zone, NULL, 10);
+	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
+		goto bad;
+	v->s = xstrdup(show_date(timestamp, tz, 0));
+	v->ul = timestamp;
+	return;
+ bad:
+	v->s = "";
+	v->ul = 0;
+}
+
+/* See grab_values */
+static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	int wholen = strlen(who);
+	const char *wholine = NULL;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (strncmp(who, name, wholen))
+			continue;
+		if (name[wholen] != 0 &&
+		    strcmp(name + wholen, "name") &&
+		    strcmp(name + wholen, "email") &&
+		    strcmp(name + wholen, "date"))
+			continue;
+		if (!wholine)
+			wholine = find_wholine(who, wholen, buf, sz);
+		if (!wholine)
+			return; /* no point looking for it */
+		if (name[wholen] == 0)
+			v->s = copy_line(wholine);
+		else if (!strcmp(name + wholen, "name"))
+			v->s = copy_name(wholine);
+		else if (!strcmp(name + wholen, "email"))
+			v->s = copy_email(wholine);
+		else if (!strcmp(name + wholen, "date"))
+			grab_date(wholine, v);
+	}
+
+	/* For a tag or a commit object, if "creator" or "creatordate" is
+	 * requested, do something special.
+	 */
+	if (strcmp(who, "tagger") && strcmp(who, "committer"))
+		return; /* "author" for commit object is not wanted */
+	if (!wholine)
+		wholine = find_wholine(who, wholen, buf, sz);
+	if (!wholine)
+		return;
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+
+		if (!strcmp(name, "creatordate"))
+			grab_date(wholine, v);
+		else if (!strcmp(name, "creator"))
+			v->s = copy_line(wholine);
+	}
+}
+
+static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)
+{
+	while (*buf) {
+		const char *eol = strchr(buf, '\n');
+		if (!eol)
+			return;
+		if (eol[1] == '\n') {
+			buf = eol + 1;
+			break; /* found end of header */
+		}
+		buf = eol + 1;
+	}
+	while (*buf == '\n')
+		buf++;
+	if (!*buf)
+		return;
+	*sub = buf; /* first non-empty line */
+	buf = strchr(buf, '\n');
+	if (!buf)
+		return; /* no body */
+	while (*buf == '\n')
+		buf++; /* skip blank between subject and body */
+	*body = buf;
+}
+
+/* See grab_values */
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	int i;
+	const char *subpos = NULL, *bodypos = NULL;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &val[i];
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+		if (strcmp(name, "subject") &&
+		    strcmp(name, "body") &&
+		    strcmp(name, "contents"))
+			continue;
+		if (!subpos)
+			find_subpos(buf, sz, &subpos, &bodypos);
+		if (!subpos)
+			return;
+
+		if (!strcmp(name, "subject"))
+			v->s = copy_line(subpos);
+		else if (!strcmp(name, "body"))
+			v->s = bodypos;
+		else if (!strcmp(name, "contents"))
+			v->s = subpos;
+	}
+}
+
+/* We want to have empty print-string for field requests
+ * that do not apply (e.g. "authordate" for a tag object)
+ */
+static void fill_missing_values(struct atom_value *val)
+{
+	int i;
+	for (i = 0; i < used_atom_cnt; i++) {
+		struct atom_value *v = &val[i];
+		if (v->s == NULL)
+			v->s = "";
+	}
+}
+
+/*
+ * val is a list of atom_value to hold returned values.  Extract
+ * the values for atoms in used_atom array out of (obj, buf, sz).
+ * when deref is false, (obj, buf, sz) is the object that is
+ * pointed at by the ref itself; otherwise it is the object the
+ * ref (which is a tag) refers to.
+ */
+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+	grab_common_values(val, deref, obj, buf, sz);
+	switch (obj->type) {
+	case OBJ_TAG:
+		grab_tag_values(val, deref, obj, buf, sz);
+		grab_sub_body_contents(val, deref, obj, buf, sz);
+		grab_person("tagger", val, deref, obj, buf, sz);
+		break;
+	case OBJ_COMMIT:
+		grab_commit_values(val, deref, obj, buf, sz);
+		grab_sub_body_contents(val, deref, obj, buf, sz);
+		grab_person("author", val, deref, obj, buf, sz);
+		grab_person("committer", val, deref, obj, buf, sz);
+		break;
+	case OBJ_TREE:
+		// grab_tree_values(val, deref, obj, buf, sz);
+		break;
+	case OBJ_BLOB:
+		// grab_blob_values(val, deref, obj, buf, sz);
+		break;
+	default:
+		die("Eh?  Object of type %d?", obj->type);
+	}
+}
+
+/*
+ * Parse the object referred by ref, and grab needed value.
+ */
+static void populate_value(struct refinfo *ref)
+{
+	void *buf;
+	struct object *obj;
+	int eaten, i;
+	unsigned long size;
+	const unsigned char *tagged;
+
+	ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
+
+	buf = get_obj(ref->objectname, &obj, &size, &eaten);
+	if (!buf)
+		die("missing object %s for %s",
+		    sha1_to_hex(ref->objectname), ref->refname);
+	if (!obj)
+		die("parse_object_buffer failed on %s for %s",
+		    sha1_to_hex(ref->objectname), ref->refname);
+
+	/* Fill in specials first */
+	for (i = 0; i < used_atom_cnt; i++) {
+		const char *name = used_atom[i];
+		struct atom_value *v = &ref->value[i];
+		if (!strcmp(name, "refname"))
+			v->s = ref->refname;
+		else if (!strcmp(name, "*refname")) {
+			int len = strlen(ref->refname);
+			char *s = xmalloc(len + 4);
+			sprintf(s, "%s^{}", ref->refname);
+			v->s = s;
+		}
+	}
+
+	grab_values(ref->value, 0, obj, buf, size);
+	if (!eaten)
+		free(buf);
+
+	/* If there is no atom that wants to know about tagged
+	 * object, we are done.
+	 */
+	if (!need_tagged || (obj->type != OBJ_TAG))
+		return;
+
+	/* If it is a tag object, see if we use a value that derefs
+	 * the object, and if we do grab the object it refers to.
+	 */
+	tagged = ((struct tag *)obj)->tagged->sha1;
+
+	/* NEEDSWORK: This derefs tag only once, which
+	 * is good to deal with chains of trust, but
+	 * is not consistent with what deref_tag() does
+	 * which peels the onion to the core.
+	 */
+	buf = get_obj(tagged, &obj, &size, &eaten);
+	if (!buf)
+		die("missing object %s for %s",
+		    sha1_to_hex(tagged), ref->refname);
+	if (!obj)
+		die("parse_object_buffer failed on %s for %s",
+		    sha1_to_hex(tagged), ref->refname);
+	grab_values(ref->value, 1, obj, buf, size);
+	if (!eaten)
+		free(buf);
+}
+
+/*
+ * Given a ref, return the value for the atom.  This lazily gets value
+ * out of the object by calling populate value.
+ */
+static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
+{
+	if (!ref->value) {
+		populate_value(ref);
+		fill_missing_values(ref->value);
+	}
+	*v = &ref->value[atom];
+}
+
+struct grab_ref_cbdata {
+	struct refinfo **grab_array;
+	const char **grab_pattern;
+	int grab_cnt;
+};
+
+/*
+ * A call-back given to for_each_ref().  It is unfortunate that we
+ * need to use global variables to pass extra information to this
+ * function.
+ */
+static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct grab_ref_cbdata *cb = cb_data;
+	struct refinfo *ref;
+	int cnt;
+
+	if (*cb->grab_pattern) {
+		const char **pattern;
+		int namelen = strlen(refname);
+		for (pattern = cb->grab_pattern; *pattern; pattern++) {
+			const char *p = *pattern;
+			int plen = strlen(p);
+
+			if ((plen <= namelen) &&
+			    !strncmp(refname, p, plen) &&
+			    (refname[plen] == '\0' ||
+			     refname[plen] == '/'))
+				break;
+			if (!fnmatch(p, refname, FNM_PATHNAME))
+				break;
+		}
+		if (!*pattern)
+			return 0;
+	}
+
+	/* We do not open the object yet; sort may only need refname
+	 * to do its job and the resulting list may yet to be pruned
+	 * by maxcount logic.
+	 */
+	ref = xcalloc(1, sizeof(*ref));
+	ref->refname = xstrdup(refname);
+	hashcpy(ref->objectname, sha1);
+
+	cnt = cb->grab_cnt;
+	cb->grab_array = xrealloc(cb->grab_array,
+				  sizeof(*cb->grab_array) * (cnt + 1));
+	cb->grab_array[cnt++] = ref;
+	cb->grab_cnt = cnt;
+	return 0;
+}
+
+static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
+{
+	struct atom_value *va, *vb;
+	int cmp;
+	cmp_type cmp_type = used_atom_type[s->atom];
+
+	get_value(a, s->atom, &va);
+	get_value(b, s->atom, &vb);
+	switch (cmp_type) {
+	case FIELD_STR:
+		cmp = strcmp(va->s, vb->s);
+		break;
+	default:
+		if (va->ul < vb->ul)
+			cmp = -1;
+		else if (va->ul == vb->ul)
+			cmp = 0;
+		else
+			cmp = 1;
+		break;
+	}
+	return (s->reverse) ? -cmp : cmp;
+}
+
+static struct ref_sort *ref_sort;
+static int compare_refs(const void *a_, const void *b_)
+{
+	struct refinfo *a = *((struct refinfo **)a_);
+	struct refinfo *b = *((struct refinfo **)b_);
+	struct ref_sort *s;
+
+	for (s = ref_sort; s; s = s->next) {
+		int cmp = cmp_ref_sort(s, a, b);
+		if (cmp)
+			return cmp;
+	}
+	return 0;
+}
+
+static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
+{
+	ref_sort = sort;
+	qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
+}
+
+static void print_value(struct refinfo *ref, int atom, int quote_style)
+{
+	struct atom_value *v;
+	get_value(ref, atom, &v);
+	switch (quote_style) {
+	case QUOTE_NONE:
+		fputs(v->s, stdout);
+		break;
+	case QUOTE_SHELL:
+		sq_quote_print(stdout, v->s);
+		break;
+	case QUOTE_PERL:
+		perl_quote_print(stdout, v->s);
+		break;
+	case QUOTE_PYTHON:
+		python_quote_print(stdout, v->s);
+		break;
+	}
+}
+
+static int hex1(char ch)
+{
+	if ('0' <= ch && ch <= '9')
+		return ch - '0';
+	else if ('a' <= ch && ch <= 'f')
+		return ch - 'a' + 10;
+	else if ('A' <= ch && ch <= 'F')
+		return ch - 'A' + 10;
+	return -1;
+}
+static int hex2(const char *cp)
+{
+	if (cp[0] && cp[1])
+		return (hex1(cp[0]) << 4) | hex1(cp[1]);
+	else
+		return -1;
+}
+
+static void emit(const char *cp, const char *ep)
+{
+	while (*cp && (!ep || cp < ep)) {
+		if (*cp == '%') {
+			if (cp[1] == '%')
+				cp++;
+			else {
+				int ch = hex2(cp + 1);
+				if (0 <= ch) {
+					putchar(ch);
+					cp += 3;
+					continue;
+				}
+			}
+		}
+		putchar(*cp);
+		cp++;
+	}
+}
+
+static void show_ref(struct refinfo *info, const char *format, int quote_style)
+{
+	const char *cp, *sp, *ep;
+
+	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
+		ep = strchr(sp, ')');
+		if (cp < sp)
+			emit(cp, sp);
+		print_value(info, parse_atom(sp + 2, ep), quote_style);
+	}
+	if (*cp) {
+		sp = cp + strlen(cp);
+		emit(cp, sp);
+	}
+	putchar('\n');
+}
+
+static struct ref_sort *default_sort(void)
+{
+	static const char cstr_name[] = "refname";
+
+	struct ref_sort *sort = xcalloc(1, sizeof(*sort));
+
+	sort->next = NULL;
+	sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
+	return sort;
+}
+
+int cmd_for_each_ref(int ac, const char **av, char *prefix)
+{
+	int i, num_refs;
+	const char *format = NULL;
+	struct ref_sort *sort = NULL, **sort_tail = &sort;
+	int maxcount = 0;
+	int quote_style = -1; /* unspecified yet */
+	struct refinfo **refs;
+	struct grab_ref_cbdata cbdata;
+
+	for (i = 1; i < ac; i++) {
+		const char *arg = av[i];
+		if (arg[0] != '-')
+			break;
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		if (!strncmp(arg, "--format=", 9)) {
+			if (format)
+				die("more than one --format?");
+			format = arg + 9;
+			continue;
+		}
+		if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) {
+			if (0 <= quote_style)
+				die("more than one quoting style?");
+			quote_style = QUOTE_SHELL;
+			continue;
+		}
+		if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) {
+			if (0 <= quote_style)
+				die("more than one quoting style?");
+			quote_style = QUOTE_PERL;
+			continue;
+		}
+		if (!strcmp(arg, "--python") ) {
+			if (0 <= quote_style)
+				die("more than one quoting style?");
+			quote_style = QUOTE_PYTHON;
+			continue;
+		}
+		if (!strncmp(arg, "--count=", 8)) {
+			if (maxcount)
+				die("more than one --count?");
+			maxcount = atoi(arg + 8);
+			if (maxcount <= 0)
+				die("The number %s did not parse", arg);
+			continue;
+		}
+		if (!strncmp(arg, "--sort=", 7)) {
+			struct ref_sort *s = xcalloc(1, sizeof(*s));
+			int len;
+
+			s->next = NULL;
+			*sort_tail = s;
+			sort_tail = &s->next;
+
+			arg += 7;
+			if (*arg == '-') {
+				s->reverse = 1;
+				arg++;
+			}
+			len = strlen(arg);
+			sort->atom = parse_atom(arg, arg+len);
+			continue;
+		}
+		break;
+	}
+	if (quote_style < 0)
+		quote_style = QUOTE_NONE;
+
+	if (!sort)
+		sort = default_sort();
+	sort_atom_limit = used_atom_cnt;
+	if (!format)
+		format = "%(objectname) %(objecttype)\t%(refname)";
+
+	verify_format(format);
+
+	memset(&cbdata, 0, sizeof(cbdata));
+	cbdata.grab_pattern = av + i;
+	for_each_ref(grab_single_ref, &cbdata);
+	refs = cbdata.grab_array;
+	num_refs = cbdata.grab_cnt;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		if (used_atom[i][0] == '*') {
+			need_tagged = 1;
+			break;
+		}
+	}
+
+	sort_refs(sort, refs, num_refs);
+
+	if (!maxcount || num_refs < maxcount)
+		maxcount = num_refs;
+	for (i = 0; i < maxcount; i++)
+		show_ref(refs[i], format, quote_style);
+	return 0;
+}
diff --git a/builtin-grep.c b/builtin-grep.c
index 4205e5d..ad7dc00 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -596,6 +596,10 @@
 					    GREP_CLOSE_PAREN);
 			continue;
 		}
+		if (!strcmp("--all-match", arg)) {
+			opt.all_match = 1;
+			continue;
+		}
 		if (!strcmp("-e", arg)) {
 			if (1 < argc) {
 				append_grep_pattern(&opt, argv[1],
diff --git a/builtin-init-db.c b/builtin-init-db.c
index c3ed1ce..235a0ee 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -218,8 +218,8 @@
 	 * branch, if it does not exist yet.
 	 */
 	strcpy(path + len, "HEAD");
-	if (read_ref(path, sha1) < 0) {
-		if (create_symref(path, "refs/heads/master") < 0)
+	if (read_ref("HEAD", sha1) < 0) {
+		if (create_symref("HEAD", "refs/heads/master") < 0)
 			exit(1);
 	}
 
diff --git a/builtin-log.c b/builtin-log.c
index 9d1ceae..fedb013 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -171,8 +171,11 @@
 static int get_patch_id(struct commit *commit, struct diff_options *options,
 		unsigned char *sha1)
 {
-	diff_tree_sha1(commit->parents->item->object.sha1, commit->object.sha1,
-			"", options);
+	if (commit->parents)
+		diff_tree_sha1(commit->parents->item->object.sha1,
+		               commit->object.sha1, "", options);
+	else
+		diff_root_tree_sha1(commit->object.sha1, "", options);
 	diffcore_std(options);
 	return diff_flush_patch_id(options, sha1);
 }
@@ -437,3 +440,109 @@
 	return 0;
 }
 
+static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
+{
+	unsigned char sha1[20];
+	if (get_sha1(arg, sha1) == 0) {
+		struct commit *commit = lookup_commit_reference(sha1);
+		if (commit) {
+			commit->object.flags |= flags;
+			add_pending_object(revs, &commit->object, arg);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static const char cherry_usage[] =
+"git-cherry [-v] <upstream> [<head>] [<limit>]";
+int cmd_cherry(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info revs;
+	struct diff_options patch_id_opts;
+	struct commit *commit;
+	struct commit_list *list = NULL;
+	const char *upstream;
+	const char *head = "HEAD";
+	const char *limit = NULL;
+	int verbose = 0;
+
+	if (argc > 1 && !strcmp(argv[1], "-v")) {
+		verbose = 1;
+		argc--;
+		argv++;
+	}
+
+	switch (argc) {
+	case 4:
+		limit = argv[3];
+		/* FALLTHROUGH */
+	case 3:
+		head = argv[2];
+		/* FALLTHROUGH */
+	case 2:
+		upstream = argv[1];
+		break;
+	default:
+		usage(cherry_usage);
+	}
+
+	init_revisions(&revs, prefix);
+	revs.diff = 1;
+	revs.combine_merges = 0;
+	revs.ignore_merges = 1;
+	revs.diffopt.recursive = 1;
+
+	if (add_pending_commit(head, &revs, 0))
+		die("Unknown commit %s", head);
+	if (add_pending_commit(upstream, &revs, UNINTERESTING))
+		die("Unknown commit %s", upstream);
+
+	/* Don't say anything if head and upstream are the same. */
+	if (revs.pending.nr == 2) {
+		struct object_array_entry *o = revs.pending.objects;
+		if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+			return 0;
+	}
+
+	get_patch_ids(&revs, &patch_id_opts, prefix);
+
+	if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
+		die("Unknown commit %s", limit);
+
+	/* reverse the list of commits */
+	prepare_revision_walk(&revs);
+	while ((commit = get_revision(&revs)) != NULL) {
+		/* ignore merges */
+		if (commit->parents && commit->parents->next)
+			continue;
+
+		commit_list_insert(commit, &list);
+	}
+
+	while (list) {
+		unsigned char sha1[20];
+		char sign = '+';
+
+		commit = list->item;
+		if (!get_patch_id(commit, &patch_id_opts, sha1) &&
+		    lookup_object(sha1))
+			sign = '-';
+
+		if (verbose) {
+			static char buf[16384];
+			pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
+			                    buf, sizeof(buf), 0, NULL, NULL, 0);
+			printf("%c %s %s\n", sign,
+			       sha1_to_hex(commit->object.sha1), buf);
+		}
+		else {
+			printf("%c %s\n", sign,
+			       sha1_to_hex(commit->object.sha1));
+		}
+
+		list = list->next;
+	}
+
+	return 0;
+}
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index 52886b6..618aa31 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -75,11 +75,10 @@
 	}
 }
 
-static int tags_only;
-
-static int name_ref(const char *path, const unsigned char *sha1)
+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
 {
 	struct object *o = parse_object(sha1);
+	int tags_only = *(int*)cb_data;
 	int deref = 0;
 
 	if (tags_only && strncmp(path, "refs/tags/", 10))
@@ -131,6 +130,7 @@
 {
 	struct object_array revs = { 0, 0, NULL };
 	int as_is = 0, all = 0, transform_stdin = 0;
+	int tags_only = 0;
 
 	git_config(git_default_config);
 
@@ -186,7 +186,7 @@
 		add_object_array((struct object *)commit, *argv, &revs);
 	}
 
-	for_each_ref(name_ref);
+	for_each_ref(name_ref, &tags_only);
 
 	if (transform_stdin) {
 		char buffer[2048];
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 96c069a..69e5dd3 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -15,7 +15,12 @@
 #include <sys/time.h>
 #include <signal.h>
 
-static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
+static const char pack_usage[] = "\
+git-pack-objects [{ -q | --progress | --all-progress }] \n\
+	[--local] [--incremental] [--window=N] [--depth=N] \n\
+	[--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
+	[--revs [--unpacked | --all]*] [--stdout | base-name] \n\
+	[<ref-list | <object-list]";
 
 struct object_entry {
 	unsigned char sha1[20];
@@ -29,6 +34,7 @@
 	enum object_type type;
 	enum object_type in_pack_type;	/* could be delta */
 	unsigned long delta_size;	/* delta data size (uncompressed) */
+#define in_pack_header_size delta_size	/* only when reusing pack data */
 	struct object_entry *delta;	/* delta base object */
 	struct packed_git *in_pack; 	/* already in pack */
 	unsigned int in_pack_offset;
@@ -60,6 +66,8 @@
 static int no_reuse_delta;
 static int local;
 static int incremental;
+static int allow_ofs_delta;
+
 static struct object_entry **sorted_by_sha, **sorted_by_type;
 static struct object_entry *objects;
 static int nr_objects, nr_alloc, nr_result;
@@ -84,17 +92,25 @@
  * Pack index for existing packs give us easy access to the offsets into
  * corresponding pack file where each object's data starts, but the entries
  * do not store the size of the compressed representation (uncompressed
- * size is easily available by examining the pack entry header).  We build
- * a hashtable of existing packs (pack_revindex), and keep reverse index
- * here -- pack index file is sorted by object name mapping to offset; this
- * pack_revindex[].revindex array is an ordered list of offsets, so if you
- * know the offset of an object, next offset is where its packed
- * representation ends.
+ * size is easily available by examining the pack entry header).  It is
+ * also rather expensive to find the sha1 for an object given its offset.
+ *
+ * We build a hashtable of existing packs (pack_revindex), and keep reverse
+ * index here -- pack index file is sorted by object name mapping to offset;
+ * this pack_revindex[].revindex array is a list of offset/index_nr pairs
+ * ordered by offset, so if you know the offset of an object, next offset
+ * is where its packed representation ends and the index_nr can be used to
+ * get the object sha1 from the main index.
  */
+struct revindex_entry {
+	unsigned int offset;
+	unsigned int nr;
+};
 struct pack_revindex {
 	struct packed_git *p;
-	unsigned long *revindex;
-} *pack_revindex = NULL;
+	struct revindex_entry *revindex;
+};
+static struct  pack_revindex *pack_revindex;
 static int pack_revindex_hashsz;
 
 /*
@@ -141,14 +157,9 @@
 
 static int cmp_offset(const void *a_, const void *b_)
 {
-	unsigned long a = *(unsigned long *) a_;
-	unsigned long b = *(unsigned long *) b_;
-	if (a < b)
-		return -1;
-	else if (a == b)
-		return 0;
-	else
-		return 1;
+	const struct revindex_entry *a = a_;
+	const struct revindex_entry *b = b_;
+	return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
 }
 
 /*
@@ -161,25 +172,27 @@
 	int i;
 	void *index = p->index_base + 256;
 
-	rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
+	rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
 	for (i = 0; i < num_ent; i++) {
 		unsigned int hl = *((unsigned int *)((char *) index + 24*i));
-		rix->revindex[i] = ntohl(hl);
+		rix->revindex[i].offset = ntohl(hl);
+		rix->revindex[i].nr = i;
 	}
 	/* This knows the pack format -- the 20-byte trailer
 	 * follows immediately after the last object data.
 	 */
-	rix->revindex[num_ent] = p->pack_size - 20;
-	qsort(rix->revindex, num_ent, sizeof(unsigned long), cmp_offset);
+	rix->revindex[num_ent].offset = p->pack_size - 20;
+	rix->revindex[num_ent].nr = -1;
+	qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
 }
 
-static unsigned long find_packed_object_size(struct packed_git *p,
-					     unsigned long ofs)
+static struct revindex_entry * find_packed_object(struct packed_git *p,
+						  unsigned int ofs)
 {
 	int num;
 	int lo, hi;
 	struct pack_revindex *rix;
-	unsigned long *revindex;
+	struct revindex_entry *revindex;
 	num = pack_revindex_ix(p);
 	if (num < 0)
 		die("internal error: pack revindex uninitialized");
@@ -191,10 +204,10 @@
 	hi = num_packed_objects(p) + 1;
 	do {
 		int mi = (lo + hi) / 2;
-		if (revindex[mi] == ofs) {
-			return revindex[mi+1] - ofs;
+		if (revindex[mi].offset == ofs) {
+			return revindex + mi;
 		}
-		else if (ofs < revindex[mi])
+		else if (ofs < revindex[mi].offset)
 			hi = mi;
 		else
 			lo = mi + 1;
@@ -202,6 +215,20 @@
 	die("internal error: pack revindex corrupt");
 }
 
+static unsigned long find_packed_object_size(struct packed_git *p,
+					     unsigned long ofs)
+{
+	struct revindex_entry *entry = find_packed_object(p, ofs);
+	return entry[1].offset - ofs;
+}
+
+static unsigned char *find_packed_object_name(struct packed_git *p,
+					      unsigned long ofs)
+{
+	struct revindex_entry *entry = find_packed_object(p, ofs);
+	return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
+}
+
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 {
 	unsigned long othersize, delta_size;
@@ -232,7 +259,7 @@
 	int n = 1;
 	unsigned char c;
 
-	if (type < OBJ_COMMIT || type > OBJ_DELTA)
+	if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
 		die("bad type %d", type);
 
 	c = (type << 4) | (size & 15);
@@ -247,6 +274,10 @@
 	return n;
 }
 
+/*
+ * we are going to reuse the existing object data as is.  make
+ * sure it is not corrupt.
+ */
 static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)
 {
 	z_stream stream;
@@ -278,32 +309,6 @@
 	return st;
 }
 
-/*
- * we are going to reuse the existing pack entry data.  make
- * sure it is not corrupt.
- */
-static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data, unsigned long len)
-{
-	enum object_type type;
-	unsigned long size, used;
-
-	if (pack_to_stdout)
-		return 0;
-
-	/* the caller has already called use_packed_git() for us,
-	 * so it is safe to access the pack data from mmapped location.
-	 * make sure the entry inflates correctly.
-	 */
-	used = unpack_object_header_gently(data, len, &type, &size);
-	if (!used)
-		return -1;
-	if (type == OBJ_DELTA)
-		used += 20; /* skip base object name */
-	data += used;
-	len -= used;
-	return check_inflate(data, len, entry->size);
-}
-
 static int revalidate_loose_object(struct object_entry *entry,
 				   unsigned char *map,
 				   unsigned long mapsize)
@@ -334,13 +339,10 @@
 	enum object_type obj_type;
 	int to_reuse = 0;
 
-	if (entry->preferred_base)
-		return 0;
-
 	obj_type = entry->type;
 	if (! entry->in_pack)
 		to_reuse = 0;	/* can't reuse what we don't have */
-	else if (obj_type == OBJ_DELTA)
+	else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
 		to_reuse = 1;	/* check_object() decided it for us */
 	else if (obj_type != entry->in_pack_type)
 		to_reuse = 0;	/* pack has delta which is unusable */
@@ -380,18 +382,35 @@
 		if (entry->delta) {
 			buf = delta_against(buf, size, entry);
 			size = entry->delta_size;
-			obj_type = OBJ_DELTA;
+			obj_type = (allow_ofs_delta && entry->delta->offset) ?
+				OBJ_OFS_DELTA : OBJ_REF_DELTA;
 		}
 		/*
 		 * The object header is a byte of 'type' followed by zero or
-		 * more bytes of length.  For deltas, the 20 bytes of delta
-		 * sha1 follows that.
+		 * more bytes of length.
 		 */
 		hdrlen = encode_header(obj_type, size, header);
 		sha1write(f, header, hdrlen);
 
-		if (entry->delta) {
-			sha1write(f, entry->delta, 20);
+		if (obj_type == OBJ_OFS_DELTA) {
+			/*
+			 * Deltas with relative base contain an additional
+			 * encoding of the relative offset for the delta
+			 * base from this object's position in the pack.
+			 */
+			unsigned long ofs = entry->offset - entry->delta->offset;
+			unsigned pos = sizeof(header) - 1;
+			header[pos] = ofs & 127;
+			while (ofs >>= 7)
+				header[--pos] = 128 | (--ofs & 127);
+			sha1write(f, header + pos, sizeof(header) - pos);
+			hdrlen += sizeof(header) - pos;
+		} else if (obj_type == OBJ_REF_DELTA) {
+			/*
+			 * Deltas with a base reference contain
+			 * an additional 20 bytes for the base sha1.
+			 */
+			sha1write(f, entry->delta->sha1, 20);
 			hdrlen += 20;
 		}
 		datalen = sha1write_compressed(f, buf, size);
@@ -399,21 +418,40 @@
 	}
 	else {
 		struct packed_git *p = entry->in_pack;
+
+		if (entry->delta) {
+			obj_type = (allow_ofs_delta && entry->delta->offset) ?
+				OBJ_OFS_DELTA : OBJ_REF_DELTA;
+			reused_delta++;
+		}
+		hdrlen = encode_header(obj_type, entry->size, header);
+		sha1write(f, header, hdrlen);
+		if (obj_type == OBJ_OFS_DELTA) {
+			unsigned long ofs = entry->offset - entry->delta->offset;
+			unsigned pos = sizeof(header) - 1;
+			header[pos] = ofs & 127;
+			while (ofs >>= 7)
+				header[--pos] = 128 | (--ofs & 127);
+			sha1write(f, header + pos, sizeof(header) - pos);
+			hdrlen += sizeof(header) - pos;
+		} else if (obj_type == OBJ_REF_DELTA) {
+			sha1write(f, entry->delta->sha1, 20);
+			hdrlen += 20;
+		}
+
 		use_packed_git(p);
-
-		datalen = find_packed_object_size(p, entry->in_pack_offset);
-		buf = (char *) p->pack_base + entry->in_pack_offset;
-
-		if (revalidate_pack_entry(entry, buf, datalen))
+		buf = (char *) p->pack_base
+			+ entry->in_pack_offset
+			+ entry->in_pack_header_size;
+		datalen = find_packed_object_size(p, entry->in_pack_offset)
+				- entry->in_pack_header_size;
+		if (!pack_to_stdout && check_inflate(buf, datalen, entry->size))
 			die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
 		sha1write(f, buf, datalen);
 		unuse_packed_git(p);
-		hdrlen = 0; /* not really */
-		if (obj_type == OBJ_DELTA)
-			reused_delta++;
 		reused++;
 	}
-	if (obj_type == OBJ_DELTA)
+	if (entry->delta)
 		written_delta++;
 	written++;
 	return hdrlen + datalen;
@@ -423,17 +461,16 @@
 			       struct object_entry *e,
 			       unsigned long offset)
 {
-	if (e->offset)
+	if (e->offset || e->preferred_base)
 		/* offset starts from header size and cannot be zero
 		 * if it is written already.
 		 */
 		return offset;
-	e->offset = offset;
-	offset += write_object(f, e);
-	/* if we are deltified, write out its base object. */
+	/* if we are deltified, write out its base object first. */
 	if (e->delta)
 		offset = write_one(f, e->delta, offset);
-	return offset;
+	e->offset = offset;
+	return offset + write_object(f, e);
 }
 
 static void write_pack_file(void)
@@ -443,15 +480,15 @@
 	unsigned long offset;
 	struct pack_header hdr;
 	unsigned last_percent = 999;
-	int do_progress = 0;
+	int do_progress = progress;
 
-	if (!base_name)
+	if (!base_name) {
 		f = sha1fd(1, "<stdout>");
-	else {
+		do_progress >>= 1;
+	}
+	else
 		f = sha1create("%s-%s.%s", base_name,
 			       sha1_to_hex(object_list_sha1), "pack");
-		do_progress = progress;
-	}
 	if (do_progress)
 		fprintf(stderr, "Writing %d objects.\n", nr_result);
 
@@ -899,26 +936,64 @@
 	char type[20];
 
 	if (entry->in_pack && !entry->preferred_base) {
-		unsigned char base[20];
-		unsigned long size;
-		struct object_entry *base_entry;
+		struct packed_git *p = entry->in_pack;
+		unsigned long left = p->pack_size - entry->in_pack_offset;
+		unsigned long size, used;
+		unsigned char *buf;
+		struct object_entry *base_entry = NULL;
+
+		use_packed_git(p);
+		buf = p->pack_base;
+		buf += entry->in_pack_offset;
 
 		/* We want in_pack_type even if we do not reuse delta.
 		 * There is no point not reusing non-delta representations.
 		 */
-		check_reuse_pack_delta(entry->in_pack,
-				       entry->in_pack_offset,
-				       base, &size,
-				       &entry->in_pack_type);
+		used = unpack_object_header_gently(buf, left,
+						   &entry->in_pack_type, &size);
+		if (!used || left - used <= 20)
+			die("corrupt pack for %s", sha1_to_hex(entry->sha1));
 
 		/* Check if it is delta, and the base is also an object
 		 * we are going to pack.  If so we will reuse the existing
 		 * delta.
 		 */
-		if (!no_reuse_delta &&
-		    entry->in_pack_type == OBJ_DELTA &&
-		    (base_entry = locate_object_entry(base)) &&
-		    (!base_entry->preferred_base)) {
+		if (!no_reuse_delta) {
+			unsigned char c, *base_name;
+			unsigned long ofs;
+			/* there is at least 20 bytes left in the pack */
+			switch (entry->in_pack_type) {
+			case OBJ_REF_DELTA:
+				base_name = buf + used;
+				used += 20;
+				break;
+			case OBJ_OFS_DELTA:
+				c = buf[used++];
+				ofs = c & 127;
+				while (c & 128) {
+					ofs += 1;
+					if (!ofs || ofs & ~(~0UL >> 7))
+						die("delta base offset overflow in pack for %s",
+						    sha1_to_hex(entry->sha1));
+					c = buf[used++];
+					ofs = (ofs << 7) + (c & 127);
+				}
+				if (ofs >= entry->in_pack_offset)
+					die("delta base offset out of bound for %s",
+					    sha1_to_hex(entry->sha1));
+				ofs = entry->in_pack_offset - ofs;
+				base_name = find_packed_object_name(p, ofs);
+				break;
+			default:
+				base_name = NULL;
+			}
+			if (base_name)
+				base_entry = locate_object_entry(base_name);
+		}
+		unuse_packed_git(p);
+		entry->in_pack_header_size = used;
+
+		if (base_entry) {
 
 			/* Depth value does not matter - find_deltas()
 			 * will never consider reused delta as the
@@ -927,9 +1002,9 @@
 			 */
 
 			/* uncompressed size of the delta data */
-			entry->size = entry->delta_size = size;
+			entry->size = size;
 			entry->delta = base_entry;
-			entry->type = OBJ_DELTA;
+			entry->type = entry->in_pack_type;
 
 			entry->delta_sibling = base_entry->delta_child;
 			base_entry->delta_child = entry;
@@ -1450,10 +1525,6 @@
 			local = 1;
 			continue;
 		}
-		if (!strcmp("--progress", arg)) {
-			progress = 1;
-			continue;
-		}
 		if (!strcmp("--incremental", arg)) {
 			incremental = 1;
 			continue;
@@ -1476,6 +1547,10 @@
 			progress = 1;
 			continue;
 		}
+		if (!strcmp("--all-progress", arg)) {
+			progress = 2;
+			continue;
+		}
 		if (!strcmp("-q", arg)) {
 			progress = 0;
 			continue;
@@ -1484,6 +1559,10 @@
 			no_reuse_delta = 1;
 			continue;
 		}
+		if (!strcmp("--delta-base-offset", arg)) {
+			allow_ofs_delta = 1;
+			continue;
+		}
 		if (!strcmp("--stdout", arg)) {
 			pack_to_stdout = 1;
 			continue;
@@ -1567,7 +1646,7 @@
 	else {
 		if (nr_result)
 			prepare_pack(window, depth);
-		if (progress && pack_to_stdout) {
+		if (progress == 1 && pack_to_stdout) {
 			/* the other end usually displays progress itself */
 			struct itimerval v = {{0,},};
 			setitimer(ITIMER_REAL, &v, NULL);
diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
new file mode 100644
index 0000000..042d271
--- /dev/null
+++ b/builtin-pack-refs.c
@@ -0,0 +1,107 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char builtin_pack_refs_usage[] =
+"git-pack-refs [--all] [--prune]";
+
+struct ref_to_prune {
+	struct ref_to_prune *next;
+	unsigned char sha1[20];
+	char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+	int prune;
+	int all;
+	struct ref_to_prune *ref_to_prune;
+	FILE *refs_file;
+};
+
+static int do_not_prune(int flags)
+{
+	/* If it is already packed or if it is a symref,
+	 * do not prune it.
+	 */
+	return (flags & (REF_ISSYMREF|REF_ISPACKED));
+}
+
+static int handle_one_ref(const char *path, const unsigned char *sha1,
+			  int flags, void *cb_data)
+{
+	struct pack_refs_cb_data *cb = cb_data;
+
+	if (!cb->all && strncmp(path, "refs/tags/", 10))
+		return 0;
+	/* Do not pack the symbolic refs */
+	if (!(flags & REF_ISSYMREF))
+		fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+	if (cb->prune && !do_not_prune(flags)) {
+		int namelen = strlen(path) + 1;
+		struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+		hashcpy(n->sha1, sha1);
+		strcpy(n->name, path);
+		n->next = cb->ref_to_prune;
+		cb->ref_to_prune = n;
+	}
+	return 0;
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+	struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
+
+	if (lock) {
+		unlink(git_path("%s", r->name));
+		unlock_ref(lock);
+	}
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+	while (r) {
+		prune_ref(r);
+		r = r->next;
+	}
+}
+
+static struct lock_file packed;
+
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+	int fd, i;
+	struct pack_refs_cb_data cbdata;
+
+	memset(&cbdata, 0, sizeof(cbdata));
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--prune")) {
+			cbdata.prune = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--all")) {
+			cbdata.all = 1;
+			continue;
+		}
+		/* perhaps other parameters later... */
+		break;
+	}
+	if (i != argc)
+		usage(builtin_pack_refs_usage);
+
+	fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
+	cbdata.refs_file = fdopen(fd, "w");
+	if (!cbdata.refs_file)
+		die("unable to create ref-pack file structure (%s)",
+		    strerror(errno));
+	for_each_ref(handle_one_ref, &cbdata);
+	fflush(cbdata.refs_file);
+	fsync(fd);
+	fclose(cbdata.refs_file);
+	if (commit_lock_file(&packed) < 0)
+		die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+	if (cbdata.prune)
+		prune_refs(cbdata.ref_to_prune);
+	return 0;
+}
diff --git a/builtin-prune.c b/builtin-prune.c
index 7290e6d..d853902 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -174,7 +174,7 @@
 	}
 }
 
-static int add_one_ref(const char *path, const unsigned char *sha1)
+static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *object = parse_object(sha1);
 	if (!object)
@@ -240,7 +240,7 @@
 	revs.tree_objects = 1;
 
 	/* Add all external refs */
-	for_each_ref(add_one_ref);
+	for_each_ref(add_one_ref, NULL);
 
 	/* Add all refs from the index file */
 	add_cache_refs();
diff --git a/builtin-push.c b/builtin-push.c
index f5150ed..d23974e 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -10,7 +10,7 @@
 
 static const char push_usage[] = "git-push [--all] [--tags] [-f | --force] <repository> [<refspec>...]";
 
-static int all, tags, force, thin = 1;
+static int all, tags, force, thin = 1, verbose;
 static const char *execute;
 
 #define BUF_SIZE (2084)
@@ -27,7 +27,7 @@
 	refspec_nr = nr;
 }
 
-static int expand_one_ref(const char *ref, const unsigned char *sha1)
+static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
 {
 	/* Ignore the "refs/" at the beginning of the refname */
 	ref += 5;
@@ -51,7 +51,7 @@
 	}
 	if (!tags)
 		return;
-	for_each_ref(expand_one_ref);
+	for_each_ref(expand_one_ref, NULL);
 }
 
 static void set_refspecs(const char **refs, int nr)
@@ -248,6 +248,8 @@
 		while (dest_refspec_nr--)
 			argv[dest_argc++] = *dest_refspec++;
 		argv[dest_argc] = NULL;
+		if (verbose)
+			fprintf(stderr, "Pushing to %s\n", dest);
 		err = run_command_v(argc, argv);
 		if (!err)
 			continue;
@@ -281,6 +283,14 @@
 			i++;
 			break;
 		}
+		if (!strcmp(arg, "-v")) {
+			verbose=1;
+			continue;
+		}
+		if (!strncmp(arg, "--repo=", 7)) {
+			repo = arg+7;
+			continue;
+		}
 		if (!strcmp(arg, "--all")) {
 			all = 1;
 			continue;
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index f60cee1..7b6e572 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -3,7 +3,7 @@
 #include <regex.h>
 
 static const char git_config_set_usage[] =
-"git-repo-config [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
+"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
 
 static char *key;
 static regex_t *key_regexp;
@@ -139,7 +139,16 @@
 			type = T_BOOL;
 		else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
 			return git_config(show_all_config);
-		else
+		else if (!strcmp(argv[1], "--global")) {
+			char *home = getenv("HOME");
+			if (home) {
+				char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
+				setenv("GIT_CONFIG", user_config, 1);
+				free(user_config);
+			} else {
+				die("$HOME not set");
+			}
+		} else
 			break;
 		argc--;
 		argv++;
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index fd3ccc8..3b716fb 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -137,7 +137,7 @@
 	}
 }
 
-static int show_reference(const char *refname, const unsigned char *sha1)
+static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	show_rev(NORMAL, sha1, refname);
 	return 0;
@@ -299,19 +299,19 @@
 				continue;
 			}
 			if (!strcmp(arg, "--all")) {
-				for_each_ref(show_reference);
+				for_each_ref(show_reference, NULL);
 				continue;
 			}
 			if (!strcmp(arg, "--branches")) {
-				for_each_branch_ref(show_reference);
+				for_each_branch_ref(show_reference, NULL);
 				continue;
 			}
 			if (!strcmp(arg, "--tags")) {
-				for_each_tag_ref(show_reference);
+				for_each_tag_ref(show_reference, NULL);
 				continue;
 			}
 			if (!strcmp(arg, "--remotes")) {
-				for_each_remote_ref(show_reference);
+				for_each_remote_ref(show_reference, NULL);
 				continue;
 			}
 			if (!strcmp(arg, "--show-prefix")) {
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 578c9fa..fb1a400 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -346,7 +346,7 @@
 	      compare_ref_name);
 }
 
-static int append_ref(const char *refname, const unsigned char *sha1)
+static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 	int i;
@@ -369,7 +369,7 @@
 	return 0;
 }
 
-static int append_head_ref(const char *refname, const unsigned char *sha1)
+static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	unsigned char tmp[20];
 	int ofs = 11;
@@ -380,14 +380,14 @@
 	 */
 	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
 		ofs = 5;
-	return append_ref(refname + ofs, sha1);
+	return append_ref(refname + ofs, sha1, flag, cb_data);
 }
 
-static int append_tag_ref(const char *refname, const unsigned char *sha1)
+static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	if (strncmp(refname, "refs/tags/", 10))
 		return 0;
-	return append_ref(refname + 5, sha1);
+	return append_ref(refname + 5, sha1, flag, cb_data);
 }
 
 static const char *match_ref_pattern = NULL;
@@ -401,7 +401,7 @@
 	return cnt;
 }
 
-static int append_matching_ref(const char *refname, const unsigned char *sha1)
+static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	/* we want to allow pattern hold/<asterisk> to show all
 	 * branches under refs/heads/hold/, and v0.99.9? to show
@@ -417,41 +417,39 @@
 	if (fnmatch(match_ref_pattern, tail, 0))
 		return 0;
 	if (!strncmp("refs/heads/", refname, 11))
-		return append_head_ref(refname, sha1);
+		return append_head_ref(refname, sha1, flag, cb_data);
 	if (!strncmp("refs/tags/", refname, 10))
-		return append_tag_ref(refname, sha1);
-	return append_ref(refname, sha1);
+		return append_tag_ref(refname, sha1, flag, cb_data);
+	return append_ref(refname, sha1, flag, cb_data);
 }
 
 static void snarf_refs(int head, int tag)
 {
 	if (head) {
 		int orig_cnt = ref_name_cnt;
-		for_each_ref(append_head_ref);
+		for_each_ref(append_head_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 	if (tag) {
 		int orig_cnt = ref_name_cnt;
-		for_each_ref(append_tag_ref);
+		for_each_ref(append_tag_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 }
 
-static int rev_is_head(char *head_path, int headlen, char *name,
+static int rev_is_head(char *head, int headlen, char *name,
 		       unsigned char *head_sha1, unsigned char *sha1)
 {
-	int namelen;
-	if ((!head_path[0]) ||
+	if ((!head[0]) ||
 	    (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
 		return 0;
-	namelen = strlen(name);
-	if ((headlen < namelen) ||
-	    memcmp(head_path + headlen - namelen, name, namelen))
-		return 0;
-	if (headlen == namelen ||
-	    head_path[headlen - namelen - 1] == '/')
-		return 1;
-	return 0;
+	if (!strncmp(head, "refs/heads/", 11))
+		head += 11;
+	if (!strncmp(name, "refs/heads/", 11))
+		name += 11;
+	else if (!strncmp(name, "heads/", 6))
+		name += 6;
+	return !strcmp(head, name);
 }
 
 static int show_merge_base(struct commit_list *seen, int num_rev)
@@ -495,7 +493,7 @@
 {
 	unsigned char revkey[20];
 	if (!get_sha1(av, revkey)) {
-		append_ref(av, revkey);
+		append_ref(av, revkey, 0, NULL);
 		return;
 	}
 	if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
@@ -503,7 +501,7 @@
 		int saved_matches = ref_name_cnt;
 		match_ref_pattern = av;
 		match_ref_slash = count_slash(av);
-		for_each_ref(append_matching_ref);
+		for_each_ref(append_matching_ref, NULL);
 		if (saved_matches == ref_name_cnt &&
 		    ref_name_cnt < MAX_REVS)
 			error("no matching refs with %s", av);
@@ -559,9 +557,9 @@
 	int all_heads = 0, all_tags = 0;
 	int all_mask, all_revs;
 	int lifo = 1;
-	char head_path[128];
-	const char *head_path_p;
-	int head_path_len;
+	char head[128];
+	const char *head_p;
+	int head_len;
 	unsigned char head_sha1[20];
 	int merge_base = 0;
 	int independent = 0;
@@ -638,31 +636,31 @@
 		ac--; av++;
 	}
 
-	head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
-	if (head_path_p) {
-		head_path_len = strlen(head_path_p);
-		memcpy(head_path, head_path_p, head_path_len + 1);
+	head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+	if (head_p) {
+		head_len = strlen(head_p);
+		memcpy(head, head_p, head_len + 1);
 	}
 	else {
-		head_path_len = 0;
-		head_path[0] = 0;
+		head_len = 0;
+		head[0] = 0;
 	}
 
-	if (with_current_branch && head_path_p) {
+	if (with_current_branch && head_p) {
 		int has_head = 0;
 		for (i = 0; !has_head && i < ref_name_cnt; i++) {
 			/* We are only interested in adding the branch
 			 * HEAD points at.
 			 */
-			if (rev_is_head(head_path,
-					head_path_len,
+			if (rev_is_head(head,
+					head_len,
 					ref_name[i],
 					head_sha1, NULL))
 				has_head++;
 		}
 		if (!has_head) {
-			int pfxlen = strlen(git_path("refs/heads/"));
-			append_one_rev(head_path + pfxlen);
+			int pfxlen = strlen("refs/heads/");
+			append_one_rev(head + pfxlen);
 		}
 	}
 
@@ -713,8 +711,8 @@
 	if (1 < num_rev || extra < 0) {
 		for (i = 0; i < num_rev; i++) {
 			int j;
-			int is_head = rev_is_head(head_path,
-						  head_path_len,
+			int is_head = rev_is_head(head,
+						  head_len,
 						  ref_name[i],
 						  head_sha1,
 						  rev[i]->object.sha1);
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
new file mode 100644
index 0000000..06ec400
--- /dev/null
+++ b/builtin-show-ref.c
@@ -0,0 +1,147 @@
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "tag.h"
+
+static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*]";
+
+static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
+	found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
+static const char **pattern;
+
+static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+	struct object *obj;
+	const char *hex;
+
+	if (tags_only || heads_only) {
+		int match;
+
+		match = heads_only && !strncmp(refname, "refs/heads/", 11);
+		match |= tags_only && !strncmp(refname, "refs/tags/", 10);
+		if (!match)
+			return 0;
+	}
+	if (pattern) {
+		int reflen = strlen(refname);
+		const char **p = pattern, *m;
+		while ((m = *p++) != NULL) {
+			int len = strlen(m);
+			if (len > reflen)
+				continue;
+			if (memcmp(m, refname + reflen - len, len))
+				continue;
+			if (len == reflen)
+				goto match;
+			/* "--verify" requires an exact match */
+			if (verify)
+				continue;
+			if (refname[reflen - len - 1] == '/')
+				goto match;
+		}
+		return 0;
+	}
+
+match:
+	found_match++;
+	obj = parse_object(sha1);
+	if (!obj) {
+		if (quiet)
+			return 0;
+		die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1));
+	}
+	if (quiet)
+		return 0;
+
+	hex = find_unique_abbrev(sha1, abbrev);
+	if (hash_only)
+		printf("%s\n", hex);
+	else
+		printf("%s %s\n", hex, refname);
+	if (deref_tags && obj->type == OBJ_TAG) {
+		obj = deref_tag(obj, refname, 0);
+		hex = find_unique_abbrev(obj->sha1, abbrev);
+		printf("%s %s^{}\n", hex, refname);
+	}
+	return 0;
+}
+
+int cmd_show_ref(int argc, const char **argv, const char *prefix)
+{
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (*arg != '-') {
+			pattern = argv + i;
+			break;
+		}
+		if (!strcmp(arg, "--")) {
+			pattern = argv + i + 1;
+			if (!*pattern)
+				pattern = NULL;
+			break;
+		}
+		if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
+			quiet = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-h") || !strcmp(arg, "--head")) {
+			show_head = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-d") || !strcmp(arg, "--dereference")) {
+			deref_tags = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-s") || !strcmp(arg, "--hash")) {
+			hash_only = 1;
+			continue;
+		}
+		if (!strncmp(arg, "--hash=", 7) ||
+		    (!strncmp(arg, "--abbrev", 8) &&
+		     (arg[8] == '=' || arg[8] == '\0'))) {
+			if (arg[3] != 'h' && !arg[8])
+				/* --abbrev only */
+				abbrev = DEFAULT_ABBREV;
+			else {
+				/* --hash= or --abbrev= */
+				char *end;
+				if (arg[3] == 'h') {
+					hash_only = 1;
+					arg += 7;
+				}
+				else
+					arg += 9;
+				abbrev = strtoul(arg, &end, 10);
+				if (*end || abbrev > 40)
+					usage(show_ref_usage);
+				if (abbrev < MINIMUM_ABBREV)
+					abbrev = MINIMUM_ABBREV;
+			}
+			continue;
+		}
+		if (!strcmp(arg, "--verify")) {
+			verify = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--tags")) {
+			tags_only = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--heads")) {
+			heads_only = 1;
+			continue;
+		}
+		usage(show_ref_usage);
+	}
+	if (show_head)
+		head_ref(show_ref, NULL);
+	for_each_ref(show_ref, NULL);
+	if (!found_match) {
+		if (verify && !quiet)
+			die("No match");
+		return 1;
+	}
+	return 0;
+}
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
index 1d3a5e2..d8be052 100644
--- a/builtin-symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "refs.h"
 
 static const char git_symbolic_ref_usage[] =
 "git-symbolic-ref name [ref]";
@@ -7,15 +8,14 @@
 static void check_symref(const char *HEAD)
 {
 	unsigned char sha1[20];
-	const char *git_HEAD = xstrdup(git_path("%s", HEAD));
-	const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
-	if (git_refs_heads_master) {
-		/* we want to strip the .git/ part */
-		int pfxlen = strlen(git_HEAD) - strlen(HEAD);
-		puts(git_refs_heads_master + pfxlen);
-	}
-	else
+	int flag;
+	const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+
+	if (!refs_heads_master)
 		die("No such ref: %s", HEAD);
+	else if (!(flag & REF_ISSYMREF))
+		die("ref %s is not a symbolic ref", HEAD);
+	puts(refs_heads_master);
 }
 
 int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
@@ -26,7 +26,7 @@
 		check_symref(argv[1]);
 		break;
 	case 3:
-		create_symref(xstrdup(git_path("%s", argv[1])), argv[2]);
+		create_symref(argv[1], argv[2]);
 		break;
 	default:
 		usage(git_symbolic_ref_usage);
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 4f96bca..e6d7574 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -15,14 +15,14 @@
 
 /* We always read in 4kB chunks. */
 static unsigned char buffer[4096];
-static unsigned long offset, len;
+static unsigned long offset, len, consumed_bytes;
 static SHA_CTX ctx;
 
 /*
  * Make sure at least "min" bytes are available in the buffer, and
  * return the pointer to the buffer.
  */
-static void * fill(int min)
+static void *fill(int min)
 {
 	if (min <= len)
 		return buffer + offset;
@@ -30,7 +30,7 @@
 		die("cannot fill %d bytes", min);
 	if (offset) {
 		SHA1_Update(&ctx, buffer, offset);
-		memcpy(buffer, buffer + offset, len);
+		memmove(buffer, buffer + offset, len);
 		offset = 0;
 	}
 	do {
@@ -51,6 +51,7 @@
 		die("used more bytes than were available");
 	len -= bytes;
 	offset += bytes;
+	consumed_bytes += bytes;
 }
 
 static void *get_data(unsigned long size)
@@ -89,35 +90,49 @@
 
 struct delta_info {
 	unsigned char base_sha1[20];
+	unsigned long base_offset;
 	unsigned long size;
 	void *delta;
+	unsigned nr;
 	struct delta_info *next;
 };
 
 static struct delta_info *delta_list;
 
-static void add_delta_to_list(unsigned char *base_sha1, void *delta, unsigned long size)
+static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
+			      unsigned long base_offset,
+			      void *delta, unsigned long size)
 {
 	struct delta_info *info = xmalloc(sizeof(*info));
 
 	hashcpy(info->base_sha1, base_sha1);
+	info->base_offset = base_offset;
 	info->size = size;
 	info->delta = delta;
+	info->nr = nr;
 	info->next = delta_list;
 	delta_list = info;
 }
 
-static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size);
-
-static void write_object(void *buf, unsigned long size, const char *type)
-{
+struct obj_info {
+	unsigned long offset;
 	unsigned char sha1[20];
-	if (write_sha1_file(buf, size, type, sha1) < 0)
+};
+
+static struct obj_info *obj_list;
+
+static void added_object(unsigned nr, const char *type, void *data,
+			 unsigned long size);
+
+static void write_object(unsigned nr, void *buf, unsigned long size,
+			 const char *type)
+{
+	if (write_sha1_file(buf, size, type, obj_list[nr].sha1) < 0)
 		die("failed to write object");
-	added_object(sha1, type, buf, size);
+	added_object(nr, type, buf, size);
 }
 
-static void resolve_delta(const char *type,
+static void resolve_delta(unsigned nr, const char *type,
 			  void *base, unsigned long base_size,
 			  void *delta, unsigned long delta_size)
 {
@@ -130,20 +145,23 @@
 	if (!result)
 		die("failed to apply delta");
 	free(delta);
-	write_object(result, result_size, type);
+	write_object(nr, result, result_size, type);
 	free(result);
 }
 
-static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size)
+static void added_object(unsigned nr, const char *type, void *data,
+			 unsigned long size)
 {
 	struct delta_info **p = &delta_list;
 	struct delta_info *info;
 
 	while ((info = *p) != NULL) {
-		if (!hashcmp(info->base_sha1, sha1)) {
+		if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
+		    info->base_offset == obj_list[nr].offset) {
 			*p = info->next;
 			p = &delta_list;
-			resolve_delta(type, data, size, info->delta, info->size);
+			resolve_delta(info->nr, type, data, size,
+				      info->delta, info->size);
 			free(info);
 			continue;
 		}
@@ -151,7 +169,8 @@
 	}
 }
 
-static void unpack_non_delta_entry(enum object_type kind, unsigned long size)
+static void unpack_non_delta_entry(enum object_type kind, unsigned long size,
+				   unsigned nr)
 {
 	void *buf = get_data(size);
 	const char *type;
@@ -164,30 +183,80 @@
 	default: die("bad type %d", kind);
 	}
 	if (!dry_run && buf)
-		write_object(buf, size, type);
+		write_object(nr, buf, size, type);
 	free(buf);
 }
 
-static void unpack_delta_entry(unsigned long delta_size)
+static void unpack_delta_entry(enum object_type kind, unsigned long delta_size,
+			       unsigned nr)
 {
 	void *delta_data, *base;
 	unsigned long base_size;
 	char type[20];
 	unsigned char base_sha1[20];
 
-	hashcpy(base_sha1, fill(20));
-	use(20);
+	if (kind == OBJ_REF_DELTA) {
+		hashcpy(base_sha1, fill(20));
+		use(20);
+		delta_data = get_data(delta_size);
+		if (dry_run || !delta_data) {
+			free(delta_data);
+			return;
+		}
+		if (!has_sha1_file(base_sha1)) {
+			hashcpy(obj_list[nr].sha1, null_sha1);
+			add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
+			return;
+		}
+	} else {
+		unsigned base_found = 0;
+		unsigned char *pack, c;
+		unsigned long base_offset;
+		unsigned lo, mid, hi;
 
-	delta_data = get_data(delta_size);
-	if (dry_run || !delta_data) {
-		free(delta_data);
-		return;
+		pack = fill(1);
+		c = *pack;
+		use(1);
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || base_offset & ~(~0UL >> 7))
+				die("offset value overflow for delta base object");
+			pack = fill(1);
+			c = *pack;
+			use(1);
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		base_offset = obj_list[nr].offset - base_offset;
+
+		delta_data = get_data(delta_size);
+		if (dry_run || !delta_data) {
+			free(delta_data);
+			return;
+		}
+		lo = 0;
+		hi = nr;
+		while (lo < hi) {
+			mid = (lo + hi)/2;
+			if (base_offset < obj_list[mid].offset) {
+				hi = mid;
+			} else if (base_offset > obj_list[mid].offset) {
+				lo = mid + 1;
+			} else {
+				hashcpy(base_sha1, obj_list[mid].sha1);
+				base_found = !is_null_sha1(base_sha1);
+				break;
+			}
+		}
+		if (!base_found) {
+			/* The delta base object is itself a delta that
+			   has not been	resolved yet. */
+			hashcpy(obj_list[nr].sha1, null_sha1);
+			add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
+			return;
+		}
 	}
 
-	if (!has_sha1_file(base_sha1)) {
-		add_delta_to_list(base_sha1, delta_data, delta_size);
-		return;
-	}
 	base = read_sha1_file(base_sha1, type, &base_size);
 	if (!base) {
 		error("failed to read delta-pack base object %s",
@@ -197,7 +266,7 @@
 		has_errors = 1;
 		return;
 	}
-	resolve_delta(type, base, base_size, delta_data, delta_size);
+	resolve_delta(nr, type, base, base_size, delta_data, delta_size);
 	free(base);
 }
 
@@ -208,6 +277,8 @@
 	unsigned long size;
 	enum object_type type;
 
+	obj_list[nr].offset = consumed_bytes;
+
 	pack = fill(1);
 	c = *pack;
 	use(1);
@@ -216,7 +287,7 @@
 	shift = 4;
 	while (c & 0x80) {
 		pack = fill(1);
-		c = *pack++;
+		c = *pack;
 		use(1);
 		size += (c & 0x7f) << shift;
 		shift += 7;
@@ -225,13 +296,14 @@
 		static unsigned long last_sec;
 		static unsigned last_percent;
 		struct timeval now;
-		unsigned percentage = (nr * 100) / total;
+		unsigned percentage = ((nr+1) * 100) / total;
 
 		gettimeofday(&now, NULL);
 		if (percentage != last_percent || now.tv_sec != last_sec) {
 			last_sec = now.tv_sec;
 			last_percent = percentage;
-			fprintf(stderr, "%4u%% (%u/%u) done\r", percentage, nr, total);
+			fprintf(stderr, "%4u%% (%u/%u) done\r",
+					percentage, (nr+1), total);
 		}
 	}
 	switch (type) {
@@ -239,10 +311,11 @@
 	case OBJ_TREE:
 	case OBJ_BLOB:
 	case OBJ_TAG:
-		unpack_non_delta_entry(type, size);
+		unpack_non_delta_entry(type, size, nr);
 		return;
-	case OBJ_DELTA:
-		unpack_delta_entry(size);
+	case OBJ_REF_DELTA:
+	case OBJ_OFS_DELTA:
+		unpack_delta_entry(type, size, nr);
 		return;
 	default:
 		error("bad object type %d", type);
@@ -265,9 +338,10 @@
 		die("unknown pack file version %d", ntohl(hdr->hdr_version));
 	fprintf(stderr, "Unpacking %d objects\n", nr_objects);
 
+	obj_list = xmalloc(nr_objects * sizeof(*obj_list));
 	use(sizeof(struct pack_header));
 	for (i = 0; i < nr_objects; i++)
-		unpack_one(i+1, nr_objects);
+		unpack_one(i, nr_objects);
 	if (delta_list)
 		die("unresolved deltas left after unpacking");
 }
@@ -297,6 +371,21 @@
 				recover = 1;
 				continue;
 			}
+			if (!strncmp(arg, "--pack_header=", 14)) {
+				struct pack_header *hdr;
+				char *c;
+
+				hdr = (struct pack_header *)buffer;
+				hdr->hdr_signature = htonl(PACK_SIGNATURE);
+				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+				if (*c != ',')
+					die("bad %s", arg);
+				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+				if (*c)
+					die("bad %s", arg);
+				len = sizeof(*hdr);
+				continue;
+			}
 			usage(unpack_usage);
 		}
 
diff --git a/builtin-update-index.c b/builtin-update-index.c
index a3c0a45..7f9c638 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -406,9 +406,9 @@
 
 static void read_head_pointers(void)
 {
-	if (read_ref(git_path("HEAD"), head_sha1))
+	if (read_ref("HEAD", head_sha1))
 		die("No HEAD -- no initial commit yet?\n");
-	if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) {
+	if (read_ref("MERGE_HEAD", merge_head_sha1)) {
 		fprintf(stderr, "Not in the middle of a merge.\n");
 		exit(0);
 	}
@@ -445,7 +445,7 @@
 	int has_head = 1;
 	const char **pathspec = get_pathspec(prefix, av + 1);
 
-	if (read_ref(git_path("HEAD"), head_sha1))
+	if (read_ref("HEAD", head_sha1))
 		/* If there is no HEAD, that means it is an initial
 		 * commit.  Update everything in the index.
 		 */
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 90a3da5..b34e598 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -3,15 +3,16 @@
 #include "builtin.h"
 
 static const char git_update_ref_usage[] =
-"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
+"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
 
 int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
 	const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
 	struct ref_lock *lock;
 	unsigned char sha1[20], oldsha1[20];
-	int i;
+	int i, delete;
 
+	delete = 0;
 	setup_ident();
 	git_config(git_default_config);
 
@@ -26,6 +27,10 @@
 				die("Refusing to perform update with \\n in message.");
 			continue;
 		}
+		if (!strcmp("-d", argv[i])) {
+			delete = 1;
+			continue;
+		}
 		if (!refname) {
 			refname = argv[i];
 			continue;
@@ -44,11 +49,18 @@
 
 	if (get_sha1(value, sha1))
 		die("%s: not a valid SHA1", value);
+
+	if (delete) {
+		if (oldval)
+			usage(git_update_ref_usage);
+		return delete_ref(refname, sha1);
+	}
+
 	hashclr(oldsha1);
-	if (oldval && get_sha1(oldval, oldsha1))
+	if (oldval && *oldval && get_sha1(oldval, oldsha1))
 		die("%s: not a valid old SHA1", oldval);
 
-	lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
+	lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
 	if (!lock)
 		return 1;
 	if (write_ref_sha1(lock, sha1, msg) < 0)
diff --git a/builtin.h b/builtin.h
index f71b962..43fed32 100644
--- a/builtin.h
+++ b/builtin.h
@@ -14,11 +14,15 @@
 extern void prune_packed_objects(int);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
 extern int cmd_archive(int argc, const char **argv, const char *prefix);
+extern int cmd_blame(int argc, const char **argv, const char *prefix);
+extern int cmd_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
+extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
@@ -27,6 +31,7 @@
 extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
 extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
 extern int cmd_grep(int argc, const char **argv, const char *prefix);
@@ -40,6 +45,7 @@
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
@@ -49,8 +55,8 @@
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
 extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
-extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
+extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
@@ -63,5 +69,7 @@
 extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
 extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
 
 #endif
diff --git a/cache.h b/cache.h
index c354701..f2ec5c8 100644
--- a/cache.h
+++ b/cache.h
@@ -179,6 +179,7 @@
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
+extern int delete_ref(const char *, unsigned char *sha1);
 
 /* Environment bits from configuration mechanism */
 extern int use_legacy_headers;
@@ -188,7 +189,6 @@
 extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
 extern int shared_repository;
-extern int deny_non_fast_forwards;
 extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
 
@@ -269,8 +269,9 @@
 	OBJ_TREE = 2,
 	OBJ_BLOB = 3,
 	OBJ_TAG = 4,
-	/* 5/6 for future expansion */
-	OBJ_DELTA = 7,
+	/* 5 for future expansion */
+	OBJ_OFS_DELTA = 6,
+	OBJ_REF_DELTA = 7,
 	OBJ_BAD,
 };
 
@@ -288,9 +289,9 @@
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 extern char *sha1_to_hex(const unsigned char *sha1);	/* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
-extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
-extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
-extern int validate_symref(const char *git_HEAD);
+extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+extern int create_symref(const char *ref, const char *refs_heads_master);
+extern int validate_symref(const char *ref);
 
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
@@ -375,6 +376,7 @@
 						char *idx_path);
 
 extern void prepare_packed_git(void);
+extern void reprepare_packed_git(void);
 extern void install_packed_git(struct packed_git *pack);
 
 extern struct packed_git *find_sha1_pack(const unsigned char *sha1, 
@@ -414,10 +416,6 @@
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 
-/* Finish off pack transfer receiving end */
-extern int receive_unpack_pack(int fd[2], const char *me, int quiet, int);
-extern int receive_keep_pack(int fd[2], const char *me, int quiet, int);
-
 /* pager.c */
 extern void setup_pager(void);
 extern int pager_in_use;
diff --git a/check-builtins.sh b/check-builtins.sh
new file mode 100755
index 0000000..d6fe6cf
--- /dev/null
+++ b/check-builtins.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+{
+	cat <<\EOF
+sayIt:
+	$(foreach b,$(BUILT_INS),echo XXX $b YYY;)
+EOF
+	cat Makefile
+} |
+make -f - sayIt 2>/dev/null |
+sed -n -e 's/.*XXX \(.*\) YYY.*/\1/p' |
+sort |
+{
+    bad=0
+    while read builtin
+    do
+	base=`expr "$builtin" : 'git-\(.*\)'`
+	x=`sed -ne 's/.*{ "'$base'", \(cmd_[^, ]*\).*/'$base'	\1/p' git.c`
+	if test -z "$x"
+	then
+		echo "$base is builtin but not listed in git.c command list"
+		bad=1
+	fi
+	for sfx in sh perl py
+	do
+		if test -f "$builtin.$sfx"
+		then
+			echo "$base is builtin but $builtin.$sfx still exists"
+			bad=1
+		fi
+	done
+    done
+    exit $bad
+}
diff --git a/combine-diff.c b/combine-diff.c
index 8bf99f2..29d0c9c 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -898,8 +898,10 @@
 		/* show stat against the first parent even
 		 * when doing combined diff.
 		 */
-		if (i == 0 && opt->output_format & DIFF_FORMAT_DIFFSTAT)
-			diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
+		int stat_opt = (opt->output_format &
+				(DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+		if (i == 0 && stat_opt)
+			diffopts.output_format = stat_opt;
 		else
 			diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
 		diff_tree_sha1(parent[i], sha1, "", &diffopts);
@@ -929,7 +931,8 @@
 			}
 			needsep = 1;
 		}
-		else if (opt->output_format & DIFF_FORMAT_DIFFSTAT)
+		else if (opt->output_format &
+			 (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
 			needsep = 1;
 		if (opt->output_format & DIFF_FORMAT_PATCH) {
 			if (needsep)
diff --git a/config.c b/config.c
index e8f0caf..3cae390 100644
--- a/config.c
+++ b/config.c
@@ -103,6 +103,11 @@
 	}
 }
 
+static inline int iskeychar(int c)
+{
+	return isalnum(c) || c == '-';
+}
+
 static int get_value(config_fn_t fn, char *name, unsigned int len)
 {
 	int c;
@@ -113,7 +118,7 @@
 		c = get_next_char();
 		if (c == EOF)
 			break;
-		if (!isalnum(c))
+		if (!iskeychar(c))
 			break;
 		name[len++] = tolower(c);
 		if (len >= MAXNAME)
@@ -181,7 +186,7 @@
 			return baselen;
 		if (isspace(c))
 			return get_extended_base_var(name, baselen, c);
-		if (!isalnum(c) && c != '.')
+		if (!iskeychar(c) && c != '.')
 			return -1;
 		if (baselen > MAXNAME / 2)
 			return -1;
@@ -573,7 +578,7 @@
 			dot = 1;
 		/* Leave the extended basename untouched.. */
 		if (!dot || i > store.baselen) {
-			if (!isalnum(c) || (i == store.baselen+1 && !isalpha(c))) {
+			if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
 				fprintf(stderr, "invalid key: %s\n", key);
 				free(store.key);
 				ret = 1;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d9cb17d..a43a177 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -19,15 +19,20 @@
 #        source ~/.git-completion.sh
 #
 
+__gitdir ()
+{
+	echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
+}
+
 __git_refs ()
 {
-	local cmd i is_hash=y
-	if [ -d "$1" ]; then
+	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+	if [ -d "$dir" ]; then
 		cmd=git-peek-remote
 	else
 		cmd=git-ls-remote
 	fi
-	for i in $($cmd "$1" 2>/dev/null); do
+	for i in $($cmd "$dir" 2>/dev/null); do
 		case "$is_hash,$i" in
 		y,*) is_hash=n ;;
 		n,*^{}) is_hash=y ;;
@@ -40,13 +45,13 @@
 
 __git_refs2 ()
 {
-	local cmd i is_hash=y
-	if [ -d "$1" ]; then
+	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+	if [ -d "$dir" ]; then
 		cmd=git-peek-remote
 	else
 		cmd=git-ls-remote
 	fi
-	for i in $($cmd "$1" 2>/dev/null); do
+	for i in $($cmd "$dir" 2>/dev/null); do
 		case "$is_hash,$i" in
 		y,*) is_hash=n ;;
 		n,*^{}) is_hash=y ;;
@@ -59,25 +64,34 @@
 
 __git_remotes ()
 {
-	local i REVERTGLOB=$(shopt -p nullglob)
+	local i ngoff IFS=$'\n' d="$(__gitdir)"
+	shopt -q nullglob || ngoff=1
 	shopt -s nullglob
-	for i in .git/remotes/*; do
-		echo ${i#.git/remotes/}
+	for i in "$d/remotes"/*; do
+		echo ${i#$d/remotes/}
 	done
-	$REVERTGLOB
+	[ "$ngoff" ] && shopt -u nullglob
+	for i in $(git --git-dir="$d" repo-config --list); do
+		case "$i" in
+		remote.*.url=*)
+			i="${i#remote.}"
+			echo "${i/.url=*/}"
+			;;
+		esac
+	done
 }
 
 __git_complete_file ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	?*:*)
-		local pfx ls ref="$(echo "$cur" | sed 's,:.*$,,')"
-		cur="$(echo "$cur" | sed 's,^.*:,,')"
+		ref="${cur%%:*}"
+		cur="${cur#*:}"
 		case "$cur" in
 		?*/*)
-			pfx="$(echo "$cur" | sed 's,/[^/]*$,,')"
-			cur="$(echo "$cur" | sed 's,^.*/,,')"
+			pfx="${cur%/*}"
+			cur="${cur##*/}"
 			ls="$ref:$pfx"
 			pfx="$pfx/"
 			;;
@@ -86,7 +100,7 @@
 			;;
 	    esac
 		COMPREPLY=($(compgen -P "$pfx" \
-			-W "$(git-ls-tree "$ls" \
+			-W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
 				| sed '/^100... blob /s,^.*	,,
 				       /^040000 tree /{
 				           s,^.*	,,
@@ -96,15 +110,40 @@
 			-- "$cur"))
 		;;
 	*)
-		COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 		;;
 	esac
 }
 
+__git_aliases ()
+{
+	local i IFS=$'\n'
+	for i in $(git --git-dir="$(__gitdir)" repo-config --list); do
+		case "$i" in
+		alias.*)
+			i="${i#alias.}"
+			echo "${i/=*/}"
+			;;
+		esac
+	done
+}
+
+__git_aliased_command ()
+{
+	local word cmdline=$(git --git-dir="$(__gitdir)" \
+		repo-config --get "alias.$1")
+	for word in $cmdline; do
+		if [ "${word##-*}" ]; then
+			echo $word
+			return
+		fi
+	done
+}
+
 _git_branch ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs .)" -- "$cur"))
+	COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur"))
 }
 
 _git_cat_file ()
@@ -126,7 +165,7 @@
 _git_checkout ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "-l -b $(__git_refs .)" -- "$cur"))
+	COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
 }
 
 _git_diff ()
@@ -137,7 +176,7 @@
 _git_diff_tree ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "-r -p -M $(__git_refs .)" -- "$cur"))
+	COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur"))
 }
 
 _git_fetch ()
@@ -154,8 +193,8 @@
 	*)
 		case "$cur" in
 		*:*)
-	        cur=$(echo "$cur" | sed 's/^.*://')
-			COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+			cur="${cur#*:}"
+			COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 			;;
 		*)
 			local remote
@@ -183,15 +222,20 @@
 
 _git_log ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local pfx cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
+	*...*)
+		pfx="${cur%...*}..."
+		cur="${cur#*...}"
+		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
+		;;
 	*..*)
-		local pfx=$(echo "$cur" | sed 's/\.\..*$/../')
-		cur=$(echo "$cur" | sed 's/^.*\.\.//')
-		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs .)" -- "$cur"))
+		pfx="${cur%..*}.."
+		cur="${cur#*..}"
+		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
 		;;
 	*)
-		COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 		;;
 	esac
 }
@@ -199,7 +243,7 @@
 _git_merge_base ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 
 _git_pull ()
@@ -243,54 +287,82 @@
 			git-push)  remote="${COMP_WORDS[1]}" ;;
 			git)       remote="${COMP_WORDS[2]}" ;;
 			esac
-	        cur=$(echo "$cur" | sed 's/^.*://')
+			cur="${cur#*:}"
 			COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
 			;;
 		*)
-			COMPREPLY=($(compgen -W "$(__git_refs2 .)" -- "$cur"))
+			COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur"))
 			;;
 		esac
 		;;
 	esac
 }
 
+_git_reset ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local opt="--mixed --hard --soft"
+	COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
+}
+
 _git_show ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
+	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 
 _git ()
 {
-	if [ $COMP_CWORD = 1 ]; then
-		COMPREPLY=($(compgen \
-			-W "--version $(git help -a|egrep '^ ')" \
-			-- "${COMP_WORDS[COMP_CWORD]}"))
-	else
-		case "${COMP_WORDS[1]}" in
-		branch)      _git_branch ;;
-		cat-file)    _git_cat_file ;;
-		checkout)    _git_checkout ;;
-		diff)        _git_diff ;;
-		diff-tree)   _git_diff_tree ;;
-		fetch)       _git_fetch ;;
-		log)         _git_log ;;
-		ls-remote)   _git_ls_remote ;;
-		ls-tree)     _git_ls_tree ;;
-		pull)        _git_pull ;;
-		push)        _git_push ;;
-		show)        _git_show ;;
-		show-branch) _git_log ;;
-		whatchanged) _git_log ;;
-		*)           COMPREPLY=() ;;
+	local i c=1 command __git_dir
+
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
+		--bare)      __git_dir="." ;;
+		--version|--help|-p|--paginate) ;;
+		*) command="$i"; break ;;
 		esac
+		c=$((++c))
+	done
+
+	if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+		COMPREPLY=($(compgen \
+			-W "--git-dir= --version \
+				$(git help -a|egrep '^ ') \
+			    $(__git_aliases)" \
+			-- "${COMP_WORDS[COMP_CWORD]}"))
+		return;
 	fi
+
+	local expansion=$(__git_aliased_command "$command")
+	[ "$expansion" ] && command="$expansion"
+
+	case "$command" in
+	branch)      _git_branch ;;
+	cat-file)    _git_cat_file ;;
+	checkout)    _git_checkout ;;
+	diff)        _git_diff ;;
+	diff-tree)   _git_diff_tree ;;
+	fetch)       _git_fetch ;;
+	log)         _git_log ;;
+	ls-remote)   _git_ls_remote ;;
+	ls-tree)     _git_ls_tree ;;
+	merge-base)  _git_merge_base ;;
+	pull)        _git_pull ;;
+	push)        _git_push ;;
+	reset)       _git_reset ;;
+	show)        _git_show ;;
+	show-branch) _git_log ;;
+	whatchanged) _git_log ;;
+	*)           COMPREPLY=() ;;
+	esac
 }
 
 _gitk ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "--all $(__git_refs .)" -- "$cur"))
+	COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur"))
 }
 
 complete -o default -o nospace -F _git git
@@ -307,13 +379,18 @@
 complete -o default            -F _git_merge_base git-merge-base
 complete -o default -o nospace -F _git_pull git-pull
 complete -o default -o nospace -F _git_push git-push
+complete -o default            -F _git_reset git-reset
 complete -o default            -F _git_show git-show
+complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_log git-whatchanged
 
 # The following are necessary only for Cygwin, and only are needed
 # when the user has tab-completed the executable name and consequently
 # included the '.exe' suffix.
 #
+if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
+complete -o default -o nospace -F _git git.exe
+complete -o default            -F _git_branch git-branch.exe
 complete -o default -o nospace -F _git_cat_file git-cat-file.exe
 complete -o default -o nospace -F _git_diff git-diff.exe
 complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
@@ -321,4 +398,6 @@
 complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 complete -o default            -F _git_merge_base git-merge-base.exe
 complete -o default -o nospace -F _git_push git-push.exe
+complete -o default -o nospace -F _git_log git-show-branch.exe
 complete -o default -o nospace -F _git_log git-whatchanged.exe
+fi
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 5354cd6..972c402 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -589,6 +589,7 @@
                           (let ((commit (git-commit-tree buffer tree head)))
                             (git-update-ref "HEAD" commit head)
                             (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
+                            (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
                             (with-current-buffer buffer (erase-buffer))
                             (git-set-files-state files 'uptodate)
                             (when (file-directory-p ".git/rr-cache")
@@ -670,6 +671,32 @@
   (unless git-status (error "Not in git-status buffer."))
   (ewoc-goto-prev git-status n))
 
+(defun git-next-unmerged-file (&optional n)
+  "Move the selection down N unmerged files."
+  (interactive "p")
+  (unless git-status (error "Not in git-status buffer."))
+  (let* ((last (ewoc-locate git-status))
+         (node (ewoc-next git-status last)))
+    (while (and node (> n 0))
+      (when (eq 'unmerged (git-fileinfo->state (ewoc-data node)))
+        (setq n (1- n))
+        (setq last node))
+      (setq node (ewoc-next git-status node)))
+    (ewoc-goto-node git-status last)))
+
+(defun git-prev-unmerged-file (&optional n)
+  "Move the selection up N unmerged files."
+  (interactive "p")
+  (unless git-status (error "Not in git-status buffer."))
+  (let* ((last (ewoc-locate git-status))
+         (node (ewoc-prev git-status last)))
+    (while (and node (> n 0))
+      (when (eq 'unmerged (git-fileinfo->state (ewoc-data node)))
+        (setq n (1- n))
+        (setq last node))
+      (setq node (ewoc-prev git-status node)))
+    (ewoc-goto-node git-status last)))
+
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
@@ -862,7 +889,7 @@
           'face 'git-header-face)
          (propertize git-log-msg-separator 'face 'git-separator-face)
          "\n")
-        (cond ((and merge-heads (file-readable-p ".git/MERGE_MSG"))
+        (cond ((file-readable-p ".git/MERGE_MSG")
                (insert-file-contents ".git/MERGE_MSG"))
               (sign-off
                (insert (format "\n\nSigned-off-by: %s <%s>\n"
@@ -873,7 +900,8 @@
               (2 font-lock-function-name-face))
              (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
               (1 font-lock-comment-face)))))
-      (log-edit #'git-do-commit nil #'git-log-edit-files buffer))))
+      (log-edit #'git-do-commit nil #'git-log-edit-files buffer)
+      (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
 
 (defun git-find-file ()
   "Visit the current file in its own buffer."
@@ -884,6 +912,15 @@
     (when (eq 'unmerged (git-fileinfo->state info))
       (smerge-mode))))
 
+(defun git-find-file-other-window ()
+  "Visit the current file in its own buffer in another window."
+  (interactive)
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((info (ewoc-data (ewoc-locate git-status))))
+    (find-file-other-window (git-fileinfo->name info))
+    (when (eq 'unmerged (git-fileinfo->state info))
+      (smerge-mode))))
+
 (defun git-find-file-imerge ()
   "Visit the current file in interactive merge mode."
   (interactive)
@@ -967,7 +1004,10 @@
     (define-key map "m"   'git-mark-file)
     (define-key map "M"   'git-mark-all)
     (define-key map "n"   'git-next-file)
+    (define-key map "N"   'git-next-unmerged-file)
+    (define-key map "o"   'git-find-file-other-window)
     (define-key map "p"   'git-prev-file)
+    (define-key map "P"   'git-prev-unmerged-file)
     (define-key map "q"   'git-status-quit)
     (define-key map "r"   'git-remove-file)
     (define-key map "R"   'git-resolve-file)
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index 80e7675..8b636192 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -33,6 +33,8 @@
 ;;  - working with revisions other than HEAD
 ;;
 
+(eval-when-compile (require 'cl))
+
 (defvar git-commits-coding-system 'utf-8
   "Default coding system for git commits.")
 
diff --git a/describe.c b/describe.c
index ab192f8..f4029ee 100644
--- a/describe.c
+++ b/describe.c
@@ -53,7 +53,7 @@
 	names = ++idx;
 }
 
-static int get_name(const char *path, const unsigned char *sha1)
+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 	struct object *object;
@@ -113,7 +113,7 @@
 
 	if (!initialized) {
 		initialized = 1;
-		for_each_ref(get_name);
+		for_each_ref(get_name, NULL);
 		qsort(name_array, names, sizeof(*name_array), compare_names);
 	}
 
diff --git a/diff.c b/diff.c
index fb82432..3315378 100644
--- a/diff.c
+++ b/diff.c
@@ -795,6 +795,23 @@
 	       set, total_files, adds, dels, reset);
 }
 
+static void show_numstat(struct diffstat_t* data, struct diff_options *options)
+{
+	int i;
+
+	for (i = 0; i < data->nr; i++) {
+		struct diffstat_file *file = data->files[i];
+
+		printf("%d\t%d\t", file->added, file->deleted);
+		if (options->line_termination &&
+		    quote_c_style(file->name, NULL, NULL, 0))
+			quote_c_style(file->name, NULL, stdout, 0);
+		else
+			fputs(file->name, stdout);
+		putchar(options->line_termination);
+	}
+}
+
 struct checkdiff_t {
 	struct xdiff_emit_state xm;
 	const char *filename;
@@ -1731,6 +1748,7 @@
 				      DIFF_FORMAT_CHECKDIFF |
 				      DIFF_FORMAT_NO_OUTPUT))
 		options->output_format &= ~(DIFF_FORMAT_RAW |
+					    DIFF_FORMAT_NUMSTAT |
 					    DIFF_FORMAT_DIFFSTAT |
 					    DIFF_FORMAT_SUMMARY |
 					    DIFF_FORMAT_PATCH);
@@ -1740,7 +1758,9 @@
 	 * recursive bits for other formats here.
 	 */
 	if (options->output_format & (DIFF_FORMAT_PATCH |
+				      DIFF_FORMAT_NUMSTAT |
 				      DIFF_FORMAT_DIFFSTAT |
+				      DIFF_FORMAT_SUMMARY |
 				      DIFF_FORMAT_CHECKDIFF))
 		options->recursive = 1;
 	/*
@@ -1828,6 +1848,9 @@
 	else if (!strcmp(arg, "--patch-with-raw")) {
 		options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
 	}
+	else if (!strcmp(arg, "--numstat")) {
+		options->output_format |= DIFF_FORMAT_NUMSTAT;
+	}
 	else if (!strncmp(arg, "--stat", 6)) {
 		char *end;
 		int width = options->stat_width;
@@ -2602,7 +2625,7 @@
 		separator++;
 	}
 
-	if (output_format & DIFF_FORMAT_DIFFSTAT) {
+	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) {
 		struct diffstat_t diffstat;
 
 		memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -2612,7 +2635,10 @@
 			if (check_pair_status(p))
 				diff_flush_stat(p, options, &diffstat);
 		}
-		show_stats(&diffstat, options);
+		if (output_format & DIFF_FORMAT_NUMSTAT)
+			show_numstat(&diffstat, options);
+		if (output_format & DIFF_FORMAT_DIFFSTAT)
+			show_stats(&diffstat, options);
 		separator++;
 	}
 
diff --git a/diff.h b/diff.h
index b48c991..101b2b5 100644
--- a/diff.h
+++ b/diff.h
@@ -26,25 +26,27 @@
 
 #define DIFF_FORMAT_RAW		0x0001
 #define DIFF_FORMAT_DIFFSTAT	0x0002
-#define DIFF_FORMAT_SUMMARY	0x0004
-#define DIFF_FORMAT_PATCH	0x0008
+#define DIFF_FORMAT_NUMSTAT	0x0004
+#define DIFF_FORMAT_SUMMARY	0x0008
+#define DIFF_FORMAT_PATCH	0x0010
 
 /* These override all above */
-#define DIFF_FORMAT_NAME	0x0010
-#define DIFF_FORMAT_NAME_STATUS	0x0020
-#define DIFF_FORMAT_CHECKDIFF	0x0040
+#define DIFF_FORMAT_NAME	0x0100
+#define DIFF_FORMAT_NAME_STATUS	0x0200
+#define DIFF_FORMAT_CHECKDIFF	0x0400
 
 /* Same as output_format = 0 but we know that -s flag was given
  * and we should not give default value to output_format.
  */
-#define DIFF_FORMAT_NO_OUTPUT	0x0080
+#define DIFF_FORMAT_NO_OUTPUT	0x0800
 
-#define DIFF_FORMAT_CALLBACK	0x0100
+#define DIFF_FORMAT_CALLBACK	0x1000
 
 struct diff_options {
 	const char *filter;
 	const char *orderfile;
 	const char *pickaxe;
+	const char *single_follow;
 	unsigned recursive:1,
 		 tree_in_recursive:1,
 		 binary:1,
@@ -101,6 +103,8 @@
 		     const char *base, struct diff_options *opt);
 extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
 			  const char *base, struct diff_options *opt);
+extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
+                               struct diff_options *opt);
 
 struct combine_diff_path {
 	struct combine_diff_path *next;
@@ -170,6 +174,7 @@
 "  --patch-with-raw\n" \
 "                output both a patch and the diff-raw format.\n" \
 "  --stat        show diffstat instead of patch.\n" \
+"  --numstat     show numeric diffstat instead of patch.\n" \
 "  --patch-with-stat\n" \
 "                output a patch and prepend its diffstat.\n" \
 "  --name-only   show only names of changed files.\n" \
diff --git a/diffcore-rename.c b/diffcore-rename.c
index ef23901..57a74b6 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -256,11 +256,15 @@
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		if (!DIFF_FILE_VALID(p->one))
+		if (!DIFF_FILE_VALID(p->one)) {
 			if (!DIFF_FILE_VALID(p->two))
 				continue; /* unmerged */
+			else if (options->single_follow &&
+				 strcmp(options->single_follow, p->two->path))
+				continue; /* not interested */
 			else
 				locate_rename_dst(p->two, 1);
+		}
 		else if (!DIFF_FILE_VALID(p->two)) {
 			/* If the source is a broken "delete", and
 			 * they did not really want to get broken,
diff --git a/environment.c b/environment.c
index 63b1d15..84d870c 100644
--- a/environment.c
+++ b/environment.c
@@ -20,7 +20,6 @@
 int repository_format_version;
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
 int shared_repository = PERM_UMASK;
-int deny_non_fast_forwards = 0;
 const char *apply_default_whitespace;
 int zlib_compression_level = Z_DEFAULT_COMPRESSION;
 int pager_in_use;
diff --git a/fetch-clone.c b/fetch-clone.c
deleted file mode 100644
index 76b99af..0000000
--- a/fetch-clone.c
+++ /dev/null
@@ -1,276 +0,0 @@
-#include "cache.h"
-#include "exec_cmd.h"
-#include "pkt-line.h"
-#include "sideband.h"
-#include <sys/wait.h>
-#include <sys/time.h>
-
-static int finish_pack(const char *pack_tmp_name, const char *me)
-{
-	int pipe_fd[2];
-	pid_t pid;
-	char idx[PATH_MAX];
-	char final[PATH_MAX];
-	char hash[41];
-	unsigned char sha1[20];
-	char *cp;
-	int err = 0;
-
-	if (pipe(pipe_fd) < 0)
-		die("%s: unable to set up pipe", me);
-
-	strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
-	cp = strrchr(idx, '/');
-	memcpy(cp, "/pidx", 5);
-
-	pid = fork();
-	if (pid < 0)
-		die("%s: unable to fork off git-index-pack", me);
-	if (!pid) {
-		close(0);
-		dup2(pipe_fd[1], 1);
-		close(pipe_fd[0]);
-		close(pipe_fd[1]);
-		execl_git_cmd("index-pack", "-o", idx, pack_tmp_name, NULL);
-		error("cannot exec git-index-pack <%s> <%s>",
-		      idx, pack_tmp_name);
-		exit(1);
-	}
-	close(pipe_fd[1]);
-	if (read(pipe_fd[0], hash, 40) != 40) {
-		error("%s: unable to read from git-index-pack", me);
-		err = 1;
-	}
-	close(pipe_fd[0]);
-
-	for (;;) {
-		int status, code;
-
-		if (waitpid(pid, &status, 0) < 0) {
-			if (errno == EINTR)
-				continue;
-			error("waitpid failed (%s)", strerror(errno));
-			goto error_die;
-		}
-		if (WIFSIGNALED(status)) {
-			int sig = WTERMSIG(status);
-			error("git-index-pack died of signal %d", sig);
-			goto error_die;
-		}
-		if (!WIFEXITED(status)) {
-			error("git-index-pack died of unnatural causes %d",
-			      status);
-			goto error_die;
-		}
-		code = WEXITSTATUS(status);
-		if (code) {
-			error("git-index-pack died with error code %d", code);
-			goto error_die;
-		}
-		if (err)
-			goto error_die;
-		break;
-	}
-	hash[40] = 0;
-	if (get_sha1_hex(hash, sha1)) {
-		error("git-index-pack reported nonsense '%s'", hash);
-		goto error_die;
-	}
-	/* Now we have pack in pack_tmp_name[], and
-	 * idx in idx[]; rename them to their final names.
-	 */
-	snprintf(final, sizeof(final),
-		 "%s/pack/pack-%s.pack", get_object_directory(), hash);
-	move_temp_to_file(pack_tmp_name, final);
-	chmod(final, 0444);
-	snprintf(final, sizeof(final),
-		 "%s/pack/pack-%s.idx", get_object_directory(), hash);
-	move_temp_to_file(idx, final);
-	chmod(final, 0444);
-	return 0;
-
- error_die:
-	unlink(idx);
-	unlink(pack_tmp_name);
-	exit(1);
-}
-
-static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2])
-{
-	pid_t side_pid;
-
-	if (!sideband) {
-		fd[0] = xd[0];
-		fd[1] = xd[1];
-		return 0;
-	}
-	/* xd[] is talking with upload-pack; subprocess reads from
-	 * xd[0], spits out band#2 to stderr, and feeds us band#1
-	 * through our fd[0].
-	 */
-	if (pipe(fd) < 0)
-		die("%s: unable to set up pipe", me);
-	side_pid = fork();
-	if (side_pid < 0)
-		die("%s: unable to fork off sideband demultiplexer", me);
-	if (!side_pid) {
-		/* subprocess */
-		close(fd[0]);
-		if (xd[0] != xd[1])
-			close(xd[1]);
-		if (recv_sideband(me, xd[0], fd[1], 2))
-			exit(1);
-		exit(0);
-	}
-	close(xd[0]);
-	close(fd[1]);
-	fd[1] = xd[1];
-	return side_pid;
-}
-
-int receive_unpack_pack(int xd[2], const char *me, int quiet, int sideband)
-{
-	int status;
-	pid_t pid, side_pid;
-	int fd[2];
-
-	side_pid = setup_sideband(sideband, me, fd, xd);
-	pid = fork();
-	if (pid < 0)
-		die("%s: unable to fork off git-unpack-objects", me);
-	if (!pid) {
-		dup2(fd[0], 0);
-		close(fd[0]);
-		close(fd[1]);
-		execl_git_cmd("unpack-objects", quiet ? "-q" : NULL, NULL);
-		die("git-unpack-objects exec failed");
-	}
-	close(fd[0]);
-	close(fd[1]);
-	while (waitpid(pid, &status, 0) < 0) {
-		if (errno != EINTR)
-			die("waiting for git-unpack-objects: %s",
-			    strerror(errno));
-	}
-	if (WIFEXITED(status)) {
-		int code = WEXITSTATUS(status);
-		if (code)
-			die("git-unpack-objects died with error code %d",
-			    code);
-		return 0;
-	}
-	if (WIFSIGNALED(status)) {
-		int sig = WTERMSIG(status);
-		die("git-unpack-objects died of signal %d", sig);
-	}
-	die("git-unpack-objects died of unnatural causes %d", status);
-}
-
-/*
- * We average out the download speed over this many "events", where
- * an event is a minimum of about half a second. That way, we get
- * a reasonably stable number.
- */
-#define NR_AVERAGE (4)
-
-/*
- * A "binary msec" is a power-of-two-msec, aka 1/1024th of a second.
- * Keeping the time in that format means that "bytes / msecs" means
- * the same as kB/s (modulo rounding).
- *
- * 1000512 is a magic number (usecs in a second, rounded up by half
- * of 1024, to make "rounding" come out right ;)
- */
-#define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10))
-
-int receive_keep_pack(int xd[2], const char *me, int quiet, int sideband)
-{
-	char tmpfile[PATH_MAX];
-	int ofd, ifd, fd[2];
-	unsigned long total;
-	static struct timeval prev_tv;
-	struct average {
-		unsigned long bytes;
-		unsigned long time;
-	} download[NR_AVERAGE] = { {0, 0}, };
-	unsigned long avg_bytes, avg_time;
-	int idx = 0;
-
-	setup_sideband(sideband, me, fd, xd);
-
-	ifd = fd[0];
-	snprintf(tmpfile, sizeof(tmpfile),
-		 "%s/pack/tmp-XXXXXX", get_object_directory());
-	ofd = mkstemp(tmpfile);
-	if (ofd < 0)
-		return error("unable to create temporary file %s", tmpfile);
-
-	gettimeofday(&prev_tv, NULL);
-	total = 0;
-	avg_bytes = 0;
-	avg_time = 0;
-	while (1) {
-		char buf[8192];
-		ssize_t sz, wsz, pos;
-		sz = read(ifd, buf, sizeof(buf));
-		if (sz == 0)
-			break;
-		if (sz < 0) {
-			if (errno != EINTR && errno != EAGAIN) {
-				error("error reading pack (%s)", strerror(errno));
-				close(ofd);
-				unlink(tmpfile);
-				return -1;
-			}
-			sz = 0;
-		}
-		pos = 0;
-		while (pos < sz) {
-			wsz = write(ofd, buf + pos, sz - pos);
-			if (wsz < 0) {
-				error("error writing pack (%s)",
-				      strerror(errno));
-				close(ofd);
-				unlink(tmpfile);
-				return -1;
-			}
-			pos += wsz;
-		}
-		total += sz;
-		if (!quiet) {
-			static unsigned long last;
-			struct timeval tv;
-			unsigned long diff = total - last;
-			/* not really "msecs", but a power-of-two millisec (1/1024th of a sec) */
-			unsigned long msecs;
-
-			gettimeofday(&tv, NULL);
-			msecs = tv.tv_sec - prev_tv.tv_sec;
-			msecs <<= 10;
-			msecs += usec_to_binarymsec(tv.tv_usec - prev_tv.tv_usec);
-
-			if (msecs > 500) {
-				prev_tv = tv;
-				last = total;
-
-				/* Update averages ..*/
-				avg_bytes += diff;
-				avg_time += msecs;
-				avg_bytes -= download[idx].bytes;
-				avg_time -= download[idx].time;
-				download[idx].bytes = diff;
-				download[idx].time = msecs;
-				idx++;
-				if (idx >= NR_AVERAGE)
-					idx = 0;
-
-				fprintf(stderr, "%4lu.%03luMB  (%lu kB/s)      \r",
-					total >> 20,
-					1000*((total >> 10) & 1023)>>10,
-					avg_bytes / avg_time );
-			}
-		}
-	}
-	close(ofd);
-	return finish_pack(tmpfile, me);
-}
diff --git a/fetch-pack.c b/fetch-pack.c
index e8708aa..0a169dc 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -3,6 +3,9 @@
 #include "pkt-line.h"
 #include "commit.h"
 #include "tag.h"
+#include "exec_cmd.h"
+#include "sideband.h"
+#include <sys/wait.h>
 
 static int keep_pack;
 static int quiet;
@@ -42,7 +45,7 @@
 	}
 }
 
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *o = deref_tag(parse_object(sha1), path, 0);
 
@@ -143,7 +146,7 @@
 	unsigned in_vain = 0;
 	int got_continue = 0;
 
-	for_each_ref(rev_list_insert_ref);
+	for_each_ref(rev_list_insert_ref, NULL);
 
 	fetching = 0;
 	for ( ; refs ; refs = refs->next) {
@@ -166,12 +169,13 @@
 		}
 
 		if (!fetching)
-			packet_write(fd[1], "want %s%s%s%s%s\n",
+			packet_write(fd[1], "want %s%s%s%s%s%s\n",
 				     sha1_to_hex(remote),
 				     (multi_ack ? " multi_ack" : ""),
 				     (use_sideband == 2 ? " side-band-64k" : ""),
 				     (use_sideband == 1 ? " side-band" : ""),
-				     (use_thin_pack ? " thin-pack" : ""));
+				     (use_thin_pack ? " thin-pack" : ""),
+				     " ofs-delta");
 		else
 			packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
 		fetching++;
@@ -253,7 +257,7 @@
 
 static struct commit_list *complete;
 
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *o = parse_object(sha1);
 
@@ -365,7 +369,7 @@
 		}
 	}
 
-	for_each_ref(mark_complete);
+	for_each_ref(mark_complete, NULL);
 	if (cutoff)
 		mark_recent_complete_commits(cutoff);
 
@@ -415,6 +419,103 @@
 	return retval;
 }
 
+static pid_t setup_sideband(int fd[2], int xd[2])
+{
+	pid_t side_pid;
+
+	if (!use_sideband) {
+		fd[0] = xd[0];
+		fd[1] = xd[1];
+		return 0;
+	}
+	/* xd[] is talking with upload-pack; subprocess reads from
+	 * xd[0], spits out band#2 to stderr, and feeds us band#1
+	 * through our fd[0].
+	 */
+	if (pipe(fd) < 0)
+		die("fetch-pack: unable to set up pipe");
+	side_pid = fork();
+	if (side_pid < 0)
+		die("fetch-pack: unable to fork off sideband demultiplexer");
+	if (!side_pid) {
+		/* subprocess */
+		close(fd[0]);
+		if (xd[0] != xd[1])
+			close(xd[1]);
+		if (recv_sideband("fetch-pack", xd[0], fd[1], 2))
+			exit(1);
+		exit(0);
+	}
+	close(xd[0]);
+	close(fd[1]);
+	fd[1] = xd[1];
+	return side_pid;
+}
+
+static int get_pack(int xd[2], const char **argv)
+{
+	int status;
+	pid_t pid, side_pid;
+	int fd[2];
+
+	side_pid = setup_sideband(fd, xd);
+	pid = fork();
+	if (pid < 0)
+		die("fetch-pack: unable to fork off %s", argv[0]);
+	if (!pid) {
+		dup2(fd[0], 0);
+		close(fd[0]);
+		close(fd[1]);
+		execv_git_cmd(argv);
+		die("%s exec failed", argv[0]);
+	}
+	close(fd[0]);
+	close(fd[1]);
+	while (waitpid(pid, &status, 0) < 0) {
+		if (errno != EINTR)
+			die("waiting for %s: %s", argv[0], strerror(errno));
+	}
+	if (WIFEXITED(status)) {
+		int code = WEXITSTATUS(status);
+		if (code)
+			die("%s died with error code %d", argv[0], code);
+		return 0;
+	}
+	if (WIFSIGNALED(status)) {
+		int sig = WTERMSIG(status);
+		die("%s died of signal %d", argv[0], sig);
+	}
+	die("%s died of unnatural causes %d", argv[0], status);
+}
+
+static int explode_rx_pack(int xd[2])
+{
+	const char *argv[3] = { "unpack-objects", quiet ? "-q" : NULL, NULL };
+	return get_pack(xd, argv);
+}
+
+static int keep_rx_pack(int xd[2])
+{
+	const char *argv[6];
+	char keep_arg[256];
+	int n = 0;
+
+	argv[n++] = "index-pack";
+	argv[n++] = "--stdin";
+	if (!quiet)
+		argv[n++] = "-v";
+	if (use_thin_pack)
+		argv[n++] = "--fix-thin";
+	if (keep_pack > 1) {
+		int s = sprintf(keep_arg, "--keep=fetch-pack %i on ", getpid());
+		if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
+			strcpy(keep_arg + s, "localhost");
+		argv[n++] = keep_arg;
+	}
+	argv[n] = NULL;
+	return get_pack(xd, argv);
+}
+
 static int fetch_pack(int fd[2], int nr_match, char **match)
 {
 	struct ref *ref;
@@ -446,17 +547,13 @@
 		goto all_done;
 	}
 	if (find_common(fd, sha1, ref) < 0)
-		if (!keep_pack)
+		if (keep_pack != 1)
 			/* When cloning, it is not unusual to have
 			 * no common commit.
 			 */
 			fprintf(stderr, "warning: no common commits\n");
 
-	if (keep_pack)
-		status = receive_keep_pack(fd, "git-fetch-pack", quiet, use_sideband);
-	else
-		status = receive_unpack_pack(fd, "git-fetch-pack", quiet, use_sideband);
-
+	status = (keep_pack) ? keep_rx_pack(fd) : explode_rx_pack(fd);
 	if (status)
 		die("git-fetch-pack: fetch failed.");
 
@@ -493,7 +590,7 @@
 				continue;
 			}
 			if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
-				keep_pack = 1;
+				keep_pack++;
 				continue;
 			}
 			if (!strcmp("--thin", arg)) {
@@ -517,8 +614,6 @@
 	}
 	if (!dest)
 		usage(fetch_pack_usage);
-	if (keep_pack)
-		use_thin_pack = 0;
 	pid = git_connect(fd, dest, exec);
 	if (pid < 0)
 		return 1;
diff --git a/fetch.c b/fetch.c
index 34df8d3..c426c04 100644
--- a/fetch.c
+++ b/fetch.c
@@ -201,7 +201,7 @@
 	return -1;
 }
 
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 	if (commit) {
@@ -266,7 +266,7 @@
 		if (!write_ref || !write_ref[i])
 			continue;
 
-		lock[i] = lock_ref_sha1(write_ref[i], NULL, 0);
+		lock[i] = lock_ref_sha1(write_ref[i], NULL);
 		if (!lock[i]) {
 			error("Can't lock ref %s", write_ref[i]);
 			goto unlock_and_fail;
@@ -274,7 +274,7 @@
 	}
 
 	if (!get_recover)
-		for_each_ref(mark_complete);
+		for_each_ref(mark_complete, NULL);
 
 	for (i = 0; i < targets; i++) {
 		if (interpret_target(target[i], &sha1[20 * i])) {
diff --git a/fsck-objects.c b/fsck-objects.c
index 4d994f3..46b628c 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -402,7 +402,7 @@
 
 static int default_refs;
 
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *obj;
 
@@ -424,7 +424,7 @@
 
 static void get_default_heads(void)
 {
-	for_each_ref(fsck_handle_ref);
+	for_each_ref(fsck_handle_ref, NULL);
 
 	/*
 	 * Not having any default heads isn't really fatal, but
@@ -458,15 +458,14 @@
 static int fsck_head_link(void)
 {
 	unsigned char sha1[20];
-	const char *git_HEAD = xstrdup(git_path("HEAD"));
-	const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
-	int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
+	int flag;
+	const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
 
-	if (!git_refs_heads_master)
+	if (!head_points_at || !(flag & REF_ISSYMREF))
 		return error("HEAD is not a symbolic ref");
-	if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
+	if (strncmp(head_points_at, "refs/heads/", 11))
 		return error("HEAD points to something strange (%s)",
-			     git_refs_heads_master + pfxlen);
+			     head_points_at);
 	if (is_null_sha1(sha1))
 		return error("HEAD: not a valid git pointer");
 	return 0;
diff --git a/git-annotate.perl b/git-annotate.perl
deleted file mode 100755
index 215ed26..0000000
--- a/git-annotate.perl
+++ /dev/null
@@ -1,708 +0,0 @@
-#!/usr/bin/perl
-# Copyright 2006, Ryan Anderson <ryan@michonline.com>
-#
-# GPL v2 (See COPYING)
-#
-# This file is licensed under the GPL v2, or a later version
-# at the discretion of Linus Torvalds.
-
-use warnings;
-use strict;
-use Getopt::Long;
-use POSIX qw(strftime gmtime);
-use File::Basename qw(basename dirname);
-
-sub usage() {
-	print STDERR "Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
-	-l, --long
-			Show long rev (Defaults off)
-	-t, --time
-			Show raw timestamp (Defaults off)
-	-r, --rename
-			Follow renames (Defaults on).
-	-S, --rev-file revs-file
-			Use revs from revs-file instead of calling git-rev-list
-	-h, --help
-			This message.
-";
-
-	exit(1);
-}
-
-our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file) = (0, 0, 1);
-
-my $rc = GetOptions(	"long|l" => \$longrev,
-			"time|t" => \$rawtime,
-			"help|h" => \$help,
-			"rename|r" => \$rename,
-			"rev-file|S=s" => \$rev_file);
-if (!$rc or $help or !@ARGV) {
-	usage();
-}
-
-my $filename = shift @ARGV;
-if (@ARGV) {
-	$starting_rev = shift @ARGV;
-}
-
-my @stack = (
-	{
-		'rev' => defined $starting_rev ? $starting_rev : "HEAD",
-		'filename' => $filename,
-	},
-);
-
-our @filelines = ();
-
-if (defined $starting_rev) {
-	@filelines = git_cat_file($starting_rev, $filename);
-} else {
-	open(F,"<",$filename)
-		or die "Failed to open filename: $!";
-
-	while(<F>) {
-		chomp;
-		push @filelines, $_;
-	}
-	close(F);
-
-}
-
-our %revs;
-our @revqueue;
-our $head;
-
-my $revsprocessed = 0;
-while (my $bound = pop @stack) {
-	my @revisions = git_rev_list($bound->{'rev'}, $bound->{'filename'});
-	foreach my $revinst (@revisions) {
-		my ($rev, @parents) = @$revinst;
-		$head ||= $rev;
-
-		if (!defined($rev)) {
-			$rev = "";
-		}
-		$revs{$rev}{'filename'} = $bound->{'filename'};
-		if (scalar @parents > 0) {
-			$revs{$rev}{'parents'} = \@parents;
-			next;
-		}
-
-		if (!$rename) {
-			next;
-		}
-
-		my $newbound = find_parent_renames($rev, $bound->{'filename'});
-		if ( exists $newbound->{'filename'} && $newbound->{'filename'} ne $bound->{'filename'}) {
-			push @stack, $newbound;
-			$revs{$rev}{'parents'} = [$newbound->{'rev'}];
-		}
-	}
-}
-push @revqueue, $head;
-init_claim( defined $starting_rev ? $head : 'dirty');
-unless (defined $starting_rev) {
-	my $diff = open_pipe("git","diff","HEAD", "--",$filename)
-		or die "Failed to call git diff to check for dirty state: $!";
-
-	_git_diff_parse($diff, [$head], "dirty", (
-				'author' => gitvar_name("GIT_AUTHOR_IDENT"),
-				'author_date' => sprintf("%s +0000",time()),
-				)
-			);
-	close($diff);
-}
-handle_rev();
-
-
-my $i = 0;
-foreach my $l (@filelines) {
-	my ($output, $rev, $committer, $date);
-	if (ref $l eq 'ARRAY') {
-		($output, $rev, $committer, $date) = @$l;
-		if (!$longrev && length($rev) > 8) {
-			$rev = substr($rev,0,8);
-		}
-	} else {
-		$output = $l;
-		($rev, $committer, $date) = ('unknown', 'unknown', 'unknown');
-	}
-
-	printf("%s\t(%10s\t%10s\t%d)%s\n", $rev, $committer,
-		format_date($date), ++$i, $output);
-}
-
-sub init_claim {
-	my ($rev) = @_;
-	for (my $i = 0; $i < @filelines; $i++) {
-		$filelines[$i] = [ $filelines[$i], '', '', '', 1];
-			# line,
-			# rev,
-			# author,
-			# date,
-			# 1 <-- belongs to the original file.
-	}
-	$revs{$rev}{'lines'} = \@filelines;
-}
-
-
-sub handle_rev {
-	my $revseen = 0;
-	my %seen;
-	while (my $rev = shift @revqueue) {
-		next if $seen{$rev}++;
-
-		my %revinfo = git_commit_info($rev);
-
-		if (exists $revs{$rev}{parents} &&
-		    scalar @{$revs{$rev}{parents}} != 0) {
-
-			git_diff_parse($revs{$rev}{'parents'}, $rev, %revinfo);
-			push @revqueue, @{$revs{$rev}{'parents'}};
-
-		} else {
-			# We must be at the initial rev here, so claim everything that is left.
-			for (my $i = 0; $i < @{$revs{$rev}{lines}}; $i++) {
-				if (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {
-					claim_line($i, $rev, $revs{$rev}{lines}, %revinfo);
-				}
-			}
-		}
-	}
-}
-
-
-sub git_rev_list {
-	my ($rev, $file) = @_;
-
-	my $revlist;
-	if ($rev_file) {
-		open($revlist, '<' . $rev_file)
-		    or die "Failed to open $rev_file : $!";
-	} else {
-		$revlist = open_pipe("git-rev-list","--parents","--remove-empty",$rev,"--",$file)
-			or die "Failed to exec git-rev-list: $!";
-	}
-
-	my @revs;
-	while(my $line = <$revlist>) {
-		chomp $line;
-		my ($rev, @parents) = split /\s+/, $line;
-		push @revs, [ $rev, @parents ];
-	}
-	close($revlist);
-
-	printf("0 revs found for rev %s (%s)\n", $rev, $file) if (@revs == 0);
-	return @revs;
-}
-
-sub find_parent_renames {
-	my ($rev, $file) = @_;
-
-	my $patch = open_pipe("git-diff-tree", "-M50", "-r","--name-status", "-z","$rev")
-		or die "Failed to exec git-diff: $!";
-
-	local $/ = "\0";
-	my %bound;
-	my $junk = <$patch>;
-	while (my $change = <$patch>) {
-		chomp $change;
-		my $filename = <$patch>;
-		if (!defined $filename) {
-			next;
-		}
-		chomp $filename;
-
-		if ($change =~ m/^[AMD]$/ ) {
-			next;
-		} elsif ($change =~ m/^R/ ) {
-			my $oldfilename = $filename;
-			$filename = <$patch>;
-			chomp $filename;
-			if ( $file eq $filename ) {
-				my $parent = git_find_parent($rev, $oldfilename);
-				@bound{'rev','filename'} = ($parent, $oldfilename);
-				last;
-			}
-		}
-	}
-	close($patch);
-
-	return \%bound;
-}
-
-
-sub git_find_parent {
-	my ($rev, $filename) = @_;
-
-	my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev","--",$filename)
-		or die "Failed to open git-rev-list to find a single parent: $!";
-
-	my $parentline = <$revparent>;
-	chomp $parentline;
-	my ($revfound,$parent) = split m/\s+/, $parentline;
-
-	close($revparent);
-
-	return $parent;
-}
-
-sub git_find_all_parents {
-	my ($rev) = @_;
-
-	my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev")
-		or die "Failed to open git-rev-list to find a single parent: $!";
-
-	my $parentline = <$revparent>;
-	chomp $parentline;
-	my ($origrev, @parents) = split m/\s+/, $parentline;
-
-	close($revparent);
-
-	return @parents;
-}
-
-sub git_merge_base {
-	my ($rev1, $rev2) = @_;
-
-	my $mb = open_pipe("git-merge-base", $rev1, $rev2)
-	        or die "Failed to open git-merge-base: $!";
-
-	my $base = <$mb>;
-	chomp $base;
-
-	close($mb);
-
-	return $base;
-}
-
-# Construct a set of pseudo parents that are in the same order,
-# and the same quantity as the real parents,
-# but whose SHA1s are as similar to the logical parents
-# as possible.
-sub get_pseudo_parents {
-	my ($all, $fake) = @_;
-
-	my @all = @$all;
-	my @fake = @$fake;
-
-	my @pseudo;
-
-	my %fake = map {$_ => 1} @fake;
-	my %seenfake;
-
-	my $fakeidx = 0;
-	foreach my $p (@all) {
-		if (exists $fake{$p}) {
-			if ($fake[$fakeidx] ne $p) {
-				die sprintf("parent mismatch: %s != %s\nall:%s\nfake:%s\n",
-					    $fake[$fakeidx], $p,
-					    join(", ", @all),
-					    join(", ", @fake),
-					   );
-			}
-
-			push @pseudo, $p;
-			$fakeidx++;
-			$seenfake{$p}++;
-
-		} else {
-			my $base = git_merge_base($fake[$fakeidx], $p);
-			if ($base ne $fake[$fakeidx]) {
-				die sprintf("Result of merge-base doesn't match fake: %s,%s != %s\n",
-				       $fake[$fakeidx], $p, $base);
-			}
-
-			# The details of how we parse the diffs
-			# mean that we cannot have a duplicate
-			# revision in the list, so if we've already
-			# seen the revision we would normally add, just use
-			# the actual revision.
-			if ($seenfake{$base}) {
-				push @pseudo, $p;
-			} else {
-				push @pseudo, $base;
-				$seenfake{$base}++;
-			}
-		}
-	}
-
-	return @pseudo;
-}
-
-
-# Get a diff between the current revision and a parent.
-# Record the commit information that results.
-sub git_diff_parse {
-	my ($parents, $rev, %revinfo) = @_;
-
-	my @pseudo_parents;
-	my @command = ("git-diff-tree");
-	my $revision_spec;
-
-	if (scalar @$parents == 1) {
-
-		$revision_spec = join("..", $parents->[0], $rev);
-		@pseudo_parents = @$parents;
-	} else {
-		my @all_parents = git_find_all_parents($rev);
-
-		if (@all_parents !=  @$parents) {
-			@pseudo_parents = get_pseudo_parents(\@all_parents, $parents);
-		} else {
-			@pseudo_parents = @$parents;
-		}
-
-		$revision_spec = $rev;
-		push @command, "-c";
-	}
-
-	my @filenames = ( $revs{$rev}{'filename'} );
-
-	foreach my $parent (@$parents) {
-		push @filenames, $revs{$parent}{'filename'};
-	}
-
-	push @command, "-p", "-M", $revision_spec, "--", @filenames;
-
-
-	my $diff = open_pipe( @command )
-		or die "Failed to call git-diff for annotation: $!";
-
-	_git_diff_parse($diff, \@pseudo_parents, $rev, %revinfo);
-
-	close($diff);
-}
-
-sub _git_diff_parse {
-	my ($diff, $parents, $rev, %revinfo) = @_;
-
-	my $ri = 0;
-
-	my $slines = $revs{$rev}{'lines'};
-	my (%plines, %pi);
-
-	my $gotheader = 0;
-	my ($remstart);
-	my $parent_count = @$parents;
-
-	my $diff_header_regexp = "^@";
-	$diff_header_regexp .= "@" x @$parents;
-	$diff_header_regexp .= ' -\d+,\d+' x @$parents;
-	$diff_header_regexp .= ' \+(\d+),\d+';
-	$diff_header_regexp .= " " . ("@" x @$parents);
-
-	my %claim_regexps;
-	my $allparentplus = '^' . '\\+' x @$parents . '(.*)$';
-
-	{
-		my $i = 0;
-		foreach my $parent (@$parents) {
-
-			$pi{$parent} = 0;
-			my $r = '^' . '.' x @$parents . '(.*)$';
-			my $p = $r;
-			substr($p,$i+1, 1) = '\\+';
-
-			my $m = $r;
-			substr($m,$i+1, 1) = '-';
-
-			$claim_regexps{$parent}{plus} = $p;
-			$claim_regexps{$parent}{minus} = $m;
-
-			$plines{$parent} = [];
-
-			$i++;
-		}
-	}
-
-	DIFF:
-	while(<$diff>) {
-		chomp;
-		#printf("%d:%s:\n", $gotheader, $_);
-		if (m/$diff_header_regexp/) {
-			$remstart = $1 - 1;
-			# (0-based arrays)
-
-			$gotheader = 1;
-
-			foreach my $parent (@$parents) {
-				for (my $i = $ri; $i < $remstart; $i++) {
-					$plines{$parent}[$pi{$parent}++] = $slines->[$i];
-				}
-			}
-			$ri = $remstart;
-
-			next DIFF;
-
-		} elsif (!$gotheader) {
-			# Skip over the leadin.
-			next DIFF;
-		}
-
-		if (m/^\\/) {
-			;
-			# Skip \No newline at end of file.
-			# But this can be internationalized, so only look
-			# for an initial \
-
-		} else {
-			my %claims = ();
-			my $negclaim = 0;
-			my $allclaimed = 0;
-			my $line;
-
-			if (m/$allparentplus/) {
-				claim_line($ri, $rev, $slines, %revinfo);
-				$allclaimed = 1;
-
-			}
-
-			PARENT:
-			foreach my $parent (keys %claim_regexps) {
-				my $m = $claim_regexps{$parent}{minus};
-				my $p = $claim_regexps{$parent}{plus};
-
-				if (m/$m/) {
-					$line = $1;
-					$plines{$parent}[$pi{$parent}++] = [ $line, '', '', '', 0 ];
-					$negclaim++;
-
-				} elsif (m/$p/) {
-					$line = $1;
-					if (get_line($slines, $ri) eq $line) {
-						# Found a match, claim
-						$claims{$parent}++;
-
-					} else {
-						die sprintf("Sync error: %d\n|%s\n|%s\n%s => %s\n",
-								$ri, $line,
-								get_line($slines, $ri),
-								$rev, $parent);
-					}
-				}
-			}
-
-			if (%claims) {
-				foreach my $parent (@$parents) {
-					next if $claims{$parent} || $allclaimed;
-					$plines{$parent}[$pi{$parent}++] = $slines->[$ri];
-					    #[ $line, '', '', '', 0 ];
-				}
-				$ri++;
-
-			} elsif ($negclaim) {
-				next DIFF;
-
-			} else {
-				if (substr($_,scalar @$parents) ne get_line($slines,$ri) ) {
-				        foreach my $parent (@$parents) {
-						printf("parent %s is on line %d\n", $parent, $pi{$parent});
-					}
-
-					my @context;
-					for (my $i = -2; $i < 2; $i++) {
-						push @context, get_line($slines, $ri + $i);
-					}
-					my $context = join("\n", @context);
-
-					my $justline = substr($_, scalar @$parents);
-					die sprintf("Line %d, does not match:\n|%s|\n|%s|\n%s\n",
-						    $ri,
-						    $justline,
-						    $context);
-				}
-				foreach my $parent (@$parents) {
-					$plines{$parent}[$pi{$parent}++] = $slines->[$ri];
-				}
-				$ri++;
-			}
-		}
-	}
-
-	for (my $i = $ri; $i < @{$slines} ; $i++) {
-		foreach my $parent (@$parents) {
-			push @{$plines{$parent}}, $slines->[$ri];
-		}
-		$ri++;
-	}
-
-	foreach my $parent (@$parents) {
-		$revs{$parent}{lines} = $plines{$parent};
-	}
-
-	return;
-}
-
-sub get_line {
-	my ($lines, $index) = @_;
-
-	return ref $lines->[$index] ne '' ? $lines->[$index][0] : $lines->[$index];
-}
-
-sub git_cat_file {
-	my ($rev, $filename) = @_;
-	return () unless defined $rev && defined $filename;
-
-	my $blob = git_ls_tree($rev, $filename);
-	die "Failed to find a blob for $filename in rev $rev\n" if !defined $blob;
-
-	my $catfile = open_pipe("git","cat-file", "blob", $blob)
-		or die "Failed to git-cat-file blob $blob (rev $rev, file $filename): " . $!;
-
-	my @lines;
-	while(<$catfile>) {
-		chomp;
-		push @lines, $_;
-	}
-	close($catfile);
-
-	return @lines;
-}
-
-sub git_ls_tree {
-	my ($rev, $filename) = @_;
-
-	my $lstree = open_pipe("git","ls-tree",$rev,$filename)
-		or die "Failed to call git ls-tree: $!";
-
-	my ($mode, $type, $blob, $tfilename);
-	while(<$lstree>) {
-		chomp;
-		($mode, $type, $blob, $tfilename) = split(/\s+/, $_, 4);
-		last if ($tfilename eq $filename);
-	}
-	close($lstree);
-
-	return $blob if ($tfilename eq $filename);
-	die "git-ls-tree failed to find blob for $filename";
-
-}
-
-
-
-sub claim_line {
-	my ($floffset, $rev, $lines, %revinfo) = @_;
-	my $oline = get_line($lines, $floffset);
-	@{$lines->[$floffset]} = ( $oline, $rev,
-		$revinfo{'author'}, $revinfo{'author_date'} );
-	#printf("Claiming line %d with rev %s: '%s'\n",
-	#		$floffset, $rev, $oline) if 1;
-}
-
-sub git_commit_info {
-	my ($rev) = @_;
-	my $commit = open_pipe("git-cat-file", "commit", $rev)
-		or die "Failed to call git-cat-file: $!";
-
-	my %info;
-	while(<$commit>) {
-		chomp;
-		last if (length $_ == 0);
-
-		if (m/^author (.*) <(.*)> (.*)$/) {
-			$info{'author'} = $1;
-			$info{'author_email'} = $2;
-			$info{'author_date'} = $3;
-		} elsif (m/^committer (.*) <(.*)> (.*)$/) {
-			$info{'committer'} = $1;
-			$info{'committer_email'} = $2;
-			$info{'committer_date'} = $3;
-		}
-	}
-	close($commit);
-
-	return %info;
-}
-
-sub format_date {
-	if ($rawtime) {
-		return $_[0];
-	}
-	my ($timestamp, $timezone) = split(' ', $_[0]);
-	my $minutes = abs($timezone);
-	$minutes = int($minutes / 100) * 60 + ($minutes % 100);
-	if ($timezone < 0) {
-	    $minutes = -$minutes;
-	}
-	my $t = $timestamp + $minutes * 60;
-	return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($t));
-}
-
-# Copied from git-send-email.perl - We need a Git.pm module..
-sub gitvar {
-    my ($var) = @_;
-    my $fh;
-    my $pid = open($fh, '-|');
-    die "$!" unless defined $pid;
-    if (!$pid) {
-	exec('git-var', $var) or die "$!";
-    }
-    my ($val) = <$fh>;
-    close $fh or die "$!";
-    chomp($val);
-    return $val;
-}
-
-sub gitvar_name {
-    my ($name) = @_;
-    my $val = gitvar($name);
-    my @field = split(/\s+/, $val);
-    return join(' ', @field[0...(@field-4)]);
-}
-
-sub open_pipe {
-	if ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {
-		return open_pipe_activestate(@_);
-	} else {
-		return open_pipe_normal(@_);
-	}
-}
-
-sub open_pipe_activestate {
-	tie *fh, "Git::ActiveStatePipe", @_;
-	return *fh;
-}
-
-sub open_pipe_normal {
-	my (@execlist) = @_;
-
-	my $pid = open my $kid, "-|";
-	defined $pid or die "Cannot fork: $!";
-
-	unless ($pid) {
-		exec @execlist;
-		die "Cannot exec @execlist: $!";
-	}
-
-	return $kid;
-}
-
-package Git::ActiveStatePipe;
-use strict;
-
-sub TIEHANDLE {
-	my ($class, @params) = @_;
-	my $cmdline = join " ", @params;
-	my  @data = qx{$cmdline};
-	bless { i => 0, data => \@data }, $class;
-}
-
-sub READLINE {
-	my $self = shift;
-	if ($self->{i} >= scalar @{$self->{data}}) {
-		return undef;
-	}
-	return $self->{'data'}->[ $self->{i}++ ];
-}
-
-sub CLOSE {
-	my $self = shift;
-	delete $self->{data};
-	delete $self->{i};
-}
-
-sub EOF {
-	my $self = shift;
-	return ($self->{i} >= scalar @{$self->{data}});
-}
diff --git a/git-bisect.sh b/git-bisect.sh
index 06a8d26..6da31e8 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -179,11 +179,12 @@
         *)
 	    usage ;;
 	esac
-	git checkout "$branch" &&
-	rm -fr "$GIT_DIR/refs/bisect"
-	rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
-	rm -f "$GIT_DIR/BISECT_LOG"
-	rm -f "$GIT_DIR/BISECT_NAMES"
+	if git checkout "$branch"; then
+		rm -fr "$GIT_DIR/refs/bisect"
+		rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
+		rm -f "$GIT_DIR/BISECT_LOG"
+		rm -f "$GIT_DIR/BISECT_NAMES"
+	fi
 }
 
 bisect_replay () {
diff --git a/git-branch.sh b/git-branch.sh
deleted file mode 100755
index f823c78..0000000
--- a/git-branch.sh
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/bin/sh
-
-USAGE='[-l] [-f] <branchname> [<start-point>] | (-d | -D) <branchname> | [-r]'
-LONG_USAGE='If no arguments, show available branches and mark current branch with a star.
-If one argument, create a new branch <branchname> based off of current HEAD.
-If two arguments, create a new branch <branchname> based off of <start-point>.'
-
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
-
-delete_branch () {
-    option="$1"
-    shift
-    for branch_name
-    do
-	case ",$headref," in
-	",$branch_name,")
-	    die "Cannot delete the branch you are on." ;;
-	,,)
-	    die "What branch are you on anyway?" ;;
-	esac
-	branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
-	    branch=$(git-rev-parse --verify "$branch^0") ||
-		die "Seriously, what branch are you talking about?"
-	case "$option" in
-	-D)
-	    ;;
-	*)
-	    mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ')
-	    case " $mbs " in
-	    *' '$branch' '*)
-		# the merge base of branch and HEAD contains branch --
-		# which means that the HEAD contains everything in both.
-		;;
-	    *)
-		echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
-If you are sure you want to delete it, run 'git branch -D $branch_name'."
-		exit 1
-		;;
-	    esac
-	    ;;
-	esac
-	rm -f "$GIT_DIR/logs/refs/heads/$branch_name"
-	rm -f "$GIT_DIR/refs/heads/$branch_name"
-	echo "Deleted branch $branch_name."
-    done
-    exit 0
-}
-
-ls_remote_branches () {
-    git-rev-parse --symbolic --all |
-    sed -ne 's|^refs/\(remotes/\)|\1|p' |
-    sort
-}
-
-force=
-create_log=
-while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
-do
-	case "$1" in
-	-d | -D)
-		delete_branch "$@"
-		exit
-		;;
-	-r)
-		ls_remote_branches
-		exit
-		;;
-	-f)
-		force="$1"
-		;;
-	-l)
-		create_log="yes"
-		;;
-	--)
-		shift
-		break
-		;;
-	-*)
-		usage
-		;;
-	esac
-	shift
-done
-
-case "$#" in
-0)
-	git-rev-parse --symbolic --branches |
-	sort |
-	while read ref
-	do
-		if test "$headref" = "$ref"
-		then
-			pfx='*'
-		else
-			pfx=' '
-		fi
-		echo "$pfx $ref"
-	done
-	exit 0 ;;
-1)
-	head=HEAD ;;
-2)
-	head="$2^0" ;;
-esac
-branchname="$1"
-
-rev=$(git-rev-parse --verify "$head") || exit
-
-git-check-ref-format "heads/$branchname" ||
-	die "we do not like '$branchname' as a branch name."
-
-if [ -d "$GIT_DIR/refs/heads/$branchname" ]
-then
-	for refdir in `cd "$GIT_DIR" && \
-		find "refs/heads/$branchname" -type d | sort -r`
-	do
-		rmdir "$GIT_DIR/$refdir" || \
-		    die "Could not delete '$refdir', there may still be a ref there."
-	done
-fi
-
-if [ -e "$GIT_DIR/refs/heads/$branchname" ]
-then
-	if test '' = "$force"
-	then
-		die "$branchname already exists."
-	elif test "$branchname" = "$headref"
-	then
-		die "cannot force-update the current branch."
-	fi
-fi
-if test "$create_log" = 'yes'
-then
-	mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$branchname")
-	touch "$GIT_DIR/logs/refs/heads/$branchname"
-fi
-git update-ref -m "branch: Created from $head" "refs/heads/$branchname" $rev
diff --git a/git-checkout.sh b/git-checkout.sh
index dd47724..119bca1 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -22,7 +22,7 @@
 		shift
 		[ -z "$newbranch" ] &&
 			die "git checkout: -b needs a branch name"
-		[ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
+		git-show-ref --verify --quiet -- "refs/heads/$newbranch" &&
 			die "git checkout: branch $newbranch already exists"
 		git-check-ref-format "heads/$newbranch" ||
 			die "git checkout: we do not like '$newbranch' as a branch name."
@@ -51,7 +51,8 @@
 			fi
 			new="$rev"
 			new_name="$arg^0"
-			if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
+			if git-show-ref --verify --quiet -- "refs/heads/$arg"
+			then
 				branch="$arg"
 			fi
 		elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
diff --git a/git-cherry.sh b/git-cherry.sh
deleted file mode 100755
index cf7af55..0000000
--- a/git-cherry.sh
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-USAGE='[-v] <upstream> [<head>] [<limit>]'
-LONG_USAGE='             __*__*__*__*__> <upstream>
-            /
-  fork-point
-            \__+__+__+__+__+__+__+__> <head>
-
-Each commit between the fork-point (or <limit> if given) and <head> is
-examined, and compared against the change each commit between the
-fork-point and <upstream> introduces.  If the change seems to be in
-the upstream, it is shown on the standard output with prefix "-".
-Otherwise it is shown with prefix "+".'
-. git-sh-setup
-
-case "$1" in -v) verbose=t; shift ;; esac 
-
-case "$#,$1" in
-1,*..*)
-    upstream=$(expr "z$1" : 'z\(.*\)\.\.') ours=$(expr "z$1" : '.*\.\.\(.*\)$')
-    set x "$upstream" "$ours"
-    shift ;;
-esac
-
-case "$#" in
-1) upstream=`git-rev-parse --verify "$1"` &&
-   ours=`git-rev-parse --verify HEAD` || exit
-   limit="$upstream"
-   ;;
-2) upstream=`git-rev-parse --verify "$1"` &&
-   ours=`git-rev-parse --verify "$2"` || exit
-   limit="$upstream"
-   ;;
-3) upstream=`git-rev-parse --verify "$1"` &&
-   ours=`git-rev-parse --verify "$2"` &&
-   limit=`git-rev-parse --verify "$3"` || exit
-   ;;
-*) usage ;;
-esac
-
-# Note that these list commits in reverse order;
-# not that the order in inup matters...
-inup=`git-rev-list ^$ours $upstream` &&
-ours=`git-rev-list $ours ^$limit` || exit
-
-tmp=.cherry-tmp$$
-patch=$tmp-patch
-mkdir $patch
-trap "rm -rf $tmp-*" 0 1 2 3 15
-
-for c in $inup
-do
-	git-diff-tree -p $c
-done | git-patch-id |
-while read id name
-do
-	echo $name >>$patch/$id
-done
-
-LF='
-'
-
-O=
-for c in $ours
-do
-	set x `git-diff-tree -p $c | git-patch-id`
-	if test "$2" != ""
-	then
-		if test -f "$patch/$2"
-		then
-			sign=-
-		else
-			sign=+
-		fi
-		case "$verbose" in
-		t)
-			c=$(git-rev-list --pretty=oneline --max-count=1 $c)
-		esac
-		case "$O" in
-		'')	O="$sign $c" ;;
-		*)	O="$sign $c$LF$O" ;;
-		esac
-	fi
-done
-case "$O" in
-'') ;;
-*)  echo "$O" ;;
-esac
diff --git a/git-clone.sh b/git-clone.sh
index 24b1195..3f006d1 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -8,11 +8,15 @@
 # See git-sh-setup why.
 unset CDPATH
 
-usage() {
-	echo >&2 "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+die() {
+	echo >&2 "$@"
 	exit 1
 }
 
+usage() {
+	die "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+}
+
 get_repo_base() {
 	(cd "$1" && (cd .git ; pwd)) 2> /dev/null
 }
@@ -35,11 +39,9 @@
 		"`git-repo-config --bool http.noEPSV`" = true ]; then
 		curl_extra_args="${curl_extra_args} --disable-epsv"
 	fi
-	http_fetch "$1/info/refs" "$clone_tmp/refs" || {
-		echo >&2 "Cannot get remote repository information.
+	http_fetch "$1/info/refs" "$clone_tmp/refs" ||
+		die "Cannot get remote repository information.
 Perhaps git-update-server-info needs to be run there?"
-		exit 1;
-	}
 	while read sha1 refname
 	do
 		name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
@@ -143,17 +145,12 @@
 		'')
 		    usage ;;
 		*/*)
-		    echo >&2 "'$2' is not suitable for an origin name"
-		    exit 1
+		    die "'$2' is not suitable for an origin name"
 		esac
-		git-check-ref-format "heads/$2" || {
-		    echo >&2 "'$2' is not suitable for a branch name"
-		    exit 1
-		}
-		test -z "$origin_override" || {
-		    echo >&2 "Do not give more than one --origin options."
-		    exit 1
-		}
+		git-check-ref-format "heads/$2" ||
+		    die "'$2' is not suitable for a branch name"
+		test -z "$origin_override" ||
+		    die "Do not give more than one --origin options."
 		origin_override=yes
 		origin="$2"; shift
 		;;
@@ -169,24 +166,19 @@
 done
 
 repo="$1"
-if test -z "$repo"
-then
-    echo >&2 'you must specify a repository to clone.'
-    exit 1
-fi
+test -n "$repo" ||
+    die 'you must specify a repository to clone.'
 
 # --bare implies --no-checkout
 if test yes = "$bare"
 then
 	if test yes = "$origin_override"
 	then
-		echo >&2 '--bare and --origin $origin options are incompatible.'
-		exit 1
+		die '--bare and --origin $origin options are incompatible.'
 	fi
 	if test t = "$use_separate_remote"
 	then
-		echo >&2 '--bare and --use-separate-remote options are incompatible.'
-		exit 1
+		die '--bare and --use-separate-remote options are incompatible.'
 	fi
 	no_checkout=yes
 fi
@@ -206,7 +198,7 @@
 dir="$2"
 # Try using "humanish" part of source repo if user didn't specify one
 [ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-[ -e "$dir" ] && echo "$dir already exists." && usage
+[ -e "$dir" ] && die "destination directory '$dir' already exists."
 mkdir -p "$dir" &&
 D=$(cd "$dir" && pwd) &&
 trap 'err=$?; cd ..; rm -rf "$D"; exit $err' 0
@@ -233,7 +225,7 @@
 		 cd reference-tmp &&
 		 tar xf -)
 	else
-		echo >&2 "$reference: not a local directory." && usage
+		die "reference repository '$reference' is not a local directory."
 	fi
 fi
 
@@ -242,10 +234,8 @@
 # We do local magic only when the user tells us to.
 case "$local,$use_local" in
 yes,yes)
-	( cd "$repo/objects" ) || {
-		echo >&2 "-l flag seen but $repo is not local."
-		exit 1
-	}
+	( cd "$repo/objects" ) ||
+		die "-l flag seen but repository '$repo' is not local."
 
 	case "$local_shared" in
 	no)
@@ -307,18 +297,15 @@
 		then
 			clone_dumb_http "$repo" "$D"
 		else
-			echo >&2 "http transport not supported, rebuild Git with curl support"
-			exit 1
+			die "http transport not supported, rebuild Git with curl support"
 		fi
 		;;
 	*)
 		case "$upload_pack" in
 		'') git-fetch-pack --all -k $quiet "$repo" ;;
 		*) git-fetch-pack --all -k $quiet "$upload_pack" "$repo" ;;
-		esac >"$GIT_DIR/CLONE_HEAD" || {
-			echo >&2 "fetch-pack from '$repo' failed."
-			exit 1
-		}
+		esac >"$GIT_DIR/CLONE_HEAD" ||
+			die "fetch-pack from '$repo' failed."
 		;;
 	esac
 	;;
diff --git a/git-commit.sh b/git-commit.sh
index 5b1cf85..81c3a0c 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -441,7 +441,7 @@
 elif test "$use_commit" != ""
 then
 	git-cat-file commit "$use_commit" | sed -e '1,/^$/d'
-elif test -f "$GIT_DIR/MERGE_HEAD" && test -f "$GIT_DIR/MERGE_MSG"
+elif test -f "$GIT_DIR/MERGE_MSG"
 then
 	cat "$GIT_DIR/MERGE_MSG"
 elif test -f "$GIT_DIR/SQUASH_MSG"
@@ -522,15 +522,15 @@
 		PARENTS=$(git-cat-file commit HEAD |
 			sed -n -e '/^$/q' -e 's/^parent /-p /p')
 	fi
-	current=$(git-rev-parse --verify HEAD)
+	current="$(git-rev-parse --verify HEAD)"
 else
 	if [ -z "$(git-ls-files)" ]; then
 		echo >&2 Nothing to commit
 		exit 1
 	fi
 	PARENTS=""
-	current=
 	rloga='commit (initial)'
+	current=''
 fi
 
 if test -z "$no_edit"
@@ -606,8 +606,8 @@
 	fi &&
 	commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
 	rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
-	git-update-ref -m "$rloga: $rlogm" HEAD $commit $current &&
-	rm -f -- "$GIT_DIR/MERGE_HEAD" &&
+	git-update-ref -m "$rloga: $rlogm" HEAD $commit "$current" &&
+	rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
 	if test -f "$NEXT_INDEX"
 	then
 		mv "$NEXT_INDEX" "$THIS_INDEX"
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index e5a00a1..14e2c61 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -495,22 +495,17 @@
 	$tip_at_start = `git-rev-parse --verify HEAD`;
 
 	# Get the last import timestamps
-	opendir(D,"$git_dir/refs/heads");
-	while(defined(my $head = readdir(D))) {
-		next if $head =~ /^\./;
-		open(F,"$git_dir/refs/heads/$head")
-			or die "Bad head branch: $head: $!\n";
-		chomp(my $ftag = <F>);
-		close(F);
-		open(F,"git-cat-file commit $ftag |");
-		while(<F>) {
-			next unless /^author\s.*\s(\d+)\s[-+]\d{4}$/;
-			$branch_date{$head} = $1;
-			last;
-		}
-		close(F);
+	my $fmt = '($ref, $author) = (%(refname), %(author));';
+	open(H, "git-for-each-ref --perl --format='$fmt' refs/heads |") or
+		die "Cannot run git-for-each-ref: $!\n";
+	while(defined(my $entry = <H>)) {
+		my ($ref, $author);
+		eval($entry) || die "cannot eval refs list: $@";
+		my ($head) = ($ref =~ m|^refs/heads/(.*)|);
+		$author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
+		$branch_date{$head} = $1;
 	}
-	closedir(D);
+	close(H);
 }
 
 -d $git_dir
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 053d0d9..ca519b7 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -2118,9 +2118,17 @@
                 mode       TEXT NOT NULL
             )
         ");
+        $self->{dbh}->do("
+            CREATE INDEX revision_ix1
+            ON revision (name,revision)
+        ");
+        $self->{dbh}->do("
+            CREATE INDEX revision_ix2
+            ON revision (name,commithash)
+        ");
     }
 
-    # Construct the revision table if required
+    # Construct the head table if required
     unless ( $self->{tables}{head} )
     {
         $self->{dbh}->do("
@@ -2134,6 +2142,10 @@
                 mode       TEXT NOT NULL
             )
         ");
+        $self->{dbh}->do("
+            CREATE INDEX head_ix1
+            ON head (name)
+        ");
     }
 
     # Construct the properties table if required
diff --git a/git-fetch.sh b/git-fetch.sh
index a674c8c..7442dd2 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -20,7 +20,7 @@
 update_head_ok=
 exec=
 upload_pack=
-keep=--thin
+keep=
 while case "$#" in 0) break ;; esac
 do
 	case "$1" in
@@ -51,7 +51,7 @@
 		verbose=Yes
 		;;
 	-k|--k|--ke|--kee|--keep)
-		keep=--keep
+		keep='-k -k'
 		;;
 	--reflog-action=*)
 		rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`
@@ -129,38 +129,44 @@
     then
 	headc_=$(git-rev-parse --verify "$head_^0") || exit
 	echo "$headc_	$not_for_merge_	$note_" >>"$GIT_DIR/FETCH_HEAD"
-	[ "$verbose" ] && echo >&2 "* committish: $head_"
-	[ "$verbose" ] && echo >&2 "  $note_"
     else
 	echo "$head_	not-for-merge	$note_" >>"$GIT_DIR/FETCH_HEAD"
-	[ "$verbose" ] && echo >&2 "* non-commit: $head_"
-	[ "$verbose" ] && echo >&2 "  $note_"
     fi
-    if test "$local_name_" != ""
-    then
-	# We are storing the head locally.  Make sure that it is
-	# a fast forward (aka "reverse push").
-	fast_forward_local "$local_name_" "$head_" "$note_"
-    fi
+
+    update_local_ref "$local_name_" "$head_" "$note_"
 }
 
-fast_forward_local () {
-    mkdir -p "$(dirname "$GIT_DIR/$1")"
+update_local_ref () {
+    # If we are storing the head locally make sure that it is
+    # a fast forward (aka "reverse push").
+
+    label_=$(git-cat-file -t $2)
+    newshort_=$(git-rev-parse --short $2)
+    if test -z "$1" ; then
+	[ "$verbose" ] && echo >&2 "* fetched $3"
+	[ "$verbose" ] && echo >&2 "  $label_: $newshort_"
+	return 0
+    fi
+    oldshort_=$(git show-ref --hash --abbrev "$1" 2>/dev/null)
+
     case "$1" in
     refs/tags/*)
 	# Tags need not be pointing at commits so there
 	# is no way to guarantee "fast-forward" anyway.
-	if test -f "$GIT_DIR/$1"
+	if test -n "$oldshort_"
 	then
-		if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
+		if now_=$(git show-ref --hash "$1") && test "$now_" = "$2"
 		then
-			[ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
+			[ "$verbose" ] && echo >&2 "* $1: same as $3"
+			[ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
 		else
 			echo >&2 "* $1: updating with $3"
+			echo >&2 "  $label_: $newshort_"
 			git-update-ref -m "$rloga: updating tag" "$1" "$2"
 		fi
 	else
 		echo >&2 "* $1: storing $3"
+		echo >&2 "  $label_: $newshort_"
 		git-update-ref -m "$rloga: storing tag" "$1" "$2"
 	fi
 	;;
@@ -178,31 +184,34 @@
 	        if test -n "$verbose"
 		then
 			echo >&2 "* $1: same as $3"
+			echo >&2 "  $label_: $newshort_"
 		fi
 		;;
 	    *,$local)
 		echo >&2 "* $1: fast forward to $3"
-		echo >&2 "  from $local to $2"
+		echo >&2 "  old..new: $oldshort_..$newshort_"
 		git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
 		;;
 	    *)
 		false
 		;;
 	    esac || {
-		echo >&2 "* $1: does not fast forward to $3;"
 		case ",$force,$single_force," in
 		*,t,*)
-			echo >&2 "  forcing update."
+			echo >&2 "* $1: forcing update to non-fast forward $3"
+			echo >&2 "  old...new: $oldshort_...$newshort_"
 			git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
 			;;
 		*)
-			echo >&2 "  not updating."
+			echo >&2 "* $1: not updating to non-fast forward $3"
+			echo >&2 "  old...new: $oldshort_...$newshort_"
 			exit 1
 			;;
 		esac
 	    }
 	else
 	    echo >&2 "* $1: storing $3"
+	    echo >&2 "  $label_: $newshort_"
 	    git-update-ref -m "$rloga: storing head" "$1" "$2"
 	fi
 	;;
@@ -359,9 +368,10 @@
       ;; # we are already done.
   *)
     ( : subshell because we muck with IFS
+      pack_lockfile=
       IFS=" 	$LF"
       (
-	  git-fetch-pack $exec $keep "$remote" $rref || echo failed "$remote"
+	  git-fetch-pack --thin $exec $keep "$remote" $rref || echo failed "$remote"
       ) |
       while read sha1 remote_name
       do
@@ -369,6 +379,12 @@
 	  failed)
 		  echo >&2 "Fetch failure: $remote"
 		  exit 1 ;;
+	  # special line coming from index-pack with the pack name
+	  pack)
+		  continue ;;
+	  keep)
+		  pack_lockfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep"
+		  continue ;;
 	  esac
 	  found=
 	  single_force=
@@ -399,6 +415,7 @@
 	  append_fetch_head "$sha1" "$remote" \
 		  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
       done
+      if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi
     ) || exit ;;
   esac
 
@@ -418,7 +435,7 @@
 		sed -ne 's|^\([0-9a-f]*\)[ 	]\(refs/tags/.*\)^{}$|\1 \2|p' |
 		while read sha1 name
 		do
-			test -f "$GIT_DIR/$name" && continue
+			git-show-ref --verify --quiet -- $name && continue
 			git-check-ref-format "$name" || {
 				echo >&2 "warning: tag ${name} ignored"
 				continue
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index fba4b0c..c49e4c6 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -23,6 +23,12 @@
 "$1.." | "$1.$1" | "$1$1.")
 	if [ "$2" ]; then
 		echo "Removing $4"
+	else
+		# read-tree checked that index matches HEAD already,
+		# so we know we do not have this path tracked.
+		# there may be an unrelated working tree file here,
+		# which we should just leave unmolested.
+		exit 0
 	fi
 	if test -f "$4"; then
 		rm -f -- "$4" &&
@@ -34,8 +40,16 @@
 #
 # Added in one.
 #
-".$2." | "..$3" )
+".$2.")
+	# the other side did not add and we added so there is nothing
+	# to be done.
+	;;
+"..$3")
 	echo "Adding $4"
+	test -f "$4" || {
+		echo "ERROR: untracked $4 is overwritten by the merge."
+		exit 1
+	}
 	git-update-index --add --cacheinfo "$6$7" "$2$3" "$4" &&
 		exec git-checkout-index -u -f -- "$4"
 	;;
diff --git a/git-merge.sh b/git-merge.sh
index 789f4de..cb09438 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -199,7 +199,7 @@
 	;;
 ?,1,"$head",*)
 	# Again the most common case of merging one remote.
-	echo "Updating from $head to $1"
+	echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
 	git-update-index --refresh 2>/dev/null
 	new_head=$(git-rev-parse --verify "$1^0") &&
 	git-read-tree -u -v -m $head "$new_head" &&
diff --git a/git-rebase.sh b/git-rebase.sh
index 413636e..546fa44 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--onto <newbase>] <upstream> [<branch>]'
+USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -39,6 +39,7 @@
 do_merge=
 dotest=$GIT_DIR/.dotest-merge
 prec=4
+verbose=
 
 continue_merge () {
 	test -n "$prev_head" || die "prev_head must be defined"
@@ -190,6 +191,9 @@
 		esac
 		do_merge=t
 		;;
+	-v|--verbose)
+		verbose=t
+		;;
 	-*)
 		usage
 		;;
@@ -273,6 +277,12 @@
 	exit 0
 fi
 
+if test -n "$verbose"
+then
+	echo "Changes from $mb to $onto:"
+	git-diff-tree --stat --summary "$mb" "$onto"
+fi
+
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
 git-reset --hard "$onto"
 
diff --git a/git-repack.sh b/git-repack.sh
index f2c9071..f150a55 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Linus Torvalds
 #
 
-USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
+USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
@@ -25,6 +25,15 @@
 	shift
 done
 
+# Later we will default repack.UseDeltaBaseOffset to true
+default_dbo=false
+
+case "`git repo-config --bool repack.usedeltabaseoffset ||
+       echo $default_dbo`" in
+true)
+	extra="$extra --delta-base-offset" ;;
+esac
+
 PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
 PACKTMP="$GIT_DIR/.tmp-$$-pack"
 rm -f "$PACKTMP"-*
@@ -36,11 +45,19 @@
 	args='--unpacked --incremental'
 	;;
 ,t,)
-	args=
-
-	# Redundancy check in all-into-one case is trivial.
-	existing=`test -d "$PACKDIR" && cd "$PACKDIR" && \
-	    find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
+	if [ -d "$PACKDIR" ]; then
+		for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \
+			| sed -e 's/^\.\///' -e 's/\.pack$//'`
+		do
+			if [ -e "$PACKDIR/$e.keep" ]; then
+				: keep
+			else
+				args="$args --unpacked=$e.pack"
+				existing="$existing $e"
+			fi
+		done
+	fi
+	[ -z "$args" ] && args='--unpacked --incremental'
 	;;
 esac
 
@@ -77,17 +94,16 @@
 
 if test "$remove_redundant" = t
 then
-	# We know $existing are all redundant only when
-	# all-into-one is used.
-	if test "$all_into_one" != '' && test "$existing" != ''
+	# We know $existing are all redundant.
+	if [ -n "$existing" ]
 	then
 		sync
 		( cd "$PACKDIR" &&
 		  for e in $existing
 		  do
 			case "$e" in
-			./pack-$name.pack | ./pack-$name.idx) ;;
-			*)	rm -f $e ;;
+			pack-$name) ;;
+			*)	rm -f "$e.pack" "$e.idx" "$e.keep" ;;
 			esac
 		  done
 		)
diff --git a/git-resolve.sh b/git-resolve.sh
index 729ec65..36b90e3 100755
--- a/git-resolve.sh
+++ b/git-resolve.sh
@@ -46,7 +46,7 @@
 	exit 0
 	;;
 "$head")
-	echo "Updating from $head to $merge"
+	echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
 	git-read-tree -u -m $head $merge || exit 1
 	git-update-ref -m "resolve $merge_name: Fast forward" \
 		HEAD "$merge" "$head"
diff --git a/git-revert.sh b/git-revert.sh
index 4fd81b6..6eab3c7 100755
--- a/git-revert.sh
+++ b/git-revert.sh
@@ -145,9 +145,18 @@
 result=$(git-write-tree 2>/dev/null) || {
     echo >&2 "Simple $me fails; trying Automatic $me."
     git-merge-index -o git-merge-one-file -a || {
+	    mv -f .msg "$GIT_DIR/MERGE_MSG"
+	    {
+		echo '
+Conflicts:
+'
+		git ls-files --unmerged |
+		sed -e 's/^[^	]*	/	/' |
+		uniq
+	    } >>"$GIT_DIR/MERGE_MSG"
 	    echo >&2 "Automatic $me failed.  After resolving the conflicts,"
 	    echo >&2 "mark the corrected paths with 'git-update-index <paths>'"
-	    echo >&2 "and commit with 'git commit -F .msg'"
+	    echo >&2 "and commit the result."
 	    case "$me" in
 	    cherry-pick)
 		echo >&2 "You may choose to use the following when making"
diff --git a/git-send-email.perl b/git-send-email.perl
index 3f50aba..4c87c20 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -83,11 +83,12 @@
 my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
-my (@to,@cc,@initial_cc,@bcclist,
+my (@to,@cc,@initial_cc,@bcclist,@xh,
 	$initial_reply_to,$initial_subject,@files,$from,$compose,$time);
 
 # Behavior modification variables
-my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0);
+my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
+	$dry_run) = (1, 0, 0, 0, 0);
 my $smtp_server;
 
 # Example reply to:
@@ -116,6 +117,7 @@
 		    "quiet" => \$quiet,
 		    "suppress-from" => \$suppress_from,
 		    "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
+		    "dry-run" => \$dry_run,
 	 );
 
 # Verify the user input
@@ -229,6 +231,9 @@
 }
 
 if (!$smtp_server) {
+	$smtp_server = $repo->config('sendemail.smtpserver');
+}
+if (!$smtp_server) {
 	foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
 		if (-x $_) {
 			$smtp_server = $_;
@@ -409,6 +414,11 @@
 	    $gitversion = Git::version();
 	}
 
+	my ($author_name) = ($from =~ /^(.*?)\s+</);
+	if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
+		my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
+		$from = "\"$name\"$addr";
+	}
 	my $header = "From: $from
 To: $to
 Cc: $cc
@@ -422,8 +432,13 @@
 		$header .= "In-Reply-To: $reply_to\n";
 		$header .= "References: $references\n";
 	}
+	if (@xh) {
+		$header .= join("\n", @xh) . "\n";
+	}
 
-	if ($smtp_server =~ m#^/#) {
+	if ($dry_run) {
+		# We don't want to send the email.
+	} elsif ($smtp_server =~ m#^/#) {
 		my $pid = open my $sm, '|-';
 		defined $pid or die $!;
 		if (!$pid) {
@@ -472,15 +487,22 @@
 
 	my $author_not_sender = undef;
 	@cc = @initial_cc;
-	my $found_mbox = 0;
+	@xh = ();
+	my $input_format = undef;
 	my $header_done = 0;
 	$message = "";
 	while(<F>) {
 		if (!$header_done) {
-			$found_mbox = 1, next if (/^From /);
+			if (/^From /) {
+				$input_format = 'mbox';
+				next;
+			}
 			chomp;
+			if (!defined $input_format && /^[-A-Za-z]+:\s/) {
+				$input_format = 'mbox';
+			}
 
-			if ($found_mbox) {
+			if (defined $input_format && $input_format eq 'mbox') {
 				if (/^Subject:\s+(.*)$/) {
 					$subject = $1;
 
@@ -495,6 +517,9 @@
 						$2, $_) unless $quiet;
 					push @cc, $2;
 				}
+				elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
+					push @xh, $_;
+				}
 
 			} else {
 				# In the traditional
@@ -502,6 +527,7 @@
 				# line 1 = cc
 				# line 2 = subject
 				# So let's support that, too.
+				$input_format = 'lots';
 				if (@cc == 0) {
 					printf("(non-mbox) Adding cc: %s from line '%s'\n",
 						$_, $_) unless $quiet;
diff --git a/git-svnimport.perl b/git-svnimport.perl
index aca0e4f..cbaa8ab 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -31,7 +31,7 @@
 $ENV{'TZ'}="UTC";
 
 our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
-    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F);
+    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F,$opt_P);
 
 sub usage() {
 	print STDERR <<END;
@@ -39,17 +39,19 @@
        [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
-       [-m] [-M regex] [-A author_file] [-S] [-F] [SVN_URL]
+       [-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL]
 END
 	exit(1);
 }
 
-getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:Suv") or usage();
+getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:uv") or usage();
 usage if $opt_h;
 
 my $tag_name = $opt_t || "tags";
 my $trunk_name = $opt_T || "trunk";
 my $branch_name = $opt_b || "branches";
+my $project_name = $opt_P || "";
+$project_name = "/" . $project_name if ($project_name);
 
 @ARGV == 1 or @ARGV == 2 or usage();
 
@@ -193,6 +195,13 @@
 	}
 }
 
+sub dir_list {
+	my($self,$path,$rev) = @_;
+	my ($dirents,undef,$properties)
+	    = $self->{'svn'}->get_dir($path,$rev,undef);
+	return $dirents;
+}
+
 package main;
 use URI;
 
@@ -342,35 +351,16 @@
 
 open BRANCHES,">>", "$git_dir/svn2git";
 
-sub node_kind($$$) {
-	my ($branch, $path, $revision) = @_;
+sub node_kind($$) {
+	my ($svnpath, $revision) = @_;
 	my $pool=SVN::Pool->new;
-	my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);
+	my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool);
 	$pool->clear;
 	return $kind;
 }
 
-sub revert_split_path($$) {
-	my($branch,$path) = @_;
-
-	my $svnpath;
-	$path = "" if $path eq "/"; # this should not happen, but ...
-	if($branch eq "/") {
-		$svnpath = "$trunk_name/$path";
-	} elsif($branch =~ m#^/#) {
-		$svnpath = "$tag_name$branch/$path";
-	} else {
-		$svnpath = "$branch_name/$branch/$path";
-	}
-
-	$svnpath =~ s#/+$##;
-	return $svnpath;
-}
-
 sub get_file($$$) {
-	my($rev,$branch,$path) = @_;
-
-	my $svnpath = revert_split_path($branch,$path);
+	my($svnpath,$rev,$path) = @_;
 
 	# now get it
 	my ($name,$mode);
@@ -413,10 +403,9 @@
 }
 
 sub get_ignore($$$$$) {
-	my($new,$old,$rev,$branch,$path) = @_;
+	my($new,$old,$rev,$path,$svnpath) = @_;
 
 	return unless $opt_I;
-	my $svnpath = revert_split_path($branch,$path);
 	my $name = $svn->ignore("$svnpath",$rev);
 	if ($path eq '/') {
 		$path = $opt_I;
@@ -435,11 +424,25 @@
 		close $F;
 		unlink $name;
 		push(@$new,['0644',$sha,$path]);
-	} else {
+	} elsif (defined $old) {
 		push(@$old,$path);
 	}
 }
 
+sub project_path($$)
+{
+	my ($path, $project) = @_;
+
+	$path = "/".$path unless ($path =~ m#^\/#) ;
+	return $1 if ($path =~ m#^$project\/(.*)$#);
+
+	$path =~ s#\.#\\\.#g;
+	$path =~ s#\+#\\\+#g;
+	return "/" if ($project =~ m#^$path.*$#);
+
+	return undef;
+}
+
 sub split_path($$) {
 	my($rev,$path) = @_;
 	my $branch;
@@ -459,7 +462,11 @@
 		print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path});
 		return ()
 	}
-	$path = "/" if $path eq "";
+	if ($path eq "") {
+		$path = "/";
+	} elsif ($project_name) {
+		$path = project_path($path, $project_name);
+	}
 	return ($branch,$path);
 }
 
@@ -480,6 +487,27 @@
 	return $therev;
 }
 
+sub expand_svndir($$$);
+
+sub expand_svndir($$$)
+{
+	my ($svnpath, $rev, $path) = @_;
+	my @list;
+	get_ignore(\@list, undef, $rev, $path, $svnpath);
+	my $dirents = $svn->dir_list($svnpath, $rev);
+	foreach my $p(keys %$dirents) {
+		my $kind = node_kind($svnpath.'/'.$p, $rev);
+		if ($kind eq $SVN::Node::file) {
+			my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p);
+			push(@list, $f) if $f;
+		} elsif ($kind eq $SVN::Node::dir) {
+			push(@list,
+			     expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p));
+		}
+	}
+	return @list;
+}
+
 sub copy_path($$$$$$$$) {
 	# Somebody copied a whole subdirectory.
 	# We need to find the index entries from the old version which the
@@ -488,8 +516,11 @@
 	my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
 
 	my($srcbranch,$srcpath) = split_path($rev,$oldpath);
-	unless(defined $srcbranch) {
-		print "Path not found when copying from $oldpath @ $rev\n";
+	unless(defined $srcbranch && defined $srcpath) {
+		print "Path not found when copying from $oldpath @ $rev.\n".
+			"Will try to copy from original SVN location...\n"
+			if $opt_v;
+		push (@$new, expand_svndir($oldpath, $rev, $path));
 		return;
 	}
 	my $therev = branch_rev($srcbranch, $rev);
@@ -503,7 +534,7 @@
 	}
 	print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
 	if ($node_kind eq $SVN::Node::dir) {
-			$srcpath =~ s#/*$#/#;
+		$srcpath =~ s#/*$#/#;
 	}
 	
 	my $pid = open my $f,'-|';
@@ -582,10 +613,12 @@
 		if(defined $oldpath) {
 			my $p;
 			($parent,$p) = split_path($revision,$oldpath);
-			if($parent eq "/") {
-				$parent = $opt_o;
-			} else {
-				$parent =~ s#^/##; # if it's a tag
+			if(defined $parent) {
+				if($parent eq "/") {
+					$parent = $opt_o;
+				} else {
+					$parent =~ s#^/##; # if it's a tag
+				}
 			}
 		} else {
 			$parent = undef;
@@ -651,9 +684,10 @@
 				push(@old,$path); # remove any old stuff
 			}
 			if(($action->[0] eq "A") || ($action->[0] eq "R")) {
-				my $node_kind = node_kind($branch,$path,$revision);
+				my $node_kind = node_kind($action->[3], $revision);
 				if ($node_kind eq $SVN::Node::file) {
-					my $f = get_file($revision,$branch,$path);
+					my $f = get_file($action->[3],
+							 $revision, $path);
 					if ($f) {
 						push(@new,$f) if $f;
 					} else {
@@ -668,19 +702,20 @@
 							  \@new, \@parents);
 					} else {
 						get_ignore(\@new, \@old, $revision,
-							   $branch, $path);
+							   $path, $action->[3]);
 					}
 				}
 			} elsif ($action->[0] eq "D") {
 				push(@old,$path);
 			} elsif ($action->[0] eq "M") {
-				my $node_kind = node_kind($branch,$path,$revision);
+				my $node_kind = node_kind($action->[3], $revision);
 				if ($node_kind eq $SVN::Node::file) {
-					my $f = get_file($revision,$branch,$path);
+					my $f = get_file($action->[3],
+							 $revision, $path);
 					push(@new,$f) if $f;
 				} elsif ($node_kind eq $SVN::Node::dir) {
 					get_ignore(\@new, \@old, $revision,
-						   $branch,$path);
+						   $path, $action->[3]);
 				}
 			} else {
 				die "$revision: unknown action '".$action->[0]."' for $path\n";
@@ -883,6 +918,7 @@
 	while(my($path,$action) = each %$changed_paths) {
 		($branch,$path) = split_path($revision,$path);
 		next if not defined $branch;
+		next if not defined $path;
 		$done{$branch}{$path} = $action;
 	}
 	while(($branch,$changed_paths) = each %done) {
diff --git a/git-tag.sh b/git-tag.sh
index a0afa25..ac269e3 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -47,8 +47,10 @@
     -d)
     	shift
 	tag_name="$1"
-	rm "$GIT_DIR/refs/tags/$tag_name" && \
-	        echo "Deleted tag $tag_name."
+	tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") ||
+		die "Seriously, what tag are you talking about?"
+	git-update-ref -m 'tag: delete' -d "refs/tags/$tag_name" "$tag" &&
+		echo "Deleted tag $tag_name."
 	exit $?
 	;;
     -*)
@@ -63,8 +65,11 @@
 
 name="$1"
 [ "$name" ] || usage
-if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then
-    die "tag '$name' already exists"
+prev=0000000000000000000000000000000000000000
+if git-show-ref --verify --quiet -- "refs/tags/$name"
+then
+    test -n "$force" || die "tag '$name' already exists"
+    prev=`git rev-parse "refs/tags/$name"`
 fi
 shift
 git-check-ref-format "tags/$name" ||
@@ -107,6 +112,5 @@
     object=$(git-mktag < "$GIT_DIR"/TAG_TMP)
 fi
 
-leading=`expr "refs/tags/$name" : '\(.*\)/'` &&
-mkdir -p "$GIT_DIR/$leading" &&
-echo $object > "$GIT_DIR/refs/tags/$name"
+git update-ref "refs/tags/$name" "$object" "$prev"
+
diff --git a/git.c b/git.c
index e089b53..1aa07a5 100644
--- a/git.c
+++ b/git.c
@@ -219,11 +219,15 @@
 		int option;
 	} commands[] = {
 		{ "add", cmd_add, RUN_SETUP },
+		{ "annotate", cmd_annotate, },
 		{ "apply", cmd_apply },
 		{ "archive", cmd_archive },
+		{ "blame", cmd_blame, RUN_SETUP | USE_PAGER },
+		{ "branch", cmd_branch, RUN_SETUP },
 		{ "cat-file", cmd_cat_file, RUN_SETUP },
 		{ "checkout-index", cmd_checkout_index, RUN_SETUP },
 		{ "check-ref-format", cmd_check_ref_format },
+		{ "cherry", cmd_cherry, RUN_SETUP },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
 		{ "diff", cmd_diff, RUN_SETUP | USE_PAGER },
@@ -232,6 +236,7 @@
 		{ "diff-stages", cmd_diff_stages, RUN_SETUP },
 		{ "diff-tree", cmd_diff_tree, RUN_SETUP },
 		{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
+		{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
 		{ "format-patch", cmd_format_patch, RUN_SETUP },
 		{ "get-tar-commit-id", cmd_get_tar_commit_id },
 		{ "grep", cmd_grep, RUN_SETUP },
@@ -245,6 +250,7 @@
 		{ "mv", cmd_mv, RUN_SETUP },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
+		{ "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
 		{ "prune", cmd_prune, RUN_SETUP },
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
 		{ "push", cmd_push, RUN_SETUP },
@@ -267,6 +273,8 @@
 		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
 		{ "write-tree", cmd_write_tree, RUN_SETUP },
 		{ "verify-pack", cmd_verify_pack },
+		{ "show-ref", cmd_show_ref, RUN_SETUP },
+		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
 	};
 	int i;
 
diff --git a/gitk b/gitk
index ebbeac6..ab383b3 100755
--- a/gitk
+++ b/gitk
@@ -4440,12 +4440,27 @@
     }
 }
 
+proc prevfile {} {
+    global difffilestart ctext
+    set prev [lindex $difffilestart 0]
+    set here [$ctext index @0,0]
+    foreach loc $difffilestart {
+	if {[$ctext compare $loc >= $here]} {
+	    $ctext yview $prev
+	    return
+	}
+	set prev $loc
+    }
+    $ctext yview $prev
+}
+
 proc nextfile {} {
     global difffilestart ctext
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
 	if {[$ctext compare $loc > $here]} {
 	    $ctext yview $loc
+	    return
 	}
     }
 }
diff --git a/gitweb/README b/gitweb/README
index 78e6fc0..e02e90f 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -26,12 +26,26 @@
  * GITWEB_LOGO
    Points to the location where you put git-logo.png on your web server.
  * GITWEB_CONFIG
-   This file will be loaded using 'require'.  If the environment
+   This file will be loaded using 'require' and can be used to override any
+   of the options above as well as some other options - see the top of
+   'gitweb.cgi' for their full list and description.  If the environment
    $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the
    environment variable will be loaded instead of the file
    specified when gitweb.cgi was created.
 
 
+Runtime gitweb configuration
+----------------------------
+
+You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
+(defaults to 'gitweb_config.perl' in the same directory as the CGI).
+See the top of 'gitweb.cgi' for the list of variables and some description.
+The most notable thing that is not configurable at compile time are the
+optional features, stored in the '%features' variable. You can find further
+description on how to reconfigure the default features setting in your
+`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'.
+
+
 Webserver configuration
 -----------------------
 
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 3f62b6d..974b47f 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -16,6 +16,13 @@
 	color: #880000;
 }
 
+span.cntrl {
+	border: dashed #aaaaaa;
+	border-width: 1px;
+	padding: 0px 2px 0px 2px;
+	margin:  0px 2px 0px 2px;
+}
+
 img.logo {
 	float: right;
 	border-width: 0px;
@@ -291,40 +298,82 @@
 	font-family: monospace;
 }
 
-div.diff a.list {
-	text-decoration: none;
+/* styling of diffs (patchsets): commitdiff and blobdiff views */
+div.diff.header,
+div.diff.extended_header {
+	white-space: normal;
 }
 
-div.diff a.list:hover {
+div.diff.header {
+	font-weight: bold;
+
+	background-color: #edece6;
+
+	margin-top: 4px;
+	padding: 4px 0px 2px 0px;
+	border: solid #d9d8d1;
+	border-width: 1px 0px 1px 0px;
+}
+
+div.diff.header a.path {
 	text-decoration: underline;
 }
 
-div.diff.to_file a.list,
-div.diff.to_file,
+div.diff.extended_header,
+div.diff.extended_header a.path,
+div.diff.extended_header a.hash {
+	color: #777777;
+}
+
+div.diff.extended_header .info {
+	color: #b0b0b0;
+}
+
+div.diff.extended_header {
+	background-color: #f6f5ee;
+	padding: 2px 0px 2px 0px;
+}
+
+div.diff a.path,
+div.diff a.hash {
+	text-decoration: none;
+}
+
+div.diff a.path:hover,
+div.diff a.hash:hover {
+	text-decoration: underline;
+}
+
+div.diff.to_file a.path,
+div.diff.to_file {
+	color: #007000;
+}
+
 div.diff.add {
 	color: #008800;
 }
 
-div.diff.from_file a.list,
-div.diff.from_file,
+div.diff.from_file a.path,
+div.diff.from_file {
+	color: #aa0000;
+}
+
 div.diff.rem {
 	color: #cc0000;
 }
 
 div.diff.chunk_header {
 	color: #990099;
+
+	border: dotted #ffe0ff;
+	border-width: 1px 0px 0px 0px;
+	margin-top: 2px;
 }
 
 div.diff.incomplete {
 	color: #cccccc;
 }
 
-div.diff_info {
-	font-family: monospace;
-	color: #000099;
-	background-color: #edece6;
-	font-style: italic;
-}
 
 div.index_include {
 	border: solid #d9d8d1;
@@ -333,6 +382,8 @@
 }
 
 div.search {
+	font-size: 12px;
+	font-weight: normal;
 	margin: 4px 8px;
 	position: absolute;
 	top: 56px;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 05e7b12..e54a29e 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -39,13 +39,20 @@
 
 # name of your site or organization to appear in page titles
 # replace this with something more descriptive for clearer bookmarks
-our $site_name = "++GITWEB_SITENAME++" || $ENV{'SERVER_NAME'} || "Untitled";
+our $site_name = "++GITWEB_SITENAME++"
+                 || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
 
+# filename of html text to include at top of each page
+our $site_header = "++GITWEB_SITE_HEADER++";
 # html text to include at home page
 our $home_text = "++GITWEB_HOMETEXT++";
+# filename of html text to include at bottom of each page
+our $site_footer = "++GITWEB_SITE_FOOTER++";
 
-# URI of default stylesheet
-our $stylesheet = "++GITWEB_CSS++";
+# URI of stylesheets
+our @stylesheets = ("++GITWEB_CSS++");
+# URI of a single stylesheet, which can be overridden in GITWEB_CONFIG.
+our $stylesheet = undef;
 # URI of GIT logo (72x27 size)
 our $logo = "++GITWEB_LOGO++";
 # URI of GIT favicon, assumed to be image/png type
@@ -93,21 +100,81 @@
 	#
 	# use gitweb_check_feature(<feature>) to check if <feature> is enabled
 
+	# Enable the 'blame' blob view, showing the last commit that modified
+	# each line in the file. This can be very CPU-intensive.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'blame'}{'default'} = [1];
+	# To have project specific config enable override in $GITWEB_CONFIG
+	# $feature{'blame'}{'override'} = 1;
+	# and in project config gitweb.blame = 0|1;
 	'blame' => {
 		'sub' => \&feature_blame,
 		'override' => 0,
 		'default' => [0]},
 
+	# Enable the 'snapshot' link, providing a compressed tarball of any
+	# tree. This can potentially generate high traffic if you have large
+	# project.
+
+	# To disable system wide have in $GITWEB_CONFIG
+	# $feature{'snapshot'}{'default'} = [undef];
+	# To have project specific config enable override in $GITWEB_CONFIG
+	# $feature{'blame'}{'override'} = 1;
+	# and in project config gitweb.snapshot = none|gzip|bzip2;
 	'snapshot' => {
 		'sub' => \&feature_snapshot,
 		'override' => 0,
 		#         => [content-encoding, suffix, program]
 		'default' => ['x-gzip', 'gz', 'gzip']},
 
+	# Enable the pickaxe search, which will list the commits that modified
+	# a given string in a file. This can be practical and quite faster
+	# alternative to 'blame', but still potentially CPU-intensive.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'pickaxe'}{'default'} = [1];
+	# To have project specific config enable override in $GITWEB_CONFIG
+	# $feature{'pickaxe'}{'override'} = 1;
+	# and in project config gitweb.pickaxe = 0|1;
 	'pickaxe' => {
 		'sub' => \&feature_pickaxe,
 		'override' => 0,
 		'default' => [1]},
+
+	# Make gitweb use an alternative format of the URLs which can be
+	# more readable and natural-looking: project name is embedded
+	# directly in the path and the query string contains other
+	# auxiliary information. All gitweb installations recognize
+	# URL in either format; this configures in which formats gitweb
+	# generates links.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'pathinfo'}{'default'} = [1];
+	# Project specific override is not supported.
+
+	# Note that you will need to change the default location of CSS,
+	# favicon, logo and possibly other files to an absolute URL. Also,
+	# if gitweb.cgi serves as your indexfile, you will need to force
+	# $my_uri to contain the script name in your $GITWEB_CONFIG.
+	'pathinfo' => {
+		'override' => 0,
+		'default' => [0]},
+
+	# Make gitweb consider projects in project root subdirectories
+	# to be forks of existing projects. Given project $projname.git,
+	# projects matching $projname/*.git will not be shown in the main
+	# projects list, instead a '+' mark will be added to $projname
+	# there and a 'forks' view will be enabled for the project, listing
+	# all the forks. This feature is supported only if project list
+	# is taken from a directory, not file.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'forks'}{'default'} = [1];
+	# Project specific override is not supported.
+	'forks' => {
+		'override' => 0,
+		'default' => [0]},
 );
 
 sub gitweb_check_feature {
@@ -118,15 +185,13 @@
 		$feature{$name}{'override'},
 		@{$feature{$name}{'default'}});
 	if (!$override) { return @defaults; }
+	if (!defined $sub) {
+		warn "feature $name is not overrideable";
+		return @defaults;
+	}
 	return $sub->(@defaults);
 }
 
-# To enable system wide have in $GITWEB_CONFIG
-# $feature{'blame'}{'default'} = [1];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'blame'}{'override'} = 1;
-# and in project config gitweb.blame = 0|1;
-
 sub feature_blame {
 	my ($val) = git_get_project_config('blame', '--bool');
 
@@ -139,12 +204,6 @@
 	return $_[0];
 }
 
-# To disable system wide have in $GITWEB_CONFIG
-# $feature{'snapshot'}{'default'} = [undef];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'blame'}{'override'} = 1;
-# and in project config  gitweb.snapshot = none|gzip|bzip2
-
 sub feature_snapshot {
 	my ($ctype, $suffix, $command) = @_;
 
@@ -168,12 +227,6 @@
 	return $have_snapshot;
 }
 
-# To enable system wide have in $GITWEB_CONFIG
-# $feature{'pickaxe'}{'default'} = [1];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'pickaxe'}{'override'} = 1;
-# and in project config gitweb.pickaxe = 0|1;
-
 sub feature_pickaxe {
 	my ($val) = git_get_project_config('pickaxe', '--bool');
 
@@ -186,6 +239,22 @@
 	return ($_[0]);
 }
 
+# checking HEAD file with -e is fragile if the repository was
+# initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
+# and then pruned.
+sub check_head_link {
+	my ($dir) = @_;
+	my $headfile = "$dir/HEAD";
+	return ((-e $headfile) ||
+		(-l $headfile && readlink($headfile) =~ /^refs\/heads\//));
+}
+
+sub check_export_ok {
+	my ($dir) = @_;
+	return (check_head_link($dir) &&
+		(!$export_ok || -e "$dir/$export_ok"));
+}
+
 # rename detection options for git-diff and git-diff-tree
 # - default is '-M', with the cost proportional to
 #   (number of removed files) * (number of new files).
@@ -218,7 +287,7 @@
 if (defined $project) {
 	if (!validate_pathname($project) ||
 	    !(-d "$projectroot/$project") ||
-	    !(-e "$projectroot/$project/HEAD") ||
+	    !check_head_link("$projectroot/$project") ||
 	    ($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
 	    ($strict_export && !project_in_list($project))) {
 		undef $project;
@@ -285,6 +354,13 @@
 	$searchtext = quotemeta $searchtext;
 }
 
+our $searchtype = $cgi->param('st');
+if (defined $searchtype) {
+	if ($searchtype =~ m/[^a-z]/) {
+		die_error(undef, "Invalid searchtype parameter");
+	}
+}
+
 # now read PATH_INFO and use it as alternative to parameters
 sub evaluate_path_info {
 	return if defined $project;
@@ -295,7 +371,7 @@
 	# find which part of PATH_INFO is project
 	$project = $path_info;
 	$project =~ s,/+$,,;
-	while ($project && !-e "$projectroot/$project/HEAD") {
+	while ($project && !check_head_link("$projectroot/$project")) {
 		$project =~ s,/*[^/]*$,,;
 	}
 	# validate project
@@ -344,11 +420,13 @@
 	"commitdiff" => \&git_commitdiff,
 	"commitdiff_plain" => \&git_commitdiff_plain,
 	"commit" => \&git_commit,
+	"forks" => \&git_forks,
 	"heads" => \&git_heads,
 	"history" => \&git_history,
 	"log" => \&git_log,
 	"rss" => \&git_rss,
 	"search" => \&git_search,
+	"search_help" => \&git_search_help,
 	"shortlog" => \&git_shortlog,
 	"summary" => \&git_summary,
 	"tag" => \&git_tag,
@@ -381,6 +459,10 @@
 
 sub href(%) {
 	my %params = @_;
+	my $href = $my_uri;
+
+	# XXX: Warning: If you touch this, check the search form for updating,
+	# too.
 
 	my @mapping = (
 		project => "p",
@@ -394,11 +476,25 @@
 		page => "pg",
 		order => "o",
 		searchtext => "s",
+		searchtype => "st",
 	);
 	my %mapping = @mapping;
 
 	$params{'project'} = $project unless exists $params{'project'};
 
+	my ($use_pathinfo) = gitweb_check_feature('pathinfo');
+	if ($use_pathinfo) {
+		# use PATH_INFO for project name
+		$href .= "/$params{'project'}" if defined $params{'project'};
+		delete $params{'project'};
+
+		# Summary just uses the project path URL
+		if (defined $params{'action'} && $params{'action'} eq 'summary') {
+			delete $params{'action'};
+		}
+	}
+
+	# now encode the parameters explicitly
 	my @result = ();
 	for (my $i = 0; $i < @mapping; $i += 2) {
 		my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
@@ -406,7 +502,9 @@
 			push @result, $symbol . "=" . esc_param($params{$name});
 		}
 	}
-	return "$my_uri?" . join(';', @result);
+	$href .= "?" . join(';', @result) if scalar @result;
+
+	return $href;
 }
 
 
@@ -472,21 +570,87 @@
 }
 
 # replace invalid utf8 character with SUBSTITUTION sequence
-sub esc_html {
+sub esc_html ($;%) {
 	my $str = shift;
+	my %opts = @_;
+
 	$str = to_utf8($str);
 	$str = escapeHTML($str);
-	$str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file)
-	$str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1)
+	if ($opts{'-nbsp'}) {
+		$str =~ s/ /&nbsp;/g;
+	}
+	$str =~ s|([[:cntrl:]])|(($1 ne "\t") ? quot_cec($1) : $1)|eg;
+	return $str;
+}
+
+# Make control characterss "printable".
+sub quot_cec {
+	my $cntrl = shift;
+	my %es = ( # character escape codes, aka escape sequences
+		   "\t" => '\t',   # tab            (HT)
+		   "\n" => '\n',   # line feed      (LF)
+		   "\r" => '\r',   # carrige return (CR)
+		   "\f" => '\f',   # form feed      (FF)
+		   "\b" => '\b',   # backspace      (BS)
+		   "\a" => '\a',   # alarm (bell)   (BEL)
+		   "\e" => '\e',   # escape         (ESC)
+		   "\013" => '\v', # vertical tab   (VT)
+		   "\000" => '\0', # nul character  (NUL)
+		   );
+	my $chr = ( (exists $es{$cntrl})
+		    ? $es{$cntrl}
+		    : sprintf('\%03o', ord($cntrl)) );
+	return "<span class=\"cntrl\">$chr</span>";
+}
+
+# Alternatively use unicode control pictures codepoints.
+sub quot_upr {
+	my $cntrl = shift;
+	my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl));
+	return "<span class=\"cntrl\">$chr</span>";
+}
+
+# quote control characters and escape filename to HTML
+sub esc_path {
+	my $str = shift;
+
+	$str = esc_html($str);
+	$str =~ s|([[:cntrl:]])|quot_cec($1)|eg;
 	return $str;
 }
 
 # git may return quoted and escaped filenames
 sub unquote {
 	my $str = shift;
+
+	sub unq {
+		my $seq = shift;
+		my %es = ( # character escape codes, aka escape sequences
+			't' => "\t",   # tab            (HT, TAB)
+			'n' => "\n",   # newline        (NL)
+			'r' => "\r",   # return         (CR)
+			'f' => "\f",   # form feed      (FF)
+			'b' => "\b",   # backspace      (BS)
+			'a' => "\a",   # alarm (bell)   (BEL)
+			'e' => "\e",   # escape         (ESC)
+			'v' => "\013", # vertical tab   (VT)
+		);
+
+		if ($seq =~ m/^[0-7]{1,3}$/) {
+			# octal char sequence
+			return chr(oct($seq));
+		} elsif (exists $es{$seq}) {
+			# C escape sequence, aka character escape code
+			return $es{$seq}
+		}
+		# quoted ordinary character
+		return $seq;
+	}
+
 	if ($str =~ m/^"(.*)"$/) {
+		# needs unquoting
 		$str = $1;
-		$str =~ s/\\([0-7]{1,3})/chr(oct($1))/eg;
+		$str =~ s/\\([^0-7]|[0-7]{1,3})/unq($1)/eg;
 	}
 	return $str;
 }
@@ -620,16 +784,41 @@
 	}
 }
 
+# convert file mode in octal to file type description string
+sub file_type_long {
+	my $mode = shift;
+
+	if ($mode !~ m/^[0-7]+$/) {
+		return $mode;
+	} else {
+		$mode = oct $mode;
+	}
+
+	if (S_ISDIR($mode & S_IFMT)) {
+		return "directory";
+	} elsif (S_ISLNK($mode)) {
+		return "symlink";
+	} elsif (S_ISREG($mode)) {
+		if ($mode & S_IXUSR) {
+			return "executable";
+		} else {
+			return "file";
+		};
+	} else {
+		return "unknown";
+	}
+}
+
+
 ## ----------------------------------------------------------------------
 ## functions returning short HTML fragments, or transforming HTML fragments
 ## which don't beling to other sections
 
-# format line of commit message or tag comment
+# format line of commit message.
 sub format_log_line_html {
 	my $line = shift;
 
-	$line = esc_html($line);
-	$line =~ s/ /&nbsp;/g;
+	$line = esc_html($line, -nbsp=>1);
 	if ($line =~ m/([0-9a-fA-F]{40})/) {
 		my $hash_text = $1;
 		if (git_get_type($hash_text) eq "commit") {
@@ -702,7 +891,7 @@
 		$diff_class = " incomplete";
 	}
 	$line = untabify($line);
-	return "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n";
+	return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
 }
 
 ## ----------------------------------------------------------------------
@@ -778,7 +967,7 @@
 	close $fd or return undef;
 
 	#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
+	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/;
 	if (defined $type && $type ne $2) {
 		# type doesn't match
 		return undef;
@@ -810,13 +999,21 @@
 }
 
 sub git_get_projects_list {
+	my ($filter) = @_;
 	my @list;
 
+	$filter ||= '';
+	$filter =~ s/\.git$//;
+
 	if (-d $projects_list) {
 		# search in directory
-		my $dir = $projects_list;
+		my $dir = $projects_list . ($filter ? "/$filter" : '');
+		# remove the trailing "/"
+		$dir =~ s!/+$!!;
 		my $pfxlen = length("$dir");
 
+		my ($check_forks) = gitweb_check_feature('forks');
+
 		File::Find::find({
 			follow_fast => 1, # follow symbolic links
 			dangling_symlinks => 0, # ignore dangling symlinks, silently
@@ -828,9 +1025,10 @@
 
 				my $subdir = substr($File::Find::name, $pfxlen + 1);
 				# we check related file in $projectroot
-				if (-e "$projectroot/$subdir/HEAD" && (!$export_ok ||
-				    -e "$projectroot/$subdir/$export_ok")) {
-					push @list, { path => $subdir };
+				if ($check_forks and $subdir =~ m#/.#) {
+					$File::Find::prune = 1;
+				} elsif (check_export_ok("$projectroot/$filter/$subdir")) {
+					push @list, { path => ($filter ? "$filter/" : '') . $subdir };
 					$File::Find::prune = 1;
 				}
 			},
@@ -850,8 +1048,18 @@
 			if (!defined $path) {
 				next;
 			}
-			if (-e "$projectroot/$path/HEAD" && (!$export_ok ||
-			    -e "$projectroot/$path/$export_ok")) {
+			if ($filter ne '') {
+				# looking for forks;
+				my $pfx = substr($path, 0, length($filter));
+				if ($pfx ne $filter) {
+					next;
+				}
+				my $sfx = substr($path, length($filter));
+				if ($sfx !~ /^\/.*\.git$/) {
+					next;
+				}
+			}
+			if (check_export_ok("$projectroot/$path")) {
 				my $pr = {
 					path => $path,
 					owner => to_utf8($owner),
@@ -896,6 +1104,24 @@
 	return $owner;
 }
 
+sub git_get_last_activity {
+	my ($path) = @_;
+	my $fd;
+
+	$git_dir = "$projectroot/$path";
+	open($fd, "-|", git_cmd(), 'for-each-ref',
+	     '--format=%(refname) %(committer)',
+	     '--sort=-committerdate',
+	     'refs/heads') or return;
+	my $most_recent = <$fd>;
+	close $fd or return;
+	if ($most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
+		my $timestamp = $1;
+		my $age = time - $timestamp;
+		return ($age, age_string($age));
+	}
+}
+
 sub git_get_references {
 	my $type = shift || "";
 	my %refs;
@@ -961,6 +1187,9 @@
 	$date{'hour_local'} = $hour;
 	$date{'minute_local'} = $min;
 	$date{'tz_local'} = $tz;
+	$date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s",
+				   1900+$year, $mon+1, $mday,
+				   $hour, $min, $sec, $tz);
 	return %date;
 }
 
@@ -1010,7 +1239,9 @@
 		@commit_lines = @$commit_text;
 	} else {
 		local $/ = "\0";
-		open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", "--max-count=1", $commit_id
+		open my $fd, "-|", git_cmd(), "rev-list",
+			"--header", "--parents", "--max-count=1",
+			$commit_id, "--"
 			or return;
 		@commit_lines = split '\n', <$fd>;
 		close $fd or return;
@@ -1174,7 +1405,7 @@
 	my %res;
 
 	#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
+	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
 
 	$res{'mode'} = $1;
 	$res{'type'} = $2;
@@ -1191,47 +1422,88 @@
 ## ......................................................................
 ## parse to array of hashes functions
 
-sub git_get_refs_list {
-	my $type = shift || "";
-	my %refs;
-	my @reflist;
+sub git_get_heads_list {
+	my $limit = shift;
+	my @headslist;
 
-	my @refs;
-	open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/"
+	open my $fd, '-|', git_cmd(), 'for-each-ref',
+		($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
+		'--format=%(objectname) %(refname) %(subject)%00%(committer)',
+		'refs/heads'
 		or return;
 	while (my $line = <$fd>) {
-		chomp $line;
-		if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?([^\^]+))(\^\{\})?$/) {
-			if (defined $refs{$1}) {
-				push @{$refs{$1}}, $2;
-			} else {
-				$refs{$1} = [ $2 ];
-			}
+		my %ref_item;
 
-			if (! $4) { # unpeeled, direct reference
-				push @refs, { hash => $1, name => $3 }; # without type
-			} elsif ($3 eq $refs[-1]{'name'}) {
-				# most likely a tag is followed by its peeled
-				# (deref) one, and when that happens we know the
-				# previous one was of type 'tag'.
-				$refs[-1]{'type'} = "tag";
-			}
+		chomp $line;
+		my ($refinfo, $committerinfo) = split(/\0/, $line);
+		my ($hash, $name, $title) = split(' ', $refinfo, 3);
+		my ($committer, $epoch, $tz) =
+			($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
+		$name =~ s!^refs/heads/!!;
+
+		$ref_item{'name'}  = $name;
+		$ref_item{'id'}    = $hash;
+		$ref_item{'title'} = $title || '(no commit message)';
+		$ref_item{'epoch'} = $epoch;
+		if ($epoch) {
+			$ref_item{'age'} = age_string(time - $ref_item{'epoch'});
+		} else {
+			$ref_item{'age'} = "unknown";
 		}
+
+		push @headslist, \%ref_item;
 	}
 	close $fd;
 
-	foreach my $ref (@refs) {
-		my $ref_file = $ref->{'name'};
-		my $ref_id   = $ref->{'hash'};
+	return wantarray ? @headslist : \@headslist;
+}
 
-		my $type = $ref->{'type'} || git_get_type($ref_id) || next;
-		my %ref_item = parse_ref($ref_file, $ref_id, $type);
+sub git_get_tags_list {
+	my $limit = shift;
+	my @tagslist;
 
-		push @reflist, \%ref_item;
+	open my $fd, '-|', git_cmd(), 'for-each-ref',
+		($limit ? '--count='.($limit+1) : ()), '--sort=-creatordate',
+		'--format=%(objectname) %(objecttype) %(refname) '.
+		'%(*objectname) %(*objecttype) %(subject)%00%(creator)',
+		'refs/tags'
+		or return;
+	while (my $line = <$fd>) {
+		my %ref_item;
+
+		chomp $line;
+		my ($refinfo, $creatorinfo) = split(/\0/, $line);
+		my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);
+		my ($creator, $epoch, $tz) =
+			($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);
+		$name =~ s!^refs/tags/!!;
+
+		$ref_item{'type'} = $type;
+		$ref_item{'id'} = $id;
+		$ref_item{'name'} = $name;
+		if ($type eq "tag") {
+			$ref_item{'subject'} = $title;
+			$ref_item{'reftype'} = $reftype;
+			$ref_item{'refid'}   = $refid;
+		} else {
+			$ref_item{'reftype'} = $type;
+			$ref_item{'refid'}   = $id;
+		}
+
+		if ($type eq "tag" || $type eq "commit") {
+			$ref_item{'epoch'} = $epoch;
+			if ($epoch) {
+				$ref_item{'age'} = age_string(time - $ref_item{'epoch'});
+			} else {
+				$ref_item{'age'} = "unknown";
+			}
+		}
+
+		push @tagslist, \%ref_item;
 	}
-	# sort refs by age
-	@reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
-	return (\@reflist, \%refs);
+	close $fd;
+
+	return wantarray ? @tagslist : \@tagslist;
 }
 
 ## ----------------------------------------------------------------------
@@ -1328,13 +1600,13 @@
 	my $status = shift || "200 OK";
 	my $expires = shift;
 
-	my $title = "$site_name git";
+	my $title = "$site_name";
 	if (defined $project) {
 		$title .= " - $project";
 		if (defined $action) {
 			$title .= "/$action";
 			if (defined $file_name) {
-				$title .= " - " . esc_html($file_name);
+				$title .= " - " . esc_path($file_name);
 				if ($action eq "tree" && $file_name !~ m|/$|) {
 					$title .= "/";
 				}
@@ -1366,8 +1638,17 @@
 <meta name="generator" content="gitweb/$version git/$git_version"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
-<link rel="stylesheet" type="text/css" href="$stylesheet"/>
 EOF
+# print out each stylesheet that exist
+	if (defined $stylesheet) {
+#provides backwards capability for those people who define style sheet in a config file
+		print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+	} else {
+		foreach my $stylesheet (@stylesheets) {
+			next unless $stylesheet;
+			print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+		}
+	}
 	if (defined $project) {
 		printf('<link rel="alternate" title="%s log" '.
 		       'href="%s" type="application/rss+xml"/>'."\n",
@@ -1385,8 +1666,15 @@
 	}
 
 	print "</head>\n" .
-	      "<body>\n" .
-	      "<div class=\"page_header\">\n" .
+	      "<body>\n";
+
+	if (-f $site_header) {
+		open (my $fd, $site_header);
+		print <$fd>;
+		close $fd;
+	}
+
+	print "<div class=\"page_header\">\n" .
 	      $cgi->a({-href => esc_url($logo_url),
 	               -title => $logo_label},
 	              qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
@@ -1410,11 +1698,16 @@
 		}
 		$cgi->param("a", "search");
 		$cgi->param("h", $search_hash);
+		$cgi->param("p", $project);
 		print $cgi->startform(-method => "get", -action => $my_uri) .
 		      "<div class=\"search\">\n" .
 		      $cgi->hidden(-name => "p") . "\n" .
 		      $cgi->hidden(-name => "a") . "\n" .
 		      $cgi->hidden(-name => "h") . "\n" .
+		      $cgi->popup_menu(-name => 'st', -default => 'commit',
+				       -values => ['commit', 'author', 'committer', 'pickaxe']) .
+		      $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
+		      " search:\n",
 		      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
 		      "</div>" .
 		      $cgi->end_form() . "\n";
@@ -1437,8 +1730,15 @@
 		print $cgi->a({-href => href(project=>undef, action=>"project_index"),
 		              -class => "rss_logo"}, "TXT") . "\n";
 	}
-	print "</div>\n" .
-	      "</body>\n" .
+	print "</div>\n" ;
+
+	if (-f $site_footer) {
+		open (my $fd, $site_footer);
+		print <$fd>;
+		close $fd;
+	}
+
+	print "</body>\n" .
 	      "</html>";
 }
 
@@ -1563,37 +1863,37 @@
 	my $type = shift;
 	my $hb = shift;
 
-	if (!defined $name) {
-		print "<div class=\"page_path\">/</div>\n";
-	} else {
+
+	print "<div class=\"page_path\">";
+	print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
+	              -title => 'tree root'}, "[$project]");
+	print " / ";
+	if (defined $name) {
 		my @dirname = split '/', $name;
 		my $basename = pop @dirname;
 		my $fullname = '';
 
-		print "<div class=\"page_path\">";
-		print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
-			      -title => 'tree root'}, "[$project]");
-		print " / ";
 		foreach my $dir (@dirname) {
 			$fullname .= ($fullname ? '/' : '') . $dir;
 			print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
 			                             hash_base=>$hb),
-			              -title => $fullname}, esc_html($dir));
+			              -title => esc_html($fullname)}, esc_path($dir));
 			print " / ";
 		}
 		if (defined $type && $type eq 'blob') {
 			print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
 			                             hash_base=>$hb),
-			              -title => $name}, esc_html($basename));
+			              -title => esc_html($name)}, esc_path($basename));
 		} elsif (defined $type && $type eq 'tree') {
 			print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
 			                             hash_base=>$hb),
-			              -title => $name}, esc_html($basename));
+			              -title => esc_html($name)}, esc_path($basename));
+			print " / ";
 		} else {
-			print esc_html($basename);
+			print esc_path($basename);
 		}
-		print "<br/></div>\n";
 	}
+	print "<br/></div>\n";
 }
 
 # sub git_print_log (\@;%) {
@@ -1646,15 +1946,6 @@
 	}
 }
 
-sub git_print_simplified_log {
-	my $log = shift;
-	my $remove_title = shift;
-
-	git_print_log($log,
-		-final_empty_line=> 1,
-		-remove_title => $remove_title);
-}
-
 # print tree entry (row of git_tree), but without encompassing <tr> element
 sub git_print_tree_entry {
 	my ($t, $basedir, $hash_base, $have_blame) = @_;
@@ -1671,18 +1962,20 @@
 		print "<td class=\"list\">" .
 			$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
 			                       file_name=>"$basedir$t->{'name'}", %base_key),
-			        -class => "list"}, esc_html($t->{'name'})) . "</td>\n";
+			        -class => "list"}, esc_path($t->{'name'})) . "</td>\n";
 		print "<td class=\"link\">";
+		print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+					     file_name=>"$basedir$t->{'name'}", %base_key)},
+			      "blob");
 		if ($have_blame) {
-			print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
+			print " | " .
+			      $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
 				                           file_name=>"$basedir$t->{'name'}", %base_key)},
 				            "blame");
 		}
 		if (defined $hash_base) {
-			if ($have_blame) {
-				print " | ";
-			}
-			print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+			print " | " .
+			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
 			                             hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
 			              "history");
 		}
@@ -1696,11 +1989,15 @@
 		print "<td class=\"list\">";
 		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
 		                             file_name=>"$basedir$t->{'name'}", %base_key)},
-		              esc_html($t->{'name'}));
+		              esc_path($t->{'name'}));
 		print "</td>\n";
 		print "<td class=\"link\">";
+		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+					     file_name=>"$basedir$t->{'name'}", %base_key)},
+			      "tree");
 		if (defined $hash_base) {
-			print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+			print " | " .
+			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
 			                             file_name=>"$basedir$t->{'name'}")},
 			              "history");
 		}
@@ -1713,7 +2010,7 @@
 
 sub git_difftree_body {
 	my ($difftree, $hash, $parent) = @_;
-
+	my ($have_blame) = gitweb_check_feature('blame');
 	print "<div class=\"list_head\">\n";
 	if ($#{$difftree} > 10) {
 		print(($#{$difftree} + 1) . " files changed:\n");
@@ -1757,7 +2054,7 @@
 			print "<td>";
 			print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff{'file'}),
-			              -class => "list"}, esc_html($diff{'file'}));
+			              -class => "list"}, esc_path($diff{'file'}));
 			print "</td>\n";
 			print "<td>$mode_chng</td>\n";
 			print "<td class=\"link\">";
@@ -1773,7 +2070,7 @@
 			print "<td>";
 			print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
 			                             hash_base=>$parent, file_name=>$diff{'file'}),
-			               -class => "list"}, esc_html($diff{'file'}));
+			               -class => "list"}, esc_path($diff{'file'}));
 			print "</td>\n";
 			print "<td>$mode_chng</td>\n";
 			print "<td class=\"link\">";
@@ -1783,9 +2080,16 @@
 				print $cgi->a({-href => "#patch$patchno"}, "patch");
 				print " | ";
 			}
-			print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
-			                             file_name=>$diff{'file'})},
-			              "blame") . " | ";
+			print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
+			                             hash_base=>$parent, file_name=>$diff{'file'})},
+				      "blob") . " | ";
+			if ($have_blame) {
+				print $cgi->a({-href =>
+						   href(action=>"blame",
+							hash_base=>$parent,
+							file_name=>$diff{'file'})},
+					      "blame") . " | ";
+			}
 			print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
 			                             file_name=>$diff{'file'})},
 			              "history");
@@ -1810,27 +2114,33 @@
 			print "<td>";
 			print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
 			                             hash_base=>$hash, file_name=>$diff{'file'}),
-			              -class => "list"}, esc_html($diff{'file'}));
+			              -class => "list"}, esc_path($diff{'file'}));
 			print "</td>\n";
 			print "<td>$mode_chnge</td>\n";
 			print "<td class=\"link\">";
-			if ($diff{'to_id'} ne $diff{'from_id'}) { # modified
-				if ($action eq 'commitdiff') {
-					# link to patch
-					$patchno++;
-					print $cgi->a({-href => "#patch$patchno"}, "patch");
-				} else {
-					print $cgi->a({-href => href(action=>"blobdiff",
-					                             hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
-					                             hash_base=>$hash, hash_parent_base=>$parent,
-					                             file_name=>$diff{'file'})},
-					              "diff");
-				}
-				print " | ";
+			if ($action eq 'commitdiff') {
+				# link to patch
+				$patchno++;
+				print $cgi->a({-href => "#patch$patchno"}, "patch") .
+				      " | ";
+			} elsif ($diff{'to_id'} ne $diff{'from_id'}) {
+				# "commit" view and modified file (not onlu mode changed)
+				print $cgi->a({-href => href(action=>"blobdiff",
+				                             hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+				                             hash_base=>$hash, hash_parent_base=>$parent,
+				                             file_name=>$diff{'file'})},
+				              "diff") .
+				      " | ";
 			}
-			print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
-			                             file_name=>$diff{'file'})},
-			              "blame") . " | ";
+			print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+						     hash_base=>$hash, file_name=>$diff{'file'})},
+				      "blob") . " | ";
+			if ($have_blame) {
+				print $cgi->a({-href => href(action=>"blame",
+							     hash_base=>$hash,
+							     file_name=>$diff{'file'})},
+					      "blame") . " | ";
+			}
 			print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
 			                             file_name=>$diff{'file'})},
 			              "history");
@@ -1847,30 +2157,36 @@
 			print "<td>" .
 			      $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
 			                             hash=>$diff{'to_id'}, file_name=>$diff{'to_file'}),
-			              -class => "list"}, esc_html($diff{'to_file'})) . "</td>\n" .
+			              -class => "list"}, esc_path($diff{'to_file'})) . "</td>\n" .
 			      "<td><span class=\"file_status $nstatus\">[$nstatus from " .
 			      $cgi->a({-href => href(action=>"blob", hash_base=>$parent,
 			                             hash=>$diff{'from_id'}, file_name=>$diff{'from_file'}),
-			              -class => "list"}, esc_html($diff{'from_file'})) .
+			              -class => "list"}, esc_path($diff{'from_file'})) .
 			      " with " . (int $diff{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
 			      "<td class=\"link\">";
-			if ($diff{'to_id'} ne $diff{'from_id'}) {
-				if ($action eq 'commitdiff') {
-					# link to patch
-					$patchno++;
-					print $cgi->a({-href => "#patch$patchno"}, "patch");
-				} else {
-					print $cgi->a({-href => href(action=>"blobdiff",
-					                             hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
-					                             hash_base=>$hash, hash_parent_base=>$parent,
-					                             file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
-					              "diff");
-				}
-				print " | ";
+			if ($action eq 'commitdiff') {
+				# link to patch
+				$patchno++;
+				print $cgi->a({-href => "#patch$patchno"}, "patch") .
+				      " | ";
+			} elsif ($diff{'to_id'} ne $diff{'from_id'}) {
+				# "commit" view and modified file (not only pure rename or copy)
+				print $cgi->a({-href => href(action=>"blobdiff",
+				                             hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+				                             hash_base=>$hash, hash_parent_base=>$parent,
+				                             file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
+				              "diff") .
+				      " | ";
 			}
-			print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
-			                             file_name=>$diff{'from_file'})},
-			              "blame") . " | ";
+			print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
+						     hash_base=>$parent, file_name=>$diff{'from_file'})},
+				      "blob") . " | ";
+			if ($have_blame) {
+				print $cgi->a({-href => href(action=>"blame",
+							     hash_base=>$hash,
+							     file_name=>$diff{'to_file'})},
+					      "blame") . " | ";
+			}
 			print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
 			                            file_name=>$diff{'from_file'})},
 			              "history");
@@ -1889,6 +2205,7 @@
 	my $in_header = 0;
 	my $patch_found = 0;
 	my $diffinfo;
+	my (%from, %to);
 
 	print "<div class=\"patchset\">\n";
 
@@ -1899,6 +2216,10 @@
 		if ($patch_line =~ m/^diff /) { # "git diff" header
 			# beginning of patch (in patchset)
 			if ($patch_found) {
+				# close extended header for previous empty patch
+				if ($in_header) {
+					print "</div>\n" # class="diff extended_header"
+				}
 				# close previous patch
 				print "</div>\n"; # class="patch"
 			} else {
@@ -1907,99 +2228,118 @@
 			}
 			print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
 
+			# read and prepare patch information
 			if (ref($difftree->[$patch_idx]) eq "HASH") {
+				# pre-parsed (or generated by hand)
 				$diffinfo = $difftree->[$patch_idx];
 			} else {
 				$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
 			}
+			$from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
+			$to{'file'}   = $diffinfo->{'to_file'}   || $diffinfo->{'file'};
+			if ($diffinfo->{'status'} ne "A") { # not new (added) file
+				$from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
+				                     hash=>$diffinfo->{'from_id'},
+				                     file_name=>$from{'file'});
+			}
+			if ($diffinfo->{'status'} ne "D") { # not deleted file
+				$to{'href'} = href(action=>"blob", hash_base=>$hash,
+				                   hash=>$diffinfo->{'to_id'},
+				                   file_name=>$to{'file'});
+			}
 			$patch_idx++;
 
-			# for now, no extended header, hence we skip empty patches
-			# companion to	next LINE if $in_header;
-			if ($diffinfo->{'from_id'} eq $diffinfo->{'to_id'}) { # no change
-				$in_header = 1;
-				next LINE;
+			# print "git diff" header
+			$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
+			if ($from{'href'}) {
+				$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
+				                       'a/' . esc_path($from{'file'}));
+			} else { # file was added
+				$patch_line .= 'a/' . esc_path($from{'file'});
+			}
+			$patch_line .= ' ';
+			if ($to{'href'}) {
+				$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
+				                       'b/' . esc_path($to{'file'}));
+			} else { # file was deleted
+				$patch_line .= 'b/' . esc_path($to{'file'});
 			}
 
-			if ($diffinfo->{'status'} eq "A") { # added
-				print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" .
-				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
-				                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
-				              $diffinfo->{'to_id'}) . " (new)" .
-				      "</div>\n"; # class="diff_info"
-
-			} elsif ($diffinfo->{'status'} eq "D") { # deleted
-				print "<div class=\"diff_info\">" . file_type($diffinfo->{'from_mode'}) . ":" .
-				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
-				                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
-				              $diffinfo->{'from_id'}) . " (deleted)" .
-				      "</div>\n"; # class="diff_info"
-
-			} elsif ($diffinfo->{'status'} eq "R" || # renamed
-			         $diffinfo->{'status'} eq "C" || # copied
-			         $diffinfo->{'status'} eq "2") { # with two filenames (from git_blobdiff)
-				print "<div class=\"diff_info\">" .
-				      file_type($diffinfo->{'from_mode'}) . ":" .
-				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
-				                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'from_file'})},
-				              $diffinfo->{'from_id'}) .
-				      " -> " .
-				      file_type($diffinfo->{'to_mode'}) . ":" .
-				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
-				                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'to_file'})},
-				              $diffinfo->{'to_id'});
-				print "</div>\n"; # class="diff_info"
-
-			} else { # modified, mode changed, ...
-				print "<div class=\"diff_info\">" .
-				      file_type($diffinfo->{'from_mode'}) . ":" .
-				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
-				                             hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
-				              $diffinfo->{'from_id'}) .
-				      " -> " .
-				      file_type($diffinfo->{'to_mode'}) . ":" .
-				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
-				                             hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
-				              $diffinfo->{'to_id'});
-				print "</div>\n"; # class="diff_info"
-			}
-
-			#print "<div class=\"diff extended_header\">\n";
+			print "<div class=\"diff header\">$patch_line</div>\n";
+			print "<div class=\"diff extended_header\">\n";
 			$in_header = 1;
 			next LINE;
-		} # start of patch in patchset
+		}
 
+		if ($in_header) {
+			if ($patch_line !~ m/^---/) {
+				# match <path>
+				if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
+					$patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
+					                        esc_path($from{'file'}));
+				}
+				if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
+					$patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
+					                      esc_path($to{'file'}));
+				}
+				# match <mode>
+				if ($patch_line =~ m/\s(\d{6})$/) {
+					$patch_line .= '<span class="info"> (' .
+					               file_type_long($1) .
+					               ')</span>';
+				}
+				# match <hash>
+				if ($patch_line =~ m/^index/) {
+					my ($from_link, $to_link);
+					if ($from{'href'}) {
+						$from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
+						                     substr($diffinfo->{'from_id'},0,7));
+					} else {
+						$from_link = '0' x 7;
+					}
+					if ($to{'href'}) {
+						$to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"},
+						                   substr($diffinfo->{'to_id'},0,7));
+					} else {
+						$to_link = '0' x 7;
+					}
+					my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
+					$patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
+				}
+				print $patch_line . "<br/>\n";
 
-		if ($in_header && $patch_line =~ m/^---/) {
-			#print "</div>\n"; # class="diff extended_header"
-			$in_header = 0;
+			} else {
+				#$in_header && $patch_line =~ m/^---/;
+				print "</div>\n"; # class="diff extended_header"
+				$in_header = 0;
 
-			my $file = $diffinfo->{'from_file'};
-			$file  ||= $diffinfo->{'file'};
-			$file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
-			                               hash=>$diffinfo->{'from_id'}, file_name=>$file),
-			                -class => "list"}, esc_html($file));
-			$patch_line =~ s|a/.*$|a/$file|g;
-			print "<div class=\"diff from_file\">$patch_line</div>\n";
+				if ($from{'href'}) {
+					$patch_line = '--- a/' .
+					              $cgi->a({-href=>$from{'href'}, -class=>"path"},
+					                      esc_path($from{'file'}));
+				}
+				print "<div class=\"diff from_file\">$patch_line</div>\n";
 
-			$patch_line = <$fd>;
-			chomp $patch_line;
+				$patch_line = <$fd>;
+				chomp $patch_line;
 
-			#$patch_line =~ m/^+++/;
-			$file    = $diffinfo->{'to_file'};
-			$file  ||= $diffinfo->{'file'};
-			$file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
-			                               hash=>$diffinfo->{'to_id'}, file_name=>$file),
-			                -class => "list"}, esc_html($file));
-			$patch_line =~ s|b/.*|b/$file|g;
-			print "<div class=\"diff to_file\">$patch_line</div>\n";
+				#$patch_line =~ m/^+++/;
+				if ($to{'href'}) {
+					$patch_line = '+++ b/' .
+					              $cgi->a({-href=>$to{'href'}, -class=>"path"},
+					                      esc_path($to{'file'}));
+				}
+				print "<div class=\"diff to_file\">$patch_line</div>\n";
+
+			}
 
 			next LINE;
 		}
-		next LINE if $in_header;
 
 		print format_diff_line($patch_line);
 	}
+	print "</div>\n" if $in_header; # extended header
+
 	print "</div>\n" if $patch_found; # class="patch"
 
 	print "</div>\n"; # class="patchset"
@@ -2007,6 +2347,131 @@
 
 # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
+sub git_project_list_body {
+	my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
+
+	my ($check_forks) = gitweb_check_feature('forks');
+
+	my @projects;
+	foreach my $pr (@$projlist) {
+		my (@aa) = git_get_last_activity($pr->{'path'});
+		unless (@aa) {
+			next;
+		}
+		($pr->{'age'}, $pr->{'age_string'}) = @aa;
+		if (!defined $pr->{'descr'}) {
+			my $descr = git_get_project_description($pr->{'path'}) || "";
+			$pr->{'descr'} = chop_str($descr, 25, 5);
+		}
+		if (!defined $pr->{'owner'}) {
+			$pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || "";
+		}
+		if ($check_forks) {
+			my $pname = $pr->{'path'};
+			if (($pname =~ s/\.git$//) &&
+			    ($pname !~ /\/$/) &&
+			    (-d "$projectroot/$pname")) {
+				$pr->{'forks'} = "-d $projectroot/$pname";
+			}
+			else {
+				$pr->{'forks'} = 0;
+			}
+		}
+		push @projects, $pr;
+	}
+
+	$order ||= "project";
+	$from = 0 unless defined $from;
+	$to = $#projects if (!defined $to || $#projects < $to);
+
+	print "<table class=\"project_list\">\n";
+	unless ($no_header) {
+		print "<tr>\n";
+		if ($check_forks) {
+			print "<th></th>\n";
+		}
+		if ($order eq "project") {
+			@projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
+			print "<th>Project</th>\n";
+		} else {
+			print "<th>" .
+			      $cgi->a({-href => href(project=>undef, order=>'project'),
+				       -class => "header"}, "Project") .
+			      "</th>\n";
+		}
+		if ($order eq "descr") {
+			@projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
+			print "<th>Description</th>\n";
+		} else {
+			print "<th>" .
+			      $cgi->a({-href => href(project=>undef, order=>'descr'),
+				       -class => "header"}, "Description") .
+			      "</th>\n";
+		}
+		if ($order eq "owner") {
+			@projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
+			print "<th>Owner</th>\n";
+		} else {
+			print "<th>" .
+			      $cgi->a({-href => href(project=>undef, order=>'owner'),
+				       -class => "header"}, "Owner") .
+			      "</th>\n";
+		}
+		if ($order eq "age") {
+			@projects = sort {$a->{'age'} <=> $b->{'age'}} @projects;
+			print "<th>Last Change</th>\n";
+		} else {
+			print "<th>" .
+			      $cgi->a({-href => href(project=>undef, order=>'age'),
+				       -class => "header"}, "Last Change") .
+			      "</th>\n";
+		}
+		print "<th></th>\n" .
+		      "</tr>\n";
+	}
+	my $alternate = 1;
+	for (my $i = $from; $i <= $to; $i++) {
+		my $pr = $projects[$i];
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		if ($check_forks) {
+			print "<td>";
+			if ($pr->{'forks'}) {
+				print "<!-- $pr->{'forks'} -->\n";
+				print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+");
+			}
+			print "</td>\n";
+		}
+		print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+		                        -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
+		      "<td>" . esc_html($pr->{'descr'}) . "</td>\n" .
+		      "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
+		print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+		      $pr->{'age_string'} . "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
+		      $cgi->a({-href => '/git-browser/by-commit.html?r='.$pr->{'path'}}, "graphiclog") . " | " .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
+		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
+		      ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
+		      "</td>\n" .
+		      "</tr>\n";
+	}
+	if (defined $extra) {
+		print "<tr>\n";
+		if ($check_forks) {
+			print "<td></td>\n";
+		}
+		print "<td colspan=\"5\">$extra</td>\n" .
+		      "</tr>\n";
+	}
+	print "</table>\n";
+}
+
 sub git_shortlog_body {
 	# uses global variable $project
 	my ($revlist, $from, $to, $refs, $extra) = @_;
@@ -2035,6 +2500,7 @@
 		                          href(action=>"commit", hash=>$commit), $ref);
 		print "</td>\n" .
 		      "<td class=\"link\">" .
+		      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
 		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
 		      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
 		if (gitweb_have_snapshot()) {
@@ -2126,8 +2592,7 @@
 	for (my $i = $from; $i <= $to; $i++) {
 		my $entry = $taglist->[$i];
 		my %tag = %$entry;
-		my $comment_lines = $tag{'comment'};
-		my $comment = shift @$comment_lines;
+		my $comment = $tag{'subject'};
 		my $comment_short;
 		if (defined $comment) {
 			$comment_short = chop_str($comment, 30, 5);
@@ -2160,7 +2625,7 @@
 		      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});
 		if ($tag{'reftype'} eq "commit") {
 			print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") .
-			      " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'refid'})}, "log");
+			      " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log");
 		} elsif ($tag{'reftype'} eq "blob") {
 			print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
 		}
@@ -2185,23 +2650,23 @@
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
 		my $entry = $headlist->[$i];
-		my %tag = %$entry;
-		my $curr = $tag{'id'} eq $head;
+		my %ref = %$entry;
+		my $curr = $ref{'id'} eq $head;
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
 			print "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
-		print "<td><i>$tag{'age'}</i></td>\n" .
-		      ($tag{'id'} eq $head ? "<td class=\"current_head\">" : "<td>") .
-		      $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'}),
-		               -class => "list name"},esc_html($tag{'name'})) .
+		print "<td><i>$ref{'age'}</i></td>\n" .
+		      ($curr ? "<td class=\"current_head\">" : "<td>") .
+		      $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'}),
+		               -class => "list name"},esc_html($ref{'name'})) .
 		      "</td>\n" .
 		      "<td class=\"link\">" .
-		      $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . " | " .
-		      $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log") . " | " .
-		      $cgi->a({-href => href(action=>"tree", hash=>$tag{'name'}, hash_base=>$tag{'name'})}, "tree") .
+		      $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'})}, "shortlog") . " | " .
+		      $cgi->a({-href => href(action=>"log", hash=>$ref{'name'})}, "log") . " | " .
+		      $cgi->a({-href => href(action=>"tree", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, "tree") .
 		      "</td>\n" .
 		      "</tr>";
 	}
@@ -2224,30 +2689,9 @@
 	}
 
 	my @list = git_get_projects_list();
-	my @projects;
 	if (!@list) {
 		die_error(undef, "No projects found");
 	}
-	foreach my $pr (@list) {
-		my $head = git_get_head_hash($pr->{'path'});
-		if (!defined $head) {
-			next;
-		}
-		$git_dir = "$projectroot/$pr->{'path'}";
-		my %co = parse_commit($head);
-		if (!%co) {
-			next;
-		}
-		$pr->{'commit'} = \%co;
-		if (!defined $pr->{'descr'}) {
-			my $descr = git_get_project_description($pr->{'path'}) || "";
-			$pr->{'descr'} = chop_str($descr, 25, 5);
-		}
-		if (!defined $pr->{'owner'}) {
-			$pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || "";
-		}
-		push @projects, $pr;
-	}
 
 	git_header_html();
 	if (-f $home_text) {
@@ -2257,75 +2701,30 @@
 		close $fd;
 		print "</div>\n";
 	}
-	print "<table class=\"project_list\">\n" .
-	      "<tr>\n";
-	$order ||= "project";
-	if ($order eq "project") {
-		@projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
-		print "<th>Project</th>\n";
-	} else {
-		print "<th>" .
-		      $cgi->a({-href => href(project=>undef, order=>'project'),
-		               -class => "header"}, "Project") .
-		      "</th>\n";
+	git_project_list_body(\@list, $order);
+	git_footer_html();
+}
+
+sub git_forks {
+	my $order = $cgi->param('o');
+	if (defined $order && $order !~ m/project|descr|owner|age/) {
+		die_error(undef, "Unknown order parameter");
 	}
-	if ($order eq "descr") {
-		@projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
-		print "<th>Description</th>\n";
-	} else {
-		print "<th>" .
-		      $cgi->a({-href => href(project=>undef, order=>'descr'),
-		               -class => "header"}, "Description") .
-		      "</th>\n";
+
+	my @list = git_get_projects_list($project);
+	if (!@list) {
+		die_error(undef, "No forks found");
 	}
-	if ($order eq "owner") {
-		@projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
-		print "<th>Owner</th>\n";
-	} else {
-		print "<th>" .
-		      $cgi->a({-href => href(project=>undef, order=>'owner'),
-		               -class => "header"}, "Owner") .
-		      "</th>\n";
-	}
-	if ($order eq "age") {
-		@projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects;
-		print "<th>Last Change</th>\n";
-	} else {
-		print "<th>" .
-		      $cgi->a({-href => href(project=>undef, order=>'age'),
-		               -class => "header"}, "Last Change") .
-		      "</th>\n";
-	}
-	print "<th></th>\n" .
-	      "</tr>\n";
-	my $alternate = 1;
-	foreach my $pr (@projects) {
-		if ($alternate) {
-			print "<tr class=\"dark\">\n";
-		} else {
-			print "<tr class=\"light\">\n";
-		}
-		$alternate ^= 1;
-		print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-		                        -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
-		      "<td>" . esc_html($pr->{'descr'}) . "</td>\n" .
-		      "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
-		print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" .
-		      $pr->{'commit'}{'age_string'} . "</td>\n" .
-		      "<td class=\"link\">" .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
-		      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
-		      "</td>\n" .
-		      "</tr>\n";
-	}
-	print "</table>\n";
+
+	git_header_html();
+	git_print_page_nav('','');
+	git_print_header_div('summary', "$project forks");
+	git_project_list_body(\@list, $order);
 	git_footer_html();
 }
 
 sub git_project_index {
-	my @projects = git_get_projects_list();
+	my @projects = git_get_projects_list($project);
 
 	print $cgi->header(
 		-type => 'text/plain',
@@ -2356,17 +2755,14 @@
 
 	my $owner = git_get_project_owner($project);
 
-	my ($reflist, $refs) = git_get_refs_list();
+	my $refs = git_get_references();
+	my @taglist  = git_get_tags_list(15);
+	my @headlist = git_get_heads_list(15);
+	my @forklist;
+	my ($check_forks) = gitweb_check_feature('forks');
 
-	my @taglist;
-	my @headlist;
-	foreach my $ref (@$reflist) {
-		if ($ref->{'name'} =~ s!^heads/!!) {
-			push @headlist, $ref;
-		} else {
-			$ref->{'name'} =~ s!^tags/!!;
-			push @taglist, $ref;
-		}
+	if ($check_forks) {
+		@forklist = git_get_projects_list($project);
 	}
 
 	git_header_html();
@@ -2389,8 +2785,16 @@
 	}
 	print "</table>\n";
 
+	if (-s "$projectroot/$project/README.html") {
+		if (open my $fd, "$projectroot/$project/README.html") {
+			print "<div class=\"title\">readme</div>\n";
+			print $_ while (<$fd>);
+			close $fd;
+		}
+	}
+
 	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
-		git_get_head_hash($project)
+		git_get_head_hash($project), "--"
 		or die_error(undef, "Open git-rev-list failed");
 	my @revlist = map { chomp; $_ } <$fd>;
 	close $fd;
@@ -2410,6 +2814,13 @@
 		               $cgi->a({-href => href(action=>"heads")}, "..."));
 	}
 
+	if (@forklist) {
+		git_print_header_div('forks');
+		git_project_list_body(\@forklist, undef, 0, 15,
+		                      $cgi->a({-href => href(action=>"forks")}, "..."),
+				      'noheader');
+	}
+
 	git_footer_html();
 }
 
@@ -2440,6 +2851,7 @@
 	print "<div class=\"page_body\">";
 	my $comment = $tag{'comment'};
 	foreach my $line (@$comment) {
+		chomp($line);
 		print esc_html($line) . "<br/>\n";
 	}
 	print "</div>\n";
@@ -2467,7 +2879,8 @@
 	if ($ftype !~ "blob") {
 		die_error("400 Bad Request", "Object is not a blob");
 	}
-	open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base)
+	open ($fd, "-|", git_cmd(), "blame", '-p', '--',
+	      $file_name, $hash_base)
 		or die_error(undef, "Open git-blame failed");
 	git_header_html();
 	my $formats_nav =
@@ -2491,25 +2904,53 @@
 <table class="blame">
 <tr><th>Commit</th><th>Line</th><th>Data</th></tr>
 HTML
-	while (<$fd>) {
-		/^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/;
-		my $full_rev = $1;
+	my %metainfo = ();
+	while (1) {
+		$_ = <$fd>;
+		last unless defined $_;
+		my ($full_rev, $orig_lineno, $lineno, $group_size) =
+		    /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
+		if (!exists $metainfo{$full_rev}) {
+			$metainfo{$full_rev} = {};
+		}
+		my $meta = $metainfo{$full_rev};
+		while (<$fd>) {
+			last if (s/^\t//);
+			if (/^(\S+) (.*)$/) {
+				$meta->{$1} = $2;
+			}
+		}
+		my $data = $_;
+		chomp($data);
 		my $rev = substr($full_rev, 0, 8);
-		my $lineno = $2;
-		my $data = $3;
-
-		if (!defined $last_rev) {
-			$last_rev = $full_rev;
-		} elsif ($last_rev ne $full_rev) {
-			$last_rev = $full_rev;
+		my $author = $meta->{'author'};
+		my %date = parse_date($meta->{'author-time'},
+				      $meta->{'author-tz'});
+		my $date = $date{'iso-tz'};
+		if ($group_size) {
 			$current_color = ++$current_color % $num_colors;
 		}
 		print "<tr class=\"$rev_color[$current_color]\">\n";
-		print "<td class=\"sha1\">" .
-			$cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)},
-			        esc_html($rev)) . "</td>\n";
-		print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" .
-		      esc_html($lineno) . "</a></td>\n";
+		if ($group_size) {
+			print "<td class=\"sha1\"";
+			print " title=\"". esc_html($author) . ", $date\"";
+			print " rowspan=\"$group_size\"" if ($group_size > 1);
+			print ">";
+			print $cgi->a({-href => href(action=>"commit",
+						     hash=>$full_rev,
+						     file_name=>$file_name)},
+				      esc_html($rev));
+			print "</td>\n";
+		}
+		my $blamed = href(action => 'blame',
+				  file_name => $meta->{'filename'},
+				  hash_base => $full_rev);
+		print "<td class=\"linenr\">";
+		print $cgi->a({ -href => "$blamed#l$orig_lineno",
+				-id => "l$lineno",
+				-class => "linenr" },
+			      esc_html($lineno));
+		print "</td>";
 		print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
 		print "</tr>\n";
 	}
@@ -2623,9 +3064,9 @@
 	git_print_page_nav('','', $head,undef,$head);
 	git_print_header_div('summary', $project);
 
-	my ($taglist) = git_get_refs_list("tags");
-	if (@$taglist) {
-		git_tags_body($taglist);
+	my @tagslist = git_get_tags_list();
+	if (@tagslist) {
+		git_tags_body(\@tagslist);
 	}
 	git_footer_html();
 }
@@ -2636,9 +3077,9 @@
 	git_print_page_nav('','', $head,undef,$head);
 	git_print_header_div('summary', $project);
 
-	my ($headlist) = git_get_refs_list("heads");
-	if (@$headlist) {
-		git_heads_body($headlist, $head);
+	my @headslist = git_get_heads_list();
+	if (@headslist) {
+		git_heads_body(\@headslist, $head);
 	}
 	git_footer_html();
 }
@@ -2751,7 +3192,7 @@
 		$nr++;
 		$line = untabify($line);
 		printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-		       $nr, $nr, $nr, esc_html($line);
+		       $nr, $nr, $nr, esc_html($line, -nbsp=>1);
 	}
 	close $fd
 		or print "Reading blob failed.\n";
@@ -2819,6 +3260,30 @@
 	print "<div class=\"page_body\">\n";
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 1;
+	# '..' (top directory) link if possible
+	if (defined $hash_base &&
+	    defined $file_name && $file_name =~ m![^/]+$!) {
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+
+		my $up = $file_name;
+		$up =~ s!/?[^/]+$!!;
+		undef $up unless $up;
+		# based on git_print_tree_entry
+		print '<td class="mode">' . mode_str('040000') . "</td>\n";
+		print '<td class="list">';
+		print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base,
+		                             file_name=>$up)},
+		              "..");
+		print "</td>\n";
+		print "<td class=\"link\"></td>\n";
+
+		print "</tr>\n";
+	}
 	foreach my $line (@entries) {
 		my %t = parse_ls_tree_line($line, -z => 1);
 
@@ -2881,7 +3346,7 @@
 	my $refs = git_get_references();
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash
+	open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--"
 		or die_error(undef, "Open git-rev-list failed");
 	my @revlist = map { chomp; $_ } <$fd>;
 	close $fd;
@@ -2920,7 +3385,7 @@
 		      "</div>\n";
 
 		print "<div class=\"log_body\">\n";
-		git_print_simplified_log($co{'comment'});
+		git_print_log($co{'comment'}, -final_empty_line=> 1);
 		print "</div>\n";
 	}
 	git_footer_html();
@@ -2938,7 +3403,8 @@
 	if (!defined $parent) {
 		$parent = "--root";
 	}
-	open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $parent, $hash
+	open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+		@diff_opts, $parent, $hash, "--"
 		or die_error(undef, "Open git-diff-tree failed");
 	my @difftree = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading git-diff-tree failed");
@@ -3043,7 +3509,8 @@
 	if (defined $hash_base && defined $hash_parent_base) {
 		if (defined $file_name) {
 			# read raw output
-			open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base,
+			open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+				$hash_parent_base, $hash_base,
 				"--", $file_name
 				or die_error(undef, "Open git-diff-tree failed");
 			@difftree = map { chomp; $_ } <$fd>;
@@ -3057,7 +3524,8 @@
 			# try to find filename from $hash
 
 			# read filtered raw output
-			open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $hash_parent_base, $hash_base
+			open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+				$hash_parent_base, $hash_base, "--"
 				or die_error(undef, "Open git-diff-tree failed");
 			@difftree =
 				# ':100644 100644 03b21826... 3b93d5e7... M	ls-files.c'
@@ -3127,7 +3595,8 @@
 		}
 
 		# open patch output
-		open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts, $hash_parent, $hash
+		open $fd, "-|", git_cmd(), "diff", '-p', @diff_opts,
+			$hash_parent, $hash, "--"
 			or die_error(undef, "Open git-diff failed");
 	} else  {
 		die_error('404 Not Found', "Missing one of the blob diff parameters")
@@ -3181,8 +3650,8 @@
 
 	} else {
 		while (my $line = <$fd>) {
-			$line =~ s!a/($hash|$hash_parent)!'a/'.esc_html($diffinfo{'from_file'})!eg;
-			$line =~ s!b/($hash|$hash_parent)!'b/'.esc_html($diffinfo{'to_file'})!eg;
+			$line =~ s!a/($hash|$hash_parent)!'a/'.esc_path($diffinfo{'from_file'})!eg;
+			$line =~ s!b/($hash|$hash_parent)!'b/'.esc_path($diffinfo{'to_file'})!eg;
 
 			print $line;
 
@@ -3204,6 +3673,51 @@
 	if (!%co) {
 		die_error(undef, "Unknown commit object");
 	}
+
+	# we need to prepare $formats_nav before any parameter munging
+	my $formats_nav;
+	if ($format eq 'html') {
+		$formats_nav =
+			$cgi->a({-href => href(action=>"commitdiff_plain",
+			                       hash=>$hash, hash_parent=>$hash_parent)},
+			        "raw");
+
+		if (defined $hash_parent) {
+			# commitdiff with two commits given
+			my $hash_parent_short = $hash_parent;
+			if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
+				$hash_parent_short = substr($hash_parent, 0, 7);
+			}
+			$formats_nav .=
+				' (from: ' .
+				$cgi->a({-href => href(action=>"commitdiff",
+				                       hash=>$hash_parent)},
+				        esc_html($hash_parent_short)) .
+				')';
+		} elsif (!$co{'parent'}) {
+			# --root commitdiff
+			$formats_nav .= ' (initial)';
+		} elsif (scalar @{$co{'parents'}} == 1) {
+			# single parent commit
+			$formats_nav .=
+				' (parent: ' .
+				$cgi->a({-href => href(action=>"commitdiff",
+				                       hash=>$co{'parent'})},
+				        esc_html(substr($co{'parent'}, 0, 7))) .
+				')';
+		} else {
+			# merge commit
+			$formats_nav .=
+				' (merge: ' .
+				join(' ', map {
+					$cgi->a({-href => href(action=>"commitdiff",
+					                       hash=>$_)},
+					        esc_html(substr($_, 0, 7)));
+				} @{$co{'parents'}} ) .
+				')';
+		}
+	}
+
 	if (!defined $hash_parent) {
 		$hash_parent = $co{'parent'} || '--root';
 	}
@@ -3213,7 +3727,8 @@
 	my @difftree;
 	if ($format eq 'html') {
 		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
-			"--patch-with-raw", "--full-index", $hash_parent, $hash
+			"--no-commit-id", "--patch-with-raw", "--full-index",
+			$hash_parent, $hash, "--"
 			or die_error(undef, "Open git-diff-tree failed");
 
 		while (chomp(my $line = <$fd>)) {
@@ -3224,7 +3739,7 @@
 
 	} elsif ($format eq 'plain') {
 		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
-			'-p', $hash_parent, $hash
+			'-p', $hash_parent, $hash, "--"
 			or die_error(undef, "Open git-diff-tree failed");
 
 	} else {
@@ -3241,19 +3756,17 @@
 	if ($format eq 'html') {
 		my $refs = git_get_references();
 		my $ref = format_ref_marker($refs, $co{'id'});
-		my $formats_nav =
-			$cgi->a({-href => href(action=>"commitdiff_plain",
-			                       hash=>$hash, hash_parent=>$hash_parent)},
-			        "raw");
 
 		git_header_html(undef, $expires);
 		git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
 		git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
 		git_print_authorship(\%co);
 		print "<div class=\"page_body\">\n";
-		print "<div class=\"log\">\n";
-		git_print_simplified_log($co{'comment'}, 1); # skip title
-		print "</div>\n"; # class="log"
+		if (@{$co{'comment'}} > 1) {
+			print "<div class=\"log\">\n";
+			git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);
+			print "</div>\n"; # class="log"
+		}
 
 	} elsif ($format eq 'plain') {
 		my $refs = git_get_references("tags");
@@ -3385,18 +3898,8 @@
 		die_error(undef, "Unknown commit object");
 	}
 
-	my $commit_search = 1;
-	my $author_search = 0;
-	my $committer_search = 0;
-	my $pickaxe_search = 0;
-	if ($searchtext =~ s/^author\\://i) {
-		$author_search = 1;
-	} elsif ($searchtext =~ s/^committer\\://i) {
-		$committer_search = 1;
-	} elsif ($searchtext =~ s/^pickaxe\\://i) {
-		$commit_search = 0;
-		$pickaxe_search = 1;
-
+	$searchtype ||= 'commit';
+	if ($searchtype eq 'pickaxe') {
 		# pickaxe may take all resources of your box and run for several minutes
 		# with every query - so decide by yourself how public you make this feature
 		my ($have_pickaxe) = gitweb_check_feature('pickaxe');
@@ -3404,23 +3907,26 @@
 			die_error('403 Permission denied', "Permission denied");
 		}
 	}
+
 	git_header_html();
 	git_print_page_nav('','', $hash,$co{'tree'},$hash);
 	git_print_header_div('commit', esc_html($co{'title'}), $hash);
 
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 1;
-	if ($commit_search) {
+	if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
 		$/ = "\0";
-		open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next;
+		open my $fd, "-|", git_cmd(), "rev-list",
+			"--header", "--parents", $hash, "--"
+			or next;
 		while (my $commit_text = <$fd>) {
 			if (!grep m/$searchtext/i, $commit_text) {
 				next;
 			}
-			if ($author_search && !grep m/\nauthor .*$searchtext/i, $commit_text) {
+			if ($searchtype eq 'author' && !grep m/\nauthor .*$searchtext/i, $commit_text) {
 				next;
 			}
-			if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
+			if ($searchtype eq 'committer' && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
 				next;
 			}
 			my @commit_lines = split "\n", $commit_text;
@@ -3462,7 +3968,7 @@
 		close $fd;
 	}
 
-	if ($pickaxe_search) {
+	if ($searchtype eq 'pickaxe') {
 		$/ = "\n";
 		my $git_command = git_cmd_str();
 		open my $fd, "-|", "$git_command rev-list $hash | " .
@@ -3502,7 +4008,7 @@
 						print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
 						                             hash=>$set{'id'}, file_name=>$set{'file'}),
 						              -class => "list"},
-						              "<span class=\"match\">" . esc_html($set{'file'}) . "</span>") .
+						              "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
 						      "<br/>\n";
 					}
 					print "</td>\n" .
@@ -3522,6 +4028,31 @@
 	git_footer_html();
 }
 
+sub git_search_help {
+	git_header_html();
+	git_print_page_nav('','', $hash,$hash,$hash);
+	print <<EOT;
+<dl>
+<dt><b>commit</b></dt>
+<dd>The commit messages and authorship information will be scanned for the given string.</dd>
+<dt><b>author</b></dt>
+<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.</dd>
+<dt><b>committer</b></dt>
+<dd>Name and e-mail of the committer and date of commit will be scanned for the given string.</dd>
+EOT
+	my ($have_pickaxe) = gitweb_check_feature('pickaxe');
+	if ($have_pickaxe) {
+		print <<EOT;
+<dt><b>pickaxe</b></dt>
+<dd>All commits that caused the string to appear or disappear from any file (changes that
+added, removed or "modified" the string) will be listed. This search can take a while and
+takes a lot of strain on the server, so please use it wisely.</dd>
+EOT
+	}
+	print "</dl>\n";
+	git_footer_html();
+}
+
 sub git_shortlog {
 	my $head = git_get_head_hash($project);
 	if (!defined $hash) {
@@ -3533,7 +4064,7 @@
 	my $refs = git_get_references();
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash
+	open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--"
 		or die_error(undef, "Open git-rev-list failed");
 	my @revlist = map { chomp; $_ } <$fd>;
 	close $fd;
@@ -3561,7 +4092,8 @@
 
 sub git_rss {
 	# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
-	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150", git_get_head_hash($project)
+	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150",
+		git_get_head_hash($project), "--"
 		or die_error(undef, "Open git-rev-list failed");
 	my @revlist = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading git-rev-list failed");
@@ -3585,7 +4117,7 @@
 		}
 		my %cd = parse_date($co{'committer_epoch'});
 		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
-			$co{'parent'}, $co{'id'}
+			$co{'parent'}, $co{'id'}, "--"
 			or next;
 		my @difftree = map { chomp; $_ } <$fd>;
 		close $fd
@@ -3611,7 +4143,7 @@
 			if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
 				next;
 			}
-			my $file = esc_html(unquote($7));
+			my $file = esc_path(unquote($7));
 			$file = to_utf8($file);
 			print "$file<br/>\n";
 		}
@@ -3630,7 +4162,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <opml version="1.0">
 <head>
-  <title>$site_name Git OPML Export</title>
+  <title>$site_name OPML Export</title>
 </head>
 <body>
 <outline text="git RSS feeds">
diff --git a/grep.c b/grep.c
index c411ddd..0fc078e 100644
--- a/grep.c
+++ b/grep.c
@@ -34,7 +34,7 @@
 	}
 }
 
-static struct grep_expr *compile_pattern_expr(struct grep_pat **);
+static struct grep_expr *compile_pattern_or(struct grep_pat **);
 static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
 {
 	struct grep_pat *p;
@@ -52,7 +52,7 @@
 		return x;
 	case GREP_OPEN_PAREN:
 		*list = p->next;
-		x = compile_pattern_expr(list);
+		x = compile_pattern_or(list);
 		if (!x)
 			return NULL;
 		if (!*list || (*list)->token != GREP_CLOSE_PAREN)
@@ -138,6 +138,9 @@
 {
 	struct grep_pat *p;
 
+	if (opt->all_match)
+		opt->extended = 1;
+
 	for (p = opt->pattern_list; p; p = p->next) {
 		switch (p->token) {
 		case GREP_PATTERN: /* atom */
@@ -309,40 +312,63 @@
 	return hit;
 }
 
-static int match_expr_eval(struct grep_opt *opt,
+static int match_expr_eval(struct grep_opt *o,
 			   struct grep_expr *x,
 			   char *bol, char *eol,
-			   enum grep_context ctx)
+			   enum grep_context ctx,
+			   int collect_hits)
 {
+	int h = 0;
+
 	switch (x->node) {
 	case GREP_NODE_ATOM:
-		return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
+		h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
 		break;
 	case GREP_NODE_NOT:
-		return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
+		h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
+		break;
 	case GREP_NODE_AND:
-		return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
-			match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+		if (!collect_hits)
+			return (match_expr_eval(o, x->u.binary.left,
+						bol, eol, ctx, 0) &&
+				match_expr_eval(o, x->u.binary.right,
+						bol, eol, ctx, 0));
+		h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+		h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
+		break;
 	case GREP_NODE_OR:
-		return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
-			match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+		if (!collect_hits)
+			return (match_expr_eval(o, x->u.binary.left,
+						bol, eol, ctx, 0) ||
+				match_expr_eval(o, x->u.binary.right,
+						bol, eol, ctx, 0));
+		h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+		x->u.binary.left->hit |= h;
+		h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
+		break;
+	default:
+		die("Unexpected node type (internal error) %d\n", x->node);
 	}
-	die("Unexpected node type (internal error) %d\n", x->node);
+	if (collect_hits)
+		x->hit |= h;
+	return h;
 }
 
 static int match_expr(struct grep_opt *opt, char *bol, char *eol,
-		      enum grep_context ctx)
+		      enum grep_context ctx, int collect_hits)
 {
 	struct grep_expr *x = opt->pattern_expression;
-	return match_expr_eval(opt, x, bol, eol, ctx);
+	return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
 }
 
 static int match_line(struct grep_opt *opt, char *bol, char *eol,
-		      enum grep_context ctx)
+		      enum grep_context ctx, int collect_hits)
 {
 	struct grep_pat *p;
 	if (opt->extended)
-		return match_expr(opt, bol, eol, ctx);
+		return match_expr(opt, bol, eol, ctx, collect_hits);
+
+	/* we do not call with collect_hits without being extended */
 	for (p = opt->pattern_list; p; p = p->next) {
 		if (match_one_pattern(opt, p, bol, eol, ctx))
 			return 1;
@@ -350,7 +376,8 @@
 	return 0;
 }
 
-int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+static int grep_buffer_1(struct grep_opt *opt, const char *name,
+			 char *buf, unsigned long size, int collect_hits)
 {
 	char *bol = buf;
 	unsigned long left = size;
@@ -386,7 +413,7 @@
 
 	while (left) {
 		char *eol, ch;
-		int hit = 0;
+		int hit;
 
 		eol = end_of_line(bol, &left);
 		ch = *eol;
@@ -395,9 +422,12 @@
 		if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
 			ctx = GREP_CONTEXT_BODY;
 
-		hit = match_line(opt, bol, eol, ctx);
+		hit = match_line(opt, bol, eol, ctx, collect_hits);
 		*eol = ch;
 
+		if (collect_hits)
+			goto next_line;
+
 		/* "grep -v -e foo -e bla" should list lines
 		 * that do not have either, so inversion should
 		 * be done outside.
@@ -477,6 +507,8 @@
 	}
 
 	free(prev);
+	if (collect_hits)
+		return 0;
 
 	if (opt->status_only)
 		return 0;
@@ -496,3 +528,49 @@
 	return !!last_hit;
 }
 
+static void clr_hit_marker(struct grep_expr *x)
+{
+	/* All-hit markers are meaningful only at the very top level
+	 * OR node.
+	 */
+	while (1) {
+		x->hit = 0;
+		if (x->node != GREP_NODE_OR)
+			return;
+		x->u.binary.left->hit = 0;
+		x = x->u.binary.right;
+	}
+}
+
+static int chk_hit_marker(struct grep_expr *x)
+{
+	/* Top level nodes have hit markers.  See if they all are hits */
+	while (1) {
+		if (x->node != GREP_NODE_OR)
+			return x->hit;
+		if (!x->u.binary.left->hit)
+			return 0;
+		x = x->u.binary.right;
+	}
+}
+
+int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+{
+	/*
+	 * we do not have to do the two-pass grep when we do not check
+	 * buffer-wide "all-match".
+	 */
+	if (!opt->all_match)
+		return grep_buffer_1(opt, name, buf, size, 0);
+
+	/* Otherwise the toplevel "or" terms hit a bit differently.
+	 * We first clear hit markers from them.
+	 */
+	clr_hit_marker(opt->pattern_expression);
+	grep_buffer_1(opt, name, buf, size, 1);
+
+	if (!chk_hit_marker(opt->pattern_expression))
+		return 0;
+
+	return grep_buffer_1(opt, name, buf, size, 0);
+}
diff --git a/grep.h b/grep.h
index af9098c..d252dd2 100644
--- a/grep.h
+++ b/grep.h
@@ -35,6 +35,7 @@
 
 struct grep_expr {
 	enum grep_expr_node node;
+	unsigned hit;
 	union {
 		struct grep_pat *atom;
 		struct grep_expr *unary;
@@ -59,6 +60,7 @@
 	unsigned count:1;
 	unsigned word_regexp:1;
 	unsigned fixed:1;
+	unsigned all_match:1;
 #define GREP_BINARY_DEFAULT	0
 #define GREP_BINARY_NOMATCH	1
 #define GREP_BINARY_TEXT	2
diff --git a/http-push.c b/http-push.c
index 670ff00..ecefdfd 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1864,7 +1864,7 @@
 static struct ref *local_refs, **local_tail;
 static struct ref *remote_refs, **remote_tail;
 
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct ref *ref;
 	int len = strlen(refname) + 1;
@@ -1913,7 +1913,7 @@
 static void get_local_heads(void)
 {
 	local_tail = &local_refs;
-	for_each_ref(one_local_ref);
+	for_each_ref(one_local_ref, NULL);
 }
 
 static void get_dav_remote_heads(void)
diff --git a/index-pack.c b/index-pack.c
index 80bc6cb..042aea8 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -6,84 +6,173 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
+#include <sys/time.h>
+#include <signal.h>
 
 static const char index_pack_usage[] =
-"git-index-pack [-o index-file] pack-file";
+"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
 
 struct object_entry
 {
 	unsigned long offset;
+	unsigned long size;
+	unsigned int hdr_size;
 	enum object_type type;
 	enum object_type real_type;
 	unsigned char sha1[20];
 };
 
-struct delta_entry
-{
-	struct object_entry *obj;
-	unsigned char base_sha1[20];
+union delta_base {
+	unsigned char sha1[20];
+	unsigned long offset;
 };
 
-static const char *pack_name;
-static unsigned char *pack_base;
-static unsigned long pack_size;
+/*
+ * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
+ * to memcmp() only the first 20 bytes.
+ */
+#define UNION_BASE_SZ	20
+
+struct delta_entry
+{
+	union delta_base base;
+	int obj_no;
+};
+
 static struct object_entry *objects;
 static struct delta_entry *deltas;
 static int nr_objects;
 static int nr_deltas;
+static int nr_resolved_deltas;
 
-static void open_pack_file(void)
+static int from_stdin;
+static int verbose;
+
+static volatile sig_atomic_t progress_update;
+
+static void progress_interval(int signum)
 {
-	int fd;
-	struct stat st;
+	progress_update = 1;
+}
 
-	fd = open(pack_name, O_RDONLY);
-	if (fd < 0)
-		die("cannot open packfile '%s': %s", pack_name,
-		    strerror(errno));
-	if (fstat(fd, &st)) {
-		int err = errno;
-		close(fd);
-		die("cannot fstat packfile '%s': %s", pack_name,
-		    strerror(err));
+static void setup_progress_signal(void)
+{
+	struct sigaction sa;
+	struct itimerval v;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = progress_interval;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGALRM, &sa, NULL);
+
+	v.it_interval.tv_sec = 1;
+	v.it_interval.tv_usec = 0;
+	v.it_value = v.it_interval;
+	setitimer(ITIMER_REAL, &v, NULL);
+
+}
+
+static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc)
+{
+	unsigned percent = n * 100 / total;
+	if (percent != last_pc || progress_update) {
+		fprintf(stderr, "%4u%% (%u/%u) done\r", percent, n, total);
+		progress_update = 0;
 	}
-	pack_size = st.st_size;
-	pack_base = mmap(NULL, pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
-	if (pack_base == MAP_FAILED) {
-		int err = errno;
-		close(fd);
-		die("cannot mmap packfile '%s': %s", pack_name,
-		    strerror(err));
+	return percent;
+}
+
+/* We always read in 4kB chunks. */
+static unsigned char input_buffer[4096];
+static unsigned long input_offset, input_len, consumed_bytes;
+static SHA_CTX input_ctx;
+static int input_fd, output_fd, mmap_fd;
+
+/* Discard current buffer used content. */
+static void flush()
+{
+	if (input_offset) {
+		if (output_fd >= 0)
+			write_or_die(output_fd, input_buffer, input_offset);
+		SHA1_Update(&input_ctx, input_buffer, input_offset);
+		memcpy(input_buffer, input_buffer + input_offset, input_len);
+		input_offset = 0;
 	}
-	close(fd);
+}
+
+/*
+ * Make sure at least "min" bytes are available in the buffer, and
+ * return the pointer to the buffer.
+ */
+static void *fill(int min)
+{
+	if (min <= input_len)
+		return input_buffer + input_offset;
+	if (min > sizeof(input_buffer))
+		die("cannot fill %d bytes", min);
+	flush();
+	do {
+		int ret = xread(input_fd, input_buffer + input_len,
+				sizeof(input_buffer) - input_len);
+		if (ret <= 0) {
+			if (!ret)
+				die("early EOF");
+			die("read error on input: %s", strerror(errno));
+		}
+		input_len += ret;
+	} while (input_len < min);
+	return input_buffer;
+}
+
+static void use(int bytes)
+{
+	if (bytes > input_len)
+		die("used more bytes than were available");
+	input_len -= bytes;
+	input_offset += bytes;
+	consumed_bytes += bytes;
+}
+
+static const char *open_pack_file(const char *pack_name)
+{
+	if (from_stdin) {
+		input_fd = 0;
+		if (!pack_name) {
+			static char tmpfile[PATH_MAX];
+			snprintf(tmpfile, sizeof(tmpfile),
+				 "%s/pack_XXXXXX", get_object_directory());
+			output_fd = mkstemp(tmpfile);
+			pack_name = xstrdup(tmpfile);
+		} else
+			output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
+		if (output_fd < 0)
+			die("unable to create %s: %s\n", pack_name, strerror(errno));
+		mmap_fd = output_fd;
+	} else {
+		input_fd = open(pack_name, O_RDONLY);
+		if (input_fd < 0)
+			die("cannot open packfile '%s': %s",
+			    pack_name, strerror(errno));
+		output_fd = -1;
+		mmap_fd = input_fd;
+	}
+	SHA1_Init(&input_ctx);
+	return pack_name;
 }
 
 static void parse_pack_header(void)
 {
-	const struct pack_header *hdr;
-	unsigned char sha1[20];
-	SHA_CTX ctx;
-
-	/* Ensure there are enough bytes for the header and final SHA1 */
-	if (pack_size < sizeof(struct pack_header) + 20)
-		die("packfile '%s' is too small", pack_name);
+	struct pack_header *hdr = fill(sizeof(struct pack_header));
 
 	/* Header consistency check */
-	hdr = (void *)pack_base;
 	if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
-		die("packfile '%s' signature mismatch", pack_name);
+		die("pack signature mismatch");
 	if (!pack_version_ok(hdr->hdr_version))
-		die("packfile '%s' version %d unsupported",
-		    pack_name, ntohl(hdr->hdr_version));
+		die("pack version %d unsupported", ntohl(hdr->hdr_version));
 
 	nr_objects = ntohl(hdr->hdr_entries);
-
-	/* Check packfile integrity */
-	SHA1_Init(&ctx);
-	SHA1_Update(&ctx, pack_base, pack_size - 20);
-	SHA1_Final(sha1, &ctx);
-	if (hashcmp(sha1, pack_base + pack_size - 20))
-		die("packfile '%s' SHA1 mismatch", pack_name);
+	use(sizeof(struct pack_header));
 }
 
 static void bad_object(unsigned long offset, const char *format,
@@ -97,90 +186,124 @@
 	va_start(params, format);
 	vsnprintf(buf, sizeof(buf), format, params);
 	va_end(params);
-	die("packfile '%s': bad object at offset %lu: %s",
-	    pack_name, offset, buf);
+	die("pack has bad object at offset %lu: %s", offset, buf);
 }
 
-static void *unpack_entry_data(unsigned long offset,
-			       unsigned long *current_pos, unsigned long size)
+static void *unpack_entry_data(unsigned long offset, unsigned long size)
 {
-	unsigned long pack_limit = pack_size - 20;
-	unsigned long pos = *current_pos;
 	z_stream stream;
 	void *buf = xmalloc(size);
 
 	memset(&stream, 0, sizeof(stream));
 	stream.next_out = buf;
 	stream.avail_out = size;
-	stream.next_in = pack_base + pos;
-	stream.avail_in = pack_limit - pos;
+	stream.next_in = fill(1);
+	stream.avail_in = input_len;
 	inflateInit(&stream);
 
 	for (;;) {
 		int ret = inflate(&stream, 0);
-		if (ret == Z_STREAM_END)
+		use(input_len - stream.avail_in);
+		if (stream.total_out == size && ret == Z_STREAM_END)
 			break;
 		if (ret != Z_OK)
 			bad_object(offset, "inflate returned %d", ret);
+		stream.next_in = fill(1);
+		stream.avail_in = input_len;
 	}
 	inflateEnd(&stream);
-	if (stream.total_out != size)
-		bad_object(offset, "size mismatch (expected %lu, got %lu)",
-			   size, stream.total_out);
-	*current_pos = pack_limit - stream.avail_in;
 	return buf;
 }
 
-static void *unpack_raw_entry(unsigned long offset,
-			      enum object_type *obj_type,
-			      unsigned long *obj_size,
-			      unsigned char *delta_base,
-			      unsigned long *next_obj_offset)
+static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
 {
-	unsigned long pack_limit = pack_size - 20;
-	unsigned long pos = offset;
-	unsigned char c;
-	unsigned long size;
+	unsigned char *p, c;
+	unsigned long size, base_offset;
 	unsigned shift;
-	enum object_type type;
-	void *data;
 
-	c = pack_base[pos++];
-	type = (c >> 4) & 7;
+	obj->offset = consumed_bytes;
+
+	p = fill(1);
+	c = *p;
+	use(1);
+	obj->type = (c >> 4) & 7;
 	size = (c & 15);
 	shift = 4;
 	while (c & 0x80) {
-		if (pos >= pack_limit)
-			bad_object(offset, "object extends past end of pack");
-		c = pack_base[pos++];
+		p = fill(1);
+		c = *p;
+		use(1);
 		size += (c & 0x7fUL) << shift;
 		shift += 7;
 	}
+	obj->size = size;
 
-	switch (type) {
-	case OBJ_DELTA:
-		if (pos + 20 >= pack_limit)
-			bad_object(offset, "object extends past end of pack");
-		hashcpy(delta_base, pack_base + pos);
-		pos += 20;
-		/* fallthru */
+	switch (obj->type) {
+	case OBJ_REF_DELTA:
+		hashcpy(delta_base->sha1, fill(20));
+		use(20);
+		break;
+	case OBJ_OFS_DELTA:
+		memset(delta_base, 0, sizeof(*delta_base));
+		p = fill(1);
+		c = *p;
+		use(1);
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || base_offset & ~(~0UL >> 7))
+				bad_object(obj->offset, "offset value overflow for delta base object");
+			p = fill(1);
+			c = *p;
+			use(1);
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		delta_base->offset = obj->offset - base_offset;
+		if (delta_base->offset >= obj->offset)
+			bad_object(obj->offset, "delta base offset is out of bound");
+		break;
 	case OBJ_COMMIT:
 	case OBJ_TREE:
 	case OBJ_BLOB:
 	case OBJ_TAG:
-		data = unpack_entry_data(offset, &pos, size);
 		break;
 	default:
-		bad_object(offset, "bad object type %d", type);
+		bad_object(obj->offset, "bad object type %d", obj->type);
 	}
+	obj->hdr_size = consumed_bytes - obj->offset;
 
-	*obj_type = type;
-	*obj_size = size;
-	*next_obj_offset = pos;
+	return unpack_entry_data(obj->offset, obj->size);
+}
+
+static void *get_data_from_pack(struct object_entry *obj)
+{
+	unsigned long from = obj[0].offset + obj[0].hdr_size;
+	unsigned long len = obj[1].offset - from;
+	unsigned pg_offset = from % getpagesize();
+	unsigned char *map, *data;
+	z_stream stream;
+	int st;
+
+	map = mmap(NULL, len + pg_offset, PROT_READ, MAP_PRIVATE,
+		   mmap_fd, from - pg_offset);
+	if (map == MAP_FAILED)
+		die("cannot mmap pack file: %s", strerror(errno));
+	data = xmalloc(obj->size);
+	memset(&stream, 0, sizeof(stream));
+	stream.next_out = data;
+	stream.avail_out = obj->size;
+	stream.next_in = map + pg_offset;
+	stream.avail_in = len;
+	inflateInit(&stream);
+	while ((st = inflate(&stream, Z_FINISH)) == Z_OK);
+	inflateEnd(&stream);
+	if (st != Z_STREAM_END || stream.total_out != obj->size)
+		die("serious inflate inconsistency");
+	munmap(map, len + pg_offset);
 	return data;
 }
 
-static int find_delta(const unsigned char *base_sha1)
+static int find_delta(const union delta_base *base)
 {
 	int first = 0, last = nr_deltas;
 
@@ -189,7 +312,7 @@
                 struct delta_entry *delta = &deltas[next];
                 int cmp;
 
-                cmp = hashcmp(base_sha1, delta->base_sha1);
+                cmp = memcmp(base, &delta->base, UNION_BASE_SZ);
                 if (!cmp)
                         return next;
                 if (cmp < 0) {
@@ -201,18 +324,18 @@
         return -first-1;
 }
 
-static int find_deltas_based_on_sha1(const unsigned char *base_sha1,
-				     int *first_index, int *last_index)
+static int find_delta_children(const union delta_base *base,
+			       int *first_index, int *last_index)
 {
-	int first = find_delta(base_sha1);
+	int first = find_delta(base);
 	int last = first;
 	int end = nr_deltas - 1;
 
 	if (first < 0)
 		return -1;
-	while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1))
+	while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
 		--first;
-	while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1))
+	while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
 		++last;
 	*first_index = first;
 	*last_index = last;
@@ -244,33 +367,46 @@
 	SHA1_Final(sha1, &ctx);
 }
 
-static void resolve_delta(struct delta_entry *delta, void *base_data,
+static void resolve_delta(struct object_entry *delta_obj, void *base_data,
 			  unsigned long base_size, enum object_type type)
 {
-	struct object_entry *obj = delta->obj;
 	void *delta_data;
 	unsigned long delta_size;
 	void *result;
 	unsigned long result_size;
-	enum object_type delta_type;
-	unsigned char base_sha1[20];
-	unsigned long next_obj_offset;
+	union delta_base delta_base;
 	int j, first, last;
 
-	obj->real_type = type;
-	delta_data = unpack_raw_entry(obj->offset, &delta_type,
-				      &delta_size, base_sha1,
-				      &next_obj_offset);
+	delta_obj->real_type = type;
+	delta_data = get_data_from_pack(delta_obj);
+	delta_size = delta_obj->size;
 	result = patch_delta(base_data, base_size, delta_data, delta_size,
 			     &result_size);
 	free(delta_data);
 	if (!result)
-		bad_object(obj->offset, "failed to apply delta");
-	sha1_object(result, result_size, type, obj->sha1);
-	if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) {
-		for (j = first; j <= last; j++)
-			resolve_delta(&deltas[j], result, result_size, type);
+		bad_object(delta_obj->offset, "failed to apply delta");
+	sha1_object(result, result_size, type, delta_obj->sha1);
+	nr_resolved_deltas++;
+
+	hashcpy(delta_base.sha1, delta_obj->sha1);
+	if (!find_delta_children(&delta_base, &first, &last)) {
+		for (j = first; j <= last; j++) {
+			struct object_entry *child = objects + deltas[j].obj_no;
+			if (child->real_type == OBJ_REF_DELTA)
+				resolve_delta(child, result, result_size, type);
+		}
 	}
+
+	memset(&delta_base, 0, sizeof(delta_base));
+	delta_base.offset = delta_obj->offset;
+	if (!find_delta_children(&delta_base, &first, &last)) {
+		for (j = first; j <= last; j++) {
+			struct object_entry *child = objects + deltas[j].obj_no;
+			if (child->real_type == OBJ_OFS_DELTA)
+				resolve_delta(child, result, result_size, type);
+		}
+	}
+
 	free(result);
 }
 
@@ -278,41 +414,60 @@
 {
 	const struct delta_entry *delta_a = a;
 	const struct delta_entry *delta_b = b;
-	return hashcmp(delta_a->base_sha1, delta_b->base_sha1);
+	return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ);
 }
 
-static void parse_pack_objects(void)
+/* Parse all objects and return the pack content SHA1 hash */
+static void parse_pack_objects(unsigned char *sha1)
 {
-	int i;
-	unsigned long offset = sizeof(struct pack_header);
-	unsigned char base_sha1[20];
+	int i, percent = -1;
+	struct delta_entry *delta = deltas;
 	void *data;
-	unsigned long data_size;
+	struct stat st;
 
 	/*
 	 * First pass:
 	 * - find locations of all objects;
 	 * - calculate SHA1 of all non-delta objects;
-	 * - remember base SHA1 for all deltas.
+	 * - remember base (SHA1 or offset) for all deltas.
 	 */
+	if (verbose)
+		fprintf(stderr, "Indexing %d objects.\n", nr_objects);
 	for (i = 0; i < nr_objects; i++) {
 		struct object_entry *obj = &objects[i];
-		obj->offset = offset;
-		data = unpack_raw_entry(offset, &obj->type, &data_size,
-					base_sha1, &offset);
+		data = unpack_raw_entry(obj, &delta->base);
 		obj->real_type = obj->type;
-		if (obj->type == OBJ_DELTA) {
-			struct delta_entry *delta = &deltas[nr_deltas++];
-			delta->obj = obj;
-			hashcpy(delta->base_sha1, base_sha1);
+		if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
+			nr_deltas++;
+			delta->obj_no = i;
+			delta++;
 		} else
-			sha1_object(data, data_size, obj->type, obj->sha1);
+			sha1_object(data, obj->size, obj->type, obj->sha1);
 		free(data);
+		if (verbose)
+			percent = display_progress(i+1, nr_objects, percent);
 	}
-	if (offset != pack_size - 20)
-		die("packfile '%s' has junk at the end", pack_name);
+	objects[i].offset = consumed_bytes;
+	if (verbose)
+		fputc('\n', stderr);
 
-	/* Sort deltas by base SHA1 for fast searching */
+	/* Check pack integrity */
+	flush();
+	SHA1_Final(sha1, &input_ctx);
+	if (hashcmp(fill(20), sha1))
+		die("pack is corrupted (SHA1 mismatch)");
+	use(20);
+
+	/* If input_fd is a file, we should have reached its end now. */
+	if (fstat(input_fd, &st))
+		die("cannot fstat packfile: %s", strerror(errno));
+	if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
+		die("pack has junk at the end");
+
+	if (!nr_deltas)
+		return;
+
+	/* Sort deltas by base SHA1/offset for fast searching */
 	qsort(deltas, nr_deltas, sizeof(struct delta_entry),
 	      compare_delta_entry);
 
@@ -324,26 +479,189 @@
 	 *   recursively checking if the resulting object is used as a base
 	 *   for some more deltas.
 	 */
+	if (verbose)
+		fprintf(stderr, "Resolving %d deltas.\n", nr_deltas);
 	for (i = 0; i < nr_objects; i++) {
 		struct object_entry *obj = &objects[i];
+		union delta_base base;
+		int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
+
+		if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
+			continue;
+		hashcpy(base.sha1, obj->sha1);
+		ref = !find_delta_children(&base, &ref_first, &ref_last);
+		memset(&base, 0, sizeof(base));
+		base.offset = obj->offset;
+		ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
+		if (!ref && !ofs)
+			continue;
+		data = get_data_from_pack(obj);
+		if (ref)
+			for (j = ref_first; j <= ref_last; j++) {
+				struct object_entry *child = objects + deltas[j].obj_no;
+				if (child->real_type == OBJ_REF_DELTA)
+					resolve_delta(child, data,
+						      obj->size, obj->type);
+			}
+		if (ofs)
+			for (j = ofs_first; j <= ofs_last; j++) {
+				struct object_entry *child = objects + deltas[j].obj_no;
+				if (child->real_type == OBJ_OFS_DELTA)
+					resolve_delta(child, data,
+						      obj->size, obj->type);
+			}
+		free(data);
+		if (verbose)
+			percent = display_progress(nr_resolved_deltas,
+						   nr_deltas, percent);
+	}
+	if (verbose && nr_resolved_deltas == nr_deltas)
+		fputc('\n', stderr);
+}
+
+static int write_compressed(int fd, void *in, unsigned int size)
+{
+	z_stream stream;
+	unsigned long maxsize;
+	void *out;
+
+	memset(&stream, 0, sizeof(stream));
+	deflateInit(&stream, zlib_compression_level);
+	maxsize = deflateBound(&stream, size);
+	out = xmalloc(maxsize);
+
+	/* Compress it */
+	stream.next_in = in;
+	stream.avail_in = size;
+	stream.next_out = out;
+	stream.avail_out = maxsize;
+	while (deflate(&stream, Z_FINISH) == Z_OK);
+	deflateEnd(&stream);
+
+	size = stream.total_out;
+	write_or_die(fd, out, size);
+	free(out);
+	return size;
+}
+
+static void append_obj_to_pack(void *buf,
+			       unsigned long size, enum object_type type)
+{
+	struct object_entry *obj = &objects[nr_objects++];
+	unsigned char header[10];
+	unsigned long s = size;
+	int n = 0;
+	unsigned char c = (type << 4) | (s & 15);
+	s >>= 4;
+	while (s) {
+		header[n++] = c | 0x80;
+		c = s & 0x7f;
+		s >>= 7;
+	}
+	header[n++] = c;
+	write_or_die(output_fd, header, n);
+	obj[1].offset = obj[0].offset + n;
+	obj[1].offset += write_compressed(output_fd, buf, size);
+	sha1_object(buf, size, type, obj->sha1);
+}
+
+static int delta_pos_compare(const void *_a, const void *_b)
+{
+	struct delta_entry *a = *(struct delta_entry **)_a;
+	struct delta_entry *b = *(struct delta_entry **)_b;
+	return a->obj_no - b->obj_no;
+}
+
+static void fix_unresolved_deltas(int nr_unresolved)
+{
+	struct delta_entry **sorted_by_pos;
+	int i, n = 0, percent = -1;
+
+	/*
+	 * Since many unresolved deltas may well be themselves base objects
+	 * for more unresolved deltas, we really want to include the
+	 * smallest number of base objects that would cover as much delta
+	 * as possible by picking the
+	 * trunc deltas first, allowing for other deltas to resolve without
+	 * additional base objects.  Since most base objects are to be found
+	 * before deltas depending on them, a good heuristic is to start
+	 * resolving deltas in the same order as their position in the pack.
+	 */
+	sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos));
+	for (i = 0; i < nr_deltas; i++) {
+		if (objects[deltas[i].obj_no].real_type != OBJ_REF_DELTA)
+			continue;
+		sorted_by_pos[n++] = &deltas[i];
+	}
+	qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare);
+
+	for (i = 0; i < n; i++) {
+		struct delta_entry *d = sorted_by_pos[i];
+		void *data;
+		unsigned long size;
+		char type[10];
+		enum object_type obj_type;
 		int j, first, last;
 
-		if (obj->type == OBJ_DELTA)
+		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
 			continue;
-		if (find_deltas_based_on_sha1(obj->sha1, &first, &last))
+		data = read_sha1_file(d->base.sha1, type, &size);
+		if (!data)
 			continue;
-		data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
-					base_sha1, &offset);
-		for (j = first; j <= last; j++)
-			resolve_delta(&deltas[j], data, data_size, obj->type);
-		free(data);
-	}
+		if      (!strcmp(type, blob_type))   obj_type = OBJ_BLOB;
+		else if (!strcmp(type, tree_type))   obj_type = OBJ_TREE;
+		else if (!strcmp(type, commit_type)) obj_type = OBJ_COMMIT;
+		else if (!strcmp(type, tag_type))    obj_type = OBJ_TAG;
+		else die("base object %s is of type '%s'",
+			 sha1_to_hex(d->base.sha1), type);
 
-	/* Check for unresolved deltas */
-	for (i = 0; i < nr_deltas; i++) {
-		if (deltas[i].obj->real_type == OBJ_DELTA)
-			die("packfile '%s' has unresolved deltas",  pack_name);
+		find_delta_children(&d->base, &first, &last);
+		for (j = first; j <= last; j++) {
+			struct object_entry *child = objects + deltas[j].obj_no;
+			if (child->real_type == OBJ_REF_DELTA)
+				resolve_delta(child, data, size, obj_type);
+		}
+
+		append_obj_to_pack(data, size, obj_type);
+		free(data);
+		if (verbose)
+			percent = display_progress(nr_resolved_deltas,
+						   nr_deltas, percent);
 	}
+	free(sorted_by_pos);
+	if (verbose)
+		fputc('\n', stderr);
+}
+
+static void readjust_pack_header_and_sha1(unsigned char *sha1)
+{
+	struct pack_header hdr;
+	SHA_CTX ctx;
+	int size;
+
+	/* Rewrite pack header with updated object number */
+	if (lseek(output_fd, 0, SEEK_SET) != 0)
+		die("cannot seek back: %s", strerror(errno));
+	if (xread(output_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+		die("cannot read pack header back: %s", strerror(errno));
+	hdr.hdr_entries = htonl(nr_objects);
+	if (lseek(output_fd, 0, SEEK_SET) != 0)
+		die("cannot seek back: %s", strerror(errno));
+	write_or_die(output_fd, &hdr, sizeof(hdr));
+	if (lseek(output_fd, 0, SEEK_SET) != 0)
+		die("cannot seek back: %s", strerror(errno));
+
+	/* Recompute and store the new pack's SHA1 */
+	SHA1_Init(&ctx);
+	do {
+		unsigned char *buf[4096];
+		size = xread(output_fd, buf, sizeof(buf));
+		if (size < 0)
+			die("cannot read pack data back: %s", strerror(errno));
+		SHA1_Update(&ctx, buf, size);
+	} while (size > 0);
+	SHA1_Final(sha1, &ctx);
+	write_or_die(output_fd, sha1, 20);
 }
 
 static int sha1_compare(const void *_a, const void *_b)
@@ -353,12 +671,16 @@
 	return hashcmp(a->sha1, b->sha1);
 }
 
-static void write_index_file(const char *index_name, unsigned char *sha1)
+/*
+ * On entry *sha1 contains the pack content SHA1 hash, on exit it is
+ * the SHA1 hash of sorted object names.
+ */
+static const char *write_index_file(const char *index_name, unsigned char *sha1)
 {
 	struct sha1file *f;
 	struct object_entry **sorted_by_sha, **list, **last;
 	unsigned int array[256];
-	int i;
+	int i, fd;
 	SHA_CTX ctx;
 
 	if (nr_objects) {
@@ -375,8 +697,19 @@
 	else
 		sorted_by_sha = list = last = NULL;
 
-	unlink(index_name);
-	f = sha1create("%s", index_name);
+	if (!index_name) {
+		static char tmpfile[PATH_MAX];
+		snprintf(tmpfile, sizeof(tmpfile),
+			 "%s/index_XXXXXX", get_object_directory());
+		fd = mkstemp(tmpfile);
+		index_name = xstrdup(tmpfile);
+	} else {
+		unlink(index_name);
+		fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+	}
+	if (fd < 0)
+		die("unable to create %s: %s", index_name, strerror(errno));
+	f = sha1fd(fd, index_name);
 
 	/*
 	 * Write the first-level table (the list is sorted,
@@ -412,24 +745,132 @@
 		sha1write(f, obj->sha1, 20);
 		SHA1_Update(&ctx, obj->sha1, 20);
 	}
-	sha1write(f, pack_base + pack_size - 20, 20);
+	sha1write(f, sha1, 20);
 	sha1close(f, NULL, 1);
 	free(sorted_by_sha);
 	SHA1_Final(sha1, &ctx);
+	return index_name;
+}
+
+static void final(const char *final_pack_name, const char *curr_pack_name,
+		  const char *final_index_name, const char *curr_index_name,
+		  const char *keep_name, const char *keep_msg,
+		  unsigned char *sha1)
+{
+	char *report = "pack";
+	char name[PATH_MAX];
+	int err;
+
+	if (!from_stdin) {
+		close(input_fd);
+	} else {
+		err = close(output_fd);
+		if (err)
+			die("error while closing pack file: %s", strerror(errno));
+		chmod(curr_pack_name, 0444);
+	}
+
+	if (keep_msg) {
+		int keep_fd, keep_msg_len = strlen(keep_msg);
+		if (!keep_name) {
+			snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
+				 get_object_directory(), sha1_to_hex(sha1));
+			keep_name = name;
+		}
+		keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+		if (keep_fd < 0) {
+			if (errno != EEXIST)
+				die("cannot write keep file");
+		} else {
+			if (keep_msg_len > 0) {
+				write_or_die(keep_fd, keep_msg, keep_msg_len);
+				write_or_die(keep_fd, "\n", 1);
+			}
+			close(keep_fd);
+			report = "keep";
+		}
+	}
+
+	if (final_pack_name != curr_pack_name) {
+		if (!final_pack_name) {
+			snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
+				 get_object_directory(), sha1_to_hex(sha1));
+			final_pack_name = name;
+		}
+		if (move_temp_to_file(curr_pack_name, final_pack_name))
+			die("cannot store pack file");
+	}
+
+	chmod(curr_index_name, 0444);
+	if (final_index_name != curr_index_name) {
+		if (!final_index_name) {
+			snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
+				 get_object_directory(), sha1_to_hex(sha1));
+			final_index_name = name;
+		}
+		if (move_temp_to_file(curr_index_name, final_index_name))
+			die("cannot store index file");
+	}
+
+	if (!from_stdin) {
+		printf("%s\n", sha1_to_hex(sha1));
+	} else {
+		char buf[48];
+		int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
+				   report, sha1_to_hex(sha1));
+		xwrite(1, buf, len);
+
+		/*
+		 * Let's just mimic git-unpack-objects here and write
+		 * the last part of the input buffer to stdout.
+		 */
+		while (input_len) {
+			err = xwrite(1, input_buffer + input_offset, input_len);
+			if (err <= 0)
+				break;
+			input_len -= err;
+			input_offset += err;
+		}
+	}
 }
 
 int main(int argc, char **argv)
 {
-	int i;
-	char *index_name = NULL;
-	char *index_name_buf = NULL;
+	int i, fix_thin_pack = 0;
+	const char *curr_pack, *pack_name = NULL;
+	const char *curr_index, *index_name = NULL;
+	const char *keep_name = NULL, *keep_msg = NULL;
+	char *index_name_buf = NULL, *keep_name_buf = NULL;
 	unsigned char sha1[20];
 
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
 		if (*arg == '-') {
-			if (!strcmp(arg, "-o")) {
+			if (!strcmp(arg, "--stdin")) {
+				from_stdin = 1;
+			} else if (!strcmp(arg, "--fix-thin")) {
+				fix_thin_pack = 1;
+			} else if (!strcmp(arg, "--keep")) {
+				keep_msg = "";
+			} else if (!strncmp(arg, "--keep=", 7)) {
+				keep_msg = arg + 7;
+			} else if (!strncmp(arg, "--pack_header=", 14)) {
+				struct pack_header *hdr;
+				char *c;
+
+				hdr = (struct pack_header *)input_buffer;
+				hdr->hdr_signature = htonl(PACK_SIGNATURE);
+				hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+				if (*c != ',')
+					die("bad %s", arg);
+				hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+				if (*c)
+					die("bad %s", arg);
+				input_len = sizeof(*hdr);
+			} else if (!strcmp(arg, "-v")) {
+				verbose = 1;
+			} else if (!strcmp(arg, "-o")) {
 				if (index_name || (i+1) >= argc)
 					usage(index_pack_usage);
 				index_name = argv[++i];
@@ -443,9 +884,11 @@
 		pack_name = arg;
 	}
 
-	if (!pack_name)
+	if (!pack_name && !from_stdin)
 		usage(index_pack_usage);
-	if (!index_name) {
+	if (fix_thin_pack && !from_stdin)
+		die("--fix-thin cannot be used without --stdin");
+	if (!index_name && pack_name) {
 		int len = strlen(pack_name);
 		if (!has_extension(pack_name, ".pack"))
 			die("packfile name '%s' does not end with '.pack'",
@@ -455,18 +898,55 @@
 		strcpy(index_name_buf + len - 5, ".idx");
 		index_name = index_name_buf;
 	}
+	if (keep_msg && !keep_name && pack_name) {
+		int len = strlen(pack_name);
+		if (!has_extension(pack_name, ".pack"))
+			die("packfile name '%s' does not end with '.pack'",
+			    pack_name);
+		keep_name_buf = xmalloc(len);
+		memcpy(keep_name_buf, pack_name, len - 5);
+		strcpy(keep_name_buf + len - 5, ".keep");
+		keep_name = keep_name_buf;
+	}
 
-	open_pack_file();
+	curr_pack = open_pack_file(pack_name);
 	parse_pack_header();
-	objects = xcalloc(nr_objects, sizeof(struct object_entry));
-	deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
-	parse_pack_objects();
+	objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));
+	deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
+	if (verbose)
+		setup_progress_signal();
+	parse_pack_objects(sha1);
+	if (nr_deltas != nr_resolved_deltas) {
+		if (fix_thin_pack) {
+			int nr_unresolved = nr_deltas - nr_resolved_deltas;
+			int nr_objects_initial = nr_objects;
+			if (nr_unresolved <= 0)
+				die("confusion beyond insanity");
+			objects = xrealloc(objects,
+					   (nr_objects + nr_unresolved + 1)
+					   * sizeof(*objects));
+			fix_unresolved_deltas(nr_unresolved);
+			if (verbose)
+				fprintf(stderr, "%d objects were added to complete this thin pack.\n",
+					nr_objects - nr_objects_initial);
+			readjust_pack_header_and_sha1(sha1);
+		}
+		if (nr_deltas != nr_resolved_deltas)
+			die("pack has %d unresolved deltas",
+			    nr_deltas - nr_resolved_deltas);
+	} else {
+		/* Flush remaining pack final 20-byte SHA1. */
+		flush();
+	}
 	free(deltas);
-	write_index_file(index_name, sha1);
+	curr_index = write_index_file(index_name, sha1);
+	final(pack_name, curr_pack,
+		index_name, curr_index,
+		keep_name, keep_msg,
+		sha1);
 	free(objects);
 	free(index_name_buf);
-
-	printf("%s\n", sha1_to_hex(sha1));
+	free(keep_name_buf);
 
 	return 0;
 }
diff --git a/log-tree.c b/log-tree.c
index fbe1399..8787df5 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -252,26 +252,6 @@
 	return 1;
 }
 
-static int diff_root_tree(struct rev_info *opt,
-			  const unsigned char *new, const char *base)
-{
-	int retval;
-	void *tree;
-	struct tree_desc empty, real;
-
-	tree = read_object_with_reference(new, tree_type, &real.size, NULL);
-	if (!tree)
-		die("unable to read root tree (%s)", sha1_to_hex(new));
-	real.buf = tree;
-
-	empty.buf = "";
-	empty.size = 0;
-	retval = diff_tree(&empty, &real, base, &opt->diffopt);
-	free(tree);
-	log_tree_diff_flush(opt);
-	return retval;
-}
-
 static int do_diff_combined(struct rev_info *opt, struct commit *commit)
 {
 	unsigned const char *sha1 = commit->object.sha1;
@@ -297,8 +277,10 @@
 	/* Root commit? */
 	parents = commit->parents;
 	if (!parents) {
-		if (opt->show_root_diff)
-			diff_root_tree(opt, sha1, "");
+		if (opt->show_root_diff) {
+			diff_root_tree_sha1(sha1, "", &opt->diffopt);
+			log_tree_diff_flush(opt);
+		}
 		return !opt->loginfo;
 	}
 
diff --git a/merge-recursive.c b/merge-recursive.c
index c81048d..cd2cc77 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -98,7 +98,7 @@
 	if (commit->util)
 		printf("virtual %s\n", (char *)commit->util);
 	else {
-		printf("%s ", sha1_to_hex(commit->object.sha1));
+		printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 		if (parse_commit(commit) != 0)
 			printf("(bad commit)\n");
 		else {
@@ -427,8 +427,9 @@
 	return renames;
 }
 
-int update_stages(const char *path, struct diff_filespec *o,
-		struct diff_filespec *a, struct diff_filespec *b, int clear)
+static int update_stages(const char *path, struct diff_filespec *o,
+			 struct diff_filespec *a, struct diff_filespec *b,
+			 int clear)
 {
 	int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
 	if (clear)
@@ -468,10 +469,10 @@
 	return ret;
 }
 
-int remove_file(int clean, const char *path)
+static int remove_file(int clean, const char *path, int no_wd)
 {
 	int update_cache = index_only || clean;
-	int update_working_directory = !index_only;
+	int update_working_directory = !index_only && !no_wd;
 
 	if (update_cache) {
 		if (!cache_dirty)
@@ -480,8 +481,7 @@
 		if (remove_file_from_cache(path))
 			return -1;
 	}
-	if (update_working_directory)
-	{
+	if (update_working_directory) {
 		unlink(path);
 		if (errno != ENOENT || errno != EISDIR)
 			return -1;
@@ -537,11 +537,11 @@
 	}
 }
 
-void update_file_flags(const unsigned char *sha,
-		       unsigned mode,
-		       const char *path,
-		       int update_cache,
-		       int update_wd)
+static void update_file_flags(const unsigned char *sha,
+			      unsigned mode,
+			      const char *path,
+			      int update_cache,
+			      int update_wd)
 {
 	if (index_only)
 		update_wd = 0;
@@ -586,10 +586,10 @@
 		add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
 }
 
-void update_file(int clean,
-		const unsigned char *sha,
-		unsigned mode,
-		const char *path)
+static void update_file(int clean,
+			const unsigned char *sha,
+			unsigned mode,
+			const char *path)
 {
 	update_file_flags(sha, mode, path, index_only || clean, !index_only);
 }
@@ -724,13 +724,13 @@
 		dst_name1 = del[delp++] = unique_path(ren1_dst, branch1);
 		output("%s is a directory in %s adding as %s instead",
 		       ren1_dst, branch2, dst_name1);
-		remove_file(0, ren1_dst);
+		remove_file(0, ren1_dst, 0);
 	}
 	if (path_list_has_path(&current_directory_set, ren2_dst)) {
 		dst_name2 = del[delp++] = unique_path(ren2_dst, branch2);
 		output("%s is a directory in %s adding as %s instead",
 		       ren2_dst, branch1, dst_name2);
-		remove_file(0, ren2_dst);
+		remove_file(0, ren2_dst, 0);
 	}
 	update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
 	update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
@@ -743,7 +743,7 @@
 {
 	char *new_path = unique_path(ren1->pair->two->path, branch1);
 	output("Renaming %s to %s instead", ren1->pair->one->path, new_path);
-	remove_file(0, ren1->pair->two->path);
+	remove_file(0, ren1->pair->two->path, 0);
 	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
 	free(new_path);
 }
@@ -758,7 +758,7 @@
 	output("Renaming %s to %s and %s to %s instead",
 	       ren1->pair->one->path, new_path1,
 	       ren2->pair->one->path, new_path2);
-	remove_file(0, ren1->pair->two->path);
+	remove_file(0, ren1->pair->two->path, 0);
 	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
 	update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
 	free(new_path2);
@@ -856,7 +856,7 @@
 				conflict_rename_rename(ren1, branch1, ren2, branch2);
 			} else {
 				struct merge_file_info mfi;
-				remove_file(1, ren1_src);
+				remove_file(1, ren1_src, 1);
 				mfi = merge_file(ren1->pair->one,
 						 ren1->pair->two,
 						 ren2->pair->two,
@@ -889,7 +889,7 @@
 			struct diff_filespec src_other, dst_other;
 			int try_merge, stage = a_renames == renames1 ? 3: 2;
 
-			remove_file(1, ren1_src);
+			remove_file(1, ren1_src, 1);
 
 			hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
 			src_other.mode = ren1->src_entry->stages[stage].mode;
@@ -1007,7 +1007,8 @@
 			 * unchanged in the other */
 			if (a_sha)
 				output("Removing %s", path);
-			remove_file(1, path);
+			/* do not touch working file if it did not exist */
+			remove_file(1, path, !a_sha);
 		} else {
 			/* Deleted in one and changed in the other */
 			clean_merge = 0;
@@ -1054,7 +1055,7 @@
 			output("CONFLICT (%s): There is a directory with name %s in %s. "
 			       "Adding %s as %s",
 			       conf, path, other_branch, path, new_path);
-			remove_file(0, path);
+			remove_file(0, path, 0);
 			update_file(0, sha, mode, new_path);
 		} else {
 			output("Adding %s", path);
@@ -1082,7 +1083,7 @@
 			output("CONFLICT (add/add): File %s added non-identically "
 			       "in both branches. Adding as %s and %s instead.",
 			       path, new_path1, new_path2);
-			remove_file(0, path);
+			remove_file(0, path, 0);
 			update_file(0, a_sha, a_mode, new_path1);
 			update_file(0, b_sha, b_mode, new_path2);
 		}
@@ -1204,14 +1205,13 @@
  * Merge the commits h1 and h2, return the resulting virtual
  * commit object and a flag indicating the cleaness of the merge.
  */
-static
-int merge(struct commit *h1,
-			  struct commit *h2,
-			  const char *branch1,
-			  const char *branch2,
-			  int call_depth /* =0 */,
-			  struct commit *ancestor /* =None */,
-			  struct commit **result)
+static int merge(struct commit *h1,
+		 struct commit *h2,
+		 const char *branch1,
+		 const char *branch2,
+		 int call_depth /* =0 */,
+		 struct commit *ancestor /* =None */,
+		 struct commit **result)
 {
 	struct commit_list *ca = NULL, *iter;
 	struct commit *merged_common_ancestors;
diff --git a/object.c b/object.c
index 9281300..de244e2 100644
--- a/object.c
+++ b/object.c
@@ -138,42 +138,56 @@
 	return obj;
 }
 
+struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p)
+{
+	struct object *obj;
+	int eaten = 0;
+
+	if (!strcmp(type, blob_type)) {
+		struct blob *blob = lookup_blob(sha1);
+		parse_blob_buffer(blob, buffer, size);
+		obj = &blob->object;
+	} else if (!strcmp(type, tree_type)) {
+		struct tree *tree = lookup_tree(sha1);
+		obj = &tree->object;
+		if (!tree->object.parsed) {
+			parse_tree_buffer(tree, buffer, size);
+			eaten = 1;
+		}
+	} else if (!strcmp(type, commit_type)) {
+		struct commit *commit = lookup_commit(sha1);
+		parse_commit_buffer(commit, buffer, size);
+		if (!commit->buffer) {
+			commit->buffer = buffer;
+			eaten = 1;
+		}
+		obj = &commit->object;
+	} else if (!strcmp(type, tag_type)) {
+		struct tag *tag = lookup_tag(sha1);
+		parse_tag_buffer(tag, buffer, size);
+		obj = &tag->object;
+	} else {
+		obj = NULL;
+	}
+	*eaten_p = eaten;
+	return obj;
+}
+
 struct object *parse_object(const unsigned char *sha1)
 {
 	unsigned long size;
 	char type[20];
+	int eaten;
 	void *buffer = read_sha1_file(sha1, type, &size);
+
 	if (buffer) {
 		struct object *obj;
 		if (check_sha1_signature(sha1, buffer, size, type) < 0)
 			printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
-		if (!strcmp(type, blob_type)) {
-			struct blob *blob = lookup_blob(sha1);
-			parse_blob_buffer(blob, buffer, size);
-			obj = &blob->object;
-		} else if (!strcmp(type, tree_type)) {
-			struct tree *tree = lookup_tree(sha1);
-			obj = &tree->object;
-			if (!tree->object.parsed) {
-				parse_tree_buffer(tree, buffer, size);
-				buffer = NULL;
-			}
-		} else if (!strcmp(type, commit_type)) {
-			struct commit *commit = lookup_commit(sha1);
-			parse_commit_buffer(commit, buffer, size);
-			if (!commit->buffer) {
-				commit->buffer = buffer;
-				buffer = NULL;
-			}
-			obj = &commit->object;
-		} else if (!strcmp(type, tag_type)) {
-			struct tag *tag = lookup_tag(sha1);
-			parse_tag_buffer(tag, buffer, size);
-			obj = &tag->object;
-		} else {
-			obj = NULL;
-		}
-		free(buffer);
+
+		obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+		if (!eaten)
+			free(buffer);
 		return obj;
 	}
 	return NULL;
diff --git a/object.h b/object.h
index 3d4ff46..caee733 100644
--- a/object.h
+++ b/object.h
@@ -59,6 +59,12 @@
 /** Returns the object, having parsed it to find out what it is. **/
 struct object *parse_object(const unsigned char *sha1);
 
+/* Given the result of read_sha1_file(), returns the object after
+ * parsing it.  eaten_p indicates if the object has a borrowed copy
+ * of buffer and the caller should not free() it.
+ */
+struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p);
+
 /** Returns the object, with potentially excess memory allocated. **/
 struct object *lookup_unknown_object(const unsigned  char *sha1);
 
diff --git a/pack.h b/pack.h
index eb07b03..4814800 100644
--- a/pack.h
+++ b/pack.h
@@ -16,7 +16,4 @@
 };
 
 extern int verify_pack(struct packed_git *, int);
-extern int check_reuse_pack_delta(struct packed_git *, unsigned long,
-				  unsigned char *, unsigned long *,
-				  enum object_type *);
 #endif
diff --git a/quote.c b/quote.c
index dc5c0a7..a418a0f 100644
--- a/quote.c
+++ b/quote.c
@@ -349,3 +349,41 @@
 	else
 		goto no_quote;
 }
+
+/* quoting as a string literal for other languages */
+
+void perl_quote_print(FILE *stream, const char *src)
+{
+	const char sq = '\'';
+	const char bq = '\\';
+	char c;
+
+	fputc(sq, stream);
+	while ((c = *src++)) {
+		if (c == sq || c == bq)
+			fputc(bq, stream);
+		fputc(c, stream);
+	}
+	fputc(sq, stream);
+}
+
+void python_quote_print(FILE *stream, const char *src)
+{
+	const char sq = '\'';
+	const char bq = '\\';
+	const char nl = '\n';
+	char c;
+
+	fputc(sq, stream);
+	while ((c = *src++)) {
+		if (c == nl) {
+			fputc(bq, stream);
+			fputc('n', stream);
+			continue;
+		}
+		if (c == sq || c == bq)
+			fputc(bq, stream);
+		fputc(c, stream);
+	}
+	fputc(sq, stream);
+}
diff --git a/quote.h b/quote.h
index 1a29e79..b55e699 100644
--- a/quote.h
+++ b/quote.h
@@ -52,4 +52,8 @@
 extern void write_name_quoted(const char *prefix, int prefix_len,
 			      const char *name, int quote, FILE *out);
 
+/* quoting as a string literal for other languages */
+extern void perl_quote_print(FILE *stream, const char *src);
+extern void python_quote_print(FILE *stream, const char *src);
+
 #endif
diff --git a/receive-pack.c b/receive-pack.c
index ea2dbd4..d56898c 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -1,20 +1,42 @@
 #include "cache.h"
+#include "pack.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "run-command.h"
+#include "exec_cmd.h"
 #include "commit.h"
 #include "object.h"
+#include <sys/wait.h>
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
-static const char *unpacker[] = { "unpack-objects", NULL };
-
+static int deny_non_fast_forwards = 0;
+static int unpack_limit = 5000;
 static int report_status;
 
 static char capabilities[] = "report-status";
 static int capabilities_sent;
 
-static int show_ref(const char *path, const unsigned char *sha1)
+static int receive_pack_config(const char *var, const char *value)
+{
+	git_default_config(var, value);
+
+	if (strcmp(var, "receive.denynonfastforwards") == 0)
+	{
+		deny_non_fast_forwards = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.unpacklimit") == 0)
+	{
+		unpack_limit = git_config_int(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	if (capabilities_sent)
 		packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
@@ -27,9 +49,9 @@
 
 static void write_head_info(void)
 {
-	for_each_ref(show_ref);
+	for_each_ref(show_ref, NULL);
 	if (!capabilities_sent)
-		show_ref("capabilities^{}", null_sha1);
+		show_ref("capabilities^{}", null_sha1, 0, NULL);
 
 }
 
@@ -43,34 +65,6 @@
 
 static struct command *commands;
 
-static int is_all_zeroes(const char *hex)
-{
-	int i;
-	for (i = 0; i < 40; i++)
-		if (*hex++ != '0')
-			return 0;
-	return 1;
-}
-
-static int verify_old_ref(const char *name, char *hex_contents)
-{
-	int fd, ret;
-	char buffer[60];
-
-	if (is_all_zeroes(hex_contents))
-		return 0;
-	fd = open(name, O_RDONLY);
-	if (fd < 0)
-		return -1;
-	ret = read(fd, buffer, 40);
-	close(fd);
-	if (ret != 40)
-		return -1;
-	if (memcmp(buffer, hex_contents, 40))
-		return -1;
-	return 0;
-}
-
 static char update_hook[] = "hooks/update";
 
 static int run_update_hook(const char *refname,
@@ -107,8 +101,8 @@
 	const char *name = cmd->ref_name;
 	unsigned char *old_sha1 = cmd->old_sha1;
 	unsigned char *new_sha1 = cmd->new_sha1;
-	char new_hex[60], *old_hex, *lock_name;
-	int newfd, namelen, written;
+	char new_hex[41], old_hex[41];
+	struct ref_lock *lock;
 
 	cmd->error_string = NULL;
 	if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) {
@@ -117,13 +111,8 @@
 			     name);
 	}
 
-	namelen = strlen(name);
-	lock_name = xmalloc(namelen + 10);
-	memcpy(lock_name, name, namelen);
-	memcpy(lock_name + namelen, ".lock", 6);
-
 	strcpy(new_hex, sha1_to_hex(new_sha1));
-	old_hex = sha1_to_hex(old_sha1);
+	strcpy(old_hex, sha1_to_hex(old_sha1));
 	if (!has_sha1_file(new_sha1)) {
 		cmd->error_string = "bad pack";
 		return error("unpack should have generated %s, "
@@ -144,47 +133,20 @@
 			return error("denying non-fast forward;"
 				     " you should pull first");
 	}
-	safe_create_leading_directories(lock_name);
-
-	newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
-	if (newfd < 0) {
-		cmd->error_string = "can't lock";
-		return error("unable to create %s (%s)",
-			     lock_name, strerror(errno));
-	}
-
-	/* Write the ref with an ending '\n' */
-	new_hex[40] = '\n';
-	new_hex[41] = 0;
-	written = write(newfd, new_hex, 41);
-	/* Remove the '\n' again */
-	new_hex[40] = 0;
-
-	close(newfd);
-	if (written != 41) {
-		unlink(lock_name);
-		cmd->error_string = "can't write";
-		return error("unable to write %s", lock_name);
-	}
-	if (verify_old_ref(name, old_hex) < 0) {
-		unlink(lock_name);
-		cmd->error_string = "raced";
-		return error("%s changed during push", name);
-	}
 	if (run_update_hook(name, old_hex, new_hex)) {
-		unlink(lock_name);
 		cmd->error_string = "hook declined";
 		return error("hook declined to update %s", name);
 	}
-	else if (rename(lock_name, name) < 0) {
-		unlink(lock_name);
-		cmd->error_string = "can't rename";
-		return error("unable to replace %s", name);
+
+	lock = lock_any_ref_for_update(name, old_sha1);
+	if (!lock) {
+		cmd->error_string = "failed to lock";
+		return error("failed to lock %s", name);
 	}
-	else {
-		fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
-		return 0;
-	}
+	write_ref_sha1(lock, new_sha1, "push");
+
+	fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
+	return 0;
 }
 
 static char update_post_hook[] = "hooks/post-update";
@@ -273,29 +235,127 @@
 	}
 }
 
-static const char *unpack(int *error_code)
+static const char *parse_pack_header(struct pack_header *hdr)
 {
-	int code = run_command_v_opt(1, unpacker, RUN_GIT_CMD);
+	char *c = (char*)hdr;
+	ssize_t remaining = sizeof(struct pack_header);
+	do {
+		ssize_t r = xread(0, c, remaining);
+		if (r <= 0)
+			return "eof before pack header was fully read";
+		remaining -= r;
+		c += r;
+	} while (remaining > 0);
+	if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
+		return "protocol error (pack signature mismatch detected)";
+	if (!pack_version_ok(hdr->hdr_version))
+		return "protocol error (pack version unsupported)";
+	return NULL;
+}
 
-	*error_code = 0;
-	switch (code) {
-	case 0:
-		return NULL;
-	case -ERR_RUN_COMMAND_FORK:
-		return "unpack fork failed";
-	case -ERR_RUN_COMMAND_EXEC:
-		return "unpack execute failed";
-	case -ERR_RUN_COMMAND_WAITPID:
-		return "waitpid failed";
-	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-		return "waitpid is confused";
-	case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-		return "unpacker died of signal";
-	case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-		return "unpacker died strangely";
-	default:
-		*error_code = -code;
-		return "unpacker exited with error code";
+static const char *pack_lockfile;
+
+static const char *unpack(void)
+{
+	struct pack_header hdr;
+	const char *hdr_err;
+	char hdr_arg[38];
+
+	hdr_err = parse_pack_header(&hdr);
+	if (hdr_err)
+		return hdr_err;
+	snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%u,%u",
+			ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
+
+	if (ntohl(hdr.hdr_entries) < unpack_limit) {
+		int code;
+		const char *unpacker[3];
+		unpacker[0] = "unpack-objects";
+		unpacker[1] = hdr_arg;
+		unpacker[2] = NULL;
+		code = run_command_v_opt(1, unpacker, RUN_GIT_CMD);
+		switch (code) {
+		case 0:
+			return NULL;
+		case -ERR_RUN_COMMAND_FORK:
+			return "unpack fork failed";
+		case -ERR_RUN_COMMAND_EXEC:
+			return "unpack execute failed";
+		case -ERR_RUN_COMMAND_WAITPID:
+			return "waitpid failed";
+		case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
+			return "waitpid is confused";
+		case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
+			return "unpacker died of signal";
+		case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
+			return "unpacker died strangely";
+		default:
+			return "unpacker exited with error code";
+		}
+	} else {
+		const char *keeper[6];
+		int fd[2], s, len, status;
+		pid_t pid;
+		char keep_arg[256];
+		char packname[46];
+
+		s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
+		if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
+			strcpy(keep_arg + s, "localhost");
+
+		keeper[0] = "index-pack";
+		keeper[1] = "--stdin";
+		keeper[2] = "--fix-thin";
+		keeper[3] = hdr_arg;
+		keeper[4] = keep_arg;
+		keeper[5] = NULL;
+
+		if (pipe(fd) < 0)
+			return "index-pack pipe failed";
+		pid = fork();
+		if (pid < 0)
+			return "index-pack fork failed";
+		if (!pid) {
+			dup2(fd[1], 1);
+			close(fd[1]);
+			close(fd[0]);
+			execv_git_cmd(keeper);
+			die("execv of index-pack failed");
+		}
+		close(fd[1]);
+
+		/*
+		 * The first thing we expects from index-pack's output
+		 * is "pack\t%40s\n" or "keep\t%40s\n" (46 bytes) where
+		 * %40s is the newly created pack SHA1 name.  In the "keep"
+		 * case, we need it to remove the corresponding .keep file
+		 * later on.  If we don't get that then tough luck with it.
+		 */
+		for (len = 0;
+		     len < 46 && (s = xread(fd[0], packname+len, 46-len)) > 0;
+		     len += s);
+		close(fd[0]);
+		if (len == 46 && packname[45] == '\n' &&
+		    memcmp(packname, "keep\t", 5) == 0) {
+			char path[PATH_MAX];
+			packname[45] = 0;
+			snprintf(path, sizeof(path), "%s/pack/pack-%s.keep",
+				 get_object_directory(), packname + 5);
+			pack_lockfile = xstrdup(path);
+		}
+
+		/* Then wrap our index-pack process. */
+		while (waitpid(pid, &status, 0) < 0)
+			if (errno != EINTR)
+				return "waitpid failed";
+		if (WIFEXITED(status)) {
+			int code = WEXITSTATUS(status);
+			if (code)
+				return "index-pack exited with error code";
+			reprepare_packed_git();
+			return NULL;
+		}
+		return "index-pack abnormal exit";
 	}
 }
 
@@ -335,9 +395,12 @@
 	if (!dir)
 		usage(receive_pack_usage);
 
-	if(!enter_repo(dir, 0))
+	if (!enter_repo(dir, 0))
 		die("'%s': unable to chdir or not a git archive", dir);
 
+	setup_ident();
+	git_config(receive_pack_config);
+
 	write_head_info();
 
 	/* EOF */
@@ -345,10 +408,11 @@
 
 	read_head_info();
 	if (commands) {
-		int code;
-		const char *unpack_status = unpack(&code);
+		const char *unpack_status = unpack();
 		if (!unpack_status)
 			execute_commands();
+		if (pack_lockfile)
+			unlink(pack_lockfile);
 		if (report_status)
 			report(unpack_status);
 	}
diff --git a/refs.c b/refs.c
index 98327d7..f003a0b 100644
--- a/refs.c
+++ b/refs.c
@@ -3,15 +3,193 @@
 
 #include <errno.h>
 
+struct ref_list {
+	struct ref_list *next;
+	unsigned char flag; /* ISSYMREF? ISPACKED? */
+	unsigned char sha1[20];
+	char name[FLEX_ARRAY];
+};
+
+static const char *parse_ref_line(char *line, unsigned char *sha1)
+{
+	/*
+	 * 42: the answer to everything.
+	 *
+	 * In this case, it happens to be the answer to
+	 *  40 (length of sha1 hex representation)
+	 *  +1 (space in between hex and name)
+	 *  +1 (newline at the end of the line)
+	 */
+	int len = strlen(line) - 42;
+
+	if (len <= 0)
+		return NULL;
+	if (get_sha1_hex(line, sha1) < 0)
+		return NULL;
+	if (!isspace(line[40]))
+		return NULL;
+	line += 41;
+	if (isspace(*line))
+		return NULL;
+	if (line[len] != '\n')
+		return NULL;
+	line[len] = 0;
+	return line;
+}
+
+static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
+				int flag, struct ref_list *list)
+{
+	int len;
+	struct ref_list **p = &list, *entry;
+
+	/* Find the place to insert the ref into.. */
+	while ((entry = *p) != NULL) {
+		int cmp = strcmp(entry->name, name);
+		if (cmp > 0)
+			break;
+
+		/* Same as existing entry? */
+		if (!cmp)
+			return list;
+		p = &entry->next;
+	}
+
+	/* Allocate it and add it in.. */
+	len = strlen(name) + 1;
+	entry = xmalloc(sizeof(struct ref_list) + len);
+	hashcpy(entry->sha1, sha1);
+	memcpy(entry->name, name, len);
+	entry->flag = flag;
+	entry->next = *p;
+	*p = entry;
+	return list;
+}
+
+/*
+ * Future: need to be in "struct repository"
+ * when doing a full libification.
+ */
+struct cached_refs {
+	char did_loose;
+	char did_packed;
+	struct ref_list *loose;
+	struct ref_list *packed;
+} cached_refs;
+
+static void free_ref_list(struct ref_list *list)
+{
+	struct ref_list *next;
+	for ( ; list; list = next) {
+		next = list->next;
+		free(list);
+	}
+}
+
+static void invalidate_cached_refs(void)
+{
+	struct cached_refs *ca = &cached_refs;
+
+	if (ca->did_loose && ca->loose)
+		free_ref_list(ca->loose);
+	if (ca->did_packed && ca->packed)
+		free_ref_list(ca->packed);
+	ca->loose = ca->packed = NULL;
+	ca->did_loose = ca->did_packed = 0;
+}
+
+static struct ref_list *get_packed_refs(void)
+{
+	if (!cached_refs.did_packed) {
+		struct ref_list *refs = NULL;
+		FILE *f = fopen(git_path("packed-refs"), "r");
+		if (f) {
+			struct ref_list *list = NULL;
+			char refline[PATH_MAX];
+			while (fgets(refline, sizeof(refline), f)) {
+				unsigned char sha1[20];
+				const char *name = parse_ref_line(refline, sha1);
+				if (!name)
+					continue;
+				list = add_ref(name, sha1, REF_ISPACKED, list);
+			}
+			fclose(f);
+			refs = list;
+		}
+		cached_refs.packed = refs;
+		cached_refs.did_packed = 1;
+	}
+	return cached_refs.packed;
+}
+
+static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+{
+	DIR *dir = opendir(git_path("%s", base));
+
+	if (dir) {
+		struct dirent *de;
+		int baselen = strlen(base);
+		char *ref = xmalloc(baselen + 257);
+
+		memcpy(ref, base, baselen);
+		if (baselen && base[baselen-1] != '/')
+			ref[baselen++] = '/';
+
+		while ((de = readdir(dir)) != NULL) {
+			unsigned char sha1[20];
+			struct stat st;
+			int flag;
+			int namelen;
+
+			if (de->d_name[0] == '.')
+				continue;
+			namelen = strlen(de->d_name);
+			if (namelen > 255)
+				continue;
+			if (has_extension(de->d_name, ".lock"))
+				continue;
+			memcpy(ref + baselen, de->d_name, namelen+1);
+			if (stat(git_path("%s", ref), &st) < 0)
+				continue;
+			if (S_ISDIR(st.st_mode)) {
+				list = get_ref_dir(ref, list);
+				continue;
+			}
+			if (!resolve_ref(ref, sha1, 1, &flag)) {
+				error("%s points nowhere!", ref);
+				continue;
+			}
+			list = add_ref(ref, sha1, flag, list);
+		}
+		free(ref);
+		closedir(dir);
+	}
+	return list;
+}
+
+static struct ref_list *get_loose_refs(void)
+{
+	if (!cached_refs.did_loose) {
+		cached_refs.loose = get_ref_dir("refs", NULL);
+		cached_refs.did_loose = 1;
+	}
+	return cached_refs.loose;
+}
+
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 
-const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
+const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
 	int depth = MAXDEPTH, len;
 	char buffer[256];
+	static char ref_buffer[256];
+
+	if (flag)
+		*flag = 0;
 
 	for (;;) {
+		const char *path = git_path("%s", ref);
 		struct stat st;
 		char *buf;
 		int fd;
@@ -27,17 +205,31 @@
 		 * reading.
 		 */
 		if (lstat(path, &st) < 0) {
+			struct ref_list *list = get_packed_refs();
+			while (list) {
+				if (!strcmp(ref, list->name)) {
+					hashcpy(sha1, list->sha1);
+					if (flag)
+						*flag |= REF_ISPACKED;
+					return ref;
+				}
+				list = list->next;
+			}
 			if (reading || errno != ENOENT)
 				return NULL;
 			hashclr(sha1);
-			return path;
+			return ref;
 		}
 
 		/* Follow "normalized" - ie "refs/.." symlinks by hand */
 		if (S_ISLNK(st.st_mode)) {
 			len = readlink(path, buffer, sizeof(buffer)-1);
 			if (len >= 5 && !memcmp("refs/", buffer, 5)) {
-				path = git_path("%.*s", len, buffer);
+				buffer[len] = 0;
+				strcpy(ref_buffer, buffer);
+				ref = ref_buffer;
+				if (flag)
+					*flag |= REF_ISSYMREF;
 				continue;
 			}
 		}
@@ -68,19 +260,24 @@
 		while (len && isspace(*buf))
 			buf++, len--;
 		while (len && isspace(buf[len-1]))
-			buf[--len] = 0;
-		path = git_path("%.*s", len, buf);
+			len--;
+		buf[len] = 0;
+		memcpy(ref_buffer, buf, len + 1);
+		ref = ref_buffer;
+		if (flag)
+			*flag |= REF_ISSYMREF;
 	}
 	if (len < 40 || get_sha1_hex(buffer, sha1))
 		return NULL;
-	return path;
+	return ref;
 }
 
-int create_symref(const char *git_HEAD, const char *refs_heads_master)
+int create_symref(const char *ref_target, const char *refs_heads_master)
 {
 	const char *lockpath;
 	char ref[1000];
 	int fd, len, written;
+	const char *git_HEAD = git_path("%s", ref_target);
 
 #ifndef NO_SYMLINK_HEAD
 	if (prefer_symlink_refs) {
@@ -118,104 +315,101 @@
 	return 0;
 }
 
-int read_ref(const char *filename, unsigned char *sha1)
+int read_ref(const char *ref, unsigned char *sha1)
 {
-	if (resolve_ref(filename, sha1, 1))
+	if (resolve_ref(ref, sha1, 1, NULL))
 		return 0;
 	return -1;
 }
 
-static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)
+static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
+			   void *cb_data)
 {
-	int retval = 0;
-	DIR *dir = opendir(git_path("%s", base));
+	int retval;
+	struct ref_list *packed = get_packed_refs();
+	struct ref_list *loose = get_loose_refs();
 
-	if (dir) {
-		struct dirent *de;
-		int baselen = strlen(base);
-		char *path = xmalloc(baselen + 257);
-
-		if (!strncmp(base, "./", 2)) {
-			base += 2;
-			baselen -= 2;
+	while (packed && loose) {
+		struct ref_list *entry;
+		int cmp = strcmp(packed->name, loose->name);
+		if (!cmp) {
+			packed = packed->next;
+			continue;
 		}
-		memcpy(path, base, baselen);
-		if (baselen && base[baselen-1] != '/')
-			path[baselen++] = '/';
-
-		while ((de = readdir(dir)) != NULL) {
-			unsigned char sha1[20];
-			struct stat st;
-			int namelen;
-
-			if (de->d_name[0] == '.')
-				continue;
-			namelen = strlen(de->d_name);
-			if (namelen > 255)
-				continue;
-			if (has_extension(de->d_name, ".lock"))
-				continue;
-			memcpy(path + baselen, de->d_name, namelen+1);
-			if (stat(git_path("%s", path), &st) < 0)
-				continue;
-			if (S_ISDIR(st.st_mode)) {
-				retval = do_for_each_ref(path, fn, trim);
-				if (retval)
-					break;
-				continue;
-			}
-			if (read_ref(git_path("%s", path), sha1) < 0) {
-				error("%s points nowhere!", path);
-				continue;
-			}
-			if (!has_sha1_file(sha1)) {
-				error("%s does not point to a valid "
-				      "commit object!", path);
-				continue;
-			}
-			retval = fn(path + trim, sha1);
-			if (retval)
-				break;
+		if (cmp > 0) {
+			entry = loose;
+			loose = loose->next;
+		} else {
+			entry = packed;
+			packed = packed->next;
 		}
-		free(path);
-		closedir(dir);
+		if (strncmp(base, entry->name, trim))
+			continue;
+		if (is_null_sha1(entry->sha1))
+			continue;
+		if (!has_sha1_file(entry->sha1)) {
+			error("%s does not point to a valid object!", entry->name);
+			continue;
+		}
+		retval = fn(entry->name + trim, entry->sha1,
+			    entry->flag, cb_data);
+		if (retval)
+			return retval;
 	}
-	return retval;
-}
 
-int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
-{
-	unsigned char sha1[20];
-	if (!read_ref(git_path("HEAD"), sha1))
-		return fn("HEAD", sha1);
+	packed = packed ? packed : loose;
+	while (packed) {
+		if (!strncmp(base, packed->name, trim)) {
+			retval = fn(packed->name + trim, packed->sha1,
+				    packed->flag, cb_data);
+			if (retval)
+				return retval;
+		}
+		packed = packed->next;
+	}
 	return 0;
 }
 
-int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int head_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs", fn, 0);
+	unsigned char sha1[20];
+	int flag;
+
+	if (resolve_ref("HEAD", sha1, 1, &flag))
+		return fn("HEAD", sha1, flag, cb_data);
+	return 0;
 }
 
-int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/tags", fn, 10);
+	return do_for_each_ref("refs/", fn, 0, cb_data);
 }
 
-int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/heads", fn, 11);
+	return do_for_each_ref("refs/tags/", fn, 10, cb_data);
 }
 
-int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/remotes", fn, 13);
+	return do_for_each_ref("refs/heads/", fn, 11, cb_data);
 }
 
+int for_each_remote_ref(each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
+}
+
+/* NEEDSWORK: This is only used by ssh-upload and it should go; the
+ * caller should do resolve_ref or read_ref like everybody else.  Or
+ * maybe everybody else should use get_ref_sha1() instead of doing
+ * read_ref().
+ */
 int get_ref_sha1(const char *ref, unsigned char *sha1)
 {
 	if (check_ref_format(ref))
 		return -1;
-	return read_ref(git_path("refs/%s", ref), sha1);
+	return read_ref(mkpath("refs/%s", ref), sha1);
 }
 
 /*
@@ -273,22 +467,13 @@
 static struct ref_lock *verify_lock(struct ref_lock *lock,
 	const unsigned char *old_sha1, int mustexist)
 {
-	char buf[40];
-	int nr, fd = open(lock->ref_file, O_RDONLY);
-	if (fd < 0 && (mustexist || errno != ENOENT)) {
-		error("Can't verify ref %s", lock->ref_file);
-		unlock_ref(lock);
-		return NULL;
-	}
-	nr = read(fd, buf, 40);
-	close(fd);
-	if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
-		error("Can't verify ref %s", lock->ref_file);
+	if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+		error("Can't verify ref %s", lock->ref_name);
 		unlock_ref(lock);
 		return NULL;
 	}
 	if (hashcmp(lock->old_sha1, old_sha1)) {
-		error("Ref %s is at %s but expected %s", lock->ref_file,
+		error("Ref %s is at %s but expected %s", lock->ref_name,
 			sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
 		unlock_ref(lock);
 		return NULL;
@@ -296,54 +481,223 @@
 	return lock;
 }
 
-static struct ref_lock *lock_ref_sha1_basic(const char *path,
-	int plen,
-	const unsigned char *old_sha1, int mustexist)
+static int remove_empty_dir_recursive(char *path, int len)
 {
-	const char *orig_path = path;
+	DIR *dir = opendir(path);
+	struct dirent *e;
+	int ret = 0;
+
+	if (!dir)
+		return -1;
+	if (path[len-1] != '/')
+		path[len++] = '/';
+	while ((e = readdir(dir)) != NULL) {
+		struct stat st;
+		int namlen;
+		if ((e->d_name[0] == '.') &&
+		    ((e->d_name[1] == 0) ||
+		     ((e->d_name[1] == '.') && e->d_name[2] == 0)))
+			continue; /* "." and ".." */
+
+		namlen = strlen(e->d_name);
+		if ((len + namlen < PATH_MAX) &&
+		    strcpy(path + len, e->d_name) &&
+		    !lstat(path, &st) &&
+		    S_ISDIR(st.st_mode) &&
+		    !remove_empty_dir_recursive(path, len + namlen))
+			continue; /* happy */
+
+		/* path too long, stat fails, or non-directory still exists */
+		ret = -1;
+		break;
+	}
+	closedir(dir);
+	if (!ret) {
+		path[len] = 0;
+		ret = rmdir(path);
+	}
+	return ret;
+}
+
+static int remove_empty_directories(char *file)
+{
+	/* we want to create a file but there is a directory there;
+	 * if that is an empty directory (or a directory that contains
+	 * only empty directories), remove them.
+	 */
+	char path[PATH_MAX];
+	int len = strlen(file);
+
+	if (len >= PATH_MAX) /* path too long ;-) */
+		return -1;
+	strcpy(path, file);
+	return remove_empty_dir_recursive(path, len);
+}
+
+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
+{
+	char *ref_file;
+	const char *orig_ref = ref;
 	struct ref_lock *lock;
 	struct stat st;
+	int last_errno = 0;
+	int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 
 	lock = xcalloc(1, sizeof(struct ref_lock));
 	lock->lock_fd = -1;
 
-	plen = strlen(path) - plen;
-	path = resolve_ref(path, lock->old_sha1, mustexist);
-	if (!path) {
-		int last_errno = errno;
-		error("unable to resolve reference %s: %s",
-			orig_path, strerror(errno));
-		unlock_ref(lock);
-		errno = last_errno;
-		return NULL;
+	ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
+	if (!ref && errno == EISDIR) {
+		/* we are trying to lock foo but we used to
+		 * have foo/bar which now does not exist;
+		 * it is normal for the empty directory 'foo'
+		 * to remain.
+		 */
+		ref_file = git_path("%s", orig_ref);
+		if (remove_empty_directories(ref_file)) {
+			last_errno = errno;
+			error("there are still refs under '%s'", orig_ref);
+			goto error_return;
+		}
+		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
 	}
+	if (!ref) {
+		last_errno = errno;
+		error("unable to resolve reference %s: %s",
+			orig_ref, strerror(errno));
+		goto error_return;
+	}
+	if (is_null_sha1(lock->old_sha1)) {
+		/* The ref did not exist and we are creating it.
+		 * Make sure there is no existing ref that is packed
+		 * whose name begins with our refname, nor a ref whose
+		 * name is a proper prefix of our refname.
+		 */
+		int namlen = strlen(ref); /* e.g. 'foo/bar' */
+		struct ref_list *list = get_packed_refs();
+		while (list) {
+			/* list->name could be 'foo' or 'foo/bar/baz' */
+			int len = strlen(list->name);
+			int cmplen = (namlen < len) ? namlen : len;
+			const char *lead = (namlen < len) ? list->name : ref;
+
+			if (!strncmp(ref, list->name, cmplen) &&
+			    lead[cmplen] == '/') {
+				error("'%s' exists; cannot create '%s'",
+				      list->name, ref);
+				goto error_return;
+			}
+			list = list->next;
+		}
+	}
+
 	lock->lk = xcalloc(1, sizeof(struct lock_file));
 
-	lock->ref_file = xstrdup(path);
-	lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen));
-	lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;
+	lock->ref_name = xstrdup(ref);
+	lock->log_file = xstrdup(git_path("logs/%s", ref));
+	ref_file = git_path("%s", ref);
+	lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
 
-	if (safe_create_leading_directories(lock->ref_file))
-		die("unable to create directory for %s", lock->ref_file);
-	lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1);
+	if (safe_create_leading_directories(ref_file)) {
+		last_errno = errno;
+		error("unable to create directory for %s", ref_file);
+		goto error_return;
+	}
+	lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
 	return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
+
+ error_return:
+	unlock_ref(lock);
+	errno = last_errno;
+	return NULL;
 }
 
-struct ref_lock *lock_ref_sha1(const char *ref,
-	const unsigned char *old_sha1, int mustexist)
+struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
 {
+	char refpath[PATH_MAX];
 	if (check_ref_format(ref))
 		return NULL;
-	return lock_ref_sha1_basic(git_path("refs/%s", ref),
-		5 + strlen(ref), old_sha1, mustexist);
+	strcpy(refpath, mkpath("refs/%s", ref));
+	return lock_ref_sha1_basic(refpath, old_sha1, NULL);
 }
 
-struct ref_lock *lock_any_ref_for_update(const char *ref,
-	const unsigned char *old_sha1, int mustexist)
+struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
 {
-	return lock_ref_sha1_basic(git_path("%s", ref),
-		strlen(ref), old_sha1, mustexist);
+	return lock_ref_sha1_basic(ref, old_sha1, NULL);
+}
+
+static struct lock_file packlock;
+
+static int repack_without_ref(const char *refname)
+{
+	struct ref_list *list, *packed_ref_list;
+	int fd;
+	int found = 0;
+
+	packed_ref_list = get_packed_refs();
+	for (list = packed_ref_list; list; list = list->next) {
+		if (!strcmp(refname, list->name)) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		return 0;
+	memset(&packlock, 0, sizeof(packlock));
+	fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
+	if (fd < 0)
+		return error("cannot delete '%s' from packed refs", refname);
+
+	for (list = packed_ref_list; list; list = list->next) {
+		char line[PATH_MAX + 100];
+		int len;
+
+		if (!strcmp(refname, list->name))
+			continue;
+		len = snprintf(line, sizeof(line), "%s %s\n",
+			       sha1_to_hex(list->sha1), list->name);
+		/* this should not happen but just being defensive */
+		if (len > sizeof(line))
+			die("too long a refname '%s'", list->name);
+		write_or_die(fd, line, len);
+	}
+	return commit_lock_file(&packlock);
+}
+
+int delete_ref(const char *refname, unsigned char *sha1)
+{
+	struct ref_lock *lock;
+	int err, i, ret = 0, flag = 0;
+
+	lock = lock_ref_sha1_basic(refname, sha1, &flag);
+	if (!lock)
+		return 1;
+	if (!(flag & REF_ISPACKED)) {
+		/* loose */
+		i = strlen(lock->lk->filename) - 5; /* .lock */
+		lock->lk->filename[i] = 0;
+		err = unlink(lock->lk->filename);
+		if (err) {
+			ret = 1;
+			error("unlink(%s) failed: %s",
+			      lock->lk->filename, strerror(errno));
+		}
+		lock->lk->filename[i] = '.';
+	}
+	/* removing the loose one could have resurrected an earlier
+	 * packed one.  Also, if it was not loose we need to repack
+	 * without it.
+	 */
+	ret |= repack_without_ref(refname);
+
+	err = unlink(lock->log_file);
+	if (err && errno != ENOENT)
+		fprintf(stderr, "warning: unlink(%s) failed: %s",
+			lock->log_file, strerror(errno));
+	invalidate_cached_refs();
+	unlock_ref(lock);
+	return ret;
 }
 
 void unlock_ref(struct ref_lock *lock)
@@ -354,7 +708,7 @@
 		if (lock->lk)
 			rollback_lock_file(lock->lk);
 	}
-	free(lock->ref_file);
+	free(lock->ref_name);
 	free(lock->log_file);
 	free(lock);
 }
@@ -367,7 +721,8 @@
 	char *logrec;
 	const char *committer;
 
-	if (log_all_ref_updates) {
+	if (log_all_ref_updates &&
+	    !strncmp(lock->ref_name, "refs/heads/", 11)) {
 		if (safe_create_leading_directories(lock->log_file) < 0)
 			return error("unable to create directory for %s",
 				lock->log_file);
@@ -376,10 +731,20 @@
 
 	logfd = open(lock->log_file, oflags, 0666);
 	if (logfd < 0) {
-		if (!log_all_ref_updates && errno == ENOENT)
+		if (!(oflags & O_CREAT) && errno == ENOENT)
 			return 0;
-		return error("Unable to append to %s: %s",
-			lock->log_file, strerror(errno));
+
+		if ((oflags & O_CREAT) && errno == EISDIR) {
+			if (remove_empty_directories(lock->log_file)) {
+				return error("There are still logs under '%s'",
+					     lock->log_file);
+			}
+			logfd = open(lock->log_file, oflags, 0666);
+		}
+
+		if (logfd < 0)
+			return error("Unable to append to %s: %s",
+				     lock->log_file, strerror(errno));
 	}
 
 	committer = git_committer_info(1);
@@ -426,12 +791,13 @@
 		unlock_ref(lock);
 		return -1;
 	}
+	invalidate_cached_refs();
 	if (log_ref_write(lock, sha1, logmsg) < 0) {
 		unlock_ref(lock);
 		return -1;
 	}
 	if (commit_lock_file(lock->lk)) {
-		error("Couldn't set %s", lock->ref_file);
+		error("Couldn't set %s", lock->ref_name);
 		unlock_ref(lock);
 		return -1;
 	}
@@ -440,7 +806,7 @@
 	return 0;
 }
 
-int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
+int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1)
 {
 	const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
 	char *tz_c;
@@ -473,7 +839,7 @@
 		if (!lastgt)
 			die("Log %s is corrupt.", logfile);
 		date = strtoul(lastgt + 1, &tz_c, 10);
-		if (date <= at_time) {
+		if (date <= at_time || cnt == 0) {
 			if (lastrec) {
 				if (get_sha1_hex(lastrec, logged_sha1))
 					die("Log %s is corrupt.", logfile);
@@ -504,6 +870,8 @@
 			return 0;
 		}
 		lastrec = rec;
+		if (cnt > 0)
+			cnt--;
 	}
 
 	rec = logdata;
diff --git a/refs.h b/refs.h
index 553155c..a57d437 100644
--- a/refs.h
+++ b/refs.h
@@ -2,7 +2,7 @@
 #define REFS_H
 
 struct ref_lock {
-	char *ref_file;
+	char *ref_name;
 	char *log_file;
 	struct lock_file *lk;
 	unsigned char old_sha1[20];
@@ -14,20 +14,23 @@
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
  */
-extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1));
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
+extern int head_ref(each_ref_fn, void *);
+extern int for_each_ref(each_ref_fn, void *);
+extern int for_each_tag_ref(each_ref_fn, void *);
+extern int for_each_branch_ref(each_ref_fn, void *);
+extern int for_each_remote_ref(each_ref_fn, void *);
 
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);
 
 /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
-extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist);
+extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
 
 /** Locks any ref (for 'HEAD' type refs). */
-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist);
+extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
 
 /** Release any lock taken but not written. **/
 extern void unlock_ref(struct ref_lock *lock);
@@ -36,7 +39,7 @@
 extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
 /** Reads log for the value of ref during at_time. **/
-extern int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1);
+extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1);
 
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
diff --git a/revision.c b/revision.c
index 36cdfcd..993bb66 100644
--- a/revision.c
+++ b/revision.c
@@ -465,7 +465,7 @@
 static int all_flags;
 static struct rev_info *all_revs;
 
-static int handle_one_ref(const char *path, const unsigned char *sha1)
+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *object = get_reference(all_revs, path, sha1, all_flags);
 	add_pending_object(all_revs, object, "");
@@ -476,7 +476,7 @@
 {
 	all_revs = revs;
 	all_flags = flags;
-	for_each_ref(handle_one_ref);
+	for_each_ref(handle_one_ref, NULL);
 }
 
 static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@ -657,6 +657,13 @@
 			return 0;
 		*dotdot = '^';
 	}
+	dotdot = strstr(arg, "^!");
+	if (dotdot && !dotdot[2]) {
+		*dotdot = 0;
+		if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
+			*dotdot = '^';
+	}
+
 	local_flags = 0;
 	if (*arg == '^') {
 		local_flags = UNINTERESTING;
@@ -729,6 +736,7 @@
 	int i, flags, seen_dashdash, show_merge;
 	const char **unrecognized = argv + 1;
 	int left = 1;
+	int all_match = 0;
 
 	/* First, search for "--" */
 	seen_dashdash = 0;
@@ -964,6 +972,10 @@
 				add_message_grep(revs, arg+7);
 				continue;
 			}
+			if (!strcmp(arg, "--all-match")) {
+				all_match = 1;
+				continue;
+			}
 
 			opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
 			if (opts > 0) {
@@ -1025,8 +1037,10 @@
 	if (diff_setup_done(&revs->diffopt) < 0)
 		die("diff_setup_done failed");
 
-	if (revs->grep_filter)
+	if (revs->grep_filter) {
+		revs->grep_filter->all_match = all_match;
 		compile_grep_patterns(revs->grep_filter);
+	}
 
 	return left;
 }
diff --git a/send-pack.c b/send-pack.c
index 5bb123a..4476666 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -29,6 +29,7 @@
 {
 	static const char *args[] = {
 		"pack-objects",
+		"--all-progress",
 		"--stdout",
 		NULL
 	};
@@ -215,7 +216,7 @@
 static struct ref *local_refs, **local_tail;
 static struct ref *remote_refs, **remote_tail;
 
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct ref *ref;
 	int len = strlen(refname) + 1;
@@ -230,7 +231,7 @@
 static void get_local_heads(void)
 {
 	local_tail = &local_refs;
-	for_each_ref(one_local_ref);
+	for_each_ref(one_local_ref, NULL);
 }
 
 static int receive_status(int in)
diff --git a/server-info.c b/server-info.c
index 2fb8f57..6cd38be 100644
--- a/server-info.c
+++ b/server-info.c
@@ -7,7 +7,7 @@
 /* refs */
 static FILE *info_ref_fp;
 
-static int add_info_ref(const char *path, const unsigned char *sha1)
+static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *o = parse_object(sha1);
 
@@ -34,7 +34,7 @@
 	info_ref_fp = fopen(path1, "w");
 	if (!info_ref_fp)
 		return error("unable to update %s", path0);
-	for_each_ref(add_info_ref);
+	for_each_ref(add_info_ref, NULL);
 	fclose(info_ref_fp);
 	rename(path1, path0);
 	free(path0);
diff --git a/setup.c b/setup.c
index 9a46a58..2afdba4 100644
--- a/setup.c
+++ b/setup.c
@@ -244,8 +244,6 @@
                repository_format_version = git_config_int(var, value);
 	else if (strcmp(var, "core.sharedrepository") == 0)
 		shared_repository = git_config_perm(var, value);
-	else if (strcmp(var, "receive.denynonfastforwards") == 0)
-		deny_non_fast_forwards = git_config_bool(var, value);
        return 0;
 }
 
diff --git a/sha1_file.c b/sha1_file.c
index c9fdaa3..09456d2 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -663,7 +663,7 @@
 	prepare_packed_git_run_once = 1;
 }
 
-static void reprepare_packed_git(void)
+void reprepare_packed_git(void)
 {
 	prepare_packed_git_run_once = 0;
 	prepare_packed_git();
@@ -877,26 +877,61 @@
 	return unpack_sha1_rest(&stream, hdr, *size);
 }
 
+static unsigned long get_delta_base(struct packed_git *p,
+				    unsigned long offset,
+				    enum object_type kind,
+				    unsigned long delta_obj_offset,
+				    unsigned long *base_obj_offset)
+{
+	unsigned char *base_info = (unsigned char *) p->pack_base + offset;
+	unsigned long base_offset;
+
+	/* there must be at least 20 bytes left regardless of delta type */
+	if (p->pack_size <= offset + 20)
+		die("truncated pack file");
+
+	if (kind == OBJ_OFS_DELTA) {
+		unsigned used = 0;
+		unsigned char c = base_info[used++];
+		base_offset = c & 127;
+		while (c & 128) {
+			base_offset += 1;
+			if (!base_offset || base_offset & ~(~0UL >> 7))
+				die("offset value overflow for delta base object");
+			c = base_info[used++];
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		base_offset = delta_obj_offset - base_offset;
+		if (base_offset >= delta_obj_offset)
+			die("delta base offset out of bound");
+		offset += used;
+	} else if (kind == OBJ_REF_DELTA) {
+		/* The base entry _must_ be in the same pack */
+		base_offset = find_pack_entry_one(base_info, p);
+		if (!base_offset)
+			die("failed to find delta-pack base object %s",
+				sha1_to_hex(base_info));
+		offset += 20;
+	} else
+		die("I am totally screwed");
+	*base_obj_offset = base_offset;
+	return offset;
+}
+
 /* forward declaration for a mutually recursive function */
 static int packed_object_info(struct packed_git *p, unsigned long offset,
 			      char *type, unsigned long *sizep);
 
 static int packed_delta_info(struct packed_git *p,
 			     unsigned long offset,
+			     enum object_type kind,
+			     unsigned long obj_offset,
 			     char *type,
 			     unsigned long *sizep)
 {
 	unsigned long base_offset;
-	unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset;
 
-	if (p->pack_size < offset + 20)
-		die("truncated pack file");
-	/* The base entry _must_ be in the same pack */
-	base_offset = find_pack_entry_one(base_sha1, p);
-	if (!base_offset)
-		die("failed to find delta-pack base object %s",
-		    sha1_to_hex(base_sha1));
-	offset += 20;
+	offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
 
 	/* We choose to only get the type of the base object and
 	 * ignore potentially corrupt pack file that expects the delta
@@ -959,25 +994,6 @@
 	return offset + used;
 }
 
-int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
-			   unsigned char *base, unsigned long *sizep,
-			   enum object_type *kindp)
-{
-	unsigned long ptr;
-	int status = -1;
-
-	use_packed_git(p);
-	ptr = offset;
-	ptr = unpack_object_header(p, ptr, kindp, sizep);
-	if (*kindp != OBJ_DELTA)
-		goto done;
-	hashcpy(base, (unsigned char *) p->pack_base + ptr);
-	status = 0;
- done:
-	unuse_packed_git(p);
-	return status;
-}
-
 void packed_object_info_detail(struct packed_git *p,
 			       unsigned long offset,
 			       char *type,
@@ -986,11 +1002,12 @@
 			       unsigned int *delta_chain_length,
 			       unsigned char *base_sha1)
 {
-	unsigned long val;
+	unsigned long obj_offset, val;
 	unsigned char *next_sha1;
 	enum object_type kind;
 
 	*delta_chain_length = 0;
+	obj_offset = offset;
 	offset = unpack_object_header(p, offset, &kind, size);
 
 	for (;;) {
@@ -1005,7 +1022,13 @@
 			strcpy(type, type_names[kind]);
 			*store_size = 0; /* notyet */
 			return;
-		case OBJ_DELTA:
+		case OBJ_OFS_DELTA:
+			get_delta_base(p, offset, kind, obj_offset, &offset);
+			if (*delta_chain_length == 0) {
+				/* TODO: find base_sha1 as pointed by offset */
+			}
+			break;
+		case OBJ_REF_DELTA:
 			if (p->pack_size <= offset + 20)
 				die("pack file %s records an incomplete delta base",
 				    p->pack_name);
@@ -1015,6 +1038,7 @@
 			offset = find_pack_entry_one(next_sha1, p);
 			break;
 		}
+		obj_offset = offset;
 		offset = unpack_object_header(p, offset, &kind, &val);
 		(*delta_chain_length)++;
 	}
@@ -1023,15 +1047,15 @@
 static int packed_object_info(struct packed_git *p, unsigned long offset,
 			      char *type, unsigned long *sizep)
 {
-	unsigned long size;
+	unsigned long size, obj_offset = offset;
 	enum object_type kind;
 
 	offset = unpack_object_header(p, offset, &kind, &size);
 
-	if (kind == OBJ_DELTA)
-		return packed_delta_info(p, offset, type, sizep);
-
 	switch (kind) {
+	case OBJ_OFS_DELTA:
+	case OBJ_REF_DELTA:
+		return packed_delta_info(p, offset, kind, obj_offset, type, sizep);
 	case OBJ_COMMIT:
 	case OBJ_TREE:
 	case OBJ_BLOB:
@@ -1077,23 +1101,15 @@
 static void *unpack_delta_entry(struct packed_git *p,
 				unsigned long offset,
 				unsigned long delta_size,
+				enum object_type kind,
+				unsigned long obj_offset,
 				char *type,
 				unsigned long *sizep)
 {
 	void *delta_data, *result, *base;
 	unsigned long result_size, base_size, base_offset;
-	unsigned char *base_sha1;
 
-	if (p->pack_size < offset + 20)
-		die("truncated pack file");
-	/* The base entry _must_ be in the same pack */
-	base_sha1 = (unsigned char*)p->pack_base + offset;
-	base_offset = find_pack_entry_one(base_sha1, p);
-	if (!base_offset)
-		die("failed to find delta-pack base object %s",
-		    sha1_to_hex(base_sha1));
-	offset += 20;
-
+	offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
 	base = unpack_entry_gently(p, base_offset, type, &base_size);
 	if (!base)
 		die("failed to read delta base object at %lu from %s",
@@ -1130,13 +1146,14 @@
 void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
 			  char *type, unsigned long *sizep)
 {
-	unsigned long size;
+	unsigned long size, obj_offset = offset;
 	enum object_type kind;
 
 	offset = unpack_object_header(p, offset, &kind, &size);
 	switch (kind) {
-	case OBJ_DELTA:
-		return unpack_delta_entry(p, offset, size, type, sizep);
+	case OBJ_OFS_DELTA:
+	case OBJ_REF_DELTA:
+		return unpack_delta_entry(p, offset, size, kind, obj_offset, type, sizep);
 	case OBJ_COMMIT:
 	case OBJ_TREE:
 	case OBJ_BLOB:
@@ -1186,6 +1203,24 @@
 	return 0;
 }
 
+static int matches_pack_name(struct packed_git *p, const char *ig)
+{
+	const char *last_c, *c;
+
+	if (!strcmp(p->pack_name, ig))
+		return 0;
+
+	for (c = p->pack_name, last_c = c; *c;)
+		if (*c == '/')
+			last_c = ++c;
+		else
+			++c;
+	if (!strcmp(last_c, ig))
+		return 0;
+
+	return 1;
+}
+
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
 {
 	struct packed_git *p;
@@ -1197,7 +1232,7 @@
 		if (ignore_packed) {
 			const char **ig;
 			for (ig = ignore_packed; *ig; ig++)
-				if (!strcmp(p->pack_name, *ig))
+				if (!matches_pack_name(p, *ig))
 					break;
 			if (*ig)
 				continue;
diff --git a/sha1_name.c b/sha1_name.c
index 6ffee22..6d7cd78 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -247,26 +247,25 @@
 		NULL
 	};
 	static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
-	const char **p, *pathname;
-	char *real_path = NULL;
-	int refs_found = 0, am;
-	unsigned long at_time = (unsigned long)-1;
+	const char **p, *ref;
+	char *real_ref = NULL;
+	int refs_found = 0;
+	int at, reflog_len;
 	unsigned char *this_result;
 	unsigned char sha1_from_ref[20];
 
 	if (len == 40 && !get_sha1_hex(str, sha1))
 		return 0;
 
-	/* At a given period of time? "@{2 hours ago}" */
-	for (am = 1; am < len - 1; am++) {
-		if (str[am] == '@' && str[am+1] == '{' && str[len-1] == '}') {
-			int date_len = len - am - 3;
-			char *date_spec = xmalloc(date_len + 1);
-			strlcpy(date_spec, str + am + 2, date_len + 1);
-			at_time = approxidate(date_spec);
-			free(date_spec);
-			len = am;
-			break;
+	/* basic@{time or number} format to query ref-log */
+	reflog_len = at = 0;
+	if (str[len-1] == '}') {
+		for (at = 1; at < len - 1; at++) {
+			if (str[at] == '@' && str[at+1] == '{') {
+				reflog_len = (len-1) - (at+2);
+				len = at;
+				break;
+			}
 		}
 	}
 
@@ -276,10 +275,10 @@
 
 	for (p = fmt; *p; p++) {
 		this_result = refs_found ? sha1_from_ref : sha1;
-		pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
-		if (pathname) {
+		ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+		if (ref) {
 			if (!refs_found++)
-				real_path = xstrdup(pathname);
+				real_ref = xstrdup(ref);
 			if (!warn_ambiguous_refs)
 				break;
 		}
@@ -291,14 +290,25 @@
 	if (warn_ambiguous_refs && refs_found > 1)
 		fprintf(stderr, warning, len, str);
 
-	if (at_time != (unsigned long)-1) {
-		read_ref_at(
-			real_path + strlen(git_path(".")) - 1,
-			at_time,
-			sha1);
+	if (reflog_len) {
+		/* Is it asking for N-th entry, or approxidate? */
+		int nth, i;
+		unsigned long at_time;
+		for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
+			char ch = str[at+2+i];
+			if ('0' <= ch && ch <= '9')
+				nth = nth * 10 + ch - '0';
+			else
+				nth = -1;
+		}
+		if (0 <= nth)
+			at_time = 0;
+		else
+			at_time = approxidate(str + at + 2);
+		read_ref_at(real_ref, at_time, nth, sha1);
 	}
 
-	free(real_path);
+	free(real_ref);
 	return 0;
 }
 
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 8baf2fe..b5ceba4 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -4,6 +4,7 @@
 check_count () {
 	head=
 	case "$1" in -h) head="$2"; shift; shift ;; esac
+	echo "$PROG file $head" >&4
 	$PROG file $head >.result || return 1
 	cat .result | perl -e '
 		my %expect = (@ARGV);
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
new file mode 100755
index 0000000..018fbea
--- /dev/null
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='read-tree -m -u checks working tree files'
+
+. ./test-lib.sh
+
+# two-tree test
+
+test_expect_success 'two-way setup' '
+
+	echo >file1 file one &&
+	echo >file2 file two &&
+	git update-index --add file1 file2 &&
+	git commit -m initial &&
+
+	git branch side &&
+	git tag -f branch-point &&
+
+	echo file2 is not tracked on the master anymore &&
+	rm -f file2 &&
+	git update-index --remove file2 &&
+	git commit -a -m "master removes file2"
+'
+
+test_expect_success 'two-way not clobbering' '
+
+	echo >file2 master creates untracked file2 &&
+	if err=`git read-tree -m -u master side 2>&1`
+	then
+		echo should have complained
+		false
+	else
+		echo "happy to see $err"
+	fi
+'
+
+# three-tree test
+
+test_expect_success 'three-way not complaining' '
+
+	rm -f file2 &&
+	git checkout side &&
+	echo >file3 file three &&
+	git update-index --add file3 &&
+	git commit -a -m "side adds file3" &&
+
+	git checkout master &&
+	echo >file2 file two is untracked on the master side &&
+
+	git-read-tree -m -u branch-point master side
+'
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index b3b920e..6a917f2 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -30,11 +30,8 @@
 test_expect_success \
 	"fail to create $n" \
 	"touch .git/$n_dir
-	 git-update-ref $n $A >out 2>err
-	 test "'$? = 1 &&
-	 test "" = "$(cat out)" &&
-	 grep "error: unable to resolve reference" err &&
-	 grep '"$n err"
+	 git-update-ref $n $A >out 2>err"'
+	 test $? != 0'
 rm -f .git/$n_dir out err
 
 test_expect_success \
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 6907cbc..acb54b6 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -17,13 +17,10 @@
      git-commit -m "Initial commit." &&
      HEAD=$(git-rev-parse --verify HEAD)'
 
-test_expect_success \
-    'git branch --help should return success now.' \
-    'git-branch --help'
-
 test_expect_failure \
     'git branch --help should not have created a bogus branch' \
-    'test -f .git/refs/heads/--help'
+    'git-branch --help </dev/null >/dev/null 2>/dev/null || :
+     test -f .git/refs/heads/--help'
 
 test_expect_success \
     'git branch abc should create a branch' \
@@ -34,7 +31,7 @@
     'git-branch a/b/c && test -f .git/refs/heads/a/b/c'
 
 cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from HEAD
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
 EOF
 test_expect_success \
     'git branch -l d/e/f should create a branch and a log' \
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
new file mode 100755
index 0000000..b1e9f2e
--- /dev/null
+++ b/t/t3210-pack-refs.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+# Copyright (c) 2006 Christian Couder
+#
+
+test_description='git pack-refs should not change the branch semantic
+
+This test runs git pack-refs and git show-ref and checks that the branch
+semantic is still the same.
+'
+. ./test-lib.sh
+
+echo '[core] logallrefupdates = true' >>.git/config
+
+test_expect_success \
+    'prepare a trivial repository' \
+    'echo Hello > A &&
+     git-update-index --add A &&
+     git-commit -m "Initial commit." &&
+     HEAD=$(git-rev-parse --verify HEAD)'
+
+SHA1=
+
+test_expect_success \
+    'see if git show-ref works as expected' \
+    'git-branch a &&
+     SHA1=`cat .git/refs/heads/a` &&
+     echo "$SHA1 refs/heads/a" >expect &&
+     git-show-ref a >result &&
+     diff expect result'
+
+test_expect_success \
+    'see if a branch still exists when packed' \
+    'git-branch b &&
+     git-pack-refs --all &&
+     rm .git/refs/heads/b &&
+     echo "$SHA1 refs/heads/b" >expect &&
+     git-show-ref b >result &&
+     diff expect result'
+
+test_expect_failure \
+    'git branch c/d should barf if branch c exists' \
+    'git-branch c &&
+     git-pack-refs --all &&
+     rm .git/refs/heads/c &&
+     git-branch c/d'
+
+test_expect_success \
+    'see if a branch still exists after git pack-refs --prune' \
+    'git-branch e &&
+     git-pack-refs --all --prune &&
+     echo "$SHA1 refs/heads/e" >expect &&
+     git-show-ref e >result &&
+     diff expect result'
+
+test_expect_failure \
+    'see if git pack-refs --prune remove ref files' \
+    'git-branch f &&
+     git-pack-refs --all --prune &&
+     ls .git/refs/heads/f'
+
+test_expect_success \
+    'git branch g should work when git branch g/h has been deleted' \
+    'git-branch g/h &&
+     git-pack-refs --all --prune &&
+     git-branch -d g/h &&
+     git-branch g &&
+     git-pack-refs --all &&
+     git-branch -d g'
+
+test_expect_failure \
+    'git branch i/j/k should barf if branch i exists' \
+    'git-branch i &&
+     git-pack-refs --all --prune &&
+     git-branch i/j/k'
+
+test_expect_success \
+    'test git branch k after branch k/l/m and k/lm have been deleted' \
+    'git-branch k/l &&
+     git-branch k/lm &&
+     git-branch -d k/l &&
+     git-branch k/l/m &&
+     git-branch -d k/l/m &&
+     git-branch -d k/lm &&
+     git-branch k'
+
+test_expect_success \
+    'test git branch n after some branch deletion and pruning' \
+    'git-branch n/o &&
+     git-branch n/op &&
+     git-branch -d n/o &&
+     git-branch n/o/p &&
+     git-branch -d n/op &&
+     git-pack-refs --all --prune &&
+     git-branch -d n/o/p &&
+     git-branch n'
+
+test_done
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
index 360a670..8b19d3c 100755
--- a/t/t3401-rebase-partial.sh
+++ b/t/t3401-rebase-partial.sh
@@ -52,13 +52,10 @@
     'rebase topic branch against new master and check git-am did not get halted' \
     'git-rebase master && test ! -d .dotest'
 
-if test -z "$no_python"
-then
-    test_expect_success \
+test_expect_success \
 	'rebase --merge topic branch that was partially merged upstream' \
 	'git-checkout -f my-topic-branch-merge &&
 	 git-rebase --merge master-merge &&
 	 test ! -d .git/.dotest-merge'
-fi
 
 test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index d34c6cf..0779aaa 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -7,12 +7,6 @@
 
 . ./test-lib.sh
 
-if test "$no_python"; then
-	echo "Skipping: no python => no recursive merge"
-	test_done
-	exit 0
-fi
-
 T="A quick brown fox
 jumps over the lazy dog."
 for i in 1 2 3 4 5 6 7 8 9 10
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index bb25315..977c498 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -10,12 +10,6 @@
 # we assume the default git-am -3 --skip strategy is tested independently
 # and always works :)
 
-if test "$no_python"; then
-	echo "Skipping: no python => no recursive merge"
-	test_done
-	exit 0
-fi
-
 test_expect_success setup '
 	echo hello > hello &&
 	git add hello &&
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
index ea48205..58e5f74 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
@@ -5,7 +5,7 @@
 
     Initial
 
- create mode 040000 dir
+ create mode 100644 dir/sub
  create mode 100644 file0
  create mode 100644 file2
 $
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 278eb66..cf08e92 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -26,6 +26,7 @@
 
 . ./test-lib.sh
 TAR=${TAR:-tar}
+UNZIP=${UNZIP:-unzip}
 
 test_expect_success \
     'populate workdir' \
@@ -95,4 +96,38 @@
     'validate file contents with prefix' \
     'diff -r a c/prefix/a'
 
+test_expect_success \
+    'git-archive --format=zip' \
+    'git-archive --format=zip HEAD >d.zip'
+
+test_expect_success \
+    'extract ZIP archive' \
+    '(mkdir d && cd d && $UNZIP ../d.zip)'
+
+test_expect_success \
+    'validate filenames' \
+    '(cd d/a && find .) | sort >d.lst &&
+     diff a.lst d.lst'
+
+test_expect_success \
+    'validate file contents' \
+    'diff -r a d/a'
+
+test_expect_success \
+    'git-archive --format=zip with prefix' \
+    'git-archive --format=zip --prefix=prefix/ HEAD >e.zip'
+
+test_expect_success \
+    'extract ZIP archive with prefix' \
+    '(mkdir e && cd e && $UNZIP ../e.zip)'
+
+test_expect_success \
+    'validate filenames with prefix' \
+    '(cd e/prefix/a && find .) | sort >e.lst &&
+     diff a.lst e.lst'
+
+test_expect_success \
+    'validate file contents with prefix' \
+    'diff -r a e/prefix/a'
+
 test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 8f7366d..499cafb 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -10,12 +10,6 @@
 test_description='Test criss-cross merge'
 . ./test-lib.sh
 
-if test "$no_python"; then
-	echo "Skipping: no python => no recursive merge"
-	test_done
-	exit 0
-fi
-
 test_expect_success 'prepare repository' \
 'echo "1
 2
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index 5ac2564..b608e20 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -3,12 +3,6 @@
 test_description='Merge-recursive merging renames'
 . ./test-lib.sh
 
-if test "$no_python"; then
-	echo "Skipping: no python => no recursive merge"
-	test_done
-	exit 0
-fi
-
 test_expect_success setup \
 '
 cat >A <<\EOF &&
@@ -48,15 +42,20 @@
 EOF
 
 git add A M &&
-git commit -m initial &&
+git commit -m "initial has A and M" &&
 git branch white &&
 git branch red &&
 git branch blue &&
+git branch yellow &&
 
 sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
 mv A+ A &&
 git commit -a -m "master updates A" &&
 
+git checkout yellow &&
+rm -f M &&
+git commit -a -m "yellow removes M" &&
+
 git checkout white &&
 sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
 sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
@@ -85,27 +84,27 @@
 	git show-branch
 	git pull . white && {
 		echo "BAD: should have conflicted"
-		exit 1
+		return 1
 	}
 	git ls-files -s
 	test "$(git ls-files -u B | wc -l)" -eq 3 || {
 		echo "BAD: should have left stages for B"
-		exit 1	
+		return 1
 	}
 	test "$(git ls-files -s N | wc -l)" -eq 1 || {
 		echo "BAD: should have merged N"
-		exit 1	
+		return 1
 	}
 	sed -ne "/^g/{
 	p
 	q
 	}" B | grep master || {
 		echo "BAD: should have listed our change first"
-		exit 1
+		return 1
 	}
 	test "$(git diff white N | wc -l)" -eq 0 || {
 		echo "BAD: should have taken colored branch"
-		exit 1
+		return 1
 	}
 '
 
@@ -116,26 +115,26 @@
 	git checkout red
 	git pull . white && {
 		echo "BAD: should have conflicted"
-		exit 1
+		return 1
 	}
 	test "$(git ls-files -u B | wc -l)" -eq 3 || {
 		echo "BAD: should have left stages"
-		exit 1	
+		return 1
 	}
 	test "$(git ls-files -s N | wc -l)" -eq 1 || {
 		echo "BAD: should have merged N"
-		exit 1	
+		return 1
 	}
 	sed -ne "/^g/{
 	p
 	q
 	}" B | grep red || {
 		echo "BAD: should have listed our change first"
-		exit 1
+		return 1
 	}
 	test "$(git diff white N | wc -l)" -eq 0 || {
 		echo "BAD: should have taken colored branch"
-		exit 1
+		return 1
 	}
 '
 
@@ -145,26 +144,26 @@
 	git show-branch
 	git pull . master && {
 		echo "BAD: should have conflicted"
-		exit 1
+		return 1
 	}
 	test "$(git ls-files -u B | wc -l)" -eq 3 || {
 		echo "BAD: should have left stages"
-		exit 1	
+		return 1
 	}
 	test "$(git ls-files -s N | wc -l)" -eq 1 || {
 		echo "BAD: should have merged N"
-		exit 1	
+		return 1
 	}
 	sed -ne "/^g/{
 	p
 	q
 	}" B | grep red || {
 		echo "BAD: should have listed our change first"
-		exit 1
+		return 1
 	}
 	test "$(git diff white N | wc -l)" -eq 0 || {
 		echo "BAD: should have taken colored branch"
-		exit 1
+		return 1
 	}
 '
 
@@ -174,35 +173,149 @@
 	git show-branch
 	git pull . blue && {
 		echo "BAD: should have conflicted"
-		exit 1
+		return 1
 	}
 	test "$(git ls-files -u A | wc -l)" -eq 1 || {
 		echo "BAD: should have left a stage"
-		exit 1	
+		return 1
 	}
 	test "$(git ls-files -u B | wc -l)" -eq 1 || {
 		echo "BAD: should have left a stage"
-		exit 1	
+		return 1
 	}
 	test "$(git ls-files -u C | wc -l)" -eq 1 || {
 		echo "BAD: should have left a stage"
-		exit 1	
+		return 1
 	}
 	test "$(git ls-files -s N | wc -l)" -eq 1 || {
 		echo "BAD: should have merged N"
-		exit 1	
+		return 1
 	}
 	sed -ne "/^g/{
 	p
 	q
 	}" B | grep red || {
 		echo "BAD: should have listed our change first"
-		exit 1
+		return 1
 	}
 	test "$(git diff white N | wc -l)" -eq 0 || {
 		echo "BAD: should have taken colored branch"
-		exit 1
+		return 1
 	}
 '
 
+test_expect_success 'interference with untracked working tree file' '
+
+	git reset --hard
+	git show-branch
+	echo >A this file should not matter
+	git pull . white && {
+		echo "BAD: should have conflicted"
+		return 1
+	}
+	test -f A || {
+		echo "BAD: should have left A intact"
+		return 1
+	}
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+	git reset --hard
+	git checkout white
+	git show-branch
+	rm -f A
+	echo >A this file should not matter
+	git pull . red && {
+		echo "BAD: should have conflicted"
+		return 1
+	}
+	test -f A || {
+		echo "BAD: should have left A intact"
+		return 1
+	}
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+	git reset --hard
+	rm -f A M
+	git checkout -f master
+	git tag -f anchor
+	git show-branch
+	git pull . yellow || {
+		echo "BAD: should have cleanly merged"
+		return 1
+	}
+	test -f M && {
+		echo "BAD: should have removed M"
+		return 1
+	}
+	git reset --hard anchor
+'
+
+test_expect_success 'updated working tree file should prevent the merge' '
+
+	git reset --hard
+	rm -f A M
+	git checkout -f master
+	git tag -f anchor
+	git show-branch
+	echo >>M one line addition
+	cat M >M.saved
+	git pull . yellow && {
+		echo "BAD: should have complained"
+		return 1
+	}
+	diff M M.saved || {
+		echo "BAD: should have left M intact"
+		return 1
+	}
+	rm -f M.saved
+'
+
+test_expect_success 'updated working tree file should prevent the merge' '
+
+	git reset --hard
+	rm -f A M
+	git checkout -f master
+	git tag -f anchor
+	git show-branch
+	echo >>M one line addition
+	cat M >M.saved
+	git update-index M
+	git pull . yellow && {
+		echo "BAD: should have complained"
+		return 1
+	}
+	diff M M.saved || {
+		echo "BAD: should have left M intact"
+		return 1
+	}
+	rm -f M.saved
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+	git reset --hard
+	rm -f A M
+	git checkout -f yellow
+	git tag -f anchor
+	git show-branch
+	echo >M this file should not matter
+	git pull . master || {
+		echo "BAD: should have cleanly merged"
+		return 1
+	}
+	test -f M || {
+		echo "BAD: should have left M intact"
+		return 1
+	}
+	git ls-files -s | grep M && {
+		echo "BAD: M must be untracked in the result"
+		return 1
+	}
+	git reset --hard anchor
+'
+
 test_done
diff --git a/tree-diff.c b/tree-diff.c
index 7e2f4f0..37d235e 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -215,6 +215,24 @@
 	return retval;
 }
 
+int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_options *opt)
+{
+	int retval;
+	void *tree;
+	struct tree_desc empty, real;
+
+	tree = read_object_with_reference(new, tree_type, &real.size, NULL);
+	if (!tree)
+		die("unable to read root tree (%s)", sha1_to_hex(new));
+	real.buf = tree;
+
+	empty.size = 0;
+	empty.buf = "";
+	retval = diff_tree(&empty, &real, base, opt);
+	free(tree);
+	return retval;
+}
+
 static int count_paths(const char **paths)
 {
 	int i = 0;
diff --git a/unpack-trees.c b/unpack-trees.c
index 3ac0289..7cfd628 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -642,7 +642,7 @@
 		    (remote_deleted && head && head_match)) {
 			if (index)
 				return deleted_entry(index, index, o);
-			else if (path)
+			else if (path && !head_deleted)
 				verify_absent(path, "removed", o);
 			return 0;
 		}
@@ -661,8 +661,6 @@
 	if (index) {
 		verify_uptodate(index, o);
 	}
-	else if (path)
-		verify_absent(path, "overwritten", o);
 
 	o->nontrivial_merge = 1;
 
diff --git a/upload-pack.c b/upload-pack.c
index 189b239..ddaa72f 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -16,7 +16,7 @@
 #define OUR_REF (1U << 1)
 #define WANTED (1U << 2)
 static int multi_ack, nr_our_refs;
-static int use_thin_pack;
+static int use_thin_pack, use_ofs_delta;
 static struct object_array have_obj;
 static struct object_array want_obj;
 static unsigned int timeout;
@@ -137,7 +137,9 @@
 		close(pu_pipe[1]);
 		close(pe_pipe[0]);
 		close(pe_pipe[1]);
-		execl_git_cmd("pack-objects", "--stdout", "--progress", NULL);
+		execl_git_cmd("pack-objects", "--stdout", "--progress",
+			      use_ofs_delta ? "--delta-base-offset" : NULL,
+			      NULL);
 		kill(pid_rev_list, SIGKILL);
 		die("git-upload-pack: unable to exec git-pack-objects");
 	}
@@ -393,6 +395,8 @@
 			multi_ack = 1;
 		if (strstr(line+45, "thin-pack"))
 			use_thin_pack = 1;
+		if (strstr(line+45, "ofs-delta"))
+			use_ofs_delta = 1;
 		if (strstr(line+45, "side-band-64k"))
 			use_sideband = LARGE_PACKET_MAX;
 		else if (strstr(line+45, "side-band"))
@@ -416,9 +420,9 @@
 	}
 }
 
-static int send_ref(const char *refname, const unsigned char *sha1)
+static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
-	static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
+	static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta";
 	struct object *o = parse_object(sha1);
 
 	if (!o)
@@ -444,8 +448,8 @@
 static void upload_pack(void)
 {
 	reset_timeout();
-	head_ref(send_ref);
-	for_each_ref(send_ref);
+	head_ref(send_ref, NULL);
+	for_each_ref(send_ref, NULL);
 	packet_flush(1);
 	receive_needs();
 	if (want_obj.nr) {
diff --git a/wt-status.c b/wt-status.c
index 6742844..de1be5b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -41,10 +41,8 @@
 
 	s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
 
-	head = resolve_ref(git_path("HEAD"), sha1, 0);
-	s->branch = head ?
-		    strdup(head + strlen(get_git_dir()) + 1) :
-		    NULL;
+	head = resolve_ref("HEAD", sha1, 0, NULL);
+	s->branch = head ? xstrdup(head) : NULL;
 
 	s->reference = "HEAD";
 	s->amend = 0;
@@ -66,31 +64,70 @@
 	color_printf_ln(color(WT_STATUS_HEADER), "#");
 }
 
+static const char *quote_crlf(const char *in, char *buf, size_t sz)
+{
+	const char *scan;
+	char *out;
+	const char *ret = in;
+
+	for (scan = in, out = buf; *scan; scan++) {
+		int ch = *scan;
+		int quoted;
+
+		switch (ch) {
+		case '\n':
+			quoted = 'n';
+			break;
+		case '\r':
+			quoted = 'r';
+			break;
+		default:
+			*out++ = ch;
+			continue;
+		}
+		*out++ = '\\';
+		*out++ = quoted;
+		ret = buf;
+	}
+	*out = '\0';
+	return ret;
+}
+
 static void wt_status_print_filepair(int t, struct diff_filepair *p)
 {
 	const char *c = color(t);
+	const char *one, *two;
+	char onebuf[PATH_MAX], twobuf[PATH_MAX];
+
+	one = quote_crlf(p->one->path, onebuf, sizeof(onebuf));
+	two = quote_crlf(p->two->path, twobuf, sizeof(twobuf));
+
 	color_printf(color(WT_STATUS_HEADER), "#\t");
 	switch (p->status) {
 	case DIFF_STATUS_ADDED:
-		color_printf(c, "new file: %s", p->one->path); break;
+		color_printf(c, "new file:   %s", one);
+		break;
 	case DIFF_STATUS_COPIED:
-		color_printf(c, "copied: %s -> %s",
-				p->one->path, p->two->path);
+		color_printf(c, "copied:     %s -> %s", one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_printf(c, "deleted: %s", p->one->path); break;
+		color_printf(c, "deleted:    %s", one);
+		break;
 	case DIFF_STATUS_MODIFIED:
-		color_printf(c, "modified: %s", p->one->path); break;
+		color_printf(c, "modified:   %s", one);
+		break;
 	case DIFF_STATUS_RENAMED:
-		color_printf(c, "renamed: %s -> %s",
-				p->one->path, p->two->path);
+		color_printf(c, "renamed:    %s -> %s", one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_printf(c, "typechange: %s", p->one->path); break;
+		color_printf(c, "typechange: %s", one);
+		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_printf(c, "unknown: %s", p->one->path); break;
+		color_printf(c, "unknown:    %s", one);
+		break;
 	case DIFF_STATUS_UNMERGED:
-		color_printf(c, "unmerged: %s", p->one->path); break;
+		color_printf(c, "unmerged:   %s", one);
+		break;
 	default:
 		die("bug: unhandled diff status %c", p->status);
 	}
@@ -136,6 +173,8 @@
 void wt_status_print_initial(struct wt_status *s)
 {
 	int i;
+	char buf[PATH_MAX];
+
 	read_cache();
 	if (active_nr) {
 		s->commitable = 1;
@@ -145,7 +184,8 @@
 	for (i = 0; i < active_nr; i++) {
 		color_printf(color(WT_STATUS_HEADER), "#\t");
 		color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
-				active_cache[i]->name);
+				quote_crlf(active_cache[i]->name,
+					   buf, sizeof(buf)));
 	}
 	if (active_nr)
 		wt_status_print_trailer();