GIT 1.5.5.6

Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6b9c715
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+* whitespace=!indent,trail,space
+*.[ch] whitespace
diff --git a/.gitignore b/.gitignore
index 7f8421d..4ff2fec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+GIT-BUILD-OPTIONS
 GIT-CFLAGS
 GIT-GUI-VARS
 GIT-VERSION-FILE
@@ -50,7 +51,6 @@
 git-get-tar-commit-id
 git-grep
 git-hash-object
-git-help--browse
 git-http-fetch
 git-http-push
 git-imap-send
@@ -136,6 +136,7 @@
 git-var
 git-verify-pack
 git-verify-tag
+git-web--browse
 git-whatchanged
 git-write-tree
 git-core-*/?*
diff --git a/.mailmap b/.mailmap
index a32d9e2..f88ae77 100644
--- a/.mailmap
+++ b/.mailmap
@@ -17,6 +17,7 @@
 H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
 H. Peter Anvin <hpa@trantor.hos.anvin.org>
 Horst H. von Brand <vonbrand@inf.utfsm.cl>
+Jay Soffian <jaysoffian+git@gmail.com>
 Joachim Berdal Haga <cjhaga@fys.uio.no>
 Jon Loeliger <jdl@freescale.com>
 Jon Seymour <jon@blackcubes.dyndns.org>
diff --git a/Documentation/.gitattributes b/Documentation/.gitattributes
new file mode 100644
index 0000000..ddb0301
--- /dev/null
+++ b/Documentation/.gitattributes
@@ -0,0 +1 @@
+*.txt whitespace
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 3b042db..994eb91 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -53,6 +53,18 @@
  - We do not write the noiseword "function" in front of shell
    functions.
 
+ - As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
+   [::], [==], nor [..]) for portability.
+
+   - We do not use \{m,n\};
+
+   - We do not use -E;
+
+   - We do not use ? nor + (which are \{0,1\} and \{1,\}
+     respectively in BRE) but that goes without saying as these
+     are ERE elements not BRE (note that \? and \+ are not even part
+     of BRE -- making them accessible from BRE is a GNU extension).
+
 For C programs:
 
  - We use tabs to indent, and interpret tabs as taking up to
diff --git a/Documentation/RelNotes-1.5.5.1.txt b/Documentation/RelNotes-1.5.5.1.txt
new file mode 100644
index 0000000..7de4197
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.1.txt
@@ -0,0 +1,44 @@
+GIT v1.5.5.1 Release Notes
+==========================
+
+Fixes since v1.5.5
+------------------
+
+ * "git archive --prefix=$path/" mishandled gitattributes.
+
+ * "git fetch -v" that fetches into FETCH_HEAD did not report the summary
+   the same way as done for updating the tracking refs.
+
+ * "git svn" misbehaved when the configuration file customized the "git
+   log" output format using format.pretty.
+
+ * "git submodule status" leaked an unnecessary error message.
+
+ * "git log --date-order --topo-order" did not override the earlier
+   date-order with topo-order as expected.
+
+ * "git bisect good $this" did not check the validity of the revision
+   given properly.
+
+ * "url.<there>.insteadOf" did not work correctly.
+
+ * "git clean" ran inside subdirectory behaved as if the directory was
+   explicitly specified for removal by the end user from the top level.
+
+ * "git bisect" from a detached head leaked an unnecessary error message.
+
+ * "git bisect good $a $b" when $a is Ok but $b is bogus should have
+   atomically failed before marking $a as good.
+
+ * "git fmt-merge-msg" did not clean up leading empty lines from commit
+   log messages like "git log" family does.
+
+ * "git am" recorded a commit with empty Subject: line without
+   complaining.
+
+ * when given a commit log message whose first paragraph consists of
+   multiple lines, "git rebase" squashed it into a single line.
+
+ * "git remote add $bogus_name $url" did not complain properly.
+
+Also comes with various documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.2.txt b/Documentation/RelNotes-1.5.5.2.txt
new file mode 100644
index 0000000..391a7b0
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.2.txt
@@ -0,0 +1,27 @@
+GIT v1.5.5.2 Release Notes
+==========================
+
+Fixes since v1.5.5.1
+--------------------
+
+ * "git repack -n" was mistakenly made no-op earlier.
+
+ * "git imap-send" wanted to always have imap.host even when use of
+   imap.tunnel made it unnecessary.
+
+ * reflog syntax that uses time e.g. "HEAD@{10 seconds ago}:path" did not
+   stop parsing at the closing "}".
+
+ * "git rev-parse --symbolic-full-name ^master^2" printed solitary "^",
+   but it should print nothing.
+
+ * "git commit" did not detect when it failed to write tree objects.
+
+ * "git fetch" sometimes transferred too many objects unnecessarily.
+
+ * a path specification "a/b" in .gitattributes file should not match
+   "sub/a/b".
+
+ * various gitweb fixes.
+
+Also comes with various documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.3.txt b/Documentation/RelNotes-1.5.5.3.txt
new file mode 100644
index 0000000..f22f98b
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.3.txt
@@ -0,0 +1,12 @@
+GIT v1.5.5.3 Release Notes
+==========================
+
+Fixes since v1.5.5.2
+--------------------
+
+ * "git send-email --compose" did not notice that non-ascii contents
+   needed some MIME magic.
+
+ * "git fast-export" did not export octopus merges correctly.
+
+Also comes with various documentation updates.
diff --git a/Documentation/RelNotes-1.5.5.4.txt b/Documentation/RelNotes-1.5.5.4.txt
new file mode 100644
index 0000000..2d0279e
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.4.txt
@@ -0,0 +1,7 @@
+GIT v1.5.5.4 Release Notes
+==========================
+
+Fixes since v1.5.5.4
+--------------------
+
+ * "git name-rev --all" used to segfault.
diff --git a/Documentation/RelNotes-1.5.5.5.txt b/Documentation/RelNotes-1.5.5.5.txt
new file mode 100644
index 0000000..30fa361
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.5.txt
@@ -0,0 +1,11 @@
+GIT v1.5.5.5 Release Notes
+==========================
+
+I personally do not think there is any reason anybody should want to
+run v1.5.5.X series these days, because 'master' version is always
+more stable than any tagged released version of git.
+
+This is primarily to futureproof "git-shell" to accept requests
+without a dash between "git" and subcommand name (e.g. "git
+upload-pack") which the newer client will start to make sometime in
+the future.
diff --git a/Documentation/RelNotes-1.5.5.6.txt b/Documentation/RelNotes-1.5.5.6.txt
new file mode 100644
index 0000000..d5e85cb
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.6.txt
@@ -0,0 +1,10 @@
+GIT v1.5.5.6 Release Notes
+==========================
+
+Fixes since 1.5.5.5
+-------------------
+
+ * Removed support for an obsolete gitweb request URI, whose
+   implementation ran "git diff" Porcelain, instead of using plumbing,
+   which would have run an external diff command specified in the
+   repository configuration as the gitweb user.
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
new file mode 100644
index 0000000..2932212
--- /dev/null
+++ b/Documentation/RelNotes-1.5.5.txt
@@ -0,0 +1,207 @@
+GIT v1.5.5 Release Notes
+========================
+
+Updates since v1.5.4
+--------------------
+
+(subsystems)
+
+ * Comes with git-gui 0.10.1
+
+(portability)
+
+ * We shouldn't ask for BSD group ownership semantics by setting g+s bit
+   on directories on older BSD systems that refuses chmod() by non root
+   users.  BSD semantics is the default there anyway.
+
+ * Bunch of portability improvement patches coming from an effort to port
+   to Solaris has been applied.
+
+(performance)
+
+ * On platforms with suboptimal qsort(3) implementation, there
+   is an option to use more reasonable substitute we ship with
+   our software.
+
+ * New configuration variable "pack.packsizelimit" can be used
+   in place of command line option --max-pack-size.
+
+ * "git fetch" over the native git protocol used to make a
+   connection to find out the set of current remote refs and
+   another to actually download the pack data.  We now use only
+   one connection for these tasks.
+
+ * "git commit" does not run lstat(2) more than necessary
+   anymore.
+
+(usability, bells and whistles)
+
+ * Bash completion script (in contrib) are aware of more commands and
+   options.
+
+ * You can be warned when core.autocrlf conversion is applied in
+   such a way that results in an irreversible conversion.
+
+ * A catch-all "color.ui" configuration variable can be used to
+   enable coloring of all color-capable commands, instead of
+   individual ones such as "color.status" and "color.branch".
+
+ * The commands refused to take absolute pathnames where they
+   require pathnames relative to the work tree or the current
+   subdirectory.  They now can take absolute pathnames in such a
+   case as long as the pathnames do not refer outside of the
+   work tree.  E.g. "git add $(pwd)/foo" now works.
+
+ * Error messages used to be sent to stderr, only to get hidden,
+   when $PAGER was in use.  They now are sent to stdout along
+   with the command output to be shown in the $PAGER.
+
+ * A pattern "foo/" in .gitignore file now matches a directory
+   "foo".  Pattern "foo" also matches as before.
+
+ * bash completion's prompt helper function can talk about
+   operation in-progress (e.g. merge, rebase, etc.).
+
+ * Configuration variables "url.<usethis>.insteadof = <otherurl>" can be
+   used to tell "git-fetch" and "git-push" to use different URL than what
+   is given from the command line.
+
+ * "git add -i" behaves better even before you make an initial commit.
+
+ * "git am" refused to run from a subdirectory without a good reason.
+
+ * After "git apply --whitespace=fix" fixes whitespace errors in a patch,
+   a line before the fix can appear as a context or preimage line in a
+   later patch, causing the patch not to apply.  The command now knows to
+   see through whitespace fixes done to context lines to successfully
+   apply such a patch series.
+
+ * "git branch" (and "git checkout -b") to branch from a local branch can
+   optionally set "branch.<name>.merge" to mark the new branch to build on
+   the other local branch, when "branch.autosetupmerge" is set to
+   "always", or when passing the command line option "--track" (this option
+   was ignored when branching from local branches).  By default, this does
+   not happen when branching from a local branch.
+
+ * "git checkout" to switch to a branch that has "branch.<name>.merge" set
+   (i.e. marked to build on another branch) reports how much the branch
+   and the other branch diverged.
+
+ * When "git checkout" has to update a lot of paths, it used to be silent
+   for 4 seconds before it showed any progress report.  It is now a bit
+   more impatient and starts showing progress report early.
+
+ * "git commit" learned a new hook "prepare-commit-msg" that can
+   inspect what is going to be committed and prepare the commit
+   log message template to be edited.
+
+ * "git cvsimport" can now take more than one -M options.
+
+ * "git describe" learned to limit the tags to be used for
+   naming with --match option.
+
+ * "git describe --contains" now barfs when the named commit
+   cannot be described.
+
+ * "git describe --exact-match" describes only commits that are tagged.
+
+ * "git describe --long" describes a tagged commit as $tag-0-$sha1,
+   instead of just showing the exact tagname.
+
+ * "git describe" warns when using a tag whose name and path contradict
+   with each other.
+
+ * "git diff" learned "--relative" option to limit and output paths
+   relative to the current directory when working in a subdirectory.
+
+ * "git diff" learned "--dirstat" option to show birds-eye-summary of
+   changes more concisely than "--diffstat".
+
+ * "git format-patch" learned --cover-letter option to generate a cover
+   letter template.
+
+ * "git gc" learned --quiet option.
+
+ * "git gc" now automatically prunes unreachable objects that are two
+   weeks old or older.
+
+ * "git gc --auto" can be disabled more easily by just setting gc.auto
+   to zero.  It also tolerates more packfiles by default.
+
+ * "git grep" now knows "--name-only" is a synonym for the "-l" option.
+
+ * "git help <alias>" now reports "'git <alias>' is alias to <what>",
+   instead of saying "No manual entry for git-<alias>".
+
+ * "git help" can use different backends to show manual pages and this can
+   be configured using "man.viewer" configuration.
+
+ * "gitk" does not restore window position from $HOME/.gitk anymore (it
+   still restores the size).
+
+ * "git log --grep=<what>" learned "--fixed-strings" option to look for
+   <what> without treating it as a regular expression.
+
+ * "git gui" learned an auto-spell checking.
+
+ * "git push <somewhere> HEAD" and "git push <somewhere> +HEAD" works as
+   expected; they push the current branch (and only the current branch).
+   In addition, HEAD can be written as the value of "remote.<there>.push"
+   configuration variable.
+
+ * When the configuration variable "pack.threads" is set to 0, "git
+   repack" auto detects the number of CPUs and uses that many threads.
+
+ * "git send-email" learned to prompt for passwords
+   interactively.
+
+ * "git send-email" learned an easier way to suppress CC
+   recipients.
+
+ * "git stash" learned "pop" command, that applies the latest stash and
+   removes it from the stash, and "drop" command to discard the named
+   stash entry.
+
+ * "git submodule" learned a new subcommand "summary" to show the
+   symmetric difference between the HEAD version and the work tree version
+   of the submodule commits.
+
+ * Various "git cvsimport", "git cvsexportcommit", "git cvsserver",
+   "git svn" and "git p4" improvements.
+
+(internal)
+
+ * Duplicated code between git-help and git-instaweb that
+   launches user's preferred browser has been refactored.
+
+ * It is now easier to write test scripts that records known
+   breakages.
+
+ * "git checkout" is rewritten in C.
+
+ * "git remote" is rewritten in C.
+
+ * Two conflict hunks that are separated by a very short span of common
+   lines are now coalesced into one larger hunk, to make the result easier
+   to read.
+
+ * Run-command API's use of file descriptors is documented clearer and
+   is more consistent now.
+
+ * diff output can be sent to FILE * that is different from stdout.  This
+   will help reimplementing more things in C.
+
+Fixes since v1.5.4
+------------------
+
+All of the fixes in v1.5.4 maintenance series are included in
+this release, unless otherwise noted.
+
+ * "git-http-push" did not allow deletion of remote ref with the usual
+   "push <remote> :<branch>" syntax.
+
+ * "git-rebase --abort" did not go back to the right location if
+   "git-reset" was run during the "git-rebase" session.
+
+ * "git imap-send" without setting imap.host did not error out but
+   segfaulted.
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index de08d09..0e155c9 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -34,9 +34,9 @@
 	- if your name is not writable in ASCII, make sure that
 	  you send off a message in the correct encoding.
 	- send the patch to the list (git@vger.kernel.org) and the
-	  maintainer (gitster@pobox.com). If you use
-	  git-send-email(1), please test it first by sending
-	  email to yourself.
+	  maintainer (gitster@pobox.com) if (and only if) the patch
+	  is ready for inclusion. If you use git-send-email(1),
+	  please test it first by sending email to yourself.
 
 Long version:
 
@@ -112,7 +112,12 @@
 
 It is a common convention to prefix your subject line with
 [PATCH].  This lets people easily distinguish patches from other
-e-mail discussions.
+e-mail discussions.  Use of additional markers after PATCH and
+the closing bracket to mark the nature of the patch is also
+encouraged.  E.g. [PATCH/RFC] is often used when the patch is
+not ready to be applied but it is for discussion, [PATCH v2],
+[PATCH v3] etc. are often seen when you are sending an update to
+what you have previously sent.
 
 "git format-patch" command follows the best current practice to
 format the body of an e-mail message.  At the beginning of the
@@ -157,7 +162,8 @@
 on the git mailing list.  If your patch is for discussion first,
 send it "To:" the mailing list, and optionally "cc:" him.  If it
 is trivially correct or after the list reached a consensus, send
-it "To:" the maintainer and optionally "cc:" the list.
+it "To:" the maintainer and optionally "cc:" the list for
+inclusion.
 
 Also note that your maintainer does not actively involve himself in
 maintaining what are in contrib/ hierarchy.  When you send fixes and
@@ -210,10 +216,53 @@
 This line can be automatically added by git if you run the git-commit
 command with the -s option.
 
-Some people also put extra tags at the end.  They'll just be ignored for
-now, but you can do this to mark internal company procedures or just
-point out some special detail about the sign-off.
+Notice that you can place your own Signed-off-by: line when
+forwarding somebody else's patch with the above rules for
+D-C-O.  Indeed you are encouraged to do so.  Do not forget to
+place an in-body "From: " line at the beginning to properly attribute
+the change to its true author (see (2) above).
 
+Some people also put extra tags at the end.
+
+"Acked-by:" says that the patch was reviewed by the person who
+is more familiar with the issues and the area the patch attempts
+to modify.  "Tested-by:" says the patch was tested by the person
+and found to have the desired effect.
+
+------------------------------------------------
+An ideal patch flow
+
+Here is an ideal patch flow for this project the current maintainer
+suggests to the contributors:
+
+ (0) You come up with an itch.  You code it up.
+
+ (1) Send it to the list and cc people who may need to know about
+     the change.
+
+     The people who may need to know are the ones whose code you
+     are butchering.  These people happen to be the ones who are
+     most likely to be knowledgeable enough to help you, but
+     they have no obligation to help you (i.e. you ask for help,
+     don't demand).  "git log -p -- $area_you_are_modifying" would
+     help you find out who they are.
+
+ (2) You get comments and suggestions for improvements.  You may
+     even get them in a "on top of your change" patch form.
+
+ (3) Polish, refine, and re-send to the list and the people who
+     spend their time to improve your patch.  Go back to step (2).
+
+ (4) The list forms consensus that the last round of your patch is
+     good.  Send it to the list and cc the maintainer.
+
+ (5) A topic branch is created with the patch and is merged to 'next',
+     and cooked further and eventually graduates to 'master'.
+
+In any time between the (2)-(3) cycle, the maintainer may pick it up
+from the list and queue it to 'pu', in order to make it easier for
+people play with it without having to pick up and apply the patch to
+their trees themselves.
 
 ------------------------------------------------
 MUA specific hints
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ffa0636..273b358 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -139,6 +139,51 @@
 	"text" (i.e. be subjected to the autocrlf mechanism) is
 	decided purely based on the contents.
 
+core.safecrlf::
+	If true, makes git check if converting `CRLF` as controlled by
+	`core.autocrlf` is reversible.  Git will verify if a command
+	modifies a file in the work tree either directly or indirectly.
+	For example, committing a file followed by checking out the
+	same file should yield the original file in the work tree.  If
+	this is not the case for the current setting of
+	`core.autocrlf`, git will reject the file.  The variable can
+	be set to "warn", in which case git will only warn about an
+	irreversible conversion but continue the operation.
++
+CRLF conversion bears a slight chance of corrupting data.
+autocrlf=true will convert CRLF to LF during commit and LF to
+CRLF during checkout.  A file that contains a mixture of LF and
+CRLF before the commit cannot be recreated by git.  For text
+files this is the right thing to do: it corrects line endings
+such that we have only LF line endings in the repository.
+But for binary files that are accidentally classified as text the
+conversion can corrupt data.
++
+If you recognize such corruption early you can easily fix it by
+setting the conversion type explicitly in .gitattributes.  Right
+after committing you still have the original file in your work
+tree and this file is not yet corrupted.  You can explicitly tell
+git that this file is binary and git will handle the file
+appropriately.
++
+Unfortunately, the desired effect of cleaning up text files with
+mixed line endings and the undesired effect of corrupting binary
+files cannot be distinguished.  In both cases CRLFs are removed
+in an irreversible way.  For text files this is the right thing
+to do because CRLFs are line endings, while for binary files
+converting CRLFs corrupts data.
++
+Note, this safety check does not mean that a checkout will generate a
+file identical to the original file for a different setting of
+`core.autocrlf`, but only for the current one.  For example, a text
+file with `LF` would be accepted with `core.autocrlf=input` and could
+later be checked out with `core.autocrlf=true`, in which case the
+resulting file would contain `CRLF`, although the original file
+contained `LF`.  However, in both work trees the line endings would be
+consistent, that is either all `LF` or all `CRLF`, but never mixed.  A
+file with mixed line endings would be reported by the `core.safecrlf`
+mechanism.
+
 core.symlinks::
 	If false, symbolic links are checked out as small plain files that
 	contain the link text. linkgit:git-update-index[1] and
@@ -308,6 +353,10 @@
   error (enabled by default).
 * `indent-with-non-tab` treats a line that is indented with 8 or more
   space characters as an error (not enabled by default).
+* `cr-at-eol` treats a carriage-return at the end of line as
+  part of the line terminator, i.e. with it, `trailing-space`
+  does not trigger if the character before such a carriage-return
+  is not a whitespace (not enabled by default).
 
 alias.*::
 	Command aliases for the linkgit:git[1] command wrapper - e.g.
@@ -330,10 +379,14 @@
 
 branch.autosetupmerge::
 	Tells `git-branch` and `git-checkout` to setup new branches
-	so that linkgit:git-pull[1] will appropriately merge from that
-	remote branch.  Note that even if this option is not set,
+	so that linkgit:git-pull[1] will appropriately merge from the
+	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
-	and `--no-track` options.  This option defaults to true.
+	and `--no-track` options. The valid settings are: `false` -- no
+	automatic setup is done; `true` -- automatic setup is done when the
+	starting point is a remote branch; `always` -- automatic setup is
+	done when the starting point is either a local branch or remote
+	branch. This option defaults to true.
 
 branch.<name>.remote::
 	When in branch <name>, it tells `git fetch` which remote to fetch.
@@ -368,6 +421,11 @@
 	it unless you understand the implications (see linkgit:git-rebase[1]
 	for details).
 
+browser.<tool>.cmd::
+	Specify the command to invoke the specified browser. The
+	specified command is evaluated in shell with the URLs passed
+	as arguments. (See linkgit:git-web--browse[1].)
+
 browser.<tool>.path::
 	Override the path for the given tool that may be used to
 	browse HTML help (see '-w' option in linkgit:git-help[1]) or a
@@ -445,6 +503,13 @@
 commit.template::
 	Specify a file to use as the template for new commit messages.
 
+color.ui::
+	When set to `always`, always use colors in all git commands which
+	are capable of colored output. When false (or `never`), never. When
+	set to `true` or `auto`, use colors only when the output is to the
+	terminal. When more specific variables of color.* are set, they always
+	take precedence over this setting. Defaults to false.
+
 diff.autorefreshindex::
 	When using `git diff` to compare with work tree
 	files, do not consider stat-only change as changed.
@@ -497,6 +562,11 @@
 	`.patch`. Use this variable to change that suffix (make sure to
 	include the dot if you want it).
 
+format.pretty::
+	The default pretty format for log/show/whatchanged command,
+	See linkgit:git-log[1], linkgit:git-show[1],
+	linkgit:git-whatchanged[1].
+
 gc.aggressiveWindow::
 	The window size parameter used in the delta compression
 	algorithm used by 'git gc --aggressive'.  This defaults
@@ -513,7 +583,7 @@
 	When there are more than this many packs that are not
 	marked with `*.keep` file in the repository, `git gc
 	--auto` consolidates them into one larger pack.  The
-	default	value is 20.  Setting this to 0 disables it.
+	default	value is 50.  Setting this to 0 disables it.
 
 gc.packrefs::
 	`git gc` does not run `git pack-refs` in a bare repository by
@@ -526,6 +596,10 @@
 	at some stage, and setting this to `false` will continue to
 	prevent `git pack-refs` from being run from `git gc`.
 
+gc.pruneexpire::
+	When `git gc` is run, it will call `prune --expire 2.weeks.ago`.
+	Override the grace period with this config variable.
+
 gc.reflogexpire::
 	`git reflog expire` removes reflog entries older than
 	this time; defaults to 90 days.
@@ -588,6 +662,13 @@
 	'gitcvs.dbuser' supports variable substitution (see
 	linkgit:git-cvsserver[1] for details).
 
+gitcvs.dbTableNamePrefix::
+	Database table name prefix.  Prepended to the names of any
+	database tables used, allowing a single database to be used
+	for several repositories.  Supports variable substitution (see
+	linkgit:git-cvsserver[1] for details).  Any non-alphabetic
+	characters will be replaced with underscores.
+
 All gitcvs variables except for 'gitcvs.allbinary' can also be
 specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'
 is one of "ext" and "pserver") to make them apply only for the given
@@ -684,14 +765,20 @@
 	Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
 	normally hide the root commit will now show it. True by default.
 
+man.viewer::
+	Specify the programs that may be used to display help in the
+	'man' format. See linkgit:git-help[1].
+
 merge.summary::
 	Whether to include summaries of merged commits in newly created
 	merge commit messages. False by default.
 
 merge.tool::
 	Controls which merge resolution program is used by
-	linkgit:git-mergetool[1].  Valid values are: "kdiff3", "tkdiff",
-	"meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
+	linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
+	"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+	"opendiff".  Any other value is treated is custom merge tool
+	and there must be a corresponing mergetool.<tool>.cmd option.
 
 merge.verbosity::
 	Controls the amount of output shown by the recursive merge
@@ -718,6 +805,31 @@
 	Override the path for the given tool.  This is useful in case
 	your tool is not in the PATH.
 
+mergetool.<tool>.cmd::
+	Specify the command to invoke the specified merge tool.  The
+	specified command is evaluated in shell with the following
+	variables available: 'BASE' is the name of a temporary file
+	containing the common base of the files to be merged, if available;
+	'LOCAL' is the name of a temporary file containing the contents of
+	the file on the current branch; 'REMOTE' is the name of a temporary
+	file containing the contents of the file from the branch being
+	merged; 'MERGED' contains the name of the file to which the merge
+	tool should write the results of a successful merge.
+
+mergetool.<tool>.trustExitCode::
+	For a custom merge command, specify whether the exit code of
+	the merge command can be used to determine whether the merge was
+	successful.  If this is not set to true then the merge target file
+	timestamp is checked and the merge assumed to have been successful
+	if the file has been updated, otherwise the user is prompted to
+	indicate the success of the merge.
+
+mergetool.keepBackup::
+	After performing a merge, the original file with conflict markers
+	can be saved as a file with a `.orig` extension.  If this variable
+	is set to `false` then this file is not preserved.  Defaults to
+	`true` (i.e. keep the backup files).
+
 pack.window::
 	The size of the window used by linkgit:git-pack-objects[1] when no
 	window size is given on the command line. Defaults to 10.
@@ -757,6 +869,8 @@
 	warning. This is meant to reduce packing time on multiprocessor
 	machines. The required amount of memory for the delta search window
 	is however multiplied by the number of threads.
+	Specifying 0 will cause git to auto-detect the number of CPU's
+	and set the number of threads accordingly.
 
 pack.indexVersion::
 	Specify the default pack index version.  Valid values are 1 for
@@ -767,6 +881,12 @@
 	whenever the corresponding pack is larger than 2 GB.  Otherwise
 	the default is 1.
 
+pack.packSizeLimit::
+	The default maximum size of a pack.  This setting only affects
+	packing to a file, i.e. the git:// protocol is unaffected.  It
+	can be overridden by the `\--max-pack-size` option of
+	linkgit:git-repack[1].
+
 pull.octopus::
 	The default merge strategy to use when pulling multiple branches
 	at once.
@@ -836,6 +956,17 @@
 	archiving user's umask will be used instead.  See umask(2) and
 	linkgit:git-archive[1].
 
+url.<base>.insteadOf::
+	Any URL that starts with this value will be rewritten to
+	start, instead, with <base>. In cases where some site serves a
+	large number of repositories, and serves them with multiple
+	access methods, and some users need to use different access
+	methods, this feature allows people to specify any of the
+	equivalent URLs and have git automatically rewrite the URL to
+	the best alternative for the particular user, even for a
+	never-before-seen repository on the site.  When more than one
+	insteadOf strings match a given URL, the longest match is used.
+
 user.email::
 	Your email address to be recorded in any newly created commits.
 	Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 60d0e53..13234fa 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -58,6 +58,14 @@
 	number of modified files, as well as number of added and deleted
 	lines.
 
+--dirstat[=limit]::
+	Output only the sub-directories that are impacted by a diff,
+	and to what degree they are impacted.  You can override the
+	default cut-off in percent (3) by "--dirstat=limit".  If you
+	want to enable "cumulative" directory statistics, you can use
+	the "--cumulative" flag, which adds up percentages recursively
+	even when they have been already reported for a sub-directory.
+
 --summary::
 	Output a condensed summary of extended header information
 	such as creations, renames and mode changes.
@@ -171,6 +179,14 @@
 	Swap two inputs; that is, show differences from index or
 	on-disk file to tree contents.
 
+--relative[=<path>]::
+	When run from a subdirectory of the project, it can be
+	told to exclude changes outside the directory and show
+	pathnames relative to it with this option.  When you are
+	not in a subdirectory (e.g. in a bare repository), you
+	can name which subdirectory to make the output relative
+	to by giving a <path> as an argument.
+
 --text::
 	Treat all files as text.
 
diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index fdbd15a..e598cdd 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -48,14 +48,12 @@
 repository health reasonably well.
 <2> check how many loose objects there are and how much
 disk space is wasted by not repacking.
-<3> repacks the local repository and performs other housekeeping tasks. Running
-without `--prune` is a safe operation even while other ones are in progress.
+<3> repacks the local repository and performs other housekeeping tasks.
 
 Repack a small project into single pack.::
 +
 ------------
 $ git gc <1>
-$ git gc --prune
 ------------
 +
 <1> pack all the objects reachable from the refs into one pack,
@@ -182,7 +180,7 @@
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <4>
 $ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5>
 $ git reset --hard ORIG_HEAD <6>
-$ git gc --prune <7>
+$ git gc <7>
 $ git fetch --tags <8>
 ------------
 +
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ab88f0a..e2389e3 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -228,6 +228,12 @@
   This lets you review what will be committed (i.e. between
   HEAD and index).
 
+Bugs
+----
+The interactive mode does not work with files whose names contain
+characters that need C-quoting.  `core.quotepath` configuration can be
+used to work this limitation around to some degree, but backslash,
+double-quote and control characters will still have problems.
 
 See Also
 --------
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e640fc7..2387a8d 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
+'git-am' [--signoff] [--keep] [--utf8 | --no-utf8]
          [--3way] [--interactive] [--binary]
          [--whitespace=<option>] [-C<n>] [-p<n>]
          <mbox>|<Maildir>...
@@ -32,10 +32,6 @@
 	Add `Signed-off-by:` line to the commit message, using
 	the committer identity of yourself.
 
--d=<dir>, --dotest=<dir>::
-	Instead of `.dotest` directory, use <dir> as a working
-	area to store extracted patches.
-
 -k, --keep::
 	Pass `-k` flag to `git-mailinfo` (see linkgit:git-mailinfo[1]).
 
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 96585ae..0855b98 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -78,7 +78,7 @@
 $ git bisect reset
 ------------------------------------------------
 
-to get back to the master branch, instead of being in one of the
+to get back to the original branch, instead of being in one of the
 bisection branches ("git bisect start" will do that for you too,
 actually: it will reset the bisection state, and before it does that
 it checks that you're not using some old bisection branch).
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 7e8874a..6f07a17 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -35,11 +35,10 @@
 new branch.
 
 When a local branch is started off a remote branch, git sets up the
-branch so that linkgit:git-pull[1] will appropriately merge from that
-remote branch.  If this behavior is not desired, it is possible to
-disable it using the global `branch.autosetupmerge` configuration
-flag.  That setting can be overridden by using the `--track`
-and `--no-track` options.
+branch so that linkgit:git-pull[1] will appropriately merge from
+the remote branch. This behavior may be changed via the global
+`branch.autosetupmerge` configuration flag. That setting can be
+overridden by using the `--track` and `--no-track` options.
 
 With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
 If <oldbranch> had a corresponding reflog, it is renamed to match
@@ -105,20 +104,19 @@
 	Display the full sha1s in output listing rather than abbreviating them.
 
 --track::
-	Set up configuration so that git-pull will automatically
-	retrieve data from the remote branch.  Use this if you always
-	pull from the same remote branch into the new branch, or if you
-	don't want to use "git pull <repository> <refspec>" explicitly.
-	This behavior is the default.  Set the
-	branch.autosetupmerge configuration variable to false if you
-	want git-checkout and git-branch to always behave as if
-	'--no-track' were given.
+	When creating a new branch, set up configuration so that git-pull
+	will automatically retrieve data from the start point, which must be
+	a branch. Use this if you always pull from the same upstream branch
+	into the new branch, and if you don't want to use "git pull
+	<repository> <refspec>" explicitly. This behavior is the default
+	when the start point is a remote branch. Set the
+	branch.autosetupmerge configuration variable to `false` if you want
+	git-checkout and git-branch to always behave as if '--no-track' were
+	given. Set it to `always` if you want this behavior when the
+	start-point is either a local or remote branch.
 
 --no-track::
-	When a branch is created off a remote branch,
-	set up configuration so that git-pull will not retrieve data
-	from the remote branch, ignoring the branch.autosetupmerge
-	configuration variable.
+	Ignore the branch.autosetupmerge configuration variable.
 
 <branchname>::
 	The name of the branch to create or delete.
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index 72f080a..18330cd 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git-bundle' create <file> [git-rev-list args]
+'git-bundle' create <file> <git-rev-list args>
 'git-bundle' verify <file>
 'git-bundle' list-heads <file> [refname...]
 'git-bundle' unbundle <file> [refname...]
@@ -99,36 +99,62 @@
 For whatever reason, direct connection between A and B is not allowed,
 but we can move data from A to B via some mechanism (CD, email, etc).
 We want to update R2 with developments made on branch master in R1.
+
+To create the bundle you have to specify the basis. You have some options:
+
+- Without basis.
++
+This is useful when sending the whole history.
+
+------------
+$ git bundle create mybundle master
+------------
+
+- Using temporally tags.
++
 We set a tag in R1 (lastR2bundle) after the previous such transport,
 and move it afterwards to help build the bundle.
 
-in R1 on A:
-
 ------------
 $ git-bundle create mybundle master ^lastR2bundle
 $ git tag -f lastR2bundle master
 ------------
 
-(move mybundle from A to B by some mechanism)
+- Using a tag present in both repositories
 
-in R2 on B:
+------------
+$ git bundle create mybundle master ^v1.0.0
+------------
+
+- A basis based on time.
+
+------------
+$ git bundle create mybundle master --since=10.days.ago
+------------
+
+- With a limit on the number of commits
+
+------------
+$ git bundle create mybundle master -n 10
+------------
+
+Then you move mybundle from A to B, and in R2 on B:
 
 ------------
 $ git-bundle verify mybundle
-$ git-fetch mybundle  refspec
+$ git-fetch mybundle master:localRef
 ------------
 
-where refspec is refInBundle:localRef
+With something like this in the config in R2:
 
-
-Also, with something like this in your config:
-
+------------------------
 [remote "bundle"]
     url = /home/me/tmp/file.bdl
     fetch = refs/heads/*:refs/remotes/origin/*
+------------------------
 
 You can first sneakernet the bundle file to ~/tmp/file.bdl and
-then these commands:
+then these commands on machine B:
 
 ------------
 $ git ls-remote bundle
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index b4cfa04..e11cddb 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-checkout - Checkout and switch to a branch
+git-checkout - Checkout a branch or paths to the working tree
 
 SYNOPSIS
 --------
@@ -48,21 +48,19 @@
 	may restrict the characters allowed in a branch name.
 
 --track::
-	When -b is given and a branch is created off a remote branch,
-	set up configuration so that git-pull will automatically
-	retrieve data from the remote branch.  Use this if you always
-	pull from the same remote branch into the new branch, or if you
-	don't want to use "git pull <repository> <refspec>" explicitly.
-	This behavior is the default.  Set the
-	branch.autosetupmerge configuration variable to false if you
-	want git-checkout and git-branch to always behave as if
-	'--no-track' were given.
+	When creating a new branch, set up configuration so that git-pull
+	will automatically retrieve data from the start point, which must be
+	a branch. Use this if you always pull from the same upstream branch
+	into the new branch, and if you don't want to use "git pull
+	<repository> <refspec>" explicitly. This behavior is the default
+	when the start point is a remote branch. Set the
+	branch.autosetupmerge configuration variable to `false` if you want
+	git-checkout and git-branch to always behave as if '--no-track' were
+	given. Set it to `always` if you want this behavior when the
+	start-point is either a local or remote branch.
 
 --no-track::
-	When -b is given and a branch is created off a remote branch,
-	set up configuration so that git-pull will not retrieve data
-	from the remote branch, ignoring the branch.autosetupmerge
-	configuration variable.
+	Ignore the branch.autosetupmerge configuration variable.
 
 -l::
 	Create the new branch's reflog.  This activates recording of
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 9758243..9b56442 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -65,10 +65,13 @@
 +
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand what it does. If you clone your
-repository using this option, then delete branches in the
-source repository and then run linkgit:git-gc[1] using the
-'--prune' option in the source repository, it may remove
-objects which are referenced by the cloned repository.
+repository using this option and then delete branches (or use any
+other git command that makes any existing commit unreferenced) in the
+source repository, some objects may become unreferenced (or dangling).
+These objects may be removed by normal git operations (such as git-commit[1])
+which automatically call git-gc[1]. If these objects are removed and
+were referenced by the cloned repository, then the cloned repository
+will become corrupt.
 
 
 
@@ -79,6 +82,8 @@
 	an already existing repository as an alternate will
 	require fewer objects to be copied from the repository
 	being cloned, reducing network and local storage costs.
++
+*NOTE*: see NOTE to --shared option.
 
 --quiet::
 -q::
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 488d873..4bb51cc 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -291,8 +291,8 @@
 
 HOOKS
 -----
-This command can run `commit-msg`, `pre-commit`, and
-`post-commit` hooks.  See link:hooks.html[hooks] for more
+This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
+and `post-commit` hooks.  See link:hooks.html[hooks] for more
 information.
 
 
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index 6f91b9e..58eefd4 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -102,13 +102,17 @@
 
 -m::
 	Attempt to detect merges based on the commit message. This option
-	will enable default regexes that try to capture the name source
+	will enable default regexes that try to capture the source
 	branch name from the commit message.
 
 -M <regex>::
 	Attempt to detect merges based on the commit message with a custom
 	regex. It can be used with '-m' to enable the default regexes
 	as well. You must escape forward slashes.
++
+The regex must capture the source branch name in $1.
++
+This option can be used several times to provide several detection regexes.
 
 -S <regex>::
 	Skip paths matching the regex.
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index 0b6db86..b110671 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -233,6 +233,11 @@
 	Database password.  Only useful if setting `dbdriver`, since
 	SQLite has no concept of database passwords.
 
+gitcvs.dbTableNamePrefix::
+	Database table name prefix.  Supports variable substitution
+	(see below).  Any non-alphabetic characters will be replaced
+	with underscores.
+
 All variables can also be set per access method, see <<configaccessmethod,above>>.
 
 Variable substitution
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 5e88b6e..69e1ab7 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -46,12 +46,30 @@
 	candidates to describe the input committish consider
 	up to <n> candidates.  Increasing <n> above 10 will take
 	slightly longer but may produce a more accurate result.
+	An <n> of 0 will cause only exact matches to be output.
+
+--exact-match::
+	Only output exact matches (a tag directly references the
+	supplied commit).  This is a synonym for --candidates=0.
 
 --debug::
 	Verbosely display information about the searching strategy
 	being employed to standard error.  The tag name will still
 	be printed to standard out.
 
+--long::
+	Always output the long format (the tag, the number of commits
+	and the abbreviated commit name) even when it matches a tag.
+	This is useful when you want to see parts of the commit object name
+	in "describe" output, even when the commit in question happens to be
+	a tagged version.  Instead of just emitting the tag name, it will
+	describe such a commit as v1.2-0-deadbeef (0th commit since tag v1.2
+	that points at object deadbeef....).
+
+--match <pattern>::
+	Only consider tags matching the given pattern (can be used to avoid
+	leaking private tags made from the repository).
+
 EXAMPLES
 --------
 
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index bd625ab..c29a4f8 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -385,6 +385,9 @@
 Omitting the `from` command in the first commit of a new branch
 will cause fast-import to create that commit with no ancestor. This
 tends to be desired only for the initial commit of a project.
+If the frontend creates all files from scratch when making a new
+branch, a `merge` command may be used instead of `from` to start
+the commit with an empty tree.
 Omitting the `from` command on existing branches is usually desired,
 as the current commit on that branch is automatically assumed to
 be the first ancestor of the new commit.
@@ -427,13 +430,15 @@
 
 `merge`
 ^^^^^^^
-Includes one additional ancestor commit, and makes the current
-commit a merge commit.  An unlimited number of `merge` commands per
+Includes one additional ancestor commit.  If the `from` command is
+omitted when creating a new branch, the first `merge` commit will be
+the first ancestor of the current commit, and the branch will start
+out with no files.  An unlimited number of `merge` commands per
 commit are permitted by fast-import, thereby establishing an n-way merge.
 However Git's other tools never create commits with more than 15
 additional ancestors (forming a 16-way merge).  For this reason
 it is suggested that frontends do not use more than 15 `merge`
-commands per commit.
+commands per commit; 16, if starting a new, empty branch.
 
 Here `<committish>` is any of the commit specification expressions
 also accepted by `from` (see above).
@@ -805,6 +810,93 @@
 inform the reader when the `checkpoint` has been completed and it
 can safely access the refs that fast-import updated.
 
+Crash Reports
+-------------
+If fast-import is supplied invalid input it will terminate with a
+non-zero exit status and create a crash report in the top level of
+the Git repository it was importing into.  Crash reports contain
+a snapshot of the internal fast-import state as well as the most
+recent commands that lead up to the crash.
+
+All recent commands (including stream comments, file changes and
+progress commands) are shown in the command history within the crash
+report, but raw file data and commit messages are excluded from the
+crash report.  This exclusion saves space within the report file
+and reduces the amount of buffering that fast-import must perform
+during execution.
+
+After writing a crash report fast-import will close the current
+packfile and export the marks table.  This allows the frontend
+developer to inspect the repository state and resume the import from
+the point where it crashed.  The modified branches and tags are not
+updated during a crash, as the import did not complete successfully.
+Branch and tag information can be found in the crash report and
+must be applied manually if the update is needed.
+
+An example crash:
+
+====
+	$ cat >in <<END_OF_INPUT
+	# my very first test commit
+	commit refs/heads/master
+	committer Shawn O. Pearce <spearce> 19283 -0400
+	# who is that guy anyway?
+	data <<EOF
+	this is my commit
+	EOF
+	M 644 inline .gitignore
+	data <<EOF
+	.gitignore
+	EOF
+	M 777 inline bob
+	END_OF_INPUT
+
+	$ git-fast-import <in
+	fatal: Corrupt mode: M 777 inline bob
+	fast-import: dumping crash report to .git/fast_import_crash_8434
+
+	$ cat .git/fast_import_crash_8434
+	fast-import crash report:
+	    fast-import process: 8434
+	    parent process     : 1391
+	    at Sat Sep 1 00:58:12 2007
+
+	fatal: Corrupt mode: M 777 inline bob
+
+	Most Recent Commands Before Crash
+	---------------------------------
+	  # my very first test commit
+	  commit refs/heads/master
+	  committer Shawn O. Pearce <spearce> 19283 -0400
+	  # who is that guy anyway?
+	  data <<EOF
+	  M 644 inline .gitignore
+	  data <<EOF
+	* M 777 inline bob
+
+	Active Branch LRU
+	-----------------
+	    active_branches = 1 cur, 5 max
+
+	  pos  clock name
+	  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	   1)      0 refs/heads/master
+
+	Inactive Branches
+	-----------------
+	refs/heads/master:
+	  status      : active loaded dirty
+	  tip commit  : 0000000000000000000000000000000000000000
+	  old tree    : 0000000000000000000000000000000000000000
+	  cur tree    : 0000000000000000000000000000000000000000
+	  commit clock: 0
+	  last pack   :
+
+
+	-------------------
+	END OF CRASH REPORT
+====
+
 Tips and Tricks
 ---------------
 The following tips and tricks have been collected from various
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 2b8ffe5..57598eb 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
+'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
 
 DESCRIPTION
 -----------
@@ -45,6 +45,12 @@
 	Spend extra cycles to minimize the number of objects to be sent.
 	Use it on slower connection.
 
+\--include-tag::
+	If the remote side supports it, annotated tags objects will
+	be downloaded on the same connection as the other objects if
+	the object the tag references is downloaded.  The caller must
+	otherwise determine the tags this option made available.
+
 \--upload-pack=<git-upload-pack>::
 	Use this to specify the path to 'git-upload-pack' on the
 	remote side, if is not found on your $PATH.
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index ee0f053..4a53096 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -25,7 +25,7 @@
 information) will be preserved.
 
 The command will only rewrite the _positive_ refs mentioned in the
-command line (i.e. if you pass 'a..b', only 'b' will be rewritten).
+command line (e.g. if you pass 'a..b', only 'b' will be rewritten).
 If you specify no filters, the commits will be recommitted without any
 changes, which would normally have no effect.  Nevertheless, this may be
 useful in the future for compensating for some git bugs or such,
@@ -42,7 +42,7 @@
 if different from the rewritten ones, will be stored in the namespace
 'refs/original/'.
 
-Note that since this operation is extensively I/O expensive, it might
+Note that since this operation is very I/O expensive, it might
 be a good idea to redirect the temporary directory off-disk with the
 '-d' option, e.g. on tmpfs.  Reportedly the speedup is very noticeable.
 
@@ -51,14 +51,15 @@
 ~~~~~~~
 
 The filters are applied in the order as listed below.  The <command>
-argument is always evaluated in shell using the 'eval' command (with the
-notable exception of the commit filter, for technical reasons).
+argument is always evaluated in the shell context using the 'eval' command
+(with the notable exception of the commit filter, for technical reasons).
 Prior to that, the $GIT_COMMIT environment variable will be set to contain
 the id of the commit being rewritten.  Also, GIT_AUTHOR_NAME,
 GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
-and GIT_COMMITTER_DATE are set according to the current commit. If any
-evaluation of <command> returns a non-zero exit status, the whole operation
-will be aborted.
+and GIT_COMMITTER_DATE are set according to the current commit.  The values
+of these variables after the filters have run, are used for the new commit.
+If any evaluation of <command> returns a non-zero exit status, the whole
+operation will be aborted.
 
 A 'map' function is available that takes an "original sha1 id" argument
 and outputs a "rewritten sha1 id" if the commit has been already
@@ -71,9 +72,9 @@
 -------
 
 --env-filter <command>::
-	This is the filter for modifying the environment in which
-	the commit will be performed.  Specifically, you might want
-	to rewrite the author/committer name/email/time environment
+	This filter may be used if you only need to modify the environment
+	in which the commit will be performed.  Specifically, you might
+	want to rewrite the author/committer name/email/time environment
 	variables (see linkgit:git-commit[1] for details).  Do not forget
 	to re-export the variables.
 
@@ -149,7 +150,7 @@
 -d <directory>::
 	Use this option to set the path to the temporary directory used for
 	rewriting.  When applying a tree filter, the command needs to
-	temporary checkout the tree to some directory, which may consume
+	temporarily check out the tree to some directory, which may consume
 	considerable space in case of large projects.  By default it
 	does this in the '.git-rewrite/' directory but you can override
 	that choice by this parameter.
@@ -176,6 +177,10 @@
 git filter-branch --tree-filter 'rm filename' HEAD
 -------------------------------------------------------
 
+However, if the file is absent from the tree of some commit,
+a simple `rm filename` will fail for that tree and commit.
+Thus you may instead want to use `rm -f filename` as the script.
+
 A significantly faster version:
 
 --------------------------------------------------------------------------
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f1f90cc..6325ff9 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -8,9 +8,8 @@
 SYNOPSIS
 --------
 [verse]
-'git-for-each-ref' [--count=<count>]\*
-                   [--shell|--perl|--python|--tcl]
-                   [--sort=<key>]\* [--format=<format>] [<pattern>]
+'git-for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
+		   [--sort=<key>]\* [--format=<format>] [<pattern>...]
 
 DESCRIPTION
 -----------
@@ -32,8 +31,9 @@
 <key>::
 	A field name to sort on.  Prefix `-` to sort in
 	descending order of the value.  When unspecified,
-	`refname` is used.  More than one sort keys can be
-	given.
+	`refname` is used.  You may use the --sort=<key> option
+	multiple times, in which case the last key becomes the primary
+	key.
 
 <format>::
 	A string that interpolates `%(fieldname)` from the
@@ -48,9 +48,10 @@
 	`%09` to `\t` (TAB) and `%0a` to `\n` (LF).
 
 <pattern>::
-	If given, the name of the ref is matched against this
-	using fnmatch(3).  Refs that do not match the pattern
-	are not shown.
+	If one or more patterns are given, only refs are shown that
+	match againt at least one pattern, either using fnmatch(3) or
+	literally, in the latter case matching completely or from the
+	beginning up to a slash.
 
 --shell, --perl, --python, --tcl::
 	If given, strings that substitute `%(fieldname)`
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 651efe6..b5207b7 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -10,13 +10,15 @@
 --------
 [verse]
 'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
-                   [--attach[=<boundary>] | --inline[=<boundary>]]
-                   [-s | --signoff] [<common diff options>]
-                   [-n | --numbered | -N | --no-numbered]
-                   [--start-number <n>] [--numbered-files]
-                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
-                   [--ignore-if-in-upstream]
-                   [--subject-prefix=Subject-Prefix]
+		   [--attach[=<boundary>] | --inline[=<boundary>]]
+		   [-s | --signoff] [<common diff options>]
+		   [-n | --numbered | -N | --no-numbered]
+		   [--start-number <n>] [--numbered-files]
+		   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+		   [--ignore-if-in-upstream]
+		   [--subject-prefix=Subject-Prefix]
+		   [--cc=<email>]
+		   [--cover-letter]
 		   [ <since> | <revision range> ]
 
 DESCRIPTION
@@ -135,6 +137,15 @@
 	allows for useful naming of a patch series, and can be
 	combined with the --numbered option.
 
+--cc=<email>::
+	Add a "Cc:" header to the email headers. This is in addition
+	to any configured headers, and may be used multiple times.
+
+--cover-letter::
+	Generate a cover letter template.  You still have to fill in
+	a description, but the shortlog and the diffstat will be
+	generated for you.
+
 --suffix=.<sfx>::
 	Instead of using `.patch` as the suffix for generated
 	filenames, use specified suffix.  A common alternative is
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 4b2dfef..b6b5ce1 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-gc' [--prune] [--aggressive] [--auto]
+'git-gc' [--aggressive] [--auto] [--quiet]
 
 DESCRIPTION
 -----------
@@ -19,23 +19,19 @@
 
 Users are encouraged to run this task on a regular basis within
 each repository to maintain good disk space utilization and good
-operating performance. Some git commands may automatically run
-`git-gc`; see the `--auto` flag below for details.
+operating performance.
+
+Some git commands may automatically run `git-gc`; see the `--auto` flag
+below for details. If you know what you're doing and all you want is to
+disable this behavior permanently without further considerations, just do:
+
+----------------------
+$ git config --global gc.auto 0
+----------------------
 
 OPTIONS
 -------
 
---prune::
-	Usually `git-gc` packs refs, expires old reflog entries,
-	packs loose objects,
-	and removes old 'rerere' records.  Removal
-	of unreferenced loose objects is an unsafe operation
-	while other git operations are in progress, so it is not
-	done by default.  Pass this option if you want it, and only
-	when you know nobody else is creating new objects in the
-	repository at the same time (e.g. never use this option
-	in a cron script).
-
 --aggressive::
 	Usually 'git-gc' runs very quickly while providing good disk
 	space utilization and performance.  This option will cause
@@ -63,6 +59,9 @@
 `git-repack`. Setting `gc.autopacklimit` to 0 disables
 automatic consolidation of packs.
 
+--quiet::
+	Suppress all progress reports.
+
 Configuration
 -------------
 
@@ -101,6 +100,25 @@
 the documentation for the --window' option in linkgit:git-repack[1] for
 more details.  This defaults to 10.
 
+The optional configuration variable 'gc.pruneExpire' controls how old
+the unreferenced loose objects have to be before they are pruned.  The
+default is "2 weeks ago".
+
+
+Notes
+-----
+
+git-gc tries very hard to be safe about the garbage it collects. In
+particular, it will keep not only objects referenced by your current set
+of branches and tags, but also objects referenced by the index, remote
+tracking branches, refs saved by linkgit:git-filter-branch[1] in
+refs/original/, or reflogs (which may references commits in branches
+that were later amended or rewound).
+
+If you are expecting some objects to be collected and they aren't, check
+all of those locations and decide whether it makes sense in your case to
+remove those references.
+
 See Also
 --------
 linkgit:git-prune[1]
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index f3cb24f..a97f055 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -75,9 +75,11 @@
 -n::
 	Prefix the line number to matching lines.
 
--l | --files-with-matches | -L | --files-without-match::
+-l | --files-with-matches | --name-only | -L | --files-without-match::
 	Instead of showing every matched line, show only the
 	names of files that contain (or do not contain) matches.
+	For better compatibility with git-diff, --name-only is a
+	synonym for --files-with-matches.
 
 -c | --count::
 	Instead of showing every matched line, show the number of
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index fb77ca3..be2ae53 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -33,45 +33,34 @@
 	option supersedes any other option.
 
 -i|--info::
-	Use the 'info' program to display the manual page, instead of
-	the 'man' program that is used by default.
+	Display manual page for the command in the 'info' format. The
+	'info' program will be used for that purpose.
 
 -m|--man::
-	Use the 'man' program to display the manual page. This may be
-	used to override a value set in the 'help.format'
-	configuration variable.
+	Display manual page for the command in the 'man' format. This
+	option may be used to override a value set in the
+	'help.format' configuration variable.
++
+By default the 'man' program will be used to display the manual page,
+but the 'man.viewer' configuration variable may be used to choose
+other display programs (see below).
 
 -w|--web::
-	Use a web browser to display the HTML manual page, instead of
-	the 'man' program that is used by default.
+	Display manual page for the command in the 'web' (HTML)
+	format. A web browser will be used for that purpose.
 +
 The web browser can be specified using the configuration variable
 'help.browser', or 'web.browser' if the former is not set. If none of
-these config variables is set, the 'git-help--browse' helper script
-(called by 'git-help') will pick a suitable default.
-+
-You can explicitly provide a full path to your preferred browser by
-setting the configuration variable 'browser.<tool>.path'. For example,
-you can configure the absolute path to firefox by setting
-'browser.firefox.path'. Otherwise, 'git-help--browse' assumes the tool
-is available in PATH.
-+
-Note that the script tries, as much as possible, to display the HTML
-page in a new tab on an already opened browser.
-+
-The following browsers are currently supported by 'git-help--browse':
-+
-* firefox (this is the default under X Window when not using KDE)
-* iceweasel
-* konqueror (this is the default under KDE)
-* w3m (this is the default outside X Window)
-* links
-* lynx
-* dillo
+these config variables is set, the 'git-web--browse' helper script
+(called by 'git-help') will pick a suitable default. See
+linkgit:git-web--browse[1] for more information about this.
 
 CONFIGURATION VARIABLES
 -----------------------
 
+help.format
+~~~~~~~~~~~
+
 If no command line option is passed, the 'help.format' configuration
 variable will be checked. The following values are supported for this
 variable; they make 'git-help' behave as their corresponding command
@@ -79,15 +68,47 @@
 
 * "man" corresponds to '-m|--man',
 * "info" corresponds to '-i|--info',
-* "web" or "html" correspond to '-w|--web',
+* "web" or "html" correspond to '-w|--web'.
+
+help.browser, web.browser and browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
 be checked if the 'web' format is chosen (either by command line
 option or configuration variable). See '-w|--web' in the OPTIONS
-section above.
+section above and linkgit:git-web--browse[1].
 
-Note that these configuration variables should probably be set using
-the '--global' flag, for example like this:
+man.viewer
+~~~~~~~~~~
+
+The 'man.viewer' config variable will be checked if the 'man' format
+is chosen. Only the following values are currently supported:
+
+* "man": use the 'man' program as usual,
+* "woman": use 'emacsclient' to launch the "woman" mode in emacs
+(this only works starting with emacsclient versions 22),
+* "konqueror": use a man KIO slave in konqueror.
+
+Multiple values may be given to this configuration variable. Their
+corresponding programs will be tried in the order listed in the
+configuration file.
+
+For example, this configuration:
+
+	[man]
+		viewer = konqueror
+		viewer = woman
+
+will try to use konqueror first. But this may fail (for example if
+DISPLAY is not set) and in that case emacs' woman mode will be tried.
+
+If everything fails the 'man' program will be tried anyway.
+
+Note about git config --global
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Note that all these configuration variables should probably be set
+using the '--global' flag, for example like this:
 
 ------------------------------------------------
 $ git config --global help.format web
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 72b5d00..a7825b6 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -75,6 +75,9 @@
 	to force the version for the generated pack index, and to force
 	64-bit index entries on objects located above the given offset.
 
+--strict::
+	Die, if the pack contains broken objects or links.
+
 
 Note
 ----
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index 841e8fa..51f1532 100644
--- a/Documentation/git-instaweb.txt
+++ b/Documentation/git-instaweb.txt
@@ -38,10 +38,11 @@
 	The port number to bind the httpd to.  (Default: 1234)
 
 -b|--browser::
-
-	The web browser command-line to execute to view the gitweb page.
-	If blank, the URL of the gitweb instance will be printed to
-	stdout.  (Default: 'firefox')
+	The web browser that should be used to view the gitweb
+	page. This will be passed to the 'git-web--browse' helper
+	script along with the URL of the gitweb instance. See
+	linkgit:git-web--browse[1] for more information about this. If
+	the script fails, the URL will be printed to stdout.
 
 --start::
 	Start the httpd instance and exit.  This does not generate
@@ -72,7 +73,8 @@
 -----------------------------------------------------------------------
 
 If the configuration variable 'instaweb.browser' is not set,
-'web.browser' will be used instead if it is defined.
+'web.browser' will be used instead if it is defined. See
+linkgit:git-web--browse[1] for more information about this.
 
 Author
 ------
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index 5d816d0..19ee017 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
+'git-merge-index' [-o] [-q] <merge-program> (-a | [--] <file>\*)
 
 DESCRIPTION
 -----------
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 50f106e..8ed4494 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -12,12 +12,12 @@
 DESCRIPTION
 -----------
 
-Use 'git mergetool' to run one of several merge utilities to resolve
+Use `git mergetool` to run one of several merge utilities to resolve
 merge conflicts.  It is typically run after linkgit:git-merge[1].
 
 If one or more <file> parameters are given, the merge tool program will
 be run to resolve differences on each file.  If no <file> names are
-specified, 'git mergetool' will run the merge tool program on every file
+specified, `git mergetool` will run the merge tool program on every file
 with merge conflicts.
 
 OPTIONS
@@ -27,16 +27,38 @@
 	Valid merge tools are:
 	kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
 +
-If a merge resolution program is not specified, 'git mergetool'
-will use the configuration variable merge.tool.  If the
-configuration variable merge.tool is not set, 'git mergetool'
+If a merge resolution program is not specified, `git mergetool`
+will use the configuration variable `merge.tool`.  If the
+configuration variable `merge.tool` is not set, `git mergetool`
 will pick a suitable default.
 +
 You can explicitly provide a full path to the tool by setting the
-configuration variable mergetool.<tool>.path. For example, you
+configuration variable `mergetool.<tool>.path`. For example, you
 can configure the absolute path to kdiff3 by setting
-mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool
-is available in PATH.
+`mergetool.kdiff3.path`. Otherwise, `git mergetool` assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs
+`git mergetool` can be customized to run an alternative program
+by specifying the command line to invoke in a configration
+variable `mergetool.<tool>.cmd`.
++
+When `git mergetool` is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration
+variable) the configured command line will be invoked with `$BASE`
+set to the name of a temporary file containing the common base for
+the merge, if available; `$LOCAL` set to the name of a temporary
+file containing the contents of the file on the current branch;
+`$REMOTE` set to the name of a temporary file containing the
+contents of the file to be merged, and `$MERGED` set to the name
+of the file to which the merge tool should write the result of the
+merge resolution.
++
+If the custom merge tool correctly indicates the success of a
+merge resolution with its exit code then the configuration
+variable `mergetool.<tool>.trustExitCode` can be set to `true`.
+Otherwise, `git mergetool` will prompt the user to indicate the
+success of the resolution after the custom tool has exited.
 
 Author
 ------
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 74cc7c1..3a1be08 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -22,8 +22,9 @@
 A packed archive is an efficient way to transfer set of objects
 between two repositories, and also is an archival format which
 is efficient to access.  The packed archive format (.pack) is
-designed to be unpackable without having anything else, but for
-random access, accompanied with the pack index file (.idx).
+designed to be self contained so that it can be unpacked without
+any further information, but for fast, random access to the objects
+in the pack, a pack index file (.idx) will be generated.
 
 Placing both in the pack/ subdirectory of $GIT_OBJECT_DIRECTORY (or
 any of the directories on $GIT_ALTERNATE_OBJECT_DIRECTORIES)
@@ -73,6 +74,11 @@
 	as if all refs under `$GIT_DIR/refs` are specified to be
 	included.
 
+--include-tag::
+	Include unasked-for annotated tags if the object they
+	reference was included in the resulting packfile.  This
+	can be useful to send new tags to native git clients.
+
 --window=[N], --depth=[N]::
 	These two options affect how the objects contained in
 	the pack are stored using delta compression.  The
@@ -99,7 +105,8 @@
 --max-pack-size=<n>::
 	Maximum size of each output packfile, expressed in MiB.
 	If specified,  multiple packfiles may be created.
-	The default is unlimited.
+	The default is unlimited, unless the config variable
+	`pack.packSizeLimit` is set.
 
 --incremental::
 	This flag causes an object already in a pack ignored
@@ -176,6 +183,8 @@
 	This is meant to reduce packing time on multiprocessor machines.
 	The required amount of memory for the delta search window is
 	however multiplied by the number of threads.
+	Specifying 0 will cause git to auto-detect the number of CPU's
+	and set the number of threads accordingly.
 
 --index-version=<version>[,<offset>]::
 	This is intended to be used by the test suite only. It allows
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index f7b90a3..3405ca0 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -15,6 +15,7 @@
 -----------
 Runs `git-fetch` with the given parameters, and calls `git-merge`
 to merge the retrieved head(s) into the current branch.
+With `--rebase`, calls `git-rebase` instead of `git-merge`.
 
 Note that you can use `.` (current directory) as the
 <repository> to pull from the local repository -- this is useful
@@ -28,19 +29,14 @@
 include::merge-options.txt[]
 
 :git-pull: 1
-include::fetch-options.txt[]
-
-include::pull-fetch-param.txt[]
-
-include::urls-remotes.txt[]
-
-include::merge-strategies.txt[]
 
 \--rebase::
 	Instead of a merge, perform a rebase after fetching.  If
 	there is a remote ref for the upstream branch, and this branch
 	was rebased since last fetched, the rebase uses that information
-	to avoid rebasing non-local changes.
+	to avoid rebasing non-local changes. To make this the default
+	for branch `<name>`, set configuration `branch.<name>.rebase`
+	to `true`.
 +
 *NOTE:* This is a potentially _dangerous_ mode of operation.
 It rewrites history, which does not bode well when you
@@ -50,6 +46,14 @@
 \--no-rebase::
 	Override earlier \--rebase.
 
+include::fetch-options.txt[]
+
+include::pull-fetch-param.txt[]
+
+include::urls-remotes.txt[]
+
+include::merge-strategies.txt[]
+
 DEFAULT BEHAVIOUR
 -----------------
 
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 3128170..0585949 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -35,14 +35,15 @@
 	by the source ref, followed by a colon `:`, followed by
 	the destination ref.
 +
-The <src> side can be an
-arbitrary "SHA1 expression" that can be used as an
-argument to `git-cat-file -t`.  E.g. `master~4` (push
-four parents before the current master head).
+The <src> side represents the source branch (or arbitrary
+"SHA1 expression", such as `master~4` (four parents before the
+tip of `master` branch); see linkgit:git-rev-parse[1]) that you
+want to push.  The <dst> side represents the destination location.
 +
 The local ref that matches <src> is used
-to fast forward the remote ref that matches <dst>.  If
-the optional plus `+` is used, the remote ref is updated
+to fast forward the remote ref that matches <dst> (or, if no <dst> was
+specified, the same ref that <src> referred to locally).  If
+the optional leading plus `+` is used, the remote ref is updated
 even if it does not result in a fast forward update.
 +
 Note: If no explicit refspec is found, (that is neither
@@ -165,7 +166,8 @@
 	Find a ref that matches `master` in the source repository
 	(most likely, it would find `refs/heads/master`), and update
 	the same ref (e.g. `refs/heads/master`) in `origin` repository
-	with it.
+	with it.  If `master` did not exist remotely, it would be
+	created.
 
 git push origin :experimental::
 	Find a ref that matches `experimental` in the `origin` repository
@@ -179,9 +181,10 @@
 
 git push origin master:refs/heads/experimental::
 	Create the branch `experimental` in the `origin` repository
-	by copying the current `master` branch.  This form is usually
-	needed to create a new branch in the remote repository as
-	there is no `experimental` branch to match.
+	by copying the current `master` branch.  This form is only
+	needed to create a new branch or tag in the remote repository when
+	the local name and the remote name are different; otherwise,
+	the ref name on its own will work.
 
 Author
 ------
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 8421d1f..c95ad9f 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -50,6 +50,9 @@
 	trees that are not directly related to the current
 	working tree status into a temporary index file.
 
+-v::
+	Show the progress of checking files out.
+
 --trivial::
 	Restrict three-way merge by `git-read-tree` to happen
 	only if there is no file-level merging required, instead
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index c11c645..e0412e0 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -9,6 +9,7 @@
 --------
 [verse]
 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
+	[-s <strategy> | --strategy=<strategy>]
 	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
 	[--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
@@ -261,8 +262,7 @@
 reject the rebase if it isn't appropriate.  Please see the template
 pre-rebase hook script for an example.
 
-You must be in the top directory of your project to start (or continue)
-a rebase.  Upon completion, <branch> will be the current branch.
+Upon completion, <branch> will be the current branch.
 
 INTERACTIVE MODE
 ----------------
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index f9bba36..047e3ce 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -19,6 +19,8 @@
 git reflog expire [--dry-run] [--stale-fix] [--verbose]
 	[--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...
 
+git reflog delete ref@\{specifier\}...
+
 git reflog [show] [log-options] [<ref>]
 
 Reflog is a mechanism to record when the tip of branches are
@@ -43,6 +45,9 @@
 point to one week ago", and so on. See linkgit:git-rev-parse[1] for
 more details.
 
+To delete single entries from the reflog, use the subcommand "delete"
+and specify the _exact_ entry (e.g. ``git reflog delete master@\{2\}'').
+
 
 OPTIONS
 -------
@@ -75,6 +80,15 @@
 --all::
 	Instead of listing <refs> explicitly, prune all refs.
 
+--updateref::
+	Update the ref with the sha1 of the top reflog entry (i.e.
+	<ref>@\{0\}) after expiring or deleting.
+
+--rewrite::
+	While expiring or deleting, adjust each reflog entry to ensure
+	that the `old` sha1 field points to the `new` sha1 field of the
+	previous entry.
+
 --verbose::
 	Print extra information on screen.
 
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 3d95749..d14ab51 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -55,8 +55,11 @@
 	linkgit:git-pack-objects[1].
 
 -n::
-        Do not update the server information with
-        `git update-server-info`.
+	Do not update the server information with
+	`git update-server-info`.  This option skips
+	updating local catalog files needed to publish
+	this repository (or a direct copy of it)
+	over HTTP or FTP.  See gitlink:git-update-server-info[1].
 
 --window=[N], --depth=[N]::
 	These two options affect how the objects contained in the pack are
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 5b96eab..d80cdf5 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -20,6 +20,9 @@
 	     [ \--full-history ]
 	     [ \--not ]
 	     [ \--all ]
+	     [ \--branches ]
+	     [ \--tags ]
+	     [ \--remotes ]
 	     [ \--stdin ]
 	     [ \--quiet ]
 	     [ \--topo-order ]
@@ -31,6 +34,7 @@
 	     [ \--(author|committer|grep)=<pattern> ]
 	     [ \--regexp-ignore-case | \-i ]
 	     [ \--extended-regexp | \-E ]
+	     [ \--fixed-strings | \-F ]
 	     [ \--date={local|relative|default|iso|rfc|short} ]
 	     [ [\--objects | \--objects-edge] [ \--unpacked ] ]
 	     [ \--pretty | \--header ]
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index f02f6bb..5981c79 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -238,16 +238,18 @@
 and C are parents of commit node A.  Parent commits are ordered
 left-to-right.
 
-    G   H   I   J
-     \ /     \ /
-      D   E   F
-       \  |  / \ 
-        \ | /   |
-         \|/    |
-          B     C
-           \   /
-            \ /
-             A
+........................................
+G   H   I   J
+ \ /     \ /
+  D   E   F
+   \  |  / \
+    \ | /   |
+     \|/    |
+      B     C
+       \   /
+        \ /
+         A
+........................................
 
     A =      = A^0
     B = A^   = A^1     = A~1
@@ -325,7 +327,7 @@
 Each line of options has this format:
 
 ------------
-<opt_spec><arg_spec>? SP+ help LF
+<opt_spec><flags>* SP+ help LF
 ------------
 
 `<opt_spec>`::
@@ -334,10 +336,17 @@
 	is necessary. `h,help`, `dry-run` and `f` are all three correct
 	`<opt_spec>`.
 
-`<arg_spec>`::
-	an `<arg_spec>` tells the option parser if the option has an argument
-	(`=`), an optional one (`?` though its use is discouraged) or none
-	(no `<arg_spec>` in that case).
+`<flags>`::
+	`<flags>` are of `*`, `=`, `?` or `!`.
+	* Use `=` if the option takes an argument.
+
+	* Use `?` to mean that the option is optional (though its use is discouraged).
+
+	* Use `*` to mean that this option should not be listed in the usage
+	  generated for the `-h` argument. It's shown for `--help-all` as
+	  documented in linkgit:gitcli[5].
+
+	* Use `!` to not make the corresponding negated long option available.
 
 The remainder of the line, after stripping the spaces, is used
 as the help associated to the option.
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 0554f2b..9d0a10c 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -96,11 +96,40 @@
 	servers typically listen to smtp port 25 and ssmtp port
 	465).
 
---smtp-user, --smtp-pass::
-	Username and password for SMTP-AUTH. Defaults are the values of
-	the configuration values 'sendemail.smtpuser' and
-	'sendemail.smtppass', but see also 'sendemail.identity'.
-	If not set, authentication is not attempted.
+--smtp-user::
+	Username for SMTP-AUTH. In place of this option, the following
+	configuration variables can be specified:
++
+--
+		* sendemail.smtpuser
+		* sendemail.<identity>.smtpuser (see sendemail.identity).
+--
++
+However, --smtp-user always overrides these variables.
++
+If a username is not specified (with --smtp-user or a
+configuration variable), then authentication is not attempted.
+
+--smtp-pass::
+	Password for SMTP-AUTH. The argument is optional: If no
+	argument is specified, then the empty string is used as
+	the password.
++
+In place of this option, the following configuration variables
+can be specified:
++
+--
+		* sendemail.smtppass
+		* sendemail.<identity>.smtppass (see sendemail.identity).
+--
++
+However, --smtp-pass always overrides these variables.
++
+Furthermore, passwords need not be specified in configuration files
+or on the command line. If a username has been specified (with
+--smtp-user or a configuration variable), but no password has been
+specified (with --smtp-pass or a configuration variable), then the
+user is prompted for a password while the input is masked for privacy.
 
 --smtp-ssl::
 	If set, connects to the SMTP server using SSL.
@@ -117,6 +146,17 @@
         Default is the value of 'sendemail.suppressfrom' configuration value;
         if that is unspecified, default to --no-suppress-from.
 
+--suppress-cc::
+	Specify an additional category of recipients to suppress the
+	auto-cc of.  'self' will avoid including the sender, 'author' will
+	avoid including the patch author, 'cc' will avoid including anyone
+	mentioned in Cc lines in the patch, 'sob' will avoid including
+	anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid
+	running the --cc-cmd.  'all' will suppress all auto cc values.
+	Default is the value of 'sendemail.suppresscc' configuration value;
+	if that is unspecified, default to 'self' if --suppress-from is
+	specified, as well as 'sob' if --no-signed-off-cc is specified.
+
 --thread, --no-thread::
 	If this is set, the In-Reply-To header will be set on each email sent.
 	If disabled with "--no-thread", no emails will have the In-Reply-To
@@ -176,6 +216,9 @@
 sendemail.smtpserver::
 	Default SMTP server to use.
 
+sendemail.smtpserverport::
+	Default SMTP server port to use.
+
 sendemail.smtpuser::
 	Default SMTP-AUTH username.
 
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index dccf0e2..29ed0ac 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -79,8 +79,6 @@
 -------------
 Documentation by David Greaves, Petr Baudis and the git-list <git@vger.kernel.org>.
 
-This manual page is a stub. You can help the git documentation by expanding it.
-
 GIT
 ---
 Part of the linkgit:git[7] suite
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 48e6f5a..8dc35d4 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' (list | show [<stash>] | apply [<stash>] | clear | drop [<stash>] | pop [<stash>])
 'git-stash' [save [<message>]]
 
 DESCRIPTION
@@ -85,6 +85,17 @@
 	Remove all the stashed states. Note that those states will then
 	be subject to pruning, and may be difficult or impossible to recover.
 
+drop [<stash>]::
+
+	Remove a single stashed state from the stash list. When no `<stash>`
+	is given, it removes the latest one. i.e. `stash@\{0}`
+
+pop [<stash>]::
+
+	Remove a single stashed state from the stash list and apply on top
+	of the current working tree state. When no `<stash>` is given,
+	`stash@\{0}` is assumed. See also `apply`.
+
 
 DISCUSSION
 ----------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index e818e6e..6ffd896 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -12,14 +12,16 @@
 'git-submodule' [--quiet] add [-b branch] [--] <repository> [<path>]
 'git-submodule' [--quiet] status [--cached] [--] [<path>...]
 'git-submodule' [--quiet] [init|update] [--] [<path>...]
+'git-submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
 
 
 COMMANDS
 --------
 add::
 	Add the given repository as a submodule at the given path
-	to the changeset to be committed next.  In particular, the
-	repository is cloned at the specified path, added to the
+	to the changeset to be committed next.  If path is a valid
+	repository within the project, it is added as is. Otherwise,
+	repository is cloned at the specified path. path is added to the
 	changeset and registered in .gitmodules.   If no path is
 	specified, the path is deduced from the repository specification.
 	If the repository url begins with ./ or ../, it is stored as
@@ -46,6 +48,11 @@
 	checkout the commit specified in the index of the containing repository.
 	This will make the submodules HEAD be detached.
 
+summary::
+	Show commit summary between the given commit (defaults to HEAD) and
+	working tree/index. For a submodule in question, a series of commits
+	in the submodule between the given super project commit and the
+	index or working tree (switched by --cached) are shown.
 
 OPTIONS
 -------
@@ -56,9 +63,16 @@
 	Branch of repository to add as submodule.
 
 --cached::
-	Display the SHA-1 stored in the index, not the SHA-1 of the currently
-	checked out submodule commit. This option is only valid for the
-	status command.
+	This option is only valid for status and summary commands.  These
+	commands typically use the commit found in the submodule HEAD, but
+	with this option, the commit stored in the index is used instead.
+
+-n, --summary-limit::
+	This option is only valid for the summary command.
+	Limit the summary size (number of commits shown in total).
+	Giving 0 will disable the summary; a negative number means unlimited
+	(the default). This limit only applies to modified submodules. The
+	size is always limited to 1 for added/deleted/typechanged submodules.
 
 <path>::
 	Path to submodule(s). When specified this will restrict the command
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 115b8be..bec9acc 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -165,6 +165,13 @@
 +
 Any other arguments are passed directly to `git log'
 
+'blame'::
+       Show what revision and author last modified each line of a file. This is
+       identical to `git blame', but SVN revision numbers are shown instead of git
+       commit hashes.
++
+All arguments are passed directly to `git blame'.
+
 --
 'find-rev'::
 	When given an SVN revision number of the form 'rN', returns the
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index b729595..9712392 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -26,6 +26,9 @@
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
+If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
+are absent, `-a` is implied.
+
 Otherwise just the SHA1 object name of the commit object is
 written (i.e. a lightweight tag).
 
@@ -68,10 +71,14 @@
 	Use the given tag message (instead of prompting).
 	If multiple `-m` options are given, there values are
 	concatenated as separate paragraphs.
+	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
+	is given.
 
 -F <file>::
 	Take the tag message from the given file.  Use '-' to
 	read the message from the standard input.
+	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
+	is given.
 
 CONFIGURATION
 -------------
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index db019a2..ba2a157 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -32,11 +32,11 @@
 -------------
 When specifying the -v option the format used is:
 
-	SHA1 type size offset-in-packfile
+	SHA1 type size size-in-pack-file offset-in-packfile
 
 for objects that are not deltified in the pack, and
 
-	SHA1 type size offset-in-packfile depth base-SHA1
+	SHA1 type size size-in-packfile offset-in-packfile depth base-SHA1
 
 for objects that are deltified.
 
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
new file mode 100644
index 0000000..ddbae5b
--- /dev/null
+++ b/Documentation/git-web--browse.txt
@@ -0,0 +1,99 @@
+git-web--browse(1)
+==================
+
+NAME
+----
+git-web--browse - git helper script to launch a web browser
+
+SYNOPSIS
+--------
+'git-web--browse' [OPTIONS] URL/FILE ...
+
+DESCRIPTION
+-----------
+
+This script tries, as much as possible, to display the URLs and FILEs
+that are passed as arguments, as HTML pages in new tabs on an already
+opened web browser.
+
+The following browsers (or commands) are currently supported:
+
+* firefox (this is the default under X Window when not using KDE)
+* iceweasel
+* konqueror (this is the default under KDE)
+* w3m (this is the default outside graphical environments)
+* links
+* lynx
+* dillo
+* open (this is the default under Mac OS X GUI)
+
+Custom commands may also be specified.
+
+OPTIONS
+-------
+-b BROWSER|--browser=BROWSER::
+	Use the specified BROWSER. It must be in the list of supported
+	browsers.
+
+-t BROWSER|--tool=BROWSER::
+	Same as above.
+
+-c CONF.VAR|--config=CONF.VAR::
+	CONF.VAR is looked up in the git config files. If it's set,
+	then its value specify the browser that should be used.
+
+CONFIGURATION VARIABLES
+-----------------------
+
+CONF.VAR (from -c option) and web.browser
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The web browser can be specified using a configuration variable passed
+with the -c (or --config) command line option, or the 'web.browser'
+configuration variable if the former is not used.
+
+browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~
+
+You can explicitly provide a full path to your preferred browser by
+setting the configuration variable 'browser.<tool>.path'. For example,
+you can configure the absolute path to firefox by setting
+'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool
+is available in PATH.
+
+browser.<tool>.cmd
+~~~~~~~~~~~~~~~~~~
+
+When the browser, specified by options or configuration variables, is
+not among the supported ones, then the corresponding
+'browser.<tool>.cmd' configuration variable will be looked up. If this
+variable exists then "git web--browse" will treat the specified tool
+as a custom command and will use a shell eval to run the command with
+the URLs passed as arguments.
+
+Note about git config --global
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Note that these configuration variables should probably be set using
+the '--global' flag, for example like this:
+
+------------------------------------------------
+$ git config --global web.browser firefox
+------------------------------------------------
+
+as they are probably more user specific than repository specific.
+See linkgit:git-config[1] for more information about this.
+
+Author
+------
+Written by Christian Couder <chriscool@tuxfamily.org> and the git-list
+<git@vger.kernel.org>, based on git-mergetool by Theodore Y. Ts'o.
+
+Documentation
+-------------
+Documentation by Christian Couder <chriscool@tuxfamily.org> and the
+git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index 54947b6..a6e7bd4 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -38,11 +38,6 @@
 	Show git internal diff output, but for the whole tree,
 	not just the top level.
 
---pretty=<format>::
-	Controls the output format for the commit logs.
-	<format> can be one of 'raw', 'medium', 'short', 'full',
-	and 'oneline'.
-
 -m::
 	By default, differences for merge commits are not shown.
 	With this flag, show differences to that commit from all
@@ -51,6 +46,10 @@
 However, it is not very useful in general, although it
 *is* useful on a file-by-file basis.
 
+include::pretty-options.txt[]
+
+include::pretty-formats.txt[]
+
 Examples
 --------
 git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 17aee93..615c307 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,25 @@
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.5.4/git.html[documentation for release 1.5.4]
+* link:v1.5.5/git.html[documentation for release 1.5.5]
 
 * release notes for
+  link:RelNotes-1.5.5.4.txt[1.5.5.4],
+  link:RelNotes-1.5.5.3.txt[1.5.5.3],
+  link:RelNotes-1.5.5.2.txt[1.5.5.2],
+  link:RelNotes-1.5.5.1.txt[1.5.5.1],
+  link:RelNotes-1.5.5.txt[1.5.5].
+
+* link:v1.5.5.4/git.html[documentation for release 1.5.5.4]
+
+* link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
+
+* release notes for
+  link:RelNotes-1.5.4.5.txt[1.5.4.5],
+  link:RelNotes-1.5.4.4.txt[1.5.4.4],
+  link:RelNotes-1.5.4.3.txt[1.5.4.3],
+  link:RelNotes-1.5.4.2.txt[1.5.4.2],
+  link:RelNotes-1.5.4.1.txt[1.5.4.1],
   link:RelNotes-1.5.4.txt[1.5.4].
 
 * link:v1.5.3.8/git.html[documentation for release 1.5.3.8]
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 970db0c..04ca63c 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -140,6 +140,26 @@
 converted to LF upon checkin, but there is no conversion done
 upon checkout.
 
+If `core.safecrlf` is set to "true" or "warn", git verifies if
+the conversion is reversible for the current setting of
+`core.autocrlf`.  For "true", git rejects irreversible
+conversions; for "warn", git only prints a warning but accepts
+an irreversible conversion.  The safety triggers to prevent such
+a conversion done to the files in the work tree, but there are a
+few exceptions.  Even though...
+
+- "git add" itself does not touch the files in the work tree, the
+  next checkout would, so the safety triggers;
+
+- "git apply" to update a text file with a patch does touch the files
+  in the work tree, but the operation is about text files and CRLF
+  conversion is about fixing the line ending inconsistencies, so the
+  safety does not trigger;
+
+- "git diff" itself does not touch the files in the work tree, it is
+  often run to inspect the changes you intend to next "git add".  To
+  catch potential problems early, safety triggers.
+
 
 `ident`
 ^^^^^^^
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index ef8a272..613dca0 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -69,6 +69,13 @@
    included again.  If a negated pattern matches, this will
    override lower precedence patterns sources.
 
+ - If the pattern ends with a slash, it is removed for the
+   purpose of the following description, but it would only find
+   a match with a directory.  In other words, `foo/` will match a
+   directory `foo` and paths underneath it, but will not match a
+   regular file or a symbolic link `foo` (this is consistent
+   with the way how pathspec works in general in git).
+
  - If the pattern does not contain a slash '/', git treats it as
    a shell glob pattern and checks for a match against the
    pathname without leading directories.
diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt
index ab4caf4..51b6353 100644
--- a/Documentation/glossary.txt
+++ b/Documentation/glossary.txt
@@ -45,9 +45,12 @@
 	"changesets" with git.
 
 [[def_checkout]]checkout::
-	The action of updating the <<def_working_tree,working tree>> to a
-	<<def_revision,revision>> which was stored in the
-	<<def_object_database,object database>>.
+	The action of updating all or part of the
+	<<def_working_tree,working tree>> with a <<def_tree_object,tree object>>
+	or <<def_blob_object,blob>> from the
+	<<def_object_database,object database>>, and updating the
+	<<def_index,index>> and <<def_HEAD,HEAD>> if the whole working tree has
+	been pointed at a new <<def_branch,branch>>.
 
 [[def_cherry-picking]]cherry-picking::
 	In <<def_SCM,SCM>> jargon, "cherry pick" means to choose a subset of
diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt
index f110162..76b8d77 100644
--- a/Documentation/hooks.txt
+++ b/Documentation/hooks.txt
@@ -61,6 +61,35 @@
 of lines with trailing whitespaces and aborts the commit when
 such a line is found.
 
+All the `git-commit` hooks are invoked with the environment
+variable `GIT_EDITOR=:` if the command will not bring up an editor
+to modify the commit message.
+
+prepare-commit-msg
+------------------
+
+This hook is invoked by `git-commit` right after preparing the
+default log message, and before the editor is started.
+
+It takes one to three parameters.  The first is the name of the file
+that the commit log message.  The second is the source of the commit
+message, and can be: `message` (if a `\-m` or `\-F` option was
+given); `template` (if a `\-t` option was given or the
+configuration option `commit.template` is set); `merge` (if the
+commit is a merge or a `.git/MERGE_MSG` file exists); `squash`
+(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by
+a commit SHA1 (if a `\-c`, `\-C` or `\--amend` option was given).
+
+If the exit status is non-zero, `git-commit` will abort.
+
+The purpose of the hook is to edit the message file in place, and
+it is not suppressed by the `\--no-verify` option.  A non-zero exit
+means a failure of the hook and aborts the commit.  It should not
+be used as replacement for pre-commit hook.
+
+The sample `prepare-commit-msg` hook that comes with git comments
+out the `Conflicts:` part of a merge's commit message.
+
 commit-msg
 ----------
 
diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt
index 8eadc20..b7d09c1 100644
--- a/Documentation/howto/setup-git-server-over-http.txt
+++ b/Documentation/howto/setup-git-server-over-http.txt
@@ -1,5 +1,5 @@
 From: Rutger Nijlunsing <rutger@nospam.com>
-Subject: Setting up a git repository which can be pushed into and pulled from over HTTP.
+Subject: Setting up a git repository which can be pushed into and pulled from over HTTP(S).
 Date: Thu, 10 Aug 2006 22:00:26 +0200
 
 Since Apache is one of those packages people like to compile
@@ -40,9 +40,13 @@
 
 - have permissions to chown a directory
 
-- have git installed at the server _and_ client
+- have git installed on the client, and
 
-In effect, this probably means you're going to be root.
+- either have git installed on the server or have a webdav client on
+  the client.
+
+In effect, this means you're going to be root, or that you're using a
+preconfigured WebDAV server.
 
 
 Step 1: setup a bare GIT repository
@@ -50,9 +54,9 @@
 
 At the time of writing, git-http-push cannot remotely create a GIT
 repository. So we have to do that at the server side with git. Another
-option would be to generate an empty repository at the client and copy
-it to the server with WebDAV. But then you're probably the first to
-try that out :)
+option is to generate an empty bare repository at the client and copy
+it to the server with a WebDAV client (which is the only option if Git
+is not installed on the server).
 
 Create the directory under the DocumentRoot of the directories served
 by Apache. As an example we take /usr/local/apache2, but try "grep
@@ -169,7 +173,9 @@
 
    Most tests should pass.
 
-A command line tool to test WebDAV is cadaver.
+A command line tool to test WebDAV is cadaver. If you prefer GUIs, for
+example, konqueror can open WebDAV URLs as "webdav://..." or
+"webdavs://...".
 
 If you're into Windows, from XP onwards Internet Explorer supports
 WebDAV. For this, do Internet Explorer -> Open Location ->
@@ -179,8 +185,9 @@
 Step 3: setup the client
 ------------------------
 
-Make sure that you have HTTP support, i.e. your git was built with curl.
-The easiest way to check is to look for the executable 'git-http-push'.
+Make sure that you have HTTP support, i.e. your git was built with
+curl (version more recent than 7.10). The command 'git http-push' with
+no argument should display a usage message.
 
 Then, add the following to your $HOME/.netrc (you can do without, but will be
 asked to input your password a _lot_ of times):
@@ -197,10 +204,10 @@
 
 To check whether all is OK, do:
 
-   curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/
+   curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/HEAD
 
-...this should give a directory listing in HTML of /var/www/my-new-repo.git .
-
+...this should give something like 'ref: refs/heads/master', which is
+the content of the file HEAD on the server.
 
 Now, add the remote in your existing repository which contains the project
 you want to export:
@@ -225,6 +232,15 @@
 defined with git-config.
 
 
+Using a proxy:
+--------------
+
+If you have to access the WebDAV server from behind an HTTP(S) proxy,
+set the variable 'all_proxy' to 'http://proxy-host.com:port', or
+'http://login-on-proxy:passwd-on-proxy@proxy-host.com:port'. See 'man
+curl' for details.
+
+
 Troubleshooting:
 ----------------
 
@@ -248,9 +264,14 @@
 
   On Debian: Read /var/log/apache2/error.log instead.
 
+If you access HTTPS locations, git may fail verifying the SSL
+certificate (this is return code 60). Setting http.sslVerify=false can
+help diagnosing the problem, but removes security checks.
+
 
 Debian References: http://www.debian-administration.org/articles/285
 
 Authors
   Johannes Schindelin <Johannes.Schindelin@gmx.de>
   Rutger Nijlunsing <git@wingding.demon.nl>
+  Matthieu Moy <Matthieu.Moy@imag.fr>
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index 7df0266..1276f85 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -33,3 +33,10 @@
 	merge is always the current branch head.  It is meant to
 	be used to supersede old development history of side
 	branches.
+
+subtree::
+	This is a modified recursive strategy. When merging trees A and
+	B, if B corresponds to a subtree of A, B is first adjusted to
+	match the tree structure of A, instead of reading the trees at
+	the same level. This adjustment is also done to the common
+	ancestor tree.
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 973d8dd..6d66c74 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -4,6 +4,9 @@
 	where '<format>' can be one of 'oneline', 'short', 'medium',
 	'full', 'fuller', 'email', 'raw' and 'format:<string>'.
 	When omitted, the format defaults to 'medium'.
++
+Note: you can specify the default pretty format in the repository
+configuration (see linkgit:git-config[1]).
 
 --abbrev-commit::
 	Instead of showing the full 40-byte hexadecimal commit object
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index a8138e2..9cd6771 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -13,7 +13,7 @@
 
 	Synonym for `--date=relative`.
 
---date={relative,local,default,iso,rfc}::
+--date={relative,local,default,iso,rfc,short}::
 
 	Only takes effect for dates shown in human-readable format, such
 	as when using "--pretty".
@@ -130,9 +130,11 @@
 
 	Show commits older than a specific date.
 
+ifdef::git-rev-list[]
 --max-age='timestamp', --min-age='timestamp'::
 
 	Limit the commits output to specified time range.
+endif::git-rev-list[]
 
 --author='pattern', --committer='pattern'::
 
@@ -153,6 +155,11 @@
 	Consider the limiting patterns to be extended regular expressions
 	instead of the default basic regular expressions.
 
+-F, --fixed-strings::
+
+	Consider the limiting patterns to be fixed strings (don't interpret
+	pattern as a regular expression).
+
 --remove-empty::
 
 	Stop when a given path disappears from the tree.
diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt
new file mode 100644
index 0000000..073b22b
--- /dev/null
+++ b/Documentation/technical/api-remote.txt
@@ -0,0 +1,123 @@
+Remotes configuration API
+=========================
+
+The API in remote.h gives access to the configuration related to
+remotes. It handles all three configuration mechanisms historically
+and currently used by git, and presents the information in a uniform
+fashion. Note that the code also handles plain URLs without any
+configuration, giving them just the default information.
+
+struct remote
+-------------
+
+`name`::
+
+	The user's nickname for the remote
+
+`url`::
+
+	An array of all of the url_nr URLs configured for the remote
+
+`push`::
+
+	 An array of refspecs configured for pushing, with
+	 push_refspec being the literal strings, and push_refspec_nr
+	 being the quantity.
+
+`fetch`::
+
+	An array of refspecs configured for fetching, with
+	fetch_refspec being the literal strings, and fetch_refspec_nr
+	being the quantity.
+
+`fetch_tags`::
+
+	The setting for whether to fetch tags (as a separate rule from
+	the configured refspecs); -1 means never to fetch tags, 0
+	means to auto-follow tags based on the default heuristic, 1
+	means to always auto-follow tags, and 2 means to fetch all
+	tags.
+
+`receivepack`, `uploadpack`::
+
+	The configured helper programs to run on the remote side, for
+	git-native protocols.
+
+`http_proxy`::
+
+	The proxy to use for curl (http, https, ftp, etc.) URLs.
+
+struct remotes can be found by name with remote_get(), and iterated
+through with for_each_remote(). remote_get(NULL) will return the
+default remote, given the current branch and configuration.
+
+struct refspec
+--------------
+
+A struct refspec holds the parsed interpretation of a refspec. If it
+will force updates (starts with a '+'), force is true. If it is a
+pattern (sides end with '*') pattern is true. src and dest are the two
+sides (if a pattern, only the part outside of the wildcards); if there
+is only one side, it is src, and dst is NULL; if sides exist but are
+empty (i.e., the refspec either starts or ends with ':'), the
+corresponding side is "".
+
+This parsing can be done to an array of strings to give an array of
+struct refpsecs with parse_ref_spec().
+
+remote_find_tracking(), given a remote and a struct refspec with
+either src or dst filled out, will fill out the other such that the
+result is in the "fetch" specification for the remote (note that this
+evaluates patterns and returns a single result).
+
+struct branch
+-------------
+
+Note that this may end up moving to branch.h
+
+struct branch holds the configuration for a branch. It can be looked
+up with branch_get(name) for "refs/heads/{name}", or with
+branch_get(NULL) for HEAD.
+
+It contains:
+
+`name`::
+
+	The short name of the branch.
+
+`refname`::
+
+	The full path for the branch ref.
+
+`remote_name`::
+
+	The name of the remote listed in the configuration.
+
+`remote`::
+
+	The struct remote for that remote.
+
+`merge_name`::
+
+	An array of the "merge" lines in the configuration.
+
+`merge`::
+
+	An array of the struct refspecs used for the merge lines. That
+	is, merge[i]->dst is a local tracking ref which should be
+	merged into this branch by default.
+
+`merge_nr`::
+
+	The number of merge configurations
+
+branch_has_merge_config() returns true if the given branch has merge
+configuration given.
+
+Other stuff
+-----------
+
+There is other stuff in remote.h that is related, in general, to the
+process of interacting with remotes.
+
+(Daniel Barkalow)
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 19d2f64..c364a22 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -1,10 +1,172 @@
 run-command API
 ===============
 
-Talk about <run-command.h>, and things like:
+The run-command API offers a versatile tool to run sub-processes with
+redirected input and output as well as with a modified environment
+and an alternate current directory.
 
-* Environment the command runs with (e.g. GIT_DIR);
-* File descriptors and pipes;
-* Exit status;
+A similar API offers the capability to run a function asynchronously,
+which is primarily used to capture the output that the function
+produces in the caller in order to process it.
 
-(Hannes, Dscho, Shawn)
+
+Functions
+---------
+
+`start_command`::
+
+	Start a sub-process. Takes a pointer to a `struct child_process`
+	that specifies the details and returns pipe FDs (if requested).
+	See below for details.
+
+`finish_command`::
+
+	Wait for the completion of a sub-process that was started with
+	start_command().
+
+`run_command`::
+
+	A convenience function that encapsulates a sequence of
+	start_command() followed by finish_command(). Takes a pointer
+	to a `struct child_process` that specifies the details.
+
+`run_command_v_opt`, `run_command_v_opt_dir`, `run_command_v_opt_cd_env`::
+
+	Convenience functions that encapsulate a sequence of
+	start_command() followed by finish_command(). The argument argv
+	specifies the program and its arguments. The argument opt is zero
+	or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
+	`RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
+	.no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
+	The argument dir corresponds the member .dir. The argument env
+	corresponds to the member .env.
+
+`start_async`::
+
+	Run a function asynchronously. Takes a pointer to a `struct
+	async` that specifies the details and returns a pipe FD
+	from which the caller reads. See below for details.
+
+`finish_async`::
+
+	Wait for the completion of an asynchronous function that was
+	started with start_async().
+
+
+Data structures
+---------------
+
+* `struct child_process`
+
+This describes the arguments, redirections, and environment of a
+command to run in a sub-process.
+
+The caller:
+
+1. allocates and clears (memset(&chld, '0', sizeof(chld));) a
+   struct child_process variable;
+2. initializes the members;
+3. calls start_command();
+4. processes the data;
+5. closes file descriptors (if necessary; see below);
+6. calls finish_command().
+
+The .argv member is set up as an array of string pointers (NULL
+terminated), of which .argv[0] is the program name to run (usually
+without a path). If the command to run is a git command, set argv[0] to
+the command name without the 'git-' prefix and set .git_cmd = 1.
+
+The members .in, .out, .err are used to redirect stdin, stdout,
+stderr as follows:
+
+. Specify 0 to request no special redirection. No new file descriptor
+  is allocated. The child process simply inherits the channel from the
+  parent.
+
+. Specify -1 to have a pipe allocated; start_command() replaces -1
+  by the pipe FD in the following way:
+
+	.in: Returns the writable pipe end into which the caller writes;
+		the readable end of the pipe becomes the child's stdin.
+
+	.out, .err: Returns the readable pipe end from which the caller
+		reads; the writable end of the pipe end becomes child's
+		stdout/stderr.
+
+  The caller of start_command() must close the so returned FDs
+  after it has completed reading from/writing to it!
+
+. Specify a file descriptor > 0 to be used by the child:
+
+	.in: The FD must be readable; it becomes child's stdin.
+	.out: The FD must be writable; it becomes child's stdout.
+	.err > 0 is not supported.
+
+  The specified FD is closed by start_command(), even if it fails to
+  run the sub-process!
+
+. Special forms of redirection are available by setting these members
+  to 1:
+
+	.no_stdin, .no_stdout, .no_stderr: The respective channel is
+		redirected to /dev/null.
+
+	.stdout_to_stderr: stdout of the child is redirected to its
+		stderr. This happens after stderr is itself redirected.
+		So stdout will follow stderr to wherever it is
+		redirected.
+
+To modify the environment of the sub-process, specify an array of
+string pointers (NULL terminated) in .env:
+
+. If the string is of the form "VAR=value", i.e. it contains '='
+  the variable is added to the child process's environment.
+
+. If the string does not contain '=', it names an environment
+  variable that will be removed from the child process's environment.
+
+To specify a new initial working directory for the sub-process,
+specify it in the .dir member.
+
+
+* `struct async`
+
+This describes a function to run asynchronously, whose purpose is
+to produce output that the caller reads.
+
+The caller:
+
+1. allocates and clears (memset(&asy, '0', sizeof(asy));) a
+   struct async variable;
+2. initializes .proc and .data;
+3. calls start_async();
+4. processes the data by reading from the fd in .out;
+5. closes .out;
+6. calls finish_async().
+
+The function pointer in .proc has the following signature:
+
+	int proc(int fd, void *data);
+
+. fd specifies a writable file descriptor to which the function must
+  write the data that it produces. The function *must* close this
+  descriptor before it returns.
+
+. data is the value that the caller has specified in the .data member
+  of struct async.
+
+. The return value of the function is 0 on success and non-zero
+  on failure. If the function indicates failure, finish_async() will
+  report failure as well.
+
+
+There are serious restrictions on what the asynchronous function can do
+because this facility is implemented by a pipe to a forked process on
+UNIX, but by a thread in the same address space on Windows:
+
+. It cannot change the program's state (global variables, environment,
+  etc.) in a way that the caller notices; in other words, .out is the
+  only communication channel to the caller.
+
+. It must not change the program's state that the caller of the
+  facility also uses.
diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt
index aa87756..1803e64 100644
--- a/Documentation/technical/pack-format.txt
+++ b/Documentation/technical/pack-format.txt
@@ -103,10 +103,24 @@
      packed object data:
         If it is not DELTA, then deflated bytes (the size above
 		is the size before compression).
-	If it is DELTA, then
+	If it is REF_DELTA, then
 	  20-byte base object name SHA1 (the size above is the
 		size of the delta data that follows).
           delta data, deflated.
+	If it is OFS_DELTA, then
+	  n-byte offset (see below) interpreted as a negative
+		offset from the type-byte of the header of the
+		ofs-delta entry (the size above is the size of
+		the delta data that follows).
+	  delta data, deflated.
+
+     offset encoding:
+	  n bytes with MSB set in all but the last one.
+	  The offset is then the number constructed by
+	  concatenating the lower 7 bit of each byte, and
+	  for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
+	  to the result.
+
 
 
 = Version 2 pack-*.idx files support packs larger than 4 GiB, and
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index e2bbda5..a8c5efb 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -103,7 +103,7 @@
 $ git commit
 ------------------------------------------------
 
-This will again prompt your for a message describing the change, and then
+This will again prompt you for a message describing the change, and then
 record a new version of the project.
 
 Alternatively, instead of running `git add` beforehand, you can use
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 81ac17f..fa34c67 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -44,3 +44,26 @@
 ifdef::git-clone[]
 They are equivalent, except the former implies --local option.
 endif::git-clone[]
+
+
+If there are a large number of similarly-named remote repositories and
+you want to use a different format for them (such that the URLs you
+use will be rewritten into URLs that work), you can create a
+configuration section of the form:
+
+------------
+	[url "<actual url base>"]
+		insteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+	[url "git://git.host.xz/"]
+		insteadOf = host.xz:/path/to/
+		insteadOf = work:
+------------
+
+a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
+rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
+
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 565aeb9..86b91a5 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1548,22 +1548,7 @@
 
 Dangling objects are not a problem.  At worst they may take up a little
 extra disk space.  They can sometimes provide a last-resort method for
-recovering lost work--see <<dangling-objects>> for details.  However, if
-you wish, you can remove them with linkgit:git-prune[1] or the `--prune`
-option to linkgit:git-gc[1]:
-
--------------------------------------------------
-$ git gc --prune
--------------------------------------------------
-
-This may be time-consuming.  Unlike most other git operations (including
-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 linkgit: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 work--see <<dangling-objects>> for details.
 
 [[recovering-lost-changes]]
 Recovering lost changes
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 94da672..af9d78d 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.4.6.GIT
+DEF_VER=v1.5.5.5.GIT
 
 LF='
 '
diff --git a/Makefile b/Makefile
index 7a3c6d1..7c70b00 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,13 @@
 
 # Define V=1 to have a more verbose compile.
 #
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+#
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+#
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies MOZILLA_SHA1.
 #
@@ -137,6 +144,13 @@
 # Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
 # parallel delta searching when packing objects.
 #
+# Define INTERNAL_QSORT to use Git's implementation of qsort(), which
+# is a simplified version of the merge sort used in glibc. This is
+# recommended if Git triggers O(n^2) behavior in your platform's qsort().
+#
+# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
+# your external grep (e.g., if your system lacks grep, if its grep is
+# broken, or spawning external process is slower than built-in grep git has).
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -218,63 +232,88 @@
 BASIC_CFLAGS =
 BASIC_LDFLAGS =
 
-SCRIPT_SH = \
-	git-bisect.sh git-checkout.sh \
-	git-clone.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 \
-	git-sh-setup.sh \
-	git-am.sh \
-	git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
-	git-merge-resolve.sh \
-	git-lost-found.sh git-quiltimport.sh git-submodule.sh \
-	git-filter-branch.sh \
-	git-stash.sh \
-	git-help--browse.sh
+SCRIPT_SH += git-am.sh
+SCRIPT_SH += git-bisect.sh
+SCRIPT_SH += git-clone.sh
+SCRIPT_SH += git-filter-branch.sh
+SCRIPT_SH += git-lost-found.sh
+SCRIPT_SH += git-merge-octopus.sh
+SCRIPT_SH += git-merge-one-file.sh
+SCRIPT_SH += git-merge-resolve.sh
+SCRIPT_SH += git-merge.sh
+SCRIPT_SH += git-merge-stupid.sh
+SCRIPT_SH += git-mergetool.sh
+SCRIPT_SH += git-parse-remote.sh
+SCRIPT_SH += git-pull.sh
+SCRIPT_SH += git-quiltimport.sh
+SCRIPT_SH += git-rebase--interactive.sh
+SCRIPT_SH += git-rebase.sh
+SCRIPT_SH += git-repack.sh
+SCRIPT_SH += git-request-pull.sh
+SCRIPT_SH += git-sh-setup.sh
+SCRIPT_SH += git-stash.sh
+SCRIPT_SH += git-submodule.sh
+SCRIPT_SH += git-web--browse.sh
 
-SCRIPT_PERL = \
-	git-add--interactive.perl \
-	git-archimport.perl git-cvsimport.perl git-relink.perl \
-	git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
-	git-send-email.perl git-svn.perl
+SCRIPT_PERL += git-add--interactive.perl
+SCRIPT_PERL += git-archimport.perl
+SCRIPT_PERL += git-cvsexportcommit.perl
+SCRIPT_PERL += git-cvsimport.perl
+SCRIPT_PERL += git-cvsserver.perl
+SCRIPT_PERL += git-relink.perl
+SCRIPT_PERL += git-send-email.perl
+SCRIPT_PERL += git-svn.perl
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 	  $(patsubst %.perl,%,$(SCRIPT_PERL)) \
 	  git-instaweb
 
-# ... and all the rest that could be moved out of bindir to gitexecdir
-PROGRAMS = \
-	git-fetch-pack$X \
-	git-hash-object$X git-index-pack$X \
-	git-fast-import$X \
-	git-daemon$X \
-	git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
-	git-receive-pack$X \
-	git-send-pack$X git-shell$X \
-	git-show-index$X \
-	git-unpack-file$X \
-	git-update-server-info$X \
-	git-upload-pack$X \
-	git-pack-redundant$X git-var$X \
-	git-merge-tree$X git-imap-send$X \
-	git-merge-recursive$X \
-	$(EXTRA_PROGRAMS)
-
 # Empty...
 EXTRA_PROGRAMS =
 
-BUILT_INS = \
-	git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
-	git-get-tar-commit-id$X git-init$X git-repo-config$X \
-	git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
-	$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
+# ... and all the rest that could be moved out of bindir to gitexecdir
+PROGRAMS += $(EXTRA_PROGRAMS)
+PROGRAMS += git-daemon$X
+PROGRAMS += git-fast-import$X
+PROGRAMS += git-fetch-pack$X
+PROGRAMS += git-hash-object$X
+PROGRAMS += git-imap-send$X
+PROGRAMS += git-index-pack$X
+PROGRAMS += git-merge-index$X
+PROGRAMS += git-merge-tree$X
+PROGRAMS += git-mktag$X
+PROGRAMS += git-mktree$X
+PROGRAMS += git-pack-redundant$X
+PROGRAMS += git-patch-id$X
+PROGRAMS += git-receive-pack$X
+PROGRAMS += git-send-pack$X
+PROGRAMS += git-shell$X
+PROGRAMS += git-show-index$X
+PROGRAMS += git-unpack-file$X
+PROGRAMS += git-update-server-info$X
+PROGRAMS += git-upload-pack$X
+PROGRAMS += git-var$X
+
+# List built-in command $C whose implementation cmd_$C() is not in
+# builtin-$C.o but is linked in as part of some other command.
+BUILT_INS += $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
+
+BUILT_INS += git-cherry-pick$X
+BUILT_INS += git-cherry$X
+BUILT_INS += git-format-patch$X
+BUILT_INS += git-fsck-objects$X
+BUILT_INS += git-get-tar-commit-id$X
+BUILT_INS += git-init$X
+BUILT_INS += git-merge-subtree$X
+BUILT_INS += git-peek-remote$X
+BUILT_INS += git-repo-config$X
+BUILT_INS += git-show$X
+BUILT_INS += git-status$X
+BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
-ALL_PROGRAMS += git-merge-subtree$X
-
 # what 'all' will build but not install in gitexecdir
 OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
 
@@ -291,108 +330,214 @@
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 
-LIB_H = \
-	archive.h blob.h cache.h cache-tree.h commit.h csum-file.h delta.h grep.h \
-	diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
-	run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-	tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
-	utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
-	mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
+LIB_H += archive.h
+LIB_H += attr.h
+LIB_H += blob.h
+LIB_H += builtin.h
+LIB_H += cache.h
+LIB_H += cache-tree.h
+LIB_H += commit.h
+LIB_H += csum-file.h
+LIB_H += decorate.h
+LIB_H += delta.h
+LIB_H += diffcore.h
+LIB_H += diff.h
+LIB_H += dir.h
+LIB_H += fsck.h
+LIB_H += git-compat-util.h
+LIB_H += grep.h
+LIB_H += hash.h
+LIB_H += list-objects.h
+LIB_H += ll-merge.h
+LIB_H += log-tree.h
+LIB_H += mailmap.h
+LIB_H += object.h
+LIB_H += pack.h
+LIB_H += pack-revindex.h
+LIB_H += parse-options.h
+LIB_H += patch-ids.h
+LIB_H += path-list.h
+LIB_H += pkt-line.h
+LIB_H += progress.h
+LIB_H += quote.h
+LIB_H += reflog-walk.h
+LIB_H += refs.h
+LIB_H += remote.h
+LIB_H += revision.h
+LIB_H += run-command.h
+LIB_H += sideband.h
+LIB_H += strbuf.h
+LIB_H += tag.h
+LIB_H += transport.h
+LIB_H += tree.h
+LIB_H += tree-walk.h
+LIB_H += unpack-trees.h
+LIB_H += utf8.h
 
-DIFF_OBJS = \
-	diff.o diff-lib.o diffcore-break.o diffcore-order.o \
-	diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \
-	diffcore-delta.o log-tree.o
+LIB_OBJS += alias.o
+LIB_OBJS += alloc.o
+LIB_OBJS += archive.o
+LIB_OBJS += archive-tar.o
+LIB_OBJS += archive-zip.o
+LIB_OBJS += attr.o
+LIB_OBJS += base85.o
+LIB_OBJS += blob.o
+LIB_OBJS += branch.o
+LIB_OBJS += bundle.o
+LIB_OBJS += cache-tree.o
+LIB_OBJS += color.o
+LIB_OBJS += combine-diff.o
+LIB_OBJS += commit.o
+LIB_OBJS += config.o
+LIB_OBJS += connect.o
+LIB_OBJS += convert.o
+LIB_OBJS += copy.o
+LIB_OBJS += csum-file.o
+LIB_OBJS += ctype.o
+LIB_OBJS += date.o
+LIB_OBJS += decorate.o
+LIB_OBJS += diffcore-break.o
+LIB_OBJS += diffcore-delta.o
+LIB_OBJS += diffcore-order.o
+LIB_OBJS += diffcore-pickaxe.o
+LIB_OBJS += diffcore-rename.o
+LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-lib.o
+LIB_OBJS += diff.o
+LIB_OBJS += dir.o
+LIB_OBJS += entry.o
+LIB_OBJS += environment.o
+LIB_OBJS += exec_cmd.o
+LIB_OBJS += fsck.o
+LIB_OBJS += grep.o
+LIB_OBJS += hash.o
+LIB_OBJS += help.o
+LIB_OBJS += ident.o
+LIB_OBJS += interpolate.o
+LIB_OBJS += list-objects.o
+LIB_OBJS += ll-merge.o
+LIB_OBJS += lockfile.o
+LIB_OBJS += log-tree.o
+LIB_OBJS += mailmap.o
+LIB_OBJS += match-trees.o
+LIB_OBJS += merge-file.o
+LIB_OBJS += object.o
+LIB_OBJS += pack-check.o
+LIB_OBJS += pack-revindex.o
+LIB_OBJS += pack-write.o
+LIB_OBJS += pager.o
+LIB_OBJS += parse-options.o
+LIB_OBJS += patch-delta.o
+LIB_OBJS += patch-ids.o
+LIB_OBJS += path-list.o
+LIB_OBJS += path.o
+LIB_OBJS += pkt-line.o
+LIB_OBJS += pretty.o
+LIB_OBJS += progress.o
+LIB_OBJS += quote.o
+LIB_OBJS += reachable.o
+LIB_OBJS += read-cache.o
+LIB_OBJS += reflog-walk.o
+LIB_OBJS += refs.o
+LIB_OBJS += remote.o
+LIB_OBJS += revision.o
+LIB_OBJS += run-command.o
+LIB_OBJS += server-info.o
+LIB_OBJS += setup.o
+LIB_OBJS += sha1_file.o
+LIB_OBJS += sha1_name.o
+LIB_OBJS += shallow.o
+LIB_OBJS += sideband.o
+LIB_OBJS += strbuf.o
+LIB_OBJS += symlinks.o
+LIB_OBJS += tag.o
+LIB_OBJS += trace.o
+LIB_OBJS += transport.o
+LIB_OBJS += tree-diff.o
+LIB_OBJS += tree.o
+LIB_OBJS += tree-walk.o
+LIB_OBJS += unpack-trees.o
+LIB_OBJS += usage.o
+LIB_OBJS += utf8.o
+LIB_OBJS += walker.o
+LIB_OBJS += write_or_die.o
+LIB_OBJS += ws.o
+LIB_OBJS += wt-status.o
+LIB_OBJS += xdiff-interface.o
 
-LIB_OBJS = \
-	blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
-	date.o diff-delta.o entry.o exec_cmd.o ident.o \
-	pretty.o interpolate.o hash.o \
-	lockfile.o \
-	patch-ids.o \
-	object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
-	sideband.o reachable.o reflog-walk.o \
-	quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
-	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
-	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
-	revision.o pager.o tree-walk.o xdiff-interface.o \
-	write_or_die.o trace.o list-objects.o grep.o match-trees.o \
-	alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
-	color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
-	convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
-	transport.o bundle.o walker.o parse-options.o ws.o archive.o
-
-BUILTIN_OBJS = \
-	builtin-add.o \
-	builtin-annotate.o \
-	builtin-apply.o \
-	builtin-archive.o \
-	builtin-blame.o \
-	builtin-branch.o \
-	builtin-bundle.o \
-	builtin-cat-file.o \
-	builtin-check-attr.o \
-	builtin-checkout-index.o \
-	builtin-check-ref-format.o \
-	builtin-clean.o \
-	builtin-commit.o \
-	builtin-commit-tree.o \
-	builtin-count-objects.o \
-	builtin-describe.o \
-	builtin-diff.o \
-	builtin-diff-files.o \
-	builtin-diff-index.o \
-	builtin-diff-tree.o \
-	builtin-fast-export.o \
-	builtin-fetch.o \
-	builtin-fetch-pack.o \
-	builtin-fetch--tool.o \
-	builtin-fmt-merge-msg.o \
-	builtin-for-each-ref.o \
-	builtin-fsck.o \
-	builtin-gc.o \
-	builtin-grep.o \
-	builtin-init-db.o \
-	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 \
-	builtin-merge-file.o \
-	builtin-merge-ours.o \
-	builtin-mv.o \
-	builtin-name-rev.o \
-	builtin-pack-objects.o \
-	builtin-prune.o \
-	builtin-prune-packed.o \
-	builtin-push.o \
-	builtin-read-tree.o \
-	builtin-reflog.o \
-	builtin-send-pack.o \
-	builtin-config.o \
-	builtin-rerere.o \
-	builtin-reset.o \
-	builtin-rev-list.o \
-	builtin-rev-parse.o \
-	builtin-revert.o \
-	builtin-rm.o \
-	builtin-shortlog.o \
-	builtin-show-branch.o \
-	builtin-stripspace.o \
-	builtin-symbolic-ref.o \
-	builtin-tag.o \
-	builtin-tar-tree.o \
-	builtin-unpack-objects.o \
-	builtin-update-index.o \
-	builtin-update-ref.o \
-	builtin-upload-archive.o \
-	builtin-verify-pack.o \
-	builtin-verify-tag.o \
-	builtin-write-tree.o \
-	builtin-show-ref.o \
-	builtin-pack-refs.o
+BUILTIN_OBJS += builtin-add.o
+BUILTIN_OBJS += builtin-annotate.o
+BUILTIN_OBJS += builtin-apply.o
+BUILTIN_OBJS += builtin-archive.o
+BUILTIN_OBJS += builtin-blame.o
+BUILTIN_OBJS += builtin-branch.o
+BUILTIN_OBJS += builtin-bundle.o
+BUILTIN_OBJS += builtin-cat-file.o
+BUILTIN_OBJS += builtin-check-attr.o
+BUILTIN_OBJS += builtin-check-ref-format.o
+BUILTIN_OBJS += builtin-checkout-index.o
+BUILTIN_OBJS += builtin-checkout.o
+BUILTIN_OBJS += builtin-clean.o
+BUILTIN_OBJS += builtin-commit-tree.o
+BUILTIN_OBJS += builtin-commit.o
+BUILTIN_OBJS += builtin-config.o
+BUILTIN_OBJS += builtin-count-objects.o
+BUILTIN_OBJS += builtin-describe.o
+BUILTIN_OBJS += builtin-diff-files.o
+BUILTIN_OBJS += builtin-diff-index.o
+BUILTIN_OBJS += builtin-diff-tree.o
+BUILTIN_OBJS += builtin-diff.o
+BUILTIN_OBJS += builtin-fast-export.o
+BUILTIN_OBJS += builtin-fetch--tool.o
+BUILTIN_OBJS += builtin-fetch-pack.o
+BUILTIN_OBJS += builtin-fetch.o
+BUILTIN_OBJS += builtin-fmt-merge-msg.o
+BUILTIN_OBJS += builtin-for-each-ref.o
+BUILTIN_OBJS += builtin-fsck.o
+BUILTIN_OBJS += builtin-gc.o
+BUILTIN_OBJS += builtin-grep.o
+BUILTIN_OBJS += builtin-init-db.o
+BUILTIN_OBJS += builtin-log.o
+BUILTIN_OBJS += builtin-ls-files.o
+BUILTIN_OBJS += builtin-ls-remote.o
+BUILTIN_OBJS += builtin-ls-tree.o
+BUILTIN_OBJS += builtin-mailinfo.o
+BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge-base.o
+BUILTIN_OBJS += builtin-merge-file.o
+BUILTIN_OBJS += builtin-merge-ours.o
+BUILTIN_OBJS += builtin-merge-recursive.o
+BUILTIN_OBJS += builtin-mv.o
+BUILTIN_OBJS += builtin-name-rev.o
+BUILTIN_OBJS += builtin-pack-objects.o
+BUILTIN_OBJS += builtin-pack-refs.o
+BUILTIN_OBJS += builtin-prune-packed.o
+BUILTIN_OBJS += builtin-prune.o
+BUILTIN_OBJS += builtin-push.o
+BUILTIN_OBJS += builtin-read-tree.o
+BUILTIN_OBJS += builtin-reflog.o
+BUILTIN_OBJS += builtin-remote.o
+BUILTIN_OBJS += builtin-rerere.o
+BUILTIN_OBJS += builtin-reset.o
+BUILTIN_OBJS += builtin-rev-list.o
+BUILTIN_OBJS += builtin-rev-parse.o
+BUILTIN_OBJS += builtin-revert.o
+BUILTIN_OBJS += builtin-rm.o
+BUILTIN_OBJS += builtin-send-pack.o
+BUILTIN_OBJS += builtin-shortlog.o
+BUILTIN_OBJS += builtin-show-branch.o
+BUILTIN_OBJS += builtin-show-ref.o
+BUILTIN_OBJS += builtin-stripspace.o
+BUILTIN_OBJS += builtin-symbolic-ref.o
+BUILTIN_OBJS += builtin-tag.o
+BUILTIN_OBJS += builtin-tar-tree.o
+BUILTIN_OBJS += builtin-unpack-objects.o
+BUILTIN_OBJS += builtin-update-index.o
+BUILTIN_OBJS += builtin-update-ref.o
+BUILTIN_OBJS += builtin-upload-archive.o
+BUILTIN_OBJS += builtin-verify-pack.o
+BUILTIN_OBJS += builtin-verify-tag.o
+BUILTIN_OBJS += builtin-write-tree.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 EXTLIBS =
@@ -467,6 +612,7 @@
 	NO_MEMMEM = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
+	DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
 endif
 ifeq ($(uname_S),OpenBSD)
 	NO_STRCASESTR = YesPlease
@@ -618,6 +764,14 @@
 ifdef NO_C99_FORMAT
 	BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
+ifdef SNPRINTF_RETURNS_BOGUS
+	COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
+	COMPAT_OBJS += compat/snprintf.o
+endif
+ifdef FREAD_READS_DIRECTORIES
+	COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
+	COMPAT_OBJS += compat/fopen.o
+endif
 ifdef NO_SYMLINK_HEAD
 	BASIC_CFLAGS += -DNO_SYMLINK_HEAD
 endif
@@ -722,10 +876,21 @@
 	COMPAT_CFLAGS += -DNO_MEMMEM
 	COMPAT_OBJS += compat/memmem.o
 endif
+ifdef INTERNAL_QSORT
+	COMPAT_CFLAGS += -DINTERNAL_QSORT
+	COMPAT_OBJS += compat/qsort.o
+endif
 
 ifdef THREADED_DELTA_SEARCH
 	BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
 	EXTLIBS += -lpthread
+	LIB_OBJS += thread-utils.o
+endif
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+	COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
+ifdef NO_EXTERNAL_GREP
+	BASIC_CFLAGS += -DNO_EXTERNAL_GREP
 endif
 
 ifeq ($(TCLTK_PATH),)
@@ -793,7 +958,7 @@
 
 ### Build rules
 
-all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS)
+all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
 	$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';)
 endif
@@ -819,12 +984,10 @@
 
 help.o: help.c common-cmds.h GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+		'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 		'-DGIT_MAN_PATH="$(mandir_SQ)"' \
 		'-DGIT_INFO_PATH="$(infodir_SQ)"' $<
 
-git-merge-subtree$X: git-merge-recursive$X
-	$(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
-
 $(BUILT_INS): git$X
 	$(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
 
@@ -836,10 +999,10 @@
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
 	$(QUIET_GEN)$(RM) $@ $@+ && \
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+	    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
 	    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-	    -e 's|@@HTMLDIR@@|$(htmldir_SQ)|g' \
 	    $@.sh >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
@@ -995,6 +1158,9 @@
 		echo "$$FLAGS" >GIT-CFLAGS; \
             fi
 
+GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
+	@echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@
+
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
 TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
@@ -1150,10 +1316,11 @@
 	$(MAKE) -C gitk-git clean
 	$(MAKE) -C git-gui clean
 endif
-	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
+	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
 
 .PHONY: all install clean strip
 .PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
+.PHONY: .FORCE-GIT-BUILD-OPTIONS
 
 ### Check documentation
 #
diff --git a/RelNotes b/RelNotes
index 8fbe7db..d94e742 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.4.7.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.5.6.txt
\ No newline at end of file
diff --git a/alias.c b/alias.c
new file mode 100644
index 0000000..116cac8
--- /dev/null
+++ b/alias.c
@@ -0,0 +1,22 @@
+#include "cache.h"
+
+static const char *alias_key;
+static char *alias_val;
+static int alias_lookup_cb(const char *k, const char *v)
+{
+	if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+		if (!v)
+			return config_error_nonbool(k);
+		alias_val = xstrdup(v);
+		return 0;
+	}
+	return 0;
+}
+
+char *alias_lookup(const char *alias)
+{
+	alias_key = alias;
+	alias_val = NULL;
+	git_config(alias_lookup_cb);
+	return alias_val;
+}
diff --git a/branch.c b/branch.c
new file mode 100644
index 0000000..daf862e
--- /dev/null
+++ b/branch.c
@@ -0,0 +1,152 @@
+#include "cache.h"
+#include "branch.h"
+#include "refs.h"
+#include "remote.h"
+#include "commit.h"
+
+struct tracking {
+	struct refspec spec;
+	char *src;
+	const char *remote;
+	int matches;
+};
+
+static int find_tracked_branch(struct remote *remote, void *priv)
+{
+	struct tracking *tracking = priv;
+
+	if (!remote_find_tracking(remote, &tracking->spec)) {
+		if (++tracking->matches == 1) {
+			tracking->src = tracking->spec.src;
+			tracking->remote = remote->name;
+		} else {
+			free(tracking->spec.src);
+			if (tracking->src) {
+				free(tracking->src);
+				tracking->src = NULL;
+			}
+		}
+		tracking->spec.src = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * This is called when new_ref is branched off of orig_ref, and tries
+ * to infer the settings for branch.<new_ref>.{remote,merge} from the
+ * config.
+ */
+static int setup_tracking(const char *new_ref, const char *orig_ref,
+                          enum branch_track track)
+{
+	char key[1024];
+	struct tracking tracking;
+
+	if (strlen(new_ref) > 1024 - 7 - 7 - 1)
+		return error("Tracking not set up: name too long: %s",
+				new_ref);
+
+	memset(&tracking, 0, sizeof(tracking));
+	tracking.spec.dst = (char *)orig_ref;
+	if (for_each_remote(find_tracked_branch, &tracking))
+		return 1;
+
+	if (!tracking.matches)
+		switch (track) {
+		case BRANCH_TRACK_ALWAYS:
+		case BRANCH_TRACK_EXPLICIT:
+			break;
+		default:
+			return 1;
+		}
+
+	if (tracking.matches > 1)
+		return error("Not tracking: ambiguous information for ref %s",
+				orig_ref);
+
+	sprintf(key, "branch.%s.remote", new_ref);
+	git_config_set(key, tracking.remote ?  tracking.remote : ".");
+	sprintf(key, "branch.%s.merge", new_ref);
+	git_config_set(key, tracking.src ? tracking.src : orig_ref);
+	free(tracking.src);
+	printf("Branch %s set up to track %s branch %s.\n", new_ref,
+		tracking.remote ? "remote" : "local", orig_ref);
+
+	return 0;
+}
+
+void create_branch(const char *head,
+		   const char *name, const char *start_name,
+		   int force, int reflog, enum branch_track track)
+{
+	struct ref_lock *lock;
+	struct commit *commit;
+	unsigned char sha1[20];
+	char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
+	int forcing = 0;
+
+	snprintf(ref, sizeof ref, "refs/heads/%s", name);
+	if (check_ref_format(ref))
+		die("'%s' is not a valid branch name.", name);
+
+	if (resolve_ref(ref, sha1, 1, NULL)) {
+		if (!force)
+			die("A branch named '%s' already exists.", name);
+		else if (!is_bare_repository() && !strcmp(head, name))
+			die("Cannot force update the current branch.");
+		forcing = 1;
+	}
+
+	real_ref = NULL;
+	if (get_sha1(start_name, sha1))
+		die("Not a valid object name: '%s'.", start_name);
+
+	switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
+	case 0:
+		/* Not branching from any existing branch */
+		if (track == BRANCH_TRACK_EXPLICIT)
+			die("Cannot setup tracking information; starting point is not a branch.");
+		break;
+	case 1:
+		/* Unique completion -- good */
+		break;
+	default:
+		die("Ambiguous object name: '%s'.", start_name);
+		break;
+	}
+
+	if ((commit = lookup_commit_reference(sha1)) == NULL)
+		die("Not a valid branch point: '%s'.", start_name);
+	hashcpy(sha1, commit->object.sha1);
+
+	lock = lock_any_ref_for_update(ref, NULL, 0);
+	if (!lock)
+		die("Failed to lock ref for update: %s.", strerror(errno));
+
+	if (reflog)
+		log_all_ref_updates = 1;
+
+	if (forcing)
+		snprintf(msg, sizeof msg, "branch: Reset from %s",
+			 start_name);
+	else
+		snprintf(msg, sizeof msg, "branch: Created from %s",
+			 start_name);
+
+	if (real_ref && track)
+		setup_tracking(name, real_ref, track);
+
+	if (write_ref_sha1(lock, sha1, msg) < 0)
+		die("Failed to write ref: %s.", strerror(errno));
+
+	free(real_ref);
+}
+
+void remove_branch_state(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("rr-cache/MERGE_RR"));
+	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("SQUASH_MSG"));
+}
diff --git a/branch.h b/branch.h
new file mode 100644
index 0000000..9f0c2a2
--- /dev/null
+++ b/branch.h
@@ -0,0 +1,24 @@
+#ifndef BRANCH_H
+#define BRANCH_H
+
+/* Functions for acting on the information about branches. */
+
+/*
+ * Creates a new branch, where head is the branch currently checked
+ * out, name is the new branch name, start_name is the name of the
+ * existing branch that the new branch should start from, force
+ * enables overwriting an existing (non-head) branch, reflog creates a
+ * reflog for the branch, and track causes the new branch to be
+ * configured to merge the remote branch that start_name is a tracking
+ * branch for (if any).
+ */
+void create_branch(const char *head, const char *name, const char *start_name,
+		   int force, int reflog, enum branch_track track);
+
+/*
+ * Remove information about the state of working on the current
+ * branch. (E.g., MERGE_HEAD)
+ */
+void remove_branch_state(void);
+
+#endif
diff --git a/builtin-apply.c b/builtin-apply.c
index 6538835..30d26e5 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -161,6 +161,84 @@
 	struct patch *next;
 };
 
+/*
+ * A line in a file, len-bytes long (includes the terminating LF,
+ * except for an incomplete line at the end if the file ends with
+ * one), and its contents hashes to 'hash'.
+ */
+struct line {
+	size_t len;
+	unsigned hash : 24;
+	unsigned flag : 8;
+#define LINE_COMMON     1
+};
+
+/*
+ * This represents a "file", which is an array of "lines".
+ */
+struct image {
+	char *buf;
+	size_t len;
+	size_t nr;
+	size_t alloc;
+	struct line *line_allocated;
+	struct line *line;
+};
+
+static uint32_t hash_line(const char *cp, size_t len)
+{
+	size_t i;
+	uint32_t h;
+	for (i = 0, h = 0; i < len; i++) {
+		if (!isspace(cp[i])) {
+			h = h * 3 + (cp[i] & 0xff);
+		}
+	}
+	return h;
+}
+
+static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
+{
+	ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
+	img->line_allocated[img->nr].len = len;
+	img->line_allocated[img->nr].hash = hash_line(bol, len);
+	img->line_allocated[img->nr].flag = flag;
+	img->nr++;
+}
+
+static void prepare_image(struct image *image, char *buf, size_t len,
+			  int prepare_linetable)
+{
+	const char *cp, *ep;
+
+	memset(image, 0, sizeof(*image));
+	image->buf = buf;
+	image->len = len;
+
+	if (!prepare_linetable)
+		return;
+
+	ep = image->buf + image->len;
+	cp = image->buf;
+	while (cp < ep) {
+		const char *next;
+		for (next = cp; next < ep && *next != '\n'; next++)
+			;
+		if (next < ep)
+			next++;
+		add_line_info(image, cp, next - cp, 0);
+		cp = next;
+	}
+	image->line = image->line_allocated;
+}
+
+static void clear_image(struct image *image)
+{
+	free(image->buf);
+	image->buf = NULL;
+	image->len = 0;
+}
+
 static void say_patch_name(FILE *output, const char *pre,
 			   struct patch *patch, const char *post)
 {
@@ -1430,234 +1508,345 @@
 	case S_IFREG:
 		if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
 			return error("unable to open or read %s", path);
-		convert_to_git(path, buf->buf, buf->len, buf);
+		convert_to_git(path, buf->buf, buf->len, buf, 0);
 		return 0;
 	default:
 		return -1;
 	}
 }
 
-static int find_offset(const char *buf, unsigned long size,
-		       const char *fragment, unsigned long fragsize,
-		       int line, int *lines)
+static void update_pre_post_images(struct image *preimage,
+				   struct image *postimage,
+				   char *buf,
+				   size_t len)
 {
-	int i;
-	unsigned long start, backwards, forwards;
+	int i, ctx;
+	char *new, *old, *fixed;
+	struct image fixed_preimage;
 
-	if (fragsize > size)
-		return -1;
+	/*
+	 * Update the preimage with whitespace fixes.  Note that we
+	 * are not losing preimage->buf -- apply_one_fragment() will
+	 * free "oldlines".
+	 */
+	prepare_image(&fixed_preimage, buf, len, 1);
+	assert(fixed_preimage.nr == preimage->nr);
+	for (i = 0; i < preimage->nr; i++)
+		fixed_preimage.line[i].flag = preimage->line[i].flag;
+	free(preimage->line_allocated);
+	*preimage = fixed_preimage;
 
-	start = 0;
-	if (line > 1) {
-		unsigned long offset = 0;
-		i = line-1;
-		while (offset + fragsize <= size) {
-			if (buf[offset++] == '\n') {
-				start = offset;
-				if (!--i)
-					break;
-			}
+	/*
+	 * Adjust the common context lines in postimage, in place.
+	 * This is possible because whitespace fixing does not make
+	 * the string grow.
+	 */
+	new = old = postimage->buf;
+	fixed = preimage->buf;
+	for (i = ctx = 0; i < postimage->nr; i++) {
+		size_t len = postimage->line[i].len;
+		if (!(postimage->line[i].flag & LINE_COMMON)) {
+			/* an added line -- no counterparts in preimage */
+			memmove(new, old, len);
+			old += len;
+			new += len;
+			continue;
 		}
+
+		/* a common context -- skip it in the original postimage */
+		old += len;
+
+		/* and find the corresponding one in the fixed preimage */
+		while (ctx < preimage->nr &&
+		       !(preimage->line[ctx].flag & LINE_COMMON)) {
+			fixed += preimage->line[ctx].len;
+			ctx++;
+		}
+		if (preimage->nr <= ctx)
+			die("oops");
+
+		/* and copy it in, while fixing the line length */
+		len = preimage->line[ctx].len;
+		memcpy(new, fixed, len);
+		new += len;
+		fixed += len;
+		postimage->line[i].len = len;
+		ctx++;
 	}
 
-	/* Exact line number? */
-	if ((start + fragsize <= size) &&
-	    !memcmp(buf + start, fragment, fragsize))
-		return start;
+	/* Fix the length of the whole thing */
+	postimage->len = new - postimage->buf;
+}
+
+static int match_fragment(struct image *img,
+			  struct image *preimage,
+			  struct image *postimage,
+			  unsigned long try,
+			  int try_lno,
+			  unsigned ws_rule,
+			  int match_beginning, int match_end)
+{
+	int i;
+	char *fixed_buf, *buf, *orig, *target;
+
+	if (preimage->nr + try_lno > img->nr)
+		return 0;
+
+	if (match_beginning && try_lno)
+		return 0;
+
+	if (match_end && preimage->nr + try_lno != img->nr)
+		return 0;
+
+	/* Quick hash check */
+	for (i = 0; i < preimage->nr; i++)
+		if (preimage->line[i].hash != img->line[try_lno + i].hash)
+			return 0;
+
+	/*
+	 * Do we have an exact match?  If we were told to match
+	 * at the end, size must be exactly at try+fragsize,
+	 * otherwise try+fragsize must be still within the preimage,
+	 * and either case, the old piece should match the preimage
+	 * exactly.
+	 */
+	if ((match_end
+	     ? (try + preimage->len == img->len)
+	     : (try + preimage->len <= img->len)) &&
+	    !memcmp(img->buf + try, preimage->buf, preimage->len))
+		return 1;
+
+	if (ws_error_action != correct_ws_error)
+		return 0;
+
+	/*
+	 * The hunk does not apply byte-by-byte, but the hash says
+	 * it might with whitespace fuzz.
+	 */
+	fixed_buf = xmalloc(preimage->len + 1);
+	buf = fixed_buf;
+	orig = preimage->buf;
+	target = img->buf + try;
+	for (i = 0; i < preimage->nr; i++) {
+		size_t fixlen; /* length after fixing the preimage */
+		size_t oldlen = preimage->line[i].len;
+		size_t tgtlen = img->line[try_lno + i].len;
+		size_t tgtfixlen; /* length after fixing the target line */
+		char tgtfixbuf[1024], *tgtfix;
+		int match;
+
+		/* Try fixing the line in the preimage */
+		fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+
+		/* Try fixing the line in the target */
+		if (sizeof(tgtfixbuf) < tgtlen)
+			tgtfix = tgtfixbuf;
+		else
+			tgtfix = xmalloc(tgtlen);
+		tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+
+		/*
+		 * If they match, either the preimage was based on
+		 * a version before our tree fixed whitespace breakage,
+		 * or we are lacking a whitespace-fix patch the tree
+		 * the preimage was based on already had (i.e. target
+		 * has whitespace breakage, the preimage doesn't).
+		 * In either case, we are fixing the whitespace breakages
+		 * so we might as well take the fix together with their
+		 * real change.
+		 */
+		match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+
+		if (tgtfix != tgtfixbuf)
+			free(tgtfix);
+		if (!match)
+			goto unmatch_exit;
+
+		orig += oldlen;
+		buf += fixlen;
+		target += tgtlen;
+	}
+
+	/*
+	 * Yes, the preimage is based on an older version that still
+	 * has whitespace breakages unfixed, and fixing them makes the
+	 * hunk match.  Update the context lines in the postimage.
+	 */
+	update_pre_post_images(preimage, postimage,
+			       fixed_buf, buf - fixed_buf);
+	return 1;
+
+ unmatch_exit:
+	free(fixed_buf);
+	return 0;
+}
+
+static int find_pos(struct image *img,
+		    struct image *preimage,
+		    struct image *postimage,
+		    int line,
+		    unsigned ws_rule,
+		    int match_beginning, int match_end)
+{
+	int i;
+	unsigned long backwards, forwards, try;
+	int backwards_lno, forwards_lno, try_lno;
+
+	if (preimage->nr > img->nr)
+		return -1;
+
+	/*
+	 * If match_begining or match_end is specified, there is no
+	 * point starting from a wrong line that will never match and
+	 * wander around and wait for a match at the specified end.
+	 */
+	if (match_beginning)
+		line = 0;
+	else if (match_end)
+		line = img->nr - preimage->nr;
+
+	if (line > img->nr)
+		line = img->nr;
+
+	try = 0;
+	for (i = 0; i < line; i++)
+		try += img->line[i].len;
 
 	/*
 	 * There's probably some smart way to do this, but I'll leave
 	 * that to the smart and beautiful people. I'm simple and stupid.
 	 */
-	backwards = start;
-	forwards = start;
-	for (i = 0; ; i++) {
-		unsigned long try;
-		int n;
+	backwards = try;
+	backwards_lno = line;
+	forwards = try;
+	forwards_lno = line;
+	try_lno = line;
 
-		/* "backward" */
+	for (i = 0; ; i++) {
+		if (match_fragment(img, preimage, postimage,
+				   try, try_lno, ws_rule,
+				   match_beginning, match_end))
+			return try_lno;
+
+	again:
+		if (backwards_lno == 0 && forwards_lno == img->nr)
+			break;
+
 		if (i & 1) {
-			if (!backwards) {
-				if (forwards + fragsize > size)
-					break;
-				continue;
+			if (backwards_lno == 0) {
+				i++;
+				goto again;
 			}
-			do {
-				--backwards;
-			} while (backwards && buf[backwards-1] != '\n');
+			backwards_lno--;
+			backwards -= img->line[backwards_lno].len;
 			try = backwards;
+			try_lno = backwards_lno;
 		} else {
-			while (forwards + fragsize <= size) {
-				if (buf[forwards++] == '\n')
-					break;
+			if (forwards_lno == img->nr) {
+				i++;
+				goto again;
 			}
+			forwards += img->line[forwards_lno].len;
+			forwards_lno++;
 			try = forwards;
+			try_lno = forwards_lno;
 		}
 
-		if (try + fragsize > size)
-			continue;
-		if (memcmp(buf + try, fragment, fragsize))
-			continue;
-		n = (i >> 1)+1;
-		if (i & 1)
-			n = -n;
-		*lines = n;
-		return try;
 	}
-
-	/*
-	 * We should start searching forward and backward.
-	 */
 	return -1;
 }
 
-static void remove_first_line(const char **rbuf, int *rsize)
+static void remove_first_line(struct image *img)
 {
-	const char *buf = *rbuf;
-	int size = *rsize;
-	unsigned long offset;
-	offset = 0;
-	while (offset <= size) {
-		if (buf[offset++] == '\n')
-			break;
-	}
-	*rsize = size - offset;
-	*rbuf = buf + offset;
+	img->buf += img->line[0].len;
+	img->len -= img->line[0].len;
+	img->line++;
+	img->nr--;
 }
 
-static void remove_last_line(const char **rbuf, int *rsize)
+static void remove_last_line(struct image *img)
 {
-	const char *buf = *rbuf;
-	int size = *rsize;
-	unsigned long offset;
-	offset = size - 1;
-	while (offset > 0) {
-		if (buf[--offset] == '\n')
-			break;
-	}
-	*rsize = offset + 1;
+	img->len -= img->line[--img->nr].len;
 }
 
-static int apply_line(char *output, const char *patch, int plen,
-		      unsigned ws_rule)
+static void update_image(struct image *img,
+			 int applied_pos,
+			 struct image *preimage,
+			 struct image *postimage)
 {
 	/*
-	 * plen is number of bytes to be copied from patch,
-	 * starting at patch+1 (patch[0] is '+').  Typically
-	 * patch[plen] is '\n', unless this is the incomplete
-	 * last line.
+	 * remove the copy of preimage at offset in img
+	 * and replace it with postimage
 	 */
-	int i;
-	int add_nl_to_tail = 0;
-	int fixed = 0;
-	int last_tab_in_indent = 0;
-	int last_space_in_indent = 0;
-	int need_fix_leading_space = 0;
-	char *buf;
+	int i, nr;
+	size_t remove_count, insert_count, applied_at = 0;
+	char *result;
 
-	if ((ws_error_action != correct_ws_error) || !whitespace_error ||
-	    *patch != '+') {
-		memcpy(output, patch + 1, plen);
-		return plen;
-	}
+	for (i = 0; i < applied_pos; i++)
+		applied_at += img->line[i].len;
 
-	/*
-	 * Strip trailing whitespace
-	 */
-	if ((ws_rule & WS_TRAILING_SPACE) &&
-	    (1 < plen && isspace(patch[plen-1]))) {
-		if (patch[plen] == '\n')
-			add_nl_to_tail = 1;
-		plen--;
-		while (0 < plen && isspace(patch[plen]))
-			plen--;
-		fixed = 1;
-	}
+	remove_count = 0;
+	for (i = 0; i < preimage->nr; i++)
+		remove_count += img->line[applied_pos + i].len;
+	insert_count = postimage->len;
 
-	/*
-	 * Check leading whitespaces (indent)
-	 */
-	for (i = 1; i < plen; i++) {
-		char ch = patch[i];
-		if (ch == '\t') {
-			last_tab_in_indent = i;
-			if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
-			    0 < last_space_in_indent)
-			    need_fix_leading_space = 1;
-		} else if (ch == ' ') {
-			last_space_in_indent = i;
-			if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
-			    8 <= i - last_tab_in_indent)
-				need_fix_leading_space = 1;
-		}
-		else
-			break;
-	}
+	/* Adjust the contents */
+	result = xmalloc(img->len + insert_count - remove_count + 1);
+	memcpy(result, img->buf, applied_at);
+	memcpy(result + applied_at, postimage->buf, postimage->len);
+	memcpy(result + applied_at + postimage->len,
+	       img->buf + (applied_at + remove_count),
+	       img->len - (applied_at + remove_count));
+	free(img->buf);
+	img->buf = result;
+	img->len += insert_count - remove_count;
+	result[img->len] = '\0';
 
-	buf = output;
-	if (need_fix_leading_space) {
-		int consecutive_spaces = 0;
-		int last = last_tab_in_indent + 1;
-
-		if (ws_rule & WS_INDENT_WITH_NON_TAB) {
-			/* have "last" point at one past the indent */
-			if (last_tab_in_indent < last_space_in_indent)
-				last = last_space_in_indent + 1;
-			else
-				last = last_tab_in_indent + 1;
-		}
-
+	/* Adjust the line table */
+	nr = img->nr + postimage->nr - preimage->nr;
+	if (preimage->nr < postimage->nr) {
 		/*
-		 * between patch[1..last], strip the funny spaces,
-		 * updating them to tab as needed.
+		 * NOTE: this knows that we never call remove_first_line()
+		 * on anything other than pre/post image.
 		 */
-		for (i = 1; i < last; i++, plen--) {
-			char ch = patch[i];
-			if (ch != ' ') {
-				consecutive_spaces = 0;
-				*output++ = ch;
-			} else {
-				consecutive_spaces++;
-				if (consecutive_spaces == 8) {
-					*output++ = '\t';
-					consecutive_spaces = 0;
-				}
-			}
-		}
-		while (0 < consecutive_spaces--)
-			*output++ = ' ';
-		fixed = 1;
-		i = last;
+		img->line = xrealloc(img->line, nr * sizeof(*img->line));
+		img->line_allocated = img->line;
 	}
-	else
-		i = 1;
-
-	memcpy(output, patch + i, plen);
-	if (add_nl_to_tail)
-		output[plen++] = '\n';
-	if (fixed)
-		applied_after_fixing_ws++;
-	return output + plen - buf;
+	if (preimage->nr != postimage->nr)
+		memmove(img->line + applied_pos + postimage->nr,
+			img->line + applied_pos + preimage->nr,
+			(img->nr - (applied_pos + preimage->nr)) *
+			sizeof(*img->line));
+	memcpy(img->line + applied_pos,
+	       postimage->line,
+	       postimage->nr * sizeof(*img->line));
+	img->nr = nr;
 }
 
-static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
+static int apply_one_fragment(struct image *img, struct fragment *frag,
 			      int inaccurate_eof, unsigned ws_rule)
 {
 	int match_beginning, match_end;
 	const char *patch = frag->patch;
-	int offset, size = frag->size;
-	char *old = xmalloc(size);
-	char *new = xmalloc(size);
-	const char *oldlines, *newlines;
-	int oldsize = 0, newsize = 0;
+	int size = frag->size;
+	char *old, *new, *oldlines, *newlines;
 	int new_blank_lines_at_end = 0;
 	unsigned long leading, trailing;
-	int pos, lines;
+	int pos, applied_pos;
+	struct image preimage;
+	struct image postimage;
 
+	memset(&preimage, 0, sizeof(preimage));
+	memset(&postimage, 0, sizeof(postimage));
+	oldlines = xmalloc(size);
+	newlines = xmalloc(size);
+
+	old = oldlines;
+	new = newlines;
 	while (size > 0) {
 		char first;
 		int len = linelen(patch, size);
-		int plen;
+		int plen, added;
 		int added_blank_line = 0;
 
 		if (!len)
@@ -1670,7 +1859,7 @@
 		 * followed by "\ No newline", then we also remove the
 		 * last one (which is the newline, of course).
 		 */
-		plen = len-1;
+		plen = len - 1;
 		if (len < size && patch[len] == '\\')
 			plen--;
 		first = *patch;
@@ -1687,25 +1876,40 @@
 			if (plen < 0)
 				/* ... followed by '\No newline'; nothing */
 				break;
-			old[oldsize++] = '\n';
-			new[newsize++] = '\n';
+			*old++ = '\n';
+			*new++ = '\n';
+			add_line_info(&preimage, "\n", 1, LINE_COMMON);
+			add_line_info(&postimage, "\n", 1, LINE_COMMON);
 			break;
 		case ' ':
 		case '-':
-			memcpy(old + oldsize, patch + 1, plen);
-			oldsize += plen;
+			memcpy(old, patch + 1, plen);
+			add_line_info(&preimage, old, plen,
+				      (first == ' ' ? LINE_COMMON : 0));
+			old += plen;
 			if (first == '-')
 				break;
 		/* Fall-through for ' ' */
 		case '+':
-			if (first != '+' || !no_add) {
-				int added = apply_line(new + newsize, patch,
-						       plen, ws_rule);
-				newsize += added;
-				if (first == '+' &&
-				    added == 1 && new[newsize-1] == '\n')
-					added_blank_line = 1;
+			/* --no-add does not add new lines */
+			if (first == '+' && no_add)
+				break;
+
+			if (first != '+' ||
+			    !whitespace_error ||
+			    ws_error_action != correct_ws_error) {
+				memcpy(new, patch + 1, plen);
+				added = plen;
 			}
+			else {
+				added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+			}
+			add_line_info(&postimage, new, added,
+				      (first == '+' ? 0 : LINE_COMMON));
+			new += added;
+			if (first == '+' &&
+			    added == 1 && new[-1] == '\n')
+				added_blank_line = 1;
 			break;
 		case '@': case '\\':
 			/* Ignore it, we already handled it */
@@ -1722,16 +1926,13 @@
 		patch += len;
 		size -= len;
 	}
-
 	if (inaccurate_eof &&
-	    oldsize > 0 && old[oldsize - 1] == '\n' &&
-	    newsize > 0 && new[newsize - 1] == '\n') {
-		oldsize--;
-		newsize--;
+	    old > oldlines && old[-1] == '\n' &&
+	    new > newlines && new[-1] == '\n') {
+		old--;
+		new--;
 	}
 
-	oldlines = old;
-	newlines = new;
 	leading = frag->leading;
 	trailing = frag->trailing;
 
@@ -1755,33 +1956,21 @@
 	 */
 	match_end = !unidiff_zero && !trailing;
 
-	lines = 0;
-	pos = frag->newpos;
+	pos = frag->newpos ? (frag->newpos - 1) : 0;
+	preimage.buf = oldlines;
+	preimage.len = old - oldlines;
+	postimage.buf = newlines;
+	postimage.len = new - newlines;
+	preimage.line = preimage.line_allocated;
+	postimage.line = postimage.line_allocated;
+
 	for (;;) {
-		offset = find_offset(buf->buf, buf->len,
-				     oldlines, oldsize, pos, &lines);
-		if (match_end && offset + oldsize != buf->len)
-			offset = -1;
-		if (match_beginning && offset)
-			offset = -1;
-		if (offset >= 0) {
-			if (ws_error_action == correct_ws_error &&
-			    (buf->len - oldsize - offset == 0)) /* end of file? */
-				newsize -= new_blank_lines_at_end;
 
-			/* Warn if it was necessary to reduce the number
-			 * of context lines.
-			 */
-			if ((leading != frag->leading) ||
-			    (trailing != frag->trailing))
-				fprintf(stderr, "Context reduced to (%ld/%ld)"
-					" to apply fragment at %d\n",
-					leading, trailing, pos + lines);
+		applied_pos = find_pos(img, &preimage, &postimage, pos,
+				       ws_rule, match_beginning, match_end);
 
-			strbuf_splice(buf, offset, oldsize, newlines, newsize);
-			offset = 0;
+		if (applied_pos >= 0)
 			break;
-		}
 
 		/* Am I at my context limits? */
 		if ((leading <= p_context) && (trailing <= p_context))
@@ -1790,33 +1979,64 @@
 			match_beginning = match_end = 0;
 			continue;
 		}
+
 		/*
 		 * Reduce the number of context lines; reduce both
 		 * leading and trailing if they are equal otherwise
 		 * just reduce the larger context.
 		 */
 		if (leading >= trailing) {
-			remove_first_line(&oldlines, &oldsize);
-			remove_first_line(&newlines, &newsize);
+			remove_first_line(&preimage);
+			remove_first_line(&postimage);
 			pos--;
 			leading--;
 		}
 		if (trailing > leading) {
-			remove_last_line(&oldlines, &oldsize);
-			remove_last_line(&newlines, &newsize);
+			remove_last_line(&preimage);
+			remove_last_line(&postimage);
 			trailing--;
 		}
 	}
 
-	if (offset && apply_verbosely)
-		error("while searching for:\n%.*s", oldsize, oldlines);
+	if (applied_pos >= 0) {
+		if (ws_error_action == correct_ws_error &&
+		    new_blank_lines_at_end &&
+		    postimage.nr + applied_pos == img->nr) {
+			/*
+			 * If the patch application adds blank lines
+			 * at the end, and if the patch applies at the
+			 * end of the image, remove those added blank
+			 * lines.
+			 */
+			while (new_blank_lines_at_end--)
+				remove_last_line(&postimage);
+		}
 
-	free(old);
-	free(new);
-	return offset;
+		/*
+		 * Warn if it was necessary to reduce the number
+		 * of context lines.
+		 */
+		if ((leading != frag->leading) ||
+		    (trailing != frag->trailing))
+			fprintf(stderr, "Context reduced to (%ld/%ld)"
+				" to apply fragment at %d\n",
+				leading, trailing, applied_pos+1);
+		update_image(img, applied_pos, &preimage, &postimage);
+	} else {
+		if (apply_verbosely)
+			error("while searching for:\n%.*s",
+			      (int)(old - oldlines), oldlines);
+	}
+
+	free(oldlines);
+	free(newlines);
+	free(preimage.line_allocated);
+	free(postimage.line_allocated);
+
+	return (applied_pos < 0);
 }
 
-static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
+static int apply_binary_fragment(struct image *img, struct patch *patch)
 {
 	struct fragment *fragment = patch->fragments;
 	unsigned long len;
@@ -1833,22 +2053,26 @@
 	}
 	switch (fragment->binary_patch_method) {
 	case BINARY_DELTA_DEFLATED:
-		dst = patch_delta(buf->buf, buf->len, fragment->patch,
+		dst = patch_delta(img->buf, img->len, fragment->patch,
 				  fragment->size, &len);
 		if (!dst)
 			return -1;
-		/* XXX patch_delta NUL-terminates */
-		strbuf_attach(buf, dst, len, len + 1);
+		clear_image(img);
+		img->buf = dst;
+		img->len = len;
 		return 0;
 	case BINARY_LITERAL_DEFLATED:
-		strbuf_reset(buf);
-		strbuf_add(buf, fragment->patch, fragment->size);
+		clear_image(img);
+		img->len = fragment->size;
+		img->buf = xmalloc(img->len+1);
+		memcpy(img->buf, fragment->patch, img->len);
+		img->buf[img->len] = '\0';
 		return 0;
 	}
 	return -1;
 }
 
-static int apply_binary(struct strbuf *buf, struct patch *patch)
+static int apply_binary(struct image *img, struct patch *patch)
 {
 	const char *name = patch->old_name ? patch->old_name : patch->new_name;
 	unsigned char sha1[20];
@@ -1869,7 +2093,7 @@
 		 * See if the old one matches what the patch
 		 * applies to.
 		 */
-		hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
+		hash_sha1_file(img->buf, img->len, blob_type, sha1);
 		if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
 			return error("the patch applies to '%s' (%s), "
 				     "which does not match the "
@@ -1878,14 +2102,14 @@
 	}
 	else {
 		/* Otherwise, the old one must be empty. */
-		if (buf->len)
+		if (img->len)
 			return error("the patch applies to an empty "
 				     "'%s' but it is not empty", name);
 	}
 
 	get_sha1_hex(patch->new_sha1_prefix, sha1);
 	if (is_null_sha1(sha1)) {
-		strbuf_release(buf);
+		clear_image(img);
 		return 0; /* deletion patch */
 	}
 
@@ -1900,20 +2124,21 @@
 			return error("the necessary postimage %s for "
 				     "'%s' cannot be read",
 				     patch->new_sha1_prefix, name);
-		/* XXX read_sha1_file NUL-terminates */
-		strbuf_attach(buf, result, size, size + 1);
+		clear_image(img);
+		img->buf = result;
+		img->len = size;
 	} else {
 		/*
 		 * We have verified buf matches the preimage;
 		 * apply the patch data to it, which is stored
 		 * in the patch->fragments->{patch,size}.
 		 */
-		if (apply_binary_fragment(buf, patch))
+		if (apply_binary_fragment(img, patch))
 			return error("binary patch does not apply to '%s'",
 				     name);
 
 		/* verify that the result matches */
-		hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
+		hash_sha1_file(img->buf, img->len, blob_type, sha1);
 		if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
 			return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
 				name, patch->new_sha1_prefix, sha1_to_hex(sha1));
@@ -1922,7 +2147,7 @@
 	return 0;
 }
 
-static int apply_fragments(struct strbuf *buf, struct patch *patch)
+static int apply_fragments(struct image *img, struct patch *patch)
 {
 	struct fragment *frag = patch->fragments;
 	const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -1930,10 +2155,10 @@
 	unsigned inaccurate_eof = patch->inaccurate_eof;
 
 	if (patch->is_binary)
-		return apply_binary(buf, patch);
+		return apply_binary(img, patch);
 
 	while (frag) {
-		if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
+		if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
 			error("patch failed: %s:%ld", name, frag->oldpos);
 			if (!apply_with_reject)
 				return -1;
@@ -1949,7 +2174,7 @@
 	if (!ce)
 		return 0;
 
-	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+	if (S_ISGITLINK(ce->ce_mode)) {
 		strbuf_grow(buf, 100);
 		strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
 	} else {
@@ -1969,6 +2194,9 @@
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
 	struct strbuf buf;
+	struct image image;
+	size_t len;
+	char *img;
 
 	strbuf_init(&buf, 0);
 	if (cached) {
@@ -1991,9 +2219,14 @@
 		}
 	}
 
-	if (apply_fragments(&buf, patch) < 0)
+	img = strbuf_detach(&buf, &len);
+	prepare_image(&image, img, len, !patch->is_binary);
+
+	if (apply_fragments(&image, patch) < 0)
 		return -1; /* note with --reject this succeeds. */
-	patch->result = strbuf_detach(&buf, &patch->resultsize);
+	patch->result = image.buf;
+	patch->resultsize = image.len;
+	free(image.line_allocated);
 
 	if (0 < patch->is_delete && patch->resultsize)
 		return error("removal patch leaves file contents");
@@ -2026,7 +2259,7 @@
 
 static int verify_index_match(struct cache_entry *ce, struct stat *st)
 {
-	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+	if (S_ISGITLINK(ce->ce_mode)) {
 		if (!S_ISDIR(st->st_mode))
 			return -1;
 		return 0;
@@ -2085,12 +2318,12 @@
 				return error("%s: does not match index",
 					     old_name);
 			if (cached)
-				st_mode = ntohl(ce->ce_mode);
+				st_mode = ce->ce_mode;
 		} else if (stat_ret < 0)
 			return error("%s: %s", old_name, strerror(errno));
 
 		if (!cached)
-			st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+			st_mode = ce_mode_from_stat(ce, st.st_mode);
 
 		if (patch->is_new < 0)
 			patch->is_new = 0;
@@ -2391,7 +2624,7 @@
 	ce = xcalloc(1, ce_size);
 	memcpy(ce->name, path, namelen);
 	ce->ce_mode = create_ce_mode(mode);
-	ce->ce_flags = htons(namelen);
+	ce->ce_flags = namelen;
 	if (S_ISGITLINK(mode)) {
 		const char *s = buf;
 
@@ -2764,7 +2997,7 @@
 	int read_stdin = 1;
 	int inaccurate_eof = 0;
 	int errs = 0;
-	int is_not_gitdir = 0;
+	int is_not_gitdir;
 
 	const char *whitespace_option = NULL;
 
diff --git a/builtin-blame.c b/builtin-blame.c
index 9b4c02e..bfd562d 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -123,8 +123,7 @@
 static void origin_decref(struct origin *o)
 {
 	if (o && --o->refcnt <= 0) {
-		if (o->file.ptr)
-			free(o->file.ptr);
+		free(o->file.ptr);
 		free(o);
 	}
 }
@@ -1894,9 +1893,7 @@
 
 static const char *add_prefix(const char *prefix, const char *path)
 {
-	if (!prefix || !prefix[0])
-		return path;
-	return prefix_path(prefix, strlen(prefix), path);
+	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
 /*
@@ -2073,7 +2070,7 @@
 		if (strbuf_read(&buf, 0, 0) < 0)
 			die("read error %s from stdin", strerror(errno));
 	}
-	convert_to_git(path, buf.buf, buf.len, &buf);
+	convert_to_git(path, buf.buf, buf.len, &buf, 0);
 	origin->file.ptr = buf.buf;
 	origin->file.size = buf.len;
 	pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
@@ -2092,7 +2089,7 @@
 	if (!mode) {
 		int pos = cache_name_pos(path, len);
 		if (0 <= pos)
-			mode = ntohl(active_cache[pos]->ce_mode);
+			mode = active_cache[pos]->ce_mode;
 		else
 			/* Let's not bother reading from HEAD tree */
 			mode = S_IFREG | 0644;
@@ -2369,7 +2366,8 @@
 	 * bottom commits we would reach while traversing as
 	 * uninteresting.
 	 */
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 
 	if (is_null_sha1(sb.final->object.sha1)) {
 		char *buf;
diff --git a/builtin-branch.c b/builtin-branch.c
index e414c88..5bc4526 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -12,6 +12,7 @@
 #include "builtin.h"
 #include "remote.h"
 #include "parse-options.h"
+#include "branch.h"
 
 static const char * const builtin_branch_usage[] = {
 	"git-branch [options] [-r | -a]",
@@ -29,9 +30,7 @@
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_track = 1;
-
-static int branch_use_color;
+static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
 	"\033[m",	/* reset */
 	"",		/* PLAIN (normal) */
@@ -75,16 +74,12 @@
 		color_parse(value, var, branch_colors[slot]);
 		return 0;
 	}
-	if (!strcmp(var, "branch.autosetupmerge")) {
-		branch_track = git_config_bool(var, value);
-		return 0;
-	}
-	return git_default_config(var, value);
+	return git_color_default_config(var, value);
 }
 
 static const char *branch_get_color(enum color_branch ix)
 {
-	if (branch_use_color)
+	if (branch_use_color > 0)
 		return branch_colors[ix];
 	return "";
 }
@@ -126,8 +121,7 @@
 			continue;
 		}
 
-		if (name)
-			free(name);
+		free(name);
 
 		name = xstrdup(mkpath(fmt, argv[i]));
 		if (!resolve_ref(name, sha1, 1, NULL)) {
@@ -172,8 +166,7 @@
 		}
 	}
 
-	if (name)
-		free(name);
+	free(name);
 
 	return(ret);
 }
@@ -359,141 +352,6 @@
 	free_ref_list(&ref_list);
 }
 
-struct tracking {
-	struct refspec spec;
-	char *src;
-	const char *remote;
-	int matches;
-};
-
-static int find_tracked_branch(struct remote *remote, void *priv)
-{
-	struct tracking *tracking = priv;
-
-	if (!remote_find_tracking(remote, &tracking->spec)) {
-		if (++tracking->matches == 1) {
-			tracking->src = tracking->spec.src;
-			tracking->remote = remote->name;
-		} else {
-			free(tracking->spec.src);
-			if (tracking->src) {
-				free(tracking->src);
-				tracking->src = NULL;
-			}
-		}
-		tracking->spec.src = NULL;
-	}
-
-	return 0;
-}
-
-
-/*
- * This is called when new_ref is branched off of orig_ref, and tries
- * to infer the settings for branch.<new_ref>.{remote,merge} from the
- * config.
- */
-static int setup_tracking(const char *new_ref, const char *orig_ref)
-{
-	char key[1024];
-	struct tracking tracking;
-
-	if (strlen(new_ref) > 1024 - 7 - 7 - 1)
-		return error("Tracking not set up: name too long: %s",
-				new_ref);
-
-	memset(&tracking, 0, sizeof(tracking));
-	tracking.spec.dst = (char *)orig_ref;
-	if (for_each_remote(find_tracked_branch, &tracking) ||
-			!tracking.matches)
-		return 1;
-
-	if (tracking.matches > 1)
-		return error("Not tracking: ambiguous information for ref %s",
-				orig_ref);
-
-	if (tracking.matches == 1) {
-		sprintf(key, "branch.%s.remote", new_ref);
-		git_config_set(key, tracking.remote ?  tracking.remote : ".");
-		sprintf(key, "branch.%s.merge", new_ref);
-		git_config_set(key, tracking.src);
-		free(tracking.src);
-		printf("Branch %s set up to track remote branch %s.\n",
-			       new_ref, orig_ref);
-	}
-
-	return 0;
-}
-
-static void create_branch(const char *name, const char *start_name,
-			  int force, int reflog, int track)
-{
-	struct ref_lock *lock;
-	struct commit *commit;
-	unsigned char sha1[20];
-	char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
-	int forcing = 0;
-
-	snprintf(ref, sizeof ref, "refs/heads/%s", name);
-	if (check_ref_format(ref))
-		die("'%s' is not a valid branch name.", name);
-
-	if (resolve_ref(ref, sha1, 1, NULL)) {
-		if (!force)
-			die("A branch named '%s' already exists.", name);
-		else if (!is_bare_repository() && !strcmp(head, name))
-			die("Cannot force update the current branch.");
-		forcing = 1;
-	}
-
-	real_ref = NULL;
-	if (get_sha1(start_name, sha1))
-		die("Not a valid object name: '%s'.", start_name);
-
-	switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
-	case 0:
-		/* Not branching from any existing branch */
-		real_ref = NULL;
-		break;
-	case 1:
-		/* Unique completion -- good */
-		break;
-	default:
-		die("Ambiguous object name: '%s'.", start_name);
-		break;
-	}
-
-	if ((commit = lookup_commit_reference(sha1)) == NULL)
-		die("Not a valid branch point: '%s'.", start_name);
-	hashcpy(sha1, commit->object.sha1);
-
-	lock = lock_any_ref_for_update(ref, NULL, 0);
-	if (!lock)
-		die("Failed to lock ref for update: %s.", strerror(errno));
-
-	if (reflog)
-		log_all_ref_updates = 1;
-
-	if (forcing)
-		snprintf(msg, sizeof msg, "branch: Reset from %s",
-			 start_name);
-	else
-		snprintf(msg, sizeof msg, "branch: Created from %s",
-			 start_name);
-
-	/* When branching off a remote branch, set up so that git-pull
-	   automatically merges from there.  So far, this is only done for
-	   remotes registered via .git/config.  */
-	if (real_ref && track)
-		setup_tracking(name, real_ref);
-
-	if (write_ref_sha1(lock, sha1, msg) < 0)
-		die("Failed to write ref: %s.", strerror(errno));
-
-	if (real_ref)
-		free(real_ref);
-}
-
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
 	char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
@@ -554,14 +412,16 @@
 {
 	int delete = 0, rename = 0, force_create = 0;
 	int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
-	int reflog = 0, track;
+	int reflog = 0;
+	enum branch_track track;
 	int kinds = REF_LOCAL_BRANCH;
 	struct commit_list *with_commit = NULL;
 
 	struct option options[] = {
 		OPT_GROUP("Generic options"),
 		OPT__VERBOSE(&verbose),
-		OPT_BOOLEAN( 0 , "track",  &track, "set up tracking mode (see git-pull(1))"),
+		OPT_SET_INT( 0 , "track",  &track, "set up tracking mode (see git-pull(1))",
+			BRANCH_TRACK_EXPLICIT),
 		OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
 		OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
 			REF_REMOTE_BRANCH),
@@ -588,7 +448,11 @@
 	};
 
 	git_config(git_branch_config);
-	track = branch_track;
+
+	if (branch_use_color == -1)
+		branch_use_color = git_use_color_default;
+
+	track = git_branch_track;
 	argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
 	if (!!delete + !!rename + !!force_create > 1)
 		usage_with_options(builtin_branch_usage, options);
@@ -614,7 +478,7 @@
 	else if (rename && (argc == 2))
 		rename_branch(argv[0], argv[1], rename > 1);
 	else if (argc <= 2)
-		create_branch(argv[0], (argc == 2) ? argv[1] : head,
+		create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
 			      force_create, reflog, track);
 	else
 		usage_with_options(builtin_branch_usage, options);
diff --git a/builtin-bundle.c b/builtin-bundle.c
index 9f38e21..ac476e7 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -14,7 +14,7 @@
 int cmd_bundle(int argc, const char **argv, const char *prefix)
 {
 	struct bundle_header header;
-	int nongit = 0;
+	int nongit;
 	const char *cmd, *bundle_file;
 	int bundle_fd = -1;
 	char buffer[PATH_MAX];
diff --git a/builtin-checkout.c b/builtin-checkout.c
new file mode 100644
index 0000000..cf9875c
--- /dev/null
+++ b/builtin-checkout.c
@@ -0,0 +1,577 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "commit.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "dir.h"
+#include "run-command.h"
+#include "merge-recursive.h"
+#include "branch.h"
+#include "diff.h"
+#include "revision.h"
+#include "remote.h"
+
+static const char * const checkout_usage[] = {
+	"git checkout [options] <branch>",
+	"git checkout [options] [<branch>] -- <file>...",
+	NULL,
+};
+
+static int post_checkout_hook(struct commit *old, struct commit *new,
+			      int changed)
+{
+	struct child_process proc;
+	const char *name = git_path("hooks/post-checkout");
+	const char *argv[5];
+
+	if (access(name, X_OK) < 0)
+		return 0;
+
+	memset(&proc, 0, sizeof(proc));
+	argv[0] = name;
+	argv[1] = xstrdup(sha1_to_hex(old->object.sha1));
+	argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
+	argv[3] = changed ? "1" : "0";
+	argv[4] = NULL;
+	proc.argv = argv;
+	proc.no_stdin = 1;
+	proc.stdout_to_stderr = 1;
+	return run_command(&proc);
+}
+
+static int update_some(const unsigned char *sha1, const char *base, int baselen,
+		       const char *pathname, unsigned mode, int stage)
+{
+	int len;
+	struct cache_entry *ce;
+
+	if (S_ISGITLINK(mode))
+		return 0;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	len = baselen + strlen(pathname);
+	ce = xcalloc(1, cache_entry_size(len));
+	hashcpy(ce->sha1, sha1);
+	memcpy(ce->name, base, baselen);
+	memcpy(ce->name + baselen, pathname, len - baselen);
+	ce->ce_flags = create_ce_flags(len, 0);
+	ce->ce_mode = create_ce_mode(mode);
+	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+	return 0;
+}
+
+static int read_tree_some(struct tree *tree, const char **pathspec)
+{
+	read_tree_recursive(tree, "", 0, 0, pathspec, update_some);
+
+	/* update the index with the given tree's info
+	 * for all args, expanding wildcards, and exit
+	 * with any non-zero return code.
+	 */
+	return 0;
+}
+
+static int checkout_paths(struct tree *source_tree, const char **pathspec)
+{
+	int pos;
+	struct checkout state;
+	static char *ps_matched;
+	unsigned char rev[20];
+	int flag;
+	struct commit *head;
+
+	int newfd;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	newfd = hold_locked_index(lock_file, 1);
+	read_cache();
+
+	if (source_tree)
+		read_tree_some(source_tree, pathspec);
+
+	for (pos = 0; pathspec[pos]; pos++)
+		;
+	ps_matched = xcalloc(1, pos);
+
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		pathspec_match(pathspec, ps_matched, ce->name, 0);
+	}
+
+	if (report_path_error(ps_matched, pathspec, 0))
+		return 1;
+
+	memset(&state, 0, sizeof(state));
+	state.force = 1;
+	state.refresh_cache = 1;
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+			checkout_entry(ce, &state, NULL);
+		}
+	}
+
+	if (write_cache(newfd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die("unable to write new index file");
+
+	resolve_ref("HEAD", rev, 0, &flag);
+	head = lookup_commit_reference_gently(rev, 1);
+
+	return post_checkout_hook(head, head, 0);
+}
+
+static void show_local_changes(struct object *head)
+{
+	struct rev_info rev;
+	/* I think we want full paths, even if we're in a subdirectory. */
+	init_revisions(&rev, NULL);
+	rev.abbrev = 0;
+	rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+	add_pending_object(&rev, head, NULL);
+	run_diff_index(&rev, 0);
+}
+
+static void describe_detached_head(char *msg, struct commit *commit)
+{
+	struct strbuf sb;
+	strbuf_init(&sb, 0);
+	parse_commit(commit);
+	pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0);
+	fprintf(stderr, "%s %s... %s\n", msg,
+		find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
+	strbuf_release(&sb);
+}
+
+static int reset_to_new(struct tree *tree, int quiet)
+{
+	struct unpack_trees_options opts;
+	struct tree_desc tree_desc;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.update = 1;
+	opts.reset = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = !quiet;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	parse_tree(tree);
+	init_tree_desc(&tree_desc, tree->buffer, tree->size);
+	if (unpack_trees(1, &tree_desc, &opts))
+		return 128;
+	return 0;
+}
+
+static void reset_clean_to_new(struct tree *tree, int quiet)
+{
+	struct unpack_trees_options opts;
+	struct tree_desc tree_desc;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.skip_unmerged = 1;
+	opts.reset = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = !quiet;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	parse_tree(tree);
+	init_tree_desc(&tree_desc, tree->buffer, tree->size);
+	if (unpack_trees(1, &tree_desc, &opts))
+		exit(128);
+}
+
+struct checkout_opts {
+	int quiet;
+	int merge;
+	int force;
+
+	char *new_branch;
+	int new_branch_log;
+	enum branch_track track;
+};
+
+struct branch_info {
+	const char *name; /* The short name used */
+	const char *path; /* The full name of a real branch */
+	struct commit *commit; /* The named commit */
+};
+
+static void setup_branch_path(struct branch_info *branch)
+{
+	struct strbuf buf;
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, branch->name);
+	branch->path = strbuf_detach(&buf, NULL);
+}
+
+static int merge_working_tree(struct checkout_opts *opts,
+			      struct branch_info *old, struct branch_info *new)
+{
+	int ret;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+	int newfd = hold_locked_index(lock_file, 1);
+	read_cache();
+
+	if (opts->force) {
+		ret = reset_to_new(new->commit->tree, opts->quiet);
+		if (ret)
+			return ret;
+	} else {
+		struct tree_desc trees[2];
+		struct tree *tree;
+		struct unpack_trees_options topts;
+
+		memset(&topts, 0, sizeof(topts));
+		topts.head_idx = -1;
+		topts.src_index = &the_index;
+		topts.dst_index = &the_index;
+
+		refresh_cache(REFRESH_QUIET);
+
+		if (unmerged_cache()) {
+			error("you need to resolve your current index first");
+			return 1;
+		}
+
+		/* 2-way merge to the new branch */
+		topts.update = 1;
+		topts.merge = 1;
+		topts.gently = opts->merge;
+		topts.verbose_update = !opts->quiet;
+		topts.fn = twoway_merge;
+		topts.dir = xcalloc(1, sizeof(*topts.dir));
+		topts.dir->show_ignored = 1;
+		topts.dir->exclude_per_dir = ".gitignore";
+		tree = parse_tree_indirect(old->commit->object.sha1);
+		init_tree_desc(&trees[0], tree->buffer, tree->size);
+		tree = parse_tree_indirect(new->commit->object.sha1);
+		init_tree_desc(&trees[1], tree->buffer, tree->size);
+
+		if (unpack_trees(2, trees, &topts)) {
+			/*
+			 * Unpack couldn't do a trivial merge; either
+			 * give up or do a real merge, depending on
+			 * whether the merge flag was used.
+			 */
+			struct tree *result;
+			struct tree *work;
+			if (!opts->merge)
+				return 1;
+			parse_commit(old->commit);
+
+			/* Do more real merge */
+
+			/*
+			 * We update the index fully, then write the
+			 * tree from the index, then merge the new
+			 * branch with the current tree, with the old
+			 * branch as the base. Then we reset the index
+			 * (but not the working tree) to the new
+			 * branch, leaving the working tree as the
+			 * merged version, but skipping unmerged
+			 * entries in the index.
+			 */
+
+			add_files_to_cache(0, NULL, NULL);
+			work = write_tree_from_memory();
+
+			ret = reset_to_new(new->commit->tree, opts->quiet);
+			if (ret)
+				return ret;
+			merge_trees(new->commit->tree, work, old->commit->tree,
+				    new->name, "local", &result);
+			reset_clean_to_new(new->commit->tree, opts->quiet);
+		}
+	}
+
+	if (write_cache(newfd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die("unable to write new index file");
+
+	if (!opts->force)
+		show_local_changes(&new->commit->object);
+
+	return 0;
+}
+
+static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
+{
+	/*
+	 * We have switched to a new branch; is it building on
+	 * top of another branch, and if so does that other branch
+	 * have changes we do not have yet?
+	 */
+	char *base;
+	unsigned char sha1[20];
+	struct commit *ours, *theirs;
+	char symmetric[84];
+	struct rev_info revs;
+	const char *rev_argv[10];
+	int rev_argc;
+	int num_ours, num_theirs;
+	const char *remote_msg;
+	struct branch *branch = branch_get(new->name);
+
+	/*
+	 * Nothing to report unless we are marked to build on top of
+	 * somebody else.
+	 */
+	if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
+		return;
+
+	/*
+	 * If what we used to build on no longer exists, there is
+	 * nothing to report.
+	 */
+	base = branch->merge[0]->dst;
+	if (!resolve_ref(base, sha1, 1, NULL))
+		return;
+
+	theirs = lookup_commit(sha1);
+	ours = new->commit;
+	if (!hashcmp(sha1, ours->object.sha1))
+		return; /* we are the same */
+
+	/* Run "rev-list --left-right ours...theirs" internally... */
+	rev_argc = 0;
+	rev_argv[rev_argc++] = NULL;
+	rev_argv[rev_argc++] = "--left-right";
+	rev_argv[rev_argc++] = symmetric;
+	rev_argv[rev_argc++] = "--";
+	rev_argv[rev_argc] = NULL;
+
+	strcpy(symmetric, sha1_to_hex(ours->object.sha1));
+	strcpy(symmetric + 40, "...");
+	strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
+
+	init_revisions(&revs, NULL);
+	setup_revisions(rev_argc, rev_argv, &revs, NULL);
+	prepare_revision_walk(&revs);
+
+	/* ... and count the commits on each side. */
+	num_ours = 0;
+	num_theirs = 0;
+	while (1) {
+		struct commit *c = get_revision(&revs);
+		if (!c)
+			break;
+		if (c->object.flags & SYMMETRIC_LEFT)
+			num_ours++;
+		else
+			num_theirs++;
+	}
+
+	if (!prefixcmp(base, "refs/remotes/")) {
+		remote_msg = " remote";
+		base += strlen("refs/remotes/");
+	} else {
+		remote_msg = "";
+	}
+
+	if (!num_theirs)
+		printf("Your branch is ahead of the tracked%s branch '%s' "
+		       "by %d commit%s.\n",
+		       remote_msg, base,
+		       num_ours, (num_ours == 1) ? "" : "s");
+	else if (!num_ours)
+		printf("Your branch is behind the tracked%s branch '%s' "
+		       "by %d commit%s,\n"
+		       "and can be fast-forwarded.\n",
+		       remote_msg, base,
+		       num_theirs, (num_theirs == 1) ? "" : "s");
+	else
+		printf("Your branch and the tracked%s branch '%s' "
+		       "have diverged,\nand respectively "
+		       "have %d and %d different commit(s) each.\n",
+		       remote_msg, base,
+		       num_ours, num_theirs);
+}
+
+static void update_refs_for_switch(struct checkout_opts *opts,
+				   struct branch_info *old,
+				   struct branch_info *new)
+{
+	struct strbuf msg;
+	const char *old_desc;
+	if (opts->new_branch) {
+		create_branch(old->name, opts->new_branch, new->name, 0,
+			      opts->new_branch_log, opts->track);
+		new->name = opts->new_branch;
+		setup_branch_path(new);
+	}
+
+	strbuf_init(&msg, 0);
+	old_desc = old->name;
+	if (!old_desc)
+		old_desc = sha1_to_hex(old->commit->object.sha1);
+	strbuf_addf(&msg, "checkout: moving from %s to %s",
+		    old_desc, new->name);
+
+	if (new->path) {
+		create_symref("HEAD", new->path, msg.buf);
+		if (!opts->quiet) {
+			if (old->path && !strcmp(new->path, old->path))
+				fprintf(stderr, "Already on \"%s\"\n",
+					new->name);
+			else
+				fprintf(stderr, "Switched to%s branch \"%s\"\n",
+					opts->new_branch ? " a new" : "",
+					new->name);
+		}
+	} else if (strcmp(new->name, "HEAD")) {
+		update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+			   REF_NODEREF, DIE_ON_ERR);
+		if (!opts->quiet) {
+			if (old->path)
+				fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
+			describe_detached_head("HEAD is now at", new->commit);
+		}
+	}
+	remove_branch_state();
+	strbuf_release(&msg);
+	if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
+		report_tracking(new, opts);
+}
+
+static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
+{
+	int ret = 0;
+	struct branch_info old;
+	unsigned char rev[20];
+	int flag;
+	memset(&old, 0, sizeof(old));
+	old.path = resolve_ref("HEAD", rev, 0, &flag);
+	old.commit = lookup_commit_reference_gently(rev, 1);
+	if (!(flag & REF_ISSYMREF))
+		old.path = NULL;
+
+	if (old.path && !prefixcmp(old.path, "refs/heads/"))
+		old.name = old.path + strlen("refs/heads/");
+
+	if (!new->name) {
+		new->name = "HEAD";
+		new->commit = old.commit;
+		if (!new->commit)
+			die("You are on a branch yet to be born");
+		parse_commit(new->commit);
+	}
+
+	/*
+	 * If the new thing isn't a branch and isn't HEAD and we're
+	 * not starting a new branch, and we want messages, and we
+	 * weren't on a branch, and we're moving to a new commit,
+	 * describe the old commit.
+	 */
+	if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch &&
+	    !opts->quiet && !old.path && new->commit != old.commit)
+		describe_detached_head("Previous HEAD position was", old.commit);
+
+	if (!old.commit) {
+		if (!opts->quiet) {
+			fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
+			fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
+		}
+		opts->force = 1;
+	}
+
+	ret = merge_working_tree(opts, &old, new);
+	if (ret)
+		return ret;
+
+	update_refs_for_switch(opts, &old, new);
+
+	return post_checkout_hook(old.commit, new->commit, 1);
+}
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	unsigned char rev[20];
+	const char *arg;
+	struct branch_info new;
+	struct tree *source_tree = NULL;
+	struct option options[] = {
+		OPT__QUIET(&opts.quiet),
+		OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
+		OPT_SET_INT( 0 , "track",  &opts.track, "track",
+			BRANCH_TRACK_EXPLICIT),
+		OPT_BOOLEAN('f', NULL, &opts.force, "force"),
+		OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
+		OPT_END(),
+	};
+
+	memset(&opts, 0, sizeof(opts));
+	memset(&new, 0, sizeof(new));
+
+	git_config(git_default_config);
+
+	opts.track = git_branch_track;
+
+	argc = parse_options(argc, argv, options, checkout_usage, 0);
+	if (argc) {
+		arg = argv[0];
+		if (get_sha1(arg, rev))
+			;
+		else if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
+			new.name = arg;
+			setup_branch_path(&new);
+			if (resolve_ref(new.path, rev, 1, NULL))
+				new.commit = lookup_commit_reference(rev);
+			else
+				new.path = NULL;
+			parse_commit(new.commit);
+			source_tree = new.commit->tree;
+			argv++;
+			argc--;
+		} else if ((source_tree = parse_tree_indirect(rev))) {
+			argv++;
+			argc--;
+		}
+	}
+
+	if (argc && !strcmp(argv[0], "--")) {
+		argv++;
+		argc--;
+	}
+
+	if (!opts.new_branch && (opts.track != git_branch_track))
+		die("git checkout: --track and --no-track require -b");
+
+	if (opts.force && opts.merge)
+		die("git checkout: -f and -m are incompatible");
+
+	if (argc) {
+		const char **pathspec = get_pathspec(prefix, argv);
+
+		if (!pathspec)
+			die("invalid path specification");
+
+		/* Checkout paths */
+		if (opts.new_branch || opts.force || opts.merge) {
+			if (argc == 1) {
+				die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
+			} else {
+				die("git checkout: updating paths is incompatible with switching branches/forcing");
+			}
+		}
+
+		return checkout_paths(source_tree, pathspec);
+	}
+
+	if (new.name && !new.commit) {
+		die("Cannot switch branch to a non-commit.");
+	}
+
+	return switch_branches(&opts, &new);
+}
diff --git a/builtin-clean.c b/builtin-clean.c
index eb853a3..6778a03 100644
--- a/builtin-clean.c
+++ b/builtin-clean.c
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "quote.h"
 
 static int force = -1; /* unset */
 
@@ -29,12 +30,13 @@
 {
 	int i;
 	int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
-	int ignored_only = 0, baselen = 0, config_set = 0;
+	int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
 	struct strbuf directory;
 	struct dir_struct dir;
 	const char *path, *base;
 	static const char **pathspec;
-	int prefix_offset = 0;
+	struct strbuf buf;
+	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
 		OPT__QUIET(&quiet),
@@ -56,6 +58,7 @@
 
 	argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
 
+	strbuf_init(&buf, 0);
 	memset(&dir, 0, sizeof(dir));
 	if (ignored_only)
 		dir.show_ignored = 1;
@@ -72,8 +75,6 @@
 	if (!ignored)
 		setup_standard_excludes(&dir);
 
-	if (prefix)
-		prefix_offset = strlen(prefix);
 	pathspec = get_pathspec(prefix, argv);
 	read_cache();
 
@@ -94,7 +95,8 @@
 
 	for (i = 0; i < dir.nr; i++) {
 		struct dir_entry *ent = dir.entries[i];
-		int len, pos, matches;
+		int len, pos;
+		int matches = 0;
 		struct cache_entry *ce;
 		struct stat st;
 
@@ -126,47 +128,48 @@
 
 		if (pathspec) {
 			memset(seen, 0, argc > 0 ? argc : 1);
-			matches = match_pathspec(pathspec, ent->name, ent->len,
+			matches = match_pathspec(pathspec, ent->name, len,
 						 baselen, seen);
-		} else {
-			matches = 0;
 		}
 
 		if (S_ISDIR(st.st_mode)) {
 			strbuf_addstr(&directory, ent->name);
-			if (show_only && (remove_directories || matches)) {
-				printf("Would remove %s\n",
-				       directory.buf + prefix_offset);
-			} else if (quiet && (remove_directories || matches)) {
-				remove_dir_recursively(&directory, 0);
-			} else if (remove_directories || matches) {
-				printf("Removing %s\n",
-				       directory.buf + prefix_offset);
-				remove_dir_recursively(&directory, 0);
+			qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
+			if (show_only && (remove_directories ||
+			    (matches == MATCHED_EXACTLY))) {
+				printf("Would remove %s\n", qname);
+			} else if (remove_directories ||
+				   (matches == MATCHED_EXACTLY)) {
+				if (!quiet)
+					printf("Removing %s\n", qname);
+				if (remove_dir_recursively(&directory, 0) != 0) {
+					warning("failed to remove '%s'", qname);
+					errors++;
+				}
 			} else if (show_only) {
-				printf("Would not remove %s\n",
-				       directory.buf + prefix_offset);
+				printf("Would not remove %s\n", qname);
 			} else {
-				printf("Not removing %s\n",
-				       directory.buf + prefix_offset);
+				printf("Not removing %s\n", qname);
 			}
 			strbuf_reset(&directory);
 		} else {
 			if (pathspec && !matches)
 				continue;
+			qname = quote_path_relative(ent->name, -1, &buf, prefix);
 			if (show_only) {
-				printf("Would remove %s\n",
-				       ent->name + prefix_offset);
+				printf("Would remove %s\n", qname);
 				continue;
 			} else if (!quiet) {
-				printf("Removing %s\n",
-				       ent->name + prefix_offset);
+				printf("Removing %s\n", qname);
 			}
-			unlink(ent->name);
+			if (unlink(ent->name) != 0) {
+				warning("failed to remove '%s'", qname);
+				errors++;
+			}
 		}
 	}
 	free(seen);
 
 	strbuf_release(&directory);
-	return 0;
+	return (errors != 0);
 }
diff --git a/builtin-commit.c b/builtin-commit.c
index 2f4d6cc..b0fe69e 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -7,6 +7,7 @@
 
 #include "cache.h"
 #include "cache-tree.h"
+#include "color.h"
 #include "dir.h"
 #include "builtin.h"
 #include "diff.h"
@@ -160,7 +161,7 @@
 
 	for (i = 0; i < active_nr; i++) {
 		struct cache_entry *ce = active_cache[i];
-		if (ce->ce_flags & htons(CE_UPDATE))
+		if (ce->ce_flags & CE_UPDATE)
 			continue;
 		if (!pathspec_match(pattern, m, ce->name, 0))
 			continue;
@@ -197,6 +198,8 @@
 	opts.head_idx = 1;
 	opts.index_only = 1;
 	opts.merge = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
 
 	opts.fn = oneway_merge;
 	tree = parse_tree_indirect(head_sha1);
@@ -204,7 +207,8 @@
 		die("failed to unpack HEAD tree object");
 	parse_tree(tree);
 	init_tree_desc(&t, tree->buffer, tree->size);
-	unpack_trees(1, &t, &opts);
+	if (unpack_trees(1, &t, &opts))
+		exit(128); /* We've already reported the error, finish dying */
 }
 
 static char *prepare_index(int argc, const char **argv, const char *prefix)
@@ -215,6 +219,8 @@
 
 	if (interactive) {
 		interactive_add(argc, argv, prefix);
+		if (read_cache() < 0)
+			die("index file corrupt");
 		commit_style = COMMIT_AS_IS;
 		return get_index_file();
 	}
@@ -347,45 +353,107 @@
 	return s.commitable;
 }
 
+static int run_hook(const char *index_file, const char *name, ...)
+{
+	struct child_process hook;
+	const char *argv[10], *env[2];
+	char index[PATH_MAX];
+	va_list args;
+	int i;
+
+	va_start(args, name);
+	argv[0] = git_path("hooks/%s", name);
+	i = 0;
+	do {
+		if (++i >= ARRAY_SIZE(argv))
+			die ("run_hook(): too many arguments");
+		argv[i] = va_arg(args, const char *);
+	} while (argv[i]);
+	va_end(args);
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+	env[0] = index;
+	env[1] = NULL;
+
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static int is_a_merge(const unsigned char *sha1)
+{
+	struct commit *commit = lookup_commit(sha1);
+	if (!commit || parse_commit(commit))
+		die("could not parse HEAD commit");
+	return !!(commit->parents && commit->parents->next);
+}
+
 static const char sign_off_header[] = "Signed-off-by: ";
 
-static int prepare_log_message(const char *index_file, const char *prefix)
+static int prepare_to_commit(const char *index_file, const char *prefix)
 {
 	struct stat statbuf;
 	int commitable, saved_color_setting;
 	struct strbuf sb;
 	char *buffer;
 	FILE *fp;
+	const char *hook_arg1 = NULL;
+	const char *hook_arg2 = NULL;
+
+	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+		return 0;
 
 	strbuf_init(&sb, 0);
 	if (message.len) {
 		strbuf_addbuf(&sb, &message);
+		hook_arg1 = "message";
 	} else if (logfile && !strcmp(logfile, "-")) {
 		if (isatty(0))
 			fprintf(stderr, "(reading log message from standard input)\n");
 		if (strbuf_read(&sb, 0, 0) < 0)
 			die("could not read log from standard input");
+		hook_arg1 = "message";
 	} else if (logfile) {
 		if (strbuf_read_file(&sb, logfile, 0) < 0)
 			die("could not read log file '%s': %s",
 			    logfile, strerror(errno));
+		hook_arg1 = "message";
 	} else if (use_message) {
 		buffer = strstr(use_message_buffer, "\n\n");
 		if (!buffer || buffer[2] == '\0')
 			die("commit has empty message");
 		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+		hook_arg1 = "commit";
+		hook_arg2 = use_message;
 	} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
 		if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
 			die("could not read MERGE_MSG: %s", strerror(errno));
+		hook_arg1 = "merge";
 	} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
 		if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
 			die("could not read SQUASH_MSG: %s", strerror(errno));
+		hook_arg1 = "squash";
 	} else if (template_file && !stat(template_file, &statbuf)) {
 		if (strbuf_read_file(&sb, template_file, 0) < 0)
 			die("could not read %s: %s",
 			    template_file, strerror(errno));
+		hook_arg1 = "template";
 	}
 
+	/*
+	 * This final case does not modify the template message,
+	 * it just sets the argument to the prepare-commit-msg hook.
+	 */
+	else if (in_merge)
+		hook_arg1 = "merge";
+
 	fp = fopen(git_path(commit_editmsg), "w");
 	if (fp == NULL)
 		die("could not open %s", git_path(commit_editmsg));
@@ -417,13 +485,38 @@
 
 	strbuf_release(&sb);
 
-	if (!use_editor) {
+	if (use_editor) {
+		if (in_merge)
+			fprintf(fp,
+				"#\n"
+				"# It looks like you may be committing a MERGE.\n"
+				"# If this is not correct, please remove the file\n"
+				"#	%s\n"
+				"# and try again.\n"
+				"#\n",
+				git_path("MERGE_HEAD"));
+
+		fprintf(fp,
+			"\n"
+			"# Please enter the commit message for your changes.\n"
+			"# (Comment lines starting with '#' will ");
+		if (cleanup_mode == CLEANUP_ALL)
+			fprintf(fp, "not be included)\n");
+		else /* CLEANUP_SPACE, that is. */
+			fprintf(fp, "be kept.\n"
+				"# You can remove them yourself if you want to)\n");
+		if (only_include_assumed)
+			fprintf(fp, "# %s\n", only_include_assumed);
+
+		saved_color_setting = wt_status_use_color;
+		wt_status_use_color = 0;
+		commitable = run_status(fp, index_file, prefix, 1);
+		wt_status_use_color = saved_color_setting;
+	} else {
 		struct rev_info rev;
 		unsigned char sha1[20];
 		const char *parent = "HEAD";
 
-		fclose(fp);
-
 		if (!active_nr && read_cache() < 0)
 			die("Cannot read index");
 
@@ -431,48 +524,60 @@
 			parent = "HEAD^1";
 
 		if (get_sha1(parent, sha1))
-			return !!active_nr;
+			commitable = !!active_nr;
+		else {
+			init_revisions(&rev, "");
+			rev.abbrev = 0;
+			setup_revisions(0, NULL, &rev, parent);
+			DIFF_OPT_SET(&rev.diffopt, QUIET);
+			DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+			run_diff_index(&rev, 1 /* cached */);
 
-		init_revisions(&rev, "");
-		rev.abbrev = 0;
-		setup_revisions(0, NULL, &rev, parent);
-		DIFF_OPT_SET(&rev.diffopt, QUIET);
-		DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-		run_diff_index(&rev, 1 /* cached */);
-
-		return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+			commitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+		}
 	}
 
-	if (in_merge)
-		fprintf(fp,
-			"#\n"
-			"# It looks like you may be committing a MERGE.\n"
-			"# If this is not correct, please remove the file\n"
-			"#	%s\n"
-			"# and try again.\n"
-			"#\n",
-			git_path("MERGE_HEAD"));
-
-	fprintf(fp,
-		"\n"
-		"# Please enter the commit message for your changes.\n"
-		"# (Comment lines starting with '#' will ");
-	if (cleanup_mode == CLEANUP_ALL)
-		fprintf(fp, "not be included)\n");
-	else /* CLEANUP_SPACE, that is. */
-		fprintf(fp, "be kept.\n"
-			"# You can remove them yourself if you want to)\n");
-	if (only_include_assumed)
-		fprintf(fp, "# %s\n", only_include_assumed);
-
-	saved_color_setting = wt_status_use_color;
-	wt_status_use_color = 0;
-	commitable = run_status(fp, index_file, prefix, 1);
-	wt_status_use_color = saved_color_setting;
-
 	fclose(fp);
 
-	return commitable;
+	if (!commitable && !in_merge && !allow_empty &&
+	    !(amend && is_a_merge(head_sha1))) {
+		run_status(stdout, index_file, prefix, 0);
+		unlink(commit_editmsg);
+		return 0;
+	}
+
+	/*
+	 * Re-read the index as pre-commit hook could have updated it,
+	 * and write it out as a tree.  We must do this before we invoke
+	 * the editor and after we invoke run_status above.
+	 */
+	discard_cache();
+	read_cache_from(index_file);
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
+	if (cache_tree_update(active_cache_tree,
+			      active_cache, active_nr, 0, 0) < 0) {
+		error("Error building trees");
+		return 0;
+	}
+
+	if (run_hook(index_file, "prepare-commit-msg",
+		     git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+		return 0;
+
+	if (use_editor) {
+		char index[PATH_MAX];
+		const char *env[2] = { index, NULL };
+		snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+		launch_editor(git_path(commit_editmsg), NULL, env);
+	}
+
+	if (!no_verify &&
+	    run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+		return 0;
+	}
+
+	return 1;
 }
 
 /*
@@ -569,6 +674,8 @@
 		use_editor = 0;
 	if (edit_flag)
 		use_editor = 1;
+	if (!use_editor)
+		setenv("GIT_EDITOR", ":", 1);
 
 	if (get_sha1("HEAD", head_sha1))
 		initial_commit = 1;
@@ -670,6 +777,9 @@
 
 	git_config(git_status_config);
 
+	if (wt_status_use_color == -1)
+		wt_status_use_color = git_use_color_default;
+
 	argc = parse_and_validate_options(argc, argv, builtin_status_usage);
 
 	index_file = prepare_index(argc, argv, prefix);
@@ -681,31 +791,6 @@
 	return commitable ? 0 : 1;
 }
 
-static int run_hook(const char *index_file, const char *name, const char *arg)
-{
-	struct child_process hook;
-	const char *argv[3], *env[2];
-	char index[PATH_MAX];
-
-	argv[0] = git_path("hooks/%s", name);
-	argv[1] = arg;
-	argv[2] = NULL;
-	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-	env[0] = index;
-	env[1] = NULL;
-
-	if (access(argv[0], X_OK) < 0)
-		return 0;
-
-	memset(&hook, 0, sizeof(hook));
-	hook.argv = argv;
-	hook.no_stdin = 1;
-	hook.stdout_to_stderr = 1;
-	hook.env = env;
-
-	return run_command(&hook);
-}
-
 static void print_summary(const char *prefix, const unsigned char *sha1)
 {
 	struct rev_info rev;
@@ -756,14 +841,6 @@
 	return git_status_config(k, v);
 }
 
-static int is_a_merge(const unsigned char *sha1)
-{
-	struct commit *commit = lookup_commit(sha1);
-	if (!commit || parse_commit(commit))
-		die("could not parse HEAD commit");
-	return !!(commit->parents && commit->parents->next);
-}
-
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
@@ -795,33 +872,13 @@
 
 	index_file = prepare_index(argc, argv, prefix);
 
-	if (!no_verify && run_hook(index_file, "pre-commit", NULL)) {
+	/* Set up everything for writing the commit object.  This includes
+	   running hooks, writing the trees, and interacting with the user.  */
+	if (!prepare_to_commit(index_file, prefix)) {
 		rollback_index_files();
 		return 1;
 	}
 
-	if (!prepare_log_message(index_file, prefix) && !in_merge &&
-	    !allow_empty && !(amend && is_a_merge(head_sha1))) {
-		run_status(stdout, index_file, prefix, 0);
-		rollback_index_files();
-		unlink(commit_editmsg);
-		return 1;
-	}
-
-	/*
-	 * Re-read the index as pre-commit hook could have updated it,
-	 * and write it out as a tree.
-	 */
-	discard_cache();
-	read_cache_from(index_file);
-	if (!active_cache_tree)
-		active_cache_tree = cache_tree();
-	if (cache_tree_update(active_cache_tree,
-			      active_cache, active_nr, 0, 0) < 0) {
-		rollback_index_files();
-		die("Error building trees");
-	}
-
 	/*
 	 * The commit object
 	 */
@@ -873,19 +930,8 @@
 		strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
 	strbuf_addch(&sb, '\n');
 
-	/* Get the commit message and validate it */
+	/* Finally, get the commit message */
 	header_len = sb.len;
-	if (use_editor) {
-		char index[PATH_MAX];
-		const char *env[2] = { index, NULL };
-		snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-		launch_editor(git_path(commit_editmsg), NULL, env);
-	}
-	if (!no_verify &&
-	    run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
-		rollback_index_files();
-		exit(1);
-	}
 	if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
 		rollback_index_files();
 		die("could not read commit message");
diff --git a/builtin-config.c b/builtin-config.c
index 077d8ef..c34bc8b 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -79,9 +79,10 @@
 		local = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!local)
 			local = repo_config = xstrdup(git_path("config"));
-		if (home)
+		if (git_config_global() && home)
 			global = xstrdup(mkpath("%s/.gitconfig", home));
-		system_wide = git_etc_gitconfig();
+		if (git_config_system())
+			system_wide = git_etc_gitconfig();
 	}
 
 	key = xstrdup(key_);
@@ -263,7 +264,7 @@
 
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
-	int nongit = 0;
+	int nongit;
 	char* value;
 	const char *file = setup_git_directory_gently(&nongit);
 
diff --git a/builtin-describe.c b/builtin-describe.c
index 7a148a2..3da99c1 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -17,11 +17,16 @@
 static int debug;	/* Display lots of verbose info */
 static int all;	/* Default to annotated tags only */
 static int tags;	/* But allow any tags if --tags is specified */
+static int longformat;
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
+const char *pattern = NULL;
+static int always;
 
 struct commit_name {
+	struct tag *tag;
 	int prio; /* annotated tag = 2, tag = 1, head = 0 */
+	unsigned char sha1[20];
 	char path[FLEX_ARRAY]; /* more */
 };
 static const char *prio_names[] = {
@@ -30,14 +35,17 @@
 
 static void add_to_known_names(const char *path,
 			       struct commit *commit,
-			       int prio)
+			       int prio,
+			       const unsigned char *sha1)
 {
 	struct commit_name *e = commit->util;
 	if (!e || e->prio < prio) {
 		size_t len = strlen(path)+1;
 		free(e);
 		e = xmalloc(sizeof(struct commit_name) + len);
+		e->tag = NULL;
 		e->prio = prio;
+		hashcpy(e->sha1, sha1);
 		memcpy(e->path, path, len);
 		commit->util = e;
 	}
@@ -45,22 +53,40 @@
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	int might_be_tag = !prefixcmp(path, "refs/tags/");
+	struct commit *commit;
 	struct object *object;
-	int prio;
+	unsigned char peeled[20];
+	int is_tag, prio;
 
-	if (!commit)
+	if (!all && !might_be_tag)
 		return 0;
-	object = parse_object(sha1);
+
+	if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
+		commit = lookup_commit_reference_gently(peeled, 1);
+		if (!commit)
+			return 0;
+		is_tag = !!hashcmp(sha1, commit->object.sha1);
+	} else {
+		commit = lookup_commit_reference_gently(sha1, 1);
+		object = parse_object(sha1);
+		if (!commit || !object)
+			return 0;
+		is_tag = object->type == OBJ_TAG;
+	}
+
 	/* If --all, then any refs are used.
 	 * If --tags, then any tags are used.
 	 * Otherwise only annotated tags are used.
 	 */
-	if (!prefixcmp(path, "refs/tags/")) {
-		if (object->type == OBJ_TAG)
+	if (might_be_tag) {
+		if (is_tag)
 			prio = 2;
 		else
 			prio = 1;
+
+		if (pattern && fnmatch(pattern, path + 10, 0))
+			prio = 0;
 	}
 	else
 		prio = 0;
@@ -71,7 +97,7 @@
 		if (!tags && prio < 2)
 			return 0;
 	}
-	add_to_known_names(all ? path + 5 : path + 10, commit, prio);
+	add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1);
 	return 0;
 }
 
@@ -128,6 +154,27 @@
 	return seen_commits;
 }
 
+static void display_name(struct commit_name *n)
+{
+	if (n->prio == 2 && !n->tag) {
+		n->tag = lookup_tag(n->sha1);
+		if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+			die("annotated tag %s not available", n->path);
+		if (strcmp(n->tag->tag, n->path))
+			warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+	}
+
+	if (n->tag)
+		printf("%s", n->tag->tag);
+	else
+		printf("%s", n->path);
+}
+
+static void show_suffix(int depth, const unsigned char *sha1)
+{
+	printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
+}
+
 static void describe(const char *arg, int last_one)
 {
 	unsigned char sha1[20];
@@ -152,10 +199,18 @@
 
 	n = cmit->util;
 	if (n) {
-		printf("%s\n", n->path);
+		/*
+		 * Exact match to an existing ref.
+		 */
+		display_name(n);
+		if (longformat)
+			show_suffix(0, n->tag->tagged->sha1);
+		printf("\n");
 		return;
 	}
 
+	if (!max_candidates)
+		die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
 	if (debug)
 		fprintf(stderr, "searching to describe %s\n", arg);
 
@@ -204,8 +259,14 @@
 		}
 	}
 
-	if (!match_cnt)
-		die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+	if (!match_cnt) {
+		const unsigned char *sha1 = cmit->object.sha1;
+		if (always) {
+			printf("%s\n", find_unique_abbrev(sha1, abbrev));
+			return;
+		}
+		die("cannot describe '%s'", sha1_to_hex(sha1));
+	}
 
 	qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
 
@@ -232,12 +293,11 @@
 				sha1_to_hex(gave_up_on->object.sha1));
 		}
 	}
-	if (abbrev == 0)
-		printf("%s\n", all_matches[0].name->path );
-	else
-		printf("%s-%d-g%s\n", all_matches[0].name->path,
-		       all_matches[0].depth,
-		       find_unique_abbrev(cmit->object.sha1, abbrev));
+
+	display_name(all_matches[0].name);
+	if (abbrev)
+		show_suffix(all_matches[0].depth, cmit->object.sha1);
+	printf("\n");
 
 	if (!last_one)
 		clear_commit_marks(cmit, -1);
@@ -251,27 +311,46 @@
 		OPT_BOOLEAN(0, "debug",      &debug, "debug search strategy on stderr"),
 		OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
 		OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
+		OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
 		OPT__ABBREV(&abbrev),
+		OPT_SET_INT(0, "exact-match", &max_candidates,
+			    "only output exact matches", 0),
 		OPT_INTEGER(0, "candidates", &max_candidates,
-					"consider <n> most recent tags (default: 10)"),
+			    "consider <n> most recent tags (default: 10)"),
+		OPT_STRING(0, "match",       &pattern, "pattern",
+			   "only consider tags matching <pattern>"),
+		OPT_BOOLEAN(0, "always",     &always,
+			   "show abbreviated commit object as fallback"),
 		OPT_END(),
 	};
 
 	argc = parse_options(argc, argv, options, describe_usage, 0);
-	if (max_candidates < 1)
-		max_candidates = 1;
+	if (max_candidates < 0)
+		max_candidates = 0;
 	else if (max_candidates > MAX_TAGS)
 		max_candidates = MAX_TAGS;
 
 	save_commit_buffer = 0;
 
+	if (longformat && abbrev == 0)
+		die("--long is incompatible with --abbrev=0");
+
 	if (contains) {
-		const char **args = xmalloc((4 + argc) * sizeof(char*));
+		const char **args = xmalloc((7 + argc) * sizeof(char*));
 		int i = 0;
 		args[i++] = "name-rev";
 		args[i++] = "--name-only";
-		if (!all)
+		args[i++] = "--no-undefined";
+		if (always)
+			args[i++] = "--always";
+		if (!all) {
 			args[i++] = "--tags";
+			if (pattern) {
+				char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
+				sprintf(s, "--refs=refs/tags/%s", pattern);
+				args[i++] = s;
+			}
+		}
 		memcpy(args + i, argv, argc * sizeof(char*));
 		args[i + argc] = NULL;
 		return cmd_name_rev(i + argc, args, prefix);
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index 4abe3c2..e2306c1 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -16,7 +16,7 @@
 int cmd_diff_files(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
-	int nongit = 0;
+	int nongit;
 	int result;
 
 	prefix = setup_git_directory_gently(&nongit);
diff --git a/builtin-diff.c b/builtin-diff.c
index 8d7a569..7c2a841 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -4,6 +4,7 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 #include "cache.h"
+#include "color.h"
 #include "commit.h"
 #include "blob.h"
 #include "tag.h"
@@ -43,12 +44,17 @@
 		tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
 		tmp_c = old_name; old_name = new_name; new_name = tmp_c;
 	}
+
+	if (opt->prefix &&
+	    (strncmp(old_name, opt->prefix, opt->prefix_length) ||
+	     strncmp(new_name, opt->prefix, opt->prefix_length)))
+		return;
+
 	one = alloc_filespec(old_name);
 	two = alloc_filespec(new_name);
 	fill_filespec(one, old_sha1, old_mode);
 	fill_filespec(two, new_sha1, new_mode);
 
-	/* NEEDSWORK: shouldn't this part of diffopt??? */
 	diff_queue(&diff_queued_diff, one, two);
 }
 
@@ -204,7 +210,7 @@
 	int ents = 0, blobs = 0, paths = 0;
 	const char *path = NULL;
 	struct blobinfo blob[2];
-	int nongit = 0;
+	int nongit;
 	int result = 0;
 
 	/*
@@ -229,6 +235,10 @@
 
 	prefix = setup_git_directory_gently(&nongit);
 	git_config(git_diff_ui_config);
+
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
 	init_revisions(&rev, prefix);
 	rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
 
@@ -241,6 +251,10 @@
 		if (diff_setup_done(&rev.diffopt) < 0)
 			die("diff_setup_done failed");
 	}
+	if (rev.diffopt.prefix && nongit) {
+		rev.diffopt.prefix = NULL;
+		rev.diffopt.prefix_length = 0;
+	}
 	DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
 	DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
 
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
old mode 100755
new mode 100644
index 4bf5b58..4ab93fc
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -196,8 +196,7 @@
 			  ? strlen(reencoded) : message
 			  ? strlen(message) : 0),
 	       reencoded ? reencoded : message ? message : "");
-	if (reencoded)
-		free(reencoded);
+	free(reencoded);
 
 	for (i = 0, p = commit->parents; p; p = p->next) {
 		int mark = get_object_mark(&p->item->object);
@@ -379,7 +378,8 @@
 
 	get_tags_and_duplicates(&revs.pending, &extra_refs);
 
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 	revs.diffopt.format_callback = show_filemodify;
 	DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
 	while ((commit = get_revision(&revs))) {
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index e68e015..c97a427 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -7,6 +7,7 @@
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
+#include "remote.h"
 #include "run-command.h"
 
 static int transfer_unpack_limit = -1;
@@ -17,7 +18,7 @@
 };
 
 static const char fetch_pack_usage[] =
-"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
 
 #define COMPLETE	(1U << 0)
 #define COMMON		(1U << 1)
@@ -25,6 +26,8 @@
 #define SEEN		(1U << 3)
 #define POPPED		(1U << 4)
 
+static int marked;
+
 /*
  * After sending this many "have"s if we do not get any new ACK , we
  * give up traversing our history.
@@ -40,7 +43,8 @@
 		commit->object.flags |= mark;
 
 		if (!(commit->object.parsed))
-			parse_commit(commit);
+			if (parse_commit(commit))
+				return;
 
 		insert_by_date(commit, &rev_list);
 
@@ -59,6 +63,16 @@
 	return 0;
 }
 
+static int clear_marks(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct object *o = deref_tag(parse_object(sha1), path, 0);
+
+	if (o && o->type == OBJ_COMMIT)
+		clear_commit_marks((struct commit *)o,
+				   COMMON | COMMON_REF | SEEN | POPPED);
+	return 0;
+}
+
 /*
    This function marks a rev and its ancestors as common.
    In some cases, it is desirable to mark only the ancestors (for example
@@ -82,7 +96,8 @@
 			if (!ancestors_only && !(o->flags & POPPED))
 				non_common_revs--;
 			if (!o->parsed && !dont_parse)
-				parse_commit(commit);
+				if (parse_commit(commit))
+					return;
 
 			for (parents = commit->parents;
 					parents;
@@ -102,20 +117,20 @@
 
 	while (commit == NULL) {
 		unsigned int mark;
-		struct commit_list* parents;
+		struct commit_list *parents;
 
 		if (rev_list == NULL || non_common_revs == 0)
 			return NULL;
 
 		commit = rev_list->item;
-		if (!(commit->object.parsed))
+		if (!commit->object.parsed)
 			parse_commit(commit);
+		parents = commit->parents;
+
 		commit->object.flags |= POPPED;
 		if (!(commit->object.flags & COMMON))
 			non_common_revs--;
 
-		parents = commit->parents;
-
 		if (commit->object.flags & COMMON) {
 			/* do not send "have", and ignore ancestors */
 			commit = NULL;
@@ -150,6 +165,10 @@
 	unsigned in_vain = 0;
 	int got_continue = 0;
 
+	if (marked)
+		for_each_ref(clear_marks, NULL);
+	marked = 1;
+
 	for_each_ref(rev_list_insert_ref, NULL);
 
 	fetching = 0;
@@ -173,13 +192,14 @@
 		}
 
 		if (!fetching)
-			packet_write(fd[1], "want %s%s%s%s%s%s%s\n",
+			packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
 				     sha1_to_hex(remote),
 				     (multi_ack ? " multi_ack" : ""),
 				     (use_sideband == 2 ? " side-band-64k" : ""),
 				     (use_sideband == 1 ? " side-band" : ""),
 				     (args.use_thin_pack ? " thin-pack" : ""),
 				     (args.no_progress ? " no-progress" : ""),
+				     (args.include_tag ? " include-tag" : ""),
 				     " ofs-delta");
 		else
 			packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -211,7 +231,8 @@
 				if (!lookup_object(sha1))
 					die("object not found: %s", line);
 				/* make sure that it is parsed as shallow */
-				parse_object(sha1);
+				if (!parse_object(sha1))
+					die("error in object: %s", line);
 				if (unregister_shallow(sha1))
 					die("no shallow found: %s", line);
 				continue;
@@ -385,7 +406,6 @@
 	int retval;
 	unsigned long cutoff = 0;
 
-	track_object_refs = 0;
 	save_commit_buffer = 0;
 
 	for (ref = *refs; ref; ref = ref->next) {
@@ -537,8 +557,10 @@
 	cmd.git_cmd = 1;
 	if (start_command(&cmd))
 		die("fetch-pack: unable to fork off %s", argv[0]);
-	if (do_keep && pack_lockfile)
+	if (do_keep && pack_lockfile) {
 		*pack_lockfile = index_pack_lockfile(cmd.out);
+		close(cmd.out);
+	}
 
 	if (finish_command(&cmd))
 		die("%s failed", argv[0]);
@@ -548,14 +570,14 @@
 }
 
 static struct ref *do_fetch_pack(int fd[2],
+		const struct ref *orig_ref,
 		int nr_match,
 		char **match,
 		char **pack_lockfile)
 {
-	struct ref *ref;
+	struct ref *ref = copy_ref_list(orig_ref);
 	unsigned char sha1[20];
 
-	get_remote_heads(fd[0], &ref, 0, NULL, 0);
 	if (is_repository_shallow() && !server_supports("shallow"))
 		die("Server does not support shallow clients");
 	if (server_supports("multi_ack")) {
@@ -573,10 +595,6 @@
 			fprintf(stderr, "Server supports side-band\n");
 		use_sideband = 1;
 	}
-	if (!ref) {
-		packet_flush(fd[1]);
-		die("no matching remote head");
-	}
 	if (everything_local(&ref, nr_match, match)) {
 		packet_flush(fd[1]);
 		goto all_done;
@@ -650,8 +668,10 @@
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 {
 	int i, ret, nr_heads;
-	struct ref *ref;
+	struct ref *ref = NULL;
 	char *dest = NULL, **heads;
+	int fd[2];
+	struct child_process *conn;
 
 	nr_heads = 0;
 	heads = NULL;
@@ -680,6 +700,10 @@
 				args.use_thin_pack = 1;
 				continue;
 			}
+			if (!strcmp("--include-tag", arg)) {
+				args.include_tag = 1;
+				continue;
+			}
 			if (!strcmp("--all", arg)) {
 				args.fetch_all = 1;
 				continue;
@@ -706,45 +730,20 @@
 	if (!dest)
 		usage(fetch_pack_usage);
 
-	ref = fetch_pack(&args, dest, nr_heads, heads, NULL);
-	ret = !ref;
-
-	while (ref) {
-		printf("%s %s\n",
-		       sha1_to_hex(ref->old_sha1), ref->name);
-		ref = ref->next;
-	}
-
-	return ret;
-}
-
-struct ref *fetch_pack(struct fetch_pack_args *my_args,
-		const char *dest,
-		int nr_heads,
-		char **heads,
-		char **pack_lockfile)
-{
-	int i, ret;
-	int fd[2];
-	struct child_process *conn;
-	struct ref *ref;
-	struct stat st;
-
-	fetch_pack_setup();
-	memcpy(&args, my_args, sizeof(args));
-	if (args.depth > 0) {
-		if (stat(git_path("shallow"), &st))
-			st.st_mtime = 0;
-	}
-
 	conn = git_connect(fd, (char *)dest, args.uploadpack,
-                          args.verbose ? CONNECT_VERBOSE : 0);
-	if (heads && nr_heads)
-		nr_heads = remove_duplicates(nr_heads, heads);
-	ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
-	close(fd[0]);
-	close(fd[1]);
-	ret = finish_connect(conn);
+			   args.verbose ? CONNECT_VERBOSE : 0);
+	if (conn) {
+		get_remote_heads(fd[0], &ref, 0, NULL, 0);
+
+		ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
+		close(fd[0]);
+		close(fd[1]);
+		if (finish_connect(conn))
+			ref = NULL;
+	} else {
+		ref = NULL;
+	}
+	ret = !ref;
 
 	if (!ret && nr_heads) {
 		/* If the heads to pull were given, we should have
@@ -758,8 +757,42 @@
 				ret = 1;
 			}
 	}
+	while (ref) {
+		printf("%s %s\n",
+		       sha1_to_hex(ref->old_sha1), ref->name);
+		ref = ref->next;
+	}
 
-	if (!ret && args.depth > 0) {
+	return ret;
+}
+
+struct ref *fetch_pack(struct fetch_pack_args *my_args,
+		       int fd[], struct child_process *conn,
+		       const struct ref *ref,
+		const char *dest,
+		int nr_heads,
+		char **heads,
+		char **pack_lockfile)
+{
+	struct stat st;
+	struct ref *ref_cpy;
+
+	fetch_pack_setup();
+	memcpy(&args, my_args, sizeof(args));
+	if (args.depth > 0) {
+		if (stat(git_path("shallow"), &st))
+			st.st_mtime = 0;
+	}
+
+	if (heads && nr_heads)
+		nr_heads = remove_duplicates(nr_heads, heads);
+	if (!ref) {
+		packet_flush(fd[1]);
+		die("no matching remote head");
+	}
+	ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
+
+	if (args.depth > 0) {
 		struct cache_time mtime;
 		char *shallow = git_path("shallow");
 		int fd;
@@ -787,8 +820,5 @@
 		}
 	}
 
-	if (ret)
-		ref = NULL;
-
-	return ref;
+	return ref_cpy;
 }
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 6fd006f..167f948 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -103,6 +103,10 @@
 	}
 }
 
+static void find_non_local_tags(struct transport *transport,
+			struct ref **head,
+			struct ref ***tail);
+
 static struct ref *get_ref_map(struct transport *transport,
 			       struct refspec *refs, int ref_count, int tags,
 			       int *autotags)
@@ -159,8 +163,11 @@
 			if (!ref_map)
 				die("Couldn't find remote ref HEAD");
 			ref_map->merge = 1;
+			tail = &ref_map->next;
 		}
 	}
+	if (tags == TAGS_DEFAULT && *autotags)
+		find_non_local_tags(transport, &ref_map, &tail);
 	ref_remove_duplicates(ref_map);
 
 	return ref_map;
@@ -208,13 +215,6 @@
 	if (type < 0)
 		die("object %s not found", sha1_to_hex(ref->new_sha1));
 
-	if (!*ref->name) {
-		/* Not storing */
-		if (verbose)
-			sprintf(display, "* branch %s -> FETCH_HEAD", remote);
-		return 0;
-	}
-
 	if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
 		if (verbose)
 			sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
@@ -358,16 +358,21 @@
 			rm->merge ? "" : "not-for-merge",
 			note);
 
-		if (ref) {
+		if (ref)
 			update_local_ref(ref, what, verbose, note);
-			if (*note) {
-				if (!shown_url) {
-					fprintf(stderr, "From %.*s\n",
-							url_len, url);
-					shown_url = 1;
-				}
-				fprintf(stderr, " %s\n", note);
+		else if (verbose)
+			sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
+				SUMMARY_WIDTH, *kind ? kind : "branch",
+				 REFCOL_WIDTH, *what ? what : "HEAD");
+		else
+			*note = '\0';
+		if (*note) {
+			if (!shown_url) {
+				fprintf(stderr, "From %.*s\n",
+						url_len, url);
+				shown_url = 1;
 			}
+			fprintf(stderr, " %s\n", note);
 		}
 	}
 	fclose(fp);
@@ -454,18 +459,28 @@
 	return 0;
 }
 
-static struct ref *find_non_local_tags(struct transport *transport,
-				       struct ref *fetch_map)
+static int will_fetch(struct ref **head, const unsigned char *sha1)
 {
-	static struct path_list existing_refs = { NULL, 0, 0, 0 };
+	struct ref *rm = *head;
+	while (rm) {
+		if (!hashcmp(rm->old_sha1, sha1))
+			return 1;
+		rm = rm->next;
+	}
+	return 0;
+}
+
+static void find_non_local_tags(struct transport *transport,
+			struct ref **head,
+			struct ref ***tail)
+{
+	struct path_list existing_refs = { NULL, 0, 0, 0 };
 	struct path_list new_refs = { NULL, 0, 0, 1 };
 	char *ref_name;
 	int ref_name_len;
 	const unsigned char *ref_sha1;
 	const struct ref *tag_ref;
 	struct ref *rm = NULL;
-	struct ref *ref_map = NULL;
-	struct ref **tail = &ref_map;
 	const struct ref *ref;
 
 	for_each_ref(add_existing, &existing_refs);
@@ -491,7 +506,8 @@
 
 		if (!path_list_has_path(&existing_refs, ref_name) &&
 		    !path_list_has_path(&new_refs, ref_name) &&
-		    has_sha1_file(ref->old_sha1)) {
+		    (has_sha1_file(ref->old_sha1) ||
+		     will_fetch(head, ref->old_sha1))) {
 			path_list_insert(ref_name, &new_refs);
 
 			rm = alloc_ref(strlen(ref_name) + 1);
@@ -500,19 +516,19 @@
 			strcpy(rm->peer_ref->name, ref_name);
 			hashcpy(rm->old_sha1, ref_sha1);
 
-			*tail = rm;
-			tail = &rm->next;
+			**tail = rm;
+			*tail = &rm->next;
 		}
 		free(ref_name);
 	}
-
-	return ref_map;
+	path_list_clear(&existing_refs, 0);
+	path_list_clear(&new_refs, 0);
 }
 
 static int do_fetch(struct transport *transport,
 		    struct refspec *refs, int ref_count)
 {
-	struct ref *ref_map, *fetch_map;
+	struct ref *ref_map;
 	struct ref *rm;
 	int autotags = (transport->remote->fetch_tags == 1);
 	if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
@@ -539,26 +555,28 @@
 			read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
 	}
 
+	if (tags == TAGS_DEFAULT && autotags)
+		transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
 	if (fetch_refs(transport, ref_map)) {
 		free_refs(ref_map);
 		return 1;
 	}
-
-	fetch_map = ref_map;
+	free_refs(ref_map);
 
 	/* if neither --no-tags nor --tags was specified, do automated tag
 	 * following ... */
 	if (tags == TAGS_DEFAULT && autotags) {
-		ref_map = find_non_local_tags(transport, fetch_map);
+		struct ref **tail = &ref_map;
+		ref_map = NULL;
+		find_non_local_tags(transport, &ref_map, &tail);
 		if (ref_map) {
+			transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
 			transport_set_option(transport, TRANS_OPT_DEPTH, "0");
 			fetch_refs(transport, ref_map);
 		}
 		free_refs(ref_map);
 	}
 
-	free_refs(fetch_map);
-
 	return 0;
 }
 
@@ -579,6 +597,7 @@
 	int i;
 	static const char **refs = NULL;
 	int ref_nr = 0;
+	int exit_code;
 
 	/* Record the command line for the reflog */
 	strbuf_addstr(&default_rla, "fetch");
@@ -615,6 +634,8 @@
 			if (!strcmp(argv[i], "tag")) {
 				char *ref;
 				i++;
+				if (i >= argc)
+					die("You need to specify a tag name.");
 				ref = xmalloc(strlen(argv[i]) * 2 + 22);
 				strcpy(ref, "refs/tags/");
 				strcat(ref, argv[i]);
@@ -630,6 +651,9 @@
 
 	signal(SIGINT, unlock_pack_on_signal);
 	atexit(unlock_pack);
-	return do_fetch(transport,
+	exit_code = do_fetch(transport,
 			parse_fetch_refspec(ref_nr, refs), ref_nr);
+	transport_disconnect(transport);
+	transport = NULL;
+	return exit_code;
 }
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index 03c2bc3..7077d52 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -187,7 +187,8 @@
 	add_pending_object(rev, branch, name);
 	add_pending_object(rev, &head->object, "^HEAD");
 	head->object.flags |= UNINTERESTING;
-	prepare_revision_walk(rev);
+	if (prepare_revision_walk(rev))
+		die("revision walk setup failed");
 	while ((commit = get_revision(rev)) != NULL) {
 		char *oneline, *bol, *eol;
 
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index f36a43c..07d9c57 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -165,7 +165,7 @@
 	for (cp = format; *cp && (sp = find_next(cp)); ) {
 		const char *ep = strchr(sp, ')');
 		if (!ep)
-			return error("malformatted format string %s", sp);
+			return error("malformed format string %s", sp);
 		/* sp points at "%(" and ep points at the closing ")" */
 		parse_atom(sp + 2, ep);
 		cp = ep + 1;
diff --git a/builtin-fsck.c b/builtin-fsck.c
index 2a6e94d..78a6e1f 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -8,6 +8,7 @@
 #include "pack.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
+#include "fsck.h"
 #include "parse-options.h"
 
 #define REACHABLE 0x0001
@@ -54,13 +55,75 @@
 	return -1;
 }
 
-static int objwarning(struct object *obj, const char *err, ...)
+static int fsck_error_func(struct object *obj, int type, const char *err, ...)
 {
 	va_list params;
 	va_start(params, err);
-	objreport(obj, "warning", err, params);
+	objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
 	va_end(params);
-	return -1;
+	return (type == FSCK_WARN) ? 0 : 1;
+}
+
+static int mark_object(struct object *obj, int type, void *data)
+{
+	struct tree *tree = NULL;
+	struct object *parent = data;
+	int result;
+
+	if (!obj) {
+		printf("broken link from %7s %s\n",
+			   typename(parent->type), sha1_to_hex(parent->sha1));
+		printf("broken link from %7s %s\n",
+			   (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
+		errors_found |= ERROR_REACHABLE;
+		return 1;
+	}
+
+	if (type != OBJ_ANY && obj->type != type)
+		objerror(parent, "wrong object type in link");
+
+	if (obj->flags & REACHABLE)
+		return 0;
+	obj->flags |= REACHABLE;
+	if (!obj->parsed) {
+		if (parent && !has_sha1_file(obj->sha1)) {
+			printf("broken link from %7s %s\n",
+				 typename(parent->type), sha1_to_hex(parent->sha1));
+			printf("              to %7s %s\n",
+				 typename(obj->type), sha1_to_hex(obj->sha1));
+			errors_found |= ERROR_REACHABLE;
+		}
+		return 1;
+	}
+
+	if (obj->type == OBJ_TREE) {
+		obj->parsed = 0;
+		tree = (struct tree *)obj;
+		if (parse_tree(tree) < 0)
+			return 1; /* error already displayed */
+	}
+	result = fsck_walk(obj, mark_object, obj);
+	if (tree) {
+		free(tree->buffer);
+		tree->buffer = NULL;
+	}
+	if (result < 0)
+		result = 1;
+
+	return result;
+}
+
+static void mark_object_reachable(struct object *obj)
+{
+	mark_object(obj, OBJ_ANY, 0);
+}
+
+static int mark_used(struct object *obj, int type, void *data)
+{
+	if (!obj)
+		return 1;
+	obj->used = 1;
+	return 0;
 }
 
 /*
@@ -68,8 +131,6 @@
  */
 static void check_reachable_object(struct object *obj)
 {
-	const struct object_refs *refs;
-
 	/*
 	 * We obviously want the object to be parsed,
 	 * except if it was in a pack-file and we didn't
@@ -82,25 +143,6 @@
 		errors_found |= ERROR_REACHABLE;
 		return;
 	}
-
-	/*
-	 * Check that everything that we try to reference is also good.
-	 */
-	refs = lookup_object_refs(obj);
-	if (refs) {
-		unsigned j;
-		for (j = 0; j < refs->count; j++) {
-			struct object *ref = refs->ref[j];
-			if (ref->parsed ||
-			    (has_sha1_file(ref->sha1)))
-				continue;
-			printf("broken link from %7s %s\n",
-			       typename(obj->type), sha1_to_hex(obj->sha1));
-			printf("              to %7s %s\n",
-			       typename(ref->type), sha1_to_hex(ref->sha1));
-			errors_found |= ERROR_REACHABLE;
-		}
-	}
 }
 
 /*
@@ -204,205 +246,6 @@
 	}
 }
 
-/*
- * The entries in a tree are ordered in the _path_ order,
- * which means that a directory entry is ordered by adding
- * a slash to the end of it.
- *
- * So a directory called "a" is ordered _after_ a file
- * called "a.c", because "a/" sorts after "a.c".
- */
-#define TREE_UNORDERED (-1)
-#define TREE_HAS_DUPS  (-2)
-
-static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
-{
-	int len1 = strlen(name1);
-	int len2 = strlen(name2);
-	int len = len1 < len2 ? len1 : len2;
-	unsigned char c1, c2;
-	int cmp;
-
-	cmp = memcmp(name1, name2, len);
-	if (cmp < 0)
-		return 0;
-	if (cmp > 0)
-		return TREE_UNORDERED;
-
-	/*
-	 * Ok, the first <len> characters are the same.
-	 * Now we need to order the next one, but turn
-	 * a '\0' into a '/' for a directory entry.
-	 */
-	c1 = name1[len];
-	c2 = name2[len];
-	if (!c1 && !c2)
-		/*
-		 * git-write-tree used to write out a nonsense tree that has
-		 * entries with the same name, one blob and one tree.  Make
-		 * sure we do not have duplicate entries.
-		 */
-		return TREE_HAS_DUPS;
-	if (!c1 && S_ISDIR(mode1))
-		c1 = '/';
-	if (!c2 && S_ISDIR(mode2))
-		c2 = '/';
-	return c1 < c2 ? 0 : TREE_UNORDERED;
-}
-
-static int fsck_tree(struct tree *item)
-{
-	int retval;
-	int has_full_path = 0;
-	int has_empty_name = 0;
-	int has_zero_pad = 0;
-	int has_bad_modes = 0;
-	int has_dup_entries = 0;
-	int not_properly_sorted = 0;
-	struct tree_desc desc;
-	unsigned o_mode;
-	const char *o_name;
-	const unsigned char *o_sha1;
-
-	if (verbose)
-		fprintf(stderr, "Checking tree %s\n",
-				sha1_to_hex(item->object.sha1));
-
-	init_tree_desc(&desc, item->buffer, item->size);
-
-	o_mode = 0;
-	o_name = NULL;
-	o_sha1 = NULL;
-	while (desc.size) {
-		unsigned mode;
-		const char *name;
-		const unsigned char *sha1;
-
-		sha1 = tree_entry_extract(&desc, &name, &mode);
-
-		if (strchr(name, '/'))
-			has_full_path = 1;
-		if (!*name)
-			has_empty_name = 1;
-		has_zero_pad |= *(char *)desc.buffer == '0';
-		update_tree_entry(&desc);
-
-		switch (mode) {
-		/*
-		 * Standard modes..
-		 */
-		case S_IFREG | 0755:
-		case S_IFREG | 0644:
-		case S_IFLNK:
-		case S_IFDIR:
-		case S_IFGITLINK:
-			break;
-		/*
-		 * This is nonstandard, but we had a few of these
-		 * early on when we honored the full set of mode
-		 * bits..
-		 */
-		case S_IFREG | 0664:
-			if (!check_strict)
-				break;
-		default:
-			has_bad_modes = 1;
-		}
-
-		if (o_name) {
-			switch (verify_ordered(o_mode, o_name, mode, name)) {
-			case TREE_UNORDERED:
-				not_properly_sorted = 1;
-				break;
-			case TREE_HAS_DUPS:
-				has_dup_entries = 1;
-				break;
-			default:
-				break;
-			}
-		}
-
-		o_mode = mode;
-		o_name = name;
-		o_sha1 = sha1;
-	}
-	free(item->buffer);
-	item->buffer = NULL;
-
-	retval = 0;
-	if (has_full_path) {
-		objwarning(&item->object, "contains full pathnames");
-	}
-	if (has_empty_name) {
-		objwarning(&item->object, "contains empty pathname");
-	}
-	if (has_zero_pad) {
-		objwarning(&item->object, "contains zero-padded file modes");
-	}
-	if (has_bad_modes) {
-		objwarning(&item->object, "contains bad file modes");
-	}
-	if (has_dup_entries) {
-		retval = objerror(&item->object, "contains duplicate file entries");
-	}
-	if (not_properly_sorted) {
-		retval = objerror(&item->object, "not properly sorted");
-	}
-	return retval;
-}
-
-static int fsck_commit(struct commit *commit)
-{
-	char *buffer = commit->buffer;
-	unsigned char tree_sha1[20], sha1[20];
-
-	if (verbose)
-		fprintf(stderr, "Checking commit %s\n",
-			sha1_to_hex(commit->object.sha1));
-
-	if (memcmp(buffer, "tree ", 5))
-		return objerror(&commit->object, "invalid format - expected 'tree' line");
-	if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
-		return objerror(&commit->object, "invalid 'tree' line format - bad sha1");
-	buffer += 46;
-	while (!memcmp(buffer, "parent ", 7)) {
-		if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
-			return objerror(&commit->object, "invalid 'parent' line format - bad sha1");
-		buffer += 48;
-	}
-	if (memcmp(buffer, "author ", 7))
-		return objerror(&commit->object, "invalid format - expected 'author' line");
-	free(commit->buffer);
-	commit->buffer = NULL;
-	if (!commit->tree)
-		return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
-	if (!commit->parents && show_root)
-		printf("root %s\n", sha1_to_hex(commit->object.sha1));
-	if (!commit->date)
-		printf("bad commit date in %s\n",
-		       sha1_to_hex(commit->object.sha1));
-	return 0;
-}
-
-static int fsck_tag(struct tag *tag)
-{
-	struct object *tagged = tag->tagged;
-
-	if (verbose)
-		fprintf(stderr, "Checking tag %s\n",
-			sha1_to_hex(tag->object.sha1));
-
-	if (!tagged) {
-		return objerror(&tag->object, "could not load tagged object");
-	}
-	if (!show_tags)
-		return 0;
-
-	printf("tagged %s %s", typename(tagged->type), sha1_to_hex(tagged->sha1));
-	printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
-	return 0;
-}
-
 static int fsck_sha1(const unsigned char *sha1)
 {
 	struct object *obj = parse_object(sha1);
@@ -414,18 +257,43 @@
 	if (obj->flags & SEEN)
 		return 0;
 	obj->flags |= SEEN;
-	if (obj->type == OBJ_BLOB)
-		return 0;
-	if (obj->type == OBJ_TREE)
-		return fsck_tree((struct tree *) obj);
-	if (obj->type == OBJ_COMMIT)
-		return fsck_commit((struct commit *) obj);
-	if (obj->type == OBJ_TAG)
-		return fsck_tag((struct tag *) obj);
 
-	/* By now, parse_object() would've returned NULL instead. */
-	return objerror(obj, "unknown type '%d' (internal fsck error)",
-			obj->type);
+	if (verbose)
+		fprintf(stderr, "Checking %s %s\n",
+			typename(obj->type), sha1_to_hex(obj->sha1));
+
+	if (fsck_walk(obj, mark_used, 0))
+		objerror(obj, "broken links");
+	if (fsck_object(obj, check_strict, fsck_error_func))
+		return -1;
+
+	if (obj->type == OBJ_TREE) {
+		struct tree *item = (struct tree *) obj;
+
+		free(item->buffer);
+		item->buffer = NULL;
+	}
+
+	if (obj->type == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *) obj;
+
+		free(commit->buffer);
+		commit->buffer = NULL;
+
+		if (!commit->parents && show_root)
+			printf("root %s\n", sha1_to_hex(commit->object.sha1));
+	}
+
+	if (obj->type == OBJ_TAG) {
+		struct tag *tag = (struct tag *) obj;
+
+		if (show_tags && tag->tagged) {
+			printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
+			printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
+		}
+	}
+
+	return 0;
 }
 
 /*
@@ -538,13 +406,13 @@
 		obj = lookup_object(osha1);
 		if (obj) {
 			obj->used = 1;
-			mark_reachable(obj, REACHABLE);
+			mark_object_reachable(obj);
 		}
 	}
 	obj = lookup_object(nsha1);
 	if (obj) {
 		obj->used = 1;
-		mark_reachable(obj, REACHABLE);
+		mark_object_reachable(obj);
 	}
 	return 0;
 }
@@ -574,7 +442,7 @@
 		error("%s: not a commit", refname);
 	default_refs++;
 	obj->used = 1;
-	mark_reachable(obj, REACHABLE);
+	mark_object_reachable(obj);
 
 	return 0;
 }
@@ -660,7 +528,7 @@
 			      sha1_to_hex(it->sha1));
 			return 1;
 		}
-		mark_reachable(obj, REACHABLE);
+		mark_object_reachable(obj);
 		obj->used = 1;
 		if (obj->type != OBJ_TREE)
 			err |= objerror(obj, "non-tree in cache-tree");
@@ -693,7 +561,6 @@
 {
 	int i, heads;
 
-	track_object_refs = 1;
 	errors_found = 0;
 
 	argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
@@ -741,7 +608,7 @@
 				continue;
 
 			obj->used = 1;
-			mark_reachable(obj, REACHABLE);
+			mark_object_reachable(obj);
 			heads++;
 			continue;
 		}
@@ -765,7 +632,7 @@
 			struct blob *blob;
 			struct object *obj;
 
-			mode = ntohl(active_cache[i]->ce_mode);
+			mode = active_cache[i]->ce_mode;
 			if (S_ISGITLINK(mode))
 				continue;
 			blob = lookup_blob(active_cache[i]->sha1);
@@ -773,7 +640,7 @@
 				continue;
 			obj = &blob->object;
 			obj->used = 1;
-			mark_reachable(obj, REACHABLE);
+			mark_object_reachable(obj);
 		}
 		if (active_cache_tree)
 			fsck_cache_tree(active_cache_tree);
diff --git a/builtin-gc.c b/builtin-gc.c
index ad4a75e..8cef36f 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -25,13 +25,14 @@
 static int pack_refs = 1;
 static int aggressive_window = -1;
 static int gc_auto_threshold = 6700;
-static int gc_auto_pack_limit = 20;
+static int gc_auto_pack_limit = 50;
+static char *prune_expire = "2.weeks.ago";
 
 #define MAX_ADD 10
 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
 static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
 static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", NULL};
+static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
 static const char *argv_rerere[] = {"rerere", "gc", NULL};
 
 static int gc_config(const char *var, const char *value)
@@ -55,6 +56,17 @@
 		gc_auto_pack_limit = git_config_int(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "gc.pruneexpire")) {
+		if (!value)
+			return config_error_nonbool(var);
+		if (strcmp(value, "now")) {
+			unsigned long now = approxidate("now");
+			if (approxidate(value) >= now)
+				return error("Invalid %s: '%s'", var, value);
+		}
+		prune_expire = xstrdup(value);
+		return 0;
+	}
 	return git_default_config(var, value);
 }
 
@@ -148,10 +160,10 @@
 static int need_to_gc(void)
 {
 	/*
-	 * Setting gc.auto and gc.autopacklimit to 0 or negative can
-	 * disable the automatic gc.
+	 * Setting gc.auto to 0 or negative can disable the
+	 * automatic gc.
 	 */
-	if (gc_auto_threshold <= 0 && gc_auto_pack_limit <= 0)
+	if (gc_auto_threshold <= 0)
 		return 0;
 
 	/*
@@ -172,12 +184,14 @@
 	int prune = 0;
 	int aggressive = 0;
 	int auto_gc = 0;
+	int quiet = 0;
 	char buf[80];
 
 	struct option builtin_gc_options[] = {
 		OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
 		OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
 		OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
+		OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
 		OPT_END()
 	};
 
@@ -197,6 +211,8 @@
 			append_option(argv_repack, buf, MAX_ADD);
 		}
 	}
+	if (quiet)
+		append_option(argv_repack, "-q", MAX_ADD);
 
 	if (auto_gc) {
 		/*
@@ -230,7 +246,8 @@
 	if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
 		return error(FAILED_RUN, argv_repack[0]);
 
-	if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
+	argv_prune[2] = prune_expire;
+	if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
 		return error(FAILED_RUN, argv_prune[0]);
 
 	if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
diff --git a/builtin-grep.c b/builtin-grep.c
index 0d6cc73..ef29910 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -12,6 +12,14 @@
 #include "builtin.h"
 #include "grep.h"
 
+#ifndef NO_EXTERNAL_GREP
+#ifdef __unix__
+#define NO_EXTERNAL_GREP 0
+#else
+#define NO_EXTERNAL_GREP 1
+#endif
+#endif
+
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
  * pathname wildcards are allowed.
@@ -153,7 +161,7 @@
 	return i;
 }
 
-#ifdef __unix__
+#if !NO_EXTERNAL_GREP
 static int exec_grep(int argc, const char **argv)
 {
 	pid_t pid;
@@ -331,7 +339,7 @@
 		struct cache_entry *ce = active_cache[i];
 		char *name;
 		int kept;
-		if (!S_ISREG(ntohl(ce->ce_mode)))
+		if (!S_ISREG(ce->ce_mode))
 			continue;
 		if (!pathspec_matches(paths, ce->name))
 			continue;
@@ -372,7 +380,7 @@
 	int nr;
 	read_cache();
 
-#ifdef __unix__
+#if !NO_EXTERNAL_GREP
 	/*
 	 * Use the external "grep" command for the case where
 	 * we grep through the checked-out files. It tends to
@@ -387,7 +395,7 @@
 
 	for (nr = 0; nr < active_nr; nr++) {
 		struct cache_entry *ce = active_cache[nr];
-		if (!S_ISREG(ntohl(ce->ce_mode)))
+		if (!S_ISREG(ce->ce_mode))
 			continue;
 		if (!pathspec_matches(paths, ce->name))
 			continue;
@@ -578,6 +586,7 @@
 			continue;
 		}
 		if (!strcmp("-l", arg) ||
+		    !strcmp("--name-only", arg) ||
 		    !strcmp("--files-with-matches", arg)) {
 			opt.name_only = 1;
 			continue;
diff --git a/builtin-http-fetch.c b/builtin-http-fetch.c
index 48128c6..b1f3389 100644
--- a/builtin-http-fetch.c
+++ b/builtin-http-fetch.c
@@ -80,8 +80,7 @@
 
 	walker_free(walker);
 
-	if (rewritten_url)
-		free(rewritten_url);
+	free(rewritten_url);
 
 	return rc;
 }
diff --git a/builtin-init-db.c b/builtin-init-db.c
index ff6e877..2854868 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -120,9 +120,9 @@
 		 */
 		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);
+			struct strbuf d = STRBUF_INIT;
+			strbuf_addf(&d, "%s/%s", git_exec_path(), template_dir);
+			template_dir = strbuf_detach(&d, NULL);
 		}
 	}
 	strcpy(template_path, template_dir);
@@ -167,9 +167,9 @@
 {
 	unsigned len = strlen(git_dir);
 	static char path[PATH_MAX];
-	unsigned char sha1[20];
 	struct stat st1;
 	char repo_version_string[10];
+	char junk[2];
 	int reinit;
 	int filemode;
 
@@ -219,7 +219,8 @@
 	 * branch, if it does not exist yet.
 	 */
 	strcpy(path + len, "HEAD");
-	reinit = !read_ref("HEAD", sha1);
+	reinit = (!access(path, R_OK)
+		  || readlink(path, junk, sizeof(junk)-1) != -1);
 	if (!reinit) {
 		if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
 			exit(1);
diff --git a/builtin-log.c b/builtin-log.c
index 99d69f0..5c00725 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -5,6 +5,7 @@
  *		 2006 Junio Hamano
  */
 #include "cache.h"
+#include "color.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
@@ -14,9 +15,12 @@
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "refs.h"
+#include "run-command.h"
+#include "shortlog.h"
 
 static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
+static const char *fmt_pretty;
 
 static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
 {
@@ -51,6 +55,8 @@
 
 	rev->abbrev = DEFAULT_ABBREV;
 	rev->commit_format = CMIT_FMT_DEFAULT;
+	if (fmt_pretty)
+		rev->commit_format = get_commit_format(fmt_pretty);
 	rev->verbose_header = 1;
 	DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
 	rev->show_root_diff = default_show_root;
@@ -197,7 +203,8 @@
 	if (rev->early_output)
 		setup_early_output(rev);
 
-	prepare_revision_walk(rev);
+	if (prepare_revision_walk(rev))
+		die("revision walk setup failed");
 
 	if (rev->early_output)
 		finish_early_output(rev);
@@ -217,6 +224,8 @@
 
 static int git_log_config(const char *var, const char *value)
 {
+	if (!strcmp(var, "format.pretty"))
+		return git_config_string(&fmt_pretty, var, value);
 	if (!strcmp(var, "format.subjectprefix")) {
 		if (!value)
 			config_error_nonbool(var);
@@ -235,6 +244,10 @@
 	struct rev_info rev;
 
 	git_config(git_log_config);
+
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
 	init_revisions(&rev, prefix);
 	rev.diff = 1;
 	rev.simplify_history = 0;
@@ -307,6 +320,10 @@
 	int i, count, ret = 0;
 
 	git_config(git_log_config);
+
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
 	init_revisions(&rev, prefix);
 	rev.diff = 1;
 	rev.combine_merges = 1;
@@ -367,6 +384,10 @@
 	struct rev_info rev;
 
 	git_config(git_log_config);
+
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
 	init_revisions(&rev, prefix);
 	init_reflog_walk(&rev.reflog_info);
 	rev.abbrev_commit = 1;
@@ -395,6 +416,10 @@
 	struct rev_info rev;
 
 	git_config(git_log_config);
+
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
 	init_revisions(&rev, prefix);
 	rev.always_show_header = 1;
 	cmd_log_init(argc, argv, prefix, &rev);
@@ -410,24 +435,47 @@
 		(c >= '0' && c <= '9') || c == '.' || c == '_';
 }
 
-static char *extra_headers = NULL;
-static int extra_headers_size = 0;
 static const char *fmt_patch_suffix = ".patch";
 static int numbered = 0;
 static int auto_number = 0;
 
+static char **extra_hdr;
+static int extra_hdr_nr;
+static int extra_hdr_alloc;
+
+static char **extra_to;
+static int extra_to_nr;
+static int extra_to_alloc;
+
+static char **extra_cc;
+static int extra_cc_nr;
+static int extra_cc_alloc;
+
+static void add_header(const char *value)
+{
+	int len = strlen(value);
+	while (value[len - 1] == '\n')
+		len--;
+	if (!strncasecmp(value, "to: ", 4)) {
+		ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
+		extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
+		return;
+	}
+	if (!strncasecmp(value, "cc: ", 4)) {
+		ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+		extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
+		return;
+	}
+	ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
+	extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
+}
+
 static int git_format_config(const char *var, const char *value)
 {
 	if (!strcmp(var, "format.headers")) {
-		int len;
-
 		if (!value)
 			die("format.headers without value");
-		len = strlen(value);
-		extra_headers_size += len + 1;
-		extra_headers = xrealloc(extra_headers, extra_headers_size);
-		extra_headers[extra_headers_size - len - 1] = 0;
-		strcat(extra_headers, value);
+		add_header(value);
 		return 0;
 	}
 	if (!strcmp(var, "format.suffix")) {
@@ -452,74 +500,81 @@
 }
 
 
-static FILE *realstdout = NULL;
-static const char *output_directory = NULL;
-
-static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
-			 int numbered_files)
+static const char *get_oneline_for_filename(struct commit *commit,
+					    int keep_subject)
 {
-	char filename[PATH_MAX];
+	static char filename[PATH_MAX];
 	char *sol;
 	int len = 0;
 	int suffix_len = strlen(fmt_patch_suffix) + 1;
 
+	sol = strstr(commit->buffer, "\n\n");
+	if (!sol)
+		filename[0] = '\0';
+	else {
+		int j, space = 0;
+
+		sol += 2;
+		/* strip [PATCH] or [PATCH blabla] */
+		if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
+			char *eos = strchr(sol + 6, ']');
+			if (eos) {
+				while (isspace(*eos))
+					eos++;
+				sol = eos;
+			}
+		}
+
+		for (j = 0;
+		     j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
+			     len < sizeof(filename) - suffix_len &&
+			     sol[j] && sol[j] != '\n';
+		     j++) {
+			if (istitlechar(sol[j])) {
+				if (space) {
+					filename[len++] = '-';
+					space = 0;
+				}
+				filename[len++] = sol[j];
+				if (sol[j] == '.')
+					while (sol[j + 1] == '.')
+						j++;
+			} else
+				space = 1;
+		}
+		while (filename[len - 1] == '.'
+		       || filename[len - 1] == '-')
+			len--;
+		filename[len] = '\0';
+	}
+	return filename;
+}
+
+static FILE *realstdout = NULL;
+static const char *output_directory = NULL;
+
+static int reopen_stdout(const char *oneline, int nr, int total)
+{
+	char filename[PATH_MAX];
+	int len = 0;
+	int suffix_len = strlen(fmt_patch_suffix) + 1;
+
 	if (output_directory) {
-		if (strlen(output_directory) >=
+		len = snprintf(filename, sizeof(filename), "%s",
+				output_directory);
+		if (len >=
 		    sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
 			return error("name of output directory is too long");
-		strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
-		len = strlen(filename);
 		if (filename[len - 1] != '/')
 			filename[len++] = '/';
 	}
 
-	if (numbered_files) {
-		sprintf(filename + len, "%d", nr);
-		len = strlen(filename);
-
-	} else {
-		sprintf(filename + len, "%04d", nr);
-		len = strlen(filename);
-
-		sol = strstr(commit->buffer, "\n\n");
-		if (sol) {
-			int j, space = 1;
-
-			sol += 2;
-			/* strip [PATCH] or [PATCH blabla] */
-			if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
-				char *eos = strchr(sol + 6, ']');
-				if (eos) {
-					while (isspace(*eos))
-						eos++;
-					sol = eos;
-				}
-			}
-
-			for (j = 0;
-			     j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
-				     len < sizeof(filename) - suffix_len &&
-				     sol[j] && sol[j] != '\n';
-			     j++) {
-				if (istitlechar(sol[j])) {
-					if (space) {
-						filename[len++] = '-';
-						space = 0;
-					}
-					filename[len++] = sol[j];
-					if (sol[j] == '.')
-						while (sol[j + 1] == '.')
-							j++;
-				} else
-					space = 1;
-			}
-			while (filename[len - 1] == '.'
-			       || filename[len - 1] == '-')
-				len--;
-			filename[len] = 0;
-		}
-		if (len + suffix_len >= sizeof(filename))
-			return error("Patch pathname too long");
+	if (!oneline)
+		len += sprintf(filename + len, "%d", nr);
+	else {
+		len += sprintf(filename + len, "%04d-", nr);
+		len += snprintf(filename + len, sizeof(filename) - len - 1
+				- suffix_len, "%s", oneline);
 		strcpy(filename + len, fmt_patch_suffix);
 	}
 
@@ -556,7 +611,8 @@
 	o2->flags ^= UNINTERESTING;
 	add_pending_object(&check_rev, o1, "o1");
 	add_pending_object(&check_rev, o2, "o2");
-	prepare_revision_walk(&check_rev);
+	if (prepare_revision_walk(&check_rev))
+		die("revision walk setup failed");
 
 	while ((commit = get_revision(&check_rev)) != NULL) {
 		/* ignore merges */
@@ -575,16 +631,92 @@
 	o2->flags = flags2;
 }
 
-static void gen_message_id(char *dest, unsigned int length, char *base)
+static void gen_message_id(struct rev_info *info, char *base)
 {
 	const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
 	const char *email_start = strrchr(committer, '<');
 	const char *email_end = strrchr(committer, '>');
-	if(!email_start || !email_end || email_start > email_end - 1)
+	struct strbuf buf;
+	if (!email_start || !email_end || email_start > email_end - 1)
 		die("Could not extract email from committer identity.");
-	snprintf(dest, length, "%s.%lu.git.%.*s", base,
-		 (unsigned long) time(NULL),
-		 (int)(email_end - email_start - 1), email_start + 1);
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
+		    (unsigned long) time(NULL),
+		    (int)(email_end - email_start - 1), email_start + 1);
+	info->message_id = strbuf_detach(&buf, NULL);
+}
+
+static void make_cover_letter(struct rev_info *rev, int use_stdout,
+			      int numbered, int numbered_files,
+			      struct commit *origin,
+			      int nr, struct commit **list, struct commit *head)
+{
+	const char *committer;
+	char *head_sha1;
+	const char *subject_start = NULL;
+	const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
+	const char *msg;
+	const char *extra_headers = rev->extra_headers;
+	struct shortlog log;
+	struct strbuf sb;
+	int i;
+	const char *encoding = "utf-8";
+	struct diff_options opts;
+	int need_8bit_cte = 0;
+
+	if (rev->commit_format != CMIT_FMT_EMAIL)
+		die("Cover letter needs email format");
+
+	if (!use_stdout && reopen_stdout(numbered_files ?
+				NULL : "cover-letter", 0, rev->total))
+		return;
+
+	head_sha1 = sha1_to_hex(head->object.sha1);
+
+	log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
+				&need_8bit_cte);
+
+	committer = git_committer_info(0);
+
+	msg = body;
+	strbuf_init(&sb, 0);
+	pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
+		     encoding);
+	pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
+		      encoding, need_8bit_cte);
+	pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
+	printf("%s\n", sb.buf);
+
+	strbuf_release(&sb);
+
+	shortlog_init(&log);
+	log.wrap_lines = 1;
+	log.wrap = 72;
+	log.in1 = 2;
+	log.in2 = 4;
+	for (i = 0; i < nr; i++)
+		shortlog_add_commit(&log, list[i]);
+
+	shortlog_output(&log);
+
+	/*
+	 * We can only do diffstat with a unique reference point
+	 */
+	if (!origin)
+		return;
+
+	memcpy(&opts, &rev->diffopt, sizeof(opts));
+	opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+
+	diff_setup_done(&opts);
+
+	diff_tree_sha1(origin->tree->object.sha1,
+		       head->tree->object.sha1,
+		       "", &opts);
+	diffcore_std(&opts);
+	diff_flush(&opts);
+
+	printf("\n");
 }
 
 static const char *clean_message_id(const char *msg_id)
@@ -622,11 +754,13 @@
 	int subject_prefix = 0;
 	int ignore_if_in_upstream = 0;
 	int thread = 0;
+	int cover_letter = 0;
+	int boundary_count = 0;
+	struct commit *origin = NULL, *head = NULL;
 	const char *in_reply_to = NULL;
 	struct patch_ids ids;
 	char *add_signoff = NULL;
-	char message_id[1024];
-	char ref_message_id[1024];
+	struct strbuf buf;
 
 	git_config(git_format_config);
 	init_revisions(&rev, prefix);
@@ -639,7 +773,6 @@
 	DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
 
 	rev.subject_prefix = fmt_patch_subject_prefix;
-	rev.extra_headers = extra_headers;
 
 	/*
 	 * Parse the arguments before setup_revisions(), or something
@@ -667,6 +800,10 @@
 				die("Need a number for --start-number");
 			start_number = strtol(argv[i], NULL, 10);
 		}
+		else if (!prefixcmp(argv[i], "--cc=")) {
+			ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+			extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
+		}
 		else if (!strcmp(argv[i], "-k") ||
 				!strcmp(argv[i], "--keep-subject")) {
 			keep_subject = 1;
@@ -723,11 +860,44 @@
 			rev.subject_prefix = argv[i] + 17;
 		} else if (!prefixcmp(argv[i], "--suffix="))
 			fmt_patch_suffix = argv[i] + 9;
+		else if (!strcmp(argv[i], "--cover-letter"))
+			cover_letter = 1;
 		else
 			argv[j++] = argv[i];
 	}
 	argc = j;
 
+	strbuf_init(&buf, 0);
+
+	for (i = 0; i < extra_hdr_nr; i++) {
+		strbuf_addstr(&buf, extra_hdr[i]);
+		strbuf_addch(&buf, '\n');
+	}
+
+	if (extra_to_nr)
+		strbuf_addstr(&buf, "To: ");
+	for (i = 0; i < extra_to_nr; i++) {
+		if (i)
+			strbuf_addstr(&buf, "    ");
+		strbuf_addstr(&buf, extra_to[i]);
+		if (i + 1 < extra_to_nr)
+			strbuf_addch(&buf, ',');
+		strbuf_addch(&buf, '\n');
+	}
+
+	if (extra_cc_nr)
+		strbuf_addstr(&buf, "Cc: ");
+	for (i = 0; i < extra_cc_nr; i++) {
+		if (i)
+			strbuf_addstr(&buf, "    ");
+		strbuf_addstr(&buf, extra_cc[i]);
+		if (i + 1 < extra_cc_nr)
+			strbuf_addch(&buf, ',');
+		strbuf_addch(&buf, '\n');
+	}
+
+	rev.extra_headers = strbuf_detach(&buf, 0);
+
 	if (start_number < 0)
 		start_number = 1;
 	if (numbered && keep_subject)
@@ -774,6 +944,18 @@
 		 * get_revision() to do the usual traversal.
 		 */
 	}
+	if (cover_letter) {
+		/* remember the range */
+		int i;
+		for (i = 0; i < rev.pending.nr; i++) {
+			struct object *o = rev.pending.objects[i].item;
+			if (!(o->flags & UNINTERESTING))
+				head = (struct commit *)o;
+		}
+		/* We can't generate a cover letter without any patches */
+		if (!head)
+			return 0;
+	}
 
 	if (ignore_if_in_upstream)
 		get_patch_ids(&rev, &ids, prefix);
@@ -781,8 +963,16 @@
 	if (!use_stdout)
 		realstdout = xfdopen(xdup(1), "w");
 
-	prepare_revision_walk(&rev);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+	rev.boundary = 1;
 	while ((commit = get_revision(&rev)) != NULL) {
+		if (commit->object.flags & BOUNDARY) {
+			boundary_count++;
+			origin = (boundary_count == 1) ? commit : NULL;
+			continue;
+		}
+
 		/* ignore merges */
 		if (commit->parents && commit->parents->next)
 			continue;
@@ -800,29 +990,42 @@
 		numbered = 1;
 	if (numbered)
 		rev.total = total + start_number - 1;
-	rev.add_signoff = add_signoff;
 	if (in_reply_to)
 		rev.ref_message_id = clean_message_id(in_reply_to);
+	if (cover_letter) {
+		if (thread)
+			gen_message_id(&rev, "cover");
+		make_cover_letter(&rev, use_stdout, numbered, numbered_files,
+				  origin, nr, list, head);
+		total++;
+		start_number--;
+	}
+	rev.add_signoff = add_signoff;
 	while (0 <= --nr) {
 		int shown;
 		commit = list[nr];
 		rev.nr = total - nr + (start_number - 1);
 		/* Make the second and subsequent mails replies to the first */
 		if (thread) {
-			if (nr == (total - 2)) {
-				strncpy(ref_message_id, message_id,
-					sizeof(ref_message_id));
-				ref_message_id[sizeof(ref_message_id)-1]='\0';
-				rev.ref_message_id = ref_message_id;
+			/* Have we already had a message ID? */
+			if (rev.message_id) {
+				/*
+				 * If we've got the ID to be a reply
+				 * to, discard the current ID;
+				 * otherwise, make everything a reply
+				 * to that.
+				 */
+				if (rev.ref_message_id)
+					free(rev.message_id);
+				else
+					rev.ref_message_id = rev.message_id;
 			}
-			gen_message_id(message_id, sizeof(message_id),
-				       sha1_to_hex(commit->object.sha1));
-			rev.message_id = message_id;
+			gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
 		}
-		if (!use_stdout)
-			if (reopen_stdout(commit, rev.nr, keep_subject,
-					  numbered_files))
-				die("Failed to create output files");
+		if (!use_stdout && reopen_stdout(numbered_files ? NULL :
+				get_oneline_for_filename(commit, keep_subject),
+				rev.nr, rev.total))
+			die("Failed to create output files");
 		shown = log_tree_commit(&rev, commit);
 		free(commit->buffer);
 		commit->buffer = NULL;
@@ -923,7 +1126,8 @@
 		die("Unknown commit %s", limit);
 
 	/* reverse the list of commits */
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 	while ((commit = get_revision(&revs)) != NULL) {
 		/* ignore merges */
 		if (commit->parents && commit->parents->next)
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 0f0ab2d..dc7eab8 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -189,7 +189,7 @@
 		return;
 
 	if (tag && *tag && show_valid_bit &&
-	    (ce->ce_flags & htons(CE_VALID))) {
+	    (ce->ce_flags & CE_VALID)) {
 		static char alttag[4];
 		memcpy(alttag, tag, 3);
 		if (isalpha(tag[0]))
@@ -210,7 +210,7 @@
 	} else {
 		printf("%s%06o %s %d\t",
 		       tag,
-		       ntohl(ce->ce_mode),
+		       ce->ce_mode,
 		       abbrev ? find_unique_abbrev(ce->sha1,abbrev)
 				: sha1_to_hex(ce->sha1),
 		       ce_stage(ce));
@@ -238,11 +238,12 @@
 	if (show_cached | show_stage) {
 		for (i = 0; i < active_nr; i++) {
 			struct cache_entry *ce = active_cache[i];
-			if (excluded(dir, ce->name) != dir->show_ignored)
+			int dtype = ce_to_dtype(ce);
+			if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
 				continue;
 			if (show_unmerged && !ce_stage(ce))
 				continue;
-			if (ce->ce_flags & htons(CE_UPDATE))
+			if (ce->ce_flags & CE_UPDATE)
 				continue;
 			show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
 		}
@@ -252,7 +253,8 @@
 			struct cache_entry *ce = active_cache[i];
 			struct stat st;
 			int err;
-			if (excluded(dir, ce->name) != dir->show_ignored)
+			int dtype = ce_to_dtype(ce);
+			if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
 				continue;
 			err = lstat(ce->name, &st);
 			if (show_deleted && err)
@@ -350,7 +352,7 @@
 		struct cache_entry *ce = active_cache[i];
 		if (!ce_stage(ce))
 			continue;
-		ce->ce_flags |= htons(CE_STAGEMASK);
+		ce->ce_flags |= CE_STAGEMASK;
 	}
 
 	if (prefix) {
@@ -379,7 +381,7 @@
 			 */
 			if (last_stage0 &&
 			    !strcmp(last_stage0->name, ce->name))
-				ce->ce_flags |= htons(CE_UPDATE);
+				ce->ce_flags |= CE_UPDATE;
 		}
 	}
 }
diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c
index 720280e..06ab8da 100644
--- a/builtin-ls-remote.c
+++ b/builtin-ls-remote.c
@@ -31,7 +31,7 @@
 {
 	int i;
 	const char *dest = NULL;
-	int nongit = 0;
+	int nongit;
 	unsigned flags = 0;
 	const char *uploadpack = NULL;
 	const char **pattern = NULL;
@@ -94,6 +94,8 @@
 		transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
 
 	ref = transport_get_remote_refs(transport);
+	if (transport_disconnect(transport))
+		return 1;
 	for ( ; ref; ref = ref->next) {
 		if (!check_ref_type(ref, flags))
 			continue;
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index baff449..3605960 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -46,7 +46,7 @@
 	}
 
 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-			&xpp, XDL_MERGE_ZEALOUS, &result);
+			&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
 
 	for (i = 0; i < 3; i++)
 		free(mmfs[i].ptr);
diff --git a/merge-recursive.c b/builtin-merge-recursive.c
similarity index 78%
rename from merge-recursive.c
rename to builtin-merge-recursive.c
index d97cbf7..910c0d2 100644
--- a/merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -7,16 +7,18 @@
 #include "cache-tree.h"
 #include "commit.h"
 #include "blob.h"
+#include "builtin.h"
 #include "tree-walk.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "run-command.h"
 #include "tag.h"
 #include "unpack-trees.h"
 #include "path-list.h"
 #include "xdiff-interface.h"
+#include "ll-merge.h"
 #include "interpolate.h"
 #include "attr.h"
+#include "merge-recursive.h"
 
 static int subtree_merge;
 
@@ -211,6 +213,8 @@
 	opts.merge = 1;
 	opts.head_idx = 2;
 	opts.fn = threeway_merge;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
 
 	init_tree_desc_from_tree(t+0, common);
 	init_tree_desc_from_tree(t+1, head);
@@ -221,22 +225,11 @@
 	return rc;
 }
 
-static int unmerged_index(void)
-{
-	int i;
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (ce_stage(ce))
-			return 1;
-	}
-	return 0;
-}
-
-static struct tree *git_write_tree(void)
+struct tree *write_tree_from_memory(void)
 {
 	struct tree *result = NULL;
 
-	if (unmerged_index()) {
+	if (unmerged_cache()) {
 		int i;
 		output(0, "There are unmerged index entries:");
 		for (i = 0; i < active_nr; i++) {
@@ -333,7 +326,7 @@
 			item->util = xcalloc(1, sizeof(struct stage_data));
 		}
 		e = item->util;
-		e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+		e->stages[ce_stage(ce)].mode = ce->ce_mode;
 		hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
 	}
 
@@ -624,364 +617,16 @@
 	mm->size = size;
 }
 
-/*
- * Customizable low-level merge drivers support.
- */
-
-struct ll_merge_driver;
-typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
-			   const char *path,
-			   mmfile_t *orig,
-			   mmfile_t *src1, const char *name1,
-			   mmfile_t *src2, const char *name2,
-			   mmbuffer_t *result);
-
-struct ll_merge_driver {
-	const char *name;
-	const char *description;
-	ll_merge_fn fn;
-	const char *recursive;
-	struct ll_merge_driver *next;
-	char *cmdline;
-};
-
-/*
- * Built-in low-levels
- */
-static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
-			   const char *path_unused,
-			   mmfile_t *orig,
-			   mmfile_t *src1, const char *name1,
-			   mmfile_t *src2, const char *name2,
-			   mmbuffer_t *result)
-{
-	/*
-	 * The tentative merge result is "ours" for the final round,
-	 * or common ancestor for an internal merge.  Still return
-	 * "conflicted merge" status.
-	 */
-	mmfile_t *stolen = index_only ? orig : src1;
-
-	result->ptr = stolen->ptr;
-	result->size = stolen->size;
-	stolen->ptr = NULL;
-	return 1;
-}
-
-static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
-			const char *path_unused,
-			mmfile_t *orig,
-			mmfile_t *src1, const char *name1,
-			mmfile_t *src2, const char *name2,
-			mmbuffer_t *result)
-{
-	xpparam_t xpp;
-
-	if (buffer_is_binary(orig->ptr, orig->size) ||
-	    buffer_is_binary(src1->ptr, src1->size) ||
-	    buffer_is_binary(src2->ptr, src2->size)) {
-		warning("Cannot merge binary files: %s vs. %s\n",
-			name1, name2);
-		return ll_binary_merge(drv_unused, path_unused,
-				       orig, src1, name1,
-				       src2, name2,
-				       result);
-	}
-
-	memset(&xpp, 0, sizeof(xpp));
-	return xdl_merge(orig,
-			 src1, name1,
-			 src2, name2,
-			 &xpp, XDL_MERGE_ZEALOUS,
-			 result);
-}
-
-static int ll_union_merge(const struct ll_merge_driver *drv_unused,
-			  const char *path_unused,
-			  mmfile_t *orig,
-			  mmfile_t *src1, const char *name1,
-			  mmfile_t *src2, const char *name2,
-			  mmbuffer_t *result)
-{
-	char *src, *dst;
-	long size;
-	const int marker_size = 7;
-
-	int status = ll_xdl_merge(drv_unused, path_unused,
-				  orig, src1, NULL, src2, NULL, result);
-	if (status <= 0)
-		return status;
-	size = result->size;
-	src = dst = result->ptr;
-	while (size) {
-		char ch;
-		if ((marker_size < size) &&
-		    (*src == '<' || *src == '=' || *src == '>')) {
-			int i;
-			ch = *src;
-			for (i = 0; i < marker_size; i++)
-				if (src[i] != ch)
-					goto not_a_marker;
-			if (src[marker_size] != '\n')
-				goto not_a_marker;
-			src += marker_size + 1;
-			size -= marker_size + 1;
-			continue;
-		}
-	not_a_marker:
-		do {
-			ch = *src++;
-			*dst++ = ch;
-			size--;
-		} while (ch != '\n' && size);
-	}
-	result->size = dst - result->ptr;
-	return 0;
-}
-
-#define LL_BINARY_MERGE 0
-#define LL_TEXT_MERGE 1
-#define LL_UNION_MERGE 2
-static struct ll_merge_driver ll_merge_drv[] = {
-	{ "binary", "built-in binary merge", ll_binary_merge },
-	{ "text", "built-in 3-way text merge", ll_xdl_merge },
-	{ "union", "built-in union merge", ll_union_merge },
-};
-
-static void create_temp(mmfile_t *src, char *path)
-{
-	int fd;
-
-	strcpy(path, ".merge_file_XXXXXX");
-	fd = xmkstemp(path);
-	if (write_in_full(fd, src->ptr, src->size) != src->size)
-		die("unable to write temp-file");
-	close(fd);
-}
-
-/*
- * User defined low-level merge driver support.
- */
-static int ll_ext_merge(const struct ll_merge_driver *fn,
-			const char *path,
-			mmfile_t *orig,
-			mmfile_t *src1, const char *name1,
-			mmfile_t *src2, const char *name2,
-			mmbuffer_t *result)
-{
-	char temp[3][50];
-	char cmdbuf[2048];
-	struct interp table[] = {
-		{ "%O" },
-		{ "%A" },
-		{ "%B" },
-	};
-	struct child_process child;
-	const char *args[20];
-	int status, fd, i;
-	struct stat st;
-
-	if (fn->cmdline == NULL)
-		die("custom merge driver %s lacks command line.", fn->name);
-
-	result->ptr = NULL;
-	result->size = 0;
-	create_temp(orig, temp[0]);
-	create_temp(src1, temp[1]);
-	create_temp(src2, temp[2]);
-
-	interp_set_entry(table, 0, temp[0]);
-	interp_set_entry(table, 1, temp[1]);
-	interp_set_entry(table, 2, temp[2]);
-
-	output(1, "merging %s using %s", path,
-	       fn->description ? fn->description : fn->name);
-
-	interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
-
-	memset(&child, 0, sizeof(child));
-	child.argv = args;
-	args[0] = "sh";
-	args[1] = "-c";
-	args[2] = cmdbuf;
-	args[3] = NULL;
-
-	status = run_command(&child);
-	if (status < -ERR_RUN_COMMAND_FORK)
-		; /* failure in run-command */
-	else
-		status = -status;
-	fd = open(temp[1], O_RDONLY);
-	if (fd < 0)
-		goto bad;
-	if (fstat(fd, &st))
-		goto close_bad;
-	result->size = st.st_size;
-	result->ptr = xmalloc(result->size + 1);
-	if (read_in_full(fd, result->ptr, result->size) != result->size) {
-		free(result->ptr);
-		result->ptr = NULL;
-		result->size = 0;
-	}
- close_bad:
-	close(fd);
- bad:
-	for (i = 0; i < 3; i++)
-		unlink(temp[i]);
-	return status;
-}
-
-/*
- * merge.default and merge.driver configuration items
- */
-static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
-static const char *default_ll_merge;
-
-static int read_merge_config(const char *var, const char *value)
-{
-	struct ll_merge_driver *fn;
-	const char *ep, *name;
-	int namelen;
-
-	if (!strcmp(var, "merge.default")) {
-		if (!value)
-			return config_error_nonbool(var);
-		default_ll_merge = strdup(value);
-		return 0;
-	}
-
-	/*
-	 * We are not interested in anything but "merge.<name>.variable";
-	 * especially, we do not want to look at variables such as
-	 * "merge.summary", "merge.tool", and "merge.verbosity".
-	 */
-	if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
-		return 0;
-
-	/*
-	 * Find existing one as we might be processing merge.<name>.var2
-	 * after seeing merge.<name>.var1.
-	 */
-	name = var + 6;
-	namelen = ep - name;
-	for (fn = ll_user_merge; fn; fn = fn->next)
-		if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
-			break;
-	if (!fn) {
-		fn = xcalloc(1, sizeof(struct ll_merge_driver));
-		fn->name = xmemdupz(name, namelen);
-		fn->fn = ll_ext_merge;
-		*ll_user_merge_tail = fn;
-		ll_user_merge_tail = &(fn->next);
-	}
-
-	ep++;
-
-	if (!strcmp("name", ep)) {
-		if (!value)
-			return config_error_nonbool(var);
-		fn->description = strdup(value);
-		return 0;
-	}
-
-	if (!strcmp("driver", ep)) {
-		if (!value)
-			return config_error_nonbool(var);
-		/*
-		 * merge.<name>.driver specifies the command line:
-		 *
-		 *	command-line
-		 *
-		 * The command-line will be interpolated with the following
-		 * tokens and is given to the shell:
-		 *
-		 *    %O - temporary file name for the merge base.
-		 *    %A - temporary file name for our version.
-		 *    %B - temporary file name for the other branches' version.
-		 *
-		 * The external merge driver should write the results in the
-		 * file named by %A, and signal that it has done with zero exit
-		 * status.
-		 */
-		fn->cmdline = strdup(value);
-		return 0;
-	}
-
-	if (!strcmp("recursive", ep)) {
-		if (!value)
-			return config_error_nonbool(var);
-		fn->recursive = strdup(value);
-		return 0;
-	}
-
-	return 0;
-}
-
-static void initialize_ll_merge(void)
-{
-	if (ll_user_merge_tail)
-		return;
-	ll_user_merge_tail = &ll_user_merge;
-	git_config(read_merge_config);
-}
-
-static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
-{
-	struct ll_merge_driver *fn;
-	const char *name;
-	int i;
-
-	initialize_ll_merge();
-
-	if (ATTR_TRUE(merge_attr))
-		return &ll_merge_drv[LL_TEXT_MERGE];
-	else if (ATTR_FALSE(merge_attr))
-		return &ll_merge_drv[LL_BINARY_MERGE];
-	else if (ATTR_UNSET(merge_attr)) {
-		if (!default_ll_merge)
-			return &ll_merge_drv[LL_TEXT_MERGE];
-		else
-			name = default_ll_merge;
-	}
-	else
-		name = merge_attr;
-
-	for (fn = ll_user_merge; fn; fn = fn->next)
-		if (!strcmp(fn->name, name))
-			return fn;
-
-	for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
-		if (!strcmp(ll_merge_drv[i].name, name))
-			return &ll_merge_drv[i];
-
-	/* default to the 3-way */
-	return &ll_merge_drv[LL_TEXT_MERGE];
-}
-
-static const char *git_path_check_merge(const char *path)
-{
-	static struct git_attr_check attr_merge_check;
-
-	if (!attr_merge_check.attr)
-		attr_merge_check.attr = git_attr("merge", 5);
-
-	if (git_checkattr(path, 1, &attr_merge_check))
-		return NULL;
-	return attr_merge_check.value;
-}
-
-static int ll_merge(mmbuffer_t *result_buf,
-		    struct diff_filespec *o,
-		    struct diff_filespec *a,
-		    struct diff_filespec *b,
-		    const char *branch1,
-		    const char *branch2)
+static int merge_3way(mmbuffer_t *result_buf,
+		      struct diff_filespec *o,
+		      struct diff_filespec *a,
+		      struct diff_filespec *b,
+		      const char *branch1,
+		      const char *branch2)
 {
 	mmfile_t orig, src1, src2;
 	char *name1, *name2;
 	int merge_status;
-	const char *ll_driver_name;
-	const struct ll_merge_driver *driver;
 
 	name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
 	name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -990,14 +635,9 @@
 	fill_mm(a->sha1, &src1);
 	fill_mm(b->sha1, &src2);
 
-	ll_driver_name = git_path_check_merge(a->path);
-	driver = find_ll_merge_driver(ll_driver_name);
-
-	if (index_only && driver->recursive)
-		driver = find_ll_merge_driver(driver->recursive);
-	merge_status = driver->fn(driver, a->path,
-				  &orig, &src1, name1, &src2, name2,
-				  result_buf);
+	merge_status = ll_merge(result_buf, a->path, &orig,
+				&src1, name1, &src2, name2,
+				index_only);
 
 	free(name1);
 	free(name2);
@@ -1049,8 +689,8 @@
 			mmbuffer_t result_buf;
 			int merge_status;
 
-			merge_status = ll_merge(&result_buf, o, a, b,
-						branch1, branch2);
+			merge_status = merge_3way(&result_buf, o, a, b,
+						  branch1, branch2);
 
 			if ((merge_status < 0) || !result_buf.ptr)
 				die("Failed to execute internal merge");
@@ -1507,12 +1147,12 @@
 	return clean_merge;
 }
 
-static int merge_trees(struct tree *head,
-		       struct tree *merge,
-		       struct tree *common,
-		       const char *branch1,
-		       const char *branch2,
-		       struct tree **result)
+int merge_trees(struct tree *head,
+		struct tree *merge,
+		struct tree *common,
+		const char *branch1,
+		const char *branch2,
+		struct tree **result)
 {
 	int code, clean;
 
@@ -1534,7 +1174,7 @@
 		    sha1_to_hex(head->object.sha1),
 		    sha1_to_hex(merge->object.sha1));
 
-	if (unmerged_index()) {
+	if (unmerged_cache()) {
 		struct path_list *entries, *re_head, *re_merge;
 		int i;
 		path_list_clear(&current_file_set, 1);
@@ -1564,7 +1204,7 @@
 		clean = 1;
 
 	if (index_only)
-		*result = git_write_tree();
+		*result = write_tree_from_memory();
 
 	return clean;
 }
@@ -1584,12 +1224,12 @@
  * Merge the commits h1 and h2, return the resulting virtual
  * commit object and a flag indicating the cleanness of the merge.
  */
-static int merge(struct commit *h1,
-		 struct commit *h2,
-		 const char *branch1,
-		 const char *branch2,
-		 struct commit_list *ca,
-		 struct commit **result)
+int merge_recursive(struct commit *h1,
+		    struct commit *h2,
+		    const char *branch1,
+		    const char *branch2,
+		    struct commit_list *ca,
+		    struct commit **result)
 {
 	struct commit_list *iter;
 	struct commit *merged_common_ancestors;
@@ -1634,11 +1274,11 @@
 		 * "conflicts" were already resolved.
 		 */
 		discard_cache();
-		merge(merged_common_ancestors, iter->item,
-		      "Temporary merge branch 1",
-		      "Temporary merge branch 2",
-		      NULL,
-		      &merged_common_ancestors);
+		merge_recursive(merged_common_ancestors, iter->item,
+				"Temporary merge branch 1",
+				"Temporary merge branch 2",
+				NULL,
+				&merged_common_ancestors);
 		call_depth--;
 
 		if (!merged_common_ancestors)
@@ -1684,6 +1324,8 @@
 	if (get_sha1(ref, sha1))
 		die("Could not resolve ref '%s'", ref);
 	object = deref_tag(parse_object(sha1), ref, strlen(ref));
+	if (!object)
+		return NULL;
 	if (object->type == OBJ_TREE)
 		return make_virtual_commit((struct tree*)object,
 			better_branch_name(ref));
@@ -1707,7 +1349,7 @@
 	return git_default_config(var, value);
 }
 
-int main(int argc, char *argv[])
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
 {
 	static const char *bases[20];
 	static unsigned bases_count = 0;
@@ -1761,7 +1403,7 @@
 		struct commit *ancestor = get_ref(bases[i]);
 		ca = commit_list_insert(ancestor, &ca);
 	}
-	clean = merge(h1, h2, branch1, branch2, ca, &result);
+	clean = merge_recursive(h1, h2, branch1, branch2, ca, &result);
 
 	if (active_cache_changed &&
 	    (write_cache(index_fd, active_cache, active_nr) ||
diff --git a/builtin-mv.c b/builtin-mv.c
index 990e213..94f6dd2 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -164,7 +164,7 @@
 				}
 
 				dst = add_slash(dst);
-				dst_len = strlen(dst) - 1;
+				dst_len = strlen(dst);
 
 				for (j = 0; j < last - first; j++) {
 					const char *path =
@@ -172,7 +172,7 @@
 					source[argc + j] = path;
 					destination[argc + j] =
 						prefix_path(dst, dst_len,
-							path + length);
+							path + length + 1);
 					modes[argc + j] = INDEX;
 				}
 				argc += last - first;
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index a0c89a8..521f061 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -125,18 +125,18 @@
 }
 
 /* returns a static buffer */
-static const char* get_rev_name(struct object *o)
+static const char *get_rev_name(const struct object *o)
 {
 	static char buffer[1024];
 	struct rev_name *n;
 	struct commit *c;
 
 	if (o->type != OBJ_COMMIT)
-		return "undefined";
+		return NULL;
 	c = (struct commit *) o;
 	n = c->util;
 	if (!n)
-		return "undefined";
+		return NULL;
 
 	if (!n->generation)
 		return n->tip_name;
@@ -151,6 +151,26 @@
 	}
 }
 
+static void show_name(const struct object *obj,
+		      const char *caller_name,
+		      int always, int allow_undefined, int name_only)
+{
+	const char *name;
+	const unsigned char *sha1 = obj->sha1;
+
+	if (!name_only)
+		printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
+	name = get_rev_name(obj);
+	if (name)
+		printf("%s\n", name);
+	else if (allow_undefined)
+		printf("undefined\n");
+	else if (always)
+		printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
+	else
+		die("cannot describe '%s'", sha1_to_hex(sha1));
+}
+
 static char const * const name_rev_usage[] = {
 	"git-name-rev [options] ( --all | --stdin | <commit>... )",
 	NULL
@@ -159,7 +179,7 @@
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
 	struct object_array revs = { 0, 0, NULL };
-	int all = 0, transform_stdin = 0;
+	int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
 	struct name_ref_data data = { 0, 0, NULL };
 	struct option opts[] = {
 		OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
@@ -169,6 +189,9 @@
 		OPT_GROUP(""),
 		OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
 		OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
+		OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
+		OPT_BOOLEAN(0, "always",     &always,
+			   "show abbreviated commit object as fallback"),
 		OPT_END(),
 	};
 
@@ -226,7 +249,7 @@
 				else if (++forty == 40 &&
 						!ishex(*(p+1))) {
 					unsigned char sha1[40];
-					const char *name = "undefined";
+					const char *name = NULL;
 					char c = *(p+1);
 
 					forty = 0;
@@ -240,11 +263,10 @@
 					}
 					*(p+1) = c;
 
-					if (!strcmp(name, "undefined"))
+					if (!name)
 						continue;
 
-					fwrite(p_start, p - p_start + 1, 1,
-					       stdout);
+					fwrite(p_start, p - p_start + 1, 1, stdout);
 					printf(" (%s)", name);
 					p_start = p + 1;
 				}
@@ -259,20 +281,17 @@
 
 		max = get_max_object_index();
 		for (i = 0; i < max; i++) {
-			struct object * obj = get_indexed_object(i);
+			struct object *obj = get_indexed_object(i);
 			if (!obj)
 				continue;
-			if (!data.name_only)
-				printf("%s ", sha1_to_hex(obj->sha1));
-			printf("%s\n", get_rev_name(obj));
+			show_name(obj, NULL,
+				  always, allow_undefined, data.name_only);
 		}
 	} else {
 		int i;
-		for (i = 0; i < revs.nr; i++) {
-			if (!data.name_only)
-				printf("%s ", revs.objects[i].name);
-			printf("%s\n", get_rev_name(revs.objects[i].item));
-		}
+		for (i = 0; i < revs.nr; i++)
+			show_name(revs.objects[i].item, revs.objects[i].name,
+				  always, allow_undefined, data.name_only);
 	}
 
 	return 0;
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index d3efeff..777f272 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -8,14 +8,17 @@
 #include "tree.h"
 #include "delta.h"
 #include "pack.h"
+#include "pack-revindex.h"
 #include "csum-file.h"
 #include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
 #include "progress.h"
+#include "refs.h"
 
 #ifdef THREADED_DELTA_SEARCH
+#include "thread-utils.h"
 #include <pthread.h>
 #endif
 
@@ -25,7 +28,8 @@
 	[--window=N] [--window-memory=N] [--depth=N] \n\
 	[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
 	[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
-	[--stdout | base-name] [--keep-unreachable] [<ref-list | <object-list]";
+	[--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
+	[<ref-list | <object-list]";
 
 struct object_entry {
 	struct pack_idx_entry idx;
@@ -61,14 +65,14 @@
 static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
 
 static int non_empty;
-static int no_reuse_delta, no_reuse_object, keep_unreachable;
+static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
 static int local;
 static int incremental;
 static int allow_ofs_delta;
 static const char *base_name;
 static int progress = 1;
 static int window = 10;
-static uint32_t pack_size_limit;
+static uint32_t pack_size_limit, pack_size_limit_cfg;
 static int depth = 50;
 static int delta_search_threads = 1;
 static int pack_to_stdout;
@@ -92,157 +96,11 @@
 static int object_ix_hashsz;
 
 /*
- * Pack index for existing packs give us easy access to the offsets into
- * corresponding pack file where each object's data starts, but the entries
- * do not store the size of the compressed representation (uncompressed
- * size is easily available by examining the pack entry header).  It is
- * also rather expensive to find the sha1 for an object given its offset.
- *
- * We build a hashtable of existing packs (pack_revindex), and keep reverse
- * index here -- pack index file is sorted by object name mapping to offset;
- * this pack_revindex[].revindex array is a list of offset/index_nr pairs
- * ordered by offset, so if you know the offset of an object, next offset
- * is where its packed representation ends and the index_nr can be used to
- * get the object sha1 from the main index.
- */
-struct revindex_entry {
-	off_t offset;
-	unsigned int nr;
-};
-struct pack_revindex {
-	struct packed_git *p;
-	struct revindex_entry *revindex;
-};
-static struct  pack_revindex *pack_revindex;
-static int pack_revindex_hashsz;
-
-/*
  * stats
  */
 static uint32_t written, written_delta;
 static uint32_t reused, reused_delta;
 
-static int pack_revindex_ix(struct packed_git *p)
-{
-	unsigned long ui = (unsigned long)p;
-	int i;
-
-	ui = ui ^ (ui >> 16); /* defeat structure alignment */
-	i = (int)(ui % pack_revindex_hashsz);
-	while (pack_revindex[i].p) {
-		if (pack_revindex[i].p == p)
-			return i;
-		if (++i == pack_revindex_hashsz)
-			i = 0;
-	}
-	return -1 - i;
-}
-
-static void prepare_pack_ix(void)
-{
-	int num;
-	struct packed_git *p;
-	for (num = 0, p = packed_git; p; p = p->next)
-		num++;
-	if (!num)
-		return;
-	pack_revindex_hashsz = num * 11;
-	pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
-	for (p = packed_git; p; p = p->next) {
-		num = pack_revindex_ix(p);
-		num = - 1 - num;
-		pack_revindex[num].p = p;
-	}
-	/* revindex elements are lazily initialized */
-}
-
-static int cmp_offset(const void *a_, const void *b_)
-{
-	const struct revindex_entry *a = a_;
-	const struct revindex_entry *b = b_;
-	return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
-}
-
-/*
- * Ordered list of offsets of objects in the pack.
- */
-static void prepare_pack_revindex(struct pack_revindex *rix)
-{
-	struct packed_git *p = rix->p;
-	int num_ent = p->num_objects;
-	int i;
-	const char *index = p->index_data;
-
-	rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
-	index += 4 * 256;
-
-	if (p->index_version > 1) {
-		const uint32_t *off_32 =
-			(uint32_t *)(index + 8 + p->num_objects * (20 + 4));
-		const uint32_t *off_64 = off_32 + p->num_objects;
-		for (i = 0; i < num_ent; i++) {
-			uint32_t off = ntohl(*off_32++);
-			if (!(off & 0x80000000)) {
-				rix->revindex[i].offset = off;
-			} else {
-				rix->revindex[i].offset =
-					((uint64_t)ntohl(*off_64++)) << 32;
-				rix->revindex[i].offset |=
-					ntohl(*off_64++);
-			}
-			rix->revindex[i].nr = i;
-		}
-	} else {
-		for (i = 0; i < num_ent; i++) {
-			uint32_t hl = *((uint32_t *)(index + 24 * i));
-			rix->revindex[i].offset = ntohl(hl);
-			rix->revindex[i].nr = i;
-		}
-	}
-
-	/* This knows the pack format -- the 20-byte trailer
-	 * follows immediately after the last object data.
-	 */
-	rix->revindex[num_ent].offset = p->pack_size - 20;
-	rix->revindex[num_ent].nr = -1;
-	qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
-}
-
-static struct revindex_entry * find_packed_object(struct packed_git *p,
-						  off_t ofs)
-{
-	int num;
-	int lo, hi;
-	struct pack_revindex *rix;
-	struct revindex_entry *revindex;
-	num = pack_revindex_ix(p);
-	if (num < 0)
-		die("internal error: pack revindex uninitialized");
-	rix = &pack_revindex[num];
-	if (!rix->revindex)
-		prepare_pack_revindex(rix);
-	revindex = rix->revindex;
-	lo = 0;
-	hi = p->num_objects + 1;
-	do {
-		int mi = (lo + hi) / 2;
-		if (revindex[mi].offset == ofs) {
-			return revindex + mi;
-		}
-		else if (ofs < revindex[mi].offset)
-			hi = mi;
-		else
-			lo = mi + 1;
-	} while (lo < hi);
-	die("internal error: pack revindex corrupt");
-}
-
-static const unsigned char *find_packed_object_name(struct packed_git *p,
-						    off_t ofs)
-{
-	struct revindex_entry *entry = find_packed_object(p, ofs);
-	return nth_packed_object_sha1(p, entry->nr);
-}
 
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 {
@@ -509,7 +367,7 @@
 		}
 		hdrlen = encode_header(obj_type, entry->size, header);
 		offset = entry->in_pack_offset;
-		revidx = find_packed_object(p, offset);
+		revidx = find_pack_revindex(p, offset);
 		datalen = revidx[1].offset - offset;
 		if (!pack_to_stdout && p->index_version > 1 &&
 		    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
@@ -596,6 +454,7 @@
 	struct pack_header hdr;
 	int do_progress = progress >> pack_to_stdout;
 	uint32_t nr_remaining = nr_result;
+	time_t last_mtime = 0;
 
 	if (do_progress)
 		progress_state = start_progress("Writing objects", nr_result);
@@ -646,6 +505,7 @@
 
 		if (!pack_to_stdout) {
 			mode_t mode = umask(0);
+			struct stat st;
 			char *idx_tmp_name, tmpname[PATH_MAX];
 
 			umask(mode);
@@ -653,6 +513,7 @@
 
 			idx_tmp_name = write_idx_file(NULL, written_list,
 						      nr_written, sha1);
+
 			snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
 				 base_name, sha1_to_hex(sha1));
 			if (adjust_perm(pack_tmp_name, mode))
@@ -661,6 +522,28 @@
 			if (rename(pack_tmp_name, tmpname))
 				die("unable to rename temporary pack file: %s",
 				    strerror(errno));
+
+			/*
+			 * Packs are runtime accessed in their mtime
+			 * order since newer packs are more likely to contain
+			 * younger objects.  So if we are creating multiple
+			 * packs then we should modify the mtime of later ones
+			 * to preserve this property.
+			 */
+			if (stat(tmpname, &st) < 0) {
+				warning("failed to stat %s: %s",
+					tmpname, strerror(errno));
+			} else if (!last_mtime) {
+				last_mtime = st.st_mtime;
+			} else {
+				struct utimbuf utb;
+				utb.actime = st.st_atime;
+				utb.modtime = --last_mtime;
+				if (utime(tmpname, &utb) < 0)
+					warning("failed utime() on %s: %s",
+						tmpname, strerror(errno));
+			}
+
 			snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
 				 base_name, sha1_to_hex(sha1));
 			if (adjust_perm(idx_tmp_name, mode))
@@ -669,6 +552,7 @@
 			if (rename(idx_tmp_name, tmpname))
 				die("unable to rename temporary index file: %s",
 				    strerror(errno));
+
 			free(idx_tmp_name);
 			free(pack_tmp_name);
 			puts(sha1_to_hex(sha1));
@@ -1161,8 +1045,11 @@
 				die("delta base offset out of bound for %s",
 				    sha1_to_hex(entry->idx.sha1));
 			ofs = entry->in_pack_offset - ofs;
-			if (!no_reuse_delta && !entry->preferred_base)
-				base_ref = find_packed_object_name(p, ofs);
+			if (!no_reuse_delta && !entry->preferred_base) {
+				struct revindex_entry *revidx;
+				revidx = find_pack_revindex(p, ofs);
+				base_ref = nth_packed_object_sha1(p, revidx->nr);
+			}
 			entry->in_pack_header_size = used + used_0;
 			break;
 		}
@@ -1239,9 +1126,11 @@
 		sorted_by_offset[i] = objects + i;
 	qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
 
-	prepare_pack_ix();
+	init_pack_revindex();
+
 	for (i = 0; i < nr_objects; i++)
 		check_object(sorted_by_offset[i]);
+
 	free(sorted_by_offset);
 }
 
@@ -1428,8 +1317,7 @@
 	 * accounting lock.  Compiler will optimize the strangeness
 	 * away when THREADED_DELTA_SEARCH is not defined.
 	 */
-	if (trg_entry->delta_data)
-		free(trg_entry->delta_data);
+	free(trg_entry->delta_data);
 	cache_lock();
 	if (trg_entry->delta_data) {
 		delta_cache_size -= trg_entry->delta_size;
@@ -1770,6 +1658,18 @@
 #define ll_find_deltas(l, s, w, d, p)	find_deltas(l, &s, w, d, p)
 #endif
 
+static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	unsigned char peeled[20];
+
+	if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+	    !peel_ref(path, peeled)        && /* peelable? */
+	    !is_null_sha1(peeled)          && /* annotated tag? */
+	    locate_object_entry(peeled))      /* object packed? */
+		add_object_entry(sha1, OBJ_TAG, NULL, 0);
+	return 0;
+}
+
 static void prepare_pack(int window, int depth)
 {
 	struct object_entry **delta_list;
@@ -1852,11 +1752,11 @@
 	}
 	if (!strcmp(k, "pack.threads")) {
 		delta_search_threads = git_config_int(k, v);
-		if (delta_search_threads < 1)
+		if (delta_search_threads < 0)
 			die("invalid number of threads specified (%d)",
 			    delta_search_threads);
 #ifndef THREADED_DELTA_SEARCH
-		if (delta_search_threads > 1)
+		if (delta_search_threads != 1)
 			warning("no threads support, ignoring %s", k);
 #endif
 		return 0;
@@ -1867,6 +1767,10 @@
 			die("bad pack.indexversion=%d", pack_idx_default_version);
 		return 0;
 	}
+	if (!strcmp(k, "pack.packsizelimit")) {
+		pack_size_limit_cfg = git_config_ulong(k, v);
+		return 0;
+	}
 	return git_default_config(k, v);
 }
 
@@ -2009,7 +1913,6 @@
 
 	init_revisions(&revs, NULL);
 	save_commit_buffer = 0;
-	track_object_refs = 0;
 	setup_revisions(ac, av, &revs, NULL);
 
 	while (fgets(line, sizeof(line), stdin) != NULL) {
@@ -2029,7 +1932,8 @@
 			die("bad revision '%s'", line);
 	}
 
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 	mark_edges_uninteresting(revs.commits, &revs, show_edge);
 	traverse_commit_list(&revs, show_commit, show_object);
 
@@ -2096,6 +2000,7 @@
 		}
 		if (!prefixcmp(arg, "--max-pack-size=")) {
 			char *end;
+			pack_size_limit_cfg = 0;
 			pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024;
 			if (!arg[16] || *end)
 				usage(pack_usage);
@@ -2116,10 +2021,10 @@
 		if (!prefixcmp(arg, "--threads=")) {
 			char *end;
 			delta_search_threads = strtoul(arg+10, &end, 0);
-			if (!arg[10] || *end || delta_search_threads < 1)
+			if (!arg[10] || *end || delta_search_threads < 0)
 				usage(pack_usage);
 #ifndef THREADED_DELTA_SEARCH
-			if (delta_search_threads > 1)
+			if (delta_search_threads != 1)
 				warning("no threads support, "
 					"ignoring %s", arg);
 #endif
@@ -2168,6 +2073,10 @@
 			keep_unreachable = 1;
 			continue;
 		}
+		if (!strcmp("--include-tag", arg)) {
+			include_tag = 1;
+			continue;
+		}
 		if (!strcmp("--unpacked", arg) ||
 		    !prefixcmp(arg, "--unpacked=") ||
 		    !strcmp("--reflog", arg) ||
@@ -2220,12 +2129,20 @@
 	if (pack_to_stdout != !base_name)
 		usage(pack_usage);
 
+	if (!pack_to_stdout && !pack_size_limit)
+		pack_size_limit = pack_size_limit_cfg;
+
 	if (pack_to_stdout && pack_size_limit)
 		die("--max-pack-size cannot be used to build a pack for transfer.");
 
 	if (!pack_to_stdout && thin)
 		die("--thin cannot be used to build an indexable pack.");
 
+#ifdef THREADED_DELTA_SEARCH
+	if (!delta_search_threads)	/* --threads=0 means autodetect */
+		delta_search_threads = online_cpus();
+#endif
+
 	prepare_packed_git();
 
 	if (progress)
@@ -2236,6 +2153,8 @@
 		rp_av[rp_ac] = NULL;
 		get_object_list(rp_ac, rp_av);
 	}
+	if (include_tag && nr_result)
+		for_each_ref(add_ref_tag, NULL);
 	stop_progress(&progress_state);
 
 	if (non_empty && !nr_result)
diff --git a/builtin-prune.c b/builtin-prune.c
index bb8ead9..25f9304 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -4,8 +4,12 @@
 #include "revision.h"
 #include "builtin.h"
 #include "reachable.h"
+#include "parse-options.h"
 
-static const char prune_usage[] = "git-prune [-n]";
+static const char * const prune_usage[] = {
+	"git-prune [-n] [--expire <time>] [--] [<head>...]",
+	NULL
+};
 static int show_only;
 static unsigned long expire;
 
@@ -123,32 +127,33 @@
 
 int cmd_prune(int argc, const char **argv, const char *prefix)
 {
-	int i;
 	struct rev_info revs;
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if (!strcmp(arg, "-n")) {
-			show_only = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--expire")) {
-			if (++i < argc) {
-				expire = approxidate(argv[i]);
-				continue;
-			}
-		}
-		else if (!prefixcmp(arg, "--expire=")) {
-			expire = approxidate(arg + 9);
-			continue;
-		}
-		usage(prune_usage);
-	}
+	const struct option options[] = {
+		OPT_BOOLEAN('n', NULL, &show_only,
+			    "do not remove, show only"),
+		OPT_DATE(0, "expire", &expire,
+			 "expire objects older than <time>"),
+		OPT_END()
+	};
 
 	save_commit_buffer = 0;
 	init_revisions(&revs, prefix);
-	mark_reachable_objects(&revs, 1);
 
+	argc = parse_options(argc, argv, options, prune_usage, 0);
+	while (argc--) {
+		unsigned char sha1[20];
+		const char *name = *argv++;
+
+		if (!get_sha1(name, sha1)) {
+			struct object *object = parse_object(sha1);
+			if (!object)
+				die("bad object: %s", name);
+			add_pending_object(&revs, object, "");
+		}
+		else
+			die("unrecognized argument: %s", name);
+	}
+	mark_reachable_objects(&revs, 1);
 	prune_object_dir(get_object_directory());
 
 	sync();
diff --git a/builtin-push.c b/builtin-push.c
index 9f727c0..b68c681 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -44,15 +44,6 @@
 			strcat(tag, refs[i]);
 			ref = tag;
 		}
-		if (!strcmp("HEAD", ref)) {
-			unsigned char sha1_dummy[20];
-			ref = resolve_ref(ref, sha1_dummy, 1, NULL);
-			if (!ref)
-				die("HEAD cannot be resolved.");
-			if (prefixcmp(ref, "refs/heads/"))
-				die("HEAD cannot be resolved to branch.");
-			ref = xstrdup(ref + 11);
-		}
 		add_refspec(ref);
 	}
 }
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index c0ea034..e9cfd2b 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -13,16 +13,15 @@
 #include "dir.h"
 #include "builtin.h"
 
-#define MAX_TREES 8
 static int nr_trees;
-static struct tree *trees[MAX_TREES];
+static struct tree *trees[MAX_UNPACK_TREES];
 
 static int list_tree(unsigned char *sha1)
 {
 	struct tree *tree;
 
-	if (nr_trees >= MAX_TREES)
-		die("I cannot read more than %d trees", MAX_TREES);
+	if (nr_trees >= MAX_UNPACK_TREES)
+		die("I cannot read more than %d trees", MAX_UNPACK_TREES);
 	tree = parse_tree_indirect(sha1);
 	if (!tree)
 		return -1;
@@ -41,12 +40,12 @@
 	for (i = 0; i < active_nr; i++) {
 		struct cache_entry *ce = active_cache[i];
 		if (ce_stage(ce)) {
+			remove_index_entry(ce);
 			if (last && !strcmp(ce->name, last->name))
 				continue;
 			cache_tree_invalidate_path(active_cache_tree, ce->name);
 			last = ce;
-			ce->ce_mode = 0;
-			ce->ce_flags &= ~htons(CE_STAGEMASK);
+			continue;
 		}
 		*dst++ = ce;
 	}
@@ -97,11 +96,13 @@
 {
 	int i, newfd, stage = 0;
 	unsigned char sha1[20];
-	struct tree_desc t[MAX_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
 	struct unpack_trees_options opts;
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = -1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
 
 	git_config(git_default_config);
 
@@ -220,27 +221,6 @@
 	if ((opts.dir && !opts.update))
 		die("--exclude-per-directory is meaningless unless -u");
 
-	if (opts.prefix) {
-		int pfxlen = strlen(opts.prefix);
-		int pos;
-		if (opts.prefix[pfxlen-1] != '/')
-			die("prefix must end with /");
-		if (stage != 2)
-			die("binding merge takes only one tree");
-		pos = cache_name_pos(opts.prefix, pfxlen);
-		if (0 <= pos)
-			die("corrupt index file");
-		pos = -pos-1;
-		if (pos < active_nr &&
-		    !strncmp(active_cache[pos]->name, opts.prefix, pfxlen))
-			die("subdirectory '%s' already exists.", opts.prefix);
-		pos = cache_name_pos(opts.prefix, pfxlen-1);
-		if (0 <= pos)
-			die("file '%.*s' already exists.",
-					pfxlen-1, opts.prefix);
-		opts.pos = -1 - pos;
-	}
-
 	if (opts.merge) {
 		if (stage < 2)
 			die("just how do you expect me to merge %d trees?", stage-1);
@@ -269,7 +249,8 @@
 		parse_tree(tree);
 		init_tree_desc(t+i, tree->buffer, tree->size);
 	}
-	unpack_trees(nr_trees, t, &opts);
+	if (unpack_trees(nr_trees, t, &opts))
+		return 128;
 
 	/*
 	 * When reading only one tree (either the most basic form,
diff --git a/builtin-reflog.c b/builtin-reflog.c
index ab53c8c..280e24e 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -14,6 +14,8 @@
 
 static const char reflog_expire_usage[] =
 "git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+static const char reflog_delete_usage[] =
+"git-reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
 
 static unsigned long default_reflog_expire;
 static unsigned long default_reflog_expire_unreachable;
@@ -22,9 +24,12 @@
 	struct rev_info revs;
 	int dry_run;
 	int stalefix;
+	int rewrite;
+	int updateref;
 	int verbose;
 	unsigned long expire_total;
 	unsigned long expire_unreachable;
+	int recno;
 };
 
 struct expire_reflog_cb {
@@ -32,6 +37,7 @@
 	const char *ref;
 	struct commit *ref_commit;
 	struct cmd_reflog_expire_cb *cmd;
+	unsigned char last_kept_sha1[20];
 };
 
 struct collected_reflog {
@@ -213,6 +219,9 @@
 	if (timestamp < cb->cmd->expire_total)
 		goto prune;
 
+	if (cb->cmd->rewrite)
+		osha1 = cb->last_kept_sha1;
+
 	old = new = NULL;
 	if (cb->cmd->stalefix &&
 	    (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
@@ -230,6 +239,9 @@
 			goto prune;
 	}
 
+	if (cb->cmd->recno && --(cb->cmd->recno) == 0)
+		goto prune;
+
 	if (cb->newlog) {
 		char sign = (tz < 0) ? '-' : '+';
 		int zone = (tz < 0) ? (-tz) : tz;
@@ -237,6 +249,7 @@
 			sha1_to_hex(osha1), sha1_to_hex(nsha1),
 			email, timestamp, sign, zone,
 			message);
+		hashcpy(cb->last_kept_sha1, nsha1);
 	}
 	if (cb->cmd->verbose)
 		printf("keep %s", message);
@@ -280,10 +293,20 @@
 			status |= error("%s: %s", strerror(errno),
 					newlog_path);
 			unlink(newlog_path);
+		} else if (cmd->updateref &&
+			(write_in_full(lock->lock_fd,
+				sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+			 write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+			 close_ref(lock) < 0)) {
+			status |= error("Couldn't write %s",
+				lock->lk->filename);
+			unlink(newlog_path);
 		} else if (rename(newlog_path, log_file)) {
 			status |= error("cannot rename %s to %s",
 					newlog_path, log_file);
 			unlink(newlog_path);
+		} else if (cmd->updateref && commit_ref(lock)) {
+			status |= error("Couldn't set %s", lock->ref_name);
 		}
 	}
 	free(newlog_path);
@@ -358,6 +381,10 @@
 			cb.expire_unreachable = approxidate(arg + 21);
 		else if (!strcmp(arg, "--stale-fix"))
 			cb.stalefix = 1;
+		else if (!strcmp(arg, "--rewrite"))
+			cb.rewrite = 1;
+		else if (!strcmp(arg, "--updateref"))
+			cb.updateref = 1;
 		else if (!strcmp(arg, "--all"))
 			do_all = 1;
 		else if (!strcmp(arg, "--verbose"))
@@ -406,6 +433,78 @@
 	return status;
 }
 
+static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+		const char *email, unsigned long timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	struct cmd_reflog_expire_cb *cb = cb_data;
+	if (!cb->expire_total || timestamp < cb->expire_total)
+		cb->recno++;
+	return 0;
+}
+
+static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
+{
+	struct cmd_reflog_expire_cb cb;
+	int i, status = 0;
+
+	memset(&cb, 0, sizeof(cb));
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+			cb.dry_run = 1;
+		else if (!strcmp(arg, "--rewrite"))
+			cb.rewrite = 1;
+		else if (!strcmp(arg, "--updateref"))
+			cb.updateref = 1;
+		else if (!strcmp(arg, "--verbose"))
+			cb.verbose = 1;
+		else if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(reflog_delete_usage);
+		else
+			break;
+	}
+
+	if (argc - i < 1)
+		return error("Nothing to delete?");
+
+	for ( ; i < argc; i++) {
+		const char *spec = strstr(argv[i], "@{");
+		unsigned char sha1[20];
+		char *ep, *ref;
+		int recno;
+
+		if (!spec) {
+			status |= error("Not a reflog: %s", argv[i]);
+			continue;
+		}
+
+		if (!dwim_ref(argv[i], spec - argv[i], sha1, &ref)) {
+			status |= error("%s points nowhere!", argv[i]);
+			continue;
+		}
+
+		recno = strtoul(spec + 2, &ep, 10);
+		if (*ep == '}') {
+			cb.recno = -recno;
+			for_each_reflog_ent(ref, count_reflog_ent, &cb);
+		} else {
+			cb.expire_total = approxidate(spec + 2);
+			for_each_reflog_ent(ref, count_reflog_ent, &cb);
+			cb.expire_total = 0;
+		}
+
+		status |= expire_reflog(ref, sha1, 0, &cb);
+		free(ref);
+	}
+	return status;
+}
+
 /*
  * main "reflog"
  */
@@ -425,6 +524,9 @@
 	if (!strcmp(argv[1], "expire"))
 		return cmd_reflog_expire(argc - 1, argv + 1, prefix);
 
+	if (!strcmp(argv[1], "delete"))
+		return cmd_reflog_delete(argc - 1, argv + 1, prefix);
+
 	/* Not a recognized reflog command..*/
 	usage(reflog_usage);
 }
diff --git a/builtin-remote.c b/builtin-remote.c
new file mode 100644
index 0000000..4149f3b
--- /dev/null
+++ b/builtin-remote.c
@@ -0,0 +1,614 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "transport.h"
+#include "remote.h"
+#include "path-list.h"
+#include "strbuf.h"
+#include "run-command.h"
+#include "refs.h"
+
+static const char * const builtin_remote_usage[] = {
+	"git remote",
+	"git remote add <name> <url>",
+	"git remote rm <name>",
+	"git remote show <name>",
+	"git remote prune <name>",
+	"git remote update [group]",
+	NULL
+};
+
+static int verbose;
+
+static inline int postfixcmp(const char *string, const char *postfix)
+{
+	int len1 = strlen(string), len2 = strlen(postfix);
+	if (len1 < len2)
+		return 1;
+	return strcmp(string + len1 - len2, postfix);
+}
+
+static inline const char *skip_prefix(const char *name, const char *prefix)
+{
+	return !name ? "" :
+		prefixcmp(name, prefix) ?  name : name + strlen(prefix);
+}
+
+static int opt_parse_track(const struct option *opt, const char *arg, int not)
+{
+	struct path_list *list = opt->value;
+	if (not)
+		path_list_clear(list, 0);
+	else
+		path_list_append(arg, list);
+	return 0;
+}
+
+static int fetch_remote(const char *name)
+{
+	const char *argv[] = { "fetch", name, NULL };
+	printf("Updating %s\n", name);
+	if (run_command_v_opt(argv, RUN_GIT_CMD))
+		return error("Could not fetch %s", name);
+	return 0;
+}
+
+static int add(int argc, const char **argv)
+{
+	int fetch = 0, mirror = 0;
+	struct path_list track = { NULL, 0, 0 };
+	const char *master = NULL;
+	struct remote *remote;
+	struct strbuf buf, buf2;
+	const char *name, *url;
+	int i;
+
+	struct option options[] = {
+		OPT_GROUP("add specific options"),
+		OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+		OPT_CALLBACK('t', "track", &track, "branch",
+			"branch(es) to track", opt_parse_track),
+		OPT_STRING('m', "master", &master, "branch", "master branch"),
+		OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+	if (argc < 2)
+		usage_with_options(builtin_remote_usage, options);
+
+	name = argv[0];
+	url = argv[1];
+
+	remote = remote_get(name);
+	if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
+			remote->fetch_refspec_nr))
+		die("remote %s already exists.", name);
+
+	strbuf_init(&buf, 0);
+	strbuf_init(&buf2, 0);
+
+	strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
+	if (!valid_fetch_refspec(buf2.buf))
+		die("'%s' is not a valid remote name", name);
+
+	strbuf_addf(&buf, "remote.%s.url", name);
+	if (git_config_set(buf.buf, url))
+		return 1;
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", name);
+
+	if (track.nr == 0)
+		path_list_append("*", &track);
+	for (i = 0; i < track.nr; i++) {
+		struct path_list_item *item = track.items + i;
+
+		strbuf_reset(&buf2);
+		strbuf_addch(&buf2, '+');
+		if (mirror)
+			strbuf_addf(&buf2, "refs/%s:refs/%s",
+					item->path, item->path);
+		else
+			strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
+					item->path, name, item->path);
+		if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+			return 1;
+	}
+
+	if (fetch && fetch_remote(name))
+		return 1;
+
+	if (master) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/remotes/%s/HEAD", name);
+
+		strbuf_reset(&buf2);
+		strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
+
+		if (create_symref(buf.buf, buf2.buf, "remote add"))
+			return error("Could not setup master '%s'", master);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&buf2);
+	path_list_clear(&track, 0);
+
+	return 0;
+}
+
+struct branch_info {
+	char *remote;
+	struct path_list merge;
+};
+
+static struct path_list branch_list;
+
+static int config_read_branches(const char *key, const char *value)
+{
+	if (!prefixcmp(key, "branch.")) {
+		char *name;
+		struct path_list_item *item;
+		struct branch_info *info;
+		enum { REMOTE, MERGE } type;
+
+		key += 7;
+		if (!postfixcmp(key, ".remote")) {
+			name = xstrndup(key, strlen(key) - 7);
+			type = REMOTE;
+		} else if (!postfixcmp(key, ".merge")) {
+			name = xstrndup(key, strlen(key) - 6);
+			type = MERGE;
+		} else
+			return 0;
+
+		item = path_list_insert(name, &branch_list);
+
+		if (!item->util)
+			item->util = xcalloc(sizeof(struct branch_info), 1);
+		info = item->util;
+		if (type == REMOTE) {
+			if (info->remote)
+				warning("more than one branch.%s", key);
+			info->remote = xstrdup(value);
+		} else {
+			char *space = strchr(value, ' ');
+			value = skip_prefix(value, "refs/heads/");
+			while (space) {
+				char *merge;
+				merge = xstrndup(value, space - value);
+				path_list_append(merge, &info->merge);
+				value = skip_prefix(space + 1, "refs/heads/");
+				space = strchr(value, ' ');
+			}
+			path_list_append(xstrdup(value), &info->merge);
+		}
+	}
+	return 0;
+}
+
+static void read_branches(void)
+{
+	if (branch_list.nr)
+		return;
+	git_config(config_read_branches);
+	sort_path_list(&branch_list);
+}
+
+struct ref_states {
+	struct remote *remote;
+	struct strbuf remote_prefix;
+	struct path_list new, stale, tracked;
+};
+
+static int handle_one_branch(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct ref_states *states = cb_data;
+	struct refspec refspec;
+
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (!remote_find_tracking(states->remote, &refspec)) {
+		struct path_list_item *item;
+		const char *name = skip_prefix(refspec.src, "refs/heads/");
+		/* symbolic refs pointing nowhere were handled already */
+		if ((flags & REF_ISSYMREF) ||
+				unsorted_path_list_has_path(&states->tracked,
+					name) ||
+				unsorted_path_list_has_path(&states->new,
+					name))
+			return 0;
+		item = path_list_append(name, &states->stale);
+		item->util = xstrdup(refname);
+	}
+	return 0;
+}
+
+static int get_ref_states(const struct ref *ref, struct ref_states *states)
+{
+	struct ref *fetch_map = NULL, **tail = &fetch_map;
+	int i;
+
+	for (i = 0; i < states->remote->fetch_refspec_nr; i++)
+		if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
+			die("Could not get fetch map for refspec %s",
+				states->remote->fetch_refspec[i]);
+
+	states->new.strdup_paths = states->tracked.strdup_paths = 1;
+	for (ref = fetch_map; ref; ref = ref->next) {
+		struct path_list *target = &states->tracked;
+		unsigned char sha1[20];
+		void *util = NULL;
+
+		if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
+			target = &states->new;
+		else {
+			target = &states->tracked;
+			if (hashcmp(sha1, ref->new_sha1))
+				util = &states;
+		}
+		path_list_append(skip_prefix(ref->name, "refs/heads/"),
+				target)->util = util;
+	}
+	free_refs(fetch_map);
+
+	strbuf_addf(&states->remote_prefix,
+		"refs/remotes/%s/", states->remote->name);
+	for_each_ref(handle_one_branch, states);
+	sort_path_list(&states->stale);
+
+	return 0;
+}
+
+struct branches_for_remote {
+	const char *prefix;
+	struct path_list *branches;
+};
+
+static int add_branch_for_removal(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct branches_for_remote *branches = cb_data;
+
+	if (!prefixcmp(refname, branches->prefix)) {
+		struct path_list_item *item;
+
+		/* make sure that symrefs are deleted */
+		if (flags & REF_ISSYMREF)
+			return unlink(git_path(refname));
+
+		item = path_list_append(refname, branches->branches);
+		item->util = xmalloc(20);
+		hashcpy(item->util, sha1);
+	}
+
+	return 0;
+}
+
+static int remove_branches(struct path_list *branches)
+{
+	int i, result = 0;
+	for (i = 0; i < branches->nr; i++) {
+		struct path_list_item *item = branches->items + i;
+		const char *refname = item->path;
+		unsigned char *sha1 = item->util;
+
+		if (delete_ref(refname, sha1))
+			result |= error("Could not remove branch %s", refname);
+	}
+	return result;
+}
+
+static int rm(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct remote *remote;
+	struct strbuf buf;
+	struct path_list branches = { NULL, 0, 0, 1 };
+	struct branches_for_remote cb_data = { NULL, &branches };
+	int i;
+
+	if (argc != 2)
+		usage_with_options(builtin_remote_usage, options);
+
+	remote = remote_get(argv[1]);
+	if (!remote)
+		die("No such remote: %s", argv[1]);
+
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "remote.%s", remote->name);
+	if (git_config_rename_section(buf.buf, NULL) < 1)
+		return error("Could not remove config section '%s'", buf.buf);
+
+	read_branches();
+	for (i = 0; i < branch_list.nr; i++) {
+		struct path_list_item *item = branch_list.items + i;
+		struct branch_info *info = item->util;
+		if (info->remote && !strcmp(info->remote, remote->name)) {
+			const char *keys[] = { "remote", "merge", NULL }, **k;
+			for (k = keys; *k; k++) {
+				strbuf_reset(&buf);
+				strbuf_addf(&buf, "branch.%s.%s",
+						item->path, *k);
+				if (git_config_set(buf.buf, NULL)) {
+					strbuf_release(&buf);
+					return -1;
+				}
+			}
+		}
+	}
+
+	/*
+	 * We cannot just pass a function to for_each_ref() which deletes
+	 * the branches one by one, since for_each_ref() relies on cached
+	 * refs, which are invalidated when deleting a branch.
+	 */
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "refs/remotes/%s/", remote->name);
+	cb_data.prefix = buf.buf;
+	i = for_each_ref(add_branch_for_removal, &cb_data);
+	strbuf_release(&buf);
+
+	if (!i)
+		i = remove_branches(&branches);
+	path_list_clear(&branches, 1);
+
+	return i;
+}
+
+static void show_list(const char *title, struct path_list *list)
+{
+	int i;
+
+	if (!list->nr)
+		return;
+
+	printf(title, list->nr > 1 ? "es" : "");
+	printf("\n    ");
+	for (i = 0; i < list->nr; i++)
+		printf("%s%s", i ? " " : "", list->items[i].path);
+	printf("\n");
+}
+
+static int show_or_prune(int argc, const char **argv, int prune)
+{
+	int dry_run = 0, result = 0;
+	struct option options[] = {
+		OPT_GROUP("show specific options"),
+		OPT__DRY_RUN(&dry_run),
+		OPT_END()
+	};
+	struct ref_states states;
+
+	argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+
+	if (argc < 1)
+		usage_with_options(builtin_remote_usage, options);
+
+	memset(&states, 0, sizeof(states));
+	for (; argc; argc--, argv++) {
+		struct transport *transport;
+		const struct ref *ref;
+		struct strbuf buf;
+		int i, got_states;
+
+		states.remote = remote_get(*argv);
+		if (!states.remote)
+			return error("No such remote: %s", *argv);
+		transport = transport_get(NULL, states.remote->url_nr > 0 ?
+			states.remote->url[0] : NULL);
+		ref = transport_get_remote_refs(transport);
+		transport_disconnect(transport);
+
+		read_branches();
+		got_states = get_ref_states(ref, &states);
+		if (got_states)
+			result = error("Error getting local info for '%s'",
+					states.remote->name);
+
+		if (prune) {
+			struct strbuf buf;
+			int prefix_len;
+
+			strbuf_init(&buf, 0);
+			if (states.remote->fetch_refspec_nr == 1 &&
+					states.remote->fetch->pattern &&
+					!strcmp(states.remote->fetch->src,
+						states.remote->fetch->dst))
+				/* handle --mirror remote */
+				strbuf_addstr(&buf, "refs/heads/");
+			else
+				strbuf_addf(&buf, "refs/remotes/%s/", *argv);
+			prefix_len = buf.len;
+
+			for (i = 0; i < states.stale.nr; i++) {
+				strbuf_setlen(&buf, prefix_len);
+				strbuf_addstr(&buf, states.stale.items[i].path);
+				result |= delete_ref(buf.buf, NULL);
+			}
+
+			strbuf_release(&buf);
+			goto cleanup_states;
+		}
+
+		printf("* remote %s\n  URL: %s\n", *argv,
+			states.remote->url_nr > 0 ?
+				states.remote->url[0] : "(no URL)");
+
+		for (i = 0; i < branch_list.nr; i++) {
+			struct path_list_item *branch = branch_list.items + i;
+			struct branch_info *info = branch->util;
+			int j;
+
+			if (!info->merge.nr || strcmp(*argv, info->remote))
+				continue;
+			printf("  Remote branch%s merged with 'git pull' "
+				"while on branch %s\n   ",
+				info->merge.nr > 1 ? "es" : "",
+				branch->path);
+			for (j = 0; j < info->merge.nr; j++)
+				printf(" %s", info->merge.items[j].path);
+			printf("\n");
+		}
+
+		if (got_states)
+			continue;
+		strbuf_init(&buf, 0);
+		strbuf_addf(&buf, "  New remote branch%%s (next fetch will "
+			"store in remotes/%s)", states.remote->name);
+		show_list(buf.buf, &states.new);
+		strbuf_release(&buf);
+		show_list("  Stale tracking branch%s (use 'git remote prune')",
+				&states.stale);
+		show_list("  Tracked remote branch%s",
+				&states.tracked);
+
+		if (states.remote->push_refspec_nr) {
+			printf("  Local branch%s pushed with 'git push'\n   ",
+				states.remote->push_refspec_nr > 1 ?
+					"es" : "");
+			for (i = 0; i < states.remote->push_refspec_nr; i++) {
+				struct refspec *spec = states.remote->push + i;
+				printf(" %s%s%s%s", spec->force ? "+" : "",
+					skip_prefix(spec->src, "refs/heads/"),
+					spec->dst ? ":" : "",
+					skip_prefix(spec->dst, "refs/heads/"));
+			}
+			printf("\n");
+		}
+cleanup_states:
+		/* NEEDSWORK: free remote */
+		path_list_clear(&states.new, 0);
+		path_list_clear(&states.stale, 0);
+		path_list_clear(&states.tracked, 0);
+	}
+
+	return result;
+}
+
+static int get_one_remote_for_update(struct remote *remote, void *priv)
+{
+	struct path_list *list = priv;
+	if (!remote->skip_default_update)
+		path_list_append(xstrdup(remote->name), list);
+	return 0;
+}
+
+struct remote_group {
+	const char *name;
+	struct path_list *list;
+} remote_group;
+
+static int get_remote_group(const char *key, const char *value)
+{
+	if (!prefixcmp(key, "remotes.") &&
+			!strcmp(key + 8, remote_group.name)) {
+		/* split list by white space */
+		int space = strcspn(value, " \t\n");
+		while (*value) {
+			if (space > 1)
+				path_list_append(xstrndup(value, space),
+						remote_group.list);
+			value += space + (value[space] != '\0');
+			space = strcspn(value, " \t\n");
+		}
+	}
+
+	return 0;
+}
+
+static int update(int argc, const char **argv)
+{
+	int i, result = 0;
+	struct path_list list = { NULL, 0, 0, 0 };
+	static const char *default_argv[] = { NULL, "default", NULL };
+
+	if (argc < 2) {
+		argc = 2;
+		argv = default_argv;
+	}
+
+	remote_group.list = &list;
+	for (i = 1; i < argc; i++) {
+		remote_group.name = argv[i];
+		result = git_config(get_remote_group);
+	}
+
+	if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
+		result = for_each_remote(get_one_remote_for_update, &list);
+
+	for (i = 0; i < list.nr; i++)
+		result |= fetch_remote(list.items[i].path);
+
+	/* all names were strdup()ed or strndup()ed */
+	list.strdup_paths = 1;
+	path_list_clear(&list, 0);
+
+	return result;
+}
+
+static int get_one_entry(struct remote *remote, void *priv)
+{
+	struct path_list *list = priv;
+
+	path_list_append(remote->name, list)->util = remote->url_nr ?
+		(void *)remote->url[0] : NULL;
+	if (remote->url_nr > 1)
+		warning("Remote %s has more than one URL", remote->name);
+
+	return 0;
+}
+
+static int show_all(void)
+{
+	struct path_list list = { NULL, 0, 0 };
+	int result = for_each_remote(get_one_entry, &list);
+
+	if (!result) {
+		int i;
+
+		sort_path_list(&list);
+		for (i = 0; i < list.nr; i++) {
+			struct path_list_item *item = list.items + i;
+			printf("%s%s%s\n", item->path,
+				verbose ? "\t" : "",
+				verbose && item->util ?
+					(const char *)item->util : "");
+		}
+	}
+	return result;
+}
+
+int cmd_remote(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = {
+		OPT__VERBOSE(&verbose),
+		OPT_END()
+	};
+	int result;
+
+	argc = parse_options(argc, argv, options, builtin_remote_usage,
+		PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (argc < 1)
+		result = show_all();
+	else if (!strcmp(argv[0], "add"))
+		result = add(argc, argv);
+	else if (!strcmp(argv[0], "rm"))
+		result = rm(argc, argv);
+	else if (!strcmp(argv[0], "show"))
+		result = show_or_prune(argc, argv, 0);
+	else if (!strcmp(argv[0], "prune"))
+		result = show_or_prune(argc, argv, 1);
+	else if (!strcmp(argv[0], "update"))
+		result = update(argc, argv);
+	else {
+		error("Unknown subcommand: %s", argv[0]);
+		usage_with_options(builtin_remote_usage, options);
+	}
+
+	return result ? 1 : 0;
+}
diff --git a/builtin-rerere.c b/builtin-rerere.c
index b2971f3..c607aad 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -149,8 +149,8 @@
 		if (ce_stage(e2) == 2 &&
 		    ce_stage(e3) == 3 &&
 		    ce_same_name(e2, e3) &&
-		    S_ISREG(ntohl(e2->ce_mode)) &&
-		    S_ISREG(ntohl(e3->ce_mode))) {
+		    S_ISREG(e2->ce_mode) &&
+		    S_ISREG(e3->ce_mode)) {
 			path_list_insert((const char *)e2->name, conflict);
 			i++; /* skip over both #2 and #3 */
 		}
diff --git a/builtin-reset.c b/builtin-reset.c
index 7ee811f..79424bb 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -16,9 +16,14 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "tree.h"
+#include "branch.h"
+#include "parse-options.h"
 
-static const char builtin_reset_usage[] =
-"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]";
+static const char * const git_reset_usage[] = {
+	"git-reset [--mixed | --soft | --hard] [-q] [<commit>]",
+	"git-reset [--mixed] <commit> [--] <paths>...",
+	NULL
+};
 
 static char *args_to_str(const char **argv)
 {
@@ -44,18 +49,6 @@
 	return !access(git_path("MERGE_HEAD"), F_OK);
 }
 
-static int unmerged_files(void)
-{
-	int i;
-	read_cache();
-	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
-		if (ce_stage(ce))
-			return 1;
-	}
-	return 0;
-}
-
 static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
 {
 	int i = 0;
@@ -74,14 +67,10 @@
 
 static void print_new_head_line(struct commit *commit)
 {
-	const char *hex, *dots = "...", *body;
+	const char *hex, *body;
 
 	hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
-	if (!hex) {
-		hex = sha1_to_hex(commit->object.sha1);
-		dots = "";
-	}
-	printf("HEAD is now at %s%s", hex, dots);
+	printf("HEAD is now at %s", hex);
 	body = strstr(commit->buffer, "\n\n");
 	if (body) {
 		const char *eol;
@@ -180,40 +169,31 @@
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
-	int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0;
+	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
 	const char *rev = "HEAD";
 	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
 				*old_orig = NULL, sha1_old_orig[20];
 	struct commit *commit;
 	char *reflog_action, msg[1024];
+	const struct option options[] = {
+		OPT_SET_INT(0, "mixed", &reset_type,
+						"reset HEAD and index", MIXED),
+		OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+		OPT_SET_INT(0, "hard", &reset_type,
+				"reset HEAD, index and working tree", HARD),
+		OPT_BOOLEAN('q', NULL, &quiet,
+				"disable showing new HEAD in hard reset"),
+		OPT_END()
+	};
 
 	git_config(git_default_config);
 
+	argc = parse_options(argc, argv, options, git_reset_usage,
+						PARSE_OPT_KEEP_DASHDASH);
 	reflog_action = args_to_str(argv);
 	setenv("GIT_REFLOG_ACTION", reflog_action, 0);
 
-	while (i < argc) {
-		if (!strcmp(argv[i], "--mixed")) {
-			reset_type = MIXED;
-			i++;
-		}
-		else if (!strcmp(argv[i], "--soft")) {
-			reset_type = SOFT;
-			i++;
-		}
-		else if (!strcmp(argv[i], "--hard")) {
-			reset_type = HARD;
-			i++;
-		}
-		else if (!strcmp(argv[i], "-q")) {
-			quiet = 1;
-			i++;
-		}
-		else
-			break;
-	}
-
-	if (i < argc && argv[i][0] != '-')
+	if (i < argc && strcmp(argv[i], "--"))
 		rev = argv[i++];
 
 	if (get_sha1(rev, sha1))
@@ -226,8 +206,6 @@
 
 	if (i < argc && !strcmp(argv[i], "--"))
 		i++;
-	else if (i < argc && argv[i][0] == '-')
-		usage(builtin_reset_usage);
 
 	/* git reset tree [--] paths... can be used to
 	 * load chosen paths from the tree into the index without
@@ -250,7 +228,7 @@
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
 	if (reset_type == SOFT) {
-		if (is_merge() || unmerged_files())
+		if (is_merge() || read_cache() < 0 || unmerged_cache())
 			die("Cannot do a soft reset in the middle of a merge.");
 	}
 	else if (reset_index_file(sha1, (reset_type == HARD)))
@@ -282,10 +260,7 @@
 		break;
 	}
 
-	unlink(git_path("MERGE_HEAD"));
-	unlink(git_path("rr-cache/MERGE_RR"));
-	unlink(git_path("MERGE_MSG"));
-	unlink(git_path("SQUASH_MSG"));
+	remove_branch_state();
 
 	free(reflog_action);
 
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index de80158..edc0bd3 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -25,11 +25,15 @@
 "    --no-merges\n"
 "    --remove-empty\n"
 "    --all\n"
+"    --branches\n"
+"    --tags\n"
+"    --remotes\n"
 "    --stdin\n"
 "    --quiet\n"
 "  ordering output:\n"
 "    --topo-order\n"
 "    --date-order\n"
+"    --reverse\n"
 "  formatting output:\n"
 "    --parents\n"
 "    --objects | --objects-edge\n"
@@ -60,6 +64,8 @@
 		fputs(header_prefix, stdout);
 	if (commit->object.flags & BOUNDARY)
 		putchar('-');
+	else if (commit->object.flags & UNINTERESTING)
+		putchar('^');
 	else if (revs.left_right) {
 		if (commit->object.flags & SYMMETRIC_LEFT)
 			putchar('<');
@@ -84,7 +90,7 @@
 	else
 		putchar('\n');
 
-	if (revs.verbose_header) {
+	if (revs.verbose_header && commit->buffer) {
 		struct strbuf buf;
 		strbuf_init(&buf, 0);
 		pretty_print_commit(revs.commit_format, commit,
@@ -605,11 +611,11 @@
 		usage(rev_list_usage);
 
 	save_commit_buffer = revs.verbose_header || revs.grep_filter;
-	track_object_refs = 0;
 	if (bisect_list)
 		revs.limited = 1;
 
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 	if (revs.tree_objects)
 		mark_edges_uninteresting(revs.commits, &revs, show_edge);
 
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 1ae086a..00b6078 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -328,18 +328,24 @@
 		o->type = OPTION_CALLBACK;
 		o->help = xstrdup(skipspaces(s));
 		o->value = &parsed;
+		o->flags = PARSE_OPT_NOARG;
 		o->callback = &parseopt_dump;
-		switch (s[-1]) {
-		case '=':
-			s--;
-			break;
-		case '?':
-			o->flags = PARSE_OPT_OPTARG;
-			s--;
-			break;
-		default:
-			o->flags = PARSE_OPT_NOARG;
-			break;
+		while (s > sb.buf && strchr("*=?!", s[-1])) {
+			switch (*--s) {
+			case '=':
+				o->flags &= ~PARSE_OPT_NOARG;
+				break;
+			case '?':
+				o->flags &= ~PARSE_OPT_NOARG;
+				o->flags |= PARSE_OPT_OPTARG;
+				break;
+			case '!':
+				o->flags |= PARSE_OPT_NONEG;
+				break;
+			case '*':
+				o->flags |= PARSE_OPT_HIDDEN;
+				break;
+			}
 		}
 
 		if (s - sb.buf == 1) /* short option only */
diff --git a/builtin-revert.c b/builtin-revert.c
index 64f0d0e..607a2f0 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -8,6 +8,7 @@
 #include "exec_cmd.h"
 #include "utf8.h"
 #include "parse-options.h"
+#include "cache-tree.h"
 #include "diff.h"
 #include "revision.h"
 
@@ -283,7 +284,7 @@
 		 * that represents the "current" state for merge-recursive
 		 * to work on.
 		 */
-		if (write_tree(head, 0, NULL))
+		if (write_cache_as_tree(head, 0, NULL))
 			die ("Your index file is unmerged.");
 	} else {
 		if (get_sha1("HEAD", head))
@@ -369,7 +370,7 @@
 	if (merge_recursive(sha1_to_hex(base->object.sha1),
 				sha1_to_hex(head), "HEAD",
 				sha1_to_hex(next->object.sha1), oneline) ||
-			write_tree(head, 0, NULL)) {
+			write_cache_as_tree(head, 0, NULL)) {
 		add_to_msg("\nConflicts:\n\n");
 		read_cache();
 		for (i = 0; i < active_nr;) {
@@ -408,8 +409,7 @@
 		else
 			return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
 	}
-	if (reencoded_message)
-		free(reencoded_message);
+	free(reencoded_message);
 
 	return 0;
 }
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 98c54d9..bb9c33a 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -71,6 +71,7 @@
 		refs = refs->next;
 	}
 
+	close(po.in);
 	if (finish_command(&po))
 		return error("pack-objects died with strange error");
 	return 0;
@@ -263,9 +264,7 @@
 
 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);
+	return find_unique_abbrev(sha1, DEFAULT_ABBREV);
 }
 
 static void print_ok_ref_status(struct ref *ref)
@@ -403,12 +402,15 @@
 	if (!remote_tail)
 		remote_tail = &remote_refs;
 	if (match_refs(local_refs, remote_refs, &remote_tail,
-					       nr_refspec, refspec, flags))
+		       nr_refspec, refspec, flags)) {
+		close(out);
 		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");
+		close(out);
 		return 0;
 	}
 
@@ -495,12 +497,11 @@
 
 	packet_flush(out);
 	if (new_refs && !args.dry_run) {
-		if (pack_objects(out, remote_refs) < 0) {
-			close(out);
+		if (pack_objects(out, remote_refs) < 0)
 			return -1;
-		}
 	}
-	close(out);
+	else
+		close(out);
 
 	if (expect_status_report)
 		ret = receive_status(in, remote_refs);
@@ -648,7 +649,7 @@
 	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]);
+	/* do_send_pack always closes fd[1] */
 	ret |= finish_connect(conn);
 	return !!ret;
 }
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 01cfd7b..e6a2865 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -6,13 +6,11 @@
 #include "revision.h"
 #include "utf8.h"
 #include "mailmap.h"
+#include "shortlog.h"
 
 static const char shortlog_usage[] =
 "git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
 
-static char *common_repo_prefix;
-static int email;
-
 static int compare_by_number(const void *a1, const void *a2)
 {
 	const struct path_list_item *i1 = a1, *i2 = a2;
@@ -26,13 +24,11 @@
 		return -1;
 }
 
-static struct path_list mailmap = {NULL, 0, 0, 0};
-
-static void insert_one_record(struct path_list *list,
+static void insert_one_record(struct shortlog *log,
 			      const char *author,
 			      const char *oneline)
 {
-	const char *dot3 = common_repo_prefix;
+	const char *dot3 = log->common_repo_prefix;
 	char *buffer, *p;
 	struct path_list_item *item;
 	struct path_list *onelines;
@@ -47,7 +43,7 @@
 	eoemail = strchr(boemail, '>');
 	if (!eoemail)
 		return;
-	if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
+	if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
 		while (author < boemail && isspace(*author))
 			author++;
 		for (len = 0;
@@ -61,14 +57,14 @@
 	else
 		len = strlen(namebuf);
 
-	if (email) {
+	if (log->email) {
 		size_t room = sizeof(namebuf) - len - 1;
 		int maillen = eoemail - boemail + 1;
 		snprintf(namebuf + len, room, " %.*s", maillen, boemail);
 	}
 
 	buffer = xstrdup(namebuf);
-	item = path_list_insert(buffer, list);
+	item = path_list_insert(buffer, &log->list);
 	if (item->util == NULL)
 		item->util = xcalloc(1, sizeof(struct path_list));
 	else
@@ -115,7 +111,7 @@
 	onelines->items[onelines->nr++].path = buffer;
 }
 
-static void read_from_stdin(struct path_list *list)
+static void read_from_stdin(struct shortlog *log)
 {
 	char author[1024], oneline[1024];
 
@@ -129,38 +125,43 @@
 		while (fgets(oneline, sizeof(oneline), stdin) &&
 		       oneline[0] == '\n')
 			; /* discard blanks */
-		insert_one_record(list, author + 8, oneline);
+		insert_one_record(log, author + 8, oneline);
 	}
 }
 
-static void get_from_rev(struct rev_info *rev, struct path_list *list)
+void shortlog_add_commit(struct shortlog *log, struct commit *commit)
+{
+	const char *author = NULL, *buffer;
+
+	buffer = commit->buffer;
+	while (*buffer && *buffer != '\n') {
+		const char *eol = strchr(buffer, '\n');
+
+		if (eol == NULL)
+			eol = buffer + strlen(buffer);
+		else
+			eol++;
+
+		if (!prefixcmp(buffer, "author "))
+			author = buffer + 7;
+		buffer = eol;
+	}
+	if (!author)
+		die("Missing author: %s",
+		    sha1_to_hex(commit->object.sha1));
+	if (*buffer)
+		buffer++;
+	insert_one_record(log, author, !*buffer ? "<none>" : buffer);
+}
+
+static void get_from_rev(struct rev_info *rev, struct shortlog *log)
 {
 	struct commit *commit;
 
-	prepare_revision_walk(rev);
-	while ((commit = get_revision(rev)) != NULL) {
-		const char *author = NULL, *buffer;
-
-		buffer = commit->buffer;
-		while (*buffer && *buffer != '\n') {
-			const char *eol = strchr(buffer, '\n');
-
-			if (eol == NULL)
-				eol = buffer + strlen(buffer);
-			else
-				eol++;
-
-			if (!prefixcmp(buffer, "author "))
-				author = buffer + 7;
-			buffer = eol;
-		}
-		if (!author)
-			die("Missing author: %s",
-			    sha1_to_hex(commit->object.sha1));
-		if (*buffer)
-			buffer++;
-		insert_one_record(list, author, !*buffer ? "<none>" : buffer);
-	}
+	if (prepare_revision_walk(rev))
+		die("revision walk setup failed");
+	while ((commit = get_revision(rev)) != NULL)
+		shortlog_add_commit(log, commit);
 }
 
 static int parse_uint(char const **arg, int comma)
@@ -212,29 +213,40 @@
 		die(wrap_arg_usage);
 }
 
+void shortlog_init(struct shortlog *log)
+{
+	memset(log, 0, sizeof(*log));
+
+	read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
+
+	log->list.strdup_paths = 1;
+	log->wrap = DEFAULT_WRAPLEN;
+	log->in1 = DEFAULT_INDENT1;
+	log->in2 = DEFAULT_INDENT2;
+}
+
 int cmd_shortlog(int argc, const char **argv, const char *prefix)
 {
+	struct shortlog log;
 	struct rev_info rev;
-	struct path_list list = { NULL, 0, 0, 1 };
-	int i, j, sort_by_number = 0, summary = 0;
-	int wrap_lines = 0;
-	int wrap = DEFAULT_WRAPLEN;
-	int in1 = DEFAULT_INDENT1;
-	int in2 = DEFAULT_INDENT2;
+	int nongit;
+
+	prefix = setup_git_directory_gently(&nongit);
+	shortlog_init(&log);
 
 	/* since -n is a shadowed rev argument, parse our args first */
 	while (argc > 1) {
 		if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
-			sort_by_number = 1;
+			log.sort_by_number = 1;
 		else if (!strcmp(argv[1], "-s") ||
 				!strcmp(argv[1], "--summary"))
-			summary = 1;
+			log.summary = 1;
 		else if (!strcmp(argv[1], "-e") ||
 			 !strcmp(argv[1], "--email"))
-			email = 1;
+			log.email = 1;
 		else if (!prefixcmp(argv[1], "-w")) {
-			wrap_lines = 1;
-			parse_wrap_args(argv[1], &in1, &in2, &wrap);
+			log.wrap_lines = 1;
+			parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
 		}
 		else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
 			usage(shortlog_usage);
@@ -248,34 +260,38 @@
 	if (argc > 1)
 		die ("unrecognized argument: %s", argv[1]);
 
-	read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
-
 	/* assume HEAD if from a tty */
-	if (!rev.pending.nr && isatty(0))
+	if (!nongit && !rev.pending.nr && isatty(0))
 		add_head_to_pending(&rev);
 	if (rev.pending.nr == 0) {
-		read_from_stdin(&list);
+		read_from_stdin(&log);
 	}
 	else
-		get_from_rev(&rev, &list);
+		get_from_rev(&rev, &log);
 
-	if (sort_by_number)
-		qsort(list.items, list.nr, sizeof(struct path_list_item),
+	shortlog_output(&log);
+	return 0;
+}
+
+void shortlog_output(struct shortlog *log)
+{
+	int i, j;
+	if (log->sort_by_number)
+		qsort(log->list.items, log->list.nr, sizeof(struct path_list_item),
 			compare_by_number);
+	for (i = 0; i < log->list.nr; i++) {
+		struct path_list *onelines = log->list.items[i].util;
 
-	for (i = 0; i < list.nr; i++) {
-		struct path_list *onelines = list.items[i].util;
-
-		if (summary) {
-			printf("%6d\t%s\n", onelines->nr, list.items[i].path);
+		if (log->summary) {
+			printf("%6d\t%s\n", onelines->nr, log->list.items[i].path);
 		} else {
-			printf("%s (%d):\n", list.items[i].path, onelines->nr);
+			printf("%s (%d):\n", log->list.items[i].path, onelines->nr);
 			for (j = onelines->nr - 1; j >= 0; j--) {
 				const char *msg = onelines->items[j].path;
 
-				if (wrap_lines) {
-					int col = print_wrapped_text(msg, in1, in2, wrap);
-					if (col != wrap)
+				if (log->wrap_lines) {
+					int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
+					if (col != log->wrap)
 						putchar('\n');
 				}
 				else
@@ -287,13 +303,11 @@
 		onelines->strdup_paths = 1;
 		path_list_clear(onelines, 1);
 		free(onelines);
-		list.items[i].util = NULL;
+		log->list.items[i].util = NULL;
 	}
 
-	list.strdup_paths = 1;
-	path_list_clear(&list, 1);
-	mailmap.strdup_paths = 1;
-	path_list_clear(&mailmap, 1);
-
-	return 0;
+	log->list.strdup_paths = 1;
+	path_list_clear(&log->list, 1);
+	log->mailmap.strdup_paths = 1;
+	path_list_clear(&log->mailmap, 1);
 }
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 019abd3..a383323 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -782,8 +782,8 @@
 				has_head++;
 		}
 		if (!has_head) {
-			int pfxlen = strlen("refs/heads/");
-			append_one_rev(head + pfxlen);
+			int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
+			append_one_rev(head + offset);
 		}
 	}
 
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 65051d1..a323633 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -86,6 +86,9 @@
 			    sha1_to_hex(sha1));
 		if (obj->type == OBJ_TAG) {
 			obj = deref_tag(obj, refname, 0);
+			if (!obj)
+				die("git-show-ref: bad tag at ref %s (%s)", refname,
+				    sha1_to_hex(sha1));
 			hex = find_unique_abbrev(obj->sha1, abbrev);
 			printf("%s %s^{}\n", hex, refname);
 		}
diff --git a/builtin-tag.c b/builtin-tag.c
index 95ecfdb..129ff57 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -230,19 +230,17 @@
 
 	if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
 		close(gpg.in);
+		close(gpg.out);
 		finish_command(&gpg);
 		return error("gpg did not accept the tag data");
 	}
 	close(gpg.in);
-	gpg.close_in = 0;
 	len = strbuf_read(buffer, gpg.out, 1024);
+	close(gpg.out);
 
 	if (finish_command(&gpg) || !len || len < 0)
 		return error("gpg failed to sign the tag");
 
-	if (len < 0)
-		return error("could not read the entire signature from gpg.");
-
 	return 0;
 }
 
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 1e51865..50e07fa 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -8,6 +8,7 @@
 #include "tag.h"
 #include "tree.h"
 #include "progress.h"
+#include "decorate.h"
 
 static int dry_run, quiet, recover, has_errors;
 static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
@@ -18,6 +19,18 @@
 static off_t consumed_bytes;
 static SHA_CTX ctx;
 
+struct obj_buffer {
+	char *buffer;
+	unsigned long size;
+};
+
+static struct decoration obj_decorate;
+
+static struct obj_buffer *lookup_object_buffer(struct object *base)
+{
+	return lookup_decoration(&obj_decorate, base);
+}
+
 /*
  * Make sure at least "min" bytes are available in the buffer, and
  * return the pointer to the buffer.
@@ -189,6 +202,7 @@
 	void *delta_data, *base;
 	unsigned long base_size;
 	unsigned char base_sha1[20];
+	struct object *obj;
 
 	if (type == OBJ_REF_DELTA) {
 		hashcpy(base_sha1, fill(20));
@@ -252,6 +266,15 @@
 		}
 	}
 
+	obj = lookup_object(base_sha1);
+	if (obj) {
+		struct obj_buffer *obj_buf = lookup_object_buffer(obj);
+		if (obj_buf) {
+			resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
+			return;
+		}
+	}
+
 	base = read_sha1_file(base_sha1, &type, &base_size);
 	if (!base) {
 		error("failed to read delta-pack base object %s",
diff --git a/builtin-update-index.c b/builtin-update-index.c
index c3a14c7..a8795d3 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -47,10 +47,10 @@
 	if (0 <= pos) {
 		switch (mark_valid_only) {
 		case MARK_VALID:
-			active_cache[pos]->ce_flags |= htons(CE_VALID);
+			active_cache[pos]->ce_flags |= CE_VALID;
 			break;
 		case UNMARK_VALID:
-			active_cache[pos]->ce_flags &= ~htons(CE_VALID);
+			active_cache[pos]->ce_flags &= ~CE_VALID;
 			break;
 		}
 		cache_tree_invalidate_path(active_cache_tree, path);
@@ -95,7 +95,7 @@
 	size = cache_entry_size(len);
 	ce = xcalloc(1, size);
 	memcpy(ce->name, path, len);
-	ce->ce_flags = htons(len);
+	ce->ce_flags = len;
 	fill_stat_cache_info(ce, st);
 	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
@@ -139,7 +139,7 @@
 	/* Exact match: file or existing gitlink */
 	if (pos >= 0) {
 		struct cache_entry *ce = active_cache[pos];
-		if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		if (S_ISGITLINK(ce->ce_mode)) {
 
 			/* Do nothing to the index if there is no HEAD! */
 			if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
@@ -183,7 +183,7 @@
 	int pos = cache_name_pos(path, len);
 	struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
 
-	if (ce && S_ISGITLINK(ntohl(ce->ce_mode)))
+	if (ce && S_ISGITLINK(ce->ce_mode))
 		return error("%s is already a gitlink, not replacing", path);
 
 	return add_one_path(ce, path, len, st);
@@ -226,7 +226,7 @@
 	ce->ce_flags = create_ce_flags(len, stage);
 	ce->ce_mode = create_ce_mode(mode);
 	if (assume_unchanged)
-		ce->ce_flags |= htons(CE_VALID);
+		ce->ce_flags |= CE_VALID;
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
 	if (add_cache_entry(ce, option))
@@ -246,14 +246,14 @@
 	if (pos < 0)
 		goto fail;
 	ce = active_cache[pos];
-	mode = ntohl(ce->ce_mode);
+	mode = ce->ce_mode;
 	if (!S_ISREG(mode))
 		goto fail;
 	switch (flip) {
 	case '+':
-		ce->ce_mode |= htonl(0111); break;
+		ce->ce_mode |= 0111; break;
 	case '-':
-		ce->ce_mode &= htonl(~0111); break;
+		ce->ce_mode &= ~0111; break;
 	default:
 		goto fail;
 	}
diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c
index 4e31c27..4958bbb 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -40,8 +40,8 @@
 	if (!pack)
 		return error("packfile %s not found.", arg);
 
+	install_packed_git(pack);
 	err = verify_pack(pack, verbose);
-	free(pack);
 
 	return err;
 }
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
index cc4c55d..db81496 100644
--- a/builtin-verify-tag.c
+++ b/builtin-verify-tag.c
@@ -45,14 +45,14 @@
 	memset(&gpg, 0, sizeof(gpg));
 	gpg.argv = args_gpg;
 	gpg.in = -1;
-	gpg.out = 1;
 	args_gpg[2] = path;
-	if (start_command(&gpg))
+	if (start_command(&gpg)) {
+		unlink(path);
 		return error("could not run gpg.");
+	}
 
 	write_in_full(gpg.in, buf, len);
 	close(gpg.in);
-	gpg.close_in = 0;
 	ret = finish_command(&gpg);
 
 	unlink(path);
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index d16b9ed..e838d01 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -11,63 +11,12 @@
 static const char write_tree_usage[] =
 "git-write-tree [--missing-ok] [--prefix=<prefix>/]";
 
-int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
-{
-	int entries, was_valid, newfd;
-
-	/* We can't free this memory, it becomes part of a linked list parsed atexit() */
-	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-
-	newfd = hold_locked_index(lock_file, 1);
-
-	entries = read_cache();
-	if (entries < 0)
-		die("git-write-tree: error reading cache");
-
-	if (!active_cache_tree)
-		active_cache_tree = cache_tree();
-
-	was_valid = cache_tree_fully_valid(active_cache_tree);
-
-	if (!was_valid) {
-		if (cache_tree_update(active_cache_tree,
-				      active_cache, active_nr,
-				      missing_ok, 0) < 0)
-			die("git-write-tree: error building trees");
-		if (0 <= newfd) {
-			if (!write_cache(newfd, active_cache, active_nr) &&
-			    !commit_lock_file(lock_file))
-				newfd = -1;
-		}
-		/* Not being able to write is fine -- we are only interested
-		 * in updating the cache-tree part, and if the next caller
-		 * ends up using the old index with unupdated cache-tree part
-		 * it misses the work we did here, but that is just a
-		 * performance penalty and not a big deal.
-		 */
-	}
-
-	if (prefix) {
-		struct cache_tree *subtree =
-			cache_tree_find(active_cache_tree, prefix);
-		if (!subtree)
-			die("git-write-tree: prefix %s not found", prefix);
-		hashcpy(sha1, subtree->sha1);
-	}
-	else
-		hashcpy(sha1, active_cache_tree->sha1);
-
-	if (0 <= newfd)
-		rollback_lock_file(lock_file);
-
-	return 0;
-}
-
 int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
 {
 	int missing_ok = 0, ret;
 	const char *prefix = NULL;
 	unsigned char sha1[20];
+	const char *me = "git-write-tree";
 
 	git_config(git_default_config);
 	while (1 < argc) {
@@ -84,8 +33,20 @@
 	if (argc > 2)
 		die("too many options");
 
-	ret = write_tree(sha1, missing_ok, prefix);
-	printf("%s\n", sha1_to_hex(sha1));
-
+	ret = write_cache_as_tree(sha1, missing_ok, prefix);
+	switch (ret) {
+	case 0:
+		printf("%s\n", sha1_to_hex(sha1));
+		break;
+	case WRITE_TREE_UNREADABLE_INDEX:
+		die("%s: error reading the index", me);
+		break;
+	case WRITE_TREE_UNMERGED_INDEX:
+		die("%s: error building trees; the index is unmerged?", me);
+		break;
+	case WRITE_TREE_PREFIX_ERROR:
+		die("%s: prefix %s not found", me, prefix);
+		break;
+	}
 	return ret;
 }
diff --git a/builtin.h b/builtin.h
index cb675c4..95126fd 100644
--- a/builtin.h
+++ b/builtin.h
@@ -8,7 +8,6 @@
 
 extern void list_common_cmds_help(void);
 extern void help_unknown_cmd(const char *cmd);
-extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 extern void prune_packed_objects(int);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
@@ -19,6 +18,7 @@
 extern int cmd_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_bundle(int argc, const char **argv, const char *prefix);
 extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
+extern int cmd_checkout(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
@@ -57,6 +57,7 @@
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
+extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
@@ -66,6 +67,7 @@
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
+extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
diff --git a/bundle.c b/bundle.c
index 5c95eca..0ba5df1 100644
--- a/bundle.c
+++ b/bundle.c
@@ -128,7 +128,8 @@
 		add_object_array(e->item, e->name, &refs);
 	}
 
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 
 	i = req_nr;
 	while (i && (commit = get_revision(&revs)))
@@ -332,10 +333,12 @@
 		write_or_die(rls.in, sha1_to_hex(object->sha1), 40);
 		write_or_die(rls.in, "\n", 1);
 	}
+	close(rls.in);
 	if (finish_command(&rls))
 		return error ("pack-objects died");
-
-	return bundle_to_stdout ? close(bundle_fd) : commit_lock_file(&lock);
+	if (!bundle_to_stdout)
+		commit_lock_file(&lock);
+	return 0;
 }
 
 int unbundle(struct bundle_header *header, int bundle_fd)
diff --git a/cache-tree.c b/cache-tree.c
index cfe937b..73cb340 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -320,13 +320,13 @@
 		}
 		else {
 			sha1 = ce->sha1;
-			mode = ntohl(ce->ce_mode);
+			mode = ce->ce_mode;
 			entlen = pathlen - baselen;
 		}
 		if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
 			return error("invalid object %s", sha1_to_hex(sha1));
 
-		if (!ce->ce_mode)
+		if (ce->ce_flags & CE_REMOVE)
 			continue; /* entry being removed */
 
 		strbuf_grow(&buffer, entlen + 100);
@@ -532,3 +532,58 @@
 	}
 	return it;
 }
+
+int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix)
+{
+	int entries, was_valid, newfd;
+
+	/*
+	 * We can't free this memory, it becomes part of a linked list
+	 * parsed atexit()
+	 */
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	newfd = hold_locked_index(lock_file, 1);
+
+	entries = read_cache();
+	if (entries < 0)
+		return WRITE_TREE_UNREADABLE_INDEX;
+
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
+
+	was_valid = cache_tree_fully_valid(active_cache_tree);
+
+	if (!was_valid) {
+		if (cache_tree_update(active_cache_tree,
+				      active_cache, active_nr,
+				      missing_ok, 0) < 0)
+			return WRITE_TREE_UNMERGED_INDEX;
+		if (0 <= newfd) {
+			if (!write_cache(newfd, active_cache, active_nr) &&
+			    !commit_lock_file(lock_file))
+				newfd = -1;
+		}
+		/* Not being able to write is fine -- we are only interested
+		 * in updating the cache-tree part, and if the next caller
+		 * ends up using the old index with unupdated cache-tree part
+		 * it misses the work we did here, but that is just a
+		 * performance penalty and not a big deal.
+		 */
+	}
+
+	if (prefix) {
+		struct cache_tree *subtree =
+			cache_tree_find(active_cache_tree, prefix);
+		if (!subtree)
+			return WRITE_TREE_PREFIX_ERROR;
+		hashcpy(sha1, subtree->sha1);
+	}
+	else
+		hashcpy(sha1, active_cache_tree->sha1);
+
+	if (0 <= newfd)
+		rollback_lock_file(lock_file);
+
+	return 0;
+}
diff --git a/cache-tree.h b/cache-tree.h
index 8243228..44aad42 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -30,4 +30,9 @@
 
 struct cache_tree *cache_tree_find(struct cache_tree *, const char *);
 
+#define WRITE_TREE_UNREADABLE_INDEX (-1)
+#define WRITE_TREE_UNMERGED_INDEX (-2)
+#define WRITE_TREE_PREFIX_ERROR (-3)
+
+int write_cache_as_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 #endif
diff --git a/cache.h b/cache.h
index 98cfed6..2a1e7ec 100644
--- a/cache.h
+++ b/cache.h
@@ -3,6 +3,7 @@
 
 #include "git-compat-util.h"
 #include "strbuf.h"
+#include "hash.h"
 
 #include SHA1_HEADER
 #include <zlib.h>
@@ -94,66 +95,148 @@
  * We save the fields in big-endian order to allow using the
  * index file over NFS transparently.
  */
+struct ondisk_cache_entry {
+	struct cache_time ctime;
+	struct cache_time mtime;
+	unsigned int dev;
+	unsigned int ino;
+	unsigned int mode;
+	unsigned int uid;
+	unsigned int gid;
+	unsigned int size;
+	unsigned char sha1[20];
+	unsigned short flags;
+	char name[FLEX_ARRAY]; /* more */
+};
+
 struct cache_entry {
-	struct cache_time ce_ctime;
-	struct cache_time ce_mtime;
+	unsigned int ce_ctime;
+	unsigned int ce_mtime;
 	unsigned int ce_dev;
 	unsigned int ce_ino;
 	unsigned int ce_mode;
 	unsigned int ce_uid;
 	unsigned int ce_gid;
 	unsigned int ce_size;
+	unsigned int ce_flags;
 	unsigned char sha1[20];
-	unsigned short ce_flags;
+	struct cache_entry *next;
 	char name[FLEX_ARRAY]; /* more */
 };
 
 #define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
-#define CE_UPDATE    (0x4000)
 #define CE_VALID     (0x8000)
 #define CE_STAGESHIFT 12
 
-#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
-#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
+/* In-memory only */
+#define CE_UPDATE    (0x10000)
+#define CE_REMOVE    (0x20000)
+#define CE_UPTODATE  (0x40000)
+
+#define CE_HASHED    (0x100000)
+#define CE_UNHASHED  (0x200000)
+
+/*
+ * Copy the sha1 and stat state of a cache entry from one to
+ * another. But we never change the name, or the hash state!
+ */
+#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
+static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
+{
+	unsigned int state = dst->ce_flags & CE_STATE_MASK;
+
+	/* Don't copy hash chain and name */
+	memcpy(dst, src, offsetof(struct cache_entry, next));
+
+	/* Restore the hash state */
+	dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
+}
+
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+static inline void remove_index_entry(struct cache_entry *ce)
+{
+	ce->ce_flags |= CE_UNHASHED;
+}
+
+static inline unsigned create_ce_flags(size_t len, unsigned stage)
+{
+	if (len >= CE_NAMEMASK)
+		len = CE_NAMEMASK;
+	return (len | (stage << CE_STAGESHIFT));
+}
+
+static inline size_t ce_namelen(const struct cache_entry *ce)
+{
+	size_t len = ce->ce_flags & CE_NAMEMASK;
+	if (len < CE_NAMEMASK)
+		return len;
+	return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
+}
+
 #define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
+#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
+#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
+#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 
 #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
 static inline unsigned int create_ce_mode(unsigned int mode)
 {
 	if (S_ISLNK(mode))
-		return htonl(S_IFLNK);
+		return S_IFLNK;
 	if (S_ISDIR(mode) || S_ISGITLINK(mode))
-		return htonl(S_IFGITLINK);
-	return htonl(S_IFREG | ce_permissions(mode));
+		return S_IFGITLINK;
+	return S_IFREG | ce_permissions(mode);
 }
 static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
 {
 	extern int trust_executable_bit, has_symlinks;
 	if (!has_symlinks && S_ISREG(mode) &&
-	    ce && S_ISLNK(ntohl(ce->ce_mode)))
+	    ce && S_ISLNK(ce->ce_mode))
 		return ce->ce_mode;
 	if (!trust_executable_bit && S_ISREG(mode)) {
-		if (ce && S_ISREG(ntohl(ce->ce_mode)))
+		if (ce && S_ISREG(ce->ce_mode))
 			return ce->ce_mode;
 		return create_ce_mode(0666);
 	}
 	return create_ce_mode(mode);
 }
+static inline int ce_to_dtype(const struct cache_entry *ce)
+{
+	unsigned ce_mode = ntohl(ce->ce_mode);
+	if (S_ISREG(ce_mode))
+		return DT_REG;
+	else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
+		return DT_DIR;
+	else if (S_ISLNK(ce_mode))
+		return DT_LNK;
+	else
+		return DT_UNKNOWN;
+}
 #define canon_mode(mode) \
 	(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 	S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 
 #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
+#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
 
 struct index_state {
 	struct cache_entry **cache;
 	unsigned int cache_nr, cache_alloc, cache_changed;
 	struct cache_tree *cache_tree;
 	time_t timestamp;
-	void *mmap;
-	size_t mmap_size;
+	void *alloc;
+	unsigned name_hash_initialized : 1;
+	struct hash_table name_hash;
 };
 
 extern struct index_state the_index;
@@ -169,6 +252,7 @@
 #define read_cache_from(path) read_index_from(&the_index, (path))
 #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
 #define discard_cache() discard_index(&the_index)
+#define unmerged_cache() unmerged_index(&the_index)
 #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
 #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
 #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
@@ -177,6 +261,7 @@
 #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
+#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
 #endif
 
 enum object_type {
@@ -189,6 +274,7 @@
 	/* 5 for future expansion */
 	OBJ_OFS_DELTA = 6,
 	OBJ_REF_DELTA = 7,
+	OBJ_ANY,
 	OBJ_MAX,
 };
 
@@ -260,10 +346,12 @@
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
 extern int read_index_from(struct index_state *, const char *path);
-extern int write_index(struct index_state *, int newfd);
+extern int write_index(const struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
+extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
-extern int index_name_pos(struct index_state *, const char *name, int namelen);
+extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
+extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1		/* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
 #define ADD_CACHE_SKIP_DFCHECK 4	/* Ok to skip DF conflict checks */
@@ -280,8 +368,8 @@
 #define CE_MATCH_IGNORE_VALID		01
 /* do not check the contents but report dirty on racily-clean entries */
 #define CE_MATCH_RACY_IS_DIRTY	02
-extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
@@ -330,6 +418,23 @@
 extern size_t delta_base_cache_limit;
 extern int auto_crlf;
 
+enum safe_crlf {
+	SAFE_CRLF_FALSE = 0,
+	SAFE_CRLF_FAIL = 1,
+	SAFE_CRLF_WARN = 2,
+};
+
+extern enum safe_crlf safe_crlf;
+
+enum branch_track {
+	BRANCH_TRACK_NEVER = 0,
+	BRANCH_TRACK_REMOTE,
+	BRANCH_TRACK_ALWAYS,
+	BRANCH_TRACK_EXPLICIT,
+};
+
+extern enum branch_track git_branch_track;
+
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
 extern int check_repository_format(void);
@@ -431,6 +536,7 @@
 extern int validate_headref(const char *ref);
 
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
 
 extern void *read_object_with_reference(const unsigned char *sha1,
@@ -438,6 +544,9 @@
 					unsigned long *size,
 					unsigned char *sha1_ret);
 
+extern struct object *peel_to_type(const char *name, int namelen,
+				   struct object *o, enum object_type);
+
 enum date_mode {
 	DATE_NORMAL = 0,
 	DATE_RELATIVE,
@@ -590,6 +699,9 @@
 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);
+extern int git_env_bool(const char *, int);
+extern int git_config_system(void);
+extern int git_config_global(void);
 extern int config_error_nonbool(const char *);
 
 #define MAX_GITNAME (1000)
@@ -636,7 +748,8 @@
 
 /* convert.c */
 /* returns 1 if *dst was used */
-extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
+extern int convert_to_git(const char *path, const char *src, size_t len,
+                          struct strbuf *dst, enum safe_crlf checksafe);
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 
 /* add */
@@ -655,6 +768,7 @@
 #define WS_TRAILING_SPACE	01
 #define WS_SPACE_BEFORE_TAB	02
 #define WS_INDENT_WITH_NON_TAB	04
+#define WS_CR_AT_EOL           010
 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 extern unsigned whitespace_rule_cfg;
 extern unsigned whitespace_rule(const char *);
@@ -663,10 +777,13 @@
     FILE *stream, const char *set,
     const char *reset, const char *ws);
 extern char *whitespace_error_string(unsigned ws);
+extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
 
 /* ls-files */
 int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
+char *alias_lookup(const char *alias);
+
 #endif /* CACHE_H */
diff --git a/color.c b/color.c
index cb70340..12a6453 100644
--- a/color.c
+++ b/color.c
@@ -3,6 +3,8 @@
 
 #define COLOR_RESET "\033[m"
 
+int git_use_color_default = 0;
+
 static int parse_color(const char *name, int len)
 {
 	static const char * const color_names[] = {
@@ -143,6 +145,16 @@
 	return 0;
 }
 
+int git_color_default_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "color.ui")) {
+		git_use_color_default = git_config_colorbool(var, value, -1);
+		return 0;
+	}
+
+	return git_default_config(var, value);
+}
+
 static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
 		va_list args, const char *trail)
 {
diff --git a/color.h b/color.h
index ff63513..ecda556 100644
--- a/color.h
+++ b/color.h
@@ -4,6 +4,17 @@
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 #define COLOR_MAXLEN 24
 
+/*
+ * This variable stores the value of color.ui
+ */
+extern int git_use_color_default;
+
+
+/*
+ * Use this instead of git_default_config if you need the value of color.ui.
+ */
+int git_color_default_config(const char *var, const char *value);
+
 int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
 void color_parse(const char *var, const char *value, char *dst);
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
diff --git a/commit.c b/commit.c
index 8b8fb04..94d5b3d 100644
--- a/commit.c
+++ b/commit.c
@@ -193,7 +193,7 @@
 	commit_graft_prepared = 1;
 }
 
-static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
+struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
 {
 	int pos;
 	prepare_commit_graft();
@@ -290,17 +290,6 @@
 	}
 	item->date = parse_commit_date(bufptr, tail);
 
-	if (track_object_refs) {
-		unsigned i = 0;
-		struct commit_list *p;
-		struct object_refs *refs = alloc_object_refs(n_refs);
-		if (item->tree)
-			refs->ref[i++] = &item->tree->object;
-		for (p = item->parents; p; p = p->next)
-			refs->ref[i++] = &p->item->object;
-		set_object_refs(&item->object, refs);
-	}
-
 	return 0;
 }
 
@@ -311,6 +300,8 @@
 	unsigned long size;
 	int ret;
 
+	if (!item)
+		return -1;
 	if (item->object.parsed)
 		return 0;
 	buffer = read_sha1_file(item->object.sha1, &type, &size);
@@ -385,8 +376,7 @@
 
 	while (parents) {
 		struct commit *commit = parents->item;
-		parse_commit(commit);
-		if (!(commit->object.flags & mark)) {
+		if (!parse_commit(commit) && !(commit->object.flags & mark)) {
 			commit->object.flags |= mark;
 			insert_by_date(commit, list);
 		}
@@ -552,8 +542,10 @@
 		 */
 		return commit_list_insert(one, &result);
 
-	parse_commit(one);
-	parse_commit(two);
+	if (parse_commit(one))
+		return NULL;
+	if (parse_commit(two))
+		return NULL;
 
 	one->object.flags |= PARENT1;
 	two->object.flags |= PARENT2;
@@ -586,7 +578,8 @@
 			parents = parents->next;
 			if ((p->object.flags & flags) == flags)
 				continue;
-			parse_commit(p);
+			if (parse_commit(p))
+				return NULL;
 			p->object.flags |= flags;
 			insert_by_date(p, &list);
 		}
diff --git a/commit.h b/commit.h
index 000528a..2f63bc8 100644
--- a/commit.h
+++ b/commit.h
@@ -71,6 +71,21 @@
                                 int abbrev, const char *subject,
                                 const char *after_subject, enum date_mode,
 				int need_8bit_cte);
+void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
+		   const char *line, enum date_mode dmode,
+		   const char *encoding);
+void pp_title_line(enum cmit_fmt fmt,
+		   const char **msg_p,
+		   struct strbuf *sb,
+		   const char *subject,
+		   const char *after_subject,
+		   const char *encoding,
+		   int need_8bit_cte);
+void pp_remainder(enum cmit_fmt fmt,
+		  const char **msg_p,
+		  struct strbuf *sb,
+		  int indent);
+
 
 /** Removes the first commit from a list sorted by date, and adds all
  * of its parents.
@@ -101,6 +116,7 @@
 struct commit_graft *read_graft_line(char *buf, int len);
 int register_commit_graft(struct commit_graft *, int);
 int read_graft_file(const char *graft_file);
+struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
 
diff --git a/compat/fopen.c b/compat/fopen.c
new file mode 100644
index 0000000..ccb9e89
--- /dev/null
+++ b/compat/fopen.c
@@ -0,0 +1,26 @@
+#include "../git-compat-util.h"
+#undef fopen
+FILE *git_fopen(const char *path, const char *mode)
+{
+	FILE *fp;
+	struct stat st;
+
+	if (mode[0] == 'w' || mode[0] == 'a')
+		return fopen(path, mode);
+
+	if (!(fp = fopen(path, mode)))
+		return NULL;
+
+	if (fstat(fileno(fp), &st)) {
+		fclose(fp);
+		return NULL;
+	}
+
+	if (S_ISDIR(st.st_mode)) {
+		fclose(fp);
+		errno = EISDIR;
+		return NULL;
+	}
+
+	return fp;
+}
diff --git a/compat/qsort.c b/compat/qsort.c
new file mode 100644
index 0000000..d93dce2
--- /dev/null
+++ b/compat/qsort.c
@@ -0,0 +1,62 @@
+#include "../git-compat-util.h"
+
+/*
+ * A merge sort implementation, simplified from the qsort implementation
+ * by Mike Haertel, which is a part of the GNU C Library.
+ */
+
+static void msort_with_tmp(void *b, size_t n, size_t s,
+			   int (*cmp)(const void *, const void *),
+			   char *t)
+{
+	char *tmp;
+	char *b1, *b2;
+	size_t n1, n2;
+
+	if (n <= 1)
+		return;
+
+	n1 = n / 2;
+	n2 = n - n1;
+	b1 = b;
+	b2 = (char *)b + (n1 * s);
+
+	msort_with_tmp(b1, n1, s, cmp, t);
+	msort_with_tmp(b2, n2, s, cmp, t);
+
+	tmp = t;
+
+	while (n1 > 0 && n2 > 0) {
+		if (cmp(b1, b2) <= 0) {
+			memcpy(tmp, b1, s);
+			tmp += s;
+			b1 += s;
+			--n1;
+		} else {
+			memcpy(tmp, b2, s);
+			tmp += s;
+			b2 += s;
+			--n2;
+		}
+	}
+	if (n1 > 0)
+		memcpy(tmp, b1, n1 * s);
+	memcpy(b, t, (n - n2) * s);
+}
+
+void git_qsort(void *b, size_t n, size_t s,
+	       int (*cmp)(const void *, const void *))
+{
+	const size_t size = n * s;
+	char buf[1024];
+
+	if (size < sizeof(buf)) {
+		/* The temporary array fits on the small on-stack buffer. */
+		msort_with_tmp(b, n, s, cmp, buf);
+	} else {
+		/* It's somewhat large, so malloc it.  */
+		char *tmp = malloc(size);
+		msort_with_tmp(b, n, s, cmp, tmp);
+		free(tmp);
+	}
+}
diff --git a/compat/snprintf.c b/compat/snprintf.c
new file mode 100644
index 0000000..dbfc2d6
--- /dev/null
+++ b/compat/snprintf.c
@@ -0,0 +1,40 @@
+#include "../git-compat-util.h"
+
+#undef vsnprintf
+int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
+{
+	char *s;
+	int ret;
+
+	ret = vsnprintf(str, maxsize, format, ap);
+	if (ret != -1)
+		return ret;
+
+	s = NULL;
+	if (maxsize < 128)
+		maxsize = 128;
+
+	while (ret == -1) {
+		maxsize *= 4;
+		str = realloc(s, maxsize);
+		if (! str)
+			break;
+		s = str;
+		ret = vsnprintf(str, maxsize, format, ap);
+	}
+	free(s);
+	return ret;
+}
+
+int git_snprintf(char *str, size_t maxsize, const char *format, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, format);
+	ret = git_vsnprintf(str, maxsize, format, ap);
+	va_end(ap);
+
+	return ret;
+}
+
diff --git a/config.c b/config.c
index b82907c..0624494 100644
--- a/config.c
+++ b/config.c
@@ -280,11 +280,18 @@
 	return 0;
 }
 
+static void die_bad_config(const char *name)
+{
+	if (config_file_name)
+		die("bad config value for '%s' in %s", name, config_file_name);
+	die("bad config value for '%s'", name);
+}
+
 int git_config_int(const char *name, const char *value)
 {
 	long ret;
 	if (!git_parse_long(value, &ret))
-		die("bad config value for '%s' in %s", name, config_file_name);
+		die_bad_config(name);
 	return ret;
 }
 
@@ -292,7 +299,7 @@
 {
 	unsigned long ret;
 	if (!git_parse_ulong(value, &ret))
-		die("bad config value for '%s' in %s", name, config_file_name);
+		die_bad_config(name);
 	return ret;
 }
 
@@ -415,6 +422,15 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.safecrlf")) {
+		if (value && !strcasecmp(value, "warn")) {
+			safe_crlf = SAFE_CRLF_WARN;
+			return 0;
+		}
+		safe_crlf = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "user.name")) {
 		if (!value)
 			return config_error_nonbool(var);
@@ -455,6 +471,14 @@
 		whitespace_rule_cfg = parse_whitespace_rule(value);
 		return 0;
 	}
+	if (!strcmp(var, "branch.autosetupmerge")) {
+		if (value && !strcasecmp(value, "always")) {
+			git_branch_track = BRANCH_TRACK_ALWAYS;
+			return 0;
+		}
+		git_branch_track = git_config_bool(var, value);
+		return 0;
+	}
 
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
@@ -485,14 +509,30 @@
 		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);
+			struct strbuf d = STRBUF_INIT;
+			strbuf_addf(&d, "%s/%s", git_exec_path(), system_wide);
+			system_wide = strbuf_detach(&d, NULL);
 		}
 	}
 	return system_wide;
 }
 
+int git_env_bool(const char *k, int def)
+{
+	const char *v = getenv(k);
+	return v ? git_config_bool(k, v) : def;
+}
+
+int git_config_system(void)
+{
+	return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
+}
+
+int git_config_global(void)
+{
+	return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
+}
+
 int git_config(config_fn_t fn)
 {
 	int ret = 0;
@@ -505,7 +545,7 @@
 	 * config file otherwise. */
 	filename = getenv(CONFIG_ENVIRONMENT);
 	if (!filename) {
-		if (!access(git_etc_gitconfig(), R_OK))
+		if (git_config_system() && !access(git_etc_gitconfig(), R_OK))
 			ret += git_config_from_file(fn, git_etc_gitconfig());
 		home = getenv("HOME");
 		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
@@ -513,7 +553,7 @@
 			filename = repo_config = xstrdup(git_path("config"));
 	}
 
-	if (home) {
+	if (git_config_global() && home) {
 		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
 		if (!access(user_config, R_OK))
 			ret = git_config_from_file(fn, user_config);
diff --git a/config.mak.in b/config.mak.in
index ee6c33d..7868dfd 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -46,3 +46,5 @@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
+SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
diff --git a/configure.ac b/configure.ac
index 85d7ef5..82584e9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -326,6 +326,60 @@
 	NO_C99_FORMAT=
 fi
 AC_SUBST(NO_C99_FORMAT)
+#
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+AC_CACHE_CHECK([whether system succeeds to read fopen'ed directory],
+ [ac_cv_fread_reads_directories],
+[
+AC_RUN_IFELSE(
+	[AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+		[[char c;
+		FILE *f = fopen(".", "r");
+		return f && fread(&c, 1, 1, f)]])],
+	[ac_cv_fread_reads_directories=no],
+	[ac_cv_fread_reads_directories=yes])
+])
+if test $ac_cv_fread_reads_directories = yes; then
+	FREAD_READS_DIRECTORIES=UnfortunatelyYes
+else
+	FREAD_READS_DIRECTORIES=
+fi
+AC_SUBST(FREAD_READS_DIRECTORIES)
+#
+# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# or vsnprintf() return -1 instead of number of characters which would
+# have been written to the final string if enough space had been available.
+AC_CACHE_CHECK([whether snprintf() and/or vsnprintf() return bogus value],
+ [ac_cv_snprintf_returns_bogus],
+[
+AC_RUN_IFELSE(
+	[AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+		#include "stdarg.h"
+
+		int test_vsnprintf(char *str, size_t maxsize, const char *format, ...)
+		{
+		  int ret;
+		  va_list ap;
+		  va_start(ap, format);
+		  ret = vsnprintf(str, maxsize, format, ap);
+		  va_end(ap);
+		  return ret;
+		}],
+		[[char buf[6];
+		  if (test_vsnprintf(buf, 3, "%s", "12345") != 5
+		      || strcmp(buf, "12")) return 1;
+		  if (snprintf(buf, 3, "%s", "12345") != 5
+		      || strcmp(buf, "12")) return 1]])],
+	[ac_cv_snprintf_returns_bogus=no],
+	[ac_cv_snprintf_returns_bogus=yes])
+])
+if test $ac_cv_snprintf_returns_bogus = yes; then
+	SNPRINTF_RETURNS_BOGUS=UnfortunatelyYes
+else
+	SNPRINTF_RETURNS_BOGUS=
+fi
+AC_SUBST(SNPRINTF_RETURNS_BOGUS)
 
 
 ## Checks for library functions.
diff --git a/connect.c b/connect.c
index 71597d4..d12b105 100644
--- a/connect.c
+++ b/connect.c
@@ -68,8 +68,7 @@
 
 		name_len = strlen(name);
 		if (len != name_len + 41) {
-			if (server_capabilities)
-				free(server_capabilities);
+			free(server_capabilities);
 			server_capabilities = xstrdup(name + name_len + 1);
 		}
 
@@ -474,14 +473,18 @@
 	return NULL;
 }
 
+static struct child_process no_fork;
+
 /*
- * This returns NULL if the transport protocol does not need fork(2), or a
- * struct child_process object if it does.  Once done, finish the connection
- * with finish_connect() with the value returned from this function
- * (it is safe to call finish_connect() with NULL to support the former
- * case).
+ * This returns a dummy child_process if the transport protocol does not
+ * need fork(2), or a struct child_process object if it does.  Once done,
+ * finish the connection with finish_connect() with the value returned from
+ * this function (it is safe to call finish_connect() with NULL to support
+ * the former case).
  *
- * If it returns, the connect is successful; it just dies on errors.
+ * If it returns, the connect is successful; it just dies on errors (this
+ * will hopefully be changed in a libification effort, to return NULL when
+ * the connection failed).
  */
 struct child_process *git_connect(int fd[2], const char *url_orig,
 				  const char *prog, int flags)
@@ -579,7 +582,7 @@
 		free(url);
 		if (free_path)
 			free(path);
-		return NULL;
+		return &no_fork;
 	}
 
 	conn = xcalloc(1, sizeof(*conn));
@@ -637,7 +640,7 @@
 int finish_connect(struct child_process *conn)
 {
 	int code;
-	if (!conn)
+	if (!conn || conn == &no_fork)
 		return 0;
 
 	code = finish_command(conn);
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 0d33f9a..6949cac 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -64,12 +64,52 @@
 
 __git_ps1 ()
 {
-	local b="$(git symbolic-ref HEAD 2>/dev/null)"
-	if [ -n "$b" ]; then
-		if [ -n "$1" ]; then
-			printf "$1" "${b##refs/heads/}"
+	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	if [ -n "$g" ]; then
+		local r
+		local b
+		if [ -d "$g/../.dotest" ]
+		then
+			if test -f "$g/../.dotest/rebasing"
+			then
+				r="|REBASE"
+			elif test -f "$g/../.dotest/applying"
+			then
+				r="|AM"
+			else
+				r="|AM/REBASE"
+			fi
+			b="$(git symbolic-ref HEAD 2>/dev/null)"
+		elif [ -f "$g/.dotest-merge/interactive" ]
+		then
+			r="|REBASE-i"
+			b="$(cat "$g/.dotest-merge/head-name")"
+		elif [ -d "$g/.dotest-merge" ]
+		then
+			r="|REBASE-m"
+			b="$(cat "$g/.dotest-merge/head-name")"
+		elif [ -f "$g/MERGE_HEAD" ]
+		then
+			r="|MERGING"
+			b="$(git symbolic-ref HEAD 2>/dev/null)"
 		else
-			printf " (%s)" "${b##refs/heads/}"
+			if [ -f "$g/BISECT_LOG" ]
+			then
+				r="|BISECTING"
+			fi
+			if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
+			then
+				if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
+				then
+					b="$(cut -c1-7 "$g/HEAD")..."
+				fi
+			fi
+		fi
+
+		if [ -n "$1" ]; then
+			printf "$1" "${b##refs/heads/}$r"
+		else
+			printf " (%s)" "${b##refs/heads/}$r"
 		fi
 	fi
 }
@@ -81,13 +121,21 @@
 	if [ $# -gt 2 ]; then
 		cur="$3"
 	fi
-	for c in $1; do
-		case "$c$4" in
-		--*=*) all="$all$c$4$s" ;;
-		*.)    all="$all$c$4$s" ;;
-		*)     all="$all$c$4 $s" ;;
-		esac
-	done
+	case "$cur" in
+	--*=)
+		COMPREPLY=()
+		return
+		;;
+	*)
+		for c in $1; do
+			case "$c$4" in
+			--*=*) all="$all$c$4$s" ;;
+			*.)    all="$all$c$4$s" ;;
+			*)     all="$all$c$4 $s" ;;
+			esac
+		done
+		;;
+	esac
 	IFS=$s
 	COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
 	return
@@ -344,7 +392,6 @@
 		show-index)       : plumbing;;
 		ssh-*)            : transport;;
 		stripspace)       : plumbing;;
-		svn)              : import export;;
 		symbolic-ref)     : plumbing;;
 		tar-tree)         : deprecated;;
 		unpack-file)      : plumbing;;
@@ -388,6 +435,22 @@
 	done
 }
 
+__git_find_subcommand ()
+{
+	local word subcommand c=1
+
+	while [ $c -lt $COMP_CWORD ]; do
+		word="${COMP_WORDS[c]}"
+		for subcommand in $1; do
+			if [ "$subcommand" = "$word" ]; then
+				echo "$subcommand"
+				return
+			fi
+		done
+		c=$((++c))
+	done
+}
+
 __git_whitespacelist="nowarn warn error error-all strip"
 
 _git_am ()
@@ -445,24 +508,14 @@
 
 _git_bisect ()
 {
-	local i c=1 command
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
-		case "$i" in
-		start|bad|good|reset|visualize|replay|log)
-			command="$i"
-			break
-			;;
-		esac
-		c=$((++c))
-	done
-
-	if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-		__gitcomp "start bad good reset visualize replay log"
+	local subcommands="start bad good reset visualize replay log"
+	local subcommand="$(__git_find_subcommand "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
 		return
 	fi
 
-	case "$command" in
+	case "$subcommand" in
 	bad|good|reset)
 		__gitcomp "$(__git_refs)"
 		;;
@@ -474,7 +527,33 @@
 
 _git_branch ()
 {
-	__gitcomp "$(__git_refs)"
+	local i c=1 only_local_ref="n" has_r="n"
+
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		-d|-m)	only_local_ref="y" ;;
+		-r)	has_r="y" ;;
+		esac
+		c=$((++c))
+	done
+
+	case "${COMP_WORDS[COMP_CWORD]}" in
+	--*=*)	COMPREPLY=() ;;
+	--*)
+		__gitcomp "
+			--color --no-color --verbose --abbrev= --no-abbrev
+			--track --no-track
+			"
+		;;
+	*)
+		if [ $only_local_ref = "y" -a $has_r = "n" ]; then
+			__gitcomp "$(__git_heads)"
+		else
+			__gitcomp "$(__git_refs)"
+		fi
+		;;
+	esac
 }
 
 _git_bundle ()
@@ -560,7 +639,10 @@
 			--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"
+			--no-ext-diff
+			--no-prefix --src-prefix= --dst-prefix=
+			--base --ours --theirs
+			"
 		return
 		;;
 	esac
@@ -616,6 +698,8 @@
 			--in-reply-to=
 			--full-index --binary
 			--not --all
+			--cover-letter
+			--no-prefix --src-prefix= --dst-prefix=
 			"
 		return
 		;;
@@ -769,8 +853,8 @@
 
 _git_rebase ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then
+	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then
 		__gitcomp "--continue --skip --abort"
 		return
 	fi
@@ -785,7 +869,7 @@
 		return
 		;;
 	--*)
-		__gitcomp "--onto --merge --strategy"
+		__gitcomp "--onto --merge --strategy --interactive"
 		return
 	esac
 	__gitcomp "$(__git_refs)"
@@ -889,7 +973,6 @@
 		core.sharedRepository
 		core.warnAmbiguousRefs
 		core.compression
-		core.legacyHeaders
 		core.packedGitWindowSize
 		core.packedGitLimit
 		clean.requireForce
@@ -920,7 +1003,8 @@
 		gitcvs.enabled
 		gitcvs.logfile
 		gitcvs.allbinary
-		gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass
+		gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass
+		gitcvs.dbtablenameprefix
 		gc.packrefs
 		gc.reflogexpire
 		gc.reflogexpireunreachable
@@ -966,21 +1050,13 @@
 
 _git_remote ()
 {
-	local i c=1 command
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
-		case "$i" in
-		add|rm|show|prune|update) command="$i"; break ;;
-		esac
-		c=$((++c))
-	done
-
-	if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-		__gitcomp "add rm show prune update"
+	local subcommands="add rm show prune update"
+	local subcommand="$(__git_find_subcommand "$subcommands")"
+	if [ -z "$subcommand" ]; then
 		return
 	fi
 
-	case "$command" in
+	case "$subcommand" in
 	rm|show|prune)
 		__gitcomp "$(__git_remotes)"
 		;;
@@ -1054,34 +1130,107 @@
 
 _git_stash ()
 {
-	__gitcomp 'list show apply clear'
+	local subcommands='save list show apply clear drop pop create'
+	if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
+		__gitcomp "$subcommands"
+	fi
 }
 
 _git_submodule ()
 {
-	local i c=1 command
-	while [ $c -lt $COMP_CWORD ]; do
-		i="${COMP_WORDS[c]}"
-		case "$i" in
-		add|status|init|update) command="$i"; break ;;
-		esac
-		c=$((++c))
-	done
-
-	if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+	local subcommands="add status init update"
+	if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
 		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$cur" in
 		--*)
 			__gitcomp "--quiet --cached"
 			;;
 		*)
-			__gitcomp "add status init update"
+			__gitcomp "$subcommands"
 			;;
 		esac
 		return
 	fi
 }
 
+_git_svn ()
+{
+	local subcommands="
+		init fetch clone rebase dcommit log find-rev
+		set-tree commit-diff info create-ignore propget
+		proplist show-ignore show-externals
+		"
+	local subcommand="$(__git_find_subcommand "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+	else
+		local remote_opts="--username= --config-dir= --no-auth-cache"
+		local fc_opts="
+			--follow-parent --authors-file= --repack=
+			--no-metadata --use-svm-props --use-svnsync-props
+			--log-window-size= --no-checkout --quiet
+			--repack-flags --user-log-author $remote_opts
+			"
+		local init_opts="
+			--template= --shared= --trunk= --tags=
+			--branches= --stdlayout --minimize-url
+			--no-metadata --use-svm-props --use-svnsync-props
+			--rewrite-root= $remote_opts
+			"
+		local cmt_opts="
+			--edit --rmdir --find-copies-harder --copy-similarity=
+			"
+
+		local cur="${COMP_WORDS[COMP_CWORD]}"
+		case "$subcommand,$cur" in
+		fetch,--*)
+			__gitcomp "--revision= --fetch-all $fc_opts"
+			;;
+		clone,--*)
+			__gitcomp "--revision= $fc_opts $init_opts"
+			;;
+		init,--*)
+			__gitcomp "$init_opts"
+			;;
+		dcommit,--*)
+			__gitcomp "
+				--merge --strategy= --verbose --dry-run
+				--fetch-all --no-rebase $cmt_opts $fc_opts
+				"
+			;;
+		set-tree,--*)
+			__gitcomp "--stdin $cmt_opts $fc_opts"
+			;;
+		create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
+		show-externals,--*)
+			__gitcomp "--revision="
+			;;
+		log,--*)
+			__gitcomp "
+				--limit= --revision= --verbose --incremental
+				--oneline --show-commit --non-recursive
+				--authors-file=
+				"
+			;;
+		rebase,--*)
+			__gitcomp "
+				--merge --verbose --strategy= --local
+				--fetch-all $fc_opts
+				"
+			;;
+		commit-diff,--*)
+			__gitcomp "--message= --file= --revision= $cmt_opts"
+			;;
+		info,--*)
+			__gitcomp "--url"
+			;;
+		*)
+			COMPREPLY=()
+			;;
+		esac
+	fi
+}
+
 _git_tag ()
 {
 	local i c=1 f=0
@@ -1131,15 +1280,18 @@
 		c=$((++c))
 	done
 
-	if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+	if [ -z "$command" ]; then
 		case "${COMP_WORDS[COMP_CWORD]}" in
 		--*=*) COMPREPLY=() ;;
 		--*)   __gitcomp "
+			--paginate
 			--no-pager
 			--git-dir=
 			--bare
 			--version
 			--exec-path
+			--work-tree=
+			--help
 			"
 			;;
 		*)     __gitcomp "$(__git_commands) $(__git_aliases)" ;;
@@ -1183,6 +1335,7 @@
 	show-branch) _git_log ;;
 	stash)       _git_stash ;;
 	submodule)   _git_submodule ;;
+	svn)         _git_svn ;;
 	tag)         _git_tag ;;
 	whatchanged) _git_log ;;
 	*)           COMPREPLY=() ;;
@@ -1233,6 +1386,7 @@
 complete -o default -o nospace -F _git_show git-show
 complete -o default -o nospace -F _git_stash git-stash
 complete -o default -o nospace -F _git_submodule git-submodule
+complete -o default -o nospace -F _git_svn git-svn
 complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_tag git-tag
 complete -o default -o nospace -F _git_log git-whatchanged
diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el
index bb671d5..9f92cd2 100644
--- a/contrib/emacs/git-blame.el
+++ b/contrib/emacs/git-blame.el
@@ -105,6 +105,13 @@
      (setq ,l (remove e ,l))
      e))
 
+(defvar git-blame-log-oneline-format
+  "format:[%cr] %cn: %s"
+  "*Formatting option used for describing current line in the minibuffer.
+
+This option is used to pass to git log --pretty= command-line option,
+and describe which commit the current line was made.")
+
 (defvar git-blame-dark-colors
   (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c")
   "*List of colors (format #RGB) to use in a dark environment.
@@ -371,7 +378,8 @@
 (defun git-describe-commit (hash)
   (with-temp-buffer
     (call-process "git" nil t nil
-                  "log" "-1" "--pretty=oneline"
+                  "log" "-1"
+		  (concat "--pretty=" git-blame-log-oneline-format)
                   hash)
     (buffer-substring (point-min) (1- (point-max)))))
 
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 0312d89..4fa853f 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -35,7 +35,6 @@
 ;;
 ;; TODO
 ;;  - portability to XEmacs
-;;  - better handling of subprocess errors
 ;;  - diff against other branch
 ;;  - renaming files from the status buffer
 ;;  - creating tags
@@ -186,14 +185,25 @@
 
 (defun git-call-process-env (buffer env &rest args)
   "Wrapper for call-process that sets environment strings."
-  (if env
-      (apply #'call-process "env" nil buffer nil
-             (append (git-get-env-strings env) (list "git") args))
+  (let ((process-environment (append (git-get-env-strings env)
+                                     process-environment)))
     (apply #'call-process "git" nil buffer nil args)))
 
+(defun git-call-process-display-error (&rest args)
+  "Wrapper for call-process that displays error messages."
+  (let* ((dir default-directory)
+         (buffer (get-buffer-create "*Git Command Output*"))
+         (ok (with-current-buffer buffer
+               (let ((default-directory dir)
+                     (buffer-read-only nil))
+                 (erase-buffer)
+                 (eq 0 (apply 'call-process "git" nil (list buffer t) nil args))))))
+    (unless ok (display-message-or-buffer buffer))
+    ok))
+
 (defun git-call-process-env-string (env &rest args)
   "Wrapper for call-process that sets environment strings,
-and returns the process output as a string."
+and returns the process output as a string, or nil if the git failed."
   (with-temp-buffer
     (and (eq 0 (apply #' git-call-process-env t env args))
          (buffer-string))))
@@ -377,7 +387,7 @@
     (when reason
      (push reason args)
      (push "-m" args))
-    (eq 0 (apply #'git-call-process-env nil nil "update-ref" args))))
+    (apply 'git-call-process-display-error "update-ref" args)))
 
 (defun git-read-tree (tree &optional index-file)
   "Read a tree into the index file."
@@ -558,12 +568,15 @@
 		     (?\100 "   (type change file -> subproject)")
 		     (?\120 "   (type change symlink -> subproject)")
 		     (t "   (subproject)")))
+                  (?\110 nil)  ;; directory (internal, not a real git state)
 		  (?\000  ;; deleted or unknown
 		   (case old-type
 		     (?\120 "   (symlink)")
 		     (?\160 "   (subproject)")))
 		  (t (format "   (unknown type %o)" new-type)))))
-    (if str (propertize str 'face 'git-status-face) "")))
+    (cond (str (propertize str 'face 'git-status-face))
+          ((eq new-type ?\110) "/")
+          (t ""))))
 
 (defun git-rename-as-string (info)
   "Return a string describing the copy or rename associated with INFO, or an empty string if none."
@@ -666,9 +679,11 @@
     (with-temp-buffer
       (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
-      (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
+      (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1)
         (let ((name (match-string 1)))
-          (push (git-create-fileinfo default-state name) infolist)
+          (push (git-create-fileinfo default-state name 0
+                                     (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0))
+                infolist)
           (setq files (delete name files)))))
     (git-insert-info-list status infolist)
     files))
@@ -713,7 +728,7 @@
 (defun git-run-ls-files-with-excludes (status files default-state &rest options)
   "Run git-ls-files on FILES with appropriate --exclude-from options."
   (let ((exclude-files (git-get-exclude-files)))
-    (apply #'git-run-ls-files status files default-state
+    (apply #'git-run-ls-files status files default-state "--directory" "--no-empty-directory"
            (concat "--exclude-per-directory=" git-per-dir-ignore-file)
            (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
 
@@ -735,6 +750,27 @@
     (git-refresh-files)
     (git-refresh-ewoc-hf git-status)))
 
+(defun git-mark-files (status files)
+  "Mark all the specified FILES, and unmark the others."
+  (setq files (sort files #'string-lessp))
+  (let ((file (and files (pop files)))
+        (node (ewoc-nth status 0)))
+    (while node
+      (let ((info (ewoc-data node)))
+        (if (and file (string-equal (git-fileinfo->name info) file))
+            (progn
+              (unless (git-fileinfo->marked info)
+                (setf (git-fileinfo->marked info) t)
+                (setf (git-fileinfo->needs-refresh info) t))
+              (setq file (pop files))
+              (setq node (ewoc-next status node)))
+          (when (git-fileinfo->marked info)
+            (setf (git-fileinfo->marked info) nil)
+            (setf (git-fileinfo->needs-refresh info) t))
+          (if (and file (string-lessp file (git-fileinfo->name info)))
+              (setq file (pop files))
+            (setq node (ewoc-next status node))))))))
+
 (defun git-marked-files ()
   "Return a list of all marked files, or if none a list containing just the file at cursor position."
   (unless git-status (error "Not in git-status buffer."))
@@ -840,16 +876,17 @@
                       (if (or (not (string-equal tree head-tree))
                               (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
                           (let ((commit (git-commit-tree buffer tree head)))
-                            (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
-                            (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
-                            (with-current-buffer buffer (erase-buffer))
-			    (git-update-status-files (git-get-filenames files) 'uptodate)
-                            (git-call-process-env nil nil "rerere")
-                            (git-call-process-env nil nil "gc" "--auto")
-                            (git-refresh-files)
-                            (git-refresh-ewoc-hf git-status)
-                            (message "Committed %s." commit)
-                            (git-run-hook "post-commit" nil))
+                            (when commit
+                              (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
+                              (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+                              (with-current-buffer buffer (erase-buffer))
+                              (git-update-status-files (git-get-filenames files) 'uptodate)
+                              (git-call-process-env nil nil "rerere")
+                              (git-call-process-env nil nil "gc" "--auto")
+                              (git-refresh-files)
+                              (git-refresh-ewoc-hf git-status)
+                              (message "Committed %s." commit)
+                              (git-run-hook "post-commit" nil)))
                         (message "Commit aborted."))))
                 (message "No files to commit.")))
           (delete-file index-file))))))
@@ -957,11 +994,12 @@
   "Add marked file(s) to the index cache."
   (interactive)
   (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
+    ;; FIXME: add support for directories
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
-    (apply #'git-call-process-env nil nil "update-index" "--add" "--" files)
-    (git-update-status-files files 'uptodate)
-    (git-success-message "Added" files)))
+    (when (apply 'git-call-process-display-error "update-index" "--add" "--" files)
+      (git-update-status-files files 'uptodate)
+      (git-success-message "Added" files))))
 
 (defun git-ignore-file ()
   "Add marked file(s) to the ignore list."
@@ -983,16 +1021,19 @@
          (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
         (progn
           (dolist (name files)
-            (when (file-exists-p name) (delete-file name)))
-          (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files)
-          (git-update-status-files files nil)
-          (git-success-message "Removed" files))
+            (ignore-errors
+              (if (file-directory-p name)
+                  (delete-directory name)
+                (delete-file name))))
+          (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files)
+            (git-update-status-files files nil)
+            (git-success-message "Removed" files)))
       (message "Aborting"))))
 
 (defun git-revert-file ()
   "Revert changes to the marked file(s)."
   (interactive)
-  (let ((files (git-marked-files))
+  (let ((files (git-marked-files-state 'added 'deleted 'modified 'unmerged))
         added modified)
     (when (and files
                (yes-or-no-p
@@ -1003,21 +1044,31 @@
           ('deleted (push (git-fileinfo->name info) modified))
           ('unmerged (push (git-fileinfo->name info) modified))
           ('modified (push (git-fileinfo->name info) modified))))
-      (when added
-        (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added))
-      (when modified
-        (apply #'git-call-process-env nil nil "checkout" "HEAD" modified))
-      (git-update-status-files (append added modified) 'uptodate)
-      (git-success-message "Reverted" (git-get-filenames files)))))
+      ;; check if a buffer contains one of the files and isn't saved
+      (dolist (file modified)
+        (let ((buffer (get-file-buffer file)))
+          (when (and buffer (buffer-modified-p buffer))
+            (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer)))))
+      (let ((ok (and
+                 (or (not added)
+                     (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added))
+                 (or (not modified)
+                     (apply 'git-call-process-display-error "checkout" "HEAD" modified)))))
+        (git-update-status-files (append added modified) 'uptodate)
+        (when ok
+          (dolist (file modified)
+            (let ((buffer (get-file-buffer file)))
+              (when buffer (with-current-buffer buffer (revert-buffer t t t)))))
+          (git-success-message "Reverted" (git-get-filenames files)))))))
 
 (defun git-resolve-file ()
   "Resolve conflicts in marked file(s)."
   (interactive)
   (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
-      (apply #'git-call-process-env nil nil "update-index" "--" files)
-      (git-update-status-files files 'uptodate)
-      (git-success-message "Resolved" files))))
+      (when (apply 'git-call-process-display-error "update-index" "--" files)
+        (git-update-status-files files 'uptodate)
+        (git-success-message "Resolved" files)))))
 
 (defun git-remove-handled ()
   "Remove handled files from the status list."
@@ -1063,6 +1114,16 @@
         (message "Inserting unknown files...done"))
     (git-remove-handled)))
 
+(defun git-expand-directory (info)
+  "Expand the directory represented by INFO to list its files."
+  (when (eq (lsh (git-fileinfo->new-perm info) -9) ?\110)
+    (let ((dir (git-fileinfo->name info)))
+      (git-set-filenames-state git-status (list dir) nil)
+      (git-run-ls-files-with-excludes git-status (list (concat dir "/")) 'unknown "-o")
+      (git-refresh-files)
+      (git-refresh-ewoc-hf git-status)
+      t)))
+
 (defun git-setup-diff-buffer (buffer)
   "Setup a buffer for displaying a diff."
   (let ((dir default-directory))
@@ -1199,7 +1260,8 @@
       (goto-char (point-min))
       (when (re-search-forward "\n+\\'" nil t)
         (replace-match "\n" t t))
-      (when sign-off (git-append-sign-off committer-name committer-email)))))
+      (when sign-off (git-append-sign-off committer-name committer-email)))
+    buffer))
 
 (defun git-commit-file ()
   "Commit the marked file(s), asking for a commit message."
@@ -1232,14 +1294,61 @@
       (setq buffer-file-coding-system coding-system)
       (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
 
+(defun git-setup-commit-buffer (commit)
+  "Setup the commit buffer with the contents of COMMIT."
+  (let (author-name author-email subject date msg)
+    (with-temp-buffer
+      (let ((coding-system (git-get-logoutput-coding-system)))
+        (git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
+        (goto-char (point-min))
+        (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
+          (setq author-name (match-string 1))
+          (setq author-email (match-string 2)))
+        (when (re-search-forward "^Date: *\\(.*\\)$" nil t)
+          (setq date (match-string 1)))
+        (while (re-search-forward "^    \\(.*\\)$" nil t)
+          (push (match-string 1) msg))
+        (setq msg (nreverse msg))
+        (setq subject (pop msg))
+        (while (and msg (zerop (length (car msg))) (pop msg)))))
+    (git-setup-log-buffer (get-buffer-create "*git-commit*")
+                          author-name author-email subject date
+                          (mapconcat #'identity msg "\n"))))
+
+(defun git-get-commit-files (commit)
+  "Retrieve the list of files modified by COMMIT."
+  (let (files)
+    (with-temp-buffer
+      (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
+      (goto-char (point-min))
+      (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
+        (push (match-string 1) files)))
+    files))
+
+(defun git-amend-commit ()
+  "Undo the last commit on HEAD, and set things up to commit an
+amended version of it."
+  (interactive)
+  (unless git-status (error "Not in git-status buffer."))
+  (when (git-empty-db-p) (error "No commit to amend."))
+  (let* ((commit (git-rev-parse "HEAD"))
+         (files (git-get-commit-files commit)))
+    (when (git-call-process-display-error "reset" "--soft" "HEAD^")
+      (git-update-status-files (copy-sequence files) 'uptodate)
+      (git-mark-files git-status files)
+      (git-refresh-files)
+      (git-setup-commit-buffer commit)
+      (git-commit-file))))
+
 (defun git-find-file ()
   "Visit the current file in its own buffer."
   (interactive)
   (unless git-status (error "Not in git-status buffer."))
   (let ((info (ewoc-data (ewoc-locate git-status))))
-    (find-file (git-fileinfo->name info))
-    (when (eq 'unmerged (git-fileinfo->state info))
-      (smerge-mode 1))))
+    (unless (git-expand-directory info)
+      (find-file (git-fileinfo->name info))
+      (when (eq 'unmerged (git-fileinfo->state info))
+        (smerge-mode 1)))))
 
 (defun git-find-file-other-window ()
   "Visit the current file in its own buffer in another window."
@@ -1309,6 +1418,7 @@
 
 (unless git-status-mode-map
   (let ((map (make-keymap))
+        (commit-map (make-sparse-keymap))
         (diff-map (make-sparse-keymap))
         (toggle-map (make-sparse-keymap)))
     (suppress-keymap map)
@@ -1317,6 +1427,7 @@
     (define-key map " "   'git-next-file)
     (define-key map "a"   'git-add-file)
     (define-key map "c"   'git-commit-file)
+    (define-key map "\C-c" commit-map)
     (define-key map "d"    diff-map)
     (define-key map "="   'git-diff-file)
     (define-key map "f"   'git-find-file)
@@ -1342,6 +1453,8 @@
     (define-key map "x"   'git-remove-handled)
     (define-key map "\C-?" 'git-unmark-file-up)
     (define-key map "\M-\C-?" 'git-unmark-all)
+    ; the commit submap
+    (define-key commit-map "\C-a" 'git-amend-commit)
     ; the diff submap
     (define-key diff-map "b" 'git-diff-file-base)
     (define-key diff-map "c" 'git-diff-file-combined)
diff --git a/git-checkout.sh b/contrib/examples/git-checkout.sh
similarity index 97%
rename from git-checkout.sh
rename to contrib/examples/git-checkout.sh
index bd74d70..1a7689a 100755
--- a/git-checkout.sh
+++ b/contrib/examples/git-checkout.sh
@@ -210,11 +210,14 @@
     git read-tree $v --reset -u $new
 else
     git update-index --refresh >/dev/null
-    merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
-	case "$merge" in
-	'')
-		echo >&2 "$merge_error"
+    git read-tree $v -m -u --exclude-per-directory=.gitignore $old $new || (
+	case "$merge,$v" in
+	,*)
 		exit 1 ;;
+	1,)
+		;; # quiet
+	*)
+		echo >&2 "Falling back to 3-way merge..." ;;
 	esac
 
 	# Match the index to the working tree, and do a three-way.
diff --git a/git-remote.perl b/contrib/examples/git-remote.perl
similarity index 97%
rename from git-remote.perl
rename to contrib/examples/git-remote.perl
index d13e4c1..b30ed73 100755
--- a/git-remote.perl
+++ b/contrib/examples/git-remote.perl
@@ -1,15 +1,16 @@
 #!/usr/bin/perl -w
 
+use strict;
 use Git;
 my $git = Git->repository();
 
 sub add_remote_config {
 	my ($hash, $name, $what, $value) = @_;
 	if ($what eq 'url') {
-		if (exists $hash->{$name}{'URL'}) {
-			print STDERR "Warning: more than one remote.$name.url\n";
+		# Having more than one is Ok -- it is used for push.
+		if (! exists $hash->{'URL'}) {
+			$hash->{$name}{'URL'} = $value;
 		}
-		$hash->{$name}{'URL'} = $value;
 	}
 	elsif ($what eq 'fetch') {
 		$hash->{$name}{'FETCH'} ||= [];
@@ -296,12 +297,13 @@
 
 sub update_remote {
 	my ($name) = @_;
+	my @remotes;
 
         my $conf = $git->config("remotes." . $name);
 	if (defined($conf)) {
 		@remotes = split(' ', $conf);
 	} elsif ($name eq 'default') {
-		undef @remotes;
+		@remotes = ();
 		for (sort keys %$remote) {
 			my $do_fetch = $git->config_bool("remote." . $_ .
 						    ".skipDefaultUpdate");
@@ -341,7 +343,7 @@
 	my @refs = $git->command('for-each-ref',
 		'--format=%(refname) %(objectname)', "refs/remotes/$name");
 	for (@refs) {
-		($ref, $object) = split;
+		my ($ref, $object) = split;
 		$git->command(qw(update-ref -d), $ref, $object);
 	}
 	return 0;
@@ -352,7 +354,7 @@
 	exit(1);
 }
 
-local $VERBOSE = 0;
+my $VERBOSE = 0;
 @ARGV = grep {
 	if ($_ eq '-v' or $_ eq '--verbose') {
 		$VERBOSE=1;
@@ -395,7 +397,7 @@
 		update_remote("default");
 		exit(1);
 	}
-	for ($i = 1; $i < @ARGV; $i++) {
+	for (my $i = 1; $i < @ARGV; $i++) {
 		update_remote($ARGV[$i]);
 	}
 }
diff --git a/contrib/examples/git-rerere.perl b/contrib/examples/git-rerere.perl
new file mode 100755
index 0000000..4f69209
--- /dev/null
+++ b/contrib/examples/git-rerere.perl
@@ -0,0 +1,284 @@
+#!/usr/bin/perl
+#
+# REuse REcorded REsolve.  This tool records a conflicted automerge
+# result and its hand resolution, and helps to resolve future
+# automerge that results in the same conflict.
+#
+# To enable this feature, create a directory 'rr-cache' under your
+# .git/ directory.
+
+use Digest;
+use File::Path;
+use File::Copy;
+
+my $git_dir = $::ENV{GIT_DIR} || ".git";
+my $rr_dir = "$git_dir/rr-cache";
+my $merge_rr = "$git_dir/rr-cache/MERGE_RR";
+
+my %merge_rr = ();
+
+sub read_rr {
+	if (!-f $merge_rr) {
+		%merge_rr = ();
+		return;
+	}
+	my $in;
+	local $/ = "\0";
+	open $in, "<$merge_rr" or die "$!: $merge_rr";
+	while (<$in>) {
+		chomp;
+		my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s;
+		$merge_rr{$path} = $name;
+	}
+	close $in;
+}
+
+sub write_rr {
+	my $out;
+	open $out, ">$merge_rr" or die "$!: $merge_rr";
+	for my $path (sort keys %merge_rr) {
+		my $name = $merge_rr{$path};
+		print $out "$name\t$path\0";
+	}
+	close $out;
+}
+
+sub compute_conflict_name {
+	my ($path) = @_;
+	my @side = ();
+	my $in;
+	open $in, "<$path"  or die "$!: $path";
+
+	my $sha1 = Digest->new("SHA-1");
+	my $hunk = 0;
+	while (<$in>) {
+		if (/^<<<<<<< .*/) {
+			$hunk++;
+			@side = ([], undef);
+		}
+		elsif (/^=======$/) {
+			$side[1] = [];
+		}
+		elsif (/^>>>>>>> .*/) {
+			my ($one, $two);
+			$one = join('', @{$side[0]});
+			$two = join('', @{$side[1]});
+			if ($two le $one) {
+				($one, $two) = ($two, $one);
+			}
+			$sha1->add($one);
+			$sha1->add("\0");
+			$sha1->add($two);
+			$sha1->add("\0");
+			@side = ();
+		}
+		elsif (@side == 0) {
+			next;
+		}
+		elsif (defined $side[1]) {
+			push @{$side[1]}, $_;
+		}
+		else {
+			push @{$side[0]}, $_;
+		}
+	}
+	close $in;
+	return ($sha1->hexdigest, $hunk);
+}
+
+sub record_preimage {
+	my ($path, $name) = @_;
+	my @side = ();
+	my ($in, $out);
+	open $in, "<$path"  or die "$!: $path";
+	open $out, ">$name" or die "$!: $name";
+
+	while (<$in>) {
+		if (/^<<<<<<< .*/) {
+			@side = ([], undef);
+		}
+		elsif (/^=======$/) {
+			$side[1] = [];
+		}
+		elsif (/^>>>>>>> .*/) {
+			my ($one, $two);
+			$one = join('', @{$side[0]});
+			$two = join('', @{$side[1]});
+			if ($two le $one) {
+				($one, $two) = ($two, $one);
+			}
+			print $out "<<<<<<<\n";
+			print $out $one;
+			print $out "=======\n";
+			print $out $two;
+			print $out ">>>>>>>\n";
+			@side = ();
+		}
+		elsif (@side == 0) {
+			print $out $_;
+		}
+		elsif (defined $side[1]) {
+			push @{$side[1]}, $_;
+		}
+		else {
+			push @{$side[0]}, $_;
+		}
+	}
+	close $out;
+	close $in;
+}
+
+sub find_conflict {
+	my $in;
+	local $/ = "\0";
+	my $pid = open($in, '-|');
+	die "$!" unless defined $pid;
+	if (!$pid) {
+		exec(qw(git ls-files -z -u)) or die "$!: ls-files";
+	}
+	my %path = ();
+	my @path = ();
+	while (<$in>) {
+		chomp;
+		my ($mode, $sha1, $stage, $path) =
+		    /^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s;
+		$path{$path} |= (1 << $stage);
+	}
+	close $in;
+	while (my ($path, $status) = each %path) {
+		if ($status == 14) { push @path, $path; }
+	}
+	return @path;
+}
+
+sub merge {
+	my ($name, $path) = @_;
+	record_preimage($path, "$rr_dir/$name/thisimage");
+	unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" }
+		       qw(this pre post))) {
+		my $in;
+		open $in, "<$rr_dir/$name/thisimage" or
+		    die "$!: $name/thisimage";
+		my $out;
+		open $out, ">$path" or die "$!: $path";
+		while (<$in>) { print $out $_; }
+		close $in;
+		close $out;
+		return 1;
+	}
+	return 0;
+}
+
+sub garbage_collect_rerere {
+	# We should allow specifying these from the command line and
+	# that is why the caller gives @ARGV to us, but I am lazy.
+
+	my $cutoff_noresolve = 15; # two weeks
+	my $cutoff_resolve = 60; # two months
+	my @to_remove;
+	while (<$rr_dir/*/preimage>) {
+		my ($dir) = /^(.*)\/preimage$/;
+		my $cutoff = ((-f "$dir/postimage")
+			      ? $cutoff_resolve
+			      : $cutoff_noresolve);
+		my $age = -M "$_";
+		if ($cutoff <= $age) {
+			push @to_remove, $dir;
+		}
+	}
+	if (@to_remove) {
+		rmtree(\@to_remove);
+	}
+}
+
+-d "$rr_dir" || exit(0);
+
+read_rr();
+
+if (@ARGV) {
+	my $arg = shift @ARGV;
+	if ($arg eq 'clear') {
+		for my $path (keys %merge_rr) {
+			my $name = $merge_rr{$path};
+			if (-d "$rr_dir/$name" &&
+			    ! -f "$rr_dir/$name/postimage") {
+				rmtree(["$rr_dir/$name"]);
+			}
+		}
+		unlink $merge_rr;
+	}
+	elsif ($arg eq 'status') {
+		for my $path (keys %merge_rr) {
+			print $path, "\n";
+		}
+	}
+	elsif ($arg eq 'diff') {
+		for my $path (keys %merge_rr) {
+			my $name = $merge_rr{$path};
+			system('diff', ((@ARGV == 0) ? ('-u') : @ARGV),
+				'-L', "a/$path", '-L', "b/$path",
+				"$rr_dir/$name/preimage", $path);
+		}
+	}
+	elsif ($arg eq 'gc') {
+		garbage_collect_rerere(@ARGV);
+	}
+	else {
+		die "$0 unknown command: $arg\n";
+	}
+	exit 0;
+}
+
+my %conflict = map { $_ => 1 } find_conflict();
+
+# MERGE_RR records paths with conflicts immediately after merge
+# failed.  Some of the conflicted paths might have been hand resolved
+# in the working tree since then, but the initial run would catch all
+# and register their preimages.
+
+for my $path (keys %conflict) {
+	# This path has conflict.  If it is not recorded yet,
+	# record the pre-image.
+	if (!exists $merge_rr{$path}) {
+		my ($name, $hunk) = compute_conflict_name($path);
+		next unless ($hunk);
+		$merge_rr{$path} = $name;
+		if (! -d "$rr_dir/$name") {
+			mkpath("$rr_dir/$name", 0, 0777);
+			print STDERR "Recorded preimage for '$path'\n";
+			record_preimage($path, "$rr_dir/$name/preimage");
+		}
+	}
+}
+
+# Now some of the paths that had conflicts earlier might have been
+# hand resolved.  Others may be similar to a conflict already that
+# was resolved before.
+
+for my $path (keys %merge_rr) {
+	my $name = $merge_rr{$path};
+
+	# We could resolve this automatically if we have images.
+	if (-f "$rr_dir/$name/preimage" &&
+	    -f "$rr_dir/$name/postimage") {
+		if (merge($name, $path)) {
+			print STDERR "Resolved '$path' using previous resolution.\n";
+			# Then we do not have to worry about this path
+			# anymore.
+			delete $merge_rr{$path};
+			next;
+		}
+	}
+
+	# Let's see if we have resolved it.
+	(undef, my $hunk) = compute_conflict_name($path);
+	next if ($hunk);
+
+	print STDERR "Recorded resolution for '$path'.\n";
+	copy($path, "$rr_dir/$name/postimage");
+	# And we do not have to worry about this path anymore.
+	delete $merge_rr{$path};
+}
+
+# Write out the rest.
+write_rr();
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index c80a6da..d8de9f6 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -90,11 +90,11 @@
     # Returns the perforce file type for the given file.
 
     result = read_pipe("p4 opened %s" % file)
-    match = re.match(".*\((.+)\)$", result)
+    match = re.match(".*\((.+)\)\r?$", result)
     if match:
         return match.group(1)
     else:
-        die("Could not determine file type for %s" % file)
+        die("Could not determine file type for %s (result: '%s')" % (file, result))
 
 def diffTreePattern():
     # This is a simple generator for the diff tree regex pattern. This could be
@@ -464,75 +464,47 @@
     def __init__(self):
         Command.__init__(self)
         self.options = [
-                optparse.make_option("--continue", action="store_false", dest="firstTime"),
                 optparse.make_option("--verbose", dest="verbose", action="store_true"),
                 optparse.make_option("--origin", dest="origin"),
-                optparse.make_option("--reset", action="store_true", dest="reset"),
-                optparse.make_option("--log-substitutions", dest="substFile"),
-                optparse.make_option("--dry-run", action="store_true"),
-                optparse.make_option("--direct", dest="directSubmit", action="store_true"),
-                optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"),
                 optparse.make_option("-M", dest="detectRename", action="store_true"),
         ]
         self.description = "Submit changes from git to the perforce depot."
         self.usage += " [name of git branch to submit into perforce depot]"
-        self.firstTime = True
-        self.reset = False
         self.interactive = True
-        self.dryRun = False
-        self.substFile = ""
-        self.firstTime = True
         self.origin = ""
-        self.directSubmit = False
-        self.trustMeLikeAFool = False
         self.detectRename = False
         self.verbose = False
         self.isWindows = (platform.system() == "Windows")
 
-        self.logSubstitutions = {}
-        self.logSubstitutions["<enter description here>"] = "%log%"
-        self.logSubstitutions["\tDetails:"] = "\tDetails:  %log%"
-
     def check(self):
         if len(p4CmdList("opened ...")) > 0:
             die("You have files opened with perforce! Close them before starting the sync.")
 
-    def start(self):
-        if len(self.config) > 0 and not self.reset:
-            die("Cannot start sync. Previous sync config found at %s\n"
-                "If you want to start submitting again from scratch "
-                "maybe you want to call git-p4 submit --reset" % self.configFile)
-
-        commits = []
-        if self.directSubmit:
-            commits.append("0")
-        else:
-            for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
-                commits.append(line.strip())
-            commits.reverse()
-
-        self.config["commits"] = commits
-
+    # replaces everything between 'Description:' and the next P4 submit template field with the
+    # commit message
     def prepareLogMessage(self, template, message):
         result = ""
 
+        inDescriptionSection = False
+
         for line in template.split("\n"):
             if line.startswith("#"):
                 result += line + "\n"
                 continue
 
-            substituted = False
-            for key in self.logSubstitutions.keys():
-                if line.find(key) != -1:
-                    value = self.logSubstitutions[key]
-                    value = value.replace("%log%", message)
-                    if value != "@remove@":
-                        result += line.replace(key, value) + "\n"
-                    substituted = True
-                    break
+            if inDescriptionSection:
+                if line.startswith("Files:"):
+                    inDescriptionSection = False
+                else:
+                    continue
+            else:
+                if line.startswith("Description:"):
+                    inDescriptionSection = True
+                    line += "\n"
+                    for messageLine in message.split("\n"):
+                        line += "\t" + messageLine + "\n"
 
-            if not substituted:
-                result += line + "\n"
+            result += line + "\n"
 
         return result
 
@@ -541,6 +513,8 @@
         template = ""
         inFilesSection = False
         for line in read_pipe_lines("p4 change -o"):
+            if line.endswith("\r\n"):
+                line = line[:-2] + "\n"
             if inFilesSection:
                 if line.startswith("\t"):
                     # path starts and ends with a tab
@@ -561,13 +535,9 @@
         return template
 
     def applyCommit(self, id):
-        if self.directSubmit:
-            print "Applying local change in working directory/index"
-            diff = self.diffStatus
-        else:
-            print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
-            diffOpts = ("", "-M")[self.detectRename]
-            diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
+        print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+        diffOpts = ("", "-M")[self.detectRename]
+        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
         filesToAdd = set()
         filesToDelete = set()
         editedFiles = set()
@@ -602,10 +572,7 @@
             else:
                 die("unknown modifier %s for %s" % (modifier, path))
 
-        if self.directSubmit:
-            diffcmd = "cat \"%s\"" % self.diffFile
-        else:
-            diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
+        diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
         patchcmd = diffcmd + " | git apply "
         tryPatchCmd = patchcmd + "--check -"
         applyPatchCmd = patchcmd + "--check --apply -"
@@ -653,85 +620,54 @@
             mode = filesToChangeExecBit[f]
             setP4ExecBit(f, mode)
 
-        logMessage = ""
-        if not self.directSubmit:
-            logMessage = extractLogMessageFromGitCommit(id)
-            logMessage = logMessage.replace("\n", "\n\t")
-            if self.isWindows:
-                logMessage = logMessage.replace("\n", "\r\n")
-            logMessage = logMessage.strip()
+        logMessage = extractLogMessageFromGitCommit(id)
+        logMessage = logMessage.strip()
 
         template = self.prepareSubmitTemplate()
 
         if self.interactive:
             submitTemplate = self.prepareLogMessage(template, logMessage)
+            if os.environ.has_key("P4DIFF"):
+                del(os.environ["P4DIFF"])
             diff = read_pipe("p4 diff -du ...")
 
+            newdiff = ""
             for newFile in filesToAdd:
-                diff += "==== new file ====\n"
-                diff += "--- /dev/null\n"
-                diff += "+++ %s\n" % newFile
+                newdiff += "==== new file ====\n"
+                newdiff += "--- /dev/null\n"
+                newdiff += "+++ %s\n" % newFile
                 f = open(newFile, "r")
                 for line in f.readlines():
-                    diff += "+" + line
+                    newdiff += "+" + line
                 f.close()
 
-            separatorLine = "######## everything below this line is just the diff #######"
+            separatorLine = "######## everything below this line is just the diff #######\n"
+
+            [handle, fileName] = tempfile.mkstemp()
+            tmpFile = os.fdopen(handle, "w+")
+            if self.isWindows:
+                submitTemplate = submitTemplate.replace("\n", "\r\n")
+                separatorLine = separatorLine.replace("\n", "\r\n")
+                newdiff = newdiff.replace("\n", "\r\n")
+            tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
+            tmpFile.close()
+            defaultEditor = "vi"
             if platform.system() == "Windows":
-                separatorLine += "\r"
-            separatorLine += "\n"
-
-            response = "e"
-            if self.trustMeLikeAFool:
-                response = "y"
-
-            firstIteration = True
-            while response == "e":
-                if not firstIteration:
-                    response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ")
-                firstIteration = False
-                if response == "e":
-                    [handle, fileName] = tempfile.mkstemp()
-                    tmpFile = os.fdopen(handle, "w+")
-                    tmpFile.write(submitTemplate + separatorLine + diff)
-                    tmpFile.close()
-                    defaultEditor = "vi"
-                    if platform.system() == "Windows":
-                        defaultEditor = "notepad"
-                    editor = os.environ.get("EDITOR", defaultEditor);
-                    system(editor + " " + fileName)
-                    tmpFile = open(fileName, "rb")
-                    message = tmpFile.read()
-                    tmpFile.close()
-                    os.remove(fileName)
-                    submitTemplate = message[:message.index(separatorLine)]
-                    if self.isWindows:
-                        submitTemplate = submitTemplate.replace("\r\n", "\n")
-
-            if response == "y" or response == "yes":
-               if self.dryRun:
-                   print submitTemplate
-                   raw_input("Press return to continue...")
-               else:
-                   if self.directSubmit:
-                       print "Submitting to git first"
-                       os.chdir(self.oldWorkingDirectory)
-                       write_pipe("git commit -a -F -", submitTemplate)
-                       os.chdir(self.clientPath)
-
-                   write_pipe("p4 submit -i", submitTemplate)
-            elif response == "s":
-                for f in editedFiles:
-                    system("p4 revert \"%s\"" % f);
-                for f in filesToAdd:
-                    system("p4 revert \"%s\"" % f);
-                    system("rm %s" %f)
-                for f in filesToDelete:
-                    system("p4 delete \"%s\"" % f);
-                return
+                defaultEditor = "notepad"
+            if os.environ.has_key("P4EDITOR"):
+                editor = os.environ.get("P4EDITOR")
             else:
-                print "Not submitting!"
-                self.interactive = False
+                editor = os.environ.get("EDITOR", defaultEditor);
+            system(editor + " " + fileName)
+            tmpFile = open(fileName, "rb")
+            message = tmpFile.read()
+            tmpFile.close()
+            os.remove(fileName)
+            submitTemplate = message[:message.index(separatorLine)]
+            if self.isWindows:
+                submitTemplate = submitTemplate.replace("\r\n", "\n")
+
+            write_pipe("p4 submit -i", submitTemplate)
         else:
             fileName = "submit.txt"
             file = open(fileName, "w+")
@@ -772,67 +708,33 @@
         print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
         self.oldWorkingDirectory = os.getcwd()
 
-        if self.directSubmit:
-            self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD")
-            if len(self.diffStatus) == 0:
-                print "No changes in working directory to submit."
-                return True
-            patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD")
-            self.diffFile = self.gitdir + "/p4-git-diff"
-            f = open(self.diffFile, "wb")
-            f.write(patch)
-            f.close();
-
         os.chdir(self.clientPath)
         print "Syncronizing p4 checkout..."
         system("p4 sync ...")
 
-        if self.reset:
-            self.firstTime = True
-
-        if len(self.substFile) > 0:
-            for line in open(self.substFile, "r").readlines():
-                tokens = line.strip().split("=")
-                self.logSubstitutions[tokens[0]] = tokens[1]
-
         self.check()
-        self.configFile = self.gitdir + "/p4-git-sync.cfg"
-        self.config = shelve.open(self.configFile, writeback=True)
 
-        if self.firstTime:
-            self.start()
-
-        commits = self.config.get("commits", [])
+        commits = []
+        for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
+            commits.append(line.strip())
+        commits.reverse()
 
         while len(commits) > 0:
-            self.firstTime = False
             commit = commits[0]
             commits = commits[1:]
-            self.config["commits"] = commits
             self.applyCommit(commit)
             if not self.interactive:
                 break
 
-        self.config.close()
-
-        if self.directSubmit:
-            os.remove(self.diffFile)
-
         if len(commits) == 0:
-            if self.firstTime:
-                print "No changes found to apply between %s and current HEAD" % self.origin
-            else:
-                print "All changes applied!"
-                os.chdir(self.oldWorkingDirectory)
+            print "All changes applied!"
+            os.chdir(self.oldWorkingDirectory)
 
-                sync = P4Sync()
-                sync.run([])
+            sync = P4Sync()
+            sync.run([])
 
-                response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ")
-                if response == "y" or response == "yes":
-                    rebase = P4Rebase()
-                    rebase.rebase()
-            os.remove(self.configFile)
+            rebase = P4Rebase()
+            rebase.rebase()
 
         return True
 
@@ -850,7 +752,9 @@
                                      help="Import into refs/heads/ , not refs/remotes"),
                 optparse.make_option("--max-changes", dest="maxChanges"),
                 optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
-                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import")
+                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
+                optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
+                                     help="Only sync files that are included in the Perforce Client Spec")
         ]
         self.description = """Imports from Perforce into a git repository.\n
     example:
@@ -876,18 +780,27 @@
         self.keepRepoPath = False
         self.depotPaths = None
         self.p4BranchesInGit = []
+        self.cloneExclude = []
+        self.useClientSpec = False
+        self.clientSpecDirs = []
 
         if gitConfig("git-p4.syncFromOrigin") == "false":
             self.syncWithOrigin = False
 
     def extractFilesFromCommit(self, commit):
+        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
+                             for path in self.cloneExclude]
         files = []
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
             path =  commit["depotFile%s" % fnum]
 
-            found = [p for p in self.depotPaths
-                     if path.startswith (p)]
+            if [p for p in self.cloneExclude
+                if path.startswith (p)]:
+                found = False
+            else:
+                found = [p for p in self.depotPaths
+                         if path.startswith (p)]
             if not found:
                 fnum = fnum + 1
                 continue
@@ -944,41 +857,61 @@
 
     ## Should move this out, doesn't use SELF.
     def readP4Files(self, files):
-        files = [f for f in files
-                 if f['action'] != 'delete']
+        filesForCommit = []
+        filesToRead = []
 
-        if not files:
-            return
+        for f in files:
+            includeFile = True
+            for val in self.clientSpecDirs:
+                if f['path'].startswith(val[0]):
+                    if val[1] <= 0:
+                        includeFile = False
+                    break
 
-        filedata = p4CmdList('-x - print',
-                             stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
-                                              for f in files]),
-                             stdin_mode='w+')
-        if "p4ExitCode" in filedata[0]:
-            die("Problems executing p4. Error: [%d]."
-                % (filedata[0]['p4ExitCode']));
+            if includeFile:
+                filesForCommit.append(f)
+                if f['action'] != 'delete':
+                    filesToRead.append(f)
+
+        filedata = []
+        if len(filesToRead) > 0:
+            filedata = p4CmdList('-x - print',
+                                 stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
+                                                  for f in filesToRead]),
+                                 stdin_mode='w+')
+
+            if "p4ExitCode" in filedata[0]:
+                die("Problems executing p4. Error: [%d]."
+                    % (filedata[0]['p4ExitCode']));
 
         j = 0;
         contents = {}
         while j < len(filedata):
             stat = filedata[j]
             j += 1
-            text = ''
-            while j < len(filedata) and filedata[j]['code'] in ('text',
-                                                                'binary'):
-                text += filedata[j]['data']
+            text = [];
+            while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
+                text.append(filedata[j]['data'])
                 j += 1
-
+            text = ''.join(text)
 
             if not stat.has_key('depotFile'):
                 sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
                 continue
 
+            if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+                text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
+            elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+                text = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text)
+
             contents[stat['depotFile']] = text
 
-        for f in files:
-            assert not f.has_key('data')
-            f['data'] = contents[f['path']]
+        for f in filesForCommit:
+            path = f['path']
+            if contents.has_key(path):
+                f['data'] = contents[path]
+
+        return filesForCommit
 
     def commit(self, details, files, branch, branchPrefixes, parent = ""):
         epoch = details["time"]
@@ -995,11 +928,7 @@
                 new_files.append (f)
             else:
                 sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
-        files = new_files
-        self.readP4Files(files)
-
-
-
+        files = self.readP4Files(new_files)
 
         self.gitStream.write("commit %s\n" % branch)
 #        gitStream.write("mark :%s\n" % details["change"])
@@ -1414,6 +1343,26 @@
             print self.gitError.read()
 
 
+    def getClientSpec(self):
+        specList = p4CmdList( "client -o" )
+        temp = {}
+        for entry in specList:
+            for k,v in entry.iteritems():
+                if k.startswith("View"):
+                    if v.startswith('"'):
+                        start = 1
+                    else:
+                        start = 0
+                    index = v.find("...")
+                    v = v[start:index]
+                    if v.startswith("-"):
+                        v = v[1:]
+                        temp[v] = -len(v)
+                    else:
+                        temp[v] = len(v)
+        self.clientSpecDirs = temp.items()
+        self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
+
     def run(self, args):
         self.depotPaths = []
         self.changeRange = ""
@@ -1446,6 +1395,9 @@
             if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
                 system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
 
+        if self.useClientSpec or gitConfig("p4.useclientspec") == "true":
+            self.getClientSpec()
+
         # TODO: should always look at previous commits,
         # merge with previous imports, if possible.
         if args == []:
@@ -1640,6 +1592,11 @@
         return self.rebase()
 
     def rebase(self):
+        if os.system("git update-index --refresh") != 0:
+            die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
+        if len(read_pipe("git diff-index HEAD --")) > 0:
+            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
+
         [upstream, settings] = findUpstreamBranchPoint()
         if len(upstream) == 0:
             die("Cannot find upstream branchpoint for rebase")
@@ -1658,19 +1615,29 @@
         P4Sync.__init__(self)
         self.description = "Creates a new git repository and imports from Perforce into it"
         self.usage = "usage: %prog [options] //depot/path[@revRange]"
-        self.options.append(
+        self.options += [
             optparse.make_option("--destination", dest="cloneDestination",
                                  action='store', default=None,
-                                 help="where to leave result of the clone"))
+                                 help="where to leave result of the clone"),
+            optparse.make_option("-/", dest="cloneExclude",
+                                 action="append", type="string",
+                                 help="exclude depot path")
+        ]
         self.cloneDestination = None
         self.needsGit = False
 
+    # This is required for the "append" cloneExclude action
+    def ensure_value(self, attr, value):
+        if not hasattr(self, attr) or getattr(self, attr) is None:
+            setattr(self, attr, value)
+        return getattr(self, attr)
+
     def defaultDestination(self, args):
         ## TODO: use common prefix of args?
         depotPath = args[0]
         depotDir = re.sub("(@[^@]*)$", "", depotPath)
         depotDir = re.sub("(#[^#]*)$", "", depotDir)
-        depotDir = re.sub(r"\.\.\.$,", "", depotDir)
+        depotDir = re.sub(r"\.\.\.$", "", depotDir)
         depotDir = re.sub(r"/$", "", depotDir)
         return os.path.split(depotDir)[1]
 
@@ -1688,6 +1655,7 @@
             self.cloneDestination = depotPaths[-1]
             depotPaths = depotPaths[:-1]
 
+        self.cloneExclude = ["/"+p for p in self.cloneExclude]
         for p in depotPaths:
             if not p.startswith("//"):
                 return False
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 77c88eb..4136895 100644
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -202,11 +202,12 @@
 
 generate_email_footer()
 {
+	SPACE=" "
 	cat <<-EOF
 
 
 	hooks/post-receive
-	--
+	--${SPACE}
 	$projectdesc
 	EOF
 }
@@ -567,7 +568,7 @@
 	echo ""
 	if [ "$newrev_type" = "commit" ]; then
 		echo $LOGBEGIN
-		git show --no-color --root -s $newrev
+		git show --no-color --root -s --pretty=medium $newrev
 		echo $LOGEND
 	else
 		# What can we do here?  The tag marks an object that is not
diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl
index aab501e..f4a7b62 100755
--- a/contrib/stats/packinfo.pl
+++ b/contrib/stats/packinfo.pl
@@ -93,7 +93,7 @@
 my @depths;
 
 while (<STDIN>) {
-    my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_);
+    my ($sha1, $type, $size, $space, $offset, $depth, $parent) = split(/\s+/, $_);
     next unless ($sha1 =~ /^[0-9a-f]{40}$/);
     $depths{$sha1} = $depth || 0;
     push(@depths, $depth || 0);
diff --git a/convert.c b/convert.c
index 552707e..d8c94cb 100644
--- a/convert.c
+++ b/convert.c
@@ -85,8 +85,39 @@
 	return 0;
 }
 
+static void check_safe_crlf(const char *path, int action,
+                            struct text_stat *stats, enum safe_crlf checksafe)
+{
+	if (!checksafe)
+		return;
+
+	if (action == CRLF_INPUT || auto_crlf <= 0) {
+		/*
+		 * CRLFs would not be restored by checkout:
+		 * check if we'd remove CRLFs
+		 */
+		if (stats->crlf) {
+			if (checksafe == SAFE_CRLF_WARN)
+				warning("CRLF will be replaced by LF in %s.", path);
+			else /* i.e. SAFE_CRLF_FAIL */
+				die("CRLF would be replaced by LF in %s.", path);
+		}
+	} else if (auto_crlf > 0) {
+		/*
+		 * CRLFs would be added by checkout:
+		 * check if we have "naked" LFs
+		 */
+		if (stats->lf != stats->crlf) {
+			if (checksafe == SAFE_CRLF_WARN)
+				warning("LF will be replaced by CRLF in %s", path);
+			else /* i.e. SAFE_CRLF_FAIL */
+				die("LF would be replaced by CRLF in %s", path);
+		}
+	}
+}
+
 static int crlf_to_git(const char *path, const char *src, size_t len,
-                       struct strbuf *buf, int action)
+                       struct strbuf *buf, int action, enum safe_crlf checksafe)
 {
 	struct text_stat stats;
 	char *dst;
@@ -95,9 +126,6 @@
 		return 0;
 
 	gather_stats(src, len, &stats);
-	/* No CR? Nothing to convert, regardless. */
-	if (!stats.cr)
-		return 0;
 
 	if (action == CRLF_GUESS) {
 		/*
@@ -115,6 +143,12 @@
 			return 0;
 	}
 
+	check_safe_crlf(path, action, &stats, checksafe);
+
+	/* Optimization: No CR? Nothing to convert, regardless. */
+	if (!stats.cr)
+		return 0;
+
 	/* only grow if not in place */
 	if (strbuf_avail(buf) + buf->len < len)
 		strbuf_grow(buf, len - buf->len);
@@ -536,7 +570,8 @@
 	return !!ATTR_TRUE(value);
 }
 
-int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst)
+int convert_to_git(const char *path, const char *src, size_t len,
+                   struct strbuf *dst, enum safe_crlf checksafe)
 {
 	struct git_attr_check check[3];
 	int crlf = CRLF_GUESS;
@@ -558,7 +593,7 @@
 		src = dst->buf;
 		len = dst->len;
 	}
-	ret |= crlf_to_git(path, src, len, dst, crlf);
+	ret |= crlf_to_git(path, src, len, dst, crlf, checksafe);
 	if (ret) {
 		src = dst->buf;
 		len = dst->len;
diff --git a/diff-lib.c b/diff-lib.c
index d85d8f3..069e450 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -9,6 +9,8 @@
 #include "revision.h"
 #include "cache-tree.h"
 #include "path-list.h"
+#include "unpack-trees.h"
+#include "refs.h"
 
 /*
  * diff-files
@@ -37,7 +39,7 @@
 	if (!path || !strcmp(path, "/dev/null"))
 		*mode = 0;
 	else if (!strcmp(path, "-"))
-		*mode = ntohl(create_ce_mode(0666));
+		*mode = create_ce_mode(0666);
 	else if (stat(path, &st))
 		return error("Could not access '%s'", path);
 	else
@@ -332,6 +334,26 @@
 	}
 	return run_diff_files(revs, options);
 }
+/*
+ * See if work tree has an entity that can be staged.  Return 0 if so,
+ * return 1 if not and return -1 if error.
+ */
+static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache)
+{
+	if (lstat(ce->name, st) < 0) {
+		if (errno != ENOENT && errno != ENOTDIR)
+			return -1;
+		return 1;
+	}
+	if (has_symlink_leading_path(ce->name, symcache))
+		return 1;
+	if (S_ISDIR(st->st_mode)) {
+		unsigned char sub[20];
+		if (resolve_gitlink_ref(ce->name, "HEAD", sub))
+			return 1;
+	}
+	return 0;
+}
 
 int run_diff_files(struct rev_info *revs, unsigned int option)
 {
@@ -340,10 +362,12 @@
 	int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
 	unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
 			      ? CE_MATCH_RACY_IS_DIRTY : 0);
+	char symcache[PATH_MAX];
 
 	if (diff_unmerged_stage < 0)
 		diff_unmerged_stage = 2;
 	entries = active_nr;
+	symcache[0] = '\0';
 	for (i = 0; i < entries; i++) {
 		struct stat st;
 		unsigned int oldmode, newmode;
@@ -375,16 +399,17 @@
 			memset(&(dpath->parent[0]), 0,
 			       sizeof(struct combine_diff_parent)*5);
 
-			if (lstat(ce->name, &st) < 0) {
-				if (errno != ENOENT && errno != ENOTDIR) {
+			changed = check_work_tree_entity(ce, &st, symcache);
+			if (!changed)
+				dpath->mode = ce_mode_from_stat(ce, st.st_mode);
+			else {
+				if (changed < 0) {
 					perror(ce->name);
 					continue;
 				}
 				if (silent_on_removed)
 					continue;
 			}
-			else
-				dpath->mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
 
 			while (i < entries) {
 				struct cache_entry *nce = active_cache[i];
@@ -398,10 +423,10 @@
 				 */
 				stage = ce_stage(nce);
 				if (2 <= stage) {
-					int mode = ntohl(nce->ce_mode);
+					int mode = nce->ce_mode;
 					num_compare_stages++;
 					hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
-					dpath->parent[stage-2].mode = ntohl(ce_mode_from_stat(nce, mode));
+					dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
 					dpath->parent[stage-2].status =
 						DIFF_STATUS_MODIFIED;
 				}
@@ -435,22 +460,26 @@
 				continue;
 		}
 
-		if (lstat(ce->name, &st) < 0) {
-			if (errno != ENOENT && errno != ENOTDIR) {
+		if (ce_uptodate(ce))
+			continue;
+
+		changed = check_work_tree_entity(ce, &st, symcache);
+		if (changed) {
+			if (changed < 0) {
 				perror(ce->name);
 				continue;
 			}
 			if (silent_on_removed)
 				continue;
-			diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode),
+			diff_addremove(&revs->diffopt, '-', ce->ce_mode,
 				       ce->sha1, ce->name, NULL);
 			continue;
 		}
 		changed = ce_match_stat(ce, &st, ce_option);
 		if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
 			continue;
-		oldmode = ntohl(ce->ce_mode);
-		newmode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+		oldmode = ce->ce_mode;
+		newmode = ce_mode_from_stat(ce, st.st_mode);
 		diff_change(&revs->diffopt, oldmode, newmode,
 			    ce->sha1, (changed ? null_sha1 : ce->sha1),
 			    ce->name, NULL);
@@ -465,30 +494,38 @@
  * diff-index
  */
 
+struct oneway_unpack_data {
+	struct rev_info *revs;
+	char symcache[PATH_MAX];
+};
+
 /* A file entry went away or appeared */
 static void diff_index_show_file(struct rev_info *revs,
 				 const char *prefix,
 				 struct cache_entry *ce,
-				 unsigned char *sha1, unsigned int mode)
+				 const unsigned char *sha1, unsigned int mode)
 {
-	diff_addremove(&revs->diffopt, prefix[0], ntohl(mode),
+	diff_addremove(&revs->diffopt, prefix[0], mode,
 		       sha1, ce->name, NULL);
 }
 
 static int get_stat_data(struct cache_entry *ce,
-			 unsigned char **sha1p,
+			 const unsigned char **sha1p,
 			 unsigned int *modep,
-			 int cached, int match_missing)
+			 int cached, int match_missing,
+			 struct oneway_unpack_data *cbdata)
 {
-	unsigned char *sha1 = ce->sha1;
+	const unsigned char *sha1 = ce->sha1;
 	unsigned int mode = ce->ce_mode;
 
 	if (!cached) {
-		static unsigned char no_sha1[20];
 		int changed;
 		struct stat st;
-		if (lstat(ce->name, &st) < 0) {
-			if (errno == ENOENT && match_missing) {
+		changed = check_work_tree_entity(ce, &st, cbdata->symcache);
+		if (changed < 0)
+			return -1;
+		else if (changed) {
+			if (match_missing) {
 				*sha1p = sha1;
 				*modep = mode;
 				return 0;
@@ -498,7 +535,7 @@
 		changed = ce_match_stat(ce, &st, 0);
 		if (changed) {
 			mode = ce_mode_from_stat(ce, st.st_mode);
-			sha1 = no_sha1;
+			sha1 = null_sha1;
 		}
 	}
 
@@ -507,32 +544,35 @@
 	return 0;
 }
 
-static void show_new_file(struct rev_info *revs,
+static void show_new_file(struct oneway_unpack_data *cbdata,
 			  struct cache_entry *new,
 			  int cached, int match_missing)
 {
-	unsigned char *sha1;
+	const unsigned char *sha1;
 	unsigned int mode;
+	struct rev_info *revs = cbdata->revs;
 
-	/* New file in the index: it might actually be different in
+	/*
+	 * New file in the index: it might actually be different in
 	 * the working copy.
 	 */
-	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
+	if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0)
 		return;
 
 	diff_index_show_file(revs, "+", new, sha1, mode);
 }
 
-static int show_modified(struct rev_info *revs,
+static int show_modified(struct oneway_unpack_data *cbdata,
 			 struct cache_entry *old,
 			 struct cache_entry *new,
 			 int report_missing,
 			 int cached, int match_missing)
 {
 	unsigned int mode, oldmode;
-	unsigned char *sha1;
+	const unsigned char *sha1;
+	struct rev_info *revs = cbdata->revs;
 
-	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
+	if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) {
 		if (report_missing)
 			diff_index_show_file(revs, "-", old,
 					     old->sha1, old->ce_mode);
@@ -550,14 +590,14 @@
 		p->len = pathlen;
 		memcpy(p->path, new->name, pathlen);
 		p->path[pathlen] = 0;
-		p->mode = ntohl(mode);
+		p->mode = mode;
 		hashclr(p->sha1);
 		memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
 		p->parent[0].status = DIFF_STATUS_MODIFIED;
-		p->parent[0].mode = ntohl(new->ce_mode);
+		p->parent[0].mode = new->ce_mode;
 		hashcpy(p->parent[0].sha1, new->sha1);
 		p->parent[1].status = DIFF_STATUS_MODIFIED;
-		p->parent[1].mode = ntohl(old->ce_mode);
+		p->parent[1].mode = old->ce_mode;
 		hashcpy(p->parent[1].sha1, old->sha1);
 		show_combined_diff(p, 2, revs->dense_combined_merges, revs);
 		free(p);
@@ -569,89 +609,11 @@
 	    !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
 		return 0;
 
-	mode = ntohl(mode);
-	oldmode = ntohl(oldmode);
-
 	diff_change(&revs->diffopt, oldmode, mode,
 		    old->sha1, sha1, old->name, NULL);
 	return 0;
 }
 
-static int diff_cache(struct rev_info *revs,
-		      struct cache_entry **ac, int entries,
-		      const char **pathspec,
-		      int cached, int match_missing)
-{
-	while (entries) {
-		struct cache_entry *ce = *ac;
-		int same = (entries > 1) && ce_same_name(ce, ac[1]);
-
-		if (DIFF_OPT_TST(&revs->diffopt, QUIET) &&
-			DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
-			break;
-
-		if (!ce_path_match(ce, pathspec))
-			goto skip_entry;
-
-		switch (ce_stage(ce)) {
-		case 0:
-			/* No stage 1 entry? That means it's a new file */
-			if (!same) {
-				show_new_file(revs, ce, cached, match_missing);
-				break;
-			}
-			/* Show difference between old and new */
-			show_modified(revs, ac[1], ce, 1,
-				      cached, match_missing);
-			break;
-		case 1:
-			/* No stage 3 (merge) entry?
-			 * That means it's been deleted.
-			 */
-			if (!same) {
-				diff_index_show_file(revs, "-", ce,
-						     ce->sha1, ce->ce_mode);
-				break;
-			}
-			/* We come here with ce pointing at stage 1
-			 * (original tree) and ac[1] pointing at stage
-			 * 3 (unmerged).  show-modified with
-			 * report-missing set to false does not say the
-			 * file is deleted but reports true if work
-			 * tree does not have it, in which case we
-			 * fall through to report the unmerged state.
-			 * Otherwise, we show the differences between
-			 * the original tree and the work tree.
-			 */
-			if (!cached &&
-			    !show_modified(revs, ce, ac[1], 0,
-					   cached, match_missing))
-				break;
-			diff_unmerge(&revs->diffopt, ce->name,
-				     ntohl(ce->ce_mode), ce->sha1);
-			break;
-		case 3:
-			diff_unmerge(&revs->diffopt, ce->name,
-				     0, null_sha1);
-			break;
-
-		default:
-			die("impossible cache entry stage");
-		}
-
-skip_entry:
-		/*
-		 * Ignore all the different stages for this file,
-		 * we've handled the relevant cases now.
-		 */
-		do {
-			ac++;
-			entries--;
-		} while (entries && ce_same_name(ce, ac[0]));
-	}
-	return 0;
-}
-
 /*
  * This turns all merge entries into "stage 3". That guarantees that
  * when we read in the new tree (into "stage 1"), we won't lose sight
@@ -664,24 +626,123 @@
 		struct cache_entry *ce = active_cache[i];
 		if (!ce_stage(ce))
 			continue;
-		ce->ce_flags |= htons(CE_STAGEMASK);
+		ce->ce_flags |= CE_STAGEMASK;
 	}
 }
 
+/*
+ * This gets a mix of an existing index and a tree, one pathname entry
+ * at a time. The index entry may be a single stage-0 one, but it could
+ * also be multiple unmerged entries (in which case idx_pos/idx_nr will
+ * give you the position and number of entries in the index).
+ */
+static void do_oneway_diff(struct unpack_trees_options *o,
+	struct cache_entry *idx,
+	struct cache_entry *tree)
+{
+	struct oneway_unpack_data *cbdata = o->unpack_data;
+	struct rev_info *revs = cbdata->revs;
+	int match_missing, cached;
+
+	/*
+	 * Backward compatibility wart - "diff-index -m" does
+	 * not mean "do not ignore merges", but "match_missing".
+	 *
+	 * But with the revision flag parsing, that's found in
+	 * "!revs->ignore_merges".
+	 */
+	cached = o->index_only;
+	match_missing = !revs->ignore_merges;
+
+	if (cached && idx && ce_stage(idx)) {
+		if (tree)
+			diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+		return;
+	}
+
+	/*
+	 * Something added to the tree?
+	 */
+	if (!tree) {
+		show_new_file(cbdata, idx, cached, match_missing);
+		return;
+	}
+
+	/*
+	 * Something removed from the tree?
+	 */
+	if (!idx) {
+		diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode);
+		return;
+	}
+
+	/* Show difference between old and new */
+	show_modified(cbdata, tree, idx, 1, cached, match_missing);
+}
+
+static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+	int len = ce_namelen(ce);
+	const struct index_state *index = o->src_index;
+
+	while (o->pos < index->cache_nr) {
+		struct cache_entry *next = index->cache[o->pos];
+		if (len != ce_namelen(next))
+			break;
+		if (memcmp(ce->name, next->name, len))
+			break;
+		o->pos++;
+	}
+}
+
+/*
+ * The unpack_trees() interface is designed for merging, so
+ * the different source entries are designed primarily for
+ * the source trees, with the old index being really mainly
+ * used for being replaced by the result.
+ *
+ * For diffing, the index is more important, and we only have a
+ * single tree.
+ *
+ * We're supposed to return how many index entries we want to skip.
+ *
+ * This wrapper makes it all more readable, and takes care of all
+ * the fairly complex unpack_trees() semantic requirements, including
+ * the skipping, the path matching, the type conflict cases etc.
+ */
+static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
+{
+	struct cache_entry *idx = src[0];
+	struct cache_entry *tree = src[1];
+	struct oneway_unpack_data *cbdata = o->unpack_data;
+	struct rev_info *revs = cbdata->revs;
+
+	if (idx && ce_stage(idx))
+		skip_same_name(idx, o);
+
+	/*
+	 * Unpack-trees generates a DF/conflict entry if
+	 * there was a directory in the index and a tree
+	 * in the tree. From a diff standpoint, that's a
+	 * delete of the tree and a create of the file.
+	 */
+	if (tree == o->df_conflict_entry)
+		tree = NULL;
+
+	if (ce_path_match(idx ? idx : tree, revs->prune_data))
+		do_oneway_diff(o, idx, tree);
+
+	return 0;
+}
+
 int run_diff_index(struct rev_info *revs, int cached)
 {
-	int ret;
 	struct object *ent;
 	struct tree *tree;
 	const char *tree_name;
-	int match_missing = 0;
-
-	/*
-	 * Backward compatibility wart - "diff-index -m" does
-	 * not mean "do not ignore merges", but totally different.
-	 */
-	if (!revs->ignore_merges)
-		match_missing = 1;
+	struct unpack_trees_options opts;
+	struct tree_desc t;
+	struct oneway_unpack_data unpack_cb;
 
 	mark_merge_entries();
 
@@ -690,13 +751,25 @@
 	tree = parse_tree_indirect(ent->sha1);
 	if (!tree)
 		return error("bad tree object %s", tree_name);
-	if (read_tree(tree, 1, revs->prune_data))
-		return error("unable to read tree object %s", tree_name);
-	ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
-			 cached, match_missing);
+
+	unpack_cb.revs = revs;
+	unpack_cb.symcache[0] = '\0';
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.index_only = cached;
+	opts.merge = 1;
+	opts.fn = oneway_diff;
+	opts.unpack_data = &unpack_cb;
+	opts.src_index = &the_index;
+	opts.dst_index = NULL;
+
+	init_tree_desc(&t, tree->buffer, tree->size);
+	if (unpack_trees(1, &t, &opts))
+		exit(128);
+
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
-	return ret;
+	return 0;
 }
 
 int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
@@ -706,6 +779,9 @@
 	int i;
 	struct cache_entry **dst;
 	struct cache_entry *last = NULL;
+	struct unpack_trees_options opts;
+	struct tree_desc t;
+	struct oneway_unpack_data unpack_cb;
 
 	/*
 	 * This is used by git-blame to run diff-cache internally;
@@ -722,8 +798,7 @@
 			cache_tree_invalidate_path(active_cache_tree,
 						   ce->name);
 			last = ce;
-			ce->ce_mode = 0;
-			ce->ce_flags &= ~htons(CE_STAGEMASK);
+			ce->ce_flags |= CE_REMOVE;
 		}
 		*dst++ = ce;
 	}
@@ -734,8 +809,20 @@
 	tree = parse_tree_indirect(tree_sha1);
 	if (!tree)
 		die("bad tree object %s", sha1_to_hex(tree_sha1));
-	if (read_tree(tree, 1, opt->paths))
-		return error("unable to read tree %s", sha1_to_hex(tree_sha1));
-	return diff_cache(&revs, active_cache, active_nr, revs.prune_data,
-			  1, 0);
+
+	unpack_cb.revs = &revs;
+	unpack_cb.symcache[0] = '\0';
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 1;
+	opts.index_only = 1;
+	opts.merge = 1;
+	opts.fn = oneway_diff;
+	opts.unpack_data = &unpack_cb;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	init_tree_desc(&t, tree->buffer, tree->size);
+	if (unpack_trees(1, &t, &opts))
+		exit(128);
+	return 0;
 }
diff --git a/diff.c b/diff.c
index 76ba5f4..20135cb 100644
--- a/diff.c
+++ b/diff.c
@@ -20,7 +20,7 @@
 
 static int diff_detect_rename_default;
 static int diff_rename_limit_default = 100;
-static int diff_use_color_default;
+int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 
@@ -118,8 +118,7 @@
 		pp->next = funcname_pattern_list;
 		funcname_pattern_list = pp;
 	}
-	if (pp->pattern)
-		free(pp->pattern);
+	free(pp->pattern);
 	pp->pattern = xstrdup(value);
 	return 0;
 }
@@ -191,7 +190,7 @@
 		}
 	}
 
-	return git_default_config(var, value);
+	return git_color_default_config(var, value);
 }
 
 static char *quote_two(const char *one, const char *two)
@@ -257,40 +256,41 @@
 	return count;
 }
 
-static void print_line_count(int count)
+static void print_line_count(FILE *file, int count)
 {
 	switch (count) {
 	case 0:
-		printf("0,0");
+		fprintf(file, "0,0");
 		break;
 	case 1:
-		printf("1");
+		fprintf(file, "1");
 		break;
 	default:
-		printf("1,%d", count);
+		fprintf(file, "1,%d", count);
 		break;
 	}
 }
 
-static void copy_file_with_prefix(int prefix, const char *data, int size,
+static void copy_file_with_prefix(FILE *file,
+				  int prefix, const char *data, int size,
 				  const char *set, const char *reset)
 {
 	int ch, nl_just_seen = 1;
 	while (0 < size--) {
 		ch = *data++;
 		if (nl_just_seen) {
-			fputs(set, stdout);
-			putchar(prefix);
+			fputs(set, file);
+			putc(prefix, file);
 		}
 		if (ch == '\n') {
 			nl_just_seen = 1;
-			fputs(reset, stdout);
+			fputs(reset, file);
 		} else
 			nl_just_seen = 0;
-		putchar(ch);
+		putc(ch, file);
 	}
 	if (!nl_just_seen)
-		printf("%s\n\\ No newline at end of file\n", reset);
+		fprintf(file, "%s\n\\ No newline at end of file\n", reset);
 }
 
 static void emit_rewrite_diff(const char *name_a,
@@ -323,17 +323,18 @@
 	diff_populate_filespec(two, 0);
 	lc_a = count_lines(one->data, one->size);
 	lc_b = count_lines(two->data, two->size);
-	printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
-	       metainfo, a_name.buf, name_a_tab, reset,
-	       metainfo, b_name.buf, name_b_tab, reset, fraginfo);
-	print_line_count(lc_a);
-	printf(" +");
-	print_line_count(lc_b);
-	printf(" @@%s\n", reset);
+	fprintf(o->file,
+		"%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
+		metainfo, a_name.buf, name_a_tab, reset,
+		metainfo, b_name.buf, name_b_tab, reset, fraginfo);
+	print_line_count(o->file, lc_a);
+	fprintf(o->file, " +");
+	print_line_count(o->file, lc_b);
+	fprintf(o->file, " @@%s\n", reset);
 	if (lc_a)
-		copy_file_with_prefix('-', one->data, one->size, old, reset);
+		copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset);
 	if (lc_b)
-		copy_file_with_prefix('+', two->data, two->size, new, reset);
+		copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset);
 }
 
 static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@ -373,9 +374,10 @@
 struct diff_words_data {
 	struct xdiff_emit_state xm;
 	struct diff_words_buffer minus, plus;
+	FILE *file;
 };
 
-static void print_word(struct diff_words_buffer *buffer, int len, int color,
+static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
 		int suppress_newline)
 {
 	const char *ptr;
@@ -392,15 +394,15 @@
 		len--;
 	}
 
-	fputs(diff_get_color(1, color), stdout);
-	fwrite(ptr, len, 1, stdout);
-	fputs(diff_get_color(1, DIFF_RESET), stdout);
+	fputs(diff_get_color(1, color), file);
+	fwrite(ptr, len, 1, file);
+	fputs(diff_get_color(1, DIFF_RESET), file);
 
 	if (eol) {
 		if (suppress_newline)
 			buffer->suppressed_newline = 1;
 		else
-			putchar('\n');
+			putc('\n', file);
 	}
 }
 
@@ -410,20 +412,23 @@
 
 	if (diff_words->minus.suppressed_newline) {
 		if (line[0] != '+')
-			putchar('\n');
+			putc('\n', diff_words->file);
 		diff_words->minus.suppressed_newline = 0;
 	}
 
 	len--;
 	switch (line[0]) {
 		case '-':
-			print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
+			print_word(diff_words->file,
+				   &diff_words->minus, len, DIFF_FILE_OLD, 1);
 			break;
 		case '+':
-			print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
+			print_word(diff_words->file,
+				   &diff_words->plus, len, DIFF_FILE_NEW, 0);
 			break;
 		case ' ':
-			print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
+			print_word(diff_words->file,
+				   &diff_words->plus, len, DIFF_PLAIN, 0);
 			diff_words->minus.current += len;
 			break;
 	}
@@ -467,7 +472,7 @@
 	diff_words->minus.text.size = diff_words->plus.text.size = 0;
 
 	if (diff_words->minus.suppressed_newline) {
-		putchar('\n');
+		putc('\n', diff_words->file);
 		diff_words->minus.suppressed_newline = 0;
 	}
 }
@@ -482,6 +487,7 @@
 	const char **label_path;
 	struct diff_words_data *diff_words;
 	int *found_changesp;
+	FILE *file;
 };
 
 static void free_diff_words_data(struct emit_callback *ecbdata)
@@ -492,10 +498,8 @@
 				ecbdata->diff_words->plus.text.size)
 			diff_words_show(ecbdata->diff_words);
 
-		if (ecbdata->diff_words->minus.text.ptr)
-			free (ecbdata->diff_words->minus.text.ptr);
-		if (ecbdata->diff_words->plus.text.ptr)
-			free (ecbdata->diff_words->plus.text.ptr);
+		free (ecbdata->diff_words->minus.text.ptr);
+		free (ecbdata->diff_words->plus.text.ptr);
 		free(ecbdata->diff_words);
 		ecbdata->diff_words = NULL;
 	}
@@ -508,11 +512,17 @@
 	return "";
 }
 
-static void emit_line(const char *set, const char *reset, const char *line, int len)
+static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
 {
-	fputs(set, stdout);
-	fwrite(line, len, 1, stdout);
-	fputs(reset, stdout);
+	int has_trailing_newline = (len > 0 && line[len-1] == '\n');
+	if (has_trailing_newline)
+		len--;
+
+	fputs(set, file);
+	fwrite(line, len, 1, file);
+	fputs(reset, file);
+	if (has_trailing_newline)
+		fputc('\n', file);
 }
 
 static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
@@ -521,13 +531,13 @@
 	const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 
 	if (!*ws)
-		emit_line(set, reset, line, len);
+		emit_line(ecbdata->file, set, reset, line, len);
 	else {
 		/* Emit just the prefix, then the rest. */
-		emit_line(set, reset, line, ecbdata->nparents);
+		emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
 		(void)check_and_emit_line(line + ecbdata->nparents,
 		    len - ecbdata->nparents, ecbdata->ws_rule,
-		    stdout, set, reset, ws);
+		    ecbdata->file, set, reset, ws);
 	}
 }
 
@@ -566,10 +576,10 @@
 		name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
 		name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-		printf("%s--- %s%s%s\n",
-		       meta, ecbdata->label_path[0], reset, name_a_tab);
-		printf("%s+++ %s%s%s\n",
-		       meta, ecbdata->label_path[1], reset, name_b_tab);
+		fprintf(ecbdata->file, "%s--- %s%s%s\n",
+			meta, ecbdata->label_path[0], reset, name_a_tab);
+		fprintf(ecbdata->file, "%s+++ %s%s%s\n",
+			meta, ecbdata->label_path[1], reset, name_b_tab);
 		ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
 	}
 
@@ -581,15 +591,16 @@
 	if (2 <= i && i < len && line[i] == ' ') {
 		ecbdata->nparents = i - 1;
 		len = sane_truncate_line(ecbdata, line, len);
-		emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
+		emit_line(ecbdata->file,
+			  diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
 			  reset, line, len);
 		if (line[len-1] != '\n')
-			putchar('\n');
+			putc('\n', ecbdata->file);
 		return;
 	}
 
 	if (len < ecbdata->nparents) {
-		emit_line(reset, reset, line, len);
+		emit_line(ecbdata->file, reset, reset, line, len);
 		return;
 	}
 
@@ -612,7 +623,7 @@
 			diff_words_show(ecbdata->diff_words);
 		line++;
 		len--;
-		emit_line(plain, reset, line, len);
+		emit_line(ecbdata->file, plain, reset, line, len);
 		return;
 	}
 	for (i = 0; i < ecbdata->nparents && len; i++) {
@@ -623,7 +634,8 @@
 	}
 
 	if (color != DIFF_FILE_NEW) {
-		emit_line(diff_get_color(ecbdata->color_diff, color),
+		emit_line(ecbdata->file,
+			  diff_get_color(ecbdata->color_diff, color),
 			  reset, line, len);
 		return;
 	}
@@ -762,20 +774,21 @@
 	return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
 }
 
-static void show_name(const char *prefix, const char *name, int len,
+static void show_name(FILE *file,
+		      const char *prefix, const char *name, int len,
 		      const char *reset, const char *set)
 {
-	printf(" %s%s%-*s%s |", set, prefix, len, name, reset);
+	fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset);
 }
 
-static void show_graph(char ch, int cnt, const char *set, const char *reset)
+static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset)
 {
 	if (cnt <= 0)
 		return;
-	printf("%s", set);
+	fprintf(file, "%s", set);
 	while (cnt--)
-		putchar(ch);
-	printf("%s", reset);
+		putc(ch, file);
+	fprintf(file, "%s", reset);
 }
 
 static void fill_print_name(struct diffstat_file *file)
@@ -880,18 +893,18 @@
 		}
 
 		if (data->files[i]->is_binary) {
-			show_name(prefix, name, len, reset, set);
-			printf("  Bin ");
-			printf("%s%d%s", del_c, deleted, reset);
-			printf(" -> ");
-			printf("%s%d%s", add_c, added, reset);
-			printf(" bytes");
-			printf("\n");
+			show_name(options->file, prefix, name, len, reset, set);
+			fprintf(options->file, "  Bin ");
+			fprintf(options->file, "%s%d%s", del_c, deleted, reset);
+			fprintf(options->file, " -> ");
+			fprintf(options->file, "%s%d%s", add_c, added, reset);
+			fprintf(options->file, " bytes");
+			fprintf(options->file, "\n");
 			continue;
 		}
 		else if (data->files[i]->is_unmerged) {
-			show_name(prefix, name, len, reset, set);
-			printf("  Unmerged\n");
+			show_name(options->file, prefix, name, len, reset, set);
+			fprintf(options->file, "  Unmerged\n");
 			continue;
 		}
 		else if (!data->files[i]->is_renamed &&
@@ -914,17 +927,18 @@
 			del = scale_linear(del, width, max_change);
 			total = add + del;
 		}
-		show_name(prefix, name, len, reset, set);
-		printf("%5d ", added + deleted);
-		show_graph('+', add, add_c, reset);
-		show_graph('-', del, del_c, reset);
-		putchar('\n');
+		show_name(options->file, prefix, name, len, reset, set);
+		fprintf(options->file, "%5d ", added + deleted);
+		show_graph(options->file, '+', add, add_c, reset);
+		show_graph(options->file, '-', del, del_c, reset);
+		fprintf(options->file, "\n");
 	}
-	printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
+	fprintf(options->file,
+	       "%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
 	       set, total_files, adds, dels, reset);
 }
 
-static void show_shortstats(struct diffstat_t* data)
+static void show_shortstats(struct diffstat_t* data, struct diff_options *options)
 {
 	int i, adds = 0, dels = 0, total_files = data->nr;
 
@@ -945,7 +959,7 @@
 			}
 		}
 	}
-	printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+	fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
 	       total_files, adds, dels);
 }
 
@@ -960,28 +974,113 @@
 		struct diffstat_file *file = data->files[i];
 
 		if (file->is_binary)
-			printf("-\t-\t");
+			fprintf(options->file, "-\t-\t");
 		else
-			printf("%d\t%d\t", file->added, file->deleted);
+			fprintf(options->file,
+				"%d\t%d\t", file->added, file->deleted);
 		if (options->line_termination) {
 			fill_print_name(file);
 			if (!file->is_renamed)
-				write_name_quoted(file->name, stdout,
+				write_name_quoted(file->name, options->file,
 						  options->line_termination);
 			else {
-				fputs(file->print_name, stdout);
-				putchar(options->line_termination);
+				fputs(file->print_name, options->file);
+				putc(options->line_termination, options->file);
 			}
 		} else {
 			if (file->is_renamed) {
-				putchar('\0');
-				write_name_quoted(file->from_name, stdout, '\0');
+				putc('\0', options->file);
+				write_name_quoted(file->from_name, options->file, '\0');
 			}
-			write_name_quoted(file->name, stdout, '\0');
+			write_name_quoted(file->name, options->file, '\0');
 		}
 	}
 }
 
+struct diffstat_dir {
+	struct diffstat_file **files;
+	int nr, percent, cumulative;
+};
+
+static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
+{
+	unsigned long this_dir = 0;
+	unsigned int sources = 0;
+
+	while (dir->nr) {
+		struct diffstat_file *f = *dir->files;
+		int namelen = strlen(f->name);
+		unsigned long this;
+		char *slash;
+
+		if (namelen < baselen)
+			break;
+		if (memcmp(f->name, base, baselen))
+			break;
+		slash = strchr(f->name + baselen, '/');
+		if (slash) {
+			int newbaselen = slash + 1 - f->name;
+			this = gather_dirstat(file, dir, changed, f->name, newbaselen);
+			sources++;
+		} else {
+			if (f->is_unmerged || f->is_binary)
+				this = 0;
+			else
+				this = f->added + f->deleted;
+			dir->files++;
+			dir->nr--;
+			sources += 2;
+		}
+		this_dir += this;
+	}
+
+	/*
+	 * We don't report dirstat's for
+	 *  - the top level
+	 *  - or cases where everything came from a single directory
+	 *    under this directory (sources == 1).
+	 */
+	if (baselen && sources != 1) {
+		int permille = this_dir * 1000 / changed;
+		if (permille) {
+			int percent = permille / 10;
+			if (percent >= dir->percent) {
+				fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+				if (!dir->cumulative)
+					return 0;
+			}
+		}
+	}
+	return this_dir;
+}
+
+static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
+{
+	int i;
+	unsigned long changed;
+	struct diffstat_dir dir;
+
+	/* Calculate total changes */
+	changed = 0;
+	for (i = 0; i < data->nr; i++) {
+		if (data->files[i]->is_binary || data->files[i]->is_unmerged)
+			continue;
+		changed += data->files[i]->added;
+		changed += data->files[i]->deleted;
+	}
+
+	/* This can happen even with many files, if everything was renames */
+	if (!changed)
+		return;
+
+	/* Show all directories with more than x% of the changes */
+	dir.files = data->files;
+	dir.nr = data->nr;
+	dir.percent = options->dirstat_percent;
+	dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
+	gather_dirstat(options->file, &dir, changed, "", 0);
+}
+
 static void free_diffstat_info(struct diffstat_t *diffstat)
 {
 	int i;
@@ -1002,6 +1101,7 @@
 	int lineno, color_diff;
 	unsigned ws_rule;
 	unsigned status;
+	FILE *file;
 };
 
 static void checkdiff_consume(void *priv, char *line, unsigned long len)
@@ -1019,11 +1119,11 @@
 		if (!data->status)
 			return;
 		err = whitespace_error_string(data->status);
-		printf("%s:%d: %s.\n", data->filename, data->lineno, err);
+		fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err);
 		free(err);
-		emit_line(set, reset, line, 1);
+		emit_line(data->file, set, reset, line, 1);
 		(void)check_and_emit_line(line + 1, len - 1, data->ws_rule,
-		    stdout, set, reset, ws);
+		    data->file, set, reset, ws);
 	} else if (line[0] == ' ')
 		data->lineno++;
 	else if (line[0] == '@') {
@@ -1059,7 +1159,7 @@
 	return deflated;
 }
 
-static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
 {
 	void *cp;
 	void *delta;
@@ -1088,13 +1188,13 @@
 	}
 
 	if (delta && delta_size < deflate_size) {
-		printf("delta %lu\n", orig_size);
+		fprintf(file, "delta %lu\n", orig_size);
 		free(deflated);
 		data = delta;
 		data_size = delta_size;
 	}
 	else {
-		printf("literal %lu\n", two->size);
+		fprintf(file, "literal %lu\n", two->size);
 		free(delta);
 		data = deflated;
 		data_size = deflate_size;
@@ -1112,17 +1212,18 @@
 			line[0] = bytes - 26 + 'a' - 1;
 		encode_85(line + 1, cp, bytes);
 		cp = (char *) cp + bytes;
-		puts(line);
+		fputs(line, file);
+		fputc('\n', file);
 	}
-	printf("\n");
+	fprintf(file, "\n");
 	free(data);
 }
 
-static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
 {
-	printf("GIT binary patch\n");
-	emit_binary_diff_body(one, two);
-	emit_binary_diff_body(two, one);
+	fprintf(file, "GIT binary patch\n");
+	emit_binary_diff_body(file, one, two);
+	emit_binary_diff_body(file, two, one);
 }
 
 static void setup_diff_attr_check(struct git_attr_check *check)
@@ -1199,7 +1300,7 @@
 			"new\\|return\\|switch\\|throw\\|while\\)\n"
 			"^[ 	]*\\(\\([ 	]*"
 			"[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
-			"[ 	]*([^;]*$\\)" },
+			"[ 	]*([^;]*\\)$" },
 	{ "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
 };
 
@@ -1253,25 +1354,25 @@
 	b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
 	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
 	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-	printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+	fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
 	if (lbl[0][0] == '/') {
 		/* /dev/null */
-		printf("%snew file mode %06o%s\n", set, two->mode, reset);
+		fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset);
 		if (xfrm_msg && xfrm_msg[0])
-			printf("%s%s%s\n", set, xfrm_msg, reset);
+			fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
 	}
 	else if (lbl[1][0] == '/') {
-		printf("%sdeleted file mode %06o%s\n", set, one->mode, reset);
+		fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
 		if (xfrm_msg && xfrm_msg[0])
-			printf("%s%s%s\n", set, xfrm_msg, reset);
+			fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
 	}
 	else {
 		if (one->mode != two->mode) {
-			printf("%sold mode %06o%s\n", set, one->mode, reset);
-			printf("%snew mode %06o%s\n", set, two->mode, reset);
+			fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset);
+			fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset);
 		}
 		if (xfrm_msg && xfrm_msg[0])
-			printf("%s%s%s\n", set, xfrm_msg, reset);
+			fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
 		/*
 		 * we do not run diff between different kind
 		 * of objects.
@@ -1295,10 +1396,10 @@
 		    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
 			goto free_ab_and_return;
 		if (DIFF_OPT_TST(o, BINARY))
-			emit_binary_diff(&mf1, &mf2);
+			emit_binary_diff(o->file, &mf1, &mf2);
 		else
-			printf("Binary files %s and %s differ\n",
-			       lbl[0], lbl[1]);
+			fprintf(o->file, "Binary files %s and %s differ\n",
+				lbl[0], lbl[1]);
 		o->found_changes = 1;
 	}
 	else {
@@ -1320,6 +1421,7 @@
 		ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
 		ecbdata.found_changesp = &o->found_changes;
 		ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+		ecbdata.file = o->file;
 		xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
 		xecfg.ctxlen = o->context;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1334,9 +1436,11 @@
 		ecb.outf = xdiff_outf;
 		ecb.priv = &ecbdata;
 		ecbdata.xm.consume = fn_out_consume;
-		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
+		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
 			ecbdata.diff_words =
 				xcalloc(1, sizeof(struct diff_words_data));
+			ecbdata.diff_words->file = o->file;
+		}
 		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
 			free_diff_words_data(&ecbdata);
@@ -1399,6 +1503,7 @@
 }
 
 static void builtin_checkdiff(const char *name_a, const char *name_b,
+			      const char *attr_path,
 			     struct diff_filespec *one,
 			     struct diff_filespec *two, struct diff_options *o)
 {
@@ -1413,7 +1518,8 @@
 	data.filename = name_b ? name_b : name_a;
 	data.lineno = 0;
 	data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
-	data.ws_rule = whitespace_rule(data.filename);
+	data.ws_rule = whitespace_rule(attr_path);
+	data.file = o->file;
 
 	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 		die("unable to read files to diff");
@@ -1512,17 +1618,22 @@
 	if (pos < 0)
 		return 0;
 	ce = active_cache[pos];
-	if ((lstat(name, &st) < 0) ||
-	    !S_ISREG(st.st_mode) || /* careful! */
-	    ce_match_stat(ce, &st, 0) ||
-	    hashcmp(sha1, ce->sha1))
-		return 0;
-	/* we return 1 only when we can stat, it is a regular file,
-	 * stat information matches, and sha1 recorded in the cache
-	 * matches.  I.e. we know the file in the work tree really is
-	 * the same as the <name, sha1> pair.
+
+	/*
+	 * This is not the sha1 we are looking for, or
+	 * unreusable because it is not a regular file.
 	 */
-	return 1;
+	if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
+		return 0;
+
+	/*
+	 * If ce matches the file in the work tree, we can reuse it.
+	 */
+	if (ce_uptodate(ce) ||
+	    (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+		return 1;
+
+	return 0;
 }
 
 static int populate_from_stdin(struct diff_filespec *s)
@@ -1626,7 +1737,7 @@
 		 * Convert from working tree format to canonical git format
 		 */
 		strbuf_init(&buf, 0);
-		if (convert_to_git(s->path, s->data, s->size, &buf)) {
+		if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
 			size_t size = 0;
 			munmap(s->data, s->size);
 			s->should_munmap = 0;
@@ -1833,6 +1944,9 @@
 {
 	struct git_attr_check attr_diff_check;
 
+	if (!name)
+		return NULL;
+
 	setup_diff_attr_check(&attr_diff_check);
 	if (!git_checkattr(name, 1, &attr_diff_check)) {
 		const char *value = attr_diff_check.value;
@@ -1852,6 +1966,7 @@
 static void run_diff_cmd(const char *pgm,
 			 const char *name,
 			 const char *other,
+			 const char *attr_path,
 			 struct diff_filespec *one,
 			 struct diff_filespec *two,
 			 const char *xfrm_msg,
@@ -1861,7 +1976,7 @@
 	if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
 		pgm = NULL;
 	else {
-		const char *cmd = external_diff_attr(name);
+		const char *cmd = external_diff_attr(attr_path);
 		if (cmd)
 			pgm = cmd;
 	}
@@ -1875,7 +1990,7 @@
 		builtin_diff(name, other ? other : name,
 			     one, two, xfrm_msg, o, complete_rewrite);
 	else
-		printf("* Unmerged path %s\n", name);
+		fprintf(o->file, "* Unmerged path %s\n", name);
 }
 
 static void diff_fill_sha1_info(struct diff_filespec *one)
@@ -1902,6 +2017,15 @@
 	return p->score * 100 / MAX_SCORE;
 }
 
+static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
+{
+	/* Strip the prefix but do not molest /dev/null and absolute paths */
+	if (*namep && **namep != '/')
+		*namep += prefix_length;
+	if (*otherp && **otherp != '/')
+		*otherp += prefix_length;
+}
+
 static void run_diff(struct diff_filepair *p, struct diff_options *o)
 {
 	const char *pgm = external_diff();
@@ -1911,16 +2035,21 @@
 	struct diff_filespec *two = p->two;
 	const char *name;
 	const char *other;
+	const char *attr_path;
 	int complete_rewrite = 0;
 
-
-	if (DIFF_PAIR_UNMERGED(p)) {
-		run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
-		return;
-	}
-
 	name  = p->one->path;
 	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+	attr_path = name;
+	if (o->prefix_length)
+		strip_prefix(o->prefix_length, &name, &other);
+
+	if (DIFF_PAIR_UNMERGED(p)) {
+		run_diff_cmd(pgm, name, NULL, attr_path,
+			     NULL, NULL, NULL, o, 0);
+		return;
+	}
+
 	diff_fill_sha1_info(one);
 	diff_fill_sha1_info(two);
 
@@ -1983,15 +2112,17 @@
 		 * needs to be split into deletion and creation.
 		 */
 		struct diff_filespec *null = alloc_filespec(two->path);
-		run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
+		run_diff_cmd(NULL, name, other, attr_path,
+			     one, null, xfrm_msg, o, 0);
 		free(null);
 		null = alloc_filespec(one->path);
-		run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
+		run_diff_cmd(NULL, name, other, attr_path,
+			     null, two, xfrm_msg, o, 0);
 		free(null);
 	}
 	else
-		run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
-			     complete_rewrite);
+		run_diff_cmd(pgm, name, other, attr_path,
+			     one, two, xfrm_msg, o, complete_rewrite);
 
 	strbuf_release(&msg);
 }
@@ -2012,6 +2143,9 @@
 	name = p->one->path;
 	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
 
+	if (o->prefix_length)
+		strip_prefix(o->prefix_length, &name, &other);
+
 	diff_fill_sha1_info(p->one);
 	diff_fill_sha1_info(p->two);
 
@@ -2024,6 +2158,7 @@
 {
 	const char *name;
 	const char *other;
+	const char *attr_path;
 
 	if (DIFF_PAIR_UNMERGED(p)) {
 		/* unmerged */
@@ -2032,25 +2167,33 @@
 
 	name = p->one->path;
 	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+	attr_path = other ? other : name;
+
+	if (o->prefix_length)
+		strip_prefix(o->prefix_length, &name, &other);
 
 	diff_fill_sha1_info(p->one);
 	diff_fill_sha1_info(p->two);
 
-	builtin_checkdiff(name, other, p->one, p->two, o);
+	builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
 }
 
 void diff_setup(struct diff_options *options)
 {
 	memset(options, 0, sizeof(*options));
+
+	options->file = stdout;
+
 	options->line_termination = '\n';
 	options->break_opt = -1;
 	options->rename_limit = -1;
+	options->dirstat_percent = 3;
 	options->context = 3;
 	options->msg_sep = "";
 
 	options->change = diff_change;
 	options->add_remove = diff_addremove;
-	if (diff_use_color_default)
+	if (diff_use_color_default > 0)
 		DIFF_OPT_SET(options, COLOR_DIFF);
 	else
 		DIFF_OPT_CLR(options, COLOR_DIFF);
@@ -2078,6 +2221,13 @@
 	if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
 		options->detect_rename = DIFF_DETECT_COPY;
 
+	if (!DIFF_OPT_TST(options, RELATIVE_NAME))
+		options->prefix = NULL;
+	if (options->prefix)
+		options->prefix_length = strlen(options->prefix);
+	else
+		options->prefix_length = 0;
+
 	if (options->output_format & (DIFF_FORMAT_NAME |
 				      DIFF_FORMAT_NAME_STATUS |
 				      DIFF_FORMAT_CHECKDIFF |
@@ -2086,6 +2236,7 @@
 					    DIFF_FORMAT_NUMSTAT |
 					    DIFF_FORMAT_DIFFSTAT |
 					    DIFF_FORMAT_SHORTSTAT |
+					    DIFF_FORMAT_DIRSTAT |
 					    DIFF_FORMAT_SUMMARY |
 					    DIFF_FORMAT_PATCH);
 
@@ -2097,6 +2248,7 @@
 				      DIFF_FORMAT_NUMSTAT |
 				      DIFF_FORMAT_DIFFSTAT |
 				      DIFF_FORMAT_SHORTSTAT |
+				      DIFF_FORMAT_DIRSTAT |
 				      DIFF_FORMAT_SUMMARY |
 				      DIFF_FORMAT_CHECKDIFF))
 		DIFF_OPT_SET(options, RECURSIVE);
@@ -2207,6 +2359,10 @@
 		options->output_format |= DIFF_FORMAT_NUMSTAT;
 	else if (!strcmp(arg, "--shortstat"))
 		options->output_format |= DIFF_FORMAT_SHORTSTAT;
+	else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
+		options->output_format |= DIFF_FORMAT_DIRSTAT;
+	else if (!strcmp(arg, "--cumulative"))
+		options->output_format |= DIFF_FORMAT_CUMULATIVE;
 	else if (!strcmp(arg, "--check"))
 		options->output_format |= DIFF_FORMAT_CHECKDIFF;
 	else if (!strcmp(arg, "--summary"))
@@ -2266,6 +2422,12 @@
 	}
 	else if (!strcmp(arg, "--no-renames"))
 		options->detect_rename = 0;
+	else if (!strcmp(arg, "--relative"))
+		DIFF_OPT_SET(options, RELATIVE_NAME);
+	else if (!prefixcmp(arg, "--relative=")) {
+		DIFF_OPT_SET(options, RELATIVE_NAME);
+		options->prefix = arg + 11;
+	}
 
 	/* xdiff options */
 	else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
@@ -2335,7 +2497,10 @@
 		options->b_prefix = arg + 13;
 	else if (!strcmp(arg, "--no-prefix"))
 		options->a_prefix = options->b_prefix = "";
-	else
+	else if (!prefixcmp(arg, "--output=")) {
+		options->file = fopen(arg + strlen("--output="), "w");
+		options->close_file = 1;
+	} else
 		return 0;
 	return 1;
 }
@@ -2446,8 +2611,6 @@
 		return sha1_to_hex(sha1);
 
 	abbrev = find_unique_abbrev(sha1, len);
-	if (!abbrev)
-		return sha1_to_hex(sha1);
 	abblen = strlen(abbrev);
 	if (abblen < 37) {
 		static char hex[41];
@@ -2466,23 +2629,31 @@
 	int inter_name_termination = line_termination ? '\t' : '\0';
 
 	if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
-		printf(":%06o %06o %s ", p->one->mode, p->two->mode,
-		       diff_unique_abbrev(p->one->sha1, opt->abbrev));
-		printf("%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
+		fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
+			diff_unique_abbrev(p->one->sha1, opt->abbrev));
+		fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
 	}
 	if (p->score) {
-		printf("%c%03d%c", p->status, similarity_index(p),
-			   inter_name_termination);
+		fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
+			inter_name_termination);
 	} else {
-		printf("%c%c", p->status, inter_name_termination);
+		fprintf(opt->file, "%c%c", p->status, inter_name_termination);
 	}
 
-	if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
-		write_name_quoted(p->one->path, stdout, inter_name_termination);
-		write_name_quoted(p->two->path, stdout, line_termination);
+	if (p->status == DIFF_STATUS_COPIED ||
+	    p->status == DIFF_STATUS_RENAMED) {
+		const char *name_a, *name_b;
+		name_a = p->one->path;
+		name_b = p->two->path;
+		strip_prefix(opt->prefix_length, &name_a, &name_b);
+		write_name_quoted(name_a, opt->file, inter_name_termination);
+		write_name_quoted(name_b, opt->file, line_termination);
 	} else {
-		const char *path = p->one->mode ? p->one->path : p->two->path;
-		write_name_quoted(path, stdout, line_termination);
+		const char *name_a, *name_b;
+		name_a = p->one->mode ? p->one->path : p->two->path;
+		name_b = NULL;
+		strip_prefix(opt->prefix_length, &name_a, &name_b);
+		write_name_quoted(name_a, opt->file, line_termination);
 	}
 }
 
@@ -2679,62 +2850,67 @@
 		diff_flush_checkdiff(p, opt);
 	else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
 		diff_flush_raw(p, opt);
-	else if (fmt & DIFF_FORMAT_NAME)
-		write_name_quoted(p->two->path, stdout, opt->line_termination);
+	else if (fmt & DIFF_FORMAT_NAME) {
+		const char *name_a, *name_b;
+		name_a = p->two->path;
+		name_b = NULL;
+		strip_prefix(opt->prefix_length, &name_a, &name_b);
+		write_name_quoted(name_a, opt->file, opt->line_termination);
+	}
 }
 
-static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
+static void show_file_mode_name(FILE *file, const char *newdelete, struct diff_filespec *fs)
 {
 	if (fs->mode)
-		printf(" %s mode %06o ", newdelete, fs->mode);
+		fprintf(file, " %s mode %06o ", newdelete, fs->mode);
 	else
-		printf(" %s ", newdelete);
-	write_name_quoted(fs->path, stdout, '\n');
+		fprintf(file, " %s ", newdelete);
+	write_name_quoted(fs->path, file, '\n');
 }
 
 
-static void show_mode_change(struct diff_filepair *p, int show_name)
+static void show_mode_change(FILE *file, struct diff_filepair *p, int show_name)
 {
 	if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
-		printf(" mode change %06o => %06o%c", p->one->mode, p->two->mode,
+		fprintf(file, " mode change %06o => %06o%c", p->one->mode, p->two->mode,
 			show_name ? ' ' : '\n');
 		if (show_name) {
-			write_name_quoted(p->two->path, stdout, '\n');
+			write_name_quoted(p->two->path, file, '\n');
 		}
 	}
 }
 
-static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
+static void show_rename_copy(FILE *file, const char *renamecopy, struct diff_filepair *p)
 {
 	char *names = pprint_rename(p->one->path, p->two->path);
 
-	printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
+	fprintf(file, " %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
 	free(names);
-	show_mode_change(p, 0);
+	show_mode_change(file, p, 0);
 }
 
-static void diff_summary(struct diff_filepair *p)
+static void diff_summary(FILE *file, struct diff_filepair *p)
 {
 	switch(p->status) {
 	case DIFF_STATUS_DELETED:
-		show_file_mode_name("delete", p->one);
+		show_file_mode_name(file, "delete", p->one);
 		break;
 	case DIFF_STATUS_ADDED:
-		show_file_mode_name("create", p->two);
+		show_file_mode_name(file, "create", p->two);
 		break;
 	case DIFF_STATUS_COPIED:
-		show_rename_copy("copy", p);
+		show_rename_copy(file, "copy", p);
 		break;
 	case DIFF_STATUS_RENAMED:
-		show_rename_copy("rename", p);
+		show_rename_copy(file, "rename", p);
 		break;
 	default:
 		if (p->score) {
-			fputs(" rewrite ", stdout);
-			write_name_quoted(p->two->path, stdout, ' ');
-			printf("(%d%%)\n", similarity_index(p));
+			fputs(" rewrite ", file);
+			write_name_quoted(p->two->path, file, ' ');
+			fprintf(file, "(%d%%)\n", similarity_index(p));
 		}
-		show_mode_change(p, !p->score);
+		show_mode_change(file, p, !p->score);
 		break;
 	}
 }
@@ -2925,7 +3101,7 @@
 		separator++;
 	}
 
-	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
+	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
 		struct diffstat_t diffstat;
 
 		memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -2935,19 +3111,21 @@
 			if (check_pair_status(p))
 				diff_flush_stat(p, options, &diffstat);
 		}
+		if (output_format & DIFF_FORMAT_DIRSTAT)
+			show_dirstat(&diffstat, options);
 		if (output_format & DIFF_FORMAT_NUMSTAT)
 			show_numstat(&diffstat, options);
 		if (output_format & DIFF_FORMAT_DIFFSTAT)
 			show_stats(&diffstat, options);
 		if (output_format & DIFF_FORMAT_SHORTSTAT)
-			show_shortstats(&diffstat);
+			show_shortstats(&diffstat, options);
 		free_diffstat_info(&diffstat);
 		separator++;
 	}
 
 	if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
 		for (i = 0; i < q->nr; i++)
-			diff_summary(q->queue[i]);
+			diff_summary(options->file, q->queue[i]);
 		separator++;
 	}
 
@@ -2955,9 +3133,9 @@
 		if (separator) {
 			if (options->stat_sep) {
 				/* attach patch instead of inline */
-				fputs(options->stat_sep, stdout);
+				fputs(options->stat_sep, options->file);
 			} else {
-				putchar(options->line_termination);
+				putc(options->line_termination, options->file);
 			}
 		}
 
@@ -2977,6 +3155,8 @@
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
+	if (options->close_file)
+		fclose(options->file);
 }
 
 static void diffcore_apply_filter(const char *filter)
@@ -3039,11 +3219,8 @@
 static int diff_filespec_is_identical(struct diff_filespec *one,
 				      struct diff_filespec *two)
 {
-	if (S_ISGITLINK(one->mode)) {
-		diff_fill_sha1_info(one);
-		diff_fill_sha1_info(two);
-		return !hashcmp(one->sha1, two->sha1);
-	}
+	if (S_ISGITLINK(one->mode))
+		return 0;
 	if (diff_populate_filespec(one, 0))
 		return 0;
 	if (diff_populate_filespec(two, 0))
@@ -3166,6 +3343,11 @@
 
 	if (!path) path = "";
 	sprintf(concatpath, "%s%s", base, path);
+
+	if (options->prefix &&
+	    strncmp(concatpath, options->prefix, options->prefix_length))
+		return;
+
 	one = alloc_filespec(concatpath);
 	two = alloc_filespec(concatpath);
 
@@ -3195,6 +3377,11 @@
 	}
 	if (!path) path = "";
 	sprintf(concatpath, "%s%s", base, path);
+
+	if (options->prefix &&
+	    strncmp(concatpath, options->prefix, options->prefix_length))
+		return;
+
 	one = alloc_filespec(concatpath);
 	two = alloc_filespec(concatpath);
 	fill_filespec(one, old_sha1, old_mode);
@@ -3209,6 +3396,11 @@
 		  unsigned mode, const unsigned char *sha1)
 {
 	struct diff_filespec *one, *two;
+
+	if (options->prefix &&
+	    strncmp(path, options->prefix, options->prefix_length))
+		return;
+
 	one = alloc_filespec(path);
 	two = alloc_filespec(path);
 	fill_filespec(one, sha1, mode);
diff --git a/diff.h b/diff.h
index 073d5cb..f2c7739 100644
--- a/diff.h
+++ b/diff.h
@@ -30,6 +30,8 @@
 #define DIFF_FORMAT_SUMMARY	0x0008
 #define DIFF_FORMAT_PATCH	0x0010
 #define DIFF_FORMAT_SHORTSTAT	0x0020
+#define DIFF_FORMAT_DIRSTAT	0x0040
+#define DIFF_FORMAT_CUMULATIVE	0x0080
 
 /* These override all above */
 #define DIFF_FORMAT_NAME	0x0100
@@ -60,6 +62,7 @@
 #define DIFF_OPT_EXIT_WITH_STATUS    (1 << 14)
 #define DIFF_OPT_REVERSE_DIFF        (1 << 15)
 #define DIFF_OPT_CHECK_FAILED        (1 << 16)
+#define DIFF_OPT_RELATIVE_NAME       (1 << 17)
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
 #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
@@ -80,8 +83,11 @@
 	int pickaxe_opts;
 	int rename_score;
 	int rename_limit;
+	int dirstat_percent;
 	int setup;
 	int abbrev;
+	const char *prefix;
+	int prefix_length;
 	const char *msg_sep;
 	const char *stat_sep;
 	long xdl_opts;
@@ -92,6 +98,9 @@
 	/* this is set by diffcore for DIFF_FORMAT_PATCH */
 	int found_changes;
 
+	FILE *file;
+	int close_file;
+
 	int nr_paths;
 	const char **paths;
 	int *pathlens;
@@ -174,6 +183,7 @@
 
 extern int git_diff_basic_config(const char *var, const char *value);
 extern int git_diff_ui_config(const char *var, const char *value);
+extern int diff_use_color_default;
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
 extern int diff_setup_done(struct diff_options *);
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 3d37725..31941bc 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -468,10 +468,11 @@
 	 */
 	if (rename_limit <= 0 || rename_limit > 32767)
 		rename_limit = 32767;
-	if (num_create > rename_limit && num_src > rename_limit)
+	if ((num_create > rename_limit && num_src > rename_limit) ||
+	    (num_create * num_src > rename_limit * rename_limit)) {
+		warning("too many files, skipping inexact rename detection");
 		goto cleanup;
-	if (num_create * num_src > rename_limit * rename_limit)
-		goto cleanup;
+	}
 
 	mx = xmalloc(sizeof(*mx) * num_create * num_src);
 	for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
diff --git a/dir.c b/dir.c
index 3e345c2..d79762c 100644
--- a/dir.c
+++ b/dir.c
@@ -17,6 +17,7 @@
 static int read_directory_recursive(struct dir_struct *dir,
 	const char *path, const char *base, int baselen,
 	int check_only, const struct path_simplify *simplify);
+static int get_dtype(struct dirent *de, const char *path);
 
 int common_prefix(const char **pathspec)
 {
@@ -79,7 +80,7 @@
 	if (strncmp(match, name, matchlen))
 		return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
 
-	if (!name[matchlen])
+	if (namelen == matchlen)
 		return MATCHED_EXACTLY;
 	if (match[matchlen-1] == '/' || name[matchlen] == '/')
 		return MATCHED_RECURSIVELY;
@@ -126,18 +127,34 @@
 void add_exclude(const char *string, const char *base,
 		 int baselen, struct exclude_list *which)
 {
-	struct exclude *x = xmalloc(sizeof (*x));
+	struct exclude *x;
+	size_t len;
+	int to_exclude = 1;
+	int flags = 0;
 
-	x->to_exclude = 1;
 	if (*string == '!') {
-		x->to_exclude = 0;
+		to_exclude = 0;
 		string++;
 	}
-	x->pattern = string;
+	len = strlen(string);
+	if (len && string[len - 1] == '/') {
+		char *s;
+		x = xmalloc(sizeof(*x) + len);
+		s = (char*)(x+1);
+		memcpy(s, string, len - 1);
+		s[len - 1] = '\0';
+		string = s;
+		x->pattern = s;
+		flags = EXC_FLAG_MUSTBEDIR;
+	} else {
+		x = xmalloc(sizeof(*x));
+		x->pattern = string;
+	}
+	x->to_exclude = to_exclude;
 	x->patternlen = strlen(string);
 	x->base = base;
 	x->baselen = baselen;
-	x->flags = 0;
+	x->flags = flags;
 	if (!strchr(string, '/'))
 		x->flags |= EXC_FLAG_NODIR;
 	if (no_wildcard(string))
@@ -261,7 +278,7 @@
  * Return 1 for exclude, 0 for include and -1 for undecided.
  */
 static int excluded_1(const char *pathname,
-		      int pathlen, const char *basename,
+		      int pathlen, const char *basename, int *dtype,
 		      struct exclude_list *el)
 {
 	int i;
@@ -272,6 +289,13 @@
 			const char *exclude = x->pattern;
 			int to_exclude = x->to_exclude;
 
+			if (x->flags & EXC_FLAG_MUSTBEDIR) {
+				if (*dtype == DT_UNKNOWN)
+					*dtype = get_dtype(NULL, pathname);
+				if (*dtype != DT_DIR)
+					continue;
+			}
+
 			if (x->flags & EXC_FLAG_NODIR) {
 				/* match basename */
 				if (x->flags & EXC_FLAG_NOWILDCARD) {
@@ -314,7 +338,7 @@
 	return -1; /* undecided */
 }
 
-int excluded(struct dir_struct *dir, const char *pathname)
+int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 {
 	int pathlen = strlen(pathname);
 	int st;
@@ -323,7 +347,8 @@
 
 	prep_exclude(dir, pathname, basename-pathname);
 	for (st = EXC_CMDL; st <= EXC_FILE; st++) {
-		switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
+		switch (excluded_1(pathname, pathlen, basename,
+				   dtype_p, &dir->exclude_list[st])) {
 		case 0:
 			return 0;
 		case 1:
@@ -346,7 +371,7 @@
 
 struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-	if (cache_name_pos(pathname, len) >= 0)
+	if (cache_name_exists(pathname, len))
 		return NULL;
 
 	ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
@@ -391,7 +416,7 @@
 			break;
 		if (endchar == '/')
 			return index_directory;
-		if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode)))
+		if (!endchar && S_ISGITLINK(ce->ce_mode))
 			return index_gitdir;
 	}
 	return index_nonexistent;
@@ -508,7 +533,7 @@
 
 static int get_dtype(struct dirent *de, const char *path)
 {
-	int dtype = DTYPE(de);
+	int dtype = de ? DTYPE(de) : DT_UNKNOWN;
 	struct stat st;
 
 	if (dtype != DT_UNKNOWN)
@@ -560,7 +585,8 @@
 			if (simplify_away(fullname, baselen + len, simplify))
 				continue;
 
-			exclude = excluded(dir, fullname);
+			dtype = DTYPE(de);
+			exclude = excluded(dir, fullname, &dtype);
 			if (exclude && dir->collect_ignored
 			    && in_pathspec(fullname, baselen + len, simplify))
 				dir_add_ignored(dir, fullname, baselen + len);
@@ -572,7 +598,8 @@
 			if (exclude && !dir->show_ignored)
 				continue;
 
-			dtype = get_dtype(de, fullname);
+			if (dtype == DT_UNKNOWN)
+				dtype = get_dtype(de, fullname);
 
 			/*
 			 * Do we want to see just the ignored files?
@@ -677,8 +704,7 @@
 
 static void free_simplify(struct path_simplify *simplify)
 {
-	if (simplify)
-		free(simplify);
+	free(simplify);
 }
 
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
diff --git a/dir.h b/dir.h
index d8814dc..2df15de 100644
--- a/dir.h
+++ b/dir.h
@@ -9,6 +9,7 @@
 #define EXC_FLAG_NODIR 1
 #define EXC_FLAG_NOWILDCARD 2
 #define EXC_FLAG_ENDSWITH 4
+#define EXC_FLAG_MUSTBEDIR 8
 
 struct exclude_list {
 	int nr;
@@ -67,7 +68,7 @@
 
 extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
 
-extern int excluded(struct dir_struct *, const char *);
+extern int excluded(struct dir_struct *, const char *, int *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
 extern void add_exclude(const char *string, const char *base,
 			int baselen, struct exclude_list *which);
diff --git a/entry.c b/entry.c
index 257ab46..222aaa3 100644
--- a/entry.c
+++ b/entry.c
@@ -103,7 +103,7 @@
 	int fd;
 	long wrote;
 
-	switch (ntohl(ce->ce_mode) & S_IFMT) {
+	switch (ce->ce_mode & S_IFMT) {
 		char *new;
 		struct strbuf buf;
 		unsigned long size;
@@ -129,7 +129,7 @@
 			strcpy(path, ".merge_file_XXXXXX");
 			fd = mkstemp(path);
 		} else
-			fd = create_file(path, ntohl(ce->ce_mode));
+			fd = create_file(path, ce->ce_mode);
 		if (fd < 0) {
 			free(new);
 			return error("git-checkout-index: unable to create file %s (%s)",
@@ -218,15 +218,15 @@
 		 * to emulate by hand - much easier to let the system
 		 * just do the right thing)
 		 */
-		unlink(path);
 		if (S_ISDIR(st.st_mode)) {
 			/* If it is a gitlink, leave it alone! */
-			if (S_ISGITLINK(ntohl(ce->ce_mode)))
+			if (S_ISGITLINK(ce->ce_mode))
 				return 0;
 			if (!state->force)
 				return error("%s is a directory", path);
 			remove_subtree(path);
-		}
+		} else if (unlink(path))
+			return error("unable to unlink old '%s' (%s)", path, strerror(errno));
 	} else if (state->not_new)
 		return 0;
 	create_directories(path, state);
diff --git a/environment.c b/environment.c
index fa36333..6739a3f 100644
--- a/environment.c
+++ b/environment.c
@@ -35,7 +35,9 @@
 const char *editor_program;
 const char *excludes_file;
 int auto_crlf = 0;	/* 1: both ways, -1: only when adding git objects */
+enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
+enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
diff --git a/fast-import.c b/fast-import.c
index 32ec159..73e5439 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -372,6 +372,8 @@
 	fputc('\n', rpt);
 }
 
+static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
+
 static void write_crash_report(const char *err)
 {
 	char *loc = git_path("fast_import_crash_%d", getpid());
@@ -430,12 +432,37 @@
 			write_branch_report(rpt, b);
 	}
 
+	if (first_tag) {
+		struct tag *tg;
+		fputc('\n', rpt);
+		fputs("Annotated Tags\n", rpt);
+		fputs("--------------\n", rpt);
+		for (tg = first_tag; tg; tg = tg->next_tag) {
+			fputs(sha1_to_hex(tg->sha1), rpt);
+			fputc(' ', rpt);
+			fputs(tg->name, rpt);
+			fputc('\n', rpt);
+		}
+	}
+
+	fputc('\n', rpt);
+	fputs("Marks\n", rpt);
+	fputs("-----\n", rpt);
+	if (mark_file)
+		fprintf(rpt, "  exported to %s\n", mark_file);
+	else
+		dump_marks_helper(rpt, 0, marks);
+
 	fputc('\n', rpt);
 	fputs("-------------------\n", rpt);
 	fputs("END OF CRASH REPORT\n", rpt);
 	fclose(rpt);
 }
 
+static void end_packfile(void);
+static void unkeep_all_packs(void);
+static void dump_marks(void);
+
 static NORETURN void die_nicely(const char *err, va_list params)
 {
 	static int zombie;
@@ -449,6 +476,9 @@
 	if (!zombie) {
 		zombie = 1;
 		write_crash_report(message);
+		end_packfile();
+		unkeep_all_packs();
+		dump_marks();
 	}
 	exit(128);
 }
@@ -1486,6 +1516,8 @@
 	struct ref_lock *lock;
 	unsigned char old_sha1[20];
 
+	if (is_null_sha1(b->sha1))
+		return 0;
 	if (read_ref(b->name, old_sha1))
 		hashclr(old_sha1);
 	lock = lock_any_ref_for_update(b->name, old_sha1, 0);
@@ -2348,6 +2380,7 @@
 {
 	unsigned int i, show_stats = 1;
 
+	setup_git_directory();
 	git_config(git_pack_config);
 	if (!pack_compression_seen && core_compression_seen)
 		pack_compression_level = core_compression_level;
diff --git a/fetch-pack.h b/fetch-pack.h
index a7888ea..8bd9c32 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -12,10 +12,13 @@
 		use_thin_pack:1,
 		fetch_all:1,
 		verbose:1,
-		no_progress:1;
+		no_progress:1,
+		include_tag:1;
 };
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
+		int fd[], struct child_process *conn,
+		const struct ref *ref,
 		const char *dest,
 		int nr_heads,
 		char **heads,
diff --git a/fsck.c b/fsck.c
new file mode 100644
index 0000000..797e317
--- /dev/null
+++ b/fsck.c
@@ -0,0 +1,333 @@
+#include "cache.h"
+#include "object.h"
+#include "blob.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "commit.h"
+#include "tag.h"
+#include "fsck.h"
+
+static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+	int res = 0;
+
+	if (parse_tree(tree))
+		return -1;
+
+	init_tree_desc(&desc, tree->buffer, tree->size);
+	while (tree_entry(&desc, &entry)) {
+		int result;
+
+		if (S_ISGITLINK(entry.mode))
+			continue;
+		if (S_ISDIR(entry.mode))
+			result = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);
+		else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
+			result = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);
+		else {
+			result = error("in tree %s: entry %s has bad mode %.6o\n",
+					sha1_to_hex(tree->object.sha1), entry.path, entry.mode);
+		}
+		if (result < 0)
+			return result;
+		if (!res)
+			res = result;
+	}
+	return res;
+}
+
+static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)
+{
+	struct commit_list *parents;
+	int res;
+	int result;
+
+	if (parse_commit(commit))
+		return -1;
+
+	result = walk((struct object *)commit->tree, OBJ_TREE, data);
+	if (result < 0)
+		return result;
+	res = result;
+
+	parents = commit->parents;
+	while (parents) {
+		result = walk((struct object *)parents->item, OBJ_COMMIT, data);
+		if (result < 0)
+			return result;
+		if (!res)
+			res = result;
+		parents = parents->next;
+	}
+	return res;
+}
+
+static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)
+{
+	if (parse_tag(tag))
+		return -1;
+	return walk(tag->tagged, OBJ_ANY, data);
+}
+
+int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)
+{
+	if (!obj)
+		return -1;
+	switch (obj->type) {
+	case OBJ_BLOB:
+		return 0;
+	case OBJ_TREE:
+		return fsck_walk_tree((struct tree *)obj, walk, data);
+	case OBJ_COMMIT:
+		return fsck_walk_commit((struct commit *)obj, walk, data);
+	case OBJ_TAG:
+		return fsck_walk_tag((struct tag *)obj, walk, data);
+	default:
+		error("Unknown object type for %s", sha1_to_hex(obj->sha1));
+		return -1;
+	}
+}
+
+/*
+ * The entries in a tree are ordered in the _path_ order,
+ * which means that a directory entry is ordered by adding
+ * a slash to the end of it.
+ *
+ * So a directory called "a" is ordered _after_ a file
+ * called "a.c", because "a/" sorts after "a.c".
+ */
+#define TREE_UNORDERED (-1)
+#define TREE_HAS_DUPS  (-2)
+
+static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
+{
+	int len1 = strlen(name1);
+	int len2 = strlen(name2);
+	int len = len1 < len2 ? len1 : len2;
+	unsigned char c1, c2;
+	int cmp;
+
+	cmp = memcmp(name1, name2, len);
+	if (cmp < 0)
+		return 0;
+	if (cmp > 0)
+		return TREE_UNORDERED;
+
+	/*
+	 * Ok, the first <len> characters are the same.
+	 * Now we need to order the next one, but turn
+	 * a '\0' into a '/' for a directory entry.
+	 */
+	c1 = name1[len];
+	c2 = name2[len];
+	if (!c1 && !c2)
+		/*
+		 * git-write-tree used to write out a nonsense tree that has
+		 * entries with the same name, one blob and one tree.  Make
+		 * sure we do not have duplicate entries.
+		 */
+		return TREE_HAS_DUPS;
+	if (!c1 && S_ISDIR(mode1))
+		c1 = '/';
+	if (!c2 && S_ISDIR(mode2))
+		c2 = '/';
+	return c1 < c2 ? 0 : TREE_UNORDERED;
+}
+
+static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
+{
+	int retval;
+	int has_full_path = 0;
+	int has_empty_name = 0;
+	int has_zero_pad = 0;
+	int has_bad_modes = 0;
+	int has_dup_entries = 0;
+	int not_properly_sorted = 0;
+	struct tree_desc desc;
+	unsigned o_mode;
+	const char *o_name;
+	const unsigned char *o_sha1;
+
+	init_tree_desc(&desc, item->buffer, item->size);
+
+	o_mode = 0;
+	o_name = NULL;
+	o_sha1 = NULL;
+
+	while (desc.size) {
+		unsigned mode;
+		const char *name;
+		const unsigned char *sha1;
+
+		sha1 = tree_entry_extract(&desc, &name, &mode);
+
+		if (strchr(name, '/'))
+			has_full_path = 1;
+		if (!*name)
+			has_empty_name = 1;
+		has_zero_pad |= *(char *)desc.buffer == '0';
+		update_tree_entry(&desc);
+
+		switch (mode) {
+		/*
+		 * Standard modes..
+		 */
+		case S_IFREG | 0755:
+		case S_IFREG | 0644:
+		case S_IFLNK:
+		case S_IFDIR:
+		case S_IFGITLINK:
+			break;
+		/*
+		 * This is nonstandard, but we had a few of these
+		 * early on when we honored the full set of mode
+		 * bits..
+		 */
+		case S_IFREG | 0664:
+			if (!strict)
+				break;
+		default:
+			has_bad_modes = 1;
+		}
+
+		if (o_name) {
+			switch (verify_ordered(o_mode, o_name, mode, name)) {
+			case TREE_UNORDERED:
+				not_properly_sorted = 1;
+				break;
+			case TREE_HAS_DUPS:
+				has_dup_entries = 1;
+				break;
+			default:
+				break;
+			}
+		}
+
+		o_mode = mode;
+		o_name = name;
+		o_sha1 = sha1;
+	}
+
+	retval = 0;
+	if (has_full_path)
+		retval += error_func(&item->object, FSCK_WARN, "contains full pathnames");
+	if (has_empty_name)
+		retval += error_func(&item->object, FSCK_WARN, "contains empty pathname");
+	if (has_zero_pad)
+		retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes");
+	if (has_bad_modes)
+		retval += error_func(&item->object, FSCK_WARN, "contains bad file modes");
+	if (has_dup_entries)
+		retval += error_func(&item->object, FSCK_ERROR, "contains duplicate file entries");
+	if (not_properly_sorted)
+		retval += error_func(&item->object, FSCK_ERROR, "not properly sorted");
+	return retval;
+}
+
+static int fsck_commit(struct commit *commit, fsck_error error_func)
+{
+	char *buffer = commit->buffer;
+	unsigned char tree_sha1[20], sha1[20];
+	struct commit_graft *graft;
+	int parents = 0;
+
+	if (!commit->date)
+		return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
+
+	if (memcmp(buffer, "tree ", 5))
+		return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
+	if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
+		return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
+	buffer += 46;
+	while (!memcmp(buffer, "parent ", 7)) {
+		if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
+			return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
+		buffer += 48;
+		parents++;
+	}
+	graft = lookup_commit_graft(commit->object.sha1);
+	if (graft) {
+		struct commit_list *p = commit->parents;
+		parents = 0;
+		while (p) {
+			p = p->next;
+			parents++;
+		}
+		if (graft->nr_parent == -1 && !parents)
+			; /* shallow commit */
+		else if (graft->nr_parent != parents)
+			return error_func(&commit->object, FSCK_ERROR, "graft objects missing");
+	} else {
+		struct commit_list *p = commit->parents;
+		while (p && parents) {
+			p = p->next;
+			parents--;
+		}
+		if (p || parents)
+			return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
+	}
+	if (memcmp(buffer, "author ", 7))
+		return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
+	if (!commit->tree)
+		return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
+
+	return 0;
+}
+
+static int fsck_tag(struct tag *tag, fsck_error error_func)
+{
+	struct object *tagged = tag->tagged;
+
+	if (!tagged)
+		return error_func(&tag->object, FSCK_ERROR, "could not load tagged object");
+	return 0;
+}
+
+int fsck_object(struct object *obj, int strict, fsck_error error_func)
+{
+	if (!obj)
+		return error_func(obj, FSCK_ERROR, "no valid object to fsck");
+
+	if (obj->type == OBJ_BLOB)
+		return 0;
+	if (obj->type == OBJ_TREE)
+		return fsck_tree((struct tree *) obj, strict, error_func);
+	if (obj->type == OBJ_COMMIT)
+		return fsck_commit((struct commit *) obj, error_func);
+	if (obj->type == OBJ_TAG)
+		return fsck_tag((struct tag *) obj, error_func);
+
+	return error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)",
+			  obj->type);
+}
+
+int fsck_error_function(struct object *obj, int type, const char *fmt, ...)
+{
+	va_list ap;
+	int len;
+	struct strbuf sb;
+
+	strbuf_init(&sb, 0);
+	strbuf_addf(&sb, "object %s:", obj->sha1?sha1_to_hex(obj->sha1):"(null)");
+
+	va_start(ap, fmt);
+	len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap);
+	va_end(ap);
+
+	if (len < 0)
+		len = 0;
+	if (len >= strbuf_avail(&sb)) {
+		strbuf_grow(&sb, len + 2);
+		va_start(ap, fmt);
+		len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap);
+		va_end(ap);
+		if (len >= strbuf_avail(&sb))
+			die("this should not happen, your snprintf is broken");
+	}
+
+	error(sb.buf);
+	strbuf_release(&sb);
+	return 1;
+}
diff --git a/fsck.h b/fsck.h
new file mode 100644
index 0000000..990ee02
--- /dev/null
+++ b/fsck.h
@@ -0,0 +1,32 @@
+#ifndef GIT_FSCK_H
+#define GIT_FSCK_H
+
+#define FSCK_ERROR 1
+#define FSCK_WARN 2
+
+/*
+ * callback function for fsck_walk
+ * type is the expected type of the object or OBJ_ANY
+ * the return value is:
+ *     0	everything OK
+ *     <0	error signaled and abort
+ *     >0	error signaled and do not abort
+ */
+typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);
+
+/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
+typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);
+
+int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
+
+/* descend in all linked child objects
+ * the return value is:
+ *    -1	error in processing the object
+ *    <0	return value of the callback, which lead to an abort
+ *    >0	return value of the first sigaled error >0 (in the case of no other errors)
+ *    0		everything OK
+ */
+int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);
+int fsck_object(struct object *obj, int strict, fsck_error error_func);
+
+#endif
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 17ca5b8..a0a81f1 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -82,6 +82,19 @@
 my $status_fmt = '%12s %12s %s';
 my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
 
+{
+	my $initial;
+	sub is_initial_commit {
+		$initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
+			unless defined $initial;
+		return $initial;
+	}
+}
+
+sub get_empty_tree {
+	return '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+}
+
 # Returns list of hashes, contents of each of which are:
 # VALUE:	pathname
 # BINARY:	is a binary path
@@ -103,8 +116,10 @@
 		return if (!@tracked);
 	}
 
+	my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
 	for (run_cmd_pipe(qw(git diff-index --cached
-			     --numstat --summary HEAD --), @tracked)) {
+			     --numstat --summary), $reference,
+			     '--', @tracked)) {
 		if (($add, $del, $file) =
 		    /^([-\d]+)	([-\d]+)	(.*)/) {
 			my ($change, $bin);
@@ -476,21 +491,27 @@
 				       HEADER => $status_head, },
 				     list_modified());
 	if (@update) {
-		my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
-					 map { $_->{VALUE} } @update);
-		my $fh;
-		open $fh, '| git update-index --index-info'
-		    or die;
-		for (@lines) {
-			print $fh $_;
+		if (is_initial_commit()) {
+			system(qw(git rm --cached),
+				map { $_->{VALUE} } @update);
 		}
-		close($fh);
-		for (@update) {
-			if ($_->{INDEX_ADDDEL} &&
-			    $_->{INDEX_ADDDEL} eq 'create') {
-				system(qw(git update-index --force-remove --),
-				       $_->{VALUE});
-				print "note: $_->{VALUE} is untracked now.\n";
+		else {
+			my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
+						 map { $_->{VALUE} } @update);
+			my $fh;
+			open $fh, '| git update-index --index-info'
+			    or die;
+			for (@lines) {
+				print $fh $_;
+			}
+			close($fh);
+			for (@update) {
+				if ($_->{INDEX_ADDDEL} &&
+				    $_->{INDEX_ADDDEL} eq 'create') {
+					system(qw(git update-index --force-remove --),
+					       $_->{VALUE});
+					print "note: $_->{VALUE} is untracked now.\n";
+				}
 			}
 		}
 		refresh();
@@ -956,7 +977,9 @@
 				     HEADER => $status_head, },
 				   @mods);
 	return if (!@them);
-	system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them);
+	my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+	system(qw(git diff -p --cached), $reference, '--',
+		map { $_->{VALUE} } @them);
 }
 
 sub quit_cmd {
diff --git a/git-am.sh b/git-am.sh
index 4dce87b..b48096e 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -2,13 +2,14 @@
 #
 # Copyright (c) 2005, 2006 Junio C Hamano
 
+SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git-am [options] <mbox>|<Maildir>...
 git-am [options] --resolved
 git-am [options] --skip
 --
-d,dotest=       use <dir> and not .dotest
+d,dotest=       (removed -- do not use)
 i,interactive   run interactively
 b,binary        pass --allow-binary-replacement to git-apply
 3,3way          allow fall back on 3way merging if needed
@@ -20,11 +21,14 @@
 p=              pass it through git-apply
 resolvemsg=     override error message when patch failure occurs
 r,resolved      to be used after a patch failure
-skip            skip the current patch"
+skip            skip the current patch
+rebasing        (internal use for git-rebase)"
 
 . git-sh-setup
+prefix=$(git rev-parse --show-prefix)
 set_reflog_action am
 require_work_tree
+cd_to_toplevel
 
 git var GIT_COMMITTER_IDENT >/dev/null || exit
 
@@ -47,10 +51,6 @@
     then
         cmdline="$cmdline -3"
     fi
-    if test '.dotest' != "$dotest"
-    then
-        cmdline="$cmdline -d=$dotest"
-    fi
     echo "When you have resolved this problem run \"$cmdline --resolved\"."
     echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
 
@@ -118,7 +118,8 @@
 }
 
 prec=4
-dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
+dotest=".dotest"
+sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
 resolvemsg= resume=
 git_apply_opt=
 
@@ -143,8 +144,11 @@
 		resolved=t ;;
 	--skip)
 		skip=t ;;
+	--rebasing)
+		rebasing=t threeway=t keep=t binary=t ;;
 	-d|--dotest)
-		shift; dotest=$1;;
+		die "-d option is no longer supported.  Do not use."
+		;;
 	--resolvemsg)
 		shift; resolvemsg=$1 ;;
 	--whitespace)
@@ -180,7 +184,7 @@
 	0,)
 		# No file input but without resume parameters; catch
 		# user error to feed us a patch from standard input
-		# when there is already .dotest.  This is somewhat
+		# when there is already $dotest.  This is somewhat
 		# unreliable -- stdin could be /dev/null for example
 		# and the caller did not intend to feed us a patch but
 		# wanted to continue unattended.
@@ -200,6 +204,24 @@
 	# Start afresh.
 	mkdir -p "$dotest" || exit
 
+	if test -n "$prefix" && test $# != 0
+	then
+		first=t
+		for arg
+		do
+			test -n "$first" && {
+				set x
+				first=
+			}
+			case "$arg" in
+			/*)
+				set "$@" "$arg" ;;
+			*)
+				set "$@" "$prefix$arg" ;;
+			esac
+		done
+		shift
+	fi
 	git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
 		rm -fr "$dotest"
 		exit 1
@@ -214,6 +236,12 @@
 	echo "$utf8" >"$dotest/utf8"
 	echo "$keep" >"$dotest/keep"
 	echo 1 >"$dotest/next"
+	if test -n "$rebasing"
+	then
+		: >"$dotest/rebasing"
+	else
+		: >"$dotest/applying"
+	fi
 fi
 
 case "$resolved" in
@@ -299,11 +327,20 @@
 			echo "Patch is empty.  Was it split wrong?"
 			stop_here $this
 		}
-		SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-		case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
+		if test -f "$dotest/rebasing" &&
+			commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
+				-e q "$dotest/$msgnum") &&
+			test "$(git cat-file -t "$commit")" = commit
+		then
+			git cat-file commit "$commit" |
+			sed -e '1,/^$/d' >"$dotest/msg-clean"
+		else
+			SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
+			case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
 
-		(printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
-			git stripspace > "$dotest/msg-clean"
+			(printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
+				git stripspace > "$dotest/msg-clean"
+		fi
 		;;
 	esac
 
@@ -326,7 +363,7 @@
 		LAST_SIGNED_OFF_BY=`
 		    sed -ne '/^Signed-off-by: /p' \
 		    "$dotest/msg-clean" |
-		    tail -n 1
+		    sed -ne '$p'
 		`
 		ADD_SIGNOFF=`
 		    test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
diff --git a/git-bisect.sh b/git-bisect.sh
index b42f94c..b1800ed 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -65,14 +65,19 @@
 	head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
 	head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
 	die "Bad HEAD - I need a HEAD"
+	#
+	# Check that we either already have BISECT_START, or that the
+	# branches bisect, new-bisect don't exist, to not override them.
+	#
+	test -s "$GIT_DIR/BISECT_START" ||
+		if git show-ref --verify -q refs/heads/bisect ||
+		    git show-ref --verify -q refs/heads/new-bisect; then
+			die 'The branches "bisect" and "new-bisect" must not exist.'
+		fi
 	start_head=''
 	case "$head" in
 	refs/heads/bisect)
-		if [ -s "$GIT_DIR/BISECT_START" ]; then
-		    branch=`cat "$GIT_DIR/BISECT_START"`
-		else
-		    branch=master
-		fi
+		branch=`cat "$GIT_DIR/BISECT_START"`
 		git checkout $branch || exit
 		;;
 	refs/heads/*|$_x40)
@@ -215,18 +220,33 @@
 	bisect_next_check && bisect_next || :
 }
 
+eval_rev_list() {
+	_eval="$1"
+
+	eval $_eval
+	res=$?
+
+	if [ $res -ne 0 ]; then
+		echo >&2 "'git rev-list --bisect-vars' failed:"
+		echo >&2 "maybe you mistake good and bad revs?"
+		exit $res
+	fi
+
+	return $res
+}
+
 filter_skipped() {
 	_eval="$1"
 	_skip="$2"
 
 	if [ -z "$_skip" ]; then
-		eval $_eval
+		eval_rev_list "$_eval"
 		return
 	fi
 
 	# Let's parse the output of:
 	# "git rev-list --bisect-vars --bisect-all ..."
-	eval $_eval | while read hash line
+	eval_rev_list "$_eval" | while read hash line
 	do
 		case "$VARS,$FOUND,$TRIED,$hash" in
 			# We display some vars.
@@ -295,14 +315,14 @@
 	bisect_next_check good
 
 	skip=$(git for-each-ref --format='%(objectname)' \
-		"refs/bisect/skip-*" | tr '[\012]' ' ') || exit
+		"refs/bisect/skip-*" | tr '\012' ' ') || exit
 
 	BISECT_OPT=''
 	test -n "$skip" && BISECT_OPT='--bisect-all'
 
 	bad=$(git rev-parse --verify refs/bisect/bad) &&
 	good=$(git for-each-ref --format='^%(objectname)' \
-		"refs/bisect/good-*" | tr '[\012]' ' ') &&
+		"refs/bisect/good-*" | tr '\012' ' ') &&
 	eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
 	eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
 	eval=$(filter_skipped "$eval" "$skip") &&
@@ -324,8 +344,8 @@
 	exit_if_skipped_commits "$bisect_rev"
 
 	echo "Bisecting: $bisect_nr revisions left to test after this"
-	git branch -f new-bisect "$bisect_rev"
-	git checkout -q new-bisect || exit
+	git branch -D new-bisect 2> /dev/null
+	git checkout -q -b new-bisect "$bisect_rev" || exit
 	git branch -M new-bisect bisect
 	git show-branch "$bisect_rev"
 }
@@ -335,9 +355,9 @@
 
 	if test $# = 0
 	then
-		case "${DISPLAY+set}" in
+		case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
 		'')	set git log ;;
-		set)	set gitk ;;
+		set*)	set gitk ;;
 		esac
 	else
 		case "$1" in
diff --git a/git-clone.sh b/git-clone.sh
index 0d686c3..8c7fc7f 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -210,11 +210,29 @@
 	then
 		local=yes
 	fi
+elif test -f "$repo"
+then
+	case "$repo" in /*) ;; *) repo="$PWD/$repo" ;; esac
 fi
 
-dir="$2"
-# Try using "humanish" part of source repo if user didn't specify one
-[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+# Decide the directory name of the new repository
+if test -n "$2"
+then
+	dir="$2"
+	test $# = 2 || die "excess parameter to git-clone"
+else
+	# Derive one from the repository name
+	# Try using "humanish" part of source repo if user didn't specify one
+	if test -f "$repo"
+	then
+		# Cloning from a bundle
+		dir=$(echo "$repo" | sed -e 's|/*\.bundle$||' -e 's|.*/||g')
+	else
+		dir=$(echo "$repo" |
+			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+	fi
+fi
+
 [ -e "$dir" ] && die "destination directory '$dir' already exists."
 [ yes = "$bare" ] && unset GIT_WORK_TREE
 [ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] &&
@@ -293,6 +311,9 @@
 		mkdir -p "$GIT_DIR/objects/info"
 		echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates"
 	else
+		cpio_quiet_flag=""
+		cpio --help 2>&1 | grep -- --quiet >/dev/null && \
+			cpio_quiet_flag=--quiet
 		l= &&
 		if test "$use_local_hardlink" = yes
 		then
@@ -313,7 +334,8 @@
 			fi
 		fi &&
 		cd "$repo" &&
-		find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
+		find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \
+			exit 1
 	fi
 	git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
 	;;
@@ -364,11 +386,17 @@
 		fi
 		;;
 	*)
-		case "$upload_pack" in
-		'') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";;
-		*) git-fetch-pack --all -k $quiet "$upload_pack" $depth $no_progress "$repo" ;;
-		esac >"$GIT_DIR/CLONE_HEAD" ||
+		if [ -f "$repo" ] ; then
+			git bundle unbundle "$repo" > "$GIT_DIR/CLONE_HEAD" ||
+			die "unbundle from '$repo' failed."
+		else
+			case "$upload_pack" in
+			'') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";;
+			*) git-fetch-pack --all -k \
+				$quiet "$upload_pack" $depth $no_progress "$repo" ;;
+			esac >"$GIT_DIR/CLONE_HEAD" ||
 			die "fetch-pack from '$repo' failed."
+		fi
 		;;
 	esac
 	;;
diff --git a/git-compat-util.h b/git-compat-util.h
index 4df90cb..a18235e 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -68,6 +68,7 @@
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <utime.h>
 #ifndef NO_SYS_SELECT_H
 #include <sys/select.h>
 #endif
@@ -204,6 +205,20 @@
                 const void *needle, size_t needlelen);
 #endif
 
+#ifdef FREAD_READS_DIRECTORIES
+#define fopen(a,b) git_fopen(a,b)
+extern FILE *git_fopen(const char*, const char*);
+#endif
+
+#ifdef SNPRINTF_RETURNS_BOGUS
+#define snprintf git_snprintf
+extern int git_snprintf(char *str, size_t maxsize,
+			const char *format, ...);
+#define vsnprintf git_vsnprintf
+extern int git_vsnprintf(char *str, size_t maxsize,
+			 const char *format, va_list ap);
+#endif
+
 #ifdef __GLIBC_PREREQ
 #if __GLIBC_PREREQ(2, 1)
 #define HAVE_STRCHRNUL
@@ -426,4 +441,16 @@
 	return 0;
 }
 
+#ifdef INTERNAL_QSORT
+void git_qsort(void *base, size_t nmemb, size_t size,
+	       int(*compar)(const void *, const void *));
+#define qsort git_qsort
+#endif
+
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
 #endif
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 33777e2..bdac5d5 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -15,7 +15,7 @@
 
 use strict;
 use warnings;
-use Getopt::Std;
+use Getopt::Long;
 use File::Spec;
 use File::Temp qw(tempfile tmpnam);
 use File::Path qw(mkpath);
@@ -29,7 +29,7 @@
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
 my (%conv_author_name, %conv_author_email);
 
 sub usage(;$) {
@@ -112,7 +112,12 @@
 
 my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
 read_repo_config($opts);
-getopts($opts) or usage();
+Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
+
+# turn the Getopt::Std specification in a Getopt::Long one,
+# with support for multiple -M options
+GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
+    or usage();
 usage if $opt_h;
 
 if (@ARGV == 0) {
@@ -164,10 +169,10 @@
 
 our @mergerx = ();
 if ($opt_m) {
-	@mergerx = ( qr/\b(?:from|of|merge|merging|merged) (\w+)/i );
+	@mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
 }
-if ($opt_M) {
-	push (@mergerx, qr/$opt_M/);
+if (@opt_M) {
+	push (@mergerx, map { qr/$_/ } @opt_M);
 }
 
 # Remember UTC of our starting time
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index afe3d0b..29dbfc9 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -73,8 +73,8 @@
     'status'          => \&req_status,
     'admin'           => \&req_CATCHALL,
     'history'         => \&req_CATCHALL,
-    'watchers'        => \&req_CATCHALL,
-    'editors'         => \&req_CATCHALL,
+    'watchers'        => \&req_EMPTY,
+    'editors'         => \&req_EMPTY,
     'annotate'        => \&req_annotate,
     'Global_option'   => \&req_Globaloption,
     #'annotate'        => \&req_CATCHALL,
@@ -199,6 +199,11 @@
     $log->warn("Unhandled command : req_$cmd : $data");
 }
 
+# This method invariably succeeds with an empty response.
+sub req_EMPTY
+{
+    print "ok\n";
+}
 
 # Root pathname \n
 #     Response expected: no. Tell the server which CVSROOT to use. Note that
@@ -958,6 +963,17 @@
             $meta = $updater->getmeta($filename);
         }
 
+        # If -p was given, "print" the contents of the requested revision.
+        if ( exists ( $state->{opt}{p} ) ) {
+            if ( defined ( $meta->{revision} ) ) {
+                $log->info("Printing '$filename' revision " . $meta->{revision});
+
+                transmitfile($meta->{filehash}, { print => 1 });
+            }
+
+            next;
+        }
+
 	if ( ! defined $meta )
 	{
 	    $meta = {
@@ -1091,9 +1107,9 @@
             my $file_local = $filepart . ".mine";
             system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local);
             my $file_old = $filepart . "." . $oldmeta->{revision};
-            transmitfile($oldmeta->{filehash}, $file_old);
+            transmitfile($oldmeta->{filehash}, { targetfile => $file_old });
             my $file_new = $filepart . "." . $meta->{revision};
-            transmitfile($meta->{filehash}, $file_new);
+            transmitfile($meta->{filehash}, { targetfile => $file_new });
 
             # we need to merge with the local changes ( M=successful merge, C=conflict merge )
             $log->info("Merging $file_local, $file_old, $file_new");
@@ -1423,6 +1439,8 @@
     {
         $filename = filecleanup($filename);
 
+        next if exists($state->{opt}{l}) && index($filename, '/', length($state->{prependdir})) >= 0;
+
         my $meta = $updater->getmeta($filename);
         my $oldmeta = $meta;
 
@@ -1466,8 +1484,10 @@
 
         $status ||= "Unknown";
 
+        my ($filepart) = filenamesplit($filename);
+
         print "M ===================================================================\n";
-        print "M File: $filename\tStatus: $status\n";
+        print "M File: $filepart\tStatus: $status\n";
         if ( defined($state->{entries}{$filename}{revision}) )
         {
             print "M Working revision:\t" . $state->{entries}{$filename}{revision} . "\n";
@@ -1541,14 +1561,14 @@
                 print "E File $filename at revision 1.$revision1 doesn't exist\n";
                 next;
             }
-            transmitfile($meta1->{filehash}, $file1);
+            transmitfile($meta1->{filehash}, { targetfile => $file1 });
         }
         # otherwise we just use the working copy revision
         else
         {
             ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
             $meta1 = $updater->getmeta($filename, $wrev);
-            transmitfile($meta1->{filehash}, $file1);
+            transmitfile($meta1->{filehash}, { targetfile => $file1 });
         }
 
         # if we have a second -r switch, use it too
@@ -1563,7 +1583,7 @@
                 next;
             }
 
-            transmitfile($meta2->{filehash}, $file2);
+            transmitfile($meta2->{filehash}, { targetfile => $file2 });
         }
         # otherwise we just use the working copy
         else
@@ -1576,7 +1596,7 @@
         {
             ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
             $meta2 = $updater->getmeta($filename, $wrev);
-            transmitfile($meta2->{filehash}, $file2);
+            transmitfile($meta2->{filehash}, { targetfile => $file2 });
         }
 
         # We need to have retrieved something useful
@@ -1708,8 +1728,7 @@
             print "M revision 1.$revision->{revision}\n";
             # reformat the date for log output
             $revision->{modified} = sprintf('%04d/%02d/%02d %s', $3, $DATE_LIST->{$2}, $1, $4 ) if ( $revision->{modified} =~ /(\d+)\s+(\w+)\s+(\d+)\s+(\S+)/ and defined($DATE_LIST->{$2}) );
-            $revision->{author} =~ s/\s+.*//;
-            $revision->{author} =~ s/^(.{8}).*/$1/;
+            $revision->{author} = cvs_author($revision->{author});
             print "M date: $revision->{modified};  author: $revision->{author};  state: " . ( $revision->{filehash} eq "deleted" ? "dead" : "Exp" ) . ";  lines: +2 -3\n";
             my $commitmessage = $updater->commitmessage($revision->{commithash});
             $commitmessage =~ s/^/M /mg;
@@ -1824,8 +1843,7 @@
                 unless ( defined ( $metadata->{$commithash} ) )
                 {
                     $metadata->{$commithash} = $updater->getmeta($filename, $commithash);
-                    $metadata->{$commithash}{author} =~ s/\s+.*//;
-                    $metadata->{$commithash}{author} =~ s/^(.{8}).*/$1/;
+                    $metadata->{$commithash}{author} = cvs_author($metadata->{$commithash}{author});
                     $metadata->{$commithash}{modified} = sprintf("%02d-%s-%02d", $1, $2, $3) if ( $metadata->{$commithash}{modified} =~ /^(\d+)\s(\w+)\s\d\d(\d\d)/ );
                 }
                 printf("M 1.%-5d      (%-8s %10s): %s\n",
@@ -2005,14 +2023,17 @@
     return undef;
 }
 
-# This method takes a file hash and does a CVS "file transfer" which transmits the
-# size of the file, and then the file contents.
-# If a second argument $targetfile is given, the file is instead written out to
-# a file by the name of $targetfile
+# This method takes a file hash and does a CVS "file transfer".  Its
+# exact behaviour depends on a second, optional hash table argument:
+# - If $options->{targetfile}, dump the contents to that file;
+# - If $options->{print}, use M/MT to transmit the contents one line
+#   at a time;
+# - Otherwise, transmit the size of the file, followed by the file
+#   contents.
 sub transmitfile
 {
     my $filehash = shift;
-    my $targetfile = shift;
+    my $options = shift;
 
     if ( defined ( $filehash ) and $filehash eq "deleted" )
     {
@@ -2034,11 +2055,20 @@
 
     if ( open my $fh, '-|', "git-cat-file", "blob", $filehash )
     {
-        if ( defined ( $targetfile ) )
+        if ( defined ( $options->{targetfile} ) )
         {
+            my $targetfile = $options->{targetfile};
             open NEWFILE, ">", $targetfile or die("Couldn't open '$targetfile' for writing : $!");
             print NEWFILE $_ while ( <$fh> );
             close NEWFILE or die("Failed to write '$targetfile': $!");
+        } elsif ( defined ( $options->{print} ) && $options->{print} ) {
+            while ( <$fh> ) {
+                if( /\n\z/ ) {
+                    print 'M ', $_;
+                } else {
+                    print 'MT text ', $_, "\n";
+                }
+            }
         } else {
             print "$size\n";
             print while ( <$fh> );
@@ -2107,6 +2137,16 @@
     }
 }
 
+# Generate a CVS author name from Git author information, by taking
+# the first eight characters of the user part of the email address.
+sub cvs_author
+{
+    my $author_line = shift;
+    (my $author) = $author_line =~ /<([^>@]{1,8})/;
+
+    $author;
+}
+
 package GITCVS::log;
 
 ####
@@ -2311,6 +2351,14 @@
 
     bless $self, $class;
 
+    $self->{valid_tables} = {'revision' => 1,
+                             'revision_ix1' => 1,
+                             'revision_ix2' => 1,
+                             'head' => 1,
+                             'head_ix1' => 1,
+                             'properties' => 1,
+                             'commitmsgs' => 1};
+
     $self->{module} = $module;
     $self->{git_path} = $config . "/";
 
@@ -2326,6 +2374,8 @@
         $cfg->{gitcvs}{dbuser} || "";
     $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||
         $cfg->{gitcvs}{dbpass} || "";
+    $self->{dbtablenameprefix} = $cfg->{gitcvs}{$state->{method}}{dbtablenameprefix} ||
+        $cfg->{gitcvs}{dbtablenameprefix} || "";
     my %mapping = ( m => $module,
                     a => $state->{method},
                     u => getlogin || getpwuid($<) || $<,
@@ -2334,6 +2384,8 @@
                     );
     $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;
     $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;
+    $self->{dbtablenameprefix} =~ s/%([mauGg])/$mapping{$1}/eg;
+    $self->{dbtablenameprefix} = mangle_tablename($self->{dbtablenameprefix});
 
     die "Invalid char ':' in dbdriver" if $self->{dbdriver} =~ /:/;
     die "Invalid char ';' in dbname" if $self->{dbname} =~ /;/;
@@ -2349,10 +2401,13 @@
     }
 
     # Construct the revision table if required
-    unless ( $self->{tables}{revision} )
+    unless ( $self->{tables}{$self->tablename("revision")} )
     {
+        my $tablename = $self->tablename("revision");
+        my $ix1name = $self->tablename("revision_ix1");
+        my $ix2name = $self->tablename("revision_ix2");
         $self->{dbh}->do("
-            CREATE TABLE revision (
+            CREATE TABLE $tablename (
                 name       TEXT NOT NULL,
                 revision   INTEGER NOT NULL,
                 filehash   TEXT NOT NULL,
@@ -2363,20 +2418,22 @@
             )
         ");
         $self->{dbh}->do("
-            CREATE INDEX revision_ix1
-            ON revision (name,revision)
+            CREATE INDEX $ix1name
+            ON $tablename (name,revision)
         ");
         $self->{dbh}->do("
-            CREATE INDEX revision_ix2
-            ON revision (name,commithash)
+            CREATE INDEX $ix2name
+            ON $tablename (name,commithash)
         ");
     }
 
     # Construct the head table if required
-    unless ( $self->{tables}{head} )
+    unless ( $self->{tables}{$self->tablename("head")} )
     {
+        my $tablename = $self->tablename("head");
+        my $ix1name = $self->tablename("head_ix1");
         $self->{dbh}->do("
-            CREATE TABLE head (
+            CREATE TABLE $tablename (
                 name       TEXT NOT NULL,
                 revision   INTEGER NOT NULL,
                 filehash   TEXT NOT NULL,
@@ -2387,16 +2444,17 @@
             )
         ");
         $self->{dbh}->do("
-            CREATE INDEX head_ix1
-            ON head (name)
+            CREATE INDEX $ix1name
+            ON $tablename (name)
         ");
     }
 
     # Construct the properties table if required
-    unless ( $self->{tables}{properties} )
+    unless ( $self->{tables}{$self->tablename("properties")} )
     {
+        my $tablename = $self->tablename("properties");
         $self->{dbh}->do("
-            CREATE TABLE properties (
+            CREATE TABLE $tablename (
                 key        TEXT NOT NULL PRIMARY KEY,
                 value      TEXT
             )
@@ -2404,10 +2462,11 @@
     }
 
     # Construct the commitmsgs table if required
-    unless ( $self->{tables}{commitmsgs} )
+    unless ( $self->{tables}{$self->tablename("commitmsgs")} )
     {
+        my $tablename = $self->tablename("commitmsgs");
         $self->{dbh}->do("
-            CREATE TABLE commitmsgs (
+            CREATE TABLE $tablename (
                 key        TEXT NOT NULL PRIMARY KEY,
                 value      TEXT
             )
@@ -2417,6 +2476,21 @@
     return $self;
 }
 
+=head2 tablename
+
+=cut
+sub tablename
+{
+    my $self = shift;
+    my $name = shift;
+
+    if (exists $self->{valid_tables}{$name}) {
+        return $self->{dbtablenameprefix} . $name;
+    } else {
+        return undef;
+    }
+}
+
 =head2 update
 
 =cut
@@ -2556,7 +2630,7 @@
                     if ($base) {
                         my @merged;
                         # print "want to log between  $base $parent \n";
-                        open(GITLOG, '-|', 'git-log', "$base..$parent")
+                        open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
 			  or die "Cannot call git-log: $!";
                         my $mergedhash;
                         while (<GITLOG>) {
@@ -2633,7 +2707,7 @@
                     };
                     $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
-                elsif ( $change eq "M" )
+                elsif ( $change eq "M" || $change eq "T" )
                 {
                     #$log->debug("MODIFIED $name");
                     $head->{$name} = {
@@ -2782,8 +2856,9 @@
     my $modified = shift;
     my $author = shift;
     my $mode = shift;
+    my $tablename = $self->tablename("revision");
 
-    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
     $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
 }
 
@@ -2792,16 +2867,18 @@
     my $self = shift;
     my $key = shift;
     my $value = shift;
+    my $tablename = $self->tablename("commitmsgs");
 
-    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
+    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO $tablename (key, value) VALUES (?,?)",{},1);
     $insert_mergelog->execute($key, $value);
 }
 
 sub delete_head
 {
     my $self = shift;
+    my $tablename = $self->tablename("head");
 
-    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
+    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM $tablename",{},1);
     $delete_head->execute();
 }
 
@@ -2815,8 +2892,9 @@
     my $modified = shift;
     my $author = shift;
     my $mode = shift;
+    my $tablename = $self->tablename("head");
 
-    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
     $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
 }
 
@@ -2824,8 +2902,9 @@
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("head");
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT filehash, revision, mode FROM head WHERE name=?",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT filehash, revision, mode FROM $tablename WHERE name=?",{},1);
     $db_query->execute($filename);
     my ( $hash, $revision, $mode ) = $db_query->fetchrow_array;
 
@@ -2836,8 +2915,9 @@
 {
     my $self = shift;
     my $key = shift;
+    my $tablename = $self->tablename("properties");
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT value FROM properties WHERE key=?",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1);
     $db_query->execute($key);
     my ( $value ) = $db_query->fetchrow_array;
 
@@ -2849,13 +2929,14 @@
     my $self = shift;
     my $key = shift;
     my $value = shift;
+    my $tablename = $self->tablename("properties");
 
-    my $db_query = $self->{dbh}->prepare_cached("UPDATE properties SET value=? WHERE key=?",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("UPDATE $tablename SET value=? WHERE key=?",{},1);
     $db_query->execute($value, $key);
 
     unless ( $db_query->rows )
     {
-        $db_query = $self->{dbh}->prepare_cached("INSERT INTO properties (key, value) VALUES (?,?)",{},1);
+        $db_query = $self->{dbh}->prepare_cached("INSERT INTO $tablename (key, value) VALUES (?,?)",{},1);
         $db_query->execute($key, $value);
     }
 
@@ -2869,10 +2950,11 @@
 sub gethead
 {
     my $self = shift;
+    my $tablename = $self->tablename("head");
 
     return $self->{gethead_cache} if ( defined ( $self->{gethead_cache} ) );
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM head ORDER BY name ASC",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM $tablename ORDER BY name ASC",{},1);
     $db_query->execute();
 
     my $tree = [];
@@ -2894,8 +2976,9 @@
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("revision");
 
-    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, author, mode, revision, modified, commithash FROM revision WHERE name=? ORDER BY revision DESC",{},1);
+    my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, author, mode, revision, modified, commithash FROM $tablename WHERE name=? ORDER BY revision DESC",{},1);
     $db_query->execute($filename);
 
     my $tree = [];
@@ -2919,19 +3002,21 @@
     my $self = shift;
     my $filename = shift;
     my $revision = shift;
+    my $tablename_rev = $self->tablename("revision");
+    my $tablename_head = $self->tablename("head");
 
     my $db_query;
     if ( defined($revision) and $revision =~ /^\d+$/ )
     {
-        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM revision WHERE name=? AND revision=?",{},1);
+        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND revision=?",{},1);
         $db_query->execute($filename, $revision);
     }
     elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ )
     {
-        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM revision WHERE name=? AND commithash=?",{},1);
+        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND commithash=?",{},1);
         $db_query->execute($filename, $revision);
     } else {
-        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM head WHERE name=?",{},1);
+        $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_head WHERE name=?",{},1);
         $db_query->execute($filename);
     }
 
@@ -2947,11 +3032,12 @@
 {
     my $self = shift;
     my $commithash = shift;
+    my $tablename = $self->tablename("commitmsgs");
 
     die("Need commithash") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{40}$/ );
 
     my $db_query;
-    $db_query = $self->{dbh}->prepare_cached("SELECT value FROM commitmsgs WHERE key=?",{},1);
+    $db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1);
     $db_query->execute($commithash);
 
     my ( $message ) = $db_query->fetchrow_array;
@@ -2979,9 +3065,10 @@
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("revision");
 
     my $db_query;
-    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM revision WHERE name=? ORDER BY revision DESC",{},1);
+    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM $tablename WHERE name=? ORDER BY revision DESC",{},1);
     $db_query->execute($filename);
 
     return $db_query->fetchall_arrayref;
@@ -3001,9 +3088,10 @@
 {
     my $self = shift;
     my $filename = shift;
+    my $tablename = $self->tablename("revision");
 
     my $db_query;
-    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM revision WHERE name=? AND filehash!='deleted' ORDER BY revision DESC",{},1);
+    $db_query = $self->{dbh}->prepare_cached("SELECT revision, filehash, commithash FROM $tablename WHERE name=? AND filehash!='deleted' ORDER BY revision DESC",{},1);
     $db_query->execute($filename);
 
     return $db_query->fetchall_arrayref;
@@ -3061,4 +3149,19 @@
     return $dirname;
 }
 
+=head2 mangle_tablename
+
+create a string from a that is suitable to use as part of an SQL table
+name, mainly by converting all chars except \w to _
+
+=cut
+sub mangle_tablename {
+    my $tablename = shift;
+    return unless defined $tablename;
+
+    $tablename =~ s/[^\w_]/_/g;
+
+    return $tablename;
+}
+
 1;
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 010353a..d846cd9 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -58,8 +58,8 @@
 # "author" or "committer
 
 set_ident () {
-	lid="$(echo "$1" | tr "A-Z" "a-z")"
-	uid="$(echo "$1" | tr "a-z" "A-Z")"
+	lid="$(echo "$1" | tr "[A-Z]" "[a-z]")"
+	uid="$(echo "$1" | tr "[a-z]" "[A-Z]")"
 	pick_id_script='
 		/^'$lid' /{
 			s/'\''/'\''\\'\'\''/g
@@ -234,7 +234,7 @@
 	;;
 *)
 	git rev-list --reverse --topo-order --default HEAD \
-		--parents --full-history "$@" -- "$filter_subdir"
+		--parents "$@" -- "$filter_subdir"
 esac > ../revs || die "Could not get the commits"
 commits=$(wc -l <../revs | tr -d " ")
 
@@ -281,7 +281,7 @@
 			die "Could not checkout the index"
 		# files that $commit removed are now still in the working tree;
 		# remove them, else they would be added again
-		git ls-files -z --others | xargs -0 rm -f
+		git clean -d -q -f -x
 		eval "$filter_tree" < /dev/null ||
 			die "tree filter failed: $filter_tree"
 
@@ -309,7 +309,7 @@
 	sed -e '1,/^$/d' <../commit | \
 		eval "$filter_msg" > ../message ||
 			die "msg filter failed: $filter_msg"
-	sh -c "$filter_commit" "git commit-tree" \
+	@SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
 		$(git write-tree) $parentstr < ../message > ../map/$commit
 done <../revs
 
diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN
index cfe46a8..0ab478e 100755
--- a/git-gui/GIT-VERSION-GEN
+++ b/git-gui/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.9.GITGUI
+DEF_VER=0.10.GITGUI
 
 LF='
 '
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 4e32174..b19fb2d 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -221,14 +221,9 @@
 	MSGFMT ?= $(TCL_PATH) po/po2msg.sh
 else
 	MSGFMT ?= msgfmt
-	ifeq ($(shell $(MSGFMT) >/dev/null 2>&1 || echo $$?),127)
+	ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
 		MSGFMT := $(TCL_PATH) po/po2msg.sh
 	endif
-	ifeq (msgfmt,$(MSGFMT))
-	ifeq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null || echo $?),1)
-		MSGFMT := $(TCL_PATH) po/po2msg.sh
-	endif
-	endif
 endif
 
 msgsdir     = $(gg_libdir)/msgs
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 04bd425..7c25bb9 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -611,7 +611,9 @@
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.diffcontext) 5
+set default_config(gui.commitmsgwidth) 75
 set default_config(gui.newbranchtemplate) {}
+set default_config(gui.spellingdictionary) {}
 set default_config(gui.fontui) [font configure font_ui]
 set default_config(gui.fontdiff) [font configure font_diff]
 set font_descs {
@@ -1683,6 +1685,7 @@
 proc do_quit {} {
 	global ui_comm is_quitting repo_config commit_type
 	global GITGUI_BCK_exists GITGUI_BCK_i
+	global ui_comm_spell
 
 	if {$is_quitting} return
 	set is_quitting 1
@@ -1710,6 +1713,12 @@
 			}
 		}
 
+		# -- Cancel our spellchecker if its running.
+		#
+		if {[info exists ui_comm_spell]} {
+			$ui_comm_spell stop
+		}
+
 		# -- Remove our editor backup, its not needed.
 		#
 		after cancel $GITGUI_BCK_i
@@ -1837,6 +1846,22 @@
 	$w tag add in_sel $begin.0 [expr {$end + 1}].0
 }
 
+proc show_more_context {} {
+	global repo_config
+	if {$repo_config(gui.diffcontext) < 99} {
+		incr repo_config(gui.diffcontext)
+		reshow_diff
+	}
+}
+
+proc show_less_context {} {
+	global repo_config
+	if {$repo_config(gui.diffcontext) >= 1} {
+		incr repo_config(gui.diffcontext) -1
+		reshow_diff
+	}
+}
+
 ######################################################################
 ##
 ## ui construction
@@ -2037,6 +2062,16 @@
 
 	.mbar.commit add separator
 
+	.mbar.commit add command -label [mc "Show Less Context"] \
+		-command show_less_context \
+		-accelerator $M1T-\-
+
+	.mbar.commit add command -label [mc "Show More Context"] \
+		-command show_more_context \
+		-accelerator $M1T-=
+
+	.mbar.commit add separator
+
 	.mbar.commit add command -label [mc "Sign Off"] \
 		-command do_signoff \
 		-accelerator $M1T-S
@@ -2080,7 +2115,7 @@
 if {[is_MacOSX]} {
 	# -- Apple Menu (Mac OS X only)
 	#
-	.mbar add cascade -label [mc Apple] -menu .mbar.apple
+	.mbar add cascade -label Apple -menu .mbar.apple
 	menu .mbar.apple
 
 	.mbar.apple add command -label [mc "About %s" [appname]] \
@@ -2281,8 +2316,9 @@
 #
 frame .vpane.files.index -height 100 -width 200
 label .vpane.files.index.title -text [mc "Staged Changes (Will Commit)"] \
-	-background lightgreen
-text $ui_index -background white -borderwidth 0 \
+	-background lightgreen -foreground black
+text $ui_index -background white -foreground black \
+	-borderwidth 0 \
 	-width 20 -height 10 \
 	-wrap none \
 	-cursor $cursor_ptr \
@@ -2300,8 +2336,9 @@
 #
 frame .vpane.files.workdir -height 100 -width 200
 label .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
-	-background lightsalmon
-text $ui_workdir -background white -borderwidth 0 \
+	-background lightsalmon -foreground black
+text $ui_workdir -background white -foreground black \
+	-borderwidth 0 \
 	-width 20 -height 10 \
 	-wrap none \
 	-cursor $cursor_ptr \
@@ -2408,12 +2445,13 @@
 pack .vpane.lower.commarea.buffer.header.amend -side right
 pack .vpane.lower.commarea.buffer.header.new -side right
 
-text $ui_comm -background white -borderwidth 1 \
+text $ui_comm -background white -foreground black \
+	-borderwidth 1 \
 	-undo true \
 	-maxundo 20 \
 	-autoseparators true \
 	-relief sunken \
-	-width 75 -height 9 -wrap none \
+	-width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
 	-font font_diff \
 	-yscrollcommand {.vpane.lower.commarea.buffer.sby set}
 scrollbar .vpane.lower.commarea.buffer.sby \
@@ -2454,7 +2492,7 @@
 $ctxm add command \
 	-label [mc "Sign Off"] \
 	-command do_signoff
-bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
+set ui_comm_ctxm $ctxm
 
 # -- Diff Header
 #
@@ -2485,15 +2523,18 @@
 frame .vpane.lower.diff.header -background gold
 label .vpane.lower.diff.header.status \
 	-background gold \
+	-foreground black \
 	-width $max_status_desc \
 	-anchor w \
 	-justify left
 label .vpane.lower.diff.header.file \
 	-background gold \
+	-foreground black \
 	-anchor w \
 	-justify left
 label .vpane.lower.diff.header.path \
 	-background gold \
+	-foreground black \
 	-anchor w \
 	-justify left
 pack .vpane.lower.diff.header.status -side left
@@ -2517,7 +2558,8 @@
 #
 frame .vpane.lower.diff.body
 set ui_diff .vpane.lower.diff.body.t
-text $ui_diff -background white -borderwidth 0 \
+text $ui_diff -background white -foreground black \
+	-borderwidth 0 \
 	-width 80 -height 15 -wrap none \
 	-font font_diff \
 	-xscrollcommand {.vpane.lower.diff.body.sbx set} \
@@ -2577,17 +2619,11 @@
 $ctxm add separator
 $ctxm add command \
 	-label [mc "Show Less Context"] \
-	-command {if {$repo_config(gui.diffcontext) >= 1} {
-		incr repo_config(gui.diffcontext) -1
-		reshow_diff
-	}}
+	-command show_less_context
 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
 $ctxm add command \
 	-label [mc "Show More Context"] \
-	-command {if {$repo_config(gui.diffcontext) < 99} {
-		incr repo_config(gui.diffcontext)
-		reshow_diff
-	}}
+	-command show_more_context
 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
 $ctxm add separator
 $ctxm add command \
@@ -2679,6 +2715,11 @@
 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+bind $ui_comm <$M1B-Key-minus> {show_less_context;break}
+bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context;break}
+bind $ui_comm <$M1B-Key-equal> {show_more_context;break}
+bind $ui_comm <$M1B-Key-plus> {show_more_context;break}
+bind $ui_comm <$M1B-Key-KP_Add> {show_more_context;break}
 
 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
@@ -2722,6 +2763,11 @@
 bind .   <$M1B-Key-T> do_add_selection
 bind .   <$M1B-Key-i> do_add_all
 bind .   <$M1B-Key-I> do_add_all
+bind .   <$M1B-Key-minus> {show_less_context;break}
+bind .   <$M1B-Key-KP_Subtract> {show_less_context;break}
+bind .   <$M1B-Key-equal> {show_more_context;break}
+bind .   <$M1B-Key-plus> {show_more_context;break}
+bind .   <$M1B-Key-KP_Add> {show_more_context;break}
 bind .   <$M1B-Key-Return> do_commit
 foreach i [list $ui_index $ui_workdir] {
 	bind $i <Button-1>       "toggle_or_diff         $i %x %y; break"
@@ -2857,6 +2903,30 @@
 	}
 
 	backup_commit_buffer
+
+	# -- If the user has aspell available we can drive it
+	#    in pipe mode to spellcheck the commit message.
+	#
+	set spell_cmd [list |]
+	set spell_dict [get_config gui.spellingdictionary]
+	lappend spell_cmd aspell
+	if {$spell_dict ne {}} {
+		lappend spell_cmd --master=$spell_dict
+	}
+	lappend spell_cmd --mode=none
+	lappend spell_cmd --encoding=utf-8
+	lappend spell_cmd pipe
+	if {$spell_dict eq {none}
+	 || [catch {set spell_fd [open $spell_cmd r+]} spell_err]} {
+		bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y]
+	} else {
+		set ui_comm_spell [spellcheck::init \
+			$spell_fd \
+			$ui_comm \
+			$ui_comm_ctxm \
+		]
+	}
+	unset -nocomplain spell_cmd spell_fd spell_err spell_dict
 }
 
 lock_index begin-read
diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl
index 719fc54..241ab89 100644
--- a/git-gui/lib/about.tcl
+++ b/git-gui/lib/about.tcl
@@ -4,6 +4,7 @@
 proc do_about {} {
 	global appvers copyright oguilib
 	global tcl_patchLevel tk_patchLevel
+	global ui_comm_spell
 
 	set w .about_dialog
 	toplevel $w
@@ -40,6 +41,11 @@
 		append v "Tcl version $tcl_patchLevel"
 		append v ", Tk version $tk_patchLevel"
 	}
+	if {[info exists ui_comm_spell]
+		&& [$ui_comm_spell version] ne {}} {
+		append v "\n"
+		append v [$ui_comm_spell version]
+	}
 
 	set d {}
 	append d "git wrapper: $::_git\n"
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl
index 00ecf21..92fac1b 100644
--- a/git-gui/lib/blame.tcl
+++ b/git-gui/lib/blame.tcl
@@ -80,6 +80,7 @@
 	label $w.header.commit_l \
 		-text [mc "Commit:"] \
 		-background gold \
+		-foreground black \
 		-anchor w \
 		-justify left
 	set w_back $w.header.commit_b
@@ -89,6 +90,7 @@
 		-relief flat \
 		-state disabled \
 		-background gold \
+		-foreground black \
 		-activebackground gold
 	bind $w_back <Button-1> "
 		if {\[$w_back cget -state\] eq {normal}} {
@@ -98,16 +100,19 @@
 	label $w.header.commit \
 		-textvariable @commit \
 		-background gold \
+		-foreground black \
 		-anchor w \
 		-justify left
 	label $w.header.path_l \
 		-text [mc "File:"] \
 		-background gold \
+		-foreground black \
 		-anchor w \
 		-justify left
 	set w_path $w.header.path
 	label $w_path \
 		-background gold \
+		-foreground black \
 		-anchor w \
 		-justify left
 	pack $w.header.commit_l -side left
@@ -135,7 +140,9 @@
 		-takefocus 0 \
 		-highlightthickness 0 \
 		-padx 0 -pady 0 \
-		-background white -borderwidth 0 \
+		-background white \
+		-foreground black \
+		-borderwidth 0 \
 		-state disabled \
 		-wrap none \
 		-height 40 \
@@ -148,7 +155,9 @@
 		-takefocus 0 \
 		-highlightthickness 0 \
 		-padx 0 -pady 0 \
-		-background white -borderwidth 0 \
+		-background white \
+		-foreground black \
+		-borderwidth 0 \
 		-state disabled \
 		-wrap none \
 		-height 40 \
@@ -166,7 +175,9 @@
 		-takefocus 0 \
 		-highlightthickness 0 \
 		-padx 0 -pady 0 \
-		-background white -borderwidth 0 \
+		-background white \
+		-foreground black \
+		-borderwidth 0 \
 		-state disabled \
 		-wrap none \
 		-height 40 \
@@ -184,7 +195,9 @@
 		-takefocus 0 \
 		-highlightthickness 0 \
 		-padx 0 -pady 0 \
-		-background white -borderwidth 0 \
+		-background white \
+		-foreground black \
+		-borderwidth 0 \
 		-state disabled \
 		-wrap none \
 		-height 40 \
@@ -213,7 +226,9 @@
 
 	set w_cviewer $w.file_pane.cm.t
 	text $w_cviewer \
-		-background white -borderwidth 0 \
+		-background white \
+		-foreground black \
+		-borderwidth 0 \
 		-state disabled \
 		-wrap none \
 		-height 10 \
diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl
index 53d5a62..ab470d1 100644
--- a/git-gui/lib/browser.tcl
+++ b/git-gui/lib/browser.tcl
@@ -39,7 +39,8 @@
 
 	frame $w.list
 	set w_list $w.list.l
-	text $w_list -background white -borderwidth 0 \
+	text $w_list -background white -foreground black \
+		-borderwidth 0 \
 		-cursor $cursor_ptr \
 		-state disabled \
 		-wrap none \
diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl
index f243966..6e14117 100644
--- a/git-gui/lib/checkout_op.tcl
+++ b/git-gui/lib/checkout_op.tcl
@@ -280,7 +280,7 @@
 	} elseif {[is_config_true gui.trustmtime]} {
 		_readtree $this
 	} else {
-		ui_status {Refreshing file status...}
+		ui_status [mc "Refreshing file status..."]
 		set fd [git_read update-index \
 			-q \
 			--unmerged \
@@ -320,7 +320,7 @@
 	set readtree_d {}
 	$::main_status start \
 		[mc "Updating working directory to '%s'..." [_name $this]] \
-		{files checked out}
+		[mc "files checked out"]
 
 	set fd [git_read --stderr read-tree \
 		-m \
@@ -447,7 +447,7 @@
 	} else {
 		repository_state commit_type HEAD MERGE_HEAD
 		set PARENT $HEAD
-		ui_status "Checked out '$name'."
+		ui_status [mc "Checked out '%s'." $name]
 	}
 	delete_this
 }
diff --git a/git-gui/lib/choose_font.tcl b/git-gui/lib/choose_font.tcl
index 0c4051b..56443b0 100644
--- a/git-gui/lib/choose_font.tcl
+++ b/git-gui/lib/choose_font.tcl
@@ -55,6 +55,7 @@
 	set w_family $w.inner.family.v
 	text $w_family \
 		-background white \
+		-foreground black \
 		-borderwidth 1 \
 		-relief sunken \
 		-cursor $::cursor_ptr \
@@ -92,6 +93,7 @@
 	set w_example $w.example.t
 	text $w_example \
 		-background white \
+		-foreground black \
 		-borderwidth 1 \
 		-relief sunken \
 		-height 3 \
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index 0adcf9d..ae4a4cd 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -38,7 +38,7 @@
 		menu $m_repo
 
 		if {[is_MacOSX]} {
-			$w.mbar add cascade -label [mc Apple] -menu .mbar.apple
+			$w.mbar add cascade -label Apple -menu .mbar.apple
 			menu $w.mbar.apple
 			$w.mbar.apple add command \
 				-label [mc "About %s" [appname]] \
diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl
index 947b201..40a7103 100644
--- a/git-gui/lib/commit.tcl
+++ b/git-gui/lib/commit.tcl
@@ -218,7 +218,7 @@
 		return
 	}
 
-	ui_status {Calling pre-commit hook...}
+	ui_status [mc "Calling pre-commit hook..."]
 	set pch_error {}
 	fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
 	fileevent $fd_ph readable \
@@ -233,7 +233,7 @@
 	if {[eof $fd_ph]} {
 		if {[catch {close $fd_ph}]} {
 			catch {file delete $msg_p}
-			ui_status {Commit declined by pre-commit hook.}
+			ui_status [mc "Commit declined by pre-commit hook."]
 			hook_failed_popup pre-commit $pch_error
 			unlock_index
 		} else {
@@ -256,7 +256,7 @@
 		return
 	}
 
-	ui_status {Calling commit-msg hook...}
+	ui_status [mc "Calling commit-msg hook..."]
 	set pch_error {}
 	fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
 	fileevent $fd_ph readable \
@@ -271,7 +271,7 @@
 	if {[eof $fd_ph]} {
 		if {[catch {close $fd_ph}]} {
 			catch {file delete $msg_p}
-			ui_status {Commit declined by commit-msg hook.}
+			ui_status [mc "Commit declined by commit-msg hook."]
 			hook_failed_popup commit-msg $pch_error
 			unlock_index
 		} else {
@@ -284,7 +284,7 @@
 }
 
 proc commit_writetree {curHEAD msg_p} {
-	ui_status {Committing changes...}
+	ui_status [mc "Committing changes..."]
 	set fd_wt [git_read write-tree]
 	fileevent $fd_wt readable \
 		[list commit_committree $fd_wt $curHEAD $msg_p]
@@ -301,7 +301,7 @@
 	if {[catch {close $fd_wt} err]} {
 		catch {file delete $msg_p}
 		error_popup [strcat [mc "write-tree failed:"] "\n\n$err"]
-		ui_status {Commit failed.}
+		ui_status [mc "Commit failed."]
 		unlock_index
 		return
 	}
@@ -345,7 +345,7 @@
 	if {[catch {set cmt_id [eval git $cmd]} err]} {
 		catch {file delete $msg_p}
 		error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"]
-		ui_status {Commit failed.}
+		ui_status [mc "Commit failed."]
 		unlock_index
 		return
 	}
@@ -365,7 +365,7 @@
 		} err]} {
 		catch {file delete $msg_p}
 		error_popup [strcat [mc "update-ref failed:"] "\n\n$err"]
-		ui_status {Commit failed.}
+		ui_status [mc "Commit failed."]
 		unlock_index
 		return
 	}
diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl
index 5597188..c112464 100644
--- a/git-gui/lib/console.tcl
+++ b/git-gui/lib/console.tcl
@@ -46,7 +46,9 @@
 		-justify left \
 		-font font_uibold
 	text $w_t \
-		-background white -borderwidth 1 \
+		-background white \
+		-foreground black \
+		-borderwidth 1 \
 		-relief sunken \
 		-width 80 -height 10 \
 		-wrap none \
@@ -180,7 +182,8 @@
 	if {$ok} {
 		if {[winfo exists $w.m.s]} {
 			bind $w.m.s <Destroy> [list delete_this $this]
-			$w.m.s conf -background green -text [mc "Success"]
+			$w.m.s conf -background green -foreground black \
+				-text [mc "Success"]
 			if {$is_toplevel} {
 				$w.ok conf -state normal
 				focus $w.ok
@@ -193,7 +196,8 @@
 			_init $this
 		}
 		bind $w.m.s <Destroy> [list delete_this $this]
-		$w.m.s conf -background red -text [mc "Error: Command Failed"]
+		$w.m.s conf -background red -foreground black \
+			-text [mc "Error: Command Failed"]
 		if {$is_toplevel} {
 			$w.ok conf -state normal
 			focus $w.ok
diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl
index 8c27678..7565015 100644
--- a/git-gui/lib/error.tcl
+++ b/git-gui/lib/error.tcl
@@ -80,7 +80,9 @@
 		-justify left \
 		-font font_uibold
 	text $w.m.t \
-		-background white -borderwidth 1 \
+		-background white \
+		-foreground black \
+		-borderwidth 1 \
 		-relief sunken \
 		-width 80 -height 10 \
 		-font font_diff \
diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl
index 30a244c..3c1fce7 100644
--- a/git-gui/lib/index.tcl
+++ b/git-gui/lib/index.tcl
@@ -310,7 +310,7 @@
 		update_index \
 			$txt \
 			$pathList \
-			[concat $after {ui_status {Ready to commit.}}]
+			[concat $after {ui_status [mc "Ready to commit."]}]
 	}
 }
 
diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl
index 63e1427..cc26b07 100644
--- a/git-gui/lib/merge.tcl
+++ b/git-gui/lib/merge.tcl
@@ -116,8 +116,7 @@
 	lappend cmd HEAD
 	lappend cmd $name
 
-	set msg [mc "Merging %s and %s" $current_branch $stitle]
-	ui_status "$msg..."
+	ui_status [mc "Merging %s and %s..." $current_branch $stitle]
 	set cons [console::new [mc "Merge"] "merge $stitle"]
 	console::exec $cons $cmd [cb _finish $cons]
 
@@ -236,7 +235,7 @@
 		set fd [git_read --stderr read-tree --reset -u -v HEAD]
 		fconfigure $fd -blocking 0 -translation binary
 		fileevent $fd readable [namespace code [list _reset_wait $fd]]
-		$::main_status start [mc "Aborting"] {files reset}
+		$::main_status start [mc "Aborting"] [mc "files reset"]
 	} else {
 		unlock_index
 	}
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl
index f812e5e..9270512 100644
--- a/git-gui/lib/option.tcl
+++ b/git-gui/lib/option.tcl
@@ -5,6 +5,7 @@
 	global default_config font_descs
 	global repo_config global_config
 	global repo_config_new global_config_new
+	global ui_comm_spell
 
 	foreach option $font_descs {
 		set name [lindex $option 0]
@@ -52,11 +53,23 @@
 			set repo_config($name) $value
 		}
 	}
+
+	if {[info exists repo_config(gui.spellingdictionary)]} {
+		set value $repo_config(gui.spellingdictionary)
+		if {$value eq {none}} {
+			if {[info exists ui_comm_spell]} {
+				$ui_comm_spell stop
+			}
+		} elseif {[info exists ui_comm_spell]} {
+			$ui_comm_spell lang $value
+		}
+	}
 }
 
 proc do_options {} {
 	global repo_config global_config font_descs
 	global repo_config_new global_config_new
+	global ui_comm_spell
 
 	array unset repo_config_new
 	array unset global_config_new
@@ -111,6 +124,7 @@
 		{b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
 		{b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
 		{i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
+		{i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
 		{t gui.newbranchtemplate {mc "New Branch Name Template"}}
 		} {
 		set type [lindex $option 0]
@@ -159,6 +173,32 @@
 		}
 	}
 
+	set all_dicts [linsert \
+		[spellcheck::available_langs] \
+		0 \
+		none]
+	incr optid
+	foreach f {repo global} {
+		if {![info exists ${f}_config_new(gui.spellingdictionary)]} {
+			if {[info exists ui_comm_spell]} {
+				set value [$ui_comm_spell lang]
+			} else {
+				set value none
+			}
+			set ${f}_config_new(gui.spellingdictionary) $value
+		}
+
+		frame $w.$f.$optid
+		label $w.$f.$optid.l -text [mc "Spelling Dictionary:"]
+		eval tk_optionMenu $w.$f.$optid.v \
+			${f}_config_new(gui.spellingdictionary) \
+			$all_dicts
+		pack $w.$f.$optid.l -side left -anchor w -fill x
+		pack $w.$f.$optid.v -side right -anchor e -padx 5
+		pack $w.$f.$optid -side top -anchor w -fill x
+	}
+	unset all_dicts
+
 	set all_fonts [lsort [font families]]
 	foreach option $font_descs {
 		set name [lindex $option 0]
diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl
new file mode 100644
index 0000000..9be7486
--- /dev/null
+++ b/git-gui/lib/spellcheck.tcl
@@ -0,0 +1,408 @@
+# git-gui spellchecking support through ispell/aspell
+# Copyright (C) 2008 Shawn Pearce
+
+class spellcheck {
+
+field s_fd      {} ; # pipe to ispell/aspell
+field s_version {} ; # ispell/aspell version string
+field s_lang    {} ; # current language code
+field s_prog aspell; # are we actually old ispell?
+field s_failed   0 ; # is $s_prog bogus and not working?
+
+field w_text      ; # text widget we are spelling
+field w_menu      ; # context menu for the widget
+field s_menuidx 0 ; # last index of insertion into $w_menu
+
+field s_i           {} ; # timer registration for _run callbacks
+field s_clear        0 ; # did we erase mispelled tags yet?
+field s_seen    [list] ; # lines last seen from $w_text in _run
+field s_checked [list] ; # lines already checked
+field s_pending [list] ; # [$line $data] sent to ispell/aspell
+field s_suggest        ; # array, list of suggestions, keyed by misspelling
+
+constructor init {pipe_fd ui_text ui_menu} {
+	set w_text $ui_text
+	set w_menu $ui_menu
+	array unset s_suggest
+
+	bind_button3 $w_text [cb _popup_suggest %X %Y @%x,%y]
+	_connect $this $pipe_fd
+	return $this
+}
+
+method _connect {pipe_fd} {
+	fconfigure $pipe_fd \
+		-encoding utf-8 \
+		-eofchar {} \
+		-translation lf
+
+	if {[gets $pipe_fd s_version] <= 0} {
+		if {[catch {close $pipe_fd} err]} {
+
+			# Eh?  Is this actually ispell choking on aspell options?
+			#
+			if {$s_prog eq {aspell}
+				&& [regexp -nocase {^Usage: } $err]
+				&& ![catch {
+						set pipe_fd [open [list | $s_prog -v] r]
+						gets $pipe_fd s_version
+						close $pipe_fd
+				}]
+				&& $s_version ne {}} {
+				if {{@(#) } eq [string range $s_version 0 4]} {
+					set s_version [string range $s_version 5 end]
+				}
+				set s_failed 1
+				error_popup [strcat \
+					[mc "Unsupported spell checker"] \
+					":\n\n$s_version"]
+				set s_version {}
+				return
+			}
+
+			regsub -nocase {^Error: } $err {} err
+			if {$s_fd eq {}} {
+				error_popup [strcat [mc "Spell checking is unavailable"] ":\n\n$err"]
+			} else {
+				error_popup [strcat \
+					[mc "Invalid spell checking configuration"] \
+					":\n\n$err\n\n" \
+					[mc "Reverting dictionary to %s." $s_lang]]
+			}
+		} else {
+			error_popup [mc "Spell checker silently failed on startup"]
+		}
+		return
+	}
+
+	if {{@(#) } ne [string range $s_version 0 4]} {
+		catch {close $pipe_fd}
+		error_popup [strcat [mc "Unrecognized spell checker"] ":\n\n$s_version"]
+		return
+	}
+	set s_version [string range $s_version 5 end]
+	regexp \
+		{International Ispell Version .* \(but really (Aspell .*?)\)$} \
+		$s_version _junk s_version
+
+	puts $pipe_fd !             ; # enable terse mode
+	puts $pipe_fd {$$cr master} ; # fetch the language
+	flush $pipe_fd
+
+	gets $pipe_fd s_lang
+	regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang
+
+	if {$::default_config(gui.spellingdictionary) eq {}
+	 && [get_config gui.spellingdictionary] eq {}} {
+		set ::default_config(gui.spellingdictionary) $s_lang
+	}
+
+	if {$s_fd ne {}} {
+		catch {close $s_fd}
+	}
+	set s_fd $pipe_fd
+
+	fconfigure $s_fd -blocking 0
+	fileevent $s_fd readable [cb _read]
+
+	$w_text tag conf misspelled \
+		-foreground red \
+		-underline 1
+
+	array unset s_suggest
+	set s_seen    [list]
+	set s_checked [list]
+	set s_pending [list]
+	_run $this
+}
+
+method lang {{n {}}} {
+	if {$n ne {} && $s_lang ne $n && !$s_failed} {
+		set spell_cmd [list |]
+		lappend spell_cmd aspell
+		lappend spell_cmd --master=$n
+		lappend spell_cmd --mode=none
+		lappend spell_cmd --encoding=UTF-8
+		lappend spell_cmd pipe
+		_connect $this [open $spell_cmd r+]
+	}
+	return $s_lang
+}
+
+method version {} {
+	if {$s_version ne {}} {
+		return "$s_version, $s_lang"
+	}
+	return {}
+}
+
+method stop {} {
+	while {$s_menuidx > 0} {
+		$w_menu delete 0
+		incr s_menuidx -1
+	}
+	$w_text tag delete misspelled
+
+	catch {close $s_fd}
+	catch {after cancel $s_i}
+	set s_fd {}
+	set s_i {}
+	set s_lang {}
+}
+
+method _popup_suggest {X Y pos} {
+	while {$s_menuidx > 0} {
+		$w_menu delete 0
+		incr s_menuidx -1
+	}
+
+	set b_loc [$w_text index "$pos wordstart"]
+	set e_loc [_wordend $this $b_loc]
+	set orig  [$w_text get $b_loc $e_loc]
+	set tags  [$w_text tag names $b_loc]
+
+	if {[lsearch -exact $tags misspelled] >= 0} {
+		if {[info exists s_suggest($orig)]} {
+			set cnt 0
+			foreach s $s_suggest($orig) {
+				if {$cnt < 5} {
+					$w_menu insert $s_menuidx command \
+						-label $s \
+						-command [cb _replace $b_loc $e_loc $s]
+					incr s_menuidx
+					incr cnt
+				} else {
+					break
+				}
+			}
+		} else {
+			$w_menu insert $s_menuidx command \
+				-label [mc "No Suggestions"] \
+				-state disabled
+			incr s_menuidx
+		}
+		$w_menu insert $s_menuidx separator
+		incr s_menuidx
+	}
+
+	$w_text mark set saved-insert insert
+	tk_popup $w_menu $X $Y
+}
+
+method _replace {b_loc e_loc word} {
+	$w_text configure -autoseparators 0
+	$w_text edit separator
+
+	$w_text delete $b_loc $e_loc
+	$w_text insert $b_loc $word
+
+	$w_text edit separator
+	$w_text configure -autoseparators 1
+	$w_text mark set insert saved-insert
+}
+
+method _restart_timer {} {
+	set s_i [after 300 [cb _run]]
+}
+
+proc _match_length {max_line arr_name} {
+	upvar $arr_name a
+
+	if {[llength $a] > $max_line} {
+		set a [lrange $a 0 $max_line]
+	}
+	while {[llength $a] <= $max_line} {
+		lappend a {}
+	}
+}
+
+method _wordend {pos} {
+	set pos  [$w_text index "$pos wordend"]
+	set tags [$w_text tag names $pos]
+	while {[lsearch -exact $tags misspelled] >= 0} {
+		set pos  [$w_text index "$pos +1c"]
+		set tags [$w_text tag names $pos]
+	}
+	return $pos
+}
+
+method _run {} {
+	set cur_pos  [$w_text index {insert -1c}]
+	set cur_line [lindex [split $cur_pos .] 0]
+	set max_line [lindex [split [$w_text index end] .] 0]
+	_match_length $max_line s_seen
+	_match_length $max_line s_checked
+
+	# Nothing in the message buffer?  Nothing to spellcheck.
+	#
+	if {$cur_line == 1
+	 && $max_line == 2
+	 && [$w_text get 1.0 end] eq "\n"} {
+		array unset s_suggest
+		_restart_timer $this
+		return
+	}
+
+	set active 0
+	for {set n 1} {$n <= $max_line} {incr n} {
+		set s [$w_text get "$n.0" "$n.end"]
+
+		# Don't spellcheck the current line unless we are at
+		# a word boundary.  The user might be typing on it.
+		#
+		if {$n == $cur_line
+		 && ![regexp {^\W$} [$w_text get $cur_pos insert]]} {
+
+			# If the current word is mispelled remove the tag
+			# but force a spellcheck later.
+			#
+			set tags [$w_text tag names $cur_pos]
+			if {[lsearch -exact $tags misspelled] >= 0} {
+				$w_text tag remove misspelled \
+					"$cur_pos wordstart" \
+					[_wordend $this $cur_pos]
+				lset s_seen    $n $s
+				lset s_checked $n {}
+			}
+
+			continue
+		}
+
+		if {[lindex $s_seen    $n] eq $s
+		 && [lindex $s_checked $n] ne $s} {
+			# Don't send empty lines to Aspell it doesn't check them.
+			#
+			if {$s eq {}} {
+				lset s_checked $n $s
+				continue
+			}
+
+			# Don't send typical s-b-o lines as the emails are
+			# almost always misspelled according to Aspell.
+			#
+			if {[regexp -nocase {^[a-z-]+-by:.*<.*@.*>$} $s]} {
+				$w_text tag remove misspelled "$n.0" "$n.end"
+				lset s_checked $n $s
+				continue
+			}
+
+			puts $s_fd ^$s
+			lappend s_pending [list $n $s]
+			set active 1
+		} else {
+			# Delay until another idle loop to make sure we don't
+			# spellcheck lines the user is actively changing.
+			#
+			lset s_seen $n $s
+		}
+	}
+
+	if {$active} {
+		set s_clear 1
+		flush $s_fd
+	} else {
+		_restart_timer $this
+	}
+}
+
+method _read {} {
+	while {[gets $s_fd line] >= 0} {
+		set lineno [lindex $s_pending 0 0]
+
+		if {$s_clear} {
+			$w_text tag remove misspelled "$lineno.0" "$lineno.end"
+			set s_clear 0
+		}
+
+		if {$line eq {}} {
+			lset s_checked $lineno [lindex $s_pending 0 1]
+			set s_pending [lrange $s_pending 1 end]
+			set s_clear 1
+			continue
+		}
+
+		set sugg [list]
+		switch -- [string range $line 0 1] {
+		{& } {
+			set line [split [string range $line 2 end] :]
+			set info [split [lindex $line 0] { }]
+			set orig [lindex $info 0]
+			set offs [lindex $info 2]
+			foreach s [split [lindex $line 1] ,] {
+				lappend sugg [string range $s 1 end]
+			}
+		}
+		{# } {
+			set info [split [string range $line 2 end] { }]
+			set orig [lindex $info 0]
+			set offs [lindex $info 1]
+		}
+		default {
+			puts stderr "<spell> $line"
+			continue
+		}
+		}
+
+		incr offs -1
+		set b_loc "$lineno.$offs"
+		set e_loc [$w_text index "$lineno.$offs wordend"]
+		set curr [$w_text get $b_loc $e_loc]
+
+		# At least for English curr = "bob", orig = "bob's"
+		# so Tk didn't include the 's but Aspell did.  We
+		# try to round out the word.
+		#
+		while {$curr ne $orig
+		 && [string equal -length [string length $curr] $curr $orig]} {
+			set n_loc  [$w_text index "$e_loc +1c"]
+			set n_curr [$w_text get $b_loc $n_loc]
+			if {$n_curr eq $curr} {
+				break
+			}
+			set curr  $n_curr
+			set e_loc $n_loc
+		}
+
+		if {$curr eq $orig} {
+			$w_text tag add misspelled $b_loc $e_loc
+			if {[llength $sugg] > 0} {
+				set s_suggest($orig) $sugg
+			} else {
+				unset -nocomplain s_suggest($orig)
+			}
+		} else {
+			unset -nocomplain s_suggest($orig)
+		}
+	}
+
+	fconfigure $s_fd -block 1
+	if {[eof $s_fd]} {
+		if {![catch {close $s_fd} err]} {
+			set err [mc "Unexpected EOF from spell checker"]
+		}
+		catch {after cancel $s_i}
+		$w_text tag remove misspelled 1.0 end
+		error_popup [strcat [mc "Spell Checker Failed"] "\n\n" $err]
+		return
+	}
+	fconfigure $s_fd -block 0
+
+	if {[llength $s_pending] == 0} {
+		_restart_timer $this
+	}
+}
+
+proc available_langs {} {
+	set langs [list]
+	catch {
+		set fd [open [list | aspell dump dicts] r]
+		while {[gets $fd line] >= 0} {
+			if {$line eq {}} continue
+			lappend langs $line
+		}
+		close $fd
+	}
+	return $langs
+}
+
+}
diff --git a/git-gui/po/README b/git-gui/po/README
index 9d8b736..5e77a7d 100644
--- a/git-gui/po/README
+++ b/git-gui/po/README
@@ -178,18 +178,6 @@
 
 	$ msgmerge -U po/af.po po/git-gui.pot
 
-[NEEDSWORK: who is responsible for updating po/git-gui.pot file by
-running xgettext?  IIRC, Christian recommended against running it
-nilly-willy because it can become a source of unnecessary merge
-conflicts.  Perhaps we should mention something like "
-
-The po/git-gui.pot file is updated by the internationalization
-coordinator from time to time.  You _could_ update it yourself, but
-translators are discouraged from doing so because we would want all
-language teams to be working off of the same version of git-gui.pot.
-
-" here?]
-
 This updates po/af.po (again, replace "af" with your language
 code) so that it contains msgid lines (i.e. the original) that
 your translation did not have before.  There are a few things to
@@ -207,3 +195,53 @@
 
  - New messages added to the software will have msgstr lines with empty
    strings.  You would need to translate them.
+
+The po/git-gui.pot file is updated by the internationalization
+coordinator from time to time.  You _could_ update it yourself, but
+translators are discouraged from doing so because we would want all
+language teams to be working off of the same version of git-gui.pot.
+
+****************************************************************
+
+This section is a note to the internationalization coordinator, and
+translators do not have to worry about it too much.
+
+The message template file po/git-gui.pot needs to be kept up to date
+relative to the software the translations apply to, and it is the
+responsibility of the internationalization coordinator.
+
+When updating po/git-gui.pot file, however, _never_ run "msgmerge -U
+po/xx.po" for individual language translations, unless you are absolutely
+sure that there is no outstanding work on translation for language xx.
+Doing so will create unnecessary merge conflicts and force needless
+re-translation on translators.  The translator however may not have access
+to the msgmerge tool, in which case the coordinator may run it for the
+translator as a service.
+
+But mistakes do happen.  Suppose a translation was based on an older
+version X, the POT file was updated at version Y and then msgmerge was run
+at version Z for the language, and the translator sent in a patch based on
+version X:
+
+         ? translated
+        /
+    ---X---Y---Z (master)
+
+The coordinator could recover from such a mistake by first applying the
+patch to X, replace the translated file in Z, and then running msgmerge
+again based on the updated POT file and commit the result.  The sequence
+would look like this:
+
+    $ git checkout X
+    $ git am -s xx.patch
+    $ git checkout master
+    $ git checkout HEAD@{1} po/xx.po
+    $ msgmerge -U po/xx.po po/git-gui.pot
+    $ git commit -c HEAD@{1} po/xx.po
+
+State in the message that the translated messages are based on a slightly
+older version, and msgmerge was run to incorporate changes to message
+templates from the updated POT file.  The result needs to be further
+translated, but at least the messages that were updated by the patch that
+were not changed by the POT update will survive the process and do not
+need to be re-translated.
diff --git a/git-gui/po/de.po b/git-gui/po/de.po
index 2dfe07e..022b816 100644
--- a/git-gui/po/de.po
+++ b/git-gui/po/de.po
@@ -7,41 +7,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
-"PO-Revision-Date: 2008-01-15 20:33+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2008-02-16 21:52+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: Programmfehler"
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Ungültige Zeichensatz-Angabe in %s:"
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "Programmschriftart"
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "Vergleich-Schriftart"
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "Git kann im PATH nicht gefunden werden."
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Git Versionsangabe kann nicht erkannt werden:"
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -60,81 +60,81 @@
 "\n"
 "Soll angenommen werden, »%s« sei Version 1.5.0?\n"
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "Git-Verzeichnis nicht gefunden:"
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr ""
 "Es konnte nicht in das oberste Verzeichnis der Arbeitskopie gewechselt "
 "werden:"
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "Unerwartete Struktur des .git Verzeichnis:"
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "Kein Arbeitsverzeichnis"
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "Dateistatus aktualisieren..."
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "Nach geänderten Dateien suchen..."
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Bereit."
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "Unverändert"
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "Verändert, nicht bereitgestellt"
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "Bereitgestellt zum Eintragen"
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "Teilweise bereitgestellt zum Eintragen"
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "Bereitgestellt zum Eintragen, fehlend"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "Nicht unter Versionskontrolle, nicht bereitgestellt"
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "Fehlend"
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "Bereitgestellt zum Löschen"
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "Bereitgestellt zum Löschen, trotzdem vorhanden"
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "Konfliktauflösung nötig"
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "Gitk wird gestartet... bitte warten."
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -145,295 +145,297 @@
 "\n"
 "%s existiert nicht"
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Projektarchiv"
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Zweig"
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Version"
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "Zusammenführen"
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Andere Archive"
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "Aktuellen Zweig durchblättern"
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "Einen Zweig durchblättern..."
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "Aktuellen Zweig darstellen"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "Alle Zweige darstellen"
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Zweig »%s« durchblättern"
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Historie von »%s« darstellen"
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Datenbankstatistik"
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Datenbank komprimieren"
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "Datenbank überprüfen"
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Desktop-Icon erstellen"
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "Beenden"
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "Rückgängig"
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "Wiederholen"
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "Ausschneiden"
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "Kopieren"
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "Einfügen"
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Löschen"
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "Alle auswählen"
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "Erstellen..."
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "Umstellen..."
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "Umbenennen..."
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "Löschen..."
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "Zurücksetzen..."
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "Neue Version"
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "Letzte nachbessern"
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Neu laden"
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "Zum Eintragen bereitstellen"
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "Geänderte Dateien bereitstellen"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "Aus der Bereitstellung herausnehmen"
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "Änderungen verwerfen"
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "Abzeichnen"
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "Eintragen"
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "Lokales Zusammenführen..."
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "Zusammenführen abbrechen..."
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "Versenden..."
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "Apple"
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "Über %s"
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "Einstellungen..."
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "Optionen..."
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "Hilfe"
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "Online-Dokumentation"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr "Fehler: Verzeichnis »%s« kann nicht gelesen werden: Datei oder Verzeichnis nicht gefunden"
+msgstr ""
+"Fehler: Verzeichnis »%s« kann nicht gelesen werden: Datei oder Verzeichnis "
+"nicht gefunden"
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "Aktueller Zweig:"
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "Bereitstellung (zum Eintragen)"
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "Nicht bereitgestellte Änderungen"
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "Alles bereitstellen"
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Versenden"
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "Erste Versionsbeschreibung:"
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
-msgstr "Nachgebesserte Versionsbeschreibung:"
+msgstr "Nachgebesserte Beschreibung:"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
-msgstr "Nachgebesserte erste Versionsbeschreibung:"
+msgstr "Nachgebesserte erste Beschreibung:"
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
-msgstr "Nachgebesserte Zusammenführungs-Versionsbeschreibung:"
+msgstr "Nachgebesserte Zusammenführungs-Beschreibung:"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
-msgstr "Zusammenführungs-Versionsbeschreibung:"
+msgstr "Zusammenführungs-Beschreibung:"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "Versionsbeschreibung:"
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Alle kopieren"
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "Datei:"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "Aktualisieren"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "Kontext anwenden/umkehren"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "Schriftgröße verkleinern"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "Schriftgröße vergrößern"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "Weniger Zeilen anzeigen"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "Mehr Zeilen anzeigen"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Aktualisieren"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Schriftgröße verkleinern"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Schriftgröße vergrößern"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "Kontext zur Bereitstellung hinzufügen"
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "Initialisieren..."
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -449,7 +451,7 @@
 "von %s an Git weitergegeben werden:\n"
 "\n"
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -459,7 +461,7 @@
 "Dies ist ein bekanntes Problem der Tcl-Version, die\n"
 "in Cygwin mitgeliefert wird."
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -475,7 +477,7 @@
 "gewünschten Werte für die Einstellung user.name und \n"
 "user.email in Ihre Datei ~/.gitconfig einfügen.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - eine grafische Oberfläche für Git."
 
@@ -487,79 +489,79 @@
 msgid "Commit:"
 msgstr "Version:"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "Version kopieren"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "%s lesen..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "Annotierungen für Kopieren/Verschieben werden geladen..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "Zeilen annotiert"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "Annotierungen für ursprünglichen Ort werden geladen..."
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "Annotierung vollständig."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "Annotierung laden..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "Autor:"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "Eintragender:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "Ursprüngliche Datei:"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "Ursprünglich von:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "In Datei:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "Kopiert oder verschoben durch:"
 
 #: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
 msgid "Checkout Branch"
-msgstr "Zweig umstellen"
+msgstr "Auf Zweig umstellen"
 
 #: lib/branch_checkout.tcl:23
 msgid "Checkout"
 msgstr "Umstellen"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "Version"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "Optionen"
 
@@ -579,7 +581,7 @@
 msgid "Create New Branch"
 msgstr "Neuen Zweig erstellen"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "Erstellen"
 
@@ -651,7 +653,7 @@
 
 #: lib/branch_delete.tcl:52
 msgid "Delete Only If Merged Into"
-msgstr "Nur löschen, wenn darin zusammengeführt"
+msgstr "Nur löschen, wenn zusammengeführt nach"
 
 #: lib/branch_delete.tcl:54
 msgid "Always (Do not perform merge test.)"
@@ -719,22 +721,22 @@
 msgid "File Browser"
 msgstr "Datei-Browser"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "%s laden..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[Nach oben]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "Dateien des Zweigs durchblättern"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr "Blättern"
 
@@ -748,7 +750,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr "Fehler: »%s« kann nicht als Zweig oder Version erkannt werden"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "Schließen"
 
@@ -805,11 +807,15 @@
 msgid "Updating working directory to '%s'..."
 msgstr "Arbeitskopie umstellen auf »%s«..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "Dateien aktualisiert"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr ""
-"Zweig umstellen von »%s« abgebrochen (Zusammenführen der Dateien ist "
+"Auf Zweig »%s« umstellen abgebrochen (Zusammenführen der Dateien ist "
 "notwendig)."
 
 #: lib/checkout_op.tcl:354
@@ -833,7 +839,7 @@
 "Wenn Sie auf einem Zweig arbeiten möchten, erstellen Sie bitte jetzt einen "
 "Zweig mit der Auswahl »Abgetrennte Arbeitskopie-Version«."
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Umgestellt auf »%s«."
@@ -854,7 +860,7 @@
 msgid "Reset '%s'?"
 msgstr "»%s« zurücksetzen?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "Darstellen"
 
@@ -884,15 +890,15 @@
 msgid "Font Family"
 msgstr "Schriftfamilie"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "Schriftgröße"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "Schriftbeispiel"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -900,225 +906,231 @@
 "Dies ist ein Beispieltext.\n"
 "Wenn Ihnen dieser Text gefällt, sollten Sie diese Schriftart wählen."
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "Neues Projektarchiv"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "Neu..."
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "Projektarchiv klonen"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "Klonen..."
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "Projektarchiv öffnen"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "Öffnen..."
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "Zuletzt benutzte Projektarchive"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "Zuletzt benutztes Projektarchiv öffnen:"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "Projektarchiv »%s« existiert bereits."
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Projektarchiv »%s« konnte nicht erstellt werden:"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "Verzeichnis:"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "Git Projektarchiv"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Verzeichnis »%s« existiert bereits."
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Datei »%s« existiert bereits."
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "Klonen"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "URL:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "Art des Klonens:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (schnell, teilweise redundant, Hardlinks)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Alles kopieren (langsamer, volle Redundanz)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Verknüpft (schnell, nicht empfohlen, kein Backup)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Kein Git-Projektarchiv in »%s« gefunden."
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "Standard ist nur für lokale Projektarchive verfügbar."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "Verknüpft ist nur für lokale Projektarchive verfügbar."
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Projektarchiv »%s« existiert bereits."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "Der Ursprungsort konnte nicht eingerichtet werden"
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "Objekte werden gezählt"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr "Buckets"
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Kopien von Objekten/Info/Alternates konnten nicht erstellt werden: %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Von »%s« konnte nichts geklont werden."
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "Der »master«-Zweig wurde noch nicht initialisiert."
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Hardlinks nicht verfügbar. Stattdessen wird kopiert."
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Kopieren von »%s«"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "Objektdatenbank kopieren"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "KB"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Objekt kann nicht kopiert werden: %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "Objekte verlinken"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "Objekte"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Für Objekt konnte kein Hardlink erstellt werden: %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
-msgstr "Zweige und Objekte konnten nicht angefordert werden.  Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben."
+msgstr ""
+"Zweige und Objekte konnten nicht angefordert werden.  Kontrollieren Sie die "
+"Ausgaben auf der Konsole für weitere Angaben."
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
-msgstr "Markierungen konnten nicht angefordert werden.  Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben."
+msgstr ""
+"Markierungen konnten nicht angefordert werden.  Kontrollieren Sie die "
+"Ausgaben auf der Konsole für weitere Angaben."
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
-msgstr "Die Zweigspitze (HEAD) konnte nicht gefunden werden.  Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben."
+msgstr ""
+"Die Zweigspitze (HEAD) konnte nicht gefunden werden.  Kontrollieren Sie die "
+"Ausgaben auf der Konsole für weitere Angaben."
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Verzeichnis »%s« kann nicht aufgeräumt werden."
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "Klonen fehlgeschlagen."
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "Kein voreingestellter Zweig gefunden."
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "»%s« wurde nicht als Version gefunden."
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "Arbeitskopie erstellen"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "Dateien"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "Erstellen der Arbeitskopie fehlgeschlagen."
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "Öffnen"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "Projektarchiv:"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Projektarchiv »%s« konnte nicht geöffnet werden."
@@ -1139,7 +1151,7 @@
 msgid "Tracking Branch"
 msgstr "Übernahmezweig"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "Markierung"
 
@@ -1156,11 +1168,11 @@
 msgid "Revision expression is empty."
 msgstr "Versions-Ausdruck ist leer."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "Aktualisiert"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "URL"
 
@@ -1273,16 +1285,47 @@
 "\n"
 "- Rest: Eine ausführliche Beschreibung, warum diese Änderung hilfreich ist.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "Aufrufen der Vor-Eintragen-Kontrolle..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "Eintragen abgelehnt durch Vor-Eintragen-Kontrolle (»pre-commit hook«)."
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "Aufrufen der Versionsbeschreibungs-Kontrolle..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr ""
+"Eintragen abgelehnt durch Versionsbeschreibungs-Kontrolle (»commit-message "
+"hook«)."
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Änderungen eintragen..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree fehlgeschlagen:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Eintragen fehlgeschlagen."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Version »%s« scheint beschädigt zu sein"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1297,37 +1340,32 @@
 "\n"
 "Das Arbeitsverzeichnis wird daher jetzt neu geladen.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "Keine Änderungen, die eingetragen werden können."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "commit-tree fehlgeschlagen:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "update-ref fehlgeschlagen:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Version %s übertragen: %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "Verarbeitung. Bitte warten..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "Erfolgreich"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "Fehler: Kommando fehlgeschlagen"
 
@@ -1353,7 +1391,7 @@
 
 #: lib/database.tcl:48
 msgid "Packed objects waiting for pruning"
-msgstr "Komprimierte Objekte, die zum Entfernen vorgesehen sind"
+msgstr "Komprimierte Objekte, die zum Aufräumen vorgesehen sind"
 
 #: lib/database.tcl:49
 msgid "Garbage files"
@@ -1438,23 +1476,24 @@
 msgid "Error loading diff:"
 msgstr "Fehler beim Laden des Vergleichs:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
-msgstr "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
+msgstr ""
+"Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "Fehler beim Bereitstellen des gewählten Kontexts."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "Fehler"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "Warnung"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr ""
 "Sie müssen die obigen Fehler zuerst beheben, bevor Sie eintragen können."
@@ -1471,7 +1510,10 @@
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
-msgstr "Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu synchronisieren."
+msgstr ""
+"Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine "
+"Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu "
+"synchronisieren."
 
 #: lib/index.tcl:27
 msgid "Continue"
@@ -1486,6 +1528,10 @@
 msgid "Unstaging %s from commit"
 msgstr "Datei »%s« aus der Bereitstellung herausnehmen"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Bereit zum Eintragen."
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1501,11 +1547,12 @@
 msgid "Revert changes in these %i files?"
 msgstr "Änderungen in den gewählten %i Dateien verwerfen?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr "Alle nicht bereitgestellten Änderungen werden beim Verwerfen verloren gehen."
+msgstr ""
+"Alle nicht bereitgestellten Änderungen werden beim Verwerfen verloren gehen."
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "Nichts tun"
 
@@ -1577,27 +1624,27 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
-msgstr "Zusammenführen von %s und %s"
+msgid "Merging %s and %s..."
+msgstr "Zusammenführen von %s und %s..."
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "Zusammenführen erfolgreich abgeschlossen."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Zusammenführen fehlgeschlagen. Konfliktauflösung ist notwendig."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
-msgstr "Zusammenführen in %s"
+msgstr "Zusammenführen in »%s«"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "Zusammenzuführende Version"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1607,7 +1654,7 @@
 "\n"
 "Sie müssen die Nachbesserung der Version abschließen.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1622,7 +1669,7 @@
 "\n"
 "Zusammenführen jetzt abbrechen?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1637,103 +1684,116 @@
 "\n"
 "Änderungen jetzt zurücksetzen?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr "Abbruch"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "Dateien zurückgesetzt"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "Abbruch fehlgeschlagen."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "Abbruch durchgeführt. Bereit."
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Voreinstellungen wiederherstellen"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "Speichern"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "Projektarchiv %s"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "Global (Alle Projektarchive)"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "Benutzername"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr "E-Mail-Adresse"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "Zusammenführungs-Versionen zusammenfassen"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "Ausführlichkeit der Zusammenführen-Meldungen"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "Vergleichsstatistik nach Zusammenführen anzeigen"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "Auf Dateiänderungsdatum verlassen"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
-msgstr "Übernahmezweige entfernen während Anforderung"
+msgstr "Übernahmezweige aufräumen während Anforderung"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "Passend zu Übernahmezweig"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "Anzahl der Kontextzeilen beim Vergleich"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+#, fuzzy
+msgid "Commit Message Text Width"
+msgstr "Versionsbeschreibung:"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "Namensvorschlag für neue Zweige"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Wörterbuch Rechtschreibprüfung:"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "Schriftart ändern"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "%s wählen"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "Optionen konnten nicht gespeichert werden:"
 
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
 msgid "Delete Remote Branch"
-msgstr "Zweig aus anderem Projektarchiv löschen"
+msgstr "Zweig in anderem Projektarchiv löschen"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
-msgstr "Von Projektarchiv"
+msgstr "In Projektarchiv"
 
 #: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
 msgid "Remote:"
@@ -1741,7 +1801,7 @@
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
 msgid "Arbitrary URL:"
-msgstr "Kommunikation mit URL:"
+msgstr "Archiv-URL:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1749,11 +1809,11 @@
 
 #: lib/remote_branch_delete.tcl:109
 msgid "Delete Only If"
-msgstr "Löschen, falls"
+msgstr "Nur löschen, wenn"
 
 #: lib/remote_branch_delete.tcl:111
 msgid "Merged Into:"
-msgstr "Zusammenführen mit:"
+msgstr "Zusammengeführt mit:"
 
 #: lib/remote_branch_delete.tcl:119
 msgid "Always (Do not perform merge checks)"
@@ -1815,7 +1875,7 @@
 
 #: lib/remote.tcl:165
 msgid "Prune from"
-msgstr "Entfernen von"
+msgstr "Aufräumen von"
 
 #: lib/remote.tcl:170
 msgid "Fetch from"
@@ -1833,6 +1893,47 @@
 msgid "Cannot write icon:"
 msgstr "Fehler beim Erstellen des Icons:"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:65
+#, fuzzy
+msgid "Spell checking is unavailable"
+msgstr "Rechtschreibprüfung fehlgeschlagen"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr ""
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr ""
+
+#: lib/spellcheck.tcl:73
+#, fuzzy
+msgid "Spell checker silently failed on startup"
+msgstr "Rechtschreibprüfung fehlgeschlagen"
+
+#: lib/spellcheck.tcl:80
+#, fuzzy
+msgid "Unrecognized spell checker"
+msgstr "Unbekannte Version von »aspell«"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Keine Vorschläge"
+
+#: lib/spellcheck.tcl:381
+#, fuzzy
+msgid "Unexpected EOF from spell checker"
+msgstr "Unerwartetes EOF von »aspell«"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "Rechtschreibprüfung fehlgeschlagen"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
@@ -1851,12 +1952,12 @@
 #: lib/transport.tcl:18
 #, tcl-format
 msgid "remote prune %s"
-msgstr "Entfernen von »%s« aus anderem Archiv"
+msgstr "Aufräumen von »%s«"
 
 #: lib/transport.tcl:19
 #, tcl-format
 msgid "Pruning tracking branches deleted from %s"
-msgstr "Übernahmezweige entfernen, die in »%s« gelöscht wurden"
+msgstr "Übernahmezweige aufräumen und entfernen, die in »%s« gelöscht wurden"
 
 #: lib/transport.tcl:25 lib/transport.tcl:71
 #, tcl-format
@@ -1879,7 +1980,7 @@
 
 #: lib/transport.tcl:103
 msgid "Source Branches"
-msgstr "Herkunftszweige"
+msgstr "Lokale Zweige"
 
 #: lib/transport.tcl:120
 msgid "Destination Repository"
@@ -1901,3 +2002,6 @@
 #: lib/transport.tcl:168
 msgid "Include tags"
 msgstr "Mit Markierungen übertragen"
+
+#~ msgid "Not connected to aspell"
+#~ msgstr "Keine Verbindung zu »aspell«"
diff --git a/git-gui/po/fr.po b/git-gui/po/fr.po
index d389bda..89b6d51 100644
--- a/git-gui/po/fr.po
+++ b/git-gui/po/fr.po
@@ -8,8 +8,8 @@
 msgstr ""
 "Project-Id-Version: fr\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
-"PO-Revision-Date: 2008-01-14 21:08+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2008-04-04 22:05+0200\n"
 "Last-Translator: Christian Couder <chriscool@tuxfamily.org>\n"
 "Language-Team: French\n"
 "MIME-Version: 1.0\n"
@@ -18,33 +18,33 @@
 "X-Generator: KBabel 1.11.4\n"
 "Plural-Forms:  nplurals=2; plural=(n > 1);\n"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: erreur fatale"
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Invalide fonte spécifiée dans %s :"
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "Fonte principale"
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "Fonte diff/console"
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "Impossible de trouver git dans PATH."
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Impossible de parser la version de Git :"
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -63,79 +63,79 @@
 "\n"
 "Peut'on considérer que '%s' est en version 1.5.0 ?\n"
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "Impossible de trouver le répertoire de Git :"
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr "Impossible d'aller à la racine du répertoire de travail :"
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "Impossible d'utiliser un drôle de répertoire git :"
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "Pas de répertoire de travail"
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "Rafraichissement du status des fichiers..."
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "Recherche de fichiers modifiés..."
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Prêt."
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "Non modifié"
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "Modifié, non pré-commité"
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "Pré-commité"
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "En partie pré-commité"
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "Pré-commité, manquant"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "Non suivi, non pré-commité"
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "Manquant"
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "Pré-commité pour suppression"
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "Pré-commité pour suppression, toujours présent"
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "Nécessite la résolution d'une fusion"
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "Lancement de gitk... merci de patienter..."
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -146,295 +146,295 @@
 "\n"
 "%s inexistant"
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Référentiel"
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "Editer"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Branche"
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Commit"
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "Fusionner"
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Référentiel distant"
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "Visionner fichiers dans branche courante"
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "Visionner fichiers de branche"
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "Visualiser historique branche courante"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "Visualiser historique toutes branches"
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Visionner fichiers de %s"
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualiser historique de %s"
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Statistiques base de donnée"
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Comprimer base de donnée"
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "Vérifier base de donnée"
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Créer icône sur bureau"
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "Quitter"
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "Défaire"
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "Refaire"
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "Couper"
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "Copier"
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "Coller"
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Supprimer"
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "Tout sélectionner"
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "Créer..."
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "Emprunter... "
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "Renommer..."
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "Supprimer..."
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "Réinitialiser..."
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "Nouveau commit"
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "Corriger dernier commit"
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Resynchroniser"
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "Commiter un pré-commit"
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "Commiter fichiers modifiés dans pré-commit"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "Commit vers pré-commit"
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "Inverser modification"
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "Se désinscrire"
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "Commiter"
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "Fusion locale..."
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "Abandonner fusion..."
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "Pousser..."
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "Pomme"
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "A propos de %s"
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "Préférences..."
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "Options..."
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "Aide"
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "Documentation en ligne"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr "fatale : pas d'infos sur le chemin %s : Fichier ou répertoire inexistant"
+msgstr "erreur fatale : pas d'infos sur le chemin %s : Fichier ou répertoire inexistant"
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "Branche courante :"
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "Modifications pré-commitées"
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "Modifications non pré-commitées"
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "Pré-commit modifié"
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Pousser"
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "Message de commit initial :"
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr "Message de commit corrigé :"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr "Message de commit initial corrigé :"
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr "Message de commit de fusion corrigé :"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "Message de commit de fusion :"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "Message de commit :"
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Copier tout"
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "Fichier :"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "Rafraichir"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "Appliquer/Inverser section"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "Réduire fonte"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "Agrandir fonte"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "Montrer moins de contexte"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "Montrer plus de contexte"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Rafraichir"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Réduire fonte"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Agrandir fonte"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "Enlever section pré-commitée"
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "Pré-commiter section"
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "Initialisation..."
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -451,7 +451,7 @@
 "sous-processus de Git lancés par %s\n"
 "\n"
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -461,7 +461,7 @@
 "Ceci est du à un problème connu avec\n"
 "le binaire Tcl distribué par Cygwin."
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -478,7 +478,7 @@
 "de l'utilisateur) et 'user.email' (addresse email\n"
 "de l'utilisateur) dans votre fichier '~/.gitconfig'.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - une interface graphique utilisateur pour Git"
 
@@ -490,56 +490,56 @@
 msgid "Commit:"
 msgstr "Commit :"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "Copier commit"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Lecture de %s..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "Chargement des annotations de suivi des copies/déplacements..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "lignes annotées"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "Chargement des annotations d'emplacement original"
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "Annotation terminée."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "Chargement des annotations..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "Auteur :"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "Commiteur :"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "Fichier original :"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "A l'origine par :"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "Dans le fichier :"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "Copié ou déplacé ici par :"
 
@@ -552,17 +552,17 @@
 msgstr "Emprunter"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Annuler"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "Révision"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "Options"
 
@@ -582,7 +582,7 @@
 msgid "Create New Branch"
 msgstr "Créer nouvelle branche"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "Créer"
 
@@ -722,22 +722,22 @@
 msgid "File Browser"
 msgstr "Visionneur de fichier"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "Chargement de %s..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[Jusqu'au parent]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "Visionner fichiers de branches"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr "Visionner"
 
@@ -749,9 +749,9 @@
 #: lib/checkout_op.tcl:127
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
-msgstr "Erreur fatale : Impossible de résoudre %s"
+msgstr "erreur fatale : Impossible de résoudre %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "Fermer"
 
@@ -807,6 +807,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr "Mise à jour du répertoire courant avec '%s'..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "fichiers empruntés"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -830,9 +834,10 @@
 msgstr ""
 "Vous n'êtes plus ur une branche locale.\n"
 "\n"
-"Si vous vouliez être sur une branche, créez en une maintenant en partant de 'Cet emprunt détaché'."
+"Si vous vouliez être sur une branche, créez en une maintenant en partant de "
+"'Cet emprunt détaché'."
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "'%s' emprunté."
@@ -851,7 +856,7 @@
 msgid "Reset '%s'?"
 msgstr "Réinitialiser '%s' ?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "Visualiser"
 
@@ -867,7 +872,9 @@
 msgstr ""
 "Le changement de la branche courante a échoué.\n"
 "\n"
-"Le répertoire courant n'est que partiellement modifié. Les fichiers ont été mis à jour avec succès, mais la mise à jour d'un fichier interne à Git a échouée.\n"
+"Le répertoire courant n'est que partiellement modifié. Les fichiers ont été "
+"mis à jour avec succès, mais la mise à jour d'un fichier interne à Git a "
+"échouée.\n"
 "\n"
 "Cela n'aurait pas du se produire. %s va abandonner et se terminer."
 
@@ -879,15 +886,15 @@
 msgid "Font Family"
 msgstr "Famille de fonte"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "Taille de fonte"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "Exemple de fonte"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -895,225 +902,229 @@
 "C'est un texte d'exemple.\n"
 "Si vous aimez ce texte, vous pouvez choisir cette fonte."
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "Créer nouveau référentiel"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "Nouveau..."
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "Cloner référentiel existant"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "Cloner..."
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "Ouvrir référentiel existant"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "Ouvrir..."
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "Référentiels récents"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "Ouvrir référentiel récent :"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "L'emplacement %s existe déjà."
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "La création du référentiel %s a échouée :"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "Répertoire :"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "Référentiel Git"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Le répertoire %s existe déjà."
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Le fichier %s existe déjà."
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "Cloner"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "URL :"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "Type de clonage :"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (rapide, semi-redondant, liens durs)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Copy complète (plus lent, sauvegarde redondante)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Partagé (le plus rapide, non recommandé, pas de sauvegarde)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "'%s' n'est pas un référentiel Git."
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "Standard n'est disponible que pour un référentiel local."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "Partagé n'est disponible que pour un référentiel local."
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "L'emplacement %s existe déjà."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "La configuration de l'origine a échouée."
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "Comptage des objets"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr "paniers"
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Impossible de copier 'objects/info/alternates' : %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Il n'y a rien à cloner depuis %s."
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "Cette branche 'master' n'a pas été initialisée."
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Les liens durs ne sont pas disponibles. On se résoud à copier."
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Clonage depuis %s"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "Copie des objets"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Impossible de copier l'objet : %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "Liaison des objets"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "objets"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Impossible créer un lien dur pour l'objet : %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
-msgstr "Impossible de récupérer les branches et objets. Voir la sortie console pour plus de détails."
+msgstr ""
+"Impossible de récupérer les branches et objets. Voir la sortie console pour "
+"plus de détails."
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
-msgstr "Impossible de récupérer les marques. Voir la sortie console pour plus de détails."
+msgstr ""
+"Impossible de récupérer les marques. Voir la sortie console pour plus de "
+"détails."
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Impossible de déterminer HEAD. Voir la sortie console pour plus de détails."
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Impossible de nettoyer %s"
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "Le clonage a échoué."
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "Aucune branche par défaut n'a été obtenue."
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Impossible de résoudre %s comme commit."
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "Création du répertoire de travail"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "fichiers"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "L'emprunt initial de fichier a échoué."
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "Ouvrir"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "Référentiel :"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Impossible d'ouvrir le référentiel %s :"
@@ -1134,7 +1145,7 @@
 msgid "Tracking Branch"
 msgstr "Suivi de branche"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "Marque"
 
@@ -1151,11 +1162,11 @@
 msgid "Revision expression is empty."
 msgstr "L'expression de révision est vide."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "Misa à jour"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "URL"
 
@@ -1168,7 +1179,8 @@
 msgstr ""
 "Il n'y a rien à corriger.\n"
 "\n"
-"Vous allez créer le commit initial. Il n'y a pas de commit avant celui-ci à corriger.\n"
+"Vous allez créer le commit initial. Il n'y a pas de commit avant celui-ci à "
+"corriger.\n"
 
 #: lib/commit.tcl:18
 msgid ""
@@ -1180,7 +1192,9 @@
 msgstr ""
 "Impossible de corriger pendant une fusion.\n"
 "\n"
-"Vous êtes actuellement au milieu d'une fusion qui n'a pas été completement terminée. Vous ne pouvez pas corriger le commit précédant sauf si vous abandonnez la fusion courante.\n"
+"Vous êtes actuellement au milieu d'une fusion qui n'a pas été completement "
+"terminée. Vous ne pouvez pas corriger le commit précédant sauf si vous "
+"abandonnez la fusion courante.\n"
 
 #: lib/commit.tcl:49
 msgid "Error loading commit data for amend:"
@@ -1203,9 +1217,12 @@
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
-"L'état lors de la dernière synchronisation ne correspond plus à l'état du référentiel.\n"
+"L'état lors de la dernière synchronisation ne correspond plus à l'état du "
+"référentiel.\n"
 "\n"
-"Un autre programme Git a modifié ce référentiel depuis la dernière synchronisation. Une resynshronisation doit être effectuée avant de pouvoir créer un nouveau commit.\n"
+"Un autre programme Git a modifié ce référentiel depuis la dernière "
+"synchronisation. Une resynshronisation doit être effectuée avant de pouvoir "
+"créer un nouveau commit.\n"
 "\n"
 "Cela va être fait tout de suite automatiquement.\n"
 
@@ -1219,7 +1236,8 @@
 msgstr ""
 "Des fichiers non fusionnés ne peuvent être commités.\n"
 "\n"
-"Le fichier %s a des conflicts de fusion. Vous devez les résoudre et pré-commiter le fichier avant de pouvoir commiter.\n"
+"Le fichier %s a des conflicts de fusion. Vous devez les résoudre et pré-"
+"commiter le fichier avant de pouvoir commiter.\n"
 
 #: lib/commit.tcl:162
 #, tcl-format
@@ -1260,16 +1278,45 @@
 "- Deuxième ligne : rien.\n"
 "- Lignes suivantes : Décrire pourquoi ces modifications sont bonnes.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "attention : Tcl ne supporte pas l'encodage '%s'."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "Appel du programme externe d'avant commit..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "Commit refusé par le programme externe d'avant commit."
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "Appel du programme externe de message de commit..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "Commit refusé par le programme externe de message de commit."
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Commit des modifications..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree a échoué :"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Le commit a échoué."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Le commit %s semble être corrompu"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1279,41 +1326,37 @@
 msgstr ""
 "Pas de modification à commiter.\n"
 "\n"
-"Aucun fichier n'a été modifié par ce commit et il ne s'agit pas d'un commit de fusion.\n"
+"Aucun fichier n'a été modifié par ce commit et il ne s'agit pas d'un commit "
+"de fusion.\n"
 "\n"
 "Une resynchronisation va être lancée tout de suite automatiquement.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "Pas de modifications à commiter."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "attention : Tcl ne supporte pas l'encodage '%s'."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "commit-tree a échoué :"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "update-ref a échoué"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Commit créé %s : %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "Travail en cours... merci de patienter..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "Succès"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "Erreur : échec de la commande"
 
@@ -1345,7 +1388,7 @@
 msgid "Garbage files"
 msgstr "Fichiers poubelle"
 
-#: lib/database.tcl:72ets
+#: lib/database.tcl:72
 msgid "Compressing the object database"
 msgstr "Compression de la base des objets"
 
@@ -1363,9 +1406,12 @@
 "\n"
 "Compress the database now?"
 msgstr ""
-"Ce référentiel comprend actuellement environ %i objets ayant leur fichier particulier.\n"
+"Ce référentiel comprend actuellement environ %i objets ayant leur fichier "
+"particulier.\n"
 "\n"
-"Pour conserver une performance optimale, il est fortement recommandé de comprimer la base quand plus de %i objets ayant leur fichier particulier existent.\n"
+"Pour conserver une performance optimale, il est fortement recommandé de "
+"comprimer la base quand plus de %i objets ayant leur fichier particulier "
+"existent.\n"
 "\n"
 "Comprimer la base maintenant ?"
 
@@ -1391,9 +1437,11 @@
 "\n"
 "%s ne comporte aucune modification.\n"
 "\n"
-"La date de modification de ce fichier a été mise à jour par une autre application, mais le contenu du fichier n'a pas changé.\n"
+"La date de modification de ce fichier a été mise à jour par une autre "
+"application, mais le contenu du fichier n'a pas changé.\n"
 "\n"
-"Une resynchronisation va être lancée automatiquement pour trouver d'autres fichiers qui pourraient se trouver dans le même état."
+"Une resynchronisation va être lancée automatiquement pour trouver d'autres "
+"fichiers qui pourraient se trouver dans le même état."
 
 #: lib/diff.tcl:81
 #, tcl-format
@@ -1421,23 +1469,23 @@
 msgid "Error loading diff:"
 msgstr "Erreur lors du chargement des différences :"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr "La suppression dans le pré-commit de la section sélectionnée a échouée."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "Le pré-commit de la section sélectionnée a échoué."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "erreur"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "attention"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr "Vous devez corriger les erreurs suivantes avant de pouvoir commiter."
 
@@ -1468,6 +1516,10 @@
 msgid "Unstaging %s from commit"
 msgstr "Supprimer %s du commit"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Prêt à être commité."
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1483,11 +1535,13 @@
 msgid "Revert changes in these %i files?"
 msgstr "Inverser les modifications dans ces %i fichiers ?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr "Toutes les modifications non pré-commitées seront définitivement perdues lors de l'inversion."
+msgstr ""
+"Toutes les modifications non pré-commitées seront définitivement perdues "
+"lors de l'inversion."
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "Ne rien faire"
 
@@ -1499,7 +1553,8 @@
 msgstr ""
 "Impossible de fucionner pendant une correction.\n"
 "\n"
-"Vous devez finir de corriger ce commit avant de lancer une quelconque fusion.\n"
+"Vous devez finir de corriger ce commit avant de lancer une quelconque "
+"fusion.\n"
 
 #: lib/merge.tcl:27
 msgid ""
@@ -1510,9 +1565,12 @@
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
-"L'état lors de la dernière synchronisation ne correspond plus à l'état du référentiel.\n"
+"L'état lors de la dernière synchronisation ne correspond plus à l'état du "
+"référentiel.\n"
 "\n"
-"Un autre programme Git a modifié ce référentiel depuis la dernière synchronisation. Une resynchronisation doit être effectuée avant de pouvoir fusionner de nouveau.\n"
+"Un autre programme Git a modifié ce référentiel depuis la dernière "
+"synchronisation. Une resynchronisation doit être effectuée avant de pouvoir "
+"fusionner de nouveau.\n"
 "\n"
 "Cela va être fait tout de suite automatiquement\n"
 
@@ -1530,7 +1588,9 @@
 "\n"
 "Le fichier %s a des conflicts de fusion.\n"
 "\n"
-"Vous devez les résoudre, puis pré-commiter le fichier, et enfin commiter pour terminer la fusion courante. Seulementà ce moment là, il sera possible d'effectuer une nouvelle fusion.\n"
+"Vous devez les résoudre, puis pré-commiter le fichier, et enfin commiter "
+"pour terminer la fusion courante. Seulementà ce moment là, il sera possible "
+"d'effectuer une nouvelle fusion.\n"
 
 #: lib/merge.tcl:54
 #, tcl-format
@@ -1546,7 +1606,9 @@
 "\n"
 "Le fichier %s est modifié.\n"
 "\n"
-"Vous devriez terminer le commit courant avant de lancer une fusion. En faisait comme cela, vous éviterez de devoir éventuellement abandonner une fusion ayant échouée.\n"
+"Vous devriez terminer le commit courant avant de lancer une fusion. En "
+"faisait comme cela, vous éviterez de devoir éventuellement abandonner une "
+"fusion ayant échouée.\n"
 
 #: lib/merge.tcl:106
 #, tcl-format
@@ -1555,27 +1617,27 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
-msgstr "Fusion de %s et %s"
+msgid "Merging %s and %s..."
+msgstr "Fusion de %s et %s..."
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "La fusion s'est faite avec succès."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "La fusion a echouée. Il est nécessaire de résoudre les conflicts."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Fusion dans %s"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "Révision à fusionner"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1585,7 +1647,7 @@
 "\n"
 "Vous devez finir de corriger ce commit.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1595,11 +1657,12 @@
 msgstr ""
 "Abandonner la fusion ?\n"
 "\n"
-"Abandonner la fusion courante entrainera la perte de TOUTES les modifications non commitées.\n"
+"Abandonner la fusion courante entrainera la perte de TOUTES les "
+"modifications non commitées.\n"
 "\n"
 "Abandonner quand même la fusion courante ?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1609,97 +1672,110 @@
 msgstr ""
 "Réinitialiser les modifications ?\n"
 "\n"
-"Réinitialiser les modifications va faire perdre TOUTES les modifications non commitées.\n"
+"Réinitialiser les modifications va faire perdre TOUTES les modifications non "
+"commitées.\n"
 "\n"
 "Réinitialiser quand même les modifications courantes ?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr "Abandon"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "fichiers réinitialisés"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "L'abandon a échoué."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "Abandon teminé. Prêt."
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Remettre les valeurs par défaut"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "Sauvegarder"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "Référentiel de %s"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "Globales (tous les référentiels)"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "Nom d'utilisateur"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr "Adresse email"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "Résumer les commits de fusion"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "Fusion bavarde"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "Montrer statistiques de diff après fusion"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "Faire confiance aux dates de modification de fichiers "
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Nettoyer les branches de suivi pendant la récupération"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "Faire correspondre les branches de suivi"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "Nombre de lignes de contexte dans les diffs"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "Largeur du texte de message de commit"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "Nouveau modèle de nom de branche"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Dictionnaire d'orthographe :"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "Modifier les fontes"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "Choisir %s"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "Préférences"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "La sauvegarde complète des options a échouée :"
 
@@ -1755,7 +1831,9 @@
 msgid ""
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
-msgstr "Une ou plusieurs des tests de fusion ont échoués parce que vous n'avez pas récupéré les commits nécessaires. Essayez de récupéré à partir de %s d'abord."
+msgstr ""
+"Une ou plusieurs des tests de fusion ont échoués parce que vous n'avez pas "
+"récupéré les commits nécessaires. Essayez de récupéré à partir de %s d'abord."
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
@@ -1805,6 +1883,43 @@
 msgid "Cannot write icon:"
 msgstr "Impossible d'écrire l'icône :"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Vérificateur d'orthographe non supporté"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "La vérification d'orthographe n'est pas disponible"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Configuration de vérification d'orthographe invalide"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Réinitialisation du dictionnaire à %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "La vérification d'orthographe a échouée silentieusement au démarrage"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Vérificateur d'orthographe non reconnu"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Aucune suggestion"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "Fin de fichier innatendue envoyée par le vérificateur d'orthographe"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "Le vérificateur d'orthographe a échoué"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
@@ -1863,7 +1978,9 @@
 
 #: lib/transport.tcl:160
 msgid "Force overwrite existing branch (may discard changes)"
-msgstr "Forcer l'écrasement d'une branche existante (peut supprimer des modifications)"
+msgstr ""
+"Forcer l'écrasement d'une branche existante (peut supprimer des "
+"modifications)"
 
 #: lib/transport.tcl:164
 msgid "Use thin pack (for slow network connections)"
diff --git a/git-gui/po/git-gui.pot b/git-gui/po/git-gui.pot
index dfa48ae..813199f 100644
--- a/git-gui/po/git-gui.pot
+++ b/git-gui/po/git-gui.pot
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,33 +16,33 @@
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr ""
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr ""
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr ""
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr ""
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr ""
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr ""
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -54,79 +54,79 @@
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr ""
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr ""
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr ""
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr ""
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr ""
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr ""
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr ""
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr ""
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr ""
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr ""
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr ""
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr ""
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr ""
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr ""
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr ""
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr ""
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr ""
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr ""
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -134,295 +134,295 @@
 "%s does not exist"
 msgstr ""
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr ""
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr ""
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr ""
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr ""
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr ""
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr ""
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr ""
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr ""
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr ""
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr ""
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr ""
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr ""
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr ""
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr ""
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr ""
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr ""
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr ""
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr ""
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr ""
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr ""
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr ""
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr ""
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr ""
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr ""
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr ""
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr ""
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr ""
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr ""
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr ""
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr ""
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr ""
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr ""
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr ""
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr ""
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr ""
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr ""
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr ""
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr ""
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr ""
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr ""
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr ""
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr ""
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr ""
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr ""
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr ""
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr ""
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr ""
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr ""
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr ""
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr ""
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr ""
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr ""
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr ""
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr ""
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr ""
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr ""
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr ""
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr ""
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr ""
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr ""
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr ""
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr ""
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr ""
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr ""
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr ""
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr ""
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -433,14 +433,14 @@
 "\n"
 msgstr ""
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
 "Tcl binary distributed by Cygwin."
 msgstr ""
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -451,7 +451,7 @@
 "~/.gitconfig file.\n"
 msgstr ""
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr ""
 
@@ -463,56 +463,56 @@
 msgid "Commit:"
 msgstr ""
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr ""
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr ""
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr ""
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr ""
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr ""
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr ""
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr ""
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr ""
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr ""
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr ""
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr ""
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr ""
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr ""
 
@@ -525,17 +525,17 @@
 msgstr ""
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr ""
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr ""
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr ""
 
@@ -555,7 +555,7 @@
 msgid "Create New Branch"
 msgstr ""
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr ""
 
@@ -690,22 +690,22 @@
 msgid "File Browser"
 msgstr ""
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr ""
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr ""
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr ""
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr ""
 
@@ -719,7 +719,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr ""
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr ""
 
@@ -766,6 +766,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr ""
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr ""
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -788,7 +792,7 @@
 "Checkout'."
 msgstr ""
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr ""
@@ -807,7 +811,7 @@
 msgid "Reset '%s'?"
 msgstr ""
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr ""
 
@@ -830,239 +834,239 @@
 msgid "Font Family"
 msgstr ""
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr ""
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr ""
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
 msgstr ""
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr ""
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr ""
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr ""
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr ""
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr ""
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr ""
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr ""
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr ""
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr ""
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr ""
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr ""
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr ""
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr ""
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr ""
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr ""
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr ""
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr ""
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr ""
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr ""
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr ""
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr ""
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr ""
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr ""
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr ""
@@ -1083,7 +1087,7 @@
 msgid "Tracking Branch"
 msgstr ""
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr ""
 
@@ -1100,11 +1104,11 @@
 msgid "Revision expression is empty."
 msgstr ""
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr ""
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr ""
 
@@ -1182,16 +1186,45 @@
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr ""
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr ""
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr ""
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr ""
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr ""
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr ""
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr ""
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr ""
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1200,37 +1233,32 @@
 "A rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr ""
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr ""
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr ""
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr ""
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr ""
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr ""
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr ""
 
@@ -1326,23 +1354,23 @@
 msgid "Error loading diff:"
 msgstr ""
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr ""
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr ""
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr ""
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr ""
 
@@ -1373,6 +1401,10 @@
 msgid "Unstaging %s from commit"
 msgstr ""
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr ""
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1388,11 +1420,11 @@
 msgid "Revert changes in these %i files?"
 msgstr ""
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr ""
 
@@ -1442,34 +1474,34 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
+msgid "Merging %s and %s..."
 msgstr ""
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr ""
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr ""
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr ""
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr ""
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
 "You must finish amending this commit.\n"
 msgstr ""
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1478,7 +1510,7 @@
 "Continue with aborting the current merge?"
 msgstr ""
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1487,93 +1519,105 @@
 "Continue with resetting the current changes?"
 msgstr ""
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr ""
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr ""
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr ""
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr ""
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr ""
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr ""
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr ""
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr ""
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr ""
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr ""
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr ""
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr ""
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr ""
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr ""
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr ""
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr ""
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr ""
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr ""
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr ""
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr ""
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr ""
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr ""
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr ""
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr ""
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr ""
 
@@ -1673,6 +1717,43 @@
 msgid "Cannot write icon:"
 msgstr ""
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr ""
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr ""
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr ""
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr ""
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr ""
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr ""
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
diff --git a/git-gui/po/glossary/de.po b/git-gui/po/glossary/de.po
index 0b33c57..35764d1 100644
--- a/git-gui/po/glossary/de.po
+++ b/git-gui/po/glossary/de.po
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: git-gui glossary\n"
 "POT-Creation-Date: 2008-01-07 21:20+0100\n"
-"PO-Revision-Date: 2008-01-15 20:32+0100\n"
+"PO-Revision-Date: 2008-02-16 21:48+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German \n"
 "MIME-Version: 1.0\n"
@@ -114,7 +114,7 @@
 
 #. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'."
 msgid "prune"
-msgstr "entfernen"
+msgstr "aufräumen (entfernen?)"
 
 #. "Pulling a branch means to fetch it and merge it."
 msgid "pull"
diff --git a/git-gui/po/hu.po b/git-gui/po/hu.po
index 627c05e..28760ed 100644
--- a/git-gui/po/hu.po
+++ b/git-gui/po/hu.po
@@ -7,8 +7,8 @@
 msgstr ""
 "Project-Id-Version: git-gui-i 18n\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
-"PO-Revision-Date: 2007-12-04 01:15+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2008-03-14 17:24+0100\n"
 "Last-Translator: Miklos Vajna <vmiklos@frugalware.org>\n"
 "Language-Team: Hungarian\n"
 "MIME-Version: 1.0\n"
@@ -16,33 +16,33 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: végzetes hiba"
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Érvénytelen font lett megadva itt: %s:"
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "Fő betűtípus"
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "Diff/konzol betűtípus"
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "A git nem található a PATH-ban."
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Nem értelmezhető a Git verzió sztring:"
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,79 +61,79 @@
 "\n"
 "Feltételezhetjük, hogy a(z) '%s' verziója legalább 1.5.0?\n"
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "A Git könyvtár nem található:"
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr "Nem lehet a munkakönyvtár tetejére lépni:"
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "Nem használható vicces .git könyvtár:"
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "Nincs munkakönyvtár"
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "A fájlok státuszának frissítése..."
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "Módosított fájlok keresése ..."
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Kész."
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "Nem módosított"
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "Módosított, de nem kiválasztott"
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "Kiválasztva commitolásra"
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "Részek kiválasztva commitolásra"
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "Kiválasztva commitolásra, hiányzó"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "Nem követett, nem kiválasztott"
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "Hiányzó"
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "Kiválasztva eltávolításra"
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "Kiválasztva eltávolításra, jelenleg is elérhető"
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "Merge feloldás szükséges"
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "A gitk indítása... várjunk..."
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -144,295 +144,296 @@
 "\n"
 "A(z) %s nem létezik"
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Repó"
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "Szerkesztés"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Branch"
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Commit@@főnév"
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "Merge"
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Távoli"
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "A jelenlegi branch fájljainak böngészése"
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "A branch fájljainak böngészése..."
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "A jelenlegi branch történetének vizualizálása"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "Az összes branch történetének vizualizálása"
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "A(z) %s branch fájljainak böngészése"
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "A(z) %s branch történetének vizualizálása"
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Adatbázis statisztikák"
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Adatbázis tömörítése"
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "Adatbázis ellenőrzése"
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Asztal ikon létrehozása"
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "Kilépés"
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "Visszavonás"
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "Mégis"
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "Kivágás"
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "Másolás"
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "Beillesztés"
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Törlés"
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "Mindent kiválaszt"
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "Létrehozás..."
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "Checkout..."
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "Átnevezés..."
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "Törlés..."
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "Visszaállítás..."
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "Új commit"
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "Utolsó commit javítása"
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Keresés újra"
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "Kiválasztás commitolásra"
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "Módosított fájlok kiválasztása commitolásra"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "Commitba való kiválasztás visszavonása"
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "Változtatások visszaállítása"
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "Aláír"
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "Commit@@ige"
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "Helyi merge..."
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "Merge megszakítása..."
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "Push..."
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "Apple"
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "Névjegy: %s"
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "Beállítások..."
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "Opciók..."
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "Segítség"
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "Online dokumentáció"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr "végzetes hiba: nem érhető el a(z) %s útvonal: Nincs ilyen fájl vagy könyvtár"
+msgstr ""
+"végzetes hiba: nem érhető el a(z) %s útvonal: Nincs ilyen fájl vagy könyvtár"
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "Jelenlegi branch:"
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "Kiválasztott változtatások (commitolva lesz)"
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "Kiválasztatlan változtatások"
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "Változtatások kiválasztása"
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Push"
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "Kezdeti commit üzenet:"
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr "Javító commit üzenet:"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr "Kezdeti javító commit üzenet:"
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr "Javító merge commit üzenet:"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "Merge commit üzenet:"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "Commit üzenet:"
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Összes másolása"
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "Fájl:"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "Frissítés"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "Hunk alkalmazása/visszaállítása"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "Font méret csökkentése"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "Fönt méret növelése"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "Kevesebb környezet mutatása"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "Több környezet mutatása"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Frissítés"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Font méret csökkentése"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Fönt méret növelése"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "Hunk törlése commitból"
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "Hunk kiválasztása commitba"
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "Inicializálás..."
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -449,7 +450,7 @@
 "indított folyamatok által:\n"
 "\n"
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -459,7 +460,7 @@
 "Ez a Cygwin által terjesztett Tcl binárisban\n"
 "lévő ismert hiba miatt van."
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -476,7 +477,7 @@
 "elhelyezése a személyes\n"
 "~/.gitconfig fájlba.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - egy grafikus felület a Githez."
 
@@ -488,56 +489,56 @@
 msgid "Commit:"
 msgstr "Commit:"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "Commit másolása"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "A(z) %s olvasása..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "A másolást/átnevezést követő annotációk betöltése..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "sor annotálva"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "Az eredeti hely annotációk betöltése..."
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "Az annotáció kész."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "Az annotáció betöltése..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "Szerző:"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "Commiter:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "Eredeti fájl:"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "Eredeti szerző:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "Ebben a fájlban:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "Ide másolta vagy helyezte:"
 
@@ -550,17 +551,17 @@
 msgstr "Checkout"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Mégsem"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "Revízió"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "Opciók"
 
@@ -580,7 +581,7 @@
 msgid "Create New Branch"
 msgstr "Új branch létrehozása"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "Létrehozás"
 
@@ -720,22 +721,22 @@
 msgid "File Browser"
 msgstr "Fájl böngésző"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "A(z) %s betöltése..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[Fel a szülőhöz]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "A branch fájljainak böngészése"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr "Böngészés"
 
@@ -749,7 +750,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr "végzetes: Nem lehet feloldani a következőt: %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "Bezárás"
 
@@ -807,6 +808,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr "A munkkönyvtár frissiítése a következőre: '%s'..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "fájl frissítve"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -833,7 +838,7 @@
 "Ha egy branchen szeretnénk lenni, hozzunk létre egyet az 'Ez a leválasztott "
 "checkout'-ból."
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "'%s' kifejtve."
@@ -853,7 +858,7 @@
 msgid "Reset '%s'?"
 msgstr "Visszaállítjuk a következőt: '%s'?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "Vizualizálás"
 
@@ -882,15 +887,15 @@
 msgid "Font Family"
 msgstr "Font család"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "Font méret"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "Font példa"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -898,225 +903,227 @@
 "Ez egy példa szöveg.\n"
 "Ha ez megfelel, ez lehet a betűtípus."
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "Új repó létrehozása"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "Új..."
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "Létező repó másolása"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "Másolás..."
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "Létező könyvtár megnyitása"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "Meggyitás..."
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "Legutóbbi repók"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "Legutóbbi repók megnyitása:"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "A(z) '%s' hely már létezik."
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Nem sikerült letrehozni a(z) %s repót:"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "Könyvtár:"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "Git repó"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "A(z) '%s' könyvtár már létezik."
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "A(z) '%s' fájl már létezik."
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "Bezárás"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "URL:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "Másolás típusa:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Általános (Gyors, félig-redundáns, hardlinkek)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Teljes másolás (Lassabb, redundáns biztonsági mentés)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Megosztott (Leggyorsabb, nem ajánlott, nincs mentés)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Nem Git repó: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "A standard csak helyi repókra érhető el."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "A megosztott csak helyi repókra érhető el."
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "A(z) '%s' hely már létezik."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "Nem sikerült beállítani az origint"
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "Objektumok számolása"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr "vödrök"
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Nem sikerült másolni az objects/info/alternates-t: %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Semmi másolni való nincs innen: %s"
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "A 'master' branch nincs inicializálva."
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Nem érhetőek el hardlinkek.  Másolás használata."
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Másolás innen: %s"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "Objektumok másolása"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Nem sikerült másolni az objektumot: %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "Objektumok összefűzése"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "objektum"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Nem sikerült hardlinkelni az objektumot: %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
-msgstr "Nem sikerült letölteni a branch-eket és az objektumokat.  Bővebben a konzolos kimenetben."
+msgstr ""
+"Nem sikerült letölteni a branch-eket és az objektumokat.  Bővebben a "
+"konzolos kimenetben."
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Nem sikerült letölteni a tageket.  Bővebben a konzolos kimenetben."
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Nem sikerült megállapítani a HEAD-et.  Bővebben a konzolos kimenetben."
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Nem sikerült tiszítani: %s."
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "A másolás nem sikerült."
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "Nincs alapértelmezett branch."
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Nem sikerült felöldani a(z) %s objektumot commitként."
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "Munkakönyvtár létrehozása"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "fájl"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "A kezdeti fájl-kibontás sikertelen."
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "Megnyitás"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "Repó:"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Nem sikerült megnyitni a(z) %s repót:"
@@ -1137,7 +1144,7 @@
 msgid "Tracking Branch"
 msgstr "Követő branch"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "Tag"
 
@@ -1154,11 +1161,11 @@
 msgid "Revision expression is empty."
 msgstr "A revízió kifejezés üres."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "Frissítve"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "URL"
 
@@ -1268,16 +1275,45 @@
 "- Második sor: Üres\n"
 "- A többi sor: Leírja, hogy miért jó ez a változtatás.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "figyelmeztetés: a Tcl nem támogatja a(z) '%s' kódolást."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "A pre-commit hurok meghívása..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "A commitot megakadályozta a pre-commit hurok. "
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "A commit-msg hurok meghívása..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "A commiot megakadályozta a commit-msg hurok."
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "A változtatások commitolása..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "a write-tree sikertelen:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "A commit nem sikerült."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "A(z) %s commit sérültnek tűnik"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1291,37 +1327,32 @@
 "\n"
 "Az újrakeresés most automatikusan el fog indulni.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "Nincs commitolandó változtatás."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "figyelmeztetés: a Tcl nem támogatja a(z) '%s' kódolást."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "a commit-tree sikertelen:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "az update-ref sikertelen:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Létrejött a %s commit: %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "Munka folyamatban.. Várjunk..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "Siker"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "Hiba: a parancs sikertelen"
 
@@ -1431,23 +1462,23 @@
 msgid "Error loading diff:"
 msgstr "Hiba a diff betöltése közben:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr "Nem visszavonni a hunk kiválasztását."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "Nem sikerült kiválasztani a hunkot."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "hiba"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "figyelmeztetés"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr "Ki kell javítanunk a fenti hibákat commit előtt."
 
@@ -1464,8 +1495,8 @@
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
 msgstr ""
-"A Git index frissítése sikertelen volt.  Egy újraolvasás automatikusan elindult, hogy "
-"a git-gui újra szinkonban legyen."
+"A Git index frissítése sikertelen volt.  Egy újraolvasás automatikusan "
+"elindult, hogy a git-gui újra szinkonban legyen."
 
 #: lib/index.tcl:27
 msgid "Continue"
@@ -1480,6 +1511,10 @@
 msgid "Unstaging %s from commit"
 msgstr "A(z) %s commitba való kiválasztásának visszavonása"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Commitolásra kész."
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1495,13 +1530,13 @@
 msgid "Revert changes in these %i files?"
 msgstr "Visszaállítja a változtatásokat ebben e %i fájlban?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Minden nem kiválasztott változtatás el fog veszni ezáltal a visszaállítás "
 "által."
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "Ne csináljunk semmit"
 
@@ -1574,27 +1609,27 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
-msgstr "A(z) %s és a(z) %s merge-ölése"
+msgid "Merging %s and %s..."
+msgstr "A(z) %s és a(z) %s merge-ölése..."
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "A merge sikeresen befejeződött."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "A merge sikertelen. Fel kell oldanunk az ütközéseket."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Merge-ölés a következőbe: %s"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "Merge-ölni szándékozott revízió"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1604,7 +1639,7 @@
 "\n"
 "Be kell fejeznünk ennek a commitnak a javítását.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1619,7 +1654,7 @@
 "\n"
 "Folytatjuk a jelenlegi merge megszakítását?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1634,93 +1669,105 @@
 "\n"
 "Folytatjuk a jelenlegi módosítások visszavonását?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr "Félbeszakítás"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "fájl visszaállítva"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "A félbeszakítás nem sikerült."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "A megkeszakítás befejeződött. Kész."
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Alapértelmezés visszaállítása"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "Mentés"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "%s Repó"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "Globális (minden repó)"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "Felhasználónév"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr "Email cím"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "A merge commitok összegzése"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "Merge beszédesség"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "Diffstat mutatása merge után"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "A fájl módosítási dátumok megbízhatóak"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr "A követő branchek eltávolítása letöltés alatt"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "A követő branchek egyeztetése"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "A diff környezeti sorok száma"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "Commit üzenet szövegének szélessége"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "Új branch név sablon"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Helyesírás-ellenőrző szótár:"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "Betűtípus megváltoztatása"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "%s választása"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "Beállítások"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "Nem sikerült teljesen elmenteni a beállításokat:"
 
@@ -1767,8 +1814,7 @@
 "\n"
 " - %s"
 msgstr ""
-"A következő branchek nem teljesen lettek merge-ölve ebbe: %s:"
-"\n"
+"A következő branchek nem teljesen lettek merge-ölve ebbe: %s:\n"
 " - %s"
 
 #: lib/remote_branch_delete.tcl:189
@@ -1829,6 +1875,43 @@
 msgid "Cannot write icon:"
 msgstr "Nem sikerült írni az ikont:"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Nem támogatott helyesírás-ellenőrző"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "A helyesírás-ellenőrzés nem elérhető"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Érvénytelen a helyesírás-ellenőrző beállítása"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Szótár visszaállítása a következőre: %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "A helyesírás-ellenőrő indítása sikertelen"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Ismeretlen helyesírás-ellenőrző"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Nincs javaslat"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "Nem várt EOF a helyesírás-ellenőrzőtől"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "A helyesírás-ellenőrzés sikertelen"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
@@ -1887,7 +1970,9 @@
 
 #: lib/transport.tcl:160
 msgid "Force overwrite existing branch (may discard changes)"
-msgstr "Létező branch felülírásának erőltetése (lehet, hogy el fog dobni változtatásokat)"
+msgstr ""
+"Létező branch felülírásának erőltetése (lehet, hogy el fog dobni "
+"változtatásokat)"
 
 #: lib/transport.tcl:164
 msgid "Use thin pack (for slow network connections)"
@@ -1897,6 +1982,9 @@
 msgid "Include tags"
 msgstr "Tageket is"
 
+#~ msgid "Not connected to aspell"
+#~ msgstr "Nincs kapcsolat az aspellhez"
+
 #~ msgid "Cannot find the git directory:"
 #~ msgstr "Nem található a git könyvtár:"
 
diff --git a/git-gui/po/it.po b/git-gui/po/it.po
index 33a8399..11cc79b 100644
--- a/git-gui/po/it.po
+++ b/git-gui/po/it.po
@@ -9,41 +9,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-09 11:18+0100\n"
-"PO-Revision-Date: 2007-11-01 21:05+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2008-03-12 22:12+0100\n"
 "Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: errore grave"
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Caratteri non validi specificati in %s:"
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "Caratteri principali"
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "Caratteri per confronti e terminale"
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "Impossibile trovare git nel PATH"
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Impossibile determinare la versione di Git:"
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -62,79 +62,79 @@
 "\n"
 "Assumere che '%s' sia alla versione 1.5.0?\n"
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "Non trovo la directory di git: "
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr "Impossibile spostarsi sulla directory principale del progetto:"
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "Impossibile usare una .git directory strana:"
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "Nessuna directory di lavoro"
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "Controllo dello stato dei file in corso..."
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "Ricerca di file modificati in corso..."
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Pronto."
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "Non modificato"
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "Modificato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "Preparato per una nuova revisione"
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "Parti preparate per una nuova revisione"
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "Preparato per una nuova revisione, mancante"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "Non tracciato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "Mancante"
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "Preparato per la rimozione"
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "Preparato alla rimozione, ancora presente"
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "Richiede risoluzione dei conflitti"
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "Avvio di gitk... attendere..."
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -145,297 +145,297 @@
 "\n"
 "%s non esiste"
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Archivio"
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "Modifica"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Ramo"
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Revisione"
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "Fusione (Merge)"
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Remoto"
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "Esplora i file del ramo attuale"
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "Esplora i file del ramo..."
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "Visualizza la cronologia del ramo attuale"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "Visualizza la cronologia di tutti i rami"
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Esplora i file di %s"
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualizza la cronologia di %s"
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Statistiche dell'archivio"
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Comprimi l'archivio"
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "Verifica l'archivio"
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Crea icona desktop"
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "Esci"
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "Annulla"
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "Ripeti"
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "Taglia"
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "Copia"
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "Incolla"
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Elimina"
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "Seleziona tutto"
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "Crea..."
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "Attiva..."
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "Rinomina"
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "Elimina..."
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "Ripristina..."
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "Nuova revisione"
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "Correggi l'ultima revisione"
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Analizza nuovamente"
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "Prepara per una nuova revisione"
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "Prepara i file modificati per una nuova revisione"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "Annulla preparazione"
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "Annulla modifiche"
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "Sign Off"
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "Nuova revisione"
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "Fusione locale..."
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "Interrompi fusione..."
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "Propaga..."
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "Apple"
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "Informazioni su %s"
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "Preferenze..."
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "Opzioni..."
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "Aiuto"
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "Documentazione sul web"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "errore grave: impossibile effettuare lo stat del path %s: file o directory "
 "non trovata"
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "Ramo attuale:"
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "Modifiche preparate (saranno nella nuova revisione)"
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "Modifiche non preparate"
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "Prepara modificati"
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Propaga (Push)"
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "Messaggio di revisione iniziale:"
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr "Messaggio di revisione corretto:"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr "Messaggio iniziale di revisione corretto:"
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr "Messaggio di fusione corretto:"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "Messaggio di fusione:"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "Messaggio di revisione:"
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Copia tutto"
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "File:"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "Rinfresca"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "Applica/Inverti sezione"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "Diminuisci dimensione caratteri"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "Aumenta dimensione caratteri"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "Mostra meno contesto"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "Mostra più contesto"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Rinfresca"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Diminuisci dimensione caratteri"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Aumenta dimensione caratteri"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "Sezione non preparata per una nuova revisione"
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "Prepara sezione per una nuova revisione"
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "Inizializzazione..."
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -452,7 +452,7 @@
 "da %s:\n"
 "\n"
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -462,7 +462,7 @@
 "Ciò è dovuto a un problema conosciuto\n"
 "causato dall'eseguibile Tcl distribuito da Cygwin."
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -478,7 +478,7 @@
 "consiste nell'assegnare valori alle variabili di configurazione\n"
 "user.name e user.email nel tuo file ~/.gitconfig personale.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - un'interfaccia grafica per Git."
 
@@ -490,56 +490,56 @@
 msgid "Commit:"
 msgstr "Revisione:"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "Copia revisione"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Lettura di %s..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "Caricamento annotazioni per copie/spostamenti..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "linee annotate"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "Caricamento annotazioni per posizione originaria..."
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "Annotazione completata."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "Caricamento annotazioni..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "Autore:"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "Revisione creata da:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "File originario:"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "In origine da:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "Nel file:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "Copiato o spostato qui da:"
 
@@ -552,17 +552,17 @@
 msgstr "Attiva"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Annulla"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "Revisione"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "Opzioni"
 
@@ -582,7 +582,7 @@
 msgid "Create New Branch"
 msgstr "Crea nuovo ramo"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "Crea"
 
@@ -723,24 +723,24 @@
 msgid "File Browser"
 msgstr "File browser"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "Caricamento %s..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[Directory superiore]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "Esplora i file del ramo"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
-msgstr "Sfoglia"
+msgstr "Esplora"
 
 #: lib/checkout_op.tcl:79
 #, tcl-format
@@ -752,7 +752,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr "errore grave: impossibile risolvere %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "Chiudi"
 
@@ -811,6 +811,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr "Aggiornamento della directory di lavoro a '%s' in corso..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "file presenti nella directory di lavoro"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -837,7 +841,7 @@
 "Se si vuole rimanere su un ramo, crearne uno ora a partire da 'Questa "
 "revisione attiva staccata'."
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Attivazione di '%s' completata."
@@ -857,7 +861,7 @@
 msgid "Reset '%s'?"
 msgstr "Ripristinare '%s'?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "Visualizza"
 
@@ -887,246 +891,246 @@
 msgid "Font Family"
 msgstr "Famiglia di caratteri"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "Dimensione caratteri"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "Esempio caratteri"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
 msgstr ""
 "Questo è un testo d'esempio.\n"
-"Se ti piace questo testo, può essere il carattere giusto."
+"Se ti piace questo testo, scegli questo carattere."
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "Crea nuovo archivio"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "Nuovo..."
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "Clona archivio esistente"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "Clona..."
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "Apri archivio esistente"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "Apri..."
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "Archivi recenti"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "Apri archivio recente:"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "La posizione %s esiste già."
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Impossibile creare l'archivio %s:"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "Directory:"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "Archivio Git"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "La directory %s esiste già."
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Il file %s esiste già."
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "Clona"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "URL:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "Tipo di clone:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (veloce, semi-ridondante, con hardlink)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Copia completa (più lento, backup ridondante)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Shared (il più veloce, non raccomandato, nessun backup)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "%s non è un archivio Git."
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "Standard è disponibile solo per archivi locali."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "Shared è disponibile solo per archivi locali."
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Il file/directory %s esiste già."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "Impossibile configurare origin"
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "Calcolo oggetti"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Impossibile copiare oggetti/info/alternate: %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Niente da clonare da %s."
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "Il ramo 'master' non è stato inizializzato."
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Impossibile utilizzare gli hardlink. Si ricorrerà alla copia."
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Clonazione da %s"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "Copia degli oggetti"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Impossibile copiare oggetto: %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "Collegamento oggetti"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "oggetti"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Hardlink impossibile sull'oggetto: %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Impossibile recuperare rami e oggetti. Controllare i dettagli forniti dalla "
 "console."
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 "Impossibile recuperare le etichette. Controllare i dettagli forniti dalla "
 "console."
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 "Impossibile determinare HEAD. Controllare i dettagli forniti dalla console."
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Impossibile ripulire %s"
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "Clonazione non riuscita."
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "Non è stato trovato un ramo predefinito."
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Impossibile risolvere %s come una revisione."
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "Creazione directory di lavoro"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "file"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "Attivazione iniziale non riuscita."
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "Apri"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "Archivio:"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Impossibile accedere all'archivio %s:"
@@ -1147,7 +1151,7 @@
 msgid "Tracking Branch"
 msgstr "Duplicato locale di ramo remoto"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "Etichetta"
 
@@ -1164,11 +1168,11 @@
 msgid "Revision expression is empty."
 msgstr "L'espressione di revisione è vuota."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "Aggiornato"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "URL"
 
@@ -1268,7 +1272,7 @@
 "\n"
 "A good commit message has the following format:\n"
 "\n"
-"- First line: Describe in one sentance what you did.\n"
+"- First line: Describe in one sentence what you did.\n"
 "- Second line: Blank\n"
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
@@ -1280,16 +1284,45 @@
 "- Seconda linea: vuota.\n"
 "- Terza linea: spiega a cosa serve la tua modifica.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "attenzione: Tcl non supporta la codifica '%s'."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "Avvio pre-commit hook..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "Revisione rifiutata dal pre-commit hook."
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "Avvio commit-msg hook..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "Revisione rifiutata dal commit-msg hook."
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Archiviazione modifiche..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree non riuscito:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Impossibile creare una nuova revisione."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "La revisione %s sembra essere danneggiata"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1303,37 +1336,32 @@
 "\n"
 "Si procederà subito ad una nuova analisi.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "Nessuna modifica per la nuova revisione."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "attenzione: Tcl non supporta la codifica '%s'."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "commit-tree non riuscito:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "update-ref non riuscito:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Creata revisione %s: %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "Elaborazione in corso... attendere..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "Successo"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "Errore: comando non riuscito"
 
@@ -1444,23 +1472,23 @@
 msgid "Error loading diff:"
 msgstr "Errore nel caricamento delle differenze:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr "Impossibile rimuovere la sezione scelta dalla nuova revisione."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "Impossibile preparare la sezione scelta per una nuova revisione."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "errore"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "attenzione"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr ""
 "Bisogna correggere gli errori suddetti prima di creare una nuova revisione."
@@ -1494,6 +1522,10 @@
 msgid "Unstaging %s from commit"
 msgstr "%s non farà parte della prossima revisione"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Pronto per creare una nuova revisione."
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1509,13 +1541,13 @@
 msgid "Revert changes in these %i files?"
 msgstr "Annullare le modifiche in questi %i file?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Tutte le modifiche non preparate per una nuova revisione saranno perse per "
 "sempre."
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "Non fare niente"
 
@@ -1589,27 +1621,27 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
-msgstr "Fusione di %s e %s in corso"
+msgid "Merging %s and %s..."
+msgstr "Fusione di %s e %s in corso..."
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "Fusione completata con successo."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Fusione non riuscita. Bisogna risolvere i conflitti."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Fusione in %s"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "Revisione da fondere"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1619,7 +1651,7 @@
 "\n"
 "Bisogna finire di correggere questa revisione.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1634,7 +1666,7 @@
 "\n"
 "Continuare con l'interruzione della fusione attuale?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1649,109 +1681,109 @@
 "\n"
 "Continuare con l'annullamento delle modifiche attuali?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
-msgstr "Interruzione in corso"
+msgstr "Interruzione"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "ripristino file"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "Interruzione non riuscita."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "Interruzione completata. Pronto."
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Ripristina valori predefiniti"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "Salva"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "Archivio di %s"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "Tutti gli archivi"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "Nome utente"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr "Indirizzo Email"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "Riepilogo nelle revisioni di fusione"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "Prolissità della fusione"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "Mostra statistiche delle differenze dopo la fusione"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "Fidati delle date di modifica dei file"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr ""
 "Effettua potatura dei duplicati locali di rami remoti durante il recupero"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "Appaia duplicati locali di rami remoti"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "Numero di linee di contesto nelle differenze"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "Larghezza del messaggio di revisione"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "Modello per il nome di un nuovo ramo"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Lingua dizionario:"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "Cambia caratteri"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "Scegli %s"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "Preferenze"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "Impossibile salvare completamente le opzioni:"
 
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr "Effettua potatura da"
-
-#: lib/remote.tcl:170
-msgid "Fetch from"
-msgstr "Recupera da"
-
-#: lib/remote.tcl:213
-msgid "Push to"
-msgstr "Propaga verso"
-
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
 msgid "Delete Remote Branch"
 msgstr "Cancella ramo remoto"
@@ -1836,6 +1868,18 @@
 msgid "Scanning %s..."
 msgstr "Analisi in corso %s..."
 
+#: lib/remote.tcl:165
+msgid "Prune from"
+msgstr "Effettua potatura da"
+
+#: lib/remote.tcl:170
+msgid "Fetch from"
+msgstr "Recupera da"
+
+#: lib/remote.tcl:213
+msgid "Push to"
+msgstr "Propaga verso"
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "Impossibile scrivere shortcut:"
@@ -1844,6 +1888,43 @@
 msgid "Cannot write icon:"
 msgstr "Impossibile scrivere icona:"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Correttore ortografico non supportato"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Correzione ortografica indisponibile"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "La configurazione del correttore ortografico non è valida"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Il dizionario è stato reimpostato su %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Il correttore ortografico ha riportato un errore all'avvio"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Correttore ortografico sconosciuto"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Nessun suggerimento"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "Il correttore ortografico ha mandato un EOF inaspettato"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "Errore nel correttore ortografico"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
diff --git a/git-gui/po/ja.po b/git-gui/po/ja.po
index e2cf5bd..28e6d62 100644
--- a/git-gui/po/ja.po
+++ b/git-gui/po/ja.po
@@ -8,41 +8,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
-"PO-Revision-Date: 2007-12-05 06:12+0900\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2008-03-15 20:12+0900\n"
 "Last-Translator: しらいし ななこ <nanako3@bluebottle.com>\n"
 "Language-Team: Japanese\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: 致命的なエラー"
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "%s に無効なフォントが指定されています:"
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "主フォント"
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "diff/コンソール・フォント"
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "PATH 中に git が見つかりません"
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Git バージョン名が理解できません:"
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,79 +61,79 @@
 "\n"
 "'%s' はバージョン 1.5.0 と思って良いですか?\n"
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "Git ディレクトリが見つかりません:"
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr "作業ディレクトリの最上位に移動できません"
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "変な .git ディレクトリは使えません"
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "作業ディレクトリがありません"
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "ファイル状態を更新しています…"
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "変更されたファイルをスキャンしています…"
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "準備完了"
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "変更無し"
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "変更あり、コミット未予定"
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "コミット予定済"
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "部分的にコミット予定済"
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "コミット予定済、ファイル無し"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "管理外、コミット未予定"
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "ファイル無し"
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "削除予定済"
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "削除予定済、ファイル未削除"
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "要マージ解決"
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "gitk を起動中…お待ち下さい…"
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -144,297 +144,297 @@
 "\n"
 "%s がありません"
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "リポジトリ"
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "編集"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "ブランチ"
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "コミット"
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "マージ"
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "リモート"
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "現在のブランチのファイルを見る"
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "ブランチのファイルを見る…"
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "現在のブランチの履歴を見る"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "全てのブランチの履歴を見る"
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "ブランチ %s のファイルを見る"
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "ブランチ %s の履歴を見る"
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "データベース統計"
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "データベース圧縮"
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "データベース検証"
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "デスクトップ・アイコンを作る"
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "終了"
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "元に戻す"
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "やり直し"
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "切り取り"
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "コピー"
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "貼り付け"
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "削除"
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "全て選択"
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "作成…"
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "チェックアウト"
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "名前変更…"
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "削除…"
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "リセット…"
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "新規コミット"
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "最新コミットを訂正"
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "再スキャン"
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "コミット予定する"
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "変更されたファイルをコミット予定"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "コミットから降ろす"
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "変更を元に戻す"
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "署名"
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "コミット"
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "ローカル・マージ…"
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "マージ中止…"
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "プッシュ…"
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "りんご"
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "%s について"
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "設定…"
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "オプション…"
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "ヘルプ"
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "オンライン・ドキュメント"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "致命的: パス %s が stat できません。そのようなファイルやディレクトリはありま"
 "せん"
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "現在のブランチ"
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "ステージングされた(コミット予定済の)変更"
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "コミット予定に入っていない変更"
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "変更をコミット予定に入れる"
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "プッシュ"
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "最初のコミットメッセージ:"
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr "訂正したコミットメッセージ:"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr "訂正した最初のコミットメッセージ:"
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr "訂正したマージコミットメッセージ:"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "マージコミットメッセージ:"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "コミットメッセージ:"
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "全てコピー"
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "ファイル:"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "再読み込み"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "パッチを適用/取り消す"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "フォントを小さく"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "フォントを大きく"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "文脈を少なく"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "文脈を多く"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "再読み込み"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "フォントを小さく"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "フォントを大きく"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "パッチをコミット予定から外す"
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "パッチをコミット予定に加える"
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "初期化しています…"
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -449,7 +449,7 @@
 "以下の環境変数は %s が起動する Git サブプロセスによって無視されるでしょう:\n"
 "\n"
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -459,7 +459,7 @@
 "これは Cygwin で配布されている Tcl バイナリに\n"
 "関しての既知の問題によります"
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -474,7 +474,7 @@
 "個人的な ~/.gitconfig ファイル内で user.name と user.email の値を設定\n"
 "するのが、%s の良い代用となります\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "Git のグラフィカルUI git-gui"
 
@@ -486,56 +486,56 @@
 msgid "Commit:"
 msgstr "コミット:"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "コミットをコピー"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "%s を読んでいます…"
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "コピー・移動追跡データを読んでいます…"
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "行を注釈しました"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "元位置行の注釈データを読んでいます…"
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "注釈完了しました"
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "注釈を読み込んでいます…"
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "作者:"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "コミット者:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "元ファイル"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "原作者:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "ファイル:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "複写・移動者:"
 
@@ -548,17 +548,17 @@
 msgstr "チェックアウト"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "中止"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "リビジョン"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "オプション"
 
@@ -578,7 +578,7 @@
 msgid "Create New Branch"
 msgstr "ブランチを新規作成"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "作成"
 
@@ -718,22 +718,22 @@
 msgid "File Browser"
 msgstr "ファイル・ブラウザ"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "%s をロード中…"
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[上位フォルダへ]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "現在のブランチのファイルを見る"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr "ブラウズ"
 
@@ -747,7 +747,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr "致命的エラー: %s を解決できません"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "閉じる"
 
@@ -804,6 +804,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr "作業ディレクトリを '%s' に更新しています…"
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "チェックアウトされたファイル"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -830,7 +834,7 @@
 "ブランチ上に滞まりたいときは、この「分離されたチェックアウト」から新規ブラン"
 "チを開始してください。"
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "'%s' をチェックアウトしました"
@@ -849,7 +853,7 @@
 msgid "Reset '%s'?"
 msgstr "'%s' をリセットしますか?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "可視化"
 
@@ -877,15 +881,15 @@
 msgid "Font Family"
 msgstr "フォント・ファミリー"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "フォントの大きさ"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "フォント・サンプル"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -893,225 +897,225 @@
 "これはサンプル文です。\n"
 "このフォントが気に入ればお使いになれます。"
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr "Git GUI"
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "新しいリポジトリを作る"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "新規…"
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "既存リポジトリを複製する"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "複製…"
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "既存リポジトリを開く"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "開く…"
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "最近使ったリポジトリ"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "最近使ったリポジトリを開く"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "'%s' は既に存在します。"
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "リポジトリ %s を作製できません:"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "ディレクトリ:"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "GIT リポジトリ"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "ディレクトリ '%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "ファイル '%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "複製"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "URL:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "複製方式:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "標準(高速・中冗長度・ハードリンク)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "全複写(低速・冗長バックアップ)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "共有(最高速・非推奨・バックアップ無し)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Git リポジトリではありません: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "標準方式は同一計算機上のリポジトリにのみ使えます。"
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "共有方式は同一計算機上のリポジトリにのみ使えます。"
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "'%s' は既に存在します。"
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "origin を設定できませんでした"
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "オブジェクトを数えています"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr "バケツ"
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "objects/info/alternates を複写できません: %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "%s から複製する内容はありません"
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "'master' ブランチが初期化されていません"
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "ハードリンクが作れないので、コピーします"
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "%s から複製しています"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "オブジェクトを複写しています"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "オブジェクトを複写できません: %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "オブジェクトを連結しています"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "オブジェクト"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "オブジェクトをハードリンクできません: %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr "ブランチやオブジェクトを取得できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "タグを取得できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "HEAD を確定できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "%s を掃除できません"
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "複写に失敗しました。"
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "デフォールト・ブランチが取得されませんでした"
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "%s をコミットとして解釈できません"
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "作業ディレクトリを作成しています"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "ファイル"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "初期チェックアウトに失敗しました"
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "開く"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "リポジトリ:"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "リポジトリ %s を開けません:"
@@ -1132,7 +1136,7 @@
 msgid "Tracking Branch"
 msgstr "トラッキング・ブランチ"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "タグ"
 
@@ -1149,11 +1153,11 @@
 msgid "Revision expression is empty."
 msgstr "リビジョン式が空です。"
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "更新しました"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "URL"
 
@@ -1262,16 +1266,45 @@
 "- 第2行: 空白\n"
 "- 残りの行: なぜ、この変更が良い変更か、の説明。\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "警告: Tcl はエンコーディング '%s' をサポートしていません"
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "コミット前フックを実行中・・・"
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "コミット前フックがコミットを拒否しました"
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "コミット・メッセージ・フックを実行中・・・"
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "コミット・メッセージ・フックがコミットを拒否しました"
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "変更点をコミット中・・・"
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree が失敗しました:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "コミットに失敗しました。"
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "コミット %s は壊れています"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1285,37 +1318,32 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "コミットする変更がありません。"
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "警告: Tcl はエンコーディング '%s' をサポートしていません"
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "commit-tree が失敗しました:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "update-ref が失敗しました:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "コミット %s を作成しました: %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "実行中…お待ち下さい…"
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "成功"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "エラー: コマンドが失敗しました"
 
@@ -1425,23 +1453,23 @@
 msgid "Error loading diff:"
 msgstr "diff を読む際のエラーです:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr "選択されたパッチをコミット予定から外せません。"
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "選択されたパッチをコミット予定に加えられません。"
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "エラー"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "警告"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr "コミットする前に、以上のエラーを修正して下さい"
 
@@ -1457,7 +1485,9 @@
 msgid ""
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
-msgstr "GIT インデックスの更新が失敗しました。git-gui と同期をとるために再スキャンします。"
+msgstr ""
+"GIT インデックスの更新が失敗しました。git-gui と同期をとるために再スキャンし"
+"ます。"
 
 #: lib/index.tcl:27
 msgid "Continue"
@@ -1472,6 +1502,10 @@
 msgid "Unstaging %s from commit"
 msgstr "コミットから '%s' を降ろす"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "コミット準備完了"
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1487,11 +1521,11 @@
 msgid "Revert changes in these %i files?"
 msgstr "これら %i 個のファイルにした変更を元に戻しますか?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr "変更を元に戻すとコミット予定していない変更は全て失われます。"
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "何もしない"
 
@@ -1562,27 +1596,27 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
-msgstr "%s と %s をマージします"
+msgid "Merging %s and %s..."
+msgstr "%s と %s をマージ中・・・"
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "マージが完了しました"
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "マージが失敗しました。衝突の解決が必要です。"
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "%s にマージ"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "マージするリビジョン"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1592,7 +1626,7 @@
 "\n"
 "まず今のコミット訂正を完了させて下さい。\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1606,7 +1640,7 @@
 "\n"
 "マージを中断してよろしいですか?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1620,93 +1654,105 @@
 "\n"
 "リセットしてよろしいですか?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr "中断しています"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "リセットしたファイル"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "中断に失敗しました。"
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "中断完了。"
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "既定値に戻す"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "保存"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "%s リポジトリ"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "大域(全てのリポジトリ)"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "ユーザ名"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr "電子メールアドレス"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "マージコミットの要約"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "マージの冗長度"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "マージ後に diffstat を表示"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "ファイル変更時刻を信頼する"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr "フェッチ中にトラッキングブランチを刈る"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "トラッキングブランチを合わせる"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "diff の文脈行数"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "コミットメッセージのテキスト幅"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "新しいブランチ名のテンプレート"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "スペルチェック辞書"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "フォントを変更"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "%s を選択"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr "ポイント"
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "設定"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "完全にオプションを保存できません:"
 
@@ -1814,6 +1860,43 @@
 msgid "Cannot write icon:"
 msgstr "アイコンが書けません:"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "サポートされていないスペルチェッカーです"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "スペルチェック機能は使えません"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "スペルチェックの設定が不正です"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "辞書を %s に巻き戻します"
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "スペルチェッカーの起動に失敗しました"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "スペルチェッカーが判別できません"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "提案なし"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "スペルチェッカーが予想外の EOF を返しました"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "スペルチェック失敗"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
@@ -1881,3 +1964,6 @@
 #: lib/transport.tcl:168
 msgid "Include tags"
 msgstr "タグを含める"
+
+#~ msgid "Not connected to aspell"
+#~ msgstr "aspell に接続していません"
diff --git a/git-gui/po/ru.po b/git-gui/po/ru.po
index 6727a83..db55b3e 100644
--- a/git-gui/po/ru.po
+++ b/git-gui/po/ru.po
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-10-31 21:23+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
 "PO-Revision-Date: 2007-10-22 22:30-0200\n"
 "Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n"
 "Language-Team: Russian Translation <git@vger.kernel.org>\n"
@@ -15,33 +15,33 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:597 git-gui.sh:611 git-gui.sh:624 git-gui.sh:707
-#: git-gui.sh:726
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: критическая ошибка"
 
-#: git-gui.sh:558
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "В %s установлен неверный шрифт:"
 
-#: git-gui.sh:583
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "Шрифт интерфейса"
 
-#: git-gui.sh:584
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "Шрифт консоли и изменений (diff)"
 
-#: git-gui.sh:598
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "git не найден в PATH."
 
-#: git-gui.sh:625
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Невозможно распознать строку версии Git: "
 
-#: git-gui.sh:643
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -59,79 +59,79 @@
 "\n"
 "Принять '%s' как версию 1.5.0?\n"
 
-#: git-gui.sh:881
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "Каталог Git не найден:"
 
-#: git-gui.sh:888
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr "Невозможно перейти к корню рабочего каталога репозитория: "
 
-#: git-gui.sh:895
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "Каталог.git испорчен: "
 
-#: git-gui.sh:900
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "Отсутствует рабочий каталог"
 
-#: git-gui.sh:1047
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "Обновление информации о состоянии файлов..."
 
-#: git-gui.sh:1112
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "Поиск измененных файлов..."
 
-#: git-gui.sh:1287 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Готово."
 
-#: git-gui.sh:1553
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "Не изменено"
 
-#: git-gui.sh:1555
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "Изменено, не подготовлено"
 
-#: git-gui.sh:1556 git-gui.sh:1561
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "Подготовлено для сохранения"
 
-#: git-gui.sh:1557 git-gui.sh:1562
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "Части, подготовленные для сохранения"
 
-#: git-gui.sh:1558 git-gui.sh:1563
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "Подготовлено для сохранения, отсутствует"
 
-#: git-gui.sh:1560
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "Не отслеживается, не подготовлено"
 
-#: git-gui.sh:1565
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "Отсутствует"
 
-#: git-gui.sh:1566
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "Подготовлено для удаления"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "Подготовлено для удаления, еще не удалено"
 
-#: git-gui.sh:1569 git-gui.sh:1570 git-gui.sh:1571 git-gui.sh:1572
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "Требуется разрешение конфликта при объединении"
 
-#: git-gui.sh:1607
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "Запускается gitk... пожалуйста, ждите..."
 
-#: git-gui.sh:1616
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -142,295 +142,295 @@
 "\n"
 "%s не существует"
 
-#: git-gui.sh:1816 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Репозиторий"
 
-#: git-gui.sh:1817
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "Редактировать"
 
-#: git-gui.sh:1819 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Ветвь"
 
-#: git-gui.sh:1822 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Состояние"
 
-#: git-gui.sh:1825 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "Объединить"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Внешние репозитории"
 
-#: git-gui.sh:1835
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "Просмотреть файлы текущей ветви"
 
-#: git-gui.sh:1839
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "Показать файлы ветви..."
 
-#: git-gui.sh:1844
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "История текущей ветви наглядно"
 
-#: git-gui.sh:1848
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "История всех ветвей наглядно"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Показать файлы ветви %s"
 
-#: git-gui.sh:1857
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "История ветви %s наглядно"
 
-#: git-gui.sh:1862 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Статистика базы данных"
 
-#: git-gui.sh:1865 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Сжать базу данных"
 
-#: git-gui.sh:1868
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "Проверить базу данных"
 
-#: git-gui.sh:1875 git-gui.sh:1879 git-gui.sh:1883 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Создать ярлык на рабочем столе"
 
-#: git-gui.sh:1888 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "Выход"
 
-#: git-gui.sh:1895
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "Отменить"
 
-#: git-gui.sh:1898
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "Повторить"
 
-#: git-gui.sh:1902 git-gui.sh:2395
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "Вырезать"
 
-#: git-gui.sh:1905 git-gui.sh:2398 git-gui.sh:2469 git-gui.sh:2541
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "Копировать"
 
-#: git-gui.sh:1908 git-gui.sh:2401
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "Вставить"
 
-#: git-gui.sh:1911 git-gui.sh:2404 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Удалить"
 
-#: git-gui.sh:1915 git-gui.sh:2408 git-gui.sh:2545 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "Выделить все"
 
-#: git-gui.sh:1924
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "Создать..."
 
-#: git-gui.sh:1930
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "Перейти..."
 
-#: git-gui.sh:1936
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "Переименовать..."
 
-#: git-gui.sh:1941 git-gui.sh:2040
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "Удалить..."
 
-#: git-gui.sh:1946
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "Сбросить..."
 
-#: git-gui.sh:1958 git-gui.sh:2342
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "Новое состояние"
 
-#: git-gui.sh:1966 git-gui.sh:2349
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "Исправить последнее состояние"
 
-#: git-gui.sh:1975 git-gui.sh:2309 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Перечитать"
 
-#: git-gui.sh:1981
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "Подготовить для сохранения"
 
-#: git-gui.sh:1986
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "Подготовить измененные файлы для сохранения"
 
-#: git-gui.sh:1992
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "Убрать из подготовленного"
 
-#: git-gui.sh:1997 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "Отменить изменения"
 
-#: git-gui.sh:2004 git-gui.sh:2321 git-gui.sh:2419
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "Подписать"
 
-#: git-gui.sh:2008 git-gui.sh:2325
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "Сохранить"
 
-#: git-gui.sh:2019
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "Локальное объединение..."
 
-#: git-gui.sh:2024
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "Прервать объединение..."
 
-#: git-gui.sh:2036
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "Отправить..."
 
-#: git-gui.sh:2047 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr ""
 
-#: git-gui.sh:2050 git-gui.sh:2072 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "О %s"
 
-#: git-gui.sh:2054
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "Настройки..."
 
-#: git-gui.sh:2062 git-gui.sh:2587
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "Настройки..."
 
-#: git-gui.sh:2068 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "Помощь"
 
-#: git-gui.sh:2109
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "Документация в интернете"
 
-#: git-gui.sh:2193
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr "критическая ошибка: %s: нет такого файла или каталога"
 
-#: git-gui.sh:2226
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "Текущая ветвь:"
 
-#: git-gui.sh:2247
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "Подготовлено (будет сохранено)"
 
-#: git-gui.sh:2266
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "Изменено (не будет сохранено)"
 
-#: git-gui.sh:2315
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "Подготовить все"
 
-#: git-gui.sh:2331 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Отправить"
 
-#: git-gui.sh:2361
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "Комментарий к первому состоянию:"
 
-#: git-gui.sh:2362
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr "Комментарий к исправленному состоянию:"
 
-#: git-gui.sh:2363
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr "Комментарий к исправленному первоначальному состоянию:"
 
-#: git-gui.sh:2364
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr "Комментарий к исправленному объединению:"
 
-#: git-gui.sh:2365
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "Комментарий к объединению:"
 
-#: git-gui.sh:2366
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "Комментарий к состоянию:"
 
-#: git-gui.sh:2411 git-gui.sh:2549 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Копировать все"
 
-#: git-gui.sh:2435 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "Файл:"
 
-#: git-gui.sh:2537
-msgid "Refresh"
-msgstr "Обновить"
-
-#: git-gui.sh:2558
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "Применить/Убрать изменение"
 
-#: git-gui.sh:2564
-msgid "Decrease Font Size"
-msgstr "Уменьшить размер шрифта"
-
-#: git-gui.sh:2568
-msgid "Increase Font Size"
-msgstr "Увеличить размер шрифта"
-
-#: git-gui.sh:2573
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "Меньше контекста"
 
-#: git-gui.sh:2580
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "Больше контекста"
 
-#: git-gui.sh:2594
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Обновить"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Уменьшить размер шрифта"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Увеличить размер шрифта"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "Не сохранять часть"
 
-#: git-gui.sh:2596
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "Подготовить часть для сохранения"
 
-#: git-gui.sh:2615
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "Инициализация..."
 
-#: git-gui.sh:2706
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -447,7 +447,7 @@
 "запущенными из %s\n"
 "\n"
 
-#: git-gui.sh:2736
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -457,7 +457,7 @@
 "Это известная проблема с Tcl,\n"
 "распространяемым Cygwin."
 
-#: git-gui.sh:2741
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -474,7 +474,7 @@
 "user.email в Вашем персональном\n"
 "файле ~/.gitconfig.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - графический пользовательский интерфейс к Git."
 
@@ -486,56 +486,56 @@
 msgid "Commit:"
 msgstr "Сохраненное состояние:"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "Скопировать SHA-1"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Чтение %s..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "Загрузка аннотации копирований/переименований..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "строк прокомментировано"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "Загрузка аннотаций первоначального положения объекта..."
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "Аннотация завершена."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "Загрузка аннотации..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "Автор:"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "Сохранил:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "Исходный файл:"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "Источник:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "Файл:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "Скопировано/перемещено в:"
 
@@ -548,17 +548,17 @@
 msgstr "Перейти"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Отменить"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "Версия"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "Настройки"
 
@@ -578,7 +578,7 @@
 msgid "Create New Branch"
 msgstr "Создать новую ветвь"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "Создать"
 
@@ -718,22 +718,22 @@
 msgid "File Browser"
 msgstr "Просмотр списка файлов"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "Загрузка %s..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[На уровень выше]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "Показать файлы ветви"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr "Показать"
 
@@ -747,7 +747,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr "критическая ошибка: невозможно разрешить %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "Закрыть"
 
@@ -804,6 +804,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr "Обновление рабочего каталога из '%s'..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "файлы извлечены"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -830,7 +834,7 @@
 "Если вы хотите снова вернуться к какой-нибудь ветви, создайте ее сейчас, "
 "начиная с 'Текущего отсоединенного состояния'."
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Ветвь '%s' сделана текущей."
@@ -849,7 +853,7 @@
 msgid "Reset '%s'?"
 msgstr "Сбросить '%s'?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "Наглядно"
 
@@ -878,15 +882,15 @@
 msgid "Font Family"
 msgstr "Шрифт"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "Размер шрифта"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "Пример текста"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -894,226 +898,226 @@
 "Это пример текста.\n"
 "Если Вам нравится этот текст, это может быть Ваш шрифт."
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr ""
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "Создать новый репозиторий"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "Новый..."
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "Склонировать существующий репозиторий"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "Склонировать..."
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "Выбрать существующий репозиторий"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "Открыть..."
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "Недавние репозитории"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "Открыть последний репозиторий"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "Путь '%s' уже существует."
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Не удалось создать репозиторий %s:"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "Каталог:"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "Репозиторий"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Каталог '%s' уже существует."
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Файл '%s' уже существует."
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "Склонировать"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "Ссылка:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "Тип клона:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Стандартный (Быстрый, полуизбыточный, \"жесткие\" ссылки)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Полная копия (Медленный, создает резервную копию)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Общий (Самый быстрый, не рекомендуется, без резервной копии)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Каталог не является репозиторием: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "Стандартный клон возможен только для локального репозитория."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "Общий клон возможен только для локального репозитория."
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Путь '%s' уже существует."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "Не могу сконфигурировать исходный репозиторий."
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "Считаю объекты"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Не могу скопировать objects/info/alternates: %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Нечего клонировать с %s."
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "Не инициализирована ветвь 'master'."
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "\"Жесткие ссылки\" не доступны. Буду использовать копирование."
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Клонирование %s"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "Копирование objects"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "КБ"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Не могу скопировать объект: %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "Создание ссылок на objects"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "объекты"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Не могу \"жестко связать\" объект: %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Не могу получить ветви и объекты. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Не могу получить метки. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Не могу определить HEAD. Дополнительная информация на консоли."
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Не могу очистить %s"
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "Клонирование не удалось."
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "Не было получено ветви по умолчанию."
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Не могу распознать %s как состояние."
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "Создаю рабочий каталог"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "файлов"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "Не удалось получить начальное состояние файлов репозитория."
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "Открыть"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "Репозиторий:"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Не удалось открыть репозиторий %s:"
@@ -1134,7 +1138,7 @@
 msgid "Tracking Branch"
 msgstr "Ветвь слежения"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "Таг"
 
@@ -1151,11 +1155,11 @@
 msgid "Revision expression is empty."
 msgstr "Пустое выражение для определения версии."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "Обновлено"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "Ссылка"
 
@@ -1251,7 +1255,7 @@
 "\n"
 "A good commit message has the following format:\n"
 "\n"
-"- First line: Describe in one sentance what you did.\n"
+"- First line: Describe in one sentence what you did.\n"
 "- Second line: Blank\n"
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
@@ -1263,16 +1267,45 @@
 "- вторая строка пустая\n"
 "- оставшиеся строки: опишите, что дают ваши изменения.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "предупреждение: Tcl не поддерживает кодировку '%s'."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "Вызов программы поддержки репозитория pre-commit..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "Сохранение прервано программой поддержки репозитория pre-commit"
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "Вызов программы поддержки репозитория commit-msg..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "Сохранение прервано программой поддержки репозитория commit-msg"
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Сохранение изменений..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "Программа write-tree завершилась с ошибкой:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Сохранить состояние не удалось."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Состояние %s выглядит поврежденным"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1286,37 +1319,32 @@
 "\n"
 "Сейчас автоматически запустится перечитывание репозитория.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "Отуствуют измения для сохранения."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "предупреждение: Tcl не поддерживает кодировку '%s'."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "Программа commit-tree завершилась с ошибкой:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "Программа update-ref завершилась с ошибкой:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Создано состояние %s: %s "
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "В процессе... пожалуйста, ждите..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "Процесс успешно завершен"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "Ошибка: не удалось выполнить команду"
 
@@ -1426,23 +1454,23 @@
 msgid "Error loading diff:"
 msgstr "Ошибка загрузки diff:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr "Не удалось исключить выбранную часть."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "Не удалось подготовить к сохранению выбранную часть."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "ошибка"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "предупреждение"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr "Прежде чем сохранить, исправьте вышеуказанные ошибки."
 
@@ -1459,8 +1487,8 @@
 "Updating the Git index failed.  A rescan will be automatically started to "
 "resynchronize git-gui."
 msgstr ""
-"Не удалось обновить индекс Git. Состояние репозитория будет"
-"перечитано автоматически."
+"Не удалось обновить индекс Git. Состояние репозитория будетперечитано "
+"автоматически."
 
 #: lib/index.tcl:27
 msgid "Continue"
@@ -1475,6 +1503,10 @@
 msgid "Unstaging %s from commit"
 msgstr "Удаление %s из подготовленного"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Подготовлено для сохранения"
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1490,13 +1522,13 @@
 msgid "Revert changes in these %i files?"
 msgstr "Отменить изменения в %i файле(-ах)?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Любые изменения, не подготовленные к сохранению, будут потеряны при данной "
 "операции."
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "Ничего не делать"
 
@@ -1567,28 +1599,27 @@
 msgstr "%s из %s"
 
 #: lib/merge.tcl:119
-#, tcl-format
-msgid "Merging %s and %s"
-msgstr "Объединение %s и %s"
+msgid "Merging %s and %s..."
+msgstr "Объединение %s и %s..."
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "Объединение успешно завершено."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Не удалось завершить объединение. Требуется разрешение конфликта."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Объединить с %s"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "Версия для объединения"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1598,7 +1629,7 @@
 "\n"
 "Завершите текущее исправление сохраненного состояния.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1612,7 +1643,7 @@
 "\n"
 "Продолжить?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1626,94 +1657,106 @@
 "\n"
 "Продолжить?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr "Прерываю"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "изменения в файлах отменены"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "Прервать не удалось."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "Прервано."
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Восстановить настройки по умолчанию"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "Сохранить"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "для репозитория %s"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "Общие (для всех репозиториев)"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "Имя пользователя"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
-msgstr "Адес электронной почты"
+msgstr "Адрес электронной почты"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "Суммарный комментарий при объединении"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "Уровень детальности сообщений при объединении"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "Показать отчет об изменениях после объединения"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "Доверять времени модификации файла"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Чистка ветвей слежения при получении изменений"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "Имя новой ветви взять из имен ветвей слежения"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "Число строк в контексте diff"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "Ширина комментария к состоянию:"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "Шаблон для имени новой ветви"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Словарь для проверки правописания:"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "Изменить шрифт"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "Выберите %s"
 
 # carbon copy
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr ""
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "Настройки"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "Не удалось полностью сохранить настройки:"
 
@@ -1820,6 +1863,43 @@
 msgid "Cannot write icon:"
 msgstr "Невозможно записать значок:"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Неподдерживаемая программа проверки правописания"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Проверка правописания не доступна"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Неправильная конфигурация программы проверки правописания"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Словарь вернут к %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Программа проверки правописания не смогла запустится"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Нераспознаная программа проверки правописания"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Исправлений не найдено"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "Программа проверки правописания прервала передачу данных"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "Ошибка проверки правописания"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
diff --git a/git-gui/po/sv.po b/git-gui/po/sv.po
index cd3f40b..4da687b 100644
--- a/git-gui/po/sv.po
+++ b/git-gui/po/sv.po
@@ -7,42 +7,41 @@
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-24 10:36+0100\n"
-"PO-Revision-Date: 2008-01-12 09:27+0100\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
+"PO-Revision-Date: 2008-03-14 07:23+0100\n"
 "Last-Translator: Peter Karlsson <peter@softwolves.pp.se>\n"
-"Language-Team: Swedish <sv@li.org>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: KBabel 1.11.4\n"
+"Content-Transfer-Encoding: 8bit"
 
-#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714
-#: git-gui.sh:733
+#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
+#: git-gui.sh:763
 msgid "git-gui: fatal error"
 msgstr "git-gui: ödesdigert fel"
 
-#: git-gui.sh:565
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Ogiltigt teckensnitt angivet i %s:"
 
-#: git-gui.sh:590
+#: git-gui.sh:620
 msgid "Main Font"
 msgstr "Huvudteckensnitt"
 
-#: git-gui.sh:591
+#: git-gui.sh:621
 msgid "Diff/Console Font"
 msgstr "Diff/konsolteckensnitt"
 
-#: git-gui.sh:605
+#: git-gui.sh:635
 msgid "Cannot find git in PATH."
 msgstr "Hittar inte git i PATH."
 
-#: git-gui.sh:632
+#: git-gui.sh:662
 msgid "Cannot parse Git version string:"
 msgstr "Kan inte tolka versionssträng från Git:"
 
-#: git-gui.sh:650
+#: git-gui.sh:680
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,79 +60,79 @@
 "\n"
 "Anta att \"%s\" är version 1.5.0?\n"
 
-#: git-gui.sh:888
+#: git-gui.sh:918
 msgid "Git directory not found:"
 msgstr "Git-katalogen hittades inte:"
 
-#: git-gui.sh:895
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
 msgstr "Kan inte gå till början på arbetskatalogen:"
 
-#: git-gui.sh:902
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
 msgstr "Kan inte använda underlig .git-katalog:"
 
-#: git-gui.sh:907
+#: git-gui.sh:937
 msgid "No working directory"
 msgstr "Ingen arbetskatalog"
 
-#: git-gui.sh:1054
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
 msgstr "Uppdaterar filstatus..."
 
-#: git-gui.sh:1119
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
 msgstr "Söker efter ändrade filer..."
 
-#: git-gui.sh:1294 lib/browser.tcl:245
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Klar."
 
-#: git-gui.sh:1560
+#: git-gui.sh:1590
 msgid "Unmodified"
 msgstr "Oförändrade"
 
-#: git-gui.sh:1562
+#: git-gui.sh:1592
 msgid "Modified, not staged"
 msgstr "Förändrade, ej köade"
 
-#: git-gui.sh:1563 git-gui.sh:1568
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
 msgstr "Köade för incheckning"
 
-#: git-gui.sh:1564 git-gui.sh:1569
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
 msgstr "Delar köade för incheckning"
 
-#: git-gui.sh:1565 git-gui.sh:1570
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
 msgstr "Köade för incheckning, saknade"
 
-#: git-gui.sh:1567
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
 msgstr "Ej spårade, ej köade"
 
-#: git-gui.sh:1572
+#: git-gui.sh:1602
 msgid "Missing"
 msgstr "Saknade"
 
-#: git-gui.sh:1573
+#: git-gui.sh:1603
 msgid "Staged for removal"
 msgstr "Köade för borttagning"
 
-#: git-gui.sh:1574
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
 msgstr "Köade för borttagning, fortfarande närvarande"
 
-#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
 msgstr "Kräver konflikthantering efter sammanslagning"
 
-#: git-gui.sh:1614
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
 msgstr "Startar gitk... vänta..."
 
-#: git-gui.sh:1623
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
@@ -144,295 +143,296 @@
 "\n"
 "%s finns inte"
 
-#: git-gui.sh:1823 lib/choose_repository.tcl:35
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Arkiv"
 
-#: git-gui.sh:1824
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "Redigera"
 
-#: git-gui.sh:1826 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Gren"
 
-#: git-gui.sh:1829 lib/choose_rev.tcl:547
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Incheckning"
 
-#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
 msgstr "Slå ihop"
 
-#: git-gui.sh:1833 lib/choose_rev.tcl:556
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Fjärr"
 
-#: git-gui.sh:1842
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
 msgstr "Bläddra i grenens filer"
 
-#: git-gui.sh:1846
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
 msgstr "Bläddra filer på gren..."
 
-#: git-gui.sh:1851
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
 msgstr "Visualisera grenens historik"
 
-#: git-gui.sh:1855
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
 msgstr "Visualisera alla grenars historik"
 
-#: git-gui.sh:1862
+#: git-gui.sh:1899
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Bläddra i filer för %s"
 
-#: git-gui.sh:1864
+#: git-gui.sh:1901
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualisera historik för %s"
 
-#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Databasstatistik"
 
-#: git-gui.sh:1872 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Komprimera databas"
 
-#: git-gui.sh:1875
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "Verifiera databas"
 
-#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Skapa skrivbordsikon"
 
-#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "Avsluta"
 
-#: git-gui.sh:1902
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "Ångra"
 
-#: git-gui.sh:1905
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "Gör om"
 
-#: git-gui.sh:1909 git-gui.sh:2403
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "Klipp ut"
 
-#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "Kopiera"
 
-#: git-gui.sh:1915 git-gui.sh:2409
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "Klistra in"
 
-#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Ta bort"
 
-#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "Markera alla"
 
-#: git-gui.sh:1931
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "Skapa..."
 
-#: git-gui.sh:1937
+#: git-gui.sh:1974
 msgid "Checkout..."
 msgstr "Checka ut..."
 
-#: git-gui.sh:1943
+#: git-gui.sh:1980
 msgid "Rename..."
 msgstr "Byt namn..."
 
-#: git-gui.sh:1948 git-gui.sh:2048
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "Ta bort..."
 
-#: git-gui.sh:1953
+#: git-gui.sh:1990
 msgid "Reset..."
 msgstr "Återställ..."
 
-#: git-gui.sh:1965 git-gui.sh:2350
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
 msgstr "Ny incheckning"
 
-#: git-gui.sh:1973 git-gui.sh:2357
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
 msgstr "Lägg till föregående incheckning"
 
-#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Sök på nytt"
 
-#: git-gui.sh:1988
+#: git-gui.sh:2025
 msgid "Stage To Commit"
 msgstr "Köa för incheckning"
 
-#: git-gui.sh:1994
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
 msgstr "Köa ändrade filer för incheckning"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
 msgstr "Ta bort från incheckningskö"
 
-#: git-gui.sh:2005 lib/index.tcl:393
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
 msgstr "Återställ ändringar"
 
-#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
 msgstr "Skriv under"
 
-#: git-gui.sh:2016 git-gui.sh:2333
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "Checka in"
 
-#: git-gui.sh:2027
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "Lokal sammanslagning..."
 
-#: git-gui.sh:2032
+#: git-gui.sh:2069
 msgid "Abort Merge..."
 msgstr "Avbryt sammanslagning..."
 
-#: git-gui.sh:2044
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "Sänd..."
 
-#: git-gui.sh:2055 lib/choose_repository.tcl:40
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "Äpple"
 
-#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13
-#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
 msgstr "Om %s"
 
-#: git-gui.sh:2062
+#: git-gui.sh:2099
 msgid "Preferences..."
 msgstr "Inställningar..."
 
-#: git-gui.sh:2070 git-gui.sh:2595
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "Alternativ..."
 
-#: git-gui.sh:2076 lib/choose_repository.tcl:46
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "Hjälp"
 
-#: git-gui.sh:2117
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "Webbdokumentation"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr "ödesdigert: kunde inte ta status på sökvägen %s: Fil eller katalog saknas"
+msgstr ""
+"ödesdigert: kunde inte ta status på sökvägen %s: Fil eller katalog saknas"
 
-#: git-gui.sh:2234
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "Aktuell gren:"
 
-#: git-gui.sh:2255
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
 msgstr "Köade ändringar (kommer att checkas in)"
 
-#: git-gui.sh:2274
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
 msgstr "Oköade ändringar"
 
-#: git-gui.sh:2323
+#: git-gui.sh:2362
 msgid "Stage Changed"
 msgstr "Köa ändrade"
 
-#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Sänd"
 
-#: git-gui.sh:2369
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
 msgstr "Inledande incheckningsmeddelande:"
 
-#: git-gui.sh:2370
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
 msgstr "Utökat incheckningsmeddelande:"
 
-#: git-gui.sh:2371
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
 msgstr "Utökat inledande incheckningsmeddelande:"
 
-#: git-gui.sh:2372
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
 msgstr "Utökat incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "Incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:2374
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "Incheckningsmeddelande:"
 
-#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Kopiera alla"
 
-#: git-gui.sh:2443 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
 msgstr "Fil:"
 
-#: git-gui.sh:2545
-msgid "Refresh"
-msgstr "Uppdatera"
-
-#: git-gui.sh:2566
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "Använd/återställ del"
 
-#: git-gui.sh:2572
-msgid "Decrease Font Size"
-msgstr "Minska teckensnittsstorlek"
-
-#: git-gui.sh:2576
-msgid "Increase Font Size"
-msgstr "Öka teckensnittsstorlek"
-
-#: git-gui.sh:2581
+#: git-gui.sh:2595
 msgid "Show Less Context"
 msgstr "Visa mindre sammanhang"
 
-#: git-gui.sh:2588
+#: git-gui.sh:2602
 msgid "Show More Context"
 msgstr "Visa mer sammanhang"
 
-#: git-gui.sh:2602
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "Uppdatera"
+
+#: git-gui.sh:2631
+msgid "Decrease Font Size"
+msgstr "Minska teckensnittsstorlek"
+
+#: git-gui.sh:2635
+msgid "Increase Font Size"
+msgstr "Öka teckensnittsstorlek"
+
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
 msgstr "Ta bort del ur incheckningskö"
 
-#: git-gui.sh:2604
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
 msgstr "Ställ del i incheckningskö"
 
-#: git-gui.sh:2623
+#: git-gui.sh:2667
 msgid "Initializing..."
 msgstr "Initierar..."
 
-#: git-gui.sh:2718
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -449,7 +449,7 @@
 "av %s:\n"
 "\n"
 
-#: git-gui.sh:2748
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -459,7 +459,7 @@
 "Detta beror på ett känt problem med\n"
 "Tcl-binären som följer med Cygwin."
 
-#: git-gui.sh:2753
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -476,7 +476,7 @@
 "user.name och user.email i din personliga\n"
 "~/.gitconfig-fil.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - ett grafiskt användargränssnitt för Git."
 
@@ -488,56 +488,56 @@
 msgid "Commit:"
 msgstr "Incheckning:"
 
-#: lib/blame.tcl:249
+#: lib/blame.tcl:264
 msgid "Copy Commit"
 msgstr "Kopiera incheckning"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Läser %s..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
 msgstr "Läser annoteringar för kopiering/flyttning..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
 msgstr "rader annoterade"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
 msgstr "Läser in annotering av originalplacering..."
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
 msgstr "Annotering fullbordad."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
 msgstr "Läser in annotering..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
 msgstr "Författare:"
 
-#: lib/blame.tcl:791
+#: lib/blame.tcl:806
 msgid "Committer:"
 msgstr "Incheckare:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
 msgstr "Ursprunglig fil:"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
 msgstr "Ursprungligen av:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
 msgstr "I filen:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
 msgstr "Kopierad eller flyttad hit av:"
 
@@ -550,17 +550,17 @@
 msgstr "Checka ut"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
 msgstr "Revision"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "Alternativ"
 
@@ -580,7 +580,7 @@
 msgid "Create New Branch"
 msgstr "Skapa ny gren"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
 msgstr "Skapa"
 
@@ -720,22 +720,22 @@
 msgid "File Browser"
 msgstr "Filbläddrare"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
 msgstr "Läser %s..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
 msgstr "[Upp till förälder]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
 msgstr "Bläddra filer på grenen"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:391
-#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492
-#: lib/choose_repository.tcl:989
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
 msgstr "Bläddra"
 
@@ -749,7 +749,7 @@
 msgid "fatal: Cannot resolve %s"
 msgstr "ödesdigert: Kunde inte slå upp %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
 msgstr "Stäng"
 
@@ -796,8 +796,8 @@
 msgstr ""
 "Det senaste inlästa tillståndet motsvarar inte tillståndet i arkivet.\n"
 "\n"
-"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du "
-"måste utföra en ny sökning innan den aktuella grenen kan ändras.\n"
+"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du måste "
+"utföra en ny sökning innan den aktuella grenen kan ändras.\n"
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
@@ -806,6 +806,10 @@
 msgid "Updating working directory to '%s'..."
 msgstr "Uppdaterar arbetskatalogen till \"%s\"..."
 
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
+msgstr "filer utcheckade"
+
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
@@ -829,10 +833,10 @@
 msgstr ""
 "Du är inte längre på en lokal gren.\n"
 "\n"
-"Om du ville vara på en gren skapar du en nu, baserad på \"Denna "
-"frånkopplade utcheckning\"."
+"Om du ville vara på en gren skapar du en nu, baserad på \"Denna frånkopplade "
+"utcheckning\"."
 
-#: lib/checkout_op.tcl:446
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "Checkade ut \"%s\"."
@@ -840,7 +844,8 @@
 #: lib/checkout_op.tcl:478
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr "Om du återställer \"%s\" till \"%s\" får följande incheckningar förlorade:"
+msgstr ""
+"Om du återställer \"%s\" till \"%s\" går följande incheckningar förlorade:"
 
 #: lib/checkout_op.tcl:500
 msgid "Recovering lost commits may not be easy."
@@ -851,7 +856,7 @@
 msgid "Reset '%s'?"
 msgstr "Återställa \"%s\"?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
 msgstr "Visualisera"
 
@@ -867,8 +872,8 @@
 msgstr ""
 "Kunde inte ställa in aktuell gren.\n"
 "\n"
-"Arbetskatalogen har bara växlats delvis. Vi uppdaterade filerna "
-"utan problem, men kunde inte uppdatera en intern fil i Git.\n"
+"Arbetskatalogen har bara växlats delvis. Vi uppdaterade filerna utan "
+"problem, men kunde inte uppdatera en intern fil i Git.\n"
 "\n"
 "Detta skulle inte ha hänt. %s kommer nu stängas och ge upp."
 
@@ -880,15 +885,15 @@
 msgid "Font Family"
 msgstr "Teckensnittsfamilj"
 
-#: lib/choose_font.tcl:73
+#: lib/choose_font.tcl:74
 msgid "Font Size"
 msgstr "Storlek"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
 msgstr "Exempel"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
@@ -896,225 +901,225 @@
 "Detta är en exempeltext.\n"
 "Om du tycker om den här texten kan den vara ditt teckensnitt."
 
-#: lib/choose_repository.tcl:27
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
 msgstr "Skapa nytt arkiv"
 
-#: lib/choose_repository.tcl:86
+#: lib/choose_repository.tcl:87
 msgid "New..."
 msgstr "Nytt..."
 
-#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
 msgstr "Klona befintligt arkiv"
 
-#: lib/choose_repository.tcl:99
+#: lib/choose_repository.tcl:100
 msgid "Clone..."
 msgstr "Klona..."
 
-#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
 msgstr "Öppna befintligt arkiv"
 
-#: lib/choose_repository.tcl:112
+#: lib/choose_repository.tcl:113
 msgid "Open..."
 msgstr "Öppna..."
 
-#: lib/choose_repository.tcl:125
+#: lib/choose_repository.tcl:126
 msgid "Recent Repositories"
 msgstr "Senaste arkiven"
 
-#: lib/choose_repository.tcl:131
+#: lib/choose_repository.tcl:132
 msgid "Open Recent Repository:"
 msgstr "Öppna tidigare arkiv:"
 
-#: lib/choose_repository.tcl:294
-#, tcl-format
-msgid "Location %s already exists."
-msgstr "Platsen %s finns redan."
-
-#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307
-#: lib/choose_repository.tcl:314
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Kunde inte skapa arkivet %s:"
 
-#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
 msgstr "Katalog:"
 
-#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544
-#: lib/choose_repository.tcl:1013
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
 msgstr "Gitarkiv"
 
-#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Katalogen %s finns redan."
 
-#: lib/choose_repository.tcl:442
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Filen %s finns redan."
 
-#: lib/choose_repository.tcl:463
+#: lib/choose_repository.tcl:455
 msgid "Clone"
 msgstr "Klona"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:468
 msgid "URL:"
 msgstr "Webbadress:"
 
-#: lib/choose_repository.tcl:496
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
 msgstr "Typ av klon:"
 
-#: lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (snabb, semiredundant, hårda länkar)"
 
-#: lib/choose_repository.tcl:508
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Full kopia (långsammare, redundant säkerhetskopia)"
 
-#: lib/choose_repository.tcl:514
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Delad (snabbast, rekommenderas ej, ingen säkerhetskopia)"
 
-#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
-#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808
-#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Inte ett Gitarkiv: %s"
 
-#: lib/choose_repository.tcl:586
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
 msgstr "Standard är endast tillgängligt för lokala arkiv."
 
-#: lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
 msgstr "Delat är endast tillgängligt för lokala arkiv."
 
-#: lib/choose_repository.tcl:617
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Platsen %s finns redan."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
 msgstr "Kunde inte konfigurera ursprung"
 
-#: lib/choose_repository.tcl:629
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
 msgstr "Räknar objekt"
 
-#: lib/choose_repository.tcl:630
+#: lib/choose_repository.tcl:628
 msgid "buckets"
 msgstr "hinkar"
 
-#: lib/choose_repository.tcl:654
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Kunde inte kopiera objekt/info/alternativ: %s"
 
-#: lib/choose_repository.tcl:690
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Ingenting att klona från %s."
 
-#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906
-#: lib/choose_repository.tcl:918
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
 msgstr "Grenen \"master\" har inte initierats."
 
-#: lib/choose_repository.tcl:705
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Hårda länkar är inte tillgängliga. Faller tillbaka på kopiering."
 
-#: lib/choose_repository.tcl:717
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Klonar från %s"
 
-#: lib/choose_repository.tcl:748
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
 msgstr "Kopierar objekt"
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:747
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:773
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Kunde inte kopiera objekt: %s"
 
-#: lib/choose_repository.tcl:783
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
 msgstr "Länkar objekt"
 
-#: lib/choose_repository.tcl:784
+#: lib/choose_repository.tcl:782
 msgid "objects"
 msgstr "objekt"
 
-#: lib/choose_repository.tcl:792
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Kunde inte hårdlänka objekt: %s"
 
-#: lib/choose_repository.tcl:847
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr "Kunde inte hämta grenar och objekt. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:858
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Kunde inte hämta taggar. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:882
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Kunde inte avgöra HEAD. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:891
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Kunde inte städa upp %s"
 
-#: lib/choose_repository.tcl:897
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
 msgstr "Kloning misslyckades."
 
-#: lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
 msgstr "Hämtade ingen standardgren."
 
-#: lib/choose_repository.tcl:915
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Kunde inte slå upp %s till någon incheckning."
 
-#: lib/choose_repository.tcl:927
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
 msgstr "Skapar arbetskatalog"
 
-#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
 #: lib/index.tcl:193
 msgid "files"
 msgstr "filer"
 
-#: lib/choose_repository.tcl:957
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
 msgstr "Inledande filutcheckning misslyckades."
 
-#: lib/choose_repository.tcl:973
+#: lib/choose_repository.tcl:971
 msgid "Open"
 msgstr "Öppna"
 
-#: lib/choose_repository.tcl:983
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
 msgstr "Arkiv:"
 
-#: lib/choose_repository.tcl:1033
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Kunde inte öppna arkivet %s:"
@@ -1135,7 +1140,7 @@
 msgid "Tracking Branch"
 msgstr "Spårande gren"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
 msgstr "Tagg"
 
@@ -1152,11 +1157,11 @@
 msgid "Revision expression is empty."
 msgstr "Revisionsuttrycket är tomt."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
 msgstr "Uppdaterad"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
 msgstr "Webbadress"
 
@@ -1182,9 +1187,9 @@
 msgstr ""
 "Kan inte utöka vid sammanslagning.\n"
 "\n"
-"Du är i mitten av en sammanslagning som inte är fullbordad. Du kan "
-"inte utöka tidigare incheckningar om du inte först avbryter den "
-"pågående sammanslagningen.\n"
+"Du är i mitten av en sammanslagning som inte är fullbordad. Du kan inte "
+"utöka tidigare incheckningar om du inte först avbryter den pågående "
+"sammanslagningen.\n"
 
 #: lib/commit.tcl:49
 msgid "Error loading commit data for amend:"
@@ -1209,8 +1214,8 @@
 msgstr ""
 "Det senaste inlästa tillståndet motsvarar inte tillståndet i arkivet.\n"
 "\n"
-"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du "
-"måste utföra en ny sökning innan du kan göra en ny incheckning.\n"
+"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du måste "
+"utföra en ny sökning innan du kan göra en ny incheckning.\n"
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
@@ -1266,16 +1271,45 @@
 "- Andra raden: Tom\n"
 "- Följande rader: Beskriv varför det här är en bra ändring.\n"
 
-#: lib/commit.tcl:257
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "varning: Tcl stöder inte teckenkodningen \"%s\"."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
+msgstr "Anropar krok före incheckning..."
+
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr "Incheckningen avvisades av krok före incheckning."
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr "Anropar krok för incheckningsmeddelande..."
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr "Incheckning avvisad av krok för incheckningsmeddelande."
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr "Checkar in ändringar..."
+
+#: lib/commit.tcl:303
 msgid "write-tree failed:"
 msgstr "write-tree misslyckades:"
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+msgid "Commit failed."
+msgstr "Incheckningen misslyckades."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Incheckningen %s verkar vara trasig"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1289,37 +1323,32 @@
 "\n"
 "En sökning kommer att startas automatiskt nu.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
 msgstr "Inga ändringar att checka in."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "varning: Tcl stöder inte teckenkodningen \"%s\"."
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
 msgstr "commit-tree misslyckades:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
 msgstr "update-ref misslyckades:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Skapade incheckningen %s: %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
 msgstr "Arbetar... vänta..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
 msgstr "Lyckades"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
 msgstr "Fel: Kommando misslyckades"
 
@@ -1398,8 +1427,8 @@
 "\n"
 "%s innehåller inga ändringar.\n"
 "\n"
-"Modifieringsdatum för filen uppdaterades av ett annat program, men innehållet "
-"i filen har inte ändrats.\n"
+"Modifieringsdatum för filen uppdaterades av ett annat program, men "
+"innehållet i filen har inte ändrats.\n"
 "\n"
 "En sökning kommer automatiskt att startas för att hitta andra filer som kan "
 "vara i samma tillstånd."
@@ -1430,23 +1459,23 @@
 msgid "Error loading diff:"
 msgstr "Fel vid inläsning av differens:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
 msgstr "Kunde inte ta bort den valda delen från kön."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
 msgstr "Kunde inte lägga till den valda delen till kön."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "fel"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
 msgstr "varning"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
 msgstr "Du måste rätta till felen ovan innan du checkar in."
 
@@ -1479,6 +1508,10 @@
 msgid "Unstaging %s from commit"
 msgstr "Tar bort %s för incheckningskön"
 
+#: lib/index.tcl:313
+msgid "Ready to commit."
+msgstr "Redo att checka in."
+
 #: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
@@ -1494,11 +1527,12 @@
 msgid "Revert changes in these %i files?"
 msgstr "Återställ ändringarna i dessa %i filer?"
 
-#: lib/index.tcl:389
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr "Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
+msgstr ""
+"Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
 
-#: lib/index.tcl:392
+#: lib/index.tcl:394
 msgid "Do Nothing"
 msgstr "Gör ingenting"
 
@@ -1510,7 +1544,7 @@
 msgstr ""
 "Kan inte slå ihop vid utökning.\n"
 "\n"
-"Du måste föra färdig utökningen av incheckningen innan du påbörjar någon "
+"Du måste göra färdig utökningen av incheckningen innan du påbörjar någon "
 "slags sammanslagning.\n"
 
 #: lib/merge.tcl:27
@@ -1524,8 +1558,8 @@
 msgstr ""
 "Det senaste inlästa tillståndet motsvarar inte tillståndet i arkivet.\n"
 "\n"
-"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du "
-"måste utföra en ny sökning innan du kan utföra en sammanslagning.\n"
+"Ett annat Git-program har ändrat arkivet sedan senaste avsökningen. Du måste "
+"utföra en ny sökning innan du kan utföra en sammanslagning.\n"
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
@@ -1571,27 +1605,27 @@
 
 #: lib/merge.tcl:119
 #, tcl-format
-msgid "Merging %s and %s"
-msgstr "Slår ihop %s och %s"
+msgid "Merging %s and %s..."
+msgstr "Slår ihop %s och %s..."
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
 msgstr "Sammanslagningen avslutades framgångsrikt."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Sammanslagningen misslyckades. Du måste lösa konflikterna."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Slå ihop i %s"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
 msgstr "Revisioner att slå ihop"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1601,7 +1635,7 @@
 "\n"
 "Du måste göra dig färdig med att utöka incheckningen.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1611,12 +1645,12 @@
 msgstr ""
 "Avbryt sammanslagning?\n"
 "\n"
-"Om du avbryter sammanslagningen kommer *ALLA* ej incheckade ändringar att "
-"gå förlorade.\n"
+"Om du avbryter sammanslagningen kommer *ALLA* ej incheckade ändringar att gå "
+"förlorade.\n"
 "\n"
 "Gå vidare med att avbryta den aktuella sammanslagningen?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1626,98 +1660,110 @@
 msgstr ""
 "Återställ ändringar?\n"
 "\n"
-"Om du återställer ändringarna kommer *ALLA* ej incheckade ändringar att "
-"gå förlorade.\n"
+"Om du återställer ändringarna kommer *ALLA* ej incheckade ändringar att gå "
+"förlorade.\n"
 "\n"
 "Gå vidare med att återställa de aktuella ändringarna?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
 msgstr "Avbryter"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:238
+msgid "files reset"
+msgstr "filer återställda"
+
+#: lib/merge.tcl:265
 msgid "Abort failed."
 msgstr "Misslyckades avbryta."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:267
 msgid "Abort completed.  Ready."
 msgstr "Avbrytning fullbordad. Redo."
 
-#: lib/option.tcl:82
+#: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Återställ standardvärden"
 
-#: lib/option.tcl:86
+#: lib/option.tcl:99
 msgid "Save"
 msgstr "Spara"
 
-#: lib/option.tcl:96
+#: lib/option.tcl:109
 #, tcl-format
 msgid "%s Repository"
 msgstr "Arkivet %s"
 
-#: lib/option.tcl:97
+#: lib/option.tcl:110
 msgid "Global (All Repositories)"
 msgstr "Globalt (alla arkiv)"
 
-#: lib/option.tcl:103
+#: lib/option.tcl:116
 msgid "User Name"
 msgstr "Användarnamn"
 
-#: lib/option.tcl:104
+#: lib/option.tcl:117
 msgid "Email Address"
 msgstr "E-postadress"
 
-#: lib/option.tcl:106
+#: lib/option.tcl:119
 msgid "Summarize Merge Commits"
 msgstr "Summera sammanslagningsincheckningar"
 
-#: lib/option.tcl:107
+#: lib/option.tcl:120
 msgid "Merge Verbosity"
 msgstr "Pratsamhet för sammanslagningar"
 
-#: lib/option.tcl:108
+#: lib/option.tcl:121
 msgid "Show Diffstat After Merge"
 msgstr "Visa diffstatistik efter sammanslagning"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
 msgstr "Lita på filändringstidsstämplar"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Städa spårade grenar vid hämtning"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
 msgstr "Matcha spårade grenar"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
 msgstr "Antal rader sammanhang i differenser"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+msgid "Commit Message Text Width"
+msgstr "Textbredd för incheckningsmeddelande"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
 msgstr "Mall för namn på nya grenar"
 
-#: lib/option.tcl:176
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
+msgstr "Stavningsordlista:"
+
+#: lib/option.tcl:216
 msgid "Change Font"
 msgstr "Byt teckensnitt"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
 msgstr "Välj %s"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
 msgstr "p."
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
 msgstr "Misslyckades med att helt spara alternativ:"
 
@@ -1774,8 +1820,8 @@
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
 msgstr ""
-"En eller flera av sammanslagningstesterna misslyckades eftersom du inte "
-"har hämtat de nödvändiga incheckningarna. Försök hämta från %s först."
+"En eller flera av sammanslagningstesterna misslyckades eftersom du inte har "
+"hämtat de nödvändiga incheckningarna. Försök hämta från %s först."
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
@@ -1787,7 +1833,7 @@
 "\n"
 "Delete the selected branches?"
 msgstr ""
-"Det kan vara svårt att återställa grenar.\n"
+"Det kan vara svårt att återställa borttagna grenar.\n"
 "\n"
 "Ta bort de valda grenarna?"
 
@@ -1825,6 +1871,43 @@
 msgid "Cannot write icon:"
 msgstr "Kan inte skriva ikon:"
 
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Stavningskontrollprogrammet stöds inte"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Stavningskontroll är ej tillgänglig"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Ogiltig inställning för stavningskontroll"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Återställer ordlistan till %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Stavningskontroll misslyckades tyst vid start"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Stavningskontrollprogrammet känns inte igen"
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr "Inga förslag"
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr "Oväntat filslut från stavningskontroll"
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
+msgstr "Stavningskontroll misslyckades"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
@@ -1893,3 +1976,5 @@
 msgid "Include tags"
 msgstr "Ta med taggar"
 
+#~ msgid "Not connected to aspell"
+#~ msgstr "Inte ansluten till aspell"
diff --git a/git-gui/po/zh_cn.po b/git-gui/po/zh_cn.po
index 621c947..d2c6866 100644
--- a/git-gui/po/zh_cn.po
+++ b/git-gui/po/zh_cn.po
@@ -3,14 +3,31 @@
 # This file is distributed under the same license as the git-gui package.
 # Xudong Guan <xudong.guan@gmail.com>, 2007.
 #
+# Please use the following translation throughout the file for consistence:
+#
+# 	repository	版本库
+# 	commit		提交
+# 	revision	版本
+# 	branch		分支
+# 	tag		标签
+# 	annotation	标注
+# 	merge		合并
+# 	fast forward	快速合并(??)
+# 	stage		缓存 (译自 index/cache)
+# 	amend		修正
+# 	reset		复位
+#
+# 2008-01-06 Eric Miao <eric.y.miao@gmail.com>
+# FIXME: checkout 的标准翻译
+#
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-10-10 04:04-0400\n"
+"POT-Creation-Date: 2008-03-14 07:18+0100\n"
 "PO-Revision-Date: 2007-07-21 01:23-0700\n"
-"Last-Translator: Xudong Guan <xudong.guan@gmail.com>\n"
+"Last-Translator: Eric Miao <eric.y.miao@gmail.com>\n"
 "Language-Team: Chinese\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -19,28 +36,28 @@
 #: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
 #: git-gui.sh:763
 msgid "git-gui: fatal error"
-msgstr ""
+msgstr "git-gui: 致命错误"
 
-#: git-gui.sh:595
+#: git-gui.sh:593
 #, tcl-format
 msgid "Invalid font specified in %s:"
-msgstr ""
+msgstr "%s 中指定的字体无效:"
 
 #: git-gui.sh:620
 msgid "Main Font"
-msgstr ""
+msgstr "主要字体"
 
 #: git-gui.sh:621
 msgid "Diff/Console Font"
-msgstr ""
+msgstr "Diff/控制终端字体"
 
 #: git-gui.sh:635
 msgid "Cannot find git in PATH."
-msgstr ""
+msgstr "PATH 中没有找到 git"
 
 #: git-gui.sh:662
 msgid "Cannot parse Git version string:"
-msgstr ""
+msgstr "无法解析 Git 的版本信息:"
 
 #: git-gui.sh:680
 #, tcl-format
@@ -53,388 +70,386 @@
 "\n"
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
+"无法确定 Git 的版本.\n"
+"\n"
+"%s 声明其版本为 '%s'.\n"
+"\n"
+"而 %s 需要 1.5.0 或这以后的 Git 版本.\n"
+"\n"
+"是否假定 '%s' 为版本 1.5.0?\n"
 
-#: git-gui.sh:853
+#: git-gui.sh:918
 msgid "Git directory not found:"
-msgstr ""
+msgstr "Git 目录无法找到:"
 
-#: git-gui.sh:860
+#: git-gui.sh:925
 msgid "Cannot move to top of working directory:"
-msgstr ""
+msgstr "无法移动到工作根目录:"
 
-#: git-gui.sh:867
+#: git-gui.sh:932
 msgid "Cannot use funny .git directory:"
-msgstr ""
+msgstr "无法使用 .git 目录:"
 
-#: git-gui.sh:872
+#: git-gui.sh:937
 msgid "No working directory"
-msgstr ""
+msgstr "没有工作目录"
 
-#: git-gui.sh:1019
+#: git-gui.sh:1084 lib/checkout_op.tcl:283
 msgid "Refreshing file status..."
-msgstr ""
+msgstr "更新文件状态..."
 
-#: git-gui.sh:1084
+#: git-gui.sh:1149
 msgid "Scanning for modified files ..."
-msgstr ""
+msgstr "扫描修改过的文件 ..."
 
-#: git-gui.sh:1259 lib/browser.tcl:245
-#, fuzzy
+#: git-gui.sh:1324 lib/browser.tcl:246
 msgid "Ready."
-msgstr "重做"
+msgstr "就绪"
 
-#: git-gui.sh:1525
+#: git-gui.sh:1590
 msgid "Unmodified"
-msgstr ""
+msgstr "未修改"
 
-#: git-gui.sh:1527
+#: git-gui.sh:1592
 msgid "Modified, not staged"
-msgstr ""
+msgstr "修改但未缓存"
 
-#: git-gui.sh:1528 git-gui.sh:1533
-#, fuzzy
+#: git-gui.sh:1593 git-gui.sh:1598
 msgid "Staged for commit"
-msgstr "从本次提交移除"
+msgstr "缓存为提交"
 
-#: git-gui.sh:1529 git-gui.sh:1534
-#, fuzzy
+#: git-gui.sh:1594 git-gui.sh:1599
 msgid "Portions staged for commit"
-msgstr "从本次提交移除"
+msgstr "部分缓存为提交"
 
-#: git-gui.sh:1530 git-gui.sh:1535
+#: git-gui.sh:1595 git-gui.sh:1600
 msgid "Staged for commit, missing"
-msgstr ""
+msgstr "缓存为提交, 不存在"
 
-#: git-gui.sh:1532
+#: git-gui.sh:1597
 msgid "Untracked, not staged"
-msgstr ""
+msgstr "未跟踪, 未缓存"
 
-#: git-gui.sh:1537
+#: git-gui.sh:1602
 msgid "Missing"
-msgstr ""
+msgstr "不存在"
 
-#: git-gui.sh:1538
+#: git-gui.sh:1603
 msgid "Staged for removal"
-msgstr ""
+msgstr "缓存为删除"
 
-#: git-gui.sh:1539
+#: git-gui.sh:1604
 msgid "Staged for removal, still present"
-msgstr ""
+msgstr "缓存为删除, 但仍存在"
 
-#: git-gui.sh:1541 git-gui.sh:1542 git-gui.sh:1543 git-gui.sh:1544
+#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
 msgid "Requires merge resolution"
-msgstr ""
+msgstr "需要解决合并冲突"
 
-#: git-gui.sh:1579
+#: git-gui.sh:1644
 msgid "Starting gitk... please wait..."
-msgstr ""
+msgstr "启动 gitk... 请等待..."
 
-#: git-gui.sh:1588
+#: git-gui.sh:1653
 #, tcl-format
 msgid ""
 "Unable to start gitk:\n"
 "\n"
 "%s does not exist"
 msgstr ""
+"无法启动 gitk:\n"
+"\n"
+"%s 不存在"
 
-#: git-gui.sh:1788 lib/choose_repository.tcl:32
+#: git-gui.sh:1860 lib/choose_repository.tcl:36
 msgid "Repository"
-msgstr "版本树"
+msgstr "版本库(repository)"
 
-#: git-gui.sh:1789
+#: git-gui.sh:1861
 msgid "Edit"
 msgstr "编辑"
 
-#: git-gui.sh:1791 lib/choose_rev.tcl:560
+#: git-gui.sh:1863 lib/choose_rev.tcl:561
 msgid "Branch"
-msgstr "分支"
+msgstr "分支(branch)"
 
-#: git-gui.sh:1794 lib/choose_rev.tcl:547
-#, fuzzy
+#: git-gui.sh:1866 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
-msgstr "提交"
+msgstr "提交(commit)"
 
-#: git-gui.sh:1797 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
 msgid "Merge"
-msgstr "合并"
+msgstr "合并(merge)"
 
-#: git-gui.sh:1798 lib/choose_rev.tcl:556
-#, fuzzy
+#: git-gui.sh:1870 lib/choose_rev.tcl:557
 msgid "Remote"
-msgstr "改名..."
+msgstr "远端(remote)"
 
-#: git-gui.sh:1807
+#: git-gui.sh:1879
 msgid "Browse Current Branch's Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览当前分支上的文件"
 
-#: git-gui.sh:1811
-#, fuzzy
+#: git-gui.sh:1883
 msgid "Browse Branch Files..."
-msgstr "浏览当前分支文件"
+msgstr "浏览分支上的文件..."
 
-#: git-gui.sh:1816
+#: git-gui.sh:1888
 msgid "Visualize Current Branch's History"
-msgstr "调用gitk显示当前分支"
+msgstr "图示当前分支的历史"
 
-#: git-gui.sh:1820
+#: git-gui.sh:1892
 msgid "Visualize All Branch History"
-msgstr "调用gitk显示所有分支"
+msgstr "图示所有分支的历史"
 
-#: git-gui.sh:1827
-#, fuzzy, tcl-format
+#: git-gui.sh:1899
+#, tcl-format
 msgid "Browse %s's Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览 %s 上的文件"
 
-#: git-gui.sh:1829
-#, fuzzy, tcl-format
+#: git-gui.sh:1901
+#, tcl-format
 msgid "Visualize %s's History"
-msgstr "调用gitk显示所有分支"
+msgstr "图示 %s 分支的历史"
 
-#: git-gui.sh:1834 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
-msgstr "数据库统计数据"
+msgstr "数据库统计信息"
 
-#: git-gui.sh:1837 lib/database.tcl:34
+#: git-gui.sh:1909 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "压缩数据库"
 
-#: git-gui.sh:1840
+#: git-gui.sh:1912
 msgid "Verify Database"
 msgstr "验证数据库"
 
-#: git-gui.sh:1847 git-gui.sh:1851 git-gui.sh:1855 lib/shortcut.tcl:9
-#: lib/shortcut.tcl:45 lib/shortcut.tcl:84
+#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
+#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "创建桌面图标"
 
-#: git-gui.sh:1860 lib/choose_repository.tcl:36 lib/choose_repository.tcl:95
+#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
 msgid "Quit"
 msgstr "退出"
 
-#: git-gui.sh:1867
+#: git-gui.sh:1939
 msgid "Undo"
 msgstr "撤销"
 
-#: git-gui.sh:1870
+#: git-gui.sh:1942
 msgid "Redo"
 msgstr "重做"
 
-#: git-gui.sh:1874 git-gui.sh:2366
+#: git-gui.sh:1946 git-gui.sh:2443
 msgid "Cut"
 msgstr "剪切"
 
-#: git-gui.sh:1877 git-gui.sh:2369 git-gui.sh:2440 git-gui.sh:2512
-#: lib/console.tcl:67
+#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: lib/console.tcl:69
 msgid "Copy"
 msgstr "复制"
 
-#: git-gui.sh:1880 git-gui.sh:2372
+#: git-gui.sh:1952 git-gui.sh:2449
 msgid "Paste"
 msgstr "粘贴"
 
-#: git-gui.sh:1883 git-gui.sh:2375 lib/branch_delete.tcl:26
+#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "删除"
 
-#: git-gui.sh:1887 git-gui.sh:2379 git-gui.sh:2516 lib/console.tcl:69
+#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
 msgid "Select All"
 msgstr "全选"
 
-#: git-gui.sh:1896
+#: git-gui.sh:1968
 msgid "Create..."
 msgstr "新建..."
 
-#: git-gui.sh:1902
+#: git-gui.sh:1974
 msgid "Checkout..."
-msgstr "切换..."
+msgstr "Checkout..."
 
-#: git-gui.sh:1908
+#: git-gui.sh:1980
 msgid "Rename..."
-msgstr "改名..."
+msgstr "更名..."
 
-#: git-gui.sh:1913 git-gui.sh:2012
+#: git-gui.sh:1985 git-gui.sh:2085
 msgid "Delete..."
 msgstr "删除..."
 
-#: git-gui.sh:1918
+#: git-gui.sh:1990
 msgid "Reset..."
-msgstr "重置所有修动..."
+msgstr "复位(Reset)..."
 
-#: git-gui.sh:1930 git-gui.sh:2313
+#: git-gui.sh:2002 git-gui.sh:2389
 msgid "New Commit"
-msgstr "新提交"
+msgstr "新建提交"
 
-#: git-gui.sh:1938 git-gui.sh:2320
+#: git-gui.sh:2010 git-gui.sh:2396
 msgid "Amend Last Commit"
-msgstr "修订上次提交"
+msgstr "修正上次提交"
 
-#: git-gui.sh:1947 git-gui.sh:2280 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "重新扫描"
 
-#: git-gui.sh:1953
-#, fuzzy
+#: git-gui.sh:2025
 msgid "Stage To Commit"
-msgstr "从本次提交移除"
+msgstr "缓存为提交"
 
-#: git-gui.sh:1958
-#, fuzzy
+#: git-gui.sh:2031
 msgid "Stage Changed Files To Commit"
-msgstr "将被提交的修改"
+msgstr "缓存修改的文件为提交"
 
-#: git-gui.sh:1964
+#: git-gui.sh:2037
 msgid "Unstage From Commit"
-msgstr "从本次提交移除"
+msgstr "从本次提交撤除"
 
-#: git-gui.sh:1969 lib/index.tcl:352
+#: git-gui.sh:2042 lib/index.tcl:395
 msgid "Revert Changes"
-msgstr "恢复修改"
+msgstr "撤销修改"
 
-#: git-gui.sh:1976 git-gui.sh:2292 git-gui.sh:2390
+#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
 msgid "Sign Off"
-msgstr "签名"
+msgstr "签名(Sign Off)"
 
-#: git-gui.sh:1980 git-gui.sh:2296
-#, fuzzy
+#: git-gui.sh:2053 git-gui.sh:2372
 msgid "Commit@@verb"
 msgstr "提交"
 
-#: git-gui.sh:1991
+#: git-gui.sh:2064
 msgid "Local Merge..."
 msgstr "本地合并..."
 
-#: git-gui.sh:1996
+#: git-gui.sh:2069
 msgid "Abort Merge..."
-msgstr "取消合并..."
+msgstr "中止合并..."
 
-#: git-gui.sh:2008
+#: git-gui.sh:2081
 msgid "Push..."
 msgstr "上传..."
 
-#: git-gui.sh:2019 lib/choose_repository.tcl:41
+#: git-gui.sh:2092 lib/choose_repository.tcl:41
 msgid "Apple"
 msgstr "苹果"
 
-#: git-gui.sh:2022 git-gui.sh:2044 lib/about.tcl:13
+#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
-msgstr "关于%s"
+msgstr "关于 %s"
 
-#: git-gui.sh:2026
+#: git-gui.sh:2099
 msgid "Preferences..."
-msgstr ""
+msgstr "首选项..."
 
-#: git-gui.sh:2034 git-gui.sh:2558
+#: git-gui.sh:2107 git-gui.sh:2639
 msgid "Options..."
 msgstr "选项..."
 
-#: git-gui.sh:2040 lib/choose_repository.tcl:47
+#: git-gui.sh:2113 lib/choose_repository.tcl:47
 msgid "Help"
 msgstr "帮助"
 
-#: git-gui.sh:2081
+#: git-gui.sh:2154
 msgid "Online Documentation"
 msgstr "在线文档"
 
-#: git-gui.sh:2165
+#: git-gui.sh:2238
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
-msgstr ""
+msgstr "致命错误: 无法获取路径 %s 的信息: 该文件或目录不存在"
 
-#: git-gui.sh:2198
+#: git-gui.sh:2271
 msgid "Current Branch:"
 msgstr "当前分支:"
 
-#: git-gui.sh:2219
-#, fuzzy
+#: git-gui.sh:2292
 msgid "Staged Changes (Will Commit)"
-msgstr "将被提交的修改"
+msgstr "已缓存的改动 (将被提交)"
 
-#: git-gui.sh:2239
+#: git-gui.sh:2312
 msgid "Unstaged Changes"
-msgstr ""
+msgstr "未缓存的改动"
 
-#: git-gui.sh:2286
+#: git-gui.sh:2362
 msgid "Stage Changed"
-msgstr ""
+msgstr "缓存改动"
 
-#: git-gui.sh:2302 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "上传"
 
-#: git-gui.sh:2332
+#: git-gui.sh:2408
 msgid "Initial Commit Message:"
-msgstr "初始提交描述:"
+msgstr "初始的提交描述:"
 
-#: git-gui.sh:2333
+#: git-gui.sh:2409
 msgid "Amended Commit Message:"
-msgstr "修订提交描述:"
+msgstr "修正的提交描述:"
 
-#: git-gui.sh:2334
+#: git-gui.sh:2410
 msgid "Amended Initial Commit Message:"
-msgstr "修订初始提交描述:"
+msgstr "修正的初始提交描述:"
 
-#: git-gui.sh:2335
+#: git-gui.sh:2411
 msgid "Amended Merge Commit Message:"
-msgstr "修订合并提交描述:"
+msgstr "修正的合并提交描述:"
 
-#: git-gui.sh:2336
+#: git-gui.sh:2412
 msgid "Merge Commit Message:"
 msgstr "合并提交描述:"
 
-#: git-gui.sh:2337
+#: git-gui.sh:2413
 msgid "Commit Message:"
 msgstr "提交描述:"
 
-#: git-gui.sh:2382 git-gui.sh:2520 lib/console.tcl:71
+#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
 msgid "Copy All"
 msgstr "全部复制"
 
-#: git-gui.sh:2406 lib/blame.tcl:104
+#: git-gui.sh:2483 lib/blame.tcl:107
 msgid "File:"
-msgstr ""
+msgstr "文件:"
 
-#: git-gui.sh:2508
-msgid "Refresh"
-msgstr "刷新"
-
-#: git-gui.sh:2529
+#: git-gui.sh:2589
 msgid "Apply/Reverse Hunk"
 msgstr "应用/撤消此修改块"
 
-#: git-gui.sh:2535
+#: git-gui.sh:2595
+msgid "Show Less Context"
+msgstr "显示更少上下文"
+
+#: git-gui.sh:2602
+msgid "Show More Context"
+msgstr "显示更多上下文"
+
+#: git-gui.sh:2610
+msgid "Refresh"
+msgstr "刷新"
+
+#: git-gui.sh:2631
 msgid "Decrease Font Size"
 msgstr "缩小字体"
 
-#: git-gui.sh:2539
+#: git-gui.sh:2635
 msgid "Increase Font Size"
 msgstr "放大字体"
 
-#: git-gui.sh:2544
-msgid "Show Less Context"
-msgstr "显示更多diff上下文"
-
-#: git-gui.sh:2551
-msgid "Show More Context"
-msgstr "显示更少diff上下文"
-
-#: git-gui.sh:2565
-#, fuzzy
+#: git-gui.sh:2646
 msgid "Unstage Hunk From Commit"
-msgstr "从本次提交移除"
+msgstr "从提交中撤除修改块"
 
-#: git-gui.sh:2567
-#, fuzzy
+#: git-gui.sh:2648
 msgid "Stage Hunk For Commit"
-msgstr "从本次提交移除"
+msgstr "缓存修改块为提交"
 
-#: git-gui.sh:2586
+#: git-gui.sh:2667
 msgid "Initializing..."
-msgstr ""
+msgstr "初始化..."
 
-#: git-gui.sh:2677
+#: git-gui.sh:2762
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -444,15 +459,22 @@
 "by %s:\n"
 "\n"
 msgstr ""
+"可能存在环境变量的问题.\n"
+"\n"
+"由 %s 执行的 Git 子进程可能忽略下列环境变量:\n"
+"\n"
 
-#: git-gui.sh:2707
+#: git-gui.sh:2792
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
 "Tcl binary distributed by Cygwin."
 msgstr ""
+"\n"
+"这是由 Cygwin 发布的 Tcl 代码中一个\n"
+"已知问题所引起."
 
-#: git-gui.sh:2712
+#: git-gui.sh:2797
 #, tcl-format
 msgid ""
 "\n"
@@ -462,206 +484,197 @@
 "user.email settings into your personal\n"
 "~/.gitconfig file.\n"
 msgstr ""
+"\n"
+"\n"
+"%s 的一个很好的替代方案是将 user.name 以及\n"
+"user.email 设置放在你的个人 ~/.gitconfig 文件中.\n"
 
-#: lib/about.tcl:25
+#: lib/about.tcl:26
 msgid "git-gui - a graphical user interface for Git."
-msgstr ""
+msgstr "git-gui - Git 的图形化用户界面"
 
 #: lib/blame.tcl:77
 msgid "File Viewer"
-msgstr ""
+msgstr "文件查看器"
 
 #: lib/blame.tcl:81
-#, fuzzy
 msgid "Commit:"
-msgstr "提交"
+msgstr "提交:"
 
-#: lib/blame.tcl:249
-#, fuzzy
+#: lib/blame.tcl:264
 msgid "Copy Commit"
-msgstr "提交"
+msgstr "复制提交"
 
-#: lib/blame.tcl:369
+#: lib/blame.tcl:384
 #, tcl-format
 msgid "Reading %s..."
-msgstr ""
+msgstr "读取 %s..."
 
-#: lib/blame.tcl:473
+#: lib/blame.tcl:488
 msgid "Loading copy/move tracking annotations..."
-msgstr ""
+msgstr "装载复制/移动跟踪标注..."
 
-#: lib/blame.tcl:493
+#: lib/blame.tcl:508
 msgid "lines annotated"
-msgstr ""
+msgstr "标注行"
 
-#: lib/blame.tcl:674
+#: lib/blame.tcl:689
 msgid "Loading original location annotations..."
-msgstr ""
+msgstr "装载原始位置标注..."
 
-#: lib/blame.tcl:677
+#: lib/blame.tcl:692
 msgid "Annotation complete."
-msgstr ""
+msgstr "标注完成."
 
-#: lib/blame.tcl:731
+#: lib/blame.tcl:746
 msgid "Loading annotation..."
-msgstr ""
+msgstr "裝載标注..."
 
-#: lib/blame.tcl:787
+#: lib/blame.tcl:802
 msgid "Author:"
-msgstr ""
+msgstr "作者:"
 
-#: lib/blame.tcl:791
-#, fuzzy
+#: lib/blame.tcl:806
 msgid "Committer:"
-msgstr "提交"
+msgstr "提交者:"
 
-#: lib/blame.tcl:796
+#: lib/blame.tcl:811
 msgid "Original File:"
-msgstr ""
+msgstr "原始文件:"
 
-#: lib/blame.tcl:910
+#: lib/blame.tcl:925
 msgid "Originally By:"
-msgstr ""
+msgstr "最初由:"
 
-#: lib/blame.tcl:916
+#: lib/blame.tcl:931
 msgid "In File:"
-msgstr ""
+msgstr "在文件:"
 
-#: lib/blame.tcl:921
+#: lib/blame.tcl:936
 msgid "Copied Or Moved Here By:"
-msgstr ""
+msgstr "由复制或移动至此:"
 
 #: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
-#, fuzzy
 msgid "Checkout Branch"
-msgstr "当前分支:"
+msgstr "Checkout 分支"
 
 #: lib/branch_checkout.tcl:23
-#, fuzzy
 msgid "Checkout"
-msgstr "切换..."
+msgstr "Checkout"
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
-#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172
-#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
+#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
 msgid "Cancel"
-msgstr ""
+msgstr "取消"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:286
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
 msgid "Revision"
-msgstr ""
+msgstr "版本"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202
-#, fuzzy
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
 msgid "Options"
 msgstr "选项..."
 
 #: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
 msgid "Fetch Tracking Branch"
-msgstr ""
+msgstr "获取跟踪分支"
 
 #: lib/branch_checkout.tcl:44
 msgid "Detach From Local Branch"
-msgstr ""
+msgstr "从本地分支脱离"
 
 #: lib/branch_create.tcl:22
-#, fuzzy
 msgid "Create Branch"
-msgstr "当前分支:"
+msgstr "创建分支"
 
 #: lib/branch_create.tcl:27
-#, fuzzy
 msgid "Create New Branch"
-msgstr "当前分支:"
+msgstr "新建分支"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:199
-#, fuzzy
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
 msgid "Create"
-msgstr "新建..."
+msgstr "新建"
 
 #: lib/branch_create.tcl:40
-#, fuzzy
 msgid "Branch Name"
-msgstr "分支"
+msgstr "分支名"
 
 #: lib/branch_create.tcl:43
 msgid "Name:"
-msgstr ""
+msgstr "名字:"
 
 #: lib/branch_create.tcl:58
 msgid "Match Tracking Branch Name"
-msgstr ""
+msgstr "匹配跟踪分支名字"
 
 #: lib/branch_create.tcl:66
 msgid "Starting Revision"
-msgstr ""
+msgstr "起始版本"
 
 #: lib/branch_create.tcl:72
 msgid "Update Existing Branch:"
-msgstr ""
+msgstr "更新已有分支:"
 
 #: lib/branch_create.tcl:75
 msgid "No"
-msgstr ""
+msgstr "号码"
 
 #: lib/branch_create.tcl:80
 msgid "Fast Forward Only"
-msgstr ""
+msgstr "仅快速合并"
 
 #: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
-#, fuzzy
 msgid "Reset"
-msgstr "重置所有修动..."
+msgstr "复位"
 
 #: lib/branch_create.tcl:97
 msgid "Checkout After Creation"
-msgstr ""
+msgstr "在创建后Checkout"
 
 #: lib/branch_create.tcl:131
 msgid "Please select a tracking branch."
-msgstr ""
+msgstr "请选择某个跟踪分支."
 
 #: lib/branch_create.tcl:140
 #, tcl-format
 msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr ""
+msgstr "跟踪分支 %s 并不是远端版本库中的一个分支"
 
 #: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
 msgid "Please supply a branch name."
-msgstr ""
+msgstr "请提供分支名字."
 
 #: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
 #, tcl-format
 msgid "'%s' is not an acceptable branch name."
-msgstr ""
+msgstr "'%s'不是一个可接受的分支名."
 
 #: lib/branch_delete.tcl:15
-#, fuzzy
 msgid "Delete Branch"
-msgstr "当前分支:"
+msgstr "删除分支"
 
 #: lib/branch_delete.tcl:20
 msgid "Delete Local Branch"
-msgstr ""
+msgstr "删除本地分支"
 
 #: lib/branch_delete.tcl:37
-#, fuzzy
 msgid "Local Branches"
-msgstr "分支"
+msgstr "本地分支"
 
 #: lib/branch_delete.tcl:52
 msgid "Delete Only If Merged Into"
-msgstr ""
+msgstr "仅在合并后删除"
 
 #: lib/branch_delete.tcl:54
 msgid "Always (Do not perform merge test.)"
-msgstr ""
+msgstr "总是合并 (不作合并测试.)"
 
 #: lib/branch_delete.tcl:103
 #, tcl-format
 msgid "The following branches are not completely merged into %s:"
-msgstr ""
+msgstr "下列分支没有完全被合并到 %s:"
 
 #: lib/branch_delete.tcl:115
 msgid ""
@@ -669,6 +682,9 @@
 "\n"
 " Delete the selected branches?"
 msgstr ""
+"恢复被删除的分支非常困难.\n"
+"\n"
+"是否要删除所选分支?"
 
 #: lib/branch_delete.tcl:141
 #, tcl-format
@@ -676,86 +692,84 @@
 "Failed to delete branches:\n"
 "%s"
 msgstr ""
+"无法删除分支:\n"
+"%s"
 
 #: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
-#, fuzzy
 msgid "Rename Branch"
-msgstr "当前分支:"
+msgstr "更改分支名:"
 
 #: lib/branch_rename.tcl:26
-#, fuzzy
 msgid "Rename"
-msgstr "改名..."
+msgstr "更名..."
 
 #: lib/branch_rename.tcl:36
-#, fuzzy
 msgid "Branch:"
-msgstr "分支"
+msgstr "分支:"
 
 #: lib/branch_rename.tcl:39
 msgid "New Name:"
-msgstr ""
+msgstr "新名字:"
 
 #: lib/branch_rename.tcl:75
 msgid "Please select a branch to rename."
-msgstr ""
+msgstr "请选择分支更名."
 
 #: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
 #, tcl-format
 msgid "Branch '%s' already exists."
-msgstr ""
+msgstr "分支 '%s' 已经存在."
 
 #: lib/branch_rename.tcl:117
 #, tcl-format
 msgid "Failed to rename '%s'."
-msgstr ""
+msgstr "无法更名 '%s'."
 
 #: lib/browser.tcl:17
 msgid "Starting..."
-msgstr ""
+msgstr "开始..."
 
 #: lib/browser.tcl:26
 msgid "File Browser"
-msgstr ""
+msgstr "文件浏览器"
 
-#: lib/browser.tcl:125 lib/browser.tcl:142
+#: lib/browser.tcl:126 lib/browser.tcl:143
 #, tcl-format
 msgid "Loading %s..."
-msgstr ""
+msgstr "装载 %s..."
 
-#: lib/browser.tcl:186
+#: lib/browser.tcl:187
 msgid "[Up To Parent]"
-msgstr ""
+msgstr "[上层目录]"
 
-#: lib/browser.tcl:266 lib/browser.tcl:272
-#, fuzzy
+#: lib/browser.tcl:267 lib/browser.tcl:273
 msgid "Browse Branch Files"
-msgstr "浏览当前分支文件"
+msgstr "浏览分支文件"
 
-#: lib/browser.tcl:277 lib/choose_repository.tcl:215
-#: lib/choose_repository.tcl:305 lib/choose_repository.tcl:315
-#: lib/choose_repository.tcl:811
+#: lib/browser.tcl:278 lib/choose_repository.tcl:387
+#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
+#: lib/choose_repository.tcl:987
 msgid "Browse"
-msgstr ""
+msgstr "浏览"
 
 #: lib/checkout_op.tcl:79
 #, tcl-format
 msgid "Fetching %s from %s"
-msgstr ""
+msgstr "获取 %s 自 %s"
 
 #: lib/checkout_op.tcl:127
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
-msgstr ""
+msgstr "致命错误: 无法解决 %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31
+#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
 msgid "Close"
-msgstr ""
+msgstr "关闭"
 
 #: lib/checkout_op.tcl:169
 #, tcl-format
 msgid "Branch '%s' does not exist."
-msgstr ""
+msgstr "分支 '%s' 并不存在."
 
 #: lib/checkout_op.tcl:206
 #, tcl-format
@@ -765,20 +779,24 @@
 "It cannot fast-forward to %s.\n"
 "A merge is required."
 msgstr ""
+"分支 '%s' 已经存在.\n"
+"\n"
+"无法快速合并到 %s.\n"
+"需要普通合并."
 
 #: lib/checkout_op.tcl:220
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
-msgstr ""
+msgstr "合并策略 '%s' 不支持."
 
 #: lib/checkout_op.tcl:239
 #, tcl-format
 msgid "Failed to update '%s'."
-msgstr ""
+msgstr "无法更新 '%s'."
 
 #: lib/checkout_op.tcl:251
 msgid "Staging area (index) is already locked."
-msgstr ""
+msgstr "缓存区域 (index) 已被锁定."
 
 #: lib/checkout_op.tcl:266
 msgid ""
@@ -789,25 +807,35 @@
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/checkout_op.tcl:322
 #, tcl-format
 msgid "Updating working directory to '%s'..."
+msgstr "更新工作目录到 '%s'..."
+
+#: lib/checkout_op.tcl:323
+msgid "files checked out"
 msgstr ""
 
 #: lib/checkout_op.tcl:353
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr ""
+msgstr "中止 '%s' 的 checkout 操作 (需要做文件级合并)."
 
 #: lib/checkout_op.tcl:354
 msgid "File level merge required."
-msgstr ""
+msgstr "需要文件级合并."
 
 #: lib/checkout_op.tcl:358
 #, tcl-format
 msgid "Staying on branch '%s'."
-msgstr ""
+msgstr "停留在分支 '%s'."
 
 #: lib/checkout_op.tcl:429
 msgid ""
@@ -816,29 +844,32 @@
 "If you wanted to be on a branch, create one now starting from 'This Detached "
 "Checkout'."
 msgstr ""
+"你不在某个本地分支上.\n"
+"\n"
+"如果你想位于某分支上, 从当前脱节的Checkout中创建一个新分支."
 
-#: lib/checkout_op.tcl:446
-#, fuzzy, tcl-format
+#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
+#, tcl-format
 msgid "Checked out '%s'."
-msgstr "切换..."
+msgstr "'%s' 已被 checkout"
 
 #: lib/checkout_op.tcl:478
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr ""
+msgstr "复位 '%s' 到 '%s' 将导致下列提交的丢失:"
 
 #: lib/checkout_op.tcl:500
 msgid "Recovering lost commits may not be easy."
-msgstr ""
+msgstr "恢复丢失的提交是比较困难的."
 
 #: lib/checkout_op.tcl:505
 #, tcl-format
 msgid "Reset '%s'?"
-msgstr ""
+msgstr "复位 '%s'?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:164
+#: lib/checkout_op.tcl:510 lib/merge.tcl:163
 msgid "Visualize"
-msgstr ""
+msgstr "图示"
 
 #: lib/checkout_op.tcl:578
 #, tcl-format
@@ -850,286 +881,301 @@
 "\n"
 "This should not have occurred.  %s will now close and give up."
 msgstr ""
+"无法设定当前分支.\n"
+"\n"
+"当前工作目录仅有部分被切换出, 我们已成功的更新了您的文件但是无法更新某个内部"
+"的Git文件.\n"
+"\n"
+"这本不该发生, %s 将关闭并放弃."
 
 #: lib/choose_font.tcl:39
-#, fuzzy
 msgid "Select"
-msgstr "全选"
+msgstr "选择"
 
 #: lib/choose_font.tcl:53
 msgid "Font Family"
-msgstr ""
+msgstr "字体族"
 
-#: lib/choose_font.tcl:73
-#, fuzzy
+#: lib/choose_font.tcl:74
 msgid "Font Size"
-msgstr "缩小字体"
+msgstr "字体大小"
 
-#: lib/choose_font.tcl:90
+#: lib/choose_font.tcl:91
 msgid "Font Example"
-msgstr ""
+msgstr "字体样例"
 
-#: lib/choose_font.tcl:101
+#: lib/choose_font.tcl:103
 msgid ""
 "This is example text.\n"
 "If you like this text, it can be your font."
 msgstr ""
+"这是样例文本.\n"
+"如果你喜欢, 你可以设置该字体."
 
-#: lib/choose_repository.tcl:25
+#: lib/choose_repository.tcl:28
 msgid "Git Gui"
-msgstr ""
+msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:69 lib/choose_repository.tcl:204
-#, fuzzy
+#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
 msgid "Create New Repository"
-msgstr "版本树"
+msgstr "创建新的版本库"
 
-#: lib/choose_repository.tcl:74 lib/choose_repository.tcl:291
-#, fuzzy
+#: lib/choose_repository.tcl:87
+msgid "New..."
+msgstr "新建..."
+
+#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
 msgid "Clone Existing Repository"
-msgstr "版本树"
+msgstr "克隆已有版本库"
 
-#: lib/choose_repository.tcl:79 lib/choose_repository.tcl:800
-#, fuzzy
+#: lib/choose_repository.tcl:100
+msgid "Clone..."
+msgstr "克隆..."
+
+#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
 msgid "Open Existing Repository"
-msgstr "版本树"
+msgstr "打开已有版本库"
 
-#: lib/choose_repository.tcl:91
-msgid "Next >"
-msgstr ""
+#: lib/choose_repository.tcl:113
+msgid "Open..."
+msgstr "打开..."
 
-#: lib/choose_repository.tcl:152
-#, tcl-format
-msgid "Location %s already exists."
-msgstr ""
+#: lib/choose_repository.tcl:126
+msgid "Recent Repositories"
+msgstr "最近版本库"
 
-#: lib/choose_repository.tcl:158 lib/choose_repository.tcl:165
-#: lib/choose_repository.tcl:172
+#: lib/choose_repository.tcl:132
+msgid "Open Recent Repository:"
+msgstr "打开最近版本库"
+
+#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
+#: lib/choose_repository.tcl:310
 #, tcl-format
 msgid "Failed to create repository %s:"
-msgstr ""
+msgstr "无法创建版本库 %s:"
 
-#: lib/choose_repository.tcl:209 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
 msgid "Directory:"
-msgstr ""
+msgstr "目录:"
 
-#: lib/choose_repository.tcl:238 lib/choose_repository.tcl:363
-#: lib/choose_repository.tcl:834
-#, fuzzy
+#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
+#: lib/choose_repository.tcl:1011
 msgid "Git Repository"
-msgstr "版本树"
+msgstr "Git 版本库"
 
-#: lib/choose_repository.tcl:253 lib/choose_repository.tcl:260
+#: lib/choose_repository.tcl:437
 #, tcl-format
 msgid "Directory %s already exists."
-msgstr ""
+msgstr "目录 %s 已经存在."
 
-#: lib/choose_repository.tcl:265
+#: lib/choose_repository.tcl:441
 #, tcl-format
 msgid "File %s already exists."
-msgstr ""
+msgstr "文件 %s 已经存在."
 
-#: lib/choose_repository.tcl:286
+#: lib/choose_repository.tcl:455
 msgid "Clone"
-msgstr ""
+msgstr "克隆"
 
-#: lib/choose_repository.tcl:299
+#: lib/choose_repository.tcl:468
 msgid "URL:"
-msgstr ""
+msgstr "URL:"
 
-#: lib/choose_repository.tcl:319
+#: lib/choose_repository.tcl:489
 msgid "Clone Type:"
-msgstr ""
+msgstr "克隆类型:"
 
-#: lib/choose_repository.tcl:325
+#: lib/choose_repository.tcl:495
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
-msgstr ""
+msgstr "标准方式 (快速, 部分备份, 作硬连接)"
 
-#: lib/choose_repository.tcl:331
+#: lib/choose_repository.tcl:501
 msgid "Full Copy (Slower, Redundant Backup)"
-msgstr ""
+msgstr "全部复制 (较慢, 做备份)"
 
-#: lib/choose_repository.tcl:337
+#: lib/choose_repository.tcl:507
 msgid "Shared (Fastest, Not Recommended, No Backup)"
-msgstr ""
+msgstr "共享方式 (最快, 不推荐, 不做备份)"
 
-#: lib/choose_repository.tcl:369 lib/choose_repository.tcl:418
-#: lib/choose_repository.tcl:560 lib/choose_repository.tcl:630
-#: lib/choose_repository.tcl:840 lib/choose_repository.tcl:848
+#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
+#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
+#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
 #, tcl-format
 msgid "Not a Git repository: %s"
-msgstr ""
+msgstr "不是一个 Git 版本库: %s"
 
-#: lib/choose_repository.tcl:405
+#: lib/choose_repository.tcl:579
 msgid "Standard only available for local repository."
-msgstr ""
+msgstr "标准方式仅当是本地版本库时有效."
 
-#: lib/choose_repository.tcl:409
+#: lib/choose_repository.tcl:583
 msgid "Shared only available for local repository."
-msgstr ""
+msgstr "共享方式仅当是本地版本库时有效."
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:604
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "位置 %s 已经存在."
+
+#: lib/choose_repository.tcl:615
 msgid "Failed to configure origin"
-msgstr ""
+msgstr "无法配置 origin"
 
-#: lib/choose_repository.tcl:451
+#: lib/choose_repository.tcl:627
 msgid "Counting objects"
-msgstr ""
+msgstr "清点对象"
 
-#: lib/choose_repository.tcl:452
+#: lib/choose_repository.tcl:628
+#, fuzzy
 msgid "buckets"
-msgstr ""
+msgstr "水桶??"
 
-#: lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:652
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
-msgstr ""
+msgstr "无法复制 objects/info/alternates: %s"
 
-#: lib/choose_repository.tcl:512
+#: lib/choose_repository.tcl:688
 #, tcl-format
 msgid "Nothing to clone from %s."
-msgstr ""
+msgstr "没有东西可从 %s 克隆."
 
-#: lib/choose_repository.tcl:514 lib/choose_repository.tcl:728
-#: lib/choose_repository.tcl:740
+#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
+#: lib/choose_repository.tcl:916
 msgid "The 'master' branch has not been initialized."
-msgstr ""
+msgstr "'master'分支尚未初始化."
 
-#: lib/choose_repository.tcl:527
+#: lib/choose_repository.tcl:703
 msgid "Hardlinks are unavailable.  Falling back to copying."
-msgstr ""
+msgstr "硬连接不可用. 使用复制."
 
-#: lib/choose_repository.tcl:539
+#: lib/choose_repository.tcl:715
 #, tcl-format
 msgid "Cloning from %s"
-msgstr ""
+msgstr "从 %s 克隆"
 
-#: lib/choose_repository.tcl:570
-#, fuzzy
+#: lib/choose_repository.tcl:746
 msgid "Copying objects"
-msgstr "压缩数据库"
+msgstr "复制 objects"
 
-#: lib/choose_repository.tcl:571
+#: lib/choose_repository.tcl:747
 msgid "KiB"
-msgstr ""
+msgstr "KiB"
 
-#: lib/choose_repository.tcl:595
+#: lib/choose_repository.tcl:771
 #, tcl-format
 msgid "Unable to copy object: %s"
-msgstr ""
+msgstr "无法复制 object: %s"
 
-#: lib/choose_repository.tcl:605
+#: lib/choose_repository.tcl:781
 msgid "Linking objects"
-msgstr ""
+msgstr "链接 objects"
 
-#: lib/choose_repository.tcl:606
+#: lib/choose_repository.tcl:782
 msgid "objects"
-msgstr ""
+msgstr "objects"
 
-#: lib/choose_repository.tcl:614
+#: lib/choose_repository.tcl:790
 #, tcl-format
 msgid "Unable to hardlink object: %s"
-msgstr ""
+msgstr "无法硬链接 object: %s"
 
-#: lib/choose_repository.tcl:669
+#: lib/choose_repository.tcl:845
 msgid "Cannot fetch branches and objects.  See console output for details."
-msgstr ""
+msgstr "无法获取分支和对象. 请查看控制终端的输出."
 
-#: lib/choose_repository.tcl:680
+#: lib/choose_repository.tcl:856
 msgid "Cannot fetch tags.  See console output for details."
-msgstr ""
+msgstr "无法获取标签. 请查看控制终端的输出."
 
-#: lib/choose_repository.tcl:704
+#: lib/choose_repository.tcl:880
 msgid "Cannot determine HEAD.  See console output for details."
-msgstr ""
+msgstr "无法确定 HEAD. 请查看控制终端的输出."
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:889
 #, tcl-format
 msgid "Unable to cleanup %s"
-msgstr ""
+msgstr "无法清理 %s"
 
-#: lib/choose_repository.tcl:719
+#: lib/choose_repository.tcl:895
 msgid "Clone failed."
-msgstr ""
+msgstr "克隆失败."
 
-#: lib/choose_repository.tcl:726
+#: lib/choose_repository.tcl:902
 msgid "No default branch obtained."
-msgstr ""
+msgstr "没有获取缺省分支"
 
-#: lib/choose_repository.tcl:737
+#: lib/choose_repository.tcl:913
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
-msgstr ""
+msgstr "无法解析 %s 为提交."
 
-#: lib/choose_repository.tcl:749
+#: lib/choose_repository.tcl:925
 msgid "Creating working directory"
-msgstr ""
+msgstr "创建工作目录"
 
-#: lib/choose_repository.tcl:750 lib/index.tcl:15 lib/index.tcl:80
-#: lib/index.tcl:149
+#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
+#: lib/index.tcl:193
 msgid "files"
-msgstr ""
+msgstr "文件"
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:955
 msgid "Initial file checkout failed."
-msgstr ""
+msgstr "初始的文件checkout失败"
 
-#: lib/choose_repository.tcl:795
+#: lib/choose_repository.tcl:971
 msgid "Open"
-msgstr ""
+msgstr "打开"
 
-#: lib/choose_repository.tcl:805
-#, fuzzy
+#: lib/choose_repository.tcl:981
 msgid "Repository:"
-msgstr "版本树"
+msgstr "版本库"
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Failed to open repository %s:"
-msgstr ""
+msgstr "无法打开版本库 %s:"
 
 #: lib/choose_rev.tcl:53
 msgid "This Detached Checkout"
-msgstr ""
+msgstr "该脱节的Checkout"
 
 #: lib/choose_rev.tcl:60
 msgid "Revision Expression:"
-msgstr ""
+msgstr "版本表达式:"
 
 #: lib/choose_rev.tcl:74
-#, fuzzy
 msgid "Local Branch"
-msgstr "分支"
+msgstr "本地分支"
 
 #: lib/choose_rev.tcl:79
-#, fuzzy
 msgid "Tracking Branch"
-msgstr "当前分支:"
+msgstr "跟踪分支:"
 
-#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
 msgid "Tag"
-msgstr ""
+msgstr "标签"
 
 #: lib/choose_rev.tcl:317
 #, tcl-format
 msgid "Invalid revision: %s"
-msgstr ""
+msgstr "无效版本: %s"
 
 #: lib/choose_rev.tcl:338
 msgid "No revision selected."
-msgstr ""
+msgstr "没有选择版本."
 
 #: lib/choose_rev.tcl:346
 msgid "Revision expression is empty."
-msgstr ""
+msgstr "版本表达式为空."
 
-#: lib/choose_rev.tcl:530
+#: lib/choose_rev.tcl:531
 msgid "Updated"
-msgstr ""
+msgstr "已更新"
 
-#: lib/choose_rev.tcl:558
+#: lib/choose_rev.tcl:559
 msgid "URL"
-msgstr ""
+msgstr "URL"
 
 #: lib/commit.tcl:9
 msgid ""
@@ -1138,6 +1184,9 @@
 "You are about to create the initial commit.  There is no commit before this "
 "to amend.\n"
 msgstr ""
+"没有改动需要修正.\n"
+"\n"
+"你正在创建最初的提交. 在此之前没有提交可以修正.\n"
 
 #: lib/commit.tcl:18
 msgid ""
@@ -1147,18 +1196,22 @@
 "completed.  You cannot amend the prior commit unless you first abort the "
 "current merge activity.\n"
 msgstr ""
+"在合并时无法修正.\n"
+"\n"
+"你当前正在一次尚未完成的合并操作过程中. 除非中止当前合并活动,\n"
+"否则无法修正之前的提交.\n"
 
 #: lib/commit.tcl:49
 msgid "Error loading commit data for amend:"
-msgstr ""
+msgstr "为修正装载提交数据出错:"
 
 #: lib/commit.tcl:76
 msgid "Unable to obtain your identity:"
-msgstr ""
+msgstr "无法获知你的身份:"
 
 #: lib/commit.tcl:81
 msgid "Invalid GIT_COMMITTER_IDENT:"
-msgstr ""
+msgstr "无效的 GIT_COMMITTER_IDENT"
 
 #: lib/commit.tcl:133
 msgid ""
@@ -1169,6 +1222,12 @@
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/commit.tcl:154
 #, tcl-format
@@ -1178,6 +1237,9 @@
 "File %s has merge conflicts.  You must resolve them and stage the file "
 "before committing.\n"
 msgstr ""
+"尚未合并的文件没有办法提交.\n"
+"\n"
+"文件 %s 有合并冲突, 你必须解决这些冲突并缓存该文件作提交.\n"
 
 #: lib/commit.tcl:162
 #, tcl-format
@@ -1186,6 +1248,9 @@
 "\n"
 "File %s cannot be committed by this program.\n"
 msgstr ""
+"检测到未知文件状态 %s.\n"
+"\n"
+"文件 %s 无法由该程序提交.\n"
 
 #: lib/commit.tcl:170
 msgid ""
@@ -1193,6 +1258,9 @@
 "\n"
 "You must stage at least 1 file before you can commit.\n"
 msgstr ""
+"没有需要提交的变动.\n"
+"\n"
+"提交前你必须首先缓存至少一个文件.\n"
 
 #: lib/commit.tcl:183
 msgid ""
@@ -1200,21 +1268,58 @@
 "\n"
 "A good commit message has the following format:\n"
 "\n"
-"- First line: Describe in one sentance what you did.\n"
+"- First line: Describe in one sentence what you did.\n"
 "- Second line: Blank\n"
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
+"请提供一条提交信息.\n"
+"\n"
+"一条好的提交信息有下列格式:\n"
+"\n"
+"- 第一行: 一句话概括你做的修改.\n"
+"- 第二行: 空行\n"
+"- 剩余行: 请描述为什么你做的这些改动是好的.\n"
 
-#: lib/commit.tcl:257
-msgid "write-tree failed:"
+#: lib/commit.tcl:207
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "警告: Tcl 不支持编码方式 '%s'."
+
+#: lib/commit.tcl:221
+msgid "Calling pre-commit hook..."
 msgstr ""
 
-#: lib/commit.tcl:275
+#: lib/commit.tcl:236
+msgid "Commit declined by pre-commit hook."
+msgstr ""
+
+#: lib/commit.tcl:259
+msgid "Calling commit-msg hook..."
+msgstr ""
+
+#: lib/commit.tcl:274
+msgid "Commit declined by commit-msg hook."
+msgstr ""
+
+#: lib/commit.tcl:287
+msgid "Committing changes..."
+msgstr ""
+
+#: lib/commit.tcl:303
+msgid "write-tree failed:"
+msgstr "write-tree 失败:"
+
+#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#, fuzzy
+msgid "Commit failed."
+msgstr "克隆失败."
+
+#: lib/commit.tcl:321
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
-msgstr ""
+msgstr "提交 %s 似乎已损坏"
 
-#: lib/commit.tcl:279
+#: lib/commit.tcl:326
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1222,77 +1327,76 @@
 "\n"
 "A rescan will be automatically started now.\n"
 msgstr ""
+"没有改动提交.\n"
+"\n"
+"该提交没有改动任何文件也不是一个合并提交.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
-#: lib/commit.tcl:286
+#: lib/commit.tcl:333
 msgid "No changes to commit."
-msgstr ""
+msgstr "没有改动要提交."
 
-#: lib/commit.tcl:303
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr ""
-
-#: lib/commit.tcl:317
+#: lib/commit.tcl:347
 msgid "commit-tree failed:"
-msgstr ""
+msgstr "commit-tree 失败:"
 
-#: lib/commit.tcl:339
+#: lib/commit.tcl:367
 msgid "update-ref failed:"
-msgstr ""
+msgstr "update-ref 失败:"
 
-#: lib/commit.tcl:430
+#: lib/commit.tcl:454
 #, tcl-format
 msgid "Created commit %s: %s"
-msgstr ""
+msgstr "创建了 commit %s: %s"
 
-#: lib/console.tcl:57
+#: lib/console.tcl:59
 msgid "Working... please wait..."
-msgstr ""
+msgstr "工作中... 请等待..."
 
-#: lib/console.tcl:183
+#: lib/console.tcl:186
 msgid "Success"
-msgstr ""
+msgstr "成功"
 
-#: lib/console.tcl:196
+#: lib/console.tcl:200
 msgid "Error: Command Failed"
-msgstr ""
+msgstr "错误: 命令失败"
 
 #: lib/database.tcl:43
 msgid "Number of loose objects"
-msgstr ""
+msgstr "松散对象的数量"
 
 #: lib/database.tcl:44
 msgid "Disk space used by loose objects"
-msgstr ""
+msgstr "松散对象所使用的磁盘空间"
 
 #: lib/database.tcl:45
 msgid "Number of packed objects"
-msgstr ""
+msgstr "压缩对象数量"
 
 #: lib/database.tcl:46
 msgid "Number of packs"
-msgstr ""
+msgstr "压缩包数量"
 
 #: lib/database.tcl:47
 msgid "Disk space used by packed objects"
-msgstr ""
+msgstr "压缩对象所使用的磁盘空间"
 
 #: lib/database.tcl:48
 msgid "Packed objects waiting for pruning"
-msgstr ""
+msgstr "压缩对象等待清理"
 
 #: lib/database.tcl:49
 msgid "Garbage files"
-msgstr ""
+msgstr "垃圾文件"
 
 #: lib/database.tcl:72
-#, fuzzy
 msgid "Compressing the object database"
-msgstr "压缩数据库"
+msgstr "压缩对象数据库"
 
 #: lib/database.tcl:83
 msgid "Verifying the object database with fsck-objects"
-msgstr ""
+msgstr "使用 fsck-objects 验证对象数据库"
 
 #: lib/database.tcl:108
 #, tcl-format
@@ -1304,11 +1408,16 @@
 "\n"
 "Compress the database now?"
 msgstr ""
+"该版本库当前约有 %i 个松散对象.\n"
+"\n"
+"为达到较优的性能,强烈建议你在松散对象多于 %i 时压缩数据库.\n"
+"\n"
+"现在就压缩数据库么?"
 
 #: lib/date.tcl:25
 #, tcl-format
 msgid "Invalid date from Git: %s"
-msgstr ""
+msgstr "无效的日期: %s"
 
 #: lib/diff.tcl:42
 #, tcl-format
@@ -1323,80 +1432,112 @@
 "A rescan will be automatically started to find other files which may have "
 "the same state."
 msgstr ""
+"未检测到改动.\n"
+"\n"
+"该文件的修改日期被另一个程序所更新, 但其内容并没有变化.\n"
+"\n"
+"对于类似情况的其他文件的重新扫描将自动开始."
 
 #: lib/diff.tcl:81
-#, tcl-format
+#, fuzzy, tcl-format
 msgid "Loading diff of %s..."
-msgstr ""
+msgstr "装载 %s 的 diff ..."
 
 #: lib/diff.tcl:114 lib/diff.tcl:184
 #, tcl-format
 msgid "Unable to display %s"
-msgstr ""
+msgstr "无法显示 %s"
 
 #: lib/diff.tcl:115
 msgid "Error loading file:"
-msgstr ""
+msgstr "装载文件出错:"
 
 #: lib/diff.tcl:122
 msgid "Git Repository (subproject)"
-msgstr ""
+msgstr "Git 版本库 (子项目)"
 
 #: lib/diff.tcl:134
 msgid "* Binary file (not showing content)."
-msgstr ""
+msgstr "* 二进制文件 (不显示内容)."
 
 #: lib/diff.tcl:185
 msgid "Error loading diff:"
-msgstr ""
+msgstr "装载 diff 错误:"
 
-#: lib/diff.tcl:302
+#: lib/diff.tcl:303
 msgid "Failed to unstage selected hunk."
-msgstr ""
+msgstr "无法将选择的代码段从缓存中删除."
 
-#: lib/diff.tcl:309
+#: lib/diff.tcl:310
 msgid "Failed to stage selected hunk."
-msgstr ""
+msgstr "无法缓存所选代码段."
 
-#: lib/error.tcl:12 lib/error.tcl:102
+#: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
-msgstr ""
+msgstr "错误"
 
-#: lib/error.tcl:28
+#: lib/error.tcl:36
 msgid "warning"
-msgstr ""
+msgstr "警告"
 
-#: lib/error.tcl:81
+#: lib/error.tcl:94
 msgid "You must correct the above errors before committing."
-msgstr ""
+msgstr "你必须在提交前修正上述错误."
 
-#: lib/index.tcl:241
-#, fuzzy, tcl-format
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "无法解锁缓存 (index)"
+
+#: lib/index.tcl:15
+msgid "Index Error"
+msgstr "缓存(Index)错误"
+
+#: lib/index.tcl:21
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr "更新 Git 缓存(Index)失败, 重新扫描将自动开始以重新同步 git-gui."
+
+#: lib/index.tcl:27
+msgid "Continue"
+msgstr "继续"
+
+#: lib/index.tcl:31
+msgid "Unlock Index"
+msgstr "解锁 Index"
+
+#: lib/index.tcl:282
+#, tcl-format
 msgid "Unstaging %s from commit"
-msgstr "从本次提交移除"
+msgstr "从提交缓存中删除 %s"
 
-#: lib/index.tcl:285
+#: lib/index.tcl:313
+#, fuzzy
+msgid "Ready to commit."
+msgstr "缓存为提交"
+
+#: lib/index.tcl:326
 #, tcl-format
 msgid "Adding %s"
-msgstr ""
+msgstr "添加 %s"
 
-#: lib/index.tcl:340
-#, fuzzy, tcl-format
+#: lib/index.tcl:381
+#, tcl-format
 msgid "Revert changes in file %s?"
-msgstr "恢复修改"
+msgstr "撤销文件 %s 中的改动?"
 
-#: lib/index.tcl:342
+#: lib/index.tcl:383
 #, tcl-format
 msgid "Revert changes in these %i files?"
-msgstr ""
+msgstr "撤销这些 (%i个) 文件的改动?"
 
-#: lib/index.tcl:348
+#: lib/index.tcl:391
 msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr ""
+msgstr "任何未缓存的改动将在这次撤销中永久丢失."
 
-#: lib/index.tcl:351
+#: lib/index.tcl:394
 msgid "Do Nothing"
-msgstr ""
+msgstr "不做操作"
 
 #: lib/merge.tcl:13
 msgid ""
@@ -1404,6 +1545,9 @@
 "\n"
 "You must finish amending this commit before starting any type of merge.\n"
 msgstr ""
+"修正时无法做合并.\n"
+"\n"
+"你必须完成对该提交的修正才能继续任何类型的合并操作.\n"
 
 #: lib/merge.tcl:27
 msgid ""
@@ -1414,6 +1558,12 @@
 "\n"
 "The rescan will be automatically started now.\n"
 msgstr ""
+"最后一次扫描的状态和当前版本库状态不符.\n"
+"\n"
+"另一 Git 程序自上次扫描后修改了本版本库. 在修改当前分支之前需要重新做一次扫"
+"描.\n"
+"\n"
+"重新扫描将自动开始.\n"
 
 #: lib/merge.tcl:44
 #, tcl-format
@@ -1425,6 +1575,12 @@
 "You must resolve them, stage the file, and commit to complete the current "
 "merge.  Only then can you begin another merge.\n"
 msgstr ""
+"你正处在一个有冲突的合并操作中.\n"
+"\n"
+"文件 %s 有合并冲突.\n"
+"\n"
+"你必须解决这些冲突, 缓存该文件, 并提交来完成当前的合并.仅当这样后才能开始下一"
+"个合并操作.\n"
 
 #: lib/merge.tcl:54
 #, tcl-format
@@ -1436,6 +1592,12 @@
 "You should complete the current commit before starting a merge.  Doing so "
 "will help you abort a failed merge, should the need arise.\n"
 msgstr ""
+"你正处在一个改动当中.\n"
+"\n"
+"文件 %s 已被修改.\n"
+"\n"
+"你必须完成当前的提交后才能开始合并. 如果需要, 这么做将有助于中止一次失败的合"
+"并.\n"
 
 #: lib/merge.tcl:106
 #, tcl-format
@@ -1443,35 +1605,38 @@
 msgstr ""
 
 #: lib/merge.tcl:119
-#, tcl-format
-msgid "Merging %s and %s"
-msgstr ""
+#, fuzzy, tcl-format
+msgid "Merging %s and %s..."
+msgstr "合并 %s 和 %s"
 
-#: lib/merge.tcl:131
+#: lib/merge.tcl:130
 msgid "Merge completed successfully."
-msgstr ""
+msgstr "合并成功完成."
 
-#: lib/merge.tcl:133
+#: lib/merge.tcl:132
 msgid "Merge failed.  Conflict resolution is required."
-msgstr ""
+msgstr "合并失败. 需要解决冲突."
 
-#: lib/merge.tcl:158
+#: lib/merge.tcl:157
 #, tcl-format
 msgid "Merge Into %s"
-msgstr ""
+msgstr "合并到 %s"
 
-#: lib/merge.tcl:177
+#: lib/merge.tcl:176
 msgid "Revision To Merge"
-msgstr ""
+msgstr "要合并的版本"
 
-#: lib/merge.tcl:212
+#: lib/merge.tcl:211
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
 "You must finish amending this commit.\n"
 msgstr ""
+"修正操作中无法中止.\n"
+"\n"
+"你必须先完成本次修正操作.\n"
 
-#: lib/merge.tcl:222
+#: lib/merge.tcl:221
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1479,8 +1644,13 @@
 "\n"
 "Continue with aborting the current merge?"
 msgstr ""
+"中止合并?\n"
+"\n"
+"中止当前的合并操作将导致 *所有* 尚未提交的改动丢失.\n"
+"\n"
+"是否要继续中止当前的合并操作?"
 
-#: lib/merge.tcl:228
+#: lib/merge.tcl:227
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1488,150 +1658,151 @@
 "\n"
 "Continue with resetting the current changes?"
 msgstr ""
+"是否复位当前改动?\n"
+"\n"
+"复位当前的改动将导致 *所有* 未提交的改动丢失.\n"
+"\n"
+"是否要继续复位当前的改动?"
 
-#: lib/merge.tcl:239
+#: lib/merge.tcl:238
 msgid "Aborting"
-msgstr ""
+msgstr "中止"
 
-#: lib/merge.tcl:266
-msgid "Abort failed."
-msgstr ""
-
-#: lib/merge.tcl:268
-msgid "Abort completed.  Ready."
-msgstr ""
-
-#: lib/option.tcl:82
-msgid "Restore Defaults"
-msgstr ""
-
-#: lib/option.tcl:86
-msgid "Save"
-msgstr ""
-
-#: lib/option.tcl:96
-#, fuzzy, tcl-format
-msgid "%s Repository"
-msgstr "版本树"
-
-#: lib/option.tcl:97
-msgid "Global (All Repositories)"
-msgstr ""
-
-#: lib/option.tcl:103
-msgid "User Name"
-msgstr ""
-
-#: lib/option.tcl:104
-msgid "Email Address"
-msgstr ""
-
-#: lib/option.tcl:106
+#: lib/merge.tcl:238
 #, fuzzy
-msgid "Summarize Merge Commits"
-msgstr "修订合并提交描述:"
+msgid "files reset"
+msgstr "文件"
 
-#: lib/option.tcl:107
-msgid "Merge Verbosity"
-msgstr ""
+#: lib/merge.tcl:265
+msgid "Abort failed."
+msgstr "中止失败"
 
-#: lib/option.tcl:108
-msgid "Show Diffstat After Merge"
-msgstr ""
+#: lib/merge.tcl:267
+msgid "Abort completed.  Ready."
+msgstr "中止完成. 就绪."
+
+#: lib/option.tcl:95
+msgid "Restore Defaults"
+msgstr "恢复默认值"
+
+#: lib/option.tcl:99
+msgid "Save"
+msgstr "保存"
+
+#: lib/option.tcl:109
+#, tcl-format
+msgid "%s Repository"
+msgstr "%s 版本库"
 
 #: lib/option.tcl:110
+msgid "Global (All Repositories)"
+msgstr "全局 (所有版本库)"
+
+#: lib/option.tcl:116
+msgid "User Name"
+msgstr "用户名"
+
+#: lib/option.tcl:117
+msgid "Email Address"
+msgstr "Email 地址"
+
+#: lib/option.tcl:119
+msgid "Summarize Merge Commits"
+msgstr "概述合并提交:"
+
+#: lib/option.tcl:120
+msgid "Merge Verbosity"
+msgstr "合并冗余度"
+
+#: lib/option.tcl:121
+msgid "Show Diffstat After Merge"
+msgstr "在合并后显示 Diffstat"
+
+#: lib/option.tcl:123
 msgid "Trust File Modification Timestamps"
-msgstr ""
+msgstr "相信文件的改动时间"
 
-#: lib/option.tcl:111
+#: lib/option.tcl:124
 msgid "Prune Tracking Branches During Fetch"
-msgstr ""
+msgstr "获取时清除跟踪分支"
 
-#: lib/option.tcl:112
+#: lib/option.tcl:125
 msgid "Match Tracking Branches"
-msgstr ""
+msgstr "匹配跟踪分支"
 
-#: lib/option.tcl:113
+#: lib/option.tcl:126
 msgid "Number of Diff Context Lines"
-msgstr ""
+msgstr "Diff 上下文行数"
 
-#: lib/option.tcl:114
+#: lib/option.tcl:127
+#, fuzzy
+msgid "Commit Message Text Width"
+msgstr "提交描述:"
+
+#: lib/option.tcl:128
 msgid "New Branch Name Template"
+msgstr "新建分支命名模板"
+
+#: lib/option.tcl:192
+msgid "Spelling Dictionary:"
 msgstr ""
 
-#: lib/option.tcl:176
+#: lib/option.tcl:216
 msgid "Change Font"
-msgstr ""
+msgstr "更改字体"
 
-#: lib/option.tcl:180
+#: lib/option.tcl:220
 #, tcl-format
 msgid "Choose %s"
-msgstr ""
+msgstr "选择 %s"
 
-#: lib/option.tcl:186
+#: lib/option.tcl:226
 msgid "pt."
-msgstr ""
+msgstr "磅"
 
-#: lib/option.tcl:200
+#: lib/option.tcl:240
 msgid "Preferences"
-msgstr ""
+msgstr "首选项"
 
-#: lib/option.tcl:235
+#: lib/option.tcl:275
 msgid "Failed to completely save options:"
-msgstr ""
-
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr ""
-
-#: lib/remote.tcl:170
-#, fuzzy
-msgid "Fetch from"
-msgstr "导入"
-
-#: lib/remote.tcl:213
-#, fuzzy
-msgid "Push to"
-msgstr "上传"
+msgstr "无法完全保存选项:"
 
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
 msgid "Delete Remote Branch"
-msgstr ""
+msgstr "删除远端分支"
 
 #: lib/remote_branch_delete.tcl:47
-#, fuzzy
 msgid "From Repository"
-msgstr "版本树"
+msgstr "从版本库"
 
 #: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
 msgid "Remote:"
-msgstr ""
+msgstr "Remote:"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
 msgid "Arbitrary URL:"
-msgstr ""
+msgstr "任意 URL:"
 
 #: lib/remote_branch_delete.tcl:84
-#, fuzzy
 msgid "Branches"
 msgstr "分支"
 
 #: lib/remote_branch_delete.tcl:109
-#, fuzzy
 msgid "Delete Only If"
-msgstr "删除"
+msgstr "删除仅当"
 
 #: lib/remote_branch_delete.tcl:111
 msgid "Merged Into:"
-msgstr ""
+msgstr "合并到"
 
 #: lib/remote_branch_delete.tcl:119
 msgid "Always (Do not perform merge checks)"
-msgstr ""
+msgstr "总是合并 (不作合并检查)"
 
 #: lib/remote_branch_delete.tcl:152
 msgid "A branch is required for 'Merged Into'."
-msgstr ""
+msgstr "'合并到' 需要指定某个分支"
 
 #: lib/remote_branch_delete.tcl:184
 #, tcl-format
@@ -1640,6 +1811,9 @@
 "\n"
 " - %s"
 msgstr ""
+"下列分支没有被全部合并到 %s 中:\n"
+"\n"
+" - %s"
 
 #: lib/remote_branch_delete.tcl:189
 #, tcl-format
@@ -1647,10 +1821,11 @@
 "One or more of the merge tests failed because you have not fetched the "
 "necessary commits.  Try fetching from %s first."
 msgstr ""
+"由于没有获取到必要的提交,一个或多个合并测试失败。请尝试从 %s 处先获取。"
 
 #: lib/remote_branch_delete.tcl:207
 msgid "Please select one or more branches to delete."
-msgstr ""
+msgstr "请选择某个或多个分支来删除"
 
 #: lib/remote_branch_delete.tcl:216
 msgid ""
@@ -1658,112 +1833,145 @@
 "\n"
 "Delete the selected branches?"
 msgstr ""
+"恢复被删除的分支非常困难.\n"
+"\n"
+"是否要删除所选分支?"
 
 #: lib/remote_branch_delete.tcl:226
 #, tcl-format
 msgid "Deleting branches from %s"
-msgstr ""
+msgstr "从 %s 中删除分支"
 
 #: lib/remote_branch_delete.tcl:286
 msgid "No repository selected."
-msgstr ""
+msgstr "没有选择版本库"
 
 #: lib/remote_branch_delete.tcl:291
 #, tcl-format
 msgid "Scanning %s..."
-msgstr ""
+msgstr "正在扫描 %s..."
 
-#: lib/shortcut.tcl:26 lib/shortcut.tcl:74
-msgid "Cannot write script:"
-msgstr ""
+#: lib/remote.tcl:165
+msgid "Prune from"
+msgstr "从..清除(prune)"
 
-#: lib/shortcut.tcl:149
+#: lib/remote.tcl:170
+msgid "Fetch from"
+msgstr "从..获取(fetch)"
+
+#: lib/remote.tcl:213
+msgid "Push to"
+msgstr "上传到(push)"
+
+#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+msgid "Cannot write shortcut:"
+msgstr "无法修改快捷方式:"
+
+#: lib/shortcut.tcl:136
 msgid "Cannot write icon:"
+msgstr "无法修改图标:"
+
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr ""
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr ""
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr ""
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr ""
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:180
+msgid "No Suggestions"
+msgstr ""
+
+#: lib/spellcheck.tcl:381
+msgid "Unexpected EOF from spell checker"
+msgstr ""
+
+#: lib/spellcheck.tcl:385
+msgid "Spell Checker Failed"
 msgstr ""
 
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
-msgstr ""
+msgstr "%s ... %*i of %*i %s (%3i%%)"
 
 #: lib/transport.tcl:6
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "fetch %s"
-msgstr "导入"
+msgstr "获取(fetch)"
 
 #: lib/transport.tcl:7
 #, tcl-format
 msgid "Fetching new changes from %s"
-msgstr ""
+msgstr "从 %s 处获取新的改动"
 
 #: lib/transport.tcl:18
 #, tcl-format
 msgid "remote prune %s"
-msgstr ""
+msgstr "清除远端 %s"
 
 #: lib/transport.tcl:19
 #, tcl-format
 msgid "Pruning tracking branches deleted from %s"
-msgstr ""
+msgstr "清除"
 
 #: lib/transport.tcl:25 lib/transport.tcl:71
 #, tcl-format
 msgid "push %s"
-msgstr ""
+msgstr "上传 %s"
 
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
-msgstr ""
+msgstr "上传改动到 %s"
 
 #: lib/transport.tcl:72
 #, tcl-format
 msgid "Pushing %s %s to %s"
-msgstr ""
+msgstr "上传 %s %s 到 %s"
 
 #: lib/transport.tcl:89
-#, fuzzy
 msgid "Push Branches"
-msgstr "分支"
+msgstr "上传分支"
 
 #: lib/transport.tcl:103
-#, fuzzy
 msgid "Source Branches"
-msgstr "当前分支:"
+msgstr "源端分支:"
 
 #: lib/transport.tcl:120
-#, fuzzy
 msgid "Destination Repository"
-msgstr "版本树"
+msgstr "目标版本库"
 
 #: lib/transport.tcl:158
 msgid "Transfer Options"
-msgstr ""
+msgstr "传输选项"
 
 #: lib/transport.tcl:160
 msgid "Force overwrite existing branch (may discard changes)"
-msgstr ""
+msgstr "强制覆盖已有的分支 (可能会丢失改动)"
 
 #: lib/transport.tcl:164
 msgid "Use thin pack (for slow network connections)"
-msgstr ""
+msgstr "使用 thin pack (适用于低速网络连接)"
 
 #: lib/transport.tcl:168
 msgid "Include tags"
-msgstr ""
-
-#~ msgid "Add To Commit"
-#~ msgstr "添加到本次提交"
-
-#~ msgid "Add Existing To Commit"
-#~ msgstr "添加默认修改文件"
-
-#~ msgid "Unstaged Changes (Will Not Be Committed)"
-#~ msgstr "不被提交的修改"
-
-#~ msgid "Add Existing"
-#~ msgstr "添加默认修改文件"
-
-#, fuzzy
-#~ msgid "Push to %s..."
-#~ msgstr "上传..."
+msgstr "包含标签"
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 3e4452b..6f91c8f 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -24,8 +24,6 @@
 fqgitdir="$GIT_DIR"
 local="`git config --bool --get instaweb.local`"
 httpd="`git config --get instaweb.httpd`"
-browser="`git config --get instaweb.browser`"
-test -z "$browser" && browser="`git config --get web.browser`"
 port=`git config --get instaweb.port`
 module_path="`git config --get instaweb.modulepath`"
 
@@ -36,9 +34,6 @@
 # if installed, it doesn't need further configuration (module_path)
 test -z "$httpd" && httpd='lighttpd -f'
 
-# probably the most popular browser among gitweb users
-test -z "$browser" && browser='firefox'
-
 # any untaken local port will do...
 test -z "$port" && port=1234
 
@@ -274,14 +269,11 @@
 	;;
 esac
 
-init_browser_path() {
-	browser_path="`git config browser.$1.path`"
-	test -z "$browser_path" && browser_path="$1"
-}
-
 start_httpd
 url=http://127.0.0.1:$port
-test -n "$browser" && {
-	init_browser_path "$browser"
-	"$browser_path" $url
-} || echo $url
+
+if test -n "$browser"; then
+	git web--browse -b "$browser" $url || echo $url
+else
+	git web--browse -c "instaweb.browser" $url || echo $url
+fi
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index 9ee3f80..e1eb963 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -48,10 +48,11 @@
 	;;
 "..$3")
 	echo "Adding $4"
-	test -f "$4" || {
+	if test -f "$4"
+	then
 		echo "ERROR: untracked $4 is overwritten by the merge."
 		exit 1
-	}
+	fi
 	git update-index --add --cacheinfo "$7" "$3" "$4" &&
 		exec git checkout-index -u -f -- "$4"
 	;;
diff --git a/git-merge.sh b/git-merge.sh
index 03cd398..7dbbb1d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -71,7 +71,7 @@
 squash_message () {
 	echo Squashed commit of the following:
 	echo
-	git log --no-merges ^"$head" $remoteheads
+	git log --no-merges --pretty=medium ^"$head" $remoteheads
 }
 
 finish () {
diff --git a/git-mergetool.sh b/git-mergetool.sh
index cbbb707..5c86f69 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -34,7 +34,7 @@
 
 cleanup_temp_files () {
     if test "$1" = --save-backup ; then
-	mv -- "$BACKUP" "$path.orig"
+	mv -- "$BACKUP" "$MERGED.orig"
 	rm -f -- "$LOCAL" "$REMOTE" "$BASE"
     else
 	rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -67,14 +67,14 @@
 	read ans
 	case "$ans" in
 	    [lL]*)
-		git checkout-index -f --stage=2 -- "$path"
-		git add -- "$path"
+		git checkout-index -f --stage=2 -- "$MERGED"
+		git add -- "$MERGED"
 		cleanup_temp_files --save-backup
 		return
 		;;
 	    [rR]*)
-		git checkout-index -f --stage=3 -- "$path"
-		git add -- "$path"
+		git checkout-index -f --stage=3 -- "$MERGED"
+		git add -- "$MERGED"
 		cleanup_temp_files --save-backup
 		return
 		;;
@@ -95,12 +95,12 @@
 	read ans
 	case "$ans" in
 	    [mMcC]*)
-		git add -- "$path"
+		git add -- "$MERGED"
 		cleanup_temp_files --save-backup
 		return
 		;;
 	    [dD]*)
-		git rm -- "$path" > /dev/null
+		git rm -- "$MERGED" > /dev/null
 		cleanup_temp_files
 		return
 		;;
@@ -112,11 +112,11 @@
 }
 
 check_unchanged () {
-    if test "$path" -nt "$BACKUP" ; then
+    if test "$MERGED" -nt "$BACKUP" ; then
 	status=0;
     else
 	while true; do
-	    echo "$path seems unchanged."
+	    echo "$MERGED seems unchanged."
 	    printf "Was the merge successful? [y/n] "
 	    read answer < /dev/tty
 	    case "$answer" in
@@ -127,50 +127,38 @@
     fi
 }
 
-save_backup () {
-    if test "$status" -eq 0; then
-	mv -- "$BACKUP" "$path.orig"
-    fi
-}
-
-remove_backup () {
-    if test "$status" -eq 0; then
-	rm "$BACKUP"
-    fi
-}
-
 merge_file () {
-    path="$1"
+    MERGED="$1"
 
-    f=`git ls-files -u -- "$path"`
+    f=`git ls-files -u -- "$MERGED"`
     if test -z "$f" ; then
-	if test ! -f "$path" ; then
-	    echo "$path: file not found"
+	if test ! -f "$MERGED" ; then
+	    echo "$MERGED: file not found"
 	else
-	    echo "$path: file does not need merging"
+	    echo "$MERGED: file does not need merging"
 	fi
 	exit 1
     fi
 
-    ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
-    BACKUP="$path.BACKUP.$ext"
-    LOCAL="$path.LOCAL.$ext"
-    REMOTE="$path.REMOTE.$ext"
-    BASE="$path.BASE.$ext"
+    ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+    BACKUP="$MERGED.BACKUP.$ext"
+    LOCAL="$MERGED.LOCAL.$ext"
+    REMOTE="$MERGED.REMOTE.$ext"
+    BASE="$MERGED.BASE.$ext"
 
-    mv -- "$path" "$BACKUP"
-    cp -- "$BACKUP" "$path"
+    mv -- "$MERGED" "$BACKUP"
+    cp -- "$BACKUP" "$MERGED"
 
-    base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
-    local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
-    remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
+    base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
+    local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
+    remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
 
-    base_present   && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null
-    local_present  && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null
-    remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null
+    base_present   && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
+    local_present  && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
+    remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
 
     if test -z "$local_mode" -o -z "$remote_mode"; then
-	echo "Deleted merge conflict for '$path':"
+	echo "Deleted merge conflict for '$MERGED':"
 	describe_file "$local_mode" "local" "$LOCAL"
 	describe_file "$remote_mode" "remote" "$REMOTE"
 	resolve_deleted_merge
@@ -178,14 +166,14 @@
     fi
 
     if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
-	echo "Symbolic link merge conflict for '$path':"
+	echo "Symbolic link merge conflict for '$MERGED':"
 	describe_file "$local_mode" "local" "$LOCAL"
 	describe_file "$remote_mode" "remote" "$REMOTE"
 	resolve_symlink_merge
 	return
     fi
 
-    echo "Normal merge conflict for '$path':"
+    echo "Normal merge conflict for '$MERGED':"
     describe_file "$local_mode" "local" "$LOCAL"
     describe_file "$remote_mode" "remote" "$REMOTE"
     printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
@@ -194,36 +182,32 @@
     case "$merge_tool" in
 	kdiff3)
 	    if base_present ; then
-		("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
-		    -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+		("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
+		    -o "$MERGED" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
 	    else
-		("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
-		    -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+		("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
+		    -o "$MERGED" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
 	    fi
 	    status=$?
-	    remove_backup
 	    ;;
 	tkdiff)
 	    if base_present ; then
-		"$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+		"$merge_tool_path" -a "$BASE" -o "$MERGED" -- "$LOCAL" "$REMOTE"
 	    else
-		"$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
+		"$merge_tool_path" -o "$MERGED" -- "$LOCAL" "$REMOTE"
 	    fi
 	    status=$?
-	    save_backup
 	    ;;
 	meld|vimdiff)
 	    touch "$BACKUP"
-	    "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
+	    "$merge_tool_path" -- "$LOCAL" "$MERGED" "$REMOTE"
 	    check_unchanged
-	    save_backup
 	    ;;
 	gvimdiff)
-		touch "$BACKUP"
-		"$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
-		check_unchanged
-		save_backup
-		;;
+	    touch "$BACKUP"
+	    "$merge_tool_path" -f -- "$LOCAL" "$MERGED" "$REMOTE"
+	    check_unchanged
+	    ;;
 	xxdiff)
 	    touch "$BACKUP"
 	    if base_present ; then
@@ -231,53 +215,68 @@
 		    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 		    -R 'Accel.Search: "Ctrl+F"' \
 		    -R 'Accel.SearchForward: "Ctrl-G"' \
-		    --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+		    --merged-file "$MERGED" -- "$LOCAL" "$BASE" "$REMOTE"
 	    else
 		"$merge_tool_path" -X --show-merged-pane \
 		    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
 		    -R 'Accel.Search: "Ctrl+F"' \
 		    -R 'Accel.SearchForward: "Ctrl-G"' \
-		    --merged-file "$path" -- "$LOCAL" "$REMOTE"
+		    --merged-file "$MERGED" -- "$LOCAL" "$REMOTE"
 	    fi
 	    check_unchanged
-	    save_backup
 	    ;;
 	opendiff)
 	    touch "$BACKUP"
 	    if base_present; then
-		"$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
+		"$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
 	    else
-		"$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
+		"$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
 	    fi
 	    check_unchanged
-	    save_backup
 	    ;;
 	ecmerge)
 	    touch "$BACKUP"
 	    if base_present; then
-		"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
+		"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
 	    else
-		"$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
+		"$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
 	    fi
 	    check_unchanged
-	    save_backup
 	    ;;
 	emerge)
 	    if base_present ; then
-		"$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
+		"$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
 	    else
-		"$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
+		"$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
 	    fi
 	    status=$?
-	    save_backup
+	    ;;
+	*)
+	    if test -n "$merge_tool_cmd"; then
+		if test "$merge_tool_trust_exit_code" = "false"; then
+		    touch "$BACKUP"
+		    ( eval $merge_tool_cmd )
+		    check_unchanged
+		else
+		    ( eval $merge_tool_cmd )
+		    status=$?
+		fi
+	    fi
 	    ;;
     esac
     if test "$status" -ne 0; then
-	echo "merge of $path failed" 1>&2
-	mv -- "$BACKUP" "$path"
+	echo "merge of $MERGED failed" 1>&2
+	mv -- "$BACKUP" "$MERGED"
 	exit 1
     fi
-    git add -- "$path"
+
+    if test "$merge_keep_backup" = "true"; then
+	mv -- "$BACKUP" "$MERGED.orig"
+    else
+	rm -- "$BACKUP"
+    fi
+
+    git add -- "$MERGED"
     cleanup_temp_files
 }
 
@@ -309,12 +308,20 @@
     shift
 done
 
+valid_custom_tool()
+{
+    merge_tool_cmd="$(git config mergetool.$1.cmd)"
+    test -n "$merge_tool_cmd"
+}
+
 valid_tool() {
 	case "$1" in
 		kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
 			;; # happy
 		*)
-			return 1
+			if ! valid_custom_tool "$1"; then
+				return 1
+			fi
 			;;
 	esac
 }
@@ -380,10 +387,16 @@
 
     init_merge_tool_path "$merge_tool"
 
-    if ! type "$merge_tool_path" > /dev/null 2>&1; then
+    merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+
+    if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
         echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
         exit 1
     fi
+
+    if ! test -z "$merge_tool_cmd"; then
+        merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
+    fi
 fi
 
 
diff --git a/git-pull.sh b/git-pull.sh
index 46da0f4..3ce32b5 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -174,6 +174,7 @@
 
 merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
-	exec git-rebase --onto $merge_head ${oldremoteref:-$merge_head}
+	exec git-rebase $strategy_args --onto $merge_head \
+	${oldremoteref:-$merge_head}
 exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
 	"$merge_name" HEAD $merge_head
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index c2bedd6..8aa7371 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -78,8 +78,8 @@
 	sed -e 1q < "$TODO" >> "$DONE"
 	sed -e 1d < "$TODO" >> "$TODO".new
 	mv -f "$TODO".new "$TODO"
-	count=$(($(grep -ve '^$' -e '^#' < "$DONE" | wc -l)))
-	total=$(($count+$(grep -ve '^$' -e '^#' < "$TODO" | wc -l)))
+	count=$(grep -c '^[^#]' < "$DONE")
+	total=$(($count+$(grep -c '^[^#]' < "$TODO")))
 	if test "$last_count" != "$count"
 	then
 		last_count=$count
@@ -110,7 +110,7 @@
 }
 
 has_action () {
-	grep -vqe '^$' -e '^#' "$1"
+	grep '^[^#]' "$1" >/dev/null
 }
 
 pick_one () {
@@ -218,7 +218,7 @@
 make_squash_message () {
 	if test -f "$SQUASH_MSG"; then
 		COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \
-			< "$SQUASH_MSG" | tail -n 1)+1))
+			< "$SQUASH_MSG" | sed -ne '$p')+1))
 		echo "# This is a combination of $COUNT commits."
 		sed -e 1d -e '2,/^./{
 			/^$/d
diff --git a/git-rebase.sh b/git-rebase.sh
index bc0c1c4..60c458f 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -18,8 +18,7 @@
 git rebase --abort instead.
 
 Note that if <branch> is not specified on the command line, the
-currently checked out branch is used.  You must be in the top
-directory of your project to start (or continue) a rebase.
+currently checked out branch is used.
 
 Example:       git-rebase master~1 topic
 
@@ -376,7 +375,7 @@
 if test -z "$do_merge"
 then
 	git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
-	git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
+	git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
 	move_to_original_branch
 	ret=$?
 	test 0 != $ret -a -d .dotest &&
diff --git a/git-send-email.perl b/git-send-email.perl
index e5d67f1..c6bba47 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -24,8 +24,6 @@
 use Term::ANSIColor;
 use Git;
 
-$SIG{INT} = sub { print color("reset"), "\n"; exit };
-
 package FakeTerm;
 sub new {
 	my ($class, $reason) = @_;
@@ -88,6 +86,12 @@
 
    --smtp-ssl     If set, connects to the SMTP server using SSL.
 
+   --suppress-cc  Suppress the specified category of auto-CC.  The category
+		  can be one of 'author' for the patch author, 'self' to
+		  avoid copying yourself, 'sob' for Signed-off-by lines,
+		  'cccmd' for the output of the cccmd, or 'all' to suppress
+		  all of these.
+
    --suppress-from Suppress sending emails to yourself. Defaults to off.
 
    --thread       Specify that the "In-Reply-To:" header should be set on all
@@ -157,7 +161,7 @@
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-	$initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
+	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
 
 my $envelope_sender;
 
@@ -179,15 +183,16 @@
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
-my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_authpass, $smtp_ssl);
+my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_ssl);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
 my ($no_validate);
+my (@suppress_cc);
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
     "chainreplyto" => [\$chain_reply_to, 1],
-    "suppressfrom" => [\$suppress_from, 0],
-    "signedoffcc" => [\$signed_off_cc, 1],
+    "suppressfrom" => [\$suppress_from, undef],
+    "signedoffcc" => [\$signed_off_cc, undef],
     "smtpssl" => [\$smtp_ssl, 0],
 );
 
@@ -201,8 +206,32 @@
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
     "aliasesfile" => \@alias_files,
+    "suppresscc" => \@suppress_cc,
 );
 
+# Handle Uncouth Termination
+sub signal_handler {
+
+	# Make text normal
+	print color("reset"), "\n";
+
+	# SMTP password masked
+	system "stty echo";
+
+	# tmp files from --compose
+	if (-e $compose_filename) {
+		print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+	}
+	if (-e ($compose_filename . ".final")) {
+		print "'$compose_filename.final' contains the composed email.\n"
+	}
+
+	exit;
+};
+
+$SIG{TERM} = \&signal_handler;
+$SIG{INT}  = \&signal_handler;
+
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
 
@@ -216,13 +245,14 @@
 		    "smtp-server=s" => \$smtp_server,
 		    "smtp-server-port=s" => \$smtp_server_port,
 		    "smtp-user=s" => \$smtp_authuser,
-		    "smtp-pass=s" => \$smtp_authpass,
+		    "smtp-pass:s" => \$smtp_authpass,
 		    "smtp-ssl!" => \$smtp_ssl,
 		    "identity=s" => \$identity,
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
 		    "suppress-from!" => \$suppress_from,
+		    "suppress-cc=s" => \@suppress_cc,
 		    "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
 		    "dry-run" => \$dry_run,
 		    "envelope-sender=s" => \$envelope_sender,
@@ -268,6 +298,35 @@
 	${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
 }
 
+# Set CC suppressions
+my(%suppress_cc);
+if (@suppress_cc) {
+	foreach my $entry (@suppress_cc) {
+		die "Unknown --suppress-cc field: '$entry'\n"
+			unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/;
+		$suppress_cc{$entry} = 1;
+	}
+}
+
+if ($suppress_cc{'all'}) {
+	foreach my $entry (qw (ccmd cc author self sob)) {
+		$suppress_cc{$entry} = 1;
+	}
+	delete $suppress_cc{'all'};
+}
+
+# If explicit old-style ones are specified, they trump --suppress-cc.
+$suppress_cc{'self'} = $suppress_from if defined $suppress_from;
+$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
+
+# Debugging, print out the suppressions.
+if (0) {
+	print "suppressions:\n";
+	foreach my $entry (keys %suppress_cc) {
+		printf "  %-5s -> $suppress_cc{$entry}\n", $entry;
+	}
+}
+
 my ($repoauthor) = $repo->ident_person('author');
 my ($repocommitter) = $repo->ident_person('committer');
 
@@ -357,9 +416,12 @@
 my $prompting = 0;
 if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter;
-	do {
+
+	while (1) {
 		$_ = $term->readline("Who should the emails appear to be from? [$sender] ");
-	} while (!defined $_);
+		last if defined $_;
+		print "\n";
+	}
 
 	$sender = $_ if ($_);
 	print "Emails will be sent from: ", $sender, "\n";
@@ -367,10 +429,14 @@
 }
 
 if (!@to) {
-	do {
-		$_ = $term->readline("Who should the emails be sent to? ",
-				"");
-	} while (!defined $_);
+
+
+	while (1) {
+		$_ = $term->readline("Who should the emails be sent to? ", "");
+		last if defined $_;
+		print "\n";
+	}
+
 	my $to = $_;
 	push @to, split /,/, $to;
 	$prompting++;
@@ -392,19 +458,22 @@
 @bcclist = expand_aliases(@bcclist);
 
 if (!defined $initial_subject && $compose) {
-	do {
-		$_ = $term->readline("What subject should the initial email start with? ",
-			$initial_subject);
-	} while (!defined $_);
+	while (1) {
+		$_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
+		last if defined $_;
+		print "\n";
+	}
+
 	$initial_subject = $_;
 	$prompting++;
 }
 
 if ($thread && !defined $initial_reply_to && $prompting) {
-	do {
-		$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ",
-			$initial_reply_to);
-	} while (!defined $_);
+	while (1) {
+		$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
+		last if defined $_;
+		print "\n";
+	}
 
 	$initial_reply_to = $_;
 }
@@ -449,16 +518,40 @@
 	open(C,"<",$compose_filename)
 		or die "Failed to open $compose_filename : " . $!;
 
+	my $need_8bit_cte = file_has_nonascii($compose_filename);
+	my $in_body = 0;
 	while(<C>) {
 		next if m/^GIT: /;
+		if (!$in_body && /^\n$/) {
+			$in_body = 1;
+			if ($need_8bit_cte) {
+				print C2 "MIME-Version: 1.0\n",
+					 "Content-Type: text/plain; ",
+					   "charset=utf-8\n",
+					 "Content-Transfer-Encoding: 8bit\n";
+			}
+		}
+		if (!$in_body && /^MIME-Version:/i) {
+			$need_8bit_cte = 0;
+		}
+		if (!$in_body && /^Subject: ?(.*)/i) {
+			my $subject = $1;
+			$_ = "Subject: " .
+				($subject =~ /[^[:ascii:]]/ ?
+				 quote_rfc2047($subject) :
+				 $subject) .
+				"\n";
+		}
 		print C2 $_;
 	}
 	close(C);
 	close(C2);
 
-	do {
+	while (1) {
 		$_ = $term->readline("Send this email? (y|n) ");
-	} while (!defined $_);
+		last if defined $_;
+		print "\n";
+	}
 
 	if (uc substr($_,0,1) ne 'Y') {
 		cleanup_compose_files();
@@ -539,6 +632,14 @@
 	return wantarray ? ($_, $encoding) : $_;
 }
 
+sub quote_rfc2047 {
+	local $_ = shift;
+	my $encoding = shift || 'utf-8';
+	s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
+	s/(.*)/=\?$encoding\?q\?$1\?=/;
+	return $_;
+}
+
 # use the simplest quoting being able to handle the recipient
 sub sanitize_address
 {
@@ -556,13 +657,12 @@
 
 	# rfc2047 is needed if a non-ascii char is included
 	if ($recipient_name =~ /[^[:ascii:]]/) {
-		$recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
-		$recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/;
+		$recipient_name = quote_rfc2047($recipient_name);
 	}
 
 	# double quotes are needed if specials or CTLs are included
 	elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
-		$recipient_name =~ s/(["\\\r])/\\$1/;
+		$recipient_name =~ s/(["\\\r])/\\$1/g;
 		$recipient_name = "\"$recipient_name\"";
 	}
 
@@ -650,9 +750,26 @@
 			die "Unable to initialize SMTP properly.  Is there something wrong with your config?";
 		}
 
-		if ((defined $smtp_authuser) && (defined $smtp_authpass)) {
+		if (defined $smtp_authuser) {
+
+			if (!defined $smtp_authpass) {
+
+				system "stty -echo";
+
+				do {
+					print "Password: ";
+					$_ = <STDIN>;
+					print "\n";
+				} while (!defined $_);
+
+				chomp($smtp_authpass = $_);
+
+				system "stty echo";
+			}
+
 			$auth ||= $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message;
 		}
+
 		$smtp->mail( $raw_from ) or die $smtp->message;
 		$smtp->to( @recipients ) or die $smtp->message;
 		$smtp->data or die $smtp->message;
@@ -714,11 +831,14 @@
 
 				} elsif (/^(Cc|From):\s+(.*)$/) {
 					if (unquote_rfc2047($2) eq $sender) {
-						next if ($suppress_from);
+						next if ($suppress_cc{'self'});
 					}
 					elsif ($1 eq 'From') {
 						($author, $author_encoding)
 						  = unquote_rfc2047($2);
+						next if ($suppress_cc{'author'});
+					} else {
+						next if ($suppress_cc{'cc'});
 					}
 					printf("(mbox) Adding cc: %s from line '%s'\n",
 						$2, $_) unless $quiet;
@@ -745,7 +865,7 @@
 				# line 2 = subject
 				# So let's support that, too.
 				$input_format = 'lots';
-				if (@cc == 0) {
+				if (@cc == 0 && !$suppress_cc{'cc'}) {
 					printf("(non-mbox) Adding cc: %s from line '%s'\n",
 						$_, $_) unless $quiet;
 
@@ -762,10 +882,12 @@
 			}
 		} else {
 			$message .=  $_;
-			if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) {
+			if (/^(Signed-off-by|Cc): (.*)$/i) {
+				next if ($suppress_cc{'sob'});
+				chomp;
 				my $c = $2;
 				chomp $c;
-				next if ($c eq $sender and $suppress_from);
+				next if ($c eq $sender and $suppress_cc{'self'});
 				push @cc, $c;
 				printf("(sob) Adding cc: %s from line '%s'\n",
 					$c, $_) unless $quiet;
@@ -774,7 +896,7 @@
 	}
 	close F;
 
-	if (defined $cc_cmd) {
+	if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
 		open(F, "$cc_cmd $t |")
 			or die "(cc-cmd) Could not execute '$cc_cmd'";
 		while(<F>) {
@@ -863,3 +985,13 @@
 	}
 	return undef;
 }
+
+sub file_has_nonascii {
+	my $fn = shift;
+	open(my $fh, '<', $fn)
+		or die "unable to open $fn: $!\n";
+	while (my $line = <$fh>) {
+		return 1 if $line =~ /[^[:ascii:]]/;
+	}
+	return 0;
+}
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index f388275..a44b1c7 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -127,20 +127,14 @@
 # if we require to be in a git repository.
 if test -z "$NONGIT_OK"
 then
+	GIT_DIR=$(git rev-parse --git-dir) || exit
 	if [ -z "$SUBDIRECTORY_OK" ]
 	then
-		: ${GIT_DIR=.git}
 		test -z "$(git rev-parse --show-cdup)" || {
 			exit=$?
 			echo >&2 "You need to run this command from the toplevel of the working tree."
 			exit $exit
 		}
-	else
-		GIT_DIR=$(git rev-parse --git-dir) || {
-		    exit=$?
-		    echo >&2 "Failed to find a valid git directory."
-		    exit $exit
-		}
 	fi
 	test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
 		echo >&2 "Unable to determine absolute path of git directory"
diff --git a/git-stash.sh b/git-stash.sh
index b00f888..c2b6820 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Copyright (c) 2007, Nanako Shiraishi
 
-USAGE='[  | save | list | show | apply | clear | create ]'
+USAGE='[  | save | list | show | apply | clear | drop | pop | create ]'
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -196,6 +196,28 @@
 	fi
 }
 
+drop_stash () {
+	have_stash || die 'No stash entries to drop'
+
+	if test $# = 0
+	then
+		set x "$ref_stash@{0}"
+		shift
+	fi
+	# Verify supplied argument looks like a stash entry
+	s=$(git rev-parse --revs-only --no-flags "$@") &&
+	git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
+	git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
+	git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
+		die "$*: not a valid stashed state"
+
+	git reflog delete --updateref --rewrite "$@" &&
+		echo "Dropped $* ($s)" || die "$*: Could not drop stash entry"
+
+	# clear_stash if we just dropped the last stash entry
+	git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
+}
+
 # Main command set
 case "$1" in
 list)
@@ -230,6 +252,18 @@
 	fi
 	create_stash "$*" && echo "$w_commit"
 	;;
+drop)
+	shift
+	drop_stash "$@"
+	;;
+pop)
+	shift
+	if apply_stash "$@"
+	then
+		test -z "$unstash_index" || shift
+		drop_stash "$@"
+	fi
+	;;
 *)
 	if test $# -eq 0
 	then
diff --git a/git-submodule.sh b/git-submodule.sh
index 67d3224..a745e42 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -4,7 +4,9 @@
 #
 # Copyright (c) 2007 Lars Hjemli
 
-USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
+USAGE="[--quiet] [--cached] \
+[add <repo> [-b branch]|status|init|update|summary [-n|--summary-limit <n>] [<commit>]] \
+[--] [<path>...]"
 OPTIONS_SPEC=
 . git-sh-setup
 require_work_tree
@@ -153,20 +155,6 @@
 		usage
 	fi
 
-	case "$repo" in
-	./*|../*)
-		# dereference source url relative to parent's url
-		realrepo="$(resolve_relative_url $repo)" ;;
-	*)
-		# Turn the source into an absolute path if
-		# it is local
-		if base=$(get_repo_base "$repo"); then
-			repo="$base"
-		fi
-		realrepo=$repo
-		;;
-	esac
-
 	# Guess path from repo if not specified or strip trailing slashes
 	if test -z "$path"; then
 		path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
@@ -174,15 +162,39 @@
 		path=$(echo "$path" | sed -e 's|/*$||')
 	fi
 
-	test -e "$path" &&
-	die "'$path' already exists"
-
 	git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
 	die "'$path' already exists in the index"
 
-	module_clone "$path" "$realrepo" || exit
-	(unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
-	die "Unable to checkout submodule '$path'"
+	# perhaps the path exists and is already a git repo, else clone it
+	if test -e "$path"
+	then
+		if test -d "$path/.git" &&
+		test "$(unset GIT_DIR; cd $path; git rev-parse --git-dir)" = ".git"
+		then
+			echo "Adding existing repo at '$path' to the index"
+		else
+			die "'$path' already exists and is not a valid git repo"
+		fi
+	else
+		case "$repo" in
+		./*|../*)
+			# dereference source url relative to parent's url
+			realrepo="$(resolve_relative_url $repo)" ;;
+		*)
+			# Turn the source into an absolute path if
+			# it is local
+			if base=$(get_repo_base "$repo"); then
+				repo="$base"
+			fi
+			realrepo=$repo
+			;;
+		esac
+
+		module_clone "$path" "$realrepo" || exit
+		(unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+		die "Unable to checkout submodule '$path'"
+	fi
+
 	git add "$path" ||
 	die "Failed to add submodule '$path'"
 
@@ -220,7 +232,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep -e '^160000 ' |
+	git ls-files --stage -- "$@" | grep '^160000 ' |
 	while read mode sha1 stage path
 	do
 		# Skip already registered paths
@@ -274,7 +286,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep -e '^160000 ' |
+	git ls-files --stage -- "$@" | grep '^160000 ' |
 	while read mode sha1 stage path
 	do
 		name=$(module_name "$path") || exit
@@ -315,12 +327,181 @@
 		cd "$1" && {
 			git describe "$2" 2>/dev/null ||
 			git describe --tags "$2" 2>/dev/null ||
-			git describe --contains --tags "$2"
+			git describe --contains "$2" 2>/dev/null ||
+			git describe --all --always "$2"
 		}
 	) )
 	test -z "$revname" || revname=" ($revname)"
 }
+#
+# Show commit summary for submodules in index or working tree
+#
+# If '--cached' is given, show summary between index and given commit,
+# or between working tree and given commit
+#
+# $@ = [commit (default 'HEAD'),] requested paths (default all)
+#
+cmd_summary() {
+	summary_limit=-1
 
+	# parse $args after "submodule ... summary".
+	while test $# -ne 0
+	do
+		case "$1" in
+		--cached)
+			cached="$1"
+			;;
+		-n|--summary-limit)
+			if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
+			then
+				:
+			else
+				usage
+			fi
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	test $summary_limit = 0 && return
+
+	if rev=$(git rev-parse --verify "$1^0" 2>/dev/null)
+	then
+		head=$rev
+		shift
+	else
+		head=HEAD
+	fi
+
+	cd_to_toplevel
+	# Get modified modules cared by user
+	modules=$(git diff-index $cached --raw $head -- "$@" |
+		grep -e '^:160000' -e '^:[0-7]* 160000' |
+		while read mod_src mod_dst sha1_src sha1_dst status name
+		do
+			# Always show modules deleted or type-changed (blob<->module)
+			test $status = D -o $status = T && echo "$name" && continue
+			# Also show added or modified modules which are checked out
+			GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
+			echo "$name"
+		done
+	)
+
+	test -n "$modules" &&
+	git diff-index $cached --raw $head -- $modules |
+	grep -e '^:160000' -e '^:[0-7]* 160000' |
+	cut -c2- |
+	while read mod_src mod_dst sha1_src sha1_dst status name
+	do
+		if test -z "$cached" &&
+			test $sha1_dst = 0000000000000000000000000000000000000000
+		then
+			case "$mod_dst" in
+			160000)
+				sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
+				;;
+			100644 | 100755 | 120000)
+				sha1_dst=$(git hash-object $name)
+				;;
+			000000)
+				;; # removed
+			*)
+				# unexpected type
+				echo >&2 "unexpected mode $mod_dst"
+				continue ;;
+			esac
+		fi
+		missing_src=
+		missing_dst=
+
+		test $mod_src = 160000 &&
+		! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_src^0 >/dev/null 2>&1 &&
+		missing_src=t
+
+		test $mod_dst = 160000 &&
+		! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_dst^0 >/dev/null 2>&1 &&
+		missing_dst=t
+
+		total_commits=
+		case "$missing_src,$missing_dst" in
+		t,)
+			errmsg="  Warn: $name doesn't contain commit $sha1_src"
+			;;
+		,t)
+			errmsg="  Warn: $name doesn't contain commit $sha1_dst"
+			;;
+		t,t)
+			errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+			;;
+		*)
+			errmsg=
+			total_commits=$(
+			if test $mod_src = 160000 -a $mod_dst = 160000
+			then
+				range="$sha1_src...$sha1_dst"
+			elif test $mod_src = 160000
+			then
+				range=$sha1_src
+			else
+				range=$sha1_dst
+			fi
+			GIT_DIR="$name/.git" \
+			git log --pretty=oneline --first-parent $range | wc -l
+			)
+			total_commits=" ($(($total_commits + 0)))"
+			;;
+		esac
+
+		sha1_abbr_src=$(echo $sha1_src | cut -c1-7)
+		sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
+		if test $status = T
+		then
+			if test $mod_dst = 160000
+			then
+				echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
+			else
+				echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
+			fi
+		else
+			echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
+		fi
+		if test -n "$errmsg"
+		then
+			# Don't give error msg for modification whose dst is not submodule
+			# i.e. deleted or changed to blob
+			test $mod_dst = 160000 && echo "$errmsg"
+		else
+			if test $mod_src = 160000 -a $mod_dst = 160000
+			then
+				limit=
+				test $summary_limit -gt 0 && limit="-$summary_limit"
+				GIT_DIR="$name/.git" \
+				git log $limit --pretty='format:  %m %s' \
+				--first-parent $sha1_src...$sha1_dst
+			elif test $mod_dst = 160000
+			then
+				GIT_DIR="$name/.git" \
+				git log --pretty='format:  > %s' -1 $sha1_dst
+			else
+				GIT_DIR="$name/.git" \
+				git log --pretty='format:  < %s' -1 $sha1_src
+			fi
+			echo
+		fi
+		echo
+	done
+}
 #
 # List all submodules, prefixed with:
 #  - submodule not initialized
@@ -357,7 +538,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep -e '^160000 ' |
+	git ls-files --stage -- "$@" | grep '^160000 ' |
 	while read mode sha1 stage path
 	do
 		name=$(module_name "$path") || exit
@@ -391,7 +572,7 @@
 while test $# != 0 && test -z "$command"
 do
 	case "$1" in
-	add | init | update | status)
+	add | init | update | status | summary)
 		command=$1
 		;;
 	-q|--quiet)
@@ -406,7 +587,7 @@
 		branch="$2"; shift
 		;;
 	--cached)
-		cached=1
+		cached="$1"
 		;;
 	--)
 		break
@@ -430,8 +611,8 @@
 	usage
 fi
 
-# "--cached" is accepted only by "status"
-if test -n "$cached" && test "$command" != status
+# "--cached" is accepted only by "status" and "summary"
+if test -n "$cached" && test "$command" != status -a "$command" != summary
 then
 	usage
 fi
diff --git a/git-svn.perl b/git-svn.perl
index 49dd806..b151049 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -186,6 +186,9 @@
 		    "Show info about the latest SVN revision
 		     on the current branch",
 		    { 'url' => \$_url, } ],
+	'blame' => [ \&Git::SVN::Log::cmd_blame,
+	            "Show what revision and author last modified each line of a file",
+	            {} ],
 );
 
 my $cmd;
@@ -1117,7 +1120,7 @@
 
 sub working_head_info {
 	my ($head, $refs) = @_;
-	my @args = ('log', '--no-color', '--first-parent');
+	my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
 	my ($fh, $ctx) = command_output_pipe(@args, $head);
 	my $hash;
 	my %max;
@@ -1249,7 +1252,8 @@
 use File::Copy qw/copy/;
 use IPC::Open3;
 
-my $_repack_nr;
+my ($_gc_nr, $_gc_period);
+
 # properties that we do not log:
 my %SKIP_PROP;
 BEGIN {
@@ -1410,9 +1414,10 @@
 }
 
 sub init_vars {
-	$_repack = 1000 unless (defined $_repack && $_repack > 0);
-	$_repack_nr = $_repack;
-	$_repack_flags ||= '-d';
+	$_gc_nr = $_gc_period = 1000;
+	if (defined $_repack || defined $_repack_flags) {
+	       warn "Repack options are obsolete; they have no effect.\n";
+	}
 }
 
 sub verify_remotes_sanity {
@@ -1895,7 +1900,7 @@
 
 	foreach (sort keys %$dirent) {
 		next if $dirent->{$_}->{kind} != $SVN::Node::dir;
-		$self->prop_walk($path . '/' . $_, $rev, $sub);
+		$self->prop_walk($p . $_, $rev, $sub);
 	}
 }
 
@@ -2103,6 +2108,10 @@
 	}
 }
 
+sub gc {
+	command_noisy('gc', '--auto');
+};
+
 sub do_git_commit {
 	my ($self, $log_entry) = @_;
 	my $lr = $self->last_rev;
@@ -2156,12 +2165,9 @@
 		                   0, $self->svm_uuid);
 	}
 	print " = $commit ($self->{ref_id})\n";
-	if ($_repack && (--$_repack_nr == 0)) {
-		$_repack_nr = $_repack;
-		# repack doesn't use any arguments with spaces in them, does it?
-		print "Running git repack $_repack_flags ...\n";
-		command_noisy('repack', split(/\s+/, $_repack_flags));
-		print "Done repacking\n";
+	if (--$_gc_nr == 0) {
+		$_gc_nr = $_gc_period;
+		gc();
 	}
 	return $commit;
 }
@@ -2233,7 +2239,13 @@
 		# just grow a tail if we're not unique enough :x
 		$ref_id .= '-' while find_ref($ref_id);
 		print STDERR "Initializing parent: $ref_id\n";
-		$gs = Git::SVN->init($new_url, '', $ref_id, $ref_id, 1);
+		my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
+		if ($u =~ s#^\Q$url\E(/|$)##) {
+			$p = $u;
+			$u = $url;
+			$repo_id = $self->{repo_id};
+		}
+		$gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
 	}
 	my ($r0, $parent) = $gs->find_rev_before($r, 1);
 	if (!defined $r0 || !defined $parent) {
@@ -3657,7 +3669,7 @@
 	my ($uri) = @_;
 	my @tmp;
 	foreach (split m{/}, $uri) {
-		s/([^\w.-])/sprintf("%%%02X",ord($1))/eg;
+		s/([^\w.%-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
 		push @tmp, $_;
 	}
 	join('/', @tmp);
@@ -3994,6 +4006,7 @@
 		$max += $inc;
 		$max = $head if ($max > $head);
 	}
+	Git::SVN::gc();
 }
 
 sub match_globs {
@@ -4450,6 +4463,24 @@
 	print commit_log_separator unless $incremental || $oneline;
 }
 
+sub cmd_blame {
+	my $path = shift;
+
+	config_pager();
+	run_pager();
+
+	my ($fh, $ctx) = command_output_pipe('blame', @_, $path);
+	while (my $line = <$fh>) {
+		if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
+			my (undef, $rev, undef) = ::cmt_metadata($1);
+			$rev = sprintf('%-10s', $rev);
+			$line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+		}
+		print $line;
+	}
+	command_close_pipe($fh, $ctx);
+}
+
 package Git::SVN::Migration;
 # these version numbers do NOT correspond to actual version numbers
 # of git nor git-svn.  They are just relative.
diff --git a/git-help--browse.sh b/git-web--browse.sh
similarity index 66%
rename from git-help--browse.sh
rename to git-web--browse.sh
index 10b0a36..384148a 100755
--- a/git-help--browse.sh
+++ b/git-web--browse.sh
@@ -16,31 +16,32 @@
 # git maintainer.
 #
 
-USAGE='[--browser=browser|--tool=browser] [cmd to display] ...'
+USAGE='[--browser=browser|--tool=browser] [--config=conf.var] url/file ...'
 
 # This must be capable of running outside of git directory, so
 # the vanilla git-sh-setup should not be used.
 NONGIT_OK=Yes
 . git-sh-setup
 
-# Install data.
-html_dir="@@HTMLDIR@@"
-
-test -f "$html_dir/git.html" || die "No documentation directory found."
+valid_custom_tool()
+{
+	browser_cmd="$(git config "browser.$1.cmd")"
+	test -n "$browser_cmd"
+}
 
 valid_tool() {
 	case "$1" in
-		firefox | iceweasel | konqueror | w3m | links | lynx | dillo)
+		firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open)
 			;; # happy
 		*)
-			return 1
+			valid_custom_tool "$1" || return 1
 			;;
 	esac
 }
 
 init_browser_path() {
-	browser_path=`git config browser.$1.path`
-	test -z "$browser_path" && browser_path=$1
+	browser_path=$(git config "browser.$1.path")
+	test -z "$browser_path" && browser_path="$1"
 }
 
 while test $# != 0
@@ -58,6 +59,18 @@
 		    shift ;;
 	    esac
 	    ;;
+	-c|--config*)
+	    case "$#,$1" in
+		*,*=*)
+		    conf=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		    ;;
+		1,*)
+		    usage ;;
+		*)
+		    conf="$2"
+		    shift ;;
+	    esac
+	    ;;
 	--)
 	    break
 	    ;;
@@ -71,17 +84,20 @@
     shift
 done
 
+test $# = 0 && usage
+
 if test -z "$browser"
 then
-    for opt in "help.browser" "web.browser"
+    for opt in "$conf" "web.browser"
     do
+	test -z "$opt" && continue
 	browser="`git config $opt`"
 	test -z "$browser" || break
     done
     if test -n "$browser" && ! valid_tool "$browser"; then
-	    echo >&2 "git config option $opt set to unknown browser: $browser"
-	    echo >&2 "Resetting to default..."
-	    unset browser
+	echo >&2 "git config option $opt set to unknown browser: $browser"
+	echo >&2 "Resetting to default..."
+	unset browser
     fi
 fi
 
@@ -94,6 +110,10 @@
     else
 	browser_candidates="w3m links lynx"
     fi
+    # SECURITYSESSIONID indicates an OS X GUI login session
+    if test -n "$SECURITYSESSIONID"; then
+	browser_candidates="open $browser_candidates"
+    fi
 
     for i in $browser_candidates; do
 	init_browser_path $i
@@ -108,21 +128,18 @@
 
     init_browser_path "$browser"
 
-    if ! type "$browser_path" > /dev/null 2>&1; then
+    if test -z "$browser_cmd" && ! type "$browser_path" > /dev/null 2>&1; then
 	die "The browser $browser is not available as '$browser_path'."
     fi
 fi
 
-pages=$(for p in "$@"; do echo "$html_dir/$p.html" ; done)
-test -z "$pages" && pages="$html_dir/git.html"
-
 case "$browser" in
     firefox|iceweasel)
 	# Check version because firefox < 2.0 does not support "-new-tab".
 	vers=$(expr "$($browser_path -version)" : '.* \([0-9][0-9]*\)\..*')
 	NEWTAB='-new-tab'
 	test "$vers" -lt 2 && NEWTAB=''
-	nohup "$browser_path" $NEWTAB $pages &
+	"$browser_path" $NEWTAB "$@" &
 	;;
     konqueror)
 	case "$(basename "$browser_path")" in
@@ -130,20 +147,25 @@
 		# It's simpler to use kfmclient to open a new tab in konqueror.
 		browser_path="$(echo "$browser_path" | sed -e 's/konqueror$/kfmclient/')"
 		type "$browser_path" > /dev/null 2>&1 || die "No '$browser_path' found."
-		eval "$browser_path" newTab $pages
+		eval "$browser_path" newTab "$@"
 		;;
 	    kfmclient)
-		eval "$browser_path" newTab $pages
+		eval "$browser_path" newTab "$@"
 		;;
 	    *)
-	        nohup "$browser_path" $pages &
+		"$browser_path" "$@" &
 		;;
 	esac
 	;;
-    w3m|links|lynx)
-	eval "$browser_path" $pages
+    w3m|links|lynx|open)
+	eval "$browser_path" "$@"
 	;;
     dillo)
-	nohup "$browser_path" $pages &
+	"$browser_path" "$@" &
+	;;
+    *)
+	if test -n "$browser_cmd"; then
+	    ( eval $browser_cmd "$@" )
+	fi
 	;;
 esac
diff --git a/git.c b/git.c
index 0cb8688..c4e4644b 100644
--- a/git.c
+++ b/git.c
@@ -87,19 +87,6 @@
 	return handled;
 }
 
-static const char *alias_command;
-static char *alias_string;
-
-static int git_alias_config(const char *var, const char *value)
-{
-	if (!prefixcmp(var, "alias.") && !strcmp(var + 6, alias_command)) {
-		if (!value)
-			return config_error_nonbool(var);
-		alias_string = xstrdup(value);
-	}
-	return 0;
-}
-
 static int split_cmdline(char *cmdline, const char ***argv)
 {
 	int src, dst, count = 0, size = 16;
@@ -155,15 +142,18 @@
 
 static int handle_alias(int *argcp, const char ***argv)
 {
-	int nongit = 0, envchanged = 0, ret = 0, saved_errno = errno;
+	int envchanged = 0, ret = 0, saved_errno = errno;
 	const char *subdir;
 	int count, option_count;
 	const char** new_argv;
+	const char *alias_command;
+	char *alias_string;
+	int unused_nongit;
 
-	subdir = setup_git_directory_gently(&nongit);
+	subdir = setup_git_directory_gently(&unused_nongit);
 
 	alias_command = (*argv)[0];
-	git_config(git_alias_config);
+	alias_string = alias_lookup(alias_command);
 	if (alias_string) {
 		if (alias_string[0] == '!') {
 			if (*argcp > 1) {
@@ -289,6 +279,7 @@
 		{ "branch", cmd_branch, RUN_SETUP },
 		{ "bundle", cmd_bundle },
 		{ "cat-file", cmd_cat_file, RUN_SETUP },
+		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
 		{ "checkout-index", cmd_checkout_index,
 			RUN_SETUP | NEED_WORK_TREE},
 		{ "check-ref-format", cmd_check_ref_format },
@@ -332,6 +323,8 @@
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
+		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
@@ -342,6 +335,7 @@
 		{ "push", cmd_push, RUN_SETUP },
 		{ "read-tree", cmd_read_tree, RUN_SETUP },
 		{ "reflog", cmd_reflog, RUN_SETUP },
+		{ "remote", cmd_remote, RUN_SETUP },
 		{ "repo-config", cmd_config },
 		{ "rerere", cmd_rerere, RUN_SETUP },
 		{ "reset", cmd_reset, RUN_SETUP },
@@ -350,7 +344,7 @@
 		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 		{ "rm", cmd_rm, RUN_SETUP },
 		{ "send-pack", cmd_send_pack, RUN_SETUP },
-		{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
+		{ "shortlog", cmd_shortlog, USE_PAGER },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
 		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
 		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
diff --git a/gitk-git/Makefile b/gitk-git/Makefile
index ae2b80b..f90dfab 100644
--- a/gitk-git/Makefile
+++ b/gitk-git/Makefile
@@ -8,6 +8,7 @@
 msgsdir    ?= $(gitk_libdir)/msgs
 msgsdir_SQ  = $(subst ','\'',$(msgsdir))
 
+TCL_PATH ?= tclsh
 TCLTK_PATH ?= wish
 INSTALL ?= install
 RM ?= rm -f
@@ -22,6 +23,9 @@
 	MSGFMT ?= $(TCL_PATH) po/po2msg.sh
 else
 	MSGFMT ?= msgfmt
+	ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+		MSGFMT := $(TCL_PATH) po/po2msg.sh
+	endif
 endif
 
 PO_TEMPLATE = po/gitk.pot
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 5560e4d..9a4d9c4 100644
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -82,7 +82,7 @@
 proc start_rev_list {view} {
     global startmsecs
     global commfd leftover tclencoding datemode
-    global viewargs viewfiles commitidx viewcomplete vnextroot
+    global viewargs viewargscmd viewfiles commitidx viewcomplete vnextroot
     global showlocalchanges commitinterest mainheadid
     global progressdirn progresscoords proglastnc curview
 
@@ -90,13 +90,23 @@
     set commitidx($view) 0
     set viewcomplete($view) 0
     set vnextroot($view) 0
+    set args $viewargs($view)
+    if {$viewargscmd($view) ne {}} {
+	if {[catch {
+	    set str [exec sh -c $viewargscmd($view)]
+	} err]} {
+	    error_popup "Error executing --argscmd command: $err"
+	    exit 1
+	}
+	set args [concat $args [split $str "\n"]]
+    }
     set order "--topo-order"
     if {$datemode} {
 	set order "--date-order"
     }
     if {[catch {
 	set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
-			 --boundary $viewargs($view) "--" $viewfiles($view)] r]
+			 --boundary $args "--" $viewfiles($view)] r]
     } err]} {
 	error_popup "[mc "Error executing git rev-list:"] $err"
 	exit 1
@@ -240,11 +250,12 @@
 	set listed 1
 	if {$j >= 0 && [string match "commit *" $cmit]} {
 	    set ids [string range $cmit 7 [expr {$j - 1}]]
-	    if {[string match {[-<>]*} $ids]} {
+	    if {[string match {[-^<>]*} $ids]} {
 		switch -- [string index $ids 0] {
 		    "-" {set listed 0}
-		    "<" {set listed 2}
-		    ">" {set listed 3}
+		    "^" {set listed 2}
+		    "<" {set listed 3}
+		    ">" {set listed 4}
 		}
 		set ids [string range $ids 1 end]
 	    }
@@ -392,6 +403,9 @@
 proc updatecommits {} {
     global viewdata curview phase displayorder ordertok idpending
     global children commitrow selectedline thickerline showneartags
+    global isworktree
+
+    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 
     if {$phase ne {}} {
 	stop_rev_list
@@ -632,6 +646,7 @@
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global diffcontextstring diffcontext
+    global ignorespace
     global maincursor textcursor curtextcursor
     global rowctxmenu fakerowmenu mergemax wrapcomment
     global highlight_files gdttype
@@ -825,6 +840,7 @@
     }
     frame .bleft.top
     frame .bleft.mid
+    frame .bleft.bottom
 
     button .bleft.top.search -text [mc "Search"] -command dosearch
     pack .bleft.top.search -side left -padx 5
@@ -849,18 +865,28 @@
     trace add variable diffcontextstring write diffcontextchange
     lappend entries .bleft.mid.diffcontext
     pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
-    set ctext .bleft.ctext
+    checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
+	-command changeignorespace -variable ignorespace
+    pack .bleft.mid.ignspace -side left -padx 5
+    set ctext .bleft.bottom.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
 	-state disabled -font textfont \
-	-yscrollcommand scrolltext -wrap none
+	-yscrollcommand scrolltext -wrap none \
+	-xscrollcommand ".bleft.bottom.sbhorizontal set"
     if {$have_tk85} {
 	$ctext conf -tabstyle wordprocessor
     }
-    scrollbar .bleft.sb -command "$ctext yview"
+    scrollbar .bleft.bottom.sb -command "$ctext yview"
+    scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h \
+	-width 10
     pack .bleft.top -side top -fill x
     pack .bleft.mid -side top -fill x
-    pack .bleft.sb -side right -fill y
-    pack $ctext -side left -fill both -expand 1
+    grid $ctext .bleft.bottom.sb -sticky nsew
+    grid .bleft.bottom.sbhorizontal -sticky ew
+    grid columnconfigure .bleft.bottom 0 -weight 1
+    grid rowconfigure .bleft.bottom 0 -weight 1
+    grid rowconfigure .bleft.bottom 1 -weight 0
+    pack .bleft.bottom -side top -fill both -expand 1
     lappend bglist $ctext
     lappend fglist $ctext
 
@@ -925,9 +951,17 @@
     .pwbottom add .bright
     .ctop add .pwbottom
 
-    # restore window position if known
+    # restore window width & height if known
     if {[info exists geometry(main)]} {
-        wm geometry . "$geometry(main)"
+	if {[scan $geometry(main) "%dx%d" w h] >= 2} {
+	    if {$w > [winfo screenwidth .]} {
+		set w [winfo screenwidth .]
+	    }
+	    if {$h > [winfo screenheight .]} {
+		set h [winfo screenheight .]
+	    }
+	    wm geometry . "${w}x$h"
+	}
     }
 
     if {[tk windowingsystem] eq {aqua}} {
@@ -1155,9 +1189,10 @@
     global canv canv2 canv3 mainfont textfont uifont tabstop
     global stuffsaved findmergefiles maxgraphpct
     global maxwidth showneartags showlocalchanges
-    global viewname viewfiles viewargs viewperm nextviewnum
+    global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
+    global autoselect
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -1172,6 +1207,7 @@
 	puts $f [list set maxwidth $maxwidth]
 	puts $f [list set cmitmode $cmitmode]
 	puts $f [list set wrapcomment $wrapcomment]
+	puts $f [list set autoselect $autoselect]
 	puts $f [list set showneartags $showneartags]
 	puts $f [list set showlocalchanges $showlocalchanges]
 	puts $f [list set datetimeformat $datetimeformat]
@@ -1194,7 +1230,7 @@
 	puts -nonewline $f "set permviews {"
 	for {set v 0} {$v < $nextviewnum} {incr v} {
 	    if {$viewperm($v)} {
-		puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
+		puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
 	    }
 	}
 	puts $f "}"
@@ -1307,45 +1343,45 @@
     }
     toplevel $w
     wm title $w [mc "Gitk key bindings"]
-    message $w.m -text [mc "
-Gitk key bindings:
+    message $w.m -text "
+[mc "Gitk key bindings:"]
 
-<$M1T-Q>		Quit
-<Home>		Move to first commit
-<End>		Move to last commit
-<Up>, p, i	Move up one commit
-<Down>, n, k	Move down one commit
-<Left>, z, j	Go back in history list
-<Right>, x, l	Go forward in history list
-<PageUp>	Move up one page in commit list
-<PageDown>	Move down one page in commit list
-<$M1T-Home>	Scroll to top of commit list
-<$M1T-End>	Scroll to bottom of commit list
-<$M1T-Up>	Scroll commit list up one line
-<$M1T-Down>	Scroll commit list down one line
-<$M1T-PageUp>	Scroll commit list up one page
-<$M1T-PageDown>	Scroll commit list down one page
-<Shift-Up>	Find backwards (upwards, later commits)
-<Shift-Down>	Find forwards (downwards, earlier commits)
-<Delete>, b	Scroll diff view up one page
-<Backspace>	Scroll diff view up one page
-<Space>		Scroll diff view down one page
-u		Scroll diff view up 18 lines
-d		Scroll diff view down 18 lines
-<$M1T-F>		Find
-<$M1T-G>		Move to next find hit
-<Return>	Move to next find hit
-/		Move to next find hit, or redo find
-?		Move to previous find hit
-f		Scroll diff view to next file
-<$M1T-S>		Search for next hit in diff view
-<$M1T-R>		Search for previous hit in diff view
-<$M1T-KP+>	Increase font size
-<$M1T-plus>	Increase font size
-<$M1T-KP->	Decrease font size
-<$M1T-minus>	Decrease font size
-<F5>		Update
-"] \
+[mc "<%s-Q>		Quit" $M1T]
+[mc "<Home>		Move to first commit"]
+[mc "<End>		Move to last commit"]
+[mc "<Up>, p, i	Move up one commit"]
+[mc "<Down>, n, k	Move down one commit"]
+[mc "<Left>, z, j	Go back in history list"]
+[mc "<Right>, x, l	Go forward in history list"]
+[mc "<PageUp>	Move up one page in commit list"]
+[mc "<PageDown>	Move down one page in commit list"]
+[mc "<%s-Home>	Scroll to top of commit list" $M1T]
+[mc "<%s-End>	Scroll to bottom of commit list" $M1T]
+[mc "<%s-Up>	Scroll commit list up one line" $M1T]
+[mc "<%s-Down>	Scroll commit list down one line" $M1T]
+[mc "<%s-PageUp>	Scroll commit list up one page" $M1T]
+[mc "<%s-PageDown>	Scroll commit list down one page" $M1T]
+[mc "<Shift-Up>	Find backwards (upwards, later commits)"]
+[mc "<Shift-Down>	Find forwards (downwards, earlier commits)"]
+[mc "<Delete>, b	Scroll diff view up one page"]
+[mc "<Backspace>	Scroll diff view up one page"]
+[mc "<Space>		Scroll diff view down one page"]
+[mc "u		Scroll diff view up 18 lines"]
+[mc "d		Scroll diff view down 18 lines"]
+[mc "<%s-F>		Find" $M1T]
+[mc "<%s-G>		Move to next find hit" $M1T]
+[mc "<Return>	Move to next find hit"]
+[mc "/		Move to next find hit, or redo find"]
+[mc "?		Move to previous find hit"]
+[mc "f		Scroll diff view to next file"]
+[mc "<%s-S>		Search for next hit in diff view" $M1T]
+[mc "<%s-R>		Search for previous hit in diff view" $M1T]
+[mc "<%s-KP+>	Increase font size" $M1T]
+[mc "<%s-plus>	Increase font size" $M1T]
+[mc "<%s-KP->	Decrease font size" $M1T]
+[mc "<%s-minus>	Decrease font size" $M1T]
+[mc "<F5>		Update"]
+" \
 	    -justify left -bg white -border 2 -relief groove
     pack $w.m -side top -fill both -padx 2 -pady 2
     button $w.ok -text [mc "Close"] -command "destroy $w" -default active
@@ -1845,7 +1881,7 @@
 
 proc newview {ishighlight} {
     global nextviewnum newviewname newviewperm newishighlight
-    global newviewargs revtreeargs
+    global newviewargs revtreeargs viewargscmd newviewargscmd curview
 
     set newishighlight $ishighlight
     set top .gitkview
@@ -1853,16 +1889,17 @@
 	raise $top
 	return
     }
-    set newviewname($nextviewnum) "View $nextviewnum"
+    set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
     set newviewperm($nextviewnum) 0
     set newviewargs($nextviewnum) [shellarglist $revtreeargs]
+    set newviewargscmd($nextviewnum) $viewargscmd($curview)
     vieweditor $top $nextviewnum [mc "Gitk view definition"]
 }
 
 proc editview {} {
     global curview
     global viewname viewperm newviewname newviewperm
-    global viewargs newviewargs
+    global viewargs newviewargs viewargscmd newviewargscmd
 
     set top .gitkvedit-$curview
     if {[winfo exists $top]} {
@@ -1872,6 +1909,7 @@
     set newviewname($curview) $viewname($curview)
     set newviewperm($curview) $viewperm($curview)
     set newviewargs($curview) [shellarglist $viewargs($curview)]
+    set newviewargscmd($curview) $viewargscmd($curview)
     vieweditor $top $curview "Gitk: edit view $viewname($curview)"
 }
 
@@ -1892,6 +1930,14 @@
     entry $top.args -width 50 -textvariable newviewargs($n) \
 	-background $bgcolor
     grid $top.args - -sticky ew -padx 5
+
+    message $top.ac -aspect 1000 \
+	-text [mc "Command to generate more commits to include:"]
+    grid $top.ac - -sticky w -pady 5
+    entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
+	-background white
+    grid $top.argscmd - -sticky ew -padx 5
+
     message $top.l -aspect 1000 \
 	-text [mc "Enter files and directories to include, one per line:"]
     grid $top.l - -sticky w
@@ -1935,7 +1981,7 @@
 proc newviewok {top n} {
     global nextviewnum newviewperm newviewname newishighlight
     global viewname viewfiles viewperm selectedview curview
-    global viewargs newviewargs viewhlmenu
+    global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
 
     if {[catch {
 	set newargs [shellsplit $newviewargs($n)]
@@ -1959,6 +2005,7 @@
 	set viewperm($n) $newviewperm($n)
 	set viewfiles($n) $files
 	set viewargs($n) $newargs
+	set viewargscmd($n) $newviewargscmd($n)
 	addviewmenu $n
 	if {!$newishighlight} {
 	    run showview $n
@@ -1975,9 +2022,11 @@
 	    # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
 		# entryconf [list -label $viewname($n) -value $viewname($n)]
 	}
-	if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
+	if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
+		$newviewargscmd($n) ne $viewargscmd($n)} {
 	    set viewfiles($n) $files
 	    set viewargs($n) $newargs
+	    set viewargscmd($n) $newviewargscmd($n)
 	    if {$curview == $n} {
 		run updatecommits
 	    }
@@ -2053,8 +2102,6 @@
 	set ybot [expr {[lindex $span 1] * $ymax}]
 	if {$ytop < $y && $y < $ybot} {
 	    set yscreen [expr {$y - $ytop}]
-	} else {
-	    set yscreen [expr {($ybot - $ytop) / 2}]
 	}
     } elseif {[info exists pending_select]} {
 	set selid $pending_select
@@ -2115,7 +2162,7 @@
     set yf 0
     set row {}
     set selectfirst 0
-    if {$selid ne {} && [info exists commitrow($n,$selid)]} {
+    if {[info exists yscreen] && [info exists commitrow($n,$selid)]} {
 	set row $commitrow($n,$selid)
 	# try to get the selected row in the same position on the screen
 	set ymax [lindex [$canv cget -scrollregion] 3]
@@ -2839,8 +2886,9 @@
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
     global localirow localfrow lserial showlocalchanges
+    global isworktree
 
-    if {!$showlocalchanges} return
+    if {!$showlocalchanges || !$isworktree} return
     incr lserial
     set localfrow -1
     set localirow -1
@@ -3627,23 +3675,23 @@
     global linehtag linentag linedtag selectedline
     global canvxmax boldrows boldnamerows fgcolor nullid nullid2
 
-    # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
+    # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
     set listed [lindex $commitlisted $row]
     if {$id eq $nullid} {
 	set ofill red
     } elseif {$id eq $nullid2} {
 	set ofill green
     } else {
-	set ofill [expr {$listed != 0? "blue": "white"}]
+	set ofill [expr {$listed != 0 ? $listed == 2 ? "gray" : "blue" : "white"}]
     }
     set x [xc $row $col]
     set y [yc $row]
     set orad [expr {$linespc / 3}]
-    if {$listed <= 1} {
+    if {$listed <= 2} {
 	set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
 		   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
 		   -fill $ofill -outline $fgcolor -width 1 -tags circle]
-    } elseif {$listed == 2} {
+    } elseif {$listed == 3} {
 	# triangle pointing left for left-side commits
 	set t [$canv create polygon \
 		   [expr {$x - $orad}] $y \
@@ -4645,6 +4693,7 @@
     global commentend idtags linknum
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
+    global autoselect
 
     catch {unset pending_select}
     $canv delete hover
@@ -4700,8 +4749,10 @@
     set currentid $id
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
-    $sha1entry selection from 0
-    $sha1entry selection to end
+    if {$autoselect} {
+	$sha1entry selection from 0
+	$sha1entry selection to end
+    }
     rhighlight_sel $id
 
     $ctext conf -state normal
@@ -5027,13 +5078,14 @@
 proc mergediff {id l} {
     global diffmergeid mdifffd
     global diffids
+    global diffcontext
     global parentlist
     global limitdiffs viewfiles curview
 
     set diffmergeid $id
     set diffids $id
     # this doesn't seem to actually affect anything...
-    set cmd [concat | git diff-tree --no-commit-id --cc $id]
+    set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
     if {$limitdiffs && $viewfiles($curview) ne {}} {
 	set cmd [concat $cmd -- $viewfiles($curview)]
     }
@@ -5270,13 +5322,21 @@
     }
 }
 
+proc changeignorespace {} {
+    reselectline
+}
+
 proc getblobdiffs {ids} {
     global blobdifffd diffids env
     global diffinhdr treediffs
     global diffcontext
+    global ignorespace
     global limitdiffs viewfiles curview
 
     set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
+    if {$ignorespace} {
+	append cmd " -w"
+    }
     if {$limitdiffs && $viewfiles($curview) ne {}} {
 	set cmd [concat $cmd -- $viewfiles($curview)]
     }
@@ -5590,7 +5650,7 @@
 proc scrolltext {f0 f1} {
     global searchstring
 
-    .bleft.sb set $f0 $f1
+    .bleft.bottom.sb set $f0 $f1
     if {$searchstring ne {}} {
 	searchmarkvisible 0
     }
@@ -6137,11 +6197,7 @@
 	return
     }
     if {[catch {
-	set dir [gitdir]
-	set fname [file join $dir "refs/tags" $tag]
-	set f [open $fname w]
-	puts $f $id
-	close $f
+	exec git tag $tag $id
     } err]} {
 	error_popup "[mc "Error creating tag:"] $err"
 	return
@@ -7933,7 +7989,7 @@
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global bgcolor fgcolor ctext diffcolors selectbgcolor
-    global tabstop limitdiffs
+    global tabstop limitdiffs autoselect
 
     set top .gitkprefs
     set prefstop $top
@@ -7963,6 +8019,11 @@
     checkbutton $top.showlocal.b -variable showlocalchanges
     pack $top.showlocal.b $top.showlocal.l -side left
     grid x $top.showlocal -sticky w
+    frame $top.autoselect
+    label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
+    checkbutton $top.autoselect.b -variable autoselect
+    pack $top.autoselect.b $top.autoselect.l -side left
+    grid x $top.autoselect -sticky w
 
     label $top.ddisp -text [mc "Diff display options"]
     grid $top.ddisp - -sticky w -pady 10
@@ -7984,11 +8045,11 @@
     grid $top.cdisp - -sticky w -pady 10
     label $top.bg -padx 40 -relief sunk -background $bgcolor
     button $top.bgbut -text [mc "Background"] -font optionfont \
-	-command [list choosecolor bgcolor 0 $top.bg background setbg]
+	-command [list choosecolor bgcolor {} $top.bg background setbg]
     grid x $top.bgbut $top.bg -sticky w
     label $top.fg -padx 40 -relief sunk -background $fgcolor
     button $top.fgbut -text [mc "Foreground"] -font optionfont \
-	-command [list choosecolor fgcolor 0 $top.fg foreground setfg]
+	-command [list choosecolor fgcolor {} $top.fg foreground setfg]
     grid x $top.fgbut $top.fg -sticky w
     label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
     button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
@@ -8008,7 +8069,7 @@
     grid x $top.hunksepbut $top.hunksep -sticky w
     label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
     button $top.selbgbut -text [mc "Select bg"] -font optionfont \
-	-command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
+	-command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
     grid x $top.selbgbut $top.selbgsep -sticky w
 
     label $top.cfont -text [mc "Fonts: press to choose"]
@@ -8453,12 +8514,14 @@
 set showlocalchanges 1
 set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
+set autoselect 1
 
 set colors {green red blue magenta darkgrey brown orange}
 set bgcolor white
 set fgcolor black
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
+set ignorespace 0
 set selectbgcolor gray85
 
 ## For msgcat loading, first locate the installation location.
@@ -8511,8 +8574,9 @@
 set revtreeargs {}
 set cmdline_files {}
 set i 0
+set revtreeargscmd {}
 foreach arg $argv {
-    switch -- $arg {
+    switch -glob -- $arg {
 	"" { }
 	"-d" { set datemode 1 }
 	"--merge" {
@@ -8523,6 +8587,9 @@
 	    set cmdline_files [lrange $argv [expr {$i + 1}] end]
 	    break
 	}
+	"--argscmd=*" {
+	    set revtreeargscmd [string range $arg 10 end]
+	}
 	default {
 	    lappend revtreeargs $arg
 	}
@@ -8624,6 +8691,7 @@
 set viewfiles(0) {}
 set viewperm(0) 0
 set viewargs(0) {}
+set viewargscmd(0) {}
 
 set cmdlineok 0
 set stopped 0
@@ -8632,6 +8700,7 @@
 set localirow -1
 set localfrow -1
 set lserial 0
+set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 setcoords
 makewindow
 # wait for the window to become visible
@@ -8639,7 +8708,7 @@
 wm title . "[file tail $argv0]: [file tail [pwd]]"
 readrefs
 
-if {$cmdline_files ne {} || $revtreeargs ne {}} {
+if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
     # create a view for the files/dirs specified on the command line
     set curview 1
     set selectedview 1
@@ -8647,6 +8716,7 @@
     set viewname(1) [mc "Command line"]
     set viewfiles(1) $cmdline_files
     set viewargs(1) $revtreeargs
+    set viewargscmd(1) $revtreeargscmd
     set viewperm(1) 0
     addviewmenu 1
     .bar.view entryconf [mc "Edit view..."] -state normal
@@ -8660,6 +8730,7 @@
 	set viewname($n) [lindex $v 0]
 	set viewfiles($n) [lindex $v 1]
 	set viewargs($n) [lindex $v 2]
+	set viewargscmd($n) [lindex $v 3]
 	set viewperm($n) 1
 	addviewmenu $n
     }
diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po
new file mode 100644
index 0000000..d0f4c2e
--- /dev/null
+++ b/gitk-git/po/it.po
@@ -0,0 +1,890 @@
+# Translation of gitk
+# Copyright (C) 2005-2008 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+# Michele Ballabio <barra_cuda@katamail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"PO-Revision-Date: 2008-03-13 17:34+0100\n"
+"Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
+"Language-Team: Italian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:111
+msgid "Error executing git rev-list:"
+msgstr "Errore nell'esecuzione di git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Lettura in corso"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Lettura delle revisioni in corso..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Impossibile elaborare i dati di git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "Nessuna revisione selezionata"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Nessuna informazione disponibile sulle revisioni"
+
+#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+msgid "OK"
+msgstr "OK"
+
+#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
+#: gitk:7924 gitk:8083
+msgid "Cancel"
+msgstr "Annulla"
+
+#: gitk:661
+msgid "File"
+msgstr "File"
+
+#: gitk:663
+msgid "Update"
+msgstr "Aggiorna"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Rileggi riferimenti"
+
+#: gitk:665
+msgid "List references"
+msgstr "Elenca riferimenti"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Esci"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Modifica"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Preferenze"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Vista"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Nuova vista..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Modifica vista..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Elimina vista"
+
+#: gitk:678
+msgid "All files"
+msgstr "Tutti i file"
+
+#: gitk:682
+msgid "Help"
+msgstr "Aiuto"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Informazioni su gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Scorciatoie da tastiera"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Trova"
+
+#: gitk:792
+msgid "next"
+msgstr "succ"
+
+#: gitk:793
+msgid "prev"
+msgstr "prec"
+
+#: gitk:794
+msgid "commit"
+msgstr "revisione"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "contenente:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "che riguarda i percorsi:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "che aggiunge/rimuove la stringa:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Esatto"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr ""
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr ""
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Tutti i campi"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Titolo"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Commenti"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Autore"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr "Revisione creata da"
+
+#: gitk:845
+msgid "Search"
+msgstr "Cerca"
+
+#: gitk:852
+msgid "Diff"
+msgstr ""
+
+#: gitk:854
+msgid "Old version"
+msgstr "Vecchia versione"
+
+#: gitk:856
+msgid "New version"
+msgstr "Nuova versione"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Linee di contesto"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignora modifiche agli spazi"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Modifiche"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Directory"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diff questo -> selezionato"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diff selezionato -> questo"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Crea patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Crea etichetta"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Scrivi revisione in un file"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Crea un nuovo ramo"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Porta questa revisione in cima al ramo attuale"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Aggiorna il ramo HEAD a questa revisione"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Attiva questo ramo"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Elimina questo ramo"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Evidenzia anche questo"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Evidenzia solo questo"
+
+#: gitk:1318
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - un visualizzatore di revisioni per git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
+"License"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Chiudi"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Scorciatoie da tastiera di Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Scorciatoie da tastiera di Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tEsci"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tVai alla prima revisione"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tVai all'ultima revisione"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tVai più in alto di una revisione"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tVai più in basso di una revisione"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tTorna indietro nella cronologia"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tVai avanti nella cronologia"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tScorri alla fine della lista delle revisioni"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tTrova"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tTrova in avanti"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tTrova in avanti"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tTrova in avanti, o cerca di nuovo"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tTrova all'indietro"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tScorri la vista delle differenze al file successivo"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumenta grandezza carattere"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumenta grandezza carattere"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDiminuisci grandezza carattere"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tDiminuisci grandezza carattere"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAggiorna"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Scelta vista Gitk"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Nome"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Ricorda questa vista"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Revisioni da includere (argomenti di git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Comando che genera altre revisioni da visualizzare:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Inserire file e directory da includere, uno per riga:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Errore negli argomenti di selezione delle revisioni:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Nessuno"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Data"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr ""
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Discendente"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "Non discendente"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Ascendente"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "Non ascendente"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Ricerca in corso"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Etichette:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Genitore"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Figlio"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Ramo"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Segue"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Precede"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Errore nella lettura delle differenze di fusione:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Vai a:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "La SHA1 id abbreviata %s è ambigua"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "La SHA1 id %s è sconosciuta"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "L'etichetta/ramo %s è sconosciuto"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Figli"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Aggiorna il ramo %s a questa revisione"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Inizio"
+
+#: gitk:6050
+msgid "From"
+msgstr "Da"
+
+#: gitk:6055
+msgid "To"
+msgstr "A"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Genera patch"
+
+#: gitk:6080
+msgid "From:"
+msgstr "Da:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "A:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Inverti"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Scrivi sul file:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Genera"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Errore nella creazione della patch:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Nome etichetta:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Crea"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "Nessuna etichetta specificata"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "L'etichetta \"%s\" esiste già"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Errore nella creazione dell'etichetta:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Scrivi"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Errore nella scrittura della revisione:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Nome:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Specificare un nome per il nuovo ramo"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr ""
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "Nessuna modifica archiviata"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Conferma git reset"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Aggiornare il ramo %s a %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Tipo di aggiornamento:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hard: Aggiorna la directory di lavoro e l'indice\n"
+"(abbandona TUTTE le modifiche locali)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "git reset in corso"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Attivazione in corso"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Impossibile cancellare il ramo attualmente attivo"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Le revisioni nel ramo %s non sono presenti su altri rami.\n"
+"Cancellare il ramo %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Etichette e rami: %s"
+
+#: gitk:6574
+msgid "Filter"
+msgstr "Filtro"
+
+#: gitk:6868
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Errore nella lettura della topologia delle revisioni: le informazioni sul "
+"ramo e le etichette precedenti e seguenti saranno incomplete."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Etichetta"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Scelta caratteri gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "B"
+
+#: gitk:7912
+msgid "I"
+msgstr "I"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Preferenze gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Opzioni visualizzazione dell'elenco revisioni"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Larghezza massima del grafico (in linee)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Larghezza massima del grafico (% del pannello)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Mostra modifiche locali"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Seleziona automaticamente SHA1 hash"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Opzioni di visualizzazione delle differenze"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Spaziatura tabulazioni"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Mostra etichette vicine"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Limita le differenze ai percorsi elencati"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Colori: premere per scegliere"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Sfondo"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Primo piano"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: vecchie linee"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: nuove linee"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: intestazione della sezione"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Sfondo selezione"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Carattere: premere per scegliere"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Carattere principale"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Carattere per differenze"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Carattere per interfaccia utente"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: scegliere un colore per %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Questa versione di Tcl/Tk non può avviare gitk.\n"
+" Gitk richiede Tcl/Tk versione 8.4 o superiore."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "Archivio git non trovato."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Directory git \"%s\" non trovata."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Gitk: argomenti errati:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
+"sono file in attesa di fusione."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
+"specificati non sono in attesa di fusione."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Linea di comando"
diff --git a/gitweb/README b/gitweb/README
index 4c8bedf..2163071 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -233,6 +233,10 @@
    Displayed in the project summary page. You can use multiple-valued
    gitweb.url repository configuration variable for that, but the file
    takes precendence.
+ * gitweb.owner
+   You can use the gitweb.owner repository configuration variable to set
+   repository's owner. It is displayed in the project list and summary
+   page. If it's not set, filesystem directory's owner is used.
  * various gitweb.* config variables (in config)
    Read description of %feature hash for detailed list, and some
    descriptions.
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 86a6ced..f37b687 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -472,13 +472,15 @@
 	}
 }
 
+our $search_use_regexp = $cgi->param('sr');
+
 our $searchtext = $cgi->param('s');
 our $search_regexp;
 if (defined $searchtext) {
 	if (length($searchtext) < 2) {
 		die_error(undef, "At least two characters are required for search parameter");
 	}
-	$search_regexp = quotemeta $searchtext;
+	$search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
 }
 
 # now read PATH_INFO and use it as alternative to parameters
@@ -504,7 +506,7 @@
 	}
 	# do not change any parameters if an action is given using the query string
 	return if $action;
-	$path_info =~ s,^$project/*,,;
+	$path_info =~ s,^\Q$project\E/*,,;
 	my ($refname, $pathname) = split(/:/, $path_info, 2);
 	if (defined $pathname) {
 		# we got "project.git/branch:filename" or "project.git/branch:dir/"
@@ -608,6 +610,7 @@
 		searchtype => "st",
 		snapshot_format => "sf",
 		extra_options => "opt",
+		search_use_regexp => "sr",
 	);
 	my %mapping = @mapping;
 
@@ -625,7 +628,7 @@
 	my ($use_pathinfo) = gitweb_check_feature('pathinfo');
 	if ($use_pathinfo) {
 		# use PATH_INFO for project name
-		$href .= "/$params{'project'}" if defined $params{'project'};
+		$href .= "/".esc_url($params{'project'}) if defined $params{'project'};
 		delete $params{'project'};
 
 		# Summary just uses the project path URL
@@ -753,29 +756,40 @@
 # Make control characters "printable", using character escape codes (CEC)
 sub quot_cec {
 	my $cntrl = shift;
+	my %opts = @_;
 	my %es = ( # character escape codes, aka escape sequences
-		   "\t" => '\t',   # tab            (HT)
-		   "\n" => '\n',   # line feed      (LF)
-		   "\r" => '\r',   # carrige return (CR)
-		   "\f" => '\f',   # form feed      (FF)
-		   "\b" => '\b',   # backspace      (BS)
-		   "\a" => '\a',   # alarm (bell)   (BEL)
-		   "\e" => '\e',   # escape         (ESC)
-		   "\013" => '\v', # vertical tab   (VT)
-		   "\000" => '\0', # nul character  (NUL)
-		   );
+		"\t" => '\t',   # tab            (HT)
+		"\n" => '\n',   # line feed      (LF)
+		"\r" => '\r',   # carrige return (CR)
+		"\f" => '\f',   # form feed      (FF)
+		"\b" => '\b',   # backspace      (BS)
+		"\a" => '\a',   # alarm (bell)   (BEL)
+		"\e" => '\e',   # escape         (ESC)
+		"\013" => '\v', # vertical tab   (VT)
+		"\000" => '\0', # nul character  (NUL)
+	);
 	my $chr = ( (exists $es{$cntrl})
 		    ? $es{$cntrl}
 		    : sprintf('\%03o', ord($cntrl)) );
-	return "<span class=\"cntrl\">$chr</span>";
+	if ($opts{-nohtml}) {
+		return $chr;
+	} else {
+		return "<span class=\"cntrl\">$chr</span>";
+	}
 }
 
 # Alternatively use unicode control pictures codepoints,
 # Unicode "printable representation" (PR)
 sub quot_upr {
 	my $cntrl = shift;
+	my %opts = @_;
+
 	my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl));
-	return "<span class=\"cntrl\">$chr</span>";
+	if ($opts{-nohtml}) {
+		return $chr;
+	} else {
+		return "<span class=\"cntrl\">$chr</span>";
+	}
 }
 
 # git may return quoted and escaped filenames
@@ -800,7 +814,7 @@
 			return chr(oct($seq));
 		} elsif (exists $es{$seq}) {
 			# C escape sequence, aka character escape code
-			return $es{$seq}
+			return $es{$seq};
 		}
 		# quoted ordinary character
 		return $seq;
@@ -837,37 +851,78 @@
 ## ----------------------------------------------------------------------
 ## HTML aware string manipulation
 
+# Try to chop given string on a word boundary between position
+# $len and $len+$add_len. If there is no word boundary there,
+# chop at $len+$add_len. Do not chop if chopped part plus ellipsis
+# (marking chopped part) would be longer than given string.
 sub chop_str {
 	my $str = shift;
 	my $len = shift;
 	my $add_len = shift || 10;
+	my $where = shift || 'right'; # 'left' | 'center' | 'right'
 
 	# allow only $len chars, but don't cut a word if it would fit in $add_len
 	# if it doesn't fit, cut it if it's still longer than the dots we would add
-	$str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/;
-	my $body = $1;
-	my $tail = $2;
-	if (length($tail) > 4) {
-		$tail = " ...";
-		$body =~ s/&[^;]*$//; # remove chopped character entities
+	# remove chopped character entities entirely
+
+	# when chopping in the middle, distribute $len into left and right part
+	# return early if chopping wouldn't make string shorter
+	if ($where eq 'center') {
+		return $str if ($len + 5 >= length($str)); # filler is length 5
+		$len = int($len/2);
+	} else {
+		return $str if ($len + 4 >= length($str)); # filler is length 4
 	}
-	return "$body$tail";
+
+	# regexps: ending and beginning with word part up to $add_len
+	my $endre = qr/.{$len}\w{0,$add_len}/;
+	my $begre = qr/\w{0,$add_len}.{$len}/;
+
+	if ($where eq 'left') {
+		$str =~ m/^(.*?)($begre)$/;
+		my ($lead, $body) = ($1, $2);
+		if (length($lead) > 4) {
+			$body =~ s/^[^;]*;// if ($lead =~ m/&[^;]*$/);
+			$lead = " ...";
+		}
+		return "$lead$body";
+
+	} elsif ($where eq 'center') {
+		$str =~ m/^($endre)(.*)$/;
+		my ($left, $str)  = ($1, $2);
+		$str =~ m/^(.*?)($begre)$/;
+		my ($mid, $right) = ($1, $2);
+		if (length($mid) > 5) {
+			$left  =~ s/&[^;]*$//;
+			$right =~ s/^[^;]*;// if ($mid =~ m/&[^;]*$/);
+			$mid = " ... ";
+		}
+		return "$left$mid$right";
+
+	} else {
+		$str =~ m/^($endre)(.*)$/;
+		my $body = $1;
+		my $tail = $2;
+		if (length($tail) > 4) {
+			$body =~ s/&[^;]*$//;
+			$tail = "... ";
+		}
+		return "$body$tail";
+	}
 }
 
 # takes the same arguments as chop_str, but also wraps a <span> around the
 # result with a title attribute if it does get chopped. Additionally, the
 # string is HTML-escaped.
 sub chop_and_escape_str {
-	my $str = shift;
-	my $len = shift;
-	my $add_len = shift || 10;
+	my ($str) = @_;
 
-	my $chopped = chop_str($str, $len, $add_len);
+	my $chopped = chop_str(@_);
 	if ($chopped eq $str) {
 		return esc_html($chopped);
 	} else {
-		return qq{<span title="} . esc_html($str) . qq{">} .
-			esc_html($chopped) . qq{</span>};
+		$str =~ s/([[:cntrl:]])/?/g;
+		return $cgi->span({-title=>$str}, esc_html($chopped));
 	}
 }
 
@@ -1620,7 +1675,7 @@
 	my $path = shift;
 
 	$git_dir = "$projectroot/$path";
-	open my $fd, "$projectroot/$path/cloneurl"
+	open my $fd, "$git_dir/cloneurl"
 		or return wantarray ?
 		@{ config_to_multi(git_get_project_config('url')) } :
 		   config_to_multi(git_get_project_config('url'));
@@ -1759,6 +1814,7 @@
 	my $owner;
 
 	return undef unless $project;
+	$git_dir = "$projectroot/$project";
 
 	if (!defined $gitweb_project_owner) {
 		git_get_project_list_from_file();
@@ -1767,8 +1823,11 @@
 	if (exists $gitweb_project_owner->{$project}) {
 		$owner = $gitweb_project_owner->{$project};
 	}
+	if (!defined $owner){
+		$owner = git_get_project_config('owner');
+	}
 	if (!defined $owner) {
-		$owner = get_file_owner("$projectroot/$project");
+		$owner = get_file_owner("$git_dir");
 	}
 
 	return $owner;
@@ -2023,7 +2082,7 @@
 }
 
 sub parse_commits {
-	my ($commit_id, $maxcount, $skip, $arg, $filename) = @_;
+	my ($commit_id, $maxcount, $skip, $filename, @args) = @_;
 	my @cos;
 
 	$maxcount ||= 1;
@@ -2033,7 +2092,7 @@
 
 	open my $fd, "-|", git_cmd(), "rev-list",
 		"--header",
-		($arg ? ($arg) : ()),
+		@args,
 		("--max-count=" . $maxcount),
 		("--skip=" . $skip),
 		@extra_options,
@@ -2105,7 +2164,7 @@
 		$res{'to_mode'} = $2;
 		$res{'from_id'} = $3;
 		$res{'to_id'} = $4;
-		$res{'status'} = $res{'status_str'} = $5;
+		$res{'status'} = $5;
 		$res{'similarity'} = $6;
 		if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied
 			($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7);
@@ -2121,7 +2180,6 @@
 		$res{'to_mode'} = pop @{$res{'from_mode'}};
 		$res{'from_id'} = [ split(' ', $3) ];
 		$res{'to_id'} = pop @{$res{'from_id'}};
-		$res{'status_str'} = $4;
 		$res{'status'} = [ split('', $4) ];
 		$res{'to_file'} = unquote($5);
 	}
@@ -2512,7 +2570,7 @@
 		my $action = $my_uri;
 		my ($use_pathinfo) = gitweb_check_feature('pathinfo');
 		if ($use_pathinfo) {
-			$action .= "/$project";
+			$action .= "/".esc_url($project);
 		} else {
 			$cgi->param("p", $project);
 		}
@@ -2528,6 +2586,10 @@
 		      $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
 		      " search:\n",
 		      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+		      "<span title=\"Extended regular expression\">" .
+		      $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+		                     -checked => $search_use_regexp) .
+		      "</span>" .
 		      "</div>" .
 		      $cgi->end_form() . "\n";
 	}
@@ -2614,7 +2676,7 @@
 }
 
 sub format_paging_nav {
-	my ($action, $hash, $head, $page, $nrevs) = @_;
+	my ($action, $hash, $head, $page, $has_next_link) = @_;
 	my $paging_nav;
 
 
@@ -2632,7 +2694,7 @@
 		$paging_nav .= " &sdot; prev";
 	}
 
-	if ($nrevs >= (100 * ($page+1)-1)) {
+	if ($has_next_link) {
 		$paging_nav .= " &sdot; " .
 			$cgi->a({-href => href(-replay=>1, page=>$page+1),
 			         -accesskey => "n", -title => "Alt-n"}, "next");
@@ -2939,7 +3001,7 @@
 sub is_deleted {
 	my $diffinfo = shift;
 
-	return $diffinfo->{'status_str'} =~ /D/;
+	return $diffinfo->{'to_id'} eq ('0' x 40);
 }
 
 # does patch correspond to [previous] difftree raw line
@@ -3769,18 +3831,24 @@
 		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
 		      "<td><i>" . $author . "</i></td>\n" .
 		      "<td>" .
-		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
-			       chop_and_escape_str($co{'title'}, 50) . "<br/>");
+		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+		               -class => "list subject"},
+		              chop_and_escape_str($co{'title'}, 50) . "<br/>");
 		my $comment = $co{'comment'};
 		foreach my $line (@$comment) {
-			if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
-				my $lead = esc_html($1) || "";
-				$lead = chop_str($lead, 30, 10);
-				my $match = esc_html($2) || "";
-				my $trail = esc_html($3) || "";
-				$trail = chop_str($trail, 30, 10);
-				my $text = "$lead<span class=\"match\">$match</span>$trail";
-				print chop_str($text, 80, 5) . "<br/>\n";
+			if ($line =~ m/^(.*?)($search_regexp)(.*)$/i) {
+				my ($lead, $match, $trail) = ($1, $2, $3);
+				$match = chop_str($match, 70, 5, 'center');
+				my $contextlen = int((80 - length($match))/2);
+				$contextlen = 30 if ($contextlen > 30);
+				$lead  = chop_str($lead,  $contextlen, 10, 'left');
+				$trail = chop_str($trail, $contextlen, 10, 'right');
+
+				$lead  = esc_html($lead);
+				$match = esc_html($match);
+				$trail = esc_html($trail);
+
+				print "$lead<span class=\"match\">$match</span>$trail<br />";
 			}
 		}
 		print "</td>\n" .
@@ -4517,7 +4585,7 @@
 
 	my @commitlist = parse_commits($hash, 101, (100 * $page));
 
-	my $paging_nav = format_paging_nav('log', $hash, $head, $page, (100 * ($page+1)));
+	my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100);
 
 	git_header_html();
 	git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
@@ -5069,14 +5137,26 @@
 	my $refs = git_get_references();
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
 
+	my @commitlist = parse_commits($hash_base, 101, (100 * $page),
+	                               $file_name, "--full-history");
+	if (!@commitlist) {
+		die_error('404 Not Found', "No such file or directory on given branch");
+	}
+
 	if (!defined $hash && defined $file_name) {
-		$hash = git_get_hash_by_path($hash_base, $file_name);
+		# some commits could have deleted file in question,
+		# and not have it in tree, but one of them has to have it
+		for (my $i = 0; $i <= @commitlist; $i++) {
+			$hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
+			last if defined $hash;
+		}
 	}
 	if (defined $hash) {
 		$ftype = git_get_type($hash);
 	}
-
-	my @commitlist = parse_commits($hash_base, 101, (100 * $page), "--full-history", $file_name);
+	if (!defined $ftype) {
+		die_error(undef, "Unknown type of object");
+	}
 
 	my $paging_nav = '';
 	if ($page > 0) {
@@ -5158,14 +5238,17 @@
 		} elsif ($searchtype eq 'committer') {
 			$greptype = "--committer=";
 		}
-		$greptype .= $search_regexp;
-		my @commitlist = parse_commits($hash, 101, (100 * $page), $greptype);
+		$greptype .= $searchtext;
+		my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
+		                               $greptype, '--regexp-ignore-case',
+		                               $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
 
 		my $paging_nav = '';
 		if ($page > 0) {
 			$paging_nav .=
 				$cgi->a({-href => href(action=>"search", hash=>$hash,
-				                       searchtext=>$searchtext, searchtype=>$searchtype)},
+				                       searchtext=>$searchtext,
+				                       searchtype=>$searchtype)},
 				        "first");
 			$paging_nav .= " &sdot; " .
 				$cgi->a({-href => href(-replay=>1, page=>$page-1),
@@ -5199,50 +5282,19 @@
 		print "<table class=\"pickaxe search\">\n";
 		my $alternate = 1;
 		$/ = "\n";
-		my $git_command = git_cmd_str();
-		my $searchqtext = $searchtext;
-		$searchqtext =~ s/'/'\\''/;
-		open my $fd, "-|", "$git_command rev-list $hash | " .
-			"$git_command diff-tree -r --stdin -S\'$searchqtext\'";
+		open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+			'--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+			($search_use_regexp ? '--pickaxe-regex' : ());
 		undef %co;
 		my @files;
 		while (my $line = <$fd>) {
-			if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
-				my %set;
-				$set{'file'} = $6;
-				$set{'from_id'} = $3;
-				$set{'to_id'} = $4;
-				$set{'id'} = $set{'to_id'};
-				if ($set{'id'} =~ m/0{40}/) {
-					$set{'id'} = $set{'from_id'};
-				}
-				if ($set{'id'} =~ m/0{40}/) {
-					next;
-				}
-				push @files, \%set;
-			} elsif ($line =~ m/^([0-9a-fA-F]{40})$/){
+			chomp $line;
+			next unless $line;
+
+			my %set = parse_difftree_raw_line($line);
+			if (defined $set{'commit'}) {
+				# finish previous commit
 				if (%co) {
-					if ($alternate) {
-						print "<tr class=\"dark\">\n";
-					} else {
-						print "<tr class=\"light\">\n";
-					}
-					$alternate ^= 1;
-					my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
-					print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-					      "<td><i>" . $author . "</i></td>\n" .
-					      "<td>" .
-					      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
-					              -class => "list subject"},
-					              chop_and_escape_str($co{'title'}, 50) . "<br/>");
-					while (my $setref = shift @files) {
-						my %set = %$setref;
-						print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
-						                             hash=>$set{'id'}, file_name=>$set{'file'}),
-						              -class => "list"},
-						              "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
-						      "<br/>\n";
-					}
 					print "</td>\n" .
 					      "<td class=\"link\">" .
 					      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
@@ -5251,11 +5303,44 @@
 					print "</td>\n" .
 					      "</tr>\n";
 				}
-				%co = parse_commit($1);
+
+				if ($alternate) {
+					print "<tr class=\"dark\">\n";
+				} else {
+					print "<tr class=\"light\">\n";
+				}
+				$alternate ^= 1;
+				%co = parse_commit($set{'commit'});
+				my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
+				print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+				      "<td><i>$author</i></td>\n" .
+				      "<td>" .
+				      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+				              -class => "list subject"},
+				              chop_and_escape_str($co{'title'}, 50) . "<br/>");
+			} elsif (defined $set{'to_id'}) {
+				next if ($set{'to_id'} =~ m/^0{40}$/);
+
+				print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+				                             hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
+				              -class => "list"},
+				              "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+				      "<br/>\n";
 			}
 		}
 		close $fd;
 
+		# finish last commit (warning: repetition!)
+		if (%co) {
+			print "</td>\n" .
+			      "<td class=\"link\">" .
+			      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
+			      " | " .
+			      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
+			print "</td>\n" .
+			      "</tr>\n";
+		}
+
 		print "</table>\n";
 	}
 
@@ -5267,7 +5352,9 @@
 		my $alternate = 1;
 		my $matches = 0;
 		$/ = "\n";
-		open my $fd, "-|", git_cmd(), 'grep', '-n', '-i', '-E', $searchtext, $co{'tree'};
+		open my $fd, "-|", git_cmd(), 'grep', '-n',
+			$search_use_regexp ? ('-E', '-i') : '-F',
+			$searchtext, $co{'tree'};
 		my $lastfile = '';
 		while (my $line = <$fd>) {
 			chomp $line;
@@ -5297,7 +5384,7 @@
 				print "<div class=\"binary\">Binary file</div>\n";
 			} else {
 				$ltext = untabify($ltext);
-				if ($ltext =~ m/^(.*)($searchtext)(.*)$/i) {
+				if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
 					$ltext = esc_html($1, -nbsp=>1);
 					$ltext .= '<span class="match">';
 					$ltext .= esc_html($2, -nbsp=>1);
@@ -5332,27 +5419,31 @@
 	git_header_html();
 	git_print_page_nav('','', $hash,$hash,$hash);
 	print <<EOT;
+<p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without
+regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,
+the pattern entered is recognized as the POSIX extended
+<a href="http://en.wikipedia.org/wiki/Regular_expression">regular expression</a> (also case
+insensitive).</p>
 <dl>
 <dt><b>commit</b></dt>
-<dd>The commit messages and authorship information will be scanned for the given string.</dd>
+<dd>The commit messages and authorship information will be scanned for the given pattern.</dd>
 EOT
 	my ($have_grep) = gitweb_check_feature('grep');
 	if ($have_grep) {
 		print <<EOT;
 <dt><b>grep</b></dt>
 <dd>All files in the currently selected tree (HEAD unless you are explicitly browsing
-    a different one) are searched for the given
-<a href="http://en.wikipedia.org/wiki/Regular_expression">regular expression</a>
-(POSIX extended) and the matches are listed. On large
-trees, this search can take a while and put some strain on the server, so please use it with
-some consideration.</dd>
+    a different one) are searched for the given pattern. On large trees, this search can take
+a while and put some strain on the server, so please use it with some consideration. Note that
+due to git-grep peculiarity, currently if regexp mode is turned off, the matches are
+case-sensitive.</dd>
 EOT
 	}
 	print <<EOT;
 <dt><b>author</b></dt>
-<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.</dd>
+<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.</dd>
 <dt><b>committer</b></dt>
-<dd>Name and e-mail of the committer and date of commit will be scanned for the given string.</dd>
+<dd>Name and e-mail of the committer and date of commit will be scanned for the given pattern.</dd>
 EOT
 	my ($have_pickaxe) = gitweb_check_feature('pickaxe');
 	if ($have_pickaxe) {
@@ -5360,7 +5451,8 @@
 <dt><b>pickaxe</b></dt>
 <dd>All commits that caused the string to appear or disappear from any file (changes that
 added, removed or "modified" the string) will be listed. This search can take a while and
-takes a lot of strain on the server, so please use it wisely.</dd>
+takes a lot of strain on the server, so please use it wisely. Note that since you may be
+interested even in changes just changing the case as well, this search is case sensitive.</dd>
 EOT
 	}
 	print "</dl>\n";
@@ -5379,7 +5471,7 @@
 
 	my @commitlist = parse_commits($hash, 101, (100 * $page));
 
-	my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, (100 * ($page+1)));
+	my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100);
 	my $next_link = '';
 	if ($#commitlist >= 100) {
 		$next_link =
@@ -5411,7 +5503,7 @@
 
 	# log/feed of current (HEAD) branch, log of given branch, history of file/directory
 	my $head = $hash || 'HEAD';
-	my @commitlist = parse_commits($head, 150, 0, undef, $file_name);
+	my @commitlist = parse_commits($head, 150, 0, $file_name);
 
 	my %latest_commit;
 	my %latest_date;
@@ -5531,7 +5623,7 @@
 			or next;
 
 		# print element (entry, item)
-		my $co_url = href(-full=>1, action=>"commit", hash=>$commit);
+		my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit);
 		if ($format eq 'rss') {
 			print "<item>\n" .
 			      "<title>" . esc_html($co{'title'}) . "</title>\n" .
diff --git a/hash-object.c b/hash-object.c
index 0a58f3f..61e7160 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -41,6 +41,7 @@
 	const char *prefix = NULL;
 	int prefix_length = -1;
 	int no_more_flags = 0;
+	int hashstdin = 0;
 
 	git_config(git_default_config);
 
@@ -65,13 +66,20 @@
 			else if (!strcmp(argv[i], "--help"))
 				usage(hash_object_usage);
 			else if (!strcmp(argv[i], "--stdin")) {
-				hash_stdin(type, write_object);
+				if (hashstdin)
+					die("Multiple --stdin arguments are not supported");
+				hashstdin = 1;
 			}
 			else
 				usage(hash_object_usage);
 		}
 		else {
 			const char *arg = argv[i];
+
+			if (hashstdin) {
+				hash_stdin(type, write_object);
+				hashstdin = 0;
+			}
 			if (0 <= prefix_length)
 				arg = prefix_filename(prefix, prefix_length,
 						      arg);
@@ -79,5 +87,7 @@
 			no_more_flags = 1;
 		}
 	}
+	if (hashstdin)
+		hash_stdin(type, write_object);
 	return 0;
 }
diff --git a/hash.c b/hash.c
index d9ec82f..1cd4c9d 100644
--- a/hash.c
+++ b/hash.c
@@ -9,7 +9,7 @@
  * the existing entry, or the empty slot if none existed. The caller
  * can then look at the (*ptr) to see whether it existed or not.
  */
-static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table)
+static struct hash_table_entry *lookup_hash_entry(unsigned int hash, const struct hash_table *table)
 {
 	unsigned int size = table->size, nr = hash % size;
 	struct hash_table_entry *array = table->array;
@@ -66,7 +66,7 @@
 	free(old_array);
 }
 
-void *lookup_hash(unsigned int hash, struct hash_table *table)
+void *lookup_hash(unsigned int hash, const struct hash_table *table)
 {
 	if (!table->array)
 		return NULL;
@@ -81,7 +81,7 @@
 	return insert_hash_entry(hash, ptr, table);
 }
 
-int for_each_hash(struct hash_table *table, int (*fn)(void *))
+int for_each_hash(const struct hash_table *table, int (*fn)(void *))
 {
 	int sum = 0;
 	unsigned int i;
diff --git a/hash.h b/hash.h
index a8b0fbb..69e33a4 100644
--- a/hash.h
+++ b/hash.h
@@ -28,9 +28,9 @@
 	struct hash_table_entry *array;
 };
 
-extern void *lookup_hash(unsigned int hash, struct hash_table *table);
+extern void *lookup_hash(unsigned int hash, const struct hash_table *table);
 extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table);
-extern int for_each_hash(struct hash_table *table, int (*fn)(void *));
+extern int for_each_hash(const struct hash_table *table, int (*fn)(void *));
 extern void free_hash(struct hash_table *table);
 
 static inline void init_hash(struct hash_table *table)
diff --git a/help.c b/help.c
index 95e7640..10298fb 100644
--- a/help.c
+++ b/help.c
@@ -7,44 +7,157 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "common-cmds.h"
+#include "parse-options.h"
+#include "run-command.h"
 
-static const char *help_default_format;
+static struct man_viewer_list {
+	void (*exec)(const char *);
+	struct man_viewer_list *next;
+} *man_viewer_list;
 
-static enum help_format {
-	man_format,
-	info_format,
-	web_format,
-} help_format = man_format;
+enum help_format {
+	HELP_FORMAT_MAN,
+	HELP_FORMAT_INFO,
+	HELP_FORMAT_WEB,
+};
 
-static void parse_help_format(const char *format)
+static int show_all = 0;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+			HELP_FORMAT_WEB),
+	OPT_SET_INT('i', "info", &help_format, "show info page",
+			HELP_FORMAT_INFO),
+	OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+	"git-help [--all] [--man|--web|--info] [command]",
+	NULL
+};
+
+static enum help_format parse_help_format(const char *format)
 {
-	if (!format) {
-		help_format = man_format;
-		return;
-	}
-	if (!strcmp(format, "man")) {
-		help_format = man_format;
-		return;
-	}
-	if (!strcmp(format, "info")) {
-		help_format = info_format;
-		return;
-	}
-	if (!strcmp(format, "web") || !strcmp(format, "html")) {
-		help_format = web_format;
-		return;
-	}
+	if (!strcmp(format, "man"))
+		return HELP_FORMAT_MAN;
+	if (!strcmp(format, "info"))
+		return HELP_FORMAT_INFO;
+	if (!strcmp(format, "web") || !strcmp(format, "html"))
+		return HELP_FORMAT_WEB;
 	die("unrecognized help format '%s'", format);
 }
 
+static int check_emacsclient_version(void)
+{
+	struct strbuf buffer = STRBUF_INIT;
+	struct child_process ec_process;
+	const char *argv_ec[] = { "emacsclient", "--version", NULL };
+	int version;
+
+	/* emacsclient prints its version number on stderr */
+	memset(&ec_process, 0, sizeof(ec_process));
+	ec_process.argv = argv_ec;
+	ec_process.err = -1;
+	ec_process.stdout_to_stderr = 1;
+	if (start_command(&ec_process)) {
+		fprintf(stderr, "Failed to start emacsclient.\n");
+		return -1;
+	}
+	strbuf_read(&buffer, ec_process.err, 20);
+	close(ec_process.err);
+
+	/*
+	 * Don't bother checking return value, because "emacsclient --version"
+	 * seems to always exits with code 1.
+	 */
+	finish_command(&ec_process);
+
+	if (prefixcmp(buffer.buf, "emacsclient")) {
+		fprintf(stderr, "Failed to parse emacsclient version.\n");
+		strbuf_release(&buffer);
+		return -1;
+	}
+
+	strbuf_remove(&buffer, 0, strlen("emacsclient"));
+	version = atoi(buffer.buf);
+
+	if (version < 22) {
+		fprintf(stderr,
+			"emacsclient version '%d' too old (< 22).\n",
+			version);
+		strbuf_release(&buffer);
+		return -1;
+	}
+
+	strbuf_release(&buffer);
+	return 0;
+}
+
+static void exec_woman_emacs(const char *page)
+{
+	if (!check_emacsclient_version()) {
+		/* This works only with emacsclient version >= 22. */
+		struct strbuf man_page = STRBUF_INIT;
+		strbuf_addf(&man_page, "(woman \"%s\")", page);
+		execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL);
+	}
+}
+
+static void exec_man_konqueror(const char *page)
+{
+	const char *display = getenv("DISPLAY");
+	if (display && *display) {
+		struct strbuf man_page = STRBUF_INIT;
+		strbuf_addf(&man_page, "man:%s(1)", page);
+		execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL);
+	}
+}
+
+static void exec_man_man(const char *page)
+{
+	execlp("man", "man", page, NULL);
+}
+
+static void do_add_man_viewer(void (*exec)(const char *))
+{
+	struct man_viewer_list **p = &man_viewer_list;
+
+	while (*p)
+		p = &((*p)->next);
+	*p = xmalloc(sizeof(**p));
+	(*p)->next = NULL;
+	(*p)->exec = exec;
+}
+
+static int add_man_viewer(const char *value)
+{
+	if (!strcasecmp(value, "man"))
+		do_add_man_viewer(exec_man_man);
+	else if (!strcasecmp(value, "woman"))
+		do_add_man_viewer(exec_woman_emacs);
+	else if (!strcasecmp(value, "konqueror"))
+		do_add_man_viewer(exec_man_konqueror);
+	else
+		warning("'%s': unsupported man viewer.", value);
+
+	return 0;
+}
+
 static int git_help_config(const char *var, const char *value)
 {
 	if (!strcmp(var, "help.format")) {
 		if (!value)
 			return config_error_nonbool(var);
-		help_default_format = xstrdup(value);
+		help_format = parse_help_format(value);
 		return 0;
 	}
+	if (!strcmp(var, "man.viewer")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer(value);
+	}
 	return git_default_config(var, value);
 }
 
@@ -205,7 +318,7 @@
 	return longest;
 }
 
-static void list_commands(void)
+static unsigned int load_command_list(void)
 {
 	unsigned int longest = 0;
 	unsigned int len;
@@ -245,6 +358,14 @@
 	uniq(&other_cmds);
 	exclude_cmds(&other_cmds, &main_cmds);
 
+	return longest;
+}
+
+static void list_commands(void)
+{
+	unsigned int longest = load_command_list();
+	const char *exec_path = git_exec_path();
+
 	if (main_cmds.cnt) {
 		printf("available git commands in '%s'\n", exec_path);
 		printf("----------------------------");
@@ -279,6 +400,22 @@
 	}
 }
 
+static int is_in_cmdlist(struct cmdnames *c, const char *s)
+{
+	int i;
+	for (i = 0; i < c->cnt; i++)
+		if (!strcmp(s, c->names[i]->name))
+			return 1;
+	return 0;
+}
+
+static int is_git_command(const char *s)
+{
+	load_command_list();
+	return is_in_cmdlist(&main_cmds, s) ||
+		is_in_cmdlist(&other_cmds, s);
+}
+
 static const char *cmd_to_page(const char *git_cmd)
 {
 	if (!git_cmd)
@@ -318,9 +455,16 @@
 
 static void show_man_page(const char *git_cmd)
 {
+	struct man_viewer_list *viewer;
 	const char *page = cmd_to_page(git_cmd);
+
 	setup_man_path();
-	execlp("man", "man", page, NULL);
+	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+	{
+		viewer->exec(page); /* will return when unable */
+	}
+	exec_man_man(page);
+	die("no man viewer handled the request");
 }
 
 static void show_info_page(const char *git_cmd)
@@ -330,10 +474,26 @@
 	execlp("info", "info", "gitman", page, NULL);
 }
 
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+	struct stat st;
+
+	/* Check that we have a git documentation directory. */
+	if (stat(GIT_HTML_PATH "/git.html", &st) || !S_ISREG(st.st_mode))
+		die("'%s': not a documentation directory.", GIT_HTML_PATH);
+
+	strbuf_init(page_path, 0);
+	strbuf_addf(page_path, GIT_HTML_PATH "/%s.html", page);
+}
+
 static void show_html_page(const char *git_cmd)
 {
 	const char *page = cmd_to_page(git_cmd);
-	execl_git_cmd("help--browse", page, NULL);
+	struct strbuf page_path; /* it leaks but we exec bellow */
+
+	get_html_page_path(&page_path, page);
+
+	execl_git_cmd("web--browse", "-c", "help.browser", page_path.buf, NULL);
 }
 
 void help_unknown_cmd(const char *cmd)
@@ -350,50 +510,43 @@
 
 int cmd_help(int argc, const char **argv, const char *prefix)
 {
-	const char *help_cmd = argv[1];
+	int nongit;
+	const char *alias;
 
-	if (argc < 2) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_common_cmds_help();
-		exit(0);
-	}
+	setup_git_directory_gently(&nongit);
+	git_config(git_help_config);
 
-	if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
+	argc = parse_options(argc, argv, builtin_help_options,
+			builtin_help_usage, 0);
+
+	if (show_all) {
 		printf("usage: %s\n\n", git_usage_string);
 		list_commands();
+		return 0;
 	}
 
-	else if (!strcmp(help_cmd, "--web") || !strcmp(help_cmd, "-w")) {
-		show_html_page(argc > 2 ? argv[2] : NULL);
+	if (!argv[0]) {
+		printf("usage: %s\n\n", git_usage_string);
+		list_common_cmds_help();
+		return 0;
 	}
 
-	else if (!strcmp(help_cmd, "--info") || !strcmp(help_cmd, "-i")) {
-		show_info_page(argc > 2 ? argv[2] : NULL);
+	alias = alias_lookup(argv[0]);
+	if (alias && !is_git_command(argv[0])) {
+		printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+		return 0;
 	}
 
-	else if (!strcmp(help_cmd, "--man") || !strcmp(help_cmd, "-m")) {
-		show_man_page(argc > 2 ? argv[2] : NULL);
-	}
-
-	else {
-		int nongit;
-
-		setup_git_directory_gently(&nongit);
-		git_config(git_help_config);
-		if (help_default_format)
-			parse_help_format(help_default_format);
-
-		switch (help_format) {
-		case man_format:
-			show_man_page(help_cmd);
-			break;
-		case info_format:
-			show_info_page(help_cmd);
-			break;
-		case web_format:
-			show_html_page(help_cmd);
-			break;
-		}
+	switch (help_format) {
+	case HELP_FORMAT_MAN:
+		show_man_page(argv[0]);
+		break;
+	case HELP_FORMAT_INFO:
+		show_info_page(argv[0]);
+		break;
+	case HELP_FORMAT_WEB:
+		show_html_page(argv[0]);
+		break;
 	}
 
 	return 0;
diff --git a/http-push.c b/http-push.c
index e3e34de..5b23038 100644
--- a/http-push.c
+++ b/http-push.c
@@ -664,8 +664,7 @@
 		close(request->local_fileno);
 	if (request->local_stream)
 		fclose(request->local_stream);
-	if (request->url != NULL)
-		free(request->url);
+	free(request->url);
 	free(request);
 }
 
@@ -1283,10 +1282,8 @@
 	strbuf_release(&in_buffer);
 
 	if (lock->token == NULL || lock->timeout <= 0) {
-		if (lock->token != NULL)
-			free(lock->token);
-		if (lock->owner != NULL)
-			free(lock->owner);
+		free(lock->token);
+		free(lock->owner);
 		free(url);
 		free(lock);
 		lock = NULL;
@@ -1344,8 +1341,7 @@
 			prev->next = prev->next->next;
 	}
 
-	if (lock->owner != NULL)
-		free(lock->owner);
+	free(lock->owner);
 	free(lock->url);
 	free(lock->token);
 	free(lock);
@@ -2035,8 +2031,7 @@
 	}
 	free(url);
 
-	if (*symref != NULL)
-		free(*symref);
+	free(*symref);
 	*symref = NULL;
 	hashclr(sha1);
 
@@ -2138,6 +2133,8 @@
 
 	/* Send delete request */
 	fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
+	if (dry_run)
+		return 0;
 	url = xmalloc(strlen(remote->url) + strlen(remote_ref->name) + 1);
 	sprintf(url, "%s%s", remote->url, remote_ref->name);
 	slot = get_active_slot();
@@ -2311,6 +2308,16 @@
 
 		if (!ref->peer_ref)
 			continue;
+
+		if (is_zero_sha1(ref->peer_ref->new_sha1)) {
+			if (delete_remote_branch(ref->name, 1) == -1) {
+				error("Could not remove %s", ref->name);
+				rc = -4;
+			}
+			new_refs++;
+			continue;
+		}
+
 		if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
 			if (push_verbosely || 1)
 				fprintf(stderr, "'%s': up-to-date\n", ref->name);
@@ -2342,11 +2349,6 @@
 			}
 		}
 		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		if (is_zero_sha1(ref->new_sha1)) {
-			error("cannot happen anymore");
-			rc = -3;
-			continue;
-		}
 		new_refs++;
 		strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 		new_hex = sha1_to_hex(ref->new_sha1);
@@ -2390,7 +2392,8 @@
 
 		/* Generate a list of objects that need to be pushed */
 		pushing = 0;
-		prepare_revision_walk(&revs);
+		if (prepare_revision_walk(&revs))
+			die("revision walk setup failed");
 		mark_edges_uninteresting(revs.commits);
 		objects_to_send = get_delta(&revs, ref_lock);
 		finish_all_active_slots();
@@ -2434,8 +2437,7 @@
 	}
 
  cleanup:
-	if (rewritten_url)
-		free(rewritten_url);
+	free(rewritten_url);
 	if (info_ref_lock)
 		unlock_remote(info_ref_lock);
 	free(remote);
diff --git a/imap-send.c b/imap-send.c
index 9025d9a..db65597 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -472,7 +472,7 @@
 	if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
 		free( cmd->cmd );
 		free( cmd );
-		if (cb && cb->data)
+		if (cb)
 			free( cb->data );
 		return NULL;
 	}
@@ -858,8 +858,7 @@
 		  normal:
 			if (cmdp->cb.done)
 				cmdp->cb.done( ctx, cmdp, resp );
-			if (cmdp->cb.data)
-				free( cmdp->cb.data );
+			free( cmdp->cb.data );
 			free( cmdp->cmd );
 			free( cmdp );
 			if (!tcmd || tcmd == cmdp)
@@ -1303,6 +1302,13 @@
 		fprintf( stderr, "no imap store specified\n" );
 		return 1;
 	}
+	if (!server.host) {
+		if (!server.tunnel) {
+			fprintf( stderr, "no imap host specified\n" );
+			return 1;
+		}
+		server.host = "tunnel";
+	}
 
 	/* read the messages */
 	if (!read_message( stdin, &all_msgs )) {
diff --git a/index-pack.c b/index-pack.c
index 9fd6982..9c0c278 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -7,9 +7,10 @@
 #include "tag.h"
 #include "tree.h"
 #include "progress.h"
+#include "fsck.h"
 
 static const char index_pack_usage[] =
-"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
+"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
 
 struct object_entry
 {
@@ -31,6 +32,9 @@
  */
 #define UNION_BASE_SZ	20
 
+#define FLAG_LINK (1u<<20)
+#define FLAG_CHECKED (1u<<21)
+
 struct delta_entry
 {
 	union delta_base base;
@@ -44,6 +48,7 @@
 static int nr_resolved_deltas;
 
 static int from_stdin;
+static int strict;
 static int verbose;
 
 static struct progress *progress;
@@ -56,6 +61,48 @@
 static uint32_t input_crc32;
 static int input_fd, output_fd, pack_fd;
 
+static int mark_link(struct object *obj, int type, void *data)
+{
+	if (!obj)
+		return -1;
+
+	if (type != OBJ_ANY && obj->type != type)
+		die("object type mismatch at %s", sha1_to_hex(obj->sha1));
+
+	obj->flags |= FLAG_LINK;
+	return 0;
+}
+
+/* The content of each linked object must have been checked
+   or it must be already present in the object database */
+static void check_object(struct object *obj)
+{
+	if (!obj)
+		return;
+
+	if (!(obj->flags & FLAG_LINK))
+		return;
+
+	if (!(obj->flags & FLAG_CHECKED)) {
+		unsigned long size;
+		int type = sha1_object_info(obj->sha1, &size);
+		if (type != obj->type || type <= 0)
+			die("object of unexpected type");
+		obj->flags |= FLAG_CHECKED;
+		return;
+	}
+}
+
+static void check_objects(void)
+{
+	unsigned i, max;
+
+	max = get_max_object_index();
+	for (i = 0; i < max; i++)
+		check_object(get_indexed_object(i));
+}
+
+
 /* Discard current buffer used content. */
 static void flush(void)
 {
@@ -341,6 +388,41 @@
 			die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
 		free(has_data);
 	}
+	if (strict) {
+		if (type == OBJ_BLOB) {
+			struct blob *blob = lookup_blob(sha1);
+			if (blob)
+				blob->object.flags |= FLAG_CHECKED;
+			else
+				die("invalid blob object %s", sha1_to_hex(sha1));
+		} else {
+			struct object *obj;
+			int eaten;
+			void *buf = (void *) data;
+
+			/*
+			 * we do not need to free the memory here, as the
+			 * buf is deleted by the caller.
+			 */
+			obj = parse_object_buffer(sha1, type, size, buf, &eaten);
+			if (!obj)
+				die("invalid %s", typename(type));
+			if (fsck_object(obj, 1, fsck_error_function))
+				die("Error in object");
+			if (fsck_walk(obj, mark_link, 0))
+				die("Not all child objects of %s are reachable", sha1_to_hex(obj->sha1));
+
+			if (obj->type == OBJ_TREE) {
+				struct tree *item = (struct tree *) obj;
+				item->buffer = NULL;
+			}
+			if (obj->type == OBJ_COMMIT) {
+				struct commit *commit = (struct commit *) obj;
+				commit->buffer = NULL;
+			}
+			obj->flags |= FLAG_CHECKED;
+		}
+	}
 }
 
 static void resolve_delta(struct object_entry *delta_obj, void *base_data,
@@ -714,6 +796,8 @@
 				from_stdin = 1;
 			} else if (!strcmp(arg, "--fix-thin")) {
 				fix_thin_pack = 1;
+			} else if (!strcmp(arg, "--strict")) {
+				strict = 1;
 			} else if (!strcmp(arg, "--keep")) {
 				keep_msg = "";
 			} else if (!prefixcmp(arg, "--keep=")) {
@@ -812,6 +896,8 @@
 			    nr_deltas - nr_resolved_deltas);
 	}
 	free(deltas);
+	if (strict)
+		check_objects();
 
 	idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
 	for (i = 0; i < nr_objects; i++)
diff --git a/interpolate.c b/interpolate.c
index 6ef53f2..7f03bd9 100644
--- a/interpolate.c
+++ b/interpolate.c
@@ -11,8 +11,7 @@
 	char *oldval = table[slot].value;
 	char *newval = NULL;
 
-	if (oldval)
-		free(oldval);
+	free(oldval);
 
 	if (value)
 		newval = xstrdup(value);
diff --git a/list-objects.c b/list-objects.c
index 4ef58e7..c8b8375 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -18,6 +18,8 @@
 
 	if (!revs->blob_objects)
 		return;
+	if (!obj)
+		die("bad blob object");
 	if (obj->flags & (UNINTERESTING | SEEN))
 		return;
 	obj->flags |= SEEN;
@@ -69,6 +71,8 @@
 
 	if (!revs->tree_objects)
 		return;
+	if (!obj)
+		die("bad tree object");
 	if (obj->flags & (UNINTERESTING | SEEN))
 		return;
 	if (parse_tree(tree) < 0)
diff --git a/ll-merge.c b/ll-merge.c
new file mode 100644
index 0000000..5ae7433
--- /dev/null
+++ b/ll-merge.c
@@ -0,0 +1,379 @@
+/*
+ * Low level 3-way in-core file merge.
+ *
+ * Copyright (c) 2007 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "attr.h"
+#include "xdiff-interface.h"
+#include "run-command.h"
+#include "interpolate.h"
+#include "ll-merge.h"
+
+struct ll_merge_driver;
+
+typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
+			   mmbuffer_t *result,
+			   const char *path,
+			   mmfile_t *orig,
+			   mmfile_t *src1, const char *name1,
+			   mmfile_t *src2, const char *name2,
+			   int virtual_ancestor);
+
+struct ll_merge_driver {
+	const char *name;
+	const char *description;
+	ll_merge_fn fn;
+	const char *recursive;
+	struct ll_merge_driver *next;
+	char *cmdline;
+};
+
+/*
+ * Built-in low-levels
+ */
+static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
+			   mmbuffer_t *result,
+			   const char *path_unused,
+			   mmfile_t *orig,
+			   mmfile_t *src1, const char *name1,
+			   mmfile_t *src2, const char *name2,
+			   int virtual_ancestor)
+{
+	/*
+	 * The tentative merge result is "ours" for the final round,
+	 * or common ancestor for an internal merge.  Still return
+	 * "conflicted merge" status.
+	 */
+	mmfile_t *stolen = virtual_ancestor ? orig : src1;
+
+	result->ptr = stolen->ptr;
+	result->size = stolen->size;
+	stolen->ptr = NULL;
+	return 1;
+}
+
+static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
+			mmbuffer_t *result,
+			const char *path_unused,
+			mmfile_t *orig,
+			mmfile_t *src1, const char *name1,
+			mmfile_t *src2, const char *name2,
+			int virtual_ancestor)
+{
+	xpparam_t xpp;
+
+	if (buffer_is_binary(orig->ptr, orig->size) ||
+	    buffer_is_binary(src1->ptr, src1->size) ||
+	    buffer_is_binary(src2->ptr, src2->size)) {
+		warning("Cannot merge binary files: %s vs. %s\n",
+			name1, name2);
+		return ll_binary_merge(drv_unused, result,
+				       path_unused,
+				       orig, src1, name1,
+				       src2, name2,
+				       virtual_ancestor);
+	}
+
+	memset(&xpp, 0, sizeof(xpp));
+	return xdl_merge(orig,
+			 src1, name1,
+			 src2, name2,
+			 &xpp, XDL_MERGE_ZEALOUS,
+			 result);
+}
+
+static int ll_union_merge(const struct ll_merge_driver *drv_unused,
+			  mmbuffer_t *result,
+			  const char *path_unused,
+			  mmfile_t *orig,
+			  mmfile_t *src1, const char *name1,
+			  mmfile_t *src2, const char *name2,
+			  int virtual_ancestor)
+{
+	char *src, *dst;
+	long size;
+	const int marker_size = 7;
+
+	int status = ll_xdl_merge(drv_unused, result, path_unused,
+				  orig, src1, NULL, src2, NULL,
+				  virtual_ancestor);
+	if (status <= 0)
+		return status;
+	size = result->size;
+	src = dst = result->ptr;
+	while (size) {
+		char ch;
+		if ((marker_size < size) &&
+		    (*src == '<' || *src == '=' || *src == '>')) {
+			int i;
+			ch = *src;
+			for (i = 0; i < marker_size; i++)
+				if (src[i] != ch)
+					goto not_a_marker;
+			if (src[marker_size] != '\n')
+				goto not_a_marker;
+			src += marker_size + 1;
+			size -= marker_size + 1;
+			continue;
+		}
+	not_a_marker:
+		do {
+			ch = *src++;
+			*dst++ = ch;
+			size--;
+		} while (ch != '\n' && size);
+	}
+	result->size = dst - result->ptr;
+	return 0;
+}
+
+#define LL_BINARY_MERGE 0
+#define LL_TEXT_MERGE 1
+#define LL_UNION_MERGE 2
+static struct ll_merge_driver ll_merge_drv[] = {
+	{ "binary", "built-in binary merge", ll_binary_merge },
+	{ "text", "built-in 3-way text merge", ll_xdl_merge },
+	{ "union", "built-in union merge", ll_union_merge },
+};
+
+static void create_temp(mmfile_t *src, char *path)
+{
+	int fd;
+
+	strcpy(path, ".merge_file_XXXXXX");
+	fd = xmkstemp(path);
+	if (write_in_full(fd, src->ptr, src->size) != src->size)
+		die("unable to write temp-file");
+	close(fd);
+}
+
+/*
+ * User defined low-level merge driver support.
+ */
+static int ll_ext_merge(const struct ll_merge_driver *fn,
+			mmbuffer_t *result,
+			const char *path,
+			mmfile_t *orig,
+			mmfile_t *src1, const char *name1,
+			mmfile_t *src2, const char *name2,
+			int virtual_ancestor)
+{
+	char temp[3][50];
+	char cmdbuf[2048];
+	struct interp table[] = {
+		{ "%O" },
+		{ "%A" },
+		{ "%B" },
+	};
+	struct child_process child;
+	const char *args[20];
+	int status, fd, i;
+	struct stat st;
+
+	if (fn->cmdline == NULL)
+		die("custom merge driver %s lacks command line.", fn->name);
+
+	result->ptr = NULL;
+	result->size = 0;
+	create_temp(orig, temp[0]);
+	create_temp(src1, temp[1]);
+	create_temp(src2, temp[2]);
+
+	interp_set_entry(table, 0, temp[0]);
+	interp_set_entry(table, 1, temp[1]);
+	interp_set_entry(table, 2, temp[2]);
+
+	interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+
+	memset(&child, 0, sizeof(child));
+	child.argv = args;
+	args[0] = "sh";
+	args[1] = "-c";
+	args[2] = cmdbuf;
+	args[3] = NULL;
+
+	status = run_command(&child);
+	if (status < -ERR_RUN_COMMAND_FORK)
+		; /* failure in run-command */
+	else
+		status = -status;
+	fd = open(temp[1], O_RDONLY);
+	if (fd < 0)
+		goto bad;
+	if (fstat(fd, &st))
+		goto close_bad;
+	result->size = st.st_size;
+	result->ptr = xmalloc(result->size + 1);
+	if (read_in_full(fd, result->ptr, result->size) != result->size) {
+		free(result->ptr);
+		result->ptr = NULL;
+		result->size = 0;
+	}
+ close_bad:
+	close(fd);
+ bad:
+	for (i = 0; i < 3; i++)
+		unlink(temp[i]);
+	return status;
+}
+
+/*
+ * merge.default and merge.driver configuration items
+ */
+static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
+static const char *default_ll_merge;
+
+static int read_merge_config(const char *var, const char *value)
+{
+	struct ll_merge_driver *fn;
+	const char *ep, *name;
+	int namelen;
+
+	if (!strcmp(var, "merge.default")) {
+		if (value)
+			default_ll_merge = strdup(value);
+		return 0;
+	}
+
+	/*
+	 * We are not interested in anything but "merge.<name>.variable";
+	 * especially, we do not want to look at variables such as
+	 * "merge.summary", "merge.tool", and "merge.verbosity".
+	 */
+	if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
+		return 0;
+
+	/*
+	 * Find existing one as we might be processing merge.<name>.var2
+	 * after seeing merge.<name>.var1.
+	 */
+	name = var + 6;
+	namelen = ep - name;
+	for (fn = ll_user_merge; fn; fn = fn->next)
+		if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+			break;
+	if (!fn) {
+		fn = xcalloc(1, sizeof(struct ll_merge_driver));
+		fn->name = xmemdupz(name, namelen);
+		fn->fn = ll_ext_merge;
+		*ll_user_merge_tail = fn;
+		ll_user_merge_tail = &(fn->next);
+	}
+
+	ep++;
+
+	if (!strcmp("name", ep)) {
+		if (!value)
+			return error("%s: lacks value", var);
+		fn->description = strdup(value);
+		return 0;
+	}
+
+	if (!strcmp("driver", ep)) {
+		if (!value)
+			return error("%s: lacks value", var);
+		/*
+		 * merge.<name>.driver specifies the command line:
+		 *
+		 *	command-line
+		 *
+		 * The command-line will be interpolated with the following
+		 * tokens and is given to the shell:
+		 *
+		 *    %O - temporary file name for the merge base.
+		 *    %A - temporary file name for our version.
+		 *    %B - temporary file name for the other branches' version.
+		 *
+		 * The external merge driver should write the results in the
+		 * file named by %A, and signal that it has done with zero exit
+		 * status.
+		 */
+		fn->cmdline = strdup(value);
+		return 0;
+	}
+
+	if (!strcmp("recursive", ep)) {
+		if (!value)
+			return error("%s: lacks value", var);
+		fn->recursive = strdup(value);
+		return 0;
+	}
+
+	return 0;
+}
+
+static void initialize_ll_merge(void)
+{
+	if (ll_user_merge_tail)
+		return;
+	ll_user_merge_tail = &ll_user_merge;
+	git_config(read_merge_config);
+}
+
+static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
+{
+	struct ll_merge_driver *fn;
+	const char *name;
+	int i;
+
+	initialize_ll_merge();
+
+	if (ATTR_TRUE(merge_attr))
+		return &ll_merge_drv[LL_TEXT_MERGE];
+	else if (ATTR_FALSE(merge_attr))
+		return &ll_merge_drv[LL_BINARY_MERGE];
+	else if (ATTR_UNSET(merge_attr)) {
+		if (!default_ll_merge)
+			return &ll_merge_drv[LL_TEXT_MERGE];
+		else
+			name = default_ll_merge;
+	}
+	else
+		name = merge_attr;
+
+	for (fn = ll_user_merge; fn; fn = fn->next)
+		if (!strcmp(fn->name, name))
+			return fn;
+
+	for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
+		if (!strcmp(ll_merge_drv[i].name, name))
+			return &ll_merge_drv[i];
+
+	/* default to the 3-way */
+	return &ll_merge_drv[LL_TEXT_MERGE];
+}
+
+static const char *git_path_check_merge(const char *path)
+{
+	static struct git_attr_check attr_merge_check;
+
+	if (!attr_merge_check.attr)
+		attr_merge_check.attr = git_attr("merge", 5);
+
+	if (git_checkattr(path, 1, &attr_merge_check))
+		return NULL;
+	return attr_merge_check.value;
+}
+
+int ll_merge(mmbuffer_t *result_buf,
+	     const char *path,
+	     mmfile_t *ancestor,
+	     mmfile_t *ours, const char *our_label,
+	     mmfile_t *theirs, const char *their_label,
+	     int virtual_ancestor)
+{
+	const char *ll_driver_name;
+	const struct ll_merge_driver *driver;
+
+	ll_driver_name = git_path_check_merge(path);
+	driver = find_ll_merge_driver(ll_driver_name);
+
+	if (virtual_ancestor && driver->recursive)
+		driver = find_ll_merge_driver(driver->recursive);
+	return driver->fn(driver, result_buf, path,
+			  ancestor,
+			  ours, our_label,
+			  theirs, their_label, virtual_ancestor);
+}
diff --git a/ll-merge.h b/ll-merge.h
new file mode 100644
index 0000000..5388422
--- /dev/null
+++ b/ll-merge.h
@@ -0,0 +1,15 @@
+/*
+ * Low level 3-way in-core file merge.
+ */
+
+#ifndef LL_MERGE_H
+#define LL_MERGE_H
+
+int ll_merge(mmbuffer_t *result_buf,
+	     const char *path,
+	     mmfile_t *ancestor,
+	     mmfile_t *ours, const char *our_label,
+	     mmfile_t *theirs, const char *their_label,
+	     int virtual_ancestor);
+
+#endif
diff --git a/log-tree.c b/log-tree.c
index dd94f39..5b29639 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -137,6 +137,77 @@
 	return 0;
 }
 
+void log_write_email_headers(struct rev_info *opt, const char *name,
+			     const char **subject_p,
+			     const char **extra_headers_p,
+			     int *need_8bit_cte_p)
+{
+	const char *subject = NULL;
+	const char *extra_headers = opt->extra_headers;
+
+	*need_8bit_cte_p = 0; /* unknown */
+	if (opt->total > 0) {
+		static char buffer[64];
+		snprintf(buffer, sizeof(buffer),
+			 "Subject: [%s %0*d/%d] ",
+			 opt->subject_prefix,
+			 digits_in_number(opt->total),
+			 opt->nr, opt->total);
+		subject = buffer;
+	} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
+		static char buffer[256];
+		snprintf(buffer, sizeof(buffer),
+			 "Subject: [%s] ",
+			 opt->subject_prefix);
+		subject = buffer;
+	} else {
+		subject = "Subject: ";
+	}
+
+	printf("From %s Mon Sep 17 00:00:00 2001\n", name);
+	if (opt->message_id)
+		printf("Message-Id: <%s>\n", opt->message_id);
+	if (opt->ref_message_id)
+		printf("In-Reply-To: <%s>\nReferences: <%s>\n",
+		       opt->ref_message_id, opt->ref_message_id);
+	if (opt->mime_boundary) {
+		static char subject_buffer[1024];
+		static char buffer[1024];
+		*need_8bit_cte_p = -1; /* NEVER */
+		snprintf(subject_buffer, sizeof(subject_buffer) - 1,
+			 "%s"
+			 "MIME-Version: 1.0\n"
+			 "Content-Type: multipart/mixed;"
+			 " boundary=\"%s%s\"\n"
+			 "\n"
+			 "This is a multi-part message in MIME "
+			 "format.\n"
+			 "--%s%s\n"
+			 "Content-Type: text/plain; "
+			 "charset=UTF-8; format=fixed\n"
+			 "Content-Transfer-Encoding: 8bit\n\n",
+			 extra_headers ? extra_headers : "",
+			 mime_boundary_leader, opt->mime_boundary,
+			 mime_boundary_leader, opt->mime_boundary);
+		extra_headers = subject_buffer;
+
+		snprintf(buffer, sizeof(buffer) - 1,
+			 "--%s%s\n"
+			 "Content-Type: text/x-patch;"
+			 " name=\"%s.diff\"\n"
+			 "Content-Transfer-Encoding: 8bit\n"
+			 "Content-Disposition: %s;"
+			 " filename=\"%s.diff\"\n\n",
+			 mime_boundary_leader, opt->mime_boundary,
+			 name,
+			 opt->no_inline ? "attachment" : "inline",
+			 name);
+		opt->diffopt.stat_sep = buffer;
+	}
+	*subject_p = subject;
+	*extra_headers_p = extra_headers;
+}
+
 void show_log(struct rev_info *opt, const char *sep)
 {
 	struct strbuf msgbuf;
@@ -150,10 +221,12 @@
 
 	opt->loginfo = NULL;
 	if (!opt->verbose_header) {
-		if (opt->left_right) {
-			if (commit->object.flags & BOUNDARY)
-				putchar('-');
-			else if (commit->object.flags & SYMMETRIC_LEFT)
+		if (commit->object.flags & BOUNDARY)
+			putchar('-');
+		else if (commit->object.flags & UNINTERESTING)
+			putchar('^');
+		else if (opt->left_right) {
+			if (commit->object.flags & SYMMETRIC_LEFT)
 				putchar('<');
 			else
 				putchar('>');
@@ -187,72 +260,17 @@
 	 */
 
 	if (opt->commit_format == CMIT_FMT_EMAIL) {
-		char *sha1 = sha1_to_hex(commit->object.sha1);
-		if (opt->total > 0) {
-			static char buffer[64];
-			snprintf(buffer, sizeof(buffer),
-					"Subject: [%s %0*d/%d] ",
-					opt->subject_prefix,
-					digits_in_number(opt->total),
-					opt->nr, opt->total);
-			subject = buffer;
-		} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
-			static char buffer[256];
-			snprintf(buffer, sizeof(buffer),
-					"Subject: [%s] ",
-					opt->subject_prefix);
-			subject = buffer;
-		} else {
-			subject = "Subject: ";
-		}
-
-		printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
-		if (opt->message_id)
-			printf("Message-Id: <%s>\n", opt->message_id);
-		if (opt->ref_message_id)
-			printf("In-Reply-To: <%s>\nReferences: <%s>\n",
-			       opt->ref_message_id, opt->ref_message_id);
-		if (opt->mime_boundary) {
-			static char subject_buffer[1024];
-			static char buffer[1024];
-
-			need_8bit_cte = -1; /* never */
-			snprintf(subject_buffer, sizeof(subject_buffer) - 1,
-				 "%s"
-				 "MIME-Version: 1.0\n"
-				 "Content-Type: multipart/mixed;"
-				 " boundary=\"%s%s\"\n"
-				 "\n"
-				 "This is a multi-part message in MIME "
-				 "format.\n"
-				 "--%s%s\n"
-				 "Content-Type: text/plain; "
-				 "charset=UTF-8; format=fixed\n"
-				 "Content-Transfer-Encoding: 8bit\n\n",
-				 extra_headers ? extra_headers : "",
-				 mime_boundary_leader, opt->mime_boundary,
-				 mime_boundary_leader, opt->mime_boundary);
-			extra_headers = subject_buffer;
-
-			snprintf(buffer, sizeof(buffer) - 1,
-				 "--%s%s\n"
-				 "Content-Type: text/x-patch;"
-				 " name=\"%s.diff\"\n"
-				 "Content-Transfer-Encoding: 8bit\n"
-				 "Content-Disposition: %s;"
-				 " filename=\"%s.diff\"\n\n",
-				 mime_boundary_leader, opt->mime_boundary,
-				 sha1,
-				 opt->no_inline ? "attachment" : "inline",
-				 sha1);
-			opt->diffopt.stat_sep = buffer;
-		}
+		log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
+					&subject, &extra_headers,
+					&need_8bit_cte);
 	} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
 		if (opt->commit_format != CMIT_FMT_ONELINE)
 			fputs("commit ", stdout);
 		if (commit->object.flags & BOUNDARY)
 			putchar('-');
+		else if (commit->object.flags & UNINTERESTING)
+			putchar('^');
 		else if (opt->left_right) {
 			if (commit->object.flags & SYMMETRIC_LEFT)
 				putchar('<');
@@ -281,6 +299,9 @@
 		}
 	}
 
+	if (!commit->buffer)
+		return;
+
 	/*
 	 * And then the pretty-printed message itself
 	 */
diff --git a/log-tree.h b/log-tree.h
index b33f7cd..8946ff3 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -13,5 +13,9 @@
 int log_tree_opt_parse(struct rev_info *, const char **, int);
 void show_log(struct rev_info *opt, const char *sep);
 void show_decorations(struct commit *commit);
+void log_write_email_headers(struct rev_info *opt, const char *name,
+			     const char **subject_p,
+			     const char **extra_headers_p,
+			     int *need_8bit_cte_p);
 
 #endif
diff --git a/merge-index.c b/merge-index.c
index fa719cb..7491c56 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -48,7 +48,7 @@
 			break;
 		found++;
 		strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
-		sprintf(ownbuf[stage], "%o", ntohl(ce->ce_mode));
+		sprintf(ownbuf[stage], "%o", ce->ce_mode);
 		arguments[stage] = hexbuf[stage];
 		arguments[stage + 4] = ownbuf[stage];
 	} while (++pos < active_nr);
@@ -91,7 +91,7 @@
 	signal(SIGCHLD, SIG_DFL);
 
 	if (argc < 3)
-		usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
+		usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
 
 	setup_git_directory();
 	read_cache();
diff --git a/merge-recursive.h b/merge-recursive.h
new file mode 100644
index 0000000..f37630a
--- /dev/null
+++ b/merge-recursive.h
@@ -0,0 +1,20 @@
+#ifndef MERGE_RECURSIVE_H
+#define MERGE_RECURSIVE_H
+
+int merge_recursive(struct commit *h1,
+		    struct commit *h2,
+		    const char *branch1,
+		    const char *branch2,
+		    struct commit_list *ancestors,
+		    struct commit **result);
+
+int merge_trees(struct tree *head,
+		struct tree *merge,
+		struct tree *common,
+		const char *branch1,
+		const char *branch2,
+		struct tree **result);
+
+struct tree *write_tree_from_memory(void);
+
+#endif
diff --git a/merge-tree.c b/merge-tree.c
index e083246..02fc10f 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -168,7 +168,13 @@
 	return res;
 }
 
-static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
+static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
+{
+	char *path = xmalloc(traverse_path_len(info, n) + 1);
+	return make_traverse_path(path, info, n);
+}
+
+static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result)
 {
 	struct merge_list *orig, *final;
 	const char *path;
@@ -177,7 +183,7 @@
 	if (!branch1)
 		return;
 
-	path = xstrdup(mkpath("%s%s", base, result->path));
+	path = traverse_path(info, result);
 	orig = create_entry(2, branch1->mode, branch1->sha1, path);
 	final = create_entry(0, result->mode, result->sha1, path);
 
@@ -186,9 +192,8 @@
 	add_merge_entry(final);
 }
 
-static int unresolved_directory(const char *base, struct name_entry n[3])
+static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3])
 {
-	int baselen, pathlen;
 	char *newbase;
 	struct name_entry *p;
 	struct tree_desc t[3];
@@ -204,13 +209,7 @@
 	}
 	if (!S_ISDIR(p->mode))
 		return 0;
-	baselen = strlen(base);
-	pathlen = tree_entry_len(p->path, p->sha1);
-	newbase = xmalloc(baselen + pathlen + 2);
-	memcpy(newbase, base, baselen);
-	memcpy(newbase + baselen, p->path, pathlen);
-	memcpy(newbase + baselen + pathlen, "/", 2);
-
+	newbase = traverse_path(info, p);
 	buf0 = fill_tree_descriptor(t+0, n[0].sha1);
 	buf1 = fill_tree_descriptor(t+1, n[1].sha1);
 	buf2 = fill_tree_descriptor(t+2, n[2].sha1);
@@ -224,7 +223,7 @@
 }
 
 
-static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
+static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
 {
 	const char *path;
 	struct merge_list *link;
@@ -234,17 +233,17 @@
 	if (entry)
 		path = entry->path;
 	else
-		path = xstrdup(mkpath("%s%s", base, n->path));
+		path = traverse_path(info, n);
 	link = create_entry(stage, n->mode, n->sha1, path);
 	link->link = entry;
 	return link;
 }
 
-static void unresolved(const char *base, struct name_entry n[3])
+static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 {
 	struct merge_list *entry = NULL;
 
-	if (unresolved_directory(base, n))
+	if (unresolved_directory(info, n))
 		return;
 
 	/*
@@ -252,9 +251,9 @@
 	 * list has the stages in order - link_entry adds new
 	 * links at the front.
 	 */
-	entry = link_entry(3, base, n + 2, entry);
-	entry = link_entry(2, base, n + 1, entry);
-	entry = link_entry(1, base, n + 0, entry);
+	entry = link_entry(3, info, n + 2, entry);
+	entry = link_entry(2, info, n + 1, entry);
+	entry = link_entry(1, info, n + 0, entry);
 
 	add_merge_entry(entry);
 }
@@ -288,36 +287,41 @@
  * The successful merge rules are the same as for the three-way merge
  * in git-read-tree.
  */
-static void threeway_callback(int n, unsigned long mask, struct name_entry *entry, const char *base)
+static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 {
 	/* Same in both? */
 	if (same_entry(entry+1, entry+2)) {
 		if (entry[0].sha1) {
-			resolve(base, NULL, entry+1);
-			return;
+			resolve(info, NULL, entry+1);
+			return mask;
 		}
 	}
 
 	if (same_entry(entry+0, entry+1)) {
 		if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
-			resolve(base, entry+1, entry+2);
-			return;
+			resolve(info, entry+1, entry+2);
+			return mask;
 		}
 	}
 
 	if (same_entry(entry+0, entry+2)) {
 		if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
-			resolve(base, NULL, entry+1);
-			return;
+			resolve(info, NULL, entry+1);
+			return mask;
 		}
 	}
 
-	unresolved(base, entry);
+	unresolved(info, entry);
+	return mask;
 }
 
 static void merge_trees(struct tree_desc t[3], const char *base)
 {
-	traverse_trees(3, t, base, threeway_callback);
+	struct traverse_info info;
+
+	setup_traverse_info(&info, base);
+	info.fn = threeway_callback;
+	traverse_trees(3, t, &info);
 }
 
 static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
diff --git a/mktag.c b/mktag.c
index b05260c..0b34341 100644
--- a/mktag.c
+++ b/mktag.c
@@ -8,10 +8,11 @@
  * message and a signature block that git itself doesn't care about,
  * but that can be verified with gpg or similar.
  *
- * The first three lines are guaranteed to be at least 63 bytes:
+ * The first four lines are guaranteed to be at least 83 bytes:
  * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
- * shortest possible type-line, and "tag .\n" at 6 bytes is the
- * shortest single-character-tag line.
+ * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
+ * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
+ * the shortest possible tagger-line.
  */
 
 /*
@@ -43,9 +44,10 @@
 	int typelen;
 	char type[20];
 	unsigned char sha1[20];
-	const char *object, *type_line, *tag_line, *tagger_line;
+	const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb;
+	size_t len;
 
-	if (size < 64)
+	if (size < 84)
 		return error("wanna fool me ? you obviously got the size wrong !");
 
 	buffer[size] = 0;
@@ -97,11 +99,51 @@
 	/* Verify the tagger line */
 	tagger_line = tag_line;
 
-	if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
-		return error("char" PD_FMT ": could not find \"tagger\"", tagger_line - buffer);
+	if (memcmp(tagger_line, "tagger ", 7))
+		return error("char" PD_FMT ": could not find \"tagger \"",
+			tagger_line - buffer);
 
-	/* TODO: check for committer info + blank line? */
-	/* Also, the minimum length is probably + "tagger .", or 63+8=71 */
+	/*
+	 * Check for correct form for name and email
+	 * i.e. " <" followed by "> " on _this_ line
+	 * No angle brackets within the name or email address fields.
+	 * No spaces within the email address field.
+	 */
+	tagger_line += 7;
+	if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
+		strpbrk(tagger_line, "<>\n") != lb+1 ||
+		strpbrk(lb+2, "><\n ") != rb)
+		return error("char" PD_FMT ": malformed tagger field",
+			tagger_line - buffer);
+
+	/* Check for author name, at least one character, space is acceptable */
+	if (lb == tagger_line)
+		return error("char" PD_FMT ": missing tagger name",
+			tagger_line - buffer);
+
+	/* timestamp, 1 or more digits followed by space */
+	tagger_line = rb + 2;
+	if (!(len = strspn(tagger_line, "0123456789")))
+		return error("char" PD_FMT ": missing tag timestamp",
+			tagger_line - buffer);
+	tagger_line += len;
+	if (*tagger_line != ' ')
+		return error("char" PD_FMT ": malformed tag timestamp",
+			tagger_line - buffer);
+	tagger_line++;
+
+	/* timezone, 5 digits [+-]hhmm, max. 1400 */
+	if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
+	      strspn(tagger_line+1, "0123456789") == 4 &&
+	      tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
+		return error("char" PD_FMT ": malformed tag timezone",
+			tagger_line - buffer);
+	tagger_line += 6;
+
+	/* Verify the blank line separating the header from the body */
+	if (*tagger_line != '\n')
+		return error("char" PD_FMT ": trailing garbage in tag header",
+			tagger_line - buffer);
 
 	/* The actual stuff afterwards we don't care about.. */
 	return 0;
diff --git a/object-refs.c b/object-refs.c
deleted file mode 100644
index 5345671..0000000
--- a/object-refs.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "cache.h"
-#include "object.h"
-#include "decorate.h"
-
-int track_object_refs = 0;
-
-static struct decoration ref_decorate;
-
-struct object_refs *lookup_object_refs(struct object *base)
-{
-	return lookup_decoration(&ref_decorate, base);
-}
-
-static void add_object_refs(struct object *obj, struct object_refs *refs)
-{
-	if (add_decoration(&ref_decorate, obj, refs))
-		die("object %s tried to add refs twice!", sha1_to_hex(obj->sha1));
-}
-
-struct object_refs *alloc_object_refs(unsigned count)
-{
-	struct object_refs *refs;
-	size_t size = sizeof(*refs) + count*sizeof(struct object *);
-
-	refs = xcalloc(1, size);
-	refs->count = count;
-	return refs;
-}
-
-static int compare_object_pointers(const void *a, const void *b)
-{
-	const struct object * const *pa = a;
-	const struct object * const *pb = b;
-	if (*pa == *pb)
-		return 0;
-	else if (*pa < *pb)
-		return -1;
-	else
-		return 1;
-}
-
-void set_object_refs(struct object *obj, struct object_refs *refs)
-{
-	unsigned int i, j;
-
-	/* Do not install empty list of references */
-	if (refs->count < 1) {
-		free(refs);
-		return;
-	}
-
-	/* Sort the list and filter out duplicates */
-	qsort(refs->ref, refs->count, sizeof(refs->ref[0]),
-	      compare_object_pointers);
-	for (i = j = 1; i < refs->count; i++) {
-		if (refs->ref[i] != refs->ref[i - 1])
-			refs->ref[j++] = refs->ref[i];
-	}
-	if (j < refs->count) {
-		/* Duplicates were found - reallocate list */
-		size_t size = sizeof(*refs) + j*sizeof(struct object *);
-		refs->count = j;
-		refs = xrealloc(refs, size);
-	}
-
-	for (i = 0; i < refs->count; i++)
-		refs->ref[i]->used = 1;
-	add_object_refs(obj, refs);
-}
-
-void mark_reachable(struct object *obj, unsigned int mask)
-{
-	const struct object_refs *refs;
-
-	if (!track_object_refs)
-		die("cannot do reachability with object refs turned off");
-	/* If we've been here already, don't bother */
-	if (obj->flags & mask)
-		return;
-	obj->flags |= mask;
-	refs = lookup_object_refs(obj);
-	if (refs) {
-		unsigned i;
-		for (i = 0; i < refs->count; i++)
-			mark_reachable(refs->ref[i], mask);
-	}
-}
diff --git a/object.c b/object.c
index 5a5ebe2..50b6528 100644
--- a/object.c
+++ b/object.c
@@ -140,7 +140,8 @@
 	if (type == OBJ_BLOB) {
 		struct blob *blob = lookup_blob(sha1);
 		if (blob) {
-			parse_blob_buffer(blob, buffer, size);
+			if (parse_blob_buffer(blob, buffer, size))
+				return NULL;
 			obj = &blob->object;
 		}
 	} else if (type == OBJ_TREE) {
@@ -148,14 +149,16 @@
 		if (tree) {
 			obj = &tree->object;
 			if (!tree->object.parsed) {
-				parse_tree_buffer(tree, buffer, size);
+				if (parse_tree_buffer(tree, buffer, size))
+					return NULL;
 				eaten = 1;
 			}
 		}
 	} else if (type == OBJ_COMMIT) {
 		struct commit *commit = lookup_commit(sha1);
 		if (commit) {
-			parse_commit_buffer(commit, buffer, size);
+			if (parse_commit_buffer(commit, buffer, size))
+				return NULL;
 			if (!commit->buffer) {
 				commit->buffer = buffer;
 				eaten = 1;
@@ -165,7 +168,8 @@
 	} else if (type == OBJ_TAG) {
 		struct tag *tag = lookup_tag(sha1);
 		if (tag) {
-			parse_tag_buffer(tag, buffer, size);
+			if (parse_tag_buffer(tag, buffer, size))
+			       return NULL;
 			obj = &tag->object;
 		}
 	} else {
diff --git a/object.h b/object.h
index 397bbfa..036bd66 100644
--- a/object.h
+++ b/object.h
@@ -35,14 +35,11 @@
 	unsigned char sha1[20];
 };
 
-extern int track_object_refs;
-
 extern const char *typename(unsigned int type);
 extern int type_from_string(const char *str);
 
 extern unsigned int get_max_object_index(void);
 extern struct object *get_indexed_object(unsigned int);
-extern struct object_refs *lookup_object_refs(struct object *);
 
 /** Internal only **/
 struct object *lookup_object(const unsigned char *sha1);
@@ -61,11 +58,6 @@
 /** Returns the object, with potentially excess memory allocated. **/
 struct object *lookup_unknown_object(const unsigned  char *sha1);
 
-struct object_refs *alloc_object_refs(unsigned count);
-void set_object_refs(struct object *obj, struct object_refs *refs);
-
-void mark_reachable(struct object *obj, unsigned int mask);
-
 struct object_list *object_list_insert(struct object *item,
 				       struct object_list **list_p);
 
diff --git a/pack-check.c b/pack-check.c
index d7dd62b..0f8ad2c 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "pack.h"
+#include "pack-revindex.h"
 
 struct idx_entry
 {
@@ -101,8 +102,10 @@
 static void show_pack_info(struct packed_git *p)
 {
 	uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
+
 	nr_objects = p->num_objects;
 	memset(chain_histogram, 0, sizeof(chain_histogram));
+	init_pack_revindex();
 
 	for (i = 0; i < nr_objects; i++) {
 		const unsigned char *sha1;
@@ -125,11 +128,11 @@
 						 base_sha1);
 		printf("%s ", sha1_to_hex(sha1));
 		if (!delta_chain_length)
-			printf("%-6s %lu %"PRIuMAX"\n",
-			       type, size, (uintmax_t)offset);
+			printf("%-6s %lu %lu %"PRIuMAX"\n",
+			       type, size, store_size, (uintmax_t)offset);
 		else {
-			printf("%-6s %lu %"PRIuMAX" %u %s\n",
-			       type, size, (uintmax_t)offset,
+			printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
+			       type, size, store_size, (uintmax_t)offset,
 			       delta_chain_length, sha1_to_hex(base_sha1));
 			if (delta_chain_length <= MAX_CHAIN)
 				chain_histogram[delta_chain_length]++;
diff --git a/pack-revindex.c b/pack-revindex.c
new file mode 100644
index 0000000..a8aa2cd
--- /dev/null
+++ b/pack-revindex.c
@@ -0,0 +1,142 @@
+#include "cache.h"
+#include "pack-revindex.h"
+
+/*
+ * Pack index for existing packs give us easy access to the offsets into
+ * corresponding pack file where each object's data starts, but the entries
+ * do not store the size of the compressed representation (uncompressed
+ * size is easily available by examining the pack entry header).  It is
+ * also rather expensive to find the sha1 for an object given its offset.
+ *
+ * We build a hashtable of existing packs (pack_revindex), and keep reverse
+ * index here -- pack index file is sorted by object name mapping to offset;
+ * this pack_revindex[].revindex array is a list of offset/index_nr pairs
+ * ordered by offset, so if you know the offset of an object, next offset
+ * is where its packed representation ends and the index_nr can be used to
+ * get the object sha1 from the main index.
+ */
+
+struct pack_revindex {
+	struct packed_git *p;
+	struct revindex_entry *revindex;
+};
+
+static struct pack_revindex *pack_revindex;
+static int pack_revindex_hashsz;
+
+static int pack_revindex_ix(struct packed_git *p)
+{
+	unsigned long ui = (unsigned long)p;
+	int i;
+
+	ui = ui ^ (ui >> 16); /* defeat structure alignment */
+	i = (int)(ui % pack_revindex_hashsz);
+	while (pack_revindex[i].p) {
+		if (pack_revindex[i].p == p)
+			return i;
+		if (++i == pack_revindex_hashsz)
+			i = 0;
+	}
+	return -1 - i;
+}
+
+void init_pack_revindex(void)
+{
+	int num;
+	struct packed_git *p;
+
+	for (num = 0, p = packed_git; p; p = p->next)
+		num++;
+	if (!num)
+		return;
+	pack_revindex_hashsz = num * 11;
+	pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
+	for (p = packed_git; p; p = p->next) {
+		num = pack_revindex_ix(p);
+		num = - 1 - num;
+		pack_revindex[num].p = p;
+	}
+	/* revindex elements are lazily initialized */
+}
+
+static int cmp_offset(const void *a_, const void *b_)
+{
+	const struct revindex_entry *a = a_;
+	const struct revindex_entry *b = b_;
+	return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
+}
+
+/*
+ * Ordered list of offsets of objects in the pack.
+ */
+static void create_pack_revindex(struct pack_revindex *rix)
+{
+	struct packed_git *p = rix->p;
+	int num_ent = p->num_objects;
+	int i;
+	const char *index = p->index_data;
+
+	rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
+	index += 4 * 256;
+
+	if (p->index_version > 1) {
+		const uint32_t *off_32 =
+			(uint32_t *)(index + 8 + p->num_objects * (20 + 4));
+		const uint32_t *off_64 = off_32 + p->num_objects;
+		for (i = 0; i < num_ent; i++) {
+			uint32_t off = ntohl(*off_32++);
+			if (!(off & 0x80000000)) {
+				rix->revindex[i].offset = off;
+			} else {
+				rix->revindex[i].offset =
+					((uint64_t)ntohl(*off_64++)) << 32;
+				rix->revindex[i].offset |=
+					ntohl(*off_64++);
+			}
+			rix->revindex[i].nr = i;
+		}
+	} else {
+		for (i = 0; i < num_ent; i++) {
+			uint32_t hl = *((uint32_t *)(index + 24 * i));
+			rix->revindex[i].offset = ntohl(hl);
+			rix->revindex[i].nr = i;
+		}
+	}
+
+	/* This knows the pack format -- the 20-byte trailer
+	 * follows immediately after the last object data.
+	 */
+	rix->revindex[num_ent].offset = p->pack_size - 20;
+	rix->revindex[num_ent].nr = -1;
+	qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
+}
+
+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
+{
+	int num;
+	int lo, hi;
+	struct pack_revindex *rix;
+	struct revindex_entry *revindex;
+
+	num = pack_revindex_ix(p);
+	if (num < 0)
+		die("internal error: pack revindex uninitialized");
+
+	rix = &pack_revindex[num];
+	if (!rix->revindex)
+		create_pack_revindex(rix);
+	revindex = rix->revindex;
+
+	lo = 0;
+	hi = p->num_objects + 1;
+	do {
+		int mi = (lo + hi) / 2;
+		if (revindex[mi].offset == ofs) {
+			return revindex + mi;
+		} else if (ofs < revindex[mi].offset)
+			hi = mi;
+		else
+			lo = mi + 1;
+	} while (lo < hi);
+	die("internal error: pack revindex corrupt");
+}
diff --git a/pack-revindex.h b/pack-revindex.h
new file mode 100644
index 0000000..c3527a7
--- /dev/null
+++ b/pack-revindex.h
@@ -0,0 +1,12 @@
+#ifndef PACK_REVINDEX_H
+#define PACK_REVINDEX_H
+
+struct revindex_entry {
+	off_t offset;
+	unsigned int nr;
+};
+
+void init_pack_revindex(void);
+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
+
+#endif
diff --git a/pager.c b/pager.c
index 0376953..ca002f9 100644
--- a/pager.c
+++ b/pager.c
@@ -57,6 +57,7 @@
 	/* return in the child */
 	if (!pid) {
 		dup2(fd[1], 1);
+		dup2(fd[1], 2);
 		close(fd[0]);
 		close(fd[1]);
 		return;
diff --git a/parse-options.c b/parse-options.c
index 59dc9ce..acf3fe3 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -6,7 +6,8 @@
 
 struct optparse_t {
 	const char **argv;
-	int argc;
+	const char **out;
+	int argc, cpidx;
 	const char *opt;
 };
 
@@ -159,6 +160,16 @@
 			continue;
 
 		rest = skip_prefix(arg, options->long_name);
+		if (options->type == OPTION_ARGUMENT) {
+			if (!rest)
+				continue;
+			if (*rest == '=')
+				return opterror(options, "takes no value", flags);
+			if (*rest)
+				continue;
+			p->out[p->cpidx++] = arg - 2;
+			return 0;
+		}
 		if (!rest) {
 			/* abbreviated? */
 			if (!strncmp(options->long_name, arg, arg_end - arg)) {
@@ -242,14 +253,15 @@
 int parse_options(int argc, const char **argv, const struct option *options,
                   const char * const usagestr[], int flags)
 {
-	struct optparse_t args = { argv + 1, argc - 1, NULL };
-	int j = 0;
+	struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
 
 	for (; args.argc; args.argc--, args.argv++) {
 		const char *arg = args.argv[0];
 
 		if (*arg != '-' || !arg[1]) {
-			argv[j++] = args.argv[0];
+			if (flags & PARSE_OPT_STOP_AT_NON_OPTION)
+				break;
+			args.out[args.cpidx++] = args.argv[0];
 			continue;
 		}
 
@@ -286,9 +298,9 @@
 			usage_with_options(usagestr, options);
 	}
 
-	memmove(argv + j, args.argv, args.argc * sizeof(*argv));
-	argv[j + args.argc] = NULL;
-	return j + args.argc;
+	memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
+	args.out[args.cpidx + args.argc] = NULL;
+	return args.cpidx + args.argc;
 }
 
 #define USAGE_OPTS_WIDTH 24
@@ -328,6 +340,8 @@
 			pos += fprintf(stderr, "--%s", opts->long_name);
 
 		switch (opts->type) {
+		case OPTION_ARGUMENT:
+			break;
 		case OPTION_INTEGER:
 			if (opts->flags & PARSE_OPT_OPTARG)
 				pos += fprintf(stderr, "[<n>]");
@@ -395,3 +409,10 @@
 	*(int *)(opt->value) = v;
 	return 0;
 }
+
+int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
+			     int unset)
+{
+	*(unsigned long *)(opt->value) = approxidate(arg);
+	return 0;
+}
diff --git a/parse-options.h b/parse-options.h
index 102ac31..4ee443d 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -4,6 +4,7 @@
 enum parse_opt_type {
 	/* special types */
 	OPTION_END,
+	OPTION_ARGUMENT,
 	OPTION_GROUP,
 	/* options with no arguments */
 	OPTION_BIT,
@@ -18,6 +19,7 @@
 
 enum parse_opt_flags {
 	PARSE_OPT_KEEP_DASHDASH = 1,
+	PARSE_OPT_STOP_AT_NON_OPTION = 2,
 };
 
 enum parse_opt_option_flags {
@@ -84,6 +86,7 @@
 };
 
 #define OPT_END()                   { OPTION_END }
+#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
 #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
 #define OPT_BIT(s, l, v, h, b)      { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
 #define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
@@ -91,6 +94,9 @@
 #define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) }
 #define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), NULL, (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
+#define OPT_DATE(s, l, v, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
+	  parse_opt_approxidate_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
 	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
 
@@ -107,6 +113,7 @@
 
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
+extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
 #define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
diff --git a/path-list.c b/path-list.c
index 3d83b7b..92e5cf2 100644
--- a/path-list.c
+++ b/path-list.c
@@ -102,3 +102,33 @@
 	for (i = 0; i < p->nr; i++)
 		printf("%s:%p\n", p->items[i].path, p->items[i].util);
 }
+
+struct path_list_item *path_list_append(const char *path, struct path_list *list)
+{
+	ALLOC_GROW(list->items, list->nr + 1, list->alloc);
+	list->items[list->nr].path =
+		list->strdup_paths ? xstrdup(path) : (char *)path;
+	return list->items + list->nr++;
+}
+
+static int cmp_items(const void *a, const void *b)
+{
+	const struct path_list_item *one = a;
+	const struct path_list_item *two = b;
+	return strcmp(one->path, two->path);
+}
+
+void sort_path_list(struct path_list *list)
+{
+	qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
+}
+
+int unsorted_path_list_has_path(struct path_list *list, const char *path)
+{
+	int i;
+	for (i = 0; i < list->nr; i++)
+		if (!strcmp(path, list->items[i].path))
+			return 1;
+	return 0;
+}
+
diff --git a/path-list.h b/path-list.h
index 5931e2c..ca2cbba 100644
--- a/path-list.h
+++ b/path-list.h
@@ -13,10 +13,16 @@
 };
 
 void print_path_list(const char *text, const struct path_list *p);
-
-int path_list_has_path(const struct path_list *list, const char *path);
 void path_list_clear(struct path_list *list, int free_util);
+
+/* Use these functions only on sorted lists: */
+int path_list_has_path(const struct path_list *list, const char *path);
 struct path_list_item *path_list_insert(const char *path, struct path_list *list);
 struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
 
+/* Use these functions only on unsorted lists: */
+struct path_list_item *path_list_append(const char *path, struct path_list *list);
+void sort_path_list(struct path_list *list);
+int unsorted_path_list_has_path(struct path_list *list, const char *path);
+
 #endif /* PATH_LIST_H */
diff --git a/path.c b/path.c
index 4260952..f4ed979 100644
--- a/path.c
+++ b/path.c
@@ -283,7 +283,7 @@
 			    ? (S_IXGRP|S_IXOTH)
 			    : 0));
 	if (S_ISDIR(mode))
-		mode |= S_ISGID;
+		mode |= FORCE_DIR_SET_GID;
 	if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
 		return -2;
 	return 0;
@@ -311,8 +311,10 @@
 			if (last_slash) {
 				*last_slash = '\0';
 				last_elem = xstrdup(last_slash + 1);
-			} else
+			} else {
 				last_elem = xstrdup(buf);
+				*buf = '\0';
+			}
 		}
 
 		if (*buf) {
diff --git a/pretty.c b/pretty.c
index 0963bb0..16bfb86 100644
--- a/pretty.c
+++ b/pretty.c
@@ -30,8 +30,7 @@
 	if (*arg == '=')
 		arg++;
 	if (!prefixcmp(arg, "format:")) {
-		if (user_format)
-			free(user_format);
+		free(user_format);
 		user_format = xstrdup(arg + 7);
 		return CMIT_FMT_USERFORMAT;
 	}
@@ -110,9 +109,9 @@
 	strbuf_addstr(sb, "?=");
 }
 
-static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-			 const char *line, enum date_mode dmode,
-			 const char *encoding)
+void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
+		  const char *line, enum date_mode dmode,
+		  const char *encoding)
 {
 	char *date;
 	int namelen;
@@ -282,59 +281,59 @@
 	return out;
 }
 
-static void format_person_part(struct strbuf *sb, char part,
+static size_t format_person_part(struct strbuf *sb, char part,
                                const char *msg, int len)
 {
+	/* currently all placeholders have same length */
+	const int placeholder_len = 2;
 	int start, end, tz = 0;
-	unsigned long date;
+	unsigned long date = 0;
 	char *ep;
 
-	/* parse name */
+	/* advance 'end' to point to email start delimiter */
 	for (end = 0; end < len && msg[end] != '<'; end++)
 		; /* do nothing */
-	/*
-	 * If it does not even have a '<' and '>', that is
-	 * quite a bogus commit author and we discard it;
-	 * this is in line with add_user_info() that is used
-	 * in the normal codepath.  When end points at the '<'
-	 * that we found, it should have matching '>' later,
-	 * which means start (beginning of email address) must
-	 * be strictly below len.
-	 */
-	start = end + 1;
-	if (start >= len - 1)
-		return;
-	while (end > 0 && isspace(msg[end - 1]))
-		end--;
-	if (part == 'n') {	/* name */
-		strbuf_add(sb, msg, end);
-		return;
-	}
 
-	/* parse email */
-	for (end = start; end < len && msg[end] != '>'; end++)
+	/*
+	 * When end points at the '<' that we found, it should have
+	 * matching '>' later, which means 'end' must be strictly
+	 * below len - 1.
+	 */
+	if (end >= len - 2)
+		goto skip;
+
+	if (part == 'n') {	/* name */
+		while (end > 0 && isspace(msg[end - 1]))
+			end--;
+		strbuf_add(sb, msg, end);
+		return placeholder_len;
+	}
+	start = ++end; /* save email start position */
+
+	/* advance 'end' to point to email end delimiter */
+	for ( ; end < len && msg[end] != '>'; end++)
 		; /* do nothing */
 
 	if (end >= len)
-		return;
+		goto skip;
 
 	if (part == 'e') {	/* email */
 		strbuf_add(sb, msg + start, end - start);
-		return;
+		return placeholder_len;
 	}
 
-	/* parse date */
+	/* advance 'start' to point to date start delimiter */
 	for (start = end + 1; start < len && isspace(msg[start]); start++)
 		; /* do nothing */
 	if (start >= len)
-		return;
+		goto skip;
 	date = strtoul(msg + start, &ep, 10);
 	if (msg + start == ep)
-		return;
+		goto skip;
 
 	if (part == 't') {	/* date, UNIX timestamp */
 		strbuf_add(sb, msg + start, ep - (msg + start));
-		return;
+		return placeholder_len;
 	}
 
 	/* parse tz */
@@ -349,17 +348,28 @@
 	switch (part) {
 	case 'd':	/* date */
 		strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
-		return;
+		return placeholder_len;
 	case 'D':	/* date, RFC2822 style */
 		strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
-		return;
+		return placeholder_len;
 	case 'r':	/* date, relative */
 		strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
-		return;
+		return placeholder_len;
 	case 'i':	/* date, ISO 8601 */
 		strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
-		return;
+		return placeholder_len;
 	}
+
+skip:
+	/*
+	 * bogus commit, 'sb' cannot be updated, but we still need to
+	 * compute a valid return value.
+	 */
+	if (part == 'n' || part == 'e' || part == 't' || part == 'd'
+	    || part == 'D' || part == 'r' || part == 'i')
+		return placeholder_len;
+
+	return 0; /* unknown placeholder */
 }
 
 struct chunk {
@@ -440,7 +450,7 @@
 	context->commit_header_parsed = 1;
 }
 
-static void format_commit_item(struct strbuf *sb, const char *placeholder,
+static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
 	struct format_commit_context *c = context;
@@ -451,23 +461,23 @@
 	/* these are independent of the commit */
 	switch (placeholder[0]) {
 	case 'C':
-		switch (placeholder[3]) {
-		case 'd':	/* red */
+		if (!prefixcmp(placeholder + 1, "red")) {
 			strbuf_addstr(sb, "\033[31m");
-			return;
-		case 'e':	/* green */
+			return 4;
+		} else if (!prefixcmp(placeholder + 1, "green")) {
 			strbuf_addstr(sb, "\033[32m");
-			return;
-		case 'u':	/* blue */
+			return 6;
+		} else if (!prefixcmp(placeholder + 1, "blue")) {
 			strbuf_addstr(sb, "\033[34m");
-			return;
-		case 's':	/* reset color */
+			return 5;
+		} else if (!prefixcmp(placeholder + 1, "reset")) {
 			strbuf_addstr(sb, "\033[m");
-			return;
-		}
+			return 6;
+		} else
+			return 0;
 	case 'n':		/* newline */
 		strbuf_addch(sb, '\n');
-		return;
+		return 1;
 	}
 
 	/* these depend on the commit */
@@ -477,34 +487,34 @@
 	switch (placeholder[0]) {
 	case 'H':		/* commit hash */
 		strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
-		return;
+		return 1;
 	case 'h':		/* abbreviated commit hash */
 		if (add_again(sb, &c->abbrev_commit_hash))
-			return;
+			return 1;
 		strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
 		                                     DEFAULT_ABBREV));
 		c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
-		return;
+		return 1;
 	case 'T':		/* tree hash */
 		strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
-		return;
+		return 1;
 	case 't':		/* abbreviated tree hash */
 		if (add_again(sb, &c->abbrev_tree_hash))
-			return;
+			return 1;
 		strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
 		                                     DEFAULT_ABBREV));
 		c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
-		return;
+		return 1;
 	case 'P':		/* parent hashes */
 		for (p = commit->parents; p; p = p->next) {
 			if (p != commit->parents)
 				strbuf_addch(sb, ' ');
 			strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
 		}
-		return;
+		return 1;
 	case 'p':		/* abbreviated parent hashes */
 		if (add_again(sb, &c->abbrev_parent_hashes))
-			return;
+			return 1;
 		for (p = commit->parents; p; p = p->next) {
 			if (p != commit->parents)
 				strbuf_addch(sb, ' ');
@@ -513,14 +523,14 @@
 		}
 		c->abbrev_parent_hashes.len = sb->len -
 		                              c->abbrev_parent_hashes.off;
-		return;
+		return 1;
 	case 'm':		/* left/right/bottom */
 		strbuf_addch(sb, (commit->object.flags & BOUNDARY)
 		                 ? '-'
 		                 : (commit->object.flags & SYMMETRIC_LEFT)
 		                 ? '<'
 		                 : '>');
-		return;
+		return 1;
 	}
 
 	/* For the rest we have to parse the commit header. */
@@ -528,66 +538,33 @@
 		parse_commit_header(c);
 
 	switch (placeholder[0]) {
-	case 's':
+	case 's':	/* subject */
 		strbuf_add(sb, msg + c->subject.off, c->subject.len);
-		return;
-	case 'a':
-		format_person_part(sb, placeholder[1],
+		return 1;
+	case 'a':	/* author ... */
+		return format_person_part(sb, placeholder[1],
 		                   msg + c->author.off, c->author.len);
-		return;
-	case 'c':
-		format_person_part(sb, placeholder[1],
+	case 'c':	/* committer ... */
+		return format_person_part(sb, placeholder[1],
 		                   msg + c->committer.off, c->committer.len);
-		return;
-	case 'e':
+	case 'e':	/* encoding */
 		strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
-		return;
-	case 'b':
+		return 1;
+	case 'b':	/* body */
 		strbuf_addstr(sb, msg + c->body_off);
-		return;
+		return 1;
 	}
+	return 0;	/* unknown placeholder */
 }
 
 void format_commit_message(const struct commit *commit,
                            const void *format, struct strbuf *sb)
 {
-	const char *placeholders[] = {
-		"H",		/* commit hash */
-		"h",		/* abbreviated commit hash */
-		"T",		/* tree hash */
-		"t",		/* abbreviated tree hash */
-		"P",		/* parent hashes */
-		"p",		/* abbreviated parent hashes */
-		"an",		/* author name */
-		"ae",		/* author email */
-		"ad",		/* author date */
-		"aD",		/* author date, RFC2822 style */
-		"ar",		/* author date, relative */
-		"at",		/* author date, UNIX timestamp */
-		"ai",		/* author date, ISO 8601 */
-		"cn",		/* committer name */
-		"ce",		/* committer email */
-		"cd",		/* committer date */
-		"cD",		/* committer date, RFC2822 style */
-		"cr",		/* committer date, relative */
-		"ct",		/* committer date, UNIX timestamp */
-		"ci",		/* committer date, ISO 8601 */
-		"e",		/* encoding */
-		"s",		/* subject */
-		"b",		/* body */
-		"Cred",		/* red */
-		"Cgreen",	/* green */
-		"Cblue",	/* blue */
-		"Creset",	/* reset color */
-		"n",		/* newline */
-		"m",		/* left/right/bottom */
-		NULL
-	};
 	struct format_commit_context context;
 
 	memset(&context, 0, sizeof(context));
 	context.commit = commit;
-	strbuf_expand(sb, format, placeholders, format_commit_item, &context);
+	strbuf_expand(sb, format, format_commit_item, &context);
 }
 
 static void pp_header(enum cmit_fmt fmt,
@@ -643,23 +620,23 @@
 		 */
 		if (!memcmp(line, "author ", 7)) {
 			strbuf_grow(sb, linelen + 80);
-			add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
+			pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
 		}
 		if (!memcmp(line, "committer ", 10) &&
 		    (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
 			strbuf_grow(sb, linelen + 80);
-			add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
+			pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
 		}
 	}
 }
 
-static void pp_title_line(enum cmit_fmt fmt,
-			  const char **msg_p,
-			  struct strbuf *sb,
-			  const char *subject,
-			  const char *after_subject,
-			  const char *encoding,
-			  int need_8bit_cte)
+void pp_title_line(enum cmit_fmt fmt,
+		   const char **msg_p,
+		   struct strbuf *sb,
+		   const char *subject,
+		   const char *after_subject,
+		   const char *encoding,
+		   int need_8bit_cte)
 {
 	struct strbuf title;
 
@@ -708,10 +685,10 @@
 	strbuf_release(&title);
 }
 
-static void pp_remainder(enum cmit_fmt fmt,
-			 const char **msg_p,
-			 struct strbuf *sb,
-			 int indent)
+void pp_remainder(enum cmit_fmt fmt,
+		  const char **msg_p,
+		  struct strbuf *sb,
+		  int indent)
 {
 	int first = 1;
 	for (;;) {
diff --git a/quote.c b/quote.c
index 40702f6..d5cf9d8 100644
--- a/quote.c
+++ b/quote.c
@@ -260,6 +260,48 @@
 	fputc(terminator, fp);
 }
 
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+			  struct strbuf *out, const char *prefix)
+{
+	int needquote;
+
+	if (len < 0)
+		len = strlen(in);
+
+	/* "../" prefix itself does not need quoting, but "in" might. */
+	needquote = next_quote_pos(in, len) < len;
+	strbuf_setlen(out, 0);
+	strbuf_grow(out, len);
+
+	if (needquote)
+		strbuf_addch(out, '"');
+	if (prefix) {
+		int off = 0;
+		while (prefix[off] && off < len && prefix[off] == in[off])
+			if (prefix[off] == '/') {
+				prefix += off + 1;
+				in += off + 1;
+				len -= off + 1;
+				off = 0;
+			} else
+				off++;
+
+		for (; *prefix; prefix++)
+			if (*prefix == '/')
+				strbuf_addstr(out, "../");
+	}
+
+	quote_c_style_counted (in, len, out, NULL, 1);
+
+	if (needquote)
+		strbuf_addch(out, '"');
+	if (!out->len)
+		strbuf_addstr(out, "./");
+
+	return out->buf;
+}
+
 /*
  * C-style name unquoting.
  *
diff --git a/quote.h b/quote.h
index 4da110e..c5eea6f 100644
--- a/quote.h
+++ b/quote.h
@@ -47,6 +47,10 @@
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
 
+/* quote path as relative to the given prefix */
+char *quote_path_relative(const char *in, int len,
+			  struct strbuf *out, const char *prefix);
+
 /* quoting as a string literal for other languages */
 extern void perl_quote_print(FILE *stream, const char *src);
 extern void python_quote_print(FILE *stream, const char *src);
diff --git a/reachable.c b/reachable.c
index 6383401..3b1c18f 100644
--- a/reachable.c
+++ b/reachable.c
@@ -15,6 +15,8 @@
 {
 	struct object *obj = &blob->object;
 
+	if (!blob)
+		die("bad blob object");
 	if (obj->flags & SEEN)
 		return;
 	obj->flags |= SEEN;
@@ -39,6 +41,8 @@
 	struct name_entry entry;
 	struct name_path me;
 
+	if (!tree)
+		die("bad tree object");
 	if (obj->flags & SEEN)
 		return;
 	obj->flags |= SEEN;
@@ -79,7 +83,8 @@
 
 	if (parse_tag(tag) < 0)
 		die("bad tag object %s", sha1_to_hex(obj->sha1));
-	add_object(tag->tagged, p, NULL, name);
+	if (tag->tagged)
+		add_object(tag->tagged, p, NULL, name);
 }
 
 static void walk_commit_list(struct rev_info *revs)
@@ -150,7 +155,8 @@
 static void add_one_tree(const unsigned char *sha1, struct rev_info *revs)
 {
 	struct tree *tree = lookup_tree(sha1);
-	add_pending_object(revs, &tree->object, "");
+	if (tree)
+		add_pending_object(revs, &tree->object, "");
 }
 
 static void add_cache_tree(struct cache_tree *it, struct rev_info *revs)
@@ -176,7 +182,7 @@
 		 * lookup_blob() on them, to avoid populating the hash table
 		 * with invalid information
 		 */
-		if (S_ISGITLINK(ntohl(active_cache[i]->ce_mode)))
+		if (S_ISGITLINK(active_cache[i]->ce_mode))
 			continue;
 
 		lookup_blob(active_cache[i]->sha1);
@@ -215,6 +221,7 @@
 	 * Set up the revision walk - this will move all commits
 	 * from the pending list to the commit walking list.
 	 */
-	prepare_revision_walk(revs);
+	if (prepare_revision_walk(revs))
+		die("revision walk setup failed");
 	walk_commit_list(revs);
 }
diff --git a/read-cache.c b/read-cache.c
index 7db5588..a92b25b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -23,6 +23,80 @@
 
 struct index_state the_index;
 
+static unsigned int hash_name(const char *name, int namelen)
+{
+	unsigned int hash = 0x123;
+
+	do {
+		unsigned char c = *name++;
+		hash = hash*101 + c;
+	} while (--namelen);
+	return hash;
+}
+
+static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
+{
+	void **pos;
+	unsigned int hash;
+
+	if (ce->ce_flags & CE_HASHED)
+		return;
+	ce->ce_flags |= CE_HASHED;
+	ce->next = NULL;
+	hash = hash_name(ce->name, ce_namelen(ce));
+	pos = insert_hash(hash, ce, &istate->name_hash);
+	if (pos) {
+		ce->next = *pos;
+		*pos = ce;
+	}
+}
+
+static void lazy_init_name_hash(struct index_state *istate)
+{
+	int nr;
+
+	if (istate->name_hash_initialized)
+		return;
+	for (nr = 0; nr < istate->cache_nr; nr++)
+		hash_index_entry(istate, istate->cache[nr]);
+	istate->name_hash_initialized = 1;
+}
+
+static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
+{
+	ce->ce_flags &= ~CE_UNHASHED;
+	istate->cache[nr] = ce;
+	if (istate->name_hash_initialized)
+		hash_index_entry(istate, ce);
+}
+
+static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
+{
+	struct cache_entry *old = istate->cache[nr];
+
+	remove_index_entry(old);
+	set_index_entry(istate, nr, ce);
+	istate->cache_changed = 1;
+}
+
+int index_name_exists(struct index_state *istate, const char *name, int namelen)
+{
+	unsigned int hash = hash_name(name, namelen);
+	struct cache_entry *ce;
+
+	lazy_init_name_hash(istate);
+	ce = lookup_hash(hash, &istate->name_hash);
+
+	while (ce) {
+		if (!(ce->ce_flags & CE_UNHASHED)) {
+			if (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))
+				return 1;
+		}
+		ce = ce->next;
+	}
+	return 0;
+}
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -30,20 +104,19 @@
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-	ce->ce_ctime.sec = htonl(st->st_ctime);
-	ce->ce_mtime.sec = htonl(st->st_mtime);
-#ifdef USE_NSEC
-	ce->ce_ctime.nsec = htonl(st->st_ctim.tv_nsec);
-	ce->ce_mtime.nsec = htonl(st->st_mtim.tv_nsec);
-#endif
-	ce->ce_dev = htonl(st->st_dev);
-	ce->ce_ino = htonl(st->st_ino);
-	ce->ce_uid = htonl(st->st_uid);
-	ce->ce_gid = htonl(st->st_gid);
-	ce->ce_size = htonl(st->st_size);
+	ce->ce_ctime = st->st_ctime;
+	ce->ce_mtime = st->st_mtime;
+	ce->ce_dev = st->st_dev;
+	ce->ce_ino = st->st_ino;
+	ce->ce_uid = st->st_uid;
+	ce->ce_gid = st->st_gid;
+	ce->ce_size = st->st_size;
 
 	if (assume_unchanged)
-		ce->ce_flags |= htons(CE_VALID);
+		ce->ce_flags |= CE_VALID;
+
+	if (S_ISREG(st->st_mode))
+		ce_mark_uptodate(ce);
 }
 
 static int ce_compare_data(struct cache_entry *ce, struct stat *st)
@@ -116,7 +189,7 @@
 			return DATA_CHANGED;
 		break;
 	case S_IFDIR:
-		if (S_ISGITLINK(ntohl(ce->ce_mode)))
+		if (S_ISGITLINK(ce->ce_mode))
 			return 0;
 	default:
 		return TYPE_CHANGED;
@@ -128,14 +201,17 @@
 {
 	unsigned int changed = 0;
 
-	switch (ntohl(ce->ce_mode) & S_IFMT) {
+	if (ce->ce_flags & CE_REMOVE)
+		return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
+
+	switch (ce->ce_mode & S_IFMT) {
 	case S_IFREG:
 		changed |= !S_ISREG(st->st_mode) ? TYPE_CHANGED : 0;
 		/* We consider only the owner x bit to be relevant for
 		 * "mode changes"
 		 */
 		if (trust_executable_bit &&
-		    (0100 & (ntohl(ce->ce_mode) ^ st->st_mode)))
+		    (0100 & (ce->ce_mode ^ st->st_mode)))
 			changed |= MODE_CHANGED;
 		break;
 	case S_IFLNK:
@@ -149,32 +225,18 @@
 		else if (ce_compare_gitlink(ce))
 			changed |= DATA_CHANGED;
 		return changed;
-	case 0: /* Special case: unmerged file in index */
-		return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
 	default:
-		die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
+		die("internal error: ce_mode is %o", ce->ce_mode);
 	}
-	if (ce->ce_mtime.sec != htonl(st->st_mtime))
+	if (ce->ce_mtime != (unsigned int) st->st_mtime)
 		changed |= MTIME_CHANGED;
-	if (ce->ce_ctime.sec != htonl(st->st_ctime))
+	if (ce->ce_ctime != (unsigned int) st->st_ctime)
 		changed |= CTIME_CHANGED;
 
-#ifdef USE_NSEC
-	/*
-	 * nsec seems unreliable - not all filesystems support it, so
-	 * as long as it is in the inode cache you get right nsec
-	 * but after it gets flushed, you get zero nsec.
-	 */
-	if (ce->ce_mtime.nsec != htonl(st->st_mtim.tv_nsec))
-		changed |= MTIME_CHANGED;
-	if (ce->ce_ctime.nsec != htonl(st->st_ctim.tv_nsec))
-		changed |= CTIME_CHANGED;
-#endif
-
-	if (ce->ce_uid != htonl(st->st_uid) ||
-	    ce->ce_gid != htonl(st->st_gid))
+	if (ce->ce_uid != (unsigned int) st->st_uid ||
+	    ce->ce_gid != (unsigned int) st->st_gid)
 		changed |= OWNER_CHANGED;
-	if (ce->ce_ino != htonl(st->st_ino))
+	if (ce->ce_ino != (unsigned int) st->st_ino)
 		changed |= INODE_CHANGED;
 
 #ifdef USE_STDEV
@@ -183,17 +245,23 @@
 	 * clients will have different views of what "device"
 	 * the filesystem is on
 	 */
-	if (ce->ce_dev != htonl(st->st_dev))
+	if (ce->ce_dev != (unsigned int) st->st_dev)
 		changed |= INODE_CHANGED;
 #endif
 
-	if (ce->ce_size != htonl(st->st_size))
+	if (ce->ce_size != (unsigned int) st->st_size)
 		changed |= DATA_CHANGED;
 
 	return changed;
 }
 
-int ie_match_stat(struct index_state *istate,
+static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
+{
+	return (istate->timestamp &&
+		((unsigned int)istate->timestamp) <= ce->ce_mtime);
+}
+
+int ie_match_stat(const struct index_state *istate,
 		  struct cache_entry *ce, struct stat *st,
 		  unsigned int options)
 {
@@ -205,7 +273,7 @@
 	 * If it's marked as always valid in the index, it's
 	 * valid whatever the checked-out copy says.
 	 */
-	if (!ignore_valid && (ce->ce_flags & htons(CE_VALID)))
+	if (!ignore_valid && (ce->ce_flags & CE_VALID))
 		return 0;
 
 	changed = ce_match_stat_basic(ce, st);
@@ -226,9 +294,7 @@
 	 * whose mtime are the same as the index file timestamp more
 	 * carefully than others.
 	 */
-	if (!changed &&
-	    istate->timestamp &&
-	    istate->timestamp <= ntohl(ce->ce_mtime.sec)) {
+	if (!changed && is_racy_timestamp(istate, ce)) {
 		if (assume_racy_is_modified)
 			changed |= DATA_CHANGED;
 		else
@@ -238,7 +304,7 @@
 	return changed;
 }
 
-int ie_modified(struct index_state *istate,
+int ie_modified(const struct index_state *istate,
 		struct cache_entry *ce, struct stat *st, unsigned int options)
 {
 	int changed, changed_fs;
@@ -257,7 +323,7 @@
 	 * the length field is zero.  For other cases the ce_size
 	 * should match the SHA1 recorded in the index entry.
 	 */
-	if ((changed & DATA_CHANGED) && ce->ce_size != htonl(0))
+	if ((changed & DATA_CHANGED) && ce->ce_size != 0)
 		return changed;
 
 	changed_fs = ce_modified_check_fs(ce, st);
@@ -285,6 +351,41 @@
 	return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
 }
 
+/*
+ * df_name_compare() is identical to base_name_compare(), except it
+ * compares conflicting directory/file entries as equal. Note that
+ * while a directory name compares as equal to a regular file, they
+ * then individually compare _differently_ to a filename that has
+ * a dot after the basename (because '\0' < '.' < '/').
+ *
+ * This is used by routines that want to traverse the git namespace
+ * but then handle conflicting entries together when possible.
+ */
+int df_name_compare(const char *name1, int len1, int mode1,
+		    const char *name2, int len2, int mode2)
+{
+	int len = len1 < len2 ? len1 : len2, cmp;
+	unsigned char c1, c2;
+
+	cmp = memcmp(name1, name2, len);
+	if (cmp)
+		return cmp;
+	/* Directories and files compare equal (same length, same name) */
+	if (len1 == len2)
+		return 0;
+	c1 = name1[len];
+	if (!c1 && S_ISDIR(mode1))
+		c1 = '/';
+	c2 = name2[len];
+	if (!c2 && S_ISDIR(mode2))
+		c2 = '/';
+	if (c1 == '/' && !c2)
+		return 0;
+	if (c2 == '/' && !c1)
+		return 0;
+	return c1 - c2;
+}
+
 int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
 {
 	int len1 = flags1 & CE_NAMEMASK;
@@ -311,7 +412,7 @@
 	return 0;
 }
 
-int index_name_pos(struct index_state *istate, const char *name, int namelen)
+int index_name_pos(const struct index_state *istate, const char *name, int namelen)
 {
 	int first, last;
 
@@ -320,7 +421,7 @@
 	while (last > first) {
 		int next = (last + first) >> 1;
 		struct cache_entry *ce = istate->cache[next];
-		int cmp = cache_name_compare(name, namelen, ce->name, ntohs(ce->ce_flags));
+		int cmp = cache_name_compare(name, namelen, ce->name, ce->ce_flags);
 		if (!cmp)
 			return next;
 		if (cmp < 0) {
@@ -335,6 +436,9 @@
 /* Remove entry, return true if there are more entries to go.. */
 int remove_index_entry_at(struct index_state *istate, int pos)
 {
+	struct cache_entry *ce = istate->cache[pos];
+
+	remove_index_entry(ce);
 	istate->cache_changed = 1;
 	istate->cache_nr--;
 	if (pos >= istate->cache_nr)
@@ -405,7 +509,7 @@
 	size = cache_entry_size(namelen);
 	ce = xcalloc(1, size);
 	memcpy(ce->name, path, namelen);
-	ce->ce_flags = htons(namelen);
+	ce->ce_flags = namelen;
 	fill_stat_cache_info(ce, &st);
 
 	if (trust_executable_bit && has_symlinks)
@@ -427,6 +531,7 @@
 	    !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
 		/* Nothing changed, really */
 		free(ce);
+		ce_mark_uptodate(istate->cache[pos]);
 		return 0;
 	}
 
@@ -583,7 +688,7 @@
 			continue;
 		if (p->name[len] != '/')
 			continue;
-		if (!ce_stage(p) && !p->ce_mode)
+		if (p->ce_flags & CE_REMOVE)
 			continue;
 		retval = -1;
 		if (!ok_to_replace)
@@ -616,7 +721,7 @@
 		}
 		len = slash - name;
 
-		pos = index_name_pos(istate, name, ntohs(create_ce_flags(len, stage)));
+		pos = index_name_pos(istate, name, create_ce_flags(len, stage));
 		if (pos >= 0) {
 			/*
 			 * Found one, but not so fast.  This could
@@ -626,7 +731,7 @@
 			 * it is Ok to have a directory at the same
 			 * path.
 			 */
-			if (stage || istate->cache[pos]->ce_mode) {
+			if (!(istate->cache[pos]->ce_flags & CE_REMOVE)) {
 				retval = -1;
 				if (!ok_to_replace)
 					break;
@@ -648,8 +753,9 @@
 			    (p->name[len] != '/') ||
 			    memcmp(p->name, name, len))
 				break; /* not our subdirectory */
-			if (ce_stage(p) == stage && (stage || p->ce_mode))
-				/* p is at the same stage as our entry, and
+			if (ce_stage(p) == stage && !(p->ce_flags & CE_REMOVE))
+				/*
+				 * p is at the same stage as our entry, and
 				 * is a subdirectory of what we are looking
 				 * at, so we cannot have conflicts at our
 				 * level or anything shorter.
@@ -679,7 +785,7 @@
 	/*
 	 * When ce is an "I am going away" entry, we allow it to be added
 	 */
-	if (!ce_stage(ce) && !ce->ce_mode)
+	if (ce->ce_flags & CE_REMOVE)
 		return 0;
 
 	/*
@@ -704,12 +810,11 @@
 	int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
 
 	cache_tree_invalidate_path(istate->cache_tree, ce->name);
-	pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
+	pos = index_name_pos(istate, ce->name, ce->ce_flags);
 
 	/* existing match? Just replace it. */
 	if (pos >= 0) {
-		istate->cache_changed = 1;
-		istate->cache[pos] = ce;
+		replace_index_entry(istate, pos, ce);
 		return 0;
 	}
 	pos = -pos-1;
@@ -736,7 +841,7 @@
 		if (!ok_to_replace)
 			return error("'%s' appears as both a file and as a directory",
 				     ce->name);
-		pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
+		pos = index_name_pos(istate, ce->name, ce->ce_flags);
 		pos = -pos-1;
 	}
 	return pos + 1;
@@ -769,7 +874,7 @@
 		memmove(istate->cache + pos + 1,
 			istate->cache + pos,
 			(istate->cache_nr - pos - 1) * sizeof(ce));
-	istate->cache[pos] = ce;
+	set_index_entry(istate, pos, ce);
 	istate->cache_changed = 1;
 	return 0;
 }
@@ -794,6 +899,9 @@
 	int changed, size;
 	int ignore_valid = options & CE_MATCH_IGNORE_VALID;
 
+	if (ce_uptodate(ce))
+		return ce;
+
 	if (lstat(ce->name, &st) < 0) {
 		if (err)
 			*err = errno;
@@ -810,10 +918,17 @@
 		 * valid again, under "assume unchanged" mode.
 		 */
 		if (ignore_valid && assume_unchanged &&
-		    !(ce->ce_flags & htons(CE_VALID)))
+		    !(ce->ce_flags & CE_VALID))
 			; /* mark this one VALID again */
-		else
+		else {
+			/*
+			 * We do not mark the index itself "modified"
+			 * because CE_UPTODATE flag is in-core only;
+			 * we are not going to write this change out.
+			 */
+			ce_mark_uptodate(ce);
 			return ce;
+		}
 	}
 
 	if (ie_modified(istate, ce, &st, options)) {
@@ -826,7 +941,6 @@
 	updated = xmalloc(size);
 	memcpy(updated, ce, size);
 	fill_stat_cache_info(updated, &st);
-
 	/*
 	 * If ignore_valid is not set, we should leave CE_VALID bit
 	 * alone.  Otherwise, paths marked with --no-assume-unchanged
@@ -834,8 +948,8 @@
 	 * automatically, which is not really what we want.
 	 */
 	if (!ignore_valid && assume_unchanged &&
-	    !(ce->ce_flags & htons(CE_VALID)))
-		updated->ce_flags &= ~htons(CE_VALID);
+	    !(ce->ce_flags & CE_VALID))
+		updated->ce_flags &= ~CE_VALID;
 
 	return updated;
 }
@@ -880,7 +994,7 @@
 				/* If we are doing --really-refresh that
 				 * means the index is not valid anymore.
 				 */
-				ce->ce_flags &= ~htons(CE_VALID);
+				ce->ce_flags &= ~CE_VALID;
 				istate->cache_changed = 1;
 			}
 			if (quiet)
@@ -889,11 +1003,8 @@
 			has_errors = 1;
 			continue;
 		}
-		istate->cache_changed = 1;
-		/* You can NOT just free istate->cache[i] here, since it
-		 * might not be necessarily malloc()ed but can also come
-		 * from mmap(). */
-		istate->cache[i] = new;
+
+		replace_index_entry(istate, i, new);
 	}
 	return has_errors;
 }
@@ -942,16 +1053,58 @@
 	return read_index_from(istate, get_index_file());
 }
 
+static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
+{
+	size_t len;
+
+	ce->ce_ctime = ntohl(ondisk->ctime.sec);
+	ce->ce_mtime = ntohl(ondisk->mtime.sec);
+	ce->ce_dev   = ntohl(ondisk->dev);
+	ce->ce_ino   = ntohl(ondisk->ino);
+	ce->ce_mode  = ntohl(ondisk->mode);
+	ce->ce_uid   = ntohl(ondisk->uid);
+	ce->ce_gid   = ntohl(ondisk->gid);
+	ce->ce_size  = ntohl(ondisk->size);
+	/* On-disk flags are just 16 bits */
+	ce->ce_flags = ntohs(ondisk->flags);
+	hashcpy(ce->sha1, ondisk->sha1);
+
+	len = ce->ce_flags & CE_NAMEMASK;
+	if (len == CE_NAMEMASK)
+		len = strlen(ondisk->name);
+	/*
+	 * NEEDSWORK: If the original index is crafted, this copy could
+	 * go unchecked.
+	 */
+	memcpy(ce->name, ondisk->name, len + 1);
+}
+
+static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
+{
+	long per_entry;
+
+	per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry);
+
+	/*
+	 * Alignment can cause differences. This should be "alignof", but
+	 * since that's a gcc'ism, just use the size of a pointer.
+	 */
+	per_entry += sizeof(void *);
+	return ondisk_size + entries*per_entry;
+}
+
 /* remember to discard_cache() before reading a different cache! */
 int read_index_from(struct index_state *istate, const char *path)
 {
 	int fd, i;
 	struct stat st;
-	unsigned long offset;
+	unsigned long src_offset, dst_offset;
 	struct cache_header *hdr;
+	void *mmap;
+	size_t mmap_size;
 
 	errno = EBUSY;
-	if (istate->mmap)
+	if (istate->alloc)
 		return istate->cache_nr;
 
 	errno = ENOENT;
@@ -967,31 +1120,47 @@
 		die("cannot stat the open index (%s)", strerror(errno));
 
 	errno = EINVAL;
-	istate->mmap_size = xsize_t(st.st_size);
-	if (istate->mmap_size < sizeof(struct cache_header) + 20)
+	mmap_size = xsize_t(st.st_size);
+	if (mmap_size < sizeof(struct cache_header) + 20)
 		die("index file smaller than expected");
 
-	istate->mmap = xmmap(NULL, istate->mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+	mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
 	close(fd);
+	if (mmap == MAP_FAILED)
+		die("unable to map index file");
 
-	hdr = istate->mmap;
-	if (verify_hdr(hdr, istate->mmap_size) < 0)
+	hdr = mmap;
+	if (verify_hdr(hdr, mmap_size) < 0)
 		goto unmap;
 
 	istate->cache_nr = ntohl(hdr->hdr_entries);
 	istate->cache_alloc = alloc_nr(istate->cache_nr);
 	istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
 
-	offset = sizeof(*hdr);
+	/*
+	 * The disk format is actually larger than the in-memory format,
+	 * due to space for nsec etc, so even though the in-memory one
+	 * has room for a few  more flags, we can allocate using the same
+	 * index size
+	 */
+	istate->alloc = xmalloc(estimate_cache_size(mmap_size, istate->cache_nr));
+
+	src_offset = sizeof(*hdr);
+	dst_offset = 0;
 	for (i = 0; i < istate->cache_nr; i++) {
+		struct ondisk_cache_entry *disk_ce;
 		struct cache_entry *ce;
 
-		ce = (struct cache_entry *)((char *)(istate->mmap) + offset);
-		offset = offset + ce_size(ce);
-		istate->cache[i] = ce;
+		disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
+		ce = (struct cache_entry *)((char *)istate->alloc + dst_offset);
+		convert_from_disk(disk_ce, ce);
+		set_index_entry(istate, i, ce);
+
+		src_offset += ondisk_ce_size(ce);
+		dst_offset += ce_size(ce);
 	}
 	istate->timestamp = st.st_mtime;
-	while (offset <= istate->mmap_size - 20 - 8) {
+	while (src_offset <= mmap_size - 20 - 8) {
 		/* After an array of active_nr index entries,
 		 * there can be arbitrary number of extended
 		 * sections, each of which is prefixed with
@@ -999,40 +1168,47 @@
 		 * in 4-byte network byte order.
 		 */
 		unsigned long extsize;
-		memcpy(&extsize, (char *)(istate->mmap) + offset + 4, 4);
+		memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
 		extsize = ntohl(extsize);
 		if (read_index_extension(istate,
-					 ((const char *) (istate->mmap)) + offset,
-					 (char *) (istate->mmap) + offset + 8,
+					 (const char *) mmap + src_offset,
+					 (char *) mmap + src_offset + 8,
 					 extsize) < 0)
 			goto unmap;
-		offset += 8;
-		offset += extsize;
+		src_offset += 8;
+		src_offset += extsize;
 	}
+	munmap(mmap, mmap_size);
 	return istate->cache_nr;
 
 unmap:
-	munmap(istate->mmap, istate->mmap_size);
+	munmap(mmap, mmap_size);
 	errno = EINVAL;
 	die("index file corrupt");
 }
 
 int discard_index(struct index_state *istate)
 {
-	int ret;
-
 	istate->cache_nr = 0;
 	istate->cache_changed = 0;
 	istate->timestamp = 0;
+	free_hash(&istate->name_hash);
 	cache_tree_free(&(istate->cache_tree));
-	if (istate->mmap == NULL)
-		return 0;
-	ret = munmap(istate->mmap, istate->mmap_size);
-	istate->mmap = NULL;
-	istate->mmap_size = 0;
+	free(istate->alloc);
+	istate->alloc = NULL;
 
 	/* no need to throw away allocated active_cache */
-	return ret;
+	return 0;
+}
+
+int unmerged_index(const struct index_state *istate)
+{
+	int i;
+	for (i = 0; i < istate->cache_nr; i++) {
+		if (ce_stage(istate->cache[i]))
+			return 1;
+	}
+	return 0;
 }
 
 #define WRITE_BUFFER_SIZE 8192
@@ -1144,11 +1320,33 @@
 		 * file, and never calls us, so the cached size information
 		 * for "frotz" stays 6 which does not match the filesystem.
 		 */
-		ce->ce_size = htonl(0);
+		ce->ce_size = 0;
 	}
 }
 
-int write_index(struct index_state *istate, int newfd)
+static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)
+{
+	int size = ondisk_ce_size(ce);
+	struct ondisk_cache_entry *ondisk = xcalloc(1, size);
+
+	ondisk->ctime.sec = htonl(ce->ce_ctime);
+	ondisk->ctime.nsec = 0;
+	ondisk->mtime.sec = htonl(ce->ce_mtime);
+	ondisk->mtime.nsec = 0;
+	ondisk->dev  = htonl(ce->ce_dev);
+	ondisk->ino  = htonl(ce->ce_ino);
+	ondisk->mode = htonl(ce->ce_mode);
+	ondisk->uid  = htonl(ce->ce_uid);
+	ondisk->gid  = htonl(ce->ce_gid);
+	ondisk->size = htonl(ce->ce_size);
+	hashcpy(ondisk->sha1, ce->sha1);
+	ondisk->flags = htons(ce->ce_flags);
+	memcpy(ondisk->name, ce->name, ce_namelen(ce));
+
+	return ce_write(c, fd, ondisk, size);
+}
+
+int write_index(const struct index_state *istate, int newfd)
 {
 	SHA_CTX c;
 	struct cache_header hdr;
@@ -1157,7 +1355,7 @@
 	int entries = istate->cache_nr;
 
 	for (i = removed = 0; i < entries; i++)
-		if (!cache[i]->ce_mode)
+		if (cache[i]->ce_flags & CE_REMOVE)
 			removed++;
 
 	hdr.hdr_signature = htonl(CACHE_SIGNATURE);
@@ -1170,12 +1368,11 @@
 
 	for (i = 0; i < entries; i++) {
 		struct cache_entry *ce = cache[i];
-		if (!ce->ce_mode)
+		if (ce->ce_flags & CE_REMOVE)
 			continue;
-		if (istate->timestamp &&
-		    istate->timestamp <= ntohl(ce->ce_mtime.sec))
+		if (is_racy_timestamp(istate, ce))
 			ce_smudge_racily_clean_entry(ce);
-		if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
+		if (ce_write_entry(&c, newfd, ce) < 0)
 			return -1;
 	}
 
diff --git a/receive-pack.c b/receive-pack.c
index c90ec7d..f83ae87 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -132,6 +132,7 @@
 				break;
 		}
 	}
+	close(proc.in);
 	return hook_status(finish_command(&proc), hook_name);
 }
 
@@ -414,6 +415,7 @@
 		if (start_command(&ip))
 			return "index-pack fork failed";
 		pack_lockfile = index_pack_lockfile(ip.out);
+		close(ip.out);
 		status = finish_command(&ip);
 		if (!status) {
 			reprepare_packed_git();
diff --git a/refs.c b/refs.c
index fb33da1..1b0050e 100644
--- a/refs.c
+++ b/refs.c
@@ -157,6 +157,7 @@
 	struct ref_list *loose;
 	struct ref_list *packed;
 } cached_refs;
+static struct ref_list *current_ref;
 
 static void free_ref_list(struct ref_list *list)
 {
@@ -476,6 +477,7 @@
 		error("%s does not point to a valid object!", entry->name);
 		return 0;
 	}
+	current_ref = entry;
 	return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
 }
 
@@ -485,6 +487,16 @@
 	unsigned char base[20];
 	struct object *o;
 
+	if (current_ref && (current_ref->name == ref
+		|| !strcmp(current_ref->name, ref))) {
+		if (current_ref->flag & REF_KNOWS_PEELED) {
+			hashcpy(sha1, current_ref->peeled);
+			return 0;
+		}
+		hashcpy(base, current_ref->sha1);
+		goto fallback;
+	}
+
 	if (!resolve_ref(ref, base, 1, &flag))
 		return -1;
 
@@ -504,7 +516,7 @@
 		}
 	}
 
-	/* fallback - callers should not call this for unpacked refs */
+fallback:
 	o = parse_object(base);
 	if (o && o->type == OBJ_TAG) {
 		o = deref_tag(o, ref, 0);
@@ -519,7 +531,7 @@
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
 			   void *cb_data)
 {
-	int retval;
+	int retval = 0;
 	struct ref_list *packed = get_packed_refs();
 	struct ref_list *loose = get_loose_refs();
 
@@ -539,15 +551,18 @@
 		}
 		retval = do_one_ref(base, fn, trim, cb_data, entry);
 		if (retval)
-			return retval;
+			goto end_each;
 	}
 
 	for (packed = packed ? packed : loose; packed; packed = packed->next) {
 		retval = do_one_ref(base, fn, trim, cb_data, packed);
 		if (retval)
-			return retval;
+			goto end_each;
 	}
-	return 0;
+
+end_each:
+	current_ref = NULL;
+	return retval;
 }
 
 int head_ref(each_ref_fn fn, void *cb_data)
@@ -1018,7 +1033,7 @@
 	return 1;
 }
 
-static int close_ref(struct ref_lock *lock)
+int close_ref(struct ref_lock *lock)
 {
 	if (close_lock_file(lock->lk))
 		return -1;
@@ -1026,7 +1041,7 @@
 	return 0;
 }
 
-static int commit_ref(struct ref_lock *lock)
+int commit_ref(struct ref_lock *lock)
 {
 	if (commit_lock_file(lock->lk))
 		return -1;
diff --git a/refs.h b/refs.h
index 9cd16f8..06abee1 100644
--- a/refs.h
+++ b/refs.h
@@ -33,6 +33,12 @@
 #define REF_NODEREF	0x01
 extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
 
+/** Close the file descriptor owned by a lock and return the status */
+extern int close_ref(struct ref_lock *lock);
+
+/** Close and commit the ref locked by the lock */
+extern int commit_ref(struct ref_lock *lock);
+
 /** Release any lock taken but not written. **/
 extern void unlock_ref(struct ref_lock *lock);
 
diff --git a/remote.c b/remote.c
index d8a4671..2d9af40 100644
--- a/remote.c
+++ b/remote.c
@@ -2,123 +2,184 @@
 #include "remote.h"
 #include "refs.h"
 
+struct counted_string {
+	size_t len;
+	const char *s;
+};
+struct rewrite {
+	const char *base;
+	size_t baselen;
+	struct counted_string *instead_of;
+	int instead_of_nr;
+	int instead_of_alloc;
+};
+
 static struct remote **remotes;
-static int allocated_remotes;
+static int remotes_alloc;
+static int remotes_nr;
 
 static struct branch **branches;
-static int allocated_branches;
+static int branches_alloc;
+static int branches_nr;
 
 static struct branch *current_branch;
 static const char *default_remote_name;
 
+static struct rewrite **rewrite;
+static int rewrite_alloc;
+static int rewrite_nr;
+
 #define BUF_SIZE (2048)
 static char buffer[BUF_SIZE];
 
+static const char *alias_url(const char *url)
+{
+	int i, j;
+	char *ret;
+	struct counted_string *longest;
+	int longest_i;
+
+	longest = NULL;
+	longest_i = -1;
+	for (i = 0; i < rewrite_nr; i++) {
+		if (!rewrite[i])
+			continue;
+		for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
+			if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+			    (!longest ||
+			     longest->len < rewrite[i]->instead_of[j].len)) {
+				longest = &(rewrite[i]->instead_of[j]);
+				longest_i = i;
+			}
+		}
+	}
+	if (!longest)
+		return url;
+
+	ret = malloc(rewrite[longest_i]->baselen +
+		     (strlen(url) - longest->len) + 1);
+	strcpy(ret, rewrite[longest_i]->base);
+	strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+	return ret;
+}
+
 static void add_push_refspec(struct remote *remote, const char *ref)
 {
-	int nr = remote->push_refspec_nr + 1;
-	remote->push_refspec =
-		xrealloc(remote->push_refspec, nr * sizeof(char *));
-	remote->push_refspec[nr-1] = ref;
-	remote->push_refspec_nr = nr;
+	ALLOC_GROW(remote->push_refspec,
+		   remote->push_refspec_nr + 1,
+		   remote->push_refspec_alloc);
+	remote->push_refspec[remote->push_refspec_nr++] = ref;
 }
 
 static void add_fetch_refspec(struct remote *remote, const char *ref)
 {
-	int nr = remote->fetch_refspec_nr + 1;
-	remote->fetch_refspec =
-		xrealloc(remote->fetch_refspec, nr * sizeof(char *));
-	remote->fetch_refspec[nr-1] = ref;
-	remote->fetch_refspec_nr = nr;
+	ALLOC_GROW(remote->fetch_refspec,
+		   remote->fetch_refspec_nr + 1,
+		   remote->fetch_refspec_alloc);
+	remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
 }
 
 static void add_url(struct remote *remote, const char *url)
 {
-	int nr = remote->url_nr + 1;
-	remote->url =
-		xrealloc(remote->url, nr * sizeof(char *));
-	remote->url[nr-1] = url;
-	remote->url_nr = nr;
+	ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
+	remote->url[remote->url_nr++] = url;
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+	add_url(remote, alias_url(url));
 }
 
 static struct remote *make_remote(const char *name, int len)
 {
-	int i, empty = -1;
+	struct remote *ret;
+	int i;
 
-	for (i = 0; i < allocated_remotes; i++) {
-		if (!remotes[i]) {
-			if (empty < 0)
-				empty = i;
-		} else {
-			if (len ? (!strncmp(name, remotes[i]->name, len) &&
-				   !remotes[i]->name[len]) :
-			    !strcmp(name, remotes[i]->name))
-				return remotes[i];
-		}
+	for (i = 0; i < remotes_nr; i++) {
+		if (len ? (!strncmp(name, remotes[i]->name, len) &&
+			   !remotes[i]->name[len]) :
+		    !strcmp(name, remotes[i]->name))
+			return remotes[i];
 	}
 
-	if (empty < 0) {
-		empty = allocated_remotes;
-		allocated_remotes += allocated_remotes ? allocated_remotes : 1;
-		remotes = xrealloc(remotes,
-				   sizeof(*remotes) * allocated_remotes);
-		memset(remotes + empty, 0,
-		       (allocated_remotes - empty) * sizeof(*remotes));
-	}
-	remotes[empty] = xcalloc(1, sizeof(struct remote));
+	ret = xcalloc(1, sizeof(struct remote));
+	ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
+	remotes[remotes_nr++] = ret;
 	if (len)
-		remotes[empty]->name = xstrndup(name, len);
+		ret->name = xstrndup(name, len);
 	else
-		remotes[empty]->name = xstrdup(name);
-	return remotes[empty];
+		ret->name = xstrdup(name);
+	return ret;
 }
 
 static void add_merge(struct branch *branch, const char *name)
 {
-	int nr = branch->merge_nr + 1;
-	branch->merge_name =
-		xrealloc(branch->merge_name, nr * sizeof(char *));
-	branch->merge_name[nr-1] = name;
-	branch->merge_nr = nr;
+	ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
+		   branch->merge_alloc);
+	branch->merge_name[branch->merge_nr++] = name;
 }
 
 static struct branch *make_branch(const char *name, int len)
 {
-	int i, empty = -1;
+	struct branch *ret;
+	int i;
 	char *refname;
 
-	for (i = 0; i < allocated_branches; i++) {
-		if (!branches[i]) {
-			if (empty < 0)
-				empty = i;
-		} else {
-			if (len ? (!strncmp(name, branches[i]->name, len) &&
-				   !branches[i]->name[len]) :
-			    !strcmp(name, branches[i]->name))
-				return branches[i];
-		}
+	for (i = 0; i < branches_nr; i++) {
+		if (len ? (!strncmp(name, branches[i]->name, len) &&
+			   !branches[i]->name[len]) :
+		    !strcmp(name, branches[i]->name))
+			return branches[i];
 	}
 
-	if (empty < 0) {
-		empty = allocated_branches;
-		allocated_branches += allocated_branches ? allocated_branches : 1;
-		branches = xrealloc(branches,
-				   sizeof(*branches) * allocated_branches);
-		memset(branches + empty, 0,
-		       (allocated_branches - empty) * sizeof(*branches));
-	}
-	branches[empty] = xcalloc(1, sizeof(struct branch));
+	ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
+	ret = xcalloc(1, sizeof(struct branch));
+	branches[branches_nr++] = ret;
 	if (len)
-		branches[empty]->name = xstrndup(name, len);
+		ret->name = xstrndup(name, len);
 	else
-		branches[empty]->name = xstrdup(name);
+		ret->name = xstrdup(name);
 	refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
 	strcpy(refname, "refs/heads/");
-	strcpy(refname + strlen("refs/heads/"),
-	       branches[empty]->name);
-	branches[empty]->refname = refname;
+	strcpy(refname + strlen("refs/heads/"), ret->name);
+	ret->refname = refname;
 
-	return branches[empty];
+	return ret;
+}
+
+static struct rewrite *make_rewrite(const char *base, int len)
+{
+	struct rewrite *ret;
+	int i;
+
+	for (i = 0; i < rewrite_nr; i++) {
+		if (len
+		    ? (len == rewrite[i]->baselen &&
+		       !strncmp(base, rewrite[i]->base, len))
+		    : !strcmp(base, rewrite[i]->base))
+			return rewrite[i];
+	}
+
+	ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+	ret = xcalloc(1, sizeof(struct rewrite));
+	rewrite[rewrite_nr++] = ret;
+	if (len) {
+		ret->base = xstrndup(base, len);
+		ret->baselen = len;
+	}
+	else {
+		ret->base = xstrdup(base);
+		ret->baselen = strlen(base);
+	}
+	return ret;
+}
+
+static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
+{
+	ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
+	rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
+	rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
+	rewrite->instead_of_nr++;
 }
 
 static void read_remotes_file(struct remote *remote)
@@ -154,7 +215,7 @@
 
 		switch (value_list) {
 		case 0:
-			add_url(remote, xstrdup(s));
+			add_url_alias(remote, xstrdup(s));
 			break;
 		case 1:
 			add_push_refspec(remote, xstrdup(s));
@@ -222,7 +283,7 @@
 		strbuf_reset(&branch);
 		strbuf_addstr(&branch, "HEAD:");
 	}
-	add_url(remote, p);
+	add_url_alias(remote, p);
 	add_fetch_refspec(remote, strbuf_detach(&branch, 0));
 	remote->fetch_tags = 1; /* always auto-follow */
 }
@@ -252,6 +313,19 @@
 		}
 		return 0;
 	}
+	if (!prefixcmp(key, "url.")) {
+		struct rewrite *rewrite;
+		name = key + 4;
+		subkey = strrchr(name, '.');
+		if (!subkey)
+			return 0;
+		rewrite = make_rewrite(name, subkey - name);
+		if (!strcmp(subkey, ".insteadof")) {
+			if (!value)
+				return config_error_nonbool(key);
+			add_instead_of(rewrite, xstrdup(value));
+		}
+	}
 	if (prefixcmp(key,  "remote."))
 		return 0;
 	name = key + 7;
@@ -299,10 +373,23 @@
 			remote->fetch_tags = -1;
 	} else if (!strcmp(subkey, ".proxy")) {
 		remote->http_proxy = xstrdup(value);
-	}
+	} else if (!strcmp(subkey, ".skipdefaultupdate"))
+		remote->skip_default_update = 1;
 	return 0;
 }
 
+static void alias_all_urls(void)
+{
+	int i, j;
+	for (i = 0; i < remotes_nr; i++) {
+		if (!remotes[i])
+			continue;
+		for (j = 0; j < remotes[i]->url_nr; j++) {
+			remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
+		}
+	}
+}
+
 static void read_config(void)
 {
 	unsigned char sha1[20];
@@ -319,9 +406,10 @@
 			make_branch(head_ref + strlen("refs/heads/"), 0);
 	}
 	git_config(handle_config);
+	alias_all_urls();
 }
 
-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
 	int i;
 	int st;
@@ -431,17 +519,32 @@
 	return rs;
 
  invalid:
+	if (verify) {
+		free(rs);
+		return NULL;
+	}
 	die("Invalid refspec '%s'", refspec[i]);
 }
 
+int valid_fetch_refspec(const char *fetch_refspec_str)
+{
+	const char *fetch_refspec[] = { fetch_refspec_str };
+	struct refspec *refspec;
+
+	refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+	if (refspec)
+		free(refspec);
+	return !!refspec;
+}
+
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
 {
-	return parse_refspec_internal(nr_refspec, refspec, 1);
+	return parse_refspec_internal(nr_refspec, refspec, 1, 0);
 }
 
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 {
-	return parse_refspec_internal(nr_refspec, refspec, 0);
+	return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
 
 static int valid_remote_nick(const char *name)
@@ -469,7 +572,7 @@
 			read_branches_file(ret);
 	}
 	if (!ret->url)
-		add_url(ret, name);
+		add_url_alias(ret, name);
 	if (!ret->url)
 		return NULL;
 	ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
@@ -481,7 +584,7 @@
 {
 	int i, result = 0;
 	read_config();
-	for (i = 0; i < allocated_remotes && !result; i++) {
+	for (i = 0; i < remotes_nr && !result; i++) {
 		struct remote *r = remotes[i];
 		if (!r)
 			continue;
@@ -608,8 +711,7 @@
 	struct ref *next;
 	while (ref) {
 		next = ref->next;
-		if (ref->peer_ref)
-			free(ref->peer_ref);
+		free(ref->peer_ref);
 		free(ref);
 		ref = next;
 	}
@@ -710,6 +812,26 @@
 	return ret;
 }
 
+static char *guess_ref(const char *name, struct ref *peer)
+{
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char sha1[20];
+
+	const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+	if (!r)
+		return NULL;
+
+	if (!prefixcmp(r, "refs/heads/"))
+		strbuf_addstr(&buf, "refs/heads/");
+	else if (!prefixcmp(r, "refs/tags/"))
+		strbuf_addstr(&buf, "refs/tags/");
+	else
+		return NULL;
+
+	strbuf_addstr(&buf, name);
+	return strbuf_detach(&buf, NULL);
+}
+
 static int match_explicit(struct ref *src, struct ref *dst,
 			  struct ref ***dst_tail,
 			  struct refspec *rs,
@@ -718,6 +840,7 @@
 	struct ref *matched_src, *matched_dst;
 
 	const char *dst_value = rs->dst;
+	char *dst_guess;
 
 	if (rs->pattern)
 		return errs;
@@ -745,9 +868,17 @@
 		errs = 1;
 
 	if (!dst_value) {
+		unsigned char sha1[20];
+		int flag;
+
 		if (!matched_src)
 			return errs;
-		dst_value = matched_src->name;
+		dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
+		if (!dst_value ||
+		    ((flag & REF_ISSYMREF) &&
+		     prefixcmp(dst_value, "refs/heads/")))
+			die("%s cannot be resolved to branch.",
+			    matched_src->name);
 	}
 
 	switch (count_refspec_match(dst_value, dst, &matched_dst)) {
@@ -756,10 +887,15 @@
 	case 0:
 		if (!memcmp(dst_value, "refs/", 5))
 			matched_dst = make_linked_ref(dst_value, dst_tail);
+		else if((dst_guess = guess_ref(dst_value, matched_src)))
+			matched_dst = make_linked_ref(dst_guess, dst_tail);
 		else
-			error("dst refspec %s does not match any "
-			      "existing ref on the remote and does "
-			      "not start with refs/.", dst_value);
+			error("unable to push to unqualified destination: %s\n"
+			      "The destination refspec neither matches an "
+			      "existing ref on the remote nor\n"
+			      "begins with refs/, and we are unable to "
+			      "guess a prefix based on the source ref.",
+			      dst_value);
 		break;
 	default:
 		matched_dst = NULL;
diff --git a/remote.h b/remote.h
index 6b8c32c..a38774b 100644
--- a/remote.h
+++ b/remote.h
@@ -6,14 +6,17 @@
 
 	const char **url;
 	int url_nr;
+	int url_alloc;
 
 	const char **push_refspec;
 	struct refspec *push;
 	int push_refspec_nr;
+	int push_refspec_alloc;
 
 	const char **fetch_refspec;
 	struct refspec *fetch;
 	int fetch_refspec_nr;
+	int fetch_refspec_alloc;
 
 	/*
 	 * -1 to never fetch tags
@@ -22,6 +25,7 @@
 	 * 2 to always fetch tags
 	 */
 	int fetch_tags;
+	int skip_default_update;
 
 	const char *receivepack;
 	const char *uploadpack;
@@ -63,6 +67,7 @@
  */
 void ref_remove_duplicates(struct ref *ref_map);
 
+int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
 
@@ -101,6 +106,7 @@
 	const char **merge_name;
 	struct refspec **merge;
 	int merge_nr;
+	int merge_alloc;
 };
 
 struct branch *branch_get(const char *name);
diff --git a/revision.c b/revision.c
index d79f2b3..ffbed3f 100644
--- a/revision.c
+++ b/revision.c
@@ -46,6 +46,8 @@
 
 static void mark_blob_uninteresting(struct blob *blob)
 {
+	if (!blob)
+		return;
 	if (blob->object.flags & UNINTERESTING)
 		return;
 	blob->object.flags |= UNINTERESTING;
@@ -57,6 +59,8 @@
 	struct name_entry entry;
 	struct object *obj = &tree->object;
 
+	if (!tree)
+		return;
 	if (obj->flags & UNINTERESTING)
 		return;
 	obj->flags |= UNINTERESTING;
@@ -173,6 +177,8 @@
 		struct tag *tag = (struct tag *) object;
 		if (revs->tag_objects && !(flags & UNINTERESTING))
 			add_pending_object(revs, object, tag->tag);
+		if (!tag->tagged)
+			die("bad tag");
 		object = parse_object(tag->tagged->sha1);
 		if (!object)
 			die("bad object %s", sha1_to_hex(tag->tagged->sha1));
@@ -558,8 +564,39 @@
 	free_patch_ids(&ids);
 }
 
+/* How many extra uninteresting commits we want to see.. */
+#define SLOP 5
+
+static int still_interesting(struct commit_list *src, unsigned long date, int slop)
+{
+	/*
+	 * No source list at all? We're definitely done..
+	 */
+	if (!src)
+		return 0;
+
+	/*
+	 * Does the destination list contain entries with a date
+	 * before the source list? Definitely _not_ done.
+	 */
+	if (date < src->item->date)
+		return SLOP;
+
+	/*
+	 * Does the source list still have interesting commits in
+	 * it? Definitely not done..
+	 */
+	if (!everybody_uninteresting(src))
+		return SLOP;
+
+	/* Ok, we're closing in.. */
+	return slop-1;
+}
+
 static int limit_list(struct rev_info *revs)
 {
+	int slop = SLOP;
+	unsigned long date = ~0ul;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -579,12 +616,19 @@
 			return -1;
 		if (obj->flags & UNINTERESTING) {
 			mark_parents_uninteresting(commit);
-			if (everybody_uninteresting(list))
-				break;
-			continue;
+			if (revs->show_all)
+				p = &commit_list_insert(commit, p)->next;
+			slop = still_interesting(list, date, slop);
+			if (slop)
+				continue;
+			/* If showing all, add the whole pending list to the end */
+			if (revs->show_all)
+				*p = list;
+			break;
 		}
 		if (revs->min_age != -1 && (commit->date > revs->min_age))
 			continue;
+		date = commit->date;
 		p = &commit_list_insert(commit, p)->next;
 
 		show = show_early_output;
@@ -617,12 +661,13 @@
 	return 0;
 }
 
-static void handle_all(struct rev_info *revs, unsigned flags)
+static void handle_refs(struct rev_info *revs, unsigned flags,
+		int (*for_each)(each_ref_fn, void *))
 {
 	struct all_refs_cb cb;
 	cb.all_revs = revs;
 	cb.all_flags = flags;
-	for_each_ref(handle_one_ref, &cb);
+	for_each(handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -685,6 +730,8 @@
 		it = get_reference(revs, arg, sha1, 0);
 		if (it->type != OBJ_TAG)
 			break;
+		if (!((struct tag*)it)->tagged)
+			return 0;
 		hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
 	}
 	if (it->type != OBJ_COMMIT)
@@ -720,6 +767,10 @@
 	revs->commit_format = CMIT_FMT_DEFAULT;
 
 	diff_setup(&revs->diffopt);
+	if (prefix && !revs->diffopt.prefix) {
+		revs->diffopt.prefix = prefix;
+		revs->diffopt.prefix_length = strlen(prefix);
+	}
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
@@ -920,6 +971,7 @@
 	int left = 1;
 	int all_match = 0;
 	int regflags = 0;
+	int fixed = 0;
 
 	/* First, search for "--" */
 	seen_dashdash = 0;
@@ -988,7 +1040,19 @@
 				continue;
 			}
 			if (!strcmp(arg, "--all")) {
-				handle_all(revs, flags);
+				handle_refs(revs, flags, for_each_ref);
+				continue;
+			}
+			if (!strcmp(arg, "--branches")) {
+				handle_refs(revs, flags, for_each_branch_ref);
+				continue;
+			}
+			if (!strcmp(arg, "--tags")) {
+				handle_refs(revs, flags, for_each_tag_ref);
+				continue;
+			}
+			if (!strcmp(arg, "--remotes")) {
+				handle_refs(revs, flags, for_each_remote_ref);
 				continue;
 			}
 			if (!strcmp(arg, "--first-parent")) {
@@ -1052,6 +1116,10 @@
 				revs->dense = 0;
 				continue;
 			}
+			if (!strcmp(arg, "--show-all")) {
+				revs->show_all = 1;
+				continue;
+			}
 			if (!strcmp(arg, "--remove-empty")) {
 				revs->remove_empty_trees = 1;
 				continue;
@@ -1213,6 +1281,11 @@
 				regflags |= REG_ICASE;
 				continue;
 			}
+			if (!strcmp(arg, "--fixed-strings") ||
+			    !strcmp(arg, "-F")) {
+				fixed = 1;
+				continue;
+			}
 			if (!strcmp(arg, "--all-match")) {
 				all_match = 1;
 				continue;
@@ -1268,8 +1341,10 @@
 		}
 	}
 
-	if (revs->grep_filter)
+	if (revs->grep_filter) {
 		revs->grep_filter->regflags |= regflags;
+		revs->grep_filter->fixed = fixed;
+	}
 
 	if (show_merge)
 		prepare_show_merge(revs);
@@ -1435,6 +1510,8 @@
 		return commit_ignore;
 	if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
 		return commit_ignore;
+	if (revs->show_all)
+		return commit_show;
 	if (commit->object.flags & UNINTERESTING)
 		return commit_ignore;
 	if (revs->min_age != -1 && (commit->date > revs->min_age))
diff --git a/revision.h b/revision.h
index 8572315..c8b3b94 100644
--- a/revision.h
+++ b/revision.h
@@ -33,6 +33,7 @@
 			prune:1,
 			no_merges:1,
 			no_walk:1,
+			show_all:1,
 			remove_empty_trees:1,
 			simplify_history:1,
 			lifo:1,
@@ -74,7 +75,7 @@
 	struct log_info *loginfo;
 	int		nr, total;
 	const char	*mime_boundary;
-	const char	*message_id;
+	char		*message_id;
 	const char	*ref_message_id;
 	const char	*add_signoff;
 	const char	*extra_headers;
diff --git a/run-command.c b/run-command.c
index 476d00c..44100a7 100644
--- a/run-command.c
+++ b/run-command.c
@@ -20,12 +20,19 @@
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 
+	/*
+	 * In case of errors we must keep the promise to close FDs
+	 * that have been passed in via ->in and ->out.
+	 */
+
 	need_in = !cmd->no_stdin && cmd->in < 0;
 	if (need_in) {
-		if (pipe(fdin) < 0)
+		if (pipe(fdin) < 0) {
+			if (cmd->out > 0)
+				close(cmd->out);
 			return -ERR_RUN_COMMAND_PIPE;
+		}
 		cmd->in = fdin[1];
-		cmd->close_in = 1;
 	}
 
 	need_out = !cmd->no_stdout
@@ -35,10 +42,11 @@
 		if (pipe(fdout) < 0) {
 			if (need_in)
 				close_pair(fdin);
+			else if (cmd->in)
+				close(cmd->in);
 			return -ERR_RUN_COMMAND_PIPE;
 		}
 		cmd->out = fdout[0];
-		cmd->close_out = 1;
 	}
 
 	need_err = !cmd->no_stderr && cmd->err < 0;
@@ -46,8 +54,12 @@
 		if (pipe(fderr) < 0) {
 			if (need_in)
 				close_pair(fdin);
+			else if (cmd->in)
+				close(cmd->in);
 			if (need_out)
 				close_pair(fdout);
+			else if (cmd->out)
+				close(cmd->out);
 			return -ERR_RUN_COMMAND_PIPE;
 		}
 		cmd->err = fderr[0];
@@ -57,8 +69,12 @@
 	if (cmd->pid < 0) {
 		if (need_in)
 			close_pair(fdin);
+		else if (cmd->in)
+			close(cmd->in);
 		if (need_out)
 			close_pair(fdout);
+		else if (cmd->out)
+			close(cmd->out);
 		if (need_err)
 			close_pair(fderr);
 		return -ERR_RUN_COMMAND_FORK;
@@ -75,6 +91,13 @@
 			close(cmd->in);
 		}
 
+		if (cmd->no_stderr)
+			dup_devnull(2);
+		else if (need_err) {
+			dup2(fderr[1], 2);
+			close_pair(fderr);
+		}
+
 		if (cmd->no_stdout)
 			dup_devnull(1);
 		else if (cmd->stdout_to_stderr)
@@ -87,13 +110,6 @@
 			close(cmd->out);
 		}
 
-		if (cmd->no_stderr)
-			dup_devnull(2);
-		else if (need_err) {
-			dup2(fderr[1], 2);
-			close_pair(fderr);
-		}
-
 		if (cmd->dir && chdir(cmd->dir))
 			die("exec %s: cd to %s failed (%s)", cmd->argv[0],
 			    cmd->dir, strerror(errno));
@@ -120,7 +136,7 @@
 
 	if (need_out)
 		close(fdout[1]);
-	else if (cmd->out > 1)
+	else if (cmd->out)
 		close(cmd->out);
 
 	if (need_err)
@@ -157,10 +173,6 @@
 
 int finish_command(struct child_process *cmd)
 {
-	if (cmd->close_in)
-		close(cmd->in);
-	if (cmd->close_out)
-		close(cmd->out);
 	return wait_or_whine(cmd->pid);
 }
 
diff --git a/run-command.h b/run-command.h
index 1fc781d..debe307 100644
--- a/run-command.h
+++ b/run-command.h
@@ -14,13 +14,29 @@
 struct child_process {
 	const char **argv;
 	pid_t pid;
+	/*
+	 * Using .in, .out, .err:
+	 * - Specify 0 for no redirections (child inherits stdin, stdout,
+	 *   stderr from parent).
+	 * - Specify -1 to have a pipe allocated as follows:
+	 *     .in: returns the writable pipe end; parent writes to it,
+	 *          the readable pipe end becomes child's stdin
+	 *     .out, .err: returns the readable pipe end; parent reads from
+	 *          it, the writable pipe end becomes child's stdout/stderr
+	 *   The caller of start_command() must close the returned FDs
+	 *   after it has completed reading from/writing to it!
+	 * - Specify > 0 to set a channel to a particular FD as follows:
+	 *     .in: a readable FD, becomes child's stdin
+	 *     .out: a writable FD, becomes child's stdout/stderr
+	 *     .err > 0 not supported
+	 *   The specified FD is closed by start_command(), even in case
+	 *   of errors!
+	 */
 	int in;
 	int out;
 	int err;
 	const char *dir;
 	const char *const *env;
-	unsigned close_in:1;
-	unsigned close_out:1;
 	unsigned no_stdin:1;
 	unsigned no_stdout:1;
 	unsigned no_stderr:1;
diff --git a/setup.c b/setup.c
index 4509598..3d2d958 100644
--- a/setup.c
+++ b/setup.c
@@ -4,51 +4,118 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
+static int sanitary_path_copy(char *dst, const char *src)
+{
+	char *dst0 = dst;
+
+	if (*src == '/') {
+		*dst++ = '/';
+		while (*src == '/')
+			src++;
+	}
+
+	for (;;) {
+		char c = *src;
+
+		/*
+		 * A path component that begins with . could be
+		 * special:
+		 * (1) "." and ends   -- ignore and terminate.
+		 * (2) "./"           -- ignore them, eat slash and continue.
+		 * (3) ".." and ends  -- strip one and terminate.
+		 * (4) "../"          -- strip one, eat slash and continue.
+		 */
+		if (c == '.') {
+			switch (src[1]) {
+			case '\0':
+				/* (1) */
+				src++;
+				break;
+			case '/':
+				/* (2) */
+				src += 2;
+				while (*src == '/')
+					src++;
+				continue;
+			case '.':
+				switch (src[2]) {
+				case '\0':
+					/* (3) */
+					src += 2;
+					goto up_one;
+				case '/':
+					/* (4) */
+					src += 3;
+					while (*src == '/')
+						src++;
+					goto up_one;
+				}
+			}
+		}
+
+		/* copy up to the next '/', and eat all '/' */
+		while ((c = *src++) != '\0' && c != '/')
+			*dst++ = c;
+		if (c == '/') {
+			*dst++ = c;
+			while (c == '/')
+				c = *src++;
+			src--;
+		} else if (!c)
+			break;
+		continue;
+
+	up_one:
+		/*
+		 * dst0..dst is prefix portion, and dst[-1] is '/';
+		 * go up one level.
+		 */
+		dst -= 2; /* go past trailing '/' if any */
+		if (dst < dst0)
+			return -1;
+		while (1) {
+			if (dst <= dst0)
+				break;
+			c = *dst--;
+			if (c == '/') {
+				dst += 2;
+				break;
+			}
+		}
+	}
+	*dst = '\0';
+	return 0;
+}
+
 const char *prefix_path(const char *prefix, int len, const char *path)
 {
 	const char *orig = path;
-	for (;;) {
-		char c;
-		if (*path != '.')
-			break;
-		c = path[1];
-		/* "." */
-		if (!c) {
-			path++;
-			break;
-		}
-		/* "./" */
-		if (c == '/') {
-			path += 2;
-			continue;
-		}
-		if (c != '.')
-			break;
-		c = path[2];
-		if (!c)
-			path += 2;
-		else if (c == '/')
-			path += 3;
-		else
-			break;
-		/* ".." and "../" */
-		/* Remove last component of the prefix */
-		do {
-			if (!len)
-				die("'%s' is outside repository", orig);
-			len--;
-		} while (len && prefix[len-1] != '/');
-		continue;
+	char *sanitized = xmalloc(len + strlen(path) + 1);
+	if (is_absolute_path(orig))
+		strcpy(sanitized, path);
+	else {
+		if (len)
+			memcpy(sanitized, prefix, len);
+		strcpy(sanitized + len, path);
 	}
-	if (len) {
-		int speclen = strlen(path);
-		char *n = xmalloc(speclen + len + 1);
-
-		memcpy(n, prefix, len);
-		memcpy(n + len, path, speclen+1);
-		path = n;
+	if (sanitary_path_copy(sanitized, sanitized))
+		goto error_out;
+	if (is_absolute_path(orig)) {
+		const char *work_tree = get_git_work_tree();
+		size_t len = strlen(work_tree);
+		size_t total = strlen(sanitized) + 1;
+		if (strncmp(sanitized, work_tree, len) ||
+		    (sanitized[len] != '\0' && sanitized[len] != '/')) {
+		error_out:
+			error("'%s' is outside repository", orig);
+			free(sanitized);
+			return NULL;
+		}
+		if (sanitized[len] == '/')
+			len++;
+		memmove(sanitized, sanitized + len, total - len);
 	}
-	return path;
+	return sanitized;
 }
 
 /*
@@ -114,7 +181,7 @@
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
 	const char *entry = *pathspec;
-	const char **p;
+	const char **src, **dst;
 	int prefixlen;
 
 	if (!prefix && !entry)
@@ -128,12 +195,21 @@
 	}
 
 	/* Otherwise we have to re-write the entries.. */
-	p = pathspec;
+	src = pathspec;
+	dst = pathspec;
 	prefixlen = prefix ? strlen(prefix) : 0;
-	do {
-		*p = prefix_path(prefix, prefixlen, entry);
-	} while ((entry = *++p) != NULL);
-	return (const char **) pathspec;
+	while (*src) {
+		const char *p = prefix_path(prefix, prefixlen, *src);
+		if (p)
+			*(dst++) = p;
+		else
+			exit(128); /* error message already given */
+		src++;
+	}
+	*dst = NULL;
+	if (!*pathspec)
+		return NULL;
+	return pathspec;
 }
 
 /*
@@ -250,6 +326,14 @@
 	int len, offset;
 
 	/*
+	 * Let's assume that we are in a git repository.
+	 * If it turns out later that we are somewhere else, the value will be
+	 * updated accordingly.
+	 */
+	if (nongit_ok)
+		*nongit_ok = 0;
+
+	/*
 	 * If GIT_DIR is set explicitly, we're not going
 	 * to do any discovery, but we still do repository
 	 * validation.
@@ -374,8 +458,7 @@
 	} else if (strcmp(var, "core.worktree") == 0) {
 		if (!value)
 			return config_error_nonbool(var);
-		if (git_work_tree_cfg)
-			free(git_work_tree_cfg);
+		free(git_work_tree_cfg);
 		git_work_tree_cfg = xstrdup(value);
 		inside_work_tree = -1;
 	}
diff --git a/sha1_file.c b/sha1_file.c
index 66a4e00..445a871 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -14,6 +14,7 @@
 #include "tag.h"
 #include "tree.h"
 #include "refs.h"
+#include "pack-revindex.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -1367,11 +1368,15 @@
 	unsigned long dummy;
 	unsigned char *next_sha1;
 	enum object_type type;
+	struct revindex_entry *revidx;
 
 	*delta_chain_length = 0;
 	curpos = obj_offset;
 	type = unpack_object_header(p, &w_curs, &curpos, size);
 
+	revidx = find_pack_revindex(p, obj_offset);
+	*store_size = revidx[1].offset - obj_offset;
+
 	for (;;) {
 		switch (type) {
 		default:
@@ -1381,14 +1386,13 @@
 		case OBJ_TREE:
 		case OBJ_BLOB:
 		case OBJ_TAG:
-			*store_size = 0; /* notyet */
 			unuse_pack(&w_curs);
 			return typename(type);
 		case OBJ_OFS_DELTA:
 			obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
 			if (*delta_chain_length == 0) {
-				/* TODO: find base_sha1 as pointed by curpos */
-				hashclr(base_sha1);
+				revidx = find_pack_revindex(p, obj_offset);
+				hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
 			}
 			break;
 		case OBJ_REF_DELTA:
@@ -1845,6 +1849,15 @@
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
 
+static struct cached_object empty_tree = {
+	/* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */
+	"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60"
+	"\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04",
+	OBJ_TREE,
+	"",
+	0
+};
+
 static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
 	int i;
@@ -1854,6 +1867,8 @@
 		if (!hashcmp(co->sha1, sha1))
 			return co;
 	}
+	if (!hashcmp(sha1, empty_tree.sha1))
+		return &empty_tree;
 	return NULL;
 }
 
@@ -1943,7 +1958,8 @@
 		}
 		ref_length = strlen(ref_type);
 
-		if (memcmp(buffer, ref_type, ref_length) ||
+		if (ref_length + 40 > isize ||
+		    memcmp(buffer, ref_type, ref_length) ||
 		    get_sha1_hex((char *) buffer + ref_length, actual_sha1)) {
 			free(buffer);
 			return NULL;
@@ -2358,7 +2374,8 @@
 	if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
 		struct strbuf nbuf;
 		strbuf_init(&nbuf, 0);
-		if (convert_to_git(path, buf, size, &nbuf)) {
+		if (convert_to_git(path, buf, size, &nbuf,
+		                   write_object ? safe_crlf : 0)) {
 			munmap(buf, size);
 			buf = strbuf_detach(&nbuf, &size);
 			re_allocated = 1;
diff --git a/sha1_name.c b/sha1_name.c
index 6298c37..b0b2167 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -192,26 +192,25 @@
 
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
-	int status, is_null;
+	int status, exists;
 	static char hex[41];
 
-	is_null = is_null_sha1(sha1);
+	exists = has_sha1_file(sha1);
 	memcpy(hex, sha1_to_hex(sha1), 40);
 	if (len == 40 || !len)
 		return hex;
 	while (len < 40) {
 		unsigned char sha1_ret[20];
 		status = get_short_sha1(hex, len, sha1_ret, 1);
-		if (!status ||
-		    (is_null && status != SHORT_NAME_AMBIGUOUS)) {
+		if (exists
+		    ? !status
+		    : status == SHORT_NAME_NOT_FOUND) {
 			hex[len] = 0;
 			return hex;
 		}
-		if (status != SHORT_NAME_AMBIGUOUS)
-			return NULL;
 		len++;
 	}
-	return NULL;
+	return hex;
 }
 
 static int ambiguous_path(const char *path, int len)
@@ -430,6 +429,37 @@
 	return 0;
 }
 
+struct object *peel_to_type(const char *name, int namelen,
+			    struct object *o, enum object_type expected_type)
+{
+	if (name && !namelen)
+		namelen = strlen(name);
+	if (!o) {
+		unsigned char sha1[20];
+		if (get_sha1_1(name, namelen, sha1))
+			return NULL;
+		o = parse_object(sha1);
+	}
+	while (1) {
+		if (!o || (!o->parsed && !parse_object(o->sha1)))
+			return NULL;
+		if (o->type == expected_type)
+			return o;
+		if (o->type == OBJ_TAG)
+			o = ((struct tag*) o)->tagged;
+		else if (o->type == OBJ_COMMIT)
+			o = &(((struct commit *) o)->tree->object);
+		else {
+			if (name)
+				error("%.*s: expected %s type, but the object "
+				      "dereferences to %s type",
+				      namelen, name, typename(expected_type),
+				      typename(o->type));
+			return NULL;
+		}
+	}
+}
+
 static int peel_onion(const char *name, int len, unsigned char *sha1)
 {
 	unsigned char outer[20];
@@ -481,29 +511,17 @@
 		hashcpy(sha1, o->sha1);
 	}
 	else {
-		/* At this point, the syntax look correct, so
+		/*
+		 * At this point, the syntax look correct, so
 		 * if we do not get the needed object, we should
 		 * barf.
 		 */
-
-		while (1) {
-			if (!o || (!o->parsed && !parse_object(o->sha1)))
-				return -1;
-			if (o->type == expected_type) {
-				hashcpy(sha1, o->sha1);
-				return 0;
-			}
-			if (o->type == OBJ_TAG)
-				o = ((struct tag*) o)->tagged;
-			else if (o->type == OBJ_COMMIT)
-				o = &(((struct commit *) o)->tree->object);
-			else
-				return error("%.*s: expected %s type, but the object dereferences to %s type",
-					     len, name, typename(expected_type),
-					     typename(o->type));
-			if (!o->parsed)
-				parse_object(o->sha1);
+		o = peel_to_type(name, len, o, expected_type);
+		if (o) {
+			hashcpy(sha1, o->sha1);
+			return 0;
 		}
+		return -1;
 	}
 	return 0;
 }
@@ -583,8 +601,11 @@
 	struct object *object = parse_object(sha1);
 	if (!object)
 		return 0;
-	if (object->type == OBJ_TAG)
+	if (object->type == OBJ_TAG) {
 		object = deref_tag(object, path, strlen(path));
+		if (!object)
+			return 0;
+	}
 	if (object->type != OBJ_COMMIT)
 		return 0;
 	insert_by_date((struct commit *)object, list);
@@ -622,9 +643,9 @@
 		unsigned long size;
 
 		commit = pop_most_recent_commit(&list, ONELINE_SEEN);
-		parse_object(commit->object.sha1);
-		if (temp_commit_buffer)
-			free(temp_commit_buffer);
+		if (!parse_object(commit->object.sha1))
+			continue;
+		free(temp_commit_buffer);
 		if (commit->buffer)
 			p = commit->buffer;
 		else {
@@ -641,8 +662,7 @@
 			break;
 		}
 	}
-	if (temp_commit_buffer)
-		free(temp_commit_buffer);
+	free(temp_commit_buffer);
 	free_commit_list(list);
 	for (l = backup; l; l = l->next)
 		clear_commit_marks(l->item, ONELINE_SEEN);
@@ -700,7 +720,7 @@
 				break;
 			if (ce_stage(ce) == stage) {
 				hashcpy(sha1, ce->sha1);
-				*mode = ntohl(ce->ce_mode);
+				*mode = ce->ce_mode;
 				return 0;
 			}
 			pos++;
diff --git a/shallow.c b/shallow.c
index dbd9f5a..4d90eda 100644
--- a/shallow.c
+++ b/shallow.c
@@ -56,7 +56,7 @@
 			if (i < heads->nr) {
 				commit = (struct commit *)
 					deref_tag(heads->objects[i++].item, NULL, 0);
-				if (commit->object.type != OBJ_COMMIT) {
+				if (!commit || commit->object.type != OBJ_COMMIT) {
 					commit = NULL;
 					continue;
 				}
@@ -70,7 +70,8 @@
 				cur_depth = *(int *)commit->util;
 			}
 		}
-		parse_commit(commit);
+		if (parse_commit(commit))
+			die("invalid commit");
 		commit->object.flags |= not_shallow_flag;
 		cur_depth++;
 		for (p = commit->parents, commit = NULL; p; p = p->next) {
diff --git a/shortlog.h b/shortlog.h
new file mode 100644
index 0000000..31ff491
--- /dev/null
+++ b/shortlog.h
@@ -0,0 +1,26 @@
+#ifndef SHORTLOG_H
+#define SHORTLOG_H
+
+#include "path-list.h"
+
+struct shortlog {
+	struct path_list list;
+	int summary;
+	int wrap_lines;
+	int sort_by_number;
+	int wrap;
+	int in1;
+	int in2;
+
+	char *common_repo_prefix;
+	int email;
+	struct path_list mailmap;
+};
+
+void shortlog_init(struct shortlog *log);
+
+void shortlog_add_commit(struct shortlog *log, struct commit *commit);
+
+void shortlog_output(struct shortlog *log);
+
+#endif
diff --git a/strbuf.c b/strbuf.c
index 5efcfc8..4aed752 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -146,11 +146,12 @@
 	strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_expand(struct strbuf *sb, const char *format,
-                   const char **placeholders, expand_fn_t fn, void *context)
+void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
+		   void *context)
 {
 	for (;;) {
-		const char *percent, **p;
+		const char *percent;
+		size_t consumed;
 
 		percent = strchrnul(format, '%');
 		strbuf_add(sb, format, percent - format);
@@ -158,14 +159,10 @@
 			break;
 		format = percent + 1;
 
-		for (p = placeholders; *p; p++) {
-			if (!prefixcmp(format, *p))
-				break;
-		}
-		if (*p) {
-			fn(sb, *p, context);
-			format += strlen(*p);
-		} else
+		consumed = fn(sb, format, context);
+		if (consumed)
+			format += consumed;
+		else
 			strbuf_addch(sb, '%');
 	}
 }
diff --git a/strbuf.h b/strbuf.h
index 36d61db..faec229 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -103,8 +103,8 @@
 }
 extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
 
-typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
-extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context);
+typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
+extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
 
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
diff --git a/t/.gitattributes b/t/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/README b/t/README
index 36f2517..73ed11b 100644
--- a/t/README
+++ b/t/README
@@ -160,14 +160,12 @@
 
  - test_expect_failure <message> <script>
 
-   This is the opposite of test_expect_success.  If <script>
-   yields success, test is considered a failure.
-
-   Example:
-
-	test_expect_failure \
-	    'git-update-index without --add should fail adding.' \
-	    'git-update-index should-be-empty'
+   This is NOT the opposite of test_expect_success, but is used
+   to mark a test that demonstrates a known breakage.  Unlike
+   the usual test_expect_success tests, which say "ok" on
+   success and "FAIL" on failure, this will say "FIXED" on
+   success and "still broken" on failure.  Failures from these
+   tests won't cause -i (immediate) to stop.
 
  - test_debug <script>
 
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
index 7dc6d7e..28b941c 100644
--- a/t/diff-lib.sh
+++ b/t/diff-lib.sh
@@ -21,8 +21,8 @@
     # Also we do not check SHA1 hash generation in this test, which
     # is a job for t0000-basic.sh
 
-    tr '\000' '\012' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
-    tr '\000' '\012' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
+    perl -pe 'y/\000/\012/' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
+    perl -pe 'y/\000/\012/' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
     git diff .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 9ee35e7..9decd2e 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -49,8 +49,28 @@
 	test-chmtime +1 "$1"
 }
 
-SVN_HTTPD_MODULE_PATH=${SVN_HTTPD_MODULE_PATH-'/usr/lib/apache2/modules'}
-SVN_HTTPD_PATH=${SVN_HTTPD_PATH-'/usr/sbin/apache2'}
+for d in \
+	"$SVN_HTTPD_PATH" \
+	/usr/sbin/apache2 \
+	/usr/sbin/httpd \
+; do
+	if test -f "$d"
+	then
+		SVN_HTTPD_PATH="$d"
+		break
+	fi
+done
+for d in \
+	"$SVN_HTTPD_MODULE_PATH" \
+	/usr/lib/apache2/modules \
+	/usr/libexec/apache2 \
+; do
+	if test -d "$d"
+	then
+		SVN_HTTPD_MODULE_PATH="$d"
+		break
+	fi
+done
 
 start_httpd () {
 	if test -z "$SVN_HTTPD_PORT"
@@ -66,6 +86,7 @@
 ServerRoot "$GIT_DIR"
 DocumentRoot "$GIT_DIR"
 PidFile "$GIT_DIR/httpd.pid"
+LockFile logs/accept.lock
 Listen 127.0.0.1:$SVN_HTTPD_PORT
 LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
 LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
new file mode 100644
index 0000000..7f206c5
--- /dev/null
+++ b/t/lib-httpd.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+if test -z "$GIT_TEST_HTTPD"
+then
+	say "skipping test, network testing disabled by default"
+	say "(define GIT_TEST_HTTPD to enable)"
+	test_done
+	exit
+fi
+
+LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'}
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
+
+TEST_PATH="$PWD"/../lib-httpd
+HTTPD_ROOT_PATH="$PWD"/httpd
+HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
+
+if ! test -x "$LIB_HTTPD_PATH"
+then
+        say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
+        test_done
+        exit
+fi
+
+HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
+	sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q'`
+
+if test -n "$HTTPD_VERSION"
+then
+	if test -z "$LIB_HTTPD_MODULE_PATH"
+	then
+		if ! test $HTTPD_VERSION -ge 2
+		then
+			say "skipping test, at least Apache version 2 is required"
+			test_done
+			exit
+		fi
+
+		LIB_HTTPD_MODULE_PATH='/usr/lib/apache2/modules'
+	fi
+else
+	error "Could not identify web server at '$LIB_HTTPD_PATH'"
+fi
+
+HTTPD_PARA="-d $HTTPD_ROOT_PATH -f $TEST_PATH/apache.conf"
+
+prepare_httpd() {
+	mkdir -p $HTTPD_DOCUMENT_ROOT_PATH
+
+	ln -s $LIB_HTTPD_MODULE_PATH $HTTPD_ROOT_PATH/modules
+
+	if test -n "$LIB_HTTPD_SSL"
+	then
+		HTTPD_URL=https://127.0.0.1:$LIB_HTTPD_PORT
+
+		RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \
+			-config $TEST_PATH/ssl.cnf \
+			-new -x509 -nodes \
+			-out $HTTPD_ROOT_PATH/httpd.pem \
+			-keyout $HTTPD_ROOT_PATH/httpd.pem
+		export GIT_SSL_NO_VERIFY=t
+		HTTPD_PARA="$HTTPD_PARA -DSSL"
+	else
+		HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
+	fi
+
+	if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
+	then
+		HTTPD_PARA="$HTTPD_PARA -DDAV"
+
+		if test -n "$LIB_HTTPD_SVN"
+		then
+			HTTPD_PARA="$HTTPD_PARA -DSVN"
+			rawsvnrepo="$HTTPD_ROOT_PATH/svnrepo"
+			svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/svn"
+		fi
+	fi
+}
+
+start_httpd() {
+	prepare_httpd
+
+	trap 'stop_httpd; die' exit
+
+	"$LIB_HTTPD_PATH" $HTTPD_PARA \
+		-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start
+}
+
+stop_httpd() {
+	trap 'die' exit
+
+	"$LIB_HTTPD_PATH" $HTTPD_PARA -k stop
+}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
new file mode 100644
index 0000000..a447346
--- /dev/null
+++ b/t/lib-httpd/apache.conf
@@ -0,0 +1,34 @@
+PidFile httpd.pid
+DocumentRoot www
+ErrorLog error.log
+
+<IfDefine SSL>
+LoadModule ssl_module modules/mod_ssl.so
+
+SSLCertificateFile httpd.pem
+SSLCertificateKeyFile httpd.pem
+SSLRandomSeed startup file:/dev/urandom 512
+SSLRandomSeed connect file:/dev/urandom 512
+SSLSessionCache none
+SSLMutex file:ssl_mutex
+SSLEngine On
+</IfDefine>
+
+<IfDefine DAV>
+	LoadModule dav_module modules/mod_dav.so
+	LoadModule dav_fs_module modules/mod_dav_fs.so
+
+	DAVLockDB DAVLock
+	<Location />
+		Dav on
+	</Location>
+</IfDefine>
+
+<IfDefine SVN>
+	LoadModule dav_svn_module modules/mod_dav_svn.so
+
+	<Location /svn>
+		DAV svn
+		SVNPath svnrepo
+	</Location>
+</IfDefine>
diff --git a/t/lib-httpd/ssl.cnf b/t/lib-httpd/ssl.cnf
new file mode 100644
index 0000000..6dab257
--- /dev/null
+++ b/t/lib-httpd/ssl.cnf
@@ -0,0 +1,8 @@
+RANDFILE                = $ENV::RANDFILE_PATH
+
+[ req ]
+default_bits            = 1024
+distinguished_name      = req_distinguished_name
+prompt                  = no
+[ req_distinguished_name ]
+commonName              = 127.0.0.1
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 4e49d59..27b54cb 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -47,12 +47,24 @@
     'test $(wc -l < full-of-directories) = 3'
 
 ################################################################
+# Test harness
+test_expect_success 'success is reported like this' '
+    :
+'
+test_expect_failure 'pretend we have a known breakage' '
+    false
+'
+test_expect_failure 'pretend we have fixed a known breakage' '
+    :
+'
+
+################################################################
 # Basics of the basics
 
 # updating a new file without --add should fail.
-test_expect_failure \
-    'git update-index without --add should fail adding.' \
-    'git update-index should-be-empty'
+test_expect_success 'git update-index without --add should fail adding.' '
+    ! git update-index should-be-empty
+'
 
 # and with --add it should succeed, even if it is empty (it used to fail).
 test_expect_success \
@@ -70,9 +82,9 @@
 
 # Removing paths.
 rm -f should-be-empty full-of-directories
-test_expect_failure \
-    'git update-index without --remove should fail removing.' \
-    'git update-index should-be-empty'
+test_expect_success 'git update-index without --remove should fail removing.' '
+    ! git update-index should-be-empty
+'
 
 test_expect_success \
     'git update-index with --remove should be able to remove.' \
@@ -204,9 +216,9 @@
     'put invalid objects into the index.' \
     'git update-index --index-info < badobjects'
 
-test_expect_failure \
-    'writing this tree without --missing-ok.' \
-    'git write-tree'
+test_expect_success 'writing this tree without --missing-ok.' '
+    ! git write-tree
+'
 
 test_expect_success \
     'writing this tree with --missing-ok.' \
@@ -292,9 +304,31 @@
 	test "$dir" = "$(test-absolute-path $dir2)" &&
 	file="$dir"/index &&
 	test "$file" = "$(test-absolute-path $dir2/index)" &&
+	basename=blub &&
+	test "$dir/$basename" = $(cd .git && test-absolute-path $basename) &&
 	ln -s ../first/file .git/syml &&
 	sym="$(cd first; pwd -P)"/file &&
 	test "$sym" = "$(test-absolute-path $dir2/syml)"
 '
 
+test_expect_success 'very long name in the index handled sanely' '
+
+	a=a && # 1
+	a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 16
+	a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 256
+	a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 4096
+	a=${a}q &&
+
+	>path4 &&
+	git update-index --add path4 &&
+	(
+		git ls-files -s path4 |
+		sed -e "s/	.*/	/" |
+		tr -d "\012"
+		echo "$a"
+	) | git update-index --index-info &&
+	len=$(git ls-files "a*" | wc -c) &&
+	test $len = 4098
+'
+
 test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index c015405..b0289e3 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -113,4 +113,21 @@
 	fi
 '
 
+test_expect_success 'reinit' '
+
+	(
+		unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG
+
+		mkdir again &&
+		cd again &&
+		git init >out1 2>err1 &&
+		git init >out2 2>err2
+	) &&
+	grep "Initialized empty" again/out1 &&
+	grep "Reinitialized existing" again/out2 &&
+	>again/empty &&
+	test_cmp again/empty again/err1 &&
+	test_cmp again/empty again/err2
+'
+
 test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index e7fa4f5..c56d2fb 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -11,7 +11,7 @@
 
 	git check-attr test -- "$path" >actual &&
 	echo "$path: test: $2" >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 }
 
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 8b27aa8..2bfeac9 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -5,7 +5,11 @@
 . ./test-lib.sh
 
 q_to_nul () {
-	tr Q '\000'
+	perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+	tr Q '\015'
 }
 
 append_cr () {
@@ -42,6 +46,60 @@
 	echo happy.
 '
 
+test_expect_success 'safecrlf: autocrlf=input, all CRLF' '
+
+	git config core.autocrlf input &&
+	git config core.safecrlf true &&
+
+	for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+	! git add allcrlf
+'
+
+test_expect_success 'safecrlf: autocrlf=input, mixed LF/CRLF' '
+
+	git config core.autocrlf input &&
+	git config core.safecrlf true &&
+
+	for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+	! git add mixed
+'
+
+test_expect_success 'safecrlf: autocrlf=true, all LF' '
+
+	git config core.autocrlf true &&
+	git config core.safecrlf true &&
+
+	for w in I am all LF; do echo $w; done >alllf &&
+	! git add alllf
+'
+
+test_expect_success 'safecrlf: autocrlf=true mixed LF/CRLF' '
+
+	git config core.autocrlf true &&
+	git config core.safecrlf true &&
+
+	for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+	! git add mixed
+'
+
+test_expect_success 'safecrlf: print warning only once' '
+
+	git config core.autocrlf input &&
+	git config core.safecrlf warn &&
+
+	for w in I am all LF; do echo $w; done >doublewarn &&
+	git add doublewarn &&
+	git commit -m "nowarn" &&
+	for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
+	test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
+'
+
+test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
+	git config core.autocrlf false &&
+	git config core.safecrlf false &&
+	git reset --hard HEAD^
+'
+
 test_expect_success 'update with autocrlf=input' '
 
 	rm -f tmp one dir/two three &&
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
index 430a1d1..7d1ce2d 100755
--- a/t/t0022-crlf-rename.sh
+++ b/t/t0022-crlf-rename.sh
Binary files differ
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index cad95f3..3ecdd66 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -243,14 +243,14 @@
     test `printf "$ttt$sss$sss$sss" | git stripspace | wc -l` -gt 0
 '
 
-test_expect_failure \
+test_expect_success \
     'text plus spaces without newline at end should not show spaces' '
-    printf "$ttt$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    printf "$ttt$sss$sss$sss" | git stripspace | grep -q "  "
+    ! (printf "$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (printf "$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (printf "$ttt$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (printf "$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (printf "$ttt$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (printf "$ttt$sss$sss$sss" | git stripspace | grep "  " >/dev/null)
 '
 
 test_expect_success \
@@ -280,14 +280,14 @@
     git diff expect actual
 '
 
-test_expect_failure \
+test_expect_success \
     'text plus spaces at end should not show spaces' '
-    echo "$ttt$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$ttt$ttt$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$ttt$sss$sss" | git stripspace | grep -q "  " ||
-    echo "$ttt$sss$sss$sss" | git stripspace | grep -q "  "
+    ! (echo "$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (echo "$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (echo "$ttt$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (echo "$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (echo "$ttt$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
+    ! (echo "$ttt$sss$sss$sss" | git stripspace | grep "  " >/dev/null)
 '
 
 test_expect_success \
@@ -339,13 +339,13 @@
     git diff expect actual
 '
 
-test_expect_failure \
+test_expect_success \
     'spaces without newline at end should not show spaces' '
-    printf "" | git stripspace | grep -q " " ||
-    printf "$sss" | git stripspace | grep -q " " ||
-    printf "$sss$sss" | git stripspace | grep -q " " ||
-    printf "$sss$sss$sss" | git stripspace | grep -q " " ||
-    printf "$sss$sss$sss$sss" | git stripspace | grep -q " "
+    ! (printf "" | git stripspace | grep " " >/dev/null) &&
+    ! (printf "$sss" | git stripspace | grep " " >/dev/null) &&
+    ! (printf "$sss$sss" | git stripspace | grep " " >/dev/null) &&
+    ! (printf "$sss$sss$sss" | git stripspace | grep " " >/dev/null) &&
+    ! (printf "$sss$sss$sss$sss" | git stripspace | grep " " >/dev/null)
 '
 
 test_expect_success \
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 0a3b55d..c23f0ac 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -21,6 +21,9 @@
     --st <st>             get another string (pervert ordering)
     -o <str>              get another string
 
+magic arguments
+    --quux                means --quux
+
 EOF
 
 test_expect_success 'test help' '
@@ -87,9 +90,9 @@
 	git diff expect output
 '
 
-test_expect_failure 'ambiguously abbreviated option' '
+test_expect_success 'ambiguously abbreviated option' '
 	test-parse-options --strin 123;
-        test $? != 129
+	test $? = 129
 '
 
 cat > expect << EOF
@@ -114,4 +117,17 @@
 	git diff expect.err output.err
 '
 
+cat > expect <<EOF
+boolean: 0
+integer: 0
+string: (not set)
+arg 00: --quux
+EOF
+
+test_expect_success 'keep some options as arguments' '
+	test-parse-options --quux > output 2> output.err &&
+        test ! -s output.err &&
+        git diff expect output
+'
+
 test_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
new file mode 100755
index 0000000..3fbad77
--- /dev/null
+++ b/t/t0050-filesystem.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='Various filesystem issues'
+
+. ./test-lib.sh
+
+auml=`printf '\xc3\xa4'`
+aumlcdiar=`printf '\x61\xcc\x88'`
+
+test_expect_success 'see if we expect ' '
+
+	test_case=test_expect_success
+	test_unicode=test_expect_success
+	mkdir junk &&
+	echo good >junk/CamelCase &&
+	echo bad >junk/camelcase &&
+	if test "$(cat junk/CamelCase)" != good
+	then
+		test_case=test_expect_failure
+		say "will test on a case insensitive filesystem"
+	fi &&
+	rm -fr junk &&
+	mkdir junk &&
+	>junk/"$auml" &&
+	case "$(cd junk && echo *)" in
+	"$aumlcdiar")
+		test_unicode=test_expect_failure
+		say "will test on a unicode corrupting filesystem"
+		;;
+	*)	;;
+	esac &&
+	rm -fr junk
+'
+
+test_expect_success "setup case tests" '
+
+	touch camelcase &&
+	git add camelcase &&
+	git commit -m "initial" &&
+	git tag initial &&
+	git checkout -b topic &&
+	git mv camelcase tmp &&
+	git mv tmp CamelCase &&
+	git commit -m "rename" &&
+	git checkout -f master
+
+'
+
+$test_case 'rename (case change)' '
+
+	git mv camelcase CamelCase &&
+	git commit -m "rename"
+
+'
+
+$test_case 'merge (case change)' '
+
+	git reset --hard initial &&
+	git merge topic
+
+'
+
+test_expect_success "setup unicode normalization tests" '
+
+  test_create_repo unicode &&
+  cd unicode &&
+  touch "$aumlcdiar" &&
+  git add "$aumlcdiar" &&
+  git commit -m initial
+  git tag initial &&
+  git checkout -b topic &&
+  git mv $aumlcdiar tmp &&
+  git mv tmp "$auml" &&
+  git commit -m rename &&
+  git checkout -f master
+
+'
+
+$test_unicode 'rename (silent unicode normalization)' '
+
+ git mv "$aumlcdiar" "$auml" &&
+ git commit -m rename
+
+'
+
+$test_unicode 'merge (silent unicode normalization)' '
+
+ git reset --hard initial &&
+ git merge topic
+
+'
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 37add1b..17f519f 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -210,12 +210,12 @@
 
 END_OF_CASE_TABLE
 
-test_expect_failure \
-    '1 - must not have an entry not in A.' \
-    "rm -f .git/index XX &&
+test_expect_success '1 - must not have an entry not in A.' "
+     rm -f .git/index XX &&
      echo XX >XX &&
      git update-index --add XX &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '2 - must match B in !O && !A && B case.' \
@@ -248,13 +248,14 @@
      echo extra >>AN &&
      git read-tree -m $tree_O $tree_A $tree_B"
 
-test_expect_failure \
-    '3 (fail) - must match A in !O && A && !B case.' \
-    "rm -f .git/index AN &&
+test_expect_success \
+    '3 (fail) - must match A in !O && A && !B case.' "
+     rm -f .git/index AN &&
      cp .orig-A/AN AN &&
      echo extra >>AN &&
      git update-index --add AN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '4 - must match and be up-to-date in !O && A && B && A!=B case.' \
@@ -264,21 +265,23 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
-    "rm -f .git/index AA &&
+test_expect_success \
+    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' "
+     rm -f .git/index AA &&
      cp .orig-A/AA AA &&
      git update-index --add AA &&
      echo extra >>AA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
-    "rm -f .git/index AA &&
+test_expect_success \
+    '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' "
+     rm -f .git/index AA &&
      cp .orig-A/AA AA &&
      echo extra >>AA &&
      git update-index --add AA &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '5 - must match in !O && A && B && A==B case.' \
@@ -297,34 +300,38 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '5 (fail) - must match A in !O && A && B && A==B case.' \
-    "rm -f .git/index LL &&
+test_expect_success \
+    '5 (fail) - must match A in !O && A && B && A==B case.' "
+     rm -f .git/index LL &&
      cp .orig-A/LL LL &&
      echo extra >>LL &&
      git update-index --add LL &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '6 - must not exist in O && !A && !B case' \
-    "rm -f .git/index DD &&
+test_expect_success \
+    '6 - must not exist in O && !A && !B case' "
+     rm -f .git/index DD &&
      echo DD >DD
      git update-index --add DD &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '7 - must not exist in O && !A && B && O!=B case' \
-    "rm -f .git/index DM &&
+test_expect_success \
+    '7 - must not exist in O && !A && B && O!=B case' "
+     rm -f .git/index DM &&
      cp .orig-B/DM DM &&
      git update-index --add DM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '8 - must not exist in O && !A && B && O==B case' \
-    "rm -f .git/index DN &&
+test_expect_success \
+    '8 - must not exist in O && !A && B && O==B case' "
+     rm -f .git/index DN &&
      cp .orig-B/DN DN &&
      git update-index --add DN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '9 - must match and be up-to-date in O && A && !B && O!=A case' \
@@ -334,21 +341,23 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
-    "rm -f .git/index MD &&
+test_expect_success \
+    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' "
+     rm -f .git/index MD &&
      cp .orig-A/MD MD &&
      git update-index --add MD &&
      echo extra >>MD &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
-    "rm -f .git/index MD &&
+test_expect_success \
+    '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' "
+     rm -f .git/index MD &&
      cp .orig-A/MD MD &&
      echo extra >>MD &&
      git update-index --add MD &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '10 - must match and be up-to-date in O && A && !B && O==A case' \
@@ -358,21 +367,23 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
-    "rm -f .git/index ND &&
+test_expect_success \
+    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' "
+     rm -f .git/index ND &&
      cp .orig-A/ND ND &&
      git update-index --add ND &&
      echo extra >>ND &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
-    "rm -f .git/index ND &&
+test_expect_success \
+    '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' "
+     rm -f .git/index ND &&
      cp .orig-A/ND ND &&
      echo extra >>ND &&
      git update-index --add ND &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
@@ -382,21 +393,23 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
-    "rm -f .git/index MM &&
+test_expect_success \
+    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' "
+     rm -f .git/index MM &&
      cp .orig-A/MM MM &&
      git update-index --add MM &&
      echo extra >>MM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
-    "rm -f .git/index MM &&
+test_expect_success \
+    '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' "
+     rm -f .git/index MM &&
      cp .orig-A/MM MM &&
      echo extra >>MM &&
      git update-index --add MM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '12 - must match A in O && A && B && O!=A && A==B case' \
@@ -415,13 +428,14 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '12 (fail) - must match A in O && A && B && O!=A && A==B case' \
-    "rm -f .git/index SS &&
+test_expect_success \
+    '12 (fail) - must match A in O && A && B && O!=A && A==B case' "
+     rm -f .git/index SS &&
      cp .orig-A/SS SS &&
      echo extra >>SS &&
      git update-index --add SS &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '13 - must match A in O && A && B && O!=A && O==B case' \
@@ -457,21 +471,23 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
-    "rm -f .git/index NM &&
+test_expect_success \
+    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' "
+     rm -f .git/index NM &&
      cp .orig-A/NM NM &&
      git update-index --add NM &&
      echo extra >>NM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
-test_expect_failure \
-    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
-    "rm -f .git/index NM &&
+test_expect_success \
+    '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' "
+     rm -f .git/index NM &&
      cp .orig-A/NM NM &&
      echo extra >>NM &&
      git update-index --add NM &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 test_expect_success \
     '15 - must match A in O && A && B && O==A && O==B case' \
@@ -490,13 +506,14 @@
      git read-tree -m $tree_O $tree_A $tree_B &&
      check_result"
 
-test_expect_failure \
-    '15 (fail) - must match A in O && A && B && O==A && O==B case' \
-    "rm -f .git/index NN &&
+test_expect_success \
+    '15 (fail) - must match A in O && A && B && O==A && O==B case' "
+     rm -f .git/index NN &&
      cp .orig-A/NN NN &&
      echo extra >>NN &&
      git update-index --add NN &&
-     git read-tree -m $tree_O $tree_A $tree_B"
+     test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
 
 # #16
 test_expect_success \
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index d609a55..570d372 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -116,4 +116,126 @@
 	git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
 '
 
+test_expect_success '3-way not overwriting local changes (setup)' '
+
+	git reset --hard &&
+	git checkout -b side-a branch-point &&
+	echo >>file1 "new line to be kept in the merge result" &&
+	git commit -a -m "side-a changes file1" &&
+	git checkout -b side-b branch-point &&
+	echo >>file2 "new line to be kept in the merge result" &&
+	git commit -a -m "side-b changes file2" &&
+	git checkout side-a
+
+'
+
+test_expect_success '3-way not overwriting local changes (our side)' '
+
+	# At this point, file1 from side-a should be kept as side-b
+	# did not touch it.
+
+	git reset --hard &&
+
+	echo >>file1 "local changes" &&
+	git read-tree -m -u branch-point side-a side-b &&
+	grep "new line to be kept" file1 &&
+	grep "local changes" file1
+
+'
+
+test_expect_success '3-way not overwriting local changes (their side)' '
+
+	# At this point, file2 from side-b should be taken as side-a
+	# did not touch it.
+
+	git reset --hard &&
+
+	echo >>file2 "local changes" &&
+	test_must_fail git read-tree -m -u branch-point side-a side-b &&
+	! grep "new line to be kept" file2 &&
+	grep "local changes" file2
+
+'
+
+test_expect_success 'funny symlink in work tree' '
+
+	git reset --hard &&
+	git checkout -b sym-b side-b &&
+	mkdir -p a &&
+	>a/b &&
+	git add a/b &&
+	git commit -m "side adds a/b" &&
+
+	rm -fr a &&
+	git checkout -b sym-a side-a &&
+	mkdir -p a &&
+	ln -s ../b a/b &&
+	git add a/b &&
+	git commit -m "we add a/b" &&
+
+	git read-tree -m -u sym-a sym-a sym-b
+
+'
+
+test_expect_success 'funny symlink in work tree, un-unlink-able' '
+
+	rm -fr a b &&
+	git reset --hard &&
+
+	git checkout sym-a &&
+	chmod a-w a &&
+	test_must_fail git read-tree -m -u sym-a sym-a sym-b
+
+'
+
+# clean-up from the above test
+chmod a+w a
+rm -fr a b
+
+test_expect_success 'D/F setup' '
+
+	git reset --hard &&
+
+	git checkout side-a &&
+	rm -f subdir/file2 &&
+	mkdir subdir/file2 &&
+	echo qfwfq >subdir/file2/another &&
+	git add subdir/file2/another &&
+	test_tick &&
+	git commit -m "side-a changes file2 to directory"
+
+'
+
+test_expect_success 'D/F' '
+
+	git checkout side-b &&
+	git read-tree -m -u branch-point side-b side-a &&
+	git ls-files -u >actual &&
+	(
+		a=$(git rev-parse branch-point:subdir/file2)
+		b=$(git rev-parse side-a:subdir/file2/another)
+		echo "100644 $a 1	subdir/file2"
+		echo "100644 $a 2	subdir/file2"
+		echo "100644 $b 3	subdir/file2/another"
+	) >expect &&
+	test_cmp actual expect
+
+'
+
+test_expect_success 'D/F resolve' '
+
+	git reset --hard &&
+	git checkout side-b &&
+	git merge-resolve branch-point -- side-b side-a
+
+'
+
+test_expect_success 'D/F recursive' '
+
+	git reset --hard &&
+	git checkout side-b &&
+	git merge-recursive branch-point -- side-b side-a
+
+'
+
 test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
new file mode 100755
index 0000000..b0d31f5
--- /dev/null
+++ b/t/t1005-read-tree-reset.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='read-tree -u --reset'
+
+. ./test-lib.sh
+
+# two-tree test
+
+test_expect_success 'setup' '
+  git init &&
+  mkdir df &&
+  echo content >df/file &&
+  git add df/file &&
+  git commit -m one &&
+  git ls-files >expect &&
+  rm -rf df &&
+  echo content >df &&
+  git add df &&
+  echo content >new &&
+  git add new &&
+  git commit -m two
+'
+
+test_expect_success 'reset should work' '
+  git read-tree -u --reset HEAD^ &&
+  git ls-files >actual &&
+  test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index 991d3c5..dcb3108 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -101,8 +101,8 @@
 echo "Lots of fun" >>example
 git commit -m 'Some fun.' -i hello example
 
-test_expect_failure 'git resolve now fails' '
-	git merge -m "Merge work in mybranch" mybranch
+test_expect_success 'git resolve now fails' '
+	! git merge -m "Merge work in mybranch" mybranch
 '
 
 cat > hello << EOF
@@ -156,6 +156,8 @@
 
 test_expect_success 'git repack' 'git repack'
 test_expect_success 'git prune-packed' 'git prune-packed'
-test_expect_failure '-> only packed objects' 'find -type f .git/objects/[0-9a-f][0-9a-f]'
+test_expect_success '-> only packed objects' '
+	! find -type f .git/objects/[0-9a-f][0-9a-f]
+'
 
 test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index d9e358e..b36a901 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -200,8 +200,9 @@
 test_expect_success 'non-match value' \
 	'test wow = $(git config --get nextsection.nonewline !for)'
 
-test_expect_failure 'ambiguous get' \
-	'git config --get nextsection.nonewline'
+test_expect_success 'ambiguous get' '
+	! git config --get nextsection.nonewline
+'
 
 test_expect_success 'get multivar' \
 	'git config --get-all nextsection.nonewline'
@@ -221,13 +222,17 @@
 
 test_expect_success 'multivar replace' 'cmp .git/config expect'
 
-test_expect_failure 'ambiguous value' 'git config nextsection.nonewline'
+test_expect_success 'ambiguous value' '
+	! git config nextsection.nonewline
+'
 
-test_expect_failure 'ambiguous unset' \
-	'git config --unset nextsection.nonewline'
+test_expect_success 'ambiguous unset' '
+	! git config --unset nextsection.nonewline
+'
 
-test_expect_failure 'invalid unset' \
-	'git config --unset somesection.nonewline'
+test_expect_success 'invalid unset' '
+	! git config --unset somesection.nonewline
+'
 
 git config --unset nextsection.nonewline "wow3$"
 
@@ -243,7 +248,7 @@
 
 test_expect_success 'multivar unset' 'cmp .git/config expect'
 
-test_expect_failure 'invalid key' 'git config inval.2key blabla'
+test_expect_success 'invalid key' '! git config inval.2key blabla'
 
 test_expect_success 'correct key' 'git config 123456.a123 987'
 
@@ -424,8 +429,9 @@
 
 test_expect_success "rename succeeded" "git diff expect .git/config"
 
-test_expect_failure "rename non-existing section" \
-	'git config --rename-section branch."world domination" branch.drei'
+test_expect_success "rename non-existing section" '
+	! git config --rename-section branch."world domination" branch.drei
+'
 
 test_expect_success "rename succeeded" "git diff expect .git/config"
 
@@ -536,14 +542,14 @@
         done &&
 	cmp expect result'
 
-test_expect_failure 'invalid bool (--get)' '
+test_expect_success 'invalid bool (--get)' '
 
 	git config bool.nobool foobar &&
-	git config --bool --get bool.nobool'
+	! git config --bool --get bool.nobool'
 
-test_expect_failure 'invalid bool (set)' '
+test_expect_success 'invalid bool (set)' '
 
-	git config --bool bool.nobool foobar'
+	! git config --bool bool.nobool foobar'
 
 rm .git/config
 
@@ -604,8 +610,9 @@
 
 test_expect_success 'quoting' 'cmp .git/config expect'
 
-test_expect_failure 'key with newline' 'git config key.with\\\
-newline 123'
+test_expect_success 'key with newline' '
+	! git config "key.with
+newline" 123'
 
 test_expect_success 'value with newline' 'git config key.sub value.with\\\
 newline'
@@ -650,12 +657,12 @@
 Qsection.sub=section.val5Q
 EOF
 
-git config --null --list | tr '\000' 'Q' > result
+git config --null --list | perl -pe 'y/\000/Q/' > result
 echo >>result
 
 test_expect_success '--null --list' 'cmp result expect'
 
-git config --null --get-regexp 'val[0-9]' | tr '\000' 'Q' > result
+git config --null --get-regexp 'val[0-9]' | perl -pe 'y/\000/Q/' > result
 echo >>result
 
 test_expect_success '--null --get-regexp' 'cmp result expect'
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 37fc1c8..9be0770 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -40,7 +40,8 @@
 	(git apply --check --index test.patch &&
 	cd test && git apply --check --index ../test.patch)'
 
-test_expect_failure 'gitdir required mode on unsupported repo' '
-	(cd test2 && git apply --check --index ../test.patch)'
+test_expect_success 'gitdir required mode on unsupported repo' '
+	(cd test2 && ! git apply --check --index ../test.patch)
+'
 
 test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 71ab2dd..78cd412 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -51,23 +51,23 @@
 	 test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
-test_expect_failure \
-	'(not) create HEAD with old sha1' \
-	"git update-ref HEAD $A $B"
-test_expect_failure \
-	"(not) prior created .git/$m" \
-	"test -f .git/$m"
+test_expect_success '(not) create HEAD with old sha1' "
+	! git update-ref HEAD $A $B
+"
+test_expect_success "(not) prior created .git/$m" "
+	! test -f .git/$m
+"
 rm -f .git/$m
 
 test_expect_success \
 	"create HEAD" \
 	"git update-ref HEAD $A"
-test_expect_failure \
-	'(not) change HEAD with wrong SHA1' \
-	"git update-ref HEAD $B $Z"
-test_expect_failure \
-	"(not) changed .git/$m" \
-	"test $B"' = $(cat .git/'"$m"')'
+test_expect_success '(not) change HEAD with wrong SHA1' "
+	! git update-ref HEAD $B $Z
+"
+test_expect_success "(not) changed .git/$m" "
+	! test $B"' = $(cat .git/'"$m"')
+'
 rm -f .git/$m
 
 : a repository with working tree always has reflog these days...
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index f959aae..73f830d 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -175,21 +175,30 @@
 
 '
 
-test_expect_success 'prune --expire' '
+test_expect_success 'delete' '
+	echo 1 > C &&
+	test_tick &&
+	git commit -m rat C &&
 
-	before=$(git count-objects | sed "s/ .*//") &&
-	BLOB=$(echo aleph | git hash-object -w --stdin) &&
-	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-	test -f $BLOB_FILE &&
-	git reset --hard &&
-	git prune --expire=1.hour.ago &&
-	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
-	test -f $BLOB_FILE &&
-	test-chmtime -86500 $BLOB_FILE &&
-	git prune --expire 1.day &&
-	test $before = $(git count-objects | sed "s/ .*//") &&
-	! test -f $BLOB_FILE
+	echo 2 > C &&
+	test_tick &&
+	git commit -m ox C &&
+
+	echo 3 > C &&
+	test_tick &&
+	git commit -m tiger C &&
+
+	test 5 = $(git reflog | wc -l) &&
+
+	git reflog delete master@{1} &&
+	git reflog show master > output &&
+	test 4 = $(wc -l < output) &&
+	! grep ox < output &&
+
+	git reflog delete master@{07.04.2005.15:15:00.-0700} &&
+	git reflog show master > output &&
+	test 3 = $(wc -l < output) &&
+	! grep dragon < output
 
 '
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index e474b3f..38a2bf0 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -33,9 +33,9 @@
 test_rev_parse toplevel false false true ''
 
 cd .git || exit 1
-test_rev_parse .git/ true true false ''
+test_rev_parse .git/ false true false ''
 cd objects || exit 1
-test_rev_parse .git/objects/ true true false ''
+test_rev_parse .git/objects/ false true false ''
 cd ../.. || exit 1
 
 mkdir -p sub/dir || exit 1
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
index ac84335..5141fab 100755
--- a/t/t2000-checkout-cache-clash.sh
+++ b/t/t2000-checkout-cache-clash.sh
@@ -36,9 +36,9 @@
 date >path0/file0
 date >path1
 
-test_expect_failure \
+test_expect_success \
     'git checkout-index without -f should fail on conflicting work tree.' \
-    'git checkout-index -a'
+    '! git checkout-index -a'
 
 test_expect_success \
     'git checkout-index with -f should succeed.' \
diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh
index f7a0055..0f441bc 100755
--- a/t/t2002-checkout-cache-u.sh
+++ b/t/t2002-checkout-cache-u.sh
@@ -16,12 +16,12 @@
 git update-index --add path0 &&
 t=$(git write-tree)'
 
-test_expect_failure \
+test_expect_success \
 'without -u, git checkout-index smudges stat information.' '
 rm -f path0 &&
 git read-tree $t &&
 git checkout-index -f -a &&
-git diff-files | diff - /dev/null'
+! git diff-files | diff - /dev/null'
 
 test_expect_success \
 'with -u, git checkout-index picks up stat information from new files.' '
diff --git a/t/t2009-checkout-statinfo.sh b/t/t2009-checkout-statinfo.sh
new file mode 100755
index 0000000..f3c2152
--- /dev/null
+++ b/t/t2009-checkout-statinfo.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='checkout should leave clean stat info'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	echo hello >world &&
+	git update-index --add world &&
+	git commit -m initial &&
+	git branch side &&
+	echo goodbye >world &&
+	git update-index --add world &&
+	git commit -m second
+
+'
+
+test_expect_success 'branch switching' '
+
+	git reset --hard &&
+	test "$(git diff-files --raw)" = "" &&
+
+	git checkout master &&
+	test "$(git diff-files --raw)" = "" &&
+
+	git checkout side &&
+	test "$(git diff-files --raw)" = "" &&
+
+	git checkout master &&
+	test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'path checkout' '
+
+	git reset --hard &&
+	test "$(git diff-files --raw)" = "" &&
+
+	git checkout master world &&
+	test "$(git diff-files --raw)" = "" &&
+
+	git checkout side world &&
+	test "$(git diff-files --raw)" = "" &&
+
+	git checkout master world &&
+	test "$(git diff-files --raw)" = ""
+
+'
+
+test_done
+
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
index 04a1ed1..9beaecd 100755
--- a/t/t2100-update-cache-badpath.sh
+++ b/t/t2100-update-cache-badpath.sh
@@ -44,8 +44,8 @@
 
 for p in path0/file0 path1/file1 path2 path3
 do
-	test_expect_failure \
+	test_expect_success \
 	    "git update-index to add conflicting path $p should fail." \
-	    "git update-index --add -- $p"
+	    "! git update-index --add -- $p"
 done
 test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 24f892f..b664341 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -62,7 +62,7 @@
 	sed -e "s/ 0	/	/" >expect &&
 	git ls-tree -r $(git write-tree) |
 	sed -e "s/ blob / /" >current &&
-	diff -u expect current
+	test_cmp expect current
 
 '
 
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
new file mode 100755
index 0000000..e15e3eb
--- /dev/null
+++ b/t/t2201-add-update-typechange.sh
@@ -0,0 +1,140 @@
+#!/bin/sh
+
+test_description='more git add -u'
+
+. ./test-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+
+test_expect_success setup '
+	>xyzzy &&
+	_empty=$(git hash-object --stdin <xyzzy) &&
+	>yomin &&
+	>caskly &&
+	ln -s frotz nitfol &&
+	mkdir rezrov &&
+	>rezrov/bozbar &&
+	git add caskly xyzzy yomin nitfol rezrov/bozbar &&
+
+	test_tick &&
+	git commit -m initial
+
+'
+
+test_expect_success modify '
+	rm -f xyzzy yomin nitfol caskly &&
+	# caskly disappears (not a submodule)
+	mkdir caskly &&
+	# nitfol changes from symlink to regular
+	>nitfol &&
+	# rezrov/bozbar disappears
+	rm -fr rezrov &&
+	ln -s xyzzy rezrov &&
+	# xyzzy disappears (not a submodule)
+	mkdir xyzzy &&
+	echo gnusto >xyzzy/bozbar &&
+	# yomin gets replaced with a submodule
+	mkdir yomin &&
+	>yomin/yomin &&
+	(
+		cd yomin &&
+		git init &&
+		git add yomin &&
+		git commit -m "sub initial"
+	) &&
+	yomin=$(GIT_DIR=yomin/.git git rev-parse HEAD) &&
+	# yonk is added and then turned into a submodule
+	# this should appear as T in diff-files and as A in diff-index
+	>yonk &&
+	git add yonk &&
+	rm -f yonk &&
+	mkdir yonk &&
+	>yonk/yonk &&
+	(
+		cd yonk &&
+		git init &&
+		git add yonk &&
+		git commit -m "sub initial"
+	) &&
+	yonk=$(GIT_DIR=yonk/.git git rev-parse HEAD) &&
+	# zifmia is added and then removed
+	# this should appear in diff-files but not in diff-index.
+	>zifmia &&
+	git add zifmia &&
+	rm -f zifmia &&
+	mkdir zifmia &&
+	{
+		git ls-tree -r HEAD |
+		sed -e "s/^/:/" -e "
+			/	caskly/{
+				s/	caskly/ $_z40 D&/
+				s/blob/000000/
+			}
+			/	nitfol/{
+				s/	nitfol/ $_z40 T&/
+				s/blob/100644/
+			}
+			/	rezrov.bozbar/{
+				s/	rezrov.bozbar/ $_z40 D&/
+				s/blob/000000/
+			}
+			/	xyzzy/{
+				s/	xyzzy/ $_z40 D&/
+				s/blob/000000/
+			}
+			/	yomin/{
+			    s/	yomin/ $_z40 T&/
+				s/blob/160000/
+			}
+		"
+	} >expect &&
+	{
+		cat expect
+		echo ":100644 160000 $_empty $_z40 T	yonk"
+		echo ":100644 000000 $_empty $_z40 D	zifmia"
+	} >expect-files &&
+	{
+		cat expect
+		echo ":000000 160000 $_z40 $_z40 A	yonk"
+	} >expect-index &&
+	{
+		echo "100644 $_empty 0	nitfol"
+		echo "160000 $yomin 0	yomin"
+		echo "160000 $yonk 0	yonk"
+	} >expect-final
+'
+
+test_expect_success diff-files '
+	git diff-files --raw >actual &&
+	diff -u expect-files actual
+'
+
+test_expect_success diff-index '
+	git diff-index --raw HEAD -- >actual &&
+	diff -u expect-index actual
+'
+
+test_expect_success 'add -u' '
+	rm -f ".git/saved-index" &&
+	cp -p ".git/index" ".git/saved-index" &&
+	git add -u &&
+	git ls-files -s >actual &&
+	diff -u expect-final actual
+'
+
+test_expect_success 'commit -a' '
+	if test -f ".git/saved-index"
+	then
+		rm -f ".git/index" &&
+		mv ".git/saved-index" ".git/index"
+	fi &&
+	git commit -m "second" -a &&
+	git ls-files -s >actual &&
+	diff -u expect-final actual &&
+	rm -f .git/index &&
+	git read-tree HEAD &&
+	git ls-files -s >actual &&
+	diff -u expect-final actual
+'
+
+test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index e25b255..55f057c 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -97,6 +97,47 @@
 EOF
 
 test_expect_success 'git-status honours core.excludesfile' \
-	'diff -u expect output'
+	'test_cmp expect output'
+
+test_expect_success 'trailing slash in exclude allows directory match(1)' '
+
+	git ls-files --others --exclude=one/ >output &&
+	if grep "^one/" output
+	then
+		echo Ooops
+		false
+	else
+		: happy
+	fi
+
+'
+
+test_expect_success 'trailing slash in exclude allows directory match (2)' '
+
+	git ls-files --others --exclude=one/two/ >output &&
+	if grep "^one/two/" output
+	then
+		echo Ooops
+		false
+	else
+		: happy
+	fi
+
+'
+
+test_expect_success 'trailing slash in exclude forces directory match (1)' '
+
+	>two
+	git ls-files --others --exclude=two/ >output &&
+	grep "^two" output
+
+'
+
+test_expect_success 'trailing slash in exclude forces directory match (2)' '
+
+	git ls-files --others --exclude=one/a.1/ >output &&
+	grep "^one/a.1" output
+
+'
 
 test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
index c83f820..f4da869 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -15,9 +15,9 @@
 git update-index --add foo bar
 git-commit -m "add foo bar"
 
-test_expect_failure \
+test_expect_success \
     'git ls-files --error-unmatch should fail with unmatched path.' \
-    'git ls-files --error-unmatch foo bar-does-not-match'
+    '! git ls-files --error-unmatch foo bar-does-not-match'
 
 test_expect_success \
     'git ls-files --error-unmatch should succeed eith matched paths.' \
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 34f26a8..2b21b10 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -26,7 +26,7 @@
 		cd cloned &&
 		(git rev-parse HEAD; git ls-files -s) >../actual
 	) &&
-	diff -u expected actual
+	test_cmp expected actual
 '
 
 test_expect_success advance '
@@ -46,7 +46,7 @@
 		git pull &&
 		(git rev-parse HEAD; git ls-files -s) >../actual
 	) &&
-	diff -u expected actual
+	test_cmp expected actual
 '
 
 test_done
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index 68eb266..3ce501b 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -66,6 +66,6 @@
 cd ..
 test_expect_success \
     'git -ls-files --with-tree should add entries from named tree.' \
-    'diff -u expected output'
+    'test_cmp expected output'
 
 test_done
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 39fe267..70f9ce9 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -120,7 +120,7 @@
 # having 1.txt and path3
 test_expect_success \
     'ls-tree filter odd names' \
-    'git ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
+    'git ls-tree $tree 1.txt ./1.txt .//1.txt path3/1.txt path3/./1.txt path3 path3// >current &&
      cat >expected <<\EOF &&
 100644 blob X	1.txt
 100644 blob X	path3/1.txt
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ef1eeb7..cb5f7a4 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -15,12 +15,16 @@
     'echo Hello > A &&
      git update-index --add A &&
      git-commit -m "Initial commit." &&
+     echo World >> A &&
+     git update-index --add A &&
+     git-commit -m "Second commit." &&
      HEAD=$(git rev-parse --verify HEAD)'
 
-test_expect_failure \
-    'git branch --help should not have created a bogus branch' \
-    'git branch --help </dev/null >/dev/null 2>/dev/null || :
-     test -f .git/refs/heads/--help'
+test_expect_success \
+    'git branch --help should not have created a bogus branch' '
+     git branch --help </dev/null >/dev/null 2>/dev/null;
+     ! test -f .git/refs/heads/--help
+'
 
 test_expect_success \
     'git branch abc should create a branch' \
@@ -71,17 +75,17 @@
         git branch -m n/n n
         test -f .git/logs/refs/heads/n'
 
-test_expect_failure \
-    'git branch -m o/o o should fail when o/p exists' \
-       'git branch o/o &&
+test_expect_success 'git branch -m o/o o should fail when o/p exists' '
+	git branch o/o &&
         git branch o/p &&
-        git branch -m o/o o'
+	! git branch -m o/o o
+'
 
-test_expect_failure \
-    'git branch -m q r/q should fail when r exists' \
-       'git branch q &&
-         git branch r &&
-         git branch -m q r/q'
+test_expect_success 'git branch -m q r/q should fail when r exists' '
+	git branch q &&
+	git branch r &&
+	! git branch -m q r/q
+'
 
 mv .git/config .git/config-saved
 
@@ -108,12 +112,13 @@
 	"test $(git config branch.s.dummy) = Hello &&
 	 ! git config branch.s/s/dummy"
 
-test_expect_failure \
-    'git branch -m u v should fail when the reflog for u is a symlink' \
-    'git branch -l u &&
+test_expect_success \
+    'git branch -m u v should fail when the reflog for u is a symlink' '
+     git branch -l u &&
      mv .git/logs/refs/heads/u real-u &&
      ln -s real-u .git/logs/refs/heads/u &&
-     git branch -m u v'
+     ! git branch -m u v
+'
 
 test_expect_success 'test tracking setup via --track' \
     'git config remote.local.url . &&
@@ -148,16 +153,6 @@
      test $(git config branch.my3.remote) = local &&
      test $(git config branch.my3.merge) = refs/heads/master'
 
-test_expect_success 'avoid ambiguous track' '
-	git config branch.autosetupmerge true &&
-	git config remote.ambi1.url = lalala &&
-	git config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&
-	git config remote.ambi2.url = lilili &&
-	git config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&
-	git branch all1 master &&
-	test -z "$(git config branch.all1.merge)"
-'
-
 test_expect_success 'test overriding tracking setup via --no-track' \
     'git config branch.autosetupmerge true &&
      git config remote.local.url . &&
@@ -169,7 +164,9 @@
      ! test "$(git config branch.my2.merge)" = refs/heads/master'
 
 test_expect_success 'no tracking without .fetch entries' \
-    'git branch --track my6 s &&
+    'git config branch.autosetupmerge true &&
+     git branch my6 s &&
+     git config branch.automsetupmerge false &&
      test -z "$(git config branch.my6.remote)" &&
      test -z "$(git config branch.my6.merge)"'
 
@@ -190,6 +187,21 @@
     'git branch my7 s &&
      test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
 
+test_expect_success 'test --track without .fetch entries' \
+    'git branch --track my8 &&
+     test "$(git config branch.my8.remote)" &&
+     test "$(git config branch.my8.merge)"'
+
+test_expect_success \
+    'branch from non-branch HEAD w/autosetupmerge=always' \
+    'git config branch.autosetupmerge always &&
+     git branch my9 HEAD^ &&
+     git config branch.autosetupmerge false'
+
+test_expect_success \
+    'branch from non-branch HEAD w/--track causes failure' \
+    '!(git branch --track my10 HEAD^)'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
@@ -202,4 +214,14 @@
 	 test -f .git/logs/refs/heads/g/h/i &&
 	 diff expect .git/logs/refs/heads/g/h/i'
 
+test_expect_success 'avoid ambiguous track' '
+	git config branch.autosetupmerge true &&
+	git config remote.ambi1.url lalala &&
+	git config remote.ambi1.fetch refs/heads/lalala:refs/heads/master &&
+	git config remote.ambi2.url lilili &&
+	git config remote.ambi2.fetch refs/heads/lilili:refs/heads/master &&
+	git branch all1 master &&
+	test -z "$(git config branch.all1.merge)"
+'
+
 test_done
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index 9ef593f..b4cf628 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -31,7 +31,7 @@
 	{
 		echo "  master" && echo "* side"
 	} >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -41,7 +41,7 @@
 	{
 		echo "  master" && echo "* side"
 	} >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -51,7 +51,7 @@
 	{
 		echo "* side"
 	} >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 4ddc634..b64ccfb 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -39,12 +39,12 @@
      git show-ref b >result &&
      diff expect result'
 
-test_expect_failure \
-    'git branch c/d should barf if branch c exists' \
-    'git branch c &&
+test_expect_success 'git branch c/d should barf if branch c exists' '
+     git branch c &&
      git pack-refs --all &&
-     rm .git/refs/heads/c &&
-     git branch c/d'
+     rm -f .git/refs/heads/c &&
+     ! git branch c/d
+'
 
 test_expect_success \
     'see if a branch still exists after git pack-refs --prune' \
@@ -54,11 +54,11 @@
      git show-ref e >result &&
      diff expect result'
 
-test_expect_failure \
-    'see if git pack-refs --prune remove ref files' \
-    'git branch f &&
+test_expect_success 'see if git pack-refs --prune remove ref files' '
+     git branch f &&
      git pack-refs --all --prune &&
-     ls .git/refs/heads/f'
+     ! test -f .git/refs/heads/f
+'
 
 test_expect_success \
     'git branch g should work when git branch g/h has been deleted' \
@@ -69,11 +69,11 @@
      git pack-refs --all &&
      git branch -d g'
 
-test_expect_failure \
-    'git branch i/j/k should barf if branch i exists' \
-    'git branch i &&
+test_expect_success 'git branch i/j/k should barf if branch i exists' '
+     git branch i &&
      git pack-refs --all --prune &&
-     git branch i/j/k'
+     ! git branch i/j/k
+'
 
 test_expect_success \
     'test git branch k after branch k/l/m and k/lm have been deleted' \
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index 98c133d..24a00a9 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -54,7 +54,7 @@
 no-funny
 tabs	," (dq) and spaces' >expected
 test_expect_success 'git ls-files -z with-funny' \
-	'git ls-files -z | tr \\000 \\012 >current &&
+	'git ls-files -z | perl -pe y/\\000/\\012/ >current &&
 	git diff expected current'
 
 t1=`git write-tree`
@@ -83,11 +83,11 @@
 echo 'A
 tabs	," (dq) and spaces' >expected
 test_expect_success 'git diff-index -z with-funny' \
-	'git diff-index -z --name-status $t0 | tr \\000 \\012 >current &&
+	'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current &&
 	git diff expected current'
 
 test_expect_success 'git diff-tree -z with-funny' \
-	'git diff-tree -z --name-status $t0 $t1 | tr \\000 \\012 >current &&
+	'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current &&
 	git diff expected current'
 
 cat > expected <<\EOF
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 95e33b5..496f4ec 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -42,9 +42,9 @@
 test_expect_success 'rebase against master' '
      git rebase master'
 
-test_expect_failure \
+test_expect_success \
     'the rebase operation should not have destroyed author information' \
-    'git log | grep "Author:" | grep "<>"'
+    '! git log | grep "Author:" | grep "<>"'
 
 test_expect_success 'rebase after merge master' '
      git reset --hard topic &&
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 657f681..0a26099 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -31,8 +31,8 @@
 	git branch skip-merge skip-reference
 	'
 
-test_expect_failure 'rebase with git am -3 (default)' '
-	git rebase master
+test_expect_success 'rebase with git am -3 (default)' '
+	! git rebase master
 '
 
 test_expect_success 'rebase --skip with am -3' '
@@ -53,7 +53,7 @@
 
 test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
 
-test_expect_failure 'rebase with --merge' 'git rebase --merge master'
+test_expect_success 'rebase with --merge' '! git rebase --merge master'
 
 test_expect_success 'rebase --skip with --merge' '
 	git rebase --skip
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index e5ed745..9cf873f 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -61,8 +61,8 @@
 	git tag I
 '
 
-cat > fake-editor.sh <<\EOF
-#!/bin/sh
+echo "#!$SHELL_PATH" >fake-editor.sh
+cat >> fake-editor.sh <<\EOF
 case "$1" in
 */COMMIT_EDITMSG)
 	test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
@@ -122,8 +122,8 @@
 
 test_expect_success 'exchange two commits' '
 	FAKE_LINES="2 1" git rebase -i HEAD~2 &&
-	test H = $(git cat-file commit HEAD^ | tail -n 1) &&
-	test G = $(git cat-file commit HEAD | tail -n 1)
+	test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+	test G = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
 cat > expect << EOF
@@ -146,11 +146,10 @@
 test_expect_success 'stop on conflicting pick' '
 	git tag new-branch1 &&
 	! git rebase -i master &&
-	diff -u expect .git/.dotest-merge/patch &&
-	diff -u expect2 file1 &&
+	test_cmp expect .git/.dotest-merge/patch &&
+	test_cmp expect2 file1 &&
 	test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
-	test 0 = $(grep -ve "^#" -e "^$" < .git/.dotest-merge/git-rebase-todo |
-		wc -l)
+	test 0 = $(grep -c "^[^#]" < .git/.dotest-merge/git-rebase-todo)
 '
 
 test_expect_success 'abort' '
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index e4e2e64..e5ad67c 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -41,8 +41,8 @@
 	git rebase master side &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
 
-	diff -u F0 F1 &&
-	diff -u F F0
+	test_cmp F0 F1 &&
+	test_cmp F F0
 '
 
 test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 332b2b2..5391080 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -37,7 +37,7 @@
 	git rebase -m master >report &&
 	sed -n -e "/^Already applied: /p" \
 		-e "/^Committed: /p" report >actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
new file mode 100755
index 0000000..e12cd57
--- /dev/null
+++ b/t/t3408-rebase-multi-line.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='rebasing a commit with multi-line first paragraph.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	>file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+
+	echo hello >file &&
+	test_tick &&
+	git commit -a -m "A sample commit log message that has a long
+summary that spills over multiple lines.
+
+But otherwise with a sane description."
+
+	git branch side &&
+
+	git reset --hard HEAD^ &&
+	>elif &&
+	git add elif &&
+	test_tick &&
+	git commit -m second
+
+'
+
+test_expect_success rebase '
+
+	git checkout side &&
+	git rebase master &&
+	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	git cat-file commit side@{1} | sed -e "1,/^$/d" >expect &&
+	test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index b1ee622..f542f0a 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -59,15 +59,16 @@
      echo "other content" > foo
      git rm --cached foo'
 
-test_expect_failure \
-    'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' \
-    'echo content > foo
+test_expect_success \
+    'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
+     echo content > foo
      git add foo
      git commit -m foo
      echo "other content" > foo
      git add foo
      echo "yet another content" > foo
-     git rm --cached foo'
+     ! git rm --cached foo
+'
 
 test_expect_success \
     'Test that git rm --cached -f foo works in case where --cached only did not' \
@@ -106,9 +107,9 @@
 
 if test "$test_failed_remove" = y; then
 chmod a-w .
-test_expect_failure \
+test_expect_success \
     'Test that "git rm -f" fails if its rm fails' \
-    'git rm -f baz'
+    '! git rm -f baz'
 chmod 775 .
 else
     test_expect_success 'skipping removal failure (perhaps running as root?)' :
@@ -212,8 +213,8 @@
 	! test -d frotz
 '
 
-test_expect_failure 'Remove nonexistent file returns nonzero exit status' '
-	git rm nonexistent
+test_expect_success 'Remove nonexistent file returns nonzero exit status' '
+	! git rm nonexistent
 '
 
 test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
new file mode 100755
index 0000000..77c90f6
--- /dev/null
+++ b/t/t3701-add-interactive.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='add -i basic tests'
+. ./test-lib.sh
+
+test_expect_success 'setup (initial)' '
+	echo content >file &&
+	git add file &&
+	echo more >>file &&
+	echo lines >>file
+'
+test_expect_success 'status works (initial)' '
+	git add -i </dev/null >output &&
+	grep "+1/-0 *+2/-0 file" output
+'
+cat >expected <<EOF
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/file
+@@ -0,0 +1 @@
++content
+EOF
+test_expect_success 'diff works (initial)' '
+	(echo d; echo 1) | git add -i >output &&
+	sed -ne "/new file/,/content/p" <output >diff &&
+	test_cmp expected diff
+'
+test_expect_success 'revert works (initial)' '
+	git add file &&
+	(echo r; echo 1) | git add -i &&
+	git ls-files >output &&
+	! grep . output
+'
+
+test_expect_success 'setup (commit)' '
+	echo baseline >file &&
+	git add file &&
+	git commit -m commit &&
+	echo content >>file &&
+	git add file &&
+	echo more >>file &&
+	echo lines >>file
+'
+test_expect_success 'status works (commit)' '
+	git add -i </dev/null >output &&
+	grep "+1/-0 *+2/-0 file" output
+'
+cat >expected <<EOF
+index 180b47c..b6f2c08 100644
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ baseline
++content
+EOF
+test_expect_success 'diff works (commit)' '
+	(echo d; echo 1) | git add -i >output &&
+	sed -ne "/^index/,/content/p" <output >diff &&
+	test_cmp expected diff
+'
+test_expect_success 'revert works (commit)' '
+	git add file &&
+	(echo r; echo 1) | git add -i &&
+	git add -i </dev/null >output &&
+	grep "unchanged *+3/-0 file" output
+'
+
+test_done
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index bdc6e13..df1fd6f 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -44,6 +44,8 @@
 xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895
 type tag
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"object" line label check' '^error: char0: .*"object "$'
@@ -55,6 +57,8 @@
 object zz9e9b33986b1c2670fff52c5067603117b3e895
 type tag
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$'
@@ -66,6 +70,8 @@
 object 779e9b33986b1c2670fff52c5067603117b3e895
 xxxx tag
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"type" line label check' '^error: char47: .*"\\ntype "$'
@@ -85,6 +91,8 @@
 object 779e9b33986b1c2670fff52c5067603117b3e895
 type tag
 xxx mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure '"tag" line label check #1' \
@@ -121,6 +129,8 @@
 object 779e9b33986b1c2670fff52c5067603117b3e895
 type tagggg
 tag mytag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure 'verify object (SHA1/type) check' \
@@ -133,6 +143,8 @@
 object $head
 type commit
 tag my	tag
+tagger . <> 0 +0000
+
 EOF
 
 check_verify_failure 'verify tag-name check' \
@@ -145,10 +157,12 @@
 object $head
 type commit
 tag mytag
+
+This is filler
 EOF
 
 check_verify_failure '"tagger" line label check #1' \
-	'^error: char70: could not find "tagger"$'
+	'^error: char70: could not find "tagger "$'
 
 ############################################################
 # 12. tagger line label check #2
@@ -158,19 +172,180 @@
 type commit
 tag mytag
 tagger
+
+This is filler
 EOF
 
 check_verify_failure '"tagger" line label check #2' \
-	'^error: char70: could not find "tagger"$'
+	'^error: char70: could not find "tagger "$'
 
 ############################################################
-# 13. create valid tag
+# 13. disallow missing tag author name
 
 cat >tag.sig <<EOF
 object $head
 type commit
 tag mytag
-tagger another@example.com
+tagger  <> 0 +0000
+
+This is filler
+EOF
+
+check_verify_failure 'disallow missing tag author name' \
+	'^error: char77: missing tagger name$'
+
+############################################################
+# 14. disallow missing tag author name
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <
+ > 0 +0000
+
+EOF
+
+check_verify_failure 'disallow malformed tagger' \
+	'^error: char77: malformed tagger field$'
+
+############################################################
+# 15. allow empty tag email
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success \
+    'allow empty tag email' \
+    'git-mktag <tag.sig >.git/refs/tags/mytag 2>message'
+
+############################################################
+# 16. disallow spaces in tag email
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tag ger@example.com> 0 +0000
+
+EOF
+
+check_verify_failure 'disallow spaces in tag email' \
+	'^error: char77: malformed tagger field$'
+
+############################################################
+# 17. disallow missing tag timestamp
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com>  
+
+EOF
+
+check_verify_failure 'disallow missing tag timestamp' \
+	'^error: char107: missing tag timestamp$'
+
+############################################################
+# 18. detect invalid tag timestamp1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008
+
+EOF
+
+check_verify_failure 'detect invalid tag timestamp1' \
+	'^error: char107: missing tag timestamp$'
+
+############################################################
+# 19. detect invalid tag timestamp2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500
+
+EOF
+
+check_verify_failure 'detect invalid tag timestamp2' \
+	'^error: char111: malformed tag timestamp$'
+
+############################################################
+# 20. detect invalid tag timezone1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 GMT
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone1' \
+	'^error: char118: malformed tag timezone$'
+
+############################################################
+# 21. detect invalid tag timezone2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 +  30
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone2' \
+	'^error: char118: malformed tag timezone$'
+
+############################################################
+# 22. detect invalid tag timezone3
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -1430
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone3' \
+	'^error: char118: malformed tag timezone$'
+
+############################################################
+# 23. detect invalid header entry
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+this line should not be here
+
+EOF
+
+check_verify_failure 'detect invalid header entry' \
+	'^error: char124: trailing garbage in tag header$'
+
+############################################################
+# 24. create valid tag
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+
 EOF
 
 test_expect_success \
@@ -178,7 +353,7 @@
     'git-mktag <tag.sig >.git/refs/tags/mytag 2>message'
 
 ############################################################
-# 14. check mytag
+# 25. check mytag
 
 test_expect_success \
     'check mytag' \
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
index 73da45f..fe4fb51 100755
--- a/t/t3902-quoted.sh
+++ b/t/t3902-quoted.sh
@@ -78,28 +78,28 @@
 
 test_expect_success 'check fully quoted output from ls-files' '
 
-	git ls-files >current && diff -u expect.quoted current
+	git ls-files >current && test_cmp expect.quoted current
 
 '
 
 test_expect_success 'check fully quoted output from diff-files' '
 
 	git diff --name-only >current &&
-	diff -u expect.quoted current
+	test_cmp expect.quoted current
 
 '
 
 test_expect_success 'check fully quoted output from diff-index' '
 
 	git diff --name-only HEAD >current &&
-	diff -u expect.quoted current
+	test_cmp expect.quoted current
 
 '
 
 test_expect_success 'check fully quoted output from diff-tree' '
 
 	git diff --name-only HEAD^ HEAD >current &&
-	diff -u expect.quoted current
+	test_cmp expect.quoted current
 
 '
 
@@ -111,28 +111,28 @@
 
 test_expect_success 'check fully quoted output from ls-files' '
 
-	git ls-files >current && diff -u expect.raw current
+	git ls-files >current && test_cmp expect.raw current
 
 '
 
 test_expect_success 'check fully quoted output from diff-files' '
 
 	git diff --name-only >current &&
-	diff -u expect.raw current
+	test_cmp expect.raw current
 
 '
 
 test_expect_success 'check fully quoted output from diff-index' '
 
 	git diff --name-only HEAD >current &&
-	diff -u expect.raw current
+	test_cmp expect.raw current
 
 '
 
 test_expect_success 'check fully quoted output from diff-tree' '
 
 	git diff --name-only HEAD^ HEAD >current &&
-	diff -u expect.raw current
+	test_cmp expect.raw current
 
 '
 
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 9a9a250..2d3ee3b 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -34,14 +34,14 @@
 test_expect_success 'parents of stash' '
 	test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
 	git diff stash^2..stash > output &&
-	diff -u output expect
+	test_cmp output expect
 '
 
 test_expect_success 'apply needs clean working directory' '
 	echo 4 > other-file &&
 	git add other-file &&
-	echo 5 > other-file
-	! git stash apply
+	echo 5 > other-file &&
+ 	test_must_fail git stash apply
 '
 
 test_expect_success 'apply stashed changes' '
@@ -70,7 +70,51 @@
 	git reset --hard HEAD &&
 	mkdir subdir &&
 	cd subdir &&
-	git stash apply
+	git stash apply &&
+	cd ..
+'
+
+test_expect_success 'drop top stash' '
+	git reset --hard &&
+	git stash list > stashlist1 &&
+	echo 7 > file &&
+	git stash &&
+	git stash drop &&
+	git stash list > stashlist2 &&
+	diff stashlist1 stashlist2 &&
+	git stash apply &&
+	test 3 = $(cat file) &&
+	test 1 = $(git show :file) &&
+	test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'drop middle stash' '
+	git reset --hard &&
+	echo 8 > file &&
+	git stash &&
+	echo 9 > file &&
+	git stash &&
+	git stash drop stash@{1} &&
+	test 2 = $(git stash list | wc -l) &&
+	git stash apply &&
+	test 9 = $(cat file) &&
+	test 1 = $(git show :file) &&
+	test 1 = $(git show HEAD:file) &&
+	git reset --hard &&
+	git stash drop &&
+	git stash apply &&
+	test 3 = $(cat file) &&
+	test 1 = $(git show :file) &&
+	test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'stash pop' '
+	git reset --hard &&
+	git stash pop &&
+	test 3 = $(cat file) &&
+	test 1 = $(git show :file) &&
+	test 1 = $(git show HEAD:file) &&
+	test 0 = $(git stash list | wc -l)
 '
 
 test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 9eec754..6b4d1c5 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -245,6 +245,7 @@
 format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
 config format.subjectprefix DIFFERENT_PREFIX
 format-patch --inline --stdout initial..master^^
+format-patch --stdout --cover-letter -n initial..master^
 
 diff --abbrev initial..side
 diff -r initial..side
diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
new file mode 100644
index 0000000..8dab4bf
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
@@ -0,0 +1,100 @@
+$ git format-patch --stdout --cover-letter -n initial..master^
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: C O Mitter <committer@example.com>
+Date: Mon, 26 Jun 2006 00:05:00 +0000
+Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
+
+*** BLURB HERE ***
+
+A U Thor (2):
+  Second
+  Third
+
+ dir/sub |    4 ++++
+ file0   |    3 +++
+ file1   |    3 +++
+ file2   |    3 ---
+ 4 files changed, 10 insertions(+), 3 deletions(-)
+ create mode 100644 file1
+ delete mode 100644 file2
+
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [DIFFERENT_PREFIX 1/2] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [DIFFERENT_PREFIX 2/2] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 0a6fe53..b2b7a8d 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -88,4 +88,146 @@
 
 '
 
+test_expect_success 'extra headers' '
+
+	git config format.headers "To: R. E. Cipient <rcipient@example.com>
+" &&
+	git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
+" &&
+	git format-patch --stdout master..side > patch2 &&
+	sed -e "/^$/q" patch2 > hdrs2 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 &&
+	grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2
+	
+'
+
+test_expect_success 'extra headers without newlines' '
+
+	git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
+	git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
+	git format-patch --stdout master..side >patch3 &&
+	sed -e "/^$/q" patch3 > hdrs3 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 &&
+	grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3
+	
+'
+
+test_expect_success 'extra headers with multiple To:s' '
+
+	git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
+	git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
+	git format-patch --stdout master..side > patch4 &&
+	sed -e "/^$/q" patch4 > hdrs4 &&
+	grep "^To: R. E. Cipient <rcipient@example.com>,$" hdrs4 &&
+	grep "^ *S. E. Cipient <scipient@example.com>$" hdrs4
+'
+
+test_expect_success 'additional command line cc' '
+
+	git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+	git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch5 &&
+	grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch5 &&
+	grep "^ *S. E. Cipient <scipient@example.com>$" patch5
+'
+
+test_expect_success 'multiple files' '
+
+	rm -rf patches/ &&
+	git checkout side &&
+	git format-patch -o patches/ master &&
+	ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
+'
+
+test_expect_success 'thread' '
+
+	rm -rf patches/ &&
+	git checkout side &&
+	git format-patch --thread -o patches/ master &&
+	FIRST_MID=$(grep "Message-Id:" patches/0001-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
+	for i in patches/0002-* patches/0003-*
+	do
+	  grep "References: $FIRST_MID" $i &&
+	  grep "In-Reply-To: $FIRST_MID" $i || break
+	done
+'
+
+test_expect_success 'thread in-reply-to' '
+
+	rm -rf patches/ &&
+	git checkout side &&
+	git format-patch --in-reply-to="<test.message>" --thread -o patches/ master &&
+	FIRST_MID="<test.message>" &&
+	for i in patches/*
+	do
+	  grep "References: $FIRST_MID" $i &&
+	  grep "In-Reply-To: $FIRST_MID" $i || break
+	done
+'
+
+test_expect_success 'thread cover-letter' '
+
+	rm -rf patches/ &&
+	git checkout side &&
+	git format-patch --cover-letter --thread -o patches/ master &&
+	FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
+	for i in patches/0001-* patches/0002-* patches/0003-* 
+	do
+	  grep "References: $FIRST_MID" $i &&
+	  grep "In-Reply-To: $FIRST_MID" $i || break
+	done
+'
+
+test_expect_success 'thread cover-letter in-reply-to' '
+
+	rm -rf patches/ &&
+	git checkout side &&
+	git format-patch --cover-letter --in-reply-to="<test.message>" --thread -o patches/ master &&
+	FIRST_MID="<test.message>" &&
+	for i in patches/*
+	do
+	  grep "References: $FIRST_MID" $i &&
+	  grep "In-Reply-To: $FIRST_MID" $i || break
+	done
+'
+
+test_expect_success 'excessive subject' '
+
+	rm -rf patches/ &&
+	git checkout side &&
+	for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
+	git update-index file &&
+	git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." &&
+	git format-patch -o patches/ master..side &&
+	ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
+'
+
+test_expect_success 'cover-letter inherits diff options' '
+
+	git mv file foo &&
+	git commit -m foo &&
+	git format-patch --cover-letter -1 &&
+	! grep "file => foo .* 0 *$" 0000-cover-letter.patch &&
+	git format-patch --cover-letter -1 -M &&
+	grep "file => foo .* 0 *$" 0000-cover-letter.patch
+
+'
+
+cat > expect << EOF
+  This is an excessively long subject line for a message due to the
+    habit some projects have of not having a short, one-line subject at
+    the start of the commit message, but rather sticking a whole
+    paragraph right at the start as the only thing in the commit
+    message. It had better not become the filename for the patch.
+  foo
+
+EOF
+
+test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
+
+	git format-patch --cover-letter -2 &&
+	sed -e "1,/A U Thor/d" -e "/^$/q" < 0000-cover-letter.patch > output &&
+	git diff expect output
+
+'
+
 test_done
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index 67e080b..0d9cbb6 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -12,6 +12,7 @@
 	echo "         Eight SP indent" >>F &&
 	echo " 	HT and SP indent" >>F &&
 	echo "With trailing SP " >>F &&
+	echo "Carriage ReturnQ" | tr Q "\015" >>F &&
 	echo "No problem" >>F
 
 '
@@ -27,6 +28,7 @@
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
 	grep With error >/dev/null &&
+	grep Return error >/dev/null &&
 	grep No normal >/dev/null
 
 '
@@ -41,6 +43,7 @@
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
 	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
 	grep No normal >/dev/null
 
 '
@@ -56,6 +59,7 @@
 	grep Eight normal >/dev/null &&
 	grep HT error >/dev/null &&
 	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
 	grep No normal >/dev/null
 
 '
@@ -71,6 +75,7 @@
 	grep Eight normal >/dev/null &&
 	grep HT normal >/dev/null &&
 	grep With error >/dev/null &&
+	grep Return error >/dev/null &&
 	grep No normal >/dev/null
 
 '
@@ -86,6 +91,7 @@
 	grep Eight normal >/dev/null &&
 	grep HT normal >/dev/null &&
 	grep With error >/dev/null &&
+	grep Return error >/dev/null &&
 	grep No normal >/dev/null
 
 '
@@ -101,6 +107,7 @@
 	grep Eight error >/dev/null &&
 	grep HT normal >/dev/null &&
 	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
 	grep No normal >/dev/null
 
 '
@@ -116,6 +123,39 @@
 	grep Eight error >/dev/null &&
 	grep HT normal >/dev/null &&
 	grep With normal >/dev/null &&
+	grep Return normal >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
+test_expect_success 'with cr-at-eol' '
+
+	rm -f .gitattributes
+	git config core.whitespace cr-at-eol
+	git diff --color >output
+	grep "$blue_grep" output >error
+	grep -v "$blue_grep" output >normal
+
+	grep Eight normal >/dev/null &&
+	grep HT error >/dev/null &&
+	grep With error >/dev/null &&
+	grep Return normal >/dev/null &&
+	grep No normal >/dev/null
+
+'
+
+test_expect_success 'with cr-at-eol (attribute)' '
+
+	git config --unset core.whitespace
+	echo "F whitespace=trailing,cr-at-eol" >.gitattributes
+	git diff --color >output
+	grep "$blue_grep" output >error
+	grep -v "$blue_grep" output >normal
+
+	grep Eight normal >/dev/null &&
+	grep HT error >/dev/null &&
+	grep With error >/dev/null &&
+	grep Return normal >/dev/null &&
 	grep No normal >/dev/null
 
 '
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 8882933..637b4e1 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -99,11 +99,12 @@
 	git diff | grep Binary
 '
 
-echo NULZbetweenZwords | tr Z '\000' > file
+echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
 
 test_expect_success 'force diff with "diff"' '
 	echo >.gitattributes "file diff" &&
-	git diff | grep -a second
+	git diff >actual &&
+	test_cmp ../t4020/diff.NUL actual
 '
 
 test_done
diff --git a/t/t4020/diff.NUL b/t/t4020/diff.NUL
new file mode 100644
index 0000000..db2f890
--- /dev/null
+++ b/t/t4020/diff.NUL
Binary files differ
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 6de4acb..bf996fc 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -8,7 +8,10 @@
 
 	cat ../../COPYING >test &&
 	git add test &&
-	tr 'a-zA-Z' 'n-za-mN-ZA-M' <../../COPYING >test
+	tr \
+	  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+	  "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
+	  <../../COPYING >test
 
 '
 
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 255604e..4dbfc6e 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -57,7 +57,7 @@
 		echo "R100	foo	bar"
 		echo "R100	bar	foo"
 	} | sort >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -68,7 +68,7 @@
 		echo "R100	foo	bar"
 		echo "R100	bar	foo"
 	} | sort >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -79,7 +79,7 @@
 		echo "R100	foo	bar"
 		echo "T100	foo"
 	} | sort >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
index 3c66102..c4d733f 100755
--- a/t/t4024-diff-optimize-common.sh
+++ b/t/t4024-diff-optimize-common.sh
@@ -150,7 +150,7 @@
 	do
 		git diff -U0 file-?$n
 	done | zc >actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
index 9ba06b7..7a3dbc1 100755
--- a/t/t4025-hunk-header.sh
+++ b/t/t4025-hunk-header.sh
@@ -37,7 +37,7 @@
 		echo " A $N$N$N$N$N$N$N$N$N2"
 		echo " L  $N$N$N$N$N$N$N$N$N1"
 	) >expected &&
-	diff -u actual expected
+	test_cmp actual expected
 
 '
 
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
new file mode 100755
index 0000000..1fd3fb7
--- /dev/null
+++ b/t/t4027-diff-submodule.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='difference in submodules'
+
+. ./test-lib.sh
+. ../diff-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+test_expect_success setup '
+	test_tick &&
+	test_create_repo sub &&
+	(
+		cd sub &&
+		echo hello >world &&
+		git add world &&
+		git commit -m submodule
+	) &&
+
+	test_tick &&
+	echo frotz >nitfol &&
+	git add nitfol sub &&
+	git commit -m superproject &&
+
+	(
+		cd sub &&
+		echo goodbye >world &&
+		git add world &&
+		git commit -m "submodule #2"
+	) &&
+
+	set x $(
+		cd sub &&
+		git rev-list HEAD
+	) &&
+	echo ":160000 160000 $3 $_z40 M	sub" >expect
+'
+
+test_expect_success 'git diff --raw HEAD' '
+	git diff --raw --abbrev=40 HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git diff-index --raw HEAD' '
+	git diff-index --raw HEAD >actual.index &&
+	test_cmp expect actual.index
+'
+
+test_expect_success 'git diff-files --raw' '
+	git diff-files --raw >actual.files &&
+	test_cmp expect actual.files
+'
+
+test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index 74f06ec..1b58233 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -24,10 +24,10 @@
 git-commit -m 'Initial Version' 2>/dev/null
 
 git-checkout -b binary
-tr 'x' '\000' <file1 >file3
+perl -pe 'y/x/\000/' <file1 >file3
 cat file3 >file4
 git add file2
-tr '\000' 'v' <file3 >file1
+perl -pe 'y/\000/v/' <file3 >file1
 rm -f file2
 git update-index --add --remove file1 file2 file3 file4
 git-commit -m 'Second Version'
@@ -46,21 +46,25 @@
 	'git-checkout master
 	 git apply --stat --summary C.diff'
 
-test_expect_failure 'check binary diff -- should fail.' \
-	'git-checkout master
-	 git apply --check B.diff'
+test_expect_success 'check binary diff -- should fail.' \
+	'git-checkout master &&
+	 ! git apply --check B.diff'
 
-test_expect_failure 'check binary diff (copy) -- should fail.' \
-	'git-checkout master
-	 git apply --check C.diff'
+test_expect_success 'check binary diff (copy) -- should fail.' \
+	'git-checkout master &&
+	 ! git apply --check C.diff'
 
-test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
-	'git-checkout master
-	 git apply --check --allow-binary-replacement B.diff'
+test_expect_success \
+	'check incomplete binary diff with replacement -- should fail.' '
+	git-checkout master &&
+	! git apply --check --allow-binary-replacement B.diff
+'
 
-test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
-	'git-checkout master
-	 git apply --check --allow-binary-replacement C.diff'
+test_expect_success \
+    'check incomplete binary diff with replacement (copy) -- should fail.' '
+	 git-checkout master &&
+	 ! git apply --check --allow-binary-replacement C.diff
+'
 
 test_expect_success 'check binary diff with replacement.' \
 	'git-checkout master
@@ -73,42 +77,42 @@
 # Now we start applying them.
 
 do_reset () {
-	rm -f file?
-	git-reset --hard
+	rm -f file? &&
+	git-reset --hard &&
 	git-checkout -f master
 }
 
-test_expect_failure 'apply binary diff -- should fail.' \
-	'do_reset
-	 git apply B.diff'
+test_expect_success 'apply binary diff -- should fail.' \
+	'do_reset &&
+	 ! git apply B.diff'
 
-test_expect_failure 'apply binary diff -- should fail.' \
-	'do_reset
-	 git apply --index B.diff'
+test_expect_success 'apply binary diff -- should fail.' \
+	'do_reset &&
+	 ! git apply --index B.diff'
 
-test_expect_failure 'apply binary diff (copy) -- should fail.' \
-	'do_reset
-	 git apply C.diff'
+test_expect_success 'apply binary diff (copy) -- should fail.' \
+	'do_reset &&
+	 ! git apply C.diff'
 
-test_expect_failure 'apply binary diff (copy) -- should fail.' \
-	'do_reset
-	 git apply --index C.diff'
+test_expect_success 'apply binary diff (copy) -- should fail.' \
+	'do_reset &&
+	 ! git apply --index C.diff'
 
 test_expect_success 'apply binary diff without replacement.' \
-	'do_reset
+	'do_reset &&
 	 git apply BF.diff'
 
 test_expect_success 'apply binary diff without replacement (copy).' \
-	'do_reset
+	'do_reset &&
 	 git apply CF.diff'
 
 test_expect_success 'apply binary diff.' \
-	'do_reset
+	'do_reset &&
 	 git apply --allow-binary-replacement --index BF.diff &&
 	 test -z "$(git diff --name-status binary)"'
 
 test_expect_success 'apply binary diff (copy).' \
-	'do_reset
+	'do_reset &&
 	 git apply --allow-binary-replacement --index CF.diff &&
 	 test -z "$(git diff --name-status binary)"'
 
diff --git a/t/t4105-apply-fuzz.sh b/t/t4105-apply-fuzz.sh
new file mode 100755
index 0000000..3266e39
--- /dev/null
+++ b/t/t4105-apply-fuzz.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='apply with fuzz and offset'
+
+. ./test-lib.sh
+
+dotest () {
+	name="$1" && shift &&
+	test_expect_success "$name" "
+		git checkout-index -f -q -u file &&
+		git apply $* &&
+		test_cmp expect file
+	"
+}
+
+test_expect_success setup '
+
+	for i in 1 2 3 4 5 6 7 8 9 10 11 12
+	do
+		echo $i
+	done >file &&
+	git update-index --add file &&
+	for i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12
+	do
+		echo $i
+	done >file &&
+	cat file >expect &&
+	git diff >O0.diff &&
+
+	sed -e "s/@@ -5,6 +5,11 @@/@@ -2,6 +2,11 @@/" >O1.diff O0.diff &&
+	sed -e "s/@@ -5,6 +5,11 @@/@@ -7,6 +7,11 @@/" >O2.diff O0.diff &&
+	sed -e "s/@@ -5,6 +5,11 @@/@@ -19,6 +19,11 @@/" >O3.diff O0.diff &&
+
+	sed -e "s/^ 5/ S/" >F0.diff O0.diff &&
+	sed -e "s/^ 5/ S/" >F1.diff O1.diff &&
+	sed -e "s/^ 5/ S/" >F2.diff O2.diff &&
+	sed -e "s/^ 5/ S/" >F3.diff O3.diff
+
+'
+
+dotest 'unmodified patch' O0.diff
+
+dotest 'minus offset' O1.diff
+
+dotest 'plus offset' O2.diff
+
+dotest 'big offset' O3.diff
+
+dotest 'fuzz with no offset' -C2 F0.diff
+
+dotest 'fuzz with minus offset' -C2 F1.diff
+
+dotest 'fuzz with plus offset' -C2 F2.diff
+
+dotest 'fuzz with big offset' -C2 F3.diff
+
+test_done
diff --git a/t/t4113-apply-ending.sh b/t/t4113-apply-ending.sh
index 1c6bec0..d741039 100755
--- a/t/t4113-apply-ending.sh
+++ b/t/t4113-apply-ending.sh
@@ -29,8 +29,8 @@
 
 # test
 
-test_expect_failure 'apply at the end' \
-    'git apply --index test-patch'
+test_expect_success 'apply at the end' \
+    '! git apply --index test-patch'
 
 cat >test-patch <<\EOF
 diff a/file b/file
@@ -47,7 +47,7 @@
 c'
 git update-index file
 
-test_expect_failure 'apply at the beginning' \
-	'git apply --index test-patch'
+test_expect_success 'apply at the beginning' \
+	'! git apply --index test-patch'
 
 test_done
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index b1d35ab..c3f4579 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -12,14 +12,14 @@
 test_expect_success setup '
 
 	for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
-	tr "ijk" '\''\000\001\002'\'' <file1 >file2 &&
+	perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
 
 	git add file1 file2 &&
 	git commit -m initial &&
 	git tag initial &&
 
 	for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
-	tr "mon" '\''\000\001\002'\'' <file1 >file2 &&
+	perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
 
 	git commit -a -m second &&
 	git tag second &&
diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh
new file mode 100755
index 0000000..3b471b6
--- /dev/null
+++ b/t/t4125-apply-ws-fuzz.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='applying patch that has broken whitespaces in context'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	>file &&
+	git add file &&
+
+	# file-0 is full of whitespace breakages
+	for l in a bb c d eeee f ggg h
+	do
+		echo "$l "
+	done >file-0 &&
+
+	# patch-0 creates a whitespace broken file
+	cat file-0 >file &&
+	git diff >patch-0 &&
+	git add file &&
+
+	# file-1 is still full of whitespace breakages,
+	# but has one line updated, without fixing any
+	# whitespaces.
+	# patch-1 records that change.
+	sed -e "s/d/D/" file-0 >file-1 &&
+	cat file-1 >file &&
+	git diff >patch-1 &&
+
+	# patch-all is the effect of both patch-0 and patch-1
+	>file &&
+	git add file &&
+	cat file-1 >file &&
+	git diff >patch-all &&
+
+	# patch-2 is the same as patch-1 but is based
+	# on a version that already has whitespace fixed,
+	# and does not introduce whitespace breakages.
+	sed -e "s/ $//" patch-1 >patch-2 &&
+
+	# If all whitespace breakages are fixed the contents
+	# should look like file-fixed
+	sed -e "s/ $//" file-1 >file-fixed
+
+'
+
+test_expect_success nofix '
+
+	>file &&
+	git add file &&
+
+	# Baseline.  Applying without fixing any whitespace
+	# breakages.
+	git apply --whitespace=nowarn patch-0 &&
+	git apply --whitespace=nowarn patch-1 &&
+
+	# The result should obviously match.
+	test_cmp file-1 file
+'
+
+test_expect_success 'withfix (forward)' '
+
+	>file &&
+	git add file &&
+
+	# The first application will munge the context lines
+	# the second patch depends on.  We should be able to
+	# adjust and still apply.
+	git apply --whitespace=fix patch-0 &&
+	git apply --whitespace=fix patch-1 &&
+
+	test_cmp file-fixed file
+'
+
+test_expect_success 'withfix (backward)' '
+
+	>file &&
+	git add file &&
+
+	# Now we have a whitespace breakages on our side.
+	git apply --whitespace=nowarn patch-0 &&
+
+	# And somebody sends in a patch based on image
+	# with whitespace already fixed.
+	git apply --whitespace=fix patch-2 &&
+
+	# The result should accept the whitespace fixed
+	# postimage.  But the line with "h" is beyond context
+	# horizon and left unfixed.
+
+	sed -e /h/d file-fixed >fixed-head &&
+	sed -e /h/d file >file-head &&
+	test_cmp fixed-head file-head &&
+
+	sed -n -e /h/p file-fixed >fixed-tail &&
+	sed -n -e /h/p file >file-tail &&
+
+	! test_cmp fixed-tail file-tail
+
+'
+
+test_done
diff --git a/t/t4150-am-subdir.sh b/t/t4150-am-subdir.sh
new file mode 100755
index 0000000..52069b4
--- /dev/null
+++ b/t/t4150-am-subdir.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git am running from a subdirectory'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo hello >world &&
+	git add world &&
+	test_tick &&
+	git commit -m initial &&
+	git tag initial &&
+	echo goodbye >world &&
+	git add world &&
+	test_tick &&
+	git commit -m second &&
+	git format-patch --stdout HEAD^ >patchfile &&
+	: >expect
+'
+
+test_expect_success 'am regularly from stdin' '
+	git checkout initial &&
+	git am <patchfile &&
+	git diff master >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'am regularly from file' '
+	git checkout initial &&
+	git am patchfile &&
+	git diff master >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'am regularly from stdin in subdirectory' '
+	rm -fr subdir &&
+	git checkout initial &&
+	(
+		mkdir -p subdir &&
+		cd subdir &&
+		git am <../patchfile
+	) &&
+	git diff master>actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory' '
+	rm -fr subdir &&
+	git checkout initial &&
+	(
+		mkdir -p subdir &&
+		cd subdir &&
+		git am ../patchfile
+	) &&
+	git diff master >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'am regularly from file in subdirectory with full path' '
+	rm -fr subdir &&
+	git checkout initial &&
+	P=$(pwd) &&
+	(
+		mkdir -p subdir &&
+		cd subdir &&
+		git am "$P/patchfile"
+	) &&
+	git diff master >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index eeff3c9..3cbfee7 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -129,7 +129,7 @@
 test_expect_success 'rerere prefers first change' 'git diff a1 expect'
 
 rm $rr/postimage
-echo "$sha1	a1" | tr '\012' '\000' > .git/rr-cache/MERGE_RR
+echo "$sha1	a1" | perl -pe 'y/\012/\000/' > .git/rr-cache/MERGE_RR
 
 test_expect_success 'rerere clear' 'git rerere clear'
 
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 6d12efb..405b971 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -45,6 +45,11 @@
 
 EOF
 
-test_expect_success 'shortlog wrapping' 'diff -u expect out'
+test_expect_success 'shortlog wrapping' 'test_cmp expect out'
+
+git log HEAD > log
+GIT_DIR=non-existing git shortlog -w < log > out
+
+test_expect_success 'shortlog from non-git directory' 'test_cmp expect out'
 
 test_done
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 6e594bf..c955fe4 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -15,7 +15,7 @@
     'rm -f .git/index*
      for i in a b c
      do
-	     dd if=/dev/zero bs=4k count=1 | tr "\\000" $i >$i &&
+	     dd if=/dev/zero bs=4k count=1 | perl -pe "y/\\000/$i/" >$i &&
 	     git update-index --add $i || return 1
      done &&
      cat c >d && echo foo >>d && git update-index --add d &&
@@ -264,8 +264,14 @@
      cp -f	.git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
 		.git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
 
-test_expect_failure \
+test_expect_success \
     'make sure index-pack detects the SHA1 collision' \
-    'git-index-pack -o bad.idx test-3.pack'
+    '! git-index-pack -o bad.idx test-3.pack'
+
+test_expect_success \
+    'honor pack.packSizeLimit' \
+    'git config pack.packSizeLimit 200 &&
+     packname_4=$(git pack-objects test-4 <obj-list) &&
+     test 3 = $(ls test-4-*.pack | wc -l)'
 
 test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 2a2878b..b88b5bb 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -42,9 +42,9 @@
     'both packs should be identical' \
     'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"'
 
-test_expect_failure \
+test_expect_success \
     'index v1 and index v2 should be different' \
-    'cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
+    '! cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
 
 test_expect_success \
     'index-pack with index version 1' \
@@ -78,9 +78,9 @@
     'git verify-pack -v "test-3-${pack3}.pack"'
 
 test "$have_64bits" &&
-test_expect_failure \
+test_expect_success \
     '64-bit offsets: should be different from previous index v2 results' \
-    'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
+    '! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
 
 test "$have_64bits" &&
 test_expect_success \
@@ -103,7 +103,7 @@
 test_expect_success \
     '[index v1] 2) create a stealth corruption in a delta base reference' \
     '# this test assumes a delta smaller than 16 bytes at the end of the pack
-     git show-index <1.idx | sort -n | tail -n 1 | (
+     git show-index <1.idx | sort -n | sed -ne \$p | (
        read delta_offs delta_sha1 &&
        git cat-file blob "$delta_sha1" > blob_1 &&
        chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
@@ -112,22 +112,22 @@
 	  bs=1 count=20 conv=notrunc &&
        git cat-file blob "$delta_sha1" > blob_2 )'
 
-test_expect_failure \
+test_expect_success \
     '[index v1] 3) corrupted delta happily returned wrong data' \
-    'cmp blob_1 blob_2'
+    '! cmp blob_1 blob_2'
 
-test_expect_failure \
+test_expect_success \
     '[index v1] 4) confirm that the pack is actually corrupted' \
-    'git fsck --full $commit'
+    '! git fsck --full $commit'
 
 test_expect_success \
     '[index v1] 5) pack-objects happily reuses corrupted data' \
     'pack4=$(git pack-objects test-4 <obj-list) &&
      test -f "test-4-${pack1}.pack"'
 
-test_expect_failure \
+test_expect_success \
     '[index v1] 6) newly created pack is BAD !' \
-    'git verify-pack -v "test-4-${pack1}.pack"'
+    '! git verify-pack -v "test-4-${pack1}.pack"'
 
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
@@ -141,7 +141,7 @@
 test_expect_success \
     '[index v2] 2) create a stealth corruption in a delta base reference' \
     '# this test assumes a delta smaller than 16 bytes at the end of the pack
-     git show-index <1.idx | sort -n | tail -n 1 | (
+     git show-index <1.idx | sort -n | sed -ne \$p | (
        read delta_offs delta_sha1 delta_crc &&
        git cat-file blob "$delta_sha1" > blob_3 &&
        chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
@@ -150,16 +150,16 @@
 	  bs=1 count=20 conv=notrunc &&
        git cat-file blob "$delta_sha1" > blob_4 )'
 
-test_expect_failure \
+test_expect_success \
     '[index v2] 3) corrupted delta happily returned wrong data' \
-    'cmp blob_3 blob_4'
+    '! cmp blob_3 blob_4'
 
-test_expect_failure \
+test_expect_success \
     '[index v2] 4) confirm that the pack is actually corrupted' \
-    'git fsck --full $commit'
+    '! git fsck --full $commit'
 
-test_expect_failure \
+test_expect_success \
     '[index v2] 5) pack-objects refuses to reuse corrupted data' \
-    'git pack-objects test-5 <obj-list'
+    '! git pack-objects test-5 <obj-list'
 
 test_done
diff --git a/t/t5303-hash-object.sh b/t/t5303-hash-object.sh
new file mode 100755
index 0000000..543c078
--- /dev/null
+++ b/t/t5303-hash-object.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description=git-hash-object
+
+. ./test-lib.sh
+
+test_expect_success \
+    'git hash-object -w --stdin saves the object' \
+    'obname=$(echo foo | git hash-object -w --stdin) &&
+    obpath=$(echo $obname | sed -e "s/\(..\)/\1\//") &&
+    test -r .git/objects/"$obpath" &&
+    rm -f .git/objects/"$obpath"'
+    
+test_expect_success \
+    'git hash-object --stdin -w saves the object' \
+    'obname=$(echo foo | git hash-object --stdin -w) &&
+    obpath=$(echo $obname | sed -e "s/\(..\)/\1\//") &&
+    test -r .git/objects/"$obpath" &&
+    rm -f .git/objects/"$obpath"'    
+
+test_expect_success \
+    'git hash-object --stdin file1 <file0 first operates on file0, then file1' \
+    'echo foo > file1 &&
+    obname0=$(echo bar | git hash-object --stdin) &&
+    obname1=$(git hash-object file1) &&
+    obname0new=$(echo bar | git hash-object --stdin file1 | sed -n -e 1p) &&
+    obname1new=$(echo bar | git hash-object --stdin file1 | sed -n -e 2p) &&
+    test "$obname0" = "$obname0new" &&
+    test "$obname1" = "$obname1new"'
+
+test_expect_success \
+    'git hash-object refuses multiple --stdin arguments' \
+    '! git hash-object --stdin --stdin < file1'
+
+test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 6560af7..9fd9d07 100644
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -29,4 +29,87 @@
 
 '
 
+test_expect_success 'prune --expire' '
+
+	before=$(git count-objects | sed "s/ .*//") &&
+	BLOB=$(echo aleph | git hash-object -w --stdin) &&
+	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE &&
+	git prune --expire=1.hour.ago &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE &&
+	test-chmtime -86500 $BLOB_FILE &&
+	git prune --expire 1.day &&
+	test $before = $(git count-objects | sed "s/ .*//") &&
+	! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: implicit prune --expire' '
+
+	before=$(git count-objects | sed "s/ .*//") &&
+	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE &&
+	test-chmtime -$((86400*14-30)) $BLOB_FILE &&
+	git gc &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE &&
+	test-chmtime -$((86400*14+1)) $BLOB_FILE &&
+	git gc &&
+	test $before = $(git count-objects | sed "s/ .*//") &&
+	! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
+
+	git config gc.pruneExpire invalid &&
+	test_must_fail git gc
+
+'
+
+test_expect_success 'gc: start with ok gc.pruneExpire' '
+
+	git config gc.pruneExpire 2.days.ago &&
+	git gc
+
+'
+
+test_expect_success 'prune: prune nonsense parameters' '
+
+	test_must_fail git prune garbage &&
+	test_must_fail git prune --- &&
+	test_must_fail git prune --no-such-option
+
+'
+
+test_expect_success 'prune: prune unreachable heads' '
+
+	git config core.logAllRefUpdates false &&
+	mv .git/logs .git/logs.old &&
+	: > file2 &&
+	git add file2 &&
+	git commit -m temporary &&
+	tmp_head=$(git rev-list -1 HEAD) &&
+	git reset HEAD^ &&
+	git prune &&
+	test_must_fail git reset $tmp_head --
+
+'
+
+test_expect_success 'prune: do not prune heads listed as an argument' '
+
+	: > file2 &&
+	git add file2 &&
+	git commit -m temporary &&
+	tmp_head=$(git rev-list -1 HEAD) &&
+	git reset HEAD^ &&
+	git prune -- $tmp_head &&
+	git reset $tmp_head --
+
+'
+
 test_done
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
new file mode 100755
index 0000000..0db2754
--- /dev/null
+++ b/t/t5305-include-tag.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git-pack-object --include-tag'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success setup '
+	echo c >d &&
+	git update-index --add d &&
+	tree=`git write-tree` &&
+	commit=`git commit-tree $tree </dev/null` &&
+	echo "object $commit" >sig &&
+	echo "type commit" >>sig &&
+	echo "tag mytag" >>sig &&
+	echo "tagger $(git var GIT_COMMITTER_IDENT)" >>sig &&
+	echo >>sig &&
+	echo "our test tag" >>sig &&
+	tag=`git mktag <sig` &&
+	rm d sig &&
+	git update-ref refs/tags/mytag $tag && {
+		echo $tree &&
+		echo $commit &&
+		git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\)	.*/\\1/"
+	} >obj-list
+'
+
+rm -rf clone.git
+test_expect_success 'pack without --include-tag' '
+	packname_1=$(git pack-objects \
+		--window=0 \
+		test-1 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+	(
+		GIT_DIR=clone.git &&
+		export GIT_DIR &&
+		git init &&
+		git unpack-objects -n <test-1-${packname_1}.pack &&
+		git unpack-objects <test-1-${packname_1}.pack
+	)
+'
+
+test_expect_success 'check unpacked result (have commit, no tag)' '
+	git rev-list --objects $commit >list.expect &&
+	(
+		GIT_DIR=clone.git &&
+		export GIT_DIR &&
+		test_must_fail git cat-file -e $tag &&
+		git rev-list --objects $commit
+	) >list.actual &&
+	git diff list.expect list.actual
+'
+
+rm -rf clone.git
+test_expect_success 'pack with --include-tag' '
+	packname_1=$(git pack-objects \
+		--window=0 \
+		--include-tag \
+		test-2 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+	(
+		GIT_DIR=clone.git &&
+		export GIT_DIR &&
+		git init &&
+		git unpack-objects -n <test-2-${packname_1}.pack &&
+		git unpack-objects <test-2-${packname_1}.pack
+	)
+'
+
+test_expect_success 'check unpacked result (have commit, have tag)' '
+	git rev-list --objects mytag >list.expect &&
+	(
+		GIT_DIR=clone.git &&
+		export GIT_DIR &&
+		git rev-list --objects $tag
+	) >list.actual &&
+	git diff list.expect list.actual
+'
+
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 2d0c07f..2b6b6e3 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -120,7 +120,7 @@
 	cd .. &&
 	git-clone parent child && cd child && git-push --all &&
 	cd ../parent &&
-	git-branch -a >branches && ! grep -q origin/master branches
+	git-branch -a >branches && ! grep origin/master branches
 '
 
 rewound_push_setup() {
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 9734fc5..9a12024 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -60,8 +60,8 @@
 EOF
 chmod u+x victim/.git/hooks/post-update
 
-test_expect_failure push '
-	git-send-pack --force ./victim/.git master tofail >send.out 2>send.err
+test_expect_success push '
+    ! git-send-pack --force ./victim/.git master tofail >send.out 2>send.err
 '
 
 test_expect_success 'updated as expected' '
@@ -112,8 +112,8 @@
 	! test -s victim/.git/post-receive.args
 '
 
-test_expect_failure 'send-pack produced no output' '
-	test -s send.out
+test_expect_success 'send-pack produced no output' '
+	! test -s send.out
 '
 
 cat <<EOF >expect
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh
index 1c4b0b3..1394047 100755
--- a/t/t5402-post-merge-hook.sh
+++ b/t/t5402-post-merge-hook.sh
@@ -30,9 +30,9 @@
     chmod u+x clone${clone}/.git/hooks/post-merge
 done
 
-test_expect_failure 'post-merge does not run for up-to-date ' '
+test_expect_success 'post-merge does not run for up-to-date ' '
         GIT_DIR=clone1/.git git merge $commit0 &&
-	test -e clone1/.git/post-merge.args
+	! test -f clone1/.git/post-merge.args
 '
 
 test_expect_success 'post-merge runs as expected ' '
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 7b6798d..788b4a5 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -176,7 +176,7 @@
 test_expect_success "clone shallow object count" \
 	"test \"count: 18\" = \"$(grep count count.shallow)\""
 
-test_expect_failure "pull in shallow repo with missing merge base" \
-	"(cd shallow; git pull --depth 4 .. A)"
+test_expect_success "pull in shallow repo with missing merge base" \
+	"(cd shallow && ! git pull --depth 4 .. A)"
 
 test_done
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
new file mode 100755
index 0000000..86e5b9b
--- /dev/null
+++ b/t/t5503-tagfollow.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='test automatic tag following'
+
+. ./test-lib.sh
+
+# End state of the repository:
+#
+#         T - tag1          S - tag2
+#        /                 /
+#   L - A ------ O ------ B
+#    \   \                 \
+#     \   C - origin/cat    \
+#      origin/master         master
+
+test_expect_success setup '
+	test_tick &&
+	echo ichi >file &&
+	git add file &&
+	git commit -m L &&
+	L=$(git rev-parse --verify HEAD) &&
+
+	(
+		mkdir cloned &&
+		cd cloned &&
+		git init-db &&
+		git remote add -f origin ..
+	) &&
+
+	test_tick &&
+	echo A >file &&
+	git add file &&
+	git commit -m A &&
+	A=$(git rev-parse --verify HEAD)
+'
+
+U=UPLOAD_LOG
+
+cat - <<EOF >expect
+#S
+want $A
+#E
+EOF
+test_expect_success 'fetch A (new commit : 1 connection)' '
+	rm -f $U
+	(
+		cd cloned &&
+		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+		test $A = $(git rev-parse --verify origin/master)
+	) &&
+	test -s $U &&
+	cut -d" " -f1,2 $U >actual &&
+	git diff expect actual
+'
+
+test_expect_success "create tag T on A, create C on branch cat" '
+	git tag -a -m tag1 tag1 $A &&
+	T=$(git rev-parse --verify tag1) &&
+
+	git checkout -b cat &&
+	echo C >file &&
+	git add file &&
+	git commit -m C &&
+	C=$(git rev-parse --verify HEAD) &&
+	git checkout master
+'
+
+cat - <<EOF >expect
+#S
+want $C
+want $T
+#E
+EOF
+test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
+	rm -f $U
+	(
+		cd cloned &&
+		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+		test $C = $(git rev-parse --verify origin/cat) &&
+		test $T = $(git rev-parse --verify tag1) &&
+		test $A = $(git rev-parse --verify tag1^0)
+	) &&
+	test -s $U &&
+	cut -d" " -f1,2 $U >actual &&
+	git diff expect actual
+'
+
+test_expect_success "create commits O, B, tag S on B" '
+	test_tick &&
+	echo O >file &&
+	git add file &&
+	git commit -m O &&
+
+	test_tick &&
+	echo B >file &&
+	git add file &&
+	git commit -m B &&
+	B=$(git rev-parse --verify HEAD) &&
+
+	git tag -a -m tag2 tag2 $B &&
+	S=$(git rev-parse --verify tag2)
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
+	rm -f $U
+	(
+		cd cloned &&
+		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+		test $B = $(git rev-parse --verify origin/master) &&
+		test $B = $(git rev-parse --verify tag2^0) &&
+		test $S = $(git rev-parse --verify tag2)
+	) &&
+	test -s $U &&
+	cut -d" " -f1,2 $U >actual &&
+	git diff expect actual
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'new clone fetch master and tags' '
+	git branch -D cat
+	rm -f $U
+	(
+		mkdir clone2 &&
+		cd clone2 &&
+		git init &&
+		git remote add origin .. &&
+		GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+		test $B = $(git rev-parse --verify origin/master) &&
+		test $S = $(git rev-parse --verify tag2) &&
+		test $B = $(git rev-parse --verify tag2^0) &&
+		test $T = $(git rev-parse --verify tag1) &&
+		test $A = $(git rev-parse --verify tag1^0)
+	) &&
+	test -s $U &&
+	cut -d" " -f1,2 $U >actual &&
+	git diff expect actual
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 4fc62f5..48ff2d4 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -10,10 +10,12 @@
 	git init &&
 	>file &&
 	git add file &&
+	test_tick &&
 	git commit -m "Initial" &&
 	git checkout -b side &&
 	>elif &&
 	git add elif &&
+	test_tick &&
 	git commit -m "Second" &&
 	git checkout master
 	)
@@ -22,7 +24,7 @@
 tokens_match () {
 	echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect &&
 	echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual &&
-	diff -u expect actual
+	test_cmp expect actual
 }
 
 check_remote_track () {
@@ -71,13 +73,24 @@
 	sed -e "/^refs\/remotes\/origin\//d" \
 	    -e "/^refs\/remotes\/second\//d" >actual &&
 	>expect &&
-	diff -u expect actual
+	test_cmp expect actual
+)
+'
+
+test_expect_success 'remote forces tracking branches' '
+(
+	cd test &&
+	case `git config remote.second.fetch` in
+	+*) true ;;
+	 *) false ;;
+	esac
 )
 '
 
 test_expect_success 'remove remote' '
 (
 	cd test &&
+	git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
 	git remote rm second
 )
 '
@@ -90,8 +103,170 @@
 	git for-each-ref "--format=%(refname)" refs/remotes |
 	sed -e "/^refs\/remotes\/origin\//d" >actual &&
 	>expect &&
-	diff -u expect actual
+	test_cmp expect actual
 )
 '
 
+cat > test/expect << EOF
+* remote origin
+  URL: $(pwd)/one/.git
+  Remote branch merged with 'git pull' while on branch master
+    master
+  New remote branch (next fetch will store in remotes/origin)
+    master
+  Tracked remote branches
+    side master
+  Local branches pushed with 'git push'
+    master:upstream +refs/tags/lastbackup
+EOF
+
+test_expect_success 'show' '
+	(cd test &&
+	 git config --add remote.origin.fetch \
+		refs/heads/master:refs/heads/upstream &&
+	 git fetch &&
+	 git branch -d -r origin/master &&
+	 (cd ../one &&
+	  echo 1 > file &&
+	  test_tick &&
+	  git commit -m update file) &&
+	 git config remote.origin.push \
+		refs/heads/master:refs/heads/upstream &&
+	 git config --add remote.origin.push \
+		+refs/tags/lastbackup &&
+	 git remote show origin > output &&
+	 git diff expect output)
+'
+
+test_expect_success 'prune' '
+	(cd one &&
+	 git branch -m side side2) &&
+	(cd test &&
+	 git fetch origin &&
+	 git remote prune origin &&
+	 git rev-parse refs/remotes/origin/side2 &&
+	 ! git rev-parse refs/remotes/origin/side)
+'
+
+test_expect_success 'add --mirror && prune' '
+	(mkdir mirror &&
+	 cd mirror &&
+	 git init &&
+	 git remote add --mirror -f origin ../one) &&
+	(cd one &&
+	 git branch -m side2 side) &&
+	(cd mirror &&
+	 git rev-parse --verify refs/heads/side2 &&
+	 ! git rev-parse --verify refs/heads/side &&
+	 git fetch origin &&
+	 git remote prune origin &&
+	 ! git rev-parse --verify refs/heads/side2 &&
+	 git rev-parse --verify refs/heads/side)
+'
+
+cat > one/expect << EOF
+  apis/master
+  apis/side
+  drosophila/another
+  drosophila/master
+  drosophila/side
+EOF
+
+test_expect_success 'update' '
+
+	(cd one &&
+	 git remote add drosophila ../two &&
+	 git remote add apis ../mirror &&
+	 git remote update &&
+	 git branch -r > output &&
+	 git diff expect output)
+
+'
+
+cat > one/expect << EOF
+  drosophila/another
+  drosophila/master
+  drosophila/side
+  manduca/master
+  manduca/side
+  megaloprepus/master
+  megaloprepus/side
+EOF
+
+test_expect_success 'update with arguments' '
+
+	(cd one &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git remote add manduca ../mirror &&
+	 git remote add megaloprepus ../mirror &&
+	 git config remotes.phobaeticus "drosophila megaloprepus" &&
+	 git config remotes.titanus manduca &&
+	 git remote update phobaeticus titanus &&
+	 git branch -r > output &&
+	 git diff expect output)
+
+'
+
+cat > one/expect << EOF
+  apis/master
+  apis/side
+  manduca/master
+  manduca/side
+  megaloprepus/master
+  megaloprepus/side
+EOF
+
+test_expect_success 'update default' '
+
+	(cd one &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git config remote.drosophila.skipDefaultUpdate true &&
+	 git remote update default &&
+	 git branch -r > output &&
+	 git diff expect output)
+
+'
+
+cat > one/expect << EOF
+  drosophila/another
+  drosophila/master
+  drosophila/side
+EOF
+
+test_expect_success 'update default (overridden, with funny whitespace)' '
+
+	(cd one &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git config remotes.default "$(printf "\t drosophila  \n")" &&
+	 git remote update default &&
+	 git branch -r > output &&
+	 git diff expect output)
+
+'
+
+test_expect_success '"remote show" does not show symbolic refs' '
+
+	git clone one three &&
+	(cd three &&
+	 git remote show origin > output &&
+	 ! grep HEAD < output &&
+	 ! grep -i stale < output)
+
+'
+
+test_expect_success 'reject adding remote with an invalid name' '
+
+	! git remote add some:url desired-name
+
+'
+
 test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 02882c1..6946557 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -95,7 +95,7 @@
 
 '
 
-test_expect_failure 'fetch must not resolve short tag name' '
+test_expect_success 'fetch must not resolve short tag name' '
 
 	cd "$D" &&
 
@@ -103,11 +103,11 @@
 	cd five &&
 	git init &&
 
-	git fetch .. anno:five
+	! git fetch .. anno:five
 
 '
 
-test_expect_failure 'fetch must not resolve short remote name' '
+test_expect_success 'fetch must not resolve short remote name' '
 
 	cd "$D" &&
 	git-update-ref refs/remotes/six/HEAD HEAD
@@ -116,7 +116,7 @@
 	cd six &&
 	git init &&
 
-	git fetch .. six:six
+	! git fetch .. six:six
 
 '
 
@@ -139,10 +139,10 @@
 	git bundle create bundle2 master~2..master
 '
 
-test_expect_failure 'unbundle 1' '
+test_expect_success 'unbundle 1' '
 	cd "$D/bundle" &&
 	git checkout -b some-branch &&
-	git fetch "$D/bundle1" master:master
+	! git fetch "$D/bundle1" master:master
 '
 
 test_expect_success 'bundle 1 has only 3 files ' '
@@ -249,7 +249,7 @@
 	do
 		echo "$(git rev-parse --verify $h) $h"
 	done >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 6ec5f7c..c0dc949 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -24,28 +24,28 @@
 test_expect_success 'ls-remote --tags .git' '
 
 	git ls-remote --tags .git >actual &&
-	diff -u expected.tag actual
+	test_cmp expected.tag actual
 
 '
 
 test_expect_success 'ls-remote .git' '
 
 	git ls-remote .git >actual &&
-	diff -u expected.all actual
+	test_cmp expected.all actual
 
 '
 
 test_expect_success 'ls-remote --tags self' '
 
 	git ls-remote --tags self >actual &&
-	diff -u expected.tag actual
+	test_cmp expected.tag actual
 
 '
 
 test_expect_success 'ls-remote self' '
 
 	git ls-remote self >actual &&
-	diff -u expected.all actual
+	test_cmp expected.all actual
 
 '
 
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 352e83b..0a757d5 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -100,6 +100,23 @@
 	)
 '
 
+test_expect_success 'fetch with insteadOf' '
+	mk_empty &&
+	(
+		TRASH=$(pwd)/ &&
+		cd testrepo &&
+		git config url.$TRASH.insteadOf trash/
+		git config remote.up.url trash/. &&
+		git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+		git fetch up &&
+
+		r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+		test "z$r" = "z$the_commit" &&
+
+		test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+	)
+'
+
 test_expect_success 'push without wildcard' '
 	mk_empty &&
 
@@ -126,6 +143,20 @@
 	)
 '
 
+test_expect_success 'push with insteadOf' '
+	mk_empty &&
+	TRASH=$(pwd)/ &&
+	git config url.$TRASH.insteadOf trash/ &&
+	git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+	(
+		cd testrepo &&
+		r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+		test "z$r" = "z$the_commit" &&
+
+		test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+	)
+'
+
 test_expect_success 'push with matching heads' '
 
 	mk_test heads/master &&
@@ -242,6 +273,37 @@
 
 '
 
+test_expect_success 'push head with non-existant, incomplete dest' '
+
+	mk_test &&
+	git push testrepo master:branch &&
+	check_push_result $the_commit heads/branch
+
+'
+
+test_expect_success 'push tag with non-existant, incomplete dest' '
+
+	mk_test &&
+	git tag -f v1.0 &&
+	git push testrepo v1.0:tag &&
+	check_push_result $the_commit tags/tag
+
+'
+
+test_expect_success 'push sha1 with non-existant, incomplete dest' '
+
+	mk_test &&
+	test_must_fail git push testrepo `git rev-parse master`:foo
+
+'
+
+test_expect_success 'push ref expression with non-existant, incomplete dest' '
+
+	mk_test &&
+	test_must_fail git push testrepo master^:branch
+
+'
+
 test_expect_success 'push with HEAD' '
 
 	mk_test heads/master &&
@@ -259,6 +321,58 @@
 	check_push_result $the_commit heads/local
 '
 
+test_expect_success 'push with +HEAD' '
+
+	mk_test heads/master &&
+	git checkout master &&
+	git branch -D local &&
+	git checkout -b local &&
+	git push testrepo master local &&
+	check_push_result $the_commit heads/master &&
+	check_push_result $the_commit heads/local &&
+
+	# Without force rewinding should fail
+	git reset --hard HEAD^ &&
+	! git push testrepo HEAD &&
+	check_push_result $the_commit heads/local &&
+
+	# With force rewinding should succeed
+	git push testrepo +HEAD &&
+	check_push_result $the_first_commit heads/local
+
+'
+
+test_expect_success 'push HEAD with non-existant, incomplete dest' '
+
+	mk_test &&
+	git checkout master &&
+	git push testrepo HEAD:branch &&
+	check_push_result $the_commit heads/branch
+
+'
+
+test_expect_success 'push with config remote.*.push = HEAD' '
+
+	mk_test heads/local &&
+	git checkout master &&
+	git branch -f local $the_commit &&
+	(
+		cd testrepo &&
+		git checkout local &&
+		git reset --hard $the_first_commit
+	) &&
+	git config remote.there.url testrepo &&
+	git config remote.there.push HEAD &&
+	git config branch.master.remote there &&
+	git push &&
+	check_push_result $the_commit heads/master &&
+	check_push_result $the_first_commit heads/local
+'
+
+# clean up the cruft left with the previous one
+git config --remove-section remote.there
+git config --remove-section branch.master
+
 test_expect_success 'push with dry-run' '
 
 	mk_test heads/master &&
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index cc8949e..8b05091 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -26,9 +26,8 @@
 
 '
 
-test_expect_failure 'fsck fails' '
-
-	git fsck
+test_expect_success 'fsck fails' '
+	! git fsck
 '
 
 test_expect_success 'upload-pack fails due to error in pack-objects' '
@@ -46,9 +45,8 @@
 
 '
 
-test_expect_failure 'fsck fails' '
-
-	git fsck
+test_expect_success 'fsck fails' '
+	! git fsck
 '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
@@ -66,9 +64,9 @@
 
 '
 
-test_expect_failure 'fetch fails' '
+test_expect_success 'fetch fails' '
 
-	git fetch .. master
+	! git fetch .. master
 
 '
 
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
new file mode 100755
index 0000000..7372439
--- /dev/null
+++ b/t/t5540-http-push.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test http-push
+
+This test runs various sanity checks on http-push.'
+
+. ./test-lib.sh
+
+ROOT_PATH="$PWD"
+LIB_HTTPD_DAV=t
+
+. ../lib-httpd.sh
+
+if ! start_httpd >&3 2>&4
+then
+	say "skipping test, web server setup failed"
+	test_done
+	exit
+fi
+
+test_expect_success 'setup remote repository' '
+	cd "$ROOT_PATH" &&
+	mkdir test_repo &&
+	cd test_repo &&
+	git init &&
+	: >path1 &&
+	git add path1 &&
+	test_tick &&
+	git commit -m initial &&
+	cd - &&
+	git clone --bare test_repo test_repo.git &&
+	cd test_repo.git &&
+	git --bare update-server-info &&
+	chmod +x hooks/post-update &&
+	cd - &&
+	mv test_repo.git $HTTPD_DOCUMENT_ROOT_PATH
+'
+	
+test_expect_success 'clone remote repository' '
+	cd "$ROOT_PATH" &&
+	git clone $HTTPD_URL/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	: >path2 &&
+	git add path2 &&
+	test_tick &&
+	git commit -m path2 &&
+	git push
+'
+
+test_expect_success 'create and delete remote branch' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	git checkout -b dev &&
+	: >path3 &&
+	git add path3 &&
+	test_tick &&
+	git commit -m dev &&
+	git push origin dev &&
+	git fetch &&
+	git push origin :dev &&
+	git branch -d -r origin/dev &&
+	git fetch &&
+	! git show-ref --verify refs/remotes/origin/dev
+'
+
+stop_httpd
+
+test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 1776b37..acf34ce 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -11,13 +11,13 @@
 
 . ./test-lib.sh
 
-test_expect_failure \
+test_expect_success \
     'clone of non-existent source should fail' \
-    'git-clone foo bar'
+    '! git-clone foo bar'
 
-test_expect_failure \
+test_expect_success \
     'failed clone should not leave a directory' \
-    'cd bar'
+    '! test -d bar'
 
 # Need a repo to clone
 test_create_repo foo
@@ -27,9 +27,9 @@
 
 # source repository given to git-clone should be relative to the
 # current path not to the target dir
-test_expect_failure \
+test_expect_success \
     'clone of non-existent (relative to $PWD) source should fail' \
-    'git-clone ../foo baz'
+    '! git-clone ../foo baz'
 
 test_expect_success \
     'clone should work now that source exists' \
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
new file mode 100755
index 0000000..dc9d63d
--- /dev/null
+++ b/t/t5601-clone.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description=clone
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	rm -fr .git &&
+	test_create_repo src &&
+	(
+		cd src
+		>file
+		git add file
+		git commit -m initial
+	)
+
+'
+
+test_expect_success 'clone with excess parameters' '
+
+	test_must_fail git clone -n "file://$(pwd)/src" dst junk
+
+'
+
+test_done
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
index 59a165a..8dfaaa4 100755
--- a/t/t5701-clone-local.sh
+++ b/t/t5701-clone-local.sh
@@ -11,6 +11,11 @@
 	git clone --bare . x &&
 	test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
 	test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
+	git bundle create b1.bundle --all HEAD &&
+	git bundle create b2.bundle --all &&
+	mkdir dir &&
+	cp b1.bundle dir/b3
+	cp b1.bundle b4
 '
 
 test_expect_success 'local clone without .git suffix' '
@@ -71,4 +76,44 @@
 	git fetch &&
 	test ! -e .git/refs/remotes/origin/HEAD'
 
+test_expect_success 'bundle clone without .bundle suffix' '
+	cd "$D" &&
+	git clone dir/b3 &&
+	cd b3 &&
+	git fetch
+'
+
+test_expect_success 'bundle clone with .bundle suffix' '
+	cd "$D" &&
+	git clone b1.bundle &&
+	cd b1 &&
+	git fetch
+'
+
+test_expect_success 'bundle clone from b4' '
+	cd "$D" &&
+	git clone b4 bdl &&
+	cd bdl &&
+	git fetch
+'
+
+test_expect_success 'bundle clone from b4.bundle that does not exist' '
+	cd "$D" &&
+	if git clone b4.bundle bb
+	then
+		echo "Oops, should have failed"
+		false
+	else
+		echo happy
+	fi
+'
+
+test_expect_success 'bundle clone with nonexistent HEAD' '
+	cd "$D" &&
+	git clone b2.bundle b2 &&
+	cd b2 &&
+	git fetch
+	test ! -e .git/refs/heads/master
+'
+
 test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index 1908dc8..910ccb4 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -87,10 +87,10 @@
 
 cd "$base_dir"
 
-test_expect_failure 'that info/alternates is necessary' \
+test_expect_success 'that info/alternates is necessary' \
 'cd C &&
-rm .git/objects/info/alternates &&
-test_valid_repo'
+rm -f .git/objects/info/alternates &&
+! (test_valid_repo)'
 
 cd "$base_dir"
 
@@ -101,9 +101,11 @@
 
 cd "$base_dir"
 
-test_expect_failure 'that relative alternate is only possible for current dir' \
-'cd D &&
-test_valid_repo'
+test_expect_success \
+    'that relative alternate is only possible for current dir' '
+    cd D &&
+    ! (test_valid_repo)
+'
 
 cd "$base_dir"
 
diff --git a/t/t6000lib.sh b/t/t6000lib.sh
index 180633e..c0baaa5 100755
--- a/t/t6000lib.sh
+++ b/t/t6000lib.sh
@@ -97,7 +97,13 @@
 # from front and back.
 name_from_description()
 {
-        tr "'" '-' | tr '~`!@#$%^&*()_+={}[]|\;:"<>,/? ' '-' | tr -s '-' | tr '[A-Z]' '[a-z]' | sed "s/^-*//;s/-*\$//"
+	perl -pe '
+		s/[^A-Za-z0-9.]/-/g;
+		s/-+/-/g;
+		s/-$//;
+		s/^-//;
+		y/A-Z/a-z/;
+	'
 }
 
 
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
index 80d7198..5dabf1c 100755
--- a/t/t6004-rev-list-path-optim.sh
+++ b/t/t6004-rev-list-path-optim.sh
@@ -45,7 +45,7 @@
 test_expect_success 'path optimization 2' '
 	( echo "$side"; echo "$initial" ) >expected &&
 	git rev-list HEAD -- a >actual &&
-	diff -u expected actual
+	test_cmp expected actual
 '
 
 test_done
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
new file mode 100755
index 0000000..c8a96a9
--- /dev/null
+++ b/t/t6009-rev-list-parent.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='properly cull all ancestors'
+
+. ./test-lib.sh
+
+commit () {
+	test_tick &&
+	echo $1 >file &&
+	git commit -a -m $1 &&
+	git tag $1
+}
+
+test_expect_success setup '
+
+	touch file &&
+	git add file &&
+
+	commit one &&
+
+	test_tick=$(($test_tick - 2400))
+
+	commit two &&
+	commit three &&
+	commit four &&
+
+	git log --pretty=oneline --abbrev-commit
+'
+
+test_expect_success 'one is ancestor of others and should not be shown' '
+
+	git rev-list one --not four >result &&
+	>expect &&
+	test_cmp expect result
+
+'
+
+test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index ae3b6f2..79dc58b 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -66,8 +66,8 @@
 	"git diff test.txt test2.txt"
 
 cp test.txt backup.txt
-test_expect_failure "merge with conflicts" \
-	"git merge-file test.txt orig.txt new3.txt"
+test_expect_success "merge with conflicts" \
+	"! git merge-file test.txt orig.txt new3.txt"
 
 cat > expect.txt << EOF
 <<<<<<< test.txt
@@ -89,8 +89,8 @@
 test_expect_success "expected conflict markers" "git diff test.txt expect.txt"
 
 cp backup.txt test.txt
-test_expect_failure "merge with conflicts, using -L" \
-	"git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
+test_expect_success "merge with conflicts, using -L" \
+	"! git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
 
 cat > expect.txt << EOF
 <<<<<<< 1
@@ -113,8 +113,8 @@
 	"git diff test.txt expect.txt"
 
 sed "s/ tu / TU /" < new1.txt > new5.txt
-test_expect_failure "conflict in removed tail" \
-	"git merge-file -p orig.txt new1.txt new5.txt > out"
+test_expect_success "conflict in removed tail" \
+	"! git merge-file -p orig.txt new1.txt new5.txt > out"
 
 cat > expect << EOF
 Dominus regit me,
@@ -139,4 +139,24 @@
 	grep "Cannot merge binary files" merge.err
 '
 
+sed -e "s/deerit.$/deerit;/" -e "s/me;$/me./" < new5.txt > new6.txt
+sed -e "s/deerit.$/deerit,/" -e "s/me;$/me,/" < new5.txt > new7.txt
+
+test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
+
+	! git merge-file -p new6.txt new5.txt new7.txt > output &&
+	test 1 = $(grep ======= < output | wc -l)
+
+'
+
+sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit;/" < new6.txt > new8.txt
+sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit --/" < new7.txt > new9.txt
+
+test_expect_success 'ZEALOUS_ALNUM' '
+
+	! git merge-file -p new8.txt new5.txt new9.txt > merge.out &&
+	test 1 = $(grep ======= < merge.out | wc -l)
+
+'
+
 test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index c154f03..23d24d3 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -60,7 +60,7 @@
 GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
 '
 
-test_expect_failure "combined merge conflicts" "git merge -m final G"
+test_expect_success "combined merge conflicts" "! git merge -m final G"
 
 cat > expect << EOF
 <<<<<<< HEAD:a1
@@ -81,8 +81,8 @@
 
 test_expect_success "virtual trees were processed" "git diff expect out"
 
-git reset --hard
 test_expect_success 'refuse to merge binary files' '
+	git reset --hard &&
 	printf "\0" > binary-file &&
 	git add binary-file &&
 	git commit -m binary &&
diff --git a/t/t6025-merge-symlinks.sh b/t/t6025-merge-symlinks.sh
index 950c2e9..6004deb 100755
--- a/t/t6025-merge-symlinks.sh
+++ b/t/t6025-merge-symlinks.sh
@@ -30,30 +30,29 @@
 git add symlink &&
 git-commit -m b-file'
 
-test_expect_failure \
+test_expect_success \
 'merge master into b-symlink, which has a different symbolic link' '
-! git-checkout b-symlink ||
-git-merge master'
+git-checkout b-symlink &&
+! git-merge master'
 
 test_expect_success \
 'the merge result must be a file' '
 test -f symlink'
 
-test_expect_failure \
+test_expect_success \
 'merge master into b-file, which has a file instead of a symbolic link' '
-! (git-reset --hard &&
-git-checkout b-file) ||
-git-merge master'
+git-reset --hard && git-checkout b-file &&
+! git-merge master'
 
 test_expect_success \
 'the merge result must be a file' '
 test -f symlink'
 
-test_expect_failure \
+test_expect_success \
 'merge b-file, which has a file instead of a symbolic link, into master' '
-! (git-reset --hard &&
-git-checkout master) ||
-git-merge b-file'
+git-reset --hard &&
+git-checkout master &&
+! git-merge b-file'
 
 test_expect_success \
 'the merge result must be a file' '
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
index a7358f7..92ca1f0 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6027-merge-binary.sh
@@ -45,7 +45,7 @@
 		false
 	else
 		git ls-files -s >current
-		diff -u current expect
+		test_cmp current expect
 	fi
 '
 
@@ -60,7 +60,7 @@
 		false
 	else
 		git ls-files -s >current
-		diff -u current expect
+		test_cmp current expect
 	fi
 '
 
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
new file mode 100755
index 0000000..43f5459
--- /dev/null
+++ b/t/t6029-merge-subtree.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='subtree merge strategy'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	s="1 2 3 4 5 6 7 8"
+	for i in $s; do echo $i; done >hello &&
+	git add hello &&
+	git commit -m initial &&
+	git checkout -b side &&
+	echo >>hello world &&
+	git add hello &&
+	git commit -m second &&
+	git checkout master &&
+	for i in mundo $s; do echo $i; done >hello &&
+	git add hello &&
+	git commit -m master
+
+'
+
+test_expect_success 'subtree available and works like recursive' '
+
+	git merge -s subtree side &&
+	for i in mundo $s world; do echo $i; done >expect &&
+	test_cmp expect hello
+
+'
+
+test_expect_success 'setup' '
+	mkdir git-gui &&
+	cd git-gui &&
+	git init &&
+	echo git-gui > git-gui.sh &&
+	o1=$(git hash-object git-gui.sh) &&
+	git add git-gui.sh &&
+	git commit -m "initial git-gui" &&
+	cd .. &&
+	mkdir git &&
+	cd git &&
+	git init &&
+	echo git >git.c &&
+	o2=$(git hash-object git.c) &&
+	git add git.c &&
+	git commit -m "initial git"
+'
+
+test_expect_success 'initial merge' '
+	git remote add -f gui ../git-gui &&
+	git merge -s ours --no-commit gui/master &&
+	git read-tree --prefix=git-gui/ -u gui/master &&
+	git commit -m "Merge git-gui as our subdirectory" &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o1 0	git-gui/git-gui.sh"
+		echo "100644 $o2 0	git.c"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_expect_success 'merge update' '
+	cd ../git-gui &&
+	echo git-gui2 > git-gui.sh &&
+	o3=$(git hash-object git-gui.sh) &&
+	git add git-gui.sh &&
+	git commit -m "update git-gui" &&
+	cd ../git &&
+	git pull -s subtree gui master &&
+	git ls-files -s >actual &&
+	(
+		echo "100644 $o3 0	git-gui/git-gui.sh"
+		echo "100644 $o2 0	git.c"
+	) >expected &&
+	git diff -u expected actual
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 14e8796..933f567 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -237,7 +237,7 @@
 	add_line_into_file "6: Yet a line." hello &&
 	HASH6=$(git rev-parse --verify HEAD) &&
 	echo "#"\!"/bin/sh" > test_script.sh &&
-	echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
+	echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
 	echo "grep line hello > /dev/null" >> test_script.sh &&
 	echo "test \$? -ne 0" >> test_script.sh &&
 	chmod +x test_script.sh &&
@@ -262,8 +262,8 @@
 	add_line_into_file "7: Should be the last line." hello &&
 	HASH7=$(git rev-parse --verify HEAD) &&
 	echo "#"\!"/bin/sh" > test_script.sh &&
-	echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
-	echo "tail -1 hello | grep day > /dev/null && exit 125" >> test_script.sh &&
+	echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
+	echo "sed -ne \\\$p hello | grep day > /dev/null && exit 125" >> test_script.sh &&
 	echo "grep Yet hello > /dev/null" >> test_script.sh &&
 	echo "test \$? -ne 0" >> test_script.sh &&
 	chmod +x test_script.sh &&
@@ -284,6 +284,31 @@
 
 '
 
+test_expect_success 'bisect refuses to start if branch bisect exists' '
+	git bisect reset &&
+	git branch bisect &&
+	test_must_fail git bisect start &&
+	git branch -d bisect &&
+	git checkout -b bisect &&
+	test_must_fail git bisect start &&
+	git checkout master &&
+	git branch -d bisect
+'
+
+test_expect_success 'bisect refuses to start if branch new-bisect exists' '
+	git bisect reset &&
+	git branch new-bisect &&
+	test_must_fail git bisect start &&
+	git branch -d new-bisect
+'
+
+test_expect_success 'bisect errors out if bad and good are mistaken' '
+	git bisect reset &&
+	test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error &&
+	grep "mistake good and bad" rev_list_error &&
+	git bisect reset
+'
+
 #
 #
 test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
index 5bb6b93..c8310ae 100755
--- a/t/t6031-merge-recursive.sh
+++ b/t/t6031-merge-recursive.sh
@@ -42,7 +42,7 @@
 		echo "100755 $H 2	file2"
 		echo "100644 $H 3	file2"
 	) >expect &&
-	diff -u actual expect &&
+	test_cmp actual expect &&
 	test -x file2
 '
 
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 0724864..2328b69 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -26,7 +26,7 @@
 test_expect_success 'final^1^2' "test $(git rev-parse start2) = $(git rev-parse final^1^2)"
 test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)"
 test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi"
-test_expect_failure '--verify start2^1' 'git rev-parse --verify start2^1'
+test_expect_success '--verify start2^1' '! git rev-parse --verify start2^1'
 test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0'
 
 test_expect_success 'repack for next test' 'git repack -a -d'
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index ae8ee11..c6bfef5 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -15,8 +15,11 @@
 check_describe () {
 	expect="$1"
 	shift
-	R=$(git describe "$@") &&
+	R=$(git describe "$@" 2>err.actual)
+	S=$?
+	cat err.actual >&3
 	test_expect_success "describe $*" '
+	test $S = 0 &&
 	case "$R" in
 	$expect)	echo happy ;;
 	*)	echo "Oops - $R is not $expect";
@@ -94,4 +97,46 @@
 check_describe A-* --tags HEAD^^2
 check_describe B --tags HEAD^^2^
 
+check_describe B-0-* --long HEAD^^2^
+check_describe A-3-* --long HEAD^^2
+
+test_expect_success 'rename tag A to Q locally' '
+	mv .git/refs/tags/A .git/refs/tags/Q
+'
+cat - >err.expect <<EOF
+warning: tag 'A' is really 'Q' here
+EOF
+check_describe A-* HEAD
+test_expect_success 'warning was displayed for Q' '
+	git diff err.expect err.actual
+'
+test_expect_success 'rename tag Q back to A' '
+	mv .git/refs/tags/Q .git/refs/tags/A
+'
+
+test_expect_success 'pack tag refs' 'git pack-refs'
+check_describe A-* HEAD
+
+test_expect_success 'set-up matching pattern tests' '
+	git tag -a -m test-annotated test-annotated &&
+	echo >>file &&
+	test_tick &&
+	git commit -a -m "one more" &&
+	git tag test1-lightweight &&
+	echo >>file &&
+	test_tick &&
+	git commit -a -m "yet another" &&
+	git tag test2-lightweight &&
+	echo >>file &&
+	test_tick &&
+	git commit -a -m "even more"
+
+'
+
+check_describe "test-annotated-*" --match="test-*"
+
+check_describe "test1-lightweight-*" --tags --match="test1-*"
+
+check_describe "test2-lightweight-*" --tags --match="test2-*"
+
 test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 8a23aaf..f46ec93 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -43,8 +43,8 @@
 	test -z "$bad"
 '
 
-test_expect_failure 'Check invalid atoms names are errors' '
-	git-for-each-ref --format="%(INVALID)" refs/heads
+test_expect_success 'Check invalid atoms names are errors' '
+	! git-for-each-ref --format="%(INVALID)" refs/heads
 '
 
 test_expect_success 'Check format specifiers are ignored in naming date atoms' '
@@ -63,8 +63,8 @@
 	git-for-each-ref --format="%(authordate:rfc2822)" refs/heads
 '
 
-test_expect_failure 'Check invalid format specifiers are errors' '
-	git-for-each-ref --format="%(authordate:INVALID)" refs/heads
+test_expect_success 'Check invalid format specifiers are errors' '
+	! git-for-each-ref --format="%(authordate:INVALID)" refs/heads
 '
 
 cat >expected <<\EOF
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index b730c90..fa382c5 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -78,9 +78,9 @@
      git diff-tree -r -M --name-status  HEAD^ HEAD | \
      grep "^R100..*path2/README..*path1/path2/README"'
 
-test_expect_failure \
+test_expect_success \
     'do not move directory over existing directory' \
-    'mkdir path0 && mkdir path0/path2 && git mv path2 path0'
+    'mkdir path0 && mkdir path0/path2 && ! git mv path2 path0'
 
 test_expect_success \
     'move into "."' \
@@ -118,4 +118,42 @@
 	git mv ab a
 '
 
+test_expect_success 'absolute pathname' '(
+
+	rm -fr mine &&
+	mkdir mine &&
+	cd mine &&
+	test_create_repo one &&
+	cd one &&
+	mkdir sub &&
+	>sub/file &&
+	git add sub/file &&
+
+	git mv sub "$(pwd)/in" &&
+	! test -d sub &&
+	test -d in &&
+	git ls-files --error-unmatch in/file
+
+
+)'
+
+test_expect_success 'absolute pathname outside should fail' '(
+
+	rm -fr mine &&
+	mkdir mine &&
+	cd mine &&
+	out=$(pwd) &&
+	test_create_repo one &&
+	cd one &&
+	mkdir sub &&
+	>sub/file &&
+	git add sub/file &&
+
+	! git mv sub "$out/out" &&
+	test -d sub &&
+	! test -d ../in &&
+	git ls-files --error-unmatch sub/file
+
+)'
+
 test_done
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 68b2b92..c8b4f65 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -107,8 +107,8 @@
 		diff expected actual
 	'
 
-        test_expect_failure "grep -c $L (no /dev/null)" '
-		git grep -c test $H | grep -q "/dev/null"
+	test_expect_success "grep -c $L (no /dev/null)" '
+		! git grep -c test $H | grep -q /dev/null
         '
 
 done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 6e14bf1..f126204 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -4,7 +4,7 @@
 . ./test-lib.sh
 
 make_commit () {
-	lower=$(echo $1 | tr A-Z a-z)
+	lower=$(echo $1 | tr '[A-Z]' '[a-z]')
 	echo $lower > $lower
 	git add $lower
 	test_tick
@@ -17,6 +17,8 @@
 	make_commit B
 	git checkout -b branch B
 	make_commit D
+	mkdir dir
+	make_commit dir/D
 	make_commit E
 	git checkout master
 	make_commit C
@@ -41,9 +43,23 @@
 '
 
 test_expect_success 'test that the file was renamed' '
-	test d = $(git show HEAD:doh) &&
+	test d = "$(git show HEAD:doh --)" &&
+	! test -f d &&
 	test -f doh &&
-	test d = $(cat doh)
+	test d = "$(cat doh)"
+'
+
+test_expect_success 'rewrite, renaming a specific directory' '
+	git-filter-branch -f --tree-filter "mv dir diroh || :" HEAD
+'
+
+test_expect_success 'test that the directory was renamed' '
+	test dir/d = "$(git show HEAD:diroh/d --)" &&
+	! test -d dir &&
+	test -d diroh &&
+	! test -d diroh/dir &&
+	test -f diroh/d &&
+	test dir/d = "$(cat diroh/d)"
 '
 
 git tag oldD HEAD~4
@@ -78,10 +94,10 @@
 test_expect_success 'subdirectory filter result looks okay' '
 	test 2 = $(git rev-list sub | wc -l) &&
 	git show sub:new &&
-	! git show sub:subdir
+	test_must_fail git show sub:subdir
 '
 
-test_expect_success 'setup and filter history that requires --full-history' '
+test_expect_success 'more setup' '
 	git checkout master &&
 	mkdir subdir &&
 	echo A > subdir/new &&
@@ -91,16 +107,7 @@
 	git rm a &&
 	test_tick &&
 	git commit -m "again subdir on master" &&
-	git merge branch &&
-	git branch sub-master &&
-	git-filter-branch -f --subdirectory-filter subdir sub-master
-'
-
-test_expect_success 'subdirectory filter result looks okay' '
-	test 3 = $(git rev-list -1 --parents sub-master | wc -w) &&
-	git show sub-master^:new &&
-	git show sub-master^2:new &&
-	! git show sub:subdir
+	git merge branch
 '
 
 test_expect_success 'use index-filter to move into a subdirectory' '
@@ -114,7 +121,7 @@
 
 test_expect_success 'stops when msg filter fails' '
 	old=$(git rev-parse HEAD) &&
-	! git-filter-branch -f --msg-filter false HEAD &&
+	test_must_fail git-filter-branch -f --msg-filter false HEAD &&
 	test $old = $(git rev-parse HEAD) &&
 	rm -rf .git-rewrite
 '
@@ -151,8 +158,8 @@
 '
 
 test_expect_success 'barf on invalid name' '
-	! git filter-branch -f master xy-problem &&
-	! git filter-branch -f HEAD^
+	test_must_fail git filter-branch -f master xy-problem &&
+	test_must_fail git filter-branch -f HEAD^
 '
 
 test_expect_success '"map" works in commit filter' '
@@ -174,7 +181,7 @@
 	git add foo &&
 	git commit -m "Adding a file" &&
 	git filter-branch --tree-filter "rm -fr foo" &&
-	! git ls-files --error-unmatch "foo/$name" &&
+	test_must_fail git ls-files --error-unmatch "foo/$name" &&
 	test $(git rev-parse --verify rerere) != $(git rev-parse --verify A)
 
 '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index df496a9..1a7141e 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -26,8 +26,8 @@
 	test `git-tag | wc -l` -eq 0
 '
 
-test_expect_failure 'looking for a tag in an empty tree should fail' \
-	'tag_exists mytag'
+test_expect_success 'looking for a tag in an empty tree should fail' \
+	'! (tag_exists mytag)'
 
 test_expect_success 'creating a tag in an empty tree should fail' '
 	! git-tag mynotag &&
@@ -83,9 +83,9 @@
 
 # special cases for creating tags:
 
-test_expect_failure \
+test_expect_success \
 	'trying to create a tag with the name of one existing should fail' \
-	'git tag mytag'
+	'! git tag mytag'
 
 test_expect_success \
 	'trying to create a tag with a non-valid name should fail' '
@@ -146,8 +146,8 @@
 	! tag_exists myhead
 '
 
-test_expect_failure 'trying to delete an already deleted tag should fail' \
-	'git-tag -d mytag'
+test_expect_success 'trying to delete an already deleted tag should fail' \
+	'! git-tag -d mytag'
 
 # listing various tags with pattern matching:
 
@@ -265,16 +265,16 @@
 	test $(git rev-parse non-annotated-tag) = $(git rev-parse HEAD)
 '
 
-test_expect_failure 'trying to verify an unknown tag should fail' \
-	'git-tag -v unknown-tag'
+test_expect_success 'trying to verify an unknown tag should fail' \
+	'! git-tag -v unknown-tag'
 
-test_expect_failure \
+test_expect_success \
 	'trying to verify a non-annotated and non-signed tag should fail' \
-	'git-tag -v non-annotated-tag'
+	'! git-tag -v non-annotated-tag'
 
-test_expect_failure \
+test_expect_success \
 	'trying to verify many non-annotated or unknown tags, should fail' \
-	'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
+	'! git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
 
 # creating annotated tags:
 
@@ -578,6 +578,14 @@
 	git diff expect actual
 '
 
+# subsequent tests require gpg; check if it is available
+gpg --version >/dev/null
+if [ $? -eq 127 ]; then
+	echo "gpg not found - skipping tag signing and verification tests"
+	test_done
+	exit
+fi
+
 # trying to verify annotated non-signed tags:
 
 test_expect_success \
@@ -600,13 +608,6 @@
 
 # creating and verifying signed tags:
 
-gpg --version >/dev/null
-if [ $? -eq 127 ]; then
-	echo "Skipping signed tags tests, because gpg was not found"
-	test_done
-	exit
-fi
-
 # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
 # the gpg version 1.0.6 didn't parse trust packets correctly, so for
 # that version, creation of signed tags using the generated key fails.
@@ -1027,21 +1028,21 @@
 
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
-test_expect_failure \
+test_expect_success \
 	'git-tag -s fails if gpg is misconfigured' \
-	'git tag -s -m tail tag-gpg-failure'
+	'! git tag -s -m tail tag-gpg-failure'
 git config --unset user.signingkey
 
 # try to verify without gpg:
 
 rm -rf gpghome
-test_expect_failure \
+test_expect_success \
 	'verify signed tag fails when public key is not present' \
-	'git-tag -v signed-tag'
+	'! git-tag -v signed-tag'
 
-test_expect_failure \
+test_expect_success \
 	'git-tag -a fails if tag annotation is empty' '
-	GIT_EDITOR=cat git tag -a initial-comment
+	! (GIT_EDITOR=cat git tag -a initial-comment)
 '
 
 test_expect_success \
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 6a74b3a..2d919d6 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -4,8 +4,6 @@
 
 . ./test-lib.sh
 
-OLD_TERM="$TERM"
-
 for i in GIT_EDITOR core_editor EDITOR VISUAL vi
 do
 	cat >e-$i.sh <<-EOF
@@ -116,6 +114,4 @@
 
 '
 
-TERM="$OLD_TERM"
-
 test_done
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
new file mode 100755
index 0000000..02cf7c5
--- /dev/null
+++ b/t/t7010-setup.sh
@@ -0,0 +1,165 @@
+#!/bin/sh
+
+test_description='setup taking and sanitizing funny paths'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	mkdir -p a/b/c a/e &&
+	D=$(pwd) &&
+	>a/b/c/d &&
+	>a/e/f
+
+'
+
+test_expect_success 'git add (absolute)' '
+
+	git add "$D/a/b/c/d" &&
+	git ls-files >current &&
+	echo a/b/c/d >expect &&
+	test_cmp expect current
+
+'
+
+
+test_expect_success 'git add (funny relative)' '
+
+	rm -f .git/index &&
+	(
+		cd a/b &&
+		git add "../e/./f"
+	) &&
+	git ls-files >current &&
+	echo a/e/f >expect &&
+	test_cmp expect current
+
+'
+
+test_expect_success 'git rm (absolute)' '
+
+	rm -f .git/index &&
+	git add a &&
+	git rm -f --cached "$D/a/b/c/d" &&
+	git ls-files >current &&
+	echo a/e/f >expect &&
+	test_cmp expect current
+
+'
+
+test_expect_success 'git rm (funny relative)' '
+
+	rm -f .git/index &&
+	git add a &&
+	(
+		cd a/b &&
+		git rm -f --cached "../e/./f"
+	) &&
+	git ls-files >current &&
+	echo a/b/c/d >expect &&
+	test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (absolute)' '
+
+	rm -f .git/index &&
+	git add a &&
+	git ls-files "$D/a/e/../b" >current &&
+	echo a/b/c/d >expect &&
+	test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (relative #1)' '
+
+	rm -f .git/index &&
+	git add a &&
+	(
+		cd a/b &&
+		git ls-files "../b/c"
+	)  >current &&
+	echo c/d >expect &&
+	test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (relative #2)' '
+
+	rm -f .git/index &&
+	git add a &&
+	(
+		cd a/b &&
+		git ls-files --full-name "../e/f"
+	)  >current &&
+	echo a/e/f >expect &&
+	test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (relative #3)' '
+
+	rm -f .git/index &&
+	git add a &&
+	(
+		cd a/b &&
+		if git ls-files "../e/f"
+		then
+			echo Gaah, should have failed
+			exit 1
+		else
+			: happy
+		fi
+	)
+
+'
+
+test_expect_success 'commit using absolute path names' '
+	git commit -m "foo" &&
+	echo aa >>a/b/c/d &&
+	git commit -m "aa" "$(pwd)/a/b/c/d"
+'
+
+test_expect_success 'log using absolute path names' '
+	echo bb >>a/b/c/d &&
+	git commit -m "bb" $(pwd)/a/b/c/d &&
+
+	git log a/b/c/d >f1.txt &&
+	git log "$(pwd)/a/b/c/d" >f2.txt &&
+	test_cmp f1.txt f2.txt
+'
+
+test_expect_success 'blame using absolute path names' '
+	git blame a/b/c/d >f1.txt &&
+	git blame "$(pwd)/a/b/c/d" >f2.txt &&
+	test_cmp f1.txt f2.txt
+'
+
+test_expect_success 'setup deeper work tree' '
+	test_create_repo tester
+'
+
+test_expect_success 'add a directory outside the work tree' '(
+	cd tester &&
+	d1="$(cd .. ; pwd)" &&
+	test_must_fail git add "$d1"
+)'
+
+
+test_expect_success 'add a file outside the work tree, nasty case 1' '(
+	cd tester &&
+	f="$(pwd)x" &&
+	echo "$f" &&
+	touch "$f" &&
+	test_must_fail git add "$f"
+)'
+
+test_expect_success 'add a file outside the work tree, nasty case 2' '(
+	cd tester &&
+	f="$(pwd | sed "s/.$//")x" &&
+	echo "$f" &&
+	touch "$f" &&
+	test_must_fail git add "$f"
+)'
+
+test_done
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
index 66d4043..0d9874b 100755
--- a/t/t7101-reset.sh
+++ b/t/t7101-reset.sh
@@ -36,28 +36,28 @@
     'test -d path0 &&
      test -f path0/COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1/path2/COPYING' \
-    'test -f path1/path2/COPYING'
+    '! test -f path1/path2/COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1/COPYING' \
-    'test -f path1/COPYING'
+    '! test -f path1/COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of COPYING' \
-    'test -f COPYING'
+    '! test -f COPYING'
 
-test_expect_failure \
+test_expect_success \
     'checking checking lack of path1/COPYING-TOO' \
-    'test -f path0/COPYING-TOO'
+    '! test -f path0/COPYING-TOO'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1/path2' \
-    'test -d path1/path2'
+    '! test -d path1/path2'
 
-test_expect_failure \
+test_expect_success \
     'checking lack of path1' \
-    'test -d path1'
+    '! test -d path1'
 
 test_done
diff --git a/t/t7104-reset.sh b/t/t7104-reset.sh
new file mode 100755
index 0000000..f136ee7
--- /dev/null
+++ b/t/t7104-reset.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='reset --hard unmerged'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	mkdir before later &&
+	>before/1 &&
+	>before/2 &&
+	>hello &&
+	>later/3 &&
+	git add before hello later &&
+	git commit -m world &&
+
+	H=$(git rev-parse :hello) &&
+	git rm --cached hello &&
+	echo "100644 $H 2	hello" | git update-index --index-info &&
+
+	rm -f hello &&
+	mkdir -p hello &&
+	>hello/world &&
+	test "$(git ls-files -o)" = hello/world
+
+'
+
+test_expect_success 'reset --hard should restore unmerged ones' '
+
+	git reset --hard &&
+	git ls-files --error-unmatch before/1 before/2 hello later/3 &&
+	test -f hello
+
+'
+
+test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
+
+	T=$(git write-tree) &&
+	rm -f .git/index &&
+	git add before hello later &&
+	U=$(git write-tree) &&
+	test "$T" = "$U"
+
+'
+
+test_done
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 73d8a00..3111baa 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -83,13 +83,13 @@
 	fill 0 1 2 3 4 5 6 7 8 >same &&
 	cp same kept
 	git checkout side >messages &&
-	git diff same kept
+	test_cmp same kept
 	(cat > messages.expect <<EOF
 M	same
 EOF
 ) &&
 	touch messages.expect &&
-	git diff messages.expect messages
+	test_cmp messages.expect messages
 '
 
 test_expect_success "checkout -m with dirty tree" '
@@ -103,29 +103,22 @@
 	test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
 
 	(cat >expect.messages <<EOF
-Merging side with local
-Merging:
-ab76817 Side M one, D two, A three
-virtual local
-found 1 common ancestor(s):
-7329388 Initial A one, A two
-Auto-merged one
 M	one
 EOF
 ) &&
-	git diff expect.messages messages &&
+	test_cmp expect.messages messages &&
 
 	fill "M	one" "A	three" "D	two" >expect.master &&
 	git diff --name-status master >current.master &&
-	diff expect.master current.master &&
+	test_cmp expect.master current.master &&
 
 	fill "M	one" >expect.side &&
 	git diff --name-status side >current.side &&
-	diff expect.side current.side &&
+	test_cmp expect.side current.side &&
 
 	: >expect.index &&
 	git diff --cached >current.index &&
-	diff expect.index current.index
+	test_cmp expect.index current.index
 '
 
 test_expect_success "checkout -m with dirty tree, renamed" '
@@ -143,7 +136,7 @@
 
 	git checkout -m renamer &&
 	fill 1 3 4 5 7 8 >expect &&
-	diff expect uno &&
+	test_cmp expect uno &&
 	! test -f one &&
 	git diff --cached >current &&
 	! test -s current
@@ -168,7 +161,7 @@
 	git diff master:one :3:uno |
 	sed -e "1,/^@@/d" -e "/^ /d" -e "s/^-/d/" -e "s/^+/a/" >current &&
 	fill d2 aT d7 aS >expect &&
-	diff current expect &&
+	test_cmp current expect &&
 	git diff --cached two >current &&
 	! test -s current
 '
@@ -185,7 +178,7 @@
 HEAD is now at 7329388... Initial A one, A two
 EOF
 ) &&
-	git diff messages.expect messages &&
+	test_cmp messages.expect messages &&
 	H=$(git rev-parse --verify HEAD) &&
 	M=$(git show-ref -s --verify refs/heads/master) &&
 	test "z$H" = "z$M" &&
@@ -214,6 +207,22 @@
 	fi
 '
 
+test_expect_success 'checkout to detach HEAD with :/message' '
+
+	git checkout -f master && git clean -f &&
+	git checkout ":/Initial" &&
+	H=$(git rev-parse --verify HEAD) &&
+	M=$(git show-ref -s --verify refs/heads/master) &&
+	test "z$H" = "z$M" &&
+	if git symbolic-ref HEAD >/dev/null 2>&1
+	then
+		echo "OOPS, HEAD is still symbolic???"
+		false
+	else
+		: happy
+	fi
+'
+
 test_expect_success 'checkout to detach HEAD with HEAD^0' '
 
 	git checkout -f master && git clean -f &&
@@ -270,4 +279,62 @@
 
 '
 
+test_expect_success 'switch branches while in subdirectory' '
+
+	git reset --hard &&
+	git checkout master &&
+
+	mkdir subs &&
+	(
+		cd subs &&
+		git checkout side
+	) &&
+	! test -f subs/one &&
+	rm -fr subs
+
+'
+
+test_expect_success 'checkout specific path while in subdirectory' '
+
+	git reset --hard &&
+	git checkout side &&
+	mkdir subs &&
+	>subs/bero &&
+	git add subs/bero &&
+	git commit -m "add subs/bero" &&
+
+	git checkout master &&
+	mkdir -p subs &&
+	(
+		cd subs &&
+		git checkout side -- bero
+	) &&
+	test -f subs/bero
+
+'
+
+test_expect_success \
+    'checkout w/--track sets up tracking' '
+    git config branch.autosetupmerge false &&
+    git checkout master &&
+    git checkout --track -b track1 &&
+    test "$(git config branch.track1.remote)" &&
+    test "$(git config branch.track1.merge)"'
+
+test_expect_success \
+    'checkout w/autosetupmerge=always sets up tracking' '
+    git config branch.autosetupmerge always &&
+    git checkout master &&
+    git checkout -b track2 &&
+    test "$(git config branch.track2.remote)" &&
+    test "$(git config branch.track2.merge)"
+    git config branch.autosetupmerge false'
+
+test_expect_success \
+    'checkout w/--track from non-branch HEAD fails' '
+    git checkout -b delete-me master &&
+    rm .git/refs/heads/delete-me &&
+    test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
+    !(git checkout --track -b track)'
+
 test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index dfd1188..a50492f 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -75,8 +75,8 @@
 
 test_expect_success 'git-clean with prefix' '
 
-	mkdir -p build docs &&
-	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+	mkdir -p build docs src/test &&
+	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so src/test/1.c &&
 	(cd src/ && git-clean) &&
 	test -f Makefile &&
 	test -f README &&
@@ -84,11 +84,64 @@
 	test -f src/part2.c &&
 	test -f a.out &&
 	test ! -f src/part3.c &&
+	test -f src/test/1.c &&
 	test -f docs/manual.txt &&
 	test -f obj.o &&
 	test -f build/lib.so
 
 '
+
+test_expect_success 'git-clean with relative prefix' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+	would_clean=$(
+		cd docs &&
+		git clean -n ../src |
+		sed -n -e "s|^Would remove ||p"
+	) &&
+	test "$would_clean" = ../src/part3.c || {
+		echo "OOps <$would_clean>"
+		false
+	}
+'
+
+test_expect_success 'git-clean with absolute path' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+	would_clean=$(
+		cd docs &&
+		git clean -n $(pwd)/../src |
+		sed -n -e "s|^Would remove ||p"
+	) &&
+	test "$would_clean" = ../src/part3.c || {
+		echo "OOps <$would_clean>"
+		false
+	}
+'
+
+test_expect_success 'git-clean with out of work tree relative path' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+	(
+		cd docs &&
+		test_must_fail git clean -n ../..
+	)
+'
+
+test_expect_success 'git-clean with out of work tree absolute path' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+	dd=$(cd .. && pwd) &&
+	(
+		cd docs &&
+		test_must_fail git clean -n $dd
+	)
+'
+
 test_expect_success 'git-clean -d with prefix and path' '
 
 	mkdir -p build docs src/feature &&
@@ -316,4 +369,15 @@
 
 '
 
+test_expect_success 'removal failure' '
+
+	mkdir foo &&
+	touch foo/bar &&
+	exec <foo/bar &&
+	chmod 0 foo &&
+	test_must_fail git clean -f -d
+
+'
+chmod 755 foo
+
 test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
new file mode 100755
index 0000000..b9a2219
--- /dev/null
+++ b/t/t7401-submodule-summary.sh
@@ -0,0 +1,195 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Ping Yin
+#
+
+test_description='Summary support for submodules
+
+This test tries to verify the sanity of summary subcommand of git-submodule.
+'
+
+. ./test-lib.sh
+
+add_file () {
+	sm=$1
+	shift
+	owd=$(pwd)
+	cd "$sm"
+	for name; do
+		echo "$name" > "$name" &&
+		git add "$name" &&
+		test_tick &&
+		git commit -m "Add $name"
+	done >/dev/null
+	git rev-parse --verify HEAD | cut -c1-7
+	cd "$owd"
+}
+commit_file () {
+	test_tick &&
+	git commit "$@" -m "Commit $*" >/dev/null
+}
+
+test_create_repo sm1 &&
+add_file . foo >/dev/null
+
+head1=$(add_file sm1 foo1 foo2)
+
+test_expect_success 'added submodule' "
+	git add sm1 &&
+	git submodule summary >actual &&
+	diff actual - <<-EOF
+* sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+"
+
+commit_file sm1 &&
+head2=$(add_file sm1 foo3)
+
+test_expect_success 'modified submodule(forward)' "
+	git submodule summary >actual &&
+	diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+  > Add foo3
+
+EOF
+"
+
+commit_file sm1 &&
+cd sm1 &&
+git reset --hard HEAD~2 >/dev/null &&
+head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
+cd ..
+
+test_expect_success 'modified submodule(backward)' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head2...$head3 (2):
+  < Add foo3
+  < Add foo2
+
+EOF
+"
+
+head4=$(add_file sm1 foo4 foo5) &&
+head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
+test_expect_success 'modified submodule(backward and forward)' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head2...$head4 (4):
+  > Add foo5
+  > Add foo4
+  < Add foo3
+  < Add foo2
+
+EOF
+"
+
+test_expect_success '--summary-limit' "
+    git submodule summary -n 3 >actual &&
+    diff actual - <<-EOF
+* sm1 $head2...$head4 (4):
+  > Add foo5
+  > Add foo4
+  < Add foo3
+
+EOF
+"
+
+commit_file sm1 &&
+mv sm1 sm1-bak &&
+echo sm1 >sm1 &&
+head5=$(git hash-object sm1 | cut -c1-7) &&
+git add sm1 &&
+rm -f sm1 &&
+mv sm1-bak sm1
+
+test_expect_success 'typechanged submodule(submodule->blob), --cached' "
+    git submodule summary --cached >actual &&
+    diff actual - <<-EOF
+* sm1 $head4(submodule)->$head5(blob) (3):
+  < Add foo5
+
+EOF
+"
+
+rm -rf sm1 &&
+git checkout-index sm1
+test_expect_success 'typechanged submodule(submodule->blob)' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head4(submodule)->$head5(blob):
+
+EOF
+"
+
+rm -f sm1 &&
+test_create_repo sm1 &&
+head6=$(add_file sm1 foo6 foo7)
+test_expect_success 'nonexistent commit' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head4...$head6:
+  Warn: sm1 doesn't contain commit $head4_full
+
+EOF
+"
+
+commit_file
+test_expect_success 'typechanged submodule(blob->submodule)' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head5(blob)->$head6(submodule) (2):
+  > Add foo7
+
+EOF
+"
+
+commit_file sm1 &&
+rm -rf sm1
+test_expect_success 'deleted submodule' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head6...0000000:
+
+EOF
+"
+
+test_create_repo sm2 &&
+head7=$(add_file sm2 foo8 foo9) &&
+git add sm2
+
+test_expect_success 'multiple submodules' "
+    git submodule summary >actual &&
+    diff actual - <<-EOF
+* sm1 $head6...0000000:
+
+* sm2 0000000...$head7 (2):
+  > Add foo9
+
+EOF
+"
+
+test_expect_success 'path filter' "
+    git submodule summary sm2 >actual &&
+    diff actual - <<-EOF
+* sm2 0000000...$head7 (2):
+  > Add foo9
+
+EOF
+"
+
+commit_file sm2
+test_expect_success 'given commit' "
+    git submodule summary HEAD^ >actual &&
+    diff actual - <<-EOF
+* sm1 $head6...0000000:
+
+* sm2 0000000...$head7 (2):
+  > Add foo9
+
+EOF
+"
+
+test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 55043d1..c0288f3 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -17,49 +17,49 @@
 	 git-add file && \
 	 git-status | grep 'Initial commit'"
 
-test_expect_failure \
+test_expect_success \
 	"fail initial amend" \
-	"git-commit --amend"
+	"! git-commit --amend"
 
 test_expect_success \
 	"initial commit" \
 	"git-commit -m initial"
 
-test_expect_failure \
+test_expect_success \
 	"invalid options 1" \
-	"git-commit -m foo -m bar -F file"
+	"! git-commit -m foo -m bar -F file"
 
-test_expect_failure \
+test_expect_success \
 	"invalid options 2" \
-	"git-commit -C HEAD -m illegal"
+	"! git-commit -C HEAD -m illegal"
 
-test_expect_failure \
+test_expect_success \
 	"using paths with -a" \
 	"echo King of the bongo >file &&
-	git-commit -m foo -a file"
+	! git-commit -m foo -a file"
 
-test_expect_failure \
+test_expect_success \
 	"using paths with --interactive" \
 	"echo bong-o-bong >file &&
-	echo 7 | git-commit -m foo --interactive file"
+	! echo 7 | git-commit -m foo --interactive file"
 
-test_expect_failure \
+test_expect_success \
 	"using invalid commit with -C" \
-	"git-commit -C bogus"
+	"! git-commit -C bogus"
 
-test_expect_failure \
+test_expect_success \
 	"testing nothing to commit" \
-	"git-commit -m initial"
+	"! git-commit -m initial"
 
 test_expect_success \
 	"next commit" \
 	"echo 'bongo bongo bongo' >file \
 	 git-commit -m next -a"
 
-test_expect_failure \
+test_expect_success \
 	"commit message from non-existing file" \
 	"echo 'more bongo: bongo bongo bongo bongo' >file && \
-	 git-commit -F gah -a"
+	 ! git-commit -F gah -a"
 
 # Empty except stray tabs and spaces on a few lines.
 sed -e 's/@$//' >msg <<EOF
@@ -68,9 +68,9 @@
   @
 Signed-off-by: hula
 EOF
-test_expect_failure \
+test_expect_success \
 	"empty commit message" \
-	"git-commit -F msg -a"
+	"! git-commit -F msg -a"
 
 test_expect_success \
 	"commit message from file" \
@@ -88,10 +88,10 @@
 	"amend commit" \
 	"VISUAL=./editor git-commit --amend"
 
-test_expect_failure \
+test_expect_success \
 	"passing -m and -F" \
 	"echo 'enough with the bongos' >file && \
-	 git-commit -F msg -m amending ."
+	 ! git-commit -F msg -m amending ."
 
 test_expect_success \
 	"using message from other commit" \
@@ -203,7 +203,7 @@
 		git var GIT_COMMITTER_IDENT |
 		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
 	) >expected &&
-	diff -u expected actual
+	test_cmp expected actual
 
 '
 
@@ -223,7 +223,7 @@
 		git var GIT_COMMITTER_IDENT |
 		sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
 	) >expected &&
-	diff -u expected actual
+	test_cmp expected actual
 
 '
 
@@ -240,7 +240,7 @@
 		echo
 		echo three
 	) >expected &&
-	diff -u expected actual
+	test_cmp expected actual
 
 '
 
@@ -301,12 +301,12 @@
 	git merge -s ours side -m "empty ok" &&
 	git diff HEAD^ HEAD >actual &&
 	: >expected &&
-	diff -u expected actual &&
+	test_cmp expected actual &&
 
 	git commit --amend -m "empty really ok" &&
 	git diff HEAD^ HEAD >actual &&
 	: >expected &&
-	diff -u expected actual
+	test_cmp expected actual
 
 '
 
@@ -323,7 +323,7 @@
 	git commit --allow-empty --amend -C "$old" &&
 	git show --pretty="format:%ad %s" "$old" >expected &&
 	git show --pretty="format:%ad %s" HEAD >actual &&
-	diff -u expected actual
+	test_cmp expected actual
 
 '
 
@@ -341,7 +341,7 @@
 	git commit --allow-empty --amend -C tagged-old &&
 	git show --pretty="format:%ad %s" "$old" >expected &&
 	git show --pretty="format:%ad %s" HEAD >actual &&
-	diff -u expected actual
+	test_cmp expected actual
 
 '
 
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index aaf497e..284c941 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -85,7 +85,7 @@
 	git add negative &&
 	git status -v | sed -ne "/^diff --git /p" >actual &&
 	echo "diff --git a/negative b/negative" >expect &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -95,7 +95,7 @@
 	{ echo;echo "# text";echo; } >expect &&
 	git commit --cleanup=verbatim -t expect -a &&
 	git cat-file -p HEAD |sed -e "1,/^\$/d" |head -n 3 >actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -104,7 +104,7 @@
 	echo >>negative &&
 	git commit --cleanup=verbatim -F expect -a &&
 	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -113,7 +113,7 @@
 	echo >>negative &&
 	git commit --cleanup=verbatim -m "$(cat expect)" -a &&
 	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -124,7 +124,7 @@
 	echo "# text" >expect &&
 	git commit --cleanup=whitespace -F text -a &&
 	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -135,7 +135,7 @@
 	echo sample >expect &&
 	git commit --cleanup=strip -F text -a &&
 	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
@@ -150,8 +150,37 @@
 	{ echo;echo sample;echo; } >text &&
 	git commit -e -F text -a &&
 	head -n 4 .git/COMMIT_EDITMSG >actual &&
-	diff -u expect actual
+	test_cmp expect actual
 
 '
 
+pwd=`pwd`
+cat >> .git/FAKE_EDITOR << EOF
+#! /bin/sh
+echo editor started > "$pwd/.git/result"
+exit 0
+EOF
+chmod +x .git/FAKE_EDITOR
+
+test_expect_success 'do not fire editor in the presence of conflicts' '
+
+	git clean
+	echo f>g
+	git add g
+	git commit -myes
+	git branch second
+	echo master>g
+	echo g>h
+	git add g h
+	git commit -mmaster
+	git checkout second
+	echo second>g
+	git add g
+	git commit -msecond
+	git cherry-pick -n master
+	echo "editor not started" > .git/result
+	GIT_EDITOR=`pwd`/.git/FAKE_EDITOR git commit && exit 1  # should fail
+	test "`cat .git/result`" = "editor not started"
+'
+
 test_done
diff --git a/t/t7502-status.sh b/t/t7502-status.sh
index e006074..cd08516 100755
--- a/t/t7502-status.sh
+++ b/t/t7502-status.sh
@@ -33,7 +33,7 @@
 
 test_expect_success 'status (1)' '
 
-	grep -e "use \"git rm --cached <file>\.\.\.\" to unstage" output
+	grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 
 '
 
@@ -146,7 +146,7 @@
 EOF
 test_expect_success 'status of partial commit excluding new file in index' '
 	git status dir1/modified >output &&
-	diff -u expect output
+	test_cmp expect output
 '
 
 test_done
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index d787cac..2dd5a5e 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -52,11 +52,11 @@
 exit 1
 EOF
 
-test_expect_failure 'with failing hook' '
+test_expect_success 'with failing hook' '
 
 	echo "another" >> file &&
 	git add file &&
-	git commit -m "another"
+	! git commit -m "another"
 
 '
 
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 751b113..eff36aa 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -98,20 +98,20 @@
 exit 1
 EOF
 
-test_expect_failure 'with failing hook' '
+test_expect_success 'with failing hook' '
 
 	echo "another" >> file &&
 	git add file &&
-	git commit -m "another"
+	! git commit -m "another"
 
 '
 
-test_expect_failure 'with failing hook (editor)' '
+test_expect_success 'with failing hook (editor)' '
 
 	echo "more another" >> file &&
 	git add file &&
 	echo "more another" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit
+	! (GIT_EDITOR="$FAKE_EDITOR" git commit)
 
 '
 
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
new file mode 100755
index 0000000..802aa62
--- /dev/null
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+test_description='prepare-commit-msg hook'
+
+. ./test-lib.sh
+
+test_expect_success 'with no hook' '
+
+	echo "foo" > file &&
+	git add file &&
+	git commit -m "first"
+
+'
+
+# set up fake editor for interactive editing
+cat > fake-editor <<'EOF'
+#!/bin/sh
+exit 0
+EOF
+chmod +x fake-editor
+FAKE_EDITOR="$(pwd)/fake-editor"
+export FAKE_EDITOR
+
+# now install hook that always succeeds and adds a message
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/prepare-commit-msg"
+mkdir -p "$HOOKDIR"
+echo "#!$SHELL_PATH" > "$HOOK"
+cat >> "$HOOK" <<'EOF'
+
+if test "$2" = commit; then
+  source=$(git-rev-parse "$3")
+else
+  source=${2-default}
+fi
+if test "$GIT_EDITOR" = :; then
+  sed -e "1s/.*/$source (no editor)/" "$1" > msg.tmp
+else
+  sed -e "1s/.*/$source/" "$1" > msg.tmp
+fi
+mv msg.tmp "$1"
+exit 0
+EOF
+chmod +x "$HOOK"
+
+echo dummy template > "$(git rev-parse --git-dir)/template"
+
+test_expect_success 'with hook (-m)' '
+
+	echo "more" >> file &&
+	git add file &&
+	git commit -m "more" &&
+	test "`git log -1 --pretty=format:%s`" = "message (no editor)"
+
+'
+
+test_expect_success 'with hook (-m editor)' '
+
+	echo "more" >> file &&
+	git add file &&
+	GIT_EDITOR="$FAKE_EDITOR" git commit -e -m "more more" &&
+	test "`git log -1 --pretty=format:%s`" = message
+
+'
+
+test_expect_success 'with hook (-t)' '
+
+	echo "more" >> file &&
+	git add file &&
+	git commit -t "$(git rev-parse --git-dir)/template" &&
+	test "`git log -1 --pretty=format:%s`" = template
+
+'
+
+test_expect_success 'with hook (-F)' '
+
+	echo "more" >> file &&
+	git add file &&
+	(echo more | git commit -F -) &&
+	test "`git log -1 --pretty=format:%s`" = "message (no editor)"
+
+'
+
+test_expect_success 'with hook (-F editor)' '
+
+	echo "more" >> file &&
+	git add file &&
+	(echo more more | GIT_EDITOR="$FAKE_EDITOR" git commit -e -F -) &&
+	test "`git log -1 --pretty=format:%s`" = message
+
+'
+
+test_expect_success 'with hook (-C)' '
+
+	head=`git rev-parse HEAD` &&
+	echo "more" >> file &&
+	git add file &&
+	git commit -C $head &&
+	test "`git log -1 --pretty=format:%s`" = "$head (no editor)"
+
+'
+
+test_expect_success 'with hook (editor)' '
+
+	echo "more more" >> file &&
+	git add file &&
+	GIT_EDITOR="$FAKE_EDITOR" git commit &&
+	test "`git log -1 --pretty=format:%s`" = default
+
+'
+
+test_expect_success 'with hook (--amend)' '
+
+	head=`git rev-parse HEAD` &&
+	echo "more" >> file &&
+	git add file &&
+	GIT_EDITOR="$FAKE_EDITOR" git commit --amend &&
+	test "`git log -1 --pretty=format:%s`" = "$head"
+
+'
+
+test_expect_success 'with hook (-c)' '
+
+	head=`git rev-parse HEAD` &&
+	echo "more" >> file &&
+	git add file &&
+	GIT_EDITOR="$FAKE_EDITOR" git commit -c $head &&
+	test "`git log -1 --pretty=format:%s`" = "$head"
+
+'
+
+cat > "$HOOK" <<'EOF'
+#!/bin/sh
+exit 1
+EOF
+
+test_expect_success 'with failing hook' '
+
+	head=`git rev-parse HEAD` &&
+	echo "more" >> file &&
+	git add file &&
+	! GIT_EDITOR="$FAKE_EDITOR" git commit -c $head
+
+'
+
+test_expect_success 'with failing hook (--no-verify)' '
+
+	head=`git rev-parse HEAD` &&
+	echo "more" >> file &&
+	git add file &&
+	! GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify -c $head
+
+'
+
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 5d16628..56869ac 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -108,7 +108,7 @@
 }
 
 verify_diff() {
-	if ! diff -u "$1" "$2"
+	if ! test_cmp "$1" "$2"
 	then
 		echo "$3"
 		false
@@ -165,7 +165,7 @@
 	fi &&
 	while test $# -gt 0
 	do
-		head=$(head -n $i .git/MERGE_HEAD | tail -n 1)
+		head=$(head -n $i .git/MERGE_HEAD | sed -ne \$p)
 		if test "$1" != "$head"
 		then
 			echo "[OOPS] MERGE_HEAD $i != $1"
@@ -371,7 +371,7 @@
 	git merge --summary c2 >diffstat.txt &&
 	verify_merge file result.1-5 msg.1-5 &&
 	verify_parents $c1 $c2 &&
-	if ! grep -e "^ file |  *2 +-$" diffstat.txt
+	if ! grep "^ file |  *2 +-$" diffstat.txt
 	then
 		echo "[OOPS] diffstat was not generated"
 	fi
@@ -386,7 +386,7 @@
 	git merge -n c2 >diffstat.txt &&
 	verify_merge file result.1-5 msg.1-5 &&
 	verify_parents $c1 $c2 &&
-	if grep -e "^ file |  *2 +-$" diffstat.txt
+	if grep "^ file |  *2 +-$" diffstat.txt
 	then
 		echo "[OOPS] diffstat was generated"
 		false
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
new file mode 100644
index 0000000..6b0483f
--- /dev/null
+++ b/t/t7610-mergetool.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Charles Bailey
+#
+
+test_description='git-mergetool
+
+Testing basic merge tool invocation'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+    echo master >file1 &&
+    git add file1 &&
+    git commit -m "added file1" &&
+    git checkout -b branch1 master &&
+    echo branch1 change >file1 &&
+    echo branch1 newfile >file2 &&
+    git add file1 file2 &&
+    git commit -m "branch1 changes" &&
+    git checkout -b branch2 master &&
+    echo branch2 change >file1 &&
+    echo branch2 newfile >file2 &&
+    git add file1 file2 &&
+    git commit -m "branch2 changes" &&
+    git checkout master &&
+    echo master updated >file1 &&
+    echo master new >file2 &&
+    git add file1 file2 &&
+    git commit -m "master updates"
+'
+
+test_expect_success 'custom mergetool' '
+    git config merge.tool mytool &&
+    git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+    git config mergetool.mytool.trustExitCode true &&
+	git checkout branch1 &&
+    ! git merge master >/dev/null 2>&1 &&
+    ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+    test "$(cat file1)" = "master updated" &&
+    test "$(cat file2)" = "master new" &&
+	git commit -m "branch1 resolved with mergetool"
+'
+
+test_done
diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh
index db51b3a..966bb0a 100755
--- a/t/t8003-blame.sh
+++ b/t/t8003-blame.sh
@@ -112,7 +112,7 @@
 		echo mouse-Second
 		echo mouse-Third
 	} >expected &&
-	diff -u expected current
+	test_cmp expected current
 
 '
 
@@ -125,7 +125,7 @@
 		echo cow-Fifth
 		echo mouse-Third
 	} >expected &&
-	diff -u expected current
+	test_cmp expected current
 
 '
 
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 2efaed4..a4bcd28 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -15,16 +15,22 @@
     'Setup helper tool' \
     '(echo "#!/bin/sh"
       echo shift
+      echo output=1
+      echo "while test -f commandline\$output; do output=\$((\$output+1)); done"
       echo for a
       echo do
       echo "  echo \"!\$a!\""
-      echo "done >commandline"
-      echo "cat > msgtxt"
+      echo "done >commandline\$output"
+      echo "cat > msgtxt\$output"
       ) >fake.sendmail &&
      chmod +x ./fake.sendmail &&
      git add fake.sendmail &&
      GIT_AUTHOR_NAME="A" git commit -a -m "Second."'
 
+clean_fake_sendmail() {
+	rm -f commandline* msgtxt*
+}
+
 test_expect_success 'Extract patches' '
     patches=`git format-patch -n HEAD^1`
 '
@@ -39,7 +45,7 @@
 EOF
 test_expect_success \
     'Verify commandline' \
-    'diff commandline expected'
+    'diff commandline1 expected'
 
 cat >expected-show-all-headers <<\EOF
 0001-Second.patch
@@ -75,14 +81,14 @@
 		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
 		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
 		>actual-show-all-headers &&
-	diff -u expected-show-all-headers actual-show-all-headers
+	test_cmp expected-show-all-headers actual-show-all-headers
 '
 
 z8=zzzzzzzz
 z64=$z8$z8$z8$z8$z8$z8$z8$z8
 z512=$z64$z64$z64$z64$z64$z64$z64$z64
 test_expect_success 'reject long lines' '
-	rm -f commandline &&
+	clean_fake_sendmail &&
 	cp $patches longline.patch &&
 	echo $z512$z512 >>longline.patch &&
 	! git send-email \
@@ -95,7 +101,7 @@
 '
 
 test_expect_success 'no patch was sent' '
-	! test -e commandline
+	! test -e commandline1
 '
 
 test_expect_success 'allow long lines with --no-validate' '
@@ -109,6 +115,7 @@
 '
 
 test_expect_success 'Invalid In-Reply-To' '
+	clean_fake_sendmail &&
 	git send-email \
 		--from="Example <nobody@example.com>" \
 		--to=nobody@example.com \
@@ -116,17 +123,106 @@
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$patches
 		2>errors
-	! grep "^In-Reply-To: < *>" msgtxt
+	! grep "^In-Reply-To: < *>" msgtxt1
 '
 
 test_expect_success 'Valid In-Reply-To when prompting' '
+	clean_fake_sendmail &&
 	(echo "From Example <from@example.com>"
 	 echo "To Example <to@example.com>"
 	 echo ""
 	) | env GIT_SEND_EMAIL_NOTTY=1 git send-email \
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$patches 2>errors &&
-	! grep "^In-Reply-To: < *>" msgtxt
+	! grep "^In-Reply-To: < *>" msgtxt1
+'
+
+test_expect_success 'setup fake editor' '
+	(echo "#!/bin/sh" &&
+	 echo "echo fake edit >>\$1"
+	) >fake-editor &&
+	chmod +x fake-editor
+'
+
+test_expect_success '--compose works' '
+	clean_fake_sendmail &&
+	echo y | \
+		GIT_EDITOR=$(pwd)/fake-editor \
+		GIT_SEND_EMAIL_NOTTY=1 \
+		git send-email \
+		--compose --subject foo \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches \
+		2>errors
+'
+
+test_expect_success 'first message is compose text' '
+	grep "^fake edit" msgtxt1
+'
+
+test_expect_success 'second message is patch' '
+	grep "Subject:.*Second" msgtxt2
+'
+
+test_expect_success '--compose adds MIME for utf8 body' '
+	clean_fake_sendmail &&
+	(echo "#!/bin/sh" &&
+	 echo "echo utf8 body: àéìöú >>\$1"
+	) >fake-editor-utf8 &&
+	chmod +x fake-editor-utf8 &&
+	echo y | \
+	  GIT_EDITOR=$(pwd)/fake-editor-utf8 \
+	  GIT_SEND_EMAIL_NOTTY=1 \
+	  git send-email \
+	  --compose --subject foo \
+	  --from="Example <nobody@example.com>" \
+	  --to=nobody@example.com \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  $patches &&
+	grep "^utf8 body" msgtxt1 &&
+	grep "^Content-Type: text/plain; charset=utf-8" msgtxt1
+'
+
+test_expect_success '--compose respects user mime type' '
+	clean_fake_sendmail &&
+	(echo "#!/bin/sh" &&
+	 echo "(echo MIME-Version: 1.0"
+	 echo " echo Content-Type: text/plain\\; charset=iso-8859-1"
+	 echo " echo Content-Transfer-Encoding: 8bit"
+	 echo " echo Subject: foo"
+	 echo " echo "
+	 echo " echo utf8 body: àéìöú) >\$1"
+	) >fake-editor-utf8-mime &&
+	chmod +x fake-editor-utf8-mime &&
+	echo y | \
+	  GIT_EDITOR=$(pwd)/fake-editor-utf8-mime \
+	  GIT_SEND_EMAIL_NOTTY=1 \
+	  git send-email \
+	  --compose --subject foo \
+	  --from="Example <nobody@example.com>" \
+	  --to=nobody@example.com \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  $patches &&
+	grep "^utf8 body" msgtxt1 &&
+	grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 &&
+	! grep "^Content-Type: text/plain; charset=utf-8" msgtxt1
+'
+
+test_expect_success '--compose adds MIME for utf8 subject' '
+	clean_fake_sendmail &&
+	echo y | \
+	  GIT_EDITOR=$(pwd)/fake-editor \
+	  GIT_SEND_EMAIL_NOTTY=1 \
+	  git send-email \
+	  --compose --subject utf8-sübjëct \
+	  --from="Example <nobody@example.com>" \
+	  --to=nobody@example.com \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  $patches &&
+	grep "^fake edit" msgtxt1 &&
+	grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
 test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 614cf50..4e24ab3 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -56,19 +56,19 @@
 
 
 name='detect node change from file to directory #1'
-test_expect_failure "$name" "
+test_expect_success "$name" "
 	mkdir dir/new_file &&
 	mv dir/file dir/new_file/file &&
 	mv dir/new_file dir/file &&
 	git update-index --remove dir/file &&
 	git update-index --add dir/file/file &&
-	git commit -m '$name'  &&
-	git-svn set-tree --find-copies-harder --rmdir \
+	git commit -m '$name' &&
+	! git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch" || true
 
 
 name='detect node change from directory to file #1'
-test_expect_failure "$name" "
+test_expect_success "$name" "
 	rm -rf dir '$GIT_DIR'/index &&
 	git checkout -f -b mybranch2 remotes/git-svn &&
 	mv bar/zzz zzz &&
@@ -77,12 +77,12 @@
 	git update-index --remove -- bar/zzz &&
 	git update-index --add -- bar &&
 	git commit -m '$name' &&
-	git-svn set-tree --find-copies-harder --rmdir \
+	! git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch2" || true
 
 
 name='detect node change from file to directory #2'
-test_expect_failure "$name" "
+test_expect_success "$name" "
 	rm -f '$GIT_DIR'/index &&
 	git checkout -f -b mybranch3 remotes/git-svn &&
 	rm bar/zzz &&
@@ -91,12 +91,12 @@
 	echo yyy > bar/zzz/yyy &&
 	git update-index --add bar/zzz/yyy &&
 	git commit -m '$name' &&
-	git-svn set-tree --find-copies-harder --rmdir \
+	! git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch3" || true
 
 
 name='detect node change from directory to file #2'
-test_expect_failure "$name" "
+test_expect_success "$name" "
 	rm -f '$GIT_DIR'/index &&
 	git checkout -f -b mybranch4 remotes/git-svn &&
 	rm -rf dir &&
@@ -105,7 +105,7 @@
 	echo asdf > dir &&
 	git update-index --add -- dir &&
 	git commit -m '$name' &&
-	git-svn set-tree --find-copies-harder --rmdir \
+	! git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch4" || true
 
 
@@ -213,18 +213,18 @@
 
 test_expect_success "$name" "git diff a expected"
 
-test_expect_failure 'exit if remote refs are ambigious' "
+test_expect_success 'exit if remote refs are ambigious' "
         git config --add svn-remote.svn.fetch \
                               bar:refs/remotes/git-svn &&
-        git-svn migrate
-        "
+	! git-svn migrate
+"
 
-test_expect_failure 'exit if init-ing a would clobber a URL' "
+test_expect_success 'exit if init-ing a would clobber a URL' "
         svnadmin create ${PWD}/svnrepo2 &&
         svn mkdir -m 'mkdir bar' ${svnrepo}2/bar &&
         git config --unset svn-remote.svn.fetch \
                                 '^bar:refs/remotes/git-svn$' &&
-        git-svn init ${svnrepo}2/bar
+	! git-svn init ${svnrepo}2/bar
         "
 
 test_expect_success \
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index 79b7968..f74ab12 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -24,11 +24,11 @@
 	rm -rf t.svn
 	"
 
-test_expect_failure 'commit conflicting change from git' "
+test_expect_success 'commit conflicting change from git' "
 	echo second line from git >> file &&
 	git commit -a -m 'second line from git' &&
-	git-svn commit-diff -r1 HEAD~1 HEAD $svnrepo
-	" || true
+	! git-svn commit-diff -r1 HEAD~1 HEAD $svnrepo
+"
 
 test_expect_success 'commit complementing change from git' "
 	git reset --hard HEAD~1 &&
@@ -39,7 +39,7 @@
 	git-svn commit-diff -r2 HEAD~1 HEAD $svnrepo
 	"
 
-test_expect_failure 'dcommit fails to commit because of conflict' "
+test_expect_success 'dcommit fails to commit because of conflict' "
 	git-svn init $svnrepo &&
 	git-svn fetch &&
 	git reset --hard refs/remotes/git-svn &&
@@ -52,8 +52,8 @@
 	rm -rf t.svn &&
 	echo 'fourth line from git' >> file &&
 	git commit -a -m 'fourth line from git' &&
-	git-svn dcommit
-	" || true
+	! git-svn dcommit
+	"
 
 test_expect_success 'dcommit does the svn equivalent of an index merge' "
 	git reset --hard refs/remotes/git-svn &&
@@ -76,15 +76,15 @@
 	rm -rf t.svn
 	"
 
-test_expect_failure 'multiple dcommit from git-svn will not clobber svn' "
+test_expect_success 'multiple dcommit from git-svn will not clobber svn' "
 	git reset --hard refs/remotes/git-svn &&
 	echo new file >> new-file &&
 	git update-index --add new-file &&
 	git commit -a -m 'new file' &&
 	echo clobber > file &&
 	git commit -a -m 'clobber' &&
-	git svn dcommit
-	" || true
+	! git svn dcommit
+	"
 
 
 test_expect_success 'check that rebase really failed' 'test -d .dotest'
diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh
index 7452546..ca8a00e 100755
--- a/t/t9106-git-svn-dcommit-clobber-series.sh
+++ b/t/t9106-git-svn-dcommit-clobber-series.sh
@@ -54,10 +54,10 @@
 		test x\"\`sed -n -e 61p < file\`\" = x6611
 	"
 
-test_expect_failure 'attempt to dcommit with a dirty index' '
+test_expect_success 'attempt to dcommit with a dirty index' '
 	echo foo >>file &&
 	git add file &&
-	git svn dcommit
+	! git svn dcommit
 '
 
 test_done
diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh
index 08313bb..646a5f0 100755
--- a/t/t9112-git-svn-md5less-file.sh
+++ b/t/t9112-git-svn-md5less-file.sh
@@ -1,3 +1,5 @@
+#!/bin/sh
+
 test_description='test that git handles an svn repository with missing md5sums'
 
 . ./lib-git-svn.sh
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 902ed41..e1e8bdf 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -55,74 +55,74 @@
 
 test_expect_success 'test ascending revision range' "
 	git reset --hard trunk &&
-	git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r1-r2-r4 -
+	git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2-r4 -
 	"
 
 printf 'r4 \nr2 \nr1 \n' > expected-range-r4-r2-r1
 
 test_expect_success 'test descending revision range' "
 	git reset --hard trunk &&
-	git svn log -r 4:1 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4-r2-r1 -
+	git svn log -r 4:1 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4-r2-r1 -
 	"
 
 printf 'r1 \nr2 \n' > expected-range-r1-r2
 
 test_expect_success 'test ascending revision range with unreachable revision' "
 	git reset --hard trunk &&
-	git svn log -r 1:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r1-r2 -
+	git svn log -r 1:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2 -
 	"
 
 printf 'r2 \nr1 \n' > expected-range-r2-r1
 
 test_expect_success 'test descending revision range with unreachable revision' "
 	git reset --hard trunk &&
-	git svn log -r 3:1 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2-r1 -
+	git svn log -r 3:1 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r2-r1 -
 	"
 
 printf 'r2 \n' > expected-range-r2
 
 test_expect_success 'test ascending revision range with unreachable upper boundary revision and 1 commit' "
 	git reset --hard trunk &&
-	git svn log -r 2:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2 -
+	git svn log -r 2:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r2 -
 	"
 
 test_expect_success 'test descending revision range with unreachable upper boundary revision and 1 commit' "
 	git reset --hard trunk &&
-	git svn log -r 3:2 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2 -
+	git svn log -r 3:2 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r2 -
 	"
 
 printf 'r4 \n' > expected-range-r4
 
 test_expect_success 'test ascending revision range with unreachable lower boundary revision and 1 commit' "
 	git reset --hard trunk &&
-	git svn log -r 3:4 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 -
+	git svn log -r 3:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
 	"
 
 test_expect_success 'test descending revision range with unreachable lower boundary revision and 1 commit' "
 	git reset --hard trunk &&
-	git svn log -r 4:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 -
+	git svn log -r 4:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
 	"
 
 printf -- '------------------------------------------------------------------------\n' > expected-separator
 
 test_expect_success 'test ascending revision range with unreachable boundary revisions and no commits' "
 	git reset --hard trunk &&
-	git svn log -r 5:6 | diff -u expected-separator -
+	git svn log -r 5:6 | test_cmp expected-separator -
 	"
 
 test_expect_success 'test descending revision range with unreachable boundary revisions and no commits' "
 	git reset --hard trunk &&
-	git svn log -r 6:5 | diff -u expected-separator -
+	git svn log -r 6:5 | test_cmp expected-separator -
 	"
 
 test_expect_success 'test ascending revision range with unreachable boundary revisions and 1 commit' "
 	git reset --hard trunk &&
-	git svn log -r 3:5 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 -
+	git svn log -r 3:5 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
 	"
 
 test_expect_success 'test descending revision range with unreachable boundary revisions and 1 commit' "
 	git reset --hard trunk &&
-	git svn log -r 5:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 -
+	git svn log -r 5:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
 	"
 
 test_done
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
new file mode 100755
index 0000000..9a4eabe
--- /dev/null
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Kevin Ballard
+#
+
+test_description='git-svn clone with percent escapes'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' "
+	mkdir project project/trunk project/branches project/tags &&
+	echo foo > project/trunk/foo &&
+	svn import -m '$test_description' project '$svnrepo/pr ject' &&
+	rm -rf project &&
+	start_httpd
+"
+
+if test "$SVN_HTTPD_PORT" = ""
+then
+	test_expect_failure 'test clone with percent escapes - needs SVN_HTTPD_PORT set' 'false'
+else
+	test_expect_success 'test clone with percent escapes' '
+		git svn clone "$svnrepo/pr%20ject" clone &&
+		cd clone &&
+			git rev-parse refs/remotes/git-svn &&
+		cd ..
+	'
+fi
+
+stop_httpd
+
+test_done
diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh
new file mode 100755
index 0000000..5143ed6
--- /dev/null
+++ b/t/t9121-git-svn-fetch-renamed-dir.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Santhosh Kumar Mani
+
+
+test_description='git-svn can fetch renamed directories'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load repository with renamed directory' "
+	svnadmin load -q $rawsvnrepo < ../t9121/renamed-dir.dump
+	"
+
+test_expect_success 'init and fetch repository' "
+	git svn init $svnrepo/newname &&
+	git svn fetch
+	"
+
+test_done
+
diff --git a/t/t9121/renamed-dir.dump b/t/t9121/renamed-dir.dump
new file mode 100644
index 0000000..5f9127b
--- /dev/null
+++ b/t/t9121/renamed-dir.dump
@@ -0,0 +1,90 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 06b9b3ad-f546-4fbe-8328-fcb4e6ef5c3f
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-04-02T09:11:59.778557Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 14
+initial import
+K 10
+svn:author
+V 8
+santhosh
+K 8
+svn:date
+V 27
+2008-04-02T09:13:03.170863Z
+PROPS-END
+
+Node-path: name
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: name/a.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 71
+Text-content-length: 6
+Text-content-md5: b1946ac92492d2347c6235b4d2611184
+Content-length: 77
+
+K 13
+svn:mime-type
+V 10
+text/plain
+K 13
+svn:eol-style
+V 2
+LF
+PROPS-END
+hello
+
+
+Revision-number: 2
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 7
+renamed
+K 10
+svn:author
+V 8
+santhosh
+K 8
+svn:date
+V 27
+2008-04-02T09:14:22.952186Z
+PROPS-END
+
+Node-path: newname
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: name
+
+
+Node-path: name
+Node-action: delete
+
+
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 58c59ed..42b144b 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -37,7 +37,7 @@
 	else
 		printf '%s\n' "$2" | tr '|' '\012' >expected
 	fi
-	diff -u expected actual
+	test_cmp expected actual
 }
 
 test_expect_success \
@@ -257,8 +257,8 @@
       (cd "$GIT_DIR" &&
       GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
       check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
-      diff -u "$CVSWORK/W/file1.txt" ../W/file1.txt &&
-      diff -u "$CVSWORK/W/file2.txt" ../W/file2.txt
+      test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
+      test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
       )
 '
 
@@ -279,9 +279,9 @@
 	git cvsexportcommit -w "$CVSWORK" -c $id &&
 	check_entries "$CVSWORK/E" "DS/1.1/|newfile5.txt/1.1/" &&
 	check_entries "$CVSWORK" "DS/1.1/|release-notes/1.2/" &&
-	diff -u "$CVSWORK/DS" DS &&
-	diff -u "$CVSWORK/E/DS" E/DS &&
-	diff -u "$CVSWORK/release-notes" release-notes
+	test_cmp "$CVSWORK/DS" DS &&
+	test_cmp "$CVSWORK/E/DS" E/DS &&
+	test_cmp "$CVSWORK/release-notes" release-notes
 
 '
 
@@ -293,7 +293,7 @@
 	id=$(git rev-parse HEAD) &&
 	git cvsexportcommit -w "$CVSWORK" -c $id &&
 	check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" &&
-	diff -u "$CVSWORK/ space" " space"
+	test_cmp "$CVSWORK/ space" " space"
 
 '
 
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 142d42f..c4f4465 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -165,9 +165,9 @@
 M 755 0000000000000000000000000000000000000001 zero1
 
 INPUT_END
-test_expect_failure \
-    'B: fail on invalid blob sha1' \
-    'git-fast-import <input'
+test_expect_success 'B: fail on invalid blob sha1' '
+    ! git-fast-import <input
+'
 rm -f .git/objects/pack_* .git/objects/index_*
 
 cat >input <<INPUT_END
@@ -180,9 +180,9 @@
 from refs/heads/master
 
 INPUT_END
-test_expect_failure \
-    'B: fail on invalid branch name ".badbranchname"' \
-    'git-fast-import <input'
+test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
+    ! git-fast-import <input
+'
 rm -f .git/objects/pack_* .git/objects/index_*
 
 cat >input <<INPUT_END
@@ -195,9 +195,9 @@
 from refs/heads/master
 
 INPUT_END
-test_expect_failure \
-    'B: fail on invalid branch name "bad[branch]name"' \
-    'git-fast-import <input'
+test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
+    ! git-fast-import <input
+'
 rm -f .git/objects/pack_* .git/objects/index_*
 
 cat >input <<INPUT_END
@@ -339,9 +339,9 @@
 from refs/heads/branch^0
 
 INPUT_END
-test_expect_failure \
-    'E: rfc2822 date, --date-format=raw' \
-    'git-fast-import --date-format=raw <input'
+test_expect_success 'E: rfc2822 date, --date-format=raw' '
+    ! git-fast-import --date-format=raw <input
+'
 test_expect_success \
     'E: rfc2822 date, --date-format=rfc2822' \
     'git-fast-import --date-format=rfc2822 <input'
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 75d1ce4..166b43f 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -54,7 +54,7 @@
 test_expect_success 'basic checkout' \
   'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
    test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/"
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | tail -n 1))" = "secondrootfile/1.1/"'
+   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
 
 #------------------------
 # PSERVER AUTHENTICATION
@@ -94,7 +94,7 @@
 
 test_expect_success 'pserver authentication' \
   'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'pserver authentication failure (non-anonymous user)' \
   'if cat request-git | git-cvsserver pserver >log 2>&1
@@ -103,11 +103,11 @@
    else
        true
    fi &&
-   tail -n1 log | grep -q "^I HATE YOU$"'
+   sed -ne \$p log | grep "^I HATE YOU$"'
 
 test_expect_success 'pserver authentication (login)' \
   'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
   'if cat login-git | git-cvsserver pserver >log 2>&1
@@ -116,7 +116,7 @@
    else
        true
    fi &&
-   tail -n1 log | grep -q "^I HATE YOU$"'
+   sed -ne \$p log | grep "^I HATE YOU$"'
 
 
 # misuse pserver authentication for testing of req_Root
@@ -146,25 +146,29 @@
    else
        true
    fi &&
-   tail log | grep -q "^error 1 Root must be an absolute pathname$"'
+   tail log | grep "^error 1 Root must be an absolute pathname$"'
 
 test_expect_success 'req_Root failure (conflicting roots)' \
   'cat request-conflict | git-cvsserver pserver >log 2>&1 &&
-   tail log | grep -q "^error 1 Conflicting roots specified$"'
+   tail log | grep "^error 1 Conflicting roots specified$"'
 
 test_expect_success 'req_Root (strict paths)' \
   'cat request-anonymous | git-cvsserver --strict-paths pserver $SERVERDIR >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (strict-paths)' \
-  'cat request-anonymous | git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1'
+test_expect_success 'req_Root failure (strict-paths)' '
+    ! cat request-anonymous |
+    git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1
+'
 
 test_expect_success 'req_Root (w/o strict-paths)' \
   'cat request-anonymous | git-cvsserver pserver $WORKDIR/ >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (w/o strict-paths)' \
-  'cat request-anonymous | git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1'
+test_expect_success 'req_Root failure (w/o strict-paths)' '
+    ! cat request-anonymous |
+    git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1
+'
 
 cat >request-base  <<EOF
 BEGIN AUTH REQUEST
@@ -177,24 +181,25 @@
 
 test_expect_success 'req_Root (base-path)' \
   'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (base-path)' \
-  'cat request-anonymous | git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1'
+test_expect_success 'req_Root failure (base-path)' '
+    ! cat request-anonymous |
+    git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1
+'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
 test_expect_success 'req_Root (export-all)' \
   'cat request-anonymous | git-cvsserver --export-all pserver $WORKDIR >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
-test_expect_failure 'req_Root failure (export-all w/o whitelist)' \
-  'cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 ||
-   false'
+test_expect_success 'req_Root failure (export-all w/o whitelist)' \
+  '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' \
   'cat request-base | git-cvsserver --export-all --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
-   tail -n1 log | grep -q "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU$"'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
 
@@ -211,7 +216,7 @@
    else
      true
    fi &&
-   cat cvs.log | grep -q "GITCVS emulation disabled" &&
+   grep "GITCVS emulation disabled" cvs.log &&
    test ! -d cvswork2'
 
 rm -fr cvswork2
@@ -232,7 +237,7 @@
    else
      true
    fi &&
-   cat cvs.log | grep -q "GITCVS emulation disabled" &&
+   grep "GITCVS emulation disabled" cvs.log &&
    test ! -d cvswork2'
 
 rm -fr cvswork2
@@ -290,15 +295,16 @@
 
 cd "$WORKDIR"
 #TODO: cvsserver doesn't support update w/o -d
-test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" \
-  'mkdir test &&
+test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" '
+   mkdir test &&
    echo >test/empty &&
    git add test &&
    git commit -q -m "Single Subdirectory" &&
    git push gitcvs.git >/dev/null &&
    cd cvswork &&
    GIT_CONFIG="$git_config" cvs -Q update &&
-   test ! -d test'
+   test ! -d test
+'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (subdirectories)' \
@@ -414,4 +420,54 @@
     GIT_CONFIG="$git_config" cvs -Q update &&
     diff -q merge ../merge'
 
+cd "$WORKDIR"
+test_expect_success 'cvs update (-p)' '
+    touch really-empty &&
+    echo Line 1 > no-lf &&
+    echo -n Line 2 >> no-lf &&
+    git add really-empty no-lf &&
+    git commit -q -m "Update -p test" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs update &&
+    rm -f failures &&
+    for i in merge no-lf empty really-empty; do
+        GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out
+        diff $i.out ../$i >>failures 2>&1
+    done &&
+    test -z "$(cat failures)"
+'
+
+#------------
+# CVS STATUS
+#------------
+
+cd "$WORKDIR"
+test_expect_success 'cvs status' '
+    mkdir status.dir &&
+    echo Line > status.dir/status.file &&
+    echo Line > status.file &&
+    git add status.dir status.file &&
+    git commit -q -m "Status test" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs update &&
+    GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
+    test $(wc -l <../out) = 2
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs status (nonrecursive)' '
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
+    test $(wc -l <../out) = 1
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs status (no subdirs in header)' '
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
+    ! grep / <../out
+'
+
 test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 796cd7d..061a259 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -483,6 +483,22 @@
 	'gitweb_run "p=.git;a=history;f=file"'
 test_debug 'cat gitweb.log'
 
+test_expect_success \
+	'logs: history (implicit HEAD, non-existent file)' \
+	'gitweb_run "p=.git;a=history;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+	'logs: history (implicit HEAD, deleted file)' \
+	'git checkout master &&
+	 echo "to be deleted" > deleted_file &&
+	 git add deleted_file &&
+	 git commit -m "Add file to be deleted" &&
+	 git rm deleted_file &&
+	 git commit -m "Delete file" &&
+	 gitweb_run "p=.git;a=history;f=deleted_file"'
+test_debug 'cat gitweb.log'
+
 # ----------------------------------------------------------------------
 # feed generation
 
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 7706430..00a74ee 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -3,6 +3,12 @@
 test_description='git-cvsimport basic tests'
 . ./test-lib.sh
 
+CVSROOT=$(pwd)/cvsroot
+export CVSROOT
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
 if ! type cvs >/dev/null 2>&1
 then
 	say 'skipping cvsimport tests, cvs not found'
@@ -26,12 +32,6 @@
 	;;
 esac
 
-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' '
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 44f5776..7c2a8ba 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -42,6 +42,7 @@
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR VISUAL
+GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
 
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
@@ -86,7 +87,7 @@
 	-q|--q|--qu|--qui|--quie|--quiet)
 		quiet=t; shift ;;
 	--no-color)
-	    color=; shift ;;
+		color=; shift ;;
 	--no-python)
 		# noop now...
 		shift ;;
@@ -149,8 +150,15 @@
 
 test_failure=0
 test_count=0
+test_fixed=0
+test_broken=0
 
-trap 'echo >&5 "FATAL: Unexpected exit with code $?"; exit 1' exit
+die () {
+	echo >&5 "FATAL: Unexpected exit with code $?"
+	exit 1
+}
+
+trap 'die' exit
 
 test_tick () {
 	if test -z "${test_tick+set}"
@@ -181,6 +189,17 @@
 	test "$immediate" = "" || { trap - exit; exit 1; }
 }
 
+test_known_broken_ok_ () {
+	test_count=$(expr "$test_count" + 1)
+	test_fixed=$(($test_fixed+1))
+	say_color "" "  FIXED $test_count: $@"
+}
+
+test_known_broken_failure_ () {
+	test_count=$(expr "$test_count" + 1)
+	test_broken=$(($test_broken+1))
+	say_color skip "  still broken $test_count: $@"
+}
 
 test_debug () {
 	test "$debug" = "" || eval "$1"
@@ -221,13 +240,13 @@
 	error "bug in the test script: not 2 parameters to test-expect-failure"
 	if ! test_skip "$@"
 	then
-		say >&3 "expecting failure: $2"
+		say >&3 "checking known breakage: $2"
 		test_run_ "$2"
-		if [ "$?" = 0 -a "$eval_ret" != 0 -a "$eval_ret" -lt 129 ]
+		if [ "$?" = 0 -a "$eval_ret" = 0 ]
 		then
-			test_ok_ "$1"
+			test_known_broken_ok_ "$1"
 		else
-			test_failure_ "$@"
+		    test_known_broken_failure_ "$1"
 		fi
 	fi
 	echo >&3 ""
@@ -281,7 +300,24 @@
 
 test_must_fail () {
 	"$@"
-	test $? -gt 0 -a $? -le 128
+	test $? -gt 0 -a $? -le 129
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+#	test_expect_success 'foo works' '
+#		echo expected >expected &&
+#		foo >actual &&
+#		test_cmp expected actual
+#	'
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+	$GIT_TEST_CMP "$@"
 }
 
 # Most tests can use the created repository, but some may need to create more.
@@ -301,6 +337,18 @@
 
 test_done () {
 	trap - exit
+
+	if test "$test_fixed" != 0
+	then
+		say_color pass "fixed $test_fixed known breakage(s)"
+	fi
+	if test "$test_broken" != 0
+	then
+		say_color error "still have $test_broken known breakage(s)"
+		msg="remaining $(($test_count-$test_broken)) test(s)"
+	else
+		msg="$test_count test(s)"
+	fi
 	case "$test_failure" in
 	0)
 		# We could:
@@ -311,11 +359,11 @@
 		# The Makefile provided will clean this test area so
 		# we will leave things as they are.
 
-		say_color pass "passed all $test_count test(s)"
+		say_color pass "passed all $msg"
 		exit 0 ;;
 
 	*)
-		say_color error "failed $test_failure among $test_count test(s)"
+		say_color error "failed $test_failure among $msg"
 		exit 1 ;;
 
 	esac
@@ -326,8 +374,11 @@
 PATH=$(pwd)/..:$PATH
 GIT_EXEC_PATH=$(pwd)/..
 GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
-GIT_CONFIG=.git/config
-export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG
+unset GIT_CONFIG
+unset GIT_CONFIG_LOCAL
+GIT_CONFIG_NOSYSTEM=1
+GIT_CONFIG_NOGLOBAL=1
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
 
 GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
 export GITPERLLIB
@@ -341,9 +392,16 @@
 	exit 1
 fi
 
+. ../GIT-BUILD-OPTIONS
+
 # Test repository
 test=trash
-rm -fr "$test"
+rm -fr "$test" || {
+	trap - exit
+	echo >&5 "FATAL: Cannot prepare test area"
+	exit 1
+}
+
 test_create_repo $test
 cd "$test"
 
diff --git a/tag.c b/tag.c
index 38bf913..4470d2b 100644
--- a/tag.c
+++ b/tag.c
@@ -9,7 +9,10 @@
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
 {
 	while (o && o->type == OBJ_TAG)
-		o = parse_object(((struct tag *)o)->tagged->sha1);
+		if (((struct tag *)o)->tagged)
+			o = parse_object(((struct tag *)o)->tagged->sha1);
+		else
+			o = NULL;
 	if (!o && warn) {
 		if (!warnlen)
 			warnlen = strlen(warn);
@@ -84,12 +87,6 @@
 		item->tagged = NULL;
 	}
 
-	if (item->tagged && track_object_refs) {
-		struct object_refs *refs = alloc_object_refs(1);
-		refs->ref[0] = item->tagged;
-		set_object_refs(&item->object, refs);
-	}
-
 	return 0;
 }
 
diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg
index c5cdb9d..4ef86eb 100644
--- a/templates/hooks--commit-msg
+++ b/templates/hooks--commit-msg
@@ -9,6 +9,9 @@
 # To enable this hook, make this file executable.
 
 # Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
 # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
 # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
 
diff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg
new file mode 100644
index 0000000..ff0f42a
--- /dev/null
+++ b/templates/hooks--prepare-commit-msg
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by git-commit with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source.  The hook's purpose is to edit the commit
+# message file.  If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, make this file executable.
+
+# This hook includes three examples.  The first comments out the
+# "Conflicts:" part of a merge commit.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output.  It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited.  This is rarely a good idea.
+
+case "$2 $3" in
+  merge)
+    sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;;
+
+# ""|template)
+#   perl -i -pe '
+#      print "\n" . `git diff --cached --name-status -r`
+#	 if /^#/ && $first++ == 0' "$1" ;;
+
+  *) ;;
+esac
+
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
diff --git a/test-parse-options.c b/test-parse-options.c
index eed8a02..73360d7 100644
--- a/test-parse-options.c
+++ b/test-parse-options.c
@@ -20,6 +20,8 @@
 		OPT_STRING(0, "string2", &string, "str", "get another string"),
 		OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
 		OPT_STRING('o', NULL, &string, "str", "get another string"),
+		OPT_GROUP("magic arguments"),
+		OPT_ARGUMENT("quux", "means --quux"),
 		OPT_END(),
 	};
 	int i;
diff --git a/test-sha1.sh b/test-sha1.sh
index bf526c8..0f0bc5d 100755
--- a/test-sha1.sh
+++ b/test-sha1.sh
@@ -10,7 +10,7 @@
 		{
 			test -z "$pfx" || echo "$pfx"
 			dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
-			tr '\000' 'g'
+			perl -pe 'y/\000/g/'
 		} | ./test-sha1 $cnt
 	`
 	if test "$expect" = "$actual"
@@ -55,7 +55,7 @@
 		{
 			test -z "$pfx" || echo "$pfx"
 			dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
-			tr '\000' 'g'
+			perl -pe 'y/\000/g/'
 		} | sha1sum |
 		sed -e 's/ .*//'
 	`
diff --git a/thread-utils.c b/thread-utils.c
new file mode 100644
index 0000000..55e7e29
--- /dev/null
+++ b/thread-utils.c
@@ -0,0 +1,48 @@
+#include "cache.h"
+
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include <windows.h>
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+#  include <sys/pstat.h>
+#endif
+
+/*
+ * By doing this in two steps we can at least get
+ * the function to be somewhat coherent, even
+ * with this disgusting nest of #ifdefs.
+ */
+#ifndef _SC_NPROCESSORS_ONLN
+#  ifdef _SC_NPROC_ONLN
+#    define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
+#  elif defined _SC_CRAY_NCPU
+#    define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
+#  endif
+#endif
+
+int online_cpus(void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+	long ncpus;
+#endif
+
+#ifdef _WIN32
+	SYSTEM_INFO info;
+	GetSystemInfo(&info);
+
+	if ((int)info.dwNumberOfProcessors > 0)
+		return (int)info.dwNumberOfProcessors;
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+	struct pst_dynamic psd;
+
+	if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
+		return (int)psd.psd_proc_cnt;
+#endif
+
+#ifdef _SC_NPROCESSORS_ONLN
+	if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
+		return (int)ncpus;
+#endif
+
+	return 1;
+}
diff --git a/thread-utils.h b/thread-utils.h
new file mode 100644
index 0000000..cce4b77
--- /dev/null
+++ b/thread-utils.h
@@ -0,0 +1,6 @@
+#ifndef THREAD_COMPAT_H
+#define THREAD_COMPAT_H
+
+extern int online_cpus(void);
+
+#endif /* THREAD_COMPAT_H */
diff --git a/transport.c b/transport.c
index 97c59dc..393e0e8 100644
--- a/transport.c
+++ b/transport.c
@@ -560,7 +560,10 @@
 struct git_transport_data {
 	unsigned thin : 1;
 	unsigned keep : 1;
+	unsigned followtags : 1;
 	int depth;
+	struct child_process *conn;
+	int fd[2];
 	const char *uploadpack;
 	const char *receivepack;
 };
@@ -578,6 +581,9 @@
 	} else if (!strcmp(name, TRANS_OPT_THIN)) {
 		data->thin = !!value;
 		return 0;
+	} else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
+		data->followtags = !!value;
+		return 0;
 	} else if (!strcmp(name, TRANS_OPT_KEEP)) {
 		data->keep = !!value;
 		return 0;
@@ -591,20 +597,20 @@
 	return 1;
 }
 
+static int connect_setup(struct transport *transport)
+{
+	struct git_transport_data *data = transport->data;
+	data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);
+	return 0;
+}
+
 static struct ref *get_refs_via_connect(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
 	struct ref *refs;
-	int fd[2];
-	char *dest = xstrdup(transport->url);
-	struct child_process *conn = git_connect(fd, dest, data->uploadpack, 0);
 
-	get_remote_heads(fd[0], &refs, 0, NULL, 0);
-	packet_flush(fd[1]);
-
-	finish_connect(conn);
-
-	free(dest);
+	connect_setup(transport);
+	get_remote_heads(data->fd[0], &refs, 0, NULL, 0);
 
 	return refs;
 }
@@ -615,28 +621,44 @@
 	struct git_transport_data *data = transport->data;
 	char **heads = xmalloc(nr_heads * sizeof(*heads));
 	char **origh = xmalloc(nr_heads * sizeof(*origh));
-	struct ref *refs;
+	const struct ref *refs;
 	char *dest = xstrdup(transport->url);
 	struct fetch_pack_args args;
 	int i;
+	struct ref *refs_tmp = NULL;
 
 	memset(&args, 0, sizeof(args));
 	args.uploadpack = data->uploadpack;
 	args.keep_pack = data->keep;
 	args.lock_pack = 1;
 	args.use_thin_pack = data->thin;
+	args.include_tag = data->followtags;
 	args.verbose = transport->verbose > 0;
 	args.depth = data->depth;
 
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
-	refs = fetch_pack(&args, dest, nr_heads, heads, &transport->pack_lockfile);
+
+	if (!data->conn) {
+		connect_setup(transport);
+		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0);
+	}
+
+	refs = fetch_pack(&args, data->fd, data->conn,
+			  refs_tmp ? refs_tmp : transport->remote_refs,
+			  dest, nr_heads, heads, &transport->pack_lockfile);
+	close(data->fd[0]);
+	close(data->fd[1]);
+	if (finish_connect(data->conn))
+		refs = NULL;
+	data->conn = NULL;
+
+	free_refs(refs_tmp);
 
 	for (i = 0; i < nr_heads; i++)
 		free(origh[i]);
 	free(origh);
 	free(heads);
-	free_refs(refs);
 	free(dest);
 	return (refs ? 0 : -1);
 }
@@ -659,7 +681,15 @@
 
 static int disconnect_git(struct transport *transport)
 {
-	free(transport->data);
+	struct git_transport_data *data = transport->data;
+	if (data->conn) {
+		packet_flush(data->fd[1]);
+		close(data->fd[0]);
+		close(data->fd[1]);
+		finish_connect(data->conn);
+	}
+
+	free(data);
 	return 0;
 }
 
@@ -719,6 +749,7 @@
 		ret->disconnect = disconnect_git;
 
 		data->thin = 1;
+		data->conn = NULL;
 		data->uploadpack = "git-upload-pack";
 		if (remote && remote->uploadpack)
 			data->uploadpack = remote->uploadpack;
diff --git a/transport.h b/transport.h
index 6fb4526..8abfc0a 100644
--- a/transport.h
+++ b/transport.h
@@ -53,6 +53,9 @@
 /* Limit the depth of the fetch if not null */
 #define TRANS_OPT_DEPTH "depth"
 
+/* Aggressively fetch annotated tags if possible */
+#define TRANS_OPT_FOLLOWTAGS "followtags"
+
 /**
  * Returns 0 if the option was used, non-zero otherwise. Prints a
  * message to stderr if the option is not used.
diff --git a/tree-walk.c b/tree-walk.c
index 142205d..02e2aed 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -62,7 +62,7 @@
 
 static int entry_compare(struct name_entry *a, struct name_entry *b)
 {
-	return base_name_compare(
+	return df_name_compare(
 			a->path, tree_entry_len(a->path, a->sha1), a->mode,
 			b->path, tree_entry_len(b->path, b->sha1), b->mode);
 }
@@ -104,12 +104,48 @@
 	return 1;
 }
 
-void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback)
+void setup_traverse_info(struct traverse_info *info, const char *base)
 {
+	int pathlen = strlen(base);
+	static struct traverse_info dummy;
+
+	memset(info, 0, sizeof(*info));
+	if (pathlen && base[pathlen-1] == '/')
+		pathlen--;
+	info->pathlen = pathlen ? pathlen + 1 : 0;
+	info->name.path = base;
+	info->name.sha1 = (void *)(base + pathlen + 1);
+	if (pathlen)
+		info->prev = &dummy;
+}
+
+char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
+{
+	int len = tree_entry_len(n->path, n->sha1);
+	int pathlen = info->pathlen;
+
+	path[pathlen + len] = 0;
+	for (;;) {
+		memcpy(path + pathlen, n->path, len);
+		if (!pathlen)
+			break;
+		path[--pathlen] = '/';
+		n = &info->name;
+		len = tree_entry_len(n->path, n->sha1);
+		info = info->prev;
+		pathlen -= len;
+	}
+	return path;
+}
+
+int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
+{
+	int ret = 0;
 	struct name_entry *entry = xmalloc(n*sizeof(*entry));
 
 	for (;;) {
 		unsigned long mask = 0;
+		unsigned long dirmask = 0;
 		int i, last;
 
 		last = -1;
@@ -134,25 +170,35 @@
 					mask = 0;
 			}
 			mask |= 1ul << i;
+			if (S_ISDIR(entry[i].mode))
+				dirmask |= 1ul << i;
 			last = i;
 		}
 		if (!mask)
 			break;
+		dirmask &= mask;
 
 		/*
-		 * Update the tree entries we've walked, and clear
-		 * all the unused name-entries.
+		 * Clear all the unused name-entries.
 		 */
 		for (i = 0; i < n; i++) {
-			if (mask & (1ul << i)) {
-				update_tree_entry(t+i);
+			if (mask & (1ul << i))
 				continue;
-			}
 			entry_clear(entry + i);
 		}
-		callback(n, mask, entry, base);
+		ret = info->fn(n, mask, dirmask, entry, info);
+		if (ret < 0)
+			break;
+		if (ret)
+			mask &= ret;
+		ret = 0;
+		for (i = 0; i < n; i++) {
+			if (mask & (1ul << i))
+				update_tree_entry(t + i);
+		}
 	}
 	free(entry);
+	return ret;
 }
 
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
diff --git a/tree-walk.h b/tree-walk.h
index db0fbdc..42110a4 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -33,10 +33,27 @@
 
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
 
-typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry *entry, const char *base);
+struct traverse_info;
+typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
+int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
 
-void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
+struct traverse_info {
+	struct traverse_info *prev;
+	struct name_entry name;
+	int pathlen;
+
+	unsigned long conflicts;
+	traverse_callback_t fn;
+	void *data;
+};
 
 int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
+extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
+extern void setup_traverse_info(struct traverse_info *info, const char *base);
+
+static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
+{
+	return info->pathlen + tree_entry_len(n->path, n->sha1);
+}
 
 #endif
diff --git a/tree.c b/tree.c
index 8c0819f..4b1825c 100644
--- a/tree.c
+++ b/tree.c
@@ -142,8 +142,8 @@
 
 	ce1 = *((const struct cache_entry **)a_);
 	ce2 = *((const struct cache_entry **)b_);
-	return cache_name_compare(ce1->name, ntohs(ce1->ce_flags),
-				  ce2->name, ntohs(ce2->ce_flags));
+	return cache_name_compare(ce1->name, ce1->ce_flags,
+				  ce2->name, ce2->ce_flags);
 }
 
 int read_tree(struct tree *tree, int stage, const char **match)
@@ -202,52 +202,6 @@
 	return (struct tree *) obj;
 }
 
-/*
- * NOTE! Tree refs to external git repositories
- * (ie gitlinks) do not count as real references.
- *
- * You don't have to have those repositories
- * available at all, much less have the objects
- * accessible from the current repository.
- */
-static void track_tree_refs(struct tree *item)
-{
-	int n_refs = 0, i;
-	struct object_refs *refs;
-	struct tree_desc desc;
-	struct name_entry entry;
-
-	/* Count how many entries there are.. */
-	init_tree_desc(&desc, item->buffer, item->size);
-	while (tree_entry(&desc, &entry)) {
-		if (S_ISGITLINK(entry.mode))
-			continue;
-		n_refs++;
-	}
-
-	/* Allocate object refs and walk it again.. */
-	i = 0;
-	refs = alloc_object_refs(n_refs);
-	init_tree_desc(&desc, item->buffer, item->size);
-	while (tree_entry(&desc, &entry)) {
-		struct object *obj;
-
-		if (S_ISGITLINK(entry.mode))
-			continue;
-		if (S_ISDIR(entry.mode))
-			obj = &lookup_tree(entry.sha1)->object;
-		else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
-			obj = &lookup_blob(entry.sha1)->object;
-		else {
-			warning("in tree %s: entry %s has bad mode %.6o\n",
-			     sha1_to_hex(item->object.sha1), entry.path, entry.mode);
-			obj = lookup_unknown_object(entry.sha1);
-		}
-		refs->ref[i++] = obj;
-	}
-	set_object_refs(&item->object, refs);
-}
-
 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
 {
 	if (item->object.parsed)
@@ -256,8 +210,6 @@
 	item->buffer = buffer;
 	item->size = size;
 
-	if (track_object_refs)
-		track_tree_refs(item);
 	return 0;
 }
 
diff --git a/unpack-trees.c b/unpack-trees.c
index aa2513e..a59f475 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 #include "dir.h"
 #include "tree.h"
@@ -7,251 +8,18 @@
 #include "progress.h"
 #include "refs.h"
 
-#define DBRT_DEBUG 1
-
-struct tree_entry_list {
-	struct tree_entry_list *next;
-	unsigned int mode;
-	const char *name;
-	const unsigned char *sha1;
-};
-
-static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
+static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+	unsigned int set, unsigned int clear)
 {
-	struct name_entry one;
-	struct tree_entry_list *ret = NULL;
-	struct tree_entry_list **list_p = &ret;
+	unsigned int size = ce_size(ce);
+	struct cache_entry *new = xmalloc(size);
 
-	while (tree_entry(desc, &one)) {
-		struct tree_entry_list *entry;
+	clear |= CE_HASHED | CE_UNHASHED;
 
-		entry = xmalloc(sizeof(struct tree_entry_list));
-		entry->name = one.path;
-		entry->sha1 = one.sha1;
-		entry->mode = one.mode;
-		entry->next = NULL;
-
-		*list_p = entry;
-		list_p = &entry->next;
-	}
-	return ret;
-}
-
-static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
-{
-	int len1 = strlen(name1);
-	int len2 = strlen(name2);
-	int len = len1 < len2 ? len1 : len2;
-	int ret = memcmp(name1, name2, len);
-	unsigned char c1, c2;
-	if (ret)
-		return ret;
-	c1 = name1[len];
-	c2 = name2[len];
-	if (!c1 && dir1)
-		c1 = '/';
-	if (!c2 && dir2)
-		c2 = '/';
-	ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-	if (c1 && c2 && !ret)
-		ret = len1 - len2;
-	return ret;
-}
-
-static inline void remove_entry(int remove)
-{
-	if (remove >= 0)
-		remove_cache_entry_at(remove);
-}
-
-static int unpack_trees_rec(struct tree_entry_list **posns, int len,
-			    const char *base, struct unpack_trees_options *o,
-			    struct tree_entry_list *df_conflict_list)
-{
-	int remove;
-	int baselen = strlen(base);
-	int src_size = len + 1;
-	int retval = 0;
-
-	do {
-		int i;
-		const char *first;
-		int firstdir = 0;
-		int pathlen;
-		unsigned ce_size;
-		struct tree_entry_list **subposns;
-		struct cache_entry **src;
-		int any_files = 0;
-		int any_dirs = 0;
-		char *cache_name;
-		int ce_stage;
-
-		/* Find the first name in the input. */
-
-		first = NULL;
-		cache_name = NULL;
-
-		/* Check the cache */
-		if (o->merge && o->pos < active_nr) {
-			/* This is a bit tricky: */
-			/* If the index has a subdirectory (with
-			 * contents) as the first name, it'll get a
-			 * filename like "foo/bar". But that's after
-			 * "foo", so the entry in trees will get
-			 * handled first, at which point we'll go into
-			 * "foo", and deal with "bar" from the index,
-			 * because the base will be "foo/". The only
-			 * way we can actually have "foo/bar" first of
-			 * all the things is if the trees don't
-			 * contain "foo" at all, in which case we'll
-			 * handle "foo/bar" without going into the
-			 * directory, but that's fine (and will return
-			 * an error anyway, with the added unknown
-			 * file case.
-			 */
-
-			cache_name = active_cache[o->pos]->name;
-			if (strlen(cache_name) > baselen &&
-			    !memcmp(cache_name, base, baselen)) {
-				cache_name += baselen;
-				first = cache_name;
-			} else {
-				cache_name = NULL;
-			}
-		}
-
-#if DBRT_DEBUG > 1
-		if (first)
-			printf("index %s\n", first);
-#endif
-		for (i = 0; i < len; i++) {
-			if (!posns[i] || posns[i] == df_conflict_list)
-				continue;
-#if DBRT_DEBUG > 1
-			printf("%d %s\n", i + 1, posns[i]->name);
-#endif
-			if (!first || entcmp(first, firstdir,
-					     posns[i]->name,
-					     S_ISDIR(posns[i]->mode)) > 0) {
-				first = posns[i]->name;
-				firstdir = S_ISDIR(posns[i]->mode);
-			}
-		}
-		/* No name means we're done */
-		if (!first)
-			goto leave_directory;
-
-		pathlen = strlen(first);
-		ce_size = cache_entry_size(baselen + pathlen);
-
-		src = xcalloc(src_size, sizeof(struct cache_entry *));
-
-		subposns = xcalloc(len, sizeof(struct tree_list_entry *));
-
-		remove = -1;
-		if (cache_name && !strcmp(cache_name, first)) {
-			any_files = 1;
-			src[0] = active_cache[o->pos];
-			remove = o->pos;
-		}
-
-		for (i = 0; i < len; i++) {
-			struct cache_entry *ce;
-
-			if (!posns[i] ||
-			    (posns[i] != df_conflict_list &&
-			     strcmp(first, posns[i]->name))) {
-				continue;
-			}
-
-			if (posns[i] == df_conflict_list) {
-				src[i + o->merge] = o->df_conflict_entry;
-				continue;
-			}
-
-			if (S_ISDIR(posns[i]->mode)) {
-				struct tree *tree = lookup_tree(posns[i]->sha1);
-				struct tree_desc t;
-				any_dirs = 1;
-				parse_tree(tree);
-				init_tree_desc(&t, tree->buffer, tree->size);
-				subposns[i] = create_tree_entry_list(&t);
-				posns[i] = posns[i]->next;
-				src[i + o->merge] = o->df_conflict_entry;
-				continue;
-			}
-
-			if (!o->merge)
-				ce_stage = 0;
-			else if (i + 1 < o->head_idx)
-				ce_stage = 1;
-			else if (i + 1 > o->head_idx)
-				ce_stage = 3;
-			else
-				ce_stage = 2;
-
-			ce = xcalloc(1, ce_size);
-			ce->ce_mode = create_ce_mode(posns[i]->mode);
-			ce->ce_flags = create_ce_flags(baselen + pathlen,
-						       ce_stage);
-			memcpy(ce->name, base, baselen);
-			memcpy(ce->name + baselen, first, pathlen + 1);
-
-			any_files = 1;
-
-			hashcpy(ce->sha1, posns[i]->sha1);
-			src[i + o->merge] = ce;
-			subposns[i] = df_conflict_list;
-			posns[i] = posns[i]->next;
-		}
-		if (any_files) {
-			if (o->merge) {
-				int ret;
-
-#if DBRT_DEBUG > 1
-				printf("%s:\n", first);
-				for (i = 0; i < src_size; i++) {
-					printf(" %d ", i);
-					if (src[i])
-						printf("%s\n", sha1_to_hex(src[i]->sha1));
-					else
-						printf("\n");
-				}
-#endif
-				ret = o->fn(src, o, remove);
-
-#if DBRT_DEBUG > 1
-				printf("Added %d entries\n", ret);
-#endif
-				o->pos += ret;
-			} else {
-				remove_entry(remove);
-				for (i = 0; i < src_size; i++) {
-					if (src[i]) {
-						add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-					}
-				}
-			}
-		}
-		if (any_dirs) {
-			char *newbase = xmalloc(baselen + 2 + pathlen);
-			memcpy(newbase, base, baselen);
-			memcpy(newbase + baselen, first, pathlen);
-			newbase[baselen + pathlen] = '/';
-			newbase[baselen + pathlen + 1] = '\0';
-			if (unpack_trees_rec(subposns, len, newbase, o,
-					     df_conflict_list)) {
-				retval = -1;
-				goto leave_directory;
-			}
-			free(newbase);
-		}
-		free(subposns);
-		free(src);
-	} while (1);
-
- leave_directory:
-	return retval;
+	memcpy(new, ce, size);
+	new->next = NULL;
+	new->ce_flags = (new->ce_flags & ~clear) | set;
+	add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
 }
 
 /* Unlink the last component and attempt to remove leading
@@ -286,63 +54,292 @@
 }
 
 static struct checkout state;
-static void check_updates(struct cache_entry **src, int nr,
-			struct unpack_trees_options *o)
+static int check_updates(struct unpack_trees_options *o)
 {
-	unsigned short mask = htons(CE_UPDATE);
 	unsigned cnt = 0, total = 0;
 	struct progress *progress = NULL;
 	char last_symlink[PATH_MAX];
+	struct index_state *index = &o->result;
+	int i;
+	int errs = 0;
 
 	if (o->update && o->verbose_update) {
-		for (total = cnt = 0; cnt < nr; cnt++) {
-			struct cache_entry *ce = src[cnt];
-			if (!ce->ce_mode || ce->ce_flags & mask)
+		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
+			struct cache_entry *ce = index->cache[cnt];
+			if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
 				total++;
 		}
 
 		progress = start_progress_delay("Checking out files",
-						total, 50, 2);
+						total, 50, 1);
 		cnt = 0;
 	}
 
 	*last_symlink = '\0';
-	while (nr--) {
-		struct cache_entry *ce = *src++;
+	for (i = 0; i < index->cache_nr; i++) {
+		struct cache_entry *ce = index->cache[i];
 
-		if (!ce->ce_mode || ce->ce_flags & mask)
+		if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
 			display_progress(progress, ++cnt);
-		if (!ce->ce_mode) {
+		if (ce->ce_flags & CE_REMOVE) {
 			if (o->update)
 				unlink_entry(ce->name, last_symlink);
+			remove_index_entry_at(&o->result, i);
+			i--;
 			continue;
 		}
-		if (ce->ce_flags & mask) {
-			ce->ce_flags &= ~mask;
+		if (ce->ce_flags & CE_UPDATE) {
+			ce->ce_flags &= ~CE_UPDATE;
 			if (o->update) {
-				checkout_entry(ce, &state, NULL);
+				errs |= checkout_entry(ce, &state, NULL);
 				*last_symlink = '\0';
 			}
 		}
 	}
 	stop_progress(&progress);
+	return errs != 0;
+}
+
+static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
+{
+	int ret = o->fn(src, o);
+	if (ret > 0)
+		ret = 0;
+	return ret;
+}
+
+static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+	struct cache_entry *src[5] = { ce, };
+
+	o->pos++;
+	if (ce_stage(ce)) {
+		if (o->skip_unmerged) {
+			add_entry(o, ce, 0, 0);
+			return 0;
+		}
+	}
+	return call_unpack_fn(src, o);
+}
+
+int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
+{
+	int i;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct traverse_info newinfo;
+	struct name_entry *p;
+
+	p = names;
+	while (!p->mode)
+		p++;
+
+	newinfo = *info;
+	newinfo.prev = info;
+	newinfo.name = *p;
+	newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1;
+	newinfo.conflicts |= df_conflicts;
+
+	for (i = 0; i < n; i++, dirmask >>= 1) {
+		const unsigned char *sha1 = NULL;
+		if (dirmask & 1)
+			sha1 = names[i].sha1;
+		fill_tree_descriptor(t+i, sha1);
+	}
+	return traverse_trees(n, t, &newinfo);
+}
+
+/*
+ * Compare the traverse-path to the cache entry without actually
+ * having to generate the textual representation of the traverse
+ * path.
+ *
+ * NOTE! This *only* compares up to the size of the traverse path
+ * itself - the caller needs to do the final check for the cache
+ * entry having more data at the end!
+ */
+static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+{
+	int len, pathlen, ce_len;
+	const char *ce_name;
+
+	if (info->prev) {
+		int cmp = do_compare_entry(ce, info->prev, &info->name);
+		if (cmp)
+			return cmp;
+	}
+	pathlen = info->pathlen;
+	ce_len = ce_namelen(ce);
+
+	/* If ce_len < pathlen then we must have previously hit "name == directory" entry */
+	if (ce_len < pathlen)
+		return -1;
+
+	ce_len -= pathlen;
+	ce_name = ce->name + pathlen;
+
+	len = tree_entry_len(n->path, n->sha1);
+	return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+}
+
+static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+{
+	int cmp = do_compare_entry(ce, info, n);
+	if (cmp)
+		return cmp;
+
+	/*
+	 * Even if the beginning compared identically, the ce should
+	 * compare as bigger than a directory leading up to it!
+	 */
+	return ce_namelen(ce) > traverse_path_len(info, n);
+}
+
+static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
+{
+	int len = traverse_path_len(info, n);
+	struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
+
+	ce->ce_mode = create_ce_mode(n->mode);
+	ce->ce_flags = create_ce_flags(len, stage);
+	hashcpy(ce->sha1, n->sha1);
+	make_traverse_path(ce->name, info, n);
+
+	return ce;
+}
+
+static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5],
+	const struct name_entry *names, const struct traverse_info *info)
+{
+	int i;
+	struct unpack_trees_options *o = info->data;
+	unsigned long conflicts;
+
+	/* Do we have *only* directories? Nothing to do */
+	if (mask == dirmask && !src[0])
+		return 0;
+
+	conflicts = info->conflicts;
+	if (o->merge)
+		conflicts >>= 1;
+	conflicts |= dirmask;
+
+	/*
+	 * Ok, we've filled in up to any potential index entry in src[0],
+	 * now do the rest.
+	 */
+	for (i = 0; i < n; i++) {
+		int stage;
+		unsigned int bit = 1ul << i;
+		if (conflicts & bit) {
+			src[i + o->merge] = o->df_conflict_entry;
+			continue;
+		}
+		if (!(mask & bit))
+			continue;
+		if (!o->merge)
+			stage = 0;
+		else if (i + 1 < o->head_idx)
+			stage = 1;
+		else if (i + 1 > o->head_idx)
+			stage = 3;
+		else
+			stage = 2;
+		src[i + o->merge] = create_ce_entry(info, names + i, stage);
+	}
+
+	if (o->merge)
+		return call_unpack_fn(src, o);
+
+	n += o->merge;
+	for (i = 0; i < n; i++)
+		add_entry(o, src[i], 0, 0);
+	return 0;
+}
+
+static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
+{
+	struct cache_entry *src[5] = { NULL, };
+	struct unpack_trees_options *o = info->data;
+	const struct name_entry *p = names;
+
+	/* Find first entry with a real name (we could use "mask" too) */
+	while (!p->mode)
+		p++;
+
+	/* Are we supposed to look at the index too? */
+	if (o->merge) {
+		while (o->pos < o->src_index->cache_nr) {
+			struct cache_entry *ce = o->src_index->cache[o->pos];
+			int cmp = compare_entry(ce, info, p);
+			if (cmp < 0) {
+				if (unpack_index_entry(ce, o) < 0)
+					return -1;
+				continue;
+			}
+			if (!cmp) {
+				o->pos++;
+				if (ce_stage(ce)) {
+					/*
+					 * If we skip unmerged index entries, we'll skip this
+					 * entry *and* the tree entries associated with it!
+					 */
+					if (o->skip_unmerged) {
+						add_entry(o, ce, 0, 0);
+						return mask;
+					}
+				}
+				src[0] = ce;
+			}
+			break;
+		}
+	}
+
+	if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
+		return -1;
+
+	/* Now handle any directories.. */
+	if (dirmask) {
+		unsigned long conflicts = mask & ~dirmask;
+		if (o->merge) {
+			conflicts <<= 1;
+			if (src[0])
+				conflicts |= 1;
+		}
+		if (traverse_trees_recursive(n, dirmask, conflicts,
+					     names, info) < 0)
+			return -1;
+		return mask;
+	}
+
+	return mask;
+}
+
+static int unpack_failed(struct unpack_trees_options *o, const char *message)
+{
+	discard_index(&o->result);
+	if (!o->gently) {
+		if (message)
+			return error(message);
+		return -1;
+	}
+	return -1;
 }
 
 int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
 {
-	struct tree_entry_list **posns;
-	int i;
-	struct tree_entry_list df_conflict_list;
 	static struct cache_entry *dfc;
 
-	memset(&df_conflict_list, 0, sizeof(df_conflict_list));
-	df_conflict_list.next = &df_conflict_list;
+	if (len > MAX_UNPACK_TREES)
+		die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
 	memset(&state, 0, sizeof(state));
 	state.base_dir = "";
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
 
+	memset(&o->result, 0, sizeof(o->result));
+	if (o->src_index)
+		o->result.timestamp = o->src_index->timestamp;
 	o->merge_size = len;
 
 	if (!dfc)
@@ -350,28 +347,43 @@
 	o->df_conflict_entry = dfc;
 
 	if (len) {
-		posns = xmalloc(len * sizeof(struct tree_entry_list *));
-		for (i = 0; i < len; i++)
-			posns[i] = create_tree_entry_list(t+i);
+		const char *prefix = o->prefix ? o->prefix : "";
+		struct traverse_info info;
 
-		if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
-				     o, &df_conflict_list))
-			return -1;
+		setup_traverse_info(&info, prefix);
+		info.fn = unpack_callback;
+		info.data = o;
+
+		if (traverse_trees(len, t, &info) < 0)
+			return unpack_failed(o, NULL);
+	}
+
+	/* Any left-over entries in the index? */
+	if (o->merge) {
+		while (o->pos < o->src_index->cache_nr) {
+			struct cache_entry *ce = o->src_index->cache[o->pos];
+			if (unpack_index_entry(ce, o) < 0)
+				return unpack_failed(o, NULL);
+		}
 	}
 
 	if (o->trivial_merges_only && o->nontrivial_merge)
-		die("Merge requires file-level merging");
+		return unpack_failed(o, "Merge requires file-level merging");
 
-	check_updates(active_cache, active_nr, o);
+	o->src_index = NULL;
+	if (check_updates(o))
+		return -1;
+	if (o->dst_index)
+		*o->dst_index = o->result;
 	return 0;
 }
 
 /* Here come the merge functions */
 
-static void reject_merge(struct cache_entry *ce)
+static int reject_merge(struct cache_entry *ce)
 {
-	die("Entry '%s' would be overwritten by merge. Cannot merge.",
-	    ce->name);
+	return error("Entry '%s' would be overwritten by merge. Cannot merge.",
+		     ce->name);
 }
 
 static int same(struct cache_entry *a, struct cache_entry *b)
@@ -389,18 +401,18 @@
  * When a CE gets turned into an unmerged entry, we
  * want it to be up-to-date
  */
-static void verify_uptodate(struct cache_entry *ce,
+static int verify_uptodate(struct cache_entry *ce,
 		struct unpack_trees_options *o)
 {
 	struct stat st;
 
 	if (o->index_only || o->reset)
-		return;
+		return 0;
 
 	if (!lstat(ce->name, &st)) {
-		unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
+		unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID);
 		if (!changed)
-			return;
+			return 0;
 		/*
 		 * NEEDSWORK: the current default policy is to allow
 		 * submodule to be out of sync wrt the supermodule
@@ -408,19 +420,20 @@
 		 * submodules that are marked to be automatically
 		 * checked out.
 		 */
-		if (S_ISGITLINK(ntohl(ce->ce_mode)))
-			return;
+		if (S_ISGITLINK(ce->ce_mode))
+			return 0;
 		errno = 0;
 	}
 	if (errno == ENOENT)
-		return;
-	die("Entry '%s' not uptodate. Cannot merge.", ce->name);
+		return 0;
+	return o->gently ? -1 :
+		error("Entry '%s' not uptodate. Cannot merge.", ce->name);
 }
 
-static void invalidate_ce_path(struct cache_entry *ce)
+static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
 {
 	if (ce)
-		cache_tree_invalidate_path(active_cache_tree, ce->name);
+		cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
 }
 
 /*
@@ -450,7 +463,7 @@
 	int cnt = 0;
 	unsigned char sha1[20];
 
-	if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
+	if (S_ISGITLINK(ce->ce_mode) &&
 	    resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
 		/* If we are not going to update the submodule, then
 		 * we don't care.
@@ -465,12 +478,12 @@
 	 * in that directory.
 	 */
 	namelen = strlen(ce->name);
-	pos = cache_name_pos(ce->name, namelen);
+	pos = index_name_pos(o->src_index, ce->name, namelen);
 	if (0 <= pos)
 		return cnt; /* we have it as nondirectory */
 	pos = -pos - 1;
-	for (i = pos; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+	for (i = pos; i < o->src_index->cache_nr; i++) {
+		struct cache_entry *ce = o->src_index->cache[i];
 		int len = ce_namelen(ce);
 		if (len < namelen ||
 		    strncmp(ce->name, ce->name, namelen) ||
@@ -480,8 +493,9 @@
 		 * ce->name is an entry in the subdirectory.
 		 */
 		if (!ce_stage(ce)) {
-			verify_uptodate(ce, o);
-			ce->ce_mode = 0;
+			if (verify_uptodate(ce, o))
+				return -1;
+			add_entry(o, ce, CE_REMOVE, 0);
 		}
 		cnt++;
 	}
@@ -499,8 +513,9 @@
 		d.exclude_per_dir = o->dir->exclude_per_dir;
 	i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
 	if (i)
-		die("Updating '%s' would lose untracked files in it",
-		    ce->name);
+		return o->gently ? -1 :
+			error("Updating '%s' would lose untracked files in it",
+			      ce->name);
 	free(pathbuf);
 	return cnt;
 }
@@ -509,26 +524,27 @@
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static void verify_absent(struct cache_entry *ce, const char *action,
-		struct unpack_trees_options *o)
+static int verify_absent(struct cache_entry *ce, const char *action,
+			 struct unpack_trees_options *o)
 {
 	struct stat st;
 
 	if (o->index_only || o->reset || !o->update)
-		return;
+		return 0;
 
 	if (has_symlink_leading_path(ce->name, NULL))
-		return;
+		return 0;
 
 	if (!lstat(ce->name, &st)) {
 		int cnt;
+		int dtype = ce_to_dtype(ce);
 
-		if (o->dir && excluded(o->dir, ce->name))
+		if (o->dir && excluded(o->dir, ce->name, &dtype))
 			/*
 			 * ce->name is explicitly excluded, so it is Ok to
 			 * overwrite it.
 			 */
-			return;
+			return 0;
 		if (S_ISDIR(st.st_mode)) {
 			/*
 			 * We are checking out path "foo" and
@@ -557,7 +573,7 @@
 			 * deleted entries here.
 			 */
 			o->pos += cnt;
-			return;
+			return 0;
 		}
 
 		/*
@@ -565,63 +581,71 @@
 		 * delete this path, which is in a subdirectory that
 		 * is being replaced with a blob.
 		 */
-		cnt = cache_name_pos(ce->name, strlen(ce->name));
+		cnt = index_name_pos(&o->result, ce->name, strlen(ce->name));
 		if (0 <= cnt) {
-			struct cache_entry *ce = active_cache[cnt];
-			if (!ce_stage(ce) && !ce->ce_mode)
-				return;
+			struct cache_entry *ce = o->result.cache[cnt];
+			if (ce->ce_flags & CE_REMOVE)
+				return 0;
 		}
 
-		die("Untracked working tree file '%s' "
-		    "would be %s by merge.", ce->name, action);
+		return o->gently ? -1 :
+			error("Untracked working tree file '%s' "
+			      "would be %s by merge.", ce->name, action);
 	}
+	return 0;
 }
 
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
 		struct unpack_trees_options *o)
 {
-	merge->ce_flags |= htons(CE_UPDATE);
+	int update = CE_UPDATE;
+
 	if (old) {
 		/*
 		 * See if we can re-use the old CE directly?
 		 * That way we get the uptodate stat info.
 		 *
-		 * This also removes the UPDATE flag on
-		 * a match.
+		 * This also removes the UPDATE flag on a match; otherwise
+		 * we will end up overwriting local changes in the work tree.
 		 */
 		if (same(old, merge)) {
-			memcpy(merge, old, offsetof(struct cache_entry, name));
+			copy_cache_entry(merge, old);
+			update = 0;
 		} else {
-			verify_uptodate(old, o);
-			invalidate_ce_path(old);
+			if (verify_uptodate(old, o))
+				return -1;
+			invalidate_ce_path(old, o);
 		}
 	}
 	else {
-		verify_absent(merge, "overwritten", o);
-		invalidate_ce_path(merge);
+		if (verify_absent(merge, "overwritten", o))
+			return -1;
+		invalidate_ce_path(merge, o);
 	}
 
-	merge->ce_flags &= ~htons(CE_STAGEMASK);
-	add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+	add_entry(o, merge, update, CE_STAGEMASK);
 	return 1;
 }
 
 static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 		struct unpack_trees_options *o)
 {
-	if (old)
-		verify_uptodate(old, o);
-	else
-		verify_absent(ce, "removed", o);
-	ce->ce_mode = 0;
-	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
-	invalidate_ce_path(ce);
+	/* Did it exist in the index? */
+	if (!old) {
+		if (verify_absent(ce, "removed", o))
+			return -1;
+		return 0;
+	}
+	if (verify_uptodate(old, o))
+		return -1;
+	add_entry(o, ce, CE_REMOVE, 0);
+	invalidate_ce_path(ce, o);
 	return 1;
 }
 
 static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+	add_entry(o, ce, 0, 0);
 	return 1;
 }
 
@@ -634,16 +658,14 @@
 	else
 		fprintf(o, "%s%06o %s %d\t%s\n",
 			label,
-			ntohl(ce->ce_mode),
+			ce->ce_mode,
 			sha1_to_hex(ce->sha1),
 			ce_stage(ce),
 			ce->name);
 }
 #endif
 
-int threeway_merge(struct cache_entry **stages,
-		struct unpack_trees_options *o,
-		int remove)
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
 {
 	struct cache_entry *index;
 	struct cache_entry *head;
@@ -700,16 +722,15 @@
 	/* #14, #14ALT, #2ALT */
 	if (remote && !df_conflict_head && head_match && !remote_match) {
 		if (index && !same(index, remote) && !same(index, head))
-			reject_merge(index);
+			return o->gently ? -1 : reject_merge(index);
 		return merged_entry(remote, index, o);
 	}
 	/*
 	 * If we have an entry in the index cache, then we want to
 	 * make sure that it matches head.
 	 */
-	if (index && !same(index, head)) {
-		reject_merge(index);
-	}
+	if (index && !same(index, head))
+		return o->gently ? -1 : reject_merge(index);
 
 	if (head) {
 		/* #5ALT, #15 */
@@ -721,10 +742,8 @@
 	}
 
 	/* #1 */
-	if (!head && !remote && any_anc_missing) {
-		remove_entry(remove);
+	if (!head && !remote && any_anc_missing)
 		return 0;
-	}
 
 	/* Under the new "aggressive" rule, we resolve mostly trivial
 	 * cases that we historically had git-merge-one-file resolve.
@@ -756,11 +775,12 @@
 		if ((head_deleted && remote_deleted) ||
 		    (head_deleted && remote && remote_match) ||
 		    (remote_deleted && head && head_match)) {
-			remove_entry(remove);
 			if (index)
 				return deleted_entry(index, index, o);
-			else if (ce && !head_deleted)
-				verify_absent(ce, "removed", o);
+			if (ce && !head_deleted) {
+				if (verify_absent(ce, "removed", o))
+					return -1;
+			}
 			return 0;
 		}
 		/*
@@ -776,10 +796,10 @@
 	 * conflict resolution files.
 	 */
 	if (index) {
-		verify_uptodate(index, o);
+		if (verify_uptodate(index, o))
+			return -1;
 	}
 
-	remove_entry(remove);
 	o->nontrivial_merge = 1;
 
 	/* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -814,9 +834,7 @@
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
  */
-int twoway_merge(struct cache_entry **src,
-		struct unpack_trees_options *o,
-		int remove)
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 {
 	struct cache_entry *current = src[0];
 	struct cache_entry *oldtree = src[1];
@@ -844,7 +862,6 @@
 		}
 		else if (oldtree && !newtree && same(current, oldtree)) {
 			/* 10 or 11 */
-			remove_entry(remove);
 			return deleted_entry(oldtree, current, o);
 		}
 		else if (oldtree && newtree &&
@@ -854,19 +871,17 @@
 		}
 		else {
 			/* all other failures */
-			remove_entry(remove);
 			if (oldtree)
-				reject_merge(oldtree);
+				return o->gently ? -1 : reject_merge(oldtree);
 			if (current)
-				reject_merge(current);
+				return o->gently ? -1 : reject_merge(current);
 			if (newtree)
-				reject_merge(newtree);
+				return o->gently ? -1 : reject_merge(newtree);
 			return -1;
 		}
 	}
 	else if (newtree)
 		return merged_entry(newtree, current, o);
-	remove_entry(remove);
 	return deleted_entry(oldtree, current, o);
 }
 
@@ -877,8 +892,7 @@
  * stage0 does not have anything there.
  */
 int bind_merge(struct cache_entry **src,
-		struct unpack_trees_options *o,
-		int remove)
+		struct unpack_trees_options *o)
 {
 	struct cache_entry *old = src[0];
 	struct cache_entry *a = src[1];
@@ -887,7 +901,8 @@
 		return error("Cannot do a bind merge of %d trees\n",
 			     o->merge_size);
 	if (a && old)
-		die("Entry '%s' overlaps.  Cannot bind.", a->name);
+		return o->gently ? -1 :
+			error("Entry '%s' overlaps with '%s'.  Cannot bind.", a->name, old->name);
 	if (!a)
 		return keep_entry(old, o);
 	else
@@ -900,9 +915,7 @@
  * The rule is:
  * - take the stat information from stage0, take the data from stage1
  */
-int oneway_merge(struct cache_entry **src,
-		struct unpack_trees_options *o,
-		int remove)
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 {
 	struct cache_entry *old = src[0];
 	struct cache_entry *a = src[1];
@@ -911,18 +924,19 @@
 		return error("Cannot do a oneway merge of %d trees",
 			     o->merge_size);
 
-	if (!a) {
-		remove_entry(remove);
+	if (!a)
 		return deleted_entry(old, old, o);
-	}
+
 	if (old && same(old, a)) {
+		int update = 0;
 		if (o->reset) {
 			struct stat st;
 			if (lstat(old->name, &st) ||
-			    ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
-				old->ce_flags |= htons(CE_UPDATE);
+			    ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
+				update |= CE_UPDATE;
 		}
-		return keep_entry(old, o);
+		add_entry(o, old, update, 0);
+		return 0;
 	}
 	return merged_entry(a, old, o);
 }
diff --git a/unpack-trees.h b/unpack-trees.h
index 5517faa..50453ed 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -1,11 +1,12 @@
 #ifndef UNPACK_TREES_H
 #define UNPACK_TREES_H
 
+#define MAX_UNPACK_TREES 8
+
 struct unpack_trees_options;
 
 typedef int (*merge_fn_t)(struct cache_entry **src,
-		struct unpack_trees_options *options,
-		int remove);
+		struct unpack_trees_options *options);
 
 struct unpack_trees_options {
 	int reset;
@@ -16,6 +17,8 @@
 	int trivial_merges_only;
 	int verbose_update;
 	int aggressive;
+	int skip_unmerged;
+	int gently;
 	const char *prefix;
 	int pos;
 	struct dir_struct *dir;
@@ -25,14 +28,19 @@
 	int merge_size;
 
 	struct cache_entry *df_conflict_entry;
+	void *unpack_data;
+
+	struct index_state *dst_index;
+	const struct index_state *src_index;
+	struct index_state result;
 };
 
 extern int unpack_trees(unsigned n, struct tree_desc *t,
 		struct unpack_trees_options *options);
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
 
 #endif
diff --git a/upload-pack.c b/upload-pack.c
index 51e3ec4..b46dd36 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -27,7 +27,8 @@
 static unsigned long oldest_have;
 
 static int multi_ack, nr_our_refs;
-static int use_thin_pack, use_ofs_delta, no_progress;
+static int use_thin_pack, use_ofs_delta, use_include_tag;
+static int no_progress;
 static struct object_array have_obj;
 static struct object_array want_obj;
 static unsigned int timeout;
@@ -35,6 +36,7 @@
  * otherwise maximum packet size (up to 65520 bytes).
  */
 static int use_sideband;
+static int debug_fd;
 
 static void reset_timeout(void)
 {
@@ -129,7 +131,8 @@
 		}
 		setup_revisions(0, NULL, &revs, NULL);
 	}
-	prepare_revision_walk(&revs);
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
 	mark_edges_uninteresting(revs.commits, &revs, show_edge);
 	traverse_commit_list(&revs, show_commit, show_object);
 	return 0;
@@ -160,6 +163,8 @@
 		argv[arg++] = "--progress";
 	if (use_ofs_delta)
 		argv[arg++] = "--delta-base-offset";
+	if (use_include_tag)
+		argv[arg++] = "--include-tag";
 	argv[arg++] = NULL;
 
 	memset(&pack_objects, 0, sizeof(pack_objects));
@@ -392,7 +397,6 @@
 	char hex[41], last_hex[41];
 	int len;
 
-	track_object_refs = 0;
 	save_commit_buffer = 0;
 
 	for(;;) {
@@ -444,6 +448,8 @@
 	static char line[1000];
 	int len, depth = 0;
 
+	if (debug_fd)
+		write_in_full(debug_fd, "#S\n", 3);
 	for (;;) {
 		struct object *o;
 		unsigned char sha1_buf[20];
@@ -451,6 +457,8 @@
 		reset_timeout();
 		if (!len)
 			break;
+		if (debug_fd)
+			write_in_full(debug_fd, line, len);
 
 		if (!prefixcmp(line, "shallow ")) {
 			unsigned char sha1[20];
@@ -489,6 +497,8 @@
 			use_sideband = DEFAULT_PACKET_MAX;
 		if (strstr(line+45, "no-progress"))
 			no_progress = 1;
+		if (strstr(line+45, "include-tag"))
+			use_include_tag = 1;
 
 		/* We have sent all our refs already, and the other end
 		 * should have chosen out of them; otherwise they are
@@ -506,6 +516,8 @@
 			add_object_array(o, NULL, &want_obj);
 		}
 	}
+	if (debug_fd)
+		write_in_full(debug_fd, "#E\n", 3);
 	if (depth == 0 && shallows.nr == 0)
 		return;
 	if (depth > 0) {
@@ -533,7 +545,8 @@
 				/* make sure the real parents are parsed */
 				unregister_shallow(object->sha1);
 				object->parsed = 0;
-				parse_commit((struct commit *)object);
+				if (parse_commit((struct commit *)object))
+					die("invalid commit");
 				parents = ((struct commit *)object)->parents;
 				while (parents) {
 					add_object_array(&parents->item->object,
@@ -557,7 +570,8 @@
 static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	static const char *capabilities = "multi_ack thin-pack side-band"
-		" side-band-64k ofs-delta shallow no-progress";
+		" side-band-64k ofs-delta shallow no-progress"
+		" include-tag";
 	struct object *o = parse_object(sha1);
 
 	if (!o)
@@ -575,7 +589,8 @@
 	}
 	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, refname, 0);
-		packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+		if (o)
+			packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 	}
 	return 0;
 }
@@ -629,6 +644,8 @@
 		die("'%s': unable to chdir or not a git archive", dir);
 	if (is_repository_shallow())
 		die("attempt to fetch/clone from a shallow repository");
+	if (getenv("GIT_DEBUG_SEND_PACK"))
+		debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
 	upload_pack();
 	return 0;
 }
diff --git a/walker.c b/walker.c
index adc3e80..c10eca8 100644
--- a/walker.c
+++ b/walker.c
@@ -256,7 +256,6 @@
 	int i;
 
 	save_commit_buffer = 0;
-	track_object_refs = 0;
 
 	for (i = 0; i < targets; i++) {
 		if (!write_ref || !write_ref[i])
diff --git a/ws.c b/ws.c
index d09b9df..ba7e834 100644
--- a/ws.c
+++ b/ws.c
@@ -14,6 +14,7 @@
 	{ "trailing-space", WS_TRAILING_SPACE },
 	{ "space-before-tab", WS_SPACE_BEFORE_TAB },
 	{ "indent-with-non-tab", WS_INDENT_WITH_NON_TAB },
+	{ "cr-at-eol", WS_CR_AT_EOL },
 };
 
 unsigned parse_whitespace_rule(const char *string)
@@ -124,6 +125,7 @@
 	int written = 0;
 	int trailing_whitespace = -1;
 	int trailing_newline = 0;
+	int trailing_carriage_return = 0;
 	int i;
 
 	/* Logic is simpler if we temporarily ignore the trailing newline. */
@@ -131,6 +133,11 @@
 		trailing_newline = 1;
 		len--;
 	}
+	if ((ws_rule & WS_CR_AT_EOL) &&
+	    len > 0 && line[len - 1] == '\r') {
+		trailing_carriage_return = 1;
+		len--;
+	}
 
 	/* Check for trailing whitespace. */
 	if (ws_rule & WS_TRAILING_SPACE) {
@@ -176,8 +183,10 @@
 	}
 
 	if (stream) {
-		/* Now the rest of the line starts at written.
-		 * The non-highlighted part ends at trailing_whitespace. */
+		/*
+		 * Now the rest of the line starts at "written".
+		 * The non-highlighted part ends at "trailing_whitespace".
+		 */
 		if (trailing_whitespace == -1)
 			trailing_whitespace = len;
 
@@ -196,8 +205,114 @@
 			    len - trailing_whitespace, 1, stream);
 			fputs(reset, stream);
 		}
+		if (trailing_carriage_return)
+			fputc('\r', stream);
 		if (trailing_newline)
 			fputc('\n', stream);
 	}
 	return result;
 }
+
+/* Copy the line to the buffer while fixing whitespaces */
+int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *error_count)
+{
+	/*
+	 * len is number of bytes to be copied from src, starting
+	 * at src.  Typically src[len-1] is '\n', unless this is
+	 * the incomplete last line.
+	 */
+	int i;
+	int add_nl_to_tail = 0;
+	int add_cr_to_tail = 0;
+	int fixed = 0;
+	int last_tab_in_indent = -1;
+	int last_space_in_indent = -1;
+	int need_fix_leading_space = 0;
+	char *buf;
+
+	/*
+	 * Strip trailing whitespace
+	 */
+	if ((ws_rule & WS_TRAILING_SPACE) &&
+	    (2 <= len && isspace(src[len-2]))) {
+		if (src[len - 1] == '\n') {
+			add_nl_to_tail = 1;
+			len--;
+			if (1 < len && src[len - 1] == '\r') {
+				add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
+				len--;
+			}
+		}
+		if (0 < len && isspace(src[len - 1])) {
+			while (0 < len && isspace(src[len-1]))
+				len--;
+			fixed = 1;
+		}
+	}
+
+	/*
+	 * Check leading whitespaces (indent)
+	 */
+	for (i = 0; i < len; i++) {
+		char ch = src[i];
+		if (ch == '\t') {
+			last_tab_in_indent = i;
+			if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
+			    0 <= last_space_in_indent)
+			    need_fix_leading_space = 1;
+		} else if (ch == ' ') {
+			last_space_in_indent = i;
+			if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+			    8 <= i - last_tab_in_indent)
+				need_fix_leading_space = 1;
+		} else
+			break;
+	}
+
+	buf = dst;
+	if (need_fix_leading_space) {
+		/* Process indent ourselves */
+		int consecutive_spaces = 0;
+		int last = last_tab_in_indent + 1;
+
+		if (ws_rule & WS_INDENT_WITH_NON_TAB) {
+			/* have "last" point at one past the indent */
+			if (last_tab_in_indent < last_space_in_indent)
+				last = last_space_in_indent + 1;
+			else
+				last = last_tab_in_indent + 1;
+		}
+
+		/*
+		 * between src[0..last-1], strip the funny spaces,
+		 * updating them to tab as needed.
+		 */
+		for (i = 0; i < last; i++) {
+			char ch = src[i];
+			if (ch != ' ') {
+				consecutive_spaces = 0;
+				*dst++ = ch;
+			} else {
+				consecutive_spaces++;
+				if (consecutive_spaces == 8) {
+					*dst++ = '\t';
+					consecutive_spaces = 0;
+				}
+			}
+		}
+		while (0 < consecutive_spaces--)
+			*dst++ = ' ';
+		len -= last;
+		src += last;
+		fixed = 1;
+	}
+
+	memcpy(dst, src, len);
+	if (add_cr_to_tail)
+		dst[len++] = '\r';
+	if (add_nl_to_tail)
+		dst[len++] = '\n';
+	if (fixed && error_count)
+		(*error_count)++;
+	return dst + len - buf;
+}
diff --git a/wt-status.c b/wt-status.c
index 991e373..b3fd57b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -7,9 +7,10 @@
 #include "diff.h"
 #include "revision.h"
 #include "diffcore.h"
+#include "quote.h"
 
 int wt_status_relative_paths = 1;
-int wt_status_use_color = 0;
+int wt_status_use_color = -1;
 static char wt_status_colors[][COLOR_MAXLEN] = {
 	"",         /* WT_STATUS_HEADER: normal */
 	"\033[32m", /* WT_STATUS_UPDATED: green */
@@ -40,7 +41,7 @@
 
 static const char* color(int slot)
 {
-	return wt_status_use_color ? wt_status_colors[slot] : "";
+	return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
 }
 
 void wt_status_prepare(struct wt_status *s)
@@ -82,51 +83,7 @@
 	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 }
 
-static char *quote_path(const char *in, int len,
-			struct strbuf *out, const char *prefix)
-{
-	if (len < 0)
-		len = strlen(in);
-
-	strbuf_grow(out, len);
-	strbuf_setlen(out, 0);
-	if (prefix) {
-		int off = 0;
-		while (prefix[off] && off < len && prefix[off] == in[off])
-			if (prefix[off] == '/') {
-				prefix += off + 1;
-				in += off + 1;
-				len -= off + 1;
-				off = 0;
-			} else
-				off++;
-
-		for (; *prefix; prefix++)
-			if (*prefix == '/')
-				strbuf_addstr(out, "../");
-	}
-
-	for ( ; len > 0; in++, len--) {
-		int ch = *in;
-
-		switch (ch) {
-		case '\n':
-			strbuf_addstr(out, "\\n");
-			break;
-		case '\r':
-			strbuf_addstr(out, "\\r");
-			break;
-		default:
-			strbuf_addch(out, ch);
-			continue;
-		}
-	}
-
-	if (!out->len)
-		strbuf_addstr(out, "./");
-
-	return out->buf;
-}
+#define quote_path quote_path_relative
 
 static void wt_status_print_filepair(struct wt_status *s,
 				     int t, struct diff_filepair *p)
@@ -217,19 +174,12 @@
 		wt_status_print_trailer(s);
 }
 
-static void wt_read_cache(struct wt_status *s)
-{
-	discard_cache();
-	read_cache_from(s->index_file);
-}
-
 static void wt_status_print_initial(struct wt_status *s)
 {
 	int i;
 	struct strbuf buf;
 
 	strbuf_init(&buf, 0);
-	wt_read_cache(s);
 	if (active_nr) {
 		s->commitable = 1;
 		wt_status_print_cached_header(s);
@@ -256,7 +206,6 @@
 	rev.diffopt.detect_rename = 1;
 	rev.diffopt.rename_limit = 100;
 	rev.diffopt.break_opt = 0;
-	wt_read_cache(s);
 	run_diff_index(&rev, 1);
 }
 
@@ -268,7 +217,6 @@
 	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
 	rev.diffopt.format_callback = wt_status_print_changed_cb;
 	rev.diffopt.format_callback_data = s;
-	wt_read_cache(s);
 	run_diff_files(&rev, 0);
 }
 
@@ -321,28 +269,14 @@
 static void wt_status_print_verbose(struct wt_status *s)
 {
 	struct rev_info rev;
-	int saved_stdout;
-
-	fflush(s->fp);
-
-	/* Sigh, the entire diff machinery is hardcoded to output to
-	 * stdout.  Do the dup-dance...*/
-	saved_stdout = dup(STDOUT_FILENO);
-	if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0)
-		die("couldn't redirect stdout\n");
 
 	init_revisions(&rev, NULL);
 	setup_revisions(0, NULL, &rev, s->reference);
 	rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 	rev.diffopt.detect_rename = 1;
-	wt_read_cache(s);
+	rev.diffopt.file = s->fp;
+	rev.diffopt.close_file = 0;
 	run_diff_index(&rev, 1);
-
-	fflush(stdout);
-
-	if (dup2(saved_stdout, STDOUT_FILENO) < 0)
-		die("couldn't restore stdout\n");
-	close(saved_stdout);
 }
 
 void wt_status_print(struct wt_status *s)
@@ -411,5 +345,5 @@
 		wt_status_relative_paths = git_config_bool(k, v);
 		return 0;
 	}
-	return git_default_config(k, v);
+	return git_color_default_config(k, v);
 }
diff --git a/xdiff-interface.c b/xdiff-interface.c
index d8ba7e7..61dc5c5 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -233,8 +233,7 @@
 			expression = value;
 		if (regcomp(&reg->re, expression, 0))
 			die("Invalid regexp to look for hunk header: %s", expression);
-		if (buffer)
-			free(buffer);
+		free(buffer);
 		value = ep + 1;
 	}
 }
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index c00ddaa..413082e 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -53,6 +53,7 @@
 #define XDL_MERGE_MINIMAL 0
 #define XDL_MERGE_EAGER 1
 #define XDL_MERGE_ZEALOUS 2
+#define XDL_MERGE_ZEALOUS_ALNUM 3
 
 typedef struct s_mmfile {
 	char *ptr;
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index b83b334..82b3573 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -248,10 +248,76 @@
 	return 0;
 }
 
+static int line_contains_alnum(const char *ptr, long size)
+{
+	while (size--)
+		if (isalnum(*(ptr++)))
+			return 1;
+	return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+	for (; chg; chg--, i++)
+		if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+				xe->xdf2.recs[i]->size))
+			return 1;
+	return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+	xdmerge_t *next_m = m->next;
+	m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+	m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+	m->next = next_m->next;
+	free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+				      int simplify_if_no_alnum)
+{
+	int result = 0;
+
+	if (!m)
+		return result;
+	for (;;) {
+		xdmerge_t *next_m = m->next;
+		int begin, end;
+
+		if (!next_m)
+			return result;
+
+		begin = m->i1 + m->chg1;
+		end = next_m->i1;
+
+		if (m->mode != 0 || next_m->mode != 0 ||
+		    (end - begin > 3 &&
+		     (!simplify_if_no_alnum ||
+		      lines_contain_alnum(xe1, begin, end - begin)))) {
+			m = next_m;
+		} else {
+			result++;
+			xdl_merge_two_conflicts(m);
+		}
+	}
+}
+
 /*
  * level == 0: mark all overlapping changes as conflict
  * level == 1: mark overlapping changes as conflict only if not identical
  * level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ *             treat hunks not containing any letter or number as conflicting
  *
  * returns < 0 on error, == 0 for no conflicts, else number of conflicts
  */
@@ -355,7 +421,9 @@
 	if (!changes)
 		changes = c;
 	/* refine conflicts */
-	if (level > 1 && xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0) {
+	if (level > 1 &&
+	    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+	     xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) {
 		xdl_cleanup_merge(changes);
 		return -1;
 	}