Merge branch 'js/config-cb'

* js/config-cb:
  Provide git_config with a callback-data parameter

Conflicts:

	builtin-add.c
	builtin-cat-file.c
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 994eb91..d2a0a76 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -89,6 +89,8 @@
    of "else if" statements, it can make sense to add braces to
    single line blocks.
 
+ - We try to avoid assignments inside if().
+
  - Try to make your code understandable.  You may put comments
    in, but comments invariably tend to stale out when the code
    they were describing changes.  Often splitting a function
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 43781fb..9750334 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -2,8 +2,9 @@
 	$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
 		$(wildcard git-*.txt)) \
 	gitk.txt
-MAN5_TXT=gitattributes.txt gitignore.txt gitcli.txt gitmodules.txt
-MAN7_TXT=git.txt
+MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt
+MAN7_TXT=git.txt gitcli.txt gittutorial.txt gittutorial-2.txt \
+	gitcvs-migration.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
 MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@ -11,14 +12,10 @@
 
 DOC_HTML=$(MAN_HTML)
 
-ARTICLES = tutorial
-ARTICLES += tutorial-2
-ARTICLES += core-tutorial
-ARTICLES += cvs-migration
+ARTICLES = core-tutorial
 ARTICLES += diffcore
 ARTICLES += howto-index
 ARTICLES += repository-layout
-ARTICLES += hooks
 ARTICLES += everyday
 ARTICLES += git-tools
 ARTICLES += glossary
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 217980f..c298dc2 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -523,8 +523,10 @@
 	one of `header` (the header text of the status message),
 	`added` or `updated` (files which are added but not committed),
 	`changed` (files which are changed but not added in the index),
-	or `untracked` (files which are not tracked by git). The values of
-	these variables may be specified as in color.branch.<slot>.
+	`untracked` (files which are not tracked by git), or
+	`nobranch` (the color the 'no branch' warning is shown in, defaulting
+	to red). The values of these variables may be specified as in
+	color.branch.<slot>.
 
 commit.template::
 	Specify a file to use as the template for new commit messages.
@@ -660,11 +662,24 @@
 	Path to a log file where the CVS server interface well... logs
 	various stuff. See linkgit:git-cvsserver[1].
 
+gitcvs.usecrlfattr
+	If true, the server will look up the `crlf` attribute for
+	files to determine the '-k' modes to use. If `crlf` is set,
+	the '-k' mode will be left blank, so cvs clients will
+	treat it as text. If `crlf` is explicitly unset, the file
+	will be set with '-kb' mode, which supresses any newline munging
+	the client might otherwise do. If `crlf` is not specified,
+	then 'gitcvs.allbinary' is used. See linkgit:gitattribute[5].
+
 gitcvs.allbinary::
-	If true, all files are sent to the client in mode '-kb'. This
-	causes the client to treat all files as binary files which suppresses
-	any newline munging it otherwise might do. A work-around for the
-	fact that there is no way yet to set single files to mode '-kb'.
+	This is used if 'gitcvs.usecrlfattr' does not resolve
+	the correct '-kb' mode to use. If true, all
+	unresolved files are sent to the client in
+	mode '-kb'. This causes the client to treat them
+	as binary files, which suppresses any newline munging it
+	otherwise might do. Alternatively, if it is set to "guess",
+	then the contents of the file are examined to decide if
+	it is binary, similar to 'core.autocrlf'.
 
 gitcvs.dbname::
 	Database used by git-cvsserver to cache revision information
@@ -695,8 +710,9 @@
 	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'
+All gitcvs variables except for 'gitcvs.usecrlfattr' and
+'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
 access method.
 
@@ -815,6 +831,12 @@
 	The port number to bind the gitweb httpd to. See
 	linkgit:git-instaweb[1].
 
+log.date::
+	Set default date-time mode for the log command. Setting log.date
+	value is similar to using git log's --date option. The value is one of
+	following alternatives: {relative,local,default,iso,rfc,short}.
+	See linkgit:git-log[1].
+
 log.showroot::
 	If true, the initial commit will be shown as a big creation event.
 	This is equivalent to a diff against an empty tree.
diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index 5a55312..b50b5dd 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -8,7 +8,7 @@
 work with a git repository.
 
 If you just need to use git as a revision control system you may prefer
-to start with link:tutorial.html[a tutorial introduction to git] or
+to start with linkgit:gittutorial[7][a tutorial introduction to git] or
 link:user-manual.html[the git user manual].
 
 However, an understanding of these low-level tools can be helpful if
@@ -1581,7 +1581,7 @@
 have to worry. git supports "shared public repository" style of
 cooperation you are probably more familiar with as well.
 
-See link:cvs-migration.html[git for CVS users] for the details.
+See linkgit:gitcvs-migration[7][git for CVS users] for the details.
 
 Bundling your work together
 ---------------------------
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 13234fa..859d679 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -228,6 +228,9 @@
 --no-ext-diff::
 	Disallow external diff drivers.
 
+--ignore-submodules::
+	Ignore changes to submodules in the diff generation.
+
 --src-prefix=<prefix>::
 	Show the given source prefix instead of "a/".
 
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index e0e730b..bb4abe2 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -9,7 +9,7 @@
 --------
 [verse]
 'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh]
-          [--] <filepattern>...
+	  [--ignore-errors] [--] <filepattern>...
 
 DESCRIPTION
 -----------
@@ -83,6 +83,11 @@
 	Don't add the file(s), but only refresh their stat()
 	information in the index.
 
+\--ignore-errors::
+	If some files could not be added because of errors indexing
+	them, do not abort the operation, but continue adding the
+	others. The command shall still exit with non-zero status.
+
 \--::
 	This option can be used to separate command-line options from
 	the list of files, (useful when filenames might be mistaken
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index df42cb1..f6c394c 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -9,12 +9,16 @@
 SYNOPSIS
 --------
 'git-cat-file' [-t | -s | -e | -p | <type>] <object>
+'git-cat-file' [--batch | --batch-check] < <list-of-objects>
 
 DESCRIPTION
 -----------
-Provides content or type of objects in the repository. The type
-is required unless '-t' or '-p' is used to find the object type,
-or '-s' is used to find the object size.
+In the first form, provides content or type of objects in the repository. The
+type is required unless '-t' or '-p' is used to find the object type, or '-s'
+is used to find the object size.
+
+In the second form, a list of object (separated by LFs) is provided on stdin,
+and the SHA1, type, and size of each object is printed on stdout.
 
 OPTIONS
 -------
@@ -46,6 +50,14 @@
 	or to ask for a "blob" with <object> being a tag object that
 	points at it.
 
+--batch::
+	Print the SHA1, type, size, and contents of each object provided on
+	stdin. May not be combined with any other options or arguments.
+
+--batch-check::
+	Print the SHA1, type, and size of each object provided on stdin. May not be
+	combined with any other options or arguments.
+
 OUTPUT
 ------
 If '-t' is specified, one of the <type>.
@@ -56,9 +68,30 @@
 
 If '-p' is specified, the contents of <object> are pretty-printed.
 
-Otherwise the raw (though uncompressed) contents of the <object> will
-be returned.
+If <type> is specified, the raw (though uncompressed) contents of the <object>
+will be returned.
 
+If '--batch' is specified, output of the following form is printed for each
+object specified on stdin:
+
+------------
+<sha1> SP <type> SP <size> LF
+<contents> LF
+------------
+
+If '--batch-check' is specified, output of the following form is printed for
+each object specified fon stdin:
+
+------------
+<sha1> SP <type> SP <size> LF
+------------
+
+For both '--batch' and '--batch-check', output of the following form is printed
+for each object specified on stdin that does not exist in the repository:
+
+------------
+<object> SP missing LF
+------------
 
 Author
 ------
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 4bb51cc..c3c9f5b 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -87,7 +87,7 @@
 
 --no-verify::
 	This option bypasses the pre-commit and commit-msg hooks.
-	See also link:hooks.html[hooks].
+	See also linkgit:githooks[5][hooks].
 
 --allow-empty::
 	Usually recording a commit that has the exact same tree as its
@@ -292,7 +292,7 @@
 HOOKS
 -----
 This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
-and `post-commit` hooks.  See link:hooks.html[hooks] for more
+and `post-commit` hooks.  See linkgit:githooks[5][hooks] for more
 information.
 
 
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index 9a47b4c..f75afaa 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-W] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
 
 DESCRIPTION
@@ -65,11 +65,22 @@
 -w::
 	Specify the location of the CVS checkout to use for the export. This
 	option does not require GIT_DIR to be set before execution if the
-	current directory is within a git repository.
+	current directory is within a git repository.  The default is the
+	value of 'cvsexportcommit.cvsdir'.
+
+-W::
+	Tell cvsexportcommit that the current working directory is not only
+	a Git checkout, but also the CVS checkout.  Therefore, Git will
+	reset the working directory to the parent commit before proceeding.
 
 -v::
 	Verbose.
 
+CONFIGURATION
+-------------
+cvsexportcommit.cvsdir::
+	The default location of the CVS checkout to use for the export.
+
 EXAMPLES
 --------
 
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index b110671..a33382e 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -301,11 +301,33 @@
 Legacy monitoring operations are not supported (edit, watch and related).
 Exports and tagging (tags and branches) are not supported at this stage.
 
-The server should set the '-k' mode to binary when relevant, however,
-this is not really implemented yet. For now, you can force the server
-to set '-kb' for all files by setting the `gitcvs.allbinary` config
-variable. In proper GIT tradition, the contents of the files are
-always respected. No keyword expansion or newline munging is supported.
+CRLF Line Ending Conversions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default the server leaves the '-k' mode blank for all files,
+which causes the cvs client to treat them as a text files, subject
+to crlf conversion on some platforms.
+
+You can make the server use `crlf` attributes to set the '-k' modes
+for files by setting the `gitcvs.usecrlfattr` config variable.
+In this case, if `crlf` is explicitly unset ('-crlf'), then the
+server will set '-kb' mode for binary files. If `crlf` is set,
+then the '-k' mode will explicitly be left blank.  See
+also linkgit:gitattributes[5] for more information about the `crlf`
+attribute.
+
+Alternatively, if `gitcvs.usecrlfattr` config is not enabled
+or if the `crlf` attribute is unspecified for a filename, then
+the server uses the `gitcvs.allbinary` config for the default setting.
+If `gitcvs.allbinary` is set, then file not otherwise
+specified will default to '-kb' mode. Otherwise the '-k' mode
+is left blank. But if `gitcvs.allbinary` is set to "guess", then
+the correct '-k' mode will be guessed based on the contents of
+the file.
+
+For best consistency with cvs, it is probably best to override the
+defaults by setting `gitcvs.usecrlfattr` to true,
+and `gitcvs.allbinary` to "guess".
 
 Dependencies
 ------------
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index fd83bc7..cf261dd 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -174,7 +174,7 @@
 upload-archive::
 	This serves `git-archive --remote`.  It is disabled by
 	default, but a repository can enable it by setting
-	`daemon.uploadarchive` configuration item to `true`.
+	`daemon.uploadarch` configuration item to `true`.
 
 receive-pack::
 	This serves `git-send-pack` clients, allowing anonymous
@@ -257,7 +257,7 @@
 ----------------------------------------------------------------
 	[daemon]
 		uploadpack = false
-		uploadarchive = true
+		uploadarch = true
 ----------------------------------------------------------------
 
 
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index d9aa2f2..69e1ab7 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -13,9 +13,10 @@
 DESCRIPTION
 -----------
 The command finds the most recent tag that is reachable from a
-commit, and if the commit itself is pointed at by the tag, shows
-the tag.  Otherwise, it suffixes the tag name with the number of
-additional commits and the abbreviated object name of the commit.
+commit.  If the tag points to the commit, then only the tag is
+shown.  Otherwise, it suffixes the tag name with the number of
+additional commits on top of the tagged object and the
+abbreviated object name of the most recent commit.
 
 
 OPTIONS
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 8d80f0d..506c37a 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -183,6 +183,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-format-patch.txt b/Documentation/git-format-patch.txt
index 7548a21..c60ce12 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -174,6 +174,7 @@
         subjectprefix = CHANGE
         suffix = .txt
         numbered = auto
+	cc = <email>
 ------------
 
 
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index 33030c0..99a2143 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-hash-object' [-t <type>] [-w] [--stdin] [--] <file>...
+'git-hash-object' [-t <type>] [-w] [--stdin | --stdin-paths] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -32,6 +32,9 @@
 --stdin::
 	Read the object from standard input instead of from a file.
 
+--stdin-paths::
+	Read file names from stdin instead of from the command-line.
+
 Author
 ------
 Written by Junio C Hamano <junkio@cox.net>
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index f92bb8c..3178bc4 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -18,12 +18,15 @@
 
 This runs `git-fsck --unreachable` using all the refs
 available in `$GIT_DIR/refs`, optionally with additional set of
-objects specified on the command line, and prunes all
+objects specified on the command line, and prunes all unpacked
 objects unreachable from any of these head objects from the object database.
 In addition, it
 prunes the unpacked objects that are also found in packs by
 running `git prune-packed`.
 
+Note that unreachable, packed objects will remain.  If this is
+not desired, see linkgit:git-repack[1].
+
 OPTIONS
 -------
 
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index f06d94e..0cc44d7 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -46,12 +46,6 @@
 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
-on the command line nor in any Push line of the
-corresponding remotes file---see below), then "matching" heads are
-pushed: for every head that exists on the local side, the remote side is
-updated if a head of the same name already exists on the remote side.
-+
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
 +
 A parameter <ref> without a colon pushes the <ref> from the source
@@ -59,6 +53,13 @@
 +
 Pushing an empty <src> allows you to delete the <dst> ref from
 the remote repository.
++
+The special refspec `:` (or `+:` to allow non-fast forward updates)
+directs git to push "matching" heads: for every head that exists on
+the local side, the remote side is updated if a head of the same name
+already exists on the remote side.  This is the default operation mode
+if no explicit refspec is found (that is neither on the command line
+nor in any Push line of the corresponding remotes file---see below).
 
 \--all::
 	Instead of naming each ref to push, specifies that all
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 3d95749..f81a660 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
+'git-repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
 
 DESCRIPTION
 -----------
@@ -37,6 +37,18 @@
 	leaves behind, but `git fsck --full` shows as
 	dangling.
 
+-A::
+	Same as `-a`, but any unreachable objects in a previous
+	pack become loose, unpacked objects, instead of being
+	left in the old pack.  Unreachable objects are never
+	intentionally added to a pack, even when repacking.
+	When used with '-d', this option
+	prevents unreachable objects from being immediately
+	deleted by way of being left in the old pack and then
+	removed.  Instead, the loose unreachable objects
+	will be pruned according to normal expiry rules
+	with the next linkgit:git-gc[1].
+
 -d::
 	After packing, if the newly created packs make some
 	existing packs redundant, remove the redundant packs.
@@ -55,8 +67,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-parse.txt b/Documentation/git-rev-parse.txt
index 110e7ba..69599ff 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -347,7 +347,7 @@
 
 	* 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].
+	  documented in linkgit:gitcli[7].
 
 	* Use `!` to not make the corresponding negated long option available.
 
@@ -378,6 +378,31 @@
 eval `echo "$OPTS_SPEC" | git-rev-parse --parseopt -- "$@" || echo exit $?`
 ------------
 
+EXAMPLES
+--------
+
+* Print the object name of the current commit:
++
+------------
+$ git rev-parse --verify HEAD
+------------
+
+* Print the commit object name from the revision in the $REV shell variable:
++
+------------
+$ git rev-parse --verify $REV
+------------
++
+This will error out if $REV is empty or not a valid revision.
+
+* Same as above:
++
+------------
+$ git rev-parse --default master --verify $REV
+------------
++
+but if $REV is empty, the commit object name from master will be printed.
+
 
 Author
 ------
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-submodule.txt b/Documentation/git-submodule.txt
index 6ffd896..0668f29 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -11,7 +11,8 @@
 [verse]
 'git-submodule' [--quiet] add [-b branch] [--] <repository> [<path>]
 'git-submodule' [--quiet] status [--cached] [--] [<path>...]
-'git-submodule' [--quiet] [init|update] [--] [<path>...]
+'git-submodule' [--quiet] init [--] [<path>...]
+'git-submodule' [--quiet] update [--init] [--] [<path>...]
 'git-submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
 
 
@@ -47,6 +48,10 @@
 	Update the registered submodules, i.e. clone missing submodules and
 	checkout the commit specified in the index of the containing repository.
 	This will make the submodules HEAD be detached.
++
+If the submodule is not yet initialized, and you just want to use the
+setting as stored in .gitmodules, you can automatically initialize the
+submodule with the --init option.
 
 summary::
 	Show commit summary between the given commit (defaults to HEAD) and
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index c6b56b4..c9e4efe 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -61,6 +61,16 @@
 	Set the 'useSvnsyncProps' option in the [svn-remote] config.
 --rewrite-root=<URL>;;
 	Set the 'rewriteRoot' option in the [svn-remote] config.
+--use-log-author;;
+	When retrieving svn commits into git (as part of fetch, rebase, or
+	dcommit operations), look for the first From: or Signed-off-by: line
+	in the log message and use that as the author string.
+--add-author-from;;
+	When committing to svn from git (as part of commit or dcommit
+	operations), if the existing log message doesn't already have a
+	From: or Signed-off-by: line, append a From: line based on the
+	git commit's author string.  If you use this, then --use-log-author
+	will retrieve a valid author string for all commits.
 --username=<USER>;;
 	For transports that SVN handles authentication for (http,
 	https, and plain svn), specify the username.  For other
@@ -196,10 +206,10 @@
 	independently of git-svn functions.
 
 'create-ignore'::
-
 	Recursively finds the svn:ignore property on directories and
 	creates matching .gitignore files. The resulting files are staged to
-	be committed, but are not committed.
+	be committed, but are not committed. Use -r/--revision to refer to a
+	specfic revision.
 
 'show-ignore'::
 	Recursively finds and lists the svn:ignore property on
@@ -223,6 +233,19 @@
 	argument.  Use the --url option to output only the value of the
 	'URL:' field.
 
+'proplist'::
+	Lists the properties stored in the Subversion repository about a
+	given file or directory.  Use -r/--revision to refer to a specific
+	Subversion revision.
+
+'propget'::
+	Gets the Subversion property given as the first argument, for a
+	file.  A specific revision can be specified with -r/--revision.
+
+'show-externals'::
+	Shows the Subversion externals.  Use -r/--revision to specify a
+	specific revision.
+
 --
 
 OPTIONS
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 66be18e..0664060 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -15,6 +15,7 @@
 	     [--cacheinfo <mode> <object> <file>]\*
 	     [--chmod=(+|-)x]
 	     [--assume-unchanged | --no-assume-unchanged]
+	     [--ignore-submodules]
 	     [--really-refresh] [--unresolve] [--again | -g]
 	     [--info-only] [--index-info]
 	     [-z] [--stdin]
@@ -54,6 +55,10 @@
         default behavior is to error out.  This option makes
         git-update-index continue anyway.
 
+--ignore-submodules:
+	Do not try to update submodules.  This option is only respected
+	when passed before --refresh.
+
 --unmerged::
         If --refresh finds unmerged changes in the index, the default
         behavior is to error out.  This option makes git-update-index
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
index 92ef574..5ba0b9f 100644
--- a/Documentation/git-web--browse.txt
+++ b/Documentation/git-web--browse.txt
@@ -78,7 +78,7 @@
 configuration variable, we launch 'kfmclient' to try to open the HTML
 man page on an already opened konqueror in a new tab if possible.
 
-For consistency, we also try such a trick if 'brower.konqueror.path' is
+For consistency, we also try such a trick if 'browser.konqueror.path' is
 set to something like 'A_PATH_TO/konqueror'. That means we will try to
 launch 'A_PATH_TO/kfmclient' instead.
 
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 6f445b1..735f0d1 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -20,10 +20,10 @@
 unusually rich command set that provides both high-level operations
 and full access to internals.
 
-See this link:tutorial.html[tutorial] to get started, then see
+See this linkgit:gittutorial[7][tutorial] to get started, then see
 link:everyday.html[Everyday Git] for a useful minimum set of commands, and
 "man git-commandname" for documentation of each command.  CVS users may
-also want to read link:cvs-migration.html[CVS migration].  See
+also want to read linkgit:gitcvs-migration[7][CVS migration].  See
 link:user-manual.html[Git User's Manual] for a more in-depth
 introduction.
 
@@ -364,7 +364,7 @@
 
 Please see the link:repository-layout.html[repository layout] document.
 
-Read link:hooks.html[hooks] for more details about each hook.
+Read linkgit:githooks[5][hooks] for more details about each hook.
 
 Higher level SCMs may provide and manage additional information in the
 `$GIT_DIR`.
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 7ee5ce3..835cb05 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -1,4 +1,4 @@
-gitcli(5)
+gitcli(7)
 =========
 
 NAME
diff --git a/Documentation/cvs-migration.txt b/Documentation/gitcvs-migration.txt
similarity index 92%
rename from Documentation/cvs-migration.txt
rename to Documentation/gitcvs-migration.txt
index 00f2e36..c410805 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/gitcvs-migration.txt
@@ -1,5 +1,16 @@
-git for CVS users
-=================
+gitcvs-migration(7)
+===================
+
+NAME
+----
+gitcvs-migration - git for CVS users
+
+SYNOPSIS
+--------
+git cvsimport *
+
+DESCRIPTION
+-----------
 
 Git differs from CVS in that every working tree contains a repository with
 a full copy of the project history, and no repository is inherently more
@@ -8,7 +19,7 @@
 this document explains how to do that.
 
 Some basic familiarity with git is required.  This
-link:tutorial.html[tutorial introduction to git] and the
+linkgit:gittutorial[7][tutorial introduction to git] and the
 link:glossary.html[git glossary] should be sufficient.
 
 Developing against a shared repository
@@ -71,7 +82,7 @@
 
 We assume you have already created a git repository for your project,
 possibly created from scratch or from a tarball (see the
-link:tutorial.html[tutorial]), or imported from an already existing CVS
+linkgit:gittutorial[7][tutorial]), or imported from an already existing CVS
 repository (see the next section).
 
 Assume your existing repo is at /home/alice/myproject.  Create a new "bare"
@@ -137,7 +148,7 @@
 
 Git allows you to specify scripts called "hooks" to be run at certain
 points.  You can use these, for example, to send all commits to the shared
-repository to a mailing list.  See link:hooks.html[Hooks used by git].
+repository to a mailing list.  See linkgit:githooks[5][Hooks used by git].
 
 You can enforce finer grained permissions using update hooks.  See
 link:howto/update-hook-example.txt[Controlling access to branches using
@@ -170,3 +181,13 @@
 
 With a small group, developers may just pull changes from each other's
 repositories without the need for a central maintainer.
+
+SEE ALSO
+--------
+linkgit:gittutorial[7], linkgit:gittutorial-2[7],
+link:everyday.html[Everyday Git],
+link:user-manual.html[The Git User's Manual]
+
+GIT
+---
+Part of the linkgit:git[7] suite.
diff --git a/Documentation/hooks.txt b/Documentation/githooks.txt
similarity index 98%
rename from Documentation/hooks.txt
rename to Documentation/githooks.txt
index d89cc22..53747fe 100644
--- a/Documentation/hooks.txt
+++ b/Documentation/githooks.txt
@@ -1,5 +1,17 @@
-Hooks used by git
-=================
+githooks(5)
+===========
+
+NAME
+----
+githooks - Hooks used by git
+
+SYNOPSIS
+--------
+$GIT_DIR/hooks/*
+
+
+DESCRIPTION
+-----------
 
 Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
@@ -285,3 +297,7 @@
 This hook is invoked by `git-gc --auto`. It takes no parameter, and
 exiting with non-zero status from this script causes the `git-gc --auto`
 to abort.
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Documentation/tutorial-2.txt b/Documentation/gittutorial-2.txt
similarity index 96%
rename from Documentation/tutorial-2.txt
rename to Documentation/gittutorial-2.txt
index 7fac47d..5bbbf43 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -1,7 +1,18 @@
-A tutorial introduction to git: part two
-========================================
+gittutorial-2(7)
+================
 
-You should work through link:tutorial.html[A tutorial introduction to
+NAME
+----
+gittutorial-2 - A tutorial introduction to git: part two
+
+SYNOPSIS
+--------
+git *
+
+DESCRIPTION
+-----------
+
+You should work through linkgit:gittutorial[7][A tutorial introduction to
 git] before reading this tutorial.
 
 The goal of this tutorial is to introduce two fundamental pieces of
@@ -394,7 +405,7 @@
 The link:user-manual.html[Git User's Manual] provides a more
 comprehensive introduction to git.
 
-The link:cvs-migration.html[CVS migration] document explains how to
+The linkgit:gitcvs-migration[7][CVS migration] document explains how to
 import a CVS repository into git, and shows how to use git in a
 CVS-like way.
 
@@ -404,3 +415,14 @@
 For git developers, the link:core-tutorial.html[Core tutorial] goes
 into detail on the lower-level git mechanisms involved in, for
 example, creating a new commit.
+
+SEE ALSO
+--------
+linkgit:gittutorial[7],
+linkgit:gitcvs-migration[7],
+link:everyday.html[Everyday git],
+link:user-manual.html[The Git User's Manual]
+
+GIT
+---
+Part of the linkgit:git[7] suite.
diff --git a/Documentation/tutorial.txt b/Documentation/gittutorial.txt
similarity index 97%
rename from Documentation/tutorial.txt
rename to Documentation/gittutorial.txt
index e2bbda5..898acdb 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -1,5 +1,16 @@
-A tutorial introduction to git (for version 1.5.1 or newer)
-===========================================================
+gittutorial(7)
+==============
+
+NAME
+----
+gittutorial - A tutorial introduction to git (for version 1.5.1 or newer)
+
+SYNOPSIS
+--------
+git *
+
+DESCRIPTION
+-----------
 
 This tutorial explains how to import a new project into git, make
 changes to it, and share changes with other developers.
@@ -381,7 +392,7 @@
 
 Git can also be used in a CVS-like mode, with a central repository
 that various users push changes to; see linkgit:git-push[1] and
-link:cvs-migration.html[git for CVS users].
+linkgit:gitcvs-migration[7][git for CVS users].
 
 Exploring history
 -----------------
@@ -560,7 +571,7 @@
     used to create commits, check out working directories, and
     hold the various trees involved in a merge.
 
-link:tutorial-2.html[Part two of this tutorial] explains the object
+linkgit:gittutorial-2[7][Part two of this tutorial] explains the object
 database, the index file, and a few other odds and ends that you'll
 need to make the most of git.
 
@@ -581,4 +592,15 @@
 
   * link:everyday.html[Everyday GIT with 20 Commands Or So]
 
-  * link:cvs-migration.html[git for CVS users].
+  * linkgit:gitcvs-migration[7][git for CVS users].
+
+SEE ALSO
+--------
+linkgit:gittutorial-2[7],
+linkgit:gitcvs-migration[7],
+link:everyday.html[Everyday git],
+link:user-manual.html[The Git User's Manual]
+
+GIT
+---
+Part of the linkgit:git[7] suite.
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
index 9719311..48ce747 100644
--- a/Documentation/merge-config.txt
+++ b/Documentation/merge-config.txt
@@ -6,6 +6,11 @@
 	Whether to include summaries of merged commits in newly created
 	merge commit messages. False by default.
 
+merge.renameLimit::
+	The number of files to consider when performing rename detection
+	during a merge; if not specified, defaults to the value of
+	diff.renameLimit.
+
 merge.tool::
 	Controls which merge resolution program is used by
 	linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt
index bbaed2e..7fd187b 100644
--- a/Documentation/repository-layout.txt
+++ b/Documentation/repository-layout.txt
@@ -124,7 +124,7 @@
 	commands.  A handful of sample hooks are installed when
 	`git init` is run, but all of them are disabled by
 	default.  To enable, they need to be made executable.
-	Read link:hooks.html[hooks] for more details about
+	Read linkgit:githooks[5][hooks] for more details about
 	each hook.
 
 index::
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 2648a55..dfcef79 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -13,10 +13,11 @@
 
 	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".
+	as when using "--pretty". `log.date` config variable sets a default
+	value for log command's --date option.
 +
 `--date=relative` shows dates relative to the current time,
 e.g. "2 hours ago".
@@ -75,6 +76,16 @@
 	-xxxxxxx... 1st on a
 -----------------------------------------------------------------------
 
+--graph::
+
+	Draw a text-based graphical representation of the commit history
+	on the left hand side of the output.  This may cause extra lines
+	to be printed in between commits, in order for the graph history
+	to be drawn properly.
++
+This implies the '--topo-order' option by default, but the
+'--date-order' option may also be specified.
+
 Diff Formatting
 ~~~~~~~~~~~~~~~
 
diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt
new file mode 100644
index 0000000..e955979
--- /dev/null
+++ b/Documentation/technical/api-history-graph.txt
@@ -0,0 +1,179 @@
+history graph API
+=================
+
+The graph API is used to draw a text-based representation of the commit
+history.  The API generates the graph in a line-by-line fashion.
+
+Functions
+---------
+
+Core functions:
+
+* `graph_init()` creates a new `struct git_graph`
+
+* `graph_release()` destroys a `struct git_graph`, and frees the memory
+  associated with it.
+
+* `graph_update()` moves the graph to a new commit.
+
+* `graph_next_line()` outputs the next line of the graph into a strbuf.  It
+  does not add a terminating newline.
+
+* `graph_padding_line()` outputs a line of vertical padding in the graph.  It
+  is similar to `graph_next_line()`, but is guaranteed to never print the line
+  containing the current commit.  Where `graph_next_line()` would print the
+  commit line next, `graph_padding_line()` prints a line that simply extends
+  all branch lines downwards one row, leaving their positions unchanged.
+
+* `graph_is_commit_finished()` determines if the graph has output all lines
+  necessary for the current commit.  If `graph_update()` is called before all
+  lines for the current commit have been printed, the next call to
+  `graph_next_line()` will output an ellipsis, to indicate that a portion of
+  the graph was omitted.
+
+The following utility functions are wrappers around `graph_next_line()` and
+`graph_is_commit_finished()`.  They always print the output to stdout.
+They can all be called with a NULL graph argument, in which case no graph
+output will be printed.
+
+* `graph_show_commit()` calls `graph_next_line()` until it returns non-zero.
+  This prints all graph lines up to, and including, the line containing this
+  commit.  Output is printed to stdout.  The last line printed does not contain
+  a terminating newline.  This should not be called if the commit line has
+  already been printed, or it will loop forever.
+
+* `graph_show_oneline()` calls `graph_next_line()` and prints the result to
+  stdout.  The line printed does not contain a terminating newline.
+
+* `graph_show_padding()` calls `graph_padding_line()` and prints the result to
+  stdout.  The line printed does not contain a terminating newline.
+
+* `graph_show_remainder()` calls `graph_next_line()` until
+  `graph_is_commit_finished()` returns non-zero.  Output is printed to stdout.
+  The last line printed does not contain a terminating newline.  Returns 1 if
+  output was printed, and 0 if no output was necessary.
+
+* `graph_show_strbuf()` prints the specified strbuf to stdout, prefixing all
+  lines but the first with a graph line.  The caller is responsible for
+  ensuring graph output for the first line has already been printed to stdout.
+  (This can be done with `graph_show_commit()` or `graph_show_oneline()`.)  If
+  a NULL graph is supplied, the strbuf is printed as-is.
+
+* `graph_show_commit_msg()` is similar to `graph_show_strbuf()`, but it also
+  prints the remainder of the graph, if more lines are needed after the strbuf
+  ends.  It is better than directly calling `graph_show_strbuf()` followed by
+  `graph_show_remainder()` since it properly handles buffers that do not end in
+  a terminating newline.  The output printed by `graph_show_commit_msg()` will
+  end in a newline if and only if the strbuf ends in a newline.
+
+Data structure
+--------------
+`struct git_graph` is an opaque data type used to store the current graph
+state.
+
+Calling sequence
+----------------
+
+* Create a `struct git_graph` by calling `graph_init()`.  When using the
+  revision walking API, this is done automatically by `setup_revisions()` if
+  the '--graph' option is supplied.
+
+* Use the revision walking API to walk through a group of contiguous commits.
+  The `get_revision()` function automatically calls `graph_update()` each time
+  it is invoked.
+
+* For each commit, call `graph_next_line()` repeatedly, until
+  `graph_is_commit_finished()` returns non-zero.  Each call go
+  `graph_next_line()` will output a single line of the graph.  The resulting
+  lines will not contain any newlines.  `graph_next_line()` returns 1 if the
+  resulting line contains the current commit, or 0 if this is merely a line
+  needed to adjust the graph before or after the current commit.  This return
+  value can be used to determine where to print the commit summary information
+  alongside the graph output.
+
+Limitations
+-----------
+
+* `graph_update()` must be called with commits in topological order.  It should
+  not be called on a commit if it has already been invoked with an ancestor of
+  that commit, or the graph output will be incorrect.
+
+* `graph_update()` must be called on a contiguous group of commits.  If
+  `graph_update()` is called on a particular commit, it should later be called
+  on all parents of that commit.  Parents must not be skipped, or the graph
+  output will appear incorrect.
++
+`graph_update()` may be used on a pruned set of commits only if the parent list
+has been rewritten so as to include only ancestors from the pruned set.
+
+* The graph API does not currently support reverse commit ordering.  In
+  order to implement reverse ordering, the graphing API needs an
+  (efficient) mechanism to find the children of a commit.
+
+Sample usage
+------------
+
+------------
+struct commit *commit;
+struct git_graph *graph = graph_init(opts);
+
+while ((commit = get_revision(opts)) != NULL) {
+	graph_update(graph, commit);
+	while (!graph_is_commit_finished(graph))
+	{
+		struct strbuf sb;
+		int is_commit_line;
+
+		strbuf_init(&sb, 0);
+		is_commit_line = graph_next_line(graph, &sb);
+		fputs(sb.buf, stdout);
+
+		if (is_commit_line)
+			log_tree_commit(opts, commit);
+		else
+			putchar(opts->diffopt.line_termination);
+	}
+}
+
+graph_release(graph);
+------------
+
+Sample output
+-------------
+
+The following is an example of the output from the graph API.  This output does
+not include any commit summary information--callers are responsible for
+outputting that information, if desired.
+
+------------
+*
+*
+M
+|\
+* |
+| | *
+| \ \
+|  \ \
+M-. \ \
+|\ \ \ \
+| | * | |
+| | | | | *
+| | | | | *
+| | | | | M
+| | | | | |\
+| | | | | | *
+| * | | | | |
+| | | | | M  \
+| | | | | |\  |
+| | | | * | | |
+| | | | * | | |
+* | | | | | | |
+| |/ / / / / /
+|/| / / / / /
+* | | | | | |
+|/ / / / / /
+* | | | | |
+| | | | | *
+| | | | |/
+| | | | *
+------------
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 86b91a5..fd8cdb6 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1881,7 +1881,7 @@
 
 (For an explanation of the last two lines, see
 linkgit:git-update-server-info[1], and the documentation
-link:hooks.html[Hooks used by git].)
+linkgit:githooks[5][Hooks used by git].)
 
 Advertise the URL of proj.git.  Anybody else should then be able to
 clone or pull from that URL, for example with a command line like:
@@ -1993,7 +1993,7 @@
 solution is to retry the push after first updating your work by either a
 pull or a fetch followed by a rebase; see the
 <<setting-up-a-shared-repository,next section>> and
-link:cvs-migration.html[git for CVS users] for more.
+linkgit:gitcvs-migration[7][git for CVS users] for more.
 
 [[setting-up-a-shared-repository]]
 Setting up a shared repository
@@ -2002,7 +2002,7 @@
 Another way to collaborate is by using a model similar to that
 commonly used in CVS, where several developers with special rights
 all push to and pull from a single shared repository.  See
-link:cvs-migration.html[git for CVS users] for instructions on how to
+linkgit:gitcvs-migration[7][git for CVS users] for instructions on how to
 set this up.
 
 However, while there is nothing wrong with git's support for shared
diff --git a/Makefile b/Makefile
index 649ee56..f08d5f7 100644
--- a/Makefile
+++ b/Makefile
@@ -235,7 +235,6 @@
 
 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
@@ -346,6 +345,7 @@
 LIB_H += dir.h
 LIB_H += fsck.h
 LIB_H += git-compat-util.h
+LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
 LIB_H += list-objects.h
@@ -411,6 +411,7 @@
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
 LIB_OBJS += help.o
@@ -482,6 +483,7 @@
 BUILTIN_OBJS += builtin-checkout-index.o
 BUILTIN_OBJS += builtin-checkout.o
 BUILTIN_OBJS += builtin-clean.o
+BUILTIN_OBJS += builtin-clone.o
 BUILTIN_OBJS += builtin-commit-tree.o
 BUILTIN_OBJS += builtin-commit.o
 BUILTIN_OBJS += builtin-config.o
@@ -636,8 +638,12 @@
 ifeq ($(uname_S),AIX)
 	NO_STRCASESTR=YesPlease
 	NO_MEMMEM = YesPlease
+	NO_MKDTEMP = YesPlease
 	NO_STRLCPY = YesPlease
+	FREAD_READS_DIRECTORIES = UnfortunatelyYes
+	INTERNAL_QSORT = UnfortunatelyYes
 	NEEDS_LIBICONV=YesPlease
+	BASIC_CFLAGS += -D_LARGE_FILES
 endif
 ifeq ($(uname_S),GNU)
 	# GNU/Hurd
diff --git a/builtin-add.c b/builtin-add.c
index 15def7d..1da22ee 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -79,12 +79,18 @@
 		prune_directory(dir, pathspec, baselen);
 }
 
+struct update_callback_data
+{
+	int flags;
+	int add_errors;
+};
+
 static void update_callback(struct diff_queue_struct *q,
 			    struct diff_options *opt, void *cbdata)
 {
-	int i, verbose;
+	int i;
+	struct update_callback_data *data = cbdata;
 
-	verbose = *((int *)cbdata);
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
 		const char *path = p->one->path;
@@ -94,27 +100,36 @@
 		case DIFF_STATUS_UNMERGED:
 		case DIFF_STATUS_MODIFIED:
 		case DIFF_STATUS_TYPE_CHANGED:
-			add_file_to_cache(path, verbose);
+			if (add_file_to_cache(path, data->flags)) {
+				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+					die("updating files failed");
+				data->add_errors++;
+			}
 			break;
 		case DIFF_STATUS_DELETED:
-			remove_file_from_cache(path);
-			if (verbose)
+			if (!(data->flags & ADD_CACHE_PRETEND))
+				remove_file_from_cache(path);
+			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
 				printf("remove '%s'\n", path);
 			break;
 		}
 	}
 }
 
-void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
 {
+	struct update_callback_data data;
 	struct rev_info rev;
 	init_revisions(&rev, prefix);
 	setup_revisions(0, NULL, &rev, NULL);
 	rev.prune_data = pathspec;
 	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
 	rev.diffopt.format_callback = update_callback;
-	rev.diffopt.format_callback_data = &verbose;
+	data.flags = flags;
+	data.add_errors = 0;
+	rev.diffopt.format_callback_data = &data;
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+	return !!data.add_errors;
 }
 
 static void refresh(int verbose, const char **pathspec)
@@ -177,6 +192,7 @@
 "The following paths are ignored by one of your .gitignore files:\n";
 
 static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
+static int ignore_add_errors;
 
 static struct option builtin_add_options[] = {
 	OPT__DRY_RUN(&show_only),
@@ -187,14 +203,26 @@
 	OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
 	OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
 	OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
+	OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
 	OPT_END(),
 };
 
+static int add_config(const char *var, const char *value, void *cb)
+{
+	if (!strcasecmp(var, "add.ignore-errors")) {
+		ignore_add_errors = git_config_bool(var, value);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
 int cmd_add(int argc, const char **argv, const char *prefix)
 {
+	int exit_status = 0;
 	int i, newfd;
 	const char **pathspec;
 	struct dir_struct dir;
+	int flags;
 
 	argc = parse_options(argc, argv, builtin_add_options,
 			  builtin_add_usage, 0);
@@ -203,16 +231,20 @@
 	if (add_interactive)
 		exit(interactive_add(argc, argv, prefix));
 
-	git_config(git_default_config, NULL);
+	git_config(add_config, NULL);
 
 	newfd = hold_locked_index(&lock_file, 1);
 
+	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
+		 (show_only ? ADD_CACHE_PRETEND : 0) |
+		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
+
 	if (take_worktree_changes) {
 		const char **pathspec;
 		if (read_cache() < 0)
 			die("index file corrupt");
 		pathspec = get_pathspec(prefix, argv);
-		add_files_to_cache(verbose, prefix, pathspec);
+		exit_status = add_files_to_cache(prefix, pathspec, flags);
 		goto finish;
 	}
 
@@ -230,17 +262,6 @@
 
 	fill_directory(&dir, pathspec, ignored_too);
 
-	if (show_only) {
-		const char *sep = "", *eof = "";
-		for (i = 0; i < dir.nr; i++) {
-			printf("%s%s", sep, dir.entries[i]->name);
-			sep = " ";
-			eof = "\n";
-		}
-		fputs(eof, stdout);
-		return 0;
-	}
-
 	if (read_cache() < 0)
 		die("index file corrupt");
 
@@ -254,7 +275,11 @@
 	}
 
 	for (i = 0; i < dir.nr; i++)
-		add_file_to_cache(dir.entries[i]->name, verbose);
+		if (add_file_to_cache(dir.entries[i]->name, flags)) {
+			if (!ignore_add_errors)
+				die("adding files failed");
+			exit_status = 1;
+		}
 
  finish:
 	if (active_cache_changed) {
@@ -263,5 +288,5 @@
 			die("Unable to write new index file");
 	}
 
-	return 0;
+	return exit_status;
 }
diff --git a/builtin-apply.c b/builtin-apply.c
index bbdf08a..c497889 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -418,7 +418,7 @@
 }
 
 /*
- * Get the name etc info from the --/+++ lines of a traditional patch header
+ * Get the name etc info from the ---/+++ lines of a traditional patch header
  *
  * FIXME! The end-of-filename heuristics are kind of screwy. For existing
  * files, we can happily check the index for a match, but for creating a
@@ -1143,21 +1143,6 @@
 	if (patch->is_delete < 0 &&
 	    (newlines || (patch->fragments && patch->fragments->next)))
 		patch->is_delete = 0;
-	if (!unidiff_zero || context) {
-		/* If the user says the patch is not generated with
-		 * --unified=0, or if we have seen context lines,
-		 * then not having oldlines means the patch is creation,
-		 * and not having newlines means the patch is deletion.
-		 */
-		if (patch->is_new < 0 && !oldlines) {
-			patch->is_new = 1;
-			patch->old_name = NULL;
-		}
-		if (patch->is_delete < 0 && !newlines) {
-			patch->is_delete = 1;
-			patch->new_name = NULL;
-		}
-	}
 
 	if (0 < patch->is_new && oldlines)
 		die("new file %s depends on old contents", patch->new_name);
@@ -2267,6 +2252,79 @@
 	return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
 }
 
+static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
+{
+	const char *old_name = patch->old_name;
+	int stat_ret = 0;
+	unsigned st_mode = 0;
+
+	/*
+	 * Make sure that we do not have local modifications from the
+	 * index when we are looking at the index.  Also make sure
+	 * we have the preimage file to be patched in the work tree,
+	 * unless --cached, which tells git to apply only in the index.
+	 */
+	if (!old_name)
+		return 0;
+
+	assert(patch->is_new <= 0);
+	if (!cached) {
+		stat_ret = lstat(old_name, st);
+		if (stat_ret && errno != ENOENT)
+			return error("%s: %s", old_name, strerror(errno));
+	}
+	if (check_index) {
+		int pos = cache_name_pos(old_name, strlen(old_name));
+		if (pos < 0) {
+			if (patch->is_new < 0)
+				goto is_new;
+			return error("%s: does not exist in index", old_name);
+		}
+		*ce = active_cache[pos];
+		if (stat_ret < 0) {
+			struct checkout costate;
+			/* checkout */
+			costate.base_dir = "";
+			costate.base_dir_len = 0;
+			costate.force = 0;
+			costate.quiet = 0;
+			costate.not_new = 0;
+			costate.refresh_cache = 1;
+			if (checkout_entry(*ce, &costate, NULL) ||
+			    lstat(old_name, st))
+				return -1;
+		}
+		if (!cached && verify_index_match(*ce, st))
+			return error("%s: does not match index", old_name);
+		if (cached)
+			st_mode = (*ce)->ce_mode;
+	} else if (stat_ret < 0) {
+		if (patch->is_new < 0)
+			goto is_new;
+		return error("%s: %s", old_name, strerror(errno));
+	}
+
+	if (!cached)
+		st_mode = ce_mode_from_stat(*ce, st->st_mode);
+
+	if (patch->is_new < 0)
+		patch->is_new = 0;
+	if (!patch->old_mode)
+		patch->old_mode = st_mode;
+	if ((st_mode ^ patch->old_mode) & S_IFMT)
+		return error("%s: wrong type", old_name);
+	if (st_mode != patch->old_mode)
+		fprintf(stderr, "warning: %s has type %o, expected %o\n",
+			old_name, st_mode, patch->old_mode);
+	return 0;
+
+ is_new:
+	patch->is_new = 1;
+	patch->is_delete = 0;
+	patch->old_name = NULL;
+	return 0;
+}
+
 static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
 	struct stat st;
@@ -2275,66 +2333,14 @@
 	const char *name = old_name ? old_name : new_name;
 	struct cache_entry *ce = NULL;
 	int ok_if_exists;
+	int status;
 
 	patch->rejected = 1; /* we will drop this after we succeed */
 
-	/*
-	 * Make sure that we do not have local modifications from the
-	 * index when we are looking at the index.  Also make sure
-	 * we have the preimage file to be patched in the work tree,
-	 * unless --cached, which tells git to apply only in the index.
-	 */
-	if (old_name) {
-		int stat_ret = 0;
-		unsigned st_mode = 0;
-
-		if (!cached)
-			stat_ret = lstat(old_name, &st);
-		if (check_index) {
-			int pos = cache_name_pos(old_name, strlen(old_name));
-			if (pos < 0)
-				return error("%s: does not exist in index",
-					     old_name);
-			ce = active_cache[pos];
-			if (stat_ret < 0) {
-				struct checkout costate;
-				if (errno != ENOENT)
-					return error("%s: %s", old_name,
-						     strerror(errno));
-				/* checkout */
-				costate.base_dir = "";
-				costate.base_dir_len = 0;
-				costate.force = 0;
-				costate.quiet = 0;
-				costate.not_new = 0;
-				costate.refresh_cache = 1;
-				if (checkout_entry(ce,
-						   &costate,
-						   NULL) ||
-				    lstat(old_name, &st))
-					return -1;
-			}
-			if (!cached && verify_index_match(ce, &st))
-				return error("%s: does not match index",
-					     old_name);
-			if (cached)
-				st_mode = ce->ce_mode;
-		} else if (stat_ret < 0)
-			return error("%s: %s", old_name, strerror(errno));
-
-		if (!cached)
-			st_mode = ce_mode_from_stat(ce, st.st_mode);
-
-		if (patch->is_new < 0)
-			patch->is_new = 0;
-		if (!patch->old_mode)
-			patch->old_mode = st_mode;
-		if ((st_mode ^ patch->old_mode) & S_IFMT)
-			return error("%s: wrong type", old_name);
-		if (st_mode != patch->old_mode)
-			fprintf(stderr, "warning: %s has type %o, expected %o\n",
-				old_name, st_mode, patch->old_mode);
-	}
+	status = check_preimage(patch, &ce, &st);
+	if (status)
+		return status;
+	old_name = patch->old_name;
 
 	if (new_name && prev_patch && 0 < prev_patch->is_delete &&
 	    !strcmp(prev_patch->old_name, new_name))
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index b488fad..200345e 100644
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
@@ -8,6 +8,10 @@
 #include "tag.h"
 #include "tree.h"
 #include "builtin.h"
+#include "parse-options.h"
+
+#define BATCH 1
+#define BATCH_CHECK 2
 
 static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
 {
@@ -76,31 +80,16 @@
 		write_or_die(1, cp, endp - cp);
 }
 
-int cmd_cat_file(int argc, const char **argv, const char *prefix)
+static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 {
 	unsigned char sha1[20];
 	enum object_type type;
 	void *buf;
 	unsigned long size;
-	int opt;
-	const char *exp_type, *obj_name;
-
-	git_config(git_default_config, NULL);
-	if (argc != 3)
-		usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
-	exp_type = argv[1];
-	obj_name = argv[2];
 
 	if (get_sha1(obj_name, sha1))
 		die("Not a valid object name %s", obj_name);
 
-	opt = 0;
-	if ( exp_type[0] == '-' ) {
-		opt = exp_type[1];
-		if ( !opt || exp_type[2] )
-			opt = -1; /* Not a single character option */
-	}
-
 	buf = NULL;
 	switch (opt) {
 	case 't':
@@ -157,3 +146,108 @@
 	write_or_die(1, buf, size);
 	return 0;
 }
+
+static int batch_one_object(const char *obj_name, int print_contents)
+{
+	unsigned char sha1[20];
+	enum object_type type;
+	unsigned long size;
+	void *contents = contents;
+
+	if (!obj_name)
+	   return 1;
+
+	if (get_sha1(obj_name, sha1)) {
+		printf("%s missing\n", obj_name);
+		return 0;
+	}
+
+	if (print_contents == BATCH)
+		contents = read_sha1_file(sha1, &type, &size);
+	else
+		type = sha1_object_info(sha1, &size);
+
+	if (type <= 0)
+		return 1;
+
+	printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
+	fflush(stdout);
+
+	if (print_contents == BATCH) {
+		write_or_die(1, contents, size);
+		printf("\n");
+		fflush(stdout);
+	}
+
+	return 0;
+}
+
+static int batch_objects(int print_contents)
+{
+	struct strbuf buf;
+
+	strbuf_init(&buf, 0);
+	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+		int error = batch_one_object(buf.buf, print_contents);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+static const char * const cat_file_usage[] = {
+	"git-cat-file [-t|-s|-e|-p|<type>] <sha1>",
+	"git-cat-file [--batch|--batch-check] < <list_of_sha1s>",
+	NULL
+};
+
+int cmd_cat_file(int argc, const char **argv, const char *prefix)
+{
+	int opt = 0, batch = 0;
+	const char *exp_type = NULL, *obj_name = NULL;
+
+	const struct option options[] = {
+		OPT_GROUP("<type> can be one of: blob, tree, commit, tag"),
+		OPT_SET_INT('t', NULL, &opt, "show object type", 't'),
+		OPT_SET_INT('s', NULL, &opt, "show object size", 's'),
+		OPT_SET_INT('e', NULL, &opt,
+			    "exit with zero when there's no error", 'e'),
+		OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
+		OPT_SET_INT(0, "batch", &batch,
+			    "show info and content of objects feeded on stdin", BATCH),
+		OPT_SET_INT(0, "batch-check", &batch,
+			    "show info about objects feeded on stdin",
+			    BATCH_CHECK),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+
+	if (argc != 3 && argc != 2)
+		usage_with_options(cat_file_usage, options);
+
+	argc = parse_options(argc, argv, options, cat_file_usage, 0);
+
+	if (opt) {
+		if (argc == 1)
+			obj_name = argv[0];
+		else
+			usage_with_options(cat_file_usage, options);
+	}
+	if (!opt && !batch) {
+		if (argc == 2) {
+			exp_type = argv[0];
+			obj_name = argv[1];
+		} else
+			usage_with_options(cat_file_usage, options);
+	}
+	if (batch && (opt || argc)) {
+		usage_with_options(cat_file_usage, options);
+	}
+
+	if (batch)
+		return batch_objects(batch);
+
+	return cat_one_file(opt, exp_type, obj_name);
+}
diff --git a/builtin-checkout.c b/builtin-checkout.c
index c077134..1ea017f 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -236,6 +236,8 @@
 		topts.src_index = &the_index;
 		topts.dst_index = &the_index;
 
+		topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
+
 		refresh_cache(REFRESH_QUIET);
 
 		if (unmerged_cache()) {
@@ -282,7 +284,7 @@
 			 * entries in the index.
 			 */
 
-			add_files_to_cache(0, NULL, NULL);
+			add_files_to_cache(NULL, NULL, 0);
 			work = write_tree_from_memory();
 
 			ret = reset_to_new(new->commit->tree, opts->quiet);
diff --git a/builtin-clone.c b/builtin-clone.c
new file mode 100644
index 0000000..4740b13
--- /dev/null
+++ b/builtin-clone.c
@@ -0,0 +1,550 @@
+/*
+ * Builtin "git clone"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ *		 2008 Daniel Barkalow <barkalow@iabervon.org>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ *
+ * Clone a repository into a different directory that does not yet exist.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "fetch-pack.h"
+#include "refs.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "transport.h"
+#include "strbuf.h"
+#include "dir.h"
+
+/*
+ * Overall FIXMEs:
+ *  - respect DB_ENVIRONMENT for .git/objects.
+ *
+ * Implementation notes:
+ *  - dropping use-separate-remote and no-separate-remote compatibility
+ *
+ */
+static const char * const builtin_clone_usage[] = {
+	"git-clone [options] [--] <repo> [<dir>]",
+	NULL
+};
+
+static int option_quiet, option_no_checkout, option_bare;
+static int option_local, option_no_hardlinks, option_shared;
+static char *option_template, *option_reference, *option_depth;
+static char *option_origin = NULL;
+static char *option_upload_pack = "git-upload-pack";
+
+static struct option builtin_clone_options[] = {
+	OPT__QUIET(&option_quiet),
+	OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
+		    "don't create a checkout"),
+	OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
+	OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"),
+	OPT_BOOLEAN('l', "local", &option_local,
+		    "to clone from a local repository"),
+	OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
+		    "don't use local hardlinks, always copy"),
+	OPT_BOOLEAN('s', "shared", &option_shared,
+		    "setup as shared repository"),
+	OPT_STRING(0, "template", &option_template, "path",
+		   "path the template repository"),
+	OPT_STRING(0, "reference", &option_reference, "repo",
+		   "reference repository"),
+	OPT_STRING('o', "origin", &option_origin, "branch",
+		   "use <branch> instead or 'origin' to track upstream"),
+	OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
+		   "path to git-upload-pack on the remote"),
+	OPT_STRING(0, "depth", &option_depth, "depth",
+		    "create a shallow clone of that depth"),
+
+	OPT_END()
+};
+
+static char *get_repo_path(const char *repo, int *is_bundle)
+{
+	static char *suffix[] = { "/.git", ".git", "" };
+	static char *bundle_suffix[] = { ".bundle", "" };
+	struct stat st;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+		const char *path;
+		path = mkpath("%s%s", repo, suffix[i]);
+		if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
+			*is_bundle = 0;
+			return xstrdup(make_absolute_path(path));
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
+		const char *path;
+		path = mkpath("%s%s", repo, bundle_suffix[i]);
+		if (!stat(path, &st) && S_ISREG(st.st_mode)) {
+			*is_bundle = 1;
+			return xstrdup(make_absolute_path(path));
+		}
+	}
+
+	return NULL;
+}
+
+static char *guess_dir_name(const char *repo, int is_bundle)
+{
+	const char *p, *start, *end, *limit;
+	int after_slash_or_colon;
+
+	/* Guess dir name from repository: strip trailing '/',
+	 * strip trailing '[:/]*.{git,bundle}', strip leading '.*[/:]'. */
+
+	after_slash_or_colon = 1;
+	limit = repo + strlen(repo);
+	start = repo;
+	end = limit;
+	for (p = repo; p < limit; p++) {
+		const char *prefix = is_bundle ? ".bundle" : ".git";
+		if (!prefixcmp(p, prefix)) {
+			if (!after_slash_or_colon)
+				end = p;
+			p += strlen(prefix) - 1;
+		} else if (!prefixcmp(p, ".bundle")) {
+			if (!after_slash_or_colon)
+				end = p;
+			p += 7;
+		} else if (*p == '/' || *p == ':') {
+			if (end == limit)
+				end = p;
+			after_slash_or_colon = 1;
+		} else if (after_slash_or_colon) {
+			start = p;
+			end = limit;
+			after_slash_or_colon = 0;
+		}
+	}
+
+	return xstrndup(start, end - start);
+}
+
+static int is_directory(const char *path)
+{
+	struct stat buf;
+
+	return !stat(path, &buf) && S_ISDIR(buf.st_mode);
+}
+
+static void setup_reference(const char *repo)
+{
+	const char *ref_git;
+	char *ref_git_copy;
+
+	struct remote *remote;
+	struct transport *transport;
+	const struct ref *extra;
+
+	ref_git = make_absolute_path(option_reference);
+
+	if (is_directory(mkpath("%s/.git/objects", ref_git)))
+		ref_git = mkpath("%s/.git", ref_git);
+	else if (!is_directory(mkpath("%s/objects", ref_git)))
+		die("reference repository '%s' is not a local directory.",
+		    option_reference);
+
+	ref_git_copy = xstrdup(ref_git);
+
+	add_to_alternates_file(ref_git_copy);
+
+	remote = remote_get(ref_git_copy);
+	transport = transport_get(remote, ref_git_copy);
+	for (extra = transport_get_remote_refs(transport); extra;
+	     extra = extra->next)
+		add_extra_ref(extra->name, extra->old_sha1, 0);
+
+	transport_disconnect(transport);
+
+	free(ref_git_copy);
+}
+
+static void copy_or_link_directory(char *src, char *dest)
+{
+	struct dirent *de;
+	struct stat buf;
+	int src_len, dest_len;
+	DIR *dir;
+
+	dir = opendir(src);
+	if (!dir)
+		die("failed to open %s\n", src);
+
+	if (mkdir(dest, 0777)) {
+		if (errno != EEXIST)
+			die("failed to create directory %s\n", dest);
+		else if (stat(dest, &buf))
+			die("failed to stat %s\n", dest);
+		else if (!S_ISDIR(buf.st_mode))
+			die("%s exists and is not a directory\n", dest);
+	}
+
+	src_len = strlen(src);
+	src[src_len] = '/';
+	dest_len = strlen(dest);
+	dest[dest_len] = '/';
+
+	while ((de = readdir(dir)) != NULL) {
+		strcpy(src + src_len + 1, de->d_name);
+		strcpy(dest + dest_len + 1, de->d_name);
+		if (stat(src, &buf)) {
+			warning ("failed to stat %s\n", src);
+			continue;
+		}
+		if (S_ISDIR(buf.st_mode)) {
+			if (de->d_name[0] != '.')
+				copy_or_link_directory(src, dest);
+			continue;
+		}
+
+		if (unlink(dest) && errno != ENOENT)
+			die("failed to unlink %s\n", dest);
+		if (!option_no_hardlinks) {
+			if (!link(src, dest))
+				continue;
+			if (option_local)
+				die("failed to create link %s\n", dest);
+			option_no_hardlinks = 1;
+		}
+		if (copy_file(dest, src, 0666))
+			die("failed to copy file to %s\n", dest);
+	}
+	closedir(dir);
+}
+
+static const struct ref *clone_local(const char *src_repo,
+				     const char *dest_repo)
+{
+	const struct ref *ret;
+	char src[PATH_MAX];
+	char dest[PATH_MAX];
+	struct remote *remote;
+	struct transport *transport;
+
+	if (option_shared)
+		add_to_alternates_file(src_repo);
+	else {
+		snprintf(src, PATH_MAX, "%s/objects", src_repo);
+		snprintf(dest, PATH_MAX, "%s/objects", dest_repo);
+		copy_or_link_directory(src, dest);
+	}
+
+	remote = remote_get(src_repo);
+	transport = transport_get(remote, src_repo);
+	ret = transport_get_remote_refs(transport);
+	transport_disconnect(transport);
+	return ret;
+}
+
+static const char *junk_work_tree;
+static const char *junk_git_dir;
+pid_t junk_pid;
+
+static void remove_junk(void)
+{
+	struct strbuf sb;
+	if (getpid() != junk_pid)
+		return;
+	strbuf_init(&sb, 0);
+	if (junk_git_dir) {
+		strbuf_addstr(&sb, junk_git_dir);
+		remove_dir_recursively(&sb, 0);
+		strbuf_reset(&sb);
+	}
+	if (junk_work_tree) {
+		strbuf_addstr(&sb, junk_work_tree);
+		remove_dir_recursively(&sb, 0);
+		strbuf_reset(&sb);
+	}
+}
+
+static void remove_junk_on_signal(int signo)
+{
+	remove_junk();
+	signal(SIGINT, SIG_DFL);
+	raise(signo);
+}
+
+static const struct ref *locate_head(const struct ref *refs,
+				     const struct ref *mapped_refs,
+				     const struct ref **remote_head_p)
+{
+	const struct ref *remote_head = NULL;
+	const struct ref *remote_master = NULL;
+	const struct ref *r;
+	for (r = refs; r; r = r->next)
+		if (!strcmp(r->name, "HEAD"))
+			remote_head = r;
+
+	for (r = mapped_refs; r; r = r->next)
+		if (!strcmp(r->name, "refs/heads/master"))
+			remote_master = r;
+
+	if (remote_head_p)
+		*remote_head_p = remote_head;
+
+	/* If there's no HEAD value at all, never mind. */
+	if (!remote_head)
+		return NULL;
+
+	/* If refs/heads/master could be right, it is. */
+	if (remote_master && !hashcmp(remote_master->old_sha1,
+				      remote_head->old_sha1))
+		return remote_master;
+
+	/* Look for another ref that points there */
+	for (r = mapped_refs; r; r = r->next)
+		if (r != remote_head &&
+		    !hashcmp(r->old_sha1, remote_head->old_sha1))
+			return r;
+
+	/* Nothing is the same */
+	return NULL;
+}
+
+static struct ref *write_remote_refs(const struct ref *refs,
+		struct refspec *refspec, const char *reflog)
+{
+	struct ref *local_refs = NULL;
+	struct ref **tail = &local_refs;
+	struct ref *r;
+
+	get_fetch_map(refs, refspec, &tail, 0);
+	get_fetch_map(refs, tag_refspec, &tail, 0);
+
+	for (r = local_refs; r; r = r->next)
+		update_ref(reflog,
+			   r->peer_ref->name, r->old_sha1, NULL, 0, DIE_ON_ERR);
+	return local_refs;
+}
+
+int cmd_clone(int argc, const char **argv, const char *prefix)
+{
+	int use_local_hardlinks = 1;
+	int use_separate_remote = 1;
+	int is_bundle = 0;
+	struct stat buf;
+	const char *repo_name, *repo, *work_tree, *git_dir;
+	char *path, *dir;
+	const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
+	char branch_top[256], key[256], value[256];
+	struct strbuf reflog_msg;
+
+	struct refspec refspec;
+
+	junk_pid = getpid();
+
+	argc = parse_options(argc, argv, builtin_clone_options,
+			     builtin_clone_usage, 0);
+
+	if (argc == 0)
+		die("You must specify a repository to clone.");
+
+	if (option_no_hardlinks)
+		use_local_hardlinks = 0;
+
+	if (option_bare) {
+		if (option_origin)
+			die("--bare and --origin %s options are incompatible.",
+			    option_origin);
+		option_no_checkout = 1;
+		use_separate_remote = 0;
+	}
+
+	if (!option_origin)
+		option_origin = "origin";
+
+	repo_name = argv[0];
+
+	path = get_repo_path(repo_name, &is_bundle);
+	if (path)
+		repo = path;
+	else if (!strchr(repo_name, ':'))
+		repo = xstrdup(make_absolute_path(repo_name));
+	else
+		repo = repo_name;
+
+	if (argc == 2)
+		dir = xstrdup(argv[1]);
+	else
+		dir = guess_dir_name(repo_name, is_bundle);
+
+	if (!stat(dir, &buf))
+		die("destination directory '%s' already exists.", dir);
+
+	strbuf_init(&reflog_msg, 0);
+	strbuf_addf(&reflog_msg, "clone: from %s", repo);
+
+	if (option_bare)
+		work_tree = NULL;
+	else {
+		work_tree = getenv("GIT_WORK_TREE");
+		if (work_tree && !stat(work_tree, &buf))
+			die("working tree '%s' already exists.", work_tree);
+	}
+
+	if (option_bare || work_tree)
+		git_dir = xstrdup(dir);
+	else {
+		work_tree = dir;
+		git_dir = xstrdup(mkpath("%s/.git", dir));
+	}
+
+	if (!option_bare) {
+		junk_work_tree = work_tree;
+		if (mkdir(work_tree, 0755))
+			die("could not create work tree dir '%s'.", work_tree);
+		set_git_work_tree(work_tree);
+	}
+	junk_git_dir = git_dir;
+	atexit(remove_junk);
+	signal(SIGINT, remove_junk_on_signal);
+
+	setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);
+
+	set_git_dir(make_absolute_path(git_dir));
+
+	fprintf(stderr, "Initialize %s\n", git_dir);
+	init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);
+
+	if (option_reference)
+		setup_reference(git_dir);
+
+	git_config(git_default_config, NULL);
+
+	if (option_bare) {
+		strcpy(branch_top, "refs/heads/");
+
+		git_config_set("core.bare", "true");
+	} else {
+		snprintf(branch_top, sizeof(branch_top),
+			 "refs/remotes/%s/", option_origin);
+
+		/* Configure the remote */
+		snprintf(key, sizeof(key), "remote.%s.url", option_origin);
+		git_config_set(key, repo);
+
+		snprintf(key, sizeof(key), "remote.%s.fetch", option_origin);
+		snprintf(value, sizeof(value),
+				"+refs/heads/*:%s*", branch_top);
+		git_config_set_multivar(key, value, "^$", 0);
+	}
+
+	refspec.force = 0;
+	refspec.pattern = 1;
+	refspec.src = "refs/heads/";
+	refspec.dst = branch_top;
+
+	if (path && !is_bundle)
+		refs = clone_local(path, git_dir);
+	else {
+		struct remote *remote = remote_get(argv[0]);
+		struct transport *transport = transport_get(remote, argv[0]);
+
+		transport_set_option(transport, TRANS_OPT_KEEP, "yes");
+
+		if (option_depth)
+			transport_set_option(transport, TRANS_OPT_DEPTH,
+					     option_depth);
+
+		if (option_quiet)
+			transport->verbose = -1;
+
+		refs = transport_get_remote_refs(transport);
+		transport_fetch_refs(transport, refs);
+	}
+
+	clear_extra_refs();
+
+	mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
+
+	head_points_at = locate_head(refs, mapped_refs, &remote_head);
+
+	if (head_points_at) {
+		/* Local default branch link */
+		create_symref("HEAD", head_points_at->name, NULL);
+
+		if (!option_bare) {
+			struct strbuf head_ref;
+			const char *head = head_points_at->name;
+
+			if (!prefixcmp(head, "refs/heads/"))
+				head += 11;
+
+			/* Set up the initial local branch */
+
+			/* Local branch initial value */
+			update_ref(reflog_msg.buf, "HEAD",
+				   head_points_at->old_sha1,
+				   NULL, 0, DIE_ON_ERR);
+
+			strbuf_init(&head_ref, 0);
+			strbuf_addstr(&head_ref, branch_top);
+			strbuf_addstr(&head_ref, "HEAD");
+
+			/* Remote branch link */
+			create_symref(head_ref.buf,
+				      head_points_at->peer_ref->name,
+				      reflog_msg.buf);
+
+			snprintf(key, sizeof(key), "branch.%s.remote", head);
+			git_config_set(key, option_origin);
+			snprintf(key, sizeof(key), "branch.%s.merge", head);
+			git_config_set(key, head_points_at->name);
+		}
+	} else if (remote_head) {
+		/* Source had detached HEAD pointing somewhere. */
+		if (!option_bare)
+			update_ref(reflog_msg.buf, "HEAD",
+				   remote_head->old_sha1,
+				   NULL, REF_NODEREF, DIE_ON_ERR);
+	} else {
+		/* Nothing to checkout out */
+		if (!option_no_checkout)
+			warning("remote HEAD refers to nonexistent ref, "
+				"unable to checkout.\n");
+		option_no_checkout = 1;
+	}
+
+	if (!option_no_checkout) {
+		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+		struct unpack_trees_options opts;
+		struct tree *tree;
+		struct tree_desc t;
+		int fd;
+
+		/* We need to be in the new work tree for the checkout */
+		setup_work_tree();
+
+		fd = hold_locked_index(lock_file, 1);
+
+		memset(&opts, 0, sizeof opts);
+		opts.update = 1;
+		opts.merge = 1;
+		opts.fn = oneway_merge;
+		opts.verbose_update = !option_quiet;
+		opts.src_index = &the_index;
+		opts.dst_index = &the_index;
+
+		tree = parse_tree_indirect(remote_head->old_sha1);
+		parse_tree(tree);
+		init_tree_desc(&t, tree->buffer, tree->size);
+		unpack_trees(1, &t, &opts);
+
+		if (write_cache(fd, active_cache, active_nr) ||
+		    commit_locked_index(lock_file))
+			die("unable to write new index file");
+	}
+
+	strbuf_release(&reflog_msg);
+	junk_pid = 0;
+	return 0;
+}
diff --git a/builtin-commit.c b/builtin-commit.c
index 9f0026e..07872c8 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -47,6 +47,7 @@
 
 static char *logfile, *force_author, *template_file;
 static char *edit_message, *use_message;
+static char *author_name, *author_email, *author_date;
 static int all, edit_flag, also, interactive, only, amend, signoff;
 static int quiet, verbose, untracked_files, no_verify, allow_empty;
 /*
@@ -178,9 +179,10 @@
 		struct stat st;
 		struct path_list_item *p = &(list->items[i]);
 
-		if (!lstat(p->path, &st))
-			add_to_cache(p->path, &st, 0);
-		else
+		if (!lstat(p->path, &st)) {
+			if (add_to_cache(p->path, &st, 0))
+				die("updating files failed");
+		} else
 			remove_file_from_cache(p->path);
 	}
 }
@@ -245,7 +247,7 @@
 	 */
 	if (all || (also && pathspec && *pathspec)) {
 		int fd = hold_locked_index(&index_lock, 1);
-		add_files_to_cache(0, also ? prefix : NULL, pathspec);
+		add_files_to_cache(also ? prefix : NULL, pathspec, 0);
 		refresh_cache(REFRESH_QUIET);
 		if (write_cache(fd, active_cache, active_nr) ||
 		    close_lock_file(&index_lock))
@@ -397,6 +399,47 @@
 
 static const char sign_off_header[] = "Signed-off-by: ";
 
+static void determine_author_info(void)
+{
+	char *name, *email, *date;
+
+	name = getenv("GIT_AUTHOR_NAME");
+	email = getenv("GIT_AUTHOR_EMAIL");
+	date = getenv("GIT_AUTHOR_DATE");
+
+	if (use_message) {
+		const char *a, *lb, *rb, *eol;
+
+		a = strstr(use_message_buffer, "\nauthor ");
+		if (!a)
+			die("invalid commit: %s", use_message);
+
+		lb = strstr(a + 8, " <");
+		rb = strstr(a + 8, "> ");
+		eol = strchr(a + 8, '\n');
+		if (!lb || !rb || !eol)
+			die("invalid commit: %s", use_message);
+
+		name = xstrndup(a + 8, lb - (a + 8));
+		email = xstrndup(lb + 2, rb - (lb + 2));
+		date = xstrndup(rb + 2, eol - (rb + 2));
+	}
+
+	if (force_author) {
+		const char *lb = strstr(force_author, " <");
+		const char *rb = strchr(force_author, '>');
+
+		if (!lb || !rb)
+			die("malformed --author parameter");
+		name = xstrndup(force_author, lb - force_author);
+		email = xstrndup(lb + 2, rb - (lb + 2));
+	}
+
+	author_name = name;
+	author_email = email;
+	author_date = date;
+}
+
 static int prepare_to_commit(const char *index_file, const char *prefix)
 {
 	struct stat statbuf;
@@ -406,6 +449,7 @@
 	FILE *fp;
 	const char *hook_arg1 = NULL;
 	const char *hook_arg2 = NULL;
+	int ident_shown = 0;
 
 	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
 		return 0;
@@ -485,7 +529,14 @@
 
 	strbuf_release(&sb);
 
+	determine_author_info();
+
+	/* This checks if committer ident is explicitly given */
+	git_committer_info(0);
 	if (use_editor) {
+		char *author_ident;
+		const char *committer_ident;
+
 		if (in_merge)
 			fprintf(fp,
 				"#\n"
@@ -508,6 +559,27 @@
 		if (only_include_assumed)
 			fprintf(fp, "# %s\n", only_include_assumed);
 
+		author_ident = xstrdup(fmt_name(author_name, author_email));
+		committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
+					   getenv("GIT_COMMITTER_EMAIL"));
+		if (strcmp(author_ident, committer_ident))
+			fprintf(fp,
+				"%s"
+				"# Author:    %s\n",
+				ident_shown++ ? "" : "#\n",
+				author_ident);
+		free(author_ident);
+
+		if (!user_ident_explicitly_given)
+			fprintf(fp,
+				"%s"
+				"# Committer: %s\n",
+				ident_shown++ ? "" : "#\n",
+				committer_ident);
+
+		if (ident_shown)
+			fprintf(fp, "#\n");
+
 		saved_color_setting = wt_status_use_color;
 		wt_status_use_color = 0;
 		commitable = run_status(fp, index_file, prefix, 1);
@@ -624,45 +696,6 @@
 	return 1;
 }
 
-static void determine_author_info(struct strbuf *sb)
-{
-	char *name, *email, *date;
-
-	name = getenv("GIT_AUTHOR_NAME");
-	email = getenv("GIT_AUTHOR_EMAIL");
-	date = getenv("GIT_AUTHOR_DATE");
-
-	if (use_message) {
-		const char *a, *lb, *rb, *eol;
-
-		a = strstr(use_message_buffer, "\nauthor ");
-		if (!a)
-			die("invalid commit: %s", use_message);
-
-		lb = strstr(a + 8, " <");
-		rb = strstr(a + 8, "> ");
-		eol = strchr(a + 8, '\n');
-		if (!lb || !rb || !eol)
-			die("invalid commit: %s", use_message);
-
-		name = xstrndup(a + 8, lb - (a + 8));
-		email = xstrndup(lb + 2, rb - (lb + 2));
-		date = xstrndup(rb + 2, eol - (rb + 2));
-	}
-
-	if (force_author) {
-		const char *lb = strstr(force_author, " <");
-		const char *rb = strchr(force_author, '>');
-
-		if (!lb || !rb)
-			die("malformed --author parameter");
-		name = xstrndup(force_author, lb - force_author);
-		email = xstrndup(lb + 2, rb - (lb + 2));
-	}
-
-	strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME));
-}
-
 static int parse_and_validate_options(int argc, const char *argv[],
 				      const char * const usage[])
 {
@@ -922,7 +955,8 @@
 		strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
 	}
 
-	determine_author_info(&sb);
+	strbuf_addf(&sb, "author %s\n",
+		    fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME));
 	strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
 	if (!is_encoding_utf8(git_commit_encoding))
 		strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
diff --git a/builtin-fetch.c b/builtin-fetch.c
index f6584ec..bfe7711 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -127,14 +127,8 @@
 		/* Merge everything on the command line, but not --tags */
 		for (rm = ref_map; rm; rm = rm->next)
 			rm->merge = 1;
-		if (tags == TAGS_SET) {
-			struct refspec refspec;
-			refspec.src = "refs/tags/";
-			refspec.dst = "refs/tags/";
-			refspec.pattern = 1;
-			refspec.force = 0;
-			get_fetch_map(remote_refs, &refspec, &tail, 0);
-		}
+		if (tags == TAGS_SET)
+			get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 	} else {
 		/* Use the defaults */
 		struct remote *remote = transport->remote;
diff --git a/builtin-gc.c b/builtin-gc.c
index e1a3299..f5625bb 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -219,7 +219,7 @@
 	char buf[80];
 
 	struct option builtin_gc_options[] = {
-		OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
+		OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
 		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"),
@@ -249,24 +249,14 @@
 		/*
 		 * Auto-gc should be least intrusive as possible.
 		 */
-		prune = 0;
 		if (!need_to_gc())
 			return 0;
 		fprintf(stderr, "Auto packing your repository for optimum "
 			"performance. You may also\n"
 			"run \"git gc\" manually. See "
 			"\"git help gc\" for more information.\n");
-	} else {
-		/*
-		 * Use safer (for shared repos) "-A" option to
-		 * repack when not pruning. Auto-gc makes its
-		 * own decision.
-		 */
-		if (prune)
-			append_option(argv_repack, "-a", MAX_ADD);
-		else
-			append_option(argv_repack, "-A", MAX_ADD);
-	}
+	} else
+		append_option(argv_repack, "-A", MAX_ADD);
 
 	if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
 		return error(FAILED_RUN, argv_pack_refs[0]);
diff --git a/builtin-init-db.c b/builtin-init-db.c
index f6aa353..d8bdf92 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -104,12 +104,14 @@
 	}
 }
 
-static void copy_templates(const char *git_dir, int len, const char *template_dir)
+static void copy_templates(const char *template_dir)
 {
 	char path[PATH_MAX];
 	char template_path[PATH_MAX];
 	int template_len;
 	DIR *dir;
+	const char *git_dir = get_git_dir();
+	int len = strlen(git_dir);
 
 	if (!template_dir)
 		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
@@ -156,6 +158,8 @@
 	}
 
 	memcpy(path, git_dir, len);
+	if (len && path[len - 1] != '/')
+		path[len++] = '/';
 	path[len] = 0;
 	copy_templates_1(path, len,
 			 template_path, template_len,
@@ -163,8 +167,9 @@
 	closedir(dir);
 }
 
-static int create_default_files(const char *git_dir, const char *template_path)
+static int create_default_files(const char *template_path)
 {
+	const char *git_dir = get_git_dir();
 	unsigned len = strlen(git_dir);
 	static char path[PATH_MAX];
 	struct stat st1;
@@ -183,19 +188,15 @@
 	/*
 	 * Create .git/refs/{heads,tags}
 	 */
-	strcpy(path + len, "refs");
-	safe_create_dir(path, 1);
-	strcpy(path + len, "refs/heads");
-	safe_create_dir(path, 1);
-	strcpy(path + len, "refs/tags");
-	safe_create_dir(path, 1);
+	safe_create_dir(git_path("refs"), 1);
+	safe_create_dir(git_path("refs/heads"), 1);
+	safe_create_dir(git_path("refs/tags"), 1);
 
 	/* First copy the templates -- we might have the default
 	 * config file there, in which case we would want to read
 	 * from it after installing.
 	 */
-	path[len] = 0;
-	copy_templates(path, len, template_path);
+	copy_templates(template_path);
 
 	git_config(git_default_config, NULL);
 
@@ -204,14 +205,10 @@
 	 * shared-repository settings, we would need to fix them up.
 	 */
 	if (shared_repository) {
-		path[len] = 0;
-		adjust_shared_perm(path);
-		strcpy(path + len, "refs");
-		adjust_shared_perm(path);
-		strcpy(path + len, "refs/heads");
-		adjust_shared_perm(path);
-		strcpy(path + len, "refs/tags");
-		adjust_shared_perm(path);
+		adjust_shared_perm(get_git_dir());
+		adjust_shared_perm(git_path("refs"));
+		adjust_shared_perm(git_path("refs/heads"));
+		adjust_shared_perm(git_path("refs/tags"));
 	}
 
 	/*
@@ -251,12 +248,14 @@
 		/* allow template config file to override the default */
 		if (log_all_ref_updates == -1)
 		    git_config_set("core.logallrefupdates", "true");
-		if (work_tree != git_work_tree_cfg)
+		if (prefixcmp(git_dir, work_tree) ||
+		    strcmp(git_dir + strlen(work_tree), "/.git")) {
 			git_config_set("core.worktree", work_tree);
+		}
 	}
 
-	/* Check if symlink is supported in the work tree */
 	if (!reinit) {
+		/* Check if symlink is supported in the work tree */
 		path[len] = 0;
 		strcpy(path + len, "tXXXXXX");
 		if (!close(xmkstemp(path)) &&
@@ -267,111 +266,24 @@
 			unlink(path); /* good */
 		else
 			git_config_set("core.symlinks", "false");
+
+		/* Check if the filesystem is case-insensitive */
+		path[len] = 0;
+		strcpy(path + len, "CoNfIg");
+		if (!access(path, F_OK))
+			git_config_set("core.ignorecase", "true");
 	}
 
 	return reinit;
 }
 
-static void guess_repository_type(const char *git_dir)
+int init_db(const char *template_dir, unsigned int flags)
 {
-	char cwd[PATH_MAX];
-	const char *slash;
-
-	if (0 <= is_bare_repository_cfg)
-		return;
-	if (!git_dir)
-		return;
-
-	/*
-	 * "GIT_DIR=. git init" is always bare.
-	 * "GIT_DIR=`pwd` git init" too.
-	 */
-	if (!strcmp(".", git_dir))
-		goto force_bare;
-	if (!getcwd(cwd, sizeof(cwd)))
-		die("cannot tell cwd");
-	if (!strcmp(git_dir, cwd))
-		goto force_bare;
-	/*
-	 * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
-	 */
-	if (!strcmp(git_dir, ".git"))
-		return;
-	slash = strrchr(git_dir, '/');
-	if (slash && !strcmp(slash, "/.git"))
-		return;
-
-	/*
-	 * Otherwise it is often bare.  At this point
-	 * we are just guessing.
-	 */
- force_bare:
-	is_bare_repository_cfg = 1;
-	return;
-}
-
-static const char init_db_usage[] =
-"git-init [-q | --quiet] [--template=<template-directory>] [--shared]";
-
-/*
- * If you want to, you can share the DB area with any number of branches.
- * That has advantages: you can save space by sharing all the SHA1 objects.
- * On the other hand, it might just make lookup slower and messier. You
- * be the judge.  The default case is to have one DB per managed directory.
- */
-int cmd_init_db(int argc, const char **argv, const char *prefix)
-{
-	const char *git_dir;
 	const char *sha1_dir;
-	const char *template_dir = NULL;
 	char *path;
-	int len, i, reinit;
-	int quiet = 0;
+	int len, reinit;
 
-	for (i = 1; i < argc; i++, argv++) {
-		const char *arg = argv[1];
-		if (!prefixcmp(arg, "--template="))
-			template_dir = arg+11;
-		else if (!strcmp(arg, "--shared"))
-			shared_repository = PERM_GROUP;
-		else if (!prefixcmp(arg, "--shared="))
-			shared_repository = git_config_perm("arg", arg+9);
-		else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
-		        quiet = 1;
-		else
-			usage(init_db_usage);
-	}
-
-	/*
-	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
-	 * without --bare.  Catch the error early.
-	 */
-	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if ((!git_dir || is_bare_repository_cfg == 1)
-	    && getenv(GIT_WORK_TREE_ENVIRONMENT))
-		die("%s (or --work-tree=<directory>) not allowed without "
-		    "specifying %s (or --git-dir=<directory>)",
-		    GIT_WORK_TREE_ENVIRONMENT,
-		    GIT_DIR_ENVIRONMENT);
-
-	guess_repository_type(git_dir);
-
-	if (is_bare_repository_cfg <= 0) {
-		git_work_tree_cfg = xcalloc(PATH_MAX, 1);
-		if (!getcwd(git_work_tree_cfg, PATH_MAX))
-			die ("Cannot access current working directory.");
-		if (access(get_git_work_tree(), X_OK))
-			die ("Cannot access work tree '%s'",
-			     get_git_work_tree());
-	}
-
-	/*
-	 * Set up the default .git directory contents
-	 */
-	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if (!git_dir)
-		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
-	safe_create_dir(git_dir, 0);
+	safe_create_dir(get_git_dir(), 0);
 
 	/* Check to see if the repository version is right.
 	 * Note that a newly created repository does not have
@@ -380,11 +292,8 @@
 	 */
 	check_repository_format();
 
-	reinit = create_default_files(git_dir, template_dir);
+	reinit = create_default_files(template_dir);
 
-	/*
-	 * And set up the object store.
-	 */
 	sha1_dir = get_object_directory();
 	len = strlen(sha1_dir);
 	path = xmalloc(len + 40);
@@ -414,11 +323,117 @@
 		git_config_set("receive.denyNonFastforwards", "true");
 	}
 
-	if (!quiet)
+	if (!(flags & INIT_DB_QUIET))
 		printf("%s%s Git repository in %s/\n",
 		       reinit ? "Reinitialized existing" : "Initialized empty",
 		       shared_repository ? " shared" : "",
-		       git_dir);
+		       get_git_dir());
 
 	return 0;
 }
+
+static int guess_repository_type(const char *git_dir)
+{
+	char cwd[PATH_MAX];
+	const char *slash;
+
+	/*
+	 * "GIT_DIR=. git init" is always bare.
+	 * "GIT_DIR=`pwd` git init" too.
+	 */
+	if (!strcmp(".", git_dir))
+		return 1;
+	if (!getcwd(cwd, sizeof(cwd)))
+		die("cannot tell cwd");
+	if (!strcmp(git_dir, cwd))
+		return 1;
+	/*
+	 * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
+	 */
+	if (!strcmp(git_dir, ".git"))
+		return 0;
+	slash = strrchr(git_dir, '/');
+	if (slash && !strcmp(slash, "/.git"))
+		return 0;
+
+	/*
+	 * Otherwise it is often bare.  At this point
+	 * we are just guessing.
+	 */
+	return 1;
+}
+
+static const char init_db_usage[] =
+"git-init [-q | --quiet] [--template=<template-directory>] [--shared]";
+
+/*
+ * If you want to, you can share the DB area with any number of branches.
+ * That has advantages: you can save space by sharing all the SHA1 objects.
+ * On the other hand, it might just make lookup slower and messier. You
+ * be the judge.  The default case is to have one DB per managed directory.
+ */
+int cmd_init_db(int argc, const char **argv, const char *prefix)
+{
+	const char *git_dir;
+	const char *template_dir = NULL;
+	unsigned int flags = 0;
+	int i;
+
+	for (i = 1; i < argc; i++, argv++) {
+		const char *arg = argv[1];
+		if (!prefixcmp(arg, "--template="))
+			template_dir = arg+11;
+		else if (!strcmp(arg, "--shared"))
+			shared_repository = PERM_GROUP;
+		else if (!prefixcmp(arg, "--shared="))
+			shared_repository = git_config_perm("arg", arg+9);
+		else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
+			flags |= INIT_DB_QUIET;
+		else
+			usage(init_db_usage);
+	}
+
+	/*
+	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
+	 * without --bare.  Catch the error early.
+	 */
+	git_dir = getenv(GIT_DIR_ENVIRONMENT);
+	if ((!git_dir || is_bare_repository_cfg == 1)
+	    && getenv(GIT_WORK_TREE_ENVIRONMENT))
+		die("%s (or --work-tree=<directory>) not allowed without "
+		    "specifying %s (or --git-dir=<directory>)",
+		    GIT_WORK_TREE_ENVIRONMENT,
+		    GIT_DIR_ENVIRONMENT);
+
+	/*
+	 * Set up the default .git directory contents
+	 */
+	if (!git_dir)
+		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+
+	if (is_bare_repository_cfg < 0)
+		is_bare_repository_cfg = guess_repository_type(git_dir);
+
+	if (!is_bare_repository_cfg) {
+		if (git_dir) {
+			const char *git_dir_parent = strrchr(git_dir, '/');
+			if (git_dir_parent) {
+				char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
+				git_work_tree_cfg = xstrdup(make_absolute_path(rel));
+				free(rel);
+			}
+		}
+		if (!git_work_tree_cfg) {
+			git_work_tree_cfg = xcalloc(PATH_MAX, 1);
+			if (!getcwd(git_work_tree_cfg, PATH_MAX))
+				die ("Cannot access current working directory.");
+		}
+		if (access(get_git_work_tree(), X_OK))
+			die ("Cannot access work tree '%s'",
+			     get_git_work_tree());
+	}
+
+	set_git_dir(make_absolute_path(git_dir));
+
+	return init_db(template_dir, flags);
+}
diff --git a/builtin-log.c b/builtin-log.c
index addc709..9817d6f 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -18,6 +18,9 @@
 #include "run-command.h"
 #include "shortlog.h"
 
+/* Set a default date-time format for git log ("log.date" config variable) */
+static const char *default_date_mode = NULL;
+
 static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
@@ -61,7 +64,12 @@
 	DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
 	rev->show_root_diff = default_show_root;
 	rev->subject_prefix = fmt_patch_subject_prefix;
+
+	if (default_date_mode)
+		rev->date_mode = parse_date_format(default_date_mode);
+
 	argc = setup_revisions(argc, argv, rev, "HEAD");
+
 	if (rev->diffopt.pickaxe || rev->diffopt.filter)
 		rev->always_show_header = 0;
 	if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
@@ -232,6 +240,8 @@
 		fmt_patch_subject_prefix = xstrdup(value);
 		return 0;
 	}
+	if (!strcmp(var, "log.date"))
+		return git_config_string(&default_date_mode, var, value);
 	if (!strcmp(var, "log.showroot")) {
 		default_show_root = git_config_bool(var, value);
 		return 0;
@@ -485,6 +495,13 @@
 		fmt_patch_suffix = xstrdup(value);
 		return 0;
 	}
+	if (!strcmp(var, "format.cc")) {
+		if (!value)
+			return config_error_nonbool(var);
+		ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+		extra_cc[extra_cc_nr++] = xstrdup(value);
+		return 0;
+	}
 	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 		return 0;
 	}
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 6e23ffd..97c1ff9 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -434,6 +434,7 @@
 
 static int decode_q_segment(char *in, char *ot, unsigned otsize, char *ep, int rfc2047)
 {
+	char *otbegin = ot;
 	char *otend = ot + otsize;
 	int c;
 	while ((c = *in++) != 0 && (in <= ep)) {
@@ -453,13 +454,14 @@
 		*ot++ = c;
 	}
 	*ot = 0;
-	return 0;
+	return (ot - otbegin);
 }
 
 static int decode_b_segment(char *in, char *ot, unsigned otsize, char *ep)
 {
 	/* Decode in..ep, possibly in-place to ot */
 	int c, pos = 0, acc = 0;
+	char *otbegin = ot;
 	char *otend = ot + otsize;
 
 	while ((c = *in++) != 0 && (in <= ep)) {
@@ -505,7 +507,7 @@
 		}
 	}
 	*ot = 0;
-	return 0;
+	return (ot - otbegin);
 }
 
 /*
@@ -623,25 +625,24 @@
 		convert_to_utf8(it, itsize, "");
 }
 
-static void decode_transfer_encoding(char *line, unsigned linesize)
+static int decode_transfer_encoding(char *line, unsigned linesize, int inputlen)
 {
 	char *ep;
 
 	switch (transfer_encoding) {
 	case TE_QP:
-		ep = line + strlen(line);
-		decode_q_segment(line, line, linesize, ep, 0);
-		break;
+		ep = line + inputlen;
+		return decode_q_segment(line, line, linesize, ep, 0);
 	case TE_BASE64:
-		ep = line + strlen(line);
-		decode_b_segment(line, line, linesize, ep);
-		break;
+		ep = line + inputlen;
+		return decode_b_segment(line, line, linesize, ep);
 	case TE_DONTCARE:
-		break;
+	default:
+		return inputlen;
 	}
 }
 
-static int handle_filter(char *line, unsigned linesize);
+static int handle_filter(char *line, unsigned linesize, int linelen);
 
 static int find_boundary(void)
 {
@@ -669,7 +670,7 @@
 					"can't recover\n");
 			exit(1);
 		}
-		handle_filter(newline, sizeof(newline));
+		handle_filter(newline, sizeof(newline), strlen(newline));
 
 		/* skip to the next boundary */
 		if (!find_boundary())
@@ -759,14 +760,14 @@
 	return 0;
 }
 
-static int handle_patch(char *line)
+static int handle_patch(char *line, int len)
 {
-	fputs(line, patchfile);
+	fwrite(line, 1, len, patchfile);
 	patch_lines++;
 	return 0;
 }
 
-static int handle_filter(char *line, unsigned linesize)
+static int handle_filter(char *line, unsigned linesize, int linelen)
 {
 	static int filter = 0;
 
@@ -779,7 +780,7 @@
 			break;
 		filter++;
 	case 1:
-		if (!handle_patch(line))
+		if (!handle_patch(line, linelen))
 			break;
 		filter++;
 	default:
@@ -794,6 +795,7 @@
 	int rc = 0;
 	static char newline[2000];
 	static char *np = newline;
+	int len = strlen(line);
 
 	/* Skip up to the first boundary */
 	if (content_top->boundary) {
@@ -805,16 +807,19 @@
 		/* process any boundary lines */
 		if (content_top->boundary && is_multipart_boundary(line)) {
 			/* flush any leftover */
-			if ((transfer_encoding == TE_BASE64)  &&
-			    (np != newline)) {
-				handle_filter(newline, sizeof(newline));
-			}
+			if (np != newline)
+				handle_filter(newline, sizeof(newline),
+					      np - newline);
 			if (!handle_boundary())
 				return;
 		}
 
 		/* Unwrap transfer encoding */
-		decode_transfer_encoding(line, sizeof(line));
+		len = decode_transfer_encoding(line, sizeof(line), len);
+		if (len < 0) {
+			error("Malformed input line");
+			return;
+		}
 
 		switch (transfer_encoding) {
 		case TE_BASE64:
@@ -824,39 +829,40 @@
 
 			/* binary data most likely doesn't have newlines */
 			if (message_type != TYPE_TEXT) {
-				rc = handle_filter(line, sizeof(newline));
+				rc = handle_filter(line, sizeof(line), len);
 				break;
 			}
 
-			/* this is a decoded line that may contain
+			/*
+			 * This is a decoded line that may contain
 			 * multiple new lines.  Pass only one chunk
 			 * at a time to handle_filter()
 			 */
-
 			do {
-				while (*op != '\n' && *op != 0)
+				while (op < line + len && *op != '\n')
 					*np++ = *op++;
 				*np = *op;
 				if (*np != 0) {
 					/* should be sitting on a new line */
 					*(++np) = 0;
 					op++;
-					rc = handle_filter(newline, sizeof(newline));
+					rc = handle_filter(newline, sizeof(newline), np - newline);
 					np = newline;
 				}
-			} while (*op != 0);
-			/* the partial chunk is saved in newline and
-			 * will be appended by the next iteration of fgets
+			} while (op < line + len);
+			/*
+			 * The partial chunk is saved in newline and will be
+			 * appended by the next iteration of read_line_with_nul().
 			 */
 			break;
 		}
 		default:
-			rc = handle_filter(line, sizeof(newline));
+			rc = handle_filter(line, sizeof(line), len);
 		}
 		if (rc)
 			/* nothing left to filter */
 			break;
-	} while (fgets(line, sizeof(line), fin));
+	} while ((len = read_line_with_nul(line, sizeof(line), fin)));
 
 	return;
 }
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index 46b27cd..ae2b4cb 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -45,6 +45,24 @@
 /* Could be as small as 64, enough to hold a Unix "From " line. */
 static char buf[4096];
 
+/* We cannot use fgets() because our lines can contain NULs */
+int read_line_with_nul(char *buf, int size, FILE *in)
+{
+	int len = 0, c;
+
+	for (;;) {
+		c = getc(in);
+		if (c == EOF)
+			break;
+		buf[len++] = c;
+		if (c == '\n' || len + 1 >= size)
+			break;
+	}
+	buf[len] = '\0';
+
+	return len;
+}
+
 /* Called with the first line (potentially partial)
  * already in buf[] -- normally that should begin with
  * the Unix "From " line.  Write it into the specified
@@ -70,19 +88,19 @@
 	 * "From " and having something that looks like a date format.
 	 */
 	for (;;) {
-		int is_partial = (buf[len-1] != '\n');
+		int is_partial = len && buf[len-1] != '\n';
 
-		if (fputs(buf, output) == EOF)
+		if (fwrite(buf, 1, len, output) != len)
 			die("cannot write output");
 
-		if (fgets(buf, sizeof(buf), mbox) == NULL) {
+		len = read_line_with_nul(buf, sizeof(buf), mbox);
+		if (len == 0) {
 			if (feof(mbox)) {
 				status = 1;
 				break;
 			}
 			die("cannot read mbox");
 		}
-		len = strlen(buf);
 		if (!is_partial && !is_bare && is_from_line(buf, len))
 			break; /* done with one message */
 	}
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index e16f5e2..362c290 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -92,7 +92,8 @@
 
 static int call_depth = 0;
 static int verbosity = 2;
-static int rename_limit = -1;
+static int diff_rename_limit = -1;
+static int merge_rename_limit = -1;
 static int buffer_output = 1;
 static struct strbuf obuf = STRBUF_INIT;
 
@@ -361,7 +362,10 @@
 	diff_setup(&opts);
 	DIFF_OPT_SET(&opts, RECURSIVE);
 	opts.detect_rename = DIFF_DETECT_RENAME;
-	opts.rename_limit = rename_limit;
+	opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit :
+			    diff_rename_limit >= 0 ? diff_rename_limit :
+			    500;
+	opts.warn_on_too_large_rename = 1;
 	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 	if (diff_setup_done(&opts) < 0)
 		die("diff setup failed");
@@ -1343,7 +1347,11 @@
 		return 0;
 	}
 	if (!strcasecmp(var, "diff.renamelimit")) {
-		rename_limit = git_config_int(var, value);
+		diff_rename_limit = git_config_int(var, value);
+		return 0;
+	}
+	if (!strcasecmp(var, "merge.renamelimit")) {
+		merge_rename_limit = git_config_int(var, value);
 		return 0;
 	}
 	return git_default_config(var, value, cb);
diff --git a/builtin-mv.c b/builtin-mv.c
index 3edebef..5530e11 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -256,7 +256,8 @@
 
 		for (i = 0; i < added.nr; i++) {
 			const char *path = added.items[i].path;
-			add_file_to_cache(path, verbose);
+			if (add_file_to_cache(path, verbose ? ADD_CACHE_VERBOSE : 0))
+				die("updating index entries failed");
 		}
 
 		for (i = 0; i < deleted.nr; i++)
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index a8dc225..70d2f5d 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -28,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] [--include-tag] [--keep-unreachable] \n\
+	[--stdout | base-name] [--include-tag] \n\
+	[--keep-unreachable | --unpack-unreachable] \n\
 	[<ref-list | <object-list]";
 
 struct object_entry {
@@ -43,6 +44,7 @@
 					     */
 	void *delta_data;	/* cached delta (uncompressed) */
 	unsigned long delta_size;	/* delta data size (uncompressed) */
+	unsigned long z_delta_size;	/* delta data size (compressed) */
 	unsigned int hash;	/* name hint hash */
 	enum object_type type;
 	enum object_type in_pack_type;	/* could be delta */
@@ -65,7 +67,8 @@
 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, include_tag;
+static int reuse_delta = 1, reuse_object = 1;
+static int keep_unreachable, unpack_unreachable, include_tag;
 static int local;
 static int incremental;
 static int allow_ofs_delta;
@@ -102,24 +105,53 @@
 static uint32_t reused, reused_delta;
 
 
-static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
+static void *get_delta(struct object_entry *entry)
 {
-	unsigned long othersize, delta_size;
+	unsigned long size, base_size, delta_size;
+	void *buf, *base_buf, *delta_buf;
 	enum object_type type;
-	void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize);
-	void *delta_buf;
 
-	if (!otherbuf)
+	buf = read_sha1_file(entry->idx.sha1, &type, &size);
+	if (!buf)
+		die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+	base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
+	if (!base_buf)
 		die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
-        delta_buf = diff_delta(otherbuf, othersize,
+	delta_buf = diff_delta(base_buf, base_size,
 			       buf, size, &delta_size, 0);
-        if (!delta_buf || delta_size != entry->delta_size)
+	if (!delta_buf || delta_size != entry->delta_size)
 		die("delta size changed");
-        free(buf);
-        free(otherbuf);
+	free(buf);
+	free(base_buf);
 	return delta_buf;
 }
 
+static unsigned long do_compress(void **pptr, unsigned long size)
+{
+	z_stream stream;
+	void *in, *out;
+	unsigned long maxsize;
+
+	memset(&stream, 0, sizeof(stream));
+	deflateInit(&stream, pack_compression_level);
+	maxsize = deflateBound(&stream, size);
+
+	in = *pptr;
+	out = xmalloc(maxsize);
+	*pptr = out;
+
+	stream.next_in = in;
+	stream.avail_in = size;
+	stream.next_out = out;
+	stream.avail_out = maxsize;
+	while (deflate(&stream, Z_FINISH) == Z_OK)
+		; /* nothing */
+	deflateEnd(&stream);
+
+	free(in);
+	return stream.total_out;
+}
+
 /*
  * The per-object header is a pretty dense thing, which is
  *  - first byte: low four bits are "size", then three bits of "type",
@@ -222,42 +254,42 @@
 				  struct object_entry *entry,
 				  off_t write_offset)
 {
-	unsigned long size;
-	enum object_type type;
+	unsigned long size, limit, datalen;
 	void *buf;
-	unsigned char header[10];
-	unsigned char dheader[10];
+	unsigned char header[10], dheader[10];
 	unsigned hdrlen;
-	off_t datalen;
-	enum object_type obj_type;
-	int to_reuse = 0;
-	/* write limit if limited packsize and not first object */
-	unsigned long limit = pack_size_limit && nr_written ?
-				pack_size_limit - write_offset : 0;
-				/* no if no delta */
-	int usable_delta =	!entry->delta ? 0 :
-				/* yes if unlimited packfile */
-				!pack_size_limit ? 1 :
-				/* no if base written to previous pack */
-				entry->delta->idx.offset == (off_t)-1 ? 0 :
-				/* otherwise double-check written to this
-				 * pack,  like we do below
-				 */
-				entry->delta->idx.offset ? 1 : 0;
+	enum object_type type;
+	int usable_delta, to_reuse;
 
 	if (!pack_to_stdout)
 		crc32_begin(f);
 
-	obj_type = entry->type;
-	if (no_reuse_object)
+	type = entry->type;
+
+	/* write limit if limited packsize and not first object */
+	limit = pack_size_limit && nr_written ?
+			pack_size_limit - write_offset : 0;
+
+	if (!entry->delta)
+		usable_delta = 0;	/* no delta */
+	else if (!pack_size_limit)
+	       usable_delta = 1;	/* unlimited packfile */
+	else if (entry->delta->idx.offset == (off_t)-1)
+		usable_delta = 0;	/* base was written to another pack */
+	else if (entry->delta->idx.offset)
+		usable_delta = 1;	/* base already exists in this pack */
+	else
+		usable_delta = 0;	/* base could end up in another pack */
+
+	if (!reuse_object)
 		to_reuse = 0;	/* explicit */
 	else if (!entry->in_pack)
 		to_reuse = 0;	/* can't reuse what we don't have */
-	else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
+	else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA)
 				/* check_object() decided it for us ... */
 		to_reuse = usable_delta;
 				/* ... but pack split may override that */
-	else if (obj_type != entry->in_pack_type)
+	else if (type != entry->in_pack_type)
 		to_reuse = 0;	/* pack has delta which is unusable */
 	else if (entry->delta)
 		to_reuse = 0;	/* we want to pack afresh */
@@ -267,50 +299,42 @@
 				 */
 
 	if (!to_reuse) {
-		z_stream stream;
-		unsigned long maxsize;
-		void *out;
 		if (!usable_delta) {
-			buf = read_sha1_file(entry->idx.sha1, &obj_type, &size);
+			buf = read_sha1_file(entry->idx.sha1, &type, &size);
 			if (!buf)
 				die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+			/*
+			 * make sure no cached delta data remains from a
+			 * previous attempt before a pack split occured.
+			 */
+			free(entry->delta_data);
+			entry->delta_data = NULL;
+			entry->z_delta_size = 0;
 		} else if (entry->delta_data) {
 			size = entry->delta_size;
 			buf = entry->delta_data;
 			entry->delta_data = NULL;
-			obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+			type = (allow_ofs_delta && entry->delta->idx.offset) ?
 				OBJ_OFS_DELTA : OBJ_REF_DELTA;
 		} else {
-			buf = read_sha1_file(entry->idx.sha1, &type, &size);
-			if (!buf)
-				die("unable to read %s", sha1_to_hex(entry->idx.sha1));
-			buf = delta_against(buf, size, entry);
+			buf = get_delta(entry);
 			size = entry->delta_size;
-			obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+			type = (allow_ofs_delta && entry->delta->idx.offset) ?
 				OBJ_OFS_DELTA : OBJ_REF_DELTA;
 		}
-		/* compress the data to store and put compressed length in datalen */
-		memset(&stream, 0, sizeof(stream));
-		deflateInit(&stream, pack_compression_level);
-		maxsize = deflateBound(&stream, size);
-		out = xmalloc(maxsize);
-		/* Compress it */
-		stream.next_in = buf;
-		stream.avail_in = size;
-		stream.next_out = out;
-		stream.avail_out = maxsize;
-		while (deflate(&stream, Z_FINISH) == Z_OK)
-			/* nothing */;
-		deflateEnd(&stream);
-		datalen = stream.total_out;
+
+		if (entry->z_delta_size)
+			datalen = entry->z_delta_size;
+		else
+			datalen = do_compress(&buf, size);
 
 		/*
 		 * The object header is a byte of 'type' followed by zero or
 		 * more bytes of length.
 		 */
-		hdrlen = encode_header(obj_type, size, header);
+		hdrlen = encode_header(type, size, header);
 
-		if (obj_type == OBJ_OFS_DELTA) {
+		if (type == OBJ_OFS_DELTA) {
 			/*
 			 * Deltas with relative base contain an additional
 			 * encoding of the relative offset for the delta
@@ -322,20 +346,18 @@
 			while (ofs >>= 7)
 				dheader[--pos] = 128 | (--ofs & 127);
 			if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
-				free(out);
 				free(buf);
 				return 0;
 			}
 			sha1write(f, header, hdrlen);
 			sha1write(f, dheader + pos, sizeof(dheader) - pos);
 			hdrlen += sizeof(dheader) - pos;
-		} else if (obj_type == OBJ_REF_DELTA) {
+		} else if (type == OBJ_REF_DELTA) {
 			/*
 			 * Deltas with a base reference contain
 			 * an additional 20 bytes for the base sha1.
 			 */
 			if (limit && hdrlen + 20 + datalen + 20 >= limit) {
-				free(out);
 				free(buf);
 				return 0;
 			}
@@ -344,14 +366,12 @@
 			hdrlen += 20;
 		} else {
 			if (limit && hdrlen + datalen + 20 >= limit) {
-				free(out);
 				free(buf);
 				return 0;
 			}
 			sha1write(f, header, hdrlen);
 		}
-		sha1write(f, out, datalen);
-		free(out);
+		sha1write(f, buf, datalen);
 		free(buf);
 	}
 	else {
@@ -361,11 +381,11 @@
 		off_t offset;
 
 		if (entry->delta) {
-			obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+			type = (allow_ofs_delta && entry->delta->idx.offset) ?
 				OBJ_OFS_DELTA : OBJ_REF_DELTA;
 			reused_delta++;
 		}
-		hdrlen = encode_header(obj_type, entry->size, header);
+		hdrlen = encode_header(type, entry->size, header);
 		offset = entry->in_pack_offset;
 		revidx = find_pack_revindex(p, offset);
 		datalen = revidx[1].offset - offset;
@@ -374,7 +394,7 @@
 			die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 		offset += entry->in_pack_header_size;
 		datalen -= entry->in_pack_header_size;
-		if (obj_type == OBJ_OFS_DELTA) {
+		if (type == OBJ_OFS_DELTA) {
 			off_t ofs = entry->idx.offset - entry->delta->idx.offset;
 			unsigned pos = sizeof(dheader) - 1;
 			dheader[pos] = ofs & 127;
@@ -385,7 +405,7 @@
 			sha1write(f, header, hdrlen);
 			sha1write(f, dheader + pos, sizeof(dheader) - pos);
 			hdrlen += sizeof(dheader) - pos;
-		} else if (obj_type == OBJ_REF_DELTA) {
+		} else if (type == OBJ_REF_DELTA) {
 			if (limit && hdrlen + 20 + datalen + 20 >= limit)
 				return 0;
 			sha1write(f, header, hdrlen);
@@ -452,11 +472,10 @@
 	struct sha1file *f;
 	off_t offset, offset_one, last_obj_offset = 0;
 	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)
+	if (progress > pack_to_stdout)
 		progress_state = start_progress("Writing objects", nr_result);
 	written_list = xmalloc(nr_objects * sizeof(*written_list));
 
@@ -1022,7 +1041,7 @@
 			unuse_pack(&w_curs);
 			return;
 		case OBJ_REF_DELTA:
-			if (!no_reuse_delta && !entry->preferred_base)
+			if (reuse_delta && !entry->preferred_base)
 				base_ref = use_pack(p, &w_curs,
 						entry->in_pack_offset + used, NULL);
 			entry->in_pack_header_size = used + 20;
@@ -1045,7 +1064,7 @@
 				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) {
+			if (reuse_delta && !entry->preferred_base) {
 				struct revindex_entry *revidx;
 				revidx = find_pack_revindex(p, ofs);
 				base_ref = nth_packed_object_sha1(p, revidx->nr);
@@ -1233,7 +1252,7 @@
 	 * We do not bother to try a delta that we discarded
 	 * on an earlier try, but only when reusing delta data.
 	 */
-	if (!no_reuse_delta && trg_entry->in_pack &&
+	if (reuse_delta && trg_entry->in_pack &&
 	    trg_entry->in_pack == src_entry->in_pack &&
 	    trg_entry->in_pack_type != OBJ_REF_DELTA &&
 	    trg_entry->in_pack_type != OBJ_OFS_DELTA)
@@ -1441,11 +1460,34 @@
 				best_base = other_idx;
 		}
 
+		/*
+		 * If we decided to cache the delta data, then it is best
+		 * to compress it right away.  First because we have to do
+		 * it anyway, and doing it here while we're threaded will
+		 * save a lot of time in the non threaded write phase,
+		 * as well as allow for caching more deltas within
+		 * the same cache size limit.
+		 * ...
+		 * But only if not writing to stdout, since in that case
+		 * the network is most likely throttling writes anyway,
+		 * and therefore it is best to go to the write phase ASAP
+		 * instead, as we can afford spending more time compressing
+		 * between writes at that moment.
+		 */
+		if (entry->delta_data && !pack_to_stdout) {
+			entry->z_delta_size = do_compress(&entry->delta_data,
+							  entry->delta_size);
+			cache_lock();
+			delta_cache_size -= entry->delta_size;
+			delta_cache_size += entry->z_delta_size;
+			cache_unlock();
+		}
+
 		/* if we made n a delta, and if n is already at max
 		 * depth, leaving it in the window is pointless.  we
 		 * should evict it first.
 		 */
-		if (entry->delta && depth <= n->depth)
+		if (entry->delta && max_depth <= n->depth)
 			continue;
 
 		/*
@@ -1688,7 +1730,7 @@
 
 		if (entry->delta)
 			/* This happens if we decided to reuse existing
-			 * delta from a pack.  "!no_reuse_delta &&" is implied.
+			 * delta from a pack.  "reuse_delta &&" is implied.
 			 */
 			continue;
 
@@ -1905,6 +1947,32 @@
 	free(in_pack.array);
 }
 
+static void loosen_unused_packed_objects(struct rev_info *revs)
+{
+	struct packed_git *p;
+	uint32_t i;
+	const unsigned char *sha1;
+
+	for (p = packed_git; p; p = p->next) {
+		for (i = 0; i < revs->num_ignore_packed; i++) {
+			if (matches_pack_name(p, revs->ignore_packed[i]))
+				break;
+		}
+		if (revs->num_ignore_packed <= i)
+			continue;
+
+		if (open_pack_index(p))
+			die("cannot open pack index");
+
+		for (i = 0; i < p->num_objects; i++) {
+			sha1 = nth_packed_object_sha1(p, i);
+			if (!locate_object_entry(sha1))
+				if (force_object_loose(sha1, p->mtime))
+					die("unable to force loose object");
+		}
+	}
+}
+
 static void get_object_list(int ac, const char **av)
 {
 	struct rev_info revs;
@@ -1939,6 +2007,8 @@
 
 	if (keep_unreachable)
 		add_objects_in_unpacked_packs(&revs);
+	if (unpack_unreachable)
+		loosen_unused_packed_objects(&revs);
 }
 
 static int adjust_perm(const char *path, mode_t mode)
@@ -2050,11 +2120,11 @@
 			continue;
 		}
 		if (!strcmp("--no-reuse-delta", arg)) {
-			no_reuse_delta = 1;
+			reuse_delta = 0;
 			continue;
 		}
 		if (!strcmp("--no-reuse-object", arg)) {
-			no_reuse_object = no_reuse_delta = 1;
+			reuse_object = reuse_delta = 0;
 			continue;
 		}
 		if (!strcmp("--delta-base-offset", arg)) {
@@ -2073,6 +2143,10 @@
 			keep_unreachable = 1;
 			continue;
 		}
+		if (!strcmp("--unpack-unreachable", arg)) {
+			unpack_unreachable = 1;
+			continue;
+		}
 		if (!strcmp("--include-tag", arg)) {
 			include_tag = 1;
 			continue;
@@ -2138,6 +2212,9 @@
 	if (!pack_to_stdout && thin)
 		die("--thin cannot be used to build an indexable pack.");
 
+	if (keep_unreachable && unpack_unreachable)
+		die("--keep-unreachable and --unpack-unreachable are incompatible.");
+
 #ifdef THREADED_DELTA_SEARCH
 	if (!delta_search_threads)	/* --threads=0 means autodetect */
 		delta_search_threads = online_cpus();
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 2742990..83a7b134 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -10,6 +10,7 @@
 #include "list-objects.h"
 #include "builtin.h"
 #include "log-tree.h"
+#include "graph.h"
 
 /* bits #0-15 in revision.h */
 
@@ -58,26 +59,31 @@
 static void finish_commit(struct commit *commit);
 static void show_commit(struct commit *commit)
 {
+	graph_show_commit(revs.graph);
+
 	if (show_timestamp)
 		printf("%lu ", commit->date);
 	if (header_prefix)
 		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('<');
-		else
-			putchar('>');
+
+	if (!revs.graph) {
+		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('<');
+			else
+				putchar('>');
+		}
 	}
 	if (revs.abbrev_commit && revs.abbrev)
 		fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
 		      stdout);
 	else
 		fputs(sha1_to_hex(commit->object.sha1), stdout);
-	if (revs.parents) {
+	if (revs.print_parents) {
 		struct commit_list *parents = commit->parents;
 		while (parents) {
 			printf(" %s", sha1_to_hex(parents->item->object.sha1));
@@ -96,9 +102,48 @@
 		pretty_print_commit(revs.commit_format, commit,
 				    &buf, revs.abbrev, NULL, NULL,
 				    revs.date_mode, 0);
-		if (buf.len)
-			printf("%s%c", buf.buf, hdr_termination);
+		if (revs.graph) {
+			if (buf.len) {
+				if (revs.commit_format != CMIT_FMT_ONELINE)
+					graph_show_oneline(revs.graph);
+
+				graph_show_commit_msg(revs.graph, &buf);
+
+				/*
+				 * Add a newline after the commit message.
+				 *
+				 * Usually, this newline produces a blank
+				 * padding line between entries, in which case
+				 * we need to add graph padding on this line.
+				 *
+				 * However, the commit message may not end in a
+				 * newline.  In this case the newline simply
+				 * ends the last line of the commit message,
+				 * and we don't need any graph output.  (This
+				 * always happens with CMIT_FMT_ONELINE, and it
+				 * happens with CMIT_FMT_USERFORMAT when the
+				 * format doesn't explicitly end in a newline.)
+				 */
+				if (buf.len && buf.buf[buf.len - 1] == '\n')
+					graph_show_padding(revs.graph);
+				putchar('\n');
+			} else {
+				/*
+				 * If the message buffer is empty, just show
+				 * the rest of the graph output for this
+				 * commit.
+				 */
+				if (graph_show_remainder(revs.graph))
+					putchar('\n');
+			}
+		} else {
+			if (buf.len)
+				printf("%s%c", buf.buf, hdr_termination);
+		}
 		strbuf_release(&buf);
+	} else {
+		if (graph_show_remainder(revs.graph))
+			putchar('\n');
 	}
 	maybe_flush_or_die(stdout, "stdout");
 	finish_commit(commit);
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 1d019b3..a7860ed 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -94,6 +94,14 @@
 		puts(arg);
 }
 
+/* Like show(), but with a negation prefix according to type */
+static void show_with_type(int type, const char *arg)
+{
+	if (type != show_type)
+		putchar('^');
+	show(arg);
+}
+
 /* Output a revision, only if filter allows it */
 static void show_rev(int type, const unsigned char *sha1, const char *name)
 {
@@ -101,8 +109,6 @@
 		return;
 	def = NULL;
 
-	if (type != show_type)
-		putchar('^');
 	if (symbolic && name) {
 		if (symbolic == SHOW_SYMBOLIC_FULL) {
 			unsigned char discard[20];
@@ -119,20 +125,20 @@
 				 */
 				break;
 			case 1: /* happy */
-				show(full);
+				show_with_type(type, full);
 				break;
 			default: /* ambiguous */
 				error("refname '%s' is ambiguous", name);
 				break;
 			}
 		} else {
-			show(name);
+			show_with_type(type, name);
 		}
 	}
 	else if (abbrev)
-		show(find_unique_abbrev(sha1, abbrev));
+		show_with_type(type, find_unique_abbrev(sha1, abbrev));
 	else
-		show(sha1_to_hex(sha1));
+		show_with_type(type, sha1_to_hex(sha1));
 }
 
 /* Output a flag, only if filter allows it. */
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index bb9c33a..d76260c 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -537,9 +537,17 @@
 	int i;
 
 	for (i = 0; i < nr_heads; i++) {
+		const char *local = heads[i];
 		const char *remote = strrchr(heads[i], ':');
 
-		remote = remote ? (remote + 1) : heads[i];
+		if (*local == '+')
+			local++;
+
+		/* A matching refspec is okay.  */
+		if (remote == local && remote[1] == '\0')
+			continue;
+
+		remote = remote ? (remote + 1) : local;
 		switch (check_ref_format(remote)) {
 		case 0: /* ok */
 		case CHECK_REF_FORMAT_ONELEVEL:
diff --git a/builtin-update-index.c b/builtin-update-index.c
index e1ca8de..9e0d7ab 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -593,6 +593,10 @@
 				refresh_flags |= REFRESH_QUIET;
 				continue;
 			}
+			if (!strcmp(path, "--ignore-submodules")) {
+				refresh_flags |= REFRESH_IGNORE_SUBMODULES;
+				continue;
+			}
 			if (!strcmp(path, "--add")) {
 				allow_add = 1;
 				continue;
diff --git a/builtin.h b/builtin.h
index 95126fd..8bda111 100644
--- a/builtin.h
+++ b/builtin.h
@@ -9,6 +9,7 @@
 extern void list_common_cmds_help(void);
 extern void help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
+extern int read_line_with_nul(char *buf, int size, FILE *file);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
@@ -24,6 +25,7 @@
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+extern int cmd_clone(int argc, const char **argv, const char *prefix);
 extern int cmd_clean(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index ebe4031..eab1a17 100644
--- a/cache.h
+++ b/cache.h
@@ -261,8 +261,8 @@
 #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))
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
-#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose))
-#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
+#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
+#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
 #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))
@@ -317,6 +317,7 @@
 extern int set_git_dir(const char *path);
 extern const char *get_git_work_tree(void);
 extern const char *read_gitfile_gently(const char *path);
+extern void set_git_work_tree(const char *tree);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
@@ -329,6 +330,10 @@
 extern void verify_filename(const char *prefix, const char *name);
 extern void verify_non_filename(const char *prefix, const char *name);
 
+#define INIT_DB_QUIET 0x0001
+
+extern int init_db(const char *template_dir, unsigned int flags);
+
 #define alloc_nr(x) (((x)+16)*3/2)
 
 /*
@@ -366,8 +371,11 @@
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern int remove_file_from_index(struct index_state *, const char *path);
-extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);
-extern int add_file_to_index(struct index_state *, const char *path, int verbose);
+#define ADD_CACHE_VERBOSE 1
+#define ADD_CACHE_PRETEND 2
+#define ADD_CACHE_IGNORE_ERRORS	4
+extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 
@@ -388,6 +396,7 @@
 #define REFRESH_UNMERGED	0x0002	/* allow unmerged */
 #define REFRESH_QUIET		0x0004	/* be quiet about it */
 #define REFRESH_IGNORE_MISSING	0x0008	/* ignore non-existent */
+#define REFRESH_IGNORE_SUBMODULES	0x0008	/* ignore submodules */
 extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
 
 struct lock_file {
@@ -398,6 +407,7 @@
 	char filename[PATH_MAX];
 };
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
+extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
 
 extern int hold_locked_index(struct lock_file *, int);
@@ -521,6 +531,7 @@
 extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
+extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
@@ -614,6 +625,7 @@
 	char base[FLEX_ARRAY]; /* more */
 } *alt_odb_list;
 extern void prepare_alt_odb(void);
+extern void add_to_alternates_file(const char *reference);
 
 struct pack_window {
 	struct pack_window *next;
@@ -735,6 +747,7 @@
 #define MAX_GITNAME (1000)
 extern char git_default_email[MAX_GITNAME];
 extern char git_default_name[MAX_GITNAME];
+extern int user_ident_explicitly_given;
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
@@ -781,7 +794,11 @@
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 
 /* add */
-void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
+/*
+ * return 0 if success, 1 - if addition of a file failed and
+ * ADD_FILES_IGNORE_ERRORS was specified in flags
+ */
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
diff --git a/config.c b/config.c
index 5431e2d..c2f2bbb 100644
--- a/config.c
+++ b/config.c
@@ -448,6 +448,8 @@
 		if (!value)
 			return config_error_nonbool(var);
 		strlcpy(git_default_name, value, sizeof(git_default_name));
+		if (git_default_email[0])
+			user_ident_explicitly_given = 1;
 		return 0;
 	}
 
@@ -455,6 +457,8 @@
 		if (!value)
 			return config_error_nonbool(var);
 		strlcpy(git_default_email, value, sizeof(git_default_email));
+		if (git_default_name[0])
+			user_ident_explicitly_given = 1;
 		return 0;
 	}
 
@@ -699,7 +703,7 @@
 	if (dot) {
 		strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
 		for (i = dot - key + 1; i < store.baselen; i++) {
-			if (key[i] == '"')
+			if (key[i] == '"' || key[i] == '\\')
 				strbuf_addch(&sb, '\\');
 			strbuf_addch(&sb, key[i]);
 		}
diff --git a/git-clone.sh b/contrib/examples/git-clone.sh
similarity index 100%
rename from git-clone.sh
rename to contrib/examples/git-clone.sh
diff --git a/diff.c b/diff.c
index 1f46ff0..62fdc54 100644
--- a/diff.c
+++ b/diff.c
@@ -19,7 +19,7 @@
 #endif
 
 static int diff_detect_rename_default;
-static int diff_rename_limit_default = 100;
+static int diff_rename_limit_default = 200;
 int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
@@ -2496,6 +2496,8 @@
 		DIFF_OPT_SET(options, ALLOW_EXTERNAL);
 	else if (!strcmp(arg, "--no-ext-diff"))
 		DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
+	else if (!strcmp(arg, "--ignore-submodules"))
+		DIFF_OPT_SET(options, IGNORE_SUBMODULES);
 
 	/* misc options */
 	else if (!strcmp(arg, "-z"))
@@ -3355,6 +3357,9 @@
 	char concatpath[PATH_MAX];
 	struct diff_filespec *one, *two;
 
+	if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
+		return;
+
 	/* This may look odd, but it is a preparation for
 	 * feeding "there are unchanged files which should
 	 * not produce diffs, but when you are doing copy
@@ -3399,6 +3404,10 @@
 	char concatpath[PATH_MAX];
 	struct diff_filespec *one, *two;
 
+	if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
+			&& S_ISGITLINK(new_mode))
+		return;
+
 	if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
 		unsigned tmp;
 		const unsigned char *tmp_c;
diff --git a/diff.h b/diff.h
index 1cf1eff..b0b700a 100644
--- a/diff.h
+++ b/diff.h
@@ -63,6 +63,7 @@
 #define DIFF_OPT_REVERSE_DIFF        (1 << 15)
 #define DIFF_OPT_CHECK_FAILED        (1 << 16)
 #define DIFF_OPT_RELATIVE_NAME       (1 << 17)
+#define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
 #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)
@@ -83,6 +84,7 @@
 	int pickaxe_opts;
 	int rename_score;
 	int rename_limit;
+	int warn_on_too_large_rename;
 	int dirstat_percent;
 	int setup;
 	int abbrev;
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 1369a5e..1b2ebb4 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -492,7 +492,8 @@
 		rename_limit = 32767;
 	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");
+		if (options->warn_on_too_large_rename)
+			warning("too many files, skipping inexact rename detection");
 		goto cleanup;
 	}
 
diff --git a/environment.c b/environment.c
index 55c2527..73feb2d 100644
--- a/environment.c
+++ b/environment.c
@@ -11,6 +11,7 @@
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
+int user_ident_explicitly_given;
 int trust_executable_bit = 1;
 int quote_path_fully = 1;
 int has_symlinks = 1;
@@ -43,7 +44,7 @@
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
-static const char *work_tree;
+static char *work_tree;
 
 static const char *git_dir;
 static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
@@ -85,10 +86,26 @@
 	return git_dir;
 }
 
+static int git_work_tree_initialized;
+
+/*
+ * Note.  This works only before you used a work tree.  This was added
+ * primarily to support git-clone to work in a new repository it just
+ * created, and is not meant to flip between different work trees.
+ */
+void set_git_work_tree(const char *new_work_tree)
+{
+	if (is_bare_repository_cfg >= 0)
+		die("cannot set work tree after initialization");
+	git_work_tree_initialized = 1;
+	free(work_tree);
+	work_tree = xstrdup(make_absolute_path(new_work_tree));
+	is_bare_repository_cfg = 0;
+}
+
 const char *get_git_work_tree(void)
 {
-	static int initialized = 0;
-	if (!initialized) {
+	if (!git_work_tree_initialized) {
 		work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
 		/* core.bare = true overrides implicit and config work tree */
 		if (!work_tree && is_bare_repository_cfg < 1) {
@@ -98,7 +115,7 @@
 				work_tree = xstrdup(make_absolute_path(git_path(work_tree)));
 		} else if (work_tree)
 			work_tree = xstrdup(make_absolute_path(work_tree));
-		initialized = 1;
+		git_work_tree_initialized = 1;
 		if (work_tree)
 			is_bare_repository_cfg = 0;
 	}
diff --git a/fast-import.c b/fast-import.c
index 36ec5b8..93119bb 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1690,7 +1690,7 @@
 		ungetc(term_char, stdin);
 }
 
-static void cmd_mark(void)
+static void parse_mark(void)
 {
 	if (!prefixcmp(command_buf.buf, "mark :")) {
 		next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
@@ -1700,7 +1700,7 @@
 		next_mark = 0;
 }
 
-static void cmd_data(struct strbuf *sb)
+static void parse_data(struct strbuf *sb)
 {
 	strbuf_reset(sb);
 
@@ -1798,13 +1798,13 @@
 	return ident;
 }
 
-static void cmd_new_blob(void)
+static void parse_new_blob(void)
 {
 	static struct strbuf buf = STRBUF_INIT;
 
 	read_next_command();
-	cmd_mark();
-	cmd_data(&buf);
+	parse_mark();
+	parse_data(&buf);
 	store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
 }
 
@@ -1908,7 +1908,7 @@
 			p = uq.buf;
 		}
 		read_next_command();
-		cmd_data(&buf);
+		parse_data(&buf);
 		store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
 	} else if (oe) {
 		if (oe->type != OBJ_BLOB)
@@ -1995,7 +1995,7 @@
 	load_tree(&b->branch_tree);
 }
 
-static void cmd_from_commit(struct branch *b, char *buf, unsigned long size)
+static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
 {
 	if (!buf || size < 46)
 		die("Not a valid commit: %s", sha1_to_hex(b->sha1));
@@ -2006,7 +2006,7 @@
 		b->branch_tree.versions[1].sha1);
 }
 
-static void cmd_from_existing(struct branch *b)
+static void parse_from_existing(struct branch *b)
 {
 	if (is_null_sha1(b->sha1)) {
 		hashclr(b->branch_tree.versions[0].sha1);
@@ -2017,12 +2017,12 @@
 
 		buf = read_object_with_reference(b->sha1,
 			commit_type, &size, b->sha1);
-		cmd_from_commit(b, buf, size);
+		parse_from_commit(b, buf, size);
 		free(buf);
 	}
 }
 
-static int cmd_from(struct branch *b)
+static int parse_from(struct branch *b)
 {
 	const char *from;
 	struct branch *s;
@@ -2053,12 +2053,12 @@
 		if (oe->pack_id != MAX_PACK_ID) {
 			unsigned long size;
 			char *buf = gfi_unpack_entry(oe, &size);
-			cmd_from_commit(b, buf, size);
+			parse_from_commit(b, buf, size);
 			free(buf);
 		} else
-			cmd_from_existing(b);
+			parse_from_existing(b);
 	} else if (!get_sha1(from, b->sha1))
-		cmd_from_existing(b);
+		parse_from_existing(b);
 	else
 		die("Invalid ref name or SHA1 expression: %s", from);
 
@@ -2066,7 +2066,7 @@
 	return 1;
 }
 
-static struct hash_list *cmd_merge(unsigned int *count)
+static struct hash_list *parse_merge(unsigned int *count)
 {
 	struct hash_list *list = NULL, *n, *e = e;
 	const char *from;
@@ -2107,7 +2107,7 @@
 	return list;
 }
 
-static void cmd_new_commit(void)
+static void parse_new_commit(void)
 {
 	static struct strbuf msg = STRBUF_INIT;
 	struct branch *b;
@@ -2124,7 +2124,7 @@
 		b = new_branch(sp);
 
 	read_next_command();
-	cmd_mark();
+	parse_mark();
 	if (!prefixcmp(command_buf.buf, "author ")) {
 		author = parse_ident(command_buf.buf + 7);
 		read_next_command();
@@ -2135,10 +2135,10 @@
 	}
 	if (!committer)
 		die("Expected committer but didn't get one");
-	cmd_data(&msg);
+	parse_data(&msg);
 	read_next_command();
-	cmd_from(b);
-	merge_list = cmd_merge(&merge_count);
+	parse_from(b);
+	merge_list = parse_merge(&merge_count);
 
 	/* ensure the branch is active/loaded */
 	if (!b->branch_tree.tree || !max_active_branches) {
@@ -2196,7 +2196,7 @@
 	b->last_commit = object_count_by_type[OBJ_COMMIT];
 }
 
-static void cmd_new_tag(void)
+static void parse_new_tag(void)
 {
 	static struct strbuf msg = STRBUF_INIT;
 	char *sp;
@@ -2253,7 +2253,7 @@
 
 	/* tag payload/message */
 	read_next_command();
-	cmd_data(&msg);
+	parse_data(&msg);
 
 	/* build the tag object */
 	strbuf_reset(&new_data);
@@ -2273,7 +2273,7 @@
 		t->pack_id = pack_id;
 }
 
-static void cmd_reset_branch(void)
+static void parse_reset_branch(void)
 {
 	struct branch *b;
 	char *sp;
@@ -2293,12 +2293,12 @@
 	else
 		b = new_branch(sp);
 	read_next_command();
-	cmd_from(b);
+	parse_from(b);
 	if (command_buf.len > 0)
 		unread_command_buf = 1;
 }
 
-static void cmd_checkpoint(void)
+static void parse_checkpoint(void)
 {
 	if (object_count) {
 		cycle_packfile();
@@ -2309,7 +2309,7 @@
 	skip_optional_lf();
 }
 
-static void cmd_progress(void)
+static void parse_progress(void)
 {
 	fwrite(command_buf.buf, 1, command_buf.len, stdout);
 	fputc('\n', stdout);
@@ -2449,17 +2449,17 @@
 	set_die_routine(die_nicely);
 	while (read_next_command() != EOF) {
 		if (!strcmp("blob", command_buf.buf))
-			cmd_new_blob();
+			parse_new_blob();
 		else if (!prefixcmp(command_buf.buf, "commit "))
-			cmd_new_commit();
+			parse_new_commit();
 		else if (!prefixcmp(command_buf.buf, "tag "))
-			cmd_new_tag();
+			parse_new_tag();
 		else if (!prefixcmp(command_buf.buf, "reset "))
-			cmd_reset_branch();
+			parse_reset_branch();
 		else if (!strcmp("checkpoint", command_buf.buf))
-			cmd_checkpoint();
+			parse_checkpoint();
 		else if (!prefixcmp(command_buf.buf, "progress "))
-			cmd_progress();
+			parse_progress();
 		else
 			die("Unsupported command: %s", command_buf.buf);
 	}
diff --git a/git-am.sh b/git-am.sh
index 75886a8..b48096e 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -11,7 +11,7 @@
 --
 d,dotest=       (removed -- do not use)
 i,interactive   run interactively
-b,binary        pass --allo-binary-replacement to git-apply
+b,binary        pass --allow-binary-replacement to git-apply
 3,3way          allow fall back on 3way merging if needed
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
diff --git a/git-bisect.sh b/git-bisect.sh
index 164e8ed..4bcbace 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -63,40 +63,40 @@
 
 bisect_start() {
 	#
-	# Verify HEAD. If we were bisecting before this, reset to the
-	# top-of-line master first!
+	# Verify HEAD.
 	#
 	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)
-		branch=`cat "$GIT_DIR/BISECT_START"`
-		git checkout $branch || exit
-		;;
-	refs/heads/*|$_x40)
-		# This error message should only be triggered by cogito usage,
-		# and cogito users should understand it relates to cg-seek.
-		[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
-		start_head="${head#refs/heads/}"
-		;;
-	*)
-		die "Bad HEAD - strange symbolic ref"
-		;;
-	esac
 
 	#
-	# Get rid of any old bisect state
+	# Check if we are bisecting.
+	#
+	start_head=''
+	if test -s "$GIT_DIR/BISECT_START"
+	then
+		# Reset to the rev from where we started.
+		start_head=$(cat "$GIT_DIR/BISECT_START")
+		git checkout "$start_head" || exit
+	else
+		# Get rev from where we start.
+		case "$head" in
+		refs/heads/*|$_x40)
+			# This error message should only be triggered by
+			# cogito usage, and cogito users should understand
+			# it relates to cg-seek.
+			[ -s "$GIT_DIR/head-name" ] &&
+				die "won't bisect on seeked tree"
+			start_head="${head#refs/heads/}"
+			;;
+		*)
+			die "Bad HEAD - strange symbolic ref"
+			;;
+		esac
+	fi
+
+	#
+	# Get rid of any old bisect state.
 	#
 	bisect_clean_state
 
@@ -118,7 +118,7 @@
 		break
 		;;
 	    *)
-		rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
+		rev=$(git rev-parse -q --verify "$arg^{commit}") || {
 		    test $has_double_dash -eq 1 &&
 		        die "'$arg' does not appear to be a valid revision"
 		    break
@@ -133,11 +133,29 @@
 	    esac
 	done
 
-	sq "$@" >"$GIT_DIR/BISECT_NAMES"
-	test -n "$start_head" && echo "$start_head" >"$GIT_DIR/BISECT_START"
-	eval "$eval"
-	echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
+	#
+	# Change state.
+	# In case of mistaken revs or checkout error, or signals received,
+	# "bisect_auto_next" below may exit or misbehave.
+	# We have to trap this to be able to clean up using
+	# "bisect_clean_state".
+	#
+	trap 'bisect_clean_state' 0
+	trap 'exit 255' 1 2 3 15
+
+	#
+	# Write new start state.
+	#
+	sq "$@" >"$GIT_DIR/BISECT_NAMES" &&
+	echo "$start_head" >"$GIT_DIR/BISECT_START" &&
+	eval "$eval" &&
+	echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
+	#
+	# Check if we can proceed to the next bisect state.
+	#
 	bisect_auto_next
+
+	trap '-' 0
 }
 
 bisect_write() {
@@ -149,9 +167,9 @@
 		good|skip)	tag="$state"-"$rev" ;;
 		*)		die "Bad bisect_write argument: $state" ;;
 	esac
-	git update-ref "refs/bisect/$tag" "$rev"
+	git update-ref "refs/bisect/$tag" "$rev" || exit
 	echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG"
-	test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
+	test -n "$nolog" || echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
 
 bisect_state() {
@@ -348,9 +366,7 @@
 	exit_if_skipped_commits "$bisect_rev"
 
 	echo "Bisecting: $bisect_nr revisions left to test after this"
-	git branch -D new-bisect 2> /dev/null
-	git checkout -q -b new-bisect "$bisect_rev" || exit
-	git branch -M new-bisect bisect
+	git checkout -q "$bisect_rev" || exit
 	git show-branch "$bisect_rev"
 }
 
@@ -392,24 +408,22 @@
 	*)
 	    usage ;;
 	esac
-	if git checkout "$branch"; then
-		# Cleanup head-name if it got left by an old version of git-bisect
-		rm -f "$GIT_DIR/head-name"
-		rm -f "$GIT_DIR/BISECT_START"
-		bisect_clean_state
-	fi
+	git checkout "$branch" && bisect_clean_state
 }
 
 bisect_clean_state() {
 	# There may be some refs packed during bisection.
-	git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect |
+	git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
 	while read ref hash
 	do
 		git update-ref -d $ref $hash
 	done
+	rm -f "$GIT_DIR/BISECT_START"
 	rm -f "$GIT_DIR/BISECT_LOG"
 	rm -f "$GIT_DIR/BISECT_NAMES"
 	rm -f "$GIT_DIR/BISECT_RUN"
+	# Cleanup head-name if it got left by an old version of git-bisect
+	rm -f "$GIT_DIR/head-name"
 }
 
 bisect_replay () {
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index b6036bd..c6c70e9 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -6,16 +6,21 @@
 use Data::Dumper;
 use File::Basename qw(basename dirname);
 use File::Spec;
+use Git;
 
-our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w);
+our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w, $opt_W);
 
-getopts('uhPpvcfam:d:w:');
+getopts('uhPpvcfam:d:w:W');
 
 $opt_h && usage();
 
 die "Need at least one commit identifier!" unless @ARGV;
 
-if ($opt_w) {
+# Get git-config settings
+my $repo = Git->repository();
+$opt_w = $repo->config('cvsexportcommit.cvsdir') unless defined $opt_w;
+
+if ($opt_w || $opt_W) {
 	# Remember where GIT_DIR is before changing to CVS checkout
 	unless ($ENV{GIT_DIR}) {
 		# No GIT_DIR set. Figure it out for ourselves
@@ -25,7 +30,9 @@
 	}
 	# Make sure GIT_DIR is absolute
 	$ENV{GIT_DIR} = File::Spec->rel2abs($ENV{GIT_DIR});
+}
 
+if ($opt_w) {
 	if (! -d $opt_w."/CVS" ) {
 		die "$opt_w is not a CVS checkout";
 	}
@@ -116,6 +123,15 @@
     }
 }
 
+my $go_back_to = 0;
+
+if ($opt_W) {
+    $opt_v && print "Resetting to $parent\n";
+    $go_back_to = `git symbolic-ref HEAD 2> /dev/null ||
+	git rev-parse HEAD` || die "Could not determine current branch";
+    system("git checkout -q $parent^0") && die "Could not check out $parent^0";
+}
+
 $opt_v && print "Applying to CVS commit $commit from parent $parent\n";
 
 # grab the commit message
@@ -210,7 +226,8 @@
 	my $basename = basename($name);
 
 	$basename = "no file " . $basename if (exists($added{$basename}));
-	chomp($basename);
+	$basename =~ s/^\s+//;
+	$basename =~ s/\s+$//;
 
 	if (!exists($fullname{$basename})) {
 	  $fullname{$basename} = $name;
@@ -259,7 +276,11 @@
 }
 
 print "Applying\n";
-`GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+if ($opt_W) {
+    system("git checkout -q $commit^0") && die "cannot patch";
+} else {
+    `GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+}
 
 print "Patch applied successfully. Adding new files and directories to CVS\n";
 my $dirtypatch = 0;
@@ -312,7 +333,9 @@
     print "using a patch program. After applying the patch and resolving the\n";
     print "problems you may commit using:";
     print "\n    cd \"$opt_w\"" if $opt_w;
-    print "\n    $cmd\n\n";
+    print "\n    $cmd\n";
+    print "\n    git checkout $go_back_to\n" if $go_back_to;
+    print "\n";
     exit(1);
 }
 
@@ -332,6 +355,14 @@
 # clean up
 unlink(".cvsexportcommit.diff");
 
+if ($opt_W) {
+    system("git checkout $go_back_to") && die "cannot move back to $go_back_to";
+    if (!($go_back_to =~ /^[0-9a-fA-F]{40}$/)) {
+	system("git symbolic-ref HEAD $go_back_to") &&
+	    die "cannot move back to $go_back_to";
+    }
+}
+
 # CVS version 1.11.x and 1.12.x sleeps the wrong way to ensure the timestamp
 # used by CVS and the one set by subsequence file modifications are different.
 # If they are not different CVS will not detect changes.
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index bdac5d5..5a02550 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -780,6 +780,7 @@
 		$xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
 		$xtag =~ tr/_/\./ if ( $opt_u );
 		$xtag =~ s/[\/]/$opt_s/g;
+		$xtag =~ s/\[//g;
 
 		system('git-tag', '-f', $xtag, $cid) == 0
 			or die "Cannot create tag $xtag: $!\n";
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 29dbfc9..920bbe1 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -21,6 +21,7 @@
 
 use Fcntl;
 use File::Temp qw/tempdir tempfile/;
+use File::Path qw/rmtree/;
 use File::Basename;
 use Getopt::Long qw(:config require_order no_ignore_case);
 
@@ -86,6 +87,17 @@
 # $state holds all the bits of information the clients sends us that could
 # potentially be useful when it comes to actually _doing_ something.
 my $state = { prependdir => '' };
+
+# Work is for managing temporary working directory
+my $work =
+    {
+        state => undef,  # undef, 1 (empty), 2 (with stuff)
+        workDir => undef,
+        index => undef,
+        emptyDir => undef,
+        tmpDir => undef
+    };
+
 $log->info("--------------- STARTING -----------------");
 
 my $usage =
@@ -189,6 +201,9 @@
 $log->debug("Processing time : user=" . (times)[0] . " system=" . (times)[1]);
 $log->info("--------------- FINISH -----------------");
 
+chdir '/';
+exit 0;
+
 # Magic catchall method.
 #    This is the method that will handle all commands we haven't yet
 #    implemented. It simply sends a warning to the log file indicating a
@@ -487,7 +502,7 @@
                 print $state->{CVSROOT} . "/$state->{module}/$filename\n";
 
                 # this is an "entries" line
-                my $kopts = kopts_from_path($filepart);
+                my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
                 $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
                 print "/$filepart/1.$meta->{revision}//$kopts/\n";
                 # permissions
@@ -518,9 +533,26 @@
 
         print "Checked-in $dirpart\n";
         print "$filename\n";
-        my $kopts = kopts_from_path($filepart);
+        my $kopts = kopts_from_path($filename,"file",
+                        $state->{entries}{$filename}{modified_filename});
         print "/$filepart/0//$kopts/\n";
 
+        my $requestedKopts = $state->{opt}{k};
+        if(defined($requestedKopts))
+        {
+            $requestedKopts = "-k$requestedKopts";
+        }
+        else
+        {
+            $requestedKopts = "";
+        }
+        if( $kopts ne $requestedKopts )
+        {
+            $log->warn("Ignoring requested -k='$requestedKopts'"
+                        . " for '$filename'; detected -k='$kopts' instead");
+            #TODO: Also have option to send warning to user?
+        }
+
         $addcount++;
     }
 
@@ -600,7 +632,7 @@
 
         print "Checked-in $dirpart\n";
         print "$filename\n";
-        my $kopts = kopts_from_path($filepart);
+        my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
         print "/$filepart/-1.$wrev//$kopts/\n";
 
         $rmcount++;
@@ -770,6 +802,7 @@
     argsplit("co");
 
     my $module = $state->{args}[0];
+    $state->{module} = $module;
     my $checkout_path = $module;
 
     # use the user specified directory if we're given it
@@ -847,6 +880,7 @@
         # Don't want to check out deleted files
         next if ( $git->{filehash} eq "deleted" );
 
+        my $fullName = $git->{name};
         ( $git->{name}, $git->{dir} ) = filenamesplit($git->{name});
 
        if (length($git->{dir}) && $git->{dir} ne './'
@@ -877,7 +911,7 @@
        print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n";
 
         # this is an "entries" line
-        my $kopts = kopts_from_path($git->{name});
+        my $kopts = kopts_from_path($fullName,"sha1",$git->{filehash});
         print "/$git->{name}/1.$git->{revision}//$kopts/\n";
         # permissions
         print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n";
@@ -1086,7 +1120,7 @@
 		print $state->{CVSROOT} . "/$state->{module}/$filename\n";
 
 		# this is an "entries" line
-		my $kopts = kopts_from_path($filepart);
+		my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
 		$log->debug("/$filepart/1.$meta->{revision}//$kopts/");
 		print "/$filepart/1.$meta->{revision}//$kopts/\n";
 
@@ -1101,10 +1135,10 @@
             $log->info("Updating '$filename'");
             my ( $filepart, $dirpart ) = filenamesplit($meta->{name},1);
 
-            my $dir = tempdir( DIR => $TEMP_DIR, CLEANUP => 1 ) . "/";
+            my $mergeDir = setupTmpDir();
 
-            chdir $dir;
             my $file_local = $filepart . ".mine";
+            my $mergedFile = "$mergeDir/$file_local";
             system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local);
             my $file_old = $filepart . "." . $oldmeta->{revision};
             transmitfile($oldmeta->{filehash}, { targetfile => $file_old });
@@ -1115,11 +1149,13 @@
             $log->info("Merging $file_local, $file_old, $file_new");
             print "M Merging differences between 1.$oldmeta->{revision} and 1.$meta->{revision} into $filename\n";
 
-            $log->debug("Temporary directory for merge is $dir");
+            $log->debug("Temporary directory for merge is $mergeDir");
 
             my $return = system("git", "merge-file", $file_local, $file_old, $file_new);
             $return >>= 8;
 
+            cleanupTmpDir();
+
             if ( $return == 0 )
             {
                 $log->info("Merged successfully");
@@ -1132,7 +1168,8 @@
                     print "Merged $dirpart\n";
                     $log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
                     print $state->{CVSROOT} . "/$state->{module}/$filename\n";
-                    my $kopts = kopts_from_path($filepart);
+                    my $kopts = kopts_from_path("$dirpart/$filepart",
+                                                "file",$mergedFile);
                     $log->debug("/$filepart/1.$meta->{revision}//$kopts/");
                     print "/$filepart/1.$meta->{revision}//$kopts/\n";
                 }
@@ -1148,7 +1185,8 @@
                 {
                     print "Merged $dirpart\n";
                     print $state->{CVSROOT} . "/$state->{module}/$filename\n";
-                    my $kopts = kopts_from_path($filepart);
+                    my $kopts = kopts_from_path("$dirpart/$filepart",
+                                                "file",$mergedFile);
                     print "/$filepart/1.$meta->{revision}/+/$kopts/\n";
                 }
             }
@@ -1168,13 +1206,11 @@
                 # transmit file, format is single integer on a line by itself (file
                 # size) followed by the file contents
                 # TODO : we should copy files in blocks
-                my $data = `cat $file_local`;
+                my $data = `cat $mergedFile`;
                 $log->debug("File size : " . length($data));
                 print length($data) . "\n";
                 print $data;
             }
-
-            chdir "/";
         }
 
     }
@@ -1195,6 +1231,7 @@
     if ( $state->{method} eq 'pserver')
     {
         print "error 1 pserver access cannot commit\n";
+        cleanupWorkTree();
         exit;
     }
 
@@ -1202,6 +1239,7 @@
     {
         $log->warn("file 'index' already exists in the git repository");
         print "error 1 Index already exists in git repo\n";
+        cleanupWorkTree();
         exit;
     }
 
@@ -1209,31 +1247,20 @@
     my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
     $updater->update();
 
-    my $tmpdir = tempdir ( DIR => $TEMP_DIR );
-    my ( undef, $file_index ) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 );
-    $log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'");
-
-    $ENV{GIT_DIR} = $state->{CVSROOT} . "/";
-    $ENV{GIT_WORK_TREE} = ".";
-    $ENV{GIT_INDEX_FILE} = $file_index;
-
     # Remember where the head was at the beginning.
     my $parenthash = `git show-ref -s refs/heads/$state->{module}`;
     chomp $parenthash;
     if ($parenthash !~ /^[0-9a-f]{40}$/) {
 	    print "error 1 pserver cannot find the current HEAD of module";
+	    cleanupWorkTree();
 	    exit;
     }
 
-    chdir $tmpdir;
+    setupWorkTree($parenthash);
 
-    # populate the temporary index
-    system("git-read-tree", $parenthash);
-    unless ($? == 0)
-    {
-	die "Error running git-read-tree $state->{module} $file_index $!";
-    }
-    $log->info("Created index '$file_index' for head $state->{module} - exit status $?");
+    $log->info("Lockless commit start, basing commit on '$work->{workDir}', index file is '$work->{index}'");
+
+    $log->info("Created index '$work->{index}' for head $state->{module} - exit status $?");
 
     my @committedfiles = ();
     my %oldmeta;
@@ -1271,7 +1298,7 @@
         {
             # fail everything if an up to date check fails
             print "error 1 Up to date check failed for $filename\n";
-            chdir "/";
+            cleanupWorkTree();
             exit;
         }
 
@@ -1313,7 +1340,7 @@
     {
         print "E No files to commit\n";
         print "ok\n";
-        chdir "/";
+        cleanupWorkTree();
         return;
     }
 
@@ -1336,7 +1363,7 @@
     {
         $log->warn("Commit failed (Invalid commit hash)");
         print "error 1 Commit failed (unknown reason)\n";
-        chdir "/";
+        cleanupWorkTree();
         exit;
     }
 
@@ -1348,7 +1375,7 @@
 		{
 			$log->warn("Commit failed (update hook declined to update ref)");
 			print "error 1 Commit failed (update hook declined)\n";
-			chdir "/";
+			cleanupWorkTree();
 			exit;
 		}
 	}
@@ -1358,6 +1385,7 @@
 			"refs/heads/$state->{module}", $commithash, $parenthash)) {
 		$log->warn("update-ref for $state->{module} failed.");
 		print "error 1 Cannot commit -- update first\n";
+		cleanupWorkTree();
 		exit;
 	}
 
@@ -1409,12 +1437,12 @@
             }
             print "Checked-in $dirpart\n";
             print "$filename\n";
-            my $kopts = kopts_from_path($filepart);
+            my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
             print "/$filepart/1.$meta->{revision}//$kopts/\n";
         }
     }
 
-    chdir "/";
+    cleanupWorkTree();
     print "ok\n";
 }
 
@@ -1757,15 +1785,9 @@
     argsfromdir($updater);
 
     # we'll need a temporary checkout dir
-    my $tmpdir = tempdir ( DIR => $TEMP_DIR );
-    my ( undef, $file_index ) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 );
-    $log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'");
+    setupWorkTree();
 
-    $ENV{GIT_DIR} = $state->{CVSROOT} . "/";
-    $ENV{GIT_WORK_TREE} = ".";
-    $ENV{GIT_INDEX_FILE} = $file_index;
-
-    chdir $tmpdir;
+    $log->info("Temp checkoutdir creation successful, basing annotate session work on '$work->{workDir}', index file is '$ENV{GIT_INDEX_FILE}'");
 
     # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
@@ -1789,10 +1811,10 @@
 	system("git-read-tree", $lastseenin);
 	unless ($? == 0)
 	{
-	    print "E error running git-read-tree $lastseenin $file_index $!\n";
+	    print "E error running git-read-tree $lastseenin $ENV{GIT_INDEX_FILE} $!\n";
 	    return;
 	}
-	$log->info("Created index '$file_index' with commit $lastseenin - exit status $?");
+	$log->info("Created index '$ENV{GIT_INDEX_FILE}' with commit $lastseenin - exit status $?");
 
         # do a checkout of the file
         system('git-checkout-index', '-f', '-u', $filename);
@@ -1808,7 +1830,7 @@
         # git-jsannotate telling us about commits we are hiding
         # from the client.
 
-        my $a_hints = "$tmpdir/.annotate_hints";
+        my $a_hints = "$work->{workDir}/.annotate_hints";
         if (!open(ANNOTATEHINTS, '>', $a_hints)) {
             print "E failed to open '$a_hints' for writing: $!\n";
             return;
@@ -1862,7 +1884,7 @@
     }
 
     # done; get out of the tempdir
-    chdir "/";
+    cleanupWorkDir();
 
     print "ok\n";
 
@@ -2115,26 +2137,388 @@
     return $filename;
 }
 
+sub validateGitDir
+{
+    if( !defined($state->{CVSROOT}) )
+    {
+        print "error 1 CVSROOT not specified\n";
+        cleanupWorkTree();
+        exit;
+    }
+    if( $ENV{GIT_DIR} ne ($state->{CVSROOT} . '/') )
+    {
+        print "error 1 Internally inconsistent CVSROOT\n";
+        cleanupWorkTree();
+        exit;
+    }
+}
+
+# Setup working directory in a work tree with the requested version
+# loaded in the index.
+sub setupWorkTree
+{
+    my ($ver) = @_;
+
+    validateGitDir();
+
+    if( ( defined($work->{state}) && $work->{state} != 1 ) ||
+        defined($work->{tmpDir}) )
+    {
+        $log->warn("Bad work tree state management");
+        print "error 1 Internal setup multiple work trees without cleanup\n";
+        cleanupWorkTree();
+        exit;
+    }
+
+    $work->{workDir} = tempdir ( DIR => $TEMP_DIR );
+
+    if( !defined($work->{index}) )
+    {
+        (undef, $work->{index}) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 );
+    }
+
+    chdir $work->{workDir} or
+        die "Unable to chdir to $work->{workDir}\n";
+
+    $log->info("Setting up GIT_WORK_TREE as '.' in '$work->{workDir}', index file is '$work->{index}'");
+
+    $ENV{GIT_WORK_TREE} = ".";
+    $ENV{GIT_INDEX_FILE} = $work->{index};
+    $work->{state} = 2;
+
+    if($ver)
+    {
+        system("git","read-tree",$ver);
+        unless ($? == 0)
+        {
+            $log->warn("Error running git-read-tree");
+            die "Error running git-read-tree $ver in $work->{workDir} $!\n";
+        }
+    }
+    # else # req_annotate reads tree for each file
+}
+
+# Ensure current directory is in some kind of working directory,
+# with a recent version loaded in the index.
+sub ensureWorkTree
+{
+    if( defined($work->{tmpDir}) )
+    {
+        $log->warn("Bad work tree state management [ensureWorkTree()]");
+        print "error 1 Internal setup multiple dirs without cleanup\n";
+        cleanupWorkTree();
+        exit;
+    }
+    if( $work->{state} )
+    {
+        return;
+    }
+
+    validateGitDir();
+
+    if( !defined($work->{emptyDir}) )
+    {
+        $work->{emptyDir} = tempdir ( DIR => $TEMP_DIR, OPEN => 0);
+    }
+    chdir $work->{emptyDir} or
+        die "Unable to chdir to $work->{emptyDir}\n";
+
+    my $ver = `git show-ref -s refs/heads/$state->{module}`;
+    chomp $ver;
+    if ($ver !~ /^[0-9a-f]{40}$/)
+    {
+        $log->warn("Error from git show-ref -s refs/head$state->{module}");
+        print "error 1 cannot find the current HEAD of module";
+        cleanupWorkTree();
+        exit;
+    }
+
+    if( !defined($work->{index}) )
+    {
+        (undef, $work->{index}) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 );
+    }
+
+    $ENV{GIT_WORK_TREE} = ".";
+    $ENV{GIT_INDEX_FILE} = $work->{index};
+    $work->{state} = 1;
+
+    system("git","read-tree",$ver);
+    unless ($? == 0)
+    {
+        die "Error running git-read-tree $ver $!\n";
+    }
+}
+
+# Cleanup working directory that is not needed any longer.
+sub cleanupWorkTree
+{
+    if( ! $work->{state} )
+    {
+        return;
+    }
+
+    chdir "/" or die "Unable to chdir '/'\n";
+
+    if( defined($work->{workDir}) )
+    {
+        rmtree( $work->{workDir} );
+        undef $work->{workDir};
+    }
+    undef $work->{state};
+}
+
+# Setup a temporary directory (not a working tree), typically for
+# merging dirty state as in req_update.
+sub setupTmpDir
+{
+    $work->{tmpDir} = tempdir ( DIR => $TEMP_DIR );
+    chdir $work->{tmpDir} or die "Unable to chdir $work->{tmpDir}\n";
+
+    return $work->{tmpDir};
+}
+
+# Clean up a previously setupTmpDir.  Restore previous work tree if
+# appropriate.
+sub cleanupTmpDir
+{
+    if ( !defined($work->{tmpDir}) )
+    {
+        $log->warn("cleanup tmpdir that has not been setup");
+        die "Cleanup tmpDir that has not been setup\n";
+    }
+    if( defined($work->{state}) )
+    {
+        if( $work->{state} == 1 )
+        {
+            chdir $work->{emptyDir} or
+                die "Unable to chdir to $work->{emptyDir}\n";
+        }
+        elsif( $work->{state} == 2 )
+        {
+            chdir $work->{workDir} or
+                die "Unable to chdir to $work->{emptyDir}\n";
+        }
+        else
+        {
+            $log->warn("Inconsistent work dir state");
+            die "Inconsistent work dir state\n";
+        }
+    }
+    else
+    {
+        chdir "/" or die "Unable to chdir '/'\n";
+    }
+}
+
 # Given a path, this function returns a string containing the kopts
 # that should go into that path's Entries line.  For example, a binary
 # file should get -kb.
 sub kopts_from_path
 {
-	my ($path) = @_;
+    my ($path, $srcType, $name) = @_;
 
-	# Once it exists, the git attributes system should be used to look up
-	# what attributes apply to this path.
-
-	# Until then, take the setting from the config file
-    unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i )
+    if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and
+         $cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i )
     {
-		# Return "" to give no special treatment to any path
-		return "";
-    } else {
-		# Alternatively, to have all files treated as if they are binary (which
-		# is more like git itself), always return the "-kb" option
-		return "-kb";
+        my ($val) = check_attr( "crlf", $path );
+        if ( $val eq "set" )
+        {
+            return "";
+        }
+        elsif ( $val eq "unset" )
+        {
+            return "-kb"
+        }
+        else
+        {
+            $log->info("Unrecognized check_attr crlf $path : $val");
+        }
     }
+
+    if ( defined ( $cfg->{gitcvs}{allbinary} ) )
+    {
+        if( ($cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i) )
+        {
+            return "-kb";
+        }
+        elsif( ($cfg->{gitcvs}{allbinary} =~ /^\s*guess\s*$/i) )
+        {
+            if( $srcType eq "sha1Or-k" &&
+                !defined($name) )
+            {
+                my ($ret)=$state->{entries}{$path}{options};
+                if( !defined($ret) )
+                {
+                    $ret=$state->{opt}{k};
+                    if(defined($ret))
+                    {
+                        $ret="-k$ret";
+                    }
+                    else
+                    {
+                        $ret="";
+                    }
+                }
+                if( ! ($ret=~/^(|-kb|-kkv|-kkvl|-kk|-ko|-kv)$/) )
+                {
+                    print "E Bad -k option\n";
+                    $log->warn("Bad -k option: $ret");
+                    die "Error: Bad -k option: $ret\n";
+                }
+
+                return $ret;
+            }
+            else
+            {
+                if( is_binary($srcType,$name) )
+                {
+                    $log->debug("... as binary");
+                    return "-kb";
+                }
+                else
+                {
+                    $log->debug("... as text");
+                }
+            }
+        }
+    }
+    # Return "" to give no special treatment to any path
+    return "";
+}
+
+sub check_attr
+{
+    my ($attr,$path) = @_;
+    ensureWorkTree();
+    if ( open my $fh, '-|', "git", "check-attr", $attr, "--", $path )
+    {
+        my $val = <$fh>;
+        close $fh;
+        $val =~ s/.*: ([^:\r\n]*)\s*$/$1/;
+        return $val;
+    }
+    else
+    {
+        return undef;
+    }
+}
+
+# This should have the same heuristics as convert.c:is_binary() and related.
+# Note that the bare CR test is done by callers in convert.c.
+sub is_binary
+{
+    my ($srcType,$name) = @_;
+    $log->debug("is_binary($srcType,$name)");
+
+    # Minimize amount of interpreted code run in the inner per-character
+    # loop for large files, by totalling each character value and
+    # then analyzing the totals.
+    my @counts;
+    my $i;
+    for($i=0;$i<256;$i++)
+    {
+        $counts[$i]=0;
+    }
+
+    my $fh = open_blob_or_die($srcType,$name);
+    my $line;
+    while( defined($line=<$fh>) )
+    {
+        # Any '\0' and bare CR are considered binary.
+        if( $line =~ /\0|(\r[^\n])/ )
+        {
+            close($fh);
+            return 1;
+        }
+
+        # Count up each character in the line:
+        my $len=length($line);
+        for($i=0;$i<$len;$i++)
+        {
+            $counts[ord(substr($line,$i,1))]++;
+        }
+    }
+    close $fh;
+
+    # Don't count CR and LF as either printable/nonprintable
+    $counts[ord("\n")]=0;
+    $counts[ord("\r")]=0;
+
+    # Categorize individual character count into printable and nonprintable:
+    my $printable=0;
+    my $nonprintable=0;
+    for($i=0;$i<256;$i++)
+    {
+        if( $i < 32 &&
+            $i != ord("\b") &&
+            $i != ord("\t") &&
+            $i != 033 &&       # ESC
+            $i != 014 )        # FF
+        {
+            $nonprintable+=$counts[$i];
+        }
+        elsif( $i==127 )  # DEL
+        {
+            $nonprintable+=$counts[$i];
+        }
+        else
+        {
+            $printable+=$counts[$i];
+        }
+    }
+
+    return ($printable >> 7) < $nonprintable;
+}
+
+# Returns open file handle.  Possible invocations:
+#  - open_blob_or_die("file",$filename);
+#  - open_blob_or_die("sha1",$filehash);
+sub open_blob_or_die
+{
+    my ($srcType,$name) = @_;
+    my ($fh);
+    if( $srcType eq "file" )
+    {
+        if( !open $fh,"<",$name )
+        {
+            $log->warn("Unable to open file $name: $!");
+            die "Unable to open file $name: $!\n";
+        }
+    }
+    elsif( $srcType eq "sha1" || $srcType eq "sha1Or-k" )
+    {
+        unless ( defined ( $name ) and $name =~ /^[a-zA-Z0-9]{40}$/ )
+        {
+            $log->warn("Need filehash");
+            die "Need filehash\n";
+        }
+
+        my $type = `git cat-file -t $name`;
+        chomp $type;
+
+        unless ( defined ( $type ) and $type eq "blob" )
+        {
+            $log->warn("Invalid type '$type' for '$name'");
+            die ( "Invalid type '$type' (expected 'blob')" )
+        }
+
+        my $size = `git cat-file -s $name`;
+        chomp $size;
+
+        $log->debug("open_blob_or_die($name) size=$size, type=$type");
+
+        unless( open $fh, '-|', "git", "cat-file", "blob", $name )
+        {
+            $log->warn("Unable to open sha1 $name");
+            die "Unable to open sha1 $name\n";
+        }
+    }
+    else
+    {
+        $log->warn("Unknown type of blob source: $srcType");
+        die "Unknown type of blob source: $srcType\n";
+    }
+    return $fh;
 }
 
 # Generate a CVS author name from Git author information, by taking
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 7c25bb9..9df4971 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -601,6 +601,7 @@
 	}
 }
 
+set default_config(branch.autosetupmerge) true
 set default_config(merge.diffstat) true
 set default_config(merge.summary) false
 set default_config(merge.verbosity) 2
diff --git a/git-gui/lib/branch_create.tcl b/git-gui/lib/branch_create.tcl
index 53dfb4c..3817771 100644
--- a/git-gui/lib/branch_create.tcl
+++ b/git-gui/lib/branch_create.tcl
@@ -183,6 +183,9 @@
 	if {$spec ne {} && $opt_fetch} {
 		$co enable_fetch $spec
 	}
+	if {$spec ne {}} {
+		$co remote_source $spec
+	}
 
 	if {[$co run]} {
 		destroy $w
diff --git a/git-gui/lib/branch_delete.tcl b/git-gui/lib/branch_delete.tcl
index 86c4f73..ef1930b 100644
--- a/git-gui/lib/branch_delete.tcl
+++ b/git-gui/lib/branch_delete.tcl
@@ -127,7 +127,7 @@
 	foreach i $to_delete {
 		set b [lindex $i 0]
 		set o [lindex $i 1]
-		if {[catch {git update-ref -d "refs/heads/$b" $o} err]} {
+		if {[catch {git branch -D $b} err]} {
 			append failed " - $b: $err\n"
 		}
 	}
diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl
index 6e14117..caca888 100644
--- a/git-gui/lib/checkout_op.tcl
+++ b/git-gui/lib/checkout_op.tcl
@@ -16,6 +16,7 @@
 field fetch_spec   {}; # refetch tracking branch if used?
 field checkout      1; # actually checkout the branch?
 field create        0; # create the branch if it doesn't exist?
+field remote_source {}; # same as fetch_spec, to setup tracking
 
 field reset_ok      0; # did the user agree to reset?
 field fetch_ok      0; # did the fetch succeed?
@@ -44,6 +45,10 @@
 	set fetch_spec $spec
 }
 
+method remote_source {spec} {
+	set remote_source $spec
+}
+
 method enable_checkout {co} {
 	set checkout $co
 }
@@ -145,7 +150,7 @@
 }
 
 method _update_ref {} {
-	global null_sha1 current_branch
+	global null_sha1 current_branch repo_config
 
 	set ref $new_ref
 	set new $new_hash
@@ -172,6 +177,23 @@
 
 		set reflog_msg "branch: Created from $new_expr"
 		set cur $null_sha1
+
+		if {($repo_config(branch.autosetupmerge) eq {true}
+			|| $repo_config(branch.autosetupmerge) eq {always})
+			&& $remote_source ne {}
+			&& "refs/heads/$newbranch" eq $ref} {
+
+			set c_remote [lindex $remote_source 1]
+			set c_merge [lindex $remote_source 2]
+			if {[catch {
+					git config branch.$newbranch.remote $c_remote
+					git config branch.$newbranch.merge  $c_merge
+				} err]} {
+				_error $this [strcat \
+				[mc "Failed to configure simplified git-pull for '%s'." $newbranch] \
+				"\n\n$err"]
+			}
+		}
 	} elseif {$create && $merge_type eq {none}} {
 		# We were told to create it, but not do a merge.
 		# Bad.  Name shouldn't have existed.
diff --git a/git-gui/lib/database.tcl b/git-gui/lib/database.tcl
index d66aa3f..a18ac8b 100644
--- a/git-gui/lib/database.tcl
+++ b/git-gui/lib/database.tcl
@@ -102,8 +102,8 @@
 		*]]
 
 	if {$objects_current >= $object_limit} {
-		set objects_current [expr {$objects_current * 256}]
-		set object_limit    [expr {$object_limit    * 256}]
+		set objects_current [expr {$objects_current * 250}]
+		set object_limit    [expr {$object_limit    * 250}]
 		if {[ask_popup \
 			[mc "This repository currently has approximately %i loose objects.
 
diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl
index 9be7486..78f344f 100644
--- a/git-gui/lib/spellcheck.tcl
+++ b/git-gui/lib/spellcheck.tcl
@@ -84,13 +84,19 @@
 	regexp \
 		{International Ispell Version .* \(but really (Aspell .*?)\)$} \
 		$s_version _junk s_version
+	regexp {^Aspell (\d)+\.(\d+)} $s_version _junk major minor
 
 	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
+	# fetch the language
+	if {$major > 0 || ($major == 0 && $minor >= 60)} {
+		puts $pipe_fd {$$cr master}
+		flush $pipe_fd
+		gets $pipe_fd s_lang
+		regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang
+	} else {
+		set s_lang {}
+	}
 
 	if {$::default_config(gui.spellingdictionary) eq {}
 	 && [get_config gui.spellingdictionary] eq {}} {
diff --git a/git-gui/po/de.po b/git-gui/po/de.po
index 022b816..f20955c 100644
--- a/git-gui/po/de.po
+++ b/git-gui/po/de.po
@@ -8,7 +8,7 @@
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2008-03-14 07:18+0100\n"
-"PO-Revision-Date: 2008-02-16 21:52+0100\n"
+"PO-Revision-Date: 2008-05-01 11:51+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -1754,9 +1754,8 @@
 msgstr "Anzahl der Kontextzeilen beim Vergleich"
 
 #: lib/option.tcl:127
-#, fuzzy
 msgid "Commit Message Text Width"
-msgstr "Versionsbeschreibung:"
+msgstr "Textbreite der Versionsbeschreibung"
 
 #: lib/option.tcl:128
 msgid "New Branch Name Template"
@@ -1895,40 +1894,36 @@
 
 #: lib/spellcheck.tcl:57
 msgid "Unsupported spell checker"
-msgstr ""
+msgstr "Rechtschreibprüfungsprogramm nicht unterstützt"
 
 #: lib/spellcheck.tcl:65
-#, fuzzy
 msgid "Spell checking is unavailable"
-msgstr "Rechtschreibprüfung fehlgeschlagen"
+msgstr "Rechtschreibprüfung nicht verfügbar"
 
 #: lib/spellcheck.tcl:68
 msgid "Invalid spell checking configuration"
-msgstr ""
+msgstr "Unbenutzbare Konfiguration der Rechtschreibprüfung"
 
 #: lib/spellcheck.tcl:70
 #, tcl-format
 msgid "Reverting dictionary to %s."
-msgstr ""
+msgstr "Wörterbuch auf %s zurückgesetzt."
 
 #: lib/spellcheck.tcl:73
-#, fuzzy
 msgid "Spell checker silently failed on startup"
-msgstr "Rechtschreibprüfung fehlgeschlagen"
+msgstr "Rechtschreibprüfungsprogramm mit Fehler abgebrochen"
 
 #: lib/spellcheck.tcl:80
-#, fuzzy
 msgid "Unrecognized spell checker"
-msgstr "Unbekannte Version von »aspell«"
+msgstr "Unbekanntes Rechtschreibprüfungsprogramm"
 
 #: 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«"
+msgstr "Unerwartetes EOF vom Rechtschreibprüfungsprogramm"
 
 #: lib/spellcheck.tcl:385
 msgid "Spell Checker Failed"
@@ -2002,6 +1997,3 @@
 #: 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-merge.sh b/git-merge.sh
index 69b35d8..5fc5f52 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -9,11 +9,9 @@
 git-merge [options] <msg> HEAD <remote>
 --
 stat                 show a diffstat at the end of the merge
-n,no-stat            don't show a diffstat at the end of the merge
+n                    don't show a diffstat at the end of the merge
 summary              (synonym to --stat)
-no-summary           (synonym to --no-stat)
 log                  add list of one-line log to merge commit message
-no-log               don't add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge sucesses (default)
 ff                   allow fast forward (default)
diff --git a/git-pull.sh b/git-pull.sh
index bf0c298..809e537 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -107,6 +107,11 @@
 }
 
 test true = "$rebase" && {
+	git update-index --refresh &&
+	git diff-files --quiet &&
+	git diff-index --cached --quiet HEAD -- ||
+	die "refusing to pull with rebase: your working tree is not up-to-date"
+
 	. git-parse-remote &&
 	origin="$1"
 	test -z "$origin" && origin=$(get_default_remote)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..8ee08ff 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -56,9 +56,9 @@
 require_clean_work_tree () {
 	# test if working tree is dirty
 	git rev-parse --verify HEAD > /dev/null &&
-	git update-index --refresh &&
-	git diff-files --quiet &&
-	git diff-index --cached --quiet HEAD -- ||
+	git update-index --ignore-submodules --refresh &&
+	git diff-files --quiet --ignore-submodules &&
+	git diff-index --cached --quiet HEAD --ignore-submodules -- ||
 	die "Working tree is dirty"
 }
 
@@ -377,11 +377,12 @@
 		# Sanity check
 		git rev-parse --verify HEAD >/dev/null ||
 			die "Cannot read HEAD"
-		git update-index --refresh && git diff-files --quiet ||
+		git update-index --ignore-submodules --refresh &&
+			git diff-files --quiet --ignore-submodules ||
 			die "Working tree is dirty"
 
 		# do we have anything to commit?
-		if git diff-index --cached --quiet HEAD --
+		if git diff-index --cached --quiet --ignore-submodules HEAD --
 		then
 			: Nothing to commit -- skip this
 		else
diff --git a/git-rebase.sh b/git-rebase.sh
index fbb0f28..dd7dfe1 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -60,7 +60,7 @@
 	fi
 
 	cmt=`cat "$dotest/current"`
-	if ! git diff-index --quiet HEAD --
+	if ! git diff-index --quiet --ignore-submodules HEAD --
 	then
 		if ! git commit --no-verify -C "$cmt"
 		then
@@ -150,7 +150,7 @@
 do
 	case "$1" in
 	--continue)
-		git diff-files --quiet || {
+		git diff-files --quiet --ignore-submodules || {
 			echo "You must edit all merge conflicts and then"
 			echo "mark them as resolved using git add"
 			exit 1
@@ -214,7 +214,7 @@
 		else
 			die "No rebase in progress?"
 		fi
-		git reset --hard $(cat $dotest/orig-head)
+		git reset --hard $(cat "$dotest/orig-head")
 		rm -r "$dotest"
 		exit
 		;;
@@ -282,8 +282,8 @@
 fi
 
 # The tree must be really really clean.
-git update-index --refresh || exit
-diff=$(git diff-index --cached --name-status -r HEAD --)
+git update-index --ignore-submodules --refresh || exit
+diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
 case "$diff" in
 ?*)	echo "cannot rebase: your index is not up-to-date"
 	echo "$diff"
diff --git a/git-repack.sh b/git-repack.sh
index 501519a..10f735c 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -8,7 +8,7 @@
 git-repack [options]
 --
 a               pack everything in a single pack
-A               same as -a, and keep unreachable objects too
+A               same as -a, and turn unreachable objects loose
 d               remove redundant packs, and run git-prune-packed
 f               pass --no-reuse-delta to git-pack-objects
 n               do not run git-update-server-info
@@ -23,7 +23,7 @@
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
-no_update_info= all_into_one= remove_redundant= keep_unreachable=
+no_update_info= all_into_one= remove_redundant= unpack_unreachable=
 local= quiet= no_reuse= extra=
 while test $# != 0
 do
@@ -31,7 +31,7 @@
 	-n)	no_update_info=t ;;
 	-a)	all_into_one=t ;;
 	-A)	all_into_one=t
-		keep_unreachable=--keep-unreachable ;;
+		unpack_unreachable=--unpack-unreachable ;;
 	-d)	remove_redundant=t ;;
 	-q)	quiet=-q ;;
 	-f)	no_reuse=--no-reuse-object ;;
@@ -79,9 +79,9 @@
 	if test -z "$args"
 	then
 		args='--unpacked --incremental'
-	elif test -n "$keep_unreachable"
+	elif test -n "$unpack_unreachable"
 	then
-		args="$args $keep_unreachable"
+		args="$args $unpack_unreachable"
 	fi
 	;;
 esac
diff --git a/git-send-email.perl b/git-send-email.perl
index 9e568bf..a598fdc 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -203,6 +203,7 @@
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
     "to" => \@to,
+    "cc" => \@initial_cc,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
@@ -512,7 +513,7 @@
 	close(C);
 
 	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-	system('sh', '-c', '$0 $@', $editor, $compose_filename);
+	system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
 
 	open(C2,">",$compose_filename . ".final")
 		or die "Failed to open $compose_filename.final : " . $!;
@@ -520,8 +521,30 @@
 	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);
@@ -612,6 +635,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
 {
@@ -629,13 +660,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\"";
 	}
 
@@ -958,3 +988,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-stash.sh b/git-stash.sh
index c2b6820..4938ade 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -15,8 +15,8 @@
 ref_stash=refs/stash
 
 no_changes () {
-	git diff-index --quiet --cached HEAD -- &&
-	git diff-files --quiet
+	git diff-index --quiet --cached HEAD --ignore-submodules -- &&
+	git diff-files --quiet --ignore-submodules
 }
 
 clear_stash () {
@@ -130,7 +130,7 @@
 }
 
 apply_stash () {
-	git diff-files --quiet ||
+	git diff-files --quiet --ignore-submodules ||
 		die 'Cannot restore on top of a dirty state'
 
 	unstash_index=
diff --git a/git-submodule.sh b/git-submodule.sh
index 67f7a28..1007372 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -5,7 +5,7 @@
 # Copyright (c) 2007 Lars Hjemli
 
 USAGE="[--quiet] [--cached] \
-[add <repo> [-b branch]|status|init|update|summary [-n|--summary-limit <n>] [<commit>]] \
+[add <repo> [-b branch]|status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
 [--] [<path>...]"
 OPTIONS_SPEC=
 . git-sh-setup
@@ -74,8 +74,7 @@
 {
 	# Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
 	re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
-	name=$( GIT_CONFIG=.gitmodules \
-		git config --get-regexp '^submodule\..*\.path$' |
+	name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
 		sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
        test -z "$name" &&
        die "No submodule mapping found in .gitmodules for path '$path'"
@@ -198,8 +197,8 @@
 	git add "$path" ||
 	die "Failed to add submodule '$path'"
 
-	GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
-	GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
+	git config -f .gitmodules submodule."$path".path "$path" &&
+	git config -f .gitmodules submodule."$path".url "$repo" &&
 	git add .gitmodules ||
 	die "Failed to register submodule '$path'"
 }
@@ -240,7 +239,7 @@
 		url=$(git config submodule."$name".url)
 		test -z "$url" || continue
 
-		url=$(GIT_CONFIG=.gitmodules git config submodule."$name".url)
+		url=$(git config -f .gitmodules submodule."$name".url)
 		test -z "$url" &&
 		die "No url found for submodule path '$path' in .gitmodules"
 
@@ -272,6 +271,10 @@
 		-q|--quiet)
 			quiet=1
 			;;
+		-i|--init)
+			shift
+			cmd_init "$@" || return
+			;;
 		--)
 			shift
 			break
@@ -297,6 +300,7 @@
 			# path have been specified
 			test "$#" != "0" &&
 			say "Submodule path '$path' not initialized"
+			say "Maybe you want to use 'update --init'?"
 			continue
 		fi
 
diff --git a/git-svn.perl b/git-svn.perl
index 2c53f39..37976f2 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -4,7 +4,7 @@
 use warnings;
 use strict;
 use vars qw/	$AUTHOR $VERSION
-		$sha1 $sha1_short $_revision
+		$sha1 $sha1_short $_revision $_repository
 		$_q $_authors %users/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
 $VERSION = '@@GIT_VERSION@@';
@@ -83,6 +83,7 @@
 		'repack-flags|repack-args|repack-opts=s' =>
 		   \$Git::SVN::_repack_flags,
 		'use-log-author' => \$Git::SVN::_use_log_author,
+		'add-author-from' => \$Git::SVN::_add_author_from,
 		%remote_opts );
 
 my ($_trunk, $_tags, $_branches, $_stdlayout);
@@ -221,6 +222,7 @@
 		}
 		$ENV{GIT_DIR} = $git_dir;
 	}
+	$_repository = Git->repository(Repository => $ENV{GIT_DIR});
 }
 
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
@@ -302,6 +304,7 @@
 			}
 		}
 		command_noisy(@init_db);
+		$_repository = Git->repository(Repository => ".git");
 	}
 	my $set;
 	my $pfx = "svn-remote.$Git::SVN::default_repo_id";
@@ -318,6 +321,7 @@
 	mkpath([$repo_path]) unless -d $repo_path;
 	chdir $repo_path or die "Couldn't chdir to $repo_path: $!\n";
 	$ENV{GIT_DIR} = '.git';
+	$_repository = Git->repository(Repository => $ENV{GIT_DIR});
 }
 
 sub cmd_clone {
@@ -1012,17 +1016,28 @@
 		my ($msg_fh, $ctx) = command_output_pipe('cat-file',
 		                                         $type, $treeish);
 		my $in_msg = 0;
+		my $author;
+		my $saw_from = 0;
 		while (<$msg_fh>) {
 			if (!$in_msg) {
 				$in_msg = 1 if (/^\s*$/);
+				$author = $1 if (/^author (.*>)/);
 			} elsif (/^git-svn-id: /) {
 				# skip this for now, we regenerate the
 				# correct one on re-fetch anyways
 				# TODO: set *:merge properties or like...
 			} else {
+				if (/^From:/ || /^Signed-off-by:/) {
+					$saw_from = 1;
+				}
 				print $log_fh $_ or croak $!;
 			}
 		}
+		if ($Git::SVN::_add_author_from && defined($author)
+		    && !$saw_from) {
+			print $log_fh "\nFrom: $author\n"
+			      or croak $!;
+		}
 		command_close_pipe($msg_fh, $ctx);
 	}
 	close $log_fh or croak $!;
@@ -1249,7 +1264,7 @@
 use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags $_use_svm_props $_head
             $_use_svnsync_props $no_reuse_existing $_minimize_url
-	    $_use_log_author/;
+	    $_use_log_author $_add_author_from/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
@@ -3018,6 +3033,7 @@
 use strict;
 use warnings;
 use Carp qw/croak/;
+use File::Temp qw/tempfile/;
 use IO::File qw//;
 
 # file baton members: path, mode_a, mode_b, pool, fh, blob, base
@@ -3173,14 +3189,9 @@
 	my $base = IO::File->new_tmpfile;
 	$base->autoflush(1);
 	if ($fb->{blob}) {
-		defined (my $pid = fork) or croak $!;
-		if (!$pid) {
-			open STDOUT, '>&', $base or croak $!;
-			print STDOUT 'link ' if ($fb->{mode_a} == 120000);
-			exec qw/git-cat-file blob/, $fb->{blob} or croak $!;
-		}
-		waitpid $pid, 0;
-		croak $? if $?;
+		print $base 'link ' if ($fb->{mode_a} == 120000);
+		my $size = $::_repository->cat_blob($fb->{blob}, $base);
+		die "Failed to read object $fb->{blob}" unless $size;
 
 		if (defined $exp) {
 			seek $base, 0, 0 or croak $!;
@@ -3221,14 +3232,18 @@
 				sysseek($fh, 0, 0) or croak $!;
 			}
 		}
-		defined(my $pid = open my $out,'-|') or die "Can't fork: $!\n";
-		if (!$pid) {
-			open STDIN, '<&', $fh or croak $!;
-			exec qw/git-hash-object -w --stdin/ or croak $!;
+
+		my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1);
+		my $result;
+		while ($result = sysread($fh, my $string, 1024)) {
+			syswrite($tmp_fh, $string, $result);
 		}
-		chomp($hash = do { local $/; <$out> });
-		close $out or croak $!;
+		defined $result or croak $!;
+		close $tmp_fh or croak $!;
+
 		close $fh or croak $!;
+
+		$hash = $::_repository->hash_and_insert_object($tmp_filename);
 		$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
 		close $fb->{base} or croak $!;
 	} else {
@@ -3554,13 +3569,8 @@
 	} elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
 		$self->change_file_prop($fbat,'svn:special',undef);
 	}
-	defined(my $pid = fork) or croak $!;
-	if (!$pid) {
-		open STDOUT, '>&', $fh or croak $!;
-		exec qw/git-cat-file blob/, $m->{sha1_b} or croak $!;
-	}
-	waitpid $pid, 0;
-	croak $? if $?;
+	my $size = $::_repository->cat_blob($m->{sha1_b}, $fh);
+	croak "Failed to read object $m->{sha1_b}" unless $size;
 	$fh->flush == 0 or croak $!;
 	seek $fh, 0, 0 or croak $!;
 
diff --git a/git.c b/git.c
index 89b431f..2c9004f 100644
--- a/git.c
+++ b/git.c
@@ -286,6 +286,7 @@
 		{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
 		{ "cherry", cmd_cherry, RUN_SETUP },
 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+		{ "clone", cmd_clone },
 		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
diff --git a/gitk-git/Makefile b/gitk-git/Makefile
index f90dfab..e1b6045 100644
--- a/gitk-git/Makefile
+++ b/gitk-git/Makefile
@@ -40,9 +40,9 @@
 all:: gitk-wish $(ALL_MSGFILES)
 
 install:: all
-	$(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
-	$(INSTALL) -d '$(DESTDIR_SQ)$(msgsdir_SQ)'
-	$(foreach p,$(ALL_MSGFILES), $(INSTALL) $p '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true
+	$(INSTALL) -m 755 gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
+	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(msgsdir_SQ)'
+	$(foreach p,$(ALL_MSGFILES), $(INSTALL) -m 644 $p '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true
 
 uninstall::
 	$(foreach p,$(ALL_MSGFILES), $(RM) '$(DESTDIR_SQ)$(msgsdir_SQ)'/$(notdir $p) &&) true
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 9a4d9c4..22bcd18 100644
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -2,7 +2,7 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright (C) 2005-2006 Paul Mackerras.  All rights reserved.
+# Copyright © 2005-2008 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
@@ -47,12 +47,24 @@
     lappend runq [list $fd $script]
 }
 
+proc nukefile {fd} {
+    global runq
+
+    for {set i 0} {$i < [llength $runq]} {} {
+	if {[lindex $runq $i 0] eq $fd} {
+	    set runq [lreplace $runq $i $i]
+	} else {
+	    incr i
+	}
+    }
+}
+
 proc dorunq {} {
     global isonrunq runq
 
     set tstart [clock clicks -milliseconds]
     set t0 $tstart
-    while {$runq ne {}} {
+    while {[llength $runq] > 0} {
 	set fd [lindex $runq 0 0]
 	set script [lindex $runq 0 1]
 	set repeat [eval $script]
@@ -78,41 +90,274 @@
     }
 }
 
-# Start off a git rev-list process and arrange to read its output
+proc unmerged_files {files} {
+    global nr_unmerged
+
+    # find the list of unmerged files
+    set mlist {}
+    set nr_unmerged 0
+    if {[catch {
+	set fd [open "| git ls-files -u" r]
+    } err]} {
+	show_error {} . "[mc "Couldn't get list of unmerged files:"] $err"
+	exit 1
+    }
+    while {[gets $fd line] >= 0} {
+	set i [string first "\t" $line]
+	if {$i < 0} continue
+	set fname [string range $line [expr {$i+1}] end]
+	if {[lsearch -exact $mlist $fname] >= 0} continue
+	incr nr_unmerged
+	if {$files eq {} || [path_filter $files $fname]} {
+	    lappend mlist $fname
+	}
+    }
+    catch {close $fd}
+    return $mlist
+}
+
+proc parseviewargs {n arglist} {
+    global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
+
+    set vdatemode($n) 0
+    set vmergeonly($n) 0
+    set glflags {}
+    set diffargs {}
+    set nextisval 0
+    set revargs {}
+    set origargs $arglist
+    set allknown 1
+    set filtered 0
+    set i -1
+    foreach arg $arglist {
+	incr i
+	if {$nextisval} {
+	    lappend glflags $arg
+	    set nextisval 0
+	    continue
+	}
+	switch -glob -- $arg {
+	    "-d" -
+	    "--date-order" {
+		set vdatemode($n) 1
+		# remove from origargs in case we hit an unknown option
+		set origargs [lreplace $origargs $i $i]
+		incr i -1
+	    }
+	    # These request or affect diff output, which we don't want.
+	    # Some could be used to set our defaults for diff display.
+	    "-[puabwcrRBMC]" -
+	    "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
+	    "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
+	    "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
+	    "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
+	    "--ignore-space-change" - "-U*" - "--unified=*" {
+		lappend diffargs $arg
+	    }
+	    # These cause our parsing of git log's output to fail, or else
+	    # they're options we want to set ourselves, so ignore them.
+	    "--raw" - "--patch-with-raw" - "--patch-with-stat" -
+	    "--name-only" - "--name-status" - "--color" - "--color-words" -
+	    "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
+	    "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
+	    "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
+	    "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
+	    "--objects" - "--objects-edge" - "--reverse" {
+	    }
+	    # These are harmless, and some are even useful
+	    "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
+	    "--check" - "--exit-code" - "--quiet" - "--topo-order" -
+	    "--full-history" - "--dense" - "--sparse" -
+	    "--follow" - "--left-right" - "--encoding=*" {
+		lappend glflags $arg
+	    }
+	    # These mean that we get a subset of the commits
+	    "--diff-filter=*" - "--no-merges" - "--unpacked" -
+	    "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
+	    "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
+	    "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
+	    "--remove-empty" - "--first-parent" - "--cherry-pick" -
+	    "-S*" - "--pickaxe-all" - "--pickaxe-regex" - {
+		set filtered 1
+		lappend glflags $arg
+	    }
+	    # This appears to be the only one that has a value as a
+	    # separate word following it
+	    "-n" {
+		set filtered 1
+		set nextisval 1
+		lappend glflags $arg
+	    }
+	    "--not" {
+		set notflag [expr {!$notflag}]
+		lappend revargs $arg
+	    }
+	    "--all" {
+		lappend revargs $arg
+	    }
+	    "--merge" {
+		set vmergeonly($n) 1
+		# git rev-parse doesn't understand --merge
+		lappend revargs --gitk-symmetric-diff-marker MERGE_HEAD...HEAD
+	    }
+	    # Other flag arguments including -<n>
+	    "-*" {
+		if {[string is digit -strict [string range $arg 1 end]]} {
+		    set filtered 1
+		} else {
+		    # a flag argument that we don't recognize;
+		    # that means we can't optimize
+		    set allknown 0
+		}
+		lappend glflags $arg
+	    }
+	    # Non-flag arguments specify commits or ranges of commits
+	    default {
+		if {[string match "*...*" $arg]} {
+		    lappend revargs --gitk-symmetric-diff-marker
+		}
+		lappend revargs $arg
+	    }
+	}
+    }
+    set vdflags($n) $diffargs
+    set vflags($n) $glflags
+    set vrevs($n) $revargs
+    set vfiltered($n) $filtered
+    set vorigargs($n) $origargs
+    return $allknown
+}
+
+proc parseviewrevs {view revs} {
+    global vposids vnegids
+
+    if {$revs eq {}} {
+	set revs HEAD
+    }
+    if {[catch {set ids [eval exec git rev-parse $revs]} err]} {
+	# we get stdout followed by stderr in $err
+	# for an unknown rev, git rev-parse echoes it and then errors out
+	set errlines [split $err "\n"]
+	set badrev {}
+	for {set l 0} {$l < [llength $errlines]} {incr l} {
+	    set line [lindex $errlines $l]
+	    if {!([string length $line] == 40 && [string is xdigit $line])} {
+		if {[string match "fatal:*" $line]} {
+		    if {[string match "fatal: ambiguous argument*" $line]
+			&& $badrev ne {}} {
+			if {[llength $badrev] == 1} {
+			    set err "unknown revision $badrev"
+			} else {
+			    set err "unknown revisions: [join $badrev ", "]"
+			}
+		    } else {
+			set err [join [lrange $errlines $l end] "\n"]
+		    }
+		    break
+		}
+		lappend badrev $line
+	    }
+	}		    
+	error_popup "Error parsing revisions: $err"
+	return {}
+    }
+    set ret {}
+    set pos {}
+    set neg {}
+    set sdm 0
+    foreach id [split $ids "\n"] {
+	if {$id eq "--gitk-symmetric-diff-marker"} {
+	    set sdm 4
+	} elseif {[string match "^*" $id]} {
+	    if {$sdm != 1} {
+		lappend ret $id
+		if {$sdm == 3} {
+		    set sdm 0
+		}
+	    }
+	    lappend neg [string range $id 1 end]
+	} else {
+	    if {$sdm != 2} {
+		lappend ret $id
+	    } else {
+		lset ret end [lindex $ret end]...$id
+	    }
+	    lappend pos $id
+	}
+	incr sdm -1
+    }
+    set vposids($view) $pos
+    set vnegids($view) $neg
+    return $ret
+}
+
+# Start off a git log process and arrange to read its output
 proc start_rev_list {view} {
-    global startmsecs
-    global commfd leftover tclencoding datemode
-    global viewargs viewargscmd viewfiles commitidx viewcomplete vnextroot
+    global startmsecs commitidx viewcomplete curview
+    global commfd leftover tclencoding
+    global viewargs viewargscmd viewfiles vfilelimit
     global showlocalchanges commitinterest mainheadid
-    global progressdirn progresscoords proglastnc curview
+    global viewactive loginstance viewinstances vmergeonly
+    global pending_select mainheadid
+    global vcanopt vflags vrevs vorigargs
 
     set startmsecs [clock clicks -milliseconds]
     set commitidx($view) 0
-    set viewcomplete($view) 0
-    set vnextroot($view) 0
+    # these are set this way for the error exits
+    set viewcomplete($view) 1
+    set viewactive($view) 0
+    varcinit $view
+
     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
+	    return 0
 	}
 	set args [concat $args [split $str "\n"]]
     }
-    set order "--topo-order"
-    if {$datemode} {
-	set order "--date-order"
+    set vcanopt($view) [parseviewargs $view $args]
+
+    set files $viewfiles($view)
+    if {$vmergeonly($view)} {
+	set files [unmerged_files $files]
+	if {$files eq {}} {
+	    global nr_unmerged
+	    if {$nr_unmerged == 0} {
+		error_popup [mc "No files selected: --merge specified but\
+			     no files are unmerged."]
+	    } else {
+		error_popup [mc "No files selected: --merge specified but\
+			     no unmerged files are within file limit."]
+	    }
+	    return 0
+	}
     }
+    set vfilelimit($view) $files
+
+    if {$vcanopt($view)} {
+	set revs [parseviewrevs $view $vrevs($view)]
+	if {$revs eq {}} {
+	    return 0
+	}
+	set args [concat $vflags($view) $revs]
+    } else {
+	set args $vorigargs($view)
+    }
+
     if {[catch {
-	set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
-			 --boundary $args "--" $viewfiles($view)] r]
+	set fd [open [concat | git log --no-color -z --pretty=raw --parents \
+			 --boundary $args "--" $files] r]
     } err]} {
-	error_popup "[mc "Error executing git rev-list:"] $err"
-	exit 1
+	error_popup "[mc "Error executing git log:"] $err"
+	return 0
     }
-    set commfd($view) $fd
-    set leftover($view) {}
+    set i [incr loginstance]
+    set viewinstances($view) [list $i]
+    set commfd($i) $fd
+    set leftover($i) {}
     if {$showlocalchanges} {
 	lappend commitinterest($mainheadid) {dodiffindex}
     }
@@ -120,35 +365,152 @@
     if {$tclencoding != {}} {
 	fconfigure $fd -encoding $tclencoding
     }
-    filerun $fd [list getcommitlines $fd $view]
+    filerun $fd [list getcommitlines $fd $i $view 0]
     nowbusy $view [mc "Reading"]
     if {$view == $curview} {
-	set progressdirn 1
-	set progresscoords {0 0}
-	set proglastnc 0
+	set pending_select $mainheadid
     }
+    set viewcomplete($view) 0
+    set viewactive($view) 1
+    return 1
 }
 
-proc stop_rev_list {} {
-    global commfd curview
+proc stop_rev_list {view} {
+    global commfd viewinstances leftover
 
-    if {![info exists commfd($curview)]} return
-    set fd $commfd($curview)
-    catch {
-	set pid [pid $fd]
-	exec kill $pid
+    foreach inst $viewinstances($view) {
+	set fd $commfd($inst)
+	catch {
+	    set pid [pid $fd]
+	    exec kill $pid
+	}
+	catch {close $fd}
+	nukefile $fd
+	unset commfd($inst)
+	unset leftover($inst)
     }
-    catch {close $fd}
-    unset commfd($curview)
+    set viewinstances($view) {}
 }
 
 proc getcommits {} {
-    global phase canv curview
+    global canv curview need_redisplay viewactive
 
-    set phase getcommits
     initlayout
-    start_rev_list $curview
-    show_status [mc "Reading commits..."]
+    if {[start_rev_list $curview]} {
+	show_status [mc "Reading commits..."]
+	set need_redisplay 1
+    } else {
+	show_status [mc "No commits selected"]
+    }
+}
+
+proc updatecommits {} {
+    global curview vcanopt vorigargs vfilelimit viewinstances
+    global viewactive viewcomplete loginstance tclencoding mainheadid
+    global startmsecs commfd showneartags showlocalchanges leftover
+    global mainheadid pending_select
+    global isworktree
+    global varcid vposids vnegids vflags vrevs
+
+    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+    set oldmainid $mainheadid
+    rereadrefs
+    if {$showlocalchanges} {
+	if {$mainheadid ne $oldmainid} {
+	    dohidelocalchanges
+	}
+	if {[commitinview $mainheadid $curview]} {
+	    dodiffindex
+	}
+    }
+    set view $curview
+    if {$vcanopt($view)} {
+	set oldpos $vposids($view)
+	set oldneg $vnegids($view)
+	set revs [parseviewrevs $view $vrevs($view)]
+	if {$revs eq {}} {
+	    return
+	}
+	# note: getting the delta when negative refs change is hard,
+	# and could require multiple git log invocations, so in that
+	# case we ask git log for all the commits (not just the delta)
+	if {$oldneg eq $vnegids($view)} {
+	    set newrevs {}
+	    set npos 0
+	    # take out positive refs that we asked for before or
+	    # that we have already seen
+	    foreach rev $revs {
+		if {[string length $rev] == 40} {
+		    if {[lsearch -exact $oldpos $rev] < 0
+			&& ![info exists varcid($view,$rev)]} {
+			lappend newrevs $rev
+			incr npos
+		    }
+		} else {
+		    lappend $newrevs $rev
+		}
+	    }
+	    if {$npos == 0} return
+	    set revs $newrevs
+	    set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
+	}
+	set args [concat $vflags($view) $revs --not $oldpos]
+    } else {
+	set args $vorigargs($view)
+    }
+    if {[catch {
+	set fd [open [concat | git log --no-color -z --pretty=raw --parents \
+			  --boundary $args "--" $vfilelimit($view)] r]
+    } err]} {
+	error_popup "Error executing git log: $err"
+	return
+    }
+    if {$viewactive($view) == 0} {
+	set startmsecs [clock clicks -milliseconds]
+    }
+    set i [incr loginstance]
+    lappend viewinstances($view) $i
+    set commfd($i) $fd
+    set leftover($i) {}
+    fconfigure $fd -blocking 0 -translation lf -eofchar {}
+    if {$tclencoding != {}} {
+	fconfigure $fd -encoding $tclencoding
+    }
+    filerun $fd [list getcommitlines $fd $i $view 1]
+    incr viewactive($view)
+    set viewcomplete($view) 0
+    set pending_select $mainheadid
+    nowbusy $view "Reading"
+    if {$showneartags} {
+	getallcommits
+    }
+}
+
+proc reloadcommits {} {
+    global curview viewcomplete selectedline currentid thickerline
+    global showneartags treediffs commitinterest cached_commitrow
+    global targetid
+
+    if {!$viewcomplete($curview)} {
+	stop_rev_list $curview
+    }
+    resetvarcs $curview
+    set selectedline {}
+    catch {unset currentid}
+    catch {unset thickerline}
+    catch {unset treediffs}
+    readrefs
+    changedrefs
+    if {$showneartags} {
+	getallcommits
+    }
+    clear_display
+    catch {unset commitinterest}
+    catch {unset cached_commitrow}
+    catch {unset targetid}
+    setcanvscroll
+    getcommits
+    return 0
 }
 
 # This makes a string representation of a positive integer which
@@ -164,46 +526,759 @@
     return [format "z%.8x" $n]
 }
 
-proc getcommitlines {fd view}  {
-    global commitlisted commitinterest
-    global leftover commfd
-    global displayorder commitidx viewcomplete commitrow commitdata
-    global parentlist children curview hlview
-    global vparentlist vdisporder vcmitlisted
-    global ordertok vnextroot idpending
+# Procedures used in reordering commits from git log (without
+# --topo-order) into the order for display.
+
+proc varcinit {view} {
+    global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
+    global vtokmod varcmod vrowmod varcix vlastins
+
+    set varcstart($view) {{}}
+    set vupptr($view) {0}
+    set vdownptr($view) {0}
+    set vleftptr($view) {0}
+    set vbackptr($view) {0}
+    set varctok($view) {{}}
+    set varcrow($view) {{}}
+    set vtokmod($view) {}
+    set varcmod($view) 0
+    set vrowmod($view) 0
+    set varcix($view) {{}}
+    set vlastins($view) {0}
+}
+
+proc resetvarcs {view} {
+    global varcid varccommits parents children vseedcount ordertok
+
+    foreach vid [array names varcid $view,*] {
+	unset varcid($vid)
+	unset children($vid)
+	unset parents($vid)
+    }
+    # some commits might have children but haven't been seen yet
+    foreach vid [array names children $view,*] {
+	unset children($vid)
+    }
+    foreach va [array names varccommits $view,*] {
+	unset varccommits($va)
+    }
+    foreach vd [array names vseedcount $view,*] {
+	unset vseedcount($vd)
+    }
+    catch {unset ordertok}
+}
+
+# returns a list of the commits with no children
+proc seeds {v} {
+    global vdownptr vleftptr varcstart
+
+    set ret {}
+    set a [lindex $vdownptr($v) 0]
+    while {$a != 0} {
+	lappend ret [lindex $varcstart($v) $a]
+	set a [lindex $vleftptr($v) $a]
+    }
+    return $ret
+}
+
+proc newvarc {view id} {
+    global varcid varctok parents children vdatemode
+    global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
+    global commitdata commitinfo vseedcount varccommits vlastins
+
+    set a [llength $varctok($view)]
+    set vid $view,$id
+    if {[llength $children($vid)] == 0 || $vdatemode($view)} {
+	if {![info exists commitinfo($id)]} {
+	    parsecommit $id $commitdata($id) 1
+	}
+	set cdate [lindex $commitinfo($id) 4]
+	if {![string is integer -strict $cdate]} {
+	    set cdate 0
+	}
+	if {![info exists vseedcount($view,$cdate)]} {
+	    set vseedcount($view,$cdate) -1
+	}
+	set c [incr vseedcount($view,$cdate)]
+	set cdate [expr {$cdate ^ 0xffffffff}]
+	set tok "s[strrep $cdate][strrep $c]"
+    } else {
+	set tok {}
+    }
+    set ka 0
+    if {[llength $children($vid)] > 0} {
+	set kid [lindex $children($vid) end]
+	set k $varcid($view,$kid)
+	if {[string compare [lindex $varctok($view) $k] $tok] > 0} {
+	    set ki $kid
+	    set ka $k
+	    set tok [lindex $varctok($view) $k]
+	}
+    }
+    if {$ka != 0} {
+	set i [lsearch -exact $parents($view,$ki) $id]
+	set j [expr {[llength $parents($view,$ki)] - 1 - $i}]
+	append tok [strrep $j]
+    }
+    set c [lindex $vlastins($view) $ka]
+    if {$c == 0 || [string compare $tok [lindex $varctok($view) $c]] < 0} {
+	set c $ka
+	set b [lindex $vdownptr($view) $ka]
+    } else {
+	set b [lindex $vleftptr($view) $c]
+    }
+    while {$b != 0 && [string compare $tok [lindex $varctok($view) $b]] >= 0} {
+	set c $b
+	set b [lindex $vleftptr($view) $c]
+    }
+    if {$c == $ka} {
+	lset vdownptr($view) $ka $a
+	lappend vbackptr($view) 0
+    } else {
+	lset vleftptr($view) $c $a
+	lappend vbackptr($view) $c
+    }
+    lset vlastins($view) $ka $a
+    lappend vupptr($view) $ka
+    lappend vleftptr($view) $b
+    if {$b != 0} {
+	lset vbackptr($view) $b $a
+    }
+    lappend varctok($view) $tok
+    lappend varcstart($view) $id
+    lappend vdownptr($view) 0
+    lappend varcrow($view) {}
+    lappend varcix($view) {}
+    set varccommits($view,$a) {}
+    lappend vlastins($view) 0
+    return $a
+}
+
+proc splitvarc {p v} {
+    global varcid varcstart varccommits varctok
+    global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
+
+    set oa $varcid($v,$p)
+    set ac $varccommits($v,$oa)
+    set i [lsearch -exact $varccommits($v,$oa) $p]
+    if {$i <= 0} return
+    set na [llength $varctok($v)]
+    # "%" sorts before "0"...
+    set tok "[lindex $varctok($v) $oa]%[strrep $i]"
+    lappend varctok($v) $tok
+    lappend varcrow($v) {}
+    lappend varcix($v) {}
+    set varccommits($v,$oa) [lrange $ac 0 [expr {$i - 1}]]
+    set varccommits($v,$na) [lrange $ac $i end]
+    lappend varcstart($v) $p
+    foreach id $varccommits($v,$na) {
+	set varcid($v,$id) $na
+    }
+    lappend vdownptr($v) [lindex $vdownptr($v) $oa]
+    lappend vlastins($v) [lindex $vlastins($v) $oa]
+    lset vdownptr($v) $oa $na
+    lset vlastins($v) $oa 0
+    lappend vupptr($v) $oa
+    lappend vleftptr($v) 0
+    lappend vbackptr($v) 0
+    for {set b [lindex $vdownptr($v) $na]} {$b != 0} {set b [lindex $vleftptr($v) $b]} {
+	lset vupptr($v) $b $na
+    }
+}
+
+proc renumbervarc {a v} {
+    global parents children varctok varcstart varccommits
+    global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
+
+    set t1 [clock clicks -milliseconds]
+    set todo {}
+    set isrelated($a) 1
+    set kidchanged($a) 1
+    set ntot 0
+    while {$a != 0} {
+	if {[info exists isrelated($a)]} {
+	    lappend todo $a
+	    set id [lindex $varccommits($v,$a) end]
+	    foreach p $parents($v,$id) {
+		if {[info exists varcid($v,$p)]} {
+		    set isrelated($varcid($v,$p)) 1
+		}
+	    }
+	}
+	incr ntot
+	set b [lindex $vdownptr($v) $a]
+	if {$b == 0} {
+	    while {$a != 0} {
+		set b [lindex $vleftptr($v) $a]
+		if {$b != 0} break
+		set a [lindex $vupptr($v) $a]
+	    }
+	}
+	set a $b
+    }
+    foreach a $todo {
+	if {![info exists kidchanged($a)]} continue
+	set id [lindex $varcstart($v) $a]
+	if {[llength $children($v,$id)] > 1} {
+	    set children($v,$id) [lsort -command [list vtokcmp $v] \
+				      $children($v,$id)]
+	}
+	set oldtok [lindex $varctok($v) $a]
+	if {!$vdatemode($v)} {
+	    set tok {}
+	} else {
+	    set tok $oldtok
+	}
+	set ka 0
+	set kid [last_real_child $v,$id]
+	if {$kid ne {}} {
+	    set k $varcid($v,$kid)
+	    if {[string compare [lindex $varctok($v) $k] $tok] > 0} {
+		set ki $kid
+		set ka $k
+		set tok [lindex $varctok($v) $k]
+	    }
+	}
+	if {$ka != 0} {
+	    set i [lsearch -exact $parents($v,$ki) $id]
+	    set j [expr {[llength $parents($v,$ki)] - 1 - $i}]
+	    append tok [strrep $j]
+	}
+	if {$tok eq $oldtok} {
+	    continue
+	}
+	set id [lindex $varccommits($v,$a) end]
+	foreach p $parents($v,$id) {
+	    if {[info exists varcid($v,$p)]} {
+		set kidchanged($varcid($v,$p)) 1
+	    } else {
+		set sortkids($p) 1
+	    }
+	}
+	lset varctok($v) $a $tok
+	set b [lindex $vupptr($v) $a]
+	if {$b != $ka} {
+	    if {[string compare [lindex $varctok($v) $ka] $vtokmod($v)] < 0} {
+		modify_arc $v $ka
+	    }
+	    if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
+		modify_arc $v $b
+	    }
+	    set c [lindex $vbackptr($v) $a]
+	    set d [lindex $vleftptr($v) $a]
+	    if {$c == 0} {
+		lset vdownptr($v) $b $d
+	    } else {
+		lset vleftptr($v) $c $d
+	    }
+	    if {$d != 0} {
+		lset vbackptr($v) $d $c
+	    }
+	    if {[lindex $vlastins($v) $b] == $a} {
+		lset vlastins($v) $b $c
+	    }
+	    lset vupptr($v) $a $ka
+	    set c [lindex $vlastins($v) $ka]
+	    if {$c == 0 || \
+		    [string compare $tok [lindex $varctok($v) $c]] < 0} {
+		set c $ka
+		set b [lindex $vdownptr($v) $ka]
+	    } else {
+		set b [lindex $vleftptr($v) $c]
+	    }
+	    while {$b != 0 && \
+		      [string compare $tok [lindex $varctok($v) $b]] >= 0} {
+		set c $b
+		set b [lindex $vleftptr($v) $c]
+	    }
+	    if {$c == $ka} {
+ 		lset vdownptr($v) $ka $a
+		lset vbackptr($v) $a 0
+	    } else {
+		lset vleftptr($v) $c $a
+		lset vbackptr($v) $a $c
+	    }
+	    lset vleftptr($v) $a $b
+	    if {$b != 0} {
+		lset vbackptr($v) $b $a
+	    }
+	    lset vlastins($v) $ka $a
+	}
+    }
+    foreach id [array names sortkids] {
+	if {[llength $children($v,$id)] > 1} {
+	    set children($v,$id) [lsort -command [list vtokcmp $v] \
+				      $children($v,$id)]
+	}
+    }
+    set t2 [clock clicks -milliseconds]
+    #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
+}
+
+# Fix up the graph after we have found out that in view $v,
+# $p (a commit that we have already seen) is actually the parent
+# of the last commit in arc $a.
+proc fix_reversal {p a v} {
+    global varcid varcstart varctok vupptr
+
+    set pa $varcid($v,$p)
+    if {$p ne [lindex $varcstart($v) $pa]} {
+	splitvarc $p $v
+	set pa $varcid($v,$p)
+    }
+    # seeds always need to be renumbered
+    if {[lindex $vupptr($v) $pa] == 0 ||
+	[string compare [lindex $varctok($v) $a] \
+	     [lindex $varctok($v) $pa]] > 0} {
+	renumbervarc $pa $v
+    }
+}
+
+proc insertrow {id p v} {
+    global cmitlisted children parents varcid varctok vtokmod
+    global varccommits ordertok commitidx numcommits curview
+    global targetid targetrow
+
+    readcommit $id
+    set vid $v,$id
+    set cmitlisted($vid) 1
+    set children($vid) {}
+    set parents($vid) [list $p]
+    set a [newvarc $v $id]
+    set varcid($vid) $a
+    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
+	modify_arc $v $a
+    }
+    lappend varccommits($v,$a) $id
+    set vp $v,$p
+    if {[llength [lappend children($vp) $id]] > 1} {
+	set children($vp) [lsort -command [list vtokcmp $v] $children($vp)]
+	catch {unset ordertok}
+    }
+    fix_reversal $p $a $v
+    incr commitidx($v)
+    if {$v == $curview} {
+	set numcommits $commitidx($v)
+	setcanvscroll
+	if {[info exists targetid]} {
+	    if {![comes_before $targetid $p]} {
+		incr targetrow
+	    }
+	}
+    }
+}
+
+proc insertfakerow {id p} {
+    global varcid varccommits parents children cmitlisted
+    global commitidx varctok vtokmod targetid targetrow curview numcommits
+
+    set v $curview
+    set a $varcid($v,$p)
+    set i [lsearch -exact $varccommits($v,$a) $p]
+    if {$i < 0} {
+	puts "oops: insertfakerow can't find [shortids $p] on arc $a"
+	return
+    }
+    set children($v,$id) {}
+    set parents($v,$id) [list $p]
+    set varcid($v,$id) $a
+    lappend children($v,$p) $id
+    set cmitlisted($v,$id) 1
+    set numcommits [incr commitidx($v)]
+    # note we deliberately don't update varcstart($v) even if $i == 0
+    set varccommits($v,$a) [linsert $varccommits($v,$a) $i $id]
+    modify_arc $v $a $i
+    if {[info exists targetid]} {
+	if {![comes_before $targetid $p]} {
+	    incr targetrow
+	}
+    }
+    setcanvscroll
+    drawvisible
+}
+
+proc removefakerow {id} {
+    global varcid varccommits parents children commitidx
+    global varctok vtokmod cmitlisted currentid selectedline
+    global targetid curview numcommits
+
+    set v $curview
+    if {[llength $parents($v,$id)] != 1} {
+	puts "oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
+	return
+    }
+    set p [lindex $parents($v,$id) 0]
+    set a $varcid($v,$id)
+    set i [lsearch -exact $varccommits($v,$a) $id]
+    if {$i < 0} {
+	puts "oops: removefakerow can't find [shortids $id] on arc $a"
+	return
+    }
+    unset varcid($v,$id)
+    set varccommits($v,$a) [lreplace $varccommits($v,$a) $i $i]
+    unset parents($v,$id)
+    unset children($v,$id)
+    unset cmitlisted($v,$id)
+    set numcommits [incr commitidx($v) -1]
+    set j [lsearch -exact $children($v,$p) $id]
+    if {$j >= 0} {
+	set children($v,$p) [lreplace $children($v,$p) $j $j]
+    }
+    modify_arc $v $a $i
+    if {[info exist currentid] && $id eq $currentid} {
+	unset currentid
+	set selectedline {}
+    }
+    if {[info exists targetid] && $targetid eq $id} {
+	set targetid $p
+    }
+    setcanvscroll
+    drawvisible
+}
+
+proc first_real_child {vp} {
+    global children nullid nullid2
+
+    foreach id $children($vp) {
+	if {$id ne $nullid && $id ne $nullid2} {
+	    return $id
+	}
+    }
+    return {}
+}
+
+proc last_real_child {vp} {
+    global children nullid nullid2
+
+    set kids $children($vp)
+    for {set i [llength $kids]} {[incr i -1] >= 0} {} {
+	set id [lindex $kids $i]
+	if {$id ne $nullid && $id ne $nullid2} {
+	    return $id
+	}
+    }
+    return {}
+}
+
+proc vtokcmp {v a b} {
+    global varctok varcid
+
+    return [string compare [lindex $varctok($v) $varcid($v,$a)] \
+		[lindex $varctok($v) $varcid($v,$b)]]
+}
+
+# This assumes that if lim is not given, the caller has checked that
+# arc a's token is less than $vtokmod($v)
+proc modify_arc {v a {lim {}}} {
+    global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
+
+    if {$lim ne {}} {
+	set c [string compare [lindex $varctok($v) $a] $vtokmod($v)]
+	if {$c > 0} return
+	if {$c == 0} {
+	    set r [lindex $varcrow($v) $a]
+	    if {$r ne {} && $vrowmod($v) <= $r + $lim} return
+	}
+    }
+    set vtokmod($v) [lindex $varctok($v) $a]
+    set varcmod($v) $a
+    if {$v == $curview} {
+	while {$a != 0 && [lindex $varcrow($v) $a] eq {}} {
+	    set a [lindex $vupptr($v) $a]
+	    set lim {}
+	}
+	set r 0
+	if {$a != 0} {
+	    if {$lim eq {}} {
+		set lim [llength $varccommits($v,$a)]
+	    }
+	    set r [expr {[lindex $varcrow($v) $a] + $lim}]
+	}
+	set vrowmod($v) $r
+	undolayout $r
+    }
+}
+
+proc update_arcrows {v} {
+    global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
+    global varcid vrownum varcorder varcix varccommits
+    global vupptr vdownptr vleftptr varctok
+    global displayorder parentlist curview cached_commitrow
+
+    if {$vrowmod($v) == $commitidx($v)} return
+    if {$v == $curview} {
+	if {[llength $displayorder] > $vrowmod($v)} {
+	    set displayorder [lrange $displayorder 0 [expr {$vrowmod($v) - 1}]]
+	    set parentlist [lrange $parentlist 0 [expr {$vrowmod($v) - 1}]]
+	}
+	catch {unset cached_commitrow}
+    }
+    set narctot [expr {[llength $varctok($v)] - 1}]
+    set a $varcmod($v)
+    while {$a != 0 && [lindex $varcix($v) $a] eq {}} {
+	# go up the tree until we find something that has a row number,
+	# or we get to a seed
+	set a [lindex $vupptr($v) $a]
+    }
+    if {$a == 0} {
+	set a [lindex $vdownptr($v) 0]
+	if {$a == 0} return
+	set vrownum($v) {0}
+	set varcorder($v) [list $a]
+	lset varcix($v) $a 0
+	lset varcrow($v) $a 0
+	set arcn 0
+	set row 0
+    } else {
+	set arcn [lindex $varcix($v) $a]
+	if {[llength $vrownum($v)] > $arcn + 1} {
+	    set vrownum($v) [lrange $vrownum($v) 0 $arcn]
+	    set varcorder($v) [lrange $varcorder($v) 0 $arcn]
+	}
+	set row [lindex $varcrow($v) $a]
+    }
+    while {1} {
+	set p $a
+	incr row [llength $varccommits($v,$a)]
+	# go down if possible
+	set b [lindex $vdownptr($v) $a]
+	if {$b == 0} {
+	    # if not, go left, or go up until we can go left
+	    while {$a != 0} {
+		set b [lindex $vleftptr($v) $a]
+		if {$b != 0} break
+		set a [lindex $vupptr($v) $a]
+	    }
+	    if {$a == 0} break
+	}
+	set a $b
+	incr arcn
+	lappend vrownum($v) $row
+	lappend varcorder($v) $a
+	lset varcix($v) $a $arcn
+	lset varcrow($v) $a $row
+    }
+    set vtokmod($v) [lindex $varctok($v) $p]
+    set varcmod($v) $p
+    set vrowmod($v) $row
+    if {[info exists currentid]} {
+	set selectedline [rowofcommit $currentid]
+    }
+}
+
+# Test whether view $v contains commit $id
+proc commitinview {id v} {
+    global varcid
+
+    return [info exists varcid($v,$id)]
+}
+
+# Return the row number for commit $id in the current view
+proc rowofcommit {id} {
+    global varcid varccommits varcrow curview cached_commitrow
+    global varctok vtokmod
+
+    set v $curview
+    if {![info exists varcid($v,$id)]} {
+	puts "oops rowofcommit no arc for [shortids $id]"
+	return {}
+    }
+    set a $varcid($v,$id)
+    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] >= 0} {
+	update_arcrows $v
+    }
+    if {[info exists cached_commitrow($id)]} {
+	return $cached_commitrow($id)
+    }
+    set i [lsearch -exact $varccommits($v,$a) $id]
+    if {$i < 0} {
+	puts "oops didn't find commit [shortids $id] in arc $a"
+	return {}
+    }
+    incr i [lindex $varcrow($v) $a]
+    set cached_commitrow($id) $i
+    return $i
+}
+
+# Returns 1 if a is on an earlier row than b, otherwise 0
+proc comes_before {a b} {
+    global varcid varctok curview
+
+    set v $curview
+    if {$a eq $b || ![info exists varcid($v,$a)] || \
+	    ![info exists varcid($v,$b)]} {
+	return 0
+    }
+    if {$varcid($v,$a) != $varcid($v,$b)} {
+	return [expr {[string compare [lindex $varctok($v) $varcid($v,$a)] \
+			   [lindex $varctok($v) $varcid($v,$b)]] < 0}]
+    }
+    return [expr {[rowofcommit $a] < [rowofcommit $b]}]
+}
+
+proc bsearch {l elt} {
+    if {[llength $l] == 0 || $elt <= [lindex $l 0]} {
+	return 0
+    }
+    set lo 0
+    set hi [llength $l]
+    while {$hi - $lo > 1} {
+	set mid [expr {int(($lo + $hi) / 2)}]
+	set t [lindex $l $mid]
+	if {$elt < $t} {
+	    set hi $mid
+	} elseif {$elt > $t} {
+	    set lo $mid
+	} else {
+	    return $mid
+	}
+    }
+    return $lo
+}
+
+# Make sure rows $start..$end-1 are valid in displayorder and parentlist
+proc make_disporder {start end} {
+    global vrownum curview commitidx displayorder parentlist
+    global varccommits varcorder parents vrowmod varcrow
+    global d_valid_start d_valid_end
+
+    if {$end > $vrowmod($curview)} {
+	update_arcrows $curview
+    }
+    set ai [bsearch $vrownum($curview) $start]
+    set start [lindex $vrownum($curview) $ai]
+    set narc [llength $vrownum($curview)]
+    for {set r $start} {$ai < $narc && $r < $end} {incr ai} {
+	set a [lindex $varcorder($curview) $ai]
+	set l [llength $displayorder]
+	set al [llength $varccommits($curview,$a)]
+	if {$l < $r + $al} {
+	    if {$l < $r} {
+		set pad [ntimes [expr {$r - $l}] {}]
+		set displayorder [concat $displayorder $pad]
+		set parentlist [concat $parentlist $pad]
+	    } elseif {$l > $r} {
+		set displayorder [lrange $displayorder 0 [expr {$r - 1}]]
+		set parentlist [lrange $parentlist 0 [expr {$r - 1}]]
+	    }
+	    foreach id $varccommits($curview,$a) {
+		lappend displayorder $id
+		lappend parentlist $parents($curview,$id)
+	    }
+	} elseif {[lindex $displayorder [expr {$r + $al - 1}]] eq {}} {
+	    set i $r
+	    foreach id $varccommits($curview,$a) {
+		lset displayorder $i $id
+		lset parentlist $i $parents($curview,$id)
+		incr i
+	    }
+	}
+	incr r $al
+    }
+}
+
+proc commitonrow {row} {
+    global displayorder
+
+    set id [lindex $displayorder $row]
+    if {$id eq {}} {
+	make_disporder $row [expr {$row + 1}]
+	set id [lindex $displayorder $row]
+    }
+    return $id
+}
+
+proc closevarcs {v} {
+    global varctok varccommits varcid parents children
+    global cmitlisted commitidx commitinterest vtokmod
+
+    set missing_parents 0
+    set scripts {}
+    set narcs [llength $varctok($v)]
+    for {set a 1} {$a < $narcs} {incr a} {
+	set id [lindex $varccommits($v,$a) end]
+	foreach p $parents($v,$id) {
+	    if {[info exists varcid($v,$p)]} continue
+	    # add p as a new commit
+	    incr missing_parents
+	    set cmitlisted($v,$p) 0
+	    set parents($v,$p) {}
+	    if {[llength $children($v,$p)] == 1 &&
+		[llength $parents($v,$id)] == 1} {
+		set b $a
+	    } else {
+		set b [newvarc $v $p]
+	    }
+	    set varcid($v,$p) $b
+	    if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
+		modify_arc $v $b
+	    }
+	    lappend varccommits($v,$b) $p
+	    incr commitidx($v)
+	    if {[info exists commitinterest($p)]} {
+		foreach script $commitinterest($p) {
+		    lappend scripts [string map [list "%I" $p] $script]
+		}
+		unset commitinterest($id)
+	    }
+	}
+    }
+    if {$missing_parents > 0} {
+	foreach s $scripts {
+	    eval $s
+	}
+    }
+}
+
+# Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
+# Assumes we already have an arc for $rwid.
+proc rewrite_commit {v id rwid} {
+    global children parents varcid varctok vtokmod varccommits
+
+    foreach ch $children($v,$id) {
+	# make $rwid be $ch's parent in place of $id
+	set i [lsearch -exact $parents($v,$ch) $id]
+	if {$i < 0} {
+	    puts "oops rewrite_commit didn't find $id in parent list for $ch"
+	}
+	set parents($v,$ch) [lreplace $parents($v,$ch) $i $i $rwid]
+	# add $ch to $rwid's children and sort the list if necessary
+	if {[llength [lappend children($v,$rwid) $ch]] > 1} {
+	    set children($v,$rwid) [lsort -command [list vtokcmp $v] \
+					$children($v,$rwid)]
+	}
+	# fix the graph after joining $id to $rwid
+	set a $varcid($v,$ch)
+	fix_reversal $rwid $a $v
+	# parentlist is wrong for the last element of arc $a
+	# even if displayorder is right, hence the 3rd arg here
+	modify_arc $v $a [expr {[llength $varccommits($v,$a)] - 1}]
+    }
+}
+
+proc getcommitlines {fd inst view updating}  {
+    global cmitlisted commitinterest leftover
+    global commitidx commitdata vdatemode
+    global parents children curview hlview
+    global idpending ordertok
+    global varccommits varcid varctok vtokmod vfilelimit
 
     set stuff [read $fd 500000]
     # git log doesn't terminate the last commit with a null...
-    if {$stuff == {} && $leftover($view) ne {} && [eof $fd]} {
+    if {$stuff == {} && $leftover($inst) ne {} && [eof $fd]} {
 	set stuff "\0"
     }
     if {$stuff == {}} {
 	if {![eof $fd]} {
 	    return 1
 	}
-	# Check if we have seen any ids listed as parents that haven't
-	# appeared in the list
-	foreach vid [array names idpending "$view,*"] {
-	    # should only get here if git log is buggy
-	    set id [lindex [split $vid ","] 1]
-	    set commitrow($vid) $commitidx($view)
-	    incr commitidx($view)
-	    if {$view == $curview} {
-		lappend parentlist {}
-		lappend displayorder $id
-		lappend commitlisted 0
-	    } else {
-		lappend vparentlist($view) {}
-		lappend vdisporder($view) $id
-		lappend vcmitlisted($view) 0
-	    }
+	global commfd viewcomplete viewactive viewname
+	global viewinstances
+	unset commfd($inst)
+	set i [lsearch -exact $viewinstances($view) $inst]
+	if {$i >= 0} {
+	    set viewinstances($view) [lreplace $viewinstances($view) $i $i]
 	}
-	set viewcomplete($view) 1
-	global viewname progresscoords
-	unset commfd($view)
-	notbusy $view
-	set progresscoords {0 0}
-	adjustprogress
 	# set it blocking so we wait for the process to terminate
 	fconfigure $fd -blocking 1
 	if {[catch {close $fd} err]} {
@@ -213,10 +1288,10 @@
 	    }
 	    if {[string range $err 0 4] == "usage"} {
 		set err "Gitk: error reading commits$fv:\
-			bad arguments to git rev-list."
+			bad arguments to git log."
 		if {$viewname($view) eq "Command line"} {
 		    append err \
-			"  (Note: arguments to gitk are passed to git rev-list\
+			"  (Note: arguments to gitk are passed to git log\
 			 to allow selection of commits to be displayed.)"
 		}
 	    } else {
@@ -224,23 +1299,31 @@
 	    }
 	    error_popup $err
 	}
+	if {[incr viewactive($view) -1] <= 0} {
+	    set viewcomplete($view) 1
+	    # Check if we have seen any ids listed as parents that haven't
+	    # appeared in the list
+	    closevarcs $view
+	    notbusy $view
+	}
 	if {$view == $curview} {
-	    run chewcommits $view
+	    run chewcommits
 	}
 	return 0
     }
     set start 0
     set gotsome 0
+    set scripts {}
     while 1 {
 	set i [string first "\0" $stuff $start]
 	if {$i < 0} {
-	    append leftover($view) [string range $stuff $start end]
+	    append leftover($inst) [string range $stuff $start end]
 	    break
 	}
 	if {$start == 0} {
-	    set cmit $leftover($view)
+	    set cmit $leftover($inst)
 	    append cmit [string range $stuff 0 [expr {$i - 1}]]
-	    set leftover($view) {}
+	    set leftover($inst) {}
 	} else {
 	    set cmit [string range $stuff $start [expr {$i - 1}]]
 	}
@@ -276,121 +1359,128 @@
 	    exit 1
 	}
 	set id [lindex $ids 0]
-	if {![info exists ordertok($view,$id)]} {
-	    set otok "o[strrep $vnextroot($view)]"
-	    incr vnextroot($view)
-	    set ordertok($view,$id) $otok
-	} else {
-	    set otok $ordertok($view,$id)
-	    unset idpending($view,$id)
+	set vid $view,$id
+
+	if {!$listed && $updating && ![info exists varcid($vid)] &&
+	    $vfilelimit($view) ne {}} {
+	    # git log doesn't rewrite parents for unlisted commits
+	    # when doing path limiting, so work around that here
+	    # by working out the rewritten parent with git rev-list
+	    # and if we already know about it, using the rewritten
+	    # parent as a substitute parent for $id's children.
+	    if {![catch {
+		set rwid [exec git rev-list --first-parent --max-count=1 \
+			      $id -- $vfilelimit($view)]
+	    }]} {
+		if {$rwid ne {} && [info exists varcid($view,$rwid)]} {
+		    # use $rwid in place of $id
+		    rewrite_commit $view $id $rwid
+		    continue
+		}
+	    }
+	}
+
+	set a 0
+	if {[info exists varcid($vid)]} {
+	    if {$cmitlisted($vid) || !$listed} continue
+	    set a $varcid($vid)
 	}
 	if {$listed} {
 	    set olds [lrange $ids 1 end]
-	    if {[llength $olds] == 1} {
-		set p [lindex $olds 0]
-		lappend children($view,$p) $id
-		if {![info exists ordertok($view,$p)]} {
-		    set ordertok($view,$p) $ordertok($view,$id)
-		    set idpending($view,$p) 1
-		}
-	    } else {
-		set i 0
-		foreach p $olds {
-		    if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
-			lappend children($view,$p) $id
-		    }
-		    if {![info exists ordertok($view,$p)]} {
-			set ordertok($view,$p) "$otok[strrep $i]]"
-			set idpending($view,$p) 1
-		    }
-		    incr i
-		}
-	    }
 	} else {
 	    set olds {}
 	}
-	if {![info exists children($view,$id)]} {
-	    set children($view,$id) {}
-	}
 	set commitdata($id) [string range $cmit [expr {$j + 1}] end]
-	set commitrow($view,$id) $commitidx($view)
-	incr commitidx($view)
-	if {$view == $curview} {
-	    lappend parentlist $olds
-	    lappend displayorder $id
-	    lappend commitlisted $listed
-	} else {
-	    lappend vparentlist($view) $olds
-	    lappend vdisporder($view) $id
-	    lappend vcmitlisted($view) $listed
+	set cmitlisted($vid) $listed
+	set parents($vid) $olds
+	if {![info exists children($vid)]} {
+	    set children($vid) {}
+	} elseif {$a == 0 && [llength $children($vid)] == 1} {
+	    set k [lindex $children($vid) 0]
+	    if {[llength $parents($view,$k)] == 1 &&
+		(!$vdatemode($view) ||
+		 $varcid($view,$k) == [llength $varctok($view)] - 1)} {
+		set a $varcid($view,$k)
+	    }
 	}
+	if {$a == 0} {
+	    # new arc
+	    set a [newvarc $view $id]
+	}
+	if {[string compare [lindex $varctok($view) $a] $vtokmod($view)] < 0} {
+	    modify_arc $view $a
+	}
+	if {![info exists varcid($vid)]} {
+	    set varcid($vid) $a
+	    lappend varccommits($view,$a) $id
+	    incr commitidx($view)
+	}
+
+	set i 0
+	foreach p $olds {
+	    if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
+		set vp $view,$p
+		if {[llength [lappend children($vp) $id]] > 1 &&
+		    [vtokcmp $view [lindex $children($vp) end-1] $id] > 0} {
+		    set children($vp) [lsort -command [list vtokcmp $view] \
+					   $children($vp)]
+		    catch {unset ordertok}
+		}
+		if {[info exists varcid($view,$p)]} {
+		    fix_reversal $p $a $view
+		}
+	    }
+	    incr i
+	}
+
 	if {[info exists commitinterest($id)]} {
 	    foreach script $commitinterest($id) {
-		eval [string map [list "%I" $id] $script]
+		lappend scripts [string map [list "%I" $id] $script]
 	    }
 	    unset commitinterest($id)
 	}
 	set gotsome 1
     }
     if {$gotsome} {
-	run chewcommits $view
+	global numcommits hlview
+
 	if {$view == $curview} {
-	    # update progress bar
-	    global progressdirn progresscoords proglastnc
-	    set inc [expr {($commitidx($view) - $proglastnc) * 0.0002}]
-	    set proglastnc $commitidx($view)
-	    set l [lindex $progresscoords 0]
-	    set r [lindex $progresscoords 1]
-	    if {$progressdirn} {
-		set r [expr {$r + $inc}]
-		if {$r >= 1.0} {
-		    set r 1.0
-		    set progressdirn 0
-		}
-		if {$r > 0.2} {
-		    set l [expr {$r - 0.2}]
-		}
-	    } else {
-		set l [expr {$l - $inc}]
-		if {$l <= 0.0} {
-		    set l 0.0
-		    set progressdirn 1
-		}
-		set r [expr {$l + 0.2}]
-	    }
-	    set progresscoords [list $l $r]
-	    adjustprogress
+	    set numcommits $commitidx($view)
+	    run chewcommits
+	}
+	if {[info exists hlview] && $view == $hlview} {
+	    # we never actually get here...
+	    run vhighlightmore
+	}
+	foreach s $scripts {
+	    eval $s
 	}
     }
     return 2
 }
 
-proc chewcommits {view} {
+proc chewcommits {} {
     global curview hlview viewcomplete
-    global selectedline pending_select
+    global pending_select
 
-    if {$view == $curview} {
-	layoutmore
-	if {$viewcomplete($view)} {
-	    global displayorder commitidx phase
-	    global numcommits startmsecs
+    layoutmore
+    if {$viewcomplete($curview)} {
+	global commitidx varctok
+	global numcommits startmsecs
+	global mainheadid nullid
 
-	    if {[info exists pending_select]} {
-		set row [first_real_row]
-		selectline $row 1
-	    }
-	    if {$commitidx($curview) > 0} {
-		#set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
-		#puts "overall $ms ms for $numcommits commits"
-	    } else {
-		show_status [mc "No commits selected"]
-	    }
-	    notbusy layout
-	    set phase {}
+	if {[info exists pending_select]} {
+	    set row [first_real_row]
+	    selectline $row 1
 	}
-    }
-    if {[info exists hlview] && $view == $hlview} {
-	vhighlightmore
+	if {$commitidx($curview) > 0} {
+	    #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
+	    #puts "overall $ms ms for $numcommits commits"
+	    #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
+	} else {
+	    show_status [mc "No commits selected"]
+	}
+	notbusy layout
     }
     return 0
 }
@@ -400,38 +1490,6 @@
     parsecommit $id $contents 0
 }
 
-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
-	set phase {}
-    }
-    set n $curview
-    foreach id $displayorder {
-	catch {unset children($n,$id)}
-	catch {unset commitrow($n,$id)}
-	catch {unset ordertok($n,$id)}
-    }
-    foreach vid [array names idpending "$n,*"] {
-	unset idpending($vid)
-    }
-    set curview -1
-    catch {unset selectedline}
-    catch {unset thickerline}
-    catch {unset viewdata($n)}
-    readrefs
-    changedrefs
-    if {$showneartags} {
-	getallcommits
-    }
-    showview $n
-}
-
 proc parsecommit {id contents listed} {
     global commitinfo cdate
 
@@ -472,7 +1530,7 @@
 	set headline [string trimright [string range $headline 0 $i]]
     }
     if {!$listed} {
-	# git rev-list indents the comment by 4 spaces;
+	# git log indents the comment by 4 spaces;
 	# if we got this via git cat-file, add the indentation
 	set newcomment {}
 	foreach line [split $comment "\n"] {
@@ -558,10 +1616,10 @@
 
 # skip over fake commits
 proc first_real_row {} {
-    global nullid nullid2 displayorder numcommits
+    global nullid nullid2 numcommits
 
     for {set row 0} {$row < $numcommits} {incr row} {
-	set id [lindex $displayorder $row]
+	set id [commitonrow $row]
 	if {$id ne $nullid && $id ne $nullid2} {
 	    break
 	}
@@ -641,7 +1699,7 @@
 }
 
 proc makewindow {} {
-    global canv canv2 canv3 linespc charspc ctext cflist
+    global canv canv2 canv3 linespc charspc ctext cflist cscroll
     global tabstop
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
@@ -654,13 +1712,14 @@
     global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
     global headctxmenu progresscanv progressitem progresscoords statusw
     global fprogitem fprogcoord lastprogupdate progupdatepending
-    global rprogitem rprogcoord
+    global rprogitem rprogcoord rownumsel numcommits
     global have_tk85
 
     menu .bar
     .bar add cascade -label [mc "File"] -menu .bar.file
     menu .bar.file
     .bar.file add command -label [mc "Update"] -command updatecommits
+    .bar.file add command -label [mc "Reload"] -command reloadcommits
     .bar.file add command -label [mc "Reread references"] -command rereadrefs
     .bar.file add command -label [mc "List references"] -command showrefs
     .bar.file add command -label [mc "Quit"] -command doquit
@@ -769,6 +1828,18 @@
 	-state disabled -width 26
     pack .tf.bar.rightbut -side left -fill y
 
+    label .tf.bar.rowlabel -text [mc "Row"]
+    set rownumsel {}
+    label .tf.bar.rownum -width 7 -font textfont -textvariable rownumsel \
+	-relief sunken -anchor e
+    label .tf.bar.rowlabel2 -text "/"
+    label .tf.bar.numcommits -width 7 -font textfont -textvariable numcommits \
+	-relief sunken -anchor e
+    pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
+	-side left
+    global selectedline
+    trace add variable selectedline write selectedline_change
+
     # Status label and progress bar
     set statusw .tf.bar.status
     label $statusw -width 15 -relief sunken
@@ -1016,7 +2087,7 @@
     bindkey k "selnextline 1"
     bindkey j "goback"
     bindkey l "goforw"
-    bindkey b "$ctext yview scroll -1 pages"
+    bindkey b prevfile
     bindkey d "$ctext yview scroll 18 units"
     bindkey u "$ctext yview scroll -18 units"
     bindkey / {dofind 1 1}
@@ -1088,6 +2159,8 @@
 	-command {flist_hl 0}
     $flist_menu add command -label [mc "Highlight this only"] \
 	-command {flist_hl 1}
+    $flist_menu add command -label [mc "External diff"] \
+        -command {external_diff}
 }
 
 # Windows sends all mouse wheel events to the current focused window, not
@@ -1108,6 +2181,17 @@
     }
 }
 
+# Update row number label when selectedline changes
+proc selectedline_change {n1 n2 op} {
+    global selectedline rownumsel
+
+    if {$selectedline eq {}} {
+	set rownumsel {}
+    } else {
+	set rownumsel [expr {$selectedline + 1}]
+    }
+}
+
 # mouse-2 makes all windows scan vertically, but only the one
 # the cursor is in scans horizontally
 proc canvscan {op w x y} {
@@ -1123,7 +2207,7 @@
 
 proc scrollcanv {cscroll f0 f1} {
     $cscroll set $f0 $f1
-    drawfrac $f0 $f1
+    drawvisible
     flushhighlights
 }
 
@@ -1192,7 +2276,7 @@
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
-    global autoselect
+    global autoselect extdifftool
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -1218,6 +2302,7 @@
 	puts $f [list set diffcolors $diffcolors]
 	puts $f [list set diffcontext $diffcontext]
 	puts $f [list set selectbgcolor $selectbgcolor]
+	puts $f [list set extdifftool $extdifftool]
 
 	puts $f "set geometry(main) [wm geometry .]"
 	puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -1318,7 +2403,7 @@
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
-Copyright © 2005-2006 Paul Mackerras
+Copyright © 2005-2008 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License"] \
 	    -justify center -aspect 400 -border 2 -bg white -relief groove
@@ -1675,7 +2760,7 @@
     -data $rectdata -maskdata $rectmask
 
 proc init_flist {first} {
-    global cflist cflist_top selectedline difffilestart
+    global cflist cflist_top difffilestart
 
     $cflist conf -state normal
     $cflist delete 0.0 end
@@ -1768,6 +2853,12 @@
 	set e [lindex $treediffs($diffids) [expr {$l-2}]]
     }
     set flist_menu_file $e
+    set xdiffstate "normal"
+    if {$cmitmode eq "tree"} {
+	set xdiffstate "disabled"
+    }
+    # Disable "External diff" item in tree mode
+    $flist_menu entryconf 2 -state $xdiffstate
     tk_popup $flist_menu $X $Y
 }
 
@@ -1783,6 +2874,113 @@
     set gdttype [mc "touching paths:"]
 }
 
+proc save_file_from_commit {filename output what} {
+    global nullfile
+
+    if {[catch {exec git show $filename -- > $output} err]} {
+	if {[string match "fatal: bad revision *" $err]} {
+	    return $nullfile
+	}
+	error_popup "Error getting \"$filename\" from $what: $err"
+	return {}
+    }
+    return $output
+}
+
+proc external_diff_get_one_file {diffid filename diffdir} {
+    global nullid nullid2 nullfile
+    global gitdir
+
+    if {$diffid == $nullid} {
+        set difffile [file join [file dirname $gitdir] $filename]
+	if {[file exists $difffile]} {
+	    return $difffile
+	}
+	return $nullfile
+    }
+    if {$diffid == $nullid2} {
+        set difffile [file join $diffdir "\[index\] [file tail $filename]"]
+        return [save_file_from_commit :$filename $difffile index]
+    }
+    set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
+    return [save_file_from_commit $diffid:$filename $difffile \
+	       "revision $diffid"]
+}
+
+proc external_diff {} {
+    global gitktmpdir nullid nullid2
+    global flist_menu_file
+    global diffids
+    global diffnum
+    global gitdir extdifftool
+
+    if {[llength $diffids] == 1} {
+        # no reference commit given
+        set diffidto [lindex $diffids 0]
+        if {$diffidto eq $nullid} {
+            # diffing working copy with index
+            set diffidfrom $nullid2
+        } elseif {$diffidto eq $nullid2} {
+            # diffing index with HEAD
+            set diffidfrom "HEAD"
+        } else {
+            # use first parent commit
+            global parentlist selectedline
+            set diffidfrom [lindex $parentlist $selectedline 0]
+        }
+    } else {
+        set diffidfrom [lindex $diffids 0]
+        set diffidto [lindex $diffids 1]
+    }
+
+    # make sure that several diffs wont collide
+    if {![info exists gitktmpdir]} {
+	set gitktmpdir [file join [file dirname $gitdir] \
+			    [format ".gitk-tmp.%s" [pid]]]
+	if {[catch {file mkdir $gitktmpdir} err]} {
+	    error_popup "Error creating temporary directory $gitktmpdir: $err"
+	    unset gitktmpdir
+	    return
+	}
+	set diffnum 0
+    }
+    incr diffnum
+    set diffdir [file join $gitktmpdir $diffnum]
+    if {[catch {file mkdir $diffdir} err]} {
+	error_popup "Error creating temporary directory $diffdir: $err"
+	return
+    }
+
+    # gather files to diff
+    set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
+    set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
+
+    if {$difffromfile ne {} && $difftofile ne {}} {
+        set cmd [concat | [shellsplit $extdifftool] \
+		     [list $difffromfile $difftofile]]
+        if {[catch {set fl [open $cmd r]} err]} {
+            file delete -force $diffdir
+            error_popup [mc "$extdifftool: command failed: $err"]
+        } else {
+            fconfigure $fl -blocking 0
+            filerun $fl [list delete_at_eof $fl $diffdir]
+        }
+    }
+}
+
+# delete $dir when we see eof on $f (presumably because the child has exited)
+proc delete_at_eof {f dir} {
+    while {[gets $f line] >= 0} {}
+    if {[eof $f]} {
+	if {[catch {close $f} err]} {
+	    error_popup "External diff viewer failed: $err"
+	}
+	file delete -force $dir
+	return 0
+    }
+    return 1
+}
+
 # Functions for adding and removing shell-type quoting
 
 proc shellquote {str} {
@@ -1925,7 +3123,7 @@
 	-variable newviewperm($n)
     grid $top.perm - -pady 5 -sticky w
     message $top.al -aspect 1000 \
-	-text [mc "Commits to include (arguments to git rev-list):"]
+	-text [mc "Commits to include (arguments to git log):"]
     grid $top.al - -sticky w -pady 5
     entry $top.args -width 50 -textvariable newviewargs($n) \
 	-background $bgcolor
@@ -2028,7 +3226,7 @@
 	    set viewargs($n) $newargs
 	    set viewargscmd($n) $newviewargscmd($n)
 	    if {$curview == $n} {
-		run updatecommits
+		run reloadcommits
 	    }
 	}
     }
@@ -2036,7 +3234,7 @@
 }
 
 proc delview {} {
-    global curview viewdata viewperm hlview selectedhlview
+    global curview viewperm hlview selectedhlview
 
     if {$curview == 0} return
     if {[info exists hlview] && $hlview == $curview} {
@@ -2044,7 +3242,6 @@
 	unset hlview
     }
     allviewmenus $curview delete
-    set viewdata($curview) {}
     set viewperm($curview) 0
     showview 0
 }
@@ -2058,48 +3255,28 @@
     #	-command [list addvhighlight $n] -variable selectedhlview
 }
 
-proc flatten {var} {
-    global $var
-
-    set ret {}
-    foreach i [array names $var] {
-	lappend ret $i [set $var\($i\)]
-    }
-    return $ret
-}
-
-proc unflatten {var l} {
-    global $var
-
-    catch {unset $var}
-    foreach {i v} $l {
-	set $var\($i\) $v
-    }
-}
-
 proc showview {n} {
-    global curview viewdata viewfiles
+    global curview cached_commitrow ordertok
     global displayorder parentlist rowidlist rowisopt rowfinal
-    global colormap rowtextx commitrow nextcolor canvxmax
-    global numcommits commitlisted
+    global colormap rowtextx nextcolor canvxmax
+    global numcommits viewcomplete
     global selectedline currentid canv canvy0
     global treediffs
-    global pending_select phase
+    global pending_select mainheadid
     global commitidx
-    global commfd
-    global selectedview selectfirst
-    global vparentlist vdisporder vcmitlisted
+    global selectedview
     global hlview selectedhlview commitinterest
 
     if {$n == $curview} return
     set selid {}
-    if {[info exists selectedline]} {
+    set ymax [lindex [$canv cget -scrollregion] 3]
+    set span [$canv yview]
+    set ytop [expr {[lindex $span 0] * $ymax}]
+    set ybot [expr {[lindex $span 1] * $ymax}]
+    set yscreen [expr {($ybot - $ytop) / 2}]
+    if {$selectedline ne {}} {
 	set selid $currentid
 	set y [yc $selectedline]
-	set ymax [lindex [$canv cget -scrollregion] 3]
-	set span [$canv yview]
-	set ytop [expr {[lindex $span 0] * $ymax}]
-	set ybot [expr {[lindex $span 1] * $ymax}]
 	if {$ytop < $y && $y < $ybot} {
 	    set yscreen [expr {$y - $ytop}]
 	}
@@ -2109,17 +3286,6 @@
     }
     unselectline
     normalline
-    if {$curview >= 0} {
-	set vparentlist($curview) $parentlist
-	set vdisporder($curview) $displayorder
-	set vcmitlisted($curview) $commitlisted
-	if {$phase ne {} ||
-	    ![info exists viewdata($curview)] ||
-	    [lindex $viewdata($curview) 0] ne {}} {
-	    set viewdata($curview) \
-		[list $phase $rowidlist $rowisopt $rowfinal]
-	}
-    }
     catch {unset treediffs}
     clear_display
     if {[info exists hlview] && $hlview == $n} {
@@ -2127,6 +3293,8 @@
 	set selectedhlview [mc "None"]
     }
     catch {unset commitinterest}
+    catch {unset cached_commitrow}
+    catch {unset ordertok}
 
     set curview $n
     set selectedview $n
@@ -2134,7 +3302,7 @@
     .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
 
     run refill_reflist
-    if {![info exists viewdata($n)]} {
+    if {![info exists viewcomplete($n)]} {
 	if {$selid ne {}} {
 	    set pending_select $selid
 	}
@@ -2142,14 +3310,11 @@
 	return
     }
 
-    set v $viewdata($n)
-    set phase [lindex $v 0]
-    set displayorder $vdisporder($n)
-    set parentlist $vparentlist($n)
-    set commitlisted $vcmitlisted($n)
-    set rowidlist [lindex $v 1]
-    set rowisopt [lindex $v 2]
-    set rowfinal [lindex $v 3]
+    set displayorder {}
+    set parentlist {}
+    set rowidlist {}
+    set rowisopt {}
+    set rowfinal {}
     set numcommits $commitidx($n)
 
     catch {unset colormap}
@@ -2161,9 +3326,8 @@
     setcanvscroll
     set yf 0
     set row {}
-    set selectfirst 0
-    if {[info exists yscreen] && [info exists commitrow($n,$selid)]} {
-	set row $commitrow($n,$selid)
+    if {$selid ne {} && [commitinview $selid $n]} {
+	set row [rowofcommit $selid]
 	# try to get the selected row in the same position on the screen
 	set ymax [lindex [$canv cget -scrollregion] 3]
 	set ytop [expr {[yc $row] - $yscreen}]
@@ -2176,21 +3340,24 @@
     drawvisible
     if {$row ne {}} {
 	selectline $row 0
-    } elseif {$selid ne {}} {
-	set pending_select $selid
+    } elseif {$mainheadid ne {} && [commitinview $mainheadid $curview]} {
+	selectline [rowofcommit $mainheadid] 1
+    } elseif {!$viewcomplete($n)} {
+	if {$selid ne {}} {
+	    set pending_select $selid
+	} else {
+	    set pending_select $mainheadid
+	}
     } else {
 	set row [first_real_row]
 	if {$row < $numcommits} {
 	    selectline $row 0
-	} else {
-	    set selectfirst 1
 	}
     }
-    if {$phase ne {}} {
-	if {$phase eq "getcommits"} {
+    if {!$viewcomplete($n)} {
+	if {$numcommits == 0} {
 	    show_status [mc "Reading commits..."]
 	}
-	run chewcommits $n
     } elseif {$numcommits == 0} {
 	show_status [mc "No commits selected"]
     }
@@ -2198,20 +3365,20 @@
 
 # Stuff relating to the highlighting facility
 
-proc ishighlighted {row} {
+proc ishighlighted {id} {
     global vhighlights fhighlights nhighlights rhighlights
 
-    if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
-	return $nhighlights($row)
+    if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
+	return $nhighlights($id)
     }
-    if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
-	return $vhighlights($row)
+    if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
+	return $vhighlights($id)
     }
-    if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
-	return $fhighlights($row)
+    if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
+	return $fhighlights($id)
     }
-    if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
-	return $rhighlights($row)
+    if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
+	return $rhighlights($id)
     }
     return 0
 }
@@ -2221,7 +3388,7 @@
 
     lappend boldrows $row
     $canv itemconf $linehtag($row) -font $font
-    if {[info exists selectedline] && $row == $selectedline} {
+    if {$row == $selectedline} {
 	$canv delete secsel
 	set t [eval $canv create rect [$canv bbox $linehtag($row)] \
 		   -outline {{}} -tags secsel \
@@ -2235,7 +3402,7 @@
 
     lappend boldnamerows $row
     $canv2 itemconf $linentag($row) -font $font
-    if {[info exists selectedline] && $row == $selectedline} {
+    if {$row == $selectedline} {
 	$canv2 delete secsel
 	set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
 		   -outline {{}} -tags secsel \
@@ -2249,7 +3416,7 @@
 
     set stillbold {}
     foreach row $boldrows {
-	if {![ishighlighted $row]} {
+	if {![ishighlighted [commitonrow $row]]} {
 	    bolden $row mainfont
 	} else {
 	    lappend stillbold $row
@@ -2259,17 +3426,13 @@
 }
 
 proc addvhighlight {n} {
-    global hlview curview viewdata vhl_done vhighlights commitidx
+    global hlview viewcomplete curview vhl_done commitidx
 
     if {[info exists hlview]} {
 	delvhighlight
     }
     set hlview $n
-    if {$n != $curview && ![info exists viewdata($n)]} {
-	set viewdata($n) [list getcommits {{}} 0 0 0]
-	set vparentlist($n) {}
-	set vdisporder($n) {}
-	set vcmitlisted($n) {}
+    if {$n != $curview && ![info exists viewcomplete($n)]} {
 	start_rev_list $n
     }
     set vhl_done $commitidx($hlview)
@@ -2288,43 +3451,38 @@
 }
 
 proc vhighlightmore {} {
-    global hlview vhl_done commitidx vhighlights
-    global displayorder vdisporder curview
+    global hlview vhl_done commitidx vhighlights curview
 
     set max $commitidx($hlview)
-    if {$hlview == $curview} {
-	set disp $displayorder
-    } else {
-	set disp $vdisporder($hlview)
-    }
     set vr [visiblerows]
     set r0 [lindex $vr 0]
     set r1 [lindex $vr 1]
     for {set i $vhl_done} {$i < $max} {incr i} {
-	set id [lindex $disp $i]
-	if {[info exists commitrow($curview,$id)]} {
-	    set row $commitrow($curview,$id)
+	set id [commitonrow $i $hlview]
+	if {[commitinview $id $curview]} {
+	    set row [rowofcommit $id]
 	    if {$r0 <= $row && $row <= $r1} {
 		if {![highlighted $row]} {
 		    bolden $row mainfontbold
 		}
-		set vhighlights($row) 1
+		set vhighlights($id) 1
 	    }
 	}
     }
     set vhl_done $max
+    return 0
 }
 
 proc askvhighlight {row id} {
-    global hlview vhighlights commitrow iddrawn
+    global hlview vhighlights iddrawn
 
-    if {[info exists commitrow($hlview,$id)]} {
-	if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
+    if {[commitinview $id $hlview]} {
+	if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
 	    bolden $row mainfontbold
 	}
-	set vhighlights($row) 1
+	set vhighlights($id) 1
     } else {
-	set vhighlights($row) 0
+	set vhighlights($id) 0
     }
 }
 
@@ -2462,12 +3620,12 @@
     global filehighlight fhighlights fhl_list
 
     lappend fhl_list $id
-    set fhighlights($row) -1
+    set fhighlights($id) -1
     puts $filehighlight $id
 }
 
 proc readfhighlight {} {
-    global filehighlight fhighlights commitrow curview iddrawn
+    global filehighlight fhighlights curview iddrawn
     global fhl_list find_dirn
 
     if {![info exists filehighlight]} {
@@ -2480,18 +3638,16 @@
 	if {$i < 0} continue
 	for {set j 0} {$j < $i} {incr j} {
 	    set id [lindex $fhl_list $j]
-	    if {[info exists commitrow($curview,$id)]} {
-		set fhighlights($commitrow($curview,$id)) 0
-	    }
+	    set fhighlights($id) 0
 	}
 	set fhl_list [lrange $fhl_list [expr {$i+1}] end]
 	if {$line eq {}} continue
-	if {![info exists commitrow($curview,$line)]} continue
-	set row $commitrow($curview,$line)
-	if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
+	if {![commitinview $line $curview]} continue
+	set row [rowofcommit $line]
+	if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
 	    bolden $row mainfontbold
 	}
-	set fhighlights($row) 1
+	set fhighlights($line) 1
     }
     if {[eof $filehighlight]} {
 	# strange...
@@ -2540,7 +3696,7 @@
 	}
     }
     if {$isbold && [info exists iddrawn($id)]} {
-	if {![ishighlighted $row]} {
+	if {![ishighlighted $id]} {
 	    bolden $row mainfontbold
 	    if {$isbold > 1} {
 		bolden_name $row mainfontbold
@@ -2550,7 +3706,7 @@
 	    markrowmatches $row $id
 	}
     }
-    set nhighlights($row) $isbold
+    set nhighlights($id) $isbold
 }
 
 proc markrowmatches {row id} {
@@ -2588,7 +3744,7 @@
 # prepare for testing whether commits are descendents or ancestors of a
 proc rhighlight_sel {a} {
     global descendent desc_todo ancestor anc_todo
-    global highlight_related rhighlights
+    global highlight_related
 
     catch {unset descendent}
     set desc_todo [list $a]
@@ -2608,16 +3764,16 @@
 }
 
 proc is_descendent {a} {
-    global curview children commitrow descendent desc_todo
+    global curview children descendent desc_todo
 
     set v $curview
-    set la $commitrow($v,$a)
+    set la [rowofcommit $a]
     set todo $desc_todo
     set leftover {}
     set done 0
     for {set i 0} {$i < [llength $todo]} {incr i} {
 	set do [lindex $todo $i]
-	if {$commitrow($v,$do) < $la} {
+	if {[rowofcommit $do] < $la} {
 	    lappend leftover $do
 	    continue
 	}
@@ -2640,20 +3796,20 @@
 }
 
 proc is_ancestor {a} {
-    global curview parentlist commitrow ancestor anc_todo
+    global curview parents ancestor anc_todo
 
     set v $curview
-    set la $commitrow($v,$a)
+    set la [rowofcommit $a]
     set todo $anc_todo
     set leftover {}
     set done 0
     for {set i 0} {$i < [llength $todo]} {incr i} {
 	set do [lindex $todo $i]
-	if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
+	if {![commitinview $do $v] || [rowofcommit $do] > $la} {
 	    lappend leftover $do
 	    continue
 	}
-	foreach np [lindex $parentlist $commitrow($v,$do)] {
+	foreach np $parents($v,$do) {
 	    if {![info exists ancestor($np)]} {
 		set ancestor($np) 1
 		lappend todo $np
@@ -2675,7 +3831,7 @@
     global descendent highlight_related iddrawn rhighlights
     global selectedline ancestor
 
-    if {![info exists selectedline]} return
+    if {$selectedline eq {}} return
     set isbold 0
     if {$highlight_related eq [mc "Descendant"] ||
 	$highlight_related eq [mc "Not descendant"]} {
@@ -2695,11 +3851,11 @@
 	}
     }
     if {[info exists iddrawn($id)]} {
-	if {$isbold && ![ishighlighted $row]} {
+	if {$isbold && ![ishighlighted $id]} {
 	    bolden $row mainfontbold
 	}
     }
-    set rhighlights($row) $isbold
+    set rhighlights($id) $isbold
 }
 
 # Graph layout functions
@@ -2730,40 +3886,81 @@
     return $ret
 }
 
+proc ordertoken {id} {
+    global ordertok curview varcid varcstart varctok curview parents children
+    global nullid nullid2
+
+    if {[info exists ordertok($id)]} {
+	return $ordertok($id)
+    }
+    set origid $id
+    set todo {}
+    while {1} {
+	if {[info exists varcid($curview,$id)]} {
+	    set a $varcid($curview,$id)
+	    set p [lindex $varcstart($curview) $a]
+	} else {
+	    set p [lindex $children($curview,$id) 0]
+	}
+	if {[info exists ordertok($p)]} {
+	    set tok $ordertok($p)
+	    break
+	}
+	set id [first_real_child $curview,$p]
+	if {$id eq {}} {
+	    # it's a root
+	    set tok [lindex $varctok($curview) $varcid($curview,$p)]
+	    break
+	}
+	if {[llength $parents($curview,$id)] == 1} {
+	    lappend todo [list $p {}]
+	} else {
+	    set j [lsearch -exact $parents($curview,$id) $p]
+	    if {$j < 0} {
+		puts "oops didn't find [shortids $p] in parents of [shortids $id]"
+	    }
+	    lappend todo [list $p [strrep $j]]
+	}
+    }
+    for {set i [llength $todo]} {[incr i -1] >= 0} {} {
+	set p [lindex $todo $i 0]
+	append tok [lindex $todo $i 1]
+	set ordertok($p) $tok
+    }
+    set ordertok($origid) $tok
+    return $tok
+}
+
 # Work out where id should go in idlist so that order-token
 # values increase from left to right
 proc idcol {idlist id {i 0}} {
-    global ordertok curview
-
-    set t $ordertok($curview,$id)
-    if {$i >= [llength $idlist] ||
-	$t < $ordertok($curview,[lindex $idlist $i])} {
+    set t [ordertoken $id]
+    if {$i < 0} {
+	set i 0
+    }
+    if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
 	if {$i > [llength $idlist]} {
 	    set i [llength $idlist]
 	}
-	while {[incr i -1] >= 0 &&
-	       $t < $ordertok($curview,[lindex $idlist $i])} {}
+	while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
 	incr i
     } else {
-	if {$t > $ordertok($curview,[lindex $idlist $i])} {
+	if {$t > [ordertoken [lindex $idlist $i]]} {
 	    while {[incr i] < [llength $idlist] &&
-		   $t >= $ordertok($curview,[lindex $idlist $i])} {}
+		   $t >= [ordertoken [lindex $idlist $i]]} {}
 	}
     }
     return $i
 }
 
 proc initlayout {} {
-    global rowidlist rowisopt rowfinal displayorder commitlisted
+    global rowidlist rowisopt rowfinal displayorder parentlist
     global numcommits canvxmax canv
     global nextcolor
-    global parentlist
     global colormap rowtextx
-    global selectfirst
 
     set numcommits 0
     set displayorder {}
-    set commitlisted {}
     set parentlist {}
     set nextcolor 0
     set rowidlist {}
@@ -2772,16 +3969,19 @@
     set canvxmax [$canv cget -width]
     catch {unset colormap}
     catch {unset rowtextx}
-    set selectfirst 1
+    setcanvscroll
 }
 
 proc setcanvscroll {} {
     global canv canv2 canv3 numcommits linespc canvxmax canvy0
+    global lastscrollset lastscrollrows
 
     set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
     $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
     $canv2 conf -scrollregion [list 0 0 0 $ymax]
     $canv3 conf -scrollregion [list 0 0 0 $ymax]
+    set lastscrollset [clock clicks -milliseconds]
+    set lastscrollrows $numcommits
 }
 
 proc visiblerows {} {
@@ -2804,102 +4004,57 @@
 }
 
 proc layoutmore {} {
-    global commitidx viewcomplete numcommits
-    global uparrowlen downarrowlen mingaplen curview
+    global commitidx viewcomplete curview
+    global numcommits pending_select curview
+    global lastscrollset lastscrollrows commitinterest
 
-    set show $commitidx($curview)
-    if {$show > $numcommits || $viewcomplete($curview)} {
-	showstuff $show $viewcomplete($curview)
-    }
-}
-
-proc showstuff {canshow last} {
-    global numcommits commitrow pending_select selectedline curview
-    global mainheadid displayorder selectfirst
-    global lastscrollset commitinterest
-
-    if {$numcommits == 0} {
-	global phase
-	set phase "incrdraw"
-	allcanvs delete all
-    }
-    set r0 $numcommits
-    set prev $numcommits
-    set numcommits $canshow
-    set t [clock clicks -milliseconds]
-    if {$prev < 100 || $last || $t - $lastscrollset > 500} {
-	set lastscrollset $t
+    if {$lastscrollrows < 100 || $viewcomplete($curview) ||
+	[clock clicks -milliseconds] - $lastscrollset > 500} {
 	setcanvscroll
     }
-    set rows [visiblerows]
-    set r1 [lindex $rows 1]
-    if {$r1 >= $canshow} {
-	set r1 [expr {$canshow - 1}]
-    }
-    if {$r0 <= $r1} {
-	drawcommits $r0 $r1
-    }
     if {[info exists pending_select] &&
-	[info exists commitrow($curview,$pending_select)] &&
-	$commitrow($curview,$pending_select) < $numcommits} {
-	selectline $commitrow($curview,$pending_select) 1
+	[commitinview $pending_select $curview]} {
+	selectline [rowofcommit $pending_select] 1
     }
-    if {$selectfirst} {
-	if {[info exists selectedline] || [info exists pending_select]} {
-	    set selectfirst 0
-	} else {
-	    set l [first_real_row]
-	    selectline $l 1
-	    set selectfirst 0
-	}
-    }
+    drawvisible
 }
 
 proc doshowlocalchanges {} {
-    global curview mainheadid phase commitrow
+    global curview mainheadid
 
-    if {[info exists commitrow($curview,$mainheadid)] &&
-	($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
+    if {[commitinview $mainheadid $curview]} {
 	dodiffindex
-    } elseif {$phase ne {}} {
-	lappend commitinterest($mainheadid) {}
+    } else {
+	lappend commitinterest($mainheadid) {dodiffindex}
     }
 }
 
 proc dohidelocalchanges {} {
-    global localfrow localirow lserial
+    global nullid nullid2 lserial curview
 
-    if {$localfrow >= 0} {
-	removerow $localfrow
-	set localfrow -1
-	if {$localirow > 0} {
-	    incr localirow -1
-	}
+    if {[commitinview $nullid $curview]} {
+	removefakerow $nullid
     }
-    if {$localirow >= 0} {
-	removerow $localirow
-	set localirow -1
+    if {[commitinview $nullid2 $curview]} {
+	removefakerow $nullid2
     }
     incr lserial
 }
 
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
-    global localirow localfrow lserial showlocalchanges
+    global lserial showlocalchanges
     global isworktree
 
     if {!$showlocalchanges || !$isworktree} return
     incr lserial
-    set localfrow -1
-    set localirow -1
     set fd [open "|git diff-index --cached HEAD" r]
     fconfigure $fd -blocking 0
     filerun $fd [list readdiffindex $fd $lserial]
 }
 
 proc readdiffindex {fd serial} {
-    global localirow commitrow mainheadid nullid2 curview
-    global commitinfo commitdata lserial
+    global mainheadid nullid nullid2 curview commitinfo commitdata lserial
 
     set isdiff 1
     if {[gets $fd line] < 0} {
@@ -2911,26 +4066,32 @@
     # we only need to see one line and we don't really care what it says...
     close $fd
 
-    # now see if there are any local changes not checked in to the index
-    if {$serial == $lserial} {
-	set fd [open "|git diff-files" r]
-	fconfigure $fd -blocking 0
-	filerun $fd [list readdifffiles $fd $serial]
+    if {$serial != $lserial} {
+	return 0
     }
 
-    if {$isdiff && $serial == $lserial && $localirow == -1} {
+    # now see if there are any local changes not checked in to the index
+    set fd [open "|git diff-files" r]
+    fconfigure $fd -blocking 0
+    filerun $fd [list readdifffiles $fd $serial]
+
+    if {$isdiff && ![commitinview $nullid2 $curview]} {
 	# add the line for the changes in the index to the graph
-	set localirow $commitrow($curview,$mainheadid)
 	set hl [mc "Local changes checked in to index but not committed"]
 	set commitinfo($nullid2) [list  $hl {} {} {} {} "    $hl\n"]
 	set commitdata($nullid2) "\n    $hl\n"
-	insertrow $localirow $nullid2
+	if {[commitinview $nullid $curview]} {
+	    removefakerow $nullid
+	}
+	insertfakerow $nullid2 $mainheadid
+    } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
+	removefakerow $nullid2
     }
     return 0
 }
 
 proc readdifffiles {fd serial} {
-    global localirow localfrow commitrow mainheadid nullid curview
+    global mainheadid nullid nullid2 curview
     global commitinfo commitdata lserial
 
     set isdiff 1
@@ -2943,50 +4104,55 @@
     # we only need to see one line and we don't really care what it says...
     close $fd
 
-    if {$isdiff && $serial == $lserial && $localfrow == -1} {
+    if {$serial != $lserial} {
+	return 0
+    }
+
+    if {$isdiff && ![commitinview $nullid $curview]} {
 	# add the line for the local diff to the graph
-	if {$localirow >= 0} {
-	    set localfrow $localirow
-	    incr localirow
-	} else {
-	    set localfrow $commitrow($curview,$mainheadid)
-	}
 	set hl [mc "Local uncommitted changes, not checked in to index"]
 	set commitinfo($nullid) [list  $hl {} {} {} {} "    $hl\n"]
 	set commitdata($nullid) "\n    $hl\n"
-	insertrow $localfrow $nullid
+	if {[commitinview $nullid2 $curview]} {
+	    set p $nullid2
+	} else {
+	    set p $mainheadid
+	}
+	insertfakerow $nullid $p
+    } elseif {!$isdiff && [commitinview $nullid $curview]} {
+	removefakerow $nullid
     }
     return 0
 }
 
 proc nextuse {id row} {
-    global commitrow curview children
+    global curview children
 
     if {[info exists children($curview,$id)]} {
 	foreach kid $children($curview,$id) {
-	    if {![info exists commitrow($curview,$kid)]} {
+	    if {![commitinview $kid $curview]} {
 		return -1
 	    }
-	    if {$commitrow($curview,$kid) > $row} {
-		return $commitrow($curview,$kid)
+	    if {[rowofcommit $kid] > $row} {
+		return [rowofcommit $kid]
 	    }
 	}
     }
-    if {[info exists commitrow($curview,$id)]} {
-	return $commitrow($curview,$id)
+    if {[commitinview $id $curview]} {
+	return [rowofcommit $id]
     }
     return -1
 }
 
 proc prevuse {id row} {
-    global commitrow curview children
+    global curview children
 
     set ret -1
     if {[info exists children($curview,$id)]} {
 	foreach kid $children($curview,$id) {
-	    if {![info exists commitrow($curview,$kid)]} break
-	    if {$commitrow($curview,$kid) < $row} {
-		set ret $commitrow($curview,$kid)
+	    if {![commitinview $kid $curview]} break
+	    if {[rowofcommit $kid] < $row} {
+		set ret [rowofcommit $kid]
 	    }
 	}
     }
@@ -2995,7 +4161,7 @@
 
 proc make_idlist {row} {
     global displayorder parentlist uparrowlen downarrowlen mingaplen
-    global commitidx curview ordertok children commitrow
+    global commitidx curview children
 
     set r [expr {$row - $mingaplen - $downarrowlen - 1}]
     if {$r < 0} {
@@ -3009,6 +4175,7 @@
     if {$rb > $commitidx($curview)} {
 	set rb $commitidx($curview)
     }
+    make_disporder $r [expr {$rb + 1}]
     set ids {}
     for {} {$r < $ra} {incr r} {
 	set nextid [lindex $displayorder [expr {$r + 1}]]
@@ -3017,7 +4184,7 @@
 	    set rn [nextuse $p $r]
 	    if {$rn >= $row &&
 		$rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
-		lappend ids [list $ordertok($curview,$p) $p]
+		lappend ids [list [ordertoken $p] $p]
 	    }
 	}
     }
@@ -3027,25 +4194,25 @@
 	    if {$p eq $nextid} continue
 	    set rn [nextuse $p $r]
 	    if {$rn < 0 || $rn >= $row} {
-		lappend ids [list $ordertok($curview,$p) $p]
+		lappend ids [list [ordertoken $p] $p]
 	    }
 	}
     }
     set id [lindex $displayorder $row]
-    lappend ids [list $ordertok($curview,$id) $id]
+    lappend ids [list [ordertoken $id] $id]
     while {$r < $rb} {
 	foreach p [lindex $parentlist $r] {
 	    set firstkid [lindex $children($curview,$p) 0]
-	    if {$commitrow($curview,$firstkid) < $row} {
-		lappend ids [list $ordertok($curview,$p) $p]
+	    if {[rowofcommit $firstkid] < $row} {
+		lappend ids [list [ordertoken $p] $p]
 	    }
 	}
 	incr r
 	set id [lindex $displayorder $r]
 	if {$id ne {}} {
 	    set firstkid [lindex $children($curview,$id) 0]
-	    if {$firstkid ne {} && $commitrow($curview,$firstkid) < $row} {
-		lappend ids [list $ordertok($curview,$id) $id]
+	    if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
+		lappend ids [list [ordertoken $id] $id]
 	    }
 	}
     }
@@ -3091,8 +4258,9 @@
     global rowidlist rowisopt rowfinal displayorder
     global uparrowlen downarrowlen maxwidth mingaplen
     global children parentlist
-    global commitidx viewcomplete curview commitrow
+    global commitidx viewcomplete curview
 
+    make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
     set idlist {}
     if {$row > 0} {
 	set rm1 [expr {$row - 1}]
@@ -3148,7 +4316,7 @@
 		foreach p [lindex $parentlist $r] {
 		    if {[lsearch -exact $idlist $p] >= 0} continue
 		    set fk [lindex $children($curview,$p) 0]
-		    if {$commitrow($curview,$fk) < $row} {
+		    if {[rowofcommit $fk] < $row} {
 			set x [idcol $idlist $p $x]
 			set idlist [linsert $idlist $x $p]
 		    }
@@ -3157,7 +4325,7 @@
 		    set p [lindex $displayorder $r]
 		    if {[lsearch -exact $idlist $p] < 0} {
 			set fk [lindex $children($curview,$p) 0]
-			if {$fk ne {} && $commitrow($curview,$fk) < $row} {
+			if {$fk ne {} && [rowofcommit $fk] < $row} {
 			    set x [idcol $idlist $p $x]
 			    set idlist [linsert $idlist $x $p]
 			}
@@ -3372,7 +4540,7 @@
 }
 
 proc rowranges {id} {
-    global commitrow curview children uparrowlen downarrowlen
+    global curview children uparrowlen downarrowlen
     global rowidlist
 
     set kids $children($curview,$id)
@@ -3382,13 +4550,13 @@
     set ret {}
     lappend kids $id
     foreach child $kids {
-	if {![info exists commitrow($curview,$child)]} break
-	set row $commitrow($curview,$child)
+	if {![commitinview $child $curview]} break
+	set row [rowofcommit $child]
 	if {![info exists prev]} {
 	    lappend ret [expr {$row + 1}]
 	} else {
 	    if {$row <= $prevrow} {
-		puts "oops children out of order [shortids $id] $row < [shortids $prev] $prevrow"
+		puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
 	    }
 	    # see if the line extends the whole way from prevrow to row
 	    if {$row > $prevrow + $uparrowlen + $downarrowlen &&
@@ -3421,7 +4589,7 @@
 	if {$child eq $id} {
 	    lappend ret $row
 	}
-	set prev $id
+	set prev $child
 	set prevrow $row
     }
     return $ret
@@ -3669,14 +4837,14 @@
 }
 
 proc drawcmittext {id row col} {
-    global linespc canv canv2 canv3 canvy0 fgcolor curview
-    global commitlisted commitinfo rowidlist parentlist
+    global linespc canv canv2 canv3 fgcolor curview
+    global cmitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag selectedline
     global canvxmax boldrows boldnamerows fgcolor nullid nullid2
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
-    set listed [lindex $commitlisted $row]
+    set listed $cmitlisted($curview,$id)
     if {$id eq $nullid} {
 	set ofill red
     } elseif {$id eq $nullid2} {
@@ -3732,7 +4900,7 @@
     set date [formatdate $date]
     set font mainfont
     set nfont mainfont
-    set isbold [ishighlighted $row]
+    set isbold [ishighlighted $id]
     if {$isbold > 0} {
 	lappend boldrows $row
 	set font mainfontbold
@@ -3748,7 +4916,7 @@
 			    -text $name -font $nfont -tags text]
     set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
 			    -text $date -font mainfont -tags text]
-    if {[info exists selectedline] && $selectedline == $row} {
+    if {$selectedline == $row} {
 	make_secsel $row
     }
     set xr [expr {$xt + [font measure $font $headline]}]
@@ -3761,7 +4929,7 @@
 proc drawcmitrow {row} {
     global displayorder rowidlist nrows_drawn
     global iddrawn markingmatches
-    global commitinfo parentlist numcommits
+    global commitinfo numcommits
     global filehighlight fhighlights findpattern nhighlights
     global hlview vhighlights
     global highlight_related rhighlights
@@ -3769,16 +4937,16 @@
     if {$row >= $numcommits} return
 
     set id [lindex $displayorder $row]
-    if {[info exists hlview] && ![info exists vhighlights($row)]} {
+    if {[info exists hlview] && ![info exists vhighlights($id)]} {
 	askvhighlight $row $id
     }
-    if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
+    if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
 	askfilehighlight $row $id
     }
-    if {$findpattern ne {} && ![info exists nhighlights($row)]} {
+    if {$findpattern ne {} && ![info exists nhighlights($id)]} {
 	askfindhighlight $row $id
     }
-    if {$highlight_related ne [mc "None"] && ![info exists rhighlights($row)]} {
+    if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
 	askrelhighlight $row $id
     }
     if {![info exists iddrawn($id)]} {
@@ -3881,30 +5049,92 @@
     }
 }
 
-proc drawfrac {f0 f1} {
-    global canv linespc
+proc undolayout {row} {
+    global uparrowlen mingaplen downarrowlen
+    global rowidlist rowisopt rowfinal need_redisplay
 
-    set ymax [lindex [$canv cget -scrollregion] 3]
-    if {$ymax eq {} || $ymax == 0} return
-    set y0 [expr {int($f0 * $ymax)}]
-    set row [expr {int(($y0 - 3) / $linespc) - 1}]
-    set y1 [expr {int($f1 * $ymax)}]
-    set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
-    drawcommits $row $endrow
+    set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
+    if {$r < 0} {
+	set r 0
+    }
+    if {[llength $rowidlist] > $r} {
+	incr r -1
+	set rowidlist [lrange $rowidlist 0 $r]
+	set rowfinal [lrange $rowfinal 0 $r]
+	set rowisopt [lrange $rowisopt 0 $r]
+	set need_redisplay 1
+	run drawvisible
+    }
 }
 
 proc drawvisible {} {
-    global canv
-    eval drawfrac [$canv yview]
+    global canv linespc curview vrowmod selectedline targetrow targetid
+    global need_redisplay cscroll numcommits
+
+    set fs [$canv yview]
+    set ymax [lindex [$canv cget -scrollregion] 3]
+    if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
+    set f0 [lindex $fs 0]
+    set f1 [lindex $fs 1]
+    set y0 [expr {int($f0 * $ymax)}]
+    set y1 [expr {int($f1 * $ymax)}]
+
+    if {[info exists targetid]} {
+	if {[commitinview $targetid $curview]} {
+	    set r [rowofcommit $targetid]
+	    if {$r != $targetrow} {
+		# Fix up the scrollregion and change the scrolling position
+		# now that our target row has moved.
+		set diff [expr {($r - $targetrow) * $linespc}]
+		set targetrow $r
+		setcanvscroll
+		set ymax [lindex [$canv cget -scrollregion] 3]
+		incr y0 $diff
+		incr y1 $diff
+		set f0 [expr {$y0 / $ymax}]
+		set f1 [expr {$y1 / $ymax}]
+		allcanvs yview moveto $f0
+		$cscroll set $f0 $f1
+		set need_redisplay 1
+	    }
+	} else {
+	    unset targetid
+	}
+    }
+
+    set row [expr {int(($y0 - 3) / $linespc) - 1}]
+    set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
+    if {$endrow >= $vrowmod($curview)} {
+	update_arcrows $curview
+    }
+    if {$selectedline ne {} &&
+	$row <= $selectedline && $selectedline <= $endrow} {
+	set targetrow $selectedline
+    } elseif {[info exists targetid]} {
+	set targetrow [expr {int(($row + $endrow) / 2)}]
+    }
+    if {[info exists targetrow]} {
+	if {$targetrow >= $numcommits} {
+	    set targetrow [expr {$numcommits - 1}]
+	}
+	set targetid [commitonrow $targetrow]
+    }
+    drawcommits $row $endrow
 }
 
 proc clear_display {} {
     global iddrawn linesegs need_redisplay nrows_drawn
     global vhighlights fhighlights nhighlights rhighlights
+    global linehtag linentag linedtag boldrows boldnamerows
 
     allcanvs delete all
     catch {unset iddrawn}
     catch {unset linesegs}
+    catch {unset linehtag}
+    catch {unset linentag}
+    catch {unset linedtag}
+    set boldrows {}
+    set boldnamerows {}
     catch {unset vhighlights}
     catch {unset fhighlights}
     catch {unset nhighlights}
@@ -3950,7 +5180,7 @@
 
 proc assigncolor {id} {
     global colormap colors nextcolor
-    global commitrow parentlist children children curview
+    global parents children children curview
 
     if {[info exists colormap($id)]} return
     set ncolors [llength $colors]
@@ -3962,7 +5192,7 @@
     if {[llength $kids] == 1} {
 	set child [lindex $kids 0]
 	if {[info exists colormap($child)]
-	    && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
+	    && [llength $parents($curview,$child)] == 1} {
 	    set colormap($id) $colormap($child)
 	    return
 	}
@@ -3990,7 +5220,7 @@
 		&& [lsearch -exact $badcolors $colormap($child)] < 0} {
 		lappend badcolors $colormap($child)
 	    }
-	    foreach p [lindex $parentlist $commitrow($curview,$child)] {
+	    foreach p $parents($curview,$child) {
 		if {[info exists colormap($p)]
 		    && [lsearch -exact $badcolors $colormap($p)] < 0} {
 		    lappend badcolors $colormap($p)
@@ -4023,7 +5253,7 @@
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
-    global canv commitrow rowtextx curview fgcolor bgcolor
+    global canv rowtextx curview fgcolor bgcolor
 
     set marks {}
     set ntags 0
@@ -4073,7 +5303,7 @@
 		       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
 		       -width 1 -outline black -fill yellow -tags tag.$id]
 	    $canv bind $t <1> [list showtag $tag 1]
-	    set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
+	    set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
 	} else {
 	    # draw a head or other ref
 	    if {[incr nheads -1] >= 0} {
@@ -4127,103 +5357,6 @@
 	-tags text -fill $fgcolor
 }
 
-# Insert a new commit as the child of the commit on row $row.
-# The new commit will be displayed on row $row and the commits
-# on that row and below will move down one row.
-proc insertrow {row newcmit} {
-    global displayorder parentlist commitlisted children
-    global commitrow curview rowidlist rowisopt rowfinal numcommits
-    global numcommits
-    global selectedline commitidx ordertok
-
-    if {$row >= $numcommits} {
-	puts "oops, inserting new row $row but only have $numcommits rows"
-	return
-    }
-    set p [lindex $displayorder $row]
-    set displayorder [linsert $displayorder $row $newcmit]
-    set parentlist [linsert $parentlist $row $p]
-    set kids $children($curview,$p)
-    lappend kids $newcmit
-    set children($curview,$p) $kids
-    set children($curview,$newcmit) {}
-    set commitlisted [linsert $commitlisted $row 1]
-    set l [llength $displayorder]
-    for {set r $row} {$r < $l} {incr r} {
-	set id [lindex $displayorder $r]
-	set commitrow($curview,$id) $r
-    }
-    incr commitidx($curview)
-    set ordertok($curview,$newcmit) $ordertok($curview,$p)
-
-    if {$row < [llength $rowidlist]} {
-	set idlist [lindex $rowidlist $row]
-	if {$idlist ne {}} {
-	    if {[llength $kids] == 1} {
-		set col [lsearch -exact $idlist $p]
-		lset idlist $col $newcmit
-	    } else {
-		set col [llength $idlist]
-		lappend idlist $newcmit
-	    }
-	}
-	set rowidlist [linsert $rowidlist $row $idlist]
-	set rowisopt [linsert $rowisopt $row 0]
-	set rowfinal [linsert $rowfinal $row [lindex $rowfinal $row]]
-    }
-
-    incr numcommits
-
-    if {[info exists selectedline] && $selectedline >= $row} {
-	incr selectedline
-    }
-    redisplay
-}
-
-# Remove a commit that was inserted with insertrow on row $row.
-proc removerow {row} {
-    global displayorder parentlist commitlisted children
-    global commitrow curview rowidlist rowisopt rowfinal numcommits
-    global numcommits
-    global linesegends selectedline commitidx
-
-    if {$row >= $numcommits} {
-	puts "oops, removing row $row but only have $numcommits rows"
-	return
-    }
-    set rp1 [expr {$row + 1}]
-    set id [lindex $displayorder $row]
-    set p [lindex $parentlist $row]
-    set displayorder [lreplace $displayorder $row $row]
-    set parentlist [lreplace $parentlist $row $row]
-    set commitlisted [lreplace $commitlisted $row $row]
-    set kids $children($curview,$p)
-    set i [lsearch -exact $kids $id]
-    if {$i >= 0} {
-	set kids [lreplace $kids $i $i]
-	set children($curview,$p) $kids
-    }
-    set l [llength $displayorder]
-    for {set r $row} {$r < $l} {incr r} {
-	set id [lindex $displayorder $r]
-	set commitrow($curview,$id) $r
-    }
-    incr commitidx($curview) -1
-
-    if {$row < [llength $rowidlist]} {
-	set rowidlist [lreplace $rowidlist $row $row]
-	set rowisopt [lreplace $rowisopt $row $row]
-	set rowfinal [lreplace $rowfinal $row $row]
-    }
-
-    incr numcommits -1
-
-    if {[info exists selectedline] && $selectedline > $row} {
-	incr selectedline -1
-    }
-    redisplay
-}
-
 # Don't change the text pane cursor if it is currently the hand cursor,
 # showing that we are over a sha1 ID link.
 proc settextcursor {c} {
@@ -4296,7 +5429,7 @@
     }
     focus .
     if {$findstring eq {} || $numcommits == 0} return
-    if {![info exists selectedline]} {
+    if {$selectedline eq {}} {
 	set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
     } else {
 	set findstartline $selectedline
@@ -4326,9 +5459,9 @@
 
 proc findmore {} {
     global commitdata commitinfo numcommits findpattern findloc
-    global findstartline findcurline displayorder
+    global findstartline findcurline findallowwrap
     global find_dirn gdttype fhighlights fprogcoord
-    global findallowwrap
+    global curview varcorder vrownum varccommits vrowmod
 
     if {![info exists find_dirn]} {
 	return 0
@@ -4364,14 +5497,31 @@
 	set n 500
 	set moretodo 1
     }
+    if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
+	update_arcrows $curview
+    }
     set found 0
     set domore 1
+    set ai [bsearch $vrownum($curview) $l]
+    set a [lindex $varcorder($curview) $ai]
+    set arow [lindex $vrownum($curview) $ai]
+    set ids [lindex $varccommits($curview,$a)]
+    set arowend [expr {$arow + [llength $ids]}]
     if {$gdttype eq [mc "containing:"]} {
 	for {} {$n > 0} {incr n -1; incr l $find_dirn} {
-	    set id [lindex $displayorder $l]
+	    if {$l < $arow || $l >= $arowend} {
+		incr ai $find_dirn
+		set a [lindex $varcorder($curview) $ai]
+		set arow [lindex $vrownum($curview) $ai]
+		set ids [lindex $varccommits($curview,$a)]
+		set arowend [expr {$arow + [llength $ids]}]
+	    }
+	    set id [lindex $ids [expr {$l - $arow}]]
 	    # shouldn't happen unless git log doesn't give all the commits...
-	    if {![info exists commitdata($id)]} continue
-	    if {![doesmatch $commitdata($id)]} continue
+	    if {![info exists commitdata($id)] ||
+		![doesmatch $commitdata($id)]} {
+		continue
+	    }
 	    if {![info exists commitinfo($id)]} {
 		getcommit $id
 	    }
@@ -4387,16 +5537,27 @@
 	}
     } else {
 	for {} {$n > 0} {incr n -1; incr l $find_dirn} {
-	    set id [lindex $displayorder $l]
-	    if {![info exists fhighlights($l)]} {
+	    if {$l < $arow || $l >= $arowend} {
+		incr ai $find_dirn
+		set a [lindex $varcorder($curview) $ai]
+		set arow [lindex $vrownum($curview) $ai]
+		set ids [lindex $varccommits($curview,$a)]
+		set arowend [expr {$arow + [llength $ids]}]
+	    }
+	    set id [lindex $ids [expr {$l - $arow}]]
+	    if {![info exists fhighlights($id)]} {
+		# this sets fhighlights($id) to -1
 		askfilehighlight $l $id
+	    }
+	    if {$fhighlights($id) > 0} {
+		set found $domore
+		break
+	    }
+	    if {$fhighlights($id) < 0} {
 		if {$domore} {
 		    set domore 0
 		    set findcurline [expr {$l - $find_dirn}]
 		}
-	    } elseif {$fhighlights($l)} {
-		set found $domore
-		break
 	    }
 	}
     }
@@ -4464,7 +5625,7 @@
 		   [expr {$x0+$xlen+2}] $y1 \
 		   -outline {} -tags [list match$l matches] -fill yellow]
 	$canv lower $t
-	if {[info exists selectedline] && $row == $selectedline} {
+	if {$row == $selectedline} {
 	    $canv raise $t secsel
 	}
     }
@@ -4490,7 +5651,9 @@
 	set l 0
     }
     if {$w eq $canv} {
-	if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
+	set xmax [lindex [$canv cget -scrollregion] 2]
+	set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
+	if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
     }
     unmarkmatches
     selectline $l 1
@@ -4511,7 +5674,7 @@
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
 proc appendwithlinks {text tags} {
-    global ctext commitrow linknum curview pendinglinks
+    global ctext linknum curview pendinglinks
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
@@ -4529,11 +5692,11 @@
 }
 
 proc setlink {id lk} {
-    global curview commitrow ctext pendinglinks commitinterest
+    global curview ctext pendinglinks commitinterest
 
-    if {[info exists commitrow($curview,$id)]} {
+    if {[commitinview $id $curview]} {
 	$ctext tag conf $lk -foreground blue -underline 1
-	$ctext tag bind $lk <1> [list selectline $commitrow($curview,$id) 1]
+	$ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
 	$ctext tag bind $lk <Enter> {linkcursor %W 1}
 	$ctext tag bind $lk <Leave> {linkcursor %W -1}
     } else {
@@ -4584,7 +5747,7 @@
 # add a list of tag or branch names at position pos
 # returns the number of names inserted
 proc appendrefs {pos ids var} {
-    global ctext commitrow linknum curview $var maxrefs
+    global ctext linknum curview $var maxrefs
 
     if {[catch {$ctext index $pos}]} {
 	return 0
@@ -4621,7 +5784,7 @@
 proc dispneartags {delay} {
     global selectedline currentid showneartags tagphase
 
-    if {![info exists selectedline] || !$showneartags} return
+    if {$selectedline eq {} || !$showneartags} return
     after cancel dispnexttag
     if {$delay} {
 	after 200 dispnexttag
@@ -4635,7 +5798,7 @@
 proc dispnexttag {} {
     global selectedline currentid showneartags tagphase ctext
 
-    if {![info exists selectedline] || !$showneartags} return
+    if {$selectedline eq {} || !$showneartags} return
     switch -- $tagphase {
 	0 {
 	    set dtags [desctags $currentid]
@@ -4687,12 +5850,12 @@
 
 proc selectline {l isnew} {
     global canv ctext commitinfo selectedline
-    global displayorder
-    global canvy0 linespc parentlist children curview
+    global canvy0 linespc parents children curview
     global currentid sha1entry
     global commentend idtags linknum
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
+    global targetrow targetid lastscrollrows
     global autoselect
 
     catch {unset pending_select}
@@ -4701,6 +5864,15 @@
     unsel_reflist
     stopfinding
     if {$l < 0 || $l >= $numcommits} return
+    set id [commitonrow $l]
+    set targetid $id
+    set targetrow $l
+    set selectedline $l
+    set currentid $id
+    if {$lastscrollrows < $numcommits} {
+	setcanvscroll
+    }
+
     set y [expr {$canvy0 + $l * $linespc}]
     set ymax [lindex [$canv cget -scrollregion] 3]
     set ytop [expr {$y - $linespc - 1}]
@@ -4740,13 +5912,9 @@
     make_secsel $l
 
     if {$isnew} {
-	addtohistory [list selectline $l 0]
+	addtohistory [list selbyid $id]
     }
 
-    set selectedline $l
-
-    set id [lindex $displayorder $l]
-    set currentid $id
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
     if {$autoselect} {
@@ -4758,6 +5926,9 @@
     $ctext conf -state normal
     clear_ctext
     set linknum 0
+    if {![info exists commitinfo($id)]} {
+	getcommit $id
+    }
     set info $commitinfo($id)
     set date [formatdate [lindex $info 2]]
     $ctext insert end "[mc "Author"]: [lindex $info 1]  $date\n"
@@ -4772,7 +5943,7 @@
     }
 
     set headers {}
-    set olds [lindex $parentlist $l]
+    set olds $parents($curview,$id)
     if {[llength $olds] > 1} {
 	set np 0
 	foreach p $olds {
@@ -4830,7 +6001,7 @@
     } elseif {[llength $olds] <= 1} {
 	startdiff $id
     } else {
-	mergediff $id $l
+	mergediff $id
     }
 }
 
@@ -4849,7 +6020,7 @@
 proc selnextline {dir} {
     global selectedline
     focus .
-    if {![info exists selectedline]} return
+    if {$selectedline eq {}} return
     set l [expr {$selectedline + $dir}]
     unmarkmatches
     selectline $l 1
@@ -4864,7 +6035,7 @@
     }
     allcanvs yview scroll [expr {$dir * $lpp}] units
     drawvisible
-    if {![info exists selectedline]} return
+    if {$selectedline eq {}} return
     set l [expr {$selectedline + $dir * $lpp}]
     if {$l < 0} {
 	set l 0
@@ -4878,7 +6049,7 @@
 proc unselectline {} {
     global selectedline currentid
 
-    catch {unset selectedline}
+    set selectedline {}
     catch {unset currentid}
     allcanvs delete secsel
     rhighlight_none
@@ -4887,7 +6058,7 @@
 proc reselectline {} {
     global selectedline
 
-    if {[info exists selectedline]} {
+    if {$selectedline ne {}} {
 	selectline $selectedline 0
     }
 }
@@ -4992,11 +6163,12 @@
 	if {$diffids eq $nullid} {
 	    set fname $line
 	} else {
-	    if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
 	    set i [string first "\t" $line]
 	    if {$i < 0} continue
-	    set sha1 [lindex $line 2]
 	    set fname [string range $line [expr {$i+1}] end]
+	    set line [string range $line 0 [expr {$i-1}]]
+	    if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
+	    set sha1 [lindex $line 2]
 	    if {[string index $fname 0] eq "\""} {
 		set fname [lindex $fname 0]
 	    }
@@ -5075,19 +6247,19 @@
     return [expr {$nl >= 1000? 2: 1}]
 }
 
-proc mergediff {id l} {
+proc mergediff {id} {
     global diffmergeid mdifffd
     global diffids
+    global parents
     global diffcontext
-    global parentlist
-    global limitdiffs viewfiles curview
+    global limitdiffs vfilelimit 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 -U$diffcontext $id]
-    if {$limitdiffs && $viewfiles($curview) ne {}} {
-	set cmd [concat $cmd -- $viewfiles($curview)]
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
+	set cmd [concat $cmd -- $vfilelimit($curview)]
     }
     if {[catch {set mdf [open $cmd r]} err]} {
 	error_popup "[mc "Error getting merge diffs:"] $err"
@@ -5095,7 +6267,7 @@
     }
     fconfigure $mdf -blocking 0
     set mdifffd($id) $mdf
-    set np [llength [lindex $parentlist $l]]
+    set np [llength $parents($curview,$id)]
     settabs $np
     filerun $mdf [list getmergediffline $mdf $id $np]
 }
@@ -5265,7 +6437,7 @@
 
 proc gettreediffline {gdtf ids} {
     global treediff treediffs treepending diffids diffmergeid
-    global cmitmode viewfiles curview limitdiffs
+    global cmitmode vfilelimit curview limitdiffs
 
     set nr 0
     while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
@@ -5282,10 +6454,10 @@
 	return [expr {$nr >= 1000? 2: 1}]
     }
     close $gdtf
-    if {$limitdiffs && $viewfiles($curview) ne {}} {
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
 	set flist {}
 	foreach f $treediff {
-	    if {[path_filter $viewfiles($curview) $f]} {
+	    if {[path_filter $vfilelimit($curview) $f]} {
 		lappend flist $f
 	    }
 	}
@@ -5331,14 +6503,14 @@
     global diffinhdr treediffs
     global diffcontext
     global ignorespace
-    global limitdiffs viewfiles curview
+    global limitdiffs vfilelimit 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)]
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
+	set cmd [concat $cmd -- $vfilelimit($curview)]
     }
     if {[catch {set bdf [open $cmd r]} err]} {
 	puts "error getting diffs: $err"
@@ -5478,26 +6650,44 @@
     $ctext tag conf d1 -elide [lindex $diffelide 1]
 }
 
+proc highlightfile {loc cline} {
+    global ctext cflist cflist_top
+
+    $ctext yview $loc
+    $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+    $cflist tag add highlight $cline.0 "$cline.0 lineend"
+    $cflist see $cline.0
+    set cflist_top $cline
+}
+
 proc prevfile {} {
-    global difffilestart ctext
-    set prev [lindex $difffilestart 0]
+    global difffilestart ctext cmitmode
+
+    if {$cmitmode eq "tree"} return
+    set prev 0.0
+    set prevline 1
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
 	if {[$ctext compare $loc >= $here]} {
-	    $ctext yview $prev
+	    highlightfile $prev $prevline
 	    return
 	}
 	set prev $loc
+	incr prevline
     }
-    $ctext yview $prev
+    highlightfile $prev $prevline
 }
 
 proc nextfile {} {
-    global difffilestart ctext
+    global difffilestart ctext cmitmode
+
+    if {$cmitmode eq "tree"} return
     set here [$ctext index @0,0]
+    set line 1
     foreach loc $difffilestart {
+	incr line
 	if {[$ctext compare $loc > $here]} {
-	    $ctext yview $loc
+	    highlightfile $loc $line
 	    return
 	}
     }
@@ -5680,7 +6870,7 @@
     setcanvscroll
     allcanvs yview moveto [lindex $span 0]
     drawvisible
-    if {[info exists selectedline]} {
+    if {$selectedline ne {}} {
 	selectline $selectedline 0
 	allcanvs yview moveto [lindex $span 0]
     }
@@ -5731,7 +6921,7 @@
 }
 
 proc incrfont {inc} {
-    global mainfont textfont ctext canv phase cflist showrefstop
+    global mainfont textfont ctext canv cflist showrefstop
     global stopped entries fontattr
 
     unmarkmatches
@@ -5782,8 +6972,7 @@
 }
 
 proc gotocommit {} {
-    global sha1string currentid commitrow tagids headids
-    global displayorder numcommits curview
+    global sha1string tagids headids curview varcid
 
     if {$sha1string == {}
 	|| ([info exists currentid] && $sha1string == $currentid)} return
@@ -5794,23 +6983,18 @@
     } else {
 	set id [string tolower $sha1string]
 	if {[regexp {^[0-9a-f]{4,39}$} $id]} {
-	    set matches {}
-	    foreach i $displayorder {
-		if {[string match $id* $i]} {
-		    lappend matches $i
-		}
-	    }
+	    set matches [array names varcid "$curview,$id*"]
 	    if {$matches ne {}} {
 		if {[llength $matches] > 1} {
 		    error_popup [mc "Short SHA1 id %s is ambiguous" $id]
 		    return
 		}
-		set id [lindex $matches 0]
+		set id [lindex [split [lindex $matches 0] ","] 1]
 	    }
 	}
     }
-    if {[info exists commitrow($curview,$id)]} {
-	selectline $commitrow($curview,$id) 1
+    if {[commitinview $id $curview]} {
+	selectline [rowofcommit $id] 1
 	return
     }
     if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
@@ -5919,7 +7103,7 @@
 }
 
 proc lineclick {x y id isnew} {
-    global ctext commitinfo children canv thickerline curview commitrow
+    global ctext commitinfo children canv thickerline curview
 
     if {![info exists commitinfo($id)] && ![getcommit $id]} return
     unmarkmatches
@@ -5987,9 +7171,9 @@
 }
 
 proc selbyid {id} {
-    global commitrow curview
-    if {[info exists commitrow($curview,$id)]} {
-	selectline $commitrow($curview,$id) 1
+    global curview
+    if {[commitinview $id $curview]} {
+	selectline [rowofcommit $id] 1
     }
 }
 
@@ -6002,20 +7186,23 @@
 }
 
 proc rowmenu {x y id} {
-    global rowctxmenu commitrow selectedline rowmenuid curview
+    global rowctxmenu selectedline rowmenuid curview
     global nullid nullid2 fakerowmenu mainhead
 
     stopfinding
     set rowmenuid $id
-    if {![info exists selectedline]
-	|| $commitrow($curview,$id) eq $selectedline} {
+    if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
 	set state disabled
     } else {
 	set state normal
     }
     if {$id ne $nullid && $id ne $nullid2} {
 	set menu $rowctxmenu
-	$menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
+	if {$mainhead ne {}} {
+	    $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
+	} else {
+	    $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
+	}
     } else {
 	set menu $fakerowmenu
     }
@@ -6026,15 +7213,15 @@
 }
 
 proc diffvssel {dirn} {
-    global rowmenuid selectedline displayorder
+    global rowmenuid selectedline
 
-    if {![info exists selectedline]} return
+    if {$selectedline eq {}} return
     if {$dirn} {
-	set oldid [lindex $displayorder $selectedline]
+	set oldid [commitonrow $selectedline]
 	set newid $rowmenuid
     } else {
 	set oldid $rowmenuid
-	set newid [lindex $displayorder $selectedline]
+	set newid [commitonrow $selectedline]
     }
     addtohistory [list doseldiff $oldid $newid]
     doseldiff $oldid $newid
@@ -6212,24 +7399,24 @@
 }
 
 proc redrawtags {id} {
-    global canv linehtag commitrow idpos selectedline curview
+    global canv linehtag idpos currentid curview
     global canvxmax iddrawn
 
-    if {![info exists commitrow($curview,$id)]} return
+    if {![commitinview $id $curview]} return
     if {![info exists iddrawn($id)]} return
-    drawcommits $commitrow($curview,$id)
+    set row [rowofcommit $id]
     $canv delete tag.$id
     set xt [eval drawtags $id $idpos($id)]
-    $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
-    set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
-    set xr [expr {$xt + [font measure mainfont $text]}]
+    $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
+    set text [$canv itemcget $linehtag($row) -text]
+    set font [$canv itemcget $linehtag($row) -font]
+    set xr [expr {$xt + [font measure $font $text]}]
     if {$xr > $canvxmax} {
 	set canvxmax $xr
 	setcanvscroll
     }
-    if {[info exists selectedline]
-	&& $selectedline == $commitrow($curview,$id)} {
-	selectline $selectedline 0
+    if {[info exists currentid] && $currentid == $id} {
+	make_secsel $row
     }
 }
 
@@ -6355,8 +7542,8 @@
 }
 
 proc cherrypick {} {
-    global rowmenuid curview commitrow
-    global mainhead
+    global rowmenuid curview
+    global mainhead mainheadid
 
     set oldhead [exec git rev-parse HEAD]
     set dheads [descheads $rowmenuid]
@@ -6382,20 +7569,22 @@
 	return
     }
     addnewchild $newhead $oldhead
-    if {[info exists commitrow($curview,$oldhead)]} {
-	insertrow $commitrow($curview,$oldhead) $newhead
+    if {[commitinview $oldhead $curview]} {
+	insertrow $newhead $oldhead $curview
 	if {$mainhead ne {}} {
 	    movehead $newhead $mainhead
 	    movedhead $newhead $mainhead
+	    set mainheadid $newhead
 	}
 	redrawtags $oldhead
 	redrawtags $newhead
+	selbyid $newhead
     }
     notbusy cherrypick
 }
 
 proc resethead {} {
-    global mainheadid mainhead rowmenuid confirm_ok resettype
+    global mainhead rowmenuid confirm_ok resettype
 
     set confirm_ok 0
     set w ".confirmreset"
@@ -6428,12 +7617,13 @@
     tkwait window $w
     if {!$confirm_ok} return
     if {[catch {set fd [open \
-	    [list | sh -c "git reset --$resettype $rowmenuid 2>&1"] r]} err]} {
+	    [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
 	error_popup $err
     } else {
 	dohidelocalchanges
 	filerun $fd [list readresetstat $fd]
 	nowbusy reset [mc "Resetting"]
+	selbyid $rowmenuid
     }
 }
 
@@ -6489,24 +7679,46 @@
     global showlocalchanges mainheadid
 
     # check the tree is clean first??
-    set oldmainhead $mainhead
     nowbusy checkout [mc "Checking out"]
     update
     dohidelocalchanges
     if {[catch {
-	exec git checkout -q $headmenuhead
+	set fd [open [list | git checkout $headmenuhead 2>@1] r]
     } err]} {
 	notbusy checkout
 	error_popup $err
-    } else {
-	notbusy checkout
-	set mainhead $headmenuhead
-	set mainheadid $headmenuid
-	if {[info exists headids($oldmainhead)]} {
-	    redrawtags $headids($oldmainhead)
+	if {$showlocalchanges} {
+	    dodiffindex
 	}
-	redrawtags $headmenuid
+    } else {
+	filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
     }
+}
+
+proc readcheckoutstat {fd newhead newheadid} {
+    global mainhead mainheadid headids showlocalchanges progresscoords
+
+    if {[gets $fd line] >= 0} {
+	if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
+	    set progresscoords [list 0 [expr {1.0 * $m / $n}]]
+	    adjustprogress
+	}
+	return 1
+    }
+    set progresscoords {0 0}
+    adjustprogress
+    notbusy checkout
+    if {[catch {close $fd} err]} {
+	error_popup $err
+    }
+    set oldmainhead $mainhead
+    set mainhead $newhead
+    set mainheadid $newheadid
+    if {[info exists headids($oldmainhead)]} {
+	redrawtags $headids($oldmainhead)
+    }
+    redrawtags $newheadid
+    selbyid $newheadid
     if {$showlocalchanges} {
 	dodiffindex
     }
@@ -6620,13 +7832,13 @@
 
 proc refill_reflist {} {
     global reflist reflistfilter showrefstop headids tagids otherrefids
-    global commitrow curview commitinterest
+    global curview commitinterest
 
     if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
     set refs {}
     foreach n [array names headids] {
 	if {[string match $reflistfilter $n]} {
-	    if {[info exists commitrow($curview,$headids($n))]} {
+	    if {[commitinview $headids($n) $curview]} {
 		lappend refs [list $n H]
 	    } else {
 		set commitinterest($headids($n)) {run refill_reflist}
@@ -6635,7 +7847,7 @@
     }
     foreach n [array names tagids] {
 	if {[string match $reflistfilter $n]} {
-	    if {[info exists commitrow($curview,$tagids($n))]} {
+	    if {[commitinview $tagids($n) $curview]} {
 		lappend refs [list $n T]
 	    } else {
 		set commitinterest($tagids($n)) {run refill_reflist}
@@ -6644,7 +7856,7 @@
     }
     foreach n [array names otherrefids] {
 	if {[string match $reflistfilter $n]} {
-	    if {[info exists commitrow($curview,$otherrefids($n))]} {
+	    if {[commitinview $otherrefids($n) $curview]} {
 		lappend refs [list $n o]
 	    } else {
 		set commitinterest($otherrefids($n)) {run refill_reflist}
@@ -7788,7 +9000,7 @@
 }
 
 proc rereadrefs {} {
-    global idtags idheads idotherrefs mainhead
+    global idtags idheads idotherrefs mainheadid
 
     set refids [concat [array names idtags] \
 		    [array names idheads] [array names idotherrefs]]
@@ -7797,7 +9009,7 @@
 	    set ref($id) [listrefs $id]
 	}
     }
-    set oldmainhead $mainhead
+    set oldmainhead $mainheadid
     readrefs
     changedrefs
     set refids [lsort -unique [concat $refids [array names idtags] \
@@ -7805,8 +9017,8 @@
     foreach id $refids {
 	set v [listrefs $id]
 	if {![info exists ref($id)] || $ref($id) != $v ||
-	    ($id eq $oldmainhead && $id ne $mainhead) ||
-	    ($id eq $mainhead && $id ne $oldmainhead)} {
+	    ($id eq $oldmainhead && $id ne $mainheadid) ||
+	    ($id eq $mainheadid && $id ne $oldmainhead)} {
 	    redrawtags $id
 	}
     }
@@ -7858,9 +9070,15 @@
 
 proc doquit {} {
     global stopped
+    global gitktmpdir
+
     set stopped 100
     savestuff .
     destroy .
+
+    if {[info exists gitktmpdir]} {
+	catch {file delete -force $gitktmpdir}
+    }
 }
 
 proc mkfontdisp {font top which} {
@@ -7989,7 +9207,7 @@
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global bgcolor fgcolor ctext diffcolors selectbgcolor
-    global tabstop limitdiffs autoselect
+    global tabstop limitdiffs autoselect extdifftool
 
     set top .gitkprefs
     set prefstop $top
@@ -8041,6 +9259,15 @@
     pack $top.ldiff.b $top.ldiff.l -side left
     grid x $top.ldiff -sticky w
 
+    entry $top.extdifft -textvariable extdifftool
+    frame $top.extdifff
+    label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
+	-padx 10
+    button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
+	-command choose_extdiff
+    pack $top.extdifff.l $top.extdifff.b -side left
+    grid x $top.extdifff $top.extdifft -sticky w
+
     label $top.cdisp -text [mc "Colors: press to choose"]
     grid $top.cdisp - -sticky w -pady 10
     label $top.bg -padx 40 -relief sunk -background $bgcolor
@@ -8088,6 +9315,15 @@
     bind $top <Visibility> "focus $top.buts.ok"
 }
 
+proc choose_extdiff {} {
+    global extdifftool
+
+    set prog [tk_getOpenFile -title "External diff tool" -multiple false]
+    if {$prog ne {}} {
+	set extdifftool $prog
+    }
+}
+
 proc choosecolor {v vi w x cmd} {
     global $v
 
@@ -8479,7 +9715,6 @@
 }
 
 # defaults...
-set datemode 0
 set wrcomcmd "git diff-tree --stdin -p --pretty"
 
 set gitencoding {}
@@ -8516,6 +9751,8 @@
 set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
 
+set extdifftool "meld"
+
 set colors {green red blue magenta darkgrey brown orange}
 set bgcolor white
 set fgcolor black
@@ -8570,7 +9807,6 @@
     exit 1
 }
 
-set mergeonly 0
 set revtreeargs {}
 set cmdline_files {}
 set i 0
@@ -8578,11 +9814,6 @@
 foreach arg $argv {
     switch -glob -- $arg {
 	"" { }
-	"-d" { set datemode 1 }
-	"--merge" {
-	    set mergeonly 1
-	    lappend revtreeargs $arg
-	}
 	"--" {
 	    set cmdline_files [lrange $argv [expr {$i + 1}] end]
 	    break
@@ -8598,7 +9829,7 @@
 }
 
 if {$i >= [llength $argv] && $revtreeargs ne {}} {
-    # no -- on command line, but some arguments (other than -d)
+    # no -- on command line, but some arguments (other than --argscmd)
     if {[catch {
 	set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
 	set cmdline_files [split $f "\n"]
@@ -8626,42 +9857,9 @@
     }
 }
 
-if {$mergeonly} {
-    # find the list of unmerged files
-    set mlist {}
-    set nr_unmerged 0
-    if {[catch {
-	set fd [open "| git ls-files -u" r]
-    } err]} {
-	show_error {} . "[mc "Couldn't get list of unmerged files:"] $err"
-	exit 1
-    }
-    while {[gets $fd line] >= 0} {
-	set i [string first "\t" $line]
-	if {$i < 0} continue
-	set fname [string range $line [expr {$i+1}] end]
-	if {[lsearch -exact $mlist $fname] >= 0} continue
-	incr nr_unmerged
-	if {$cmdline_files eq {} || [path_filter $cmdline_files $fname]} {
-	    lappend mlist $fname
-	}
-    }
-    catch {close $fd}
-    if {$mlist eq {}} {
-	if {$nr_unmerged == 0} {
-	    show_error {} . [mc "No files selected: --merge specified but\
-			     no files are unmerged."]
-	} else {
-	    show_error {} . [mc "No files selected: --merge specified but\
-			     no unmerged files are within file limit."]
-	}
-	exit 1
-    }
-    set cmdline_files $mlist
-}
-
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
+set nullfile "/dev/null"
 
 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
 
@@ -8693,12 +9891,13 @@
 set viewargs(0) {}
 set viewargscmd(0) {}
 
+set selectedline {}
+set numcommits 0
+set loginstance 0
 set cmdlineok 0
 set stopped 0
 set stuffsaved 0
 set patchnum 0
-set localirow -1
-set localfrow -1
 set lserial 0
 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 setcoords
@@ -8718,6 +9917,7 @@
     set viewargs(1) $revtreeargs
     set viewargscmd(1) $revtreeargscmd
     set viewperm(1) 0
+    set vdatemode(1) 0
     addviewmenu 1
     .bar.view entryconf [mc "Edit view..."] -state normal
     .bar.view entryconf [mc "Delete view"] -state normal
diff --git a/gitk-git/po/de.po b/gitk-git/po/de.po
index 5ee2fca..b9867bf 100644
--- a/gitk-git/po/de.po
+++ b/gitk-git/po/de.po
@@ -7,249 +7,253 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-01-09 22:20+0100\n"
-"PO-Revision-Date: 2008-01-09 22:21+0100\n"
+"POT-Creation-Date: 2008-05-01 11:54+0200\n"
+"PO-Revision-Date: 2008-05-02 21:12+0200\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"
 
-#: gitk:101
+#: gitk:111
 msgid "Error executing git rev-list:"
 msgstr "Fehler beim Ausführen von git-rev-list:"
 
-#: gitk:114
+#: gitk:124
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:141 gitk:2143
+#: gitk:151 gitk:2191
 msgid "Reading commits..."
 msgstr "Versionen lesen..."
 
-#: gitk:264
+#: gitk:275
 msgid "Can't parse git log output:"
-msgstr "Git log Ausgabe kann nicht erkannt werden:"
+msgstr "Ausgabe von git-log kann nicht erkannt werden:"
 
-#: gitk:375 gitk:2147
+#: gitk:386 gitk:2195
 msgid "No commits selected"
 msgstr "Keine Versionen ausgewählt."
 
-#: gitk:486
+#: gitk:500
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:585 gitk:607 gitk:1908 gitk:6366 gitk:7866 gitk:8020
+#: gitk:599 gitk:621 gitk:1955 gitk:6424 gitk:7924 gitk:8083
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:609 gitk:1909 gitk:6046 gitk:6117 gitk:6218 gitk:6264 gitk:6368
-#: gitk:7867 gitk:8021
+#: gitk:623 gitk:1956 gitk:6108 gitk:6179 gitk:6276 gitk:6322 gitk:6426
+#: gitk:7925 gitk:8084
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:646
+#: gitk:661
 msgid "File"
 msgstr "Datei"
 
-#: gitk:648
+#: gitk:663
 msgid "Update"
 msgstr "Aktualisieren"
 
-#: gitk:649
+#: gitk:664
 msgid "Reread references"
 msgstr "Zweige neu laden"
 
-#: gitk:650
+#: gitk:665
 msgid "List references"
-msgstr "Zweige auflisten"
+msgstr "Zweige/Markierungen auflisten"
 
-#: gitk:651
+#: gitk:666
 msgid "Quit"
 msgstr "Beenden"
 
-#: gitk:653
+#: gitk:668
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: gitk:654
+#: gitk:669
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: gitk:657
+#: gitk:672 gitk:1892
 msgid "View"
 msgstr "Ansicht"
 
-#: gitk:658
+#: gitk:673
 msgid "New view..."
 msgstr "Neue Ansicht..."
 
-#: gitk:659 gitk:2085 gitk:8651
+#: gitk:674 gitk:2133 gitk:8723
 msgid "Edit view..."
 msgstr "Ansicht bearbeiten..."
 
-#: gitk:661 gitk:2086 gitk:8652
+#: gitk:676 gitk:2134 gitk:8724
 msgid "Delete view"
 msgstr "Ansicht löschen"
 
-#: gitk:663
+#: gitk:678
 msgid "All files"
 msgstr "Alle Dateien"
 
-#: gitk:667
+#: gitk:682
 msgid "Help"
 msgstr "Hilfe"
 
-#: gitk:668 gitk:1280
+#: gitk:683 gitk:1317
 msgid "About gitk"
 msgstr "Über gitk"
 
-#: gitk:669
+#: gitk:684
 msgid "Key bindings"
 msgstr "Tastenkürzel"
 
-#: gitk:726
+#: gitk:741
 msgid "SHA1 ID: "
 msgstr "SHA1:"
 
-#: gitk:776
+#: gitk:791
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:777
+#: gitk:792
 msgid "next"
 msgstr "nächste"
 
-#: gitk:778
+#: gitk:793
 msgid "prev"
 msgstr "vorige"
 
-#: gitk:779
+#: gitk:794
 msgid "commit"
 msgstr "Version"
 
-#: gitk:782 gitk:784 gitk:2308 gitk:2331 gitk:2355 gitk:4257 gitk:4320
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
 msgid "containing:"
 msgstr "enthaltend:"
 
-#: gitk:785 gitk:1741 gitk:1746 gitk:2383
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
 msgid "touching paths:"
 msgstr "Pfad betreffend:"
 
-#: gitk:786 gitk:2388
+#: gitk:801 gitk:2436
 msgid "adding/removing string:"
-msgstr "String dazu/löschen:"
+msgstr "Zeichenkette ändernd:"
 
-#: gitk:795 gitk:797
+#: gitk:810 gitk:812
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:797 gitk:2466 gitk:4225
+#: gitk:812 gitk:2514 gitk:4274
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:797 gitk:2357 gitk:2464 gitk:4221
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:799 gitk:800 gitk:2485 gitk:2515 gitk:2522 gitk:4331 gitk:4387
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:800 gitk:2483 gitk:2515 gitk:4287
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:801 gitk:2483 gitk:4287 gitk:4387 gitk:4775
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:801 gitk:2483 gitk:2487 gitk:2522 gitk:4287 gitk:4711 gitk:5895
-#: gitk:5910
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5957
+#: gitk:5972
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:801 gitk:2483 gitk:4287 gitk:4713
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:829
+#: gitk:845
 msgid "Search"
 msgstr "Suche"
 
-#: gitk:836
+#: gitk:852
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:838
+#: gitk:854
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:840
+#: gitk:856
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:842
+#: gitk:858
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:900
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Leerzeichenänderungen ignorieren"
+
+#: gitk:926
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:902
+#: gitk:928
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:1018 gitk:1033 gitk:5961
+#: gitk:1053 gitk:1068 gitk:6023
 msgid "Diff this -> selected"
 msgstr "Vergleich diese -> gewählte"
 
-#: gitk:1020 gitk:1035 gitk:5962
+#: gitk:1055 gitk:1070 gitk:6024
 msgid "Diff selected -> this"
 msgstr "Vergleich gewählte -> diese"
 
-#: gitk:1022 gitk:1037 gitk:5963
+#: gitk:1057 gitk:1072 gitk:6025
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:1023 gitk:6101
+#: gitk:1058 gitk:6163
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:1024 gitk:6198
+#: gitk:1059 gitk:6256
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:1025 gitk:6252
+#: gitk:1060 gitk:6310
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:1026
+#: gitk:1061
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:1028
+#: gitk:1063
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:1044
+#: gitk:1079
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:1046
+#: gitk:1081
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:1052
+#: gitk:1087
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:1054
+#: gitk:1089
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:1281
+#: gitk:1318
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -267,262 +271,425 @@
 "License\n"
 "        "
 
-#: gitk:1289 gitk:1350 gitk:6524
+#: gitk:1326 gitk:1387 gitk:6582
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:1308
+#: gitk:1345
 msgid "Gitk key bindings"
 msgstr "Gitk Tastaturbelegung"
 
-#: gitk:1858
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Gitk Tastaturbelegung:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tBeenden"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Pos1>\t\tZur neuesten Version springen"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<Ende>\t\tZur ältesten Version springen"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Hoch>, p, i\tNächste neuere Version"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Runter>, n, k\tNächste ältere Version"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Links>, z, j\tEine Version zurückgehen"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Rechts>, x, l\tEine Version weitergehen"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<BildHoch>\tEine Seite nach oben blättern"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<BildRunter>\tEine Seite nach unten blättern"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-BildHoch>\tVersionsliste eine Seite hoch blättern"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tVergleich um 18 Zeilen nach oben (»up«) blättern"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tVergleich um 18 Zeilen nach unten (»down«) blättern"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tSuchen"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tWeitersuchen"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Eingabetaste>\tWeitersuchen"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tWeitersuchen oder neue Suche beginnen"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tRückwärts weitersuchen"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tVergleich zur nächsten Datei (»file«) blättern"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-Nummerblock-Plus>\tSchriftgröße vergrößern"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-Plus>\tSchriftgröße vergrößern"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-Nummernblock-> Schriftgröße verkleinern"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-Minus>\tSchriftgröße verkleinern"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAktualisieren"
+
+#: gitk:1896
 msgid "Gitk view definition"
 msgstr "Gitk Ansichten"
 
-#: gitk:1882
+#: gitk:1921
 msgid "Name"
 msgstr "Name"
 
-#: gitk:1885
+#: gitk:1924
 msgid "Remember this view"
 msgstr "Diese Ansicht speichern"
 
-#: gitk:1889
+#: gitk:1928
 msgid "Commits to include (arguments to git rev-list):"
 msgstr "Versionen anzeigen (Argumente von git-rev-list):"
 
-#: gitk:1895
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
+
+#: gitk:1942
 msgid "Enter files and directories to include, one per line:"
 msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
 
-#: gitk:1942
+#: gitk:1989
 msgid "Error in commit selection arguments:"
 msgstr "Fehler in den ausgewählten Versionen:"
 
-#: gitk:1993 gitk:2079 gitk:2535 gitk:2549 gitk:3732 gitk:8620 gitk:8621
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8689 gitk:8690
 msgid "None"
 msgstr "Keine"
 
-#: gitk:2483 gitk:4287 gitk:5897 gitk:5912
+#: gitk:2531 gitk:4336 gitk:5959 gitk:5974
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:2483 gitk:4287
+#: gitk:2531 gitk:4336
 msgid "CDate"
 msgstr "Eintragedatum"
 
-#: gitk:2632 gitk:2637
+#: gitk:2680 gitk:2685
 msgid "Descendant"
 msgstr "Abkömmling"
 
-#: gitk:2633
+#: gitk:2681
 msgid "Not descendant"
 msgstr "Nicht Abkömmling"
 
-#: gitk:2640 gitk:2645
+#: gitk:2688 gitk:2693
 msgid "Ancestor"
 msgstr "Vorgänger"
 
-#: gitk:2641
+#: gitk:2689
 msgid "Not ancestor"
 msgstr "Nicht Vorgänger"
 
-#: gitk:2875
+#: gitk:2924
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
 
-#: gitk:2905
+#: gitk:2954
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokale Änderungen, nicht bereitgestellt"
 
-#: gitk:4256
+#: gitk:4305
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:4715
+#: gitk:4767
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:4732 gitk:4738 gitk:5890
+#: gitk:4784 gitk:4790 gitk:5952
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:4743
+#: gitk:4795
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:4752
+#: gitk:4804
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:4755
+#: gitk:4807
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:4758
+#: gitk:4810
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:5040
+#: gitk:5094
 msgid "Error getting merge diffs:"
 msgstr "Fehler beim Laden des Vergleichs:"
 
-#: gitk:5717
+#: gitk:5779
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:5719
+#: gitk:5781
 msgid "SHA1 ID:"
-msgstr "SHA1 Kennung:"
+msgstr "SHA1-Hashwert:"
 
-#: gitk:5744
+#: gitk:5806
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
-msgstr "Kurze SHA1-Kennung »%s« ist mehrdeutig"
+msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:5756
+#: gitk:5818
 #, tcl-format
 msgid "SHA1 id %s is not known"
-msgstr "SHA1-Kennung »%s« unbekannt"
+msgstr "SHA1-Hashwert »%s« unbekannt"
 
-#: gitk:5758
+#: gitk:5820
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Markierung/Zweig »%s« ist unbekannt"
 
-#: gitk:5900
+#: gitk:5962
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:5957
+#: gitk:6019
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:5988
+#: gitk:6050
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:5989
+#: gitk:6051
 msgid "From"
 msgstr "Von"
 
-#: gitk:5994
+#: gitk:6056
 msgid "To"
 msgstr "bis"
 
-#: gitk:6017
+#: gitk:6079
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:6019
+#: gitk:6081
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:6028
+#: gitk:6090
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:6037
+#: gitk:6099
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:6039 gitk:6212
+#: gitk:6101 gitk:6270
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:6045
+#: gitk:6107
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:6081
+#: gitk:6143
 msgid "Error creating patch:"
 msgstr "Fehler beim Patch erzeugen:"
 
-#: gitk:6103 gitk:6200 gitk:6254
+#: gitk:6165 gitk:6258 gitk:6312
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6112
+#: gitk:6174
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:6116 gitk:6263
+#: gitk:6178 gitk:6321
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:6131
+#: gitk:6193
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:6135
+#: gitk:6197
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:6145
+#: gitk:6203
 msgid "Error creating tag:"
 msgstr "Fehler bei Markierung erstellen:"
 
-#: gitk:6209
+#: gitk:6267
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:6217
+#: gitk:6275
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:6233
+#: gitk:6291
 msgid "Error writing commit:"
-msgstr "Fehler beim Version eintragen:"
+msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:6259
+#: gitk:6317
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:6278
+#: gitk:6336
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:6307
+#: gitk:6365
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut "
 "eintragen?"
 
-#: gitk:6312
+#: gitk:6370
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:6324
+#: gitk:6382
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:6347
+#: gitk:6405
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:6349
+#: gitk:6407
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:6353
+#: gitk:6411
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:6357
+#: gitk:6415
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:6360
+#: gitk:6418
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:6363
+#: gitk:6421
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -530,21 +697,21 @@
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:6379
+#: gitk:6437
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:6436
+#: gitk:6494
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:6466
+#: gitk:6524
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:6472
+#: gitk:6530
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -553,16 +720,16 @@
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:6503
+#: gitk:6561
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:6517
+#: gitk:6575
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:6811
+#: gitk:6869
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -570,113 +737,117 @@
 "Fehler beim Lesen der Strukturinformationen; Zweige und Vorgänger/Nachfolger "
 "Informationen werden unvollständig sein."
 
-#: gitk:7795
+#: gitk:7853
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:7795
+#: gitk:7853
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7835
+#: gitk:7893
 msgid "Gitk font chooser"
 msgstr "Gitk Schriften wählen"
 
-#: gitk:7852
+#: gitk:7910
 msgid "B"
 msgstr "F"
 
-#: gitk:7855
+#: gitk:7913
 msgid "I"
 msgstr "K"
 
-#: gitk:7948
+#: gitk:8006
 msgid "Gitk preferences"
 msgstr "Gitk Einstellungen"
 
-#: gitk:7949
+#: gitk:8007
 msgid "Commit list display options"
 msgstr "Anzeige Versionsliste"
 
-#: gitk:7952
+#: gitk:8010
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:7956
+#: gitk:8014
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:7961
+#: gitk:8019
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:7966
+#: gitk:8024
+msgid "Auto-select SHA1"
+msgstr "SHA1-Hashwert automatisch markieren"
+
+#: gitk:8029
 msgid "Diff display options"
 msgstr "Anzeige Vergleich"
 
-#: gitk:7968
+#: gitk:8031
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:7972
+#: gitk:8035
 msgid "Display nearby tags"
 msgstr "Naheliegende Überschriften anzeigen"
 
-#: gitk:7977
+#: gitk:8040
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:7982
+#: gitk:8045
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:7985
+#: gitk:8048
 msgid "Background"
-msgstr "Vordergrund"
-
-#: gitk:7989
-msgid "Foreground"
 msgstr "Hintergrund"
 
-#: gitk:7993
+#: gitk:8052
+msgid "Foreground"
+msgstr "Vordergrund"
+
+#: gitk:8056
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:7998
+#: gitk:8061
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:8003
+#: gitk:8066
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:8009
+#: gitk:8072
 msgid "Select bg"
 msgstr "Hintergrundfarbe Auswählen"
 
-#: gitk:8013
+#: gitk:8076
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:8015
+#: gitk:8078
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:8016
+#: gitk:8079
 msgid "Diff display font"
 msgstr "Vergleich"
 
-#: gitk:8017
+#: gitk:8080
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:8033
+#: gitk:8096
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:8414
+#: gitk:8477
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -684,35 +855,33 @@
 "Gitk läuft nicht mit dieser Version von Tcl/Tk.\n"
 "Gitk benötigt mindestens Tcl/Tk 8.4."
 
-#: gitk:8501
+#: gitk:8566
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:8505
+#: gitk:8570
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Git-Verzeichnis »%s« wurde nicht gefunden."
 
-#: gitk:8544
+#: gitk:8613
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:8556
+#: gitk:8625
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-#: gitk:8568
+#: gitk:8637
 msgid "Couldn't get list of unmerged files:"
 msgstr "Liste der nicht-zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:8584
+#: gitk:8653
 msgid "No files selected: --merge specified but no files are unmerged."
-msgstr ""
-"Keine Dateien ausgewähle: --merge angegeben, es existieren aber keine nicht-"
-"zusammengeführten Dateien."
+msgstr "Keine Dateien ausgewählt: --merge angegeben, es existieren aber keine nicht-zusammengeführten Dateien."
 
-#: gitk:8587
+#: gitk:8656
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -720,6 +889,6 @@
 "Keine Dateien ausgewähle: --merge angegeben, aber keine nicht-"
 "zusammengeführten Dateien sind in der Dateiauswahl."
 
-#: gitk:8646
+#: gitk:8717
 msgid "Command line"
 msgstr "Kommandozeile"
diff --git a/gitk-git/po/es.po b/gitk-git/po/es.po
new file mode 100644
index 0000000..2cb1486
--- /dev/null
+++ b/gitk-git/po/es.po
@@ -0,0 +1,890 @@
+# Translation of gitk
+# Copyright (C) 2005-2008 Santiago Gala
+# This file is distributed under the same license as the gitk package.
+# Santiago Gala <santiago.gala@gmail.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-25 11:20+0100\n"
+"Last-Translator: Santiago Gala <santiago.gala@gmail.com>\n"
+"Language-Team: Spanish\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 "Error al ejecutar git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Leyendo"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Leyendo revisiones..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Error analizando la salida de git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "No se seleccionaron revisiones"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Falta información sobre las revisiones"
+
+#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+msgid "OK"
+msgstr "Aceptar"
+
+#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
+#: gitk:7924 gitk:8083
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: gitk:661
+msgid "File"
+msgstr "Archivo"
+
+#: gitk:663
+msgid "Update"
+msgstr "Actualizar"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Releer referencias"
+
+#: gitk:665
+msgid "List references"
+msgstr "Lista de referencias"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Salir"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Editar"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Preferencias"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Vista"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Nueva vista..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Modificar vista..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Eliminar vista"
+
+#: gitk:678
+msgid "All files"
+msgstr "Todos los archivos"
+
+#: gitk:682
+msgid "Help"
+msgstr "Ayuda"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Acerca de gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Combinaciones de teclas"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Buscar"
+
+#: gitk:792
+msgid "next"
+msgstr "<<"
+
+#: gitk:793
+msgid "prev"
+msgstr ">>"
+
+#: gitk:794
+msgid "commit"
+msgstr "revisión"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "que contiene:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "que modifica la ruta:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "que añade/elimina cadena:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Exacto"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr "NoMayús"
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr "Regex"
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Todos los campos"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Título"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Comentarios"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Autor"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr ""
+
+#: gitk:845
+msgid "Search"
+msgstr "Buscar"
+
+#: gitk:852
+msgid "Diff"
+msgstr "Diferencia"
+
+#: gitk:854
+msgid "Old version"
+msgstr "Versión antigua"
+
+#: gitk:856
+msgid "New version"
+msgstr "Versión nueva"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Líneas de contexto"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignora cambios de espaciado"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Parche"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Árbol"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diferencia de esta -> seleccionada"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diferencia de seleccionada -> esta"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Crear patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Crear etiqueta"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Escribir revisiones a archivo"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Crear nueva rama"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Añadir esta revisión a la rama actual (cherry-pick)"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Traer la rama HEAD aquí"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Cambiar a esta rama"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Eliminar esta rama"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Seleccionar también"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Seleccionar sólo"
+
+#: 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 visualizador de revisiones para git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Uso y redistribución permitidos según los términos de la Licencia Pública General de "
+"GNU (GNU GPL)"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Cerrar"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Combinaciones de tecla de Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Combinaciones de tecla de Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tSalir"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tIr a la primera revisión"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tIr a la última revisión"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tSubir una revisión"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tBajar una revisión"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tRetroceder en la historia"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tAvanzar en la historia"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tSubir una página en la lista de revisiones"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tBajar una página en la lista de revisiones"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tDesplazarse al inicio de la lista de revisiones"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tDesplazarse al final de la lista de revisiones"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tDesplazar una línea hacia arriba la lista de revisiones"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tDesplazar una línea hacia abajo la lista de revisiones"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tDesplazar una página hacia arriba la lista de revisiones"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tDesplazar una página hacia abajo la lista de revisiones"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tBuscar hacia atrás (arriba, revisiones siguientes)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tBuscar hacia adelante (abajo, revisiones anteriores)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tDesplaza hacia arriba una página la vista de diferencias"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tDesplaza hacia arriba una página la vista de diferencias"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tDesplaza hacia abajo una página la vista de diferencias"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tDesplaza hacia arriba 18 líneas la vista de diferencias"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tDesplaza hacia abajo 18 líneas la vista de diferencias"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tBuscar"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tBuscar el siguiente"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tBuscar el siguiente"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tBuscar el siguiente, o reiniciar la búsqueda"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tBuscar el anterior"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tBuscar siguiente en la vista de diferencias"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tBuscar anterior en la vista de diferencias"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumentar tamaño del texto"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumentar tamaño del texto"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDisminuir tamaño del texto"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tDisminuir tamaño del texto"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tActualizar"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Definición de vistas de Gitk"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Nombre"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Recordar esta vista"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Revisiones a incluir (argumentos a git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Comando que genera más revisiones a incluir:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Introducir archivos y directorios a incluir, uno por línea:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Error en los argumentos de selección de las revisiones:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Ninguno"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Fecha"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr "Fecha de creación"
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Descendiente"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "No descendiente"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Antepasado"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "No antepasado"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Cambios locales añadidos al índice pero sin completar revisión"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Cambios locales sin añadir al índice"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Buscando"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Etiquetas:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Padre"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Hija"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Rama"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Sigue-a"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Precede-a"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Error al leer las diferencias de fusión:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Ir a:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "La id SHA1 abreviada %s es ambigua"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "La id SHA1 %s es desconocida"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "La etiqueta/rama %s es deconocida"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Hijas"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Poner la rama %s en esta revisión"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Origen"
+
+#: gitk:6050
+msgid "From"
+msgstr "De"
+
+#: gitk:6055
+msgid "To"
+msgstr "A"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Generar parche"
+
+#: gitk:6080
+msgid "From:"
+msgstr "De:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "Para:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Invertir"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Escribir a archivo:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Generar"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Error en la creación del parche:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Nombre de etiqueta:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Crear"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "No se ha especificado etiqueta"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "La etiqueta \"%s\" ya existe"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Error al crear la etiqueta:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Escribir"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Error al escribir revisión:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Nombre:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Especifique un nombre para la nueva rama"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "La revisión %s ya está incluida en la rama %s -- ¿Volver a aplicarla?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr "Eligiendo revisiones (cherry-picking)"
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "No se han guardado cambios"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Confirmar git reset"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "¿Reponer la rama %s a %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Tipo de reposición:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Suave: No altera la copia de trabajo ni el índice"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Mixta: Actualiza el índice, no altera la copia de trabajo"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Dura: Actualiza el índice y la copia de trabajo\n"
+"(abandona TODAS las modificaciones locales)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "Reponiendo"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Creando copia de trabajo"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "No se puede borrar la rama actual"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Las revisiones de la rama %s no están presentes en otras ramas.\n"
+"¿Borrar la rama %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Etiquetas y ramas: %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 ""
+"Error al leer la topología de revisiones: la información sobre "
+"las ramas y etiquetas precedentes y siguientes será incompleta."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Etiqueta"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Selector de tipografías gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "B"
+
+#: gitk:7912
+msgid "I"
+msgstr "I"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Preferencias de gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Opciones de visualización de la lista de revisiones"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Ancho máximo del gráfico (en líneas)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Ancho máximo del gráfico (en % del panel)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Mostrar cambios locales"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Seleccionar automáticamente SHA1 hash"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Opciones de visualización de diferencias"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Espaciado de tabulador"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Mostrar etiquetas cercanas"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Limitar las diferencias a las rutas seleccionadas"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Colores: pulse para seleccionar"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Fondo"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Primer plano"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: líneas viejas"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: líneas nuevas"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: cabecera de fragmento"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Color de fondo de la selección"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Tipografías: pulse para elegir"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Tipografía principal"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Tipografía para diferencias"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Tipografía para interfaz de usuario"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: elegir color para %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Esta versión de Tcl/Tk es demasiado antigua.\n"
+" Gitk requiere Tcl/Tk versión 8.4 o superior."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "No hay un repositorio git aquí."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "No hay directorio git \"%s\"."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Argumentos incorrectos a Gitk:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Imposible obtener la lista de archivos pendientes de fusión:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"No hay archivos seleccionados: se seleccionó la opción --merge pero no hay "
+"archivos pendientes de fusión."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"No hay archivos seleccionados: se seleccionó la opción --merge pero los archivos "
+"especificados no necesitan fusión."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Línea de comandos"
diff --git a/gitk-git/po/sv.po b/gitk-git/po/sv.po
new file mode 100644
index 0000000..f6b080d
--- /dev/null
+++ b/gitk-git/po/sv.po
@@ -0,0 +1,887 @@
+# Swedish translation for gitk
+# Copyright (C) 2005-2008 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+#
+# Peter Karlsson <peter@softwolves.pp.se>, 2008.
+msgid ""
+msgstr ""
+"Project-Id-Version: sv\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-14 15:03+0100\n"
+"PO-Revision-Date: 2008-03-14 16:06CET-1\n"
+"Last-Translator: Peter Karlsson <peter@softwolves.pp.se>\n"
+"Language-Team: Swedish <sv@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit"
+
+#: gitk:111
+msgid "Error executing git rev-list:"
+msgstr "Fel vid körning av git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Läser"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Läser incheckningar..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Kan inte tolka utdata från git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "Inga incheckningar markerade"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Ingen incheckningsinformation är tillgänglig"
+
+#: 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 "Avbryt"
+
+#: gitk:661
+msgid "File"
+msgstr "Arkiv"
+
+#: gitk:663
+msgid "Update"
+msgstr "Uppdatera"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Läs om referenser"
+
+#: gitk:665
+msgid "List references"
+msgstr "Visa referenser"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Avsluta"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Redigera"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Inställningar"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Visa"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Ny vy..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Ändra vy..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Ta bort vy"
+
+#: gitk:678
+msgid "All files"
+msgstr "Alla filer"
+
+#: gitk:682
+msgid "Help"
+msgstr "Hjälp"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Om gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Tangentbordsbindningar"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1-id: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Sök"
+
+#: gitk:792
+msgid "next"
+msgstr "nästa"
+
+#: gitk:793
+msgid "prev"
+msgstr "föreg"
+
+#: gitk:794
+msgid "commit"
+msgstr "incheckning"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "som innehåller:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "som rör sökväg:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "som lägger/till tar bort sträng:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Exakt"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr "IgnVersaler"
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr "Reg.uttr."
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Alla fält"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Rubrik"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Kommentarer"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Författare"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr "Incheckare"
+
+#: gitk:845
+msgid "Search"
+msgstr "Sök"
+
+#: gitk:852
+msgid "Diff"
+msgstr "Diff"
+
+#: gitk:854
+msgid "Old version"
+msgstr "Gammal version"
+
+#: gitk:856
+msgid "New version"
+msgstr "Ny version"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Rader sammanhang"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignorera ändringar i blanksteg"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Patch"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Träd"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diff denna -> markerad"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diff markerad -> denna"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Skapa patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Skapa tagg"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Skriv incheckning till fil"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Skapa ny gren"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Plocka denna incheckning"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Återställ HEAD-grenen hit"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Checka ut denna gren"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Ta bort denna gren"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Markera även detta"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Markera bara detta"
+
+#: 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 - en incheckningsvisare för git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Använd och vidareförmedla enligt villkoren i GNU General Public License"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Stäng"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Tangentbordsbindningar för Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Tangentbordsbindningar för Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tAvsluta"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tGå till första incheckning"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tGå till sista incheckning"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Upp>, p, i\tGå en incheckning upp"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Ned>, n, k\tGå en incheckning ned"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Vänster>, z, j\tGå bakåt i historiken"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Höger>, x, l\tGå framåt i historiken"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tRulla till början av incheckningslistan"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tRulla diffvisningen upp 18 rader"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tRulla diffvisningen ned 18 rader"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tSök"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tGå till nästa sökträff"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\t\tGå till nästa sökträff"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tGå till föregående sökträff"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tRulla diffvisningen till nästa fil"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-Num+>\tÖka teckenstorlek"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tÖka teckenstorlek"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-Num->\tMinska teckenstorlek"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tMinska teckenstorlek"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tUppdatera"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Definition av Gitk-vy"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Namn"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Spara denna vy"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Incheckningar att ta med (argument till git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Kommando för att generera fler incheckningar att ta med:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Ange filer och kataloger att ta med, en per rad:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Fel i argument för val av incheckningar:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Inget"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Datum"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr "Skapat datum"
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Avkomling"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "Inte avkomling"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Förfader"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "Inte förfader"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Lokala ändringar sparade i indexet men inte incheckade"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Lokala ändringar, ej sparade i indexet"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Söker"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Taggar:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Förälder"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Barn"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Gren"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Följer"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Föregår"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Fel vid hämtning av sammanslagningsdiff:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Gå till:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1-id:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Förkortat SHA1-id %s är tvetydigt"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "SHA-id:t %s är inte känt"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "Tagg/huvud %s är okänt"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Barn"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Återställ grenen %s hit"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Topp"
+
+#: gitk:6050
+msgid "From"
+msgstr "Från"
+
+#: gitk:6055
+msgid "To"
+msgstr "Till"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Generera patch"
+
+#: gitk:6080
+msgid "From:"
+msgstr "Från:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "Till:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Vänd"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Utdatafil:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Generera"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Fel vid generering av patch:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "Id:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Taggnamn:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Skapa"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "Inget taggnamn angavs"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Taggen \"%s\" finns redan"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Fel vid skapande av tagg:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Kommando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Skriv"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Fel vid skrivning av incheckning:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Namn:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Ange ett namn för den nya grenen"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras på nytt?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr "Plockar"
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "Inga ändringar incheckade"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Bekräfta återställning"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Återställa grenen %s till %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Typ av återställning:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Mjuk: Rör inte utcheckning och index"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Blandad: Rör inte utcheckning, återställ index"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hård: Återställ utcheckning och index\n"
+"(förkastar ALLA lokala ändringar)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "Återställer"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Checkar ut"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Kan inte ta bort den just nu utcheckade grenen"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
+"Vill du verkligen ta bort grenen %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Taggar och huvuden: %s"
+
+#: gitk:6574
+msgid "Filter"
+msgstr "Filter"
+
+#: gitk:6868
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Fel vid läsning av information om incheckningstopologi; information om "
+"grenar och föregående/senare taggar kommer inte vara komplett."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Tagg"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Teckensnittsväljare för Gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "F"
+
+#: gitk:7912
+msgid "I"
+msgstr "K"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Inställningar för Gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Alternativ för incheckningslistvy"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Maximal grafbredd (rader)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Maximal grafbredd (% av ruta)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Visa lokala ändringar"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Välj SHA1 automatiskt"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Alternativ för diffvy"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Blanksteg för tabulatortecken"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Visa närliggande taggar"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Begränsa diff till listade sökvägar"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Färger: tryck för att välja"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Bakgrund"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Förgrund"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: gamla rader"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: nya rader"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: delhuvud"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Markerad bakgrund"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Teckensnitt: tryck för att välja"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Huvudteckensnitt"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Teckensnitt för diffvisning"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Teckensnitt för användargränssnitt"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: välj färg för %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
+" Gitk kräver åtminstone Tcl/Tk 8.4."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "Hittar inget gitk-arkiv här."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Hittar inte git-katalogen \"%s\"."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Felaktiga argument till gitk:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Kunde inta hämta lista över ej sammanslagna filer:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr "Inga filer valdes: --merge angavs men det finns inga filer som inte har slagits samman."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Inga filer valdes: --merge angavs men det finns inga filer inom "
+"filbegränsningen."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Kommandorad"
+
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2facf2d..8308e22 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -866,6 +866,10 @@
 	my $add_len = shift || 10;
 	my $where = shift || 'right'; # 'left' | 'center' | 'right'
 
+	# Make sure perl knows it is utf8 encoded so we don't
+	# cut in the middle of a utf8 multibyte char.
+	$str = to_utf8($str);
+
 	# 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
 	# remove chopped character entities entirely
diff --git a/graph.c b/graph.c
new file mode 100644
index 0000000..26b8c52
--- /dev/null
+++ b/graph.c
@@ -0,0 +1,1025 @@
+#include "cache.h"
+#include "commit.h"
+#include "graph.h"
+#include "diff.h"
+#include "revision.h"
+
+/*
+ * TODO:
+ * - Add colors to the graph.
+ *   Pick a color for each column, and print all characters
+ *   in that column with the specified color.
+ *
+ * - Limit the number of columns, similar to the way gitk does.
+ *   If we reach more than a specified number of columns, omit
+ *   sections of some columns.
+ *
+ * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states
+ *   could be made more compact by printing horizontal lines, instead of
+ *   long diagonal lines.  For example, during collapsing, something like
+ *   this:          instead of this:
+ *   | | | | |      | | | | |
+ *   | |_|_|/       | | | |/
+ *   |/| | |        | | |/|
+ *   | | | |        | |/| |
+ *                  |/| | |
+ *                  | | | |
+ *
+ *   If there are several parallel diagonal lines, they will need to be
+ *   replaced with horizontal lines on subsequent rows.
+ */
+
+struct column {
+	/*
+	 * The parent commit of this column.
+	 */
+	struct commit *commit;
+	/*
+	 * XXX: Once we add support for colors, struct column could also
+	 * contain the color of its branch line.
+	 */
+};
+
+enum graph_state {
+	GRAPH_PADDING,
+	GRAPH_SKIP,
+	GRAPH_PRE_COMMIT,
+	GRAPH_COMMIT,
+	GRAPH_POST_MERGE,
+	GRAPH_COLLAPSING
+};
+
+struct git_graph {
+	/*
+	 * The commit currently being processed
+	 */
+	struct commit *commit;
+	/* The rev-info used for the current traversal */
+	struct rev_info *revs;
+	/*
+	 * The number of interesting parents that this commit has.
+	 *
+	 * Note that this is not the same as the actual number of parents.
+	 * This count excludes parents that won't be printed in the graph
+	 * output, as determined by graph_is_interesting().
+	 */
+	int num_parents;
+	/*
+	 * The width of the graph output for this commit.
+	 * All rows for this commit are padded to this width, so that
+	 * messages printed after the graph output are aligned.
+	 */
+	int width;
+	/*
+	 * The next expansion row to print
+	 * when state is GRAPH_PRE_COMMIT
+	 */
+	int expansion_row;
+	/*
+	 * The current output state.
+	 * This tells us what kind of line graph_next_line() should output.
+	 */
+	enum graph_state state;
+	/*
+	 * The maximum number of columns that can be stored in the columns
+	 * and new_columns arrays.  This is also half the number of entries
+	 * that can be stored in the mapping and new_mapping arrays.
+	 */
+	int column_capacity;
+	/*
+	 * The number of columns (also called "branch lines" in some places)
+	 */
+	int num_columns;
+	/*
+	 * The number of columns in the new_columns array
+	 */
+	int num_new_columns;
+	/*
+	 * The number of entries in the mapping array
+	 */
+	int mapping_size;
+	/*
+	 * The column state before we output the current commit.
+	 */
+	struct column *columns;
+	/*
+	 * The new column state after we output the current commit.
+	 * Only valid when state is GRAPH_COLLAPSING.
+	 */
+	struct column *new_columns;
+	/*
+	 * An array that tracks the current state of each
+	 * character in the output line during state GRAPH_COLLAPSING.
+	 * Each entry is -1 if this character is empty, or a non-negative
+	 * integer if the character contains a branch line.  The value of
+	 * the integer indicates the target position for this branch line.
+	 * (I.e., this array maps the current column positions to their
+	 * desired positions.)
+	 *
+	 * The maximum capacity of this array is always
+	 * sizeof(int) * 2 * column_capacity.
+	 */
+	int *mapping;
+	/*
+	 * A temporary array for computing the next mapping state
+	 * while we are outputting a mapping line.  This is stored as part
+	 * of the git_graph simply so we don't have to allocate a new
+	 * temporary array each time we have to output a collapsing line.
+	 */
+	int *new_mapping;
+};
+
+struct git_graph *graph_init(struct rev_info *opt)
+{
+	struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+	graph->commit = NULL;
+	graph->revs = opt;
+	graph->num_parents = 0;
+	graph->expansion_row = 0;
+	graph->state = GRAPH_PADDING;
+	graph->num_columns = 0;
+	graph->num_new_columns = 0;
+	graph->mapping_size = 0;
+
+	/*
+	 * Allocate a reasonably large default number of columns
+	 * We'll automatically grow columns later if we need more room.
+	 */
+	graph->column_capacity = 30;
+	graph->columns = xmalloc(sizeof(struct column) *
+				 graph->column_capacity);
+	graph->new_columns = xmalloc(sizeof(struct column) *
+				     graph->column_capacity);
+	graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
+	graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
+
+	return graph;
+}
+
+void graph_release(struct git_graph *graph)
+{
+	free(graph->columns);
+	free(graph->new_columns);
+	free(graph->mapping);
+	free(graph);
+}
+
+static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
+{
+	if (graph->column_capacity >= num_columns)
+		return;
+
+	do {
+		graph->column_capacity *= 2;
+	} while (graph->column_capacity < num_columns);
+
+	graph->columns = xrealloc(graph->columns,
+				  sizeof(struct column) *
+				  graph->column_capacity);
+	graph->new_columns = xrealloc(graph->new_columns,
+				      sizeof(struct column) *
+				      graph->column_capacity);
+	graph->mapping = xrealloc(graph->mapping,
+				  sizeof(int) * 2 * graph->column_capacity);
+	graph->new_mapping = xrealloc(graph->new_mapping,
+				      sizeof(int) * 2 * graph->column_capacity);
+}
+
+/*
+ * Returns 1 if the commit will be printed in the graph output,
+ * and 0 otherwise.
+ */
+static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
+{
+	/*
+	 * If revs->boundary is set, commits whose children have
+	 * been shown are always interesting, even if they have the
+	 * UNINTERESTING or TREESAME flags set.
+	 */
+	if (graph->revs && graph->revs->boundary) {
+		if (commit->object.flags & CHILD_SHOWN)
+			return 1;
+	}
+
+	/*
+	 * Uninteresting and pruned commits won't be printed
+	 */
+	return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1;
+}
+
+static void graph_insert_into_new_columns(struct git_graph *graph,
+					  struct commit *commit,
+					  int *mapping_index)
+{
+	int i;
+
+	/*
+	 * Ignore uinteresting commits
+	 */
+	if (!graph_is_interesting(graph, commit))
+		return;
+
+	/*
+	 * If the commit is already in the new_columns list, we don't need to
+	 * add it.  Just update the mapping correctly.
+	 */
+	for (i = 0; i < graph->num_new_columns; i++) {
+		if (graph->new_columns[i].commit == commit) {
+			graph->mapping[*mapping_index] = i;
+			*mapping_index += 2;
+			return;
+		}
+	}
+
+	/*
+	 * This commit isn't already in new_columns.  Add it.
+	 */
+	graph->new_columns[graph->num_new_columns].commit = commit;
+	graph->mapping[*mapping_index] = graph->num_new_columns;
+	*mapping_index += 2;
+	graph->num_new_columns++;
+}
+
+static void graph_update_width(struct git_graph *graph,
+			       int is_commit_in_existing_columns)
+{
+	/*
+	 * Compute the width needed to display the graph for this commit.
+	 * This is the maximum width needed for any row.  All other rows
+	 * will be padded to this width.
+	 *
+	 * Compute the number of columns in the widest row:
+	 * Count each existing column (graph->num_columns), and each new
+	 * column added by this commit.
+	 */
+	int max_cols = graph->num_columns + graph->num_parents;
+
+	/*
+	 * Even if the current commit has no parents to be printed, it
+	 * still takes up a column for itself.
+	 */
+	if (graph->num_parents < 1)
+		max_cols++;
+
+	/*
+	 * We added a column for the the current commit as part of
+	 * graph->num_parents.  If the current commit was already in
+	 * graph->columns, then we have double counted it.
+	 */
+	if (is_commit_in_existing_columns)
+		max_cols--;
+
+	/*
+	 * Each column takes up 2 spaces
+	 */
+	graph->width = max_cols * 2;
+}
+
+static void graph_update_columns(struct git_graph *graph)
+{
+	struct commit_list *parent;
+	struct column *tmp_columns;
+	int max_new_columns;
+	int mapping_idx;
+	int i, seen_this, is_commit_in_columns;
+
+	/*
+	 * Swap graph->columns with graph->new_columns
+	 * graph->columns contains the state for the previous commit,
+	 * and new_columns now contains the state for our commit.
+	 *
+	 * We'll re-use the old columns array as storage to compute the new
+	 * columns list for the commit after this one.
+	 */
+	tmp_columns = graph->columns;
+	graph->columns = graph->new_columns;
+	graph->num_columns = graph->num_new_columns;
+
+	graph->new_columns = tmp_columns;
+	graph->num_new_columns = 0;
+
+	/*
+	 * Now update new_columns and mapping with the information for the
+	 * commit after this one.
+	 *
+	 * First, make sure we have enough room.  At most, there will
+	 * be graph->num_columns + graph->num_parents columns for the next
+	 * commit.
+	 */
+	max_new_columns = graph->num_columns + graph->num_parents;
+	graph_ensure_capacity(graph, max_new_columns);
+
+	/*
+	 * Clear out graph->mapping
+	 */
+	graph->mapping_size = 2 * max_new_columns;
+	for (i = 0; i < graph->mapping_size; i++)
+		graph->mapping[i] = -1;
+
+	/*
+	 * Populate graph->new_columns and graph->mapping
+	 *
+	 * Some of the parents of this commit may already be in
+	 * graph->columns.  If so, graph->new_columns should only contain a
+	 * single entry for each such commit.  graph->mapping should
+	 * contain information about where each current branch line is
+	 * supposed to end up after the collapsing is performed.
+	 */
+	seen_this = 0;
+	mapping_idx = 0;
+	is_commit_in_columns = 1;
+	for (i = 0; i <= graph->num_columns; i++) {
+		struct commit *col_commit;
+		if (i == graph->num_columns) {
+			if (seen_this)
+				break;
+			is_commit_in_columns = 0;
+			col_commit = graph->commit;
+		} else {
+			col_commit = graph->columns[i].commit;
+		}
+
+		if (col_commit == graph->commit) {
+			int old_mapping_idx = mapping_idx;
+			seen_this = 1;
+			for (parent = graph->commit->parents;
+			     parent;
+			     parent = parent->next) {
+				graph_insert_into_new_columns(graph,
+							      parent->item,
+							      &mapping_idx);
+			}
+			/*
+			 * We always need to increment mapping_idx by at
+			 * least 2, even if it has no interesting parents.
+			 * The current commit always takes up at least 2
+			 * spaces.
+			 */
+			if (mapping_idx == old_mapping_idx)
+				mapping_idx += 2;
+		} else {
+			graph_insert_into_new_columns(graph, col_commit,
+						      &mapping_idx);
+		}
+	}
+
+	/*
+	 * Shrink mapping_size to be the minimum necessary
+	 */
+	while (graph->mapping_size > 1 &&
+	       graph->mapping[graph->mapping_size - 1] < 0)
+		graph->mapping_size--;
+
+	/*
+	 * Compute graph->width for this commit
+	 */
+	graph_update_width(graph, is_commit_in_columns);
+}
+
+void graph_update(struct git_graph *graph, struct commit *commit)
+{
+	struct commit_list *parent;
+
+	/*
+	 * Set the new commit
+	 */
+	graph->commit = commit;
+
+	/*
+	 * Count how many interesting parents this commit has
+	 */
+	graph->num_parents = 0;
+	for (parent = commit->parents; parent; parent = parent->next) {
+		if (graph_is_interesting(graph, parent->item))
+			graph->num_parents++;
+	}
+
+	/*
+	 * Call graph_update_columns() to update
+	 * columns, new_columns, and mapping.
+	 */
+	graph_update_columns(graph);
+
+	graph->expansion_row = 0;
+
+	/*
+	 * Update graph->state.
+	 *
+	 * If the previous commit didn't get to the GRAPH_PADDING state,
+	 * it never finished its output.  Goto GRAPH_SKIP, to print out
+	 * a line to indicate that portion of the graph is missing.
+	 *
+	 * Otherwise, if there are 3 or more parents, we need to print
+	 * extra rows before the commit, to expand the branch lines around
+	 * it and make room for it.
+	 *
+	 * If there are less than 3 parents, we can immediately print the
+	 * commit line.
+	 */
+	if (graph->state != GRAPH_PADDING)
+		graph->state = GRAPH_SKIP;
+	else if (graph->num_parents >= 3)
+		graph->state = GRAPH_PRE_COMMIT;
+	else
+		graph->state = GRAPH_COMMIT;
+}
+
+static int graph_is_mapping_correct(struct git_graph *graph)
+{
+	int i;
+
+	/*
+	 * The mapping is up to date if each entry is at its target,
+	 * or is 1 greater than its target.
+	 * (If it is 1 greater than the target, '/' will be printed, so it
+	 * will look correct on the next row.)
+	 */
+	for (i = 0; i < graph->mapping_size; i++) {
+		int target = graph->mapping[i];
+		if (target < 0)
+			continue;
+		if (target == (i / 2))
+			continue;
+		return 0;
+	}
+
+	return 1;
+}
+
+static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
+{
+	/*
+	 * Add additional spaces to the end of the strbuf, so that all
+	 * lines for a particular commit have the same width.
+	 *
+	 * This way, fields printed to the right of the graph will remain
+	 * aligned for the entire commit.
+	 */
+	int extra;
+	if (sb->len >= graph->width)
+		return;
+
+	extra = graph->width - sb->len;
+	strbuf_addf(sb, "%*s", (int) extra, "");
+}
+
+static void graph_output_padding_line(struct git_graph *graph,
+				      struct strbuf *sb)
+{
+	int i;
+
+	/*
+	 * We could conceivable be called with a NULL commit
+	 * if our caller has a bug, and invokes graph_next_line()
+	 * immediately after graph_init(), without first calling
+	 * graph_update().  Return without outputting anything in this
+	 * case.
+	 */
+	if (!graph->commit)
+		return;
+
+	/*
+	 * Output a padding row, that leaves all branch lines unchanged
+	 */
+	for (i = 0; i < graph->num_new_columns; i++) {
+		strbuf_addstr(sb, "| ");
+	}
+
+	graph_pad_horizontally(graph, sb);
+}
+
+static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
+{
+	/*
+	 * Output an ellipsis to indicate that a portion
+	 * of the graph is missing.
+	 */
+	strbuf_addstr(sb, "...");
+	graph_pad_horizontally(graph, sb);
+
+	if (graph->num_parents >= 3)
+		graph->state = GRAPH_PRE_COMMIT;
+	else
+		graph->state = GRAPH_COMMIT;
+}
+
+static void graph_output_pre_commit_line(struct git_graph *graph,
+					 struct strbuf *sb)
+{
+	int num_expansion_rows;
+	int i, seen_this;
+
+	/*
+	 * This function formats a row that increases the space around a commit
+	 * with multiple parents, to make room for it.  It should only be
+	 * called when there are 3 or more parents.
+	 *
+	 * We need 2 extra rows for every parent over 2.
+	 */
+	assert(graph->num_parents >= 3);
+	num_expansion_rows = (graph->num_parents - 2) * 2;
+
+	/*
+	 * graph->expansion_row tracks the current expansion row we are on.
+	 * It should be in the range [0, num_expansion_rows - 1]
+	 */
+	assert(0 <= graph->expansion_row &&
+	       graph->expansion_row < num_expansion_rows);
+
+	/*
+	 * Output the row
+	 */
+	seen_this = 0;
+	for (i = 0; i < graph->num_columns; i++) {
+		struct column *col = &graph->columns[i];
+		if (col->commit == graph->commit) {
+			seen_this = 1;
+			strbuf_addf(sb, "| %*s", graph->expansion_row, "");
+		} else if (seen_this) {
+			strbuf_addstr(sb, "\\ ");
+		} else {
+			strbuf_addstr(sb, "| ");
+		}
+	}
+
+	graph_pad_horizontally(graph, sb);
+
+	/*
+	 * Increment graph->expansion_row,
+	 * and move to state GRAPH_COMMIT if necessary
+	 */
+	graph->expansion_row++;
+	if (graph->expansion_row >= num_expansion_rows)
+		graph->state = GRAPH_COMMIT;
+}
+
+static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
+{
+	/*
+	 * For boundary commits, print 'o'
+	 * (We should only see boundary commits when revs->boundary is set.)
+	 */
+	if (graph->commit->object.flags & BOUNDARY) {
+		assert(graph->revs->boundary);
+		strbuf_addch(sb, 'o');
+		return;
+	}
+
+	/*
+	 * If revs->left_right is set, print '<' for commits that
+	 * come from the left side, and '>' for commits from the right
+	 * side.
+	 */
+	if (graph->revs && graph->revs->left_right) {
+		if (graph->commit->object.flags & SYMMETRIC_LEFT)
+			strbuf_addch(sb, '<');
+		else
+			strbuf_addch(sb, '>');
+		return;
+	}
+
+	/*
+	 * Print 'M' for merge commits
+	 *
+	 * Note that we don't check graph->num_parents to determine if the
+	 * commit is a merge, since that only tracks the number of
+	 * "interesting" parents.  We want to print 'M' for merge commits
+	 * even if they have less than 2 interesting parents.
+	 */
+	if (graph->commit->parents != NULL &&
+	    graph->commit->parents->next != NULL) {
+		strbuf_addch(sb, 'M');
+		return;
+	}
+
+	/*
+	 * Print '*' in all other cases
+	 */
+	strbuf_addch(sb, '*');
+}
+
+void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
+{
+	int seen_this = 0;
+	int i, j;
+
+	/*
+	 * Output the row containing this commit
+	 * Iterate up to and including graph->num_columns,
+	 * since the current commit may not be in any of the existing
+	 * columns.  (This happens when the current commit doesn't have any
+	 * children that we have already processed.)
+	 */
+	seen_this = 0;
+	for (i = 0; i <= graph->num_columns; i++) {
+		struct commit *col_commit;
+		if (i == graph->num_columns) {
+			if (seen_this)
+				break;
+			col_commit = graph->commit;
+		} else {
+			col_commit = graph->columns[i].commit;
+		}
+
+		if (col_commit == graph->commit) {
+			seen_this = 1;
+			graph_output_commit_char(graph, sb);
+
+			if (graph->num_parents < 2)
+				strbuf_addch(sb, ' ');
+			else if (graph->num_parents == 2)
+				strbuf_addstr(sb, "  ");
+			else {
+				int num_dashes =
+					((graph->num_parents - 2) * 2) - 1;
+				for (j = 0; j < num_dashes; j++)
+					strbuf_addch(sb, '-');
+				strbuf_addstr(sb, ". ");
+			}
+		} else if (seen_this && (graph->num_parents > 1)) {
+			strbuf_addstr(sb, "\\ ");
+		} else {
+			strbuf_addstr(sb, "| ");
+		}
+	}
+
+	graph_pad_horizontally(graph, sb);
+
+	/*
+	 * Update graph->state
+	 */
+	if (graph->num_parents > 1)
+		graph->state = GRAPH_POST_MERGE;
+	else if (graph_is_mapping_correct(graph))
+		graph->state = GRAPH_PADDING;
+	else
+		graph->state = GRAPH_COLLAPSING;
+}
+
+void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
+{
+	int seen_this = 0;
+	int i, j;
+
+	/*
+	 * Output the post-merge row
+	 */
+	for (i = 0; i <= graph->num_columns; i++) {
+		struct commit *col_commit;
+		if (i == graph->num_columns) {
+			if (seen_this)
+				break;
+			col_commit = graph->commit;
+		} else {
+			col_commit = graph->columns[i].commit;
+		}
+
+		if (col_commit == graph->commit) {
+			seen_this = 1;
+			strbuf_addch(sb, '|');
+			for (j = 0; j < graph->num_parents - 1; j++)
+				strbuf_addstr(sb, "\\ ");
+			if (graph->num_parents == 2)
+				strbuf_addch(sb, ' ');
+		} else if (seen_this && (graph->num_parents > 2)) {
+			strbuf_addstr(sb, "\\ ");
+		} else {
+			strbuf_addstr(sb, "| ");
+		}
+	}
+
+	graph_pad_horizontally(graph, sb);
+
+	/*
+	 * Update graph->state
+	 */
+	if (graph_is_mapping_correct(graph))
+		graph->state = GRAPH_PADDING;
+	else
+		graph->state = GRAPH_COLLAPSING;
+}
+
+void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
+{
+	int i;
+	int *tmp_mapping;
+
+	/*
+	 * Clear out the new_mapping array
+	 */
+	for (i = 0; i < graph->mapping_size; i++)
+		graph->new_mapping[i] = -1;
+
+	for (i = 0; i < graph->mapping_size; i++) {
+		int target = graph->mapping[i];
+		if (target < 0)
+			continue;
+
+		/*
+		 * Since update_columns() always inserts the leftmost
+		 * column first, each branch's target location should
+		 * always be either its current location or to the left of
+		 * its current location.
+		 *
+		 * We never have to move branches to the right.  This makes
+		 * the graph much more legible, since whenever branches
+		 * cross, only one is moving directions.
+		 */
+		assert(target * 2 <= i);
+
+		if (target * 2 == i) {
+			/*
+			 * This column is already in the
+			 * correct place
+			 */
+			assert(graph->new_mapping[i] == -1);
+			graph->new_mapping[i] = target;
+		} else if (graph->new_mapping[i - 1] < 0) {
+			/*
+			 * Nothing is to the left.
+			 * Move to the left by one
+			 */
+			graph->new_mapping[i - 1] = target;
+		} else if (graph->new_mapping[i - 1] == target) {
+			/*
+			 * There is a branch line to our left
+			 * already, and it is our target.  We
+			 * combine with this line, since we share
+			 * the same parent commit.
+			 *
+			 * We don't have to add anything to the
+			 * output or new_mapping, since the
+			 * existing branch line has already taken
+			 * care of it.
+			 */
+		} else {
+			/*
+			 * There is a branch line to our left,
+			 * but it isn't our target.  We need to
+			 * cross over it.
+			 *
+			 * The space just to the left of this
+			 * branch should always be empty.
+			 */
+			assert(graph->new_mapping[i - 1] > target);
+			assert(graph->new_mapping[i - 2] < 0);
+			graph->new_mapping[i - 2] = target;
+		}
+	}
+
+	/*
+	 * The new mapping may be 1 smaller than the old mapping
+	 */
+	if (graph->new_mapping[graph->mapping_size - 1] < 0)
+		graph->mapping_size--;
+
+	/*
+	 * Output out a line based on the new mapping info
+	 */
+	for (i = 0; i < graph->mapping_size; i++) {
+		int target = graph->new_mapping[i];
+		if (target < 0)
+			strbuf_addch(sb, ' ');
+		else if (target * 2 == i)
+			strbuf_addch(sb, '|');
+		else
+			strbuf_addch(sb, '/');
+	}
+
+	graph_pad_horizontally(graph, sb);
+
+	/*
+	 * Swap mapping and new_mapping
+	 */
+	tmp_mapping = graph->mapping;
+	graph->mapping = graph->new_mapping;
+	graph->new_mapping = tmp_mapping;
+
+	/*
+	 * If graph->mapping indicates that all of the branch lines
+	 * are already in the correct positions, we are done.
+	 * Otherwise, we need to collapse some branch lines together.
+	 */
+	if (graph_is_mapping_correct(graph))
+		graph->state = GRAPH_PADDING;
+}
+
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+{
+	switch (graph->state) {
+	case GRAPH_PADDING:
+		graph_output_padding_line(graph, sb);
+		return 0;
+	case GRAPH_SKIP:
+		graph_output_skip_line(graph, sb);
+		return 0;
+	case GRAPH_PRE_COMMIT:
+		graph_output_pre_commit_line(graph, sb);
+		return 0;
+	case GRAPH_COMMIT:
+		graph_output_commit_line(graph, sb);
+		return 1;
+	case GRAPH_POST_MERGE:
+		graph_output_post_merge_line(graph, sb);
+		return 0;
+	case GRAPH_COLLAPSING:
+		graph_output_collapsing_line(graph, sb);
+		return 0;
+	}
+
+	assert(0);
+	return 0;
+}
+
+void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
+{
+	int i, j;
+
+	if (graph->state != GRAPH_COMMIT) {
+		graph_next_line(graph, sb);
+		return;
+	}
+
+	/*
+	 * Output the row containing this commit
+	 * Iterate up to and including graph->num_columns,
+	 * since the current commit may not be in any of the existing
+	 * columns.  (This happens when the current commit doesn't have any
+	 * children that we have already processed.)
+	 */
+	for (i = 0; i < graph->num_columns; i++) {
+		struct commit *col_commit = graph->columns[i].commit;
+		if (col_commit == graph->commit) {
+			strbuf_addch(sb, '|');
+
+			if (graph->num_parents < 3)
+				strbuf_addch(sb, ' ');
+			else {
+				int num_spaces = ((graph->num_parents - 2) * 2);
+				for (j = 0; j < num_spaces; j++)
+					strbuf_addch(sb, ' ');
+			}
+		} else {
+			strbuf_addstr(sb, "| ");
+		}
+	}
+
+	graph_pad_horizontally(graph, sb);
+}
+
+int graph_is_commit_finished(struct git_graph const *graph)
+{
+	return (graph->state == GRAPH_PADDING);
+}
+
+void graph_show_commit(struct git_graph *graph)
+{
+	struct strbuf msgbuf;
+	int shown_commit_line = 0;
+
+	if (!graph)
+		return;
+
+	strbuf_init(&msgbuf, 0);
+
+	while (!shown_commit_line) {
+		shown_commit_line = graph_next_line(graph, &msgbuf);
+		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+		if (!shown_commit_line)
+			putchar('\n');
+		strbuf_setlen(&msgbuf, 0);
+	}
+
+	strbuf_release(&msgbuf);
+}
+
+void graph_show_oneline(struct git_graph *graph)
+{
+	struct strbuf msgbuf;
+
+	if (!graph)
+		return;
+
+	strbuf_init(&msgbuf, 0);
+	graph_next_line(graph, &msgbuf);
+	fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+	strbuf_release(&msgbuf);
+}
+
+void graph_show_padding(struct git_graph *graph)
+{
+	struct strbuf msgbuf;
+
+	if (!graph)
+		return;
+
+	strbuf_init(&msgbuf, 0);
+	graph_padding_line(graph, &msgbuf);
+	fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+	strbuf_release(&msgbuf);
+}
+
+int graph_show_remainder(struct git_graph *graph)
+{
+	struct strbuf msgbuf;
+	int shown = 0;
+
+	if (!graph)
+		return 0;
+
+	if (graph_is_commit_finished(graph))
+		return 0;
+
+	strbuf_init(&msgbuf, 0);
+	for (;;) {
+		graph_next_line(graph, &msgbuf);
+		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+		strbuf_setlen(&msgbuf, 0);
+		shown = 1;
+
+		if (!graph_is_commit_finished(graph))
+			putchar('\n');
+		else
+			break;
+	}
+	strbuf_release(&msgbuf);
+
+	return shown;
+}
+
+
+void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
+{
+	char *p;
+
+	if (!graph) {
+		fwrite(sb->buf, sizeof(char), sb->len, stdout);
+		return;
+	}
+
+	/*
+	 * Print the strbuf line by line,
+	 * and display the graph info before each line but the first.
+	 */
+	p = sb->buf;
+	while (p) {
+		size_t len;
+		char *next_p = strchr(p, '\n');
+		if (next_p) {
+			next_p++;
+			len = next_p - p;
+		} else {
+			len = (sb->buf + sb->len) - p;
+		}
+		fwrite(p, sizeof(char), len, stdout);
+		if (next_p && *next_p != '\0')
+			graph_show_oneline(graph);
+		p = next_p;
+	}
+}
+
+void graph_show_commit_msg(struct git_graph *graph,
+			   struct strbuf const *sb)
+{
+	int newline_terminated;
+
+	if (!graph) {
+		/*
+		 * If there's no graph, just print the message buffer.
+		 *
+		 * The message buffer for CMIT_FMT_ONELINE and
+		 * CMIT_FMT_USERFORMAT are already missing a terminating
+		 * newline.  All of the other formats should have it.
+		 */
+		fwrite(sb->buf, sizeof(char), sb->len, stdout);
+		return;
+	}
+
+	newline_terminated = (sb->len && sb->buf[sb->len - 1] == '\n');
+
+	/*
+	 * Show the commit message
+	 */
+	graph_show_strbuf(graph, sb);
+
+	/*
+	 * If there is more output needed for this commit, show it now
+	 */
+	if (!graph_is_commit_finished(graph)) {
+		/*
+		 * If sb doesn't have a terminating newline, print one now,
+		 * so we can start the remainder of the graph output on a
+		 * new line.
+		 */
+		if (!newline_terminated)
+			putchar('\n');
+
+		graph_show_remainder(graph);
+
+		/*
+		 * If sb ends with a newline, our output should too.
+		 */
+		if (newline_terminated)
+			putchar('\n');
+	}
+}
diff --git a/graph.h b/graph.h
new file mode 100644
index 0000000..eab4e3d
--- /dev/null
+++ b/graph.h
@@ -0,0 +1,121 @@
+#ifndef GRAPH_H
+#define GRAPH_H
+
+/* A graph is a pointer to this opaque structure */
+struct git_graph;
+
+/*
+ * Create a new struct git_graph.
+ * The graph should be freed with graph_release() when no longer needed.
+ */
+struct git_graph *graph_init(struct rev_info *opt);
+
+/*
+ * Destroy a struct git_graph and free associated memory.
+ */
+void graph_release(struct git_graph *graph);
+
+/*
+ * Update a git_graph with a new commit.
+ * This will cause the graph to begin outputting lines for the new commit
+ * the next time graph_next_line() is called.
+ *
+ * If graph_update() is called before graph_is_commit_finished() returns 1,
+ * the next call to graph_next_line() will output an ellipsis ("...")
+ * to indicate that a portion of the graph is missing.
+ */
+void graph_update(struct git_graph *graph, struct commit *commit);
+
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Output a padding line in the graph.
+ * This is similar to graph_next_line().  However, it is guaranteed to
+ * never print the current commit line.  Instead, if the commit line is
+ * next, it will simply output a line of vertical padding, extending the
+ * branch lines downwards, but leaving them otherwise unchanged.
+ */
+void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Determine if a graph has finished outputting lines for the current
+ * commit.
+ *
+ * Returns 1 if graph_next_line() needs to be called again before
+ * graph_update() should be called.  Returns 0 if no more lines are needed
+ * for this commit.  If 0 is returned, graph_next_line() may still be
+ * called without calling graph_update(), and it will merely output
+ * appropriate "vertical padding" in the graph.
+ */
+int graph_is_commit_finished(struct git_graph const *graph);
+
+
+/*
+ * graph_show_*: helper functions for printing to stdout
+ */
+
+
+/*
+ * If the graph is non-NULL, print the history graph to stdout,
+ * up to and including the line containing this commit.
+ * Does not print a terminating newline on the last line.
+ */
+void graph_show_commit(struct git_graph *graph);
+
+/*
+ * If the graph is non-NULL, print one line of the history graph to stdout.
+ * Does not print a terminating newline on the last line.
+ */
+void graph_show_oneline(struct git_graph *graph);
+
+/*
+ * If the graph is non-NULL, print one line of vertical graph padding to
+ * stdout.  Does not print a terminating newline on the last line.
+ */
+void graph_show_padding(struct git_graph *graph);
+
+/*
+ * If the graph is non-NULL, print the rest of the history graph for this
+ * commit to stdout.  Does not print a terminating newline on the last line.
+ */
+int graph_show_remainder(struct git_graph *graph);
+
+/*
+ * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
+ * first will be prefixed with the graph output.
+ *
+ * If the strbuf ends with a newline, the output will end after this
+ * newline.  A new graph line will not be printed after the final newline.
+ * If the strbuf is empty, no output will be printed.
+ *
+ * Since the first line will not include the graph ouput, the caller is
+ * responsible for printing this line's graph (perhaps via
+ * graph_show_commit() or graph_show_oneline()) before calling
+ * graph_show_strbuf().
+ */
+void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
+
+/*
+ * Print a commit message strbuf and the remainder of the graph to stdout.
+ *
+ * This is similar to graph_show_strbuf(), but it always prints the
+ * remainder of the graph.
+ *
+ * If the strbuf ends with a newline, the output printed by
+ * graph_show_commit_msg() will end with a newline.  If the strbuf is
+ * missing a terminating newline (including if it is empty), the output
+ * printed by graph_show_commit_msg() will also be missing a terminating
+ * newline.
+ */
+void graph_show_commit_msg(struct git_graph *graph, struct strbuf const *sb);
+
+#endif /* GRAPH_H */
diff --git a/hash-object.c b/hash-object.c
index 3d77390..48d5223 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -6,6 +6,7 @@
  */
 #include "cache.h"
 #include "blob.h"
+#include "quote.h"
 
 static void hash_object(const char *path, enum object_type type, int write_object)
 {
@@ -20,6 +21,7 @@
 		    ? "Unable to add %s to database"
 		    : "Unable to hash %s", path);
 	printf("%s\n", sha1_to_hex(sha1));
+	maybe_flush_or_die(stdout, "hash to stdout");
 }
 
 static void hash_stdin(const char *type, int write_object)
@@ -30,8 +32,27 @@
 	printf("%s\n", sha1_to_hex(sha1));
 }
 
+static void hash_stdin_paths(const char *type, int write_objects)
+{
+	struct strbuf buf, nbuf;
+
+	strbuf_init(&buf, 0);
+	strbuf_init(&nbuf, 0);
+	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+		if (buf.buf[0] == '"') {
+			strbuf_reset(&nbuf);
+			if (unquote_c_style(&nbuf, buf.buf, NULL))
+				die("line is badly quoted");
+			strbuf_swap(&buf, &nbuf);
+		}
+		hash_object(buf.buf, type_from_string(type), write_objects);
+	}
+	strbuf_release(&buf);
+	strbuf_release(&nbuf);
+}
+
 static const char hash_object_usage[] =
-"git-hash-object [-t <type>] [-w] [--stdin] <file>...";
+"git-hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]";
 
 int main(int argc, char **argv)
 {
@@ -42,6 +63,7 @@
 	int prefix_length = -1;
 	int no_more_flags = 0;
 	int hashstdin = 0;
+	int stdin_paths = 0;
 
 	git_config(git_default_config, NULL);
 
@@ -65,7 +87,19 @@
 			}
 			else if (!strcmp(argv[i], "--help"))
 				usage(hash_object_usage);
+			else if (!strcmp(argv[i], "--stdin-paths")) {
+				if (hashstdin) {
+					error("Can't use --stdin-paths with --stdin");
+					usage(hash_object_usage);
+				}
+				stdin_paths = 1;
+
+			}
 			else if (!strcmp(argv[i], "--stdin")) {
+				if (stdin_paths) {
+					error("Can't use %s with --stdin-paths", argv[i]);
+					usage(hash_object_usage);
+				}
 				if (hashstdin)
 					die("Multiple --stdin arguments are not supported");
 				hashstdin = 1;
@@ -76,6 +110,11 @@
 		else {
 			const char *arg = argv[i];
 
+			if (stdin_paths) {
+				error("Can't specify files (such as \"%s\") with --stdin-paths", arg);
+				usage(hash_object_usage);
+			}
+
 			if (hashstdin) {
 				hash_stdin(type, write_object);
 				hashstdin = 0;
@@ -87,6 +126,10 @@
 			no_more_flags = 1;
 		}
 	}
+
+	if (stdin_paths)
+		hash_stdin_paths(type, write_object);
+
 	if (hashstdin)
 		hash_stdin(type, write_object);
 	return 0;
diff --git a/http-push.c b/http-push.c
index 42727c8..f173dcd 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1349,6 +1349,24 @@
 	return rc;
 }
 
+static void remove_locks(void)
+{
+	struct remote_lock *lock = remote->locks;
+
+	fprintf(stderr, "Removing remote locks...\n");
+	while (lock) {
+		unlock_remote(lock);
+		lock = lock->next;
+	}
+}
+
+static void remove_locks_on_signal(int signo)
+{
+	remove_locks();
+	signal(signo, SIG_DFL);
+	raise(signo);
+}
+
 static void remote_ls(const char *path, int flags,
 		      void (*userFunc)(struct remote_ls_ctx *ls),
 		      void *userData);
@@ -2256,6 +2274,10 @@
 		goto cleanup;
 	}
 
+	signal(SIGINT, remove_locks_on_signal);
+	signal(SIGHUP, remove_locks_on_signal);
+	signal(SIGQUIT, remove_locks_on_signal);
+
 	/* Check whether the remote has server info files */
 	remote->can_update_info_refs = 0;
 	remote->has_info_refs = remote_exists("info/refs");
diff --git a/ident.c b/ident.c
index ed44a53..b35504a 100644
--- a/ident.c
+++ b/ident.c
@@ -250,6 +250,9 @@
 
 const char *git_committer_info(int flag)
 {
+	if (getenv("GIT_COMMITTER_NAME") &&
+	    getenv("GIT_COMMITTER_EMAIL"))
+		user_ident_explicitly_given = 1;
 	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
 			 getenv("GIT_COMMITTER_EMAIL"),
 			 getenv("GIT_COMMITTER_DATE"),
diff --git a/lockfile.c b/lockfile.c
index 663f18f..cfc7335 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -24,7 +24,7 @@
 static void remove_lock_file_on_signal(int signo)
 {
 	remove_lock_file();
-	signal(SIGINT, SIG_DFL);
+	signal(signo, SIG_DFL);
 	raise(signo);
 }
 
@@ -160,6 +160,34 @@
 	return fd;
 }
 
+int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on_error)
+{
+	int fd, orig_fd;
+
+	fd = lock_file(lk, path);
+	if (fd < 0) {
+		if (die_on_error)
+			die("unable to create '%s.lock': %s", path, strerror(errno));
+		return fd;
+	}
+
+	orig_fd = open(path, O_RDONLY);
+	if (orig_fd < 0) {
+		if (errno != ENOENT) {
+			if (die_on_error)
+				die("cannot open '%s' for copying", path);
+			close(fd);
+			return error("cannot open '%s' for copying", path);
+		}
+	} else if (copy_fd(orig_fd, fd)) {
+		if (die_on_error)
+			exit(128);
+		close(fd);
+		return -1;
+	}
+	return fd;
+}
+
 int close_lock_file(struct lock_file *lk)
 {
 	int fd = lk->fd;
diff --git a/log-tree.c b/log-tree.c
index d3fb0e5..5505606 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
+#include "graph.h"
 #include "log-tree.h"
 #include "reflog-walk.h"
 
@@ -165,11 +166,16 @@
 	}
 
 	printf("From %s Mon Sep 17 00:00:00 2001\n", name);
-	if (opt->message_id)
+	graph_show_oneline(opt->graph);
+	if (opt->message_id) {
 		printf("Message-Id: <%s>\n", opt->message_id);
-	if (opt->ref_message_id)
+		graph_show_oneline(opt->graph);
+	}
+	if (opt->ref_message_id) {
 		printf("In-Reply-To: <%s>\nReferences: <%s>\n",
 		       opt->ref_message_id, opt->ref_message_id);
+		graph_show_oneline(opt->graph);
+	}
 	if (opt->mime_boundary) {
 		static char subject_buffer[1024];
 		static char buffer[1024];
@@ -220,20 +226,28 @@
 
 	opt->loginfo = NULL;
 	if (!opt->verbose_header) {
-		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('>');
+		graph_show_commit(opt->graph);
+
+		if (!opt->graph) {
+			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('>');
+			}
 		}
 		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
-		if (opt->parents)
+		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
 		show_decorations(commit);
+		if (opt->graph && !graph_is_commit_finished(opt->graph)) {
+			putchar('\n');
+			graph_show_remainder(opt->graph);
+		}
 		putchar(opt->diffopt.line_termination);
 		return;
 	}
@@ -243,11 +257,33 @@
 	 * Otherwise, add a diffopt.line_termination character before all
 	 * entries but the first.  (IOW, as a separator between entries)
 	 */
-	if (opt->shown_one && !opt->use_terminator)
+	if (opt->shown_one && !opt->use_terminator) {
+		/*
+		 * If entries are separated by a newline, the output
+		 * should look human-readable.  If the last entry ended
+		 * with a newline, print the graph output before this
+		 * newline.  Otherwise it will end up as a completely blank
+		 * line and will look like a gap in the graph.
+		 *
+		 * If the entry separator is not a newline, the output is
+		 * primarily intended for programmatic consumption, and we
+		 * never want the extra graph output before the entry
+		 * separator.
+		 */
+		if (opt->diffopt.line_termination == '\n' &&
+		    !opt->missing_newline)
+			graph_show_padding(opt->graph);
 		putchar(opt->diffopt.line_termination);
+	}
 	opt->shown_one = 1;
 
 	/*
+	 * If the history graph was requested,
+	 * print the graph, up to this commit's line
+	 */
+	graph_show_commit(opt->graph);
+
+	/*
 	 * Print header line of header..
 	 */
 
@@ -259,19 +295,22 @@
 		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('<');
-			else
-				putchar('>');
+
+		if (!opt->graph) {
+			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('>');
+			}
 		}
 		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit),
 		      stdout);
-		if (opt->parents)
+		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
 		if (parent)
 			printf(" (from %s)",
@@ -279,8 +318,19 @@
 						  abbrev_commit));
 		show_decorations(commit);
 		printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
-		putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+		if (opt->commit_format == CMIT_FMT_ONELINE) {
+			putchar(' ');
+		} else {
+			putchar('\n');
+			graph_show_oneline(opt->graph);
+		}
 		if (opt->reflog_info) {
+			/*
+			 * setup_revisions() ensures that opt->reflog_info
+			 * and opt->graph cannot both be set,
+			 * so we don't need to worry about printing the
+			 * graph info here.
+			 */
 			show_reflog_message(opt->reflog_info,
 				    opt->commit_format == CMIT_FMT_ONELINE,
 				    opt->date_mode);
@@ -304,13 +354,30 @@
 
 	if (opt->add_signoff)
 		append_signoff(&msgbuf, opt->add_signoff);
-	if (opt->show_log_size)
+	if (opt->show_log_size) {
 		printf("log size %i\n", (int)msgbuf.len);
+		graph_show_oneline(opt->graph);
+	}
 
-	if (msgbuf.len)
+	/*
+	 * Set opt->missing_newline if msgbuf doesn't
+	 * end in a newline (including if it is empty)
+	 */
+	if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n')
+		opt->missing_newline = 1;
+	else
+		opt->missing_newline = 0;
+
+	if (opt->graph)
+		graph_show_commit_msg(opt->graph, &msgbuf);
+	else
 		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
-	if (opt->use_terminator)
+	if (opt->use_terminator) {
+		if (!opt->missing_newline)
+			graph_show_padding(opt->graph);
 		putchar('\n');
+	}
+
 	strbuf_release(&msgbuf);
 }
 
diff --git a/perl/Git.pm b/perl/Git.pm
index 2e7f896..6ba8ee5 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -39,6 +39,10 @@
   my $lastrev = $repo->command_oneline( [ 'rev-list', '--all' ],
                                         STDERR => 0 );
 
+  my $sha1 = $repo->hash_and_insert_object('file.txt');
+  my $tempfile = tempfile();
+  my $size = $repo->cat_blob($sha1, $tempfile);
+
 =cut
 
 
@@ -51,6 +55,7 @@
 # Methods which can be called as standalone functions as well:
 @EXPORT_OK = qw(command command_oneline command_noisy
                 command_output_pipe command_input_pipe command_close_pipe
+                command_bidi_pipe command_close_bidi_pipe
                 version exec_path hash_object git_cmd_try);
 
 
@@ -92,6 +97,7 @@
 use Carp qw(carp croak); # but croak is bad - throw instead
 use Error qw(:try);
 use Cwd qw(abs_path);
+use IPC::Open2 qw(open2);
 
 }
 
@@ -216,7 +222,6 @@
 	bless $self, $class;
 }
 
-
 =back
 
 =head1 METHODS
@@ -375,6 +380,60 @@
 	_cmd_close($fh, $ctx);
 }
 
+=item command_bidi_pipe ( COMMAND [, ARGUMENTS... ] )
+
+Execute the given C<COMMAND> in the same way as command_output_pipe()
+does but return both an input pipe filehandle and an output pipe filehandle.
+
+The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>.
+See C<command_close_bidi_pipe()> for details.
+
+=cut
+
+sub command_bidi_pipe {
+	my ($pid, $in, $out);
+	$pid = open2($in, $out, 'git', @_);
+	return ($pid, $in, $out, join(' ', @_));
+}
+
+=item command_close_bidi_pipe ( PID, PIPE_IN, PIPE_OUT [, CTX] )
+
+Close the C<PIPE_IN> and C<PIPE_OUT> as returned from C<command_bidi_pipe()>,
+checking whether the command finished successfully. The optional C<CTX>
+argument is required if you want to see the command name in the error message,
+and it is the fourth value returned by C<command_bidi_pipe()>.  The call idiom
+is:
+
+	my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check');
+	print "000000000\n" $out;
+	while (<$in>) { ... }
+	$r->command_close_bidi_pipe($pid, $in, $out, $ctx);
+
+Note that you should not rely on whatever actually is in C<CTX>;
+currently it is simply the command name but in future the context might
+have more complicated structure.
+
+=cut
+
+sub command_close_bidi_pipe {
+	my ($pid, $in, $out, $ctx) = @_;
+	foreach my $fh ($in, $out) {
+		unless (close $fh) {
+			if ($!) {
+				carp "error closing pipe: $!";
+			} elsif ($? >> 8) {
+				throw Git::Error::Command($ctx, $? >>8);
+			}
+		}
+	}
+
+	waitpid $pid, 0;
+
+	if ($? >> 8) {
+		throw Git::Error::Command($ctx, $? >>8);
+	}
+}
+
 
 =item command_noisy ( COMMAND [, ARGUMENTS... ] )
 
@@ -678,6 +737,147 @@
 }
 
 
+=item hash_and_insert_object ( FILENAME )
+
+Compute the SHA1 object id of the given C<FILENAME> and add the object to the
+object database.
+
+The function returns the SHA1 hash.
+
+=cut
+
+# TODO: Support for passing FILEHANDLE instead of FILENAME
+sub hash_and_insert_object {
+	my ($self, $filename) = @_;
+
+	carp "Bad filename \"$filename\"" if $filename =~ /[\r\n]/;
+
+	$self->_open_hash_and_insert_object_if_needed();
+	my ($in, $out) = ($self->{hash_object_in}, $self->{hash_object_out});
+
+	unless (print $out $filename, "\n") {
+		$self->_close_hash_and_insert_object();
+		throw Error::Simple("out pipe went bad");
+	}
+
+	chomp(my $hash = <$in>);
+	unless (defined($hash)) {
+		$self->_close_hash_and_insert_object();
+		throw Error::Simple("in pipe went bad");
+	}
+
+	return $hash;
+}
+
+sub _open_hash_and_insert_object_if_needed {
+	my ($self) = @_;
+
+	return if defined($self->{hash_object_pid});
+
+	($self->{hash_object_pid}, $self->{hash_object_in},
+	 $self->{hash_object_out}, $self->{hash_object_ctx}) =
+		command_bidi_pipe(qw(hash-object -w --stdin-paths));
+}
+
+sub _close_hash_and_insert_object {
+	my ($self) = @_;
+
+	return unless defined($self->{hash_object_pid});
+
+	my @vars = map { 'hash_object_' . $_ } qw(pid in out ctx);
+
+	command_close_bidi_pipe($self->{@vars});
+	delete $self->{@vars};
+}
+
+=item cat_blob ( SHA1, FILEHANDLE )
+
+Prints the contents of the blob identified by C<SHA1> to C<FILEHANDLE> and
+returns the number of bytes printed.
+
+=cut
+
+sub cat_blob {
+	my ($self, $sha1, $fh) = @_;
+
+	$self->_open_cat_blob_if_needed();
+	my ($in, $out) = ($self->{cat_blob_in}, $self->{cat_blob_out});
+
+	unless (print $out $sha1, "\n") {
+		$self->_close_cat_blob();
+		throw Error::Simple("out pipe went bad");
+	}
+
+	my $description = <$in>;
+	if ($description =~ / missing$/) {
+		carp "$sha1 doesn't exist in the repository";
+		return 0;
+	}
+
+	if ($description !~ /^[0-9a-fA-F]{40} \S+ (\d+)$/) {
+		carp "Unexpected result returned from git cat-file";
+		return 0;
+	}
+
+	my $size = $1;
+
+	my $blob;
+	my $bytesRead = 0;
+
+	while (1) {
+		my $bytesLeft = $size - $bytesRead;
+		last unless $bytesLeft;
+
+		my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024;
+		my $read = read($in, $blob, $bytesToRead, $bytesRead);
+		unless (defined($read)) {
+			$self->_close_cat_blob();
+			throw Error::Simple("in pipe went bad");
+		}
+
+		$bytesRead += $read;
+	}
+
+	# Skip past the trailing newline.
+	my $newline;
+	my $read = read($in, $newline, 1);
+	unless (defined($read)) {
+		$self->_close_cat_blob();
+		throw Error::Simple("in pipe went bad");
+	}
+	unless ($read == 1 && $newline eq "\n") {
+		$self->_close_cat_blob();
+		throw Error::Simple("didn't find newline after blob");
+	}
+
+	unless (print $fh $blob) {
+		$self->_close_cat_blob();
+		throw Error::Simple("couldn't write to passed in filehandle");
+	}
+
+	return $size;
+}
+
+sub _open_cat_blob_if_needed {
+	my ($self) = @_;
+
+	return if defined($self->{cat_blob_pid});
+
+	($self->{cat_blob_pid}, $self->{cat_blob_in},
+	 $self->{cat_blob_out}, $self->{cat_blob_ctx}) =
+		command_bidi_pipe(qw(cat-file --batch));
+}
+
+sub _close_cat_blob {
+	my ($self) = @_;
+
+	return unless defined($self->{cat_blob_pid});
+
+	my @vars = map { 'cat_blob_' . $_ } qw(pid in out ctx);
+
+	command_close_bidi_pipe($self->{@vars});
+	delete $self->{@vars};
+}
 
 =back
 
@@ -895,7 +1095,11 @@
 }
 
 
-sub DESTROY { }
+sub DESTROY {
+	my ($self) = @_;
+	$self->_close_hash_and_insert_object();
+	$self->_close_cat_blob();
+}
 
 
 # Pipe implementation for ActiveState Perl.
diff --git a/read-cache.c b/read-cache.c
index 0382804..ac9a8e7 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -462,15 +462,17 @@
 	return new;
 }
 
-int add_to_index(struct index_state *istate, const char *path, struct stat *st, int verbose)
+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
 {
-	int size, namelen;
+	int size, namelen, was_same;
 	mode_t st_mode = st->st_mode;
 	struct cache_entry *ce, *alias;
 	unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
+	int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
+	int pretend = flags & ADD_CACHE_PRETEND;
 
 	if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
-		die("%s: can only add regular files, symbolic links or git-directories", path);
+		return error("%s: can only add regular files, symbolic links or git-directories", path);
 
 	namelen = strlen(path);
 	if (S_ISDIR(st_mode)) {
@@ -505,23 +507,32 @@
 		return 0;
 	}
 	if (index_path(ce->sha1, path, st, 1))
-		die("unable to index file %s", path);
+		return error("unable to index file %s", path);
 	if (ignore_case && alias && different_name(ce, alias))
 		ce = create_alias_ce(ce, alias);
 	ce->ce_flags |= CE_ADDED;
-	if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
-		die("unable to add %s to index",path);
-	if (verbose)
+
+	/* It was suspected to be recily clean, but it turns out to be Ok */
+	was_same = (alias &&
+		    !ce_stage(alias) &&
+		    !hashcmp(alias->sha1, ce->sha1) &&
+		    ce->ce_mode == alias->ce_mode);
+
+	if (pretend)
+		;
+	else if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
+		return error("unable to add %s to index",path);
+	if (verbose && !was_same)
 		printf("add '%s'\n", path);
 	return 0;
 }
 
-int add_file_to_index(struct index_state *istate, const char *path, int verbose)
+int add_file_to_index(struct index_state *istate, const char *path, int flags)
 {
 	struct stat st;
 	if (lstat(path, &st))
 		die("%s: unable to stat (%s)", path, strerror(errno));
-	return add_to_index(istate, path, &st, verbose);
+	return add_to_index(istate, path, &st, flags);
 }
 
 struct cache_entry *make_cache_entry(unsigned int mode,
@@ -942,6 +953,7 @@
 	int allow_unmerged = (flags & REFRESH_UNMERGED) != 0;
 	int quiet = (flags & REFRESH_QUIET) != 0;
 	int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
+	int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
 	unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
 
 	for (i = 0; i < istate->cache_nr; i++) {
@@ -949,6 +961,9 @@
 		int cache_errno = 0;
 
 		ce = istate->cache[i];
+		if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
+			continue;
+
 		if (ce_stage(ce)) {
 			while ((i < istate->cache_nr) &&
 			       ! strcmp(istate->cache[i]->name, ce->name))
diff --git a/refs.c b/refs.c
index 9b495eb..9e8e858 100644
--- a/refs.c
+++ b/refs.c
@@ -159,6 +159,8 @@
 } cached_refs;
 static struct ref_list *current_ref;
 
+static struct ref_list *extra_refs;
+
 static void free_ref_list(struct ref_list *list)
 {
 	struct ref_list *next;
@@ -215,6 +217,17 @@
 	cached_refs->packed = sort_ref_list(list);
 }
 
+void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
+{
+	extra_refs = add_ref(name, sha1, flag, extra_refs, NULL);
+}
+
+void clear_extra_refs(void)
+{
+	free_ref_list(extra_refs);
+	extra_refs = NULL;
+}
+
 static struct ref_list *get_packed_refs(void)
 {
 	if (!cached_refs.did_packed) {
@@ -547,6 +560,11 @@
 	struct ref_list *packed = get_packed_refs();
 	struct ref_list *loose = get_loose_refs();
 
+	struct ref_list *extra;
+
+	for (extra = extra_refs; extra; extra = extra->next)
+		retval = do_one_ref(base, fn, trim, cb_data, extra);
+
 	while (packed && loose) {
 		struct ref_list *entry;
 		int cmp = strcmp(packed->name, loose->name);
diff --git a/refs.h b/refs.h
index 06abee1..06ad260 100644
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,15 @@
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 
+/*
+ * Extra refs will be listed by for_each_ref() before any actual refs
+ * for the duration of this process or until clear_extra_refs() is
+ * called. Only extra refs added before for_each_ref() is called will
+ * be listed on a given call of for_each_ref().
+ */
+extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags);
+extern void clear_extra_refs(void);
+
 extern int peel_ref(const char *, unsigned char *);
 
 /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
diff --git a/remote.c b/remote.c
index dff6c5f..91e3b11 100644
--- a/remote.c
+++ b/remote.c
@@ -2,6 +2,16 @@
 #include "remote.h"
 #include "refs.h"
 
+static struct refspec s_tag_refspec = {
+	0,
+	1,
+	0,
+	"refs/tags/",
+	"refs/tags/"
+};
+
+const struct refspec *tag_refspec = &s_tag_refspec;
+
 struct counted_string {
 	size_t len;
 	const char *s;
@@ -434,6 +444,16 @@
 		}
 
 		rhs = strrchr(lhs, ':');
+
+		/*
+		 * Before going on, special case ":" (or "+:") as a refspec
+		 * for matching refs.
+		 */
+		if (!fetch && rhs == lhs && rhs[1] == '\0') {
+			rs[i].matching = 1;
+			continue;
+		}
+
 		if (rhs) {
 			rhs++;
 			rlen = strlen(rhs);
@@ -855,7 +875,7 @@
 	const char *dst_value = rs->dst;
 	char *dst_guess;
 
-	if (rs->pattern)
+	if (rs->pattern || rs->matching)
 		return errs;
 
 	matched_src = matched_dst = NULL;
@@ -945,13 +965,23 @@
 						 const struct ref *src)
 {
 	int i;
+	int matching_refs = -1;
 	for (i = 0; i < rs_nr; i++) {
+		if (rs[i].matching &&
+		    (matching_refs == -1 || rs[i].force)) {
+			matching_refs = i;
+			continue;
+		}
+
 		if (rs[i].pattern &&
 		    !prefixcmp(src->name, rs[i].src) &&
 		    src->name[strlen(rs[i].src)] == '/')
 			return rs + i;
 	}
-	return NULL;
+	if (matching_refs != -1)
+		return rs + matching_refs;
+	else
+		return NULL;
 }
 
 /*
@@ -962,11 +992,16 @@
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 	       int nr_refspec, const char **refspec, int flags)
 {
-	struct refspec *rs =
-		parse_push_refspec(nr_refspec, (const char **) refspec);
+	struct refspec *rs;
 	int send_all = flags & MATCH_REFS_ALL;
 	int send_mirror = flags & MATCH_REFS_MIRROR;
+	static const char *default_refspec[] = { ":", 0 };
 
+	if (!nr_refspec) {
+		nr_refspec = 1;
+		refspec = default_refspec;
+	}
+	rs = parse_push_refspec(nr_refspec, (const char **) refspec);
 	if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
 		return -1;
 
@@ -977,48 +1012,50 @@
 		char *dst_name;
 		if (src->peer_ref)
 			continue;
-		if (nr_refspec) {
-			pat = check_pattern_match(rs, nr_refspec, src);
-			if (!pat)
-				continue;
-		}
-		else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
+
+		pat = check_pattern_match(rs, nr_refspec, src);
+		if (!pat)
+			continue;
+
+		if (pat->matching) {
 			/*
 			 * "matching refs"; traditionally we pushed everything
 			 * including refs outside refs/heads/ hierarchy, but
 			 * that does not make much sense these days.
 			 */
-			continue;
+			if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
+				continue;
+			dst_name = xstrdup(src->name);
 
-		if (pat) {
+		} else {
 			const char *dst_side = pat->dst ? pat->dst : pat->src;
 			dst_name = xmalloc(strlen(dst_side) +
 					   strlen(src->name) -
 					   strlen(pat->src) + 2);
 			strcpy(dst_name, dst_side);
 			strcat(dst_name, src->name + strlen(pat->src));
-		} else
-			dst_name = xstrdup(src->name);
+		}
 		dst_peer = find_ref_by_name(dst, dst_name);
-		if (dst_peer && dst_peer->peer_ref)
-			/* We're already sending something to this ref. */
-			goto free_name;
+		if (dst_peer) {
+			if (dst_peer->peer_ref)
+				/* We're already sending something to this ref. */
+				goto free_name;
 
-		if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
-			/*
-			 * Remote doesn't have it, and we have no
-			 * explicit pattern, and we don't have
-			 * --all nor --mirror.
-			 */
-			goto free_name;
-		if (!dst_peer) {
+		} else {
+			if (pat->matching && !(send_all || send_mirror))
+				/*
+				 * Remote doesn't have it, and we have no
+				 * explicit pattern, and we don't have
+				 * --all nor --mirror.
+				 */
+				goto free_name;
+
 			/* Create a new one and link it */
 			dst_peer = make_linked_ref(dst_name, dst_tail);
 			hashcpy(dst_peer->new_sha1, src->new_sha1);
 		}
 		dst_peer->peer_ref = src;
-		if (pat)
-			dst_peer->force = pat->force;
+		dst_peer->force = pat->force;
 	free_name:
 		free(dst_name);
 	}
diff --git a/remote.h b/remote.h
index 2ee83a3..8eed87b 100644
--- a/remote.h
+++ b/remote.h
@@ -47,11 +47,14 @@
 struct refspec {
 	unsigned force : 1;
 	unsigned pattern : 1;
+	unsigned matching : 1;
 
 	char *src;
 	char *dst;
 };
 
+extern const struct refspec *tag_refspec;
+
 struct ref *alloc_ref(unsigned namelen);
 
 struct ref *alloc_ref_from_str(const char* str);
diff --git a/revision.c b/revision.c
index 4231ea2..fb9924e 100644
--- a/revision.c
+++ b/revision.c
@@ -6,6 +6,7 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
+#include "graph.h"
 #include "grep.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
@@ -415,7 +416,6 @@
 {
 	struct commit_list *parent = commit->parents;
 	unsigned left_flag;
-	int add, rest;
 
 	if (commit->object.flags & ADDED)
 		return 0;
@@ -462,19 +462,18 @@
 
 	left_flag = (commit->object.flags & SYMMETRIC_LEFT);
 
-	rest = !revs->first_parent_only;
-	for (parent = commit->parents, add = 1; parent; add = rest) {
+	for (parent = commit->parents; parent; parent = parent->next) {
 		struct commit *p = parent->item;
 
-		parent = parent->next;
 		if (parse_commit(p) < 0)
 			return -1;
 		p->object.flags |= left_flag;
-		if (p->object.flags & SEEN)
-			continue;
-		p->object.flags |= SEEN;
-		if (add)
+		if (!(p->object.flags & SEEN)) {
+			p->object.flags |= SEEN;
 			insert_by_date(p, list);
+		}
+		if(revs->first_parent_only)
+			break;
 	}
 	return 0;
 }
@@ -1105,7 +1104,8 @@
 				}
 			}
 			if (!strcmp(arg, "--parents")) {
-				revs->parents = 1;
+				revs->rewrite_parents = 1;
+				revs->print_parents = 1;
 				continue;
 			}
 			if (!strcmp(arg, "--dense")) {
@@ -1202,6 +1202,12 @@
 				get_commit_format(arg+8, revs);
 				continue;
 			}
+			if (!prefixcmp(arg, "--graph")) {
+				revs->topo_order = 1;
+				revs->rewrite_parents = 1;
+				revs->graph = graph_init(revs);
+				continue;
+			}
 			if (!strcmp(arg, "--root")) {
 				revs->show_root_diff = 1;
 				continue;
@@ -1396,6 +1402,15 @@
 	if (revs->reverse && revs->reflog_info)
 		die("cannot combine --reverse with --walk-reflogs");
 
+	/*
+	 * Limitations on the graph functionality
+	 */
+	if (revs->reverse && revs->graph)
+		die("cannot combine --reverse with --graph");
+
+	if (revs->reflog_info && revs->graph)
+		die("cannot combine --walk-reflogs with --graph");
+
 	return left;
 }
 
@@ -1524,13 +1539,13 @@
 		/* Commit without changes? */
 		if (commit->object.flags & TREESAME) {
 			/* drop merges unless we want parenthood */
-			if (!revs->parents)
+			if (!revs->rewrite_parents)
 				return commit_ignore;
 			/* non-merge - always ignore it */
 			if (!commit->parents || !commit->parents->next)
 				return commit_ignore;
 		}
-		if (revs->parents && rewrite_parents(revs, commit) < 0)
+		if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)
 			return commit_error;
 	}
 	return commit_show;
@@ -1597,28 +1612,62 @@
 	}
 }
 
-struct commit *get_revision(struct rev_info *revs)
+static void create_boundary_commit_list(struct rev_info *revs)
+{
+	unsigned i;
+	struct commit *c;
+	struct object_array *array = &revs->boundary_commits;
+	struct object_array_entry *objects = array->objects;
+
+	/*
+	 * If revs->commits is non-NULL at this point, an error occurred in
+	 * get_revision_1().  Ignore the error and continue printing the
+	 * boundary commits anyway.  (This is what the code has always
+	 * done.)
+	 */
+	if (revs->commits) {
+		free_commit_list(revs->commits);
+		revs->commits = NULL;
+	}
+
+	/*
+	 * Put all of the actual boundary commits from revs->boundary_commits
+	 * into revs->commits
+	 */
+	for (i = 0; i < array->nr; i++) {
+		c = (struct commit *)(objects[i].item);
+		if (!c)
+			continue;
+		if (!(c->object.flags & CHILD_SHOWN))
+			continue;
+		if (c->object.flags & (SHOWN | BOUNDARY))
+			continue;
+		c->object.flags |= BOUNDARY;
+		commit_list_insert(c, &revs->commits);
+	}
+
+	/*
+	 * If revs->topo_order is set, sort the boundary commits
+	 * in topological order
+	 */
+	sort_in_topological_order(&revs->commits, revs->lifo);
+}
+
+static struct commit *get_revision_internal(struct rev_info *revs)
 {
 	struct commit *c = NULL;
 	struct commit_list *l;
 
 	if (revs->boundary == 2) {
-		unsigned i;
-		struct object_array *array = &revs->boundary_commits;
-		struct object_array_entry *objects = array->objects;
-		for (i = 0; i < array->nr; i++) {
-			c = (struct commit *)(objects[i].item);
-			if (!c)
-				continue;
-			if (!(c->object.flags & CHILD_SHOWN))
-				continue;
-			if (!(c->object.flags & SHOWN))
-				break;
-		}
-		if (array->nr <= i)
-			return NULL;
-
-		c->object.flags |= SHOWN | BOUNDARY;
+		/*
+		 * All of the normal commits have already been returned,
+		 * and we are now returning boundary commits.
+		 * create_boundary_commit_list() has populated
+		 * revs->commits with the remaining commits to return.
+		 */
+		c = pop_commit(&revs->commits);
+		if (c)
+			c->object.flags |= SHOWN;
 		return c;
 	}
 
@@ -1682,7 +1731,14 @@
 		 * switch to boundary commits output mode.
 		 */
 		revs->boundary = 2;
-		return get_revision(revs);
+
+		/*
+		 * Update revs->commits to contain the list of
+		 * boundary commits.
+		 */
+		create_boundary_commit_list(revs);
+
+		return get_revision_internal(revs);
 	}
 
 	/*
@@ -1704,3 +1760,11 @@
 
 	return c;
 }
+
+struct commit *get_revision(struct rev_info *revs)
+{
+	struct commit *c = get_revision_internal(revs);
+	if (c && revs->graph)
+		graph_update(revs->graph, c);
+	return c;
+}
diff --git a/revision.h b/revision.h
index 31217f8..abce500 100644
--- a/revision.h
+++ b/revision.h
@@ -46,7 +46,8 @@
 			unpacked:1, /* see also ignore_packed below */
 			boundary:2,
 			left_right:1,
-			parents:1,
+			rewrite_parents:1,
+			print_parents:1,
 			reverse:1,
 			cherry_pick:1,
 			first_parent_only:1;
@@ -65,7 +66,8 @@
 	/* Format info */
 	unsigned int	shown_one:1,
 			abbrev_commit:1,
-			use_terminator:1;
+			use_terminator:1,
+			missing_newline:1;
 	enum date_mode date_mode;
 
 	const char **ignore_packed; /* pretend objects in these are unpacked */
@@ -88,6 +90,9 @@
 	/* Filter by commit log message */
 	struct grep_opt	*grep_filter;
 
+	/* Display history graph */
+	struct git_graph *graph;
+
 	/* special limits */
 	int skip_count;
 	int max_count;
diff --git a/sha1_file.c b/sha1_file.c
index 3516777..9679040 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -176,21 +176,22 @@
 	return base;
 }
 
-char *sha1_pack_name(const unsigned char *sha1)
+static char *sha1_get_pack_name(const unsigned char *sha1,
+				char **name, char **base)
 {
 	static const char hex[] = "0123456789abcdef";
-	static char *name, *base, *buf;
+	char *buf;
 	int i;
 
-	if (!base) {
+	if (!*base) {
 		const char *sha1_file_directory = get_object_directory();
 		int len = strlen(sha1_file_directory);
-		base = xmalloc(len + 60);
-		sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory);
-		name = base + len + 11;
+		*base = xmalloc(len + 60);
+		sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory);
+		*name = *base + len + 11;
 	}
 
-	buf = name;
+	buf = *name;
 
 	for (i = 0; i < 20; i++) {
 		unsigned int val = *sha1++;
@@ -198,32 +199,21 @@
 		*buf++ = hex[val & 0xf];
 	}
 
-	return base;
+	return *base;
+}
+
+char *sha1_pack_name(const unsigned char *sha1)
+{
+	static char *name, *base;
+
+	return sha1_get_pack_name(sha1, &name, &base);
 }
 
 char *sha1_pack_index_name(const unsigned char *sha1)
 {
-	static const char hex[] = "0123456789abcdef";
-	static char *name, *base, *buf;
-	int i;
+	static char *name, *base;
 
-	if (!base) {
-		const char *sha1_file_directory = get_object_directory();
-		int len = strlen(sha1_file_directory);
-		base = xmalloc(len + 60);
-		sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.idx", sha1_file_directory);
-		name = base + len + 11;
-	}
-
-	buf = name;
-
-	for (i = 0; i < 20; i++) {
-		unsigned int val = *sha1++;
-		*buf++ = hex[val >> 4];
-		*buf++ = hex[val & 0xf];
-	}
-
-	return base;
+	return sha1_get_pack_name(sha1, &name, &base);
 }
 
 struct alternate_object_database *alt_odb_list;
@@ -380,6 +370,18 @@
 	munmap(map, mapsz);
 }
 
+void add_to_alternates_file(const char *reference)
+{
+	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+	int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1);
+	char *alt = mkpath("%s/objects\n", reference);
+	write_or_die(fd, alt, strlen(alt));
+	if (commit_lock_file(lock))
+		die("could not close alternates file");
+	if (alt_odb_tail)
+		link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
+}
+
 void prepare_alt_odb(void)
 {
 	const char *alt;
@@ -2102,26 +2104,16 @@
 	return 0;
 }
 
-int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
+			      void *buf, unsigned long len, time_t mtime)
 {
-	int size, ret;
+	int fd, size, ret;
 	unsigned char *compressed;
 	z_stream stream;
-	unsigned char sha1[20];
 	char *filename;
 	static char tmpfile[PATH_MAX];
-	char hdr[32];
-	int fd, hdrlen;
 
-	/* Normally if we have it in the pack then we do not bother writing
-	 * it out into .git/objects/??/?{38} file.
-	 */
-	write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
 	filename = sha1_file_name(sha1);
-	if (returnsha1)
-		hashcpy(returnsha1, sha1);
-	if (has_sha1_file(sha1))
-		return 0;
 	fd = open(filename, O_RDONLY);
 	if (fd >= 0) {
 		/*
@@ -2182,9 +2174,53 @@
 		die("unable to write sha1 file");
 	free(compressed);
 
+	if (mtime) {
+		struct utimbuf utb;
+		utb.actime = mtime;
+		utb.modtime = mtime;
+		if (utime(tmpfile, &utb) < 0)
+			warning("failed utime() on %s: %s",
+				tmpfile, strerror(errno));
+	}
+
 	return move_temp_to_file(tmpfile, filename);
 }
 
+int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+{
+	unsigned char sha1[20];
+	char hdr[32];
+	int hdrlen;
+
+	/* Normally if we have it in the pack then we do not bother writing
+	 * it out into .git/objects/??/?{38} file.
+	 */
+	write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+	if (returnsha1)
+		hashcpy(returnsha1, sha1);
+	if (has_sha1_file(sha1))
+		return 0;
+	return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
+}
+
+int force_object_loose(const unsigned char *sha1, time_t mtime)
+{
+	struct stat st;
+	void *buf;
+	unsigned long len;
+	enum object_type type;
+	char hdr[32];
+	int hdrlen;
+
+	if (find_sha1_file(sha1, &st))
+		return 0;
+	buf = read_packed_sha1(sha1, &type, &len);
+	if (!buf)
+		return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
+	hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
+	return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
+}
+
 /*
  * We need to unpack and recompress the object for writing
  * it out to a different file.
diff --git a/t/.gitignore b/t/.gitignore
index fad67c0..11ffd91 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1 +1 @@
-trash
+/trash directory
diff --git a/t/Makefile b/t/Makefile
index 72d7884..c6a60ab 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -20,7 +20,7 @@
 	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 clean:
-	$(RM) -r trash
+	$(RM) -r 'trash directory'
 
 # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
 full-svn-test:
diff --git a/t/README b/t/README
index 73ed11b..70841a4 100644
--- a/t/README
+++ b/t/README
@@ -123,7 +123,7 @@
    (or -h), it shows the test_description and exits.
 
  - Creates an empty test directory with an empty .git/objects
-   database and chdir(2) into it.  This directory is 't/trash'
+   database and chdir(2) into it.  This directory is 't/trash directory'
    if you must know, but I do not think you care.
 
  - Defines standard test helper functions for your scripts to
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index d8f3355..5d3bd9d 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -20,12 +20,13 @@
 fi
 
 svnrepo=$PWD/svnrepo
+export svnrepo
 
 perl -w -e "
 use SVN::Core;
 use SVN::Repos;
 \$SVN::Core::VERSION gt '1.1.0' or exit(42);
-system(qw/svnadmin create --fs-type fsfs/, '$svnrepo') == 0 or exit(41);
+system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
 " >&3 2>&4
 x=$?
 if test $x -ne 0
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 7f206c5..a5c4436 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -61,7 +61,8 @@
 			-new -x509 -nodes \
 			-out $HTTPD_ROOT_PATH/httpd.pem \
 			-keyout $HTTPD_ROOT_PATH/httpd.pem
-		export GIT_SSL_NO_VERIFY=t
+		GIT_SSL_NO_VERIFY=t
+		export GIT_SSL_NO_VERIFY
 		HTTPD_PARA="$HTTPD_PARA -DSSL"
 	else
 		HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 27b54cb..690f80a 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -305,10 +305,10 @@
 	file="$dir"/index &&
 	test "$file" = "$(test-absolute-path $dir2/index)" &&
 	basename=blub &&
-	test "$dir/$basename" = $(cd .git && test-absolute-path $basename) &&
+	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 "$sym" = "$(test-absolute-path "$dir2/syml")"
 '
 
 test_expect_success 'very long name in the index handled sanely' '
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 3fbad77..c5360e2 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -7,6 +7,7 @@
 auml=`printf '\xc3\xa4'`
 aumlcdiar=`printf '\x61\xcc\x88'`
 
+case_insensitive=
 test_expect_success 'see if we expect ' '
 
 	test_case=test_expect_success
@@ -17,6 +18,7 @@
 	if test "$(cat junk/CamelCase)" != good
 	then
 		test_case=test_expect_failure
+		case_insensitive=t
 		say "will test on a case insensitive filesystem"
 	fi &&
 	rm -fr junk &&
@@ -32,8 +34,23 @@
 	rm -fr junk
 '
 
+if test "$case_insensitive"
+then
+test_expect_success "detection of case insensitive filesystem during repo init" '
+
+	test $(git config --bool core.ignorecase) = true
+'
+else
+test_expect_success "detection of case insensitive filesystem during repo init" '
+
+	! git config --bool core.ignorecase >/dev/null ||
+	test $(git config --bool core.ignorecase) = false
+'
+fi
+
 test_expect_success "setup case tests" '
 
+	git config core.ignorecase true &&
 	touch camelcase &&
 	git add camelcase &&
 	git commit -m "initial" &&
@@ -55,11 +72,23 @@
 
 $test_case 'merge (case change)' '
 
+	rm -f CamelCase &&
+	rm -f camelcase &&
 	git reset --hard initial &&
 	git merge topic
 
 '
 
+$test_case 'add (with different case)' '
+
+	git reset --hard initial &&
+	rm camelcase &&
+	echo 1 >CamelCase &&
+	git add CamelCase &&
+	test $(git-ls-files | grep -i camelcase | wc -l) = 1
+
+'
+
 test_expect_success "setup unicode normalization tests" '
 
   test_create_repo unicode &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
new file mode 100755
index 0000000..cb1fbe5
--- /dev/null
+++ b/t/t1006-cat-file.sh
@@ -0,0 +1,226 @@
+#!/bin/sh
+
+test_description='git cat-file'
+
+. ./test-lib.sh
+
+echo_without_newline () {
+    printf '%s' "$*"
+}
+
+strlen () {
+    echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
+}
+
+maybe_remove_timestamp () {
+    if test -z "$2"; then
+        echo_without_newline "$1"
+    else
+	echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')"
+    fi
+}
+
+run_tests () {
+    type=$1
+    sha1=$2
+    size=$3
+    content=$4
+    pretty_content=$5
+    no_ts=$6
+
+    batch_output="$sha1 $type $size
+$content"
+
+    test_expect_success "$type exists" '
+	git cat-file -e $sha1
+    '
+
+    test_expect_success "Type of $type is correct" '
+        test $type = "$(git cat-file -t $sha1)"
+    '
+
+    test_expect_success "Size of $type is correct" '
+        test $size = "$(git cat-file -s $sha1)"
+    '
+
+    test -z "$content" ||
+    test_expect_success "Content of $type is correct" '
+	expect="$(maybe_remove_timestamp "$content" $no_ts)"
+	actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)"
+
+        if test "z$expect" = "z$actual"
+	then
+		: happy
+	else
+		echo "Oops: expected $expect"
+		echo "but got $actual"
+		false
+        fi
+    '
+
+    test_expect_success "Pretty content of $type is correct" '
+	expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)"
+	actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)"
+        if test "z$expect" = "z$actual"
+	then
+		: happy
+	else
+		echo "Oops: expected $expect"
+		echo "but got $actual"
+		false
+        fi
+    '
+
+    test -z "$content" ||
+    test_expect_success "--batch output of $type is correct" '
+	expect="$(maybe_remove_timestamp "$batch_output" $no_ts)"
+	actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" no_ts)"
+        if test "z$expect" = "z$actual"
+	then
+		: happy
+	else
+		echo "Oops: expected $expect"
+		echo "but got $actual"
+		false
+        fi
+    '
+
+    test_expect_success "--batch-check output of $type is correct" '
+	expect="$sha1 $type $size"
+	actual="$(echo_without_newline $sha1 | git cat-file --batch-check)"
+        if test "z$expect" = "z$actual"
+	then
+		: happy
+	else
+		echo "Oops: expected $expect"
+		echo "but got $actual"
+		false
+        fi
+    '
+}
+
+hello_content="Hello World"
+hello_size=$(strlen "$hello_content")
+hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+
+test_expect_success "setup" '
+	echo_without_newline "$hello_content" > hello &&
+	git update-index --add hello
+'
+
+run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+
+tree_sha1=$(git write-tree)
+tree_size=33
+tree_pretty_content="100644 blob $hello_sha1	hello"
+
+run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+
+commit_message="Intial commit"
+commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_size=176
+commit_content="tree $tree_sha1
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
+
+$commit_message"
+
+run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1
+
+tag_header_without_timestamp="object $hello_sha1
+type blob
+tag hellotag
+tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_description="This is a tag"
+tag_content="$tag_header_without_timestamp 0000000000 +0000
+
+$tag_description"
+tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000
+
+$tag_description"
+
+tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
+tag_size=$(strlen "$tag_content")
+
+run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1
+
+test_expect_success \
+    "Reach a blob from a tag pointing to it" \
+    "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\""
+
+for batch in batch batch-check
+do
+    for opt in t s e p
+    do
+	test_expect_success "Passing -$opt with --$batch fails" '
+	    test_must_fail git cat-file --$batch -$opt $hello_sha1
+	'
+
+	test_expect_success "Passing --$batch with -$opt fails" '
+	    test_must_fail git cat-file -$opt --$batch $hello_sha1
+	'
+    done
+
+    test_expect_success "Passing <type> with --$batch fails" '
+	test_must_fail git cat-file --$batch blob $hello_sha1
+    '
+
+    test_expect_success "Passing --$batch with <type> fails" '
+	test_must_fail git cat-file blob --$batch $hello_sha1
+    '
+
+    test_expect_success "Passing sha1 with --$batch fails" '
+	test_must_fail git cat-file --$batch $hello_sha1
+    '
+done
+
+test_expect_success "--batch-check for a non-existent object" '
+    test "deadbeef missing" = \
+    "$(echo_without_newline deadbeef | git cat-file --batch-check)"
+'
+
+test_expect_success "--batch-check for an emtpy line" '
+    test " missing" = "$(echo | git cat-file --batch-check)"
+'
+
+batch_input="$hello_sha1
+$commit_sha1
+$tag_sha1
+deadbeef
+
+"
+
+batch_output="$hello_sha1 blob $hello_size
+$hello_content
+$commit_sha1 commit $commit_size
+$commit_content
+$tag_sha1 tag $tag_size
+$tag_content
+deadbeef missing
+ missing"
+
+test_expect_success '--batch with multiple sha1s gives correct format' '
+	test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)"
+'
+
+batch_check_input="$hello_sha1
+$tree_sha1
+$commit_sha1
+$tag_sha1
+deadbeef
+
+"
+
+batch_check_output="$hello_sha1 blob $hello_size
+$tree_sha1 tree $tree_size
+$commit_sha1 commit $commit_size
+$tag_sha1 tag $tag_size
+deadbeef missing
+ missing"
+
+test_expect_success "--batch-check with multiple sha1s gives correct format" '
+    test "$batch_check_output" = \
+    "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
+'
+
+test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
new file mode 100755
index 0000000..0526295
--- /dev/null
+++ b/t/t1007-hash-object.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+test_description=git-hash-object
+
+. ./test-lib.sh
+
+echo_without_newline() {
+	printf '%s' "$*"
+}
+
+test_blob_does_not_exist() {
+	test_expect_success 'blob does not exist in database' "
+		test_must_fail git cat-file blob $1
+	"
+}
+
+test_blob_exists() {
+	test_expect_success 'blob exists in database' "
+		git cat-file blob $1
+	"
+}
+
+hello_content="Hello World"
+hello_sha1=5e1c309dae7f45e0f39b1bf3ac3cd9db12e7d689
+
+example_content="This is an example"
+example_sha1=ddd3f836d3e3fbb7ae289aa9ae83536f76956399
+
+setup_repo() {
+	echo_without_newline "$hello_content" > hello
+	echo_without_newline "$example_content" > example
+}
+
+test_repo=test
+push_repo() {
+	test_create_repo $test_repo
+	cd $test_repo
+
+	setup_repo
+}
+
+pop_repo() {
+	cd ..
+	rm -rf $test_repo
+}
+
+setup_repo
+
+# Argument checking
+
+test_expect_success "multiple '--stdin's are rejected" '
+	test_must_fail git hash-object --stdin --stdin < example
+'
+
+test_expect_success "Can't use --stdin and --stdin-paths together" '
+	test_must_fail git hash-object --stdin --stdin-paths &&
+	test_must_fail git hash-object --stdin-paths --stdin
+'
+
+test_expect_success "Can't pass filenames as arguments with --stdin-paths" '
+	test_must_fail git hash-object --stdin-paths hello < example
+'
+
+# Behavior
+
+push_repo
+
+test_expect_success 'hash a file' '
+	test $hello_sha1 = $(git hash-object hello)
+'
+
+test_blob_does_not_exist $hello_sha1
+
+test_expect_success 'hash from stdin' '
+	test $example_sha1 = $(git hash-object --stdin < example)
+'
+
+test_blob_does_not_exist $example_sha1
+
+test_expect_success 'hash a file and write to database' '
+	test $hello_sha1 = $(git hash-object -w hello)
+'
+
+test_blob_exists $hello_sha1
+
+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"
+'
+
+pop_repo
+
+for args in "-w --stdin" "--stdin -w"; do
+	push_repo
+
+	test_expect_success "hash from stdin and write to database ($args)" '
+		test $example_sha1 = $(git hash-object $args < example)
+	'
+
+	test_blob_exists $example_sha1
+
+	pop_repo
+done
+
+filenames="hello
+example"
+
+sha1s="$hello_sha1
+$example_sha1"
+
+test_expect_success "hash two files with names on stdin" '
+	test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object --stdin-paths)"
+'
+
+for args in "-w --stdin-paths" "--stdin-paths -w"; do
+	push_repo
+
+	test_expect_success "hash two files with names on stdin and write to database ($args)" '
+		test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object $args)"
+	'
+
+	test_blob_exists $hello_sha1
+	test_blob_exists $example_sha1
+
+	pop_repo
+done
+
+test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index b9cef34..fc386ba 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -21,7 +21,7 @@
 '
 
 test_expect_success 'update-index and ls-files' '
-	cd $HERE &&
+	cd "$HERE" &&
 	git update-index --add one &&
 	case "`git ls-files`" in
 	one) echo ok one ;;
@@ -41,7 +41,7 @@
 '
 
 test_expect_success 'cat-file' '
-	cd $HERE &&
+	cd "$HERE" &&
 	two=`git ls-files -s dir/two` &&
 	two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` &&
 	echo "$two" &&
@@ -54,7 +54,7 @@
 rm -f actual dir/actual
 
 test_expect_success 'diff-files' '
-	cd $HERE &&
+	cd "$HERE" &&
 	echo a >>one &&
 	echo d >>dir/two &&
 	case "`git diff-files --name-only`" in
@@ -74,7 +74,7 @@
 '
 
 test_expect_success 'write-tree' '
-	cd $HERE &&
+	cd "$HERE" &&
 	top=`git write-tree` &&
 	echo $top &&
 	cd dir &&
@@ -84,7 +84,7 @@
 '
 
 test_expect_success 'checkout-index' '
-	cd $HERE &&
+	cd "$HERE" &&
 	git checkout-index -f -u one &&
 	cmp one original.one &&
 	cd dir &&
@@ -93,7 +93,7 @@
 '
 
 test_expect_success 'read-tree' '
-	cd $HERE &&
+	cd "$HERE" &&
 	rm -f one dir/two &&
 	tree=`git write-tree` &&
 	git read-tree --reset -u "$tree" &&
@@ -107,27 +107,27 @@
 '
 
 test_expect_success 'no file/rev ambiguity check inside .git' '
-	cd $HERE &&
+	cd "$HERE" &&
 	git commit -a -m 1 &&
-	cd $HERE/.git &&
+	cd "$HERE"/.git &&
 	git show -s HEAD
 '
 
 test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-	cd $HERE &&
+	cd "$HERE" &&
 	git clone -s --bare .git foo.git &&
 	cd foo.git && GIT_DIR=. git show -s HEAD
 '
 
 # This still does not work as it should...
 : test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-	cd $HERE &&
+	cd "$HERE" &&
 	git clone -s --bare .git foo.git &&
 	cd foo.git && git show -s HEAD
 '
 
 test_expect_success 'detection should not be fooled by a symlink' '
-	cd $HERE &&
+	cd "$HERE" &&
 	rm -fr foo.git &&
 	git clone -s .git another &&
 	ln -s another yetanother &&
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
index 99985dc..f366b53 100755
--- a/t/t1303-wacky-config.sh
+++ b/t/t1303-wacky-config.sh
@@ -34,4 +34,10 @@
 	check section2.key bar
 '
 
+SECTION="test.q\"s\\sq'sp e.key"
+test_expect_success 'make sure git-config escapes section names properly' '
+	git config "$SECTION" bar &&
+	check "$SECTION" bar
+'
+
 test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 38a2bf0..85da4ca 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -51,8 +51,9 @@
 
 mkdir work || exit 1
 cd work || exit 1
-export GIT_DIR=../.git
-export GIT_CONFIG="$(pwd)"/../.git/config
+GIT_DIR=../.git
+GIT_CONFIG="$(pwd)"/../.git/config
+export GIT_DIR GIT_CONFIG
 
 git config core.bare false
 test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
@@ -64,8 +65,8 @@
 test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
 
 mv ../.git ../repo.git || exit 1
-export GIT_DIR=../repo.git
-export GIT_CONFIG="$(pwd)"/../repo.git/config
+GIT_DIR=../repo.git
+GIT_CONFIG="$(pwd)"/../repo.git/config
 
 git config core.bare false
 test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index 7ee3820..2ee88d8 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -32,24 +32,25 @@
 mv .git repo.git || exit 1
 
 say "core.worktree = relative path"
-export GIT_DIR=repo.git
-export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+GIT_DIR=repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+export GIT_DIR GIT_CONFIG
 unset GIT_WORK_TREE
 git config core.worktree ../work
 test_rev_parse 'outside'      false false false
 cd work || exit 1
-export GIT_DIR=../repo.git
-export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+GIT_DIR=../repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
 test_rev_parse 'inside'       false false true ''
 cd sub/dir || exit 1
-export GIT_DIR=../../../repo.git
-export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+GIT_DIR=../../../repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
 test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
 say "core.worktree = absolute path"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
 git config core.worktree "$(pwd)/work"
 test_rev_parse 'outside'      false false false
 cd work || exit 1
@@ -59,25 +60,26 @@
 cd ../../.. || exit 1
 
 say "GIT_WORK_TREE=relative path (override core.worktree)"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
 git config core.worktree non-existent
-export GIT_WORK_TREE=work
+GIT_WORK_TREE=work
+export GIT_WORK_TREE
 test_rev_parse 'outside'      false false false
 cd work || exit 1
-export GIT_WORK_TREE=.
+GIT_WORK_TREE=.
 test_rev_parse 'inside'       false false true ''
 cd sub/dir || exit 1
-export GIT_WORK_TREE=../..
+GIT_WORK_TREE=../..
 test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
 mv work repo.git/work
 
 say "GIT_WORK_TREE=absolute path, work tree below git dir"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
-export GIT_WORK_TREE=$(pwd)/repo.git/work
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
+GIT_WORK_TREE=$(pwd)/repo.git/work
 test_rev_parse 'outside'              false false false
 cd repo.git || exit 1
 test_rev_parse 'in repo.git'              false true  false
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index b664341..f57a6e0 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -111,4 +111,21 @@
 
 '
 
+test_expect_success 'add -n -u should not add but just report' '
+
+	(
+		echo "add '\''check'\''" &&
+		echo "remove '\''top'\''"
+	) >expect &&
+	before=$(git ls-files -s check top) &&
+	echo changed >>check &&
+	rm -f top &&
+	git add -n -u >actual &&
+	after=$(git ls-files -s check top) &&
+
+	test "$before" = "$after" &&
+	test_cmp expect actual
+
+'
+
 test_done
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index 2b21b10..4261e96 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -20,7 +20,7 @@
 '
 
 test_expect_success clone '
-	git clone file://`pwd`/.git cloned &&
+	git clone "file://$(pwd)/.git" cloned &&
 	(git rev-parse HEAD; git ls-files -s) >expected &&
 	(
 		cd cloned &&
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index fdad7da..91bb5e1 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -9,7 +9,8 @@
 '
 . ./test-lib.sh
 
-export GIT_AUTHOR_EMAIL=bogus_email_address
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
 
 test_expect_success \
     'prepare repository with topic branches' \
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 9cf873f..b9e3dbd 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -91,9 +91,8 @@
 done
 EOF
 
+test_set_editor "$(pwd)/fake-editor.sh"
 chmod a+x fake-editor.sh
-VISUAL="$(pwd)/fake-editor.sh"
-export VISUAL
 
 test_expect_success 'no changes are a nop' '
 	git rebase -i F &&
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 37944c3..1777ffe 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -4,7 +4,13 @@
 
 . ./test-lib.sh
 
+### Test that we handle space characters properly
+work_dir="$(pwd)/test dir"
+
 test_expect_success setup '
+	mkdir -p "$work_dir" &&
+	cd "$work_dir" &&
+	git init &&
 	echo a > a &&
 	git add a &&
 	git commit -m a &&
@@ -28,32 +34,35 @@
 	dotest=$2
 
 	test_expect_success "rebase$type --abort" '
+		cd "$work_dir" &&
 		# Clean up the state from the previous one
-		git reset --hard pre-rebase
-		test_must_fail git rebase'"$type"' master &&
-		test -d '$dotest' &&
+		git reset --hard pre-rebase &&
+		test_must_fail git rebase$type master &&
+		test -d "$dotest" &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-		test ! -d '$dotest'
+		test ! -d "$dotest"
 	'
 
 	test_expect_success "rebase$type --abort after --skip" '
+		cd "$work_dir" &&
 		# Clean up the state from the previous one
-		git reset --hard pre-rebase
-		test_must_fail git rebase'"$type"' master &&
-		test -d '$dotest' &&
+		git reset --hard pre-rebase &&
+		test_must_fail git rebase$type master &&
+		test -d "$dotest" &&
 		test_must_fail git rebase --skip &&
 		test $(git rev-parse HEAD) = $(git rev-parse master) &&
 		git-rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-		test ! -d '$dotest'
+		test ! -d "$dotest"
 	'
 
 	test_expect_success "rebase$type --abort after --continue" '
+		cd "$work_dir" &&
 		# Clean up the state from the previous one
-		git reset --hard pre-rebase
-		test_must_fail git rebase'"$type"' master &&
-		test -d '$dotest' &&
+		git reset --hard pre-rebase &&
+		test_must_fail git rebase$type master &&
+		test -d "$dotest" &&
 		echo c > a &&
 		echo d >> a &&
 		git add a &&
@@ -61,7 +70,7 @@
 		test $(git rev-parse HEAD) != $(git rev-parse master) &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-		test ! -d '$dotest'
+		test ! -d "$dotest"
 	'
 }
 
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index d0a440f..4911c48 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -10,7 +10,8 @@
 '
 . ./test-lib.sh
 
-export GIT_AUTHOR_EMAIL=bogus_email_address
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
 
 test_expect_success \
     'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 68c5dde..e83fa1f 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -179,4 +179,47 @@
 	test -z "`git diff-index HEAD -- foo`"
 '
 
+test_expect_success 'git add should fail atomically upon an unreadable file' '
+	git reset --hard &&
+	date >foo1 &&
+	date >foo2 &&
+	chmod 0 foo2 &&
+	test_must_fail git add --verbose . &&
+	! ( git ls-files foo1 | grep foo1 )
+'
+
+rm -f foo2
+
+test_expect_success 'git add --ignore-errors' '
+	git reset --hard &&
+	date >foo1 &&
+	date >foo2 &&
+	chmod 0 foo2 &&
+	test_must_fail git add --verbose --ignore-errors . &&
+	git ls-files foo1 | grep foo1
+'
+
+rm -f foo2
+
+test_expect_success 'git add (add.ignore-errors)' '
+	git config add.ignore-errors 1 &&
+	git reset --hard &&
+	date >foo1 &&
+	date >foo2 &&
+	chmod 0 foo2 &&
+	test_must_fail git add --verbose . &&
+	git ls-files foo1 | grep foo1
+'
+rm -f foo2
+
+test_expect_success 'git add (add.ignore-errors = false)' '
+	git config add.ignore-errors 0 &&
+	git reset --hard &&
+	date >foo1 &&
+	date >foo2 &&
+	chmod 0 foo2 &&
+	test_must_fail git add --verbose . &&
+	! ( git ls-files foo1 | grep foo1 )
+'
+
 test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index f15be93..fae64ea 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -66,6 +66,11 @@
 	grep "unchanged *+3/-0 file" output
 '
 
+if test "$(git config --bool core.filemode)" = false
+then
+    say 'skipping filemode tests (filesystem does not properly support modes)'
+else
+
 test_expect_success 'patch does not affect mode' '
 	git reset --hard &&
 	echo content >>file &&
@@ -84,5 +89,7 @@
 	git diff          file | grep "+content"
 '
 
+fi
+# end of tests disabled when filemode is not usable
 
 test_done
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
new file mode 100755
index 0000000..0cfd47c
--- /dev/null
+++ b/t/t4126-apply-empty.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='apply empty'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	>empty &&
+	git add empty &&
+	test_tick &&
+	git commit -m initial &&
+	for i in a b c d e
+	do
+		echo $i
+	done >empty &&
+	cat empty >expect &&
+	git diff |
+	sed -e "/^diff --git/d" \
+	    -e "/^index /d" \
+	    -e "s|a/empty|empty.orig|" \
+	    -e "s|b/empty|empty|" >patch0 &&
+	sed -e "s|empty|missing|" patch0 >patch1 &&
+	>empty &&
+	git update-index --refresh
+'
+
+test_expect_success 'apply empty' '
+	git reset --hard &&
+	>empty &&
+	rm -f missing &&
+	git apply patch0 &&
+	test_cmp expect empty
+'
+
+test_expect_success 'apply --index empty' '
+	git reset --hard &&
+	>empty &&
+	rm -f missing &&
+	git apply --index patch0 &&
+	test_cmp expect empty &&
+	git diff --exit-code
+'
+
+test_expect_success 'apply create' '
+	git reset --hard &&
+	>empty &&
+	rm -f missing &&
+	git apply patch1 &&
+	test_cmp expect missing
+'
+
+test_expect_success 'apply --index create' '
+	git reset --hard &&
+	>empty &&
+	rm -f missing &&
+	git apply --index patch1 &&
+	test_cmp expect missing &&
+	git diff --exit-code
+'
+
+test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index d6c55c1..a8b78eb 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -25,4 +25,22 @@
 		diff ../t5100/info$mail info$mail"
 done
 
+test_expect_success 'respect NULs' '
+
+	git mailsplit -d3 -o. ../t5100/nul &&
+	cmp ../t5100/nul 001 &&
+	(cat 001 | git mailinfo msg patch) &&
+	test 4 = $(wc -l < patch)
+
+'
+
+test_expect_success 'Preserve NULs out of MIME encoded message' '
+
+	git mailsplit -d5 -o. ../t5100/nul-b64.in &&
+	cmp ../t5100/nul-b64.in 00001 &&
+	git mailinfo msg patch <00001 &&
+	cmp ../t5100/nul-b64.expect patch
+
+'
+
 test_done
diff --git a/t/t5100/nul b/t/t5100/nul
new file mode 100644
index 0000000..3d40691
--- /dev/null
+++ b/t/t5100/nul
Binary files differ
diff --git a/t/t5100/nul-b64.expect b/t/t5100/nul-b64.expect
new file mode 100644
index 0000000..d7d680f
--- /dev/null
+++ b/t/t5100/nul-b64.expect
Binary files differ
diff --git a/t/t5100/nul-b64.in b/t/t5100/nul-b64.in
new file mode 100644
index 0000000..16540d9
--- /dev/null
+++ b/t/t5100/nul-b64.in
@@ -0,0 +1,37 @@
+From 667d8940e719cddee1cfe237cbbe215e20270b09 Mon Sep 17 00:00:00 2001
+From: Junio C Hamano <gitster@pobox.com>
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] second
+Content-Transfer-Encoding: base64
+
+LS0tCiBmaWxlIHwgIEJpbiAxMzU3IC0+IDEzNTcgYnl0ZXMKIDEgZmlsZXMgY2hhbmdlZCwg
+MCBpbnNlcnRpb25zKCspLCAwIGRlbGV0aW9ucygtKQoKZGlmZiAtLWdpdCBhL2ZpbGUgYi9m
+aWxlCmluZGV4IDc3MzYxZDguLjllMDJiZTYgMTAwNjQ0Ci0tLSBhL2ZpbGUKKysrIGIvZmls
+ZQpAQCAtMSwxMiArMSwxMiBAQAogTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNl
+Y3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIFN1c3BlbmRpc3NlCiBzaXQgYW1ldCB0dXJwaXMg
+ZWdldCBlc3QgY3Vyc3VzIGxhb3JlZXQuIEFsaXF1YW0gbWF1cmlzLiBQcmFlc2VudAotdm9s
+dXRwYXQuIFByb2luIGluIHB1cnVzLiBOdWxsYSB1cm5hIHNhcGllbiwgZGFwaWJ1cyBzaXQg
+YW1ldCwKK3ZvbHV0cGF0LiBQcm9pbiBpbiBwdXJ1cy4gTnVsbGEgdXJuYSBzYXBpZW4sIGRh
+cGkAdXMgc2l0IGFtZXQsCiBoZW5kcmVyaXQgbmVjLCB0ZW1wdXMgZXUsIG1pLiBVdCBwb3J0
+YSwgbGVvIGlkIHRpbmNpZHVudCB1bGxhbWNvcnBlciwKLXZlbGl0IGZlbGlzIHRyaXN0aXF1
+ZSBhbnRlLCBhdCBsb2JvcnRpcyBkaWFtIHBlZGUgdXQgZHVpLiBQcm9pbiBhYwordmVsaXQg
+ZmVsaXMgdHJpc3RpcXVlIGFudGUsIGF0IGxvAG9ydGlzIGRpYW0gcGVkZSB1dCBkdWkuIFBy
+b2luIGFjCiBsZWN0dXMuIERvbmVjIGF0IG1hc3NhIGFjIGlwc3VtIGhlbmRyZXJpdCBzb2xs
+aWNpdHVkaW4uIE5hbSBkaWN0dW0KIG5pc2kgc2VkIG1pLiBEdWlzIHNlZCBhbnRlLiBVdCB2
+aXRhZSBlc3QgdXQgZHVpIHVsdHJpY2llcyBkaWduaXNzaW0uCiAKLUluIHZlbCBvZGlvIGVn
+ZXQgbmlzbCBjb252YWxsaXMgdm9sdXRwYXQuIE1vcmJpIHZpdGFlIG5pYmguIE51bGxhbQor
+SW4gdmVsIG9kaW8gZWdldCBuaXNsIGNvbnZhbGxpcyB2b2x1dHBhdC4gTW9yAGkgdml0YWUg
+bmkAaC4gTnVsbGFtCiBhY2N1bXNhbiwgZG9sb3IgcXVpcyBhbGlxdWFtIHNjZWxlcmlzcXVl
+LCBlbGl0IGVuaW0gY29uZGltZW50dW0KIG1hdXJpcywgbm9uIHRyaXN0aXF1ZSBtYXVyaXMg
+dHVycGlzIGV0IG1hdXJpcy4gVXQgbm9uIG5pc2wuIE5hbSBkaWFtCiBtaSwgc2VtcGVyIHBv
+c3VlcmUsIGVsZWlmZW5kIHV0LCBhdWN0b3IgdmVsLCBlcmF0LiBTZWQgcG9zdWVyZQpAQCAt
+MTYsNyArMTYsNyBAQCBzZWQgZXN0LiBFdGlhbSBkaWFtIGZlbGlzLCBmZXJtZW50dW0gZWdl
+dCwgYWRpcGlzY2luZyBhdCwgcG9zdWVyZSBpbiwKIGR1aS4gRXRpYW0gbHVjdHVzLgogCiBO
+dWxsYSBpZCBhdWd1ZS4gTmFtIGlhY3VsaXMgYWNjdW1zYW4gbmlzaS4gU3VzcGVuZGlzc2Ug
+cG90ZW50aS4gTnVuYwotdmFyaXVzIGF1Z3VlIG5lYyBvcmNpLiBVdCBjb25kaW1lbnR1bSBk
+b2xvciBzYWdpdHRpcyBuaWJoLiBTdXNwZW5kaXNzZQordmFyaXVzIGF1Z3VlIG5lYyBvcmNp
+LiBVdCBjb25kaW1lbnR1bSBkb2xvciBzYWdpdHRpcyBuaQBoLiBTdXNwZW5kaXNzZQogdGVt
+cG9yIGxlY3R1cyBzZWQgbWFnbmEuIFN1c3BlbmRpc3NlIHBvdGVudGkuIE51bGxhbSB0ZW1w
+b3IgaXBzdW0uIFNlZAogbW9sZXN0aWUgdGVsbHVzLiBQaGFzZWxsdXMgbGlndWxhLiBJbiB2
+ZWhpY3VsYSB1bHRyaWNlcwogbmlzaS4gU3VzcGVuZGlzc2UgZmVsaXMgYXVndWUsIHBlbGxl
+bnRlc3F1ZSBhdCwgZGljdHVtIHZpdmVycmEsCi0tIAoxLjUuNS4xLjU0MC5nNTc3ODAKCg==
diff --git a/t/t5303-hash-object.sh b/t/t5303-hash-object.sh
deleted file mode 100755
index 543c078..0000000
--- a/t/t5303-hash-object.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/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/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 788b4a5..140e874 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -31,7 +31,7 @@
 	sec=$(($sec+1))
 	commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \
 		git commit-tree $tree $parents 2>>log2.txt)
-	export $name=$commit
+	eval "$name=$commit; export $name"
 	echo $commit > .git/refs/heads/$branch
 	eval ${branch}TIP=$commit
 }
@@ -129,7 +129,7 @@
 
 pull_to_client 3rd "A" $((1*3)) # old fails
 
-test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow"
+test_expect_success "clone shallow" 'git-clone --depth 2 "file://$(pwd)/." shallow'
 
 (cd shallow; git count-objects -v) > count.shallow
 
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index 670a8f1..22ba380 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -23,10 +23,13 @@
 }
 
 test_refspec push ''						invalid
-test_refspec push ':'						invalid
+test_refspec push ':'
+test_refspec push '::'						invalid
+test_refspec push '+:'
 
 test_refspec fetch ''
 test_refspec fetch ':'
+test_refspec fetch '::'						invalid
 
 test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
 test_refspec push 'refs/heads/*:refs/remotes/frotz'		invalid
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index c0dc949..1dd8eed 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -17,7 +17,7 @@
 		git show-ref -d	| sed -e "s/ /	/"
 	) >expected.all &&
 
-	git remote add self $(pwd)/.git
+	git remote add self "$(pwd)/.git"
 
 '
 
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 0a757d5..c5c5933 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -105,7 +105,7 @@
 	(
 		TRASH=$(pwd)/ &&
 		cd testrepo &&
-		git config url.$TRASH.insteadOf trash/
+		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 &&
@@ -145,8 +145,8 @@
 
 test_expect_success 'push with insteadOf' '
 	mk_empty &&
-	TRASH=$(pwd)/ &&
-	git config url.$TRASH.insteadOf trash/ &&
+	TRASH="$(pwd)/" &&
+	git config "url./$TRASH/.insteadOf" trash/ &&
 	git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
 	(
 		cd testrepo &&
@@ -165,6 +165,47 @@
 
 '
 
+test_expect_success 'push with matching heads on the command line' '
+
+	mk_test heads/master &&
+	git push testrepo : &&
+	check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'failed (non-fast-forward) push with matching heads' '
+
+	mk_test heads/master &&
+	git push testrepo : &&
+	git commit --amend -massaged &&
+	! git push testrepo &&
+	check_push_result $the_commit heads/master &&
+	git reset --hard $the_commit
+
+'
+
+test_expect_success 'push --force with matching heads' '
+
+	mk_test heads/master &&
+	git push testrepo : &&
+	git commit --amend -massaged &&
+	git push --force testrepo &&
+	! check_push_result $the_commit heads/master &&
+	git reset --hard $the_commit
+
+'
+
+test_expect_success 'push with matching heads and forced update' '
+
+	mk_test heads/master &&
+	git push testrepo : &&
+	git commit --amend -massaged &&
+	git push testrepo +: &&
+	! check_push_result $the_commit heads/master &&
+	git reset --hard $the_commit
+
+'
+
 test_expect_success 'push with no ambiguity (1)' '
 
 	mk_test heads/master &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 9484129..997b2db 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -92,4 +92,22 @@
 
 '
 
+test_expect_success 'pull --rebase dies early with dirty working directory' '
+
+	git update-ref refs/remotes/me/copy copy^ &&
+	COPY=$(git rev-parse --verify me/copy) &&
+	git rebase --onto $COPY copy &&
+	git config branch.to-rebase.remote me &&
+	git config branch.to-rebase.merge refs/heads/copy &&
+	git config branch.to-rebase.rebase true &&
+	echo dirty >> file &&
+	git add file &&
+	test_must_fail git pull &&
+	test $COPY = $(git rev-parse --verify me/copy) &&
+	git checkout HEAD -- file &&
+	git pull &&
+	test $COPY != $(git rev-parse --verify me/copy)
+
+'
+
 test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index dc9d63d..593d1a3 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -23,4 +23,11 @@
 
 '
 
+test_expect_success 'clone checks out files' '
+
+	git clone src dst &&
+	test -f dst/file
+
+'
+
 test_done
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index b6a5486..e1ca730 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -8,6 +8,8 @@
 
 base_dir=`pwd`
 
+U=$base_dir/UPLOAD_LOG
+
 test_expect_success 'preparing first repository' \
 'test_create_repo A && cd A &&
 echo first > file1 &&
@@ -50,8 +52,13 @@
 
 cd "$base_dir"
 
+rm -f $U
+
 test_expect_success 'cloning with reference (no -l -s)' \
-'git clone --reference B file://`pwd`/A D'
+'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>$U'
+
+test_expect_success 'fetched no objects' \
+'! grep "^want" $U'
 
 cd "$base_dir"
 
@@ -113,4 +120,30 @@
 
 cd "$base_dir"
 
+test_expect_success 'preparing alternate repository #1' \
+'test_create_repo F && cd F &&
+echo first > file1 &&
+git add file1 &&
+git commit -m initial'
+
+cd "$base_dir"
+
+test_expect_success 'cloning alternate repo #2 and adding changes to repo #1' \
+'git clone F G && cd F &&
+echo second > file2 &&
+git add file2 &&
+git commit -m addition'
+
+cd "$base_dir"
+
+test_expect_success 'cloning alternate repo #1, using #2 as reference' \
+'git clone --reference G F H'
+
+cd "$base_dir"
+
+test_expect_success 'cloning with reference being subset of source (-l -s)' \
+'git clone -l -s --reference A B E'
+
+cd "$base_dir"
+
 test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index 910ccb4..ef7127c 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -81,9 +81,9 @@
 cd "$base_dir"
 
 test_expect_success 'breaking of loops' \
-"echo '$base_dir/B/.git/objects' >> '$base_dir'/A/.git/objects/info/alternates&&
+'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&&
 cd C &&
-test_valid_repo"
+test_valid_repo'
 
 cd "$base_dir"
 
diff --git a/t/t6000lib.sh b/t/t6000lib.sh
index c0baaa5..f55627b 100755
--- a/t/t6000lib.sh
+++ b/t/t6000lib.sh
@@ -49,13 +49,15 @@
 	shift 1
         _save=$GIT_AUTHOR_EMAIL
 
-	export GIT_AUTHOR_EMAIL="$_author"
+	GIT_AUTHOR_EMAIL="$_author"
+	export GIT_AUTHOR_EMAIL
 	"$@"
 	if test -z "$_save"
 	then
 		unset GIT_AUTHOR_EMAIL
 	else
-		export GIT_AUTHOR_EMAIL="$_save"
+		GIT_AUTHOR_EMAIL="$_save"
+		export GIT_AUTHOR_EMAIL
 	fi
 }
 
@@ -69,7 +71,8 @@
 {
     _date=$1
     shift 1
-    export GIT_COMMITTER_DATE="$_date"
+    GIT_COMMITTER_DATE="$_date"
+    export GIT_COMMITTER_DATE
     "$@"
     unset GIT_COMMITTER_DATE
 }
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 96f3d35..b6e57b2 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -13,10 +13,11 @@
 M=1130000000
 Z=+0000
 
-export GIT_COMMITTER_EMAIL=git@comm.iter.xz
-export GIT_COMMITTER_NAME='C O Mmiter'
-export GIT_AUTHOR_NAME='A U Thor'
-export GIT_AUTHOR_EMAIL=git@au.thor.xz
+GIT_COMMITTER_EMAIL=git@comm.iter.xz
+GIT_COMMITTER_NAME='C O Mmiter'
+GIT_AUTHOR_NAME='A U Thor'
+GIT_AUTHOR_EMAIL=git@au.thor.xz
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 
 doit() {
 	OFFSET=$1; shift
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 933f567..0626544 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -126,6 +126,47 @@
 	test -z "$(git for-each-ref "refs/heads/bisect")"
 '
 
+test_expect_success 'bisect start: back in good branch' '
+	git branch > branch.output &&
+	grep "* other" branch.output > /dev/null &&
+	git bisect start $HASH4 $HASH1 -- &&
+	git bisect good &&
+	git bisect start $HASH4 $HASH1 -- &&
+	git bisect bad &&
+	git bisect reset &&
+	git branch > branch.output &&
+	grep "* other" branch.output > /dev/null
+'
+
+test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' '
+	git bisect start $HASH4 $HASH1 -- &&
+	git bisect good &&
+	test_must_fail git bisect start $HASH4 foo -- &&
+	git branch > branch.output &&
+	grep "* other" branch.output > /dev/null &&
+	test_must_fail test -e .git/BISECT_START
+'
+
+test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
+	git bisect start $HASH4 $HASH1 -- &&
+	git bisect good &&
+	test_must_fail git bisect start $HASH1 $HASH4 -- &&
+	git branch > branch.output &&
+	grep "* other" branch.output > /dev/null &&
+	test_must_fail test -e .git/BISECT_START
+'
+
+test_expect_success 'bisect start: no ".git/BISECT_START" if checkout error' '
+	echo "temp stuff" > hello &&
+	test_must_fail git bisect start $HASH4 $HASH1 -- &&
+	git branch &&
+	git branch > branch.output &&
+	grep "* other" branch.output > /dev/null &&
+	test_must_fail test -e .git/BISECT_START &&
+	test -z "$(git for-each-ref "refs/bisect/*")" &&
+	git checkout HEAD hello
+'
+
 # $HASH1 is good, $HASH4 is bad, we skip $HASH3
 # but $HASH2 is bad,
 # so we should find $HASH2 as the first bad commit
@@ -281,25 +322,6 @@
 	test $HEAD = $(cat .git/BISECT_START) &&
 	git bisect reset &&
 	test $HEAD = $(git rev-parse --verify HEAD)
-
-'
-
-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' '
@@ -309,6 +331,25 @@
 	git bisect reset
 '
 
+test_expect_success 'bisect does not create a "bisect" branch' '
+	git bisect reset &&
+	git bisect start $HASH7 $HASH1 &&
+	git branch bisect &&
+	rev_hash4=$(git rev-parse --verify HEAD) &&
+	test "$rev_hash4" = "$HASH4" &&
+	git branch -D bisect &&
+	git bisect good &&
+	git branch bisect &&
+	rev_hash6=$(git rev-parse --verify HEAD) &&
+	test "$rev_hash6" = "$HASH6" &&
+	git bisect good > my_bisect_log.txt &&
+	grep "$HASH7 is first bad commit" my_bisect_log.txt &&
+	git bisect reset &&
+	rev_hash6=$(git rev-parse --verify bisect) &&
+	test "$rev_hash6" = "$HASH6" &&
+	git branch -D bisect
+'
+
 #
 #
 test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
index c8310ae..8073e0c 100755
--- a/t/t6031-merge-recursive.sh
+++ b/t/t6031-merge-recursive.sh
@@ -3,6 +3,9 @@
 test_description='merge-recursive: handle file mode'
 . ./test-lib.sh
 
+# Note that we follow "chmod +x F" with "update-index --chmod=+x F" to
+# help filesystems that do not have the executable bit.
+
 test_expect_success 'mode change in one branch: keep changed version' '
 	: >file1 &&
 	git add file1 &&
@@ -13,7 +16,7 @@
 	git commit -m a &&
 	git checkout -b b1 master &&
 	chmod +x file1 &&
-	git add file1 &&
+	git update-index --chmod=+x file1 &&
 	git commit -m b1 &&
 	git checkout a1 &&
 	git merge-recursive master -- a1 b1 &&
@@ -26,7 +29,7 @@
 	: >file2 &&
 	H=$(git hash-object file2) &&
 	chmod +x file2 &&
-	git add file2 &&
+	git update-index --add --chmod=+x file2 &&
 	git commit -m a2 &&
 	git checkout -b b2 master &&
 	: >file2 &&
diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh
new file mode 100755
index 0000000..eac5eba
--- /dev/null
+++ b/t/t6032-merge-large-rename.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='merging with large rename matrix'
+. ./test-lib.sh
+
+count() {
+	i=1
+	while test $i -le $1; do
+		echo $i
+		i=$(($i + 1))
+	done
+}
+
+test_expect_success 'setup (initial)' '
+	touch file &&
+	git add . &&
+	git commit -m initial &&
+	git tag initial
+'
+
+make_text() {
+	echo $1: $2
+	for i in `count 20`; do
+		echo $1: $i
+	done
+	echo $1: $3
+}
+
+test_rename() {
+	test_expect_success "rename ($1, $2)" '
+	n='$1'
+	expect='$2'
+	git checkout -f master &&
+	git branch -D test$n || true &&
+	git reset --hard initial &&
+	for i in $(count $n); do
+		make_text $i initial initial >$i
+	done &&
+	git add . &&
+	git commit -m add=$n &&
+	for i in $(count $n); do
+		make_text $i changed initial >$i
+	done &&
+	git commit -a -m change=$n &&
+	git checkout -b test$n HEAD^ &&
+	for i in $(count $n); do
+		git rm $i
+		make_text $i initial changed >$i.moved
+	done &&
+	git add . &&
+	git commit -m change+rename=$n &&
+	case "$expect" in
+		ok) git merge master ;;
+		 *) test_must_fail git merge master ;;
+	esac
+	'
+}
+
+test_rename 5 ok
+
+test_expect_success 'set diff.renamelimit to 4' '
+	git config diff.renamelimit 4
+'
+test_rename 4 ok
+test_rename 5 fail
+
+test_expect_success 'set merge.renamelimit to 5' '
+	git config merge.renamelimit 5
+'
+test_rename 5 ok
+test_rename 6 fail
+
+test_done
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index bd4e49b..c9bf6fd 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -82,14 +82,14 @@
 	git diff actual expected
 '
 
-cat >expected <<\EOF
-Merge branch 'left' of ../trash
+cat >expected <<EOF
+Merge branch 'left' of ../$test
 EOF
 
 test_expect_success 'merge-msg test #2' '
 
 	git checkout master &&
-	git fetch ../trash left &&
+	git fetch ../"$test" left &&
 
 	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
 	git diff actual expected
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 16df3d4..1639c7a 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -125,7 +125,7 @@
 		 "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
 	          GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
 			git update-index --index-info &&
-		  mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved &&
+		  mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
 	test -z "$(git diff HEAD directorymoved:newsubdir)"'
 
 test_expect_success 'stops when msg filter fails' '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 1a7141e..2dcee7c 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -626,7 +626,8 @@
 
 cp -R ../t7004 ./gpghome
 chmod 0700 gpghome
-export GNUPGHOME="$(pwd)/gpghome"
+GNUPGHOME="$(pwd)/gpghome"
+export GNUPGHOME
 
 get_tag_header signed-tag $commit commit $time >expect
 echo 'A signed tag message' >>expect
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
index 02cf7c5..d8a7c79 100755
--- a/t/t7010-setup.sh
+++ b/t/t7010-setup.sh
@@ -122,7 +122,7 @@
 
 test_expect_success 'log using absolute path names' '
 	echo bb >>a/b/c/d &&
-	git commit -m "bb" $(pwd)/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 &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index a50492f..bd77239 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -112,7 +112,7 @@
 	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
 	would_clean=$(
 		cd docs &&
-		git clean -n $(pwd)/../src |
+		git clean -n "$(pwd)/../src" |
 		sed -n -e "s|^Would remove ||p"
 	) &&
 	test "$would_clean" = ../src/part3.c || {
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 2ef85a8..6c7b902 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -196,4 +196,17 @@
 	test -z "$D"
 '
 
+test_expect_success 'update --init' '
+
+	mv init init2 &&
+	git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+	git config --remove-section submodule.example
+	git submodule update init > update.out &&
+	grep "not initialized" update.out &&
+	test ! -d init/.git &&
+	git submodule update --init init &&
+	test -d init/.git
+
+'
+
 test_done
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
new file mode 100755
index 0000000..5becb3e
--- /dev/null
+++ b/t/t7402-submodule-rebase.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Johannes Schindelin
+#
+
+test_description='Test rebasing and stashing with dirty submodules'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	echo file > file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git clone . submodule &&
+	git add submodule &&
+	test_tick &&
+	git commit -m submodule &&
+	echo second line >> file &&
+	(cd submodule && git pull) &&
+	test_tick &&
+	git commit -m file-and-submodule -a
+
+'
+
+test_expect_success 'rebase with a dirty submodule' '
+
+	(cd submodule &&
+	 echo 3rd line >> file &&
+	 test_tick &&
+	 git commit -m fork -a) &&
+	echo unrelated >> file2 &&
+	git add file2 &&
+	test_tick &&
+	git commit -m unrelated file2 &&
+	echo other line >> file &&
+	test_tick &&
+	git commit -m update file &&
+	CURRENT=$(cd submodule && git rev-parse HEAD) &&
+	EXPECTED=$(git rev-parse HEAD~2:submodule) &&
+	GIT_TRACE=1 git rebase --onto HEAD~2 HEAD^ &&
+	STORED=$(git rev-parse HEAD:submodule) &&
+	test $EXPECTED = $STORED &&
+	test $CURRENT = $(cd submodule && git rev-parse HEAD)
+
+'
+
+cat > fake-editor.sh << \EOF
+#!/bin/sh
+echo $EDITOR_TEXT
+EOF
+chmod a+x fake-editor.sh
+
+test_expect_success 'interactive rebase with a dirty submodule' '
+
+	test submodule = $(git diff --name-only) &&
+	HEAD=$(git rev-parse HEAD) &&
+	GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \
+		git rebase -i HEAD^ &&
+	test submodule = $(git diff --name-only)
+
+'
+
+test_expect_success 'rebase with dirty file and submodule fails' '
+
+	echo yet another line >> file &&
+	test_tick &&
+	git commit -m next file &&
+	echo rewrite > file &&
+	test_tick &&
+	git commit -m rewrite file &&
+	echo dirty > file &&
+	! git rebase --onto HEAD~2 HEAD^
+
+'
+
+test_expect_success 'stash with a dirty submodule' '
+
+	echo new > file &&
+	CURRENT=$(cd submodule && git rev-parse HEAD) &&
+	git stash &&
+	test new != $(cat file) &&
+	test submodule = $(git diff --name-only) &&
+	test $CURRENT = $(cd submodule && git rev-parse HEAD) &&
+	git stash apply &&
+	test new = $(cat file) &&
+	test $CURRENT = $(cd submodule && git rev-parse HEAD)
+
+'
+
+test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 89710af..d3370ff 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -79,8 +79,8 @@
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -e "s/a file/an amend commit/g" < $1 > $1-
-mv $1- $1
+sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+mv "$1-" "$1"
 EOF
 chmod 755 editor
 
@@ -99,8 +99,8 @@
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -e "s/amend/older/g"  < $1 > $1-
-mv $1- $1
+sed -e "s/amend/older/g"  < "$1" > "$1-"
+mv "$1-" "$1"
 EOF
 chmod 755 editor
 
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 284c941..3531a99 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -154,6 +154,35 @@
 
 '
 
+echo "#
+# Author:    $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+#" >> expect
+
+test_expect_success 'author different from committer' '
+
+	echo >>negative &&
+	git commit -e -m "sample"
+	head -n 7 .git/COMMIT_EDITMSG >actual &&
+	test_cmp expect actual
+'
+
+mv expect expect.tmp
+sed '$d' < expect.tmp > expect
+rm -f expect.tmp
+echo "# Committer:
+#" >> expect
+unset GIT_COMMITTER_EMAIL
+unset GIT_COMMITTER_NAME
+
+test_expect_success 'committer is automatic' '
+
+	echo >>negative &&
+	git commit -e -m "sample"
+	head -n 8 .git/COMMIT_EDITMSG |	\
+	sed "s/^# Committer: .*/# Committer:/" >actual &&
+	test_cmp expect actual
+'
+
 pwd=`pwd`
 cat >> .git/FAKE_EDITOR << EOF
 #! /bin/sh
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index eff36aa..88577af 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -19,6 +19,9 @@
 exit 0
 EOF
 chmod +x fake-editor
+
+## Not using test_set_editor here so we can easily ensure the editor variable
+## is only set for the editor tests
 FAKE_EDITOR="$(pwd)/fake-editor"
 export FAKE_EDITOR
 
@@ -27,7 +30,7 @@
 	echo "more foo" >> file &&
 	git add file &&
 	echo "more foo" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit
 
 '
 
@@ -44,7 +47,7 @@
 	echo "more bar" > file &&
 	git add file &&
 	echo "more bar" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -71,7 +74,7 @@
 	echo "more more" >> file &&
 	git add file &&
 	echo "more more" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit
 
 '
 
@@ -88,7 +91,7 @@
 	echo "even more more" >> file &&
 	git add file &&
 	echo "even more more" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -111,7 +114,7 @@
 	echo "more another" >> file &&
 	git add file &&
 	echo "more another" > FAKE_MSG &&
-	! (GIT_EDITOR="$FAKE_EDITOR" git commit)
+	! (GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit)
 
 '
 
@@ -128,7 +131,7 @@
 	echo "more stuff" >> file &&
 	git add file &&
 	echo "more stuff" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -146,7 +149,7 @@
 	echo "content again" >> file &&
 	git add file &&
 	echo "content again" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit -m "content again"
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -m "content again"
 
 '
 
@@ -163,7 +166,7 @@
 	echo "even more content" >> file &&
 	git add file &&
 	echo "even more content" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -193,7 +196,7 @@
 	echo "additional content" >> file &&
 	git add file &&
 	echo "additional content" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit &&
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
 	commit_msg_is "new message"
 
 '
@@ -212,7 +215,7 @@
 	echo "more plus" >> file &&
 	git add file &&
 	echo "more plus" > FAKE_MSG &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify &&
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
 	commit_msg_is "more plus"
 
 '
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index 802aa62..cd6c7c8 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -18,6 +18,9 @@
 exit 0
 EOF
 chmod +x fake-editor
+
+## Not using test_set_editor here so we can easily ensure the editor variable
+## is only set for the editor tests
 FAKE_EDITOR="$(pwd)/fake-editor"
 export FAKE_EDITOR
 
@@ -58,7 +61,7 @@
 
 	echo "more" >> file &&
 	git add file &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit -e -m "more more" &&
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -m "more more" &&
 	test "`git log -1 --pretty=format:%s`" = message
 
 '
@@ -85,7 +88,7 @@
 
 	echo "more" >> file &&
 	git add file &&
-	(echo more more | GIT_EDITOR="$FAKE_EDITOR" git commit -e -F -) &&
+	(echo more more | GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -F -) &&
 	test "`git log -1 --pretty=format:%s`" = message
 
 '
@@ -104,7 +107,7 @@
 
 	echo "more more" >> file &&
 	git add file &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit &&
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
 	test "`git log -1 --pretty=format:%s`" = default
 
 '
@@ -114,7 +117,7 @@
 	head=`git rev-parse HEAD` &&
 	echo "more" >> file &&
 	git add file &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit --amend &&
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --amend &&
 	test "`git log -1 --pretty=format:%s`" = "$head"
 
 '
@@ -124,7 +127,7 @@
 	head=`git rev-parse HEAD` &&
 	echo "more" >> file &&
 	git add file &&
-	GIT_EDITOR="$FAKE_EDITOR" git commit -c $head &&
+	GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head &&
 	test "`git log -1 --pretty=format:%s`" = "$head"
 
 '
@@ -139,7 +142,7 @@
 	head=`git rev-parse HEAD` &&
 	echo "more" >> file &&
 	git add file &&
-	! GIT_EDITOR="$FAKE_EDITOR" git commit -c $head
+	! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
 
 '
 
@@ -148,7 +151,7 @@
 	head=`git rev-parse HEAD` &&
 	echo "more" >> file &&
 	git add file &&
-	! GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify -c $head
+	! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
 
 '
 
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
new file mode 100755
index 0000000..6a5211f
--- /dev/null
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='git-repack works correctly'
+
+. ./test-lib.sh
+
+test_expect_success '-A option leaves unreachable objects unpacked' '
+	echo content > file1 &&
+	git add . &&
+	git commit -m initial_commit &&
+	# create a transient branch with unique content
+	git checkout -b transient_branch &&
+	echo more content >> file1 &&
+	# record the objects created in the database for file, commit, tree
+	fsha1=$(git hash-object file1) &&
+	git commit -a -m more_content &&
+	csha1=$(git rev-parse HEAD^{commit}) &&
+	tsha1=$(git rev-parse HEAD^{tree}) &&
+	git checkout master &&
+	echo even more content >> file1 &&
+	git commit -a -m even_more_content &&
+	# delete the transient branch
+	git branch -D transient_branch &&
+	# pack the repo
+	git repack -A -d -l &&
+	# verify objects are packed in repository
+	test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+		   grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+		   sort | uniq | wc -l) &&
+	git show $fsha1 &&
+	git show $csha1 &&
+	git show $tsha1 &&
+	# now expire the reflog
+	sleep 1 &&
+	git reflog expire --expire-unreachable=now --all &&
+	# and repack
+	git repack -A -d -l &&
+	# verify objects are retained unpacked
+	test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+		   grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+		   sort | uniq | wc -l) &&
+	git show $fsha1 &&
+	git show $csha1 &&
+	git show $tsha1
+'
+
+test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index c0973b4..3e4eb63 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -139,15 +139,16 @@
 
 test_expect_success 'setup fake editor' '
 	(echo "#!/bin/sh" &&
-	 echo "echo fake edit >>\$1"
+	 echo "echo fake edit >>\"\$1\""
 	) >fake-editor &&
 	chmod +x fake-editor
 '
 
+test_set_editor "$(pwd)/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 \
@@ -166,4 +167,129 @@
 	grep "Subject:.*Second" msgtxt2
 '
 
+cat >expected-show-all-headers <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: cc@example.com, A <author@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cc set' '
+	git config sendemail.cc cc@example.com &&
+	git send-email \
+		--dry-run \
+		--from="Example <from@example.com>" \
+		--to=to@example.com \
+		--smtp-server relay.example.com \
+		$patches |
+	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \
+		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+		>actual-show-all-headers &&
+	test_cmp expected-show-all-headers actual-show-all-headers
+'
+
+cat >expected-show-all-headers <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cc unset' '
+	git config --unset sendemail.cc &&
+	git send-email \
+		--dry-run \
+		--from="Example <from@example.com>" \
+		--to=to@example.com \
+		--smtp-server relay.example.com \
+		$patches |
+	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \
+		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+		>actual-show-all-headers &&
+	test_cmp expected-show-all-headers actual-show-all-headers
+'
+
+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 4e24ab3..bdf29c1 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -20,39 +20,39 @@
 echo 'define NO_SVN_TESTS to skip git-svn tests'
 
 test_expect_success \
-    'initialize git-svn' "
+    'initialize git-svn' '
 	mkdir import &&
 	cd import &&
 	echo foo > foo &&
 	ln -s foo foo.link
 	mkdir -p dir/a/b/c/d/e &&
-	echo 'deep dir' > dir/a/b/c/d/e/file &&
+	echo "deep dir" > dir/a/b/c/d/e/file &&
 	mkdir bar &&
-	echo 'zzz' > bar/zzz &&
-	echo '#!/bin/sh' > exec.sh &&
+	echo "zzz" > bar/zzz &&
+	echo "#!/bin/sh" > exec.sh &&
 	chmod +x exec.sh &&
-	svn import -m 'import for git-svn' . $svnrepo >/dev/null &&
+	svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
 	cd .. &&
 	rm -rf import &&
-	git-svn init $svnrepo"
+	git-svn init "$svnrepo"'
 
 test_expect_success \
     'import an SVN revision into git' \
     'git-svn fetch'
 
-test_expect_success "checkout from svn" "svn co $svnrepo '$SVN_TREE'"
+test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
 name='try a deep --rmdir with a commit'
-test_expect_success "$name" "
+test_expect_success "$name" '
 	git checkout -f -b mybranch remotes/git-svn &&
 	mv dir/a/b/c/d/e/file dir/file &&
 	cp dir/file file &&
 	git update-index --add --remove dir/a/b/c/d/e/file dir/file file &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch &&
-	svn up '$SVN_TREE' &&
-	test -d '$SVN_TREE'/dir && test ! -d '$SVN_TREE'/dir/a"
+	svn up "$SVN_TREE" &&
+	test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
 
 
 name='detect node change from file to directory #1'
@@ -68,108 +68,108 @@
 
 
 name='detect node change from directory to file #1'
-test_expect_success "$name" "
-	rm -rf dir '$GIT_DIR'/index &&
+test_expect_success "$name" '
+	rm -rf dir "$GIT_DIR"/index &&
 	git checkout -f -b mybranch2 remotes/git-svn &&
 	mv bar/zzz zzz &&
 	rm -rf bar &&
 	mv zzz bar &&
 	git update-index --remove -- bar/zzz &&
 	git update-index --add -- bar &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	! git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch2" || true
+		remotes/git-svn..mybranch2' || true
 
 
 name='detect node change from file to directory #2'
-test_expect_success "$name" "
-	rm -f '$GIT_DIR'/index &&
+test_expect_success "$name" '
+	rm -f "$GIT_DIR"/index &&
 	git checkout -f -b mybranch3 remotes/git-svn &&
 	rm bar/zzz &&
 	git update-index --remove bar/zzz &&
 	mkdir bar/zzz &&
 	echo yyy > bar/zzz/yyy &&
 	git update-index --add bar/zzz/yyy &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	! git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch3" || true
+		remotes/git-svn..mybranch3' || true
 
 
 name='detect node change from directory to file #2'
-test_expect_success "$name" "
-	rm -f '$GIT_DIR'/index &&
+test_expect_success "$name" '
+	rm -f "$GIT_DIR"/index &&
 	git checkout -f -b mybranch4 remotes/git-svn &&
 	rm -rf dir &&
 	git update-index --remove -- dir/file &&
 	touch dir &&
 	echo asdf > dir &&
 	git update-index --add -- dir &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	! git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch4" || true
+		remotes/git-svn..mybranch4' || true
 
 
 name='remove executable bit from a file'
-test_expect_success "$name" "
-	rm -f '$GIT_DIR'/index &&
+test_expect_success "$name" '
+	rm -f "$GIT_DIR"/index &&
 	git checkout -f -b mybranch5 remotes/git-svn &&
 	chmod -x exec.sh &&
 	git update-index exec.sh &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch5 &&
-	svn up '$SVN_TREE' &&
-	test ! -x '$SVN_TREE'/exec.sh"
+	svn up "$SVN_TREE" &&
+	test ! -x "$SVN_TREE"/exec.sh'
 
 
 name='add executable bit back file'
-test_expect_success "$name" "
+test_expect_success "$name" '
 	chmod +x exec.sh &&
 	git update-index exec.sh &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch5 &&
-	svn up '$SVN_TREE' &&
-	test -x '$SVN_TREE'/exec.sh"
+	svn up "$SVN_TREE" &&
+	test -x "$SVN_TREE"/exec.sh'
 
 
 name='executable file becomes a symlink to bar/zzz (file)'
-test_expect_success "$name" "
+test_expect_success "$name" '
 	rm exec.sh &&
 	ln -s bar/zzz exec.sh &&
 	git update-index exec.sh &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch5 &&
-	svn up '$SVN_TREE' &&
-	test -L '$SVN_TREE'/exec.sh"
+	svn up "$SVN_TREE" &&
+	test -L "$SVN_TREE"/exec.sh'
 
 name='new symlink is added to a file that was also just made executable'
 
-test_expect_success "$name" "
+test_expect_success "$name" '
 	chmod +x bar/zzz &&
 	ln -s bar/zzz exec-2.sh &&
 	git update-index --add bar/zzz exec-2.sh &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch5 &&
-	svn up '$SVN_TREE' &&
-	test -x '$SVN_TREE'/bar/zzz &&
-	test -L '$SVN_TREE'/exec-2.sh"
+	svn up "$SVN_TREE" &&
+	test -x "$SVN_TREE"/bar/zzz &&
+	test -L "$SVN_TREE"/exec-2.sh'
 
 name='modify a symlink to become a file'
-test_expect_success "$name" "
+test_expect_success "$name" '
 	echo git help > help || true &&
 	rm exec-2.sh &&
 	cp help exec-2.sh &&
 	git update-index exec-2.sh &&
-	git commit -m '$name' &&
+	git commit -m "$name" &&
 	git-svn set-tree --find-copies-harder --rmdir \
 		remotes/git-svn..mybranch5 &&
-	svn up '$SVN_TREE' &&
-	test -f '$SVN_TREE'/exec-2.sh &&
-	test ! -L '$SVN_TREE'/exec-2.sh &&
-	git diff help $SVN_TREE/exec-2.sh"
+	svn up "$SVN_TREE" &&
+	test -f "$SVN_TREE"/exec-2.sh &&
+	test ! -L "$SVN_TREE"/exec-2.sh &&
+	git diff help "$SVN_TREE"/exec-2.sh'
 
 if test "$have_utf8" = t
 then
@@ -190,10 +190,10 @@
 GIT_SVN_ID=alt
 export GIT_SVN_ID
 test_expect_success "$name" \
-    "git-svn init $svnrepo && git-svn fetch &&
+    'git-svn init "$svnrepo" && git-svn fetch &&
      git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a &&
      git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
-     git diff a b"
+     git diff a b'
 
 name='check imported tree checksums expected tree checksums'
 rm -f expected
@@ -219,22 +219,22 @@
 	! git-svn migrate
 "
 
-test_expect_success 'exit if init-ing a would clobber a URL' "
-        svnadmin create ${PWD}/svnrepo2 &&
-        svn mkdir -m 'mkdir bar' ${svnrepo}2/bar &&
+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
-        "
+                                "^bar:refs/remotes/git-svn$" &&
+	! git-svn init "${svnrepo}2/bar"
+        '
 
 test_expect_success \
-  'init allows us to connect to another directory in the same repo' "
-        git-svn init --minimize-url -i bar $svnrepo/bar &&
+  'init allows us to connect to another directory in the same repo' '
+        git-svn init --minimize-url -i bar "$svnrepo/bar" &&
         git config --get svn-remote.svn.fetch \
-                              '^bar:refs/remotes/bar$' &&
+                              "^bar:refs/remotes/bar$" &&
         git config --get svn-remote.svn.fetch \
-                              '^:refs/remotes/git-svn$'
-        "
+                              "^:refs/remotes/git-svn$"
+        '
 
 test_expect_success 'able to dcommit to a subdirectory' "
 	git-svn fetch -i bar &&
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index d7a7047..f420796 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -52,7 +52,7 @@
 cd ..
 
 rm -rf import
-test_expect_success 'checkout working copy from svn' "svn co $svnrepo test_wc"
+test_expect_success 'checkout working copy from svn' 'svn co "$svnrepo" test_wc'
 test_expect_success 'setup some commits to svn' \
 	'cd test_wc &&
 		echo Greetings >> kw.c &&
@@ -66,7 +66,7 @@
 		svn commit -m "Propset Id" &&
 	cd ..'
 
-test_expect_success 'initialize git-svn' "git-svn init $svnrepo"
+test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"'
 test_expect_success 'fetch revisions from svn' 'git-svn fetch'
 
 name='test svn:keywords ignoring'
@@ -90,9 +90,9 @@
 	 cd ..'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
-	"git-svn fetch &&
+	'git-svn fetch &&
 	 git pull . remotes/git-svn &&
-	 svn co $svnrepo new_wc"
+	 svn co "$svnrepo" new_wc'
 
 for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
 do
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
index 4e08083..0e7ce34 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -2,29 +2,29 @@
 test_description='git-svn rmdir'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
 	mkdir import &&
 	cd import &&
 	mkdir -p deeply/nested/directory/number/1 &&
 	mkdir -p deeply/nested/directory/number/2 &&
 	echo foo > deeply/nested/directory/number/1/file &&
 	echo foo > deeply/nested/directory/number/2/another &&
-	svn import -m 'import for git-svn' . $svnrepo &&
+	svn import -m "import for git-svn" . "$svnrepo" &&
 	cd ..
-	"
+	'
 
-test_expect_success 'mirror via git-svn' "
-	git-svn init $svnrepo &&
+test_expect_success 'mirror via git-svn' '
+	git-svn init "$svnrepo" &&
 	git-svn fetch &&
 	git checkout -f -b test-rmdir remotes/git-svn
-	"
+	'
 
-test_expect_success 'Try a commit on rmdir' "
+test_expect_success 'Try a commit on rmdir' '
 	git rm -f deeply/nested/directory/number/2/another &&
-	git commit -a -m 'remove another' &&
+	git commit -a -m "remove another" &&
 	git-svn set-tree --rmdir HEAD &&
-	svn ls -R $svnrepo | grep ^deeply/nested/directory/number/1
-	"
+	svn ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1
+	'
 
 
 test_done
diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh
index 0f0b0fd..9ffd845 100755
--- a/t/t9103-git-svn-tracked-directory-removed.sh
+++ b/t/t9103-git-svn-tracked-directory-removed.sh
@@ -10,30 +10,30 @@
 	mkdir import &&
 	mkdir import/trunk &&
 	echo hello >> import/trunk/README &&
-	svn import -m initial import $svnrepo &&
+	svn import -m initial import "$svnrepo" &&
 	rm -rf import &&
-	svn co $svnrepo/trunk trunk &&
+	svn co "$svnrepo"/trunk trunk &&
 	echo bye bye >> trunk/README &&
-	svn rm -m "gone" $svnrepo/trunk &&
+	svn rm -m "gone" "$svnrepo"/trunk &&
 	rm -rf trunk &&
 	mkdir trunk &&
 	echo "new" > trunk/FOLLOWME &&
-	svn import -m "new trunk" trunk $svnrepo/trunk
+	svn import -m "new trunk" trunk "$svnrepo"/trunk
 '
 
 test_expect_success 'clone repo with git' '
-	git svn clone -s $svnrepo x &&
+	git svn clone -s "$svnrepo" x &&
 	test -f x/FOLLOWME &&
 	test ! -f x/README
 '
 
-test_expect_success 'make sure r2 still has old file' '
+test_expect_success 'make sure r2 still has old file' "
 	cd x &&
-		test -n "$(git svn find-rev r1)" &&
-		git reset --hard $(git svn find-rev r1) &&
+		test -n \"\$(git svn find-rev r1)\" &&
+		git reset --hard \$(git svn find-rev r1) &&
 		test -f README &&
 		test ! -f FOLLOWME &&
-		test x$(git svn find-rev r2) = x
-'
+		test x\$(git svn find-rev r2) = x
+"
 
 test_done
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 7ba7630..4d964e2 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -6,165 +6,165 @@
 test_description='git-svn fetching'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
 	mkdir import &&
 	cd import &&
 	mkdir -p trunk &&
 	echo hello > trunk/readme &&
-	svn import -m 'initial' . $svnrepo &&
+	svn import -m "initial" . "$svnrepo" &&
 	cd .. &&
-	svn co $svnrepo wc &&
+	svn co "$svnrepo" wc &&
 	cd wc &&
 	echo world >> trunk/readme &&
 	poke trunk/readme &&
-	svn commit -m 'another commit' &&
+	svn commit -m "another commit" &&
 	svn up &&
 	svn mv trunk thunk &&
 	echo goodbye >> thunk/readme &&
 	poke thunk/readme &&
-	svn commit -m 'bye now' &&
+	svn commit -m "bye now" &&
 	cd ..
-	"
+	'
 
-test_expect_success 'init and fetch a moved directory' "
-	git-svn init --minimize-url -i thunk $svnrepo/thunk &&
+test_expect_success 'init and fetch a moved directory' '
+	git-svn init --minimize-url -i thunk "$svnrepo"/thunk &&
 	git-svn fetch -i thunk &&
-	test \"\`git rev-parse --verify refs/remotes/thunk@2\`\" \
-           = \"\`git rev-parse --verify refs/remotes/thunk~1\`\" &&
-        test \"\`git cat-file blob refs/remotes/thunk:readme |\
-                 sed -n -e '3p'\`\" = goodbye &&
-	test -z \"\`git config --get svn-remote.svn.fetch \
-	         '^trunk:refs/remotes/thunk@2$'\`\"
-	"
+	test "`git rev-parse --verify refs/remotes/thunk@2`" \
+           = "`git rev-parse --verify refs/remotes/thunk~1`" &&
+        test "`git cat-file blob refs/remotes/thunk:readme |\
+                 sed -n -e "3p"`" = goodbye &&
+	test -z "`git config --get svn-remote.svn.fetch \
+	         "^trunk:refs/remotes/thunk@2$"`"
+	'
 
-test_expect_success 'init and fetch from one svn-remote' "
-        git config svn-remote.svn.url $svnrepo &&
+test_expect_success 'init and fetch from one svn-remote' '
+        git config svn-remote.svn.url "$svnrepo" &&
         git config --add svn-remote.svn.fetch \
           trunk:refs/remotes/svn/trunk &&
         git config --add svn-remote.svn.fetch \
           thunk:refs/remotes/svn/thunk &&
         git-svn fetch -i svn/thunk &&
-	test \"\`git rev-parse --verify refs/remotes/svn/trunk\`\" \
-           = \"\`git rev-parse --verify refs/remotes/svn/thunk~1\`\" &&
-        test \"\`git cat-file blob refs/remotes/svn/thunk:readme |\
-                 sed -n -e '3p'\`\" = goodbye
-        "
+	test "`git rev-parse --verify refs/remotes/svn/trunk`" \
+           = "`git rev-parse --verify refs/remotes/svn/thunk~1`" &&
+        test "`git cat-file blob refs/remotes/svn/thunk:readme |\
+                 sed -n -e "3p"`" = goodbye
+        '
 
-test_expect_success 'follow deleted parent' "
-        (svn cp -m 'resurrecting trunk as junk' \
-               $svnrepo/trunk@2 $svnrepo/junk ||
-         svn cp -m 'resurrecting trunk as junk' \
-               -r2 $svnrepo/trunk $svnrepo/junk) &&
+test_expect_success 'follow deleted parent' '
+        (svn cp -m "resurrecting trunk as junk" \
+               "$svnrepo"/trunk@2 "$svnrepo"/junk ||
+         svn cp -m "resurrecting trunk as junk" \
+               -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
         git config --add svn-remote.svn.fetch \
           junk:refs/remotes/svn/junk &&
         git-svn fetch -i svn/thunk &&
         git-svn fetch -i svn/junk &&
-        test -z \"\`git diff svn/junk svn/trunk\`\" &&
-        test \"\`git merge-base svn/junk svn/trunk\`\" \
-           = \"\`git rev-parse svn/trunk\`\"
-        "
+        test -z "`git diff svn/junk svn/trunk`" &&
+        test "`git merge-base svn/junk svn/trunk`" \
+           = "`git rev-parse svn/trunk`"
+        '
 
-test_expect_success 'follow larger parent' "
+test_expect_success 'follow larger parent' '
         mkdir -p import/trunk/thunk/bump/thud &&
         echo hi > import/trunk/thunk/bump/thud/file &&
-        svn import -m 'import a larger parent' import $svnrepo/larger-parent &&
-        svn cp -m 'hi' $svnrepo/larger-parent $svnrepo/another-larger &&
+        svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
+        svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
         git-svn init --minimize-url -i larger \
-          $svnrepo/another-larger/trunk/thunk/bump/thud &&
+          "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
         git-svn fetch -i larger &&
         git rev-parse --verify refs/remotes/larger &&
         git rev-parse --verify \
            refs/remotes/larger-parent/trunk/thunk/bump/thud &&
-        test \"\`git merge-base \
+        test "`git merge-base \
                  refs/remotes/larger-parent/trunk/thunk/bump/thud \
-                 refs/remotes/larger\`\" = \
-             \"\`git rev-parse refs/remotes/larger\`\"
+                 refs/remotes/larger`" = \
+             "`git rev-parse refs/remotes/larger`"
         true
-        "
+        '
 
-test_expect_success 'follow higher-level parent' "
-        svn mkdir -m 'follow higher-level parent' $svnrepo/blob &&
-        svn co $svnrepo/blob blob &&
+test_expect_success 'follow higher-level parent' '
+        svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
+        svn co "$svnrepo"/blob blob &&
         cd blob &&
                 echo hi > hi &&
                 svn add hi &&
-                svn commit -m 'hihi' &&
+                svn commit -m "hihi" &&
                 cd ..
-        svn mkdir -m 'new glob at top level' $svnrepo/glob &&
-        svn mv -m 'move blob down a level' $svnrepo/blob $svnrepo/glob/blob &&
-        git-svn init --minimize-url -i blob $svnrepo/glob/blob &&
+        svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
+        svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
+        git-svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
         git-svn fetch -i blob
-        "
+        '
 
-test_expect_success 'follow deleted directory' "
-	svn mv -m 'bye!' $svnrepo/glob/blob/hi $svnrepo/glob/blob/bye &&
-	svn rm -m 'remove glob' $svnrepo/glob &&
-	git-svn init --minimize-url -i glob $svnrepo/glob &&
+test_expect_success 'follow deleted directory' '
+	svn mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye &&
+	svn rm -m "remove glob" "$svnrepo"/glob &&
+	git-svn init --minimize-url -i glob "$svnrepo"/glob &&
 	git-svn fetch -i glob &&
-	test \"\`git cat-file blob refs/remotes/glob:blob/bye\`\" = hi &&
-	test \"\`git ls-tree refs/remotes/glob | wc -l \`\" -eq 1
-	"
+	test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi &&
+	test "`git ls-tree refs/remotes/glob | wc -l `" -eq 1
+	'
 
 # ref: r9270 of the Subversion repository: (http://svn.collab.net/repos/svn)
 # in trunk/subversion/bindings/swig/perl
-test_expect_success 'follow-parent avoids deleting relevant info' "
+test_expect_success 'follow-parent avoids deleting relevant info' '
 	mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
 	for i in a b c ; do \
-	  echo \$i > import/trunk/subversion/bindings/swig/perl/\$i.pm &&
-	  echo _\$i > import/trunk/subversion/bindings/swig/perl/t/\$i.t; \
+	  echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&
+	  echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \
 	done &&
-	  echo 'bad delete test' > \
+	  echo "bad delete test" > \
 	   import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
-	  echo 'bad delete test 2' > \
+	  echo "bad delete test 2" > \
 	   import/trunk/subversion/bindings/swig/perl/another-larger &&
 	cd import &&
-	  svn import -m 'r9270 test' . $svnrepo/r9270 &&
+	  svn import -m "r9270 test" . "$svnrepo"/r9270 &&
 	cd .. &&
-	svn co $svnrepo/r9270/trunk/subversion/bindings/swig/perl r9270 &&
+	svn co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
 	cd r9270 &&
 	  svn mkdir native &&
 	  svn mv t native/t &&
-	  for i in a b c; do svn mv \$i.pm native/\$i.pm; done &&
+	  for i in a b c; do svn mv $i.pm native/$i.pm; done &&
 	  echo z >> native/t/c.t &&
 	  poke native/t/c.t &&
-	  svn commit -m 'reorg test' &&
+	  svn commit -m "reorg test" &&
 	cd .. &&
 	git-svn init --minimize-url -i r9270-t \
-	  $svnrepo/r9270/trunk/subversion/bindings/swig/perl/native/t &&
+	  "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t &&
 	git-svn fetch -i r9270-t &&
-	test \`git rev-list r9270-t | wc -l\` -eq 2 &&
-	test \"\`git ls-tree --name-only r9270-t~1\`\" = \
-	     \"\`git ls-tree --name-only r9270-t\`\"
-	"
+	test `git rev-list r9270-t | wc -l` -eq 2 &&
+	test "`git ls-tree --name-only r9270-t~1`" = \
+	     "`git ls-tree --name-only r9270-t`"
+	'
 
-test_expect_success "track initial change if it was only made to parent" "
-	svn cp -m 'wheee!' $svnrepo/r9270/trunk $svnrepo/r9270/drunk &&
+test_expect_success "track initial change if it was only made to parent" '
+	svn cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk &&
 	git-svn init --minimize-url -i r9270-d \
-	  $svnrepo/r9270/drunk/subversion/bindings/swig/perl/native/t &&
+	  "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t &&
 	git-svn fetch -i r9270-d &&
-	test \`git rev-list r9270-d | wc -l\` -eq 3 &&
-	test \"\`git ls-tree --name-only r9270-t\`\" = \
-	     \"\`git ls-tree --name-only r9270-d\`\" &&
-	test \"\`git rev-parse r9270-t\`\" = \
-	     \"\`git rev-parse r9270-d~1\`\"
-	"
+	test `git rev-list r9270-d | wc -l` -eq 3 &&
+	test "`git ls-tree --name-only r9270-t`" = \
+	     "`git ls-tree --name-only r9270-d`" &&
+	test "`git rev-parse r9270-t`" = \
+	     "`git rev-parse r9270-d~1`"
+	'
 
-test_expect_success "track multi-parent paths" "
-	svn cp -m 'resurrect /glob' $svnrepo/r9270 $svnrepo/glob &&
+test_expect_success "track multi-parent paths" '
+	svn cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob &&
 	git-svn multi-fetch &&
-	test \`git cat-file commit refs/remotes/glob | \
-	       grep '^parent ' | wc -l\` -eq 2
-	"
+	test `git cat-file commit refs/remotes/glob | \
+	       grep "^parent " | wc -l` -eq 2
+	'
 
 test_expect_success "multi-fetch continues to work" "
 	git-svn multi-fetch
 	"
 
-test_expect_success "multi-fetch works off a 'clean' repository" "
-	rm -r $GIT_DIR/svn $GIT_DIR/refs/remotes $GIT_DIR/logs &&
-	mkdir $GIT_DIR/svn &&
+test_expect_success "multi-fetch works off a 'clean' repository" '
+	rm -r "$GIT_DIR/svn" "$GIT_DIR/refs/remotes" "$GIT_DIR/logs" &&
+	mkdir "$GIT_DIR/svn" &&
 	git-svn multi-fetch
-	"
+	'
 
 test_debug 'gitk --all &'
 
diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
index 318e172..6323036 100755
--- a/t/t9105-git-svn-commit-diff.sh
+++ b/t/t9105-git-svn-commit-diff.sh
@@ -4,18 +4,18 @@
 test_description='git-svn commit-diff'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
 	mkdir import &&
 	cd import &&
 	echo hello > readme &&
-	svn import -m 'initial' . $svnrepo &&
+	svn import -m "initial" . "$svnrepo" &&
 	cd .. &&
 	echo hello > readme &&
 	git update-index --add readme &&
-	git commit -a -m 'initial' &&
+	git commit -a -m "initial" &&
 	echo world >> readme &&
-	git commit -a -m 'another'
-	"
+	git commit -a -m "another"
+	'
 
 head=`git rev-parse --verify HEAD^0`
 prev=`git rev-parse --verify HEAD^1`
@@ -24,20 +24,20 @@
 # commit, so only a basic test of functionality is needed since we've
 # already tested commit extensively elsewhere
 
-test_expect_success 'test the commit-diff command' "
-	test -n '$prev' && test -n '$head' &&
-	git-svn commit-diff -r1 '$prev' '$head' '$svnrepo' &&
-	svn co $svnrepo wc &&
+test_expect_success 'test the commit-diff command' '
+	test -n "$prev" && test -n "$head" &&
+	git-svn commit-diff -r1 "$prev" "$head" "$svnrepo" &&
+	svn co "$svnrepo" wc &&
 	cmp readme wc/readme
-	"
+	'
 
-test_expect_success 'commit-diff to a sub-directory (with git-svn config)' "
-	svn import -m 'sub-directory' import $svnrepo/subdir &&
-	git-svn init --minimize-url $svnrepo/subdir &&
+test_expect_success 'commit-diff to a sub-directory (with git-svn config)' '
+	svn import -m "sub-directory" import "$svnrepo"/subdir &&
+	git-svn init --minimize-url "$svnrepo"/subdir &&
 	git-svn fetch &&
-	git-svn commit-diff -r3 '$prev' '$head' &&
-	svn cat $svnrepo/subdir/readme > readme.2 &&
+	git-svn commit-diff -r3 "$prev" "$head" &&
+	svn cat "$svnrepo"/subdir/readme > readme.2 &&
 	cmp readme readme.2
-	"
+	'
 
 test_done
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index f74ab12..58a3a7b 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -4,56 +4,56 @@
 test_description='git-svn commit-diff clobber'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
 	mkdir import &&
 	cd import &&
 	echo initial > file &&
-	svn import -m 'initial' . $svnrepo &&
+	svn import -m "initial" . "$svnrepo" &&
 	cd .. &&
 	echo initial > file &&
 	git update-index --add file &&
-	git commit -a -m 'initial'
-	"
-test_expect_success 'commit change from svn side' "
-	svn co $svnrepo t.svn &&
+	git commit -a -m "initial"
+	'
+test_expect_success 'commit change from svn side' '
+	svn co "$svnrepo" t.svn &&
 	cd t.svn &&
 	echo second line from svn >> file &&
 	poke file &&
-	svn commit -m 'second line from svn' &&
+	svn commit -m "second line from svn" &&
 	cd .. &&
 	rm -rf t.svn
-	"
+	'
 
-test_expect_success '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
-"
+	git commit -a -m "second line from git" &&
+	! git-svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
+'
 
-test_expect_success 'commit complementing change from git' "
+test_expect_success 'commit complementing change from git' '
 	git reset --hard HEAD~1 &&
 	echo second line from svn >> file &&
-	git commit -a -m 'second line from svn' &&
+	git commit -a -m "second line from svn" &&
 	echo third line from git >> file &&
-	git commit -a -m 'third line from git' &&
-	git-svn commit-diff -r2 HEAD~1 HEAD $svnrepo
-	"
+	git commit -a -m "third line from git" &&
+	git-svn commit-diff -r2 HEAD~1 HEAD "$svnrepo"
+	'
 
-test_expect_success 'dcommit fails to commit because of conflict' "
-	git-svn init $svnrepo &&
+test_expect_success 'dcommit fails to commit because of conflict' '
+	git-svn init "$svnrepo" &&
 	git-svn fetch &&
 	git reset --hard refs/remotes/git-svn &&
-	svn co $svnrepo t.svn &&
+	svn co "$svnrepo" t.svn &&
 	cd t.svn &&
 	echo fourth line from svn >> file &&
 	poke file &&
-	svn commit -m 'fourth line from svn' &&
+	svn commit -m "fourth line from svn" &&
 	cd .. &&
 	rm -rf t.svn &&
-	echo 'fourth line from git' >> file &&
-	git commit -a -m 'fourth line from git' &&
+	echo "fourth line from git" >> file &&
+	git commit -a -m "fourth line from git" &&
 	! git-svn dcommit
-	"
+	'
 
 test_expect_success 'dcommit does the svn equivalent of an index merge' "
 	git reset --hard refs/remotes/git-svn &&
@@ -66,15 +66,15 @@
 	git-svn dcommit
 	"
 
-test_expect_success 'commit another change from svn side' "
-	svn co $svnrepo t.svn &&
+test_expect_success 'commit another change from svn side' '
+	svn co "$svnrepo" t.svn &&
 	cd t.svn &&
 		echo third line from svn >> file &&
 		poke file &&
-		svn commit -m 'third line from svn' &&
+		svn commit -m "third line from svn" &&
 	cd .. &&
 	rm -rf t.svn
-	"
+	'
 
 test_expect_success 'multiple dcommit from git-svn will not clobber svn' "
 	git reset --hard refs/remotes/git-svn &&
diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh
index ca8a00e..a400dc7 100755
--- a/t/t9106-git-svn-dcommit-clobber-series.sh
+++ b/t/t9106-git-svn-dcommit-clobber-series.sh
@@ -4,30 +4,30 @@
 test_description='git-svn dcommit clobber series'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
 	mkdir import &&
 	cd import &&
-	awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file
-	svn import -m 'initial' . $svnrepo &&
+	awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+	svn import -m "initial" . "$svnrepo" &&
 	cd .. &&
-	git svn init $svnrepo &&
+	git svn init "$svnrepo" &&
 	git svn fetch &&
 	test -e file
-	"
+	'
 
-test_expect_success '(supposedly) non-conflicting change from SVN' "
-	test x\"\`sed -n -e 58p < file\`\" = x58 &&
-	test x\"\`sed -n -e 61p < file\`\" = x61 &&
-	svn co $svnrepo tmp &&
+test_expect_success '(supposedly) non-conflicting change from SVN' '
+	test x"`sed -n -e 58p < file`" = x58 &&
+	test x"`sed -n -e 61p < file`" = x61 &&
+	svn co "$svnrepo" tmp &&
 	cd tmp &&
-		perl -i -p -e 's/^58\$/5588/' file &&
-		perl -i -p -e 's/^61\$/6611/' file &&
+		perl -i -p -e "s/^58$/5588/" file &&
+		perl -i -p -e "s/^61$/6611/" file &&
 		poke file &&
-		test x\"\`sed -n -e 58p < file\`\" = x5588 &&
-		test x\"\`sed -n -e 61p < file\`\" = x6611 &&
-		svn commit -m '58 => 5588, 61 => 6611' &&
+		test x"`sed -n -e 58p < file`" = x5588 &&
+		test x"`sed -n -e 61p < file`" = x6611 &&
+		svn commit -m "58 => 5588, 61 => 6611" &&
 		cd ..
-	"
+	'
 
 test_expect_success 'some unrelated changes to git' "
 	echo hi > life &&
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index 0a41d52..d9b553a 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -3,61 +3,61 @@
 test_description='git-svn metadata migrations from previous versions'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup old-looking metadata' "
-	cp $GIT_DIR/config $GIT_DIR/config-old-git-svn &&
+test_expect_success 'setup old-looking metadata' '
+	cp "$GIT_DIR"/config "$GIT_DIR"/config-old-git-svn &&
 	mkdir import &&
 	cd import &&
 		for i in trunk branches/a branches/b \
 		         tags/0.1 tags/0.2 tags/0.3; do
-			mkdir -p \$i && \
-			echo hello >> \$i/README || exit 1
+			mkdir -p $i && \
+			echo hello >> $i/README || exit 1
 		done && \
-		svn import -m test . $svnrepo
+		svn import -m test . "$svnrepo"
 		cd .. &&
-	git-svn init $svnrepo &&
+	git-svn init "$svnrepo" &&
 	git-svn fetch &&
-	mv $GIT_DIR/svn/* $GIT_DIR/ &&
-	mv $GIT_DIR/svn/.metadata $GIT_DIR/ &&
-	rmdir $GIT_DIR/svn &&
+	mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
+	mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
+	rmdir "$GIT_DIR"/svn &&
 	git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn &&
 	git update-ref refs/heads/svn-HEAD refs/remotes/git-svn &&
 	git update-ref -d refs/remotes/git-svn refs/remotes/git-svn
-	"
+	'
 
 head=`git rev-parse --verify refs/heads/git-svn-HEAD^0`
 test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'"
 
-test_expect_success 'initialize old-style (v0) git-svn layout' "
-	mkdir -p $GIT_DIR/git-svn/info $GIT_DIR/svn/info &&
-	echo $svnrepo > $GIT_DIR/git-svn/info/url &&
-	echo $svnrepo > $GIT_DIR/svn/info/url &&
+test_expect_success 'initialize old-style (v0) git-svn layout' '
+	mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
+	echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url &&
+	echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
 	git-svn migrate &&
-	! test -d $GIT_DIR/git-svn &&
+	! test -d "$GIT_DIR"/git-svn &&
 	git rev-parse --verify refs/remotes/git-svn^0 &&
 	git rev-parse --verify refs/remotes/svn^0 &&
-	test \`git config --get svn-remote.svn.url\` = '$svnrepo' &&
-	test \`git config --get svn-remote.svn.fetch\` = \
-             ':refs/remotes/git-svn'
-	"
+	test "$(git config --get svn-remote.svn.url)" = "$svnrepo" &&
+	test `git config --get svn-remote.svn.fetch` = \
+             ":refs/remotes/git-svn"
+	'
 
-test_expect_success 'initialize a multi-repository repo' "
-	git-svn init $svnrepo -T trunk -t tags -b branches &&
+test_expect_success 'initialize a multi-repository repo' '
+	git-svn init "$svnrepo" -T trunk -t tags -b branches &&
 	git config --get-all svn-remote.svn.fetch > fetch.out &&
-	grep '^trunk:refs/remotes/trunk$' fetch.out &&
-	test -n \"\`git config --get svn-remote.svn.branches \
-	            '^branches/\*:refs/remotes/\*$'\`\" &&
-	test -n \"\`git config --get svn-remote.svn.tags \
-	            '^tags/\*:refs/remotes/tags/\*$'\`\" &&
+	grep "^trunk:refs/remotes/trunk$" fetch.out &&
+	test -n "`git config --get svn-remote.svn.branches \
+	            "^branches/\*:refs/remotes/\*$"`" &&
+	test -n "`git config --get svn-remote.svn.tags \
+	            "^tags/\*:refs/remotes/tags/\*$"`" &&
 	git config --unset svn-remote.svn.branches \
-	                        '^branches/\*:refs/remotes/\*$' &&
+	                        "^branches/\*:refs/remotes/\*$" &&
 	git config --unset svn-remote.svn.tags \
-	                        '^tags/\*:refs/remotes/tags/\*$' &&
-	git config --add svn-remote.svn.fetch 'branches/a:refs/remotes/a' &&
-	git config --add svn-remote.svn.fetch 'branches/b:refs/remotes/b' &&
+	                        "^tags/\*:refs/remotes/tags/\*$" &&
+	git config --add svn-remote.svn.fetch "branches/a:refs/remotes/a" &&
+	git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
 	for i in tags/0.1 tags/0.2 tags/0.3; do
 		git config --add svn-remote.svn.fetch \
-		                 \$i:refs/remotes/\$i || exit 1; done
-	"
+		                 $i:refs/remotes/$i || exit 1; done
+	'
 
 # refs should all be different, but the trees should all be the same:
 test_expect_success 'multi-fetch works on partial urls + paths' "
@@ -73,43 +73,43 @@
 	                         refs/remotes/\$j\`\" ||exit 1; done; done
 	"
 
-test_expect_success 'migrate --minimize on old inited layout' "
+test_expect_success 'migrate --minimize on old inited layout' '
 	git config --unset-all svn-remote.svn.fetch &&
 	git config --unset-all svn-remote.svn.url &&
-	rm -rf $GIT_DIR/svn &&
-	for i in \`cat fetch.out\`; do
-		path=\`expr \$i : '\\([^:]*\\):.*$'\`
-		ref=\`expr \$i : '[^:]*:refs/remotes/\\(.*\\)$'\`
-		if test -z \"\$ref\"; then continue; fi
-		if test -n \"\$path\"; then path=\"/\$path\"; fi
-		( mkdir -p $GIT_DIR/svn/\$ref/info/ &&
-		echo $svnrepo\$path > $GIT_DIR/svn/\$ref/info/url ) || exit 1;
+	rm -rf "$GIT_DIR"/svn &&
+	for i in `cat fetch.out`; do
+		path=`expr $i : "\([^:]*\):.*$"`
+		ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
+		if test -z "$ref"; then continue; fi
+		if test -n "$path"; then path="/$path"; fi
+		( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
+		echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
 	done &&
 	git-svn migrate --minimize &&
-	test -z \"\`git config -l |grep -v '^svn-remote\.git-svn\.'\`\" &&
+	test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
 	git config --get-all svn-remote.svn.fetch > fetch.out &&
-	grep '^trunk:refs/remotes/trunk$' fetch.out &&
-	grep '^branches/a:refs/remotes/a$' fetch.out &&
-	grep '^branches/b:refs/remotes/b$' fetch.out &&
-	grep '^tags/0\.1:refs/remotes/tags/0\.1$' fetch.out &&
-	grep '^tags/0\.2:refs/remotes/tags/0\.2$' fetch.out &&
-	grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out
-	grep '^:refs/remotes/git-svn' fetch.out
-	"
+	grep "^trunk:refs/remotes/trunk$" fetch.out &&
+	grep "^branches/a:refs/remotes/a$" fetch.out &&
+	grep "^branches/b:refs/remotes/b$" fetch.out &&
+	grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+	grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+	grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
+	grep "^:refs/remotes/git-svn" fetch.out
+	'
 
-test_expect_success  ".rev_db auto-converted to .rev_map.UUID" "
+test_expect_success  ".rev_db auto-converted to .rev_map.UUID" '
 	git-svn fetch -i trunk &&
-	test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
-	expect=\"\$(ls $GIT_DIR/svn/trunk/.rev_map.*)\" &&
-	test -n \"\$expect\" &&
-	rev_db=\$(echo \$expect | sed -e 's,_map,_db,') &&
-	convert_to_rev_db \$expect \$rev_db &&
-	rm -f \$expect &&
-	test -f \$rev_db &&
+	test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
+	expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
+	test -n "$expect" &&
+	rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
+	convert_to_rev_db "$expect" "$rev_db" &&
+	rm -f "$expect" &&
+	test -f "$rev_db" &&
 	git-svn fetch -i trunk &&
-	test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
-	test ! -e $GIT_DIR/svn/trunk/.rev_db &&
-	test -f \$expect
-	"
+	test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
+	test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
+	test -f "$expect"
+	'
 
 test_done
diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh
index db4344c..f6f71d0 100755
--- a/t/t9108-git-svn-glob.sh
+++ b/t/t9108-git-svn-glob.sh
@@ -10,77 +10,77 @@
 initial
 EOF
 
-test_expect_success 'test refspec globbing' "
+test_expect_success 'test refspec globbing' '
 	mkdir -p trunk/src/a trunk/src/b trunk/doc &&
-	echo 'hello world' > trunk/src/a/readme &&
-	echo 'goodbye world' > trunk/src/b/readme &&
-	svn import -m 'initial' trunk $svnrepo/trunk &&
-	svn co $svnrepo tmp &&
+	echo "hello world" > trunk/src/a/readme &&
+	echo "goodbye world" > trunk/src/b/readme &&
+	svn import -m "initial" trunk "$svnrepo"/trunk &&
+	svn co "$svnrepo" tmp &&
 	cd tmp &&
 		mkdir branches tags &&
 		svn add branches tags &&
 		svn cp trunk branches/start &&
-		svn commit -m 'start a new branch' &&
+		svn commit -m "start a new branch" &&
 		svn up &&
-		echo 'hi' >> branches/start/src/b/readme &&
+		echo "hi" >> branches/start/src/b/readme &&
 		poke branches/start/src/b/readme &&
-		echo 'hey' >> branches/start/src/a/readme &&
+		echo "hey" >> branches/start/src/a/readme &&
 		poke branches/start/src/a/readme &&
-		svn commit -m 'hi' &&
+		svn commit -m "hi" &&
 		svn up &&
 		svn cp branches/start tags/end &&
-		echo 'bye' >> tags/end/src/b/readme &&
+		echo "bye" >> tags/end/src/b/readme &&
 		poke tags/end/src/b/readme &&
-		echo 'aye' >> tags/end/src/a/readme &&
+		echo "aye" >> tags/end/src/a/readme &&
 		poke tags/end/src/a/readme &&
-		svn commit -m 'the end' &&
-		echo 'byebye' >> tags/end/src/b/readme &&
+		svn commit -m "the end" &&
+		echo "byebye" >> tags/end/src/b/readme &&
 		poke tags/end/src/b/readme &&
-		svn commit -m 'nothing to see here'
+		svn commit -m "nothing to see here"
 		cd .. &&
-	git config --add svn-remote.svn.url $svnrepo &&
+	git config --add svn-remote.svn.url "$svnrepo" &&
 	git config --add svn-remote.svn.fetch \
-	                 'trunk/src/a:refs/remotes/trunk' &&
+	                 "trunk/src/a:refs/remotes/trunk" &&
 	git config --add svn-remote.svn.branches \
-	                 'branches/*/src/a:refs/remotes/branches/*' &&
+	                 "branches/*/src/a:refs/remotes/branches/*" &&
 	git config --add svn-remote.svn.tags\
-	                 'tags/*/src/a:refs/remotes/tags/*' &&
+	                 "tags/*/src/a:refs/remotes/tags/*" &&
 	git-svn multi-fetch &&
 	git log --pretty=oneline refs/remotes/tags/end | \
-	    sed -e 's/^.\{41\}//' > output.end &&
+	    sed -e "s/^.\{41\}//" > output.end &&
 	cmp expect.end output.end &&
-	test \"\`git rev-parse refs/remotes/tags/end~1\`\" = \
-		\"\`git rev-parse refs/remotes/branches/start\`\" &&
-	test \"\`git rev-parse refs/remotes/branches/start~2\`\" = \
-		\"\`git rev-parse refs/remotes/trunk\`\"
-	"
+	test "`git rev-parse refs/remotes/tags/end~1`" = \
+		"`git rev-parse refs/remotes/branches/start`" &&
+	test "`git rev-parse refs/remotes/branches/start~2`" = \
+		"`git rev-parse refs/remotes/trunk`"
+	'
 
 echo try to try > expect.two
 echo nothing to see here >> expect.two
 cat expect.end >> expect.two
 
-test_expect_success 'test left-hand-side only globbing' "
-	git config --add svn-remote.two.url $svnrepo &&
+test_expect_success 'test left-hand-side only globbing' '
+	git config --add svn-remote.two.url "$svnrepo" &&
 	git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk &&
 	git config --add svn-remote.two.branches \
-	                 'branches/*:refs/remotes/two/branches/*' &&
+	                 "branches/*:refs/remotes/two/branches/*" &&
 	git config --add svn-remote.two.tags \
-	                 'tags/*:refs/remotes/two/tags/*' &&
+	                 "tags/*:refs/remotes/two/tags/*" &&
 	cd tmp &&
-		echo 'try try' >> tags/end/src/b/readme &&
+		echo "try try" >> tags/end/src/b/readme &&
 		poke tags/end/src/b/readme &&
-		svn commit -m 'try to try'
+		svn commit -m "try to try"
 		cd .. &&
 	git-svn fetch two &&
-	test \`git rev-list refs/remotes/two/tags/end | wc -l\` -eq 6 &&
-	test \`git rev-list refs/remotes/two/branches/start | wc -l\` -eq 3 &&
-	test \`git rev-parse refs/remotes/two/branches/start~2\` = \
-	     \`git rev-parse refs/remotes/two/trunk\` &&
-	test \`git rev-parse refs/remotes/two/tags/end~3\` = \
-	     \`git rev-parse refs/remotes/two/branches/start\` &&
+	test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
+	test `git rev-list refs/remotes/two/branches/start | wc -l` -eq 3 &&
+	test `git rev-parse refs/remotes/two/branches/start~2` = \
+	     `git rev-parse refs/remotes/two/trunk` &&
+	test `git rev-parse refs/remotes/two/tags/end~3` = \
+	     `git rev-parse refs/remotes/two/branches/start` &&
 	git log --pretty=oneline refs/remotes/two/tags/end | \
-	    sed -e 's/^.\{41\}//' > output.two &&
+	    sed -e "s/^.\{41\}//" > output.two &&
 	cmp expect.two output.two
-	"
+	'
 
 test_done
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index 6235af4..047659f 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -7,15 +7,15 @@
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load svm repo' "
-	svnadmin load -q $rawsvnrepo < ../t9110/svm.dump &&
-	git-svn init --minimize-url -R arr -i bar $svnrepo/mirror/arr &&
-	git-svn init --minimize-url -R argh -i dir $svnrepo/mirror/argh &&
+test_expect_success 'load svm repo' '
+	svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump &&
+	git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
+	git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
 	git-svn init --minimize-url -R argh -i e \
-	  $svnrepo/mirror/argh/a/b/c/d/e &&
+	  "$svnrepo"/mirror/argh/a/b/c/d/e &&
 	git config svn.useSvmProps true &&
 	git-svn fetch --all
-	"
+	'
 
 uuid=161ce429-a9dd-4828-af4a-52023f968c89
 
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
index ec7dedd..a8d74dc 100755
--- a/t/t9111-git-svn-use-svnsync-props.sh
+++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -7,14 +7,14 @@
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load svnsync repo' "
-	svnadmin load -q $rawsvnrepo < ../t9111/svnsync.dump &&
-	git-svn init --minimize-url -R arr -i bar $svnrepo/bar &&
-	git-svn init --minimize-url -R argh -i dir $svnrepo/dir &&
-	git-svn init --minimize-url -R argh -i e $svnrepo/dir/a/b/c/d/e &&
+test_expect_success 'load svnsync repo' '
+	svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump &&
+	git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
+	git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
+	git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
 	git config svn.useSvnsyncProps true &&
 	git-svn fetch --all
-	"
+	'
 
 uuid=161ce429-a9dd-4828-af4a-52023f968c89
 
diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh
index 646a5f0..d470a92 100755
--- a/t/t9112-git-svn-md5less-file.sh
+++ b/t/t9112-git-svn-md5less-file.sh
@@ -40,8 +40,8 @@
 
 EOF
 
-test_expect_success 'load svn dumpfile' "svnadmin load $rawsvnrepo < dumpfile.svn"
+test_expect_success 'load svn dumpfile' 'svnadmin load "$rawsvnrepo" < dumpfile.svn'
 
-test_expect_success 'initialize git-svn' "git-svn init $svnrepo"
+test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"'
 test_expect_success 'fetch revisions from svn' 'git-svn fetch'
 test_done
diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh
index 9ef0db9..31c929b 100755
--- a/t/t9113-git-svn-dcommit-new-file.sh
+++ b/t/t9113-git-svn-dcommit-new-file.sh
@@ -15,18 +15,18 @@
 
 start_svnserve () {
 	svnserve --listen-port $SVNSERVE_PORT \
-	         --root $rawsvnrepo \
+	         --root "$rawsvnrepo" \
 	         --listen-once \
 	         --listen-host 127.0.0.1 &
 }
 
-test_expect_success 'start tracking an empty repo' "
-	svn mkdir -m 'empty dir' $svnrepo/empty-dir &&
-	echo anon-access = write >> $rawsvnrepo/conf/svnserve.conf &&
+test_expect_success 'start tracking an empty repo' '
+	svn mkdir -m "empty dir" "$svnrepo"/empty-dir &&
+	echo anon-access = write >> "$rawsvnrepo"/conf/svnserve.conf &&
 	start_svnserve &&
 	git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
 	git svn fetch
-	"
+	'
 
 test_expect_success 'create files in new directory with dcommit' "
 	mkdir git-new-dir &&
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 225060b..61d7781 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -34,35 +34,35 @@
 EOF
 }
 
-test_expect_success 'setup svn repository' "
-	svn co $svnrepo mysvnwork &&
+test_expect_success 'setup svn repository' '
+	svn co "$svnrepo" mysvnwork &&
 	mkdir -p mysvnwork/trunk &&
 	cd mysvnwork &&
 		big_text_block >> trunk/README &&
 		svn add trunk &&
-		svn ci -m 'first commit' trunk &&
+		svn ci -m "first commit" trunk &&
 		cd ..
-	"
+	'
 
-test_expect_success 'setup git mirror and merge' "
-	git svn init $svnrepo -t tags -T trunk -b branches &&
+test_expect_success 'setup git mirror and merge' '
+	git svn init "$svnrepo" -t tags -T trunk -b branches &&
 	git svn fetch &&
 	git checkout --track -b svn remotes/trunk &&
 	git checkout -b merge &&
 	echo new file > new_file &&
 	git add new_file &&
-	git commit -a -m 'New file' &&
+	git commit -a -m "New file" &&
 	echo hello >> README &&
-	git commit -a -m 'hello' &&
+	git commit -a -m "hello" &&
 	echo add some stuff >> new_file &&
-	git commit -a -m 'add some stuff' &&
+	git commit -a -m "add some stuff" &&
 	git checkout svn &&
 	mv -f README tmp &&
 	echo friend > README &&
 	cat tmp >> README &&
-	git commit -a -m 'friend' &&
+	git commit -a -m "friend" &&
 	git pull . merge
-	"
+	'
 
 test_debug 'gitk --all & sleep 1'
 
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 4acbcb0..f0fbd3a 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -7,16 +7,16 @@
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load repository with strange names' "
-	svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
+test_expect_success 'load repository with strange names' '
+	svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump &&
 	start_httpd gtk+
-	"
+	'
 
-test_expect_success 'init and fetch repository' "
-	git svn init $svnrepo &&
+test_expect_success 'init and fetch repository' '
+	git svn init "$svnrepo" &&
 	git svn fetch &&
 	git reset --hard git-svn
-	"
+	'
 
 test_expect_success 'create file in existing ugly and empty dir' '
 	mkdir "#{bad_directory_name}" &&
@@ -59,28 +59,28 @@
 	git svn dcommit
 	'
 
-test_expect_success 'clone the repository to test rebase' "
-	git svn clone $svnrepo test-rebase &&
+test_expect_success 'clone the repository to test rebase' '
+	git svn clone "$svnrepo" test-rebase &&
 	cd test-rebase &&
 		echo test-rebase > test-rebase &&
 		git add test-rebase &&
 		git commit -m test-rebase &&
 		cd ..
-	"
+	'
 
-test_expect_success 'make a commit to test rebase' "
+test_expect_success 'make a commit to test rebase' '
 		echo test-rebase-main > test-rebase-main &&
 		git add test-rebase-main &&
 		git commit -m test-rebase-main &&
 		git svn dcommit
-	"
+	'
 
-test_expect_success 'git-svn rebase works inside a fresh-cloned repository' "
+test_expect_success 'git-svn rebase works inside a fresh-cloned repository' '
 	cd test-rebase &&
 		git svn rebase &&
 		test -e test-rebase-main &&
 		test -e test-rebase
-	"
+	'
 
 stop_httpd
 
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index e1e8bdf..4b2cc87 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -6,17 +6,17 @@
 test_description='git-svn log tests'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup repository and import' "
+test_expect_success 'setup repository and import' '
 	mkdir import &&
 	cd import &&
 		for i in trunk branches/a branches/b \
 		         tags/0.1 tags/0.2 tags/0.3; do
-			mkdir -p \$i && \
-			echo hello >> \$i/README || exit 1
+			mkdir -p $i && \
+			echo hello >> $i/README || exit 1
 		done && \
-		svn import -m test . $svnrepo
+		svn import -m test . "$svnrepo"
 		cd .. &&
-	git-svn init $svnrepo -T trunk -b branches -t tags &&
+	git-svn init "$svnrepo" -T trunk -b branches -t tags &&
 	git-svn fetch &&
 	git reset --hard trunk &&
 	echo bye >> README &&
@@ -37,7 +37,7 @@
 	echo try >> README &&
 	git commit -a -m try &&
 	git svn dcommit
-	"
+	'
 
 test_expect_success 'run log' "
 	git reset --hard a &&
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index d482b40..7a689bb 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -13,43 +13,43 @@
 mkdir tmp
 cd tmp
 
-test_expect_success 'setup svnrepo' "
+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/project &&
+	svn import -m "$test_description" project "$svnrepo"/project &&
 	rm -rf project
-	"
+	'
 
-test_expect_success 'basic clone' "
+test_expect_success 'basic clone' '
 	test ! -d trunk &&
-	git svn clone $svnrepo/project/trunk &&
+	git svn clone "$svnrepo"/project/trunk &&
 	test -d trunk/.git/svn &&
 	test -e trunk/foo &&
 	rm -rf trunk
-	"
+	'
 
-test_expect_success 'clone to target directory' "
+test_expect_success 'clone to target directory' '
 	test ! -d target &&
-	git svn clone $svnrepo/project/trunk target &&
+	git svn clone "$svnrepo"/project/trunk target &&
 	test -d target/.git/svn &&
 	test -e target/foo &&
 	rm -rf target
-	"
+	'
 
-test_expect_success 'clone with --stdlayout' "
+test_expect_success 'clone with --stdlayout' '
 	test ! -d project &&
-	git svn clone -s $svnrepo/project &&
+	git svn clone -s "$svnrepo"/project &&
 	test -d project/.git/svn &&
 	test -e project/foo &&
 	rm -rf project
-	"
+	'
 
-test_expect_success 'clone to target directory with --stdlayout' "
+test_expect_success 'clone to target directory with --stdlayout' '
 	test ! -d target &&
-	git svn clone -s $svnrepo/project target &&
+	git svn clone -s "$svnrepo"/project target &&
 	test -d target/.git/svn &&
 	test -e target/foo &&
 	rm -rf target
-	"
+	'
 
 test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index 640bb06..3281cbd 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -6,25 +6,25 @@
 test_description='git-svn funky branch names'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup svnrepo' "
+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\" &&
+	svn import -m "$test_description" project "$svnrepo/pr ject" &&
 	rm -rf project &&
-	svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \
-	                \"$svnrepo/pr ject/branches/fun plugin\" &&
-	svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \
-	                      \"$svnrepo/pr ject/branches/more fun plugin!\" &&
+	svn cp -m "fun" "$svnrepo/pr ject/trunk" \
+	                "$svnrepo/pr ject/branches/fun plugin" &&
+	svn cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \
+	                      "$svnrepo/pr ject/branches/more fun plugin!" &&
 	start_httpd
-	"
+	'
 
-test_expect_success 'test clone with funky branch names' "
-	git svn clone -s \"$svnrepo/pr ject\" project &&
+test_expect_success 'test clone with funky branch names' '
+	git svn clone -s "$svnrepo/pr ject" project &&
 	cd project &&
-		git rev-parse 'refs/remotes/fun%20plugin' &&
-		git rev-parse 'refs/remotes/more%20fun%20plugin!' &&
+		git rev-parse "refs/remotes/fun%20plugin" &&
+		git rev-parse "refs/remotes/more%20fun%20plugin!" &&
 	cd ..
-	"
+	'
 
 test_expect_success 'test dcommit to funky branch' "
 	cd project &&
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index 9a4eabe..5979e13 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -6,13 +6,13 @@
 test_description='git-svn clone with percent escapes'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup svnrepo' "
+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' &&
+	svn import -m "$test_description" project "$svnrepo/pr ject" &&
 	rm -rf project &&
 	start_httpd
-"
+'
 
 if test "$SVN_HTTPD_PORT" = ""
 then
diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh
index 5143ed6..99230b0 100755
--- a/t/t9121-git-svn-fetch-renamed-dir.sh
+++ b/t/t9121-git-svn-fetch-renamed-dir.sh
@@ -7,14 +7,14 @@
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load repository with renamed directory' "
-	svnadmin load -q $rawsvnrepo < ../t9121/renamed-dir.dump
-	"
+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 &&
+test_expect_success 'init and fetch repository' '
+	git svn init "$svnrepo/newname" &&
 	git svn fetch
-	"
+	'
 
 test_done
 
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
new file mode 100755
index 0000000..8c58f0b
--- /dev/null
+++ b/t/t9122-git-svn-author.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='git svn authorship'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repository' '
+	svn checkout "$svnrepo" work.svn &&
+	(
+		cd work.svn &&
+		echo >file
+		svn add file
+		svn commit -m "first commit" file
+	)
+'
+
+test_expect_success 'interact with it via git-svn' '
+	mkdir work.git &&
+	(
+		cd work.git &&
+		git svn init "$svnrepo"
+		git svn fetch &&
+
+		echo modification >file &&
+		test_tick &&
+		git commit -a -m second &&
+
+		test_tick &&
+		git svn dcommit &&
+
+		echo "further modification" >file &&
+		test_tick &&
+		git commit -a -m third &&
+
+		test_tick &&
+		git svn --add-author-from dcommit &&
+
+		echo "yet further modification" >file &&
+		test_tick &&
+		git commit -a -m fourth &&
+
+		test_tick &&
+		git svn --add-author-from --use-log-author dcommit &&
+
+		git log &&
+
+		git show -s HEAD^^ >../actual.2 &&
+		git show -s HEAD^  >../actual.3 &&
+		git show -s HEAD   >../actual.4
+
+	) &&
+
+	# Make sure that --add-author-from without --use-log-author
+	# did not affect the authorship information
+	myself=$(grep "^Author: " actual.2) &&
+	unaffected=$(grep "^Author: " actual.3) &&
+	test "z$myself" = "z$unaffected" &&
+
+	# Make sure lack of --add-author-from did not add cruft
+	! grep "^    From: A U Thor " actual.2 &&
+
+	# Make sure --add-author-from added cruft
+	grep "^    From: A U Thor " actual.3 &&
+	grep "^    From: A U Thor " actual.4 &&
+
+	# Make sure --add-author-from with --use-log-author affected
+	# the authorship information
+	grep "^Author: A U Thor " actual.4
+'
+
+test_done
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 42b144b..b1dc32d 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -297,4 +297,21 @@
 
 '
 
+test_expect_success 'use the same checkout for Git and CVS' '
+
+	(mkdir shared &&
+	 cd shared &&
+	 unset GIT_DIR &&
+	 cvs co . &&
+	 git init &&
+	 git add " space" &&
+	 git commit -m "fake initial commit" &&
+	 echo Hello >> " space" &&
+	 git commit -m "Another change" " space" &&
+	 git cvsexportcommit -W -p -u -c HEAD &&
+	 grep Hello " space" &&
+	 git diff-files)
+
+'
+
 test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 166b43f..e97aaa6 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -153,21 +153,21 @@
    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 &&
+  'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (strict-paths)' '
     ! cat request-anonymous |
-    git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1
+    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 &&
+  'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
     ! cat request-anonymous |
-    git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1
+    git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
 '
 
 cat >request-base  <<EOF
@@ -180,25 +180,25 @@
 EOF
 
 test_expect_success 'req_Root (base-path)' \
-  'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
+  'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (base-path)' '
     ! cat request-anonymous |
-    git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1
+    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 &&
+  'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 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 &&
+  'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
new file mode 100755
index 0000000..e27a1c5
--- /dev/null
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -0,0 +1,337 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Matthew Ogilvie
+# Parts adapted from other tests.
+#
+
+test_description='git-cvsserver -kb modes
+
+tests -kb mode for binary files when accessing a git
+repository using cvs CLI client via git-cvsserver server'
+
+. ./test-lib.sh
+
+q_to_nul () {
+    perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+    tr Q '\015'
+}
+
+marked_as () {
+    foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
+    if [ x"$foundEntry" = x"" ] ; then
+       echo "NOT FOUND: $1 $2 1 $3" >> "${WORKDIR}/marked.log"
+       return 1
+    fi
+    test x"$(grep "^/$2/" "$1/CVS/Entries" | cut -d/ -f5)" = x"$3"
+    stat=$?
+    echo "$1 $2 $stat '$3'" >> "${WORKDIR}/marked.log"
+    return $stat
+}
+
+not_present() {
+    foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
+    if [ -r "$1/$2" ] ; then
+        echo "Error: File still exists: $1 $2" >> "${WORKDIR}/marked.log"
+        return 1;
+    fi
+    if [ x"$foundEntry" != x"" ] ; then
+        echo "Error: should not have found: $1 $2" >> "${WORKDIR}/marked.log"
+        return 1;
+    else
+        echo "Correctly not found: $1 $2" >> "${WORKDIR}/marked.log"
+        return 0;
+    fi
+}
+
+cvs >/dev/null 2>&1
+if test $? -ne 1
+then
+    test_expect_success 'skipping git-cvsserver tests, cvs not found' :
+    test_done
+    exit
+fi
+perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+    test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
+    test_done
+    exit
+}
+
+unset GIT_DIR GIT_CONFIG
+WORKDIR=$(pwd)
+SERVERDIR=$(pwd)/gitcvs.git
+git_config="$SERVERDIR/config"
+CVSROOT=":fork:$SERVERDIR"
+CVSWORK="$(pwd)/cvswork"
+CVS_SERVER=git-cvsserver
+export CVSROOT CVS_SERVER
+
+rm -rf "$CVSWORK" "$SERVERDIR"
+test_expect_success 'setup' '
+    echo "Simple text file" >textfile.c &&
+    echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin &&
+    mkdir subdir &&
+    echo "Another text file" > subdir/file.h &&
+    echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin &&
+    echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c
+    echo "Unspecified" > subdir/unspecified.other &&
+    echo "/*.bin -crlf" > .gitattributes &&
+    echo "/*.c crlf" >> .gitattributes &&
+    echo "subdir/*.bin -crlf" >> .gitattributes &&
+    echo "subdir/*.c crlf" >> .gitattributes &&
+    echo "subdir/file.h crlf" >> .gitattributes &&
+    git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* &&
+    git commit -q -m "First Commit" &&
+    git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+    GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+    GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
+'
+
+test_expect_success 'cvs co (default crlf)' '
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    test x"$(grep '/-k' cvswork/CVS/Entries cvswork/subdir/CVS/Entries)" = x""
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (allbinary)' '
+    GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary true &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    marked_as cvswork textfile.c -kb &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes -kb &&
+    marked_as cvswork mixedUp.c -kb &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h -kb &&
+    marked_as cvswork/subdir unspecified.other -kb
+'
+
+rm -rf cvswork cvs.log
+test_expect_success 'cvs co (use attributes/allbinary)' '
+    GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr true &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    marked_as cvswork textfile.c "" &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes -kb &&
+    marked_as cvswork mixedUp.c "" &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h "" &&
+    marked_as cvswork/subdir unspecified.other -kb
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (use attributes)' '
+    GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary false &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    marked_as cvswork textfile.c "" &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes "" &&
+    marked_as cvswork mixedUp.c "" &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h "" &&
+    marked_as cvswork/subdir unspecified.other ""
+'
+
+test_expect_success 'adding files' '
+    cd cvswork/subdir &&
+    echo "more text" > src.c &&
+    GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 &&
+    marked_as . src.c "" &&
+    echo "psuedo-binary" > temp.bin &&
+    cd .. &&
+    GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 &&
+    marked_as subdir temp.bin "-kb" &&
+    cd subdir &&
+    GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 &&
+    marked_as . temp.bin "-kb" &&
+    marked_as . src.c ""
+'
+
+cd "$WORKDIR"
+test_expect_success 'updating' '
+    git pull gitcvs.git &&
+    echo 'hi' > subdir/newfile.bin &&
+    echo 'junk' > subdir/file.h &&
+    echo 'hi' > subdir/newfile.c &&
+    echo 'hello' >> binfile.bin &&
+    git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
+    git commit -q -m "Add and change some files" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q update &&
+    cd .. &&
+    marked_as cvswork textfile.c "" &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes "" &&
+    marked_as cvswork mixedUp.c "" &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h "" &&
+    marked_as cvswork/subdir unspecified.other "" &&
+    marked_as cvswork/subdir newfile.bin -kb &&
+    marked_as cvswork/subdir newfile.c "" &&
+    echo "File with embedded NUL: Q <- there" | q_to_nul > tmpExpect1 &&
+    echo "hello" >> tmpExpect1 &&
+    cmp cvswork/binfile.bin tmpExpect1
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (use attributes/guess)' '
+    GIT_DIR="$SERVERDIR" git config gitcvs.allbinary guess &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    marked_as cvswork textfile.c "" &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes "" &&
+    marked_as cvswork mixedUp.c "" &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h "" &&
+    marked_as cvswork/subdir unspecified.other "" &&
+    marked_as cvswork/subdir newfile.bin -kb &&
+    marked_as cvswork/subdir newfile.c ""
+'
+
+test_expect_success 'setup multi-line files' '
+    ( echo "line 1" &&
+      echo "line 2" &&
+      echo "line 3" &&
+      echo "line 4 with NUL: Q <-" ) | q_to_nul > multiline.c &&
+    git add multiline.c &&
+    ( echo "line 1" &&
+      echo "line 2" &&
+      echo "line 3" &&
+      echo "line 4" ) | q_to_nul > multilineTxt.c &&
+    git add multilineTxt.c &&
+    git commit -q -m "multiline files" &&
+    git push gitcvs.git >/dev/null
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (guess)' '
+    GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr false &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    marked_as cvswork textfile.c "" &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes "" &&
+    marked_as cvswork mixedUp.c -kb &&
+    marked_as cvswork multiline.c -kb &&
+    marked_as cvswork multilineTxt.c "" &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h "" &&
+    marked_as cvswork/subdir unspecified.other "" &&
+    marked_as cvswork/subdir newfile.bin "" &&
+    marked_as cvswork/subdir newfile.c ""
+'
+
+test_expect_success 'cvs co another copy (guess)' '
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+    marked_as cvswork2 textfile.c "" &&
+    marked_as cvswork2 binfile.bin -kb &&
+    marked_as cvswork2 .gitattributes "" &&
+    marked_as cvswork2 mixedUp.c -kb &&
+    marked_as cvswork2 multiline.c -kb &&
+    marked_as cvswork2 multilineTxt.c "" &&
+    marked_as cvswork2/subdir withCr.bin -kb &&
+    marked_as cvswork2/subdir file.h "" &&
+    marked_as cvswork2/subdir unspecified.other "" &&
+    marked_as cvswork2/subdir newfile.bin "" &&
+    marked_as cvswork2/subdir newfile.c ""
+'
+
+test_expect_success 'add text (guess)' '
+    cd cvswork &&
+    echo "simpleText" > simpleText.c &&
+    GIT_CONFIG="$git_config" cvs -Q add simpleText.c &&
+    cd .. &&
+    marked_as cvswork simpleText.c ""
+'
+
+test_expect_success 'add bin (guess)' '
+    cd cvswork &&
+    echo "simpleBin: NUL: Q <- there" | q_to_nul > simpleBin.bin &&
+    GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin &&
+    cd .. &&
+    marked_as cvswork simpleBin.bin -kb
+'
+
+test_expect_success 'remove files (guess)' '
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q rm -f subdir/file.h &&
+    cd subdir &&
+    GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin &&
+    cd ../.. &&
+    marked_as cvswork/subdir withCr.bin -kb &&
+    marked_as cvswork/subdir file.h ""
+'
+
+test_expect_success 'cvs ci (guess)' '
+    cd cvswork &&
+    GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1 &&
+    cd .. &&
+    marked_as cvswork textfile.c "" &&
+    marked_as cvswork binfile.bin -kb &&
+    marked_as cvswork .gitattributes "" &&
+    marked_as cvswork mixedUp.c -kb &&
+    marked_as cvswork multiline.c -kb &&
+    marked_as cvswork multilineTxt.c "" &&
+    not_present cvswork/subdir withCr.bin &&
+    not_present cvswork/subdir file.h &&
+    marked_as cvswork/subdir unspecified.other "" &&
+    marked_as cvswork/subdir newfile.bin "" &&
+    marked_as cvswork/subdir newfile.c "" &&
+    marked_as cvswork simpleBin.bin -kb &&
+    marked_as cvswork simpleText.c ""
+'
+
+test_expect_success 'update subdir of other copy (guess)' '
+    cd cvswork2/subdir &&
+    GIT_CONFIG="$git_config" cvs -Q update &&
+    cd ../.. &&
+    marked_as cvswork2 textfile.c "" &&
+    marked_as cvswork2 binfile.bin -kb &&
+    marked_as cvswork2 .gitattributes "" &&
+    marked_as cvswork2 mixedUp.c -kb &&
+    marked_as cvswork2 multiline.c -kb &&
+    marked_as cvswork2 multilineTxt.c "" &&
+    not_present cvswork2/subdir withCr.bin &&
+    not_present cvswork2/subdir file.h &&
+    marked_as cvswork2/subdir unspecified.other "" &&
+    marked_as cvswork2/subdir newfile.bin "" &&
+    marked_as cvswork2/subdir newfile.c "" &&
+    not_present cvswork2 simpleBin.bin &&
+    not_present cvswork2 simpleText.c
+'
+
+echo "starting update/merge" >> "${WORKDIR}/marked.log"
+test_expect_success 'update/merge full other copy (guess)' '
+    git pull gitcvs.git master &&
+    sed "s/3/replaced_3/" < multilineTxt.c > ml.temp &&
+    mv ml.temp multilineTxt.c &&
+    git add multilineTxt.c &&
+    git commit -q -m "modify multiline file" >> "${WORKDIR}/marked.log" &&
+    git push gitcvs.git >/dev/null &&
+    cd cvswork2 &&
+    sed "s/1/replaced_1/" < multilineTxt.c > ml.temp &&
+    mv ml.temp multilineTxt.c &&
+    GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1 &&
+    cd .. &&
+    marked_as cvswork2 textfile.c "" &&
+    marked_as cvswork2 binfile.bin -kb &&
+    marked_as cvswork2 .gitattributes "" &&
+    marked_as cvswork2 mixedUp.c -kb &&
+    marked_as cvswork2 multiline.c -kb &&
+    marked_as cvswork2 multilineTxt.c "" &&
+    not_present cvswork2/subdir withCr.bin &&
+    not_present cvswork2/subdir file.h &&
+    marked_as cvswork2/subdir unspecified.other "" &&
+    marked_as cvswork2/subdir newfile.bin "" &&
+    marked_as cvswork2/subdir newfile.c "" &&
+    marked_as cvswork2 simpleBin.bin -kb &&
+    marked_as cvswork2 simpleText.c "" &&
+    echo "line replaced_1" > tmpExpect2 &&
+    echo "line 2" >> tmpExpect2 &&
+    echo "line replaced_3" >> tmpExpect2 &&
+    echo "line 4" | q_to_nul >> tmpExpect2 &&
+    cmp cvswork2/multilineTxt.c tmpExpect2
+'
+
+test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 061a259..ae7082b 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -10,6 +10,7 @@
 or warnings to log.'
 
 gitweb_init () {
+	safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
 	cat >gitweb_config.perl <<EOF
 #!/usr/bin/perl
 
@@ -17,16 +18,16 @@
 
 our \$version = "current";
 our \$GIT = "git";
-our \$projectroot = "$(pwd)";
+our \$projectroot = "$safe_pwd";
 our \$project_maxdepth = 8;
 our \$home_link_str = "projects";
 our \$site_name = "[localhost]";
 our \$site_header = "";
 our \$site_footer = "";
 our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css");
-our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png";
-our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png";
+our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
+our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
+our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
 our \$projects_list = "";
 our \$export_ok = "";
 our \$strict_export = "";
@@ -39,19 +40,21 @@
 }
 
 gitweb_run () {
-	export GATEWAY_INTERFACE="CGI/1.1"
-	export HTTP_ACCEPT="*/*"
-	export REQUEST_METHOD="GET"
-	export QUERY_STRING=""$1""
-	export PATH_INFO=""$2""
+	GATEWAY_INTERFACE="CGI/1.1"
+	HTTP_ACCEPT="*/*"
+	REQUEST_METHOD="GET"
+	QUERY_STRING=""$1""
+	PATH_INFO=""$2""
+	export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD QUERY_STRING PATH_INFO
 
-	export GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+	GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+	export GITWEB_CONFIG
 
 	# some of git commands write to STDERR on error, but this is not
 	# written to web server logs, so we are not interested in that:
 	# we are interested only in properly formatted errors/warnings
 	rm -f gitweb.log &&
-	perl -- $(pwd)/../../gitweb/gitweb.perl \
+	perl -- "$(pwd)/../../gitweb/gitweb.perl" \
 		>/dev/null 2>gitweb.log &&
 	if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
 
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 00a74ee..0b115a1 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -36,7 +36,7 @@
 
 test_expect_success 'setup a cvs module' '
 
-	mkdir $CVSROOT/module &&
+	mkdir "$CVSROOT/module" &&
 	cvs co -d module-cvs module &&
 	cd module-cvs &&
 	cat <<EOF >o_fortuna &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 7c2a8ba..3bf570b 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -160,6 +160,22 @@
 
 trap 'die' exit
 
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using. 
+test_set_editor () {
+	FAKE_EDITOR="$1"
+	export FAKE_EDITOR
+	VISUAL='"$FAKE_EDITOR"'
+	export VISUAL
+}
+
 test_tick () {
 	if test -z "${test_tick+set}"
 	then
@@ -329,7 +345,7 @@
 	repo="$1"
 	mkdir "$repo"
 	cd "$repo" || error "Cannot setup test environment"
-	"$GIT_EXEC_PATH/git" init --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 ||
+	"$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >/dev/null 2>&1 ||
 	error "cannot run git init -- have you built things yet?"
 	mv .git/hooks .git/hooks-disabled
 	cd "$owd"
@@ -352,7 +368,7 @@
 	case "$test_failure" in
 	0)
 		# We could:
-		# cd .. && rm -fr trash
+		# cd .. && rm -fr 'trash directory'
 		# but that means we forbid any tests that use their own
 		# subdirectory from calling test_done without coming back
 		# to where they started from.
@@ -395,15 +411,15 @@
 . ../GIT-BUILD-OPTIONS
 
 # Test repository
-test=trash
+test="trash directory"
 rm -fr "$test" || {
 	trap - exit
 	echo >&5 "FATAL: Cannot prepare test area"
 	exit 1
 }
 
-test_create_repo $test
-cd "$test"
+test_create_repo "$test"
+cd "$test" || exit 1
 
 this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
 for skp in $GIT_SKIP_TESTS
diff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg
index ff0f42a..d3c1da3 100644
--- a/templates/hooks--prepare-commit-msg
+++ b/templates/hooks--prepare-commit-msg
@@ -20,11 +20,11 @@
 # 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" ;;
+case "$2,$3" in
+  merge,)
+    perl -i -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
 
-# ""|template)
+# ,|template,)
 #   perl -i -pe '
 #      print "\n" . `git diff --cached --name-status -r`
 #	 if /^#/ && $first++ == 0' "$1" ;;
diff --git a/transport.c b/transport.c
index 1bc16f2..3ff8519 100644
--- a/transport.c
+++ b/transport.c
@@ -203,7 +203,7 @@
 }
 
 static int fetch_objs_via_rsync(struct transport *transport,
-				 int nr_objs, struct ref **to_fetch)
+				int nr_objs, const struct ref **to_fetch)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process rsync;
@@ -350,7 +350,7 @@
 
 #ifndef NO_CURL /* http fetch is the only user */
 static int fetch_objs_via_walker(struct transport *transport,
-				 int nr_objs, struct ref **to_fetch)
+				 int nr_objs, const struct ref **to_fetch)
 {
 	char *dest = xstrdup(transport->url);
 	struct walker *walker = transport->data;
@@ -517,7 +517,7 @@
 }
 
 static int fetch_objs_via_curl(struct transport *transport,
-				 int nr_objs, struct ref **to_fetch)
+				 int nr_objs, const struct ref **to_fetch)
 {
 	if (!transport->data)
 		transport->data = get_http_walker(transport->url,
@@ -554,7 +554,7 @@
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
-			       int nr_heads, struct ref **to_fetch)
+			       int nr_heads, const struct ref **to_fetch)
 {
 	struct bundle_transport_data *data = transport->data;
 	return unbundle(&data->header, data->fd);
@@ -628,7 +628,7 @@
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
-			       int nr_heads, struct ref **to_fetch)
+			       int nr_heads, const struct ref **to_fetch)
 {
 	struct git_transport_data *data = transport->data;
 	char **heads = xmalloc(nr_heads * sizeof(*heads));
@@ -796,12 +796,12 @@
 	return transport->remote_refs;
 }
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs)
+int transport_fetch_refs(struct transport *transport, const struct ref *refs)
 {
 	int rc;
 	int nr_heads = 0, nr_alloc = 0;
-	struct ref **heads = NULL;
-	struct ref *rm;
+	const struct ref **heads = NULL;
+	const struct ref *rm;
 
 	for (rm = refs; rm; rm = rm->next) {
 		if (rm->peer_ref &&
diff --git a/transport.h b/transport.h
index 8abfc0a..d0b5205 100644
--- a/transport.h
+++ b/transport.h
@@ -19,7 +19,7 @@
 			  const char *value);
 
 	struct ref *(*get_refs_list)(struct transport *transport);
-	int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
+	int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 
 	int (*disconnect)(struct transport *connection);
@@ -68,7 +68,7 @@
 
 const struct ref *transport_get_remote_refs(struct transport *transport);
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs);
+int transport_fetch_refs(struct transport *transport, const struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 
diff --git a/unpack-trees.c b/unpack-trees.c
index 1ab28fd..0de5a31 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -8,6 +8,36 @@
 #include "progress.h"
 #include "refs.h"
 
+/*
+ * Error messages expected by scripts out of plumbing commands such as
+ * read-tree.  Non-scripted Porcelain is not required to use these messages
+ * and in fact are encouraged to reword them to better suit their particular
+ * situation better.  See how "git checkout" replaces not_uptodate_file to
+ * explain why it does not allow switching between branches when you have
+ * local changes, for example.
+ */
+static struct unpack_trees_error_msgs unpack_plumbing_errors = {
+	/* would_overwrite */
+	"Entry '%s' would be overwritten by merge. Cannot merge.",
+
+	/* not_uptodate_file */
+	"Entry '%s' not uptodate. Cannot merge.",
+
+	/* not_uptodate_dir */
+	"Updating '%s' would lose untracked files in it",
+
+	/* would_lose_untracked */
+	"Untracked working tree file '%s' would be %s by merge.",
+
+	/* bind_overlap */
+	"Entry '%s' overlaps with '%s'.  Cannot bind.",
+};
+
+#define ERRORMSG(o,fld) \
+	( ((o) && (o)->msgs.fld) \
+	? ((o)->msgs.fld) \
+	: (unpack_plumbing_errors.fld) )
+
 static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 	unsigned int set, unsigned int clear)
 {
@@ -383,10 +413,9 @@
 
 /* Here come the merge functions */
 
-static int reject_merge(struct cache_entry *ce)
+static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-	return error("Entry '%s' would be overwritten by merge. Cannot merge.",
-		     ce->name);
+	return error(ERRORMSG(o, would_overwrite), ce->name);
 }
 
 static int same(struct cache_entry *a, struct cache_entry *b)
@@ -430,7 +459,7 @@
 	if (errno == ENOENT)
 		return 0;
 	return o->gently ? -1 :
-		error("Entry '%s' not uptodate. Cannot merge.", ce->name);
+		error(ERRORMSG(o, not_uptodate_file), ce->name);
 }
 
 static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -517,8 +546,7 @@
 	i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
 	if (i)
 		return o->gently ? -1 :
-			error("Updating '%s' would lose untracked files in it",
-			      ce->name);
+			error(ERRORMSG(o, not_uptodate_dir), ce->name);
 	free(pathbuf);
 	return cnt;
 }
@@ -618,8 +646,7 @@
 		}
 
 		return o->gently ? -1 :
-			error("Untracked working tree file '%s' "
-			      "would be %s by merge.", ce->name, action);
+			error(ERRORMSG(o, would_lose_untracked), ce->name, action);
 	}
 	return 0;
 }
@@ -751,7 +778,7 @@
 	/* #14, #14ALT, #2ALT */
 	if (remote && !df_conflict_head && head_match && !remote_match) {
 		if (index && !same(index, remote) && !same(index, head))
-			return o->gently ? -1 : reject_merge(index);
+			return o->gently ? -1 : reject_merge(index, o);
 		return merged_entry(remote, index, o);
 	}
 	/*
@@ -759,7 +786,7 @@
 	 * make sure that it matches head.
 	 */
 	if (index && !same(index, head))
-		return o->gently ? -1 : reject_merge(index);
+		return o->gently ? -1 : reject_merge(index, o);
 
 	if (head) {
 		/* #5ALT, #15 */
@@ -901,11 +928,11 @@
 		else {
 			/* all other failures */
 			if (oldtree)
-				return o->gently ? -1 : reject_merge(oldtree);
+				return o->gently ? -1 : reject_merge(oldtree, o);
 			if (current)
-				return o->gently ? -1 : reject_merge(current);
+				return o->gently ? -1 : reject_merge(current, o);
 			if (newtree)
-				return o->gently ? -1 : reject_merge(newtree);
+				return o->gently ? -1 : reject_merge(newtree, o);
 			return -1;
 		}
 	}
@@ -931,7 +958,7 @@
 			     o->merge_size);
 	if (a && old)
 		return o->gently ? -1 :
-			error("Entry '%s' overlaps with '%s'.  Cannot bind.", a->name, old->name);
+			error(ERRORMSG(o, bind_overlap), a->name, old->name);
 	if (!a)
 		return keep_entry(old, o);
 	else
diff --git a/unpack-trees.h b/unpack-trees.h
index d436d6c..94e5672 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -8,6 +8,14 @@
 typedef int (*merge_fn_t)(struct cache_entry **src,
 		struct unpack_trees_options *options);
 
+struct unpack_trees_error_msgs {
+	const char *would_overwrite;
+	const char *not_uptodate_file;
+	const char *not_uptodate_dir;
+	const char *would_lose_untracked;
+	const char *bind_overlap;
+};
+
 struct unpack_trees_options {
 	unsigned int reset:1,
 		     merge:1,
@@ -23,6 +31,7 @@
 	int pos;
 	struct dir_struct *dir;
 	merge_fn_t fn;
+	struct unpack_trees_error_msgs msgs;
 
 	int head_idx;
 	int merge_size;
diff --git a/wt-status.c b/wt-status.c
index c932b39..5b4d74c 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -18,6 +18,7 @@
 	"\033[32m", /* WT_STATUS_UPDATED: green */
 	"\033[31m", /* WT_STATUS_CHANGED: red */
 	"\033[31m", /* WT_STATUS_UNTRACKED: red */
+	"\033[31m", /* WT_STATUS_NOBRANCH: red */
 };
 
 static const char use_add_msg[] =
@@ -38,6 +39,8 @@
 		return WT_STATUS_CHANGED;
 	if (!strcasecmp(var+offset, "untracked"))
 		return WT_STATUS_UNTRACKED;
+	if (!strcasecmp(var+offset, "nobranch"))
+		return WT_STATUS_NOBRANCH;
 	die("bad config variable '%s'", var);
 }
 
@@ -206,7 +209,7 @@
 	rev.diffopt.format_callback = wt_status_print_updated_cb;
 	rev.diffopt.format_callback_data = s;
 	rev.diffopt.detect_rename = 1;
-	rev.diffopt.rename_limit = 100;
+	rev.diffopt.rename_limit = 200;
 	rev.diffopt.break_opt = 0;
 	run_diff_index(&rev, 1);
 }
@@ -314,8 +317,9 @@
 void wt_status_print(struct wt_status *s)
 {
 	unsigned char sha1[20];
-	s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+	const char *branch_color = color(WT_STATUS_HEADER);
 
+	s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
 	if (s->branch) {
 		const char *on_what = "On branch ";
 		const char *branch_name = s->branch;
@@ -323,10 +327,11 @@
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_name = "";
+			branch_color = color(WT_STATUS_NOBRANCH);
 			on_what = "Not currently on any branch.";
 		}
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
-			"# %s%s", on_what, branch_name);
+		color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
+		color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
 	}
 
 	if (s->is_initial) {
diff --git a/wt-status.h b/wt-status.h
index f2c7130..597c7ea 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -8,6 +8,7 @@
 	WT_STATUS_UPDATED,
 	WT_STATUS_CHANGED,
 	WT_STATUS_UNTRACKED,
+	WT_STATUS_NOBRANCH,
 };
 
 struct wt_status {