GIT 0.99.9l aka 1.0rc4
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index a25d04a..200c9b2 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -4,6 +4,11 @@
 	option old data in `.git/FETCH_HEAD` will be overwritten.
 
 -f, \--force::
+	When `git-fetch` is used with `<rbranch>:<lbranch>`
+	refspec, it refuses to update the local branch
+	`<lbranch>` unless the remote branch `<rbranch>` it
+	fetches is a descendant of `<lbranch>`.  This option
+	overrides that check.
 
 -t, \--tags::
 	By default, the git core utilities will not fetch and store
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 39fa665..8a39970 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -8,13 +8,13 @@
 
 SYNOPSIS
 --------
-'git bisect' start
-'git bisect' bad <rev>
-'git bisect' good <rev>
-'git bisect' reset [<branch>]
-'git bisect' visualize
-'git bisect' replay <logfile>
-'git bisect' log
+ 'git bisect' start
+ 'git bisect' bad <rev>
+ 'git bisect' good <rev>
+ 'git bisect' reset [<branch>]
+ 'git bisect' visualize
+ 'git bisect' replay <logfile>
+ 'git bisect' log
 
 DESCRIPTION
 -----------
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index ab4dcae..9a7700f 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-cat-file' (-t | -s | <type>) <object>
+'git-cat-file' (-t | -s | -e | <type>) <object>
 
 DESCRIPTION
 -----------
@@ -29,6 +29,10 @@
 	Instead of the content, show the object size identified by
 	<object>.
 
+-e::
+	Suppress all output; instead exit with zero status if <object>
+	exists and is a valid object.
+
 <type>::
 	Typically this matches the real type of <object> but asking
 	for a type that can trivially be dereferenced from the given
@@ -39,8 +43,11 @@
 
 OUTPUT
 ------
-If '-t' is specified, one of the <type>.  If '-s' is specified,
-the size of the <object> in bytes.
+If '-t' is specified, one of the <type>.
+
+If '-s' is specified, the size of the <object> in bytes.
+
+If '-e' is specified, no output.
 
 Otherwise the raw (though uncompressed) contents of the <object> will
 be returned.
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 3b04bfe..67f5126 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -21,6 +21,15 @@
 -------
 include::diff-options.txt[]
 
+-1 -2 -3 or --base --ours --theirs, and -0::
+	Diff against the "base" version, "our branch" or "their
+	branch" respectively.  With these options, diffs for
+	merged entries are not shown.
++
+The default is to diff against our branch (-2) and the 
+cleanly resolved paths.  The option -0 can be given to
+omit diff output for unmerged entries and just show "Unmerged".
+
 -q::
 	Remain silent even on nonexisting files
 
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index dc7d725..8890754 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-mailinfo' [-k] [-u] <msg> <patch>
+'git-mailinfo' [-k] [-u | --encoding=<encoding>] <msg> <patch>
 
 
 DESCRIPTION
@@ -37,10 +37,17 @@
 	author email are taken from the e-mail without any
 	charset conversion, after minimally decoding MIME
 	transfer encoding.  This flag causes the resulting
-	commit to be encoded in utf-8 by transliterating them.
+	commit to be encoded in the encoding specified by
+	i18n.commitencoding configuration (defaults to utf-8) by
+	transliterating them. 
 	Note that the patch is always used as is without charset
 	conversion, even with this flag.
 
+--encoding=<encoding>::
+	Similar to -u but if the local convention is different
+	from what is specified by i18n.commitencoding, this flag
+	can be used to override it.
+
 <msg>::
 	The commit log message extracted from e-mail, usually
 	except the title line which comes from e-mail Subject.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 904e2fc..0cac563 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -37,6 +37,103 @@
 include::merge-strategies.txt[]
 
 
+HOW MERGE WORKS
+---------------
+
+A merge is always between the current `HEAD` and one or more
+remote branch heads, and the index file must exactly match the
+tree of `HEAD` commit (i.e. the contents of the last commit) when
+it happens.  In other words, `git-diff --cached HEAD` must
+report no changes.
+
+[NOTE]
+This is a bit of lie.  In certain special cases, your index are
+allowed to be different from the tree of `HEAD` commit.  The most
+notable case is when your `HEAD` commit is already ahead of what
+is being merged, in which case your index can have arbitrary
+difference from your `HEAD` commit.  Otherwise, your index entries
+are allowed have differences from your `HEAD` commit that match
+the result of trivial merge (e.g. you received the same patch
+from external source to produce the same result as what you are
+merging).  For example, if a path did not exist in the common
+ancestor and your head commit but exists in the tree you are
+merging into your repository, and if you already happen to have
+that path exactly in your index, the merge does not have to
+fail.
+
+Otherwise, merge will refuse to do any harm to your repository
+(that is, it may fetch the objects from remote, and it may even
+update the local branch used to keep track of the remote branch
+with `git pull remote rbranch:lbranch`, but your working tree,
+`.git/HEAD` pointer and index file are left intact).
+
+You may have local modifications in the working tree files.  In
+other words, `git-diff` is allowed to report changes.
+However, the merge uses your working tree as the working area,
+and in order to prevent the merge operation from losing such
+changes, it makes sure that they do not interfere with the
+merge. Those complex tables in read-tree documentation define
+what it means for a path to "interfere with the merge".  And if
+your local modifications interfere with the merge, again, it
+stops before touching anything.
+
+So in the above two "failed merge" case, you do not have to
+worry about lossage of data --- you simply were not ready to do
+a merge, so no merge happened at all.  You may want to finish
+whatever you were in the middle of doing, and retry the same
+pull after you are done and ready.
+
+When things cleanly merge, these things happen:
+
+1. the results are updated both in the index file and in your
+   working tree,
+2. index file is written out as a tree,
+3. the tree gets committed, and 
+4. the `HEAD` pointer gets advanced.
+
+Because of 2., we require that the original state of the index
+file to match exactly the current `HEAD` commit; otherwise we
+will write out your local changes already registered in your
+index file along with the merge result, which is not good.
+Because 1. involves only the paths different between your
+branch and the remote branch you are pulling from during the
+merge (which is typically a fraction of the whole tree), you can
+have local modifications in your working tree as long as they do
+not overlap with what the merge updates.
+
+When there are conflicts, these things happen:
+
+1. `HEAD` stays the same.
+
+2. Cleanly merged paths are updated both in the index file and
+   in your working tree.
+
+3. For conflicting paths, the index file records up to three
+   versions; stage1 stores the version from the common ancestor,
+   stage2 from `HEAD`, and stage3 from the remote branch (you
+   can inspect the stages with `git-ls-files -u`).  The working
+   tree files have the result of "merge" program; i.e. 3-way
+   merge result with familiar conflict markers `<<< === >>>`.
+
+4. No other changes are done.  In particular, the local
+   modifications you had before you started merge will stay the
+   same and the index entries for them stay as they were,
+   i.e. matching `HEAD`.
+
+After seeing a conflict, you can do two things:
+
+ * Decide not to merge.  The only clean-up you need are to reset
+   the index file to the `HEAD` commit to reverse 2. and to clean
+   up working tree changes made by 2. and 3.; `git-reset` can
+   be used for this.
+
+ * Resolve the conflicts.  `git-diff` would report only the
+   conflicting paths because of the above 2. and 3..  Edit the
+   working tree files into a desirable shape, `git-update-index`
+   them, to make the index file contain what the merge result
+   should be, and run `git-commit` to commit the result.
+
+
 SEE ALSO
 --------
 gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1]
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index f2d5882..3013b8d 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -8,14 +8,14 @@
 
 SYNOPSIS
 --------
-'git-mv' [-f] [-n] <source> <destination>
-'git-mv' [-f] [-k] [-n] <source> ... <destination directory>
+ 'git-mv' [-f] [-n] <source> <destination>
+ 'git-mv' [-f] [-n] [-k] <source> ... <destination directory>
 
 DESCRIPTION
 -----------
 This script is used to move or rename a file, directory or symlink.
 In the first form, it renames <source>, which must exist and be either
-a file, symlink or directory, to <destination>, which must not exist.
+a file, symlink or directory, to <destination>.
 In the second form, the last argument has to be an existing
 directory; the given sources will be moved into this directory.
 
@@ -25,7 +25,7 @@
 OPTIONS
 -------
 -f::
-	Force renaming or moving even targets exist
+	Force renaming or moving of a file even if the target exists
 -k::
         Skip move or rename actions which would lead to an error
 	condition. An error happens when a source is neither existing nor
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 8b91847..6e92e4a 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -28,11 +28,14 @@
 OPTIONS
 -------
 -m::
-	Perform a merge, not just a read.
+	Perform a merge, not just a read.  The command will
+	refuse to run if your index file has unmerged entries,
+	indicating that you have not finished previous merge you
+	started.
 
 --reset::
-
-        Same as -m except that unmerged entries will be silently ignored.
+        Same as -m, except that unmerged entries are discarded
+        instead of failing.
 
 -u::
 	After a successful merge, update the files in the work
@@ -47,7 +50,6 @@
 	trees that are not directly related to the current
 	working tree status into a temporary index file.
 
-
 <tree-ish#>::
 	The id of the tree object(s) to be read/merged.
 
diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index fcc79fa..f8dbee7 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -10,7 +10,7 @@
 SYNOPSIS
 --------
 'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
-			[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_nr_changes]
+			[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
 			[ -b branch_subdir ] [ -t trunk_subdir ] [ -T tag_subdir ]
 			[ -s start_chg ] [ -m ] [ -M regex ]
 			<SVN_repository_URL> [ <path> ]
@@ -71,14 +71,11 @@
 	regex. It can be used with -m to also see the default regexes.
 	You must escape forward slashes.
 
--l <max_num_changes>::
-	Limit the number of SVN changesets we pull before quitting.
-	This option is necessary because the SVN library has serious memory
-	leaks; the recommended value for nontrivial imports is 100.
+-l <max_rev>::
+	Specify a maximum revision number to pull.
 
-	git-svnimport will still exit with a zero exit code. You can check
-	the size of the file ".git/svn2git" to determine whether to call
-	the importer again.
+	Formerly, this option controlled how many revisions to pull, due to
+	SVN memory leaks. (These have been worked around.)
 
 -v::
 	Verbosity: let 'svnimport' report what it is doing.
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 95de436..841c9dc 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,26 @@
 --------
 'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>]
 
+OPTIONS
+-------
+-a::
+	Make an unsigned, annotated tag object
+
+-s::
+	Make a GPG-signed tag, using the default e-mail address's key
+
+-u <key-id>::
+	Make a GPG-signed tag, using the given key
+
+-f::
+	Replace an existing tag with the given name (instead of failing)
+
+-d::
+	Delete an existing tag with the given name
+
+-m <msg>::
+	Use the given tag message (instead of prompting)
+
 DESCRIPTION
 -----------
 Adds a 'tag' reference in .git/refs/tags/
@@ -23,7 +43,7 @@
 in the tag message.
 
 Otherwise just the SHA1 object name of the commit object is
-written (i.e. an lightweight tag).
+written (i.e. a lightweight tag).
 
 A GnuPG signed tag object will be created when `-s` or `-u
 <key-id>` is used.  When `-u <key-id>` is not used, the
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 6413d52..b5b9792 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -15,10 +15,10 @@
 - ssh://host.xz/~/path/to/repo.git
 ===============================================================
 +
-	SSH Is the default transport protocol and also supports an
-	scp-like syntax.  Both syntaxes support username expansion,
-	as does the native git protocol. The following three are
-	identical to the last three above, respectively:
+SSH Is the default transport protocol and also supports an
+scp-like syntax.  Both syntaxes support username expansion,
+as does the native git protocol. The following three are
+identical to the last three above, respectively:
 +
 ===============================================================
 - host.xz:/path/to/repo.git/
@@ -26,8 +26,8 @@
 - host.xz:path/to/repo.git
 ===============================================================
 +
-       To sync with a local directory, use:
-
+To sync with a local directory, use:
++
 ===============================================================
 - /path/to/repo.git/
 ===============================================================
@@ -113,7 +113,7 @@
 `git pull . remote-B`, while you are on `my-B` branch.
 The common `Pull: master:origin` mapping of a remote `master`
 branch to a local `origin` branch, which is then merged to a
-ocal development branch, again typically named `master`, is made
+local development branch, again typically named `master`, is made
 when you run `git clone` for you to follow this pattern.
 +
 [NOTE]
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index e2dfb00..cf7ba76 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -898,9 +898,8 @@
 	fatal: Merge requires file-level merging
 	Nope.
 	...
-	merge: warning: conflicts during merge
-	ERROR: Merge conflict in hello.
-	fatal: merge program failed
+	Auto-merging hello 
+	CONFLICT (content): Merge conflict in hello 
 	Automatic merge failed/prevented; fix up by hand
 ----------------
 
@@ -942,10 +941,10 @@
 
 ------------------------------------------------
 $ git show-branch master mybranch
-* [master] Merged "mybranch" changes.
+* [master] Merge work in mybranch
  ! [mybranch] Some work.
 --
-+  [master] Merged "mybranch" changes.
++  [master] Merge work in mybranch
 ++ [mybranch] Some work.
 ------------------------------------------------
 
@@ -998,10 +997,10 @@
 
 ------------------------------------------------
 $ git show-branch master mybranch
-! [master] Merged "mybranch" changes.
- * [mybranch] Merged "mybranch" changes.
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
 --
-++ [master] Merged "mybranch" changes.
+++ [master] Merge work in mybranch
 ------------------------------------------------
 
 
diff --git a/Makefile b/Makefile
index adff025..ba05986 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all:
+
 # Define MOZILLA_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
 # on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
@@ -18,6 +21,8 @@
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
+# Define NO_SETENV if you don't have setenv in the C library.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
@@ -50,7 +55,7 @@
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
 
-GIT_VERSION = 0.99.9k
+GIT_VERSION = 0.99.9l
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
@@ -138,8 +143,6 @@
 # Backward compatibility -- to be removed after 1.0
 PROGRAMS += git-ssh-pull$X git-ssh-push$X
 
-GIT_LIST_TWEAK =
-
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
 	SHELL_PATH = /bin/sh
@@ -154,20 +157,6 @@
 PYMODULES = \
 	gitMergeCommon.py
 
-ifdef WITH_OWN_SUBPROCESS_PY
-	PYMODULES += compat/subprocess.py
-else
-	ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
-		PYMODULES += compat/subprocess.py
-	endif
-endif
-
-ifdef WITH_SEND_EMAIL
-	SCRIPT_PERL += git-send-email.perl
-else
-	GIT_LIST_TWEAK += -e '/^send-email$$/d'
-endif
-
 LIB_FILE=libgit.a
 
 LIB_H = \
@@ -207,6 +196,7 @@
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
 uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
 uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
 
 ifeq ($(uname_S),Darwin)
 	NEEDS_SSL_WITH_CRYPTO = YesPlease
@@ -224,6 +214,9 @@
 	NEEDS_LIBICONV = YesPlease
 	SHELL_PATH = /bin/bash
 	NO_STRCASESTR = YesPlease
+	ifeq ($(uname_R),5.8)
+		NO_SETENV = YesPlease
+	endif
 	INSTALL = ginstall
 	TAR = gtar
 	ALL_CFLAGS += -D__EXTENSIONS__
@@ -256,6 +249,18 @@
 
 -include config.mak
 
+ifdef WITH_OWN_SUBPROCESS_PY
+	PYMODULES += compat/subprocess.py
+else
+	ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
+		PYMODULES += compat/subprocess.py
+	endif
+endif
+
+ifdef WITH_SEND_EMAIL
+	SCRIPT_PERL += git-send-email.perl
+endif
+
 ifndef NO_CURL
 	ifdef CURLDIR
 		# This is still problematic -- gcc does not always want -R.
@@ -315,12 +320,16 @@
 	SIMPLE_LIB += -lnsl
 endif
 ifdef NO_STRCASESTR
-	ALL_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
-	LIB_OBJS += compat/strcasestr.o
+	COMPAT_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
+	COMPAT_OBJS += compat/strcasestr.o
+endif
+ifdef NO_SETENV
+	COMPAT_CFLAGS += -Dsetenv=gitsetenv -DNO_SETENV=1
+	COMPAT_OBJS += compat/setenv.o
 endif
 ifdef NO_MMAP
-	ALL_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
-	LIB_OBJS += compat/mmap.o
+	COMPAT_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
+	COMPAT_OBJS += compat/mmap.o
 endif
 ifdef NO_IPV6
 	ALL_CFLAGS += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
@@ -344,8 +353,8 @@
 endif
 endif
 
-ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER))
-
+ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) $(COMPAT_CFLAGS)
+LIB_OBJS += $(COMPAT_OBJS)
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 ### Build rules
 
@@ -354,10 +363,9 @@
 all:
 	$(MAKE) -C templates
 
-# Only use $(CFLAGS). We don't need anything else.
-git$(X): git.c Makefile
+git$(X): git.c $(COMPAT_OBJS) Makefile
 	$(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
-		$(CFLAGS) $< -o $@
+		$(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(filter %.o,$^)
 
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
 	rm -f $@
diff --git a/apply.c b/apply.c
index 50be8f3..1742ab2 100644
--- a/apply.c
+++ b/apply.c
@@ -16,6 +16,9 @@
 //  --numstat does numeric diffstat, and doesn't actually apply
 //  --index-info shows the old and new index info for paths if available.
 //
+static const char *prefix;
+static int prefix_length = -1;
+
 static int allow_binary_replacement = 0;
 static int check_index = 0;
 static int write_index = 0;
@@ -1706,6 +1709,12 @@
 			return 0;
 		x = x->next;
 	}
+	if (0 < prefix_length) {
+		int pathlen = strlen(pathname);
+		if (pathlen <= prefix_length ||
+		    memcmp(prefix, pathname, prefix_length))
+			return 0;
+	}
 	return 1;
 }
 
@@ -1845,6 +1854,15 @@
 			line_termination = 0;
 			continue;
 		}
+
+		if (check_index && prefix_length < 0) {
+			prefix = setup_git_directory();
+			prefix_length = prefix ? strlen(prefix) : 0;
+			git_config(git_default_config);
+		}
+		if (0 < prefix_length)
+			arg = prefix_filename(prefix, prefix_length, arg);
+
 		fd = open(arg, O_RDONLY);
 		if (fd < 0)
 			usage(apply_usage);
diff --git a/cache.h b/cache.h
index 6ac94c5..f9b367f 100644
--- a/cache.h
+++ b/cache.h
@@ -147,8 +147,10 @@
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
 extern const char **get_pathspec(const char *prefix, const char **pathspec);
+extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
 extern const char *prefix_path(const char *prefix, int len, const char *path);
+extern const char *prefix_filename(const char *prefix, int len, const char *path);
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
@@ -182,6 +184,10 @@
 extern int only_use_symrefs;
 extern int diff_rename_limit_default;
 
+#define GIT_REPO_VERSION 0
+extern int repository_format_version;
+extern int check_repository_format(void);
+
 #define MTIME_CHANGED	0x0001
 #define CTIME_CHANGED	0x0002
 #define OWNER_CHANGED	0x0004
@@ -383,16 +389,21 @@
 
 typedef int (*config_fn_t)(const char *, const char *);
 extern int git_default_config(const char *, const char *);
+extern int git_config_from_file(config_fn_t fn, const char *);
 extern int git_config(config_fn_t fn);
 extern int git_config_int(const char *, const char *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int check_repository_format_version(const char *var, const char *value);
 
 #define MAX_GITNAME (1000)
 extern char git_default_email[MAX_GITNAME];
 extern char git_default_name[MAX_GITNAME];
 
+#define MAX_ENCODING_LENGTH 64
+extern char git_commit_encoding[MAX_ENCODING_LENGTH];
+
 /* Sane ctype - no locale, and works with signed chars */
 #undef isspace
 #undef isdigit
diff --git a/cat-file.c b/cat-file.c
index d775a15..7594108 100644
--- a/cat-file.c
+++ b/cat-file.c
@@ -11,27 +11,44 @@
 	char type[20];
 	void *buf;
 	unsigned long size;
+	int opt;
 
 	setup_git_directory();
 	if (argc != 3 || get_sha1(argv[2], sha1))
-		usage("git-cat-file [-t | -s | <type>] <sha1>");
+		usage("git-cat-file [-t|-s|-e|<type>] <sha1>");
 
-	if (!strcmp("-t", argv[1]) || !strcmp("-s", argv[1])) {
-		if (!sha1_object_info(sha1, type,
-				      argv[1][1] == 's' ? &size : NULL)) {
-			switch (argv[1][1]) {
-			case 't':
-				printf("%s\n", type);
-				break;
-			case 's':
-				printf("%lu\n", size);
-				break;
-			}
+	opt = 0;
+	if ( argv[1][0] == '-' ) {
+		opt = argv[1][1];
+		if ( !opt || argv[1][2] )
+			opt = -1; /* Not a single character option */
+	}
+
+	buf = NULL;
+	switch (opt) {
+	case 't':
+		if (!sha1_object_info(sha1, type, NULL)) {
+			printf("%s\n", type);
 			return 0;
 		}
-		buf = NULL;
-	} else {
+		break;
+
+	case 's':
+		if (!sha1_object_info(sha1, type, &size)) {
+			printf("%lu\n", size);
+			return 0;
+		}
+		break;
+
+	case 'e':
+		return !has_sha1_file(sha1);
+
+	case 0:
 		buf = read_object_with_reference(sha1, argv[1], &size, NULL);
+		break;
+
+	default:
+		die("git-cat-file: unknown option: %s\n", argv[1]);
 	}
 
 	if (!buf)
diff --git a/checkout-index.c b/checkout-index.c
index dab3778..f1e716d 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -34,6 +34,9 @@
  */
 #include "cache.h"
 
+static const char *prefix;
+static int prefix_length;
+
 static struct checkout state = {
 	.base_dir = "",
 	.base_dir_len = 0,
@@ -69,6 +72,10 @@
 		struct cache_entry *ce = active_cache[i];
 		if (ce_stage(ce))
 			continue;
+		if (prefix && *prefix &&
+		    ( ce_namelen(ce) <= prefix_length ||
+		      memcmp(prefix, ce->name, prefix_length) ))
+			continue;
 		if (checkout_entry(ce, &state) < 0)
 			errs++;
 	}
@@ -91,6 +98,9 @@
 	int newfd = -1;
 	int all = 0;
 
+	prefix = setup_git_directory();
+	prefix_length = prefix ? strlen(prefix) : 0;
+
 	if (read_cache() < 0) {
 		die("invalid cache");
 	}
@@ -155,7 +165,7 @@
 
 		if (all)
 			die("git-checkout-index: don't mix '--all' and explicit filenames");
-		checkout_file(arg);
+		checkout_file(prefix_path(prefix, prefix_length, arg));
 	}
 
 	if (all)
diff --git a/clone-pack.c b/clone-pack.c
index 9609219..a99a95c 100644
--- a/clone-pack.c
+++ b/clone-pack.c
@@ -271,6 +271,8 @@
 	int fd[2];
 	pid_t pid;
 
+	setup_git_directory();
+
 	nr_heads = 0;
 	heads = NULL;
 	for (i = 1; i < argc; i++) {
diff --git a/commit-tree.c b/commit-tree.c
index b60299f..4634b50 100644
--- a/commit-tree.c
+++ b/commit-tree.c
@@ -91,6 +91,8 @@
 	if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
 		usage(commit_tree_usage);
 
+	setup_git_directory();
+
 	check_valid(tree_sha1, "tree");
 	for (i = 2; i < argc; i += 2) {
 		char *a, *b;
diff --git a/compat/setenv.c b/compat/setenv.c
new file mode 100644
index 0000000..94acd2d
--- /dev/null
+++ b/compat/setenv.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <string.h>
+
+int gitsetenv(const char *name, const char *value, int replace)
+{
+	int out;
+	size_t namelen, valuelen;
+	char *envstr;
+
+	if (!name || !value) return -1;
+	if (!replace) {
+		char *oldval = NULL;
+		oldval = getenv(name);
+		if (oldval) return 0;
+	}
+
+	namelen = strlen(name);
+	valuelen = strlen(value);
+	envstr = malloc((namelen + valuelen + 2) * sizeof(char));
+	if (!envstr) return -1;
+
+	memcpy(envstr, name, namelen);
+	envstr[namelen] = '=';
+	memcpy(envstr + namelen + 1, value, valuelen);
+	envstr[namelen + valuelen + 1] = 0;
+
+	out = putenv(envstr);
+
+	free(envstr);
+	return out;
+}
diff --git a/config.c b/config.c
index 5cc8535..34584f6 100644
--- a/config.c
+++ b/config.c
@@ -11,6 +11,7 @@
 #define MAXNAME (256)
 
 static FILE *config_file;
+static const char *config_file_name;
 static int config_linenr;
 static int get_next_char(void)
 {
@@ -186,7 +187,7 @@
 		if (get_value(fn, var, baselen+1) < 0)
 			break;
 	}
-	die("bad config file line %d", config_linenr);
+	die("bad config file line %d in %s", config_linenr, config_file_name);
 }
 
 int git_config_int(const char *name, const char *value)
@@ -197,7 +198,7 @@
 		if (!*end)
 			return val;
 	}
-	die("bad config value for '%s'", name);
+	die("bad config value for '%s' in %s", name, config_file_name);
 }
 
 int git_config_bool(const char *name, const char *value)
@@ -236,25 +237,37 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "i18n.commitencoding")) {
+		strncpy(git_commit_encoding, value, sizeof(git_commit_encoding));
+		return 0;
+	}
+
 	/* Add other config variables here.. */
 	return 0;
 }
 
-int git_config(config_fn_t fn)
+int git_config_from_file(config_fn_t fn, const char *filename)
 {
 	int ret;
-	FILE *f = fopen(git_path("config"), "r");
+	FILE *f = fopen(filename, "r");
 
 	ret = -1;
 	if (f) {
 		config_file = f;
+		config_file_name = filename;
 		config_linenr = 1;
 		ret = git_parse_file(fn);
 		fclose(f);
+		config_file_name = NULL;
 	}
 	return ret;
 }
 
+int git_config(config_fn_t fn)
+{
+	return git_config_from_file(fn, git_path("config"));
+}
+
 /*
  * Find all the stuff for git_config_set() below.
  */
diff --git a/convert-objects.c b/convert-objects.c
index a892013..d78a8b4 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -316,6 +316,8 @@
 	unsigned char sha1[20];
 	struct entry *entry;
 
+	setup_git_directory();
+
 	if (argc != 2 || get_sha1(argv[1], sha1))
 		usage("git-convert-objects <sha1>");
 
diff --git a/daemon.c b/daemon.c
index 91b9656..539f6e8 100644
--- a/daemon.c
+++ b/daemon.c
@@ -82,9 +82,63 @@
 	va_end(params);
 }
 
+static int avoid_alias(char *p)
+{
+	int sl, ndot;
+
+	/* 
+	 * This resurrects the belts and suspenders paranoia check by HPA
+	 * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
+	 * does not do getcwd() based path canonicalizations.
+	 *
+	 * sl becomes true immediately after seeing '/' and continues to
+	 * be true as long as dots continue after that without intervening
+	 * non-dot character.
+	 */
+	if (!p || (*p != '/' && *p != '~'))
+		return -1;
+	sl = 1; ndot = 0;
+	p++;
+
+	while (1) {
+		char ch = *p++;
+		if (sl) {
+			if (ch == '.')
+				ndot++;
+			else if (ch == '/') {
+				if (ndot < 3)
+					/* reject //, /./ and /../ */
+					return -1;
+				ndot = 0;
+			}
+			else if (ch == 0) {
+				if (0 < ndot && ndot < 3)
+					/* reject /.$ and /..$ */
+					return -1;
+				return 0;
+			}
+			else
+				sl = ndot = 0;
+		}
+		else if (ch == 0)
+			return 0;
+		else if (ch == '/') {
+			sl = 1;
+			ndot = 0;
+		}
+	}
+}
+
 static char *path_ok(char *dir)
 {
-	char *path = enter_repo(dir, strict_paths);
+	char *path;
+
+	if (avoid_alias(dir)) {
+		logerror("'%s': aliased", dir);
+		return NULL;
+	}
+
+	path = enter_repo(dir, strict_paths);
 
 	if (!path) {
 		logerror("'%s': unable to chdir or not a git archive", dir);
@@ -96,9 +150,11 @@
 		int pathlen = strlen(path);
 
 		/* The validation is done on the paths after enter_repo
-		 * canonicalization, so whitelist should be written in
-		 * terms of real pathnames (i.e. after ~user is expanded
-		 * and symlinks resolved).
+		 * appends optional {.git,.git/.git} and friends, but 
+		 * it does not use getcwd().  So if your /pub is
+		 * a symlink to /mnt/pub, you can whitelist /pub and
+		 * do not have to say /mnt/pub.
+		 * Do not say /pub/.
 		 */
 		for ( pp = ok_paths ; *pp ; pp++ ) {
 			int len = strlen(*pp);
diff --git a/debian/changelog b/debian/changelog
index 7356fe7..7e5d1ac 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+git-core (0.99.9l-0) unstable; urgency=low
+
+  * GIT 0.99.9l aka 1.0rc4
+
+ -- Junio C Hamano <junkio@cox.net>  Sat,  3 Dec 2005 23:45:23 -0800
+
 git-core (0.99.9k-0) unstable; urgency=low
 
   * GIT 0.99.9k but not 1.0rc yet.
diff --git a/diff-files.c b/diff-files.c
index 38599b5..6c0696c 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -7,12 +7,12 @@
 #include "diff.h"
 
 static const char diff_files_usage[] =
-"git-diff-files [-q] "
-"[<common diff options>] [<path>...]"
+"git-diff-files [-q] [-0/-1/2/3] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
 static struct diff_options diff_options;
 static int silent = 0;
+static int diff_unmerged_stage = 2;
 
 static void show_unmerge(const char *path)
 {
@@ -46,7 +46,21 @@
 			argc--;
 			break;
 		}
-		if (!strcmp(argv[1], "-q"))
+		if (!strcmp(argv[1], "-0"))
+			diff_unmerged_stage = 0;
+		else if (!strcmp(argv[1], "-1"))
+			diff_unmerged_stage = 1;
+		else if (!strcmp(argv[1], "-2"))
+			diff_unmerged_stage = 2;
+		else if (!strcmp(argv[1], "-3"))
+			diff_unmerged_stage = 3;
+		else if (!strcmp(argv[1], "--base"))
+			diff_unmerged_stage = 1;
+		else if (!strcmp(argv[1], "--ours"))
+			diff_unmerged_stage = 2;
+		else if (!strcmp(argv[1], "--theirs"))
+			diff_unmerged_stage = 3;
+		else if (!strcmp(argv[1], "-q"))
 			silent = 1;
 		else if (!strcmp(argv[1], "-r"))
 			; /* no-op */
@@ -95,11 +109,26 @@
 
 		if (ce_stage(ce)) {
 			show_unmerge(ce->name);
-			while (i < entries &&
-			       !strcmp(ce->name, active_cache[i]->name))
+			while (i < entries) {
+				struct cache_entry *nce = active_cache[i];
+
+				if (strcmp(ce->name, nce->name))
+					break;
+				/* diff against the proper unmerged stage */
+				if (ce_stage(nce) == diff_unmerged_stage)
+					ce = nce;
 				i++;
-			i--; /* compensate for loop control increments */
-			continue;
+			}
+			/*
+			 * Compensate for loop update
+			 */
+			i--;
+			/*
+			 * Show the diff for the 'ce' if we found the one
+			 * from the desired stage.
+			 */
+			if (ce_stage(ce) != diff_unmerged_stage)
+				continue;
 		}
 
 		if (lstat(ce->name, &st) < 0) {
diff --git a/environment.c b/environment.c
index b5026f1..0886ad3 100644
--- a/environment.c
+++ b/environment.c
@@ -13,6 +13,8 @@
 char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
 int only_use_symrefs = 0;
+int repository_format_version = 0;
+char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
 
 static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
 	*git_graft_file;
diff --git a/fetch-pack.c b/fetch-pack.c
index 6565982..58ba209 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -424,6 +424,8 @@
 	int fd[2];
 	pid_t pid;
 
+	setup_git_directory();
+
 	nr_heads = 0;
 	heads = NULL;
 	for (i = 1; i < argc; i++) {
diff --git a/fsck-objects.c b/fsck-objects.c
index 0433a1d..90e638e 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -431,6 +431,8 @@
 {
 	int i, heads;
 
+	setup_git_directory();
+
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/git-add.sh b/git-add.sh
index b5fe46a..fdec86d 100755
--- a/git-add.sh
+++ b/git-add.sh
@@ -1,5 +1,10 @@
 #!/bin/sh
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 usage() {
     die "usage: git add [-n] [-v] <file>..."
 }
diff --git a/git-applymbox.sh b/git-applymbox.sh
index 24d4a8c..c686cc8 100755
--- a/git-applymbox.sh
+++ b/git-applymbox.sh
@@ -33,7 +33,7 @@
 	-k)	keep_subject=-k ;;
 	-q)	query_apply=t ;;
 	-c)	continue="$2"; resume=f; shift ;;
-	-m)	fallback_3way=t ;;
+	-m)	fall_back_3way=t ;;
 	-*)	usage ;;
 	*)	break ;;
 	esac
diff --git a/git-applypatch.sh b/git-applypatch.sh
index f0549960..4c577eb 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -120,26 +120,36 @@
 	O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
 	rm -fr .patch-merge-*
 
+	if git-apply -z --index-info "$PATCHFILE" \
+		>.patch-merge-index-info 2>/dev/null &&
+		GIT_INDEX_FILE=.patch-merge-tmp-index \
+		git-update-index -z --index-info <.patch-merge-index-info &&
+		GIT_INDEX_FILE=.patch-merge-tmp-index \
+		git-write-tree >.patch-merge-tmp-base &&
+		(
+			mkdir .patch-merge-tmp-dir &&
+			cd .patch-merge-tmp-dir &&
+			GIT_INDEX_FILE="../.patch-merge-tmp-index" \
+			GIT_OBJECT_DIRECTORY="$O_OBJECT" \
+			git-apply $binary --index
+		) <"$PATCHFILE"
+	then
+		echo Using index info to reconstruct a base tree...
+		mv .patch-merge-tmp-base .patch-merge-base
+		mv .patch-merge-tmp-index .patch-merge-index
+	else
 	(
 		N=10
 
-		# if the patch records the base tree...
-		sed -ne '
-			/^diff /q
-			/^applies-to: \([0-9a-f]*\)$/{
-				s//\1/p
-				q
-			}
-		' "$PATCHFILE"
-
-		# or hoping the patch is against our recent commits...
+		# Otherwise, try nearby trees that can be used to apply the
+		# patch.
 		git-rev-list --max-count=$N HEAD
 
 		# or hoping the patch is against known tags...
 		git-ls-remote --tags .
 	) |
-	while read base junk
-	do
+	    while read base junk
+	    do
 		# Try it if we have it as a tree.
 		git-cat-file tree "$base" >/dev/null 2>&1 || continue
 
@@ -155,7 +165,8 @@
 			mv ../.patch-merge-tmp-index ../.patch-merge-index &&
 			echo "$base" >../.patch-merge-base
 		) <"$PATCHFILE"  2>/dev/null && break
-	done
+	    done
+	fi
 
 	test -f .patch-merge-index &&
 	his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
diff --git a/git-bisect.sh b/git-bisect.sh
index d92993b..68838f3 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -1,9 +1,19 @@
 #!/bin/sh
 . git-sh-setup
 
+sq() {
+	perl -e '
+		for (@ARGV) {
+			s/'\''/'\'\\\\\'\''/g;
+			print " '\''$_'\''";
+		}
+		print "\n";
+	' "$@"
+}
+
 usage() {
     echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]
-git bisect start		reset bisect state and start bisection.
+git bisect start [<pathspec>]	reset bisect state and start bisection.
 git bisect bad [<rev>]		mark <rev> a known-bad revision.
 git bisect good [<rev>...]	mark <rev>... known-good revisions.
 git bisect next			find next bisection to test and check it out.
@@ -33,7 +43,6 @@
 }
 
 bisect_start() {
-        case "$#" in 0) ;; *) usage ;; esac
 	#
 	# Verify HEAD. If we were bisecting before this, reset to the
 	# top-of-line master first!
@@ -57,7 +66,11 @@
 	rm -f "$GIT_DIR/refs/heads/bisect"
 	rm -rf "$GIT_DIR/refs/bisect/"
 	mkdir "$GIT_DIR/refs/bisect"
-	echo "git-bisect start" >"$GIT_DIR/BISECT_LOG"
+	{
+	    echo -n "git-bisect start"
+	    sq "$@"
+	} >"$GIT_DIR/BISECT_LOG"
+	sq "$@" >"$GIT_DIR/BISECT_NAMES"
 }
 
 bisect_bad() {
@@ -121,7 +134,7 @@
 	bad=$(git-rev-parse --verify refs/bisect/bad) &&
 	good=$(git-rev-parse --sq --revs-only --not \
 		$(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
-	rev=$(eval "git-rev-list --bisect $good $bad") || exit
+	rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
 	if [ -z "$rev" ]; then
 	    echo "$bad was both good and bad"
 	    exit 1
@@ -131,7 +144,7 @@
 	    git-diff-tree --pretty $rev
 	    exit 0
 	fi
-	nr=$(eval "git-rev-list $rev $good" | wc -l) || exit
+	nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
 	echo "Bisecting: $nr revisions left to test after this"
 	echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
 	git checkout new-bisect || exit
@@ -142,7 +155,8 @@
 
 bisect_visualize() {
 	bisect_next_check fail
-	gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*`
+	not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
+	eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
@@ -173,7 +187,8 @@
 		test "$bisect" = "git-bisect" || continue
 		case "$command" in
 		start)
-			bisect_start
+			cmd="bisect_start $rev"
+			eval "$cmd"
 			;;
 		good)
 			echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
diff --git a/git-branch.sh b/git-branch.sh
index 4cd5da16..5306b27 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -1,6 +1,11 @@
 #!/bin/sh
 
-. git-sh-setup
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
+
+die () {
+    echo >&2 "$*"
+    exit 1
+}
 
 usage () {
     echo >&2 "usage: $(basename $0)"' [-d <branch>] | [[-f] <branch> [start-point]]
@@ -12,8 +17,7 @@
     exit 1
 }
 
-headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD |
-	sed -e 's|^refs/heads/||')
+headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')
 
 delete_branch () {
     option="$1"
@@ -114,4 +118,3 @@
 	fi
 fi
 git update-ref "refs/heads/$branchname" $rev
-
diff --git a/git-clone.sh b/git-clone.sh
index c09979a..699205e 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -73,7 +73,7 @@
 	*,-n) no_checkout=yes ;;
 	*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
         *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) 
-          local_shared=yes ;;
+          local_shared=yes; use_local=yes ;;
 	*,-q|*,--quiet) quiet=-q ;;
 	1,-u|1,--upload-pack) usage ;;
 	*,-u|*,--upload-pack)
diff --git a/git-count-objects.sh b/git-count-objects.sh
index d6e9a32..40c58ef 100755
--- a/git-count-objects.sh
+++ b/git-count-objects.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-. git-sh-setup
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
 
 dc </dev/null 2>/dev/null || {
 	# This is not a real DC at all -- it just knows how
diff --git a/git-diff.sh b/git-diff.sh
index b3ec84b..b62e583 100755
--- a/git-diff.sh
+++ b/git-diff.sh
@@ -7,7 +7,10 @@
 flags=$(git-rev-parse --no-revs --flags --sq "$@")
 files=$(git-rev-parse --no-revs --no-flags --sq "$@")
 
-: ${flags:="'-M' '-p'"}
+die () {
+    echo >&2 "$*"
+    exit 1
+}
 
 # I often say 'git diff --cached -p' and get scolded by git-diff-files, but
 # obviously I mean 'git diff --cached -p HEAD' in that case.
@@ -20,6 +23,21 @@
 	esac
 esac
 
+# If we do not have --name-status, --name-only nor -r, default to -p.
+# If we do not have -B nor -C, default to -M.
+case " $flags " in
+*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* )
+	;;
+*)
+	flags="$flags'-p' " ;;
+esac
+case " $flags " in
+*" '-"[BCM]* | *" '--find-copies-harder' "*)
+	;; # something like -M50.
+*)
+	flags="$flags'-M' " ;;
+esac
+
 case "$rev" in
 ?*' '?*' '?*)
 	echo >&2 "I don't understand"
diff --git a/git-format-patch.sh b/git-format-patch.sh
index bc56876..1eebe85 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -5,6 +5,10 @@
 
 . git-sh-setup
 
+# Force diff to run in C locale.
+LANG=C LC_ALL=C
+export LANG LC_ALL
+
 usage () {
     echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
     [--check] [--signoff] [-<diff options>...]
@@ -202,7 +206,7 @@
 	    ;;
 	esac
 
-	eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)"
+	eval "$(sed -ne "$whosepatchScript" $commsg)"
 	test "$author,$au" = ",$me" || {
 		mailScript="$mailScript"'
 	a\
@@ -238,9 +242,8 @@
 	echo
 	git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
 	echo
-	git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
 	git-diff-tree -p $diff_opts "$commit"
-	echo "---"
+	echo "-- "
 	echo "@@GIT_VERSION@@"
 
 	case "$mbox" in
@@ -268,7 +271,7 @@
     file=`printf '%04d-%stxt' $i "$title"`
     if test '' = "$stdout"
     then
-	    echo "* $file"
+	    echo "$file"
 	    process_one >"$outdir$file"
 	    if test t = "$check"
 	    then
@@ -279,7 +282,7 @@
 		:
 	    fi
     else
-	    echo >&2 "* $file"
+	    echo >&2 "$file"
 	    process_one
     fi
     i=`expr "$i" + 1`
diff --git a/git-lost-found.sh b/git-lost-found.sh
index 9dd7430..2beec2a 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -1,7 +1,6 @@
 #!/bin/sh
 
-. git-sh-setup
-
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
 laf="$GIT_DIR/lost-found"
 rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
 
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index dc6a775..f699268 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -6,6 +6,11 @@
     exit 1;
 }
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 while case "$#" in 0) break;; esac
 do
   case "$1" in
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index bb58e22..7adffdc 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -8,6 +8,11 @@
 LF='
 '
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 # The first parameters up to -- are merge bases; the rest are heads.
 bases= head= remotes= sep_seen=
 for arg
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index c3eca8b..eafef77 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -26,7 +26,7 @@
 	fi
 	if test -f "$4"; then
 		rm -f -- "$4" &&
-		rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null
+		rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null || :
 	fi &&
 		exec git-update-index --remove -- "$4"
 	;;
@@ -58,6 +58,14 @@
 # Modified in both, but differently.
 #
 "$1$2$3" | ".$2$3")
+
+	case ",$6,$7," in
+	*,120000,*)
+		echo "ERROR: $4: Not merging symbolic link changes."
+		exit 1
+		;;
+	esac
+
 	src2=`git-unpack-file $3`
 	case "$1" in
 	'')
@@ -79,10 +87,12 @@
 		;;
 	esac
 
-	# We reset the index to the first branch, making
-	# git-diff-file useful
-	git-update-index --add --cacheinfo "$6" "$2" "$4"
-		git-checkout-index -u -f -- "$4" &&
+	# Create the working tree file, with the correct permission bits.
+	# we can not rely on the fact that our tree has the path, because
+	# we allow the merge to be done in an unchecked-out working tree.
+	rm -f "$4" &&
+		git-cat-file blob "$2" >"$4" &&
+		case "$6" in *7??) chmod +x "$4" ;; esac &&
 		merge "$4" "$orig" "$src2"
 	ret=$?
 	rm -f -- "$orig" "$src2"
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index 0129233..b7fb096 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -98,7 +98,7 @@
 def getFilesAndDirs(tree):
     files = Set()
     dirs = Set()
-    out = runProgram(['git-ls-tree', '-r', '-z', tree])
+    out = runProgram(['git-ls-tree', '-r', '-z', '-t', tree])
     for l in out.split('\0'):
         m = getFilesRE.match(l)
         if m:
@@ -828,8 +828,6 @@
             if cacheOnly:
                 updateFile(False, sha, mode, path)
             else:
-                updateFileExt(aSha, aMode, path,
-                              updateCache=True, updateWd=False)
                 updateFileExt(sha, mode, path, updateCache=False, updateWd=True)
     else:
         die("ERROR: Fatal merge failure, shouldn't happen.")
diff --git a/git-merge.sh b/git-merge.sh
index d352a3c..a221daa 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -273,7 +273,8 @@
 case "$best_strategy" in
 '')
 	restorestate
-	die "No merge strategy handled the merge."
+	echo >&2 "No merge strategy handled the merge."
+	exit 2
 	;;
 "$wt_strategy")
 	# We already have its result in the working tree.
diff --git a/git-mv.perl b/git-mv.perl
index bf54c38..b6c0b48 100755
--- a/git-mv.perl
+++ b/git-mv.perl
@@ -13,35 +13,15 @@
 
 sub usage() {
 	print <<EOT;
-$0 [-f] [-n] <source> <dest>
-$0 [-f] [-k] [-n] <source> ... <dest directory>
-
-In the first form, source must exist and be either a file,
-symlink or directory, dest must not exist. It renames source to dest.
-In the second form, the last argument has to be an existing
-directory; the given sources will be moved into this directory.
-
-Updates the git cache to reflect the change.
-Use "git commit" to make the change permanently.
-
-Options:
-  -f   Force renaming/moving, even if target exists
-  -k   Continue on error by skipping
-       not-existing or not revision-controlled source
-  -n   Do nothing; show what would happen
+$0 [-f] [-n] <source> <destination>
+$0 [-f] [-n] [-k] <source> ... <destination directory>
 EOT
 	exit(1);
 }
 
-# Sanity checks:
-my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
-
-unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && 
-	-d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
-    print "Git repository not found.";
-    usage();
-}
-
+my $GIT_DIR = `git rev-parse --git-dir`;
+exit 1 if $?; # rev-parse would have given "not a git dir" message.
+chomp($GIT_DIR);
 
 our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
 getopts("hnfkv") || usage;
@@ -70,7 +50,7 @@
 	print "Error: moving to directory '"
 	    . $ARGV[$argCount-1]
 	    . "' not possible; not exisiting\n";
-	usage;
+	exit(1);
     }
     @srcArgs = ($ARGV[0]);
     @dstArgs = ($ARGV[1]);
@@ -122,7 +102,7 @@
 	}
     }
     
-    if (($bad eq "") && ($src eq $dstDir)) {
+    if (($bad eq "") && ($dst =~ /^$safesrc\//)) {
 	$bad = "can not move directory '$src' into itself";
     }
 
@@ -148,7 +128,7 @@
 	    next;
 	}
 	print "Error: $bad\n";
-	usage();
+	exit(1);
     }
     push @srcs, $src;
     push @dsts, $dst;
@@ -156,14 +136,22 @@
 
 # Final pass: rename/move
 my (@deletedfiles,@addedfiles,@changedfiles);
+$bad = "";
 while(scalar @srcs > 0) {
     $src = shift @srcs;
     $dst = shift @dsts;
 
     if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; }
     if (!$opt_n) {
-	rename($src,$dst)
-	    or die "rename failed: $!";
+	if (!rename($src,$dst)) {
+	    $bad = "renaming '$src' failed: $!";
+	    if ($opt_k) {
+		print "Warning: skipped: $bad\n";
+		$bad = "";
+		next;
+	    }
+	    last;
+	}
     }
 
     $safesrc = quotemeta($src);
@@ -187,20 +175,44 @@
 }
 
 if ($opt_n) {
+    if (@changedfiles) {
 	print "Changed  : ". join(", ", @changedfiles) ."\n";
+    }
+    if (@addedfiles) {
 	print "Adding   : ". join(", ", @addedfiles) ."\n";
+    }
+    if (@deletedfiles) {
 	print "Deleting : ". join(", ", @deletedfiles) ."\n";
-	exit(1);
+    }
 }
-	
-my $rc;
-if (scalar @changedfiles >0) {
-	$rc = system("git-update-index","--",@changedfiles);
-	die "git-update-index failed to update changed files with code $?\n" if $rc;
+else {
+    if (@changedfiles) {
+	open(H, "| git-update-index -z --stdin")
+		or die "git-update-index failed to update changed files with code $!\n";
+	foreach my $fileName (@changedfiles) {
+		print H "$fileName\0";
+	}
+	close(H);
+    }
+    if (@addedfiles) {
+	open(H, "| git-update-index --add -z --stdin")
+		or die "git-update-index failed to add new names with code $!\n";
+	foreach my $fileName (@addedfiles) {
+		print H "$fileName\0";
+	}
+	close(H);
+    }
+    if (@deletedfiles) {
+	open(H, "| git-update-index --remove -z --stdin")
+		or die "git-update-index failed to remove old names with code $!\n";
+	foreach my $fileName (@deletedfiles) {
+		print H "$fileName\0";
+	}
+	close(H);
+    }
 }
-if (scalar @addedfiles >0) {
-	$rc = system("git-update-index","--add","--",@addedfiles);
-	die "git-update-index failed to add new names with code $?\n" if $rc;
+
+if ($bad ne "") {
+    print "Error: $bad\n";
+    exit(1);
 }
-$rc = system("git-update-index","--remove","--",@deletedfiles);
-die "git-update-index failed to remove old names with code $?\n" if $rc;
diff --git a/git-rebase.sh b/git-rebase.sh
index 2bc3a12..638ff0d 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -5,9 +5,25 @@
 
 . git-sh-setup
 
-# The other head is given
+# Make sure we do not have .dotest
+if mkdir .dotest
+then
+	rmdir .dotest
+else
+	echo >&2 '
+It seems that I cannot create a .dotest directory, and I wonder if you
+are in the middle of patch application or another rebase.  If that is not
+the case, please rm -fr .dotest and run me again.  I am stopping in case
+you still have something valuable there.'
+	exit 1
+fi
+
+# The other head is given.  Make sure it is valid.
 other=$(git-rev-parse --verify "$1^0") || exit
 
+# Make sure we have HEAD that is valid.
+head=$(git-rev-parse --verify "HEAD^0") || exit
+
 # The tree must be really really clean.
 git-update-index --refresh || exit
 diff=$(git-diff-index --cached --name-status -r HEAD)
@@ -23,6 +39,16 @@
 	git-checkout "$2" || exit
 esac
 
+# If the HEAD is a proper descendant of $other, we do not even need
+# to rebase.  Make sure we do not do needless rebase.  In such a
+# case, merge-base should be the same as "$other".
+mb=$(git-merge-base "$other" "$head")
+if test "$mb" = "$other"
+then
+	echo >&2 "Current branch `git-symbolic-ref HEAD` is up to date."
+	exit 0
+fi
+
 # Rewind the head to "$other"
 git-reset --hard "$other"
 git-format-patch -k --stdout --full-index "$other" ORIG_HEAD |
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 45d77c5..65868a9 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -35,7 +35,7 @@
 sub usage() {
 	print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from SVN
-       [-o branch-for-HEAD] [-h] [-v] [-l max_num_changes]
+       [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-s start_chg] [-m] [-M regex] [SVN_URL]
 END
@@ -126,8 +126,9 @@
 package main;
 use URI;
 
-my $svn = $svn_url;
+our $svn = $svn_url;
 $svn .= "/$svn_dir" if defined $svn_dir;
+my $svn2 = SVNconn->new($svn);
 $svn = SVNconn->new($svn);
 
 my $lwp_ua;
@@ -198,7 +199,7 @@
 my $maxnum = 0;
 my $last_rev = "";
 my $last_branch;
-my $current_rev = $opt_s-1;
+my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
 	system("git-init-db");
 	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
@@ -254,7 +255,7 @@
 		my($num,$branch,$ref) = split;
 		$branches{$branch}{$num} = $ref;
 		$branches{$branch}{"LAST"} = $ref;
-		$current_rev = $num if $current_rev < $num;
+		$current_rev = $num+1 if $current_rev <= $num;
 	}
 	close($B);
 }
@@ -708,17 +709,17 @@
 	print "DONE: $revision $dest $cid\n" if $opt_v;
 }
 
-my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
-sub _commit_all {
-	($changed_paths, $revision, $author, $date, $message, $pool) = @_;
+sub commit_all {
+	# Recursive use of the SVN connection does not work
+	local $svn = $svn2;
+
+	my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
 	my %p;
 	while(my($path,$action) = each %$changed_paths) {
 		$p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ];
 	}
 	$changed_paths = \%p;
-}
 
-sub commit_all {
 	my %done;
 	my @col;
 	my $pref;
@@ -734,18 +735,12 @@
 	}
 }
 
-while(++$current_rev <= $svn->{'maxrev'}) {
-	if (defined $opt_l) {
-		$opt_l--;
-		if ($opt_l < 0) {
-			last;
-		}
-	}
-	my $pool=SVN::Pool->new;
-	$svn->{'svn'}->get_log("/",$current_rev,$current_rev,1,1,1,\&_commit_all,$pool);
-	$pool->clear;
-	commit_all();
-}
+$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'};
+print "Fetching from $current_rev to $opt_l ...\n" if $opt_v;
+
+my $pool=SVN::Pool->new;
+$svn->{'svn'}->get_log("/",$current_rev,$opt_l,0,1,1,\&commit_all,$pool);
+$pool->clear;
 
 
 unlink($git_index);
diff --git a/git-tag.sh b/git-tag.sh
index 16efc5b..2435a75 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -1,13 +1,18 @@
 #!/bin/sh
 # Copyright (c) 2005 Linus Torvalds
 
-. git-sh-setup
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
 
 usage () {
     echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]"
     exit 1
 }
 
+die () {
+    echo >&2 "$*"
+    exit 1
+}
+
 annotate=
 signed=
 force=
diff --git a/git-verify-tag.sh b/git-verify-tag.sh
index 3c65f4a..1f44da5 100755
--- a/git-verify-tag.sh
+++ b/git-verify-tag.sh
@@ -1,5 +1,11 @@
 #!/bin/sh
-. git-sh-setup
+
+GIT_DIR=`git-rev-parse --git-dir` || exit $?
+
+die () {
+    echo >&2 "$*"
+    exit 1
+}
 
 type="$(git-cat-file -t "$1" 2>/dev/null)" ||
 	die "$1: no such object."
@@ -7,6 +13,9 @@
 test "$type" = tag ||
 	die "$1: cannot verify a non-tag object of type $type."
 
-git-cat-file tag "$1" > .tmp-vtag || exit 1
-cat .tmp-vtag | sed '/-----BEGIN PGP/Q' | gpg --verify .tmp-vtag - || exit 1
-rm -f .tmp-vtag
+git-cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1
+cat "$GIT_DIR/.tmp-vtag" |
+sed '/-----BEGIN PGP/Q' |
+gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1
+rm -f "$GIT_DIR/.tmp-vtag"
+
diff --git a/git.c b/git.c
index 0b10b6e..619f25a 100644
--- a/git.c
+++ b/git.c
@@ -13,6 +13,10 @@
 # define PATH_MAX 4096
 #endif
 
+#ifdef NO_SETENV
+extern int gitsetenv(char *name, char *value, int overwrite);
+#endif
+
 static const char git_usage[] =
 	"Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
 
@@ -283,16 +287,21 @@
 	len = strlen(git_command);
 	prepend_to_path(git_command, len);
 
-	strncat(&git_command[len], "/git-", sizeof(git_command) - len);
-	len += 5;
-	strncat(&git_command[len], argv[i], sizeof(git_command) - len);
-
-	if (access(git_command, X_OK))
-		usage(exec_path, "'%s' is not a git-command", argv[i]);
+	len += snprintf(git_command + len, sizeof(git_command) - len,
+			"/git-%s", argv[i]);
+	if (sizeof(git_command) <= len) {
+		fprintf(stderr, "git: command name given is too long (%d)\n", len);
+		exit(1);
+	}
 
 	/* execve() can only ever return if it fails */
 	execve(git_command, &argv[i], envp);
-	printf("Failed to run command '%s': %s\n", git_command, strerror(errno));
+
+	if (errno == ENOENT)
+		usage(exec_path, "'%s' is not a git-command", argv[i]);
+
+	fprintf(stderr, "Failed to run command '%s': %s\n",
+		git_command, strerror(errno));
 
 	return 1;
 }
diff --git a/gitk b/gitk
index 3dd97e2..a847ef6 100755
--- a/gitk
+++ b/gitk
@@ -19,7 +19,7 @@
 proc getcommits {rargs} {
     global commits commfd phase canv mainfont env
     global startmsecs nextupdate ncmupdate
-    global ctext maincursor textcursor leftover
+    global ctext maincursor textcursor leftover gitencoding
 
     # check that we can find a .git directory somewhere...
     set gitdir [gitdir]
@@ -30,7 +30,7 @@
     set commits {}
     set phase getcommits
     set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
+    set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
     if [catch {
 	set parse_args [concat --default HEAD $rargs]
@@ -49,7 +49,7 @@
 	exit 1
     }
     set leftover {}
-    fconfigure $commfd -blocking 0 -translation lf
+    fconfigure $commfd -blocking 0 -translation lf -encoding $gitencoding
     fileevent $commfd readable [list getcommitlines $commfd]
     $canv delete all
     $canv create text 3 3 -anchor nw -text "Reading commits..." \
@@ -74,9 +74,9 @@
 	}
 	if {[string range $err 0 4] == "usage"} {
 	    set err \
-{Gitk: error reading commits: bad arguments to git-rev-list.
-(Note: arguments to gitk are passed to git-rev-list
-to allow selection of commits to be displayed.)}
+		"Gitk: error reading commits: bad arguments to git-rev-list.\
+		(Note: arguments to gitk are passed to git-rev-list\
+		to allow selection of commits to be displayed.)"
 	} else {
 	    set err "Error reading commits: $err"
 	}
@@ -297,23 +297,26 @@
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
-    global rowctxmenu gaudydiff mergemax
+    global rowctxmenu mergemax
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
     menu .bar.file
     .bar.file add command -label "Reread references" -command rereadrefs
     .bar.file add command -label "Quit" -command doquit
+    menu .bar.edit
+    .bar add cascade -label "Edit" -menu .bar.edit
+    .bar.edit add command -label "Preferences" -command doprefs
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
     . configure -menu .bar
 
     if {![info exists geometry(canv1)]} {
-	set geometry(canv1) [expr 45 * $charspc]
-	set geometry(canv2) [expr 30 * $charspc]
-	set geometry(canv3) [expr 15 * $charspc]
-	set geometry(canvh) [expr 25 * $linespc + 4]
+	set geometry(canv1) [expr {45 * $charspc}]
+	set geometry(canv2) [expr {30 * $charspc}]
+	set geometry(canv3) [expr {15 * $charspc}]
+	set geometry(canvh) [expr {25 * $linespc + 4}]
 	set geometry(ctextw) 80
 	set geometry(ctexth) 30
 	set geometry(cflistw) 30
@@ -414,25 +417,19 @@
     .ctop.cdet add .ctop.cdet.left
 
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    if {$gaudydiff} {
-	$ctext tag conf hunksep -back blue -fore white
-	$ctext tag conf d0 -back "#ff8080"
-	$ctext tag conf d1 -back green
-    } else {
-	$ctext tag conf hunksep -fore blue
-	$ctext tag conf d0 -fore red
-	$ctext tag conf d1 -fore "#00a000"
-	$ctext tag conf m0 -fore red
-	$ctext tag conf m1 -fore blue
-	$ctext tag conf m2 -fore green
-	$ctext tag conf m3 -fore purple
-	$ctext tag conf m4 -fore brown
-	$ctext tag conf mmax -fore darkgrey
-	set mergemax 5
-	$ctext tag conf mresult -font [concat $textfont bold]
-	$ctext tag conf msep -font [concat $textfont bold]
-	$ctext tag conf found -back yellow
-    }
+    $ctext tag conf hunksep -fore blue
+    $ctext tag conf d0 -fore red
+    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf m0 -fore red
+    $ctext tag conf m1 -fore blue
+    $ctext tag conf m2 -fore green
+    $ctext tag conf m3 -fore purple
+    $ctext tag conf m4 -fore brown
+    $ctext tag conf mmax -fore darkgrey
+    set mergemax 5
+    $ctext tag conf mresult -font [concat $textfont bold]
+    $ctext tag conf msep -font [concat $textfont bold]
+    $ctext tag conf found -back yellow
 
     frame .ctop.cdet.right
     set cflist .ctop.cdet.right.cfiles
@@ -533,7 +530,7 @@
 
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont
-    global stuffsaved findmergefiles gaudydiff maxgraphpct
+    global stuffsaved findmergefiles maxgraphpct
     global maxwidth
 
     if {$stuffsaved} return
@@ -543,15 +540,14 @@
 	puts $f [list set mainfont $mainfont]
 	puts $f [list set textfont $textfont]
 	puts $f [list set findmergefiles $findmergefiles]
-	puts $f [list set gaudydiff $gaudydiff]
 	puts $f [list set maxgraphpct $maxgraphpct]
 	puts $f [list set maxwidth $maxwidth]
 	puts $f "set geometry(width) [winfo width .ctop]"
 	puts $f "set geometry(height) [winfo height .ctop]"
-	puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
-	puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
-	puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
-	puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
+	puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
+	puts $f "set geometry(canv2) [expr {[winfo width $canv2]-2}]"
+	puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]"
+	puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]"
 	set wid [expr {([winfo width $ctext] - 8) \
 			   / [font measure $textfont "0"]}]
 	puts $f "set geometry(ctextw) $wid"
@@ -580,12 +576,12 @@
 		set sash0 30
 	    }
 	    if {$sash1 < $sash0 + 20} {
-		set sash1 [expr $sash0 + 20]
+		set sash1 [expr {$sash0 + 20}]
 	    }
 	    if {$sash1 > $w - 10} {
-		set sash1 [expr $w - 10]
+		set sash1 [expr {$w - 10}]
 		if {$sash0 > $sash1 - 20} {
-		    set sash0 [expr $sash1 - 20]
+		    set sash0 [expr {$sash1 - 20}]
 		}
 	    }
 	}
@@ -608,7 +604,7 @@
 		set sash0 45
 	    }
 	    if {$sash0 > $w - 15} {
-		set sash0 [expr $w - 15]
+		set sash0 [expr {$w - 15}]
 	    }
 	}
 	$win sash place 0 $sash0 [lindex $s0 1]
@@ -819,9 +815,9 @@
     }
     set x [xcoord $level $level $lineno]
     set y1 $canvy
-    set canvy [expr $canvy + $linespc]
+    set canvy [expr {$canvy + $linespc}]
     allcanvs conf -scrollregion \
-	[list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
+	[list 0 0 0 [expr {$y1 + 0.5 * $linespc + 2}]]
     if {[info exists mainline($id)]} {
 	lappend mainline($id) $x $y1
 	if {$mainlinearrow($id) ne "none"} {
@@ -830,8 +826,8 @@
     }
     drawlines $id 0 0
     set orad [expr {$linespc / 3}]
-    set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
-	       [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
+    set t [$canv create oval [expr {$x - $orad}] [expr {$y1 - $orad}] \
+	       [expr {$x + $orad - 1}] [expr {$y1 + $orad - 1}] \
 	       -fill $ofill -outline black -width 1]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
@@ -886,8 +882,8 @@
     }
 
     set delta [expr {int(0.5 * ($linespc - $lthickness))}]
-    set yt [expr $y1 - 0.5 * $linespc]
-    set yb [expr $yt + $linespc - 1]
+    set yt [expr {$y1 - 0.5 * $linespc}]
+    set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
     foreach tag $marks {
@@ -900,12 +896,12 @@
 	       -width $lthickness -fill black -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
-	set xl [expr $x + $delta]
-	set xr [expr $x + $delta + $wid + $lthickness]
+	set xl [expr {$x + $delta}]
+	set xr [expr {$x + $delta + $wid + $lthickness}]
 	if {[incr ntags -1] >= 0} {
 	    # draw a tag
-	    set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \
-		       $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
+	    set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
+		       $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($idline($id)) [expr {$xr + $linespc}]
@@ -916,7 +912,7 @@
 	    } else {
 		set col "#ddddff"
 	    }
-	    set xl [expr $xl - $delta/2]
+	    set xl [expr {$xl - $delta/2}]
 	    $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
 		-width 1 -outline black -fill $col -tags tag.$id
 	}
@@ -1491,7 +1487,7 @@
 
     if {$displayorder == {}} return
     set startmsecs [clock clicks -milliseconds]
-    set nextupdate [expr $startmsecs + 100]
+    set nextupdate [expr {$startmsecs + 100}]
     set ncmupdate 1
     initgraph
     foreach id $displayorder {
@@ -1520,7 +1516,7 @@
     }
     drawmore 0
     set phase {}
-    set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
+    set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
     #puts "overall $drawmsecs ms for $numcommits commits"
     if {$redisplaying} {
 	if {$stopped == 0 && [info exists selectedline]} {
@@ -1548,8 +1544,8 @@
 	set matches {}
 	set i 0
 	while {[set j [string first $foundstring $str $i]] >= 0} {
-	    lappend matches [list $j [expr $j+$foundstrlen-1]]
-	    set i [expr $j + $foundstrlen]
+	    lappend matches [list $j [expr {$j+$foundstrlen-1}]]
+	    set i [expr {$j + $foundstrlen}]
 	}
     }
     return $matches
@@ -1630,7 +1626,7 @@
 	set matches [findmatches $f]
 	foreach match $matches {
 	    set start [lindex $match 0]
-	    set end [expr [lindex $match 1] + 1]
+	    set end [expr {[lindex $match 1] + 1}]
 	    $ctext tag add found "1.0 + $start c" "1.0 + $end c"
 	}
     }
@@ -1984,9 +1980,10 @@
 	set start [lindex $match 0]
 	set end [lindex $match 1]
 	if {$start > $end} continue
-	set xoff [font measure $font [string range $str 0 [expr $start-1]]]
-	set xlen [font measure $font [string range $str 0 [expr $end]]]
-	set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
+	set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
+	set xlen [font measure $font [string range $str 0 [expr {$end}]]]
+	set t [$canv create rect [expr {$x0+$xoff}] $y0 \
+		   [expr {$x0+$xlen+2}] $y1 \
 		   -outline {} -tags matches -fill yellow]
 	$canv lower $t
     }
@@ -2078,8 +2075,8 @@
     set ytop [expr {$y - $linespc - 1}]
     set ybot [expr {$y + $linespc + 1}]
     set wnow [$canv yview]
-    set wtop [expr [lindex $wnow 0] * $ymax]
-    set wbot [expr [lindex $wnow 1] * $ymax]
+    set wtop [expr {[lindex $wnow 0] * $ymax}]
+    set wbot [expr {[lindex $wnow 1] * $ymax}]
     set wh [expr {$wbot - $wtop}]
     set newtop $wtop
     if {$ytop < $wtop} {
@@ -2105,7 +2102,7 @@
 	if {$newtop < 0} {
 	    set newtop 0
 	}
-	allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
+	allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
     }
 
     if {$isnew} {
@@ -2164,7 +2161,7 @@
     $cflist delete 0 end
     $cflist insert end "Comments"
     if {$nparents($id) == 1} {
-	startdiff [concat $id $parents($id)]
+	startdiff $id
     } elseif {$nparents($id) > 1} {
 	mergediff $id
     }
@@ -2173,7 +2170,7 @@
 proc selnextline {dir} {
     global selectedline
     if {![info exists selectedline]} return
-    set l [expr $selectedline + $dir]
+    set l [expr {$selectedline + $dir}]
     unmarkmatches
     selectline $l 1
 }
@@ -2273,12 +2270,12 @@
     # diff the child against each of the parents, and diff
     # each of the parents against the GCA.
     while 1 {
-	if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
-	    set ids [list [lindex $ids 1] $diffmergegca]
+	if {[lindex $ids 1] == $diffmergeid && $diffmergegca ne {}} {
+	    set ids [list $diffmergegca [lindex $ids 0]]
 	} else {
 	    if {[incr diffpindex] >= $nparents($diffmergeid)} break
 	    set p [lindex $parents($diffmergeid) $diffpindex]
-	    set ids [list $diffmergeid $p]
+	    set ids [list $p $diffmergeid]
 	}
 	if {![info exists treediffs($ids)]} {
 	    set diffids $ids
@@ -2296,8 +2293,8 @@
     if {$diffmergegca ne {}} {
 	set files {}
 	foreach p $parents($diffmergeid) {
-	    set gcadiffs $treediffs([list $p $diffmergegca])
-	    foreach f $treediffs([list $diffmergeid $p]) {
+	    set gcadiffs $treediffs([list $diffmergegca $p])
+	    foreach f $treediffs([list $p $diffmergeid]) {
 		if {[lsearch -exact $files $f] < 0
 		    && [lsearch -exact $gcadiffs $f] >= 0} {
 		    lappend files $f
@@ -2310,7 +2307,7 @@
 	set files $treediffs([list $diffmergeid $p])
 	for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
 	    set p [lindex $parents($diffmergeid) $i]
-	    set df $treediffs([list $diffmergeid $p])
+	    set df $treediffs([list $p $diffmergeid])
 	    set nf {}
 	    foreach f $files {
 		if {[lsearch -exact $df $f] >= 0} {
@@ -2787,8 +2784,7 @@
     global treediff parents treepending
     set treepending $ids
     set treediff {}
-    set id [lindex $ids 0]
-    if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return
+    if [catch {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]}] return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
 }
@@ -2821,9 +2817,8 @@
     global diffopts blobdifffd diffids env curdifftag curtagstart
     global difffilestart nextupdate diffinhdr treediffs
 
-    set id [lindex $ids 0]
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [list | git-diff-tree --no-commit-id -r -p -C $id]
+    set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
     if {[catch {set bdf [open $cmd r]} err]} {
 	puts "error getting diffs: $err"
 	return
@@ -2842,7 +2837,6 @@
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
     global nextupdate diffinhdr treediffs
-    global gaudydiff
 
     set n [gets $bdf line]
     if {$n < 0} {
@@ -2891,26 +2885,14 @@
 	set diffinhdr 0
     } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
 		   $line match f1l f1c f2l f2c rest]} {
-	if {$gaudydiff} {
-	    $ctext insert end "\t" hunksep
-	    $ctext insert end "    $f1l    " d0 "    $f2l    " d1
-	    $ctext insert end "    $rest \n" hunksep
-	} else {
-	    $ctext insert end "$line\n" hunksep
-	}
+	$ctext insert end "$line\n" hunksep
 	set diffinhdr 0
     } else {
 	set x [string range $line 0 0]
 	if {$x == "-" || $x == "+"} {
 	    set tag [expr {$x == "+"}]
-	    if {$gaudydiff} {
-		set line [string range $line 1 end]
-	    }
 	    $ctext insert end "$line\n" d$tag
 	} elseif {$x == " "} {
-	    if {$gaudydiff} {
-		set line [string range $line 1 end]
-	    }
 	    $ctext insert end "$line\n"
 	} elseif {$diffinhdr || $x == "\\"} {
 	    # e.g. "\ No newline at end of file"
@@ -2966,8 +2948,8 @@
 
     set linespc [font metrics $mainfont -linespace]
     set charspc [font measure $mainfont "m"]
-    set canvy0 [expr 3 + 0.5 * $linespc]
-    set canvx0 [expr 3 + 0.5 * $linespc]
+    set canvy0 [expr {3 + 0.5 * $linespc}]
+    set canvx0 [expr {3 + 0.5 * $linespc}]
     set lthickness [expr {int($linespc / 9) + 1}]
     set xspc1(0) $linespc
     set xspc2 $linespc
@@ -3340,7 +3322,7 @@
     $ctext conf -state disabled
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
-    startdiff [list $newid $oldid]
+    startdiff [list $oldid $newid]
 }
 
 proc mkpatch {} {
@@ -3635,33 +3617,94 @@
     destroy .
 }
 
-proc formatdate {d} {
-    global hours nhours tfd fastdate
+proc doprefs {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
 
-    if {!$fastdate} {
-	return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+    set top .gitkprefs
+    set prefstop $top
+    if {[winfo exists $top]} {
+	raise $top
+	return
     }
-    set hr [expr {$d / 3600}]
-    set ms [expr {$d % 3600}]
-    if {![info exists hours($hr)]} {
-	set hours($hr) [clock format $d -format "%Y-%m-%d %H"]
-	set nhours($hr) 0
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+	set oldprefs($v) [set $v]
     }
-    incr nhours($hr)
-    set minsec [format "%.2d:%.2d" [expr {$ms/60}] [expr {$ms%60}]]
-    return "$hours($hr):$minsec"
+    toplevel $top
+    wm title $top "Gitk preferences"
+    label $top.ldisp -text "Commit list display options"
+    grid $top.ldisp - -sticky w -pady 10
+    label $top.spacer -text " "
+    label $top.maxwidthl -text "Maximum graph width (lines)" \
+	-font optionfont
+    spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
+    grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
+    label $top.maxpctl -text "Maximum graph width (% of pane)" \
+	-font optionfont
+    spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
+    grid x $top.maxpctl $top.maxpct -sticky w
+    checkbutton $top.findm -variable findmergefiles
+    label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
+	-font optionfont
+    grid $top.findm $top.findml - -sticky w
+    label $top.ddisp -text "Diff display options"
+    grid $top.ddisp - -sticky w -pady 10
+    label $top.diffoptl -text "Options for diff program" \
+	-font optionfont
+    entry $top.diffopt -width 20 -textvariable diffopts
+    grid x $top.diffoptl $top.diffopt -sticky w
+    frame $top.buts
+    button $top.buts.ok -text "OK" -command prefsok
+    button $top.buts.can -text "Cancel" -command prefscan
+    grid $top.buts.ok $top.buts.can
+    grid columnconfigure $top.buts 0 -weight 1 -uniform a
+    grid columnconfigure $top.buts 1 -weight 1 -uniform a
+    grid $top.buts - - -pady 10 -sticky ew
+}
+
+proc prefscan {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
+
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+	set $v $oldprefs($v)
+    }
+    catch {destroy $prefstop}
+    unset prefstop
+}
+
+proc prefsok {} {
+    global maxwidth maxgraphpct
+    global oldprefs prefstop
+
+    catch {destroy $prefstop}
+    unset prefstop
+    if {$maxwidth != $oldprefs(maxwidth)
+	|| $maxgraphpct != $oldprefs(maxgraphpct)} {
+	redisplay
+    }
+}
+
+proc formatdate {d} {
+    return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
 }
 
 # defaults...
 set datemode 0
-set boldnames 0
 set diffopts "-U 5 -p"
 set wrcomcmd "git-diff-tree --stdin -p --pretty"
 
+set gitencoding ""
+catch {
+    set gitencoding [exec git-repo-config --get i18n.commitencoding]
+}
+if {$gitencoding == ""} {
+	set gitencoding "utf-8"
+}
+
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set findmergefiles 0
-set gaudydiff 0
 set maxgraphpct 50
 set maxwidth 16
 set revlistorder 0
@@ -3672,15 +3715,13 @@
 catch {source ~/.gitk}
 
 set namefont $mainfont
-if {$boldnames} {
-    lappend namefont bold
-}
+
+font create optionfont -family sans-serif -size -12
 
 set revtreeargs {}
 foreach arg $argv {
     switch -regexp -- $arg {
 	"^$" { }
-	"^-b" { set boldnames 1 }
 	"^-d" { set datemode 1 }
 	"^-r" { set revlistorder 1 }
 	default {
diff --git a/hash-object.c b/hash-object.c
index c8c9adb..ccba11c 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -29,6 +29,8 @@
 	int i;
 	const char *type = "blob";
 	int write_object = 0;
+	const char *prefix = NULL;
+	int prefix_length = -1;
 
 	for (i = 1 ; i < argc; i++) {
 		if (!strcmp(argv[i], "-t")) {
@@ -36,10 +38,20 @@
 				die(hash_object_usage);
 			type = argv[i];
 		}
-		else if (!strcmp(argv[i], "-w"))
+		else if (!strcmp(argv[i], "-w")) {
+			if (prefix_length < 0) {
+				prefix = setup_git_directory();
+				prefix_length = prefix ? strlen(prefix) : 0;
+			}
 			write_object = 1;
-		else
-			hash_object(argv[i], type, write_object);
+		}
+		else {
+			const char *arg = argv[i];
+			if (0 <= prefix_length)
+				arg = prefix_filename(prefix, prefix_length,
+						      arg);
+			hash_object(arg, type, write_object);
+		}
 	}
 	return 0;
 }
diff --git a/http-fetch.c b/http-fetch.c
index 4353173..ad59f1c 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -922,6 +922,8 @@
 	int arg = 1;
 	int rc = 0;
 
+	setup_git_directory();
+
 	while (arg < argc && argv[arg][0] == '-') {
 		if (argv[arg][1] == 't') {
 			get_tree = 1;
diff --git a/http-push.c b/http-push.c
index 76c7886..fe92560 100644
--- a/http-push.c
+++ b/http-push.c
@@ -784,7 +784,7 @@
 					strtol(ctx->cdata + 7, NULL, 10);
 		} else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
 			if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) {
-				lock->token = xmalloc(strlen(ctx->cdata - 15));
+				lock->token = xmalloc(strlen(ctx->cdata) - 15);
 				strcpy(lock->token, ctx->cdata + 16);
 			}
 		}
@@ -1008,9 +1008,7 @@
 	if (lock->owner != NULL)
 		free(lock->owner);
 	free(lock->url);
-/* Freeing the token causes a segfault...
 	free(lock->token);
-*/
 	free(lock);
 
 	return rc;
@@ -1239,6 +1237,7 @@
 	int rc = 0;
 	int i;
 
+	setup_git_directory();
 	setup_ident();
 
 	remote = xmalloc(sizeof(*remote));
@@ -1273,6 +1272,9 @@
 		break;
 	}
 
+	if (!remote->url)
+		usage(http_push_usage);
+
 	memset(remote_dir_exists, 0, 256);
 
 	http_init();
diff --git a/init-db.c b/init-db.c
index bd88291..8195b68 100644
--- a/init-db.c
+++ b/init-db.c
@@ -132,6 +132,23 @@
 		return;
 	}
 
+	/* Make sure that template is from the correct vintage */
+	strcpy(template_path + template_len, "config");
+	repository_format_version = 0;
+	git_config_from_file(check_repository_format_version,
+			     template_path);
+	template_path[template_len] = 0;
+
+	if (repository_format_version &&
+	    repository_format_version != GIT_REPO_VERSION) {
+		fprintf(stderr, "warning: not copying templates of "
+			"a wrong format version %d from '%s'\n",
+			repository_format_version,
+			template_dir);
+		closedir(dir);
+		return;
+	}
+
 	memcpy(path, git_dir, len);
 	path[len] = 0;
 	copy_templates_1(path, len,
@@ -140,12 +157,13 @@
 	closedir(dir);
 }
 
-static void create_default_files(const char *git_dir,
-				 char *template_path)
+static void create_default_files(const char *git_dir, char *template_path)
 {
 	unsigned len = strlen(git_dir);
 	static char path[PATH_MAX];
 	unsigned char sha1[20];
+	struct stat st1;
+	char repo_version_string[10];
 
 	if (len > sizeof(path)-50)
 		die("insane git directory %s", git_dir);
@@ -164,6 +182,15 @@
 	strcpy(path + len, "refs/tags");
 	safe_create_dir(path);
 
+	/* 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);
+
+	git_config(git_default_config);
+
 	/*
 	 * Create the default symlink from ".git/HEAD" to the "master"
 	 * branch, if it does not exist yet.
@@ -173,44 +200,22 @@
 		if (create_symref(path, "refs/heads/master") < 0)
 			exit(1);
 	}
+
+	/* This forces creation of new config file */
+	sprintf(repo_version_string, "%d", GIT_REPO_VERSION);
+	git_config_set("core.repositoryformatversion", repo_version_string);
+
 	path[len] = 0;
-	copy_templates(path, len, template_path);
-
-	/*
-	 * Find out if we can trust the executable bit.
-	 */
-	safe_create_dir(path);
 	strcpy(path + len, "config");
-	if (access(path, R_OK) < 0) {
-		static const char contents[] =
-			"#\n"
-			"# This is the config file\n"
-			"#\n"
-			"\n"
-			"; core variables\n"
-			"[core]\n"
-			"	; Don't trust file modes\n"
-			"	filemode = false\n"
-			"\n";
-		FILE *config = fopen(path, "w");
-		struct stat st;
 
-		if (!config)
-			die("Can not write to %s?", path);
-
-		fwrite(contents, sizeof(contents)-1, 1, config);
-
-		fclose(config);
-
-		if (!lstat(path, &st)) {
-			struct stat st2;
-			if (!chmod(path, st.st_mode ^ S_IXUSR) &&
-					!lstat(path, &st2) &&
-					st.st_mode != st2.st_mode)
-				unlink(path);
-			else
-				fprintf(stderr, "Ignoring file modes\n");
-		}
+	/* Check filemode trustability */
+	if (!lstat(path, &st1)) {
+		struct stat st2;
+		int filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+				!lstat(path, &st2) &&
+				st1.st_mode != st2.st_mode);
+		git_config_set("core.filemode",
+			       filemode ? "true" : "false");
 	}
 }
 
@@ -249,6 +254,14 @@
 		fprintf(stderr, "defaulting to local storage area\n");
 	}
 	safe_create_dir(git_dir);
+
+	/* Check to see if the repository version is right.
+	 * Note that a newly created repository does not have
+	 * config file, so this will not fail.  What we are catching
+	 * is an attempt to reinitialize new repository with an old tool.
+	 */
+	check_repository_format();
+
 	create_default_files(git_dir, template_dir);
 
 	/*
diff --git a/local-fetch.c b/local-fetch.c
index 0931109..fa9e697 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -207,6 +207,8 @@
 	char *commit_id;
 	int arg = 1;
 
+	setup_git_directory();
+
 	while (arg < argc && argv[arg][0] == '-') {
 		if (argv[arg][1] == 't')
 			get_tree = 1;
diff --git a/ls-files.c b/ls-files.c
index db2288a..f3f1a6a 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -344,7 +344,7 @@
 		return;
 
 	fputs(tag, stdout);
-	write_name_quoted("", ent->name + offset, line_terminator, stdout);
+	write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
 	putchar(line_terminator);
 }
 
@@ -433,7 +433,8 @@
 
 	if (!show_stage) {
 		fputs(tag, stdout);
-		write_name_quoted("", ce->name + offset, line_terminator, stdout);
+		write_name_quoted("", 0, ce->name + offset,
+				  line_terminator, stdout);
 		putchar(line_terminator);
 	}
 	else {
@@ -442,7 +443,8 @@
 		       ntohl(ce->ce_mode),
 		       sha1_to_hex(ce->sha1),
 		       ce_stage(ce));
-		write_name_quoted("", ce->name + offset, line_terminator, stdout);
+		write_name_quoted("", 0, ce->name + offset,
+				  line_terminator, stdout);
 		putchar(line_terminator);
 	}
 }
diff --git a/ls-tree.c b/ls-tree.c
index d9f15e3..dae377d 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -11,217 +11,75 @@
 static int line_termination = '\n';
 #define LS_RECURSIVE 1
 #define LS_TREE_ONLY 2
+#define LS_SHOW_TREES 4
+#define LS_NAME_ONLY 8
 static int ls_options = 0;
-
-static struct tree_entry_list root_entry;
-
-static void prepare_root(unsigned char *sha1)
-{
-	unsigned char rsha[20];
-	unsigned long size;
-	void *buf;
-	struct tree *root_tree;
-
-	buf = read_object_with_reference(sha1, "tree", &size, rsha);
-	free(buf);
-	if (!buf)
-		die("Could not read %s", sha1_to_hex(sha1));
-
-	root_tree = lookup_tree(rsha);
-	if (!root_tree)
-		die("Could not read %s", sha1_to_hex(sha1));
-
-	/* Prepare a fake entry */
-	root_entry.directory = 1;
-	root_entry.executable = root_entry.symlink = 0;
-	root_entry.mode = S_IFDIR;
-	root_entry.name = "";
-	root_entry.item.tree = root_tree;
-	root_entry.parent = NULL;
-}
-
-static int prepare_children(struct tree_entry_list *elem)
-{
-	if (!elem->directory)
-		return -1;
-	if (!elem->item.tree->object.parsed) {
-		struct tree_entry_list *e;
-		if (parse_tree(elem->item.tree))
-			return -1;
-		/* Set up the parent link */
-		for (e = elem->item.tree->entries; e; e = e->next)
-			e->parent = elem;
-	}
-	return 0;
-}
-
-static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
-{
-	const char *next, *slash;
-	int len;
-	struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
-
-	*(pathbuf) = '\0';
-
-	/* Find tree element, descending from root, that
-	 * corresponds to the named path, lazily expanding
-	 * the tree if possible.
-	 */
-
-	while (path) {
-		/* The fact we still have path means that the caller
-		 * wants us to make sure that elem at this point is a
-		 * directory, and possibly descend into it.  Even what
-		 * is left is just trailing slashes, we loop back to
-		 * here, and this call to prepare_children() will
-		 * catch elem not being a tree.  Nice.
-		 */
-		if (prepare_children(elem))
-			return NULL;
-
-		slash = strchr(path, '/');
-		if (!slash) {
-			len = strlen(path);
-			next = NULL;
-		}
-		else {
-			next = slash + 1;
-			len = slash - path;
-		}
-		if (len) {
-			if (oldelem) {
-				pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
-			}
-
-			/* (len == 0) if the original path was "drivers/char/"
-			 * and we have run already two rounds, having elem
-			 * pointing at the drivers/char directory.
-			 */
-			elem = elem->item.tree->entries;
-			while (elem) {
-				if ((strlen(elem->name) == len) &&
-				    !strncmp(elem->name, path, len)) {
-					/* found */
-					break;
-				}
-				elem = elem->next;
-			}
-			if (!elem)
-				return NULL;
-
-			oldelem = elem;
-		}
-		path = next;
-	}
-
-	return elem;
-}
-
-static const char *entry_type(struct tree_entry_list *e)
-{
-	return (e->directory ? "tree" : "blob");
-}
-
-static const char *entry_hex(struct tree_entry_list *e)
-{
-	return sha1_to_hex(e->directory
-			   ? e->item.tree->object.sha1
-			   : e->item.blob->object.sha1);
-}
-
-/* forward declaration for mutually recursive routines */
-static int show_entry(struct tree_entry_list *, int, char *pathbuf);
-
-static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
-{
-	int oldlen = strlen(pathbuf);
-
-	if (e != &root_entry)
-		sprintf(pathbuf + oldlen, "%s/", e->name);
-
-	if (prepare_children(e))
-		die("internal error: ls-tree show_children called with non tree");
-	e = e->item.tree->entries;
-	while (e) {
-		show_entry(e, level, pathbuf);
-		e = e->next;
-	}
-
-	pathbuf[oldlen] = '\0';
-
-	return 0;
-}
-
-static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
-{
-	int err = 0; 
-
-	if (e != &root_entry) {
-		printf("%06o %s %s	",
-		       e->mode, entry_type(e), entry_hex(e));
-		write_name_quoted(pathbuf, e->name, line_termination, stdout);
-		putchar(line_termination);
-	}
-
-	if (e->directory) {
-		/* If this is a directory, we have the following cases:
-		 * (1) This is the top-level request (explicit path from the
-		 *     command line, or "root" if there is no command line).
-		 *  a. Without any flag.  We show direct children.  We do not 
-		 *     recurse into them.
-		 *  b. With -r.  We do recurse into children.
-		 *  c. With -d.  We do not recurse into children.
-		 * (2) We came here because our caller is either (1-a) or
-		 *     (1-b).
-		 *  a. Without any flag.  We do not show our children (which
-		 *     are grandchildren for the original request).
-		 *  b. With -r.  We continue to recurse into our children.
-		 *  c. With -d.  We should not have come here to begin with.
-		 */
-		if (level == 0 && !(ls_options & LS_TREE_ONLY))
-			/* case (1)-a and (1)-b */
-			err = err | show_children(e, level+1, pathbuf);
-		else if (level && ls_options & LS_RECURSIVE)
-			/* case (2)-b */
-			err = err | show_children(e, level+1, pathbuf);
-	}
-	return err;
-}
-
-static int list_one(const char *path)
-{
-	int err = 0;
-	char pathbuf[MAXPATHLEN + 1];
-	struct tree_entry_list *e = find_entry(path, pathbuf);
-	if (!e) {
-		/* traditionally ls-tree does not complain about
-		 * missing path.  We may change this later to match
-		 * what "/bin/ls -a" does, which is to complain.
-		 */
-		return err;
-	}
-	err = err | show_entry(e, 0, pathbuf);
-	return err;
-}
-
-static int list(char **path)
-{
-	int i;
-	int err = 0;
-	for (i = 0; path[i]; i++)
-		err = err | list_one(path[i]);
-	return err;
-}
+const char **pathspec;
 
 static const char ls_tree_usage[] =
-	"git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
+	"git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] <tree-ish> [path...]";
 
-int main(int argc, char **argv)
+static int show_recursive(const char *base, int baselen, const char *pathname)
 {
-	static char *path0[] = { "", NULL };
-	char **path;
-	unsigned char sha1[20];
+	const char **s;
 
+	if (ls_options & LS_RECURSIVE)
+		return 1;
+
+	s = pathspec;
+	if (!s)
+		return 0;
+
+	for (;;) {
+		const char *spec = *s++;
+		int len, speclen;
+
+		if (!spec)
+			return 0;
+		if (strncmp(base, spec, baselen))
+			continue;
+		len = strlen(pathname);
+		spec += baselen;
+		speclen = strlen(spec);
+		if (speclen <= len)
+			continue;
+		if (memcmp(pathname, spec, len))
+			continue;
+		return 1;
+	}
+}
+
+static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+{
+	int retval = 0;
+	const char *type = "blob";
+
+	if (S_ISDIR(mode)) {
+		if (show_recursive(base, baselen, pathname)) {
+			retval = READ_TREE_RECURSIVE;
+			if (!(ls_options & LS_SHOW_TREES))
+				return retval;
+		}
+		type = "tree";
+	}
+	else if (ls_options & LS_TREE_ONLY)
+		return 0;
+
+	if (!(ls_options & LS_NAME_ONLY))
+		printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1));
+	write_name_quoted(base, baselen, pathname, line_termination, stdout);
+	putchar(line_termination);
+	return retval;
+}
+
+int main(int argc, const char **argv)
+{
+	const char *prefix;
+	unsigned char sha1[20];
+	char *buf;
+	unsigned long size;
+
+	prefix = setup_git_directory();
 	while (1 < argc && argv[1][0] == '-') {
 		switch (argv[1][1]) {
 		case 'z':
@@ -233,20 +91,36 @@
 		case 'd':
 			ls_options |= LS_TREE_ONLY;
 			break;
+		case 't':
+			ls_options |= LS_SHOW_TREES;
+			break;
+		case '-':
+			if (!strcmp(argv[1]+2, "name-only") ||
+			    !strcmp(argv[1]+2, "name-status")) {
+				ls_options |= LS_NAME_ONLY;
+				break;
+			}
+			/* otherwise fallthru */
 		default:
 			usage(ls_tree_usage);
 		}
 		argc--; argv++;
 	}
+	/* -d -r should imply -t, but -d by itself should not have to. */
+	if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
+	    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
+		ls_options |= LS_SHOW_TREES;
 
 	if (argc < 2)
 		usage(ls_tree_usage);
 	if (get_sha1(argv[1], sha1) < 0)
 		usage(ls_tree_usage);
 
-	path = (argc == 2) ? path0 : (argv + 2);
-	prepare_root(sha1);
-	if (list(path) < 0)
-		die("list failed");
+	pathspec = get_pathspec(prefix, argv + 2);
+	buf = read_object_with_reference(sha1, "tree", &size, NULL);
+	if (!buf)
+		die("not a tree object");
+	read_tree_recursive(buf, size, "", 0, 0, pathspec, show_tree);
+
 	return 0;
 }
diff --git a/mailinfo.c b/mailinfo.c
index cb853df..890e348 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -8,6 +8,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <iconv.h>
+#include "cache.h"
 
 #ifdef NO_STRCASESTR
 extern char *gitstrcasestr(const char *haystack, const char *needle);
@@ -16,7 +17,7 @@
 static FILE *cmitmsg, *patchfile;
 
 static int keep_subject = 0;
-static int metainfo_utf8 = 0;
+static char *metainfo_charset = NULL;
 static char line[1000];
 static char date[1000];
 static char name[1000];
@@ -441,29 +442,38 @@
 
 static void convert_to_utf8(char *line, char *charset)
 {
-	if (*charset) {
-		char *in, *out;
-		size_t insize, outsize, nrc;
-		char outbuf[4096]; /* cheat */
-		iconv_t conv = iconv_open("utf-8", charset);
+	char *in, *out;
+	size_t insize, outsize, nrc;
+	char outbuf[4096]; /* cheat */
+	static char latin_one[] = "latin-1";
+	char *input_charset = *charset ? charset : latin_one;
+	iconv_t conv = iconv_open(metainfo_charset, input_charset);
 
-		if (conv == (iconv_t) -1) {
-			fprintf(stderr, "cannot convert from %s to utf-8\n",
-				charset);
+	if (conv == (iconv_t) -1) {
+		static int warned_latin1_once = 0;
+		if (input_charset != latin_one) {
+			fprintf(stderr, "cannot convert from %s to %s\n",
+				input_charset, metainfo_charset);
 			*charset = 0;
-			return;
 		}
-		in = line;
-		insize = strlen(in);
-		out = outbuf;
-		outsize = sizeof(outbuf);
-		nrc = iconv(conv, &in, &insize, &out, &outsize);
-		iconv_close(conv);
-		if (nrc == (size_t) -1)
-			return;
-		*out = 0;
-		strcpy(line, outbuf);
+		else if (!warned_latin1_once) {
+			warned_latin1_once = 1;
+			fprintf(stderr, "tried to convert from %s to %s, "
+				"but your iconv does not work with it.\n",
+				input_charset, metainfo_charset);
+		}
+		return;
 	}
+	in = line;
+	insize = strlen(in);
+	out = outbuf;
+	outsize = sizeof(outbuf);
+	nrc = iconv(conv, &in, &insize, &out, &outsize);
+	iconv_close(conv);
+	if (nrc == (size_t) -1)
+		return;
+	*out = 0;
+	strcpy(line, outbuf);
 }
 
 static void decode_header_bq(char *it)
@@ -511,7 +521,7 @@
 		}
 		if (sz < 0)
 			return;
-		if (metainfo_utf8)
+		if (metainfo_charset)
 			convert_to_utf8(piecebuf, charset_q);
 		strcpy(out, piecebuf);
 		out += strlen(out);
@@ -590,7 +600,7 @@
 		 * normalize the log message to UTF-8.
 		 */
 		decode_transfer_encoding(line);
-		if (metainfo_utf8)
+		if (metainfo_charset)
 			convert_to_utf8(line, charset);
 		fputs(line, cmitmsg);
 	} while (fgets(line, sizeof(line), stdin) != NULL);
@@ -707,27 +717,29 @@
 }
 
 static const char mailinfo_usage[] =
-	"git-mailinfo [-k] [-u] msg patch <mail >info";
-
-static void usage(void) {
-	fprintf(stderr, "%s\n", mailinfo_usage);
-	exit(1);
-}
+	"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
 
 int main(int argc, char **argv)
 {
+	/* NEEDSWORK: might want to do the optional .git/ directory
+	 * discovery
+	 */
+	git_config(git_default_config);
+
 	while (1 < argc && argv[1][0] == '-') {
 		if (!strcmp(argv[1], "-k"))
 			keep_subject = 1;
 		else if (!strcmp(argv[1], "-u"))
-			metainfo_utf8 = 1;
+			metainfo_charset = git_commit_encoding;
+		else if (!strncmp(argv[1], "--encoding=", 11))
+			metainfo_charset = argv[1] + 11;
 		else
-			usage();
+			usage(mailinfo_usage);
 		argc--; argv++;
 	}
 
 	if (argc != 3)
-		usage();
+		usage(mailinfo_usage);
 	cmitmsg = fopen(argv[1], "w");
 	if (!cmitmsg) {
 		perror(argv[1]);
diff --git a/merge-base.c b/merge-base.c
index 751c3c2..e73fca7 100644
--- a/merge-base.c
+++ b/merge-base.c
@@ -236,6 +236,8 @@
 	struct commit *rev1, *rev2;
 	unsigned char rev1key[20], rev2key[20];
 
+	setup_git_directory();
+
 	while (1 < argc && argv[1][0] == '-') {
 		char *arg = argv[1];
 		if (!strcmp(arg, "-a") || !strcmp(arg, "--all"))
diff --git a/merge-index.c b/merge-index.c
index 727527f..024196e 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -102,6 +102,7 @@
 	if (argc < 3)
 		usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
 
+	setup_git_directory();
 	read_cache();
 
 	i = 1;
diff --git a/mktag.c b/mktag.c
index 585677e..97e270a 100644
--- a/mktag.c
+++ b/mktag.c
@@ -111,6 +111,8 @@
 	if (argc != 1)
 		usage("cat <signaturefile> | git-mktag");
 
+	setup_git_directory();
+
 	// Read the signature
 	size = 0;
 	for (;;) {
diff --git a/name-rev.c b/name-rev.c
index 817e36b..65333d4 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -21,7 +21,7 @@
 {
 	struct rev_name *name = (struct rev_name *)commit->object.util;
 	struct commit_list *parents;
-	int parent_number = 0;
+	int parent_number = 1;
 
 	if (!commit->object.parsed)
 		parse_commit(commit);
@@ -56,7 +56,7 @@
 	for (parents = commit->parents;
 			parents;
 			parents = parents->next, parent_number++) {
-		if (parent_number > 0) {
+		if (parent_number > 1) {
 			char *new_name = xmalloc(strlen(tip_name)+8);
 
 			if (generation > 0)
@@ -217,10 +217,9 @@
 					if (!strcmp(name, "undefined"))
 						continue;
 
-					fwrite(p_start, p - p_start, 1, stdout);
-					fputc('(', stdout);
-					fputs(name, stdout);
-					fputc(')', stdout);
+					fwrite(p_start, p - p_start + 1, 1,
+					       stdout);
+					printf(" (%s)", name);
 					p_start = p + 1;
 				}
 			}
diff --git a/pack-objects.c b/pack-objects.c
index 8864a31..a62c9f8 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -473,6 +473,8 @@
 	struct object_entry **list;
 	int i;
 
+	setup_git_directory();
+
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/pack-redundant.c b/pack-redundant.c
index 793fa08..0a43278 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -600,6 +600,8 @@
 	unsigned char *sha1;
 	char buf[42]; /* 40 byte sha1 + \n + \0 */
 
+	setup_git_directory();
+
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		if(!strcmp(arg, "--")) {
diff --git a/path.c b/path.c
index 4d88947..334b2bd 100644
--- a/path.c
+++ b/path.c
@@ -131,75 +131,121 @@
 	return -1;
 }
 
-static char *current_dir(void)
+static char *user_path(char *buf, char *path, int sz)
 {
-	return getcwd(pathname, sizeof(pathname));
-}
+	struct passwd *pw;
+	char *slash;
+	int len, baselen;
 
-static int user_chdir(char *path)
-{
-	char *dir = path;
-
-	if(*dir == '~') {		/* user-relative path */
-		struct passwd *pw;
-		char *slash = strchr(dir, '/');
-
-		dir++;
-		/* '~/' and '~' (no slash) means users own home-dir */
-		if(!*dir || *dir == '/')
-			pw = getpwuid(getuid());
-		else {
-			if (slash) {
-				*slash = '\0';
-				pw = getpwnam(dir);
-				*slash = '/';
-			}
-			else
-				pw = getpwnam(dir);
-		}
-
-		/* make sure we got something back that we can chdir() to */
-		if(!pw || chdir(pw->pw_dir) < 0)
-			return -1;
-
-		if(!slash || !slash[1]) /* no path following username */
-			return 0;
-
-		dir = slash + 1;
-	}
-
-	/* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */
-	if(chdir(dir) < 0)
-		return -1;
-
-	return 0;
-}
-
-char *enter_repo(char *path, int strict)
-{
-	if(!path)
+	if (!path || path[0] != '~')
 		return NULL;
-
-	if (strict) {
-		if (chdir(path) < 0)
-			return NULL;
+	path++;
+	slash = strchr(path, '/');
+	if (path[0] == '/' || !path[0]) {
+		pw = getpwuid(getuid());
 	}
 	else {
-		if (!*path)
-			; /* happy -- no chdir */
-		else if (!user_chdir(path))
-			; /* happy -- as given */
-		else if (!user_chdir(mkpath("%s.git", path)))
-			; /* happy -- uemacs --> uemacs.git */
+		if (slash) {
+			*slash = 0;
+			pw = getpwnam(path);
+			*slash = '/';
+		}
 		else
-			return NULL;
-		(void)chdir(".git");
+			pw = getpwnam(path);
 	}
+	if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
+		return NULL;
+	baselen = strlen(pw->pw_dir);
+	memcpy(buf, pw->pw_dir, baselen);
+	while ((1 < baselen) && (buf[baselen-1] == '/')) {
+		buf[baselen-1] = 0;
+		baselen--;
+	}
+	if (slash && slash[1]) {
+		len = strlen(slash);
+		if (sz <= baselen + len)
+			return NULL;
+		memcpy(buf + baselen, slash, len + 1);
+	}
+	return buf;
+}
 
-	if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
-	   validate_symref("HEAD") == 0) {
+/*
+ * First, one directory to try is determined by the following algorithm.
+ *
+ * (0) If "strict" is given, the path is used as given and no DWIM is
+ *     done. Otherwise:
+ * (1) "~/path" to mean path under the running user's home directory;
+ * (2) "~user/path" to mean path under named user's home directory;
+ * (3) "relative/path" to mean cwd relative directory; or
+ * (4) "/absolute/path" to mean absolute directory.
+ *
+ * Unless "strict" is given, we try access() for existence of "%s.git/.git",
+ * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
+ * what we try.
+ *
+ * Second, we try chdir() to that.  Upon failure, we return NULL.
+ *
+ * Then, we try if the current directory is a valid git repository.
+ * Upon failure, we return NULL.
+ *
+ * If all goes well, we return the directory we used to chdir() (but
+ * before ~user is expanded), avoiding getcwd() resolving symbolic
+ * links.  User relative paths are also returned as they are given,
+ * except DWIM suffixing.
+ */
+char *enter_repo(char *path, int strict)
+{
+	static char used_path[PATH_MAX];
+	static char validated_path[PATH_MAX];
+
+	if (!path)
+		return NULL;
+
+	if (!strict) {
+		static const char *suffix[] = {
+			".git/.git", "/.git", ".git", "", NULL,
+		};
+		int len = strlen(path);
+		int i;
+		while ((1 < len) && (path[len-1] == '/')) {
+			path[len-1] = 0;
+			len--;
+		}
+		if (PATH_MAX <= len)
+			return NULL;
+		if (path[0] == '~') {
+			if (!user_path(used_path, path, PATH_MAX))
+				return NULL;
+			strcpy(validated_path, path);
+			path = used_path;
+		}
+		else if (PATH_MAX - 10 < len)
+			return NULL;
+		else {
+			path = strcpy(used_path, path);
+			strcpy(validated_path, path);
+		}
+		len = strlen(path);
+		for (i = 0; suffix[i]; i++) {
+			strcpy(path + len, suffix[i]);
+			if (!access(path, F_OK)) {
+				strcat(validated_path, suffix[i]);
+				break;
+			}
+		}
+		if (!suffix[i] || chdir(path))
+			return NULL;
+		path = validated_path;
+	}
+	else if (chdir(path))
+		return NULL;
+
+	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
+	    validate_symref("HEAD") == 0) {
 		putenv("GIT_DIR=.");
-		return current_dir();
+		check_repository_format();
+		return path;
 	}
 
 	return NULL;
diff --git a/peek-remote.c b/peek-remote.c
index ee49bf3..a90cf22 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -27,6 +27,9 @@
 	char *dest = NULL;
 	int fd[2];
 	pid_t pid;
+	int nongit = 0;
+
+	setup_git_directory_gently(&nongit);
 
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
diff --git a/prune-packed.c b/prune-packed.c
index 26123f7..d24b097 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -58,6 +58,8 @@
 {
 	int i;
 
+	setup_git_directory();
+
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/quote.c b/quote.c
index e662a7d..76eb144 100644
--- a/quote.c
+++ b/quote.c
@@ -112,7 +112,8 @@
  *     but not enclosed in double-quote pair.  Return value is undefined.
  */
 
-int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+static int quote_c_style_counted(const char *name, int namelen,
+				 char *outbuf, FILE *outfp, int no_dq)
 {
 #undef EMIT
 #define EMIT(c) \
@@ -125,7 +126,7 @@
 
 	if (!no_dq)
 		EMIT('"');
-	for (sp = name; (ch = *sp++); ) {
+	for (sp = name; (ch = *sp++) && (sp - name) <= namelen; ) {
 
 		if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
 		    (ch == 0177)) {
@@ -162,6 +163,12 @@
 	return needquote ? count : 0;
 }
 
+int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq)
+{
+	int cnt = strlen(name);
+	return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq);
+}
+
 /*
  * C-style name unquoting.
  *
@@ -227,28 +234,30 @@
 	}
 }
 
-void write_name_quoted(const char *prefix, const char *name,
-		       int quote, FILE *out)
+void write_name_quoted(const char *prefix, int prefix_len,
+		       const char *name, int quote, FILE *out)
 {
 	int needquote;
 
 	if (!quote) {
 	no_quote:
-		if (prefix && prefix[0])
-			fputs(prefix, out);
+		if (prefix_len)
+			fprintf(out, "%.*s", prefix_len, prefix);
 		fputs(name, out);
 		return;
 	}
 
 	needquote = 0;
-	if (prefix && prefix[0])
-		needquote = quote_c_style(prefix, NULL, NULL, 0);
+	if (prefix_len)
+		needquote = quote_c_style_counted(prefix, prefix_len,
+						  NULL, NULL, 0);
 	if (!needquote)
 		needquote = quote_c_style(name, NULL, NULL, 0);
 	if (needquote) {
 		fputc('"', out);
-		if (prefix && prefix[0])
-			quote_c_style(prefix, NULL, out, 1);
+		if (prefix_len)
+			quote_c_style_counted(prefix, prefix_len,
+					      NULL, out, 1);
 		quote_c_style(name, NULL, out, 1);
 		fputc('"', out);
 	}
diff --git a/quote.h b/quote.h
index 2486e6e..c1ab378 100644
--- a/quote.h
+++ b/quote.h
@@ -41,7 +41,7 @@
 			 int nodq);
 extern char *unquote_c_style(const char *quoted, const char **endp);
 
-extern void write_name_quoted(const char *prefix, const char *name,
-			      int quote, FILE *out);
+extern void write_name_quoted(const char *prefix, int prefix_len,
+			      const char *name, int quote, FILE *out);
 
 #endif
diff --git a/read-tree.c b/read-tree.c
index df156ea..e3b9c0d 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -629,6 +629,8 @@
 	unsigned char sha1[20];
 	merge_fn_t fn = NULL;
 
+	setup_git_directory();
+
 	newfd = hold_index_file_for_update(&cache_file, get_index_file());
 	if (newfd < 0)
 		die("unable to create new cachefile");
diff --git a/rev-list.c b/rev-list.c
index e17f928..8020d97 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -350,7 +350,8 @@
 
 		if (commit->object.flags & (UNINTERESTING | COUNTED))
 			break;
-		nr++;
+		if (!paths || (commit->object.flags & TREECHANGE))
+			nr++;
 		commit->object.flags |= COUNTED;
 		p = commit->parents;
 		entry = p;
@@ -362,6 +363,7 @@
 			}
 		}
 	}
+
 	return nr;
 }
 
@@ -382,15 +384,20 @@
 	nr = 0;
 	p = list;
 	while (p) {
-		nr++;
+		if (!paths || (p->item->object.flags & TREECHANGE))
+			nr++;
 		p = p->next;
 	}
 	closest = 0;
 	best = list;
 
-	p = list;
-	while (p) {
-		int distance = count_distance(p);
+	for (p = list; p; p = p->next) {
+		int distance;
+
+		if (paths && !(p->item->object.flags & TREECHANGE))
+			continue;
+
+		distance = count_distance(p);
 		clear_distance(list);
 		if (nr - distance < distance)
 			distance = nr - distance;
@@ -398,7 +405,6 @@
 			best = p;
 			closest = distance;
 		}
-		p = p->next;
 	}
 	if (best)
 		best->next = NULL;
diff --git a/send-pack.c b/send-pack.c
index 3eeb18f..2a14b00 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -273,6 +273,7 @@
 	int fd[2], ret;
 	pid_t pid;
 
+	setup_git_directory();
 	argv++;
 	for (i = 1; i < argc; i++, argv++) {
 		char *arg = *argv;
diff --git a/setup.c b/setup.c
index ab3c778..d3556ed 100644
--- a/setup.c
+++ b/setup.c
@@ -47,6 +47,21 @@
 	return path;
 }
 
+/* 
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ */
+const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
+{
+	static char path[PATH_MAX];
+	if (!pfx || !*pfx || arg[0] == '/')
+		return arg;
+	memcpy(path, pfx, pfx_len);
+	strcpy(path + pfx_len, arg);
+	return path;
+}
+
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
 	const char *entry = *pathspec;
@@ -92,7 +107,7 @@
 	return 1;
 }
 
-static const char *setup_git_directory_1(void)
+const char *setup_git_directory_gently(int *nongit_ok)
 {
 	static char cwd[PATH_MAX+1];
 	int len, offset;
@@ -116,7 +131,7 @@
 		if (validate_symref(path))
 			goto bad_dir_environ;
 		if (getenv(DB_ENVIRONMENT)) {
-			if (access(DB_ENVIRONMENT, X_OK))
+			if (access(getenv(DB_ENVIRONMENT), X_OK))
 				goto bad_dir_environ;
 		}
 		else {
@@ -139,8 +154,15 @@
 			break;
 		chdir("..");
 		do {
-			if (!offset)
+			if (!offset) {
+				if (nongit_ok) {
+					if (chdir(cwd))
+						die("Cannot come back to cwd");
+					*nongit_ok = 1;
+					return NULL;
+				}
 				die("Not a git repository");
+			}
 		} while (cwd[--offset] != '/');
 	}
 
@@ -154,8 +176,25 @@
 	return cwd + offset;
 }
 
+int check_repository_format_version(const char *var, const char *value)
+{
+       if (strcmp(var, "core.repositoryformatversion") == 0)
+               repository_format_version = git_config_int(var, value);
+       return 0;
+}
+
+int check_repository_format(void)
+{
+	git_config(check_repository_format_version);
+	if (GIT_REPO_VERSION < repository_format_version)
+		die ("Expected git repo version <= %d, found %d",
+		     GIT_REPO_VERSION, repository_format_version);
+	return 0;
+}
+
 const char *setup_git_directory(void)
 {
-	const char *retval = setup_git_directory_1();
+	const char *retval = setup_git_directory_gently(NULL);
+	check_repository_format();
 	return retval;
 }
diff --git a/shell.c b/shell.c
index 2c4789e..cd31618 100644
--- a/shell.c
+++ b/shell.c
@@ -5,8 +5,7 @@
 {
 	const char *my_argv[4];
 
-	arg = sq_dequote(arg);
-	if (!arg)
+	if (!arg || !(arg = sq_dequote(arg)))
 		die("bad argument");
 
 	my_argv[0] = me;
diff --git a/ssh-fetch.c b/ssh-fetch.c
index bf01fbc..4eb9e04 100644
--- a/ssh-fetch.c
+++ b/ssh-fetch.c
@@ -131,6 +131,8 @@
 	prog = getenv("GIT_SSH_PUSH");
 	if (!prog) prog = "git-ssh-upload";
 
+	setup_git_directory();
+
 	while (arg < argc && argv[arg][0] == '-') {
 		if (argv[arg][1] == 't') {
 			get_tree = 1;
diff --git a/ssh-upload.c b/ssh-upload.c
index 603abcc..b675a0b 100644
--- a/ssh-upload.c
+++ b/ssh-upload.c
@@ -121,6 +121,9 @@
 
 	prog = getenv(COUNTERPART_ENV_NAME);
 	if (!prog) prog = COUNTERPART_PROGRAM_NAME;
+
+	setup_git_directory();
+
 	while (arg < argc && argv[arg][0] == '-') {
 		if (argv[arg][1] == 'w')
 			arg++;
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index dff7d69..22bdaca 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -126,19 +126,18 @@
     'git-ls-tree output for a known tree.' \
     'diff current expected'
 
+# This changed in ls-tree pathspec change -- recursive does
+# not show tree nodes anymore.
 test_expect_success \
     'showing tree with git-ls-tree -r' \
     'git-ls-tree -r $tree >current'
 cat >expected <<\EOF
 100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
 120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
 100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
 120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
-040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2	path3/subp3
 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
 EOF
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index c6ce56c..2ec06d3 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -54,6 +54,18 @@
      cat >expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
+100644 blob X	path2/baz/b
+120000 blob X	path2/bazbo
+100644 blob X	path2/foo
+EOF
+     test_output'
+
+test_expect_success \
+    'ls-tree recursive with -t' \
+    'git-ls-tree -r -t $tree >current &&
+     cat >expected <<\EOF &&
+100644 blob X	path0
+120000 blob X	path1
 040000 tree X	path2
 040000 tree X	path2/baz
 100644 blob X	path2/baz/b
@@ -63,6 +75,15 @@
      test_output'
 
 test_expect_success \
+    'ls-tree recursive with -d' \
+    'git-ls-tree -r -d $tree >current &&
+     cat >expected <<\EOF &&
+040000 tree X	path2
+040000 tree X	path2/baz
+EOF
+     test_output'
+
+test_expect_success \
     'ls-tree filtered with path' \
     'git-ls-tree $tree path >current &&
      cat >expected <<\EOF &&
@@ -70,12 +91,14 @@
      test_output'
 
 
+# it used to be path1 and then path0, but with pathspec semantics
+# they are shown in canonical order.
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git-ls-tree $tree path1 path0 >current &&
      cat >expected <<\EOF &&
-120000 blob X	path1
 100644 blob X	path0
+120000 blob X	path1
 EOF
      test_output'
 
@@ -86,45 +109,49 @@
 EOF
      test_output'
 
+# It used to show path2 and its immediate children but
+# with pathspec semantics it shows only path2
 test_expect_success \
     'ls-tree filtered with path2' \
     'git-ls-tree $tree path2 >current &&
      cat >expected <<\EOF &&
 040000 tree X	path2
+EOF
+     test_output'
+
+# ... and path2/ shows the children.
+test_expect_success \
+    'ls-tree filtered with path2/' \
+    'git-ls-tree $tree path2/ >current &&
+     cat >expected <<\EOF &&
 040000 tree X	path2/baz
 120000 blob X	path2/bazbo
 100644 blob X	path2/foo
 EOF
      test_output'
 
+# The same change -- exact match does not show children of
+# path2/baz
 test_expect_success \
     'ls-tree filtered with path2/baz' \
     'git-ls-tree $tree path2/baz >current &&
      cat >expected <<\EOF &&
 040000 tree X	path2/baz
-100644 blob X	path2/baz/b
 EOF
      test_output'
 
 test_expect_success \
-    'ls-tree filtered with path2' \
-    'git-ls-tree $tree path2 >current &&
+    'ls-tree filtered with path2/bak' \
+    'git-ls-tree $tree path2/bak >current &&
      cat >expected <<\EOF &&
-040000 tree X	path2
-040000 tree X	path2/baz
-120000 blob X	path2/bazbo
-100644 blob X	path2/foo
 EOF
      test_output'
 
 test_expect_success \
-    'ls-tree filtered with path2/' \
-    'git-ls-tree $tree path2/ >current &&
+    'ls-tree -t filtered with path2/bak' \
+    'git-ls-tree -t $tree path2/bak >current &&
      cat >expected <<\EOF &&
 040000 tree X	path2
-040000 tree X	path2/baz
-120000 blob X	path2/bazbo
-100644 blob X	path2/foo
 EOF
      test_output'
 
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 5410368..d78deb1 100644
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -59,24 +59,16 @@
 EOF
      test_output'
 
+# Recursive does not show tree nodes anymore...
 test_expect_success \
     'ls-tree recursive' \
     'git-ls-tree -r $tree >current &&
      cat >expected <<\EOF &&
 100644 blob X	1.txt
 100644 blob X	2.txt
-040000 tree X	path0
-040000 tree X	path0/a
-040000 tree X	path0/a/b
-040000 tree X	path0/a/b/c
 100644 blob X	path0/a/b/c/1.txt
-040000 tree X	path1
-040000 tree X	path1/b
-040000 tree X	path1/b/c
 100644 blob X	path1/b/c/1.txt
-040000 tree X	path2
 100644 blob X	path2/1.txt
-040000 tree X	path3
 100644 blob X	path3/1.txt
 100644 blob X	path3/2.txt
 EOF
@@ -110,41 +102,27 @@
 EOF
      test_output'
 
+# I am not so sure about this one after ls-tree doing pathspec match.
+# Having both path0/a and path0/a/b/c makes path0/a redundant, and
+# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
 test_expect_success \
     'ls-tree filter directories' \
     'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
      cat >expected <<\EOF &&
-040000 tree X	path3
-100644 blob X	path3/1.txt
-100644 blob X	path3/2.txt
-040000 tree X	path2
-100644 blob X	path2/1.txt
 040000 tree X	path0/a/b/c
-100644 blob X	path0/a/b/c/1.txt
 040000 tree X	path1/b/c
-100644 blob X	path1/b/c/1.txt
-040000 tree X	path0/a
-040000 tree X	path0/a/b
+040000 tree X	path2
+040000 tree X	path3
 EOF
      test_output'
 
+# Again, duplicates are filtered away so this is equivalent to
+# having 1.txt and path3
 test_expect_success \
     'ls-tree filter odd names' \
     'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
      cat >expected <<\EOF &&
 100644 blob X	1.txt
-100644 blob X	1.txt
-100644 blob X	1.txt
-100644 blob X	path3/1.txt
-100644 blob X	path3/1.txt
-100644 blob X	path3/1.txt
-040000 tree X	path3
-100644 blob X	path3/1.txt
-100644 blob X	path3/2.txt
-040000 tree X	path3
-100644 blob X	path3/1.txt
-100644 blob X	path3/2.txt
-040000 tree X	path3
 100644 blob X	path3/1.txt
 100644 blob X	path3/2.txt
 EOF
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
new file mode 100755
index 0000000..a19d49d
--- /dev/null
+++ b/t/t6020-merge-df.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+test_description='Test merge with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "Hello" > init &&
+git add init &&
+git commit -m "Initial commit" &&
+git branch B &&
+mkdir dir &&
+echo "foo" > dir/foo &&
+git add dir/foo &&
+git commit -m "File: dir/foo" &&
+git checkout B &&
+echo "file dir" > dir &&
+git add dir &&
+git commit -m "File: dir"'
+
+test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
+test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
new file mode 100755
index 0000000..e8606c7
--- /dev/null
+++ b/t/t6021-merge-criss-cross.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# nice decription of what this is about.
+
+
+test_description='Test criss-cross merge'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "1
+2
+3
+4
+5
+6
+7
+8
+9" > file &&
+git add file && 
+git commit -m "Initial commit" file &&
+git branch A &&
+git branch B &&
+git checkout A &&
+echo "1
+2
+3
+4
+5
+6
+7
+8 changed in B8, branch A
+9" > file &&
+git commit -m "B8" file &&
+git checkout B &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8
+9
+" > file &&
+git commit -m "C3" file &&
+git branch C3 &&
+git merge "pre E3 merge" B A &&
+echo "1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in B8, branch A
+9
+" > file &&
+git commit -m "E3" file &&
+git checkout A &&
+git merge "pre D8 merge" A C3 &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9" > file &&
+git commit -m D8 file'
+
+test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
+
+cat > file-expect <<EOF
+1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9
+EOF
+
+test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
new file mode 100755
index 0000000..43d74c5
--- /dev/null
+++ b/t/t7001-mv.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='git-mv in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git-add path0/COPYING &&
+     git-commit -m add -a'
+
+test_expect_success \
+    'moving the file' \
+    'cd path0 && git-mv COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+    'commiting the change' \
+    'cd .. && git-commit -m move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+    grep -E "^R100.+path0/COPYING.+path1/COPYING"'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e654155..f2eccd7 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -133,6 +133,19 @@
 	fi
 }
 
+test_expect_code () {
+	test "$#" = 3 ||
+	error "bug in the test script: not 3 parameters to test-expect-code"
+	say >&3 "expecting exit code $1: $3"
+	test_run_ "$3"
+	if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+	then
+		test_ok_ "$2"
+	else
+		test_failure_ "$@"
+	fi
+}
+
 test_done () {
 	trap - exit
 	case "$test_failure" in
diff --git a/tar-tree.c b/tar-tree.c
index 970c4bb..bacb23a 100644
--- a/tar-tree.c
+++ b/tar-tree.c
@@ -407,6 +407,8 @@
 	void *buffer;
 	unsigned long size;
 
+	setup_git_directory();
+
 	switch (argc) {
 	case 3:
 		basedir = argv[2];
diff --git a/tree.c b/tree.c
index 8b42a07..043f032 100644
--- a/tree.c
+++ b/tree.c
@@ -9,9 +9,16 @@
 
 static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-	int len = strlen(pathname);
-	unsigned int size = cache_entry_size(baselen + len);
-	struct cache_entry *ce = xmalloc(size);
+	int len;
+	unsigned int size;
+	struct cache_entry *ce;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	len = strlen(pathname);
+	size = cache_entry_size(baselen + len);
+	ce = xmalloc(size);
 
 	memset(ce, 0, size);
 
@@ -67,9 +74,10 @@
 	return 0;
 }
 
-static int read_tree_recursive(void *buffer, unsigned long size,
-			       const char *base, int baselen,
-			       int stage, const char **match)
+int read_tree_recursive(void *buffer, unsigned long size,
+			const char *base, int baselen,
+			int stage, const char **match,
+			read_tree_fn_t fn)
 {
 	while (size) {
 		int len = strlen(buffer)+1;
@@ -86,6 +94,14 @@
 		if (!match_tree_entry(base, baselen, path, mode, match))
 			continue;
 
+		switch (fn(sha1, base, baselen, path, mode, stage)) {
+		case 0:
+			continue;
+		case READ_TREE_RECURSIVE:
+			break;;
+		default:
+			return -1;
+		}
 		if (S_ISDIR(mode)) {
 			int retval;
 			int pathlen = strlen(path);
@@ -106,22 +122,20 @@
 			retval = read_tree_recursive(eltbuf, eltsize,
 						     newbase,
 						     baselen + pathlen + 1,
-						     stage, match);
+						     stage, match, fn);
 			free(eltbuf);
 			free(newbase);
 			if (retval)
 				return -1;
 			continue;
 		}
-		if (read_one_entry(sha1, base, baselen, path, mode, stage) < 0)
-			return -1;
 	}
 	return 0;
 }
 
 int read_tree(void *buffer, unsigned long size, int stage, const char **match)
 {
-	return read_tree_recursive(buffer, size, "", 0, stage, match);
+	return read_tree_recursive(buffer, size, "", 0, stage, match, read_one_entry);
 }
 
 struct tree *lookup_tree(const unsigned char *sha1)
diff --git a/tree.h b/tree.h
index 9975e88..768e5e9 100644
--- a/tree.h
+++ b/tree.h
@@ -35,4 +35,13 @@
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const unsigned char *sha1);
 
+#define READ_TREE_RECURSIVE 1
+typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int);
+
+extern int read_tree_recursive(void *buffer, unsigned long size,
+			const char *base, int baselen,
+			int stage, const char **match,
+			read_tree_fn_t fn);
+
+
 #endif /* TREE_H */
diff --git a/unpack-file.c b/unpack-file.c
index d4ac3a5..07303f8 100644
--- a/unpack-file.c
+++ b/unpack-file.c
@@ -29,6 +29,8 @@
 	if (argc != 2 || get_sha1(argv[1], sha1))
 		usage("git-unpack-file <sha1>");
 
+	setup_git_directory();
+
 	puts(create_temp_file(sha1));
 	return 0;
 }
diff --git a/unpack-objects.c b/unpack-objects.c
index 8490895..cfd61ae 100644
--- a/unpack-objects.c
+++ b/unpack-objects.c
@@ -269,6 +269,8 @@
 	int i;
 	unsigned char sha1[20];
 
+	setup_git_directory();
+
 	for (i = 1 ; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/update-server-info.c b/update-server-info.c
index e824f62..0b6c383 100644
--- a/update-server-info.c
+++ b/update-server-info.c
@@ -19,5 +19,7 @@
 	if (i != ac)
 		usage(update_server_info_usage);
 
+	setup_git_directory();
+
 	return !!update_server_info(force);
 }
diff --git a/write-tree.c b/write-tree.c
index 2b2c6b7..0aac32f 100644
--- a/write-tree.c
+++ b/write-tree.c
@@ -86,9 +86,12 @@
 int main(int argc, char **argv)
 {
 	int i, funny;
-	int entries = read_cache();
+	int entries;
 	unsigned char sha1[20];
 	
+	setup_git_directory();
+
+	entries = read_cache();
 	if (argc == 2) {
 		if (!strcmp(argv[1], "--missing-ok"))
 			missing_ok = 1;