Merge branch 'jc/branch-contains'

* jc/branch-contains:
  git-branch --contains: doc and test
  git-branch --contains=commit
  parse-options: Allow to hide options from the default usage.
diff --git a/Documentation/RelNotes-1.5.3.7.txt b/Documentation/RelNotes-1.5.3.7.txt
new file mode 100644
index 0000000..2f69061
--- /dev/null
+++ b/Documentation/RelNotes-1.5.3.7.txt
@@ -0,0 +1,45 @@
+GIT v1.5.3.7 Release Notes
+==========================
+
+Fixes since v1.5.3.6
+--------------------
+
+ * git-send-email added 8-bit contents to the payload without
+   marking it as 8-bit in a CTE header.
+
+ * "git-bundle create a.bndl HEAD" dereferenced the symref and
+   did not record the ref as 'HEAD'; this prevented a bundle
+   from being used as a normal source of git-clone.
+
+ * The code to reject nonsense command line of the form
+   "git-commit -a paths..." and "git-commit --interactive
+   paths..." were broken.
+
+ * Adding a signature that is not ASCII-only to an original
+   commit that is ASCII-only would make the result non-ASCII.
+   "git-format-patch -s" did not mark such a message correctly
+   with MIME encoding header.
+
+ * git-add sometimes did not mark the resulting index entry
+   stat-clean.  This affected only cases when adding the
+   contents with the same length as the previously staged
+   contents, and the previous staging made the index entry
+   "racily clean".
+
+ * git-commit did not honor GIT_INDEX_FILE the user had in the
+   environment.
+
+ * When checking out a revision, git-checkout did not report where the
+   updated HEAD is if you happened to have a file called HEAD in the
+   work tree.
+
+ * "git-rev-list --objects" mishandled a tree that points at a
+   submodule.
+
+ * "git cvsimport" was not ready for packed refs that "git gc" can
+   produce and gave incorrect results.
+
+ * Many scripted Porcelains were confused when you happened to have a
+   file called "HEAD" in your work tree.
+
+Also it contains updates to the user manual and documentation.
diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt
index a4a2a7f..192766f 100644
--- a/Documentation/RelNotes-1.5.4.txt
+++ b/Documentation/RelNotes-1.5.4.txt
@@ -1,20 +1,70 @@
 GIT v1.5.4 Release Notes
 ========================
 
+Deprecation notices
+-------------------
+
+ * Next feature release of git (this change is scheduled for v1.5.5 but
+   it could slip) will by default install dashed form of commands
+   (e.g. "git-commit") outside of users' normal $PATH, and will install
+   only selected commands ("git" itself, and "gitk") in $PATH.  This
+   implies:
+
+   - Using dashed form of git commands (e.g. "git-commit") from the
+     command line has been informally deprecated since early 2006, but
+     now it officially is, and will be removed in the future.  Use
+     dashless form (e.g. "git commit") instead.
+
+   - Using dashed from from your scripts, without first prepending the
+     return value from "git --exec-path" to the scripts' PATH, has been
+     informally deprecated since early 2006, but now it officially is.
+
+   - Use of dashed form with "PATH=$(git --exec-path):$PATH; export
+     PATH" early in your script is not deprecated with this change.
+
+  Users are strongly encouraged to adjust their habits and scripts now
+  to prepare for this.
+
+ * The post-receive hook was introduced in March 2007 to supersede
+   post-update hook, primarily to overcome the command line length
+   limitation of the latter.  Use of post-update hook will be deprecated
+   in future versions of git, perhaps in v1.5.5.
+
+
 Updates since v1.5.3
 --------------------
 
  * Comes with much improved gitk.
 
+ * Comes with git-gui 0.9.0 with i18n.
+
+ * git-lost-found was deprecated in favor of git-fsck's --lost-found
+   option.
+
+ * git-peek-remote is deprecated, as git-ls-remote was written in C and
+   works for all transports.
+
+ * git-cherry-pick made a misguided attempt to repeat the original
+   command line in the generated log message, when told to cherry-pick a
+   commit by naming a tag that points at it.  It does not anymore.
+
  * "progress display" from many commands are a lot nicer to the
    eye.  Transfer commands show throughput data.
 
+ * many commands that pay attention to per-directory .gitignore now do
+   so lazily, which makes the usual case go much faster.
+
  * git-reset is now built-in and its output can be squelched with -q.
 
  * git-send-email can optionally talk over ssmtp and use SMTP-AUTH.
 
  * git-rebase learned --whitespace option.
 
+ * In git-rebase, when you decide not to replay a particular change
+   after the command stopped with a conflict, you can say "git-rebase
+   --skip" without first running "git reset --hard", as the command now
+   runs it for you.
+
  * git-remote knows --mirror mode.
 
  * git-merge can call the "post-merge" hook.
@@ -37,11 +87,20 @@
    variable used to mean "do not require", but we now use the safer
    default).
 
+ * git-clean has been rewritten in C.
+
  * git-push has been rewritten in C.
 
  * git-push learned --dry-run option to show what would happen
    if a push is run.
 
+ * git-push does not update a tracking ref on the pushing side when the
+   remote refused to update the corresponding ref.
+
+ * git-push learned --mirror option.  This is to push the local refs
+   one-to-one to the remote, and deletes refs from the remote that do
+   not exist anymore in the repository on the pushing side.
+
  * git-remote learned "rm" subcommand.
 
  * git-rebase --interactive mode can now work on detached HEAD.
@@ -54,9 +113,6 @@
 
  * Various Perforce importer updates.
 
- * git-lost-found was deprecated in favor of git-fsck's --lost-found
-   option.
-
  * "git log" learned --early-output option to help interactive
    GUI implementations.
 
@@ -86,14 +142,41 @@
    to allow checking out a path outside the current directory
    without cd'ing up.
 
+ * "git send-email --dry-run" shows full headers for easier
+   diagnosis.
+
+ * "git merge-ours" is built-in.
+
+ * "git svn" learned "info" and "show-externals" subcommands.
+
+ * calling "git svn" from a subdirectory failed read settings from the
+   .git/config.
+
+ * "git svn" learned --use-log-author option, which picks up more
+   descriptive name from From: and Signed-off-by: lines in the commit
+   message.
+
+ * "git status" from a subdirectory now shows relative paths
+   which makes copy-and-pasting for git-checkout/git-add/git-rm
+   easier.
+
+ * "git checkout" from and to detached HEAD leaves a bit more
+   information in the reflog.
+
  * Output processing for '--pretty=format:<user format>' has
    been optimized.
 
- * Rename detection diff family, while detecting exact matches,
-   has been greatly optimized.
+ * Rename detection of diff family, while detecting exact matches, has
+   been greatly optimized.
+
+ * Rename detection of diff family tries to make more naturally looking
+   pairing.  Earlier if more than one identical rename sources were
+   found in the preimage, they were picked pretty much at random.
 
  * Example update and post-receive hooks have been improved.
 
+ * A corrupt ref at the remote site can be removed via "git push".
+
  * In addition there are quite a few internal clean-ups. Notably
 
    - many fork/exec have been replaced with run-command API,
@@ -119,6 +202,6 @@
 
 --
 exec >/var/tmp/1
-O=v1.5.3.6-727-g5d3d1ca
+O=v1.5.3.7-948-gb52e985
 echo O=`git describe refs/heads/master`
 git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 57a790d..b709551 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -151,7 +151,7 @@
 git-pack-refs                           ancillarymanipulators
 git-parse-remote                        synchelpers
 git-patch-id                            purehelpers
-git-peek-remote                         purehelpers
+git-peek-remote                         purehelpers	deprecated
 git-prune                               ancillarymanipulators
 git-prune-packed                        plumbingmanipulators
 git-pull                                mainporcelain
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 16303ef..d3f21c7 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -49,17 +49,22 @@
 
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
-has a reflog then the reflog will also be deleted. Use -r together with -d
-to delete remote-tracking branches.
+has a reflog then the reflog will also be deleted.
+
+Use -r together with -d to delete remote-tracking branches. Note, that it
+only makes sense to delete remote-tracking branches if they no longer exist
+in remote repository or if gitlink:git-fetch[1] was configured not to fetch
+them again. See also 'prune' subcommand of gitlink:git-remote[1] for way to
+clean up all obsolete remote-tracking branches.
 
 
 OPTIONS
 -------
 -d::
-	Delete a branch. The branch must be fully merged.
+	Delete a branch. The branch must be fully merged in HEAD.
 
 -D::
-	Delete a branch irrespective of its index status.
+	Delete a branch irrespective of its merged status.
 
 -l::
 	Create the branch's reflog.  This activates recording of
@@ -157,9 +162,11 @@
 $ git branch -D test                                    <2>
 ------------
 +
-<1> Delete remote-tracking branches "todo", "html", "man"
-<2> Delete "test" branch even if the "master" branch does not have all
-commits from test branch.
+<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or
+'pull' will create them again unless you configure them not to. See
+gitlink:git-fetch[1].
+<2> Delete "test" branch even if the "master" branch (or whichever branch is
+currently checked out) does not have all commits from test branch.
 
 
 Notes
diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt
index abc1712..38a5325 100644
--- a/Documentation/git-peek-remote.txt
+++ b/Documentation/git-peek-remote.txt
@@ -12,8 +12,7 @@
 
 DESCRIPTION
 -----------
-Lists the references the remote repository has, and optionally
-stores them in the local repository under the same name.
+This command is deprecated; use `git-ls-remote` instead.
 
 OPTIONS
 -------
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 4a68aab..b8003c6 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -63,6 +63,14 @@
 	Instead of naming each ref to push, specifies that all
 	refs under `$GIT_DIR/refs/heads/` be pushed.
 
+\--mirror::
+	Instead of naming each ref to push, specifies that all
+	refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/`
+	be mirrored to the remote repository.  Newly created local
+	refs will be pushed to the remote end, locally updated refs
+	will be force updated on the remote end, and deleted refs
+	will be removed from the remote end.
+
 \--dry-run::
 	Do everything except actually send the updates.
 
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 10d3e3f..784ec6d 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -65,7 +65,9 @@
 	Typing "git tag" without arguments, also lists all tags.
 
 -m <msg>::
-	Use the given tag message (instead of prompting)
+	Use the given tag message (instead of prompting).
+	If multiple `-m` options are given, there values are
+	concatenated as separate paragraphs.
 
 -F <file>::
 	Take the tag message from the given file.  Use '-' to
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 5460201..9ff4659 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -46,6 +46,7 @@
 * link:v1.5.3/git.html[documentation for release 1.5.3]
 
 * release notes for
+  link:RelNotes-1.5.3.7.txt[1.5.3.7],
   link:RelNotes-1.5.3.6.txt[1.5.3.6],
   link:RelNotes-1.5.3.5.txt[1.5.3.5],
   link:RelNotes-1.5.3.4.txt[1.5.3.4],
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 3661879..93a47b4 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -56,11 +56,12 @@
 The initial clone may be time-consuming for a large project, but you
 will only need to clone once.
 
-The clone command creates a new directory named after the project
-("git" or "linux-2.6" in the examples above).  After you cd into this
+The clone command creates a new directory named after the project ("git"
+or "linux-2.6" in the examples above).  After you cd into this
 directory, you will see that it contains a copy of the project files,
-together with a special top-level directory named ".git", which
-contains all the information about the history of the project.
+called the <<def_working_tree,working tree>>, together with a special
+top-level directory named ".git", which contains all the information
+about the history of the project.
 
 [[how-to-check-out]]
 How to check out a different version of a project
@@ -71,8 +72,13 @@
 interrelated snapshots of the project's contents.  In git each such
 version is called a <<def_commit,commit>>.
 
-A single git repository may contain multiple branches.  It keeps track
-of them by keeping a list of <<def_head,heads>> which reference the
+Those snapshots aren't necessarily all arranged in a single line from
+oldest to newest; instead, work may simultaneously proceed along
+parallel lines of development, called <def_branch,branches>>, which may
+merge and diverge.
+
+A single git repository can track development on multiple branches.  It
+does this by keeping a list of <<def_head,heads>> which reference the
 latest commit on each branch; the gitlink:git-branch[1] command shows
 you the list of branch heads:
 
@@ -1410,8 +1416,8 @@
 conflicts manually, just as in the case of <<resolving-a-merge,
 resolving a merge>>.
 
-[[fixing-a-mistake-by-editing-history]]
-Fixing a mistake by editing history
+[[fixing-a-mistake-by-rewriting-history]]
+Fixing a mistake by rewriting history
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 If the problematic commit is the most recent commit, and you have not
@@ -1434,7 +1440,7 @@
 been merged into another branch; use gitlink:git-revert[1] instead in
 that case.
 
-It is also possible to edit commits further back in the history, but
+It is also possible to replace commits further back in the history, but
 this is an advanced topic to be left for
 <<cleaning-up-history,another chapter>>.
 
@@ -1554,6 +1560,11 @@
 git-gc when run without any options), it is not safe to prune while
 other git operations are in progress in the same repository.
 
+If gitlink:git-fsck[1] complains about sha1 mismatches or missing
+objects, you may have a much more serious problem; your best option is
+probably restoring from backups.  See
+<<recovering-from-repository-corruption>> for a detailed discussion.
+
 [[recovering-lost-changes]]
 Recovering lost changes
 ~~~~~~~~~~~~~~~~~~~~~~~
@@ -1923,15 +1934,9 @@
 $ git push ssh://yourserver.com/~you/proj.git master
 -------------------------------------------------
 
-As with git-fetch, git-push will complain if this does not result in
-a <<fast-forwards,fast forward>>.  Normally this is a sign of
-something wrong.  However, if you are sure you know what you're
-doing, you may force git-push to perform the update anyway by
-preceding the branch name by a plus sign:
-
--------------------------------------------------
-$ git push ssh://yourserver.com/~you/proj.git +master
--------------------------------------------------
+As with git-fetch, git-push will complain if this does not result in a
+<<fast-forwards,fast forward>>; see the following section for details on
+handling this case.
 
 Note that the target of a "push" is normally a
 <<def_bare_repository,bare>> repository.  You can also push to a
@@ -1959,6 +1964,52 @@
 and remote.<name>.push options in gitlink:git-config[1] for
 details.
 
+[[forcing-push]]
+What to do when a push fails
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a push would not result in a <<fast-forwards,fast forward>> of the
+remote branch, then it will fail with an error like:
+
+-------------------------------------------------
+error: remote 'refs/heads/master' is not an ancestor of
+ local  'refs/heads/master'.
+ Maybe you are not up-to-date and need to pull first?
+error: failed to push to 'ssh://yourserver.com/~you/proj.git'
+-------------------------------------------------
+
+This can happen, for example, if you:
+
+	- use `git reset --hard` to remove already-published commits, or
+	- use `git commit --amend` to replace already-published commits
+	  (as in <<fixing-a-mistake-by-rewriting-history>>), or
+	- use `git rebase` to rebase any already-published commits (as
+	  in <<using-git-rebase>>).
+
+You may force git-push to perform the update anyway by preceding the
+branch name with a plus sign:
+
+-------------------------------------------------
+$ git push ssh://yourserver.com/~you/proj.git +master
+-------------------------------------------------
+
+Normally whenever a branch head in a public repository is modified, it
+is modified to point to a descendent of the commit that it pointed to
+before.  By forcing a push in this situation, you break that convention.
+(See <<problems-with-rewriting-history>>.)
+
+Nevertheless, this is a common practice for people that need a simple
+way to publish a work-in-progress patch series, and it is an acceptable
+compromise as long as you warn other developers that this is how you
+intend to manage the branch.
+
+It's also possible for a push to fail in this way when other people have
+the right to push to the same repository.  In that case, the correct
+solution is to retry the push after first updating your work by either a
+pull or a fetch followed by a rebase; see the
+<<setting-up-a-shared-repository,next section>> and
+link:cvs-migration.html[git for CVS users] for more.
+
 [[setting-up-a-shared-repository]]
 Setting up a shared repository
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2426,11 +2477,11 @@
 $ git rebase --abort
 -------------------------------------------------
 
-[[modifying-one-commit]]
-Modifying a single commit
+[[rewriting-one-commit]]
+Rewriting a single commit
 -------------------------
 
-We saw in <<fixing-a-mistake-by-editing-history>> that you can replace the
+We saw in <<fixing-a-mistake-by-rewriting-history>> that you can replace the
 most recent commit using
 
 -------------------------------------------------
@@ -2440,8 +2491,10 @@
 which will replace the old commit by a new commit incorporating your
 changes, giving you a chance to edit the old commit message first.
 
-You can also use a combination of this and gitlink:git-rebase[1] to edit
-commits further back in your history.  First, tag the problematic commit with
+You can also use a combination of this and gitlink:git-rebase[1] to
+replace a commit further back in your history and recreate the
+intervening changes on top of it.  First, tag the problematic commit
+with
 
 -------------------------------------------------
 $ git tag bad mywork~5
@@ -3172,6 +3225,127 @@
 contrast, running "git prune" while somebody is actively changing the
 repository is a *BAD* idea).
 
+[[recovering-from-repository-corruption]]
+Recovering from repository corruption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By design, git treats data trusted to it with caution.  However, even in
+the absence of bugs in git itself, it is still possible that hardware or
+operating system errors could corrupt data.
+
+The first defense against such problems is backups.  You can back up a
+git directory using clone, or just using cp, tar, or any other backup
+mechanism.
+
+As a last resort, you can search for the corrupted objects and attempt
+to replace them by hand.  Back up your repository before attempting this
+in case you corrupt things even more in the process.
+
+We'll assume that the problem is a single missing or corrupted blob,
+which is sometimes a solveable problem.  (Recovering missing trees and
+especially commits is *much* harder).
+
+Before starting, verify that there is corruption, and figure out where
+it is with gitlink:git-fsck[1]; this may be time-consuming.
+
+Assume the output looks like this:
+
+------------------------------------------------
+$ git-fsck --full
+broken link from    tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+              to    blob 4b9458b3786228369c63936db65827de3cc06200
+missing blob 4b9458b3786228369c63936db65827de3cc06200
+------------------------------------------------
+
+(Typically there will be some "dangling object" messages too, but they
+aren't interesting.)
+
+Now you know that blob 4b9458b3 is missing, and that the tree 2d9263c6
+points to it.  If you could find just one copy of that missing blob
+object, possibly in some other repository, you could move it into
+.git/objects/4b/9458b3... and be done.  Suppose you can't.  You can
+still examine the tree that pointed to it with gitlink:git-ls-tree[1],
+which might output something like:
+
+------------------------------------------------
+$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
+100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8	.gitignore
+100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883	.mailmap
+100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c	COPYING
+...
+100644 blob 4b9458b3786228369c63936db65827de3cc06200	myfile
+...
+------------------------------------------------
+
+So now you know that the missing blob was the data for a file named
+"myfile".  And chances are you can also identify the directory--let's
+say it's in "somedirectory".  If you're lucky the missing copy might be
+the same as the copy you have checked out in your working tree at
+"somedirectory/myfile"; you can test whether that's right with
+gitlink:git-hash-object[1]:
+
+------------------------------------------------
+$ git hash-object -w somedirectory/myfile
+------------------------------------------------
+
+which will create and store a blob object with the contents of
+somedirectory/myfile, and output the sha1 of that object.  if you're
+extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
+which case you've guessed right, and the corruption is fixed!
+
+Otherwise, you need more information.  How do you tell which version of
+the file has been lost?
+
+The easiest way to do this is with:
+
+------------------------------------------------
+$ git log --raw --all --full-history -- somedirectory/myfile
+------------------------------------------------
+
+Because you're asking for raw output, you'll now get something like
+
+------------------------------------------------
+commit abc
+Author:
+Date:
+...
+:100644 100644 4b9458b... newsha... M somedirectory/myfile
+
+
+commit xyz
+Author:
+Date:
+
+...
+:100644 100644 oldsha... 4b9458b... M somedirectory/myfile
+------------------------------------------------
+
+This tells you that the immediately preceding version of the file was
+"newsha", and that the immediately following version was "oldsha".
+You also know the commit messages that went with the change from oldsha
+to 4b9458b and with the change from 4b9458b to newsha.
+
+If you've been committing small enough changes, you may now have a good
+shot at reconstructing the contents of the in-between state 4b9458b.
+
+If you can do that, you can now recreate the missing object with
+
+------------------------------------------------
+$ git hash-object -w <recreated-file>
+------------------------------------------------
+
+and your repository is good again!
+
+(Btw, you could have ignored the fsck, and started with doing a
+
+------------------------------------------------
+$ git log --raw --all
+------------------------------------------------
+
+and just looked for the sha of the missing object (4b9458b..) in that
+whole thing. It's up to you - git does *have* a lot of information, it is
+just missing one particular blob version.
+
 [[the-index]]
 The index
 -----------
@@ -3533,7 +3707,7 @@
 NOTE! A `--remove` flag does 'not' mean that subsequent filenames will
 necessarily be removed: if the files still exist in your directory
 structure, the index will be updated with their new status, not
-removed. The only thing `--remove` means is that update-cache will be
+removed. The only thing `--remove` means is that update-index will be
 considering a removed file to be a valid thing, and if the file really
 does not exist any more, it will update the index accordingly.
 
@@ -4382,4 +4556,7 @@
 
 Alternates, clone -reference, etc.
 
-git unpack-objects -r for recovery
+More on recovery from repository corruption.  See:
+	http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2
+	http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
+	http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
diff --git a/Makefile b/Makefile
index 7a0ee78..e869b85 100644
--- a/Makefile
+++ b/Makefile
@@ -111,7 +111,7 @@
 # times (my ext3 doesn't).
 #
 # Define USE_STDEV below if you want git to care about the underlying device
-# change being considered an inode change from the update-cache perspective.
+# change being considered an inode change from the update-index perspective.
 #
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
@@ -213,8 +213,7 @@
 
 SCRIPT_SH = \
 	git-bisect.sh git-checkout.sh \
-	git-clean.sh git-clone.sh git-commit.sh \
-	git-ls-remote.sh \
+	git-clone.sh git-commit.sh \
 	git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
 	git-pull.sh git-rebase.sh git-rebase--interactive.sh \
 	git-repack.sh git-request-pull.sh \
@@ -243,7 +242,7 @@
 	git-fast-import$X \
 	git-daemon$X \
 	git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
-	git-peek-remote$X git-receive-pack$X \
+	git-receive-pack$X \
 	git-send-pack$X git-shell$X \
 	git-show-index$X \
 	git-unpack-file$X \
@@ -330,6 +329,7 @@
 	builtin-check-attr.o \
 	builtin-checkout-index.o \
 	builtin-check-ref-format.o \
+	builtin-clean.o \
 	builtin-commit-tree.o \
 	builtin-count-objects.o \
 	builtin-describe.o \
@@ -349,6 +349,7 @@
 	builtin-log.o \
 	builtin-ls-files.o \
 	builtin-ls-tree.o \
+	builtin-ls-remote.o \
 	builtin-mailinfo.o \
 	builtin-mailsplit.o \
 	builtin-merge-base.o \
@@ -362,6 +363,7 @@
 	builtin-push.o \
 	builtin-read-tree.o \
 	builtin-reflog.o \
+	builtin-send-pack.o \
 	builtin-config.o \
 	builtin-rerere.o \
 	builtin-reset.o \
@@ -754,7 +756,7 @@
 LIBS = $(GITLIBS) $(EXTLIBS)
 
 BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
-	-DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS)
+	$(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
 ALL_CFLAGS += $(BASIC_CFLAGS)
@@ -903,6 +905,9 @@
 builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
 
+config.o: config.c GIT-CFLAGS
+	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
+
 http.o: http.c GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 
diff --git a/builtin-clean.c b/builtin-clean.c
new file mode 100644
index 0000000..56ae4eb
--- /dev/null
+++ b/builtin-clean.c
@@ -0,0 +1,153 @@
+/*
+ * "git clean" builtin command
+ *
+ * Copyright (C) 2007 Shawn Bohrer
+ *
+ * Based on git-clean.sh by Pavel Roskin
+ */
+
+#include "builtin.h"
+#include "cache.h"
+#include "dir.h"
+#include "parse-options.h"
+
+static int force = -1; /* unset */
+
+static const char *const builtin_clean_usage[] = {
+	"git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+	NULL
+};
+
+static int git_clean_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "clean.requireforce"))
+		force = !git_config_bool(var, value);
+	return git_default_config(var, value);
+}
+
+int cmd_clean(int argc, const char **argv, const char *prefix)
+{
+	int j;
+	int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
+	int ignored_only = 0, baselen = 0, config_set = 0;
+	struct strbuf directory;
+	struct dir_struct dir;
+	const char *path, *base;
+	static const char **pathspec;
+	struct option options[] = {
+		OPT__QUIET(&quiet),
+		OPT__DRY_RUN(&show_only),
+		OPT_BOOLEAN('f', NULL, &force, "force"),
+		OPT_BOOLEAN('d', NULL, &remove_directories,
+				"remove whole directories"),
+		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
+		OPT_BOOLEAN('X', NULL, &ignored_only,
+				"remove only ignored files"),
+		OPT_END()
+	};
+
+	git_config(git_clean_config);
+	if (force < 0)
+		force = 0;
+	else
+		config_set = 1;
+
+	argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
+
+	memset(&dir, 0, sizeof(dir));
+	if (ignored_only)
+		dir.show_ignored = 1;
+
+	if (ignored && ignored_only)
+		die("-x and -X cannot be used together");
+
+	if (!show_only && !force)
+		die("clean.requireForce%s set and -n or -f not given; "
+		    "refusing to clean", config_set ? "" : " not");
+
+	dir.show_other_directories = 1;
+
+	if (!ignored)
+		setup_standard_excludes(&dir);
+
+	pathspec = get_pathspec(prefix, argv);
+	read_cache();
+
+	/*
+	 * Calculate common prefix for the pathspec, and
+	 * use that to optimize the directory walk
+	 */
+	baselen = common_prefix(pathspec);
+	path = ".";
+	base = "";
+	if (baselen)
+		path = base = xmemdupz(*pathspec, baselen);
+	read_directory(&dir, path, base, baselen, pathspec);
+	strbuf_init(&directory, 0);
+
+	for (j = 0; j < dir.nr; ++j) {
+		struct dir_entry *ent = dir.entries[j];
+		int len, pos, specs;
+		struct cache_entry *ce;
+		struct stat st;
+		char *seen;
+
+		/*
+		 * Remove the '/' at the end that directory
+		 * walking adds for directory entries.
+		 */
+		len = ent->len;
+		if (len && ent->name[len-1] == '/')
+			len--;
+		pos = cache_name_pos(ent->name, len);
+		if (0 <= pos)
+			continue;	/* exact match */
+		pos = -pos - 1;
+		if (pos < active_nr) {
+			ce = active_cache[pos];
+			if (ce_namelen(ce) == len &&
+			    !memcmp(ce->name, ent->name, len))
+				continue; /* Yup, this one exists unmerged */
+		}
+
+		if (!lstat(ent->name, &st) && (S_ISDIR(st.st_mode))) {
+			int matched_path = 0;
+			strbuf_addstr(&directory, ent->name);
+			if (pathspec) {
+				for (specs =0; pathspec[specs]; ++specs)
+					/* nothing */;
+				seen = xcalloc(specs, 1);
+				/* Check if directory was explictly passed as
+				 * pathspec.  If so we want to remove it */
+				if (match_pathspec(pathspec, ent->name, ent->len,
+						   baselen, seen))
+					matched_path = 1;
+				free(seen);
+			}
+			if (show_only && (remove_directories || matched_path)) {
+				printf("Would remove %s\n", directory.buf);
+			} else if (quiet && (remove_directories || matched_path)) {
+				remove_dir_recursively(&directory, 0);
+			} else if (remove_directories || matched_path) {
+				printf("Removing %s\n", directory.buf);
+				remove_dir_recursively(&directory, 0);
+			} else if (show_only) {
+				printf("Would not remove %s\n", directory.buf);
+			} else {
+				printf("Not removing %s\n", directory.buf);
+			}
+			strbuf_reset(&directory);
+		} else {
+			if (show_only) {
+				printf("Would remove %s\n", ent->name);
+				continue;
+			} else if (!quiet) {
+				printf("Removing %s\n", ent->name);
+			}
+			unlink(ent->name);
+		}
+	}
+
+	strbuf_release(&directory);
+	return 0;
+}
diff --git a/builtin-config.c b/builtin-config.c
index e5e243f..f672c9c 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -81,7 +81,7 @@
 			local = repo_config = xstrdup(git_path("config"));
 		if (home)
 			global = xstrdup(mkpath("%s/.gitconfig", home));
-		system_wide = ETC_GITCONFIG;
+		system_wide = git_etc_gitconfig();
 	}
 
 	key = xstrdup(key_);
@@ -191,7 +191,7 @@
 			}
 		}
 		else if (!strcmp(argv[1], "--system"))
-			setenv(CONFIG_ENVIRONMENT, ETC_GITCONFIG, 1);
+			setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1);
 		else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
 			if (argc < 3)
 				usage(git_config_set_usage);
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index ed60847..7460ab7 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -511,10 +511,14 @@
 	if (!strcmp("append-fetch-head", argv[1])) {
 		int result;
 		FILE *fp;
+		char *filename;
 
 		if (argc != 8)
 			return error("append-fetch-head takes 6 args");
-		fp = fopen(git_path("FETCH_HEAD"), "a");
+		filename = git_path("FETCH_HEAD");
+		fp = fopen(filename, "a");
+		if (!fp)
+			return error("cannot open %s: %s\n", filename, strerror(errno));
 		result = append_fetch_head(fp, argv[2], argv[3],
 					   argv[4], argv[5],
 					   argv[6], !!argv[7][0],
@@ -525,10 +529,14 @@
 	if (!strcmp("native-store", argv[1])) {
 		int result;
 		FILE *fp;
+		char *filename;
 
 		if (argc != 5)
 			return error("fetch-native-store takes 3 args");
-		fp = fopen(git_path("FETCH_HEAD"), "a");
+		filename = git_path("FETCH_HEAD");
+		fp = fopen(filename, "a");
+		if (!fp)
+			return error("cannot open %s: %s\n", filename, strerror(errno));
 		result = fetch_native_store(fp, argv[2], argv[3], argv[4],
 					    verbose, force);
 		fclose(fp);
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index bb1742f..807fa93 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -462,34 +462,12 @@
 {
 	int *xd = data;
 
-	close(xd[1]);
 	return recv_sideband("fetch-pack", xd[0], fd, 2);
 }
 
-static void setup_sideband(int fd[2], int xd[2], struct async *demux)
-{
-	if (!use_sideband) {
-		fd[0] = xd[0];
-		fd[1] = xd[1];
-		return;
-	}
-	/* xd[] is talking with upload-pack; subprocess reads from
-	 * xd[0], spits out band#2 to stderr, and feeds us band#1
-	 * through demux->out.
-	 */
-	demux->proc = sideband_demux;
-	demux->data = xd;
-	if (start_async(demux))
-		die("fetch-pack: unable to fork off sideband demultiplexer");
-	close(xd[0]);
-	fd[0] = demux->out;
-	fd[1] = xd[1];
-}
-
 static int get_pack(int xd[2], char **pack_lockfile)
 {
 	struct async demux;
-	int fd[2];
 	const char *argv[20];
 	char keep_arg[256];
 	char hdr_arg[256];
@@ -497,7 +475,20 @@
 	int do_keep = args.keep_pack;
 	struct child_process cmd;
 
-	setup_sideband(fd, xd, &demux);
+	memset(&demux, 0, sizeof(demux));
+	if (use_sideband) {
+		/* xd[] is talking with upload-pack; subprocess reads from
+		 * xd[0], spits out band#2 to stderr, and feeds us band#1
+		 * through demux->out.
+		 */
+		demux.proc = sideband_demux;
+		demux.data = xd;
+		if (start_async(&demux))
+			die("fetch-pack: unable to fork off sideband"
+			    " demultiplexer");
+	}
+	else
+		demux.out = xd[0];
 
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.argv = argv;
@@ -506,7 +497,7 @@
 	if (!args.keep_pack && unpack_limit) {
 		struct pack_header header;
 
-		if (read_pack_header(fd[0], &header))
+		if (read_pack_header(demux.out, &header))
 			die("protocol error: bad pack header");
 		snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%u,%u",
 			 ntohl(header.hdr_version), ntohl(header.hdr_entries));
@@ -542,11 +533,10 @@
 		*av++ = hdr_arg;
 	*av++ = NULL;
 
-	cmd.in = fd[0];
+	cmd.in = demux.out;
 	cmd.git_cmd = 1;
 	if (start_command(&cmd))
 		die("fetch-pack: unable to fork off %s", argv[0]);
-	close(fd[1]);
 	if (do_keep && pack_lockfile)
 		*pack_lockfile = index_pack_lockfile(cmd.out);
 
diff --git a/builtin-fetch.c b/builtin-fetch.c
index be9e3ea..de9947e 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -31,7 +31,7 @@
 }
 
 static void add_merge_config(struct ref **head,
-		           struct ref *remote_refs,
+			   const struct ref *remote_refs,
 		           struct branch *branch,
 		           struct ref ***tail)
 {
@@ -79,7 +79,7 @@
 	struct ref *ref_map = NULL;
 	struct ref **tail = &ref_map;
 
-	struct ref *remote_refs = transport_get_remote_refs(transport);
+	const struct ref *remote_refs = transport_get_remote_refs(transport);
 
 	if (ref_count || tags) {
 		for (i = 0; i < ref_count; i++) {
@@ -255,7 +255,7 @@
 	}
 }
 
-static void store_updated_refs(const char *url, struct ref *ref_map)
+static int store_updated_refs(const char *url, struct ref *ref_map)
 {
 	FILE *fp;
 	struct commit *commit;
@@ -263,8 +263,11 @@
 	char note[1024];
 	const char *what, *kind;
 	struct ref *rm;
+	char *filename = git_path("FETCH_HEAD");
 
-	fp = fopen(git_path("FETCH_HEAD"), "a");
+	fp = fopen(filename, "a");
+	if (!fp)
+		return error("cannot open %s: %s\n", filename, strerror(errno));
 	for (rm = ref_map; rm; rm = rm->next) {
 		struct ref *ref = NULL;
 
@@ -335,6 +338,7 @@
 		}
 	}
 	fclose(fp);
+	return 0;
 }
 
 /*
@@ -404,7 +408,7 @@
 	if (ret)
 		ret = transport_fetch_refs(transport, ref_map);
 	if (!ret)
-		store_updated_refs(transport->url, ref_map);
+		ret |= store_updated_refs(transport->url, ref_map);
 	transport_unlock_pack(transport);
 	return ret;
 }
@@ -424,12 +428,12 @@
 	struct path_list new_refs = { NULL, 0, 0, 1 };
 	char *ref_name;
 	int ref_name_len;
-	unsigned char *ref_sha1;
-	struct ref *tag_ref;
+	const unsigned char *ref_sha1;
+	const struct ref *tag_ref;
 	struct ref *rm = NULL;
 	struct ref *ref_map = NULL;
 	struct ref **tail = &ref_map;
-	struct ref *ref;
+	const struct ref *ref;
 
 	for_each_ref(add_existing, &existing_refs);
 	for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -487,8 +491,13 @@
 		die("Don't know how to fetch from %s", transport->url);
 
 	/* if not appending, truncate FETCH_HEAD */
-	if (!append)
-		fclose(fopen(git_path("FETCH_HEAD"), "w"));
+	if (!append) {
+		char *filename = git_path("FETCH_HEAD");
+		FILE *fp = fopen(filename, "w");
+		if (!fp)
+			return error("cannot open %s: %s\n", filename, strerror(errno));
+		fclose(fp);
+	}
 
 	ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
 
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 763fa55..e1393b8 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "builtin.h"
+#include "exec_cmd.h"
 
 #ifndef DEFAULT_GIT_TEMPLATE_DIR
 #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -131,10 +132,19 @@
 	int template_len;
 	DIR *dir;
 
-	if (!template_dir) {
+	if (!template_dir)
 		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
-		if (!template_dir)
-			template_dir = DEFAULT_GIT_TEMPLATE_DIR;
+	if (!template_dir) {
+		/*
+		 * if the hard-coded template is relative, it is
+		 * interpreted relative to the exec_dir
+		 */
+		template_dir = DEFAULT_GIT_TEMPLATE_DIR;
+		if (!is_absolute_path(template_dir)) {
+			const char *exec_path = git_exec_path();
+			template_dir = prefix_path(exec_path, strlen(exec_path),
+						   template_dir);
+		}
 	}
 	strcpy(template_path, template_dir);
 	template_len = strlen(template_path);
diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c
new file mode 100644
index 0000000..56f3f88
--- /dev/null
+++ b/builtin-ls-remote.c
@@ -0,0 +1,74 @@
+#include "builtin.h"
+#include "cache.h"
+#include "transport.h"
+#include "remote.h"
+
+static const char ls_remote_usage[] =
+"git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
+
+int cmd_ls_remote(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	const char *dest = NULL;
+	int nongit = 0;
+	unsigned flags = 0;
+	const char *uploadpack = NULL;
+
+	struct remote *remote;
+	struct transport *transport;
+	const struct ref *ref;
+
+	setup_git_directory_gently(&nongit);
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (*arg == '-') {
+			if (!prefixcmp(arg, "--upload-pack=")) {
+				uploadpack = arg + 14;
+				continue;
+			}
+			if (!prefixcmp(arg, "--exec=")) {
+				uploadpack = arg + 7;
+				continue;
+			}
+			if (!strcmp("--tags", arg)) {
+				flags |= REF_TAGS;
+				continue;
+			}
+			if (!strcmp("--heads", arg)) {
+				flags |= REF_HEADS;
+				continue;
+			}
+			if (!strcmp("--refs", arg)) {
+				flags |= REF_NORMAL;
+				continue;
+			}
+			usage(ls_remote_usage);
+		}
+		dest = arg;
+		break;
+	}
+
+	if (!dest || i != argc - 1)
+		usage(ls_remote_usage);
+
+	remote = nongit ? NULL : remote_get(dest);
+	if (remote && !remote->url_nr)
+		die("remote %s has no configured URL", dest);
+	transport = transport_get(remote, remote ? remote->url[0] : dest);
+	if (uploadpack != NULL)
+		transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
+
+	ref = transport_get_remote_refs(transport);
+
+	if (!ref)
+		return 1;
+
+	while (ref) {
+		if (check_ref_type(ref, flags))
+			printf("%s	%s\n", sha1_to_hex(ref->old_sha1), ref->name);
+		ref = ref->next;
+	}
+	return 0;
+}
diff --git a/builtin-push.c b/builtin-push.c
index 6d1da07..41df717 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -10,7 +10,7 @@
 #include "parse-options.h"
 
 static const char * const push_usage[] = {
-	"git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
+	"git-push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
 	NULL,
 };
 
@@ -91,6 +91,7 @@
 {
 	int flags = 0;
 	int all = 0;
+	int mirror = 0;
 	int dry_run = 0;
 	int force = 0;
 	int tags = 0;
@@ -100,6 +101,7 @@
 		OPT__VERBOSE(&verbose),
 		OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
 		OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
+		OPT_BOOLEAN( 0 , "mirror", &mirror, "mirror all refs"),
 		OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
 		OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
 		OPT_BOOLEAN('f', "force", &force, "force updates"),
@@ -121,13 +123,21 @@
 		add_refspec("refs/tags/*");
 	if (all)
 		flags |= TRANSPORT_PUSH_ALL;
+	if (mirror)
+		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
 	if (argc > 0) {
 		repo = argv[0];
 		set_refspecs(argv + 1, argc - 1);
 	}
-	if ((flags & TRANSPORT_PUSH_ALL) && refspec)
+	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
 		usage_with_options(push_usage, options);
 
+	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
+				(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
+		error("--all and --mirror are incompatible");
+		usage_with_options(push_usage, options);
+	}
+
 	return do_push(repo, flags);
 }
diff --git a/builtin-revert.c b/builtin-revert.c
index 365b330..a0586f9 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -30,7 +30,7 @@
 	NULL
 };
 
-static int edit, no_replay, no_commit, needed_deref, mainline;
+static int edit, no_replay, no_commit, mainline;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 
@@ -66,7 +66,6 @@
 	if (commit->object.type == OBJ_TAG) {
 		commit = (struct commit *)
 			deref_tag((struct object *)commit, arg, strlen(arg));
-		needed_deref = 1;
 	}
 	if (commit->object.type != OBJ_COMMIT)
 		die ("'%s' does not point to a commit", arg);
@@ -333,17 +332,6 @@
 			add_to_msg(")\n");
 		}
 	}
-	if (needed_deref) {
-		add_to_msg("(original 'git ");
-		add_to_msg(me);
-		add_to_msg("' arguments: ");
-		for (i = 0; i < argc; i++) {
-			if (i)
-				add_to_msg(" ");
-			add_to_msg(argv[i]);
-		}
-		add_to_msg(")\n");
-	}
 
 	if (merge_recursive(sha1_to_hex(base->object.sha1),
 				sha1_to_hex(head), "HEAD",
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
new file mode 100644
index 0000000..25ae1fe
--- /dev/null
+++ b/builtin-send-pack.c
@@ -0,0 +1,652 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "run-command.h"
+#include "remote.h"
+#include "send-pack.h"
+
+static const char send_pack_usage[] =
+"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"  --all and explicit <ref> specification are mutually exclusive.";
+
+static struct send_pack_args args = {
+	/* .receivepack = */ "git-receive-pack",
+};
+
+/*
+ * Make a pack stream and spit it out into file descriptor fd
+ */
+static int pack_objects(int fd, struct ref *refs)
+{
+	/*
+	 * The child becomes pack-objects --revs; we feed
+	 * the revision parameters to it via its stdin and
+	 * let its stdout go back to the other end.
+	 */
+	const char *argv[] = {
+		"pack-objects",
+		"--all-progress",
+		"--revs",
+		"--stdout",
+		NULL,
+		NULL,
+	};
+	struct child_process po;
+
+	if (args.use_thin_pack)
+		argv[4] = "--thin";
+	memset(&po, 0, sizeof(po));
+	po.argv = argv;
+	po.in = -1;
+	po.out = fd;
+	po.git_cmd = 1;
+	if (start_command(&po))
+		die("git-pack-objects failed (%s)", strerror(errno));
+
+	/*
+	 * We feed the pack-objects we just spawned with revision
+	 * parameters by writing to the pipe.
+	 */
+	while (refs) {
+		char buf[42];
+
+		if (!is_null_sha1(refs->old_sha1) &&
+		    has_sha1_file(refs->old_sha1)) {
+			memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
+			buf[0] = '^';
+			buf[41] = '\n';
+			if (!write_or_whine(po.in, buf, 42,
+						"send-pack: send refs"))
+				break;
+		}
+		if (!is_null_sha1(refs->new_sha1)) {
+			memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
+			buf[40] = '\n';
+			if (!write_or_whine(po.in, buf, 41,
+						"send-pack: send refs"))
+				break;
+		}
+		refs = refs->next;
+	}
+
+	if (finish_command(&po))
+		return error("pack-objects died with strange error");
+	return 0;
+}
+
+static void unmark_and_free(struct commit_list *list, unsigned int mark)
+{
+	while (list) {
+		struct commit_list *temp = list;
+		temp->item->object.flags &= ~mark;
+		list = temp->next;
+		free(temp);
+	}
+}
+
+static int ref_newer(const unsigned char *new_sha1,
+		     const unsigned char *old_sha1)
+{
+	struct object *o;
+	struct commit *old, *new;
+	struct commit_list *list, *used;
+	int found = 0;
+
+	/* Both new and old must be commit-ish and new is descendant of
+	 * old.  Otherwise we require --force.
+	 */
+	o = deref_tag(parse_object(old_sha1), NULL, 0);
+	if (!o || o->type != OBJ_COMMIT)
+		return 0;
+	old = (struct commit *) o;
+
+	o = deref_tag(parse_object(new_sha1), NULL, 0);
+	if (!o || o->type != OBJ_COMMIT)
+		return 0;
+	new = (struct commit *) o;
+
+	if (parse_commit(new) < 0)
+		return 0;
+
+	used = list = NULL;
+	commit_list_insert(new, &list);
+	while (list) {
+		new = pop_most_recent_commit(&list, 1);
+		commit_list_insert(new, &used);
+		if (new == old) {
+			found = 1;
+			break;
+		}
+	}
+	unmark_and_free(list, 1);
+	unmark_and_free(used, 1);
+	return found;
+}
+
+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, int flag, void *cb_data)
+{
+	struct ref *ref;
+	int len = strlen(refname) + 1;
+	ref = xcalloc(1, sizeof(*ref) + len);
+	hashcpy(ref->new_sha1, sha1);
+	memcpy(ref->name, refname, len);
+	*local_tail = ref;
+	local_tail = &ref->next;
+	return 0;
+}
+
+static void get_local_heads(void)
+{
+	local_tail = &local_refs;
+	for_each_ref(one_local_ref, NULL);
+}
+
+static int receive_status(int in, struct ref *refs)
+{
+	struct ref *hint;
+	char line[1000];
+	int ret = 0;
+	int len = packet_read_line(in, line, sizeof(line));
+	if (len < 10 || memcmp(line, "unpack ", 7))
+		return error("did not receive remote status");
+	if (memcmp(line, "unpack ok\n", 10)) {
+		char *p = line + strlen(line) - 1;
+		if (*p == '\n')
+			*p = '\0';
+		error("unpack failed: %s", line + 7);
+		ret = -1;
+	}
+	hint = NULL;
+	while (1) {
+		char *refname;
+		char *msg;
+		len = packet_read_line(in, line, sizeof(line));
+		if (!len)
+			break;
+		if (len < 3 ||
+		    (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
+			fprintf(stderr, "protocol error: %s\n", line);
+			ret = -1;
+			break;
+		}
+
+		line[strlen(line)-1] = '\0';
+		refname = line + 3;
+		msg = strchr(refname, ' ');
+		if (msg)
+			*msg++ = '\0';
+
+		/* first try searching at our hint, falling back to all refs */
+		if (hint)
+			hint = find_ref_by_name(hint, refname);
+		if (!hint)
+			hint = find_ref_by_name(refs, refname);
+		if (!hint) {
+			warning("remote reported status on unknown ref: %s",
+					refname);
+			continue;
+		}
+		if (hint->status != REF_STATUS_EXPECTING_REPORT) {
+			warning("remote reported status on unexpected ref: %s",
+					refname);
+			continue;
+		}
+
+		if (line[0] == 'o' && line[1] == 'k')
+			hint->status = REF_STATUS_OK;
+		else {
+			hint->status = REF_STATUS_REMOTE_REJECT;
+			ret = -1;
+		}
+		if (msg)
+			hint->remote_status = xstrdup(msg);
+		/* start our next search from the next ref */
+		hint = hint->next;
+	}
+	return ret;
+}
+
+static void update_tracking_ref(struct remote *remote, struct ref *ref)
+{
+	struct refspec rs;
+
+	if (ref->status != REF_STATUS_OK)
+		return;
+
+	rs.src = ref->name;
+	rs.dst = NULL;
+
+	if (!remote_find_tracking(remote, &rs)) {
+		if (args.verbose)
+			fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+		if (ref->deletion) {
+			if (delete_ref(rs.dst, NULL))
+				error("Failed to delete");
+		} else
+			update_ref("update by push", rs.dst,
+					ref->new_sha1, NULL, 0, 0);
+		free(rs.dst);
+	}
+}
+
+static const char *prettify_ref(const struct ref *ref)
+{
+	const char *name = ref->name;
+	return name + (
+		!prefixcmp(name, "refs/heads/") ? 11 :
+		!prefixcmp(name, "refs/tags/") ? 10 :
+		!prefixcmp(name, "refs/remotes/") ? 13 :
+		0);
+}
+
+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+{
+	fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+	if (from)
+		fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+	else
+		fputs(prettify_ref(to), stderr);
+	if (msg) {
+		fputs(" (", stderr);
+		fputs(msg, stderr);
+		fputc(')', stderr);
+	}
+	fputc('\n', stderr);
+}
+
+static const char *status_abbrev(unsigned char sha1[20])
+{
+	const char *abbrev;
+	abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
+	return abbrev ? abbrev : sha1_to_hex(sha1);
+}
+
+static void print_ok_ref_status(struct ref *ref)
+{
+	if (ref->deletion)
+		print_ref_status('-', "[deleted]", ref, NULL, NULL);
+	else if (is_null_sha1(ref->old_sha1))
+		print_ref_status('*',
+			(!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+			  "[new branch]"),
+			ref, ref->peer_ref, NULL);
+	else {
+		char quickref[84];
+		char type;
+		const char *msg;
+
+		strcpy(quickref, status_abbrev(ref->old_sha1));
+		if (ref->nonfastforward) {
+			strcat(quickref, "...");
+			type = '+';
+			msg = "forced update";
+		} else {
+			strcat(quickref, "..");
+			type = ' ';
+			msg = NULL;
+		}
+		strcat(quickref, status_abbrev(ref->new_sha1));
+
+		print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+	}
+}
+
+static int print_one_push_status(struct ref *ref, const char *dest, int count)
+{
+	if (!count)
+		fprintf(stderr, "To %s\n", dest);
+
+	switch(ref->status) {
+	case REF_STATUS_NONE:
+		print_ref_status('X', "[no match]", ref, NULL, NULL);
+		break;
+	case REF_STATUS_REJECT_NODELETE:
+		print_ref_status('!', "[rejected]", ref, NULL,
+				"remote does not support deleting refs");
+		break;
+	case REF_STATUS_UPTODATE:
+		print_ref_status('=', "[up to date]", ref,
+				ref->peer_ref, NULL);
+		break;
+	case REF_STATUS_REJECT_NONFASTFORWARD:
+		print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+				"non-fast forward");
+		break;
+	case REF_STATUS_REMOTE_REJECT:
+		print_ref_status('!', "[remote rejected]", ref,
+				ref->deletion ? NULL : ref->peer_ref,
+				ref->remote_status);
+		break;
+	case REF_STATUS_EXPECTING_REPORT:
+		print_ref_status('!', "[remote failure]", ref,
+				ref->deletion ? NULL : ref->peer_ref,
+				"remote failed to report status");
+		break;
+	case REF_STATUS_OK:
+		print_ok_ref_status(ref);
+		break;
+	}
+
+	return 1;
+}
+
+static void print_push_status(const char *dest, struct ref *refs)
+{
+	struct ref *ref;
+	int n = 0;
+
+	if (args.verbose) {
+		for (ref = refs; ref; ref = ref->next)
+			if (ref->status == REF_STATUS_UPTODATE)
+				n += print_one_push_status(ref, dest, n);
+	}
+
+	for (ref = refs; ref; ref = ref->next)
+		if (ref->status == REF_STATUS_OK)
+			n += print_one_push_status(ref, dest, n);
+
+	for (ref = refs; ref; ref = ref->next) {
+		if (ref->status != REF_STATUS_NONE &&
+		    ref->status != REF_STATUS_UPTODATE &&
+		    ref->status != REF_STATUS_OK)
+			n += print_one_push_status(ref, dest, n);
+	}
+}
+
+static int refs_pushed(struct ref *ref)
+{
+	for (; ref; ref = ref->next) {
+		switch(ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+			break;
+		default:
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
+{
+	struct ref *ref;
+	int new_refs;
+	int ask_for_status_report = 0;
+	int allow_deleting_refs = 0;
+	int expect_status_report = 0;
+	int flags = MATCH_REFS_NONE;
+	int ret;
+
+	if (args.send_all)
+		flags |= MATCH_REFS_ALL;
+	if (args.send_mirror)
+		flags |= MATCH_REFS_MIRROR;
+
+	/* No funny business with the matcher */
+	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
+	get_local_heads();
+
+	/* Does the other end support the reporting? */
+	if (server_supports("report-status"))
+		ask_for_status_report = 1;
+	if (server_supports("delete-refs"))
+		allow_deleting_refs = 1;
+
+	/* match them up */
+	if (!remote_tail)
+		remote_tail = &remote_refs;
+	if (match_refs(local_refs, remote_refs, &remote_tail,
+					       nr_refspec, refspec, flags))
+		return -1;
+
+	if (!remote_refs) {
+		fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+			"Perhaps you should specify a branch such as 'master'.\n");
+		return 0;
+	}
+
+	/*
+	 * Finally, tell the other end!
+	 */
+	new_refs = 0;
+	for (ref = remote_refs; ref; ref = ref->next) {
+		const unsigned char *new_sha1;
+
+		if (!ref->peer_ref) {
+			if (!args.send_mirror)
+				continue;
+			new_sha1 = null_sha1;
+		}
+		else
+			new_sha1 = ref->peer_ref->new_sha1;
+
+
+		ref->deletion = is_null_sha1(new_sha1);
+		if (ref->deletion && !allow_deleting_refs) {
+			ref->status = REF_STATUS_REJECT_NODELETE;
+			continue;
+		}
+		if (!ref->deletion &&
+		    !hashcmp(ref->old_sha1, new_sha1)) {
+			ref->status = REF_STATUS_UPTODATE;
+			continue;
+		}
+
+		/* This part determines what can overwrite what.
+		 * The rules are:
+		 *
+		 * (0) you can always use --force or +A:B notation to
+		 *     selectively force individual ref pairs.
+		 *
+		 * (1) if the old thing does not exist, it is OK.
+		 *
+		 * (2) if you do not have the old thing, you are not allowed
+		 *     to overwrite it; you would not know what you are losing
+		 *     otherwise.
+		 *
+		 * (3) if both new and old are commit-ish, and new is a
+		 *     descendant of old, it is OK.
+		 *
+		 * (4) regardless of all of the above, removing :B is
+		 *     always allowed.
+		 */
+
+		ref->nonfastforward =
+		    !ref->deletion &&
+		    !is_null_sha1(ref->old_sha1) &&
+		    (!has_sha1_file(ref->old_sha1)
+		      || !ref_newer(new_sha1, ref->old_sha1));
+
+		if (ref->nonfastforward && !ref->force && !args.force_update) {
+			ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+			continue;
+		}
+
+		hashcpy(ref->new_sha1, new_sha1);
+		if (!ref->deletion)
+			new_refs++;
+
+		if (!args.dry_run) {
+			char *old_hex = sha1_to_hex(ref->old_sha1);
+			char *new_hex = sha1_to_hex(ref->new_sha1);
+
+			if (ask_for_status_report) {
+				packet_write(out, "%s %s %s%c%s",
+					old_hex, new_hex, ref->name, 0,
+					"report-status");
+				ask_for_status_report = 0;
+				expect_status_report = 1;
+			}
+			else
+				packet_write(out, "%s %s %s",
+					old_hex, new_hex, ref->name);
+		}
+		ref->status = expect_status_report ?
+			REF_STATUS_EXPECTING_REPORT :
+			REF_STATUS_OK;
+	}
+
+	packet_flush(out);
+	if (new_refs && !args.dry_run) {
+		if (pack_objects(out, remote_refs) < 0) {
+			close(out);
+			return -1;
+		}
+	}
+	close(out);
+
+	if (expect_status_report)
+		ret = receive_status(in, remote_refs);
+	else
+		ret = 0;
+
+	print_push_status(dest, remote_refs);
+
+	if (!args.dry_run && remote) {
+		for (ref = remote_refs; ref; ref = ref->next)
+			update_tracking_ref(remote, ref);
+	}
+
+	if (!refs_pushed(remote_refs))
+		fprintf(stderr, "Everything up-to-date\n");
+	if (ret < 0)
+		return ret;
+	for (ref = remote_refs; ref; ref = ref->next) {
+		switch (ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+		case REF_STATUS_OK:
+			break;
+		default:
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void verify_remote_names(int nr_heads, const char **heads)
+{
+	int i;
+
+	for (i = 0; i < nr_heads; i++) {
+		const char *remote = strchr(heads[i], ':');
+
+		remote = remote ? (remote + 1) : heads[i];
+		switch (check_ref_format(remote)) {
+		case 0: /* ok */
+		case -2: /* ok but a single level -- that is fine for
+			  * a match pattern.
+			  */
+		case -3: /* ok but ends with a pattern-match character */
+			continue;
+		}
+		die("remote part of refspec is not a valid name in %s",
+		    heads[i]);
+	}
+}
+
+int cmd_send_pack(int argc, const char **argv, const char *prefix)
+{
+	int i, nr_heads = 0;
+	const char **heads = NULL;
+	const char *remote_name = NULL;
+	struct remote *remote = NULL;
+	const char *dest = NULL;
+
+	argv++;
+	for (i = 1; i < argc; i++, argv++) {
+		const char *arg = *argv;
+
+		if (*arg == '-') {
+			if (!prefixcmp(arg, "--receive-pack=")) {
+				args.receivepack = arg + 15;
+				continue;
+			}
+			if (!prefixcmp(arg, "--exec=")) {
+				args.receivepack = arg + 7;
+				continue;
+			}
+			if (!prefixcmp(arg, "--remote=")) {
+				remote_name = arg + 9;
+				continue;
+			}
+			if (!strcmp(arg, "--all")) {
+				args.send_all = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--dry-run")) {
+				args.dry_run = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--mirror")) {
+				args.send_mirror = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--force")) {
+				args.force_update = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--verbose")) {
+				args.verbose = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--thin")) {
+				args.use_thin_pack = 1;
+				continue;
+			}
+			usage(send_pack_usage);
+		}
+		if (!dest) {
+			dest = arg;
+			continue;
+		}
+		heads = (const char **) argv;
+		nr_heads = argc - i;
+		break;
+	}
+	if (!dest)
+		usage(send_pack_usage);
+	/*
+	 * --all and --mirror are incompatible; neither makes sense
+	 * with any refspecs.
+	 */
+	if ((heads && (args.send_all || args.send_mirror)) ||
+					(args.send_all && args.send_mirror))
+		usage(send_pack_usage);
+
+	if (remote_name) {
+		remote = remote_get(remote_name);
+		if (!remote_has_url(remote, dest)) {
+			die("Destination %s is not a uri for %s",
+			    dest, remote_name);
+		}
+	}
+
+	return send_pack(&args, dest, remote, nr_heads, heads);
+}
+
+int send_pack(struct send_pack_args *my_args,
+	      const char *dest, struct remote *remote,
+	      int nr_heads, const char **heads)
+{
+	int fd[2], ret;
+	struct child_process *conn;
+
+	memcpy(&args, my_args, sizeof(args));
+
+	verify_remote_names(nr_heads, heads);
+
+	conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
+	ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
+	close(fd[0]);
+	close(fd[1]);
+	ret |= finish_connect(conn);
+	return !!ret;
+}
diff --git a/builtin-tag.c b/builtin-tag.c
index cbb0f04..114c684 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -11,9 +11,15 @@
 #include "refs.h"
 #include "tag.h"
 #include "run-command.h"
+#include "parse-options.h"
 
-static const char builtin_tag_usage[] =
-  "git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
+static const char * const git_tag_usage[] = {
+	"git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
+	"git-tag -d <tagname>...",
+	"git-tag [-n [<num>]] -l [<pattern>]",
+	"git-tag -v <tagname>...",
+	NULL
+};
 
 static char signingkey[1000];
 
@@ -276,7 +282,7 @@
 
 static void create_tag(const unsigned char *object, const char *tag,
 		       struct strbuf *buf, int message, int sign,
-			   unsigned char *prev, unsigned char *result)
+		       unsigned char *prev, unsigned char *result)
 {
 	enum object_type type;
 	char header_buf[1024];
@@ -335,105 +341,101 @@
 		die("unable to write tag file");
 }
 
+struct msg_arg {
+	int given;
+	struct strbuf buf;
+};
+
+static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+{
+	struct msg_arg *msg = opt->value;
+
+	if (!arg)
+		return -1;
+	if (msg->buf.len)
+		strbuf_addstr(&(msg->buf), "\n\n");
+	strbuf_addstr(&(msg->buf), arg);
+	msg->given = 1;
+	return 0;
+}
+
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
 	struct strbuf buf;
 	unsigned char object[20], prev[20];
-	int annotate = 0, sign = 0, force = 0, lines = 0, message = 0;
 	char ref[PATH_MAX];
 	const char *object_ref, *tag;
-	int i;
 	struct ref_lock *lock;
 
+	int annotate = 0, sign = 0, force = 0, lines = 0,
+					delete = 0, verify = 0;
+	char *list = NULL, *msgfile = NULL, *keyid = NULL;
+	const char *no_pattern = "NO_PATTERN";
+	struct msg_arg msg = { 0, STRBUF_INIT };
+	struct option options[] = {
+		{ OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names",
+			PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern },
+		{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
+				"print n lines of each tag message",
+				PARSE_OPT_OPTARG, NULL, 1 },
+		OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
+		OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
+
+		OPT_GROUP("Tag creation options"),
+		OPT_BOOLEAN('a', NULL, &annotate,
+					"annotated tag, needs a message"),
+		OPT_CALLBACK('m', NULL, &msg, "msg",
+			     "message for the tag", parse_msg_arg),
+		OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
+		OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
+		OPT_STRING('u', NULL, &keyid, "key-id",
+					"use another key to sign the tag"),
+		OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+		OPT_END()
+	};
+
 	git_config(git_tag_config);
+
+	argc = parse_options(argc, argv, options, git_tag_usage, 0);
+
+	if (sign)
+		annotate = 1;
+
+	if (list)
+		return list_tags(list == no_pattern ? NULL : list, lines);
+	if (delete)
+		return for_each_tag_name(argv, delete_tag);
+	if (verify)
+		return for_each_tag_name(argv, verify_tag);
+
 	strbuf_init(&buf, 0);
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (arg[0] != '-')
-			break;
-		if (!strcmp(arg, "-a")) {
-			annotate = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-s")) {
-			annotate = 1;
-			sign = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-f")) {
-			force = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-n")) {
-			if (i + 1 == argc || *argv[i + 1] == '-')
-				/* no argument */
-				lines = 1;
-			else
-				lines = isdigit(*argv[++i]) ?
-					atoi(argv[i]) : 1;
-			continue;
-		}
-		if (!strcmp(arg, "-m")) {
-			annotate = 1;
-			i++;
-			if (i == argc)
-				die("option -m needs an argument.");
-			if (message)
-				die("only one -F or -m option is allowed.");
-			strbuf_addstr(&buf, argv[i]);
-			message = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-F")) {
-			annotate = 1;
-			i++;
-			if (i == argc)
-				die("option -F needs an argument.");
-			if (message)
-				die("only one -F or -m option is allowed.");
-
-			if (!strcmp(argv[i], "-")) {
+	if (msg.given || msgfile) {
+		if (msg.given && msgfile)
+			die("only one -F or -m option is allowed.");
+		annotate = 1;
+		if (msg.given)
+			strbuf_addbuf(&buf, &(msg.buf));
+		else {
+			if (!strcmp(msgfile, "-")) {
 				if (strbuf_read(&buf, 0, 1024) < 0)
-					die("cannot read %s", argv[i]);
+					die("cannot read %s", msgfile);
 			} else {
-				if (strbuf_read_file(&buf, argv[i], 1024) < 0)
+				if (strbuf_read_file(&buf, msgfile, 1024) < 0)
 					die("could not open or read '%s': %s",
-						argv[i], strerror(errno));
+						msgfile, strerror(errno));
 			}
-			message = 1;
-			continue;
 		}
-		if (!strcmp(arg, "-u")) {
-			annotate = 1;
-			sign = 1;
-			i++;
-			if (i == argc)
-				die("option -u needs an argument.");
-			if (strlcpy(signingkey, argv[i], sizeof(signingkey))
-							>= sizeof(signingkey))
-				die("argument to option -u too long");
-			continue;
-		}
-		if (!strcmp(arg, "-l"))
-			return list_tags(argv[i + 1], lines);
-		if (!strcmp(arg, "-d"))
-			return for_each_tag_name(argv + i + 1, delete_tag);
-		if (!strcmp(arg, "-v"))
-			return for_each_tag_name(argv + i + 1, verify_tag);
-		usage(builtin_tag_usage);
 	}
 
-	if (i == argc) {
+	if (argc == 0) {
 		if (annotate)
-			usage(builtin_tag_usage);
+			usage_with_options(git_tag_usage, options);
 		return list_tags(NULL, lines);
 	}
-	tag = argv[i++];
+	tag = argv[0];
 
-	object_ref = i < argc ? argv[i] : "HEAD";
-	if (i + 1 < argc)
+	object_ref = argc == 2 ? argv[1] : "HEAD";
+	if (argc > 2)
 		die("too many params");
 
 	if (get_sha1(object_ref, object))
@@ -450,7 +452,8 @@
 		die("tag '%s' already exists", tag);
 
 	if (annotate)
-		create_tag(object, tag, &buf, message, sign, prev, object);
+		create_tag(object, tag, &buf, msg.given || msgfile,
+			   sign, prev, object);
 
 	lock = lock_any_ref_for_update(ref, prev, 0);
 	if (!lock)
diff --git a/builtin.h b/builtin.h
index bcb54aa..98ada7b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -24,6 +24,7 @@
 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_cherry_pick(int argc, const char **argv, const char *prefix);
+extern int cmd_clean(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_describe(int argc, const char **argv, const char *prefix);
@@ -48,6 +49,7 @@
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
@@ -70,6 +72,7 @@
 extern int cmd_revert(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_send_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_shortlog(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);
diff --git a/cache.h b/cache.h
index 33ebccf..aaa135b 100644
--- a/cache.h
+++ b/cache.h
@@ -290,6 +290,7 @@
 
 struct lock_file {
 	struct lock_file *next;
+	int fd;
 	pid_t owner;
 	char on_list;
 	char filename[PATH_MAX];
@@ -499,8 +500,20 @@
 	struct ref *next;
 	unsigned char old_sha1[20];
 	unsigned char new_sha1[20];
-	unsigned char force;
-	unsigned char merge;
+	unsigned int force:1,
+		merge:1,
+		nonfastforward:1,
+		deletion:1;
+	enum {
+		REF_STATUS_NONE = 0,
+		REF_STATUS_OK,
+		REF_STATUS_REJECT_NONFASTFORWARD,
+		REF_STATUS_REJECT_NODELETE,
+		REF_STATUS_UPTODATE,
+		REF_STATUS_REMOTE_REJECT,
+		REF_STATUS_EXPECTING_REPORT,
+	} status;
+	char *remote_status;
 	struct ref *peer_ref; /* when renaming */
 	char name[FLEX_ARRAY]; /* more */
 };
@@ -509,8 +522,10 @@
 #define REF_HEADS	(1u << 1)
 #define REF_TAGS	(1u << 2)
 
+extern struct ref *find_ref_by_name(struct ref *list, const char *name);
+
 #define CONNECT_VERBOSE       (1u << 0)
-extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
+extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 extern int finish_connect(struct child_process *conn);
 extern int path_match(const char *path, int nr, char **match);
 extern int get_ack(int fd, unsigned char *result_sha1);
@@ -556,6 +571,7 @@
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
 extern int git_config_rename_section(const char *, const char *);
+extern const char *git_etc_gitconfig(void);
 extern int check_repository_format_version(const char *var, const char *value);
 
 #define MAX_GITNAME (1000)
diff --git a/config.c b/config.c
index 56e99fc..ed96213 100644
--- a/config.c
+++ b/config.c
@@ -6,6 +6,7 @@
  *
  */
 #include "cache.h"
+#include "exec_cmd.h"
 
 #define MAXNAME (256)
 
@@ -459,6 +460,21 @@
 	return ret;
 }
 
+const char *git_etc_gitconfig(void)
+{
+	static const char *system_wide;
+	if (!system_wide) {
+		system_wide = ETC_GITCONFIG;
+		if (!is_absolute_path(system_wide)) {
+			/* interpret path relative to exec-dir */
+			const char *exec_path = git_exec_path();
+			system_wide = prefix_path(exec_path, strlen(exec_path),
+						system_wide);
+		}
+	}
+	return system_wide;
+}
+
 int git_config(config_fn_t fn)
 {
 	int ret = 0;
@@ -471,8 +487,8 @@
 	 * config file otherwise. */
 	filename = getenv(CONFIG_ENVIRONMENT);
 	if (!filename) {
-		if (!access(ETC_GITCONFIG, R_OK))
-			ret += git_config_from_file(fn, ETC_GITCONFIG);
+		if (!access(git_etc_gitconfig(), R_OK))
+			ret += git_config_from_file(fn, git_etc_gitconfig());
 		home = getenv("HOME");
 		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!filename)
diff --git a/configure.ac b/configure.ac
index 7bcf1a4..5f8a15b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -415,7 +415,7 @@
 # times (my ext3 doesn't).
 #
 # Define USE_STDEV below if you want git to care about the underlying device
-# change being considered an inode change from the update-cache perspective.
+# change being considered an inode change from the update-index perspective.
 
 
 ## Output files
diff --git a/connect.c b/connect.c
index 44e423d..3aefd4a 100644
--- a/connect.c
+++ b/connect.c
@@ -36,6 +36,11 @@
 	return !(flags & ~REF_NORMAL);
 }
 
+int check_ref_type(const struct ref *ref, int flags)
+{
+	return check_ref(ref->name, strlen(ref->name), flags);
+}
+
 /*
  * Read all the refs from the other end
  */
@@ -476,9 +481,10 @@
  *
  * If it returns, the connect is successful; it just dies on errors.
  */
-struct child_process *git_connect(int fd[2], char *url,
+struct child_process *git_connect(int fd[2], const char *url_orig,
 				  const char *prog, int flags)
 {
+	char *url = xstrdup(url_orig);
 	char *host, *path = url;
 	char *end;
 	int c;
@@ -568,6 +574,7 @@
 			     prog, path, 0,
 			     target_host, 0);
 		free(target_host);
+		free(url);
 		if (free_path)
 			free(path);
 		return NULL;
@@ -619,6 +626,7 @@
 	fd[0] = conn->out; /* read from child's stdout */
 	fd[1] = conn->in;  /* write to child's stdin */
 	strbuf_release(&cmd);
+	free(url);
 	if (free_path)
 		free(path);
 	return conn;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 599b2fc..58e0e53 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -551,6 +551,20 @@
 
 _git_diff ()
 {
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--cached --stat --numstat --shortstat --summary
+			--patch-with-stat --name-only --name-status --color
+			--no-color --color-words --no-renames --check
+			--full-index --binary --abbrev --diff-filter
+			--find-copies-harder --pickaxe-all --pickaxe-regex
+			--text --ignore-space-at-eol --ignore-space-change
+			--ignore-all-space --exit-code --quiet --ext-diff
+			--no-ext-diff"
+		return
+		;;
+	esac
 	__git_complete_file
 }
 
diff --git a/git-clean.sh b/contrib/examples/git-clean.sh
similarity index 100%
rename from git-clean.sh
rename to contrib/examples/git-clean.sh
diff --git a/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh
similarity index 100%
rename from git-ls-remote.sh
rename to contrib/examples/git-ls-remote.sh
diff --git a/contrib/examples/git-merge-ours.sh b/contrib/examples/git-merge-ours.sh
index c81a790..29dba4b 100755
--- a/contrib/examples/git-merge-ours.sh
+++ b/contrib/examples/git-merge-ours.sh
@@ -9,6 +9,6 @@
 # because the current index is what we will be committing as the
 # merge result.
 
-git diff-index --quiet --cached HEAD || exit 2
+git diff-index --quiet --cached HEAD -- || exit 2
 
 exit 0
diff --git a/diff-lib.c b/diff-lib.c
index f8e936a..d85d8f3 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -231,7 +231,7 @@
 static int is_outside_repo(const char *path, int nongit, const char *prefix)
 {
 	int i;
-	if (nongit || !strcmp(path, "-") || path[0] == '/')
+	if (nongit || !strcmp(path, "-") || is_absolute_path(path))
 		return 1;
 	if (prefixcmp(path, "../"))
 		return 0;
diff --git a/diffcore-rename.c b/diffcore-rename.c
index f9ebea5..3d37725 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -244,28 +244,35 @@
 	 * Walk over all the destinations ...
 	 */
 	do {
-		struct diff_filespec *one = dst->filespec;
+		struct diff_filespec *target = dst->filespec;
 		struct file_similarity *p, *best;
-		int i = 100;
+		int i = 100, best_score = -1;
 
 		/*
 		 * .. to find the best source match
 		 */
 		best = NULL;
 		for (p = src; p; p = p->next) {
-			struct diff_filespec *two = p->filespec;
+			int score;
+			struct diff_filespec *source = p->filespec;
 
 			/* False hash collission? */
-			if (hashcmp(one->sha1, two->sha1))
+			if (hashcmp(source->sha1, target->sha1))
 				continue;
 			/* Non-regular files? If so, the modes must match! */
-			if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) {
-				if (one->mode != two->mode)
+			if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
+				if (source->mode != target->mode)
 					continue;
 			}
-			best = p;
-			if (basename_same(one, two))
-				break;
+			/* Give higher scores to sources that haven't been used already */
+			score = !source->rename_used;
+			score += basename_same(source, target);
+			if (score > best_score) {
+				best = p;
+				best_score = score;
+				if (score == 2)
+					break;
+			}
 
 			/* Too many identical alternatives? Pick one */
 			if (!--i)
@@ -490,6 +497,19 @@
 	qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
 	for (i = 0; i < num_create * num_src; i++) {
 		struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
+		struct diff_filespec *src;
+		if (dst->pair)
+			continue; /* already done, either exact or fuzzy. */
+		if (mx[i].score < minimum_score)
+			break; /* there is no more usable pair. */
+		src = rename_src[mx[i].src].one;
+		if (src->rename_used)
+			continue;
+		record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
+		rename_count++;
+	}
+	for (i = 0; i < num_create * num_src; i++) {
+		struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
 		if (dst->pair)
 			continue; /* already done, either exact or fuzzy. */
 		if (mx[i].score < minimum_score)
diff --git a/dir.c b/dir.c
index 225fdfb..d448902 100644
--- a/dir.c
+++ b/dir.c
@@ -144,17 +144,14 @@
 		x->flags |= EXC_FLAG_NOWILDCARD;
 	if (*string == '*' && no_wildcard(string+1))
 		x->flags |= EXC_FLAG_ENDSWITH;
-	if (which->nr == which->alloc) {
-		which->alloc = alloc_nr(which->alloc);
-		which->excludes = xrealloc(which->excludes,
-					   which->alloc * sizeof(x));
-	}
+	ALLOC_GROW(which->excludes, which->nr + 1, which->alloc);
 	which->excludes[which->nr++] = x;
 }
 
 static int add_excludes_from_file_1(const char *fname,
 				    const char *base,
 				    int baselen,
+				    char **buf_p,
 				    struct exclude_list *which)
 {
 	struct stat st;
@@ -175,6 +172,8 @@
 		goto err;
 	close(fd);
 
+	if (buf_p)
+		*buf_p = buf;
 	buf[size++] = '\n';
 	entry = buf;
 	for (i = 0; i < size; i++) {
@@ -196,31 +195,63 @@
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
-	if (add_excludes_from_file_1(fname, "", 0,
+	if (add_excludes_from_file_1(fname, "", 0, NULL,
 				     &dir->exclude_list[EXC_FILE]) < 0)
 		die("cannot use %s as an exclude file", fname);
 }
 
-int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
+static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
 {
-	char exclude_file[PATH_MAX];
-	struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
-	int current_nr = el->nr;
+	struct exclude_list *el;
+	struct exclude_stack *stk = NULL;
+	int current;
 
-	if (dir->exclude_per_dir) {
-		memcpy(exclude_file, base, baselen);
-		strcpy(exclude_file + baselen, dir->exclude_per_dir);
-		add_excludes_from_file_1(exclude_file, base, baselen, el);
+	if ((!dir->exclude_per_dir) ||
+	    (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
+		return; /* too long a path -- ignore */
+
+	/* Pop the ones that are not the prefix of the path being checked. */
+	el = &dir->exclude_list[EXC_DIRS];
+	while ((stk = dir->exclude_stack) != NULL) {
+		if (stk->baselen <= baselen &&
+		    !strncmp(dir->basebuf, base, stk->baselen))
+			break;
+		dir->exclude_stack = stk->prev;
+		while (stk->exclude_ix < el->nr)
+			free(el->excludes[--el->nr]);
+		free(stk->filebuf);
+		free(stk);
 	}
-	return current_nr;
-}
 
-void pop_exclude_per_directory(struct dir_struct *dir, int stk)
-{
-	struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
+	/* Read from the parent directories and push them down. */
+	current = stk ? stk->baselen : -1;
+	while (current < baselen) {
+		struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
+		const char *cp;
 
-	while (stk < el->nr)
-		free(el->excludes[--el->nr]);
+		if (current < 0) {
+			cp = base;
+			current = 0;
+		}
+		else {
+			cp = strchr(base + current + 1, '/');
+			if (!cp)
+				die("oops in prep_exclude");
+			cp++;
+		}
+		stk->prev = dir->exclude_stack;
+		stk->baselen = cp - base;
+		stk->exclude_ix = el->nr;
+		memcpy(dir->basebuf + current, base + current,
+		       stk->baselen - current);
+		strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
+		add_excludes_from_file_1(dir->basebuf,
+					 dir->basebuf, stk->baselen,
+					 &stk->filebuf, el);
+		dir->exclude_stack = stk;
+		current = stk->baselen;
+	}
+	dir->basebuf[baselen] = '\0';
 }
 
 /* Scan the list and let the last match determines the fate.
@@ -287,6 +318,7 @@
 	const char *basename = strrchr(pathname, '/');
 	basename = (basename) ? basename+1 : pathname;
 
+	prep_exclude(dir, pathname, basename-pathname);
 	for (st = EXC_CMDL; st <= EXC_FILE; st++) {
 		switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
 		case 0:
@@ -504,13 +536,10 @@
 	int contents = 0;
 
 	if (fdir) {
-		int exclude_stk;
 		struct dirent *de;
 		char fullname[PATH_MAX + 1];
 		memcpy(fullname, base, baselen);
 
-		exclude_stk = push_exclude_per_directory(dir, base, baselen);
-
 		while ((de = readdir(fdir)) != NULL) {
 			int len, dtype;
 			int exclude;
@@ -584,8 +613,6 @@
 		}
 exit_early:
 		closedir(fdir);
-
-		pop_exclude_per_directory(dir, exclude_stk);
 	}
 
 	return contents;
@@ -654,47 +681,18 @@
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
 	struct path_simplify *simplify = create_simplify(pathspec);
-	char *pp = NULL;
-
-	/*
-	 * Make sure to do the per-directory exclude for all the
-	 * directories leading up to our base.
-	 */
-	if (baselen) {
-		if (dir->exclude_per_dir) {
-			char *p;
-			pp = xmalloc(baselen+1);
-			memcpy(pp, base, baselen+1);
-			p = pp;
-			while (1) {
-				char save = *p;
-				*p = 0;
-				push_exclude_per_directory(dir, pp, p-pp);
-				*p++ = save;
-				if (!save)
-					break;
-				p = strchr(p, '/');
-				if (p)
-					p++;
-				else
-					p = pp + baselen;
-			}
-		}
-	}
 
 	read_directory_recursive(dir, path, base, baselen, 0, simplify);
 	free_simplify(simplify);
-	free(pp);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
 	qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
 	return dir->nr;
 }
 
-int
-file_exists(const char *f)
+int file_exists(const char *f)
 {
-  struct stat sb;
-  return stat(f, &sb) == 0;
+	struct stat sb;
+	return stat(f, &sb) == 0;
 }
 
 /*
diff --git a/dir.h b/dir.h
index 82009dc..d8814dc 100644
--- a/dir.h
+++ b/dir.h
@@ -1,17 +1,6 @@
 #ifndef DIR_H
 #define DIR_H
 
-/*
- * We maintain three exclude pattern lists:
- * EXC_CMDL lists patterns explicitly given on the command line.
- * EXC_DIRS lists patterns obtained from per-directory ignore files.
- * EXC_FILE lists patterns from fallback ignore files.
- */
-#define EXC_CMDL 0
-#define EXC_DIRS 1
-#define EXC_FILE 2
-
-
 struct dir_entry {
 	unsigned int len;
 	char name[FLEX_ARRAY]; /* more */
@@ -34,6 +23,13 @@
 	} **excludes;
 };
 
+struct exclude_stack {
+	struct exclude_stack *prev;
+	char *filebuf;
+	int baselen;
+	int exclude_ix;
+};
+
 struct dir_struct {
 	int nr, alloc;
 	int ignored_nr, ignored_alloc;
@@ -48,6 +44,18 @@
 	/* Exclude info */
 	const char *exclude_per_dir;
 	struct exclude_list exclude_list[3];
+	/*
+	 * We maintain three exclude pattern lists:
+	 * EXC_CMDL lists patterns explicitly given on the command line.
+	 * EXC_DIRS lists patterns obtained from per-directory ignore files.
+	 * EXC_FILE lists patterns from fallback ignore files.
+	 */
+#define EXC_CMDL 0
+#define EXC_DIRS 1
+#define EXC_FILE 2
+
+	struct exclude_stack *exclude_stack;
+	char basebuf[PATH_MAX];
 };
 
 extern int common_prefix(const char **pathspec);
@@ -58,8 +66,6 @@
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 
 extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
-extern int push_exclude_per_directory(struct dir_struct *, const char *, int);
-extern void pop_exclude_per_directory(struct dir_struct *, int);
 
 extern int excluded(struct dir_struct *, const char *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
diff --git a/git-am.sh b/git-am.sh
index 4126f0e..76c1c84 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -218,7 +218,7 @@
 
 case "$resolved" in
 '')
-	files=$(git diff-index --cached --name-only HEAD) || exit
+	files=$(git diff-index --cached --name-only HEAD --) || exit
 	if [ "$files" ]; then
 	   echo "Dirty index: cannot apply patches (dirty: $files)" >&2
 	   exit 1
@@ -352,7 +352,7 @@
 		case "$resolved$interactive" in
 		tt)
 			# This is used only for interactive view option.
-			git diff-index -p --cached HEAD >"$dotest/patch"
+			git diff-index -p --cached HEAD -- >"$dotest/patch"
 			;;
 		esac
 	esac
@@ -411,7 +411,7 @@
 		# trust what the user has in the index file and the
 		# working tree.
 		resolved=
-		git diff-index --quiet --cached HEAD && {
+		git diff-index --quiet --cached HEAD -- && {
 			echo "No changes - did you forget to use 'git add'?"
 			stop_here_user_resolve $this
 		}
@@ -433,7 +433,7 @@
 		then
 		    # Applying the patch to an earlier tree and merging the
 		    # result may have produced the same tree as ours.
-		    git diff-index --quiet --cached HEAD && {
+		    git diff-index --quiet --cached HEAD -- && {
 			echo No changes -- Patch already applied.
 			go_next
 			continue
diff --git a/git-bisect.sh b/git-bisect.sh
index 3aac816..7a6521e 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -37,7 +37,7 @@
 }
 
 bisect_autostart() {
-	test -d "$GIT_DIR/refs/bisect" || {
+	test -f "$GIT_DIR/BISECT_NAMES" || {
 		echo >&2 'You need to start by "git bisect start"'
 		if test -t 0
 		then
@@ -72,7 +72,7 @@
 		;;
 	refs/heads/*)
 		[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
-		echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
+		echo "${head#refs/heads/}" >"$GIT_DIR/head-name"
 		;;
 	*)
 		die "Bad HEAD - strange symbolic ref"
@@ -83,7 +83,6 @@
 	# Get rid of any old bisect state
 	#
 	bisect_clean_state
-	mkdir "$GIT_DIR/refs/bisect"
 
 	#
 	# Check for one bad and then some good revisions.
@@ -131,7 +130,7 @@
 		good|skip)	tag="$state"-"$rev" ;;
 		*)		die "Bad bisect_write argument: $state" ;;
 	esac
-	echo "$rev" >"$GIT_DIR/refs/bisect/$tag"
+	git update-ref "refs/bisect/$tag" "$rev"
 	echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
 	test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
@@ -192,7 +191,7 @@
 		;;
 	*)
 		THEN=''
-		test -d "$GIT_DIR/refs/bisect" || {
+		test -f "$GIT_DIR/BISECT_NAMES" || {
 			echo >&2 'You need to start by "git bisect start".'
 			THEN='then '
 		}
@@ -276,8 +275,7 @@
 	if expr "$_tried" : ".*[|].*" > /dev/null ; then
 		echo "There are only 'skip'ped commit left to test."
 		echo "The first bad commit could be any of:"
-		echo "$_tried" | sed -e 's/[|]/\
-/g'
+		echo "$_tried" | tr '[|]' '[\012]'
 		echo "We cannot bisect more!"
 		exit 2
 	fi
@@ -318,20 +316,23 @@
 	exit_if_skipped_commits "$bisect_rev"
 
 	echo "Bisecting: $bisect_nr revisions left to test after this"
-	echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
+	git branch -f new-bisect "$bisect_rev"
 	git checkout -q new-bisect || exit
-	mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
-	GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect
+	git branch -M new-bisect bisect
 	git show-branch "$bisect_rev"
 }
 
 bisect_visualize() {
 	bisect_next_check fail
-	not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
-	eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
+	not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
+	eval gitk refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
+	test -f "$GIT_DIR/BISECT_NAMES" || {
+		echo "We are not bisecting."
+		return
+	}
 	case "$#" in
 	0) if [ -s "$GIT_DIR/head-name" ]; then
 	       branch=`cat "$GIT_DIR/head-name"`
@@ -351,8 +352,12 @@
 }
 
 bisect_clean_state() {
-	rm -fr "$GIT_DIR/refs/bisect"
-	rm -f "$GIT_DIR/refs/heads/bisect"
+	# There may be some refs packed during bisection.
+	git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect |
+	while read ref hash
+	do
+		git update-ref -d $ref $hash
+	done
 	rm -f "$GIT_DIR/BISECT_LOG"
 	rm -f "$GIT_DIR/BISECT_NAMES"
 	rm -f "$GIT_DIR/BISECT_RUN"
diff --git a/git-checkout.sh b/git-checkout.sh
index aa724ac..f6d58ac 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -175,7 +175,7 @@
 describe_detached_head () {
 	test -n "$quiet" || {
 		printf >&2 "$1 "
-		GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
+		GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" --
 	}
 }
 
@@ -266,7 +266,7 @@
 	if test -n "$branch"
 	then
 		old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
-		GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch"
+		GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch"
 		if test -n "$quiet"
 		then
 			true	# nothing
@@ -278,7 +278,8 @@
 		fi
 	elif test -n "$detached"
 	then
-		git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
+		old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
+		git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" ||
 			die "Cannot detach HEAD"
 		if test -n "$detach_warn"
 		then
diff --git a/git-compat-util.h b/git-compat-util.h
index 86c8962..79eb10e 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -67,6 +67,8 @@
 #include <fnmatch.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
 #include <assert.h>
 #include <regex.h>
 #include <netinet/in.h>
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index efa6a0c..92648f4 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -108,10 +108,6 @@
             }
 		}
 	}
-    if (@ARGV == 0) {
-        chomp(my $module = `git-repo-config --get cvsimport.module`);
-        push(@ARGV, $module);
-    }
 }
 
 my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
@@ -119,6 +115,10 @@
 getopts($opts) or usage();
 usage if $opt_h;
 
+if (@ARGV == 0) {
+		chomp(my $module = `git-repo-config --get cvsimport.module`);
+		push(@ARGV, $module) if $? == 0;
+}
 @ARGV <= 1 or usage("You can't specify more than one CVS module");
 
 if ($opt_d) {
@@ -527,18 +527,12 @@
 	return $s =~ /^[a-f0-9]{40}$/;
 }
 
-sub get_headref ($$) {
-    my $name    = shift;
-    my $git_dir = shift;
-
-    my $f = "$git_dir/$remote/$name";
-    if (open(my $fh, $f)) {
-	    chomp(my $r = <$fh>);
-	    is_sha1($r) or die "Cannot get head id for $name ($r): $!";
-	    return $r;
-    }
-    die "unable to open $f: $!" unless $! == POSIX::ENOENT;
-    return undef;
+sub get_headref ($) {
+	my $name = shift;
+	my $r = `git rev-parse --verify '$name' 2>/dev/null`;
+	return undef unless $? == 0;
+	chomp $r;
+	return $r;
 }
 
 -d $git_tree
@@ -698,7 +692,8 @@
 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
 
 sub commit {
-	if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
+	if ($branch eq $opt_o && !$index{branch} &&
+		!get_headref("$remote/$branch")) {
 	    # looks like an initial commit
 	    # use the index primed by git-init
 	    $ENV{GIT_INDEX_FILE} = "$git_dir/index";
@@ -722,7 +717,7 @@
 	update_index(@old, @new);
 	@old = @new = ();
 	my $tree = write_tree();
-	my $parent = get_headref($last_branch, $git_dir);
+	my $parent = get_headref("$remote/$last_branch");
 	print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
 
 	my @commit_args;
@@ -733,7 +728,7 @@
 	foreach my $rx (@mergerx) {
 		next unless $logmsg =~ $rx && $1;
 		my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
-		if (my $sha1 = get_headref($mparent, $git_dir)) {
+		if (my $sha1 = get_headref("$remote/$mparent")) {
 			push @commit_args, '-p', $mparent;
 			print "Merge parent branch: $mparent\n" if $opt_v;
 		}
@@ -870,29 +865,27 @@
 				print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
 				$ancestor = $opt_o;
 			}
-			if (-f "$git_dir/$remote/$branch") {
+			if (defined get_headref("$remote/$branch")) {
 				print STDERR "Branch $branch already exists!\n";
 				$state=11;
 				next;
 			}
-			unless (open(H,"$git_dir/$remote/$ancestor")) {
+			my $id = get_headref("$remote/$ancestor");
+			if (!$id) {
 				print STDERR "Branch $ancestor does not exist!\n";
 				$ignorebranch{$branch} = 1;
 				$state=11;
 				next;
 			}
-			chomp(my $id = <H>);
-			close(H);
-			unless (open(H,"> $git_dir/$remote/$branch")) {
-				print STDERR "Could not create branch $branch: $!\n";
+
+			system(qw(git update-ref -m cvsimport),
+				"$remote/$branch", $id);
+			if($? != 0) {
+				print STDERR "Could not create branch $branch\n";
 				$ignorebranch{$branch} = 1;
 				$state=11;
 				next;
 			}
-			print H "$id\n"
-				or die "Could not write branch $branch: $!";
-			close(H)
-				or die "Could not write branch $branch: $!";
 		}
 		$last_branch = $branch if $branch ne $last_branch;
 		$state = 9;
@@ -1004,7 +997,7 @@
 	$orig_branch = "master";
 	print "DONE; creating $orig_branch branch\n" if $opt_v;
 	system("git-update-ref", "refs/heads/master", "$remote/$opt_o")
-		unless -f "$git_dir/refs/heads/master";
+		unless defined get_headref('refs/heads/master');
 	system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
 		if ($opt_r && $opt_o ne 'HEAD');
 	system('git-update-ref', 'HEAD', "$orig_branch");
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index c9f515d..674a25d 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -8,6 +8,9 @@
 # a new branch. You can specify a number of filters to modify the commits,
 # files and trees.
 
+# The following functions will also be available in the commit filter:
+
+functions=$(cat << \EOF
 warn () {
         echo "$*" >&2
 }
@@ -46,6 +49,10 @@
 	echo "$*" >&2
 	exit 1
 }
+EOF
+)
+
+eval "$functions"
 
 # When piped a commit, output a script to set the ident of either
 # "author" or "committer
@@ -80,11 +87,6 @@
 	echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\""
 }
 
-# This script can be sourced by the commit filter to get the functions
-test "a$SOURCE_FUNCTIONS" = a1 && return
-this_script="$(cd "$(dirname "$0")"; pwd)"/$(basename "$0")
-export this_script
-
 USAGE="[--env-filter <command>] [--tree-filter <command>] \
 [--index-filter <command>] [--parent-filter <command>] \
 [--msg-filter <command>] [--commit-filter <command>] \
@@ -96,7 +98,7 @@
 . git-sh-setup
 
 git diff-files --quiet &&
-	git diff-index --cached --quiet HEAD ||
+	git diff-index --cached --quiet HEAD -- ||
 	die "Cannot rewrite branch(es) with a dirty working directory."
 
 tempdir=.git-rewrite
@@ -156,7 +158,7 @@
 		filter_msg="$OPTARG"
 		;;
 	--commit-filter)
-		filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG"
+		filter_commit="$functions; $OPTARG"
 		;;
 	--tag-name-filter)
 		filter_tag_name="$OPTARG"
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index bf44b6a..e9cd6fd 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -53,7 +53,7 @@
 	git rev-parse --verify HEAD > /dev/null &&
 	git update-index --refresh &&
 	git diff-files --quiet &&
-	git diff-index --cached --quiet HEAD ||
+	git diff-index --cached --quiet HEAD -- ||
 	die "Working tree is dirty"
 }
 
@@ -356,7 +356,7 @@
 		git rev-parse --verify HEAD > /dev/null &&
 		git update-index --refresh &&
 		git diff-files --quiet &&
-		! git diff-index --cached --quiet HEAD &&
+		! git diff-index --cached --quiet HEAD -- &&
 		. "$DOTEST"/author-script && {
 			test ! -f "$DOTEST"/amend || git reset --soft HEAD^
 		} &&
diff --git a/git-rebase.sh b/git-rebase.sh
index df5fd65..bdcea0e 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -61,7 +61,7 @@
 	fi
 
 	cmt=`cat "$dotest/current"`
-	if ! git diff-index --quiet HEAD
+	if ! git diff-index --quiet HEAD --
 	then
 		if ! git-commit -C "$cmt"
 		then
@@ -179,6 +179,7 @@
 		exit
 		;;
 	--skip)
+		git reset --hard HEAD || exit $?
 		if test -d "$dotest"
 		then
 			git rerere clear
@@ -284,7 +285,7 @@
 
 # The tree must be really really clean.
 git update-index --refresh || exit
-diff=$(git diff-index --cached --name-status -r HEAD)
+diff=$(git diff-index --cached --name-status -r HEAD --)
 case "$diff" in
 ?*)	echo "cannot rebase: your index is not up-to-date"
 	echo "$diff"
diff --git a/git-stash.sh b/git-stash.sh
index 534eb16..b1529e2 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -15,7 +15,7 @@
 ref_stash=refs/stash
 
 no_changes () {
-	git diff-index --quiet --cached HEAD &&
+	git diff-index --quiet --cached HEAD -- &&
 	git diff-files --quiet
 }
 
@@ -37,7 +37,7 @@
 	# state of the base commit
 	if b_commit=$(git rev-parse --verify HEAD)
 	then
-		head=$(git log --abbrev-commit --pretty=oneline -n 1 HEAD)
+		head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
 	else
 		die "You do not have the initial commit yet"
 	fi
@@ -108,7 +108,7 @@
 
 list_stash () {
 	have_stash || return 0
-	git log --pretty=oneline -g "$@" $ref_stash |
+	git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
 	sed -n -e 's/^[.0-9a-f]* refs\///p'
 }
 
diff --git a/git-svn.perl b/git-svn.perl
index 43e1591..9f884eb 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -35,6 +35,7 @@
 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
 push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
 use Carp qw/croak/;
+use Digest::MD5;
 use IO::File qw//;
 use File::Basename qw/dirname basename/;
 use File::Path qw/mkpath/;
@@ -48,8 +49,7 @@
 	foreach (qw/command command_oneline command_noisy command_output_pipe
 	            command_input_pipe command_close_pipe/) {
 		for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
-			Git::SVN::Migration Git::SVN::Log Git::SVN
-			Git::SVN::Util),
+			Git::SVN::Migration Git::SVN::Log Git::SVN),
 			__PACKAGE__) {
 			*{"${package}::$_"} = \&{"Git::$_"};
 		}
@@ -81,6 +81,7 @@
 		'quiet|q' => \$_q,
 		'repack-flags|repack-args|repack-opts=s' =>
 		   \$Git::SVN::_repack_flags,
+		'use-log-author' => \$Git::SVN::_use_log_author,
 		%remote_opts );
 
 my ($_trunk, $_tags, $_branches, $_stdlayout);
@@ -142,6 +143,9 @@
 	'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings",
 			{ 'revision|r=i' => \$_revision
 			} ],
+	'show-externals' => [ \&cmd_show_externals, "Show svn:externals listings",
+			{ 'revision|r=i' => \$_revision
+			} ],
 	'multi-fetch' => [ \&cmd_multi_fetch,
 	                   "Deprecated alias for $0 fetch --all",
 			   { 'revision|r=s' => \$_revision, %fc_opts } ],
@@ -193,23 +197,6 @@
 	}
 };
 
-my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
-
-read_repo_config(\%opts);
-Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
-my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
-                    'minimize-connections' => \$Git::SVN::Migration::_minimize,
-                    'id|i=s' => \$Git::SVN::default_ref_id,
-                    'svn-remote|remote|R=s' => sub {
-                       $Git::SVN::no_reuse_existing = 1;
-                       $Git::SVN::default_repo_id = $_[1] });
-exit 1 if (!$rv && $cmd && $cmd ne 'log');
-
-usage(0) if $_help;
-version() if $_version;
-usage(1) unless defined $cmd;
-load_authors() if $_authors;
-
 # make sure we're always running
 unless ($cmd =~ /(?:clone|init|multi-init)$/) {
 	unless (-d $ENV{GIT_DIR}) {
@@ -231,6 +218,24 @@
 		$ENV{GIT_DIR} = $git_dir;
 	}
 }
+
+my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
+
+read_repo_config(\%opts);
+Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
+my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
+                    'minimize-connections' => \$Git::SVN::Migration::_minimize,
+                    'id|i=s' => \$Git::SVN::default_ref_id,
+                    'svn-remote|remote|R=s' => sub {
+                       $Git::SVN::no_reuse_existing = 1;
+                       $Git::SVN::default_repo_id = $_[1] });
+exit 1 if (!$rv && $cmd && $cmd ne 'log');
+
+usage(0) if $_help;
+version() if $_version;
+usage(1) unless defined $cmd;
+load_authors() if $_authors;
+
 unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) {
 	Git::SVN::Migration::migration_check();
 }
@@ -545,6 +550,8 @@
 		exit 1;
 	}
 	unless ($_local) {
+		# rebase will checkout for us, so no need to do it explicitly
+		$_no_checkout = 'true';
 		$_fetch_all ? $gs->fetch_all : $gs->fetch;
 	}
 	command_noisy(rebase_cmd(), $gs->refname);
@@ -565,6 +572,21 @@
 	});
 }
 
+sub cmd_show_externals {
+	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+	$gs ||= Git::SVN->new;
+	my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
+	$gs->prop_walk($gs->{path}, $r, sub {
+		my ($gs, $path, $props) = @_;
+		print STDOUT "\n# $path\n";
+		my $s = $props->{'svn:externals'} or return;
+		$s =~ s/[\r\n]+/\n/g;
+		chomp $s;
+		$s =~ s#^#$path#gm;
+		print STDOUT "$s\n";
+	});
+}
+
 sub cmd_create_ignore {
 	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
 	$gs ||= Git::SVN->new;
@@ -840,19 +862,19 @@
 			    command_output_pipe(qw(cat-file blob), "HEAD:$path");
 			if ($file_type eq "link") {
 				my $file_name = <$fh>;
-				$checksum = Git::SVN::Util::md5sum("link $file_name");
+				$checksum = md5sum("link $file_name");
 			} else {
-				$checksum = Git::SVN::Util::md5sum($fh);
+				$checksum = md5sum($fh);
 			}
 			command_close_pipe($fh, $ctx);
 		} elsif ($file_type eq "link") {
 			my $file_name =
 			    command(qw(cat-file blob), "HEAD:$path");
 			$checksum =
-			    Git::SVN::Util::md5sum("link " . $file_name);
+			    md5sum("link " . $file_name);
 		} else {
 			open FILE, "<", $path or die $!;
-			$checksum = Git::SVN::Util::md5sum(\*FILE);
+			$checksum = md5sum(\*FILE);
 			close FILE or die $!;
 		}
 		$result .= "Checksum: " . $checksum . "\n";
@@ -1193,11 +1215,6 @@
 	return ("file", $diff_status);
 }
 
-package Git::SVN::Util;
-use strict;
-use warnings;
-use Digest::MD5;
-
 sub md5sum {
 	my $arg = shift;
 	my $ref = ref $arg;
@@ -1219,7 +1236,8 @@
 use warnings;
 use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags $_use_svm_props $_head
-            $_use_svnsync_props $no_reuse_existing $_minimize_url/;
+            $_use_svnsync_props $no_reuse_existing $_minimize_url
+	    $_use_log_author/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
@@ -2059,11 +2077,17 @@
 		croak "$log_entry->{revision} = $c already exists! ",
 		      "Why are we refetching it?\n";
 	}
-	$ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $log_entry->{name};
-	$ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} =
-	                                                  $log_entry->{email};
+	$ENV{GIT_AUTHOR_NAME} = $log_entry->{name};
+	$ENV{GIT_AUTHOR_EMAIL} = $log_entry->{email};
 	$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date};
 
+	$ENV{GIT_COMMITTER_NAME} = (defined $log_entry->{commit_name})
+						? $log_entry->{commit_name}
+						: $log_entry->{name};
+	$ENV{GIT_COMMITTER_EMAIL} = (defined $log_entry->{commit_email})
+						? $log_entry->{commit_email}
+						: $log_entry->{email};
+
 	my $tree = $log_entry->{tree};
 	if (!defined $tree) {
 		$tree = $self->tmp_index_do(sub {
@@ -2351,7 +2375,17 @@
 	$log_entry{log} .= "\n";
 	my $author = $log_entry{author} = check_author($log_entry{author});
 	my ($name, $email) = defined $::users{$author} ? @{$::users{$author}}
-	                                               : ($author, undef);
+						       : ($author, undef);
+
+	my ($commit_name, $commit_email) = ($name, $email);
+	if ($_use_log_author) {
+		if ($log_entry{log} =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) {
+			($name, $email) = ($1, $2);
+		} elsif ($log_entry{log} =~
+		                      /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
+			($name, $email) = ($1, $2);
+		}
+	}
 	if (defined $headrev && $self->use_svm_props) {
 		if ($self->rewrite_root) {
 			die "Can't have both 'useSvmProps' and 'rewriteRoot' ",
@@ -2374,23 +2408,28 @@
 		remove_username($full_url);
 		$log_entry{metadata} = "$full_url\@$r $uuid";
 		$log_entry{svm_revision} = $r;
-		$email ||= "$author\@$uuid"
+		$email ||= "$author\@$uuid";
+		$commit_email ||= "$author\@$uuid";
 	} elsif ($self->use_svnsync_props) {
 		my $full_url = $self->svnsync->{url};
 		$full_url .= "/$self->{path}" if length $self->{path};
 		remove_username($full_url);
 		my $uuid = $self->svnsync->{uuid};
 		$log_entry{metadata} = "$full_url\@$rev $uuid";
-		$email ||= "$author\@$uuid"
+		$email ||= "$author\@$uuid";
+		$commit_email ||= "$author\@$uuid";
 	} else {
 		my $url = $self->metadata_url;
 		remove_username($url);
 		$log_entry{metadata} = "$url\@$rev " .
 		                       $self->ra->get_uuid;
 		$email ||= "$author\@" . $self->ra->get_uuid;
+		$commit_email ||= "$author\@" . $self->ra->get_uuid;
 	}
 	$log_entry{name} = $name;
 	$log_entry{email} = $email;
+	$log_entry{commit_name} = $commit_name;
+	$log_entry{commit_email} = $commit_email;
 	\%log_entry;
 }
 
@@ -2947,7 +2986,7 @@
 
 		if (defined $exp) {
 			seek $base, 0, 0 or croak $!;
-			my $got = Git::SVN::Util::md5sum($base);
+			my $got = ::md5sum($base);
 			die "Checksum mismatch: $fb->{path} $fb->{blob}\n",
 			    "expected: $exp\n",
 			    "     got: $got\n" if ($got ne $exp);
@@ -2966,7 +3005,7 @@
 	if (my $fh = $fb->{fh}) {
 		if (defined $exp) {
 			seek($fh, 0, 0) or croak $!;
-			my $got = Git::SVN::Util::md5sum($fh);
+			my $got = ::md5sum($fh);
 			if ($got ne $exp) {
 				die "Checksum mismatch: $path\n",
 				    "expected: $exp\n    got: $got\n";
@@ -3321,7 +3360,7 @@
 	$fh->flush == 0 or croak $!;
 	seek $fh, 0, 0 or croak $!;
 
-	my $exp = Git::SVN::Util::md5sum($fh);
+	my $exp = ::md5sum($fh);
 	seek $fh, 0, 0 or croak $!;
 
 	my $pool = SVN::Pool->new;
diff --git a/git.c b/git.c
index 80c2f14..01bbbc7 100644
--- a/git.c
+++ b/git.c
@@ -256,7 +256,7 @@
 
 	status = p->fn(argc, argv, prefix);
 	if (status)
-		return status;
+		return status & 0xff;
 
 	/* Somebody closed stdout? */
 	if (fstat(fileno(stdout), &st))
@@ -293,6 +293,7 @@
 		{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
 		{ "cherry", cmd_cherry, RUN_SETUP },
 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
@@ -321,6 +322,7 @@
 		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
 		{ "ls-files", cmd_ls_files, RUN_SETUP },
 		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
+		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
@@ -329,6 +331,7 @@
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
+		{ "peek-remote", cmd_ls_remote },
 		{ "pickaxe", cmd_blame, RUN_SETUP },
 		{ "prune", cmd_prune, RUN_SETUP },
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
@@ -343,6 +346,7 @@
 		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 		{ "rm", cmd_rm, RUN_SETUP },
 		{ "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
+		{ "send-pack", cmd_send_pack, RUN_SETUP },
 		{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
 		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
diff --git a/help.c b/help.c
index 8217d97..d340b6a 100644
--- a/help.c
+++ b/help.c
@@ -7,7 +7,6 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "common-cmds.h"
-#include <sys/ioctl.h>
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
diff --git a/http-push.c b/http-push.c
index 9314621..78283b4 100644
--- a/http-push.c
+++ b/http-push.c
@@ -78,7 +78,7 @@
 static struct curl_slist *default_headers;
 
 static int push_verbosely;
-static int push_all;
+static int push_all = MATCH_REFS_NONE;
 static int force_all;
 static int dry_run;
 
@@ -433,7 +433,7 @@
 	packfile = fopen(request->tmpfile, "a");
 	if (!packfile) {
 		fprintf(stderr, "Unable to open local file %s for pack",
-			filename);
+			request->tmpfile);
 		remote->can_update_info_refs = 0;
 		free(url);
 		return;
@@ -941,7 +941,7 @@
 	indexfile = fopen(tmpfile, "a");
 	if (!indexfile)
 		return error("Unable to open local file %s for pack index",
-			     filename);
+			     tmpfile);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -2300,7 +2300,7 @@
 
 		if (*arg == '-') {
 			if (!strcmp(arg, "--all")) {
-				push_all = 1;
+				push_all = MATCH_REFS_ALL;
 				continue;
 			}
 			if (!strcmp(arg, "--force")) {
@@ -2393,7 +2393,7 @@
 	if (!remote_tail)
 		remote_tail = &remote_refs;
 	if (match_refs(local_refs, remote_refs, &remote_tail,
-		       nr_refspec, refspec, push_all))
+		       nr_refspec, (const char **) refspec, push_all))
 		return -1;
 	if (!remote_refs) {
 		fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
diff --git a/http-walker.c b/http-walker.c
index 444aebf..a3fb596 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -405,7 +405,7 @@
 	indexfile = fopen(tmpfile, "a");
 	if (!indexfile)
 		return error("Unable to open local file %s for pack index",
-			     filename);
+			     tmpfile);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -770,7 +770,7 @@
 	packfile = fopen(tmpfile, "a");
 	if (!packfile)
 		return error("Unable to open local file %s for pack",
-			     filename);
+			     tmpfile);
 
 	slot = get_active_slot();
 	slot->results = &results;
diff --git a/lockfile.c b/lockfile.c
index 9a1f64d..f45d3ed 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -12,8 +12,10 @@
 
 	while (lock_file_list) {
 		if (lock_file_list->owner == me &&
-		    lock_file_list->filename[0])
+		    lock_file_list->filename[0]) {
+			close(lock_file_list->fd);
 			unlink(lock_file_list->filename);
+		}
 		lock_file_list = lock_file_list->next;
 	}
 }
@@ -92,7 +94,7 @@
 			return p;
 		}
 
-		if (link[0] == '/') {
+		if (is_absolute_path(link)) {
 			/* absolute path simply replaces p */
 			if (link_len < s)
 				strcpy(p, link);
@@ -120,8 +122,6 @@
 
 static int lock_file(struct lock_file *lk, const char *path)
 {
-	int fd;
-
 	if (strlen(path) >= sizeof(lk->filename)) return -1;
 	strcpy(lk->filename, path);
 	/*
@@ -130,8 +130,8 @@
 	 */
 	resolve_symlink(lk->filename, sizeof(lk->filename)-5);
 	strcat(lk->filename, ".lock");
-	fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
-	if (0 <= fd) {
+	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
+	if (0 <= lk->fd) {
 		if (!lock_file_list) {
 			signal(SIGINT, remove_lock_file_on_signal);
 			atexit(remove_lock_file);
@@ -148,7 +148,7 @@
 	}
 	else
 		lk->filename[0] = 0;
-	return fd;
+	return lk->fd;
 }
 
 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error)
@@ -163,6 +163,7 @@
 {
 	char result_file[PATH_MAX];
 	int i;
+	close(lk->fd);
 	strcpy(result_file, lk->filename);
 	i = strlen(result_file) - 5; /* .lock */
 	result_file[i] = 0;
@@ -194,7 +195,9 @@
 
 void rollback_lock_file(struct lock_file *lk)
 {
-	if (lk->filename[0])
+	if (lk->filename[0]) {
+		close(lk->fd);
 		unlink(lk->filename);
+	}
 	lk->filename[0] = 0;
 }
diff --git a/pager.c b/pager.c
index 8bac9d9..fb7a1a6 100644
--- a/pager.c
+++ b/pager.c
@@ -1,7 +1,5 @@
 #include "cache.h"
 
-#include <sys/select.h>
-
 /*
  * This is split up from the rest of git so that we might do
  * something different on Windows, for example.
diff --git a/peek-remote.c b/peek-remote.c
deleted file mode 100644
index 8d20f7c..0000000
--- a/peek-remote.c
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "pkt-line.h"
-
-static const char peek_remote_usage[] =
-"git-peek-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
-static const char *uploadpack = "git-upload-pack";
-
-static int peek_remote(int fd[2], unsigned flags)
-{
-	struct ref *ref;
-
-	get_remote_heads(fd[0], &ref, 0, NULL, flags);
-	packet_flush(fd[1]);
-
-	while (ref) {
-		printf("%s	%s\n", sha1_to_hex(ref->old_sha1), ref->name);
-		ref = ref->next;
-	}
-	return 0;
-}
-
-int main(int argc, char **argv)
-{
-	int i, ret;
-	char *dest = NULL;
-	int fd[2];
-	struct child_process *conn;
-	int nongit = 0;
-	unsigned flags = 0;
-
-	setup_git_directory_gently(&nongit);
-
-	for (i = 1; i < argc; i++) {
-		char *arg = argv[i];
-
-		if (*arg == '-') {
-			if (!prefixcmp(arg, "--upload-pack=")) {
-				uploadpack = arg + 14;
-				continue;
-			}
-			if (!prefixcmp(arg, "--exec=")) {
-				uploadpack = arg + 7;
-				continue;
-			}
-			if (!strcmp("--tags", arg)) {
-				flags |= REF_TAGS;
-				continue;
-			}
-			if (!strcmp("--heads", arg)) {
-				flags |= REF_HEADS;
-				continue;
-			}
-			if (!strcmp("--refs", arg)) {
-				flags |= REF_NORMAL;
-				continue;
-			}
-			usage(peek_remote_usage);
-		}
-		dest = arg;
-		break;
-	}
-
-	if (!dest || i != argc - 1)
-		usage(peek_remote_usage);
-
-	conn = git_connect(fd, dest, uploadpack, 0);
-	ret = peek_remote(fd, flags);
-	close(fd[0]);
-	close(fd[1]);
-	ret |= finish_connect(conn);
-	return !!ret;
-}
diff --git a/perl/Git.pm b/perl/Git.pm
index dca92c8..7468460 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -549,6 +549,37 @@
 	};
 }
 
+=item config_int ( VARIABLE )
+
+Retrieve the integer configuration C<VARIABLE>. The return value
+is simple decimal number.  An optional value suffix of 'k', 'm',
+or 'g' in the config file will cause the value to be multiplied
+by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
+It would return C<undef> if configuration variable is not defined,
+
+Must be called on a repository instance.
+
+This currently wraps command('config') so it is not so fast.
+
+=cut
+
+sub config_int {
+	my ($self, $var) = @_;
+	$self->repo_path()
+		or throw Error::Simple("not a repository");
+
+	try {
+		return $self->command_oneline('config', '--int', '--get', $var);
+	} catch Git::Error::Command with {
+		my $E = shift;
+		if ($E->value() == 1) {
+			# Key not found.
+			return undef;
+		} else {
+			throw $E;
+		}
+	};
+}
 
 =item ident ( TYPE | IDENTSTR )
 
diff --git a/receive-pack.c b/receive-pack.c
index 38e35c0..fba4cf8 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -200,12 +200,14 @@
 	}
 
 	if (is_null_sha1(new_sha1)) {
+		if (!parse_object(old_sha1)) {
+			warning ("Allowing deletion of corrupt ref.");
+			old_sha1 = NULL;
+		}
 		if (delete_ref(name, old_sha1)) {
 			error("failed to delete %s", name);
 			return "failed to delete";
 		}
-		fprintf(stderr, "%s: %s -> deleted\n", name,
-			sha1_to_hex(old_sha1));
 		return NULL; /* good */
 	}
 	else {
@@ -217,8 +219,6 @@
 		if (write_ref_sha1(lock, new_sha1, "push")) {
 			return "failed to write"; /* error() already called */
 		}
-		fprintf(stderr, "%s: %s -> %s\n", name,
-			sha1_to_hex(old_sha1), sha1_to_hex(new_sha1));
 		return NULL; /* good */
 	}
 }
diff --git a/refs.c b/refs.c
index ae53254..54ec98d 100644
--- a/refs.c
+++ b/refs.c
@@ -1433,3 +1433,11 @@
 	}
 	return 0;
 }
+
+struct ref *find_ref_by_name(struct ref *list, const char *name)
+{
+	for ( ; list; list = list->next)
+		if (!strcmp(list->name, name))
+			return list;
+	return NULL;
+}
diff --git a/remote.c b/remote.c
index bec2ba1..bb01059 100644
--- a/remote.c
+++ b/remote.c
@@ -485,7 +485,7 @@
 	return ret;
 }
 
-static struct ref *copy_ref(struct ref *ref)
+static struct ref *copy_ref(const struct ref *ref)
 {
 	struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
 	memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
@@ -493,6 +493,18 @@
 	return ret;
 }
 
+struct ref *copy_ref_list(const struct ref *ref)
+{
+	struct ref *ret = NULL;
+	struct ref **tail = &ret;
+	while (ref) {
+		*tail = copy_ref(ref);
+		ref = ref->next;
+		tail = &((*tail)->next);
+	}
+	return ret;
+}
+
 void free_refs(struct ref *ref)
 {
 	struct ref *next;
@@ -684,14 +696,6 @@
 	return -errs;
 }
 
-static struct ref *find_ref_by_name(struct ref *list, const char *name)
-{
-	for ( ; list; list = list->next)
-		if (!strcmp(list->name, name))
-			return list;
-	return NULL;
-}
-
 static const struct refspec *check_pattern_match(const struct refspec *rs,
 						 int rs_nr,
 						 const struct ref *src)
@@ -710,10 +714,12 @@
  * without thinking.
  */
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
-	       int nr_refspec, char **refspec, int all)
+	       int nr_refspec, const char **refspec, int flags)
 {
 	struct refspec *rs =
 		parse_ref_spec(nr_refspec, (const char **) refspec);
+	int send_all = flags & MATCH_REFS_ALL;
+	int send_mirror = flags & MATCH_REFS_MIRROR;
 
 	if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
 		return -1;
@@ -730,7 +736,7 @@
 			if (!pat)
 				continue;
 		}
-		else if (prefixcmp(src->name, "refs/heads/"))
+		else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
 			/*
 			 * "matching refs"; traditionally we pushed everything
 			 * including refs outside refs/heads/ hierarchy, but
@@ -751,10 +757,13 @@
 		if (dst_peer && dst_peer->peer_ref)
 			/* We're already sending something to this ref. */
 			goto free_name;
-		if (!dst_peer && !nr_refspec && !all)
-			/* Remote doesn't have it, and we have no
+
+		if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
+			/*
+			 * Remote doesn't have it, and we have no
 			 * explicit pattern, and we don't have
-			 * --all. */
+			 * --all nor --mirror.
+			 */
 			goto free_name;
 		if (!dst_peer) {
 			/* Create a new one and link it */
@@ -810,10 +819,10 @@
 	return ref_matches_abbrev(branch->merge[i]->src, refname);
 }
 
-static struct ref *get_expanded_map(struct ref *remote_refs,
+static struct ref *get_expanded_map(const struct ref *remote_refs,
 				    const struct refspec *refspec)
 {
-	struct ref *ref;
+	const struct ref *ref;
 	struct ref *ret = NULL;
 	struct ref **tail = &ret;
 
@@ -824,7 +833,7 @@
 		if (strchr(ref->name, '^'))
 			continue; /* a dereference item */
 		if (!prefixcmp(ref->name, refspec->src)) {
-			char *match;
+			const char *match;
 			struct ref *cpy = copy_ref(ref);
 			match = ref->name + remote_prefix_len;
 
@@ -842,9 +851,9 @@
 	return ret;
 }
 
-static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name)
+static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
 {
-	struct ref *ref;
+	const struct ref *ref;
 	for (ref = refs; ref; ref = ref->next) {
 		if (ref_matches_abbrev(name, ref->name))
 			return ref;
@@ -852,9 +861,9 @@
 	return NULL;
 }
 
-struct ref *get_remote_ref(struct ref *remote_refs, const char *name)
+struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 {
-	struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
+	const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
 
 	if (!ref)
 		return NULL;
@@ -887,7 +896,7 @@
 	return ret;
 }
 
-int get_fetch_map(struct ref *remote_refs,
+int get_fetch_map(const struct ref *remote_refs,
 		  const struct refspec *refspec,
 		  struct ref ***tail,
 		  int missing_ok)
diff --git a/remote.h b/remote.h
index 878b4ec..b10036c 100644
--- a/remote.h
+++ b/remote.h
@@ -44,6 +44,10 @@
 
 struct ref *alloc_ref(unsigned namelen);
 
+struct ref *copy_ref_list(const struct ref *ref);
+
+int check_ref_type(const struct ref *ref, int flags);
+
 /*
  * Frees the entire list and peers of elements.
  */
@@ -57,7 +61,7 @@
 struct refspec *parse_ref_spec(int nr_refspec, const char **refspec);
 
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
-	       int nr_refspec, char **refspec, int all);
+	       int nr_refspec, const char **refspec, int all);
 
 /*
  * Given a list of the remote refs and the specification of things to
@@ -71,10 +75,10 @@
  * missing_ok is usually false, but when we are adding branch.$name.merge
  * it is Ok if the branch is not at the remote anymore.
  */
-int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec,
+int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec,
 		  struct ref ***tail, int missing_ok);
 
-struct ref *get_remote_ref(struct ref *remote_refs, const char *name);
+struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);
 
 /*
  * For the given remote, reads the refspec's src and sets the other fields.
@@ -98,4 +102,11 @@
 int branch_has_merge_config(struct branch *branch);
 int branch_merge_matches(struct branch *, int n, const char *);
 
+/* Flags to match_refs. */
+enum match_refs_flags {
+	MATCH_REFS_NONE		= 0,
+	MATCH_REFS_ALL 		= (1 << 0),
+	MATCH_REFS_MIRROR	= (1 << 1),
+};
+
 #endif
diff --git a/send-pack.c b/send-pack.c
deleted file mode 100644
index b74fd45..0000000
--- a/send-pack.c
+++ /dev/null
@@ -1,461 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "tag.h"
-#include "refs.h"
-#include "pkt-line.h"
-#include "run-command.h"
-#include "remote.h"
-
-static const char send_pack_usage[] =
-"git-send-pack [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
-"  --all and explicit <ref> specification are mutually exclusive.";
-static const char *receivepack = "git-receive-pack";
-static int verbose;
-static int send_all;
-static int force_update;
-static int use_thin_pack;
-static int dry_run;
-
-/*
- * Make a pack stream and spit it out into file descriptor fd
- */
-static int pack_objects(int fd, struct ref *refs)
-{
-	/*
-	 * The child becomes pack-objects --revs; we feed
-	 * the revision parameters to it via its stdin and
-	 * let its stdout go back to the other end.
-	 */
-	const char *args[] = {
-		"pack-objects",
-		"--all-progress",
-		"--revs",
-		"--stdout",
-		NULL,
-		NULL,
-	};
-	struct child_process po;
-
-	if (use_thin_pack)
-		args[4] = "--thin";
-	memset(&po, 0, sizeof(po));
-	po.argv = args;
-	po.in = -1;
-	po.out = fd;
-	po.git_cmd = 1;
-	if (start_command(&po))
-		die("git-pack-objects failed (%s)", strerror(errno));
-
-	/*
-	 * We feed the pack-objects we just spawned with revision
-	 * parameters by writing to the pipe.
-	 */
-	while (refs) {
-		char buf[42];
-
-		if (!is_null_sha1(refs->old_sha1) &&
-		    has_sha1_file(refs->old_sha1)) {
-			memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
-			buf[0] = '^';
-			buf[41] = '\n';
-			if (!write_or_whine(po.in, buf, 42,
-						"send-pack: send refs"))
-				break;
-		}
-		if (!is_null_sha1(refs->new_sha1)) {
-			memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
-			buf[40] = '\n';
-			if (!write_or_whine(po.in, buf, 41,
-						"send-pack: send refs"))
-				break;
-		}
-		refs = refs->next;
-	}
-
-	if (finish_command(&po))
-		return error("pack-objects died with strange error");
-	return 0;
-}
-
-static void unmark_and_free(struct commit_list *list, unsigned int mark)
-{
-	while (list) {
-		struct commit_list *temp = list;
-		temp->item->object.flags &= ~mark;
-		list = temp->next;
-		free(temp);
-	}
-}
-
-static int ref_newer(const unsigned char *new_sha1,
-		     const unsigned char *old_sha1)
-{
-	struct object *o;
-	struct commit *old, *new;
-	struct commit_list *list, *used;
-	int found = 0;
-
-	/* Both new and old must be commit-ish and new is descendant of
-	 * old.  Otherwise we require --force.
-	 */
-	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != OBJ_COMMIT)
-		return 0;
-	old = (struct commit *) o;
-
-	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != OBJ_COMMIT)
-		return 0;
-	new = (struct commit *) o;
-
-	if (parse_commit(new) < 0)
-		return 0;
-
-	used = list = NULL;
-	commit_list_insert(new, &list);
-	while (list) {
-		new = pop_most_recent_commit(&list, 1);
-		commit_list_insert(new, &used);
-		if (new == old) {
-			found = 1;
-			break;
-		}
-	}
-	unmark_and_free(list, 1);
-	unmark_and_free(used, 1);
-	return found;
-}
-
-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, int flag, void *cb_data)
-{
-	struct ref *ref;
-	int len = strlen(refname) + 1;
-	ref = xcalloc(1, sizeof(*ref) + len);
-	hashcpy(ref->new_sha1, sha1);
-	memcpy(ref->name, refname, len);
-	*local_tail = ref;
-	local_tail = &ref->next;
-	return 0;
-}
-
-static void get_local_heads(void)
-{
-	local_tail = &local_refs;
-	for_each_ref(one_local_ref, NULL);
-}
-
-static int receive_status(int in)
-{
-	char line[1000];
-	int ret = 0;
-	int len = packet_read_line(in, line, sizeof(line));
-	if (len < 10 || memcmp(line, "unpack ", 7)) {
-		fprintf(stderr, "did not receive status back\n");
-		return -1;
-	}
-	if (memcmp(line, "unpack ok\n", 10)) {
-		fputs(line, stderr);
-		ret = -1;
-	}
-	while (1) {
-		len = packet_read_line(in, line, sizeof(line));
-		if (!len)
-			break;
-		if (len < 3 ||
-		    (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) {
-			fprintf(stderr, "protocol error: %s\n", line);
-			ret = -1;
-			break;
-		}
-		if (!memcmp(line, "ok", 2))
-			continue;
-		fputs(line, stderr);
-		ret = -1;
-	}
-	return ret;
-}
-
-static void update_tracking_ref(struct remote *remote, struct ref *ref)
-{
-	struct refspec rs;
-	int will_delete_ref;
-
-	rs.src = ref->name;
-	rs.dst = NULL;
-
-	if (!ref->peer_ref)
-		return;
-
-	will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-
-	if (!will_delete_ref &&
-			!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
-		return;
-
-	if (!remote_find_tracking(remote, &rs)) {
-		fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
-		if (is_null_sha1(ref->peer_ref->new_sha1)) {
-			if (delete_ref(rs.dst, NULL))
-				error("Failed to delete");
-		} else
-			update_ref("update by push", rs.dst,
-					ref->new_sha1, NULL, 0, 0);
-		free(rs.dst);
-	}
-}
-
-static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
-{
-	struct ref *ref;
-	int new_refs;
-	int ret = 0;
-	int ask_for_status_report = 0;
-	int allow_deleting_refs = 0;
-	int expect_status_report = 0;
-
-	/* No funny business with the matcher */
-	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
-	get_local_heads();
-
-	/* Does the other end support the reporting? */
-	if (server_supports("report-status"))
-		ask_for_status_report = 1;
-	if (server_supports("delete-refs"))
-		allow_deleting_refs = 1;
-
-	/* match them up */
-	if (!remote_tail)
-		remote_tail = &remote_refs;
-	if (match_refs(local_refs, remote_refs, &remote_tail,
-		       nr_refspec, refspec, send_all))
-		return -1;
-
-	if (!remote_refs) {
-		fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
-			"Perhaps you should specify a branch such as 'master'.\n");
-		return 0;
-	}
-
-	/*
-	 * Finally, tell the other end!
-	 */
-	new_refs = 0;
-	for (ref = remote_refs; ref; ref = ref->next) {
-		char old_hex[60], *new_hex;
-		int will_delete_ref;
-
-		if (!ref->peer_ref)
-			continue;
-
-
-		will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-		if (will_delete_ref && !allow_deleting_refs) {
-			error("remote does not support deleting refs");
-			ret = -2;
-			continue;
-		}
-		if (!will_delete_ref &&
-		    !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
-			if (verbose)
-				fprintf(stderr, "'%s': up-to-date\n", ref->name);
-			continue;
-		}
-
-		/* This part determines what can overwrite what.
-		 * The rules are:
-		 *
-		 * (0) you can always use --force or +A:B notation to
-		 *     selectively force individual ref pairs.
-		 *
-		 * (1) if the old thing does not exist, it is OK.
-		 *
-		 * (2) if you do not have the old thing, you are not allowed
-		 *     to overwrite it; you would not know what you are losing
-		 *     otherwise.
-		 *
-		 * (3) if both new and old are commit-ish, and new is a
-		 *     descendant of old, it is OK.
-		 *
-		 * (4) regardless of all of the above, removing :B is
-		 *     always allowed.
-		 */
-
-		if (!force_update &&
-		    !will_delete_ref &&
-		    !is_null_sha1(ref->old_sha1) &&
-		    !ref->force) {
-			if (!has_sha1_file(ref->old_sha1) ||
-			    !ref_newer(ref->peer_ref->new_sha1,
-				       ref->old_sha1)) {
-				/* We do not have the remote ref, or
-				 * we know that the remote ref is not
-				 * an ancestor of what we are trying to
-				 * push.  Either way this can be losing
-				 * commits at the remote end and likely
-				 * we were not up to date to begin with.
-				 */
-				error("remote '%s' is not an ancestor of\n"
-				      " local  '%s'.\n"
-				      " Maybe you are not up-to-date and "
-				      "need to pull first?",
-				      ref->name,
-				      ref->peer_ref->name);
-				ret = -2;
-				continue;
-			}
-		}
-		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		if (!will_delete_ref)
-			new_refs++;
-		strcpy(old_hex, sha1_to_hex(ref->old_sha1));
-		new_hex = sha1_to_hex(ref->new_sha1);
-
-		if (!dry_run) {
-			if (ask_for_status_report) {
-				packet_write(out, "%s %s %s%c%s",
-					old_hex, new_hex, ref->name, 0,
-					"report-status");
-				ask_for_status_report = 0;
-				expect_status_report = 1;
-			}
-			else
-				packet_write(out, "%s %s %s",
-					old_hex, new_hex, ref->name);
-		}
-		if (will_delete_ref)
-			fprintf(stderr, "deleting '%s'\n", ref->name);
-		else {
-			fprintf(stderr, "updating '%s'", ref->name);
-			if (strcmp(ref->name, ref->peer_ref->name))
-				fprintf(stderr, " using '%s'",
-					ref->peer_ref->name);
-			fprintf(stderr, "\n  from %s\n  to   %s\n",
-				old_hex, new_hex);
-		}
-	}
-
-	packet_flush(out);
-	if (new_refs && !dry_run)
-		ret = pack_objects(out, remote_refs);
-	close(out);
-
-	if (expect_status_report) {
-		if (receive_status(in))
-			ret = -4;
-	}
-
-	if (!dry_run && remote && ret == 0) {
-		for (ref = remote_refs; ref; ref = ref->next)
-			update_tracking_ref(remote, ref);
-	}
-
-	if (!new_refs && ret == 0)
-		fprintf(stderr, "Everything up-to-date\n");
-	return ret;
-}
-
-static void verify_remote_names(int nr_heads, char **heads)
-{
-	int i;
-
-	for (i = 0; i < nr_heads; i++) {
-		const char *remote = strchr(heads[i], ':');
-
-		remote = remote ? (remote + 1) : heads[i];
-		switch (check_ref_format(remote)) {
-		case 0: /* ok */
-		case -2: /* ok but a single level -- that is fine for
-			  * a match pattern.
-			  */
-		case -3: /* ok but ends with a pattern-match character */
-			continue;
-		}
-		die("remote part of refspec is not a valid name in %s",
-		    heads[i]);
-	}
-}
-
-int main(int argc, char **argv)
-{
-	int i, nr_heads = 0;
-	char *dest = NULL;
-	char **heads = NULL;
-	int fd[2], ret;
-	struct child_process *conn;
-	char *remote_name = NULL;
-	struct remote *remote = NULL;
-
-	setup_git_directory();
-	git_config(git_default_config);
-
-	argv++;
-	for (i = 1; i < argc; i++, argv++) {
-		char *arg = *argv;
-
-		if (*arg == '-') {
-			if (!prefixcmp(arg, "--receive-pack=")) {
-				receivepack = arg + 15;
-				continue;
-			}
-			if (!prefixcmp(arg, "--exec=")) {
-				receivepack = arg + 7;
-				continue;
-			}
-			if (!prefixcmp(arg, "--remote=")) {
-				remote_name = arg + 9;
-				continue;
-			}
-			if (!strcmp(arg, "--all")) {
-				send_all = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--dry-run")) {
-				dry_run = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--force")) {
-				force_update = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--verbose")) {
-				verbose = 1;
-				continue;
-			}
-			if (!strcmp(arg, "--thin")) {
-				use_thin_pack = 1;
-				continue;
-			}
-			usage(send_pack_usage);
-		}
-		if (!dest) {
-			dest = arg;
-			continue;
-		}
-		heads = argv;
-		nr_heads = argc - i;
-		break;
-	}
-	if (!dest)
-		usage(send_pack_usage);
-	if (heads && send_all)
-		usage(send_pack_usage);
-	verify_remote_names(nr_heads, heads);
-
-	if (remote_name) {
-		remote = remote_get(remote_name);
-		if (!remote_has_url(remote, dest)) {
-			die("Destination %s is not a uri for %s",
-			    dest, remote_name);
-		}
-	}
-
-	conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
-	ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
-	close(fd[0]);
-	close(fd[1]);
-	ret |= finish_connect(conn);
-	return !!ret;
-}
diff --git a/send-pack.h b/send-pack.h
new file mode 100644
index 0000000..8ff1dc3
--- /dev/null
+++ b/send-pack.h
@@ -0,0 +1,18 @@
+#ifndef SEND_PACK_H
+#define SEND_PACK_H
+
+struct send_pack_args {
+	const char *receivepack;
+	unsigned verbose:1,
+		send_all:1,
+		send_mirror:1,
+		force_update:1,
+		use_thin_pack:1,
+		dry_run:1;
+};
+
+int send_pack(struct send_pack_args *args,
+	      const char *dest, struct remote *remote,
+	      int nr_heads, const char **heads);
+
+#endif
diff --git a/server-info.c b/server-info.c
index 0d1312c..a051e49 100644
--- a/server-info.c
+++ b/server-info.c
@@ -35,7 +35,7 @@
 	safe_create_leading_directories(path0);
 	info_ref_fp = fopen(path1, "w");
 	if (!info_ref_fp)
-		return error("unable to update %s", path0);
+		return error("unable to update %s", path1);
 	for_each_ref(add_info_ref, NULL);
 	fclose(info_ref_fp);
 	adjust_shared_perm(path1);
diff --git a/setup.c b/setup.c
index 43cd3f9..2c7b5cb 100644
--- a/setup.c
+++ b/setup.c
@@ -59,7 +59,7 @@
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
 	static char path[PATH_MAX];
-	if (!pfx || !*pfx || arg[0] == '/')
+	if (!pfx || !*pfx || is_absolute_path(arg))
 		return arg;
 	memcpy(path, pfx, pfx_len);
 	strcpy(path + pfx_len, arg);
diff --git a/sha1_file.c b/sha1_file.c
index f007874..b0c2435 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -25,8 +25,10 @@
 
 #ifdef NO_C99_FORMAT
 #define SZ_FMT "lu"
+static unsigned long sz_fmt(size_t s) { return (unsigned long)s; }
 #else
 #define SZ_FMT "zu"
+static size_t sz_fmt(size_t s) { return s; }
 #endif
 
 const unsigned char null_sha1[20];
@@ -86,7 +88,7 @@
 	char *pos = path;
 	struct stat st;
 
-	if (*pos == '/')
+	if (is_absolute_path(path))
 		pos++;
 
 	while (pos) {
@@ -253,7 +255,7 @@
 	int entlen = pfxlen + 43;
 	int base_len = -1;
 
-	if (*entry != '/' && relative_base) {
+	if (!is_absolute_path(entry) && relative_base) {
 		/* Relative alt-odb */
 		if (base_len < 0)
 			base_len = strlen(relative_base) + 1;
@@ -262,7 +264,7 @@
 	}
 	ent = xmalloc(sizeof(*ent) + entlen);
 
-	if (*entry != '/' && relative_base) {
+	if (!is_absolute_path(entry) && relative_base) {
 		memcpy(ent->base, relative_base, base_len - 1);
 		ent->base[base_len - 1] = '/';
 		memcpy(ent->base + base_len, entry, len);
@@ -333,7 +335,7 @@
 		while (cp < ep && *cp != sep)
 			cp++;
 		if (last != cp) {
-			if ((*last != '/') && depth) {
+			if (!is_absolute_path(last) && depth) {
 				error("%s: ignoring relative alternate object store %s",
 						relative_base, last);
 			} else {
@@ -423,9 +425,9 @@
 		"pack_report: getpagesize()            = %10" SZ_FMT "\n"
 		"pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
 		"pack_report: core.packedGitLimit      = %10" SZ_FMT "\n",
-		(size_t) getpagesize(),
-		packed_git_window_size,
-		packed_git_limit);
+		sz_fmt(getpagesize()),
+		sz_fmt(packed_git_window_size),
+		sz_fmt(packed_git_limit));
 	fprintf(stderr,
 		"pack_report: pack_used_ctr            = %10u\n"
 		"pack_report: pack_mmap_calls          = %10u\n"
@@ -435,7 +437,7 @@
 		pack_used_ctr,
 		pack_mmap_calls,
 		pack_open_windows, peak_pack_open_windows,
-		pack_mapped, peak_pack_mapped);
+		sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
 }
 
 static int check_packed_git_idx(const char *path,  struct packed_git *p)
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 2ee5a00..657f681 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -36,7 +36,6 @@
 '
 
 test_expect_success 'rebase --skip with am -3' '
-	git reset --hard HEAD &&
 	git rebase --skip
 	'
 
@@ -57,7 +56,6 @@
 test_expect_failure 'rebase with --merge' 'git rebase --merge master'
 
 test_expect_success 'rebase --skip with --merge' '
-	git reset --hard HEAD &&
 	git rebase --skip
 	'
 
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
index 245fb3b..73da45f 100755
--- a/t/t3902-quoted.sh
+++ b/t/t3902-quoted.sh
@@ -20,6 +20,13 @@
 '
 DQ='"'
 
+echo foo > "Name and an${HT}HT"
+test -f "Name and an${HT}HT" || {
+	# since FAT/NTFS does not allow tabs in filenames, skip this test
+	say 'Your filesystem does not allow tabs in filenames, test skipped.'
+	test_done
+}
+
 for_each_name () {
 	for name in \
 	    Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index 7d92ae3..c44b27a 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -16,7 +16,7 @@
 chmod +x path1
 
 test_expect_success \
-    'update-cache --add two files with and without +x.' \
+    'update-index --add two files with and without +x.' \
     'git update-index --add path0 path1'
 
 mv path0 path0-
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 063e792..2fe50bc 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -27,7 +27,7 @@
 '
 
 test_expect_success \
-    'update-cache --add a file.' \
+    'update-index --add a file.' \
     'git update-index --add path0'
 
 test_expect_success \
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 5836e3a..1287d2a 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -119,7 +119,7 @@
     'compare_diff_raw expected current'
 
 test_expect_success \
-    'run diff with -B' \
+    'run diff with -B -M' \
     'git diff-index -B -M "$tree" >current'
 
 # This should not mistake file0 as the copy source of new file1
diff --git a/t/t4100/t-apply-1.patch b/t/t4100/t-apply-1.patch
index de58751..90ab54f 100644
--- a/t/t4100/t-apply-1.patch
+++ b/t/t4100/t-apply-1.patch
@@ -90,7 +90,7 @@
 diff --git a/Makefile b/Makefile
 --- a/Makefile
 +++ b/Makefile
-@@ -30,7 +30,7 @@ PROG=   git-update-cache git-diff-files 
+@@ -30,7 +30,7 @@ PROG=   git-update-index git-diff-files
  	git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
  	git-check-files git-ls-tree git-merge-base git-merge-cache \
  	git-unpack-file git-export git-diff-cache git-convert-cache \
diff --git a/t/t4100/t-apply-2.patch b/t/t4100/t-apply-2.patch
index cfdc808..f5c7d60 100644
--- a/t/t4100/t-apply-2.patch
+++ b/t/t4100/t-apply-2.patch
@@ -9,7 +9,7 @@
 -	git-deltafy-script
 +	git-deltafy-script git-fetch-script
  
- PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
+ PROG=   git-update-index git-diff-files git-init-db git-write-tree \
  	git-read-tree git-commit-tree git-cat-file git-fsck-cache \
 diff --git a/git-pull-script b/git-fetch-script
 similarity index 87%
diff --git a/t/t4100/t-apply-5.patch b/t/t4100/t-apply-5.patch
index de11623..5f6ddc1 100644
--- a/t/t4100/t-apply-5.patch
+++ b/t/t4100/t-apply-5.patch
@@ -200,7 +200,7 @@
 diff a/Makefile b/Makefile
 --- a/Makefile
 +++ b/Makefile
-@@ -30,7 +30,7 @@ PROG=   git-update-cache git-diff-files 
+@@ -30,7 +30,7 @@ PROG=   git-update-index git-diff-files
  	git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
  	git-check-files git-ls-tree git-merge-base git-merge-cache \
  	git-unpack-file git-export git-diff-cache git-convert-cache \
diff --git a/t/t4100/t-apply-6.patch b/t/t4100/t-apply-6.patch
index d975363..a72729a 100644
--- a/t/t4100/t-apply-6.patch
+++ b/t/t4100/t-apply-6.patch
@@ -8,7 +8,7 @@
 -	git-deltafy-script
 +	git-deltafy-script git-fetch-script
  
- PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
+ PROG=   git-update-index git-diff-files git-init-db git-write-tree \
  	git-read-tree git-commit-tree git-cat-file git-fsck-cache \
 diff a/git-fetch-script b/git-fetch-script
 --- /dev/null
diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh
index 65571e0..b540f72 100755
--- a/t/t4119-apply-config.sh
+++ b/t/t4119-apply-config.sh
@@ -24,7 +24,7 @@
 +++ file1+	2007-02-21 01:07:44.000000000 -0800
 @@ -1 +1 @@
 -A
-+B
++B 
 EOF
 
 sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index ba7579c..f1106e6 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -187,49 +187,51 @@
 			test-3-${packname_3}.idx'
 
 test_expect_success \
-    'corrupt a pack and see if verify catches' \
+    'verify-pack catches mismatched .idx and .pack files' \
     'cat test-1-${packname_1}.idx >test-3.idx &&
      cat test-2-${packname_2}.pack >test-3.pack &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : PACK_SIGNATURE &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
+test_expect_success \
+    'verify-pack catches a corrupted pack signature' \
+    'cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : PACK_VERSION &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
+test_expect_success \
+    'verify-pack catches a corrupted pack version' \
+    'cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : TYPE/SIZE byte of the first packed object data &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
+test_expect_success \
+    'verify-pack catches a corrupted type/size of the 1st packed object data' \
+    'cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
      if git verify-pack test-3.idx
      then false
      else :;
-     fi &&
+     fi'
 
-     : sum of the index file itself &&
-     l=`wc -c <test-3.idx` &&
+test_expect_success \
+    'verify-pack catches a corrupted sum of the index file itself' \
+    'l=`wc -c <test-3.idx` &&
      l=`expr $l - 20` &&
      cat test-1-${packname_1}.pack >test-3.pack &&
      dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
      if git verify-pack test-3.pack
      then false
      else :;
-     fi &&
-
-     :'
+     fi'
 
 test_expect_success \
     'build pack index for an existing pack' \
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 4f58c4c..2a2878b 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -61,17 +61,33 @@
 
 test_expect_success \
     'index v2: force some 64-bit offsets with pack-objects' \
-    'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list) &&
-     git verify-pack -v "test-3-${pack3}.pack"'
+    'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
 
+have_64bits=
+if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
+	! echo "$msg" | grep "pack too large .* off_t"
+then
+	have_64bits=t
+else
+	say "skipping tests concerning 64-bit offsets"
+fi
+
+test "$have_64bits" &&
+test_expect_success \
+    'index v2: verify a pack with some 64-bit offsets' \
+    'git verify-pack -v "test-3-${pack3}.pack"'
+
+test "$have_64bits" &&
 test_expect_failure \
     '64-bit offsets: should be different from previous index v2 results' \
     'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
 
+test "$have_64bits" &&
 test_expect_success \
     'index v2: force some 64-bit offsets with index-pack' \
     'git-index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
 
+test "$have_64bits" &&
 test_expect_success \
     '64-bit offsets: index-pack result should match pack-objects one' \
     'cmp "test-3-${pack3}.idx" "3.idx"'
@@ -116,11 +132,11 @@
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
     'rm -f .git/objects/pack/* &&
-     git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" &&
+     git-index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
      git prune-packed &&
      git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
      cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
-     cmp "test-3-${pack1}.idx"  ".git/objects/pack/pack-${pack1}.idx"'
+     cmp "test-2-${pack1}.idx"  ".git/objects/pack/pack-${pack1}.idx"'
 
 test_expect_success \
     '[index v2] 2) create a stealth corruption in a delta base reference' \
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
new file mode 100755
index 0000000..1493a92
--- /dev/null
+++ b/t/t5404-tracking-branches.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='tracking branch update checks for git push'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo 1 >file &&
+	git add file &&
+	git commit -m 1 &&
+	git branch b1 &&
+	git branch b2 &&
+	git clone . aa &&
+	git checkout b1 &&
+	echo b1 >>file &&
+	git commit -a -m b1 &&
+	git checkout b2 &&
+	echo b2 >>file &&
+	git commit -a -m b2
+'
+
+test_expect_success 'prepare pushable branches' '
+	cd aa &&
+	b1=$(git rev-parse origin/b1) &&
+	b2=$(git rev-parse origin/b2) &&
+	git checkout -b b1 origin/b1 &&
+	echo aa-b1 >>file &&
+	git commit -a -m aa-b1 &&
+	git checkout -b b2 origin/b2 &&
+	echo aa-b2 >>file &&
+	git commit -a -m aa-b2 &&
+	git checkout master &&
+	echo aa-master >>file &&
+	git commit -a -m aa-master
+'
+
+test_expect_success 'mixed-success push returns error' '! git push'
+
+test_expect_success 'check tracking branches updated correctly after push' '
+	test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+'
+
+test_expect_success 'check tracking branches not updated for failed refs' '
+	test "$(git rev-parse origin/b1)" = "$b1" &&
+	test "$(git rev-parse origin/b2)" = "$b2"
+'
+
+test_expect_success 'deleted branches have their tracking branches removed' '
+	git push origin :b1 &&
+	test "$(git rev-parse origin/b1)" = "origin/b1"
+'
+
+test_done
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
new file mode 100755
index 0000000..86abc62
--- /dev/null
+++ b/t/t5405-send-pack-rewind.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='forced push to replace commit we do not have'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	>file1 && git add file1 && test_tick &&
+	git commit -m Initial &&
+
+	mkdir another && (
+		cd another &&
+		git init &&
+		git fetch .. master:master
+	) &&
+
+	>file2 && git add file2 && test_tick &&
+	git commit -m Second
+
+'
+
+test_expect_success 'non forced push should die not segfault' '
+
+	(
+		cd another &&
+		git push .. master:master
+		test $? = 1
+	)
+
+'
+
+test_expect_success 'forced push should succeed' '
+
+	(
+		cd another &&
+		git push .. +master:master
+	)
+
+'
+
+test_done
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
new file mode 100755
index 0000000..46b2cb4
--- /dev/null
+++ b/t/t5406-remote-rejects.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='remote push rejects are reported by client'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir .git/hooks &&
+	(echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
+	chmod +x .git/hooks/update &&
+	echo 1 >file &&
+	git add file &&
+	git commit -m 1 &&
+	git clone . child &&
+	cd child &&
+	echo 2 >file &&
+	git commit -a -m 2
+'
+
+test_expect_success 'push reports error' '! git push 2>stderr'
+
+test_expect_success 'individual ref reports error' 'grep rejected stderr'
+
+test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
new file mode 100755
index 0000000..6ec5f7c
--- /dev/null
+++ b/t/t5512-ls-remote.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='git ls-remote'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	>file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git tag mark &&
+	git show-ref --tags -d | sed -e "s/ /	/" >expected.tag &&
+	(
+		echo "$(git rev-parse HEAD)	HEAD"
+		git show-ref -d	| sed -e "s/ /	/"
+	) >expected.all &&
+
+	git remote add self $(pwd)/.git
+
+'
+
+test_expect_success 'ls-remote --tags .git' '
+
+	git ls-remote --tags .git >actual &&
+	diff -u expected.tag actual
+
+'
+
+test_expect_success 'ls-remote .git' '
+
+	git ls-remote .git >actual &&
+	diff -u expected.all actual
+
+'
+
+test_expect_success 'ls-remote --tags self' '
+
+	git ls-remote --tags self >actual &&
+	diff -u expected.tag actual
+
+'
+
+test_expect_success 'ls-remote self' '
+
+	git ls-remote self >actual &&
+	diff -u expected.all actual
+
+'
+
+test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 86f9b53..987c9d2 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -247,9 +247,8 @@
 test_expect_success 'push with dry-run' '
 
 	mk_test heads/master &&
-	cd testrepo &&
-	old_commit=$(git show-ref -s --verify refs/heads/master) &&
-	cd .. &&
+	(cd testrepo &&
+	 old_commit=$(git show-ref -s --verify refs/heads/master)) &&
 	git push --dry-run testrepo &&
 	check_push_result $old_commit heads/master
 '
@@ -257,28 +256,40 @@
 test_expect_success 'push updates local refs' '
 
 	rm -rf parent child &&
-	mkdir parent && cd parent && git init &&
-		echo one >foo && git add foo && git commit -m one &&
-	cd .. &&
-	git clone parent child && cd child &&
+	mkdir parent &&
+	(cd parent && git init &&
+		echo one >foo && git add foo && git commit -m one) &&
+	git clone parent child &&
+	(cd child &&
 		echo two >foo && git commit -a -m two &&
 		git push &&
-	test $(git rev-parse master) = $(git rev-parse remotes/origin/master)
+	test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
 
 '
 
 test_expect_success 'push does not update local refs on failure' '
 
 	rm -rf parent child &&
-	mkdir parent && cd parent && git init &&
+	mkdir parent &&
+	(cd parent && git init &&
 		echo one >foo && git add foo && git commit -m one &&
 		echo exit 1 >.git/hooks/pre-receive &&
-		chmod +x .git/hooks/pre-receive &&
-	cd .. &&
-	git clone parent child && cd child &&
-		echo two >foo && git commit -a -m two || exit 1
-		git push && exit 1
-	test $(git rev-parse master) != $(git rev-parse remotes/origin/master)
+		chmod +x .git/hooks/pre-receive) &&
+	git clone parent child &&
+	(cd child &&
+		echo two >foo && git commit -a -m two &&
+		! git push &&
+		test $(git rev-parse master) != \
+			$(git rev-parse remotes/origin/master))
+
+'
+
+test_expect_success 'allow deleting an invalid remote ref' '
+
+	pwd &&
+	rm -f testrepo/.git/objects/??/* &&
+	git push testrepo :refs/heads/master &&
+	(cd testrepo && ! git rev-parse --verify refs/heads/master)
 
 '
 
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
new file mode 100755
index 0000000..ed3fec1
--- /dev/null
+++ b/t/t5517-push-mirror.sh
@@ -0,0 +1,228 @@
+#!/bin/sh
+
+test_description='pushing to a mirror repository'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+invert () {
+	if "$@"; then
+		return 1
+	else
+		return 0
+	fi
+}
+
+mk_repo_pair () {
+	rm -rf master mirror &&
+	mkdir mirror &&
+	(
+		cd mirror &&
+		git init
+	) &&
+	mkdir master &&
+	(
+		cd master &&
+		git init &&
+		git config remote.up.url ../mirror
+	)
+}
+
+
+# BRANCH tests
+test_expect_success 'push mirror creates new branches' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+	test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror updates existing branches' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git push --mirror up &&
+		echo two >foo && git add foo && git commit -m two &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+	test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror force updates existing branches' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git push --mirror up &&
+		echo two >foo && git add foo && git commit -m two &&
+		git push --mirror up &&
+		git reset --hard HEAD^
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+	test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror removes branches' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git branch remove master &&
+		git push --mirror up &&
+		git branch -D remove
+		git push --mirror up
+	) &&
+	(
+		cd mirror &&
+		invert git show-ref -s --verify refs/heads/remove
+	)
+
+'
+
+test_expect_success 'push mirror adds, updates and removes branches together' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git branch remove master &&
+		git push --mirror up &&
+		git branch -D remove &&
+		git branch add master &&
+		echo two >foo && git add foo && git commit -m two &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+	master_add=$(cd master && git show-ref -s --verify refs/heads/add) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+	mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) &&
+	test "$master_master" = "$mirror_master" &&
+	test "$master_add" = "$mirror_add" &&
+	(
+		cd mirror &&
+		invert git show-ref -s --verify refs/heads/remove
+	)
+
+'
+
+
+# TAG tests
+test_expect_success 'push mirror creates new tags' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git tag -f tmaster master &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+	test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror updates existing tags' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git tag -f tmaster master &&
+		git push --mirror up &&
+		echo two >foo && git add foo && git commit -m two &&
+		git tag -f tmaster master &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+	test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror force updates existing tags' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git tag -f tmaster master &&
+		git push --mirror up &&
+		echo two >foo && git add foo && git commit -m two &&
+		git tag -f tmaster master &&
+		git push --mirror up &&
+		git reset --hard HEAD^
+		git tag -f tmaster master &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+	test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror removes tags' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git tag -f tremove master &&
+		git push --mirror up &&
+		git tag -d tremove
+		git push --mirror up
+	) &&
+	(
+		cd mirror &&
+		invert git show-ref -s --verify refs/tags/tremove
+	)
+
+'
+
+test_expect_success 'push mirror adds, updates and removes tags together' '
+
+	mk_repo_pair &&
+	(
+		cd master &&
+		echo one >foo && git add foo && git commit -m one &&
+		git tag -f tmaster master &&
+		git tag -f tremove master &&
+		git push --mirror up &&
+		git tag -d tremove &&
+		git tag tadd master &&
+		echo two >foo && git add foo && git commit -m two &&
+		git tag -f tmaster master &&
+		git push --mirror up
+	) &&
+	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+	master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) &&
+	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+	mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) &&
+	test "$master_master" = "$mirror_master" &&
+	test "$master_add" = "$mirror_add" &&
+	(
+		cd mirror &&
+		invert git show-ref -s --verify refs/tags/tremove
+	)
+
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 53956c0..2ba4b00 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -71,6 +71,43 @@
 	git bisect next
 '
 
+test_expect_success 'bisect reset: back in the master branch' '
+	git bisect reset &&
+	echo "* master" > branch.expect &&
+	git branch > branch.output &&
+	cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset: back in another branch' '
+	git checkout -b other &&
+	git bisect start &&
+	git bisect good $HASH1 &&
+	git bisect bad $HASH3 &&
+	git bisect reset &&
+	echo "  master" > branch.expect &&
+	echo "* other" >> branch.expect &&
+	git branch > branch.output &&
+	cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset when not bisecting' '
+	git bisect reset &&
+	git branch > branch.output &&
+	cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset removes packed refs' '
+	git bisect reset &&
+	git bisect start &&
+	git bisect good $HASH1 &&
+	git bisect bad $HASH3 &&
+	git pack-refs --all --prune &&
+	git bisect next &&
+	git bisect reset &&
+	test -z "$(git for-each-ref "refs/bisect/*")" &&
+	test -z "$(git for-each-ref "refs/heads/bisect")"
+'
+
 # $HASH1 is good, $HASH4 is bad, we skip $HASH3
 # but $HASH2 is bad,
 # so we should find $HASH2 as the first bad commit
@@ -167,7 +204,7 @@
 	git bisect skip &&
 	git bisect good > my_bisect_log.txt &&
 	grep "$HASH5 is first bad commit" my_bisect_log.txt &&
-	git bisect log > log_to_replay.txt
+	git bisect log > log_to_replay.txt &&
 	git bisect reset
 '
 
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 2089351..5f60b22 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -114,7 +114,7 @@
 
 test_expect_success 'stops when msg filter fails' '
 	old=$(git rev-parse HEAD) &&
-	! git-filter-branch -f --msg-filter false &&
+	! git-filter-branch -f --msg-filter false HEAD &&
 	test $old = $(git rev-parse HEAD) &&
 	rm -rf .git-rewrite
 '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 5f7e388..c7130c4 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -339,20 +339,14 @@
 '
 
 test_expect_success \
-	'trying to create tags giving many -m or -F options should fail' '
+	'trying to create tags giving both -m or -F options should fail' '
 	echo "message file 1" >msgfile1 &&
 	echo "message file 2" >msgfile2 &&
 	! tag_exists msgtag &&
-	! git-tag -m "message 1" -m "message 2" msgtag &&
-	! tag_exists msgtag &&
-	! git-tag -F msgfile1 -F msgfile2 msgtag &&
-	! tag_exists msgtag &&
 	! git-tag -m "message 1" -F msgfile1 msgtag &&
 	! tag_exists msgtag &&
 	! git-tag -F msgfile1 -m "message 1" msgtag &&
 	! tag_exists msgtag &&
-	! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
-	! tag_exists msgtag &&
 	! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
 	! tag_exists msgtag
 '
@@ -673,6 +667,22 @@
 	git diff expect actual
 '
 
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+test -n "$1" && exec >"$1"
+echo A signed tag message
+echo from a fake editor.
+EOF
+chmod +x fakeeditor
+get_tag_header implied-annotate $commit commit $time >expect
+./fakeeditor >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success '-s implies annotated tag' '
+	GIT_EDITOR=./fakeeditor git-tag -s implied-annotate &&
+	get_tag_msg implied-annotate >actual &&
+	git diff expect actual
+'
+
 test_expect_success \
 	'trying to create a signed tag with non-existing -F file should fail' '
 	! test -f nonexistingfile &&
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 9dba104..31a6f63 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -79,7 +79,8 @@
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -i -e "s/a file/an amend commit/g" $1
+sed -e "s/a file/an amend commit/g" < $1 > $1-
+mv $1- $1
 EOF
 chmod 755 editor
 
@@ -98,7 +99,8 @@
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -i -e "s/amend/older/g" $1
+sed -e "s/amend/older/g"  < $1 > $1-
+mv $1- $1
 EOF
 chmod 755 editor
 
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
new file mode 100755
index 0000000..29fee2d
--- /dev/null
+++ b/t/t9600-cvsimport.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='git-cvsimport basic tests'
+. ./test-lib.sh
+
+if ! ( type cvs && type cvsps ) >/dev/null 2>&1
+then
+	test_expect_success 'skipping cvsimport tests, cvs/cvsps not found' ''
+	test_done
+	exit
+fi
+
+CVSROOT=$(pwd)/cvsroot
+export CVSROOT
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
+test_expect_success 'setup cvsroot' 'cvs init'
+
+test_expect_success 'setup a cvs module' '
+
+	mkdir $CVSROOT/module &&
+	cvs co -d module-cvs module &&
+	cd module-cvs &&
+	cat <<EOF >o_fortuna &&
+O Fortuna
+velut luna
+statu variabilis,
+
+semper crescis
+aut decrescis;
+vita detestabilis
+
+nunc obdurat
+et tunc curat
+ludo mentis aciem,
+
+egestatem,
+potestatem
+dissolvit ut glaciem.
+EOF
+	cvs add o_fortuna &&
+	cat <<EOF >message &&
+add "O Fortuna" lyrics
+
+These public domain lyrics make an excellent sample text.
+EOF
+	cvs commit -F message &&
+	cd ..
+'
+
+test_expect_success 'import a trivial module' '
+
+	git cvsimport -a -z 0 -C module-git module &&
+	git diff module-cvs/o_fortuna module-git/o_fortuna
+
+'
+
+test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
+
+test_expect_success 'update cvs module' '
+
+	cd module-cvs &&
+	cat <<EOF >o_fortuna &&
+O Fortune,
+like the moon
+you are changeable,
+
+ever waxing
+and waning;
+hateful life
+
+first oppresses
+and then soothes
+as fancy takes it;
+
+poverty
+and power
+it melts them like ice.
+EOF
+	cat <<EOF >message &&
+translate to English
+
+My Latin is terrible.
+EOF
+	cvs commit -F message &&
+	cd ..
+'
+
+test_expect_success 'update git module' '
+
+	cd module-git &&
+	git cvsimport -a -z 0 module &&
+	git merge origin &&
+	cd .. &&
+	git diff module-cvs/o_fortuna module-git/o_fortuna
+
+'
+
+test_expect_success 'update cvs module' '
+
+	cd module-cvs &&
+		echo 1 >tick &&
+		cvs add tick &&
+		cvs commit -m 1
+	cd ..
+
+'
+
+test_expect_success 'cvsimport.module config works' '
+
+	cd module-git &&
+		git config cvsimport.module module &&
+		git cvsimport -a -z0 &&
+		git merge origin &&
+	cd .. &&
+	git diff module-cvs/tick module-git/tick
+
+'
+
+test_done
diff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit
index a19279b..7092bae 100644
--- a/templates/hooks--pre-commit
+++ b/templates/hooks--pre-commit
@@ -13,7 +13,7 @@
 
 if git-rev-parse --verify HEAD 2>/dev/null
 then
-	git-diff-index -p -M --cached HEAD
+	git-diff-index -p -M --cached HEAD --
 else
 	# NEEDSWORK: we should produce a diff with an empty tree here
 	# if we want to do the same verification for the initial import.
diff --git a/trace.c b/trace.c
index 0d89dbe..d3d1b6d 100644
--- a/trace.c
+++ b/trace.c
@@ -37,7 +37,7 @@
 		return STDERR_FILENO;
 	if (strlen(trace) == 1 && isdigit(*trace))
 		return atoi(trace);
-	if (*trace == '/') {
+	if (is_absolute_path(trace)) {
 		int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
 		if (fd == -1) {
 			fprintf(stderr,
diff --git a/transport.c b/transport.c
index 43b9e7c..50db980 100644
--- a/transport.c
+++ b/transport.c
@@ -6,6 +6,7 @@
 #endif
 #include "pkt-line.h"
 #include "fetch-pack.h"
+#include "send-pack.h"
 #include "walker.h"
 #include "bundle.h"
 #include "dir.h"
@@ -141,7 +142,7 @@
 	}
 }
 
-static struct ref *get_refs_via_rsync(const struct transport *transport)
+static struct ref *get_refs_via_rsync(struct transport *transport)
 {
 	struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
 	struct ref dummy, *tail = &dummy;
@@ -283,6 +284,9 @@
 	struct child_process rsync;
 	const char *args[10];
 
+	if (flags & TRANSPORT_PUSH_MIRROR)
+		return error("rsync transport does not support mirror mode");
+
 	/* first push the objects */
 
 	strbuf_addstr(&buf, transport->url);
@@ -388,6 +392,9 @@
 	int argc;
 	int err;
 
+	if (flags & TRANSPORT_PUSH_MIRROR)
+		return error("http transport does not support mirror mode");
+
 	argv = xmalloc((refspec_nr + 12) * sizeof(char *));
 	argv[0] = "http-push";
 	argc = 1;
@@ -432,7 +439,7 @@
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
-static struct ref *get_refs_via_curl(const struct transport *transport)
+static struct ref *get_refs_via_curl(struct transport *transport)
 {
 	struct buffer buffer;
 	char *data, *start, *mid;
@@ -529,7 +536,7 @@
 	struct bundle_header header;
 };
 
-static struct ref *get_refs_from_bundle(const struct transport *transport)
+static struct ref *get_refs_from_bundle(struct transport *transport)
 {
 	struct bundle_transport_data *data = transport->data;
 	struct ref *result = NULL;
@@ -601,7 +608,7 @@
 	return 1;
 }
 
-static struct ref *get_refs_via_connect(const struct transport *transport)
+static struct ref *get_refs_via_connect(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
 	struct ref *refs;
@@ -654,50 +661,17 @@
 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
 {
 	struct git_transport_data *data = transport->data;
-	const char **argv;
-	char *rem;
-	int argc;
-	int err;
+	struct send_pack_args args;
 
-	argv = xmalloc((refspec_nr + 12) * sizeof(char *));
-	argv[0] = "send-pack";
-	argc = 1;
-	if (flags & TRANSPORT_PUSH_ALL)
-		argv[argc++] = "--all";
-	if (flags & TRANSPORT_PUSH_FORCE)
-		argv[argc++] = "--force";
-	if (flags & TRANSPORT_PUSH_DRY_RUN)
-		argv[argc++] = "--dry-run";
-	if (flags & TRANSPORT_PUSH_VERBOSE)
-		argv[argc++] = "--verbose";
-	if (data->receivepack) {
-		char *rp = xmalloc(strlen(data->receivepack) + 16);
-		sprintf(rp, "--receive-pack=%s", data->receivepack);
-		argv[argc++] = rp;
-	}
-	if (data->thin)
-		argv[argc++] = "--thin";
-	rem = xmalloc(strlen(transport->remote->name) + 10);
-	sprintf(rem, "--remote=%s", transport->remote->name);
-	argv[argc++] = rem;
-	argv[argc++] = transport->url;
-	while (refspec_nr--)
-		argv[argc++] = *refspec++;
-	argv[argc] = NULL;
-	err = run_command_v_opt(argv, RUN_GIT_CMD);
-	switch (err) {
-	case -ERR_RUN_COMMAND_FORK:
-		error("unable to fork for %s", argv[0]);
-	case -ERR_RUN_COMMAND_EXEC:
-		error("unable to exec %s", argv[0]);
-		break;
-	case -ERR_RUN_COMMAND_WAITPID:
-	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-	case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-	case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-		error("%s died with strange error", argv[0]);
-	}
-	return !!err;
+	args.receivepack = data->receivepack;
+	args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
+	args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
+	args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
+	args.use_thin_pack = data->thin;
+	args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
+	args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
+
+	return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
 }
 
 static int disconnect_git(struct transport *transport)
@@ -789,7 +763,7 @@
 	return transport->push(transport, refspec_nr, refspec, flags);
 }
 
-struct ref *transport_get_remote_refs(struct transport *transport)
+const struct ref *transport_get_remote_refs(struct transport *transport)
 {
 	if (!transport->remote_refs)
 		transport->remote_refs = transport->get_refs_list(transport);
diff --git a/transport.h b/transport.h
index 2f80ab4..6fb4526 100644
--- a/transport.h
+++ b/transport.h
@@ -8,7 +8,7 @@
 	struct remote *remote;
 	const char *url;
 	void *data;
-	struct ref *remote_refs;
+	const struct ref *remote_refs;
 
 	/**
 	 * Returns 0 if successful, positive if the option is not
@@ -18,7 +18,7 @@
 	int (*set_option)(struct transport *connection, const char *name,
 			  const char *value);
 
-	struct ref *(*get_refs_list)(const struct transport *transport);
+	struct ref *(*get_refs_list)(struct transport *transport);
 	int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 
@@ -30,7 +30,8 @@
 #define TRANSPORT_PUSH_ALL 1
 #define TRANSPORT_PUSH_FORCE 2
 #define TRANSPORT_PUSH_DRY_RUN 4
-#define TRANSPORT_PUSH_VERBOSE 8
+#define TRANSPORT_PUSH_MIRROR 8
+#define TRANSPORT_PUSH_VERBOSE 16
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -62,7 +63,7 @@
 int transport_push(struct transport *connection,
 		   int refspec_nr, const char **refspec, int flags);
 
-struct ref *transport_get_remote_refs(struct transport *transport);
+const struct ref *transport_get_remote_refs(struct transport *transport);
 
 int transport_fetch_refs(struct transport *transport, struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
diff --git a/unpack-trees.c b/unpack-trees.c
index aea16ad..e9eb795 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -71,12 +71,8 @@
 	int remove;
 	int baselen = strlen(base);
 	int src_size = len + 1;
-	int i_stk = i_stk;
 	int retval = 0;
 
-	if (o->dir)
-		i_stk = push_exclude_per_directory(o->dir, base, strlen(base));
-
 	do {
 		int i;
 		const char *first;
@@ -255,8 +251,6 @@
 	} while (1);
 
  leave_directory:
-	if (o->dir)
-		pop_exclude_per_directory(o->dir, i_stk);
 	return retval;
 }