Merge branch 'lt/pack-object-memuse'

* lt/pack-object-memuse:
  show_object(): push path_name() call further down
  process_{tree,blob}: show objects without buffering

Conflicts:
	builtin-pack-objects.c
	builtin-rev-list.c
	list-objects.c
	list-objects.h
	upload-pack.c
diff --git a/.gitignore b/.gitignore
index a213e8e..41c0b20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
 git-archimport
 git-archive
 git-bisect
+git-bisect--helper
 git-blame
 git-branch
 git-bundle
@@ -35,6 +36,8 @@
 git-diff-files
 git-diff-index
 git-diff-tree
+git-difftool
+git-difftool--helper
 git-describe
 git-fast-export
 git-fast-import
@@ -51,6 +54,7 @@
 git-get-tar-commit-id
 git-grep
 git-hash-object
+git-help
 git-http-fetch
 git-http-push
 git-imap-send
@@ -77,6 +81,7 @@
 git-merge-resolve
 git-merge-subtree
 git-mergetool
+git-mergetool--lib
 git-mktag
 git-mktree
 git-name-rev
@@ -117,6 +122,7 @@
 git-show-branch
 git-show-index
 git-show-ref
+git-stage
 git-stash
 git-status
 git-stripspace
@@ -142,6 +148,7 @@
 gitk-wish
 gitweb/gitweb.cgi
 test-chmtime
+test-ctype
 test-date
 test-delta
 test-dump-cache-tree
@@ -150,6 +157,7 @@
 test-parse-options
 test-path-utils
 test-sha1
+test-sigchain
 common-cmds.h
 *.tar.gz
 *.dsc
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index f628c1f..b8bf618 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -21,8 +21,13 @@
 
 As for more concrete guidelines, just imitate the existing code
 (this is a good guideline, no matter which project you are
-contributing to).  But if you must have a list of rules,
-here they are.
+contributing to). It is always preferable to match the _local_
+convention. New code added to git suite is expected to match
+the overall style of existing code. Modifications to existing
+code is expected to match the style the surrounding code already
+uses (even if it doesn't match the overall style of existing code).
+
+But if you must have a list of rules, here they are.
 
 For shell scripts specifically (not exhaustive):
 
@@ -124,3 +129,6 @@
    used in the git core command set (unless your command is clearly
    separate from it, such as an importer to convert random-scm-X
    repositories to git).
+
+ - When we pass <string, length> pair to functions, we should try to
+   pass them in that order.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 62269e3..e18242a 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -6,7 +6,7 @@
 	gitrepository-layout.txt
 MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
 	gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
-	gitdiffcore.txt
+	gitdiffcore.txt gitworkflows.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
 MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@ -32,6 +32,7 @@
 prefix?=$(HOME)
 bindir?=$(prefix)/bin
 htmldir?=$(prefix)/share/doc/git-doc
+pdfdir?=$(prefix)/share/doc/git-doc
 mandir?=$(prefix)/share/man
 man1dir=$(mandir)/man1
 man5dir=$(mandir)/man5
@@ -40,15 +41,18 @@
 
 ASCIIDOC=asciidoc
 ASCIIDOC_EXTRA =
-MANPAGE_XSL = callouts.xsl
+MANPAGE_XSL = manpage-normal.xsl
+XMLTO_EXTRA =
 INSTALL?=install
 RM ?= rm -f
 DOC_REF = origin/man
+HTML_REF = origin/html
 
 infodir?=$(prefix)/share/info
 MAKEINFO=makeinfo
 INSTALL_INFO=install-info
 DOCBOOK2X_TEXI=docbook2x-texi
+DBLATEX=dblatex
 ifndef PERL_PATH
 	PERL_PATH = /usr/bin/perl
 endif
@@ -56,13 +60,52 @@
 -include ../config.mak.autogen
 -include ../config.mak
 
+#
+# For asciidoc ...
+#	-7.1.2,	no extra settings are needed.
+#	8.0-,	set ASCIIDOC8.
+#
+
+#
+# For docbook-xsl ...
+#	-1.68.1,	set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0)
+#	1.69.0,		no extra settings are needed?
+#	1.69.1-1.71.0,	set DOCBOOK_SUPPRESS_SP?
+#	1.71.1,		no extra settings are needed?
+#	1.72.0,		set DOCBOOK_XSL_172.
+#	1.73.0-,	set ASCIIDOC_NO_ROFF
+#
+
+#
+# If you had been using DOCBOOK_XSL_172 in an attempt to get rid
+# of 'the ".ft C" problem' in your generated manpages, and you
+# instead ended up with weird characters around callouts, try
+# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8).
+#
+
 ifdef ASCIIDOC8
 ASCIIDOC_EXTRA += -a asciidoc7compatible
 endif
 ifdef DOCBOOK_XSL_172
-ASCIIDOC_EXTRA += -a docbook-xsl-172
+ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
 MANPAGE_XSL = manpage-1.72.xsl
+else
+	ifdef ASCIIDOC_NO_ROFF
+	# docbook-xsl after 1.72 needs the regular XSL, but will not
+	# pass-thru raw roff codes from asciidoc.conf, so turn them off.
+	ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
+	endif
 endif
+ifdef MAN_BOLD_LITERAL
+XMLTO_EXTRA += -m manpage-bold-literal.xsl
+endif
+ifdef DOCBOOK_SUPPRESS_SP
+XMLTO_EXTRA += -m manpage-suppress-sp.xsl
+endif
+
+SHELL_PATH ?= $(SHELL)
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
 #
 # Please note that there is a minor bug in asciidoc.
@@ -73,6 +116,32 @@
 # yourself - yes, all 6 characters of it!
 #
 
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+	QUIET_ASCIIDOC	= @echo '   ' ASCIIDOC $@;
+	QUIET_XMLTO	= @echo '   ' XMLTO $@;
+	QUIET_DB2TEXI	= @echo '   ' DB2TEXI $@;
+	QUIET_MAKEINFO	= @echo '   ' MAKEINFO $@;
+	QUIET_DBLATEX	= @echo '   ' DBLATEX $@;
+	QUIET_XSLTPROC	= @echo '   ' XSLTPROC $@;
+	QUIET_GEN	= @echo '   ' GEN $@;
+	QUIET_STDERR	= 2> /dev/null
+	QUIET_SUBDIR0	= +@subdir=
+	QUIET_SUBDIR1	= ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+			  $(MAKE) $(PRINT_DIR) -C $$subdir
+	export V
+endif
+endif
+
 all: html man
 
 html: $(DOC_HTML)
@@ -86,7 +155,11 @@
 
 info: git.info gitman.info
 
-install: man
+pdf: user-manual.pdf
+
+install: install-man
+
+install-man: man
 	$(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
 	$(INSTALL) -d -m 755 $(DESTDIR)$(man5dir)
 	$(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
@@ -104,11 +177,15 @@
 	  echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
 	fi
 
+install-pdf: pdf
+	$(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir)
+	$(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
+
 install-html: html
-	sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
+	'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
 
 ../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
-	$(MAKE) -C ../ GIT-VERSION-FILE
+	$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
 
 -include ../GIT-VERSION-FILE
 
@@ -116,8 +193,8 @@
 # Determine "include::" file references in asciidoc files.
 #
 doc.dep : $(wildcard *.txt) build-docdep.perl
-	$(RM) $@+ $@
-	$(PERL_PATH) ./build-docdep.perl >$@+
+	$(QUIET_GEN)$(RM) $@+ $@ && \
+	$(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
 	mv $@+ $@
 
 -include doc.dep
@@ -135,91 +212,105 @@
 $(cmds_txt): cmd-list.made
 
 cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
-	$(RM) $@
-	$(PERL_PATH) ./cmd-list.perl ../command-list.txt
+	$(QUIET_GEN)$(RM) $@ && \
+	$(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
 	date >$@
 
 clean:
 	$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
-	$(RM) *.texi *.texi+ git.info gitman.info
+	$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
 	$(RM) howto-index.txt howto/*.html doc.dep
 	$(RM) technical/api-*.html technical/api-index.txt
 	$(RM) $(cmds_txt) *.made
 
 $(MAN_HTML): %.html : %.txt
-	$(RM) $@+ $@
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
 	$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
-		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
+		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
 	mv $@+ $@
 
 %.1 %.5 %.7 : %.xml
-	$(RM) $@
-	xmlto -m $(MANPAGE_XSL) man $<
+	$(QUIET_XMLTO)$(RM) $@ && \
+	xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
 %.xml : %.txt
-	$(RM) $@+ $@
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
 	$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
-		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
+		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
 	mv $@+ $@
 
 user-manual.xml: user-manual.txt user-manual.conf
-	$(ASCIIDOC) -b docbook -d book $<
+	$(QUIET_ASCIIDOC)$(ASCIIDOC) -b docbook -d book $<
 
 technical/api-index.txt: technical/api-index-skel.txt \
 	technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
-	cd technical && sh ./api-index.sh
+	$(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
 
 $(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt
-	$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
+	$(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
 		$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt
 
 XSLT = docbook.xsl
 XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
 
 user-manual.html: user-manual.xml
-	xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
+	$(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
 
 git.info: user-manual.texi
-	$(MAKEINFO) --no-split -o $@ user-manual.texi
+	$(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
 
 user-manual.texi: user-manual.xml
-	$(RM) $@+ $@
-	$(DOCBOOK2X_TEXI) user-manual.xml --to-stdout | $(PERL_PATH) fix-texi.perl >$@+
+	$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+	$(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
+	$(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
+	rm $@++ && \
+	mv $@+ $@
+
+user-manual.pdf: user-manual.xml
+	$(QUIET_DBLATEX)$(RM) $@+ $@ && \
+	$(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \
 	mv $@+ $@
 
 gitman.texi: $(MAN_XML) cat-texi.perl
-	$(RM) $@+ $@
-	($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --to-stdout $(xml);)) | \
-	$(PERL_PATH) cat-texi.perl $@ >$@+
+	$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+	($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
+		--to-stdout $(xml) &&) true) > $@++ && \
+	$(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \
+	rm $@++ && \
 	mv $@+ $@
 
 gitman.info: gitman.texi
-	$(MAKEINFO) --no-split $*.texi
+	$(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi
 
 $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
-	$(RM) $@+ $@
-	$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+
+	$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
+	$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \
 	mv $@+ $@
 
 howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
-	$(RM) $@+ $@
-	sh ./howto-index.sh $(wildcard howto/*.txt) >$@+
+	$(QUIET_GEN)$(RM) $@+ $@ && \
+	'$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \
 	mv $@+ $@
 
 $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
-	$(ASCIIDOC) -b xhtml11 $*.txt
+	$(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 $*.txt
 
 WEBDOC_DEST = /pub/software/scm/git/docs
 
 $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
-	$(RM) $@+ $@
-	sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+
+	$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+	sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ && \
 	mv $@+ $@
 
 install-webdoc : html
-	sh ./install-webdoc.sh $(WEBDOC_DEST)
+	'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
 
-quick-install:
-	sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
+quick-install: quick-install-man
+
+quick-install-man:
+	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
+
+quick-install-html:
+	'$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
 
 .PHONY: .FORCE-GIT-VERSION-FILE
diff --git a/Documentation/RelNotes-1.5.2.2.txt b/Documentation/RelNotes-1.5.2.2.txt
index f6393f8..7bfa341 100644
--- a/Documentation/RelNotes-1.5.2.2.txt
+++ b/Documentation/RelNotes-1.5.2.2.txt
@@ -45,7 +45,7 @@
     correctly when the branch name had slash in it.
 
   - The email address of the user specified with user.email
-    configuration was overriden by EMAIL environment variable.
+    configuration was overridden by EMAIL environment variable.
 
   - The tree parser did not warn about tree entries with
     nonsense file modes, and assumed they must be blobs.
diff --git a/Documentation/RelNotes-1.6.0.2.txt b/Documentation/RelNotes-1.6.0.2.txt
index 7a9646f..51b32f5 100644
--- a/Documentation/RelNotes-1.6.0.2.txt
+++ b/Documentation/RelNotes-1.6.0.2.txt
@@ -7,7 +7,7 @@
 * Installation on platforms that needs .exe suffix to git-* programs were
   broken in 1.6.0.1.
 
-* Installation on filesystems without symbolic links support did nto
+* Installation on filesystems without symbolic links support did not
   work well.
 
 * In-tree documentations and test scripts now use "git foo" form to set a
diff --git a/Documentation/RelNotes-1.6.1.1.txt b/Documentation/RelNotes-1.6.1.1.txt
new file mode 100644
index 0000000..8c594ba
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.1.txt
@@ -0,0 +1,59 @@
+GIT v1.6.1.1 Release Notes
+==========================
+
+Fixes since v1.6.1
+------------------
+
+* "git add frotz/nitfol" when "frotz" is a submodule should have errored
+  out, but it didn't.
+
+* "git apply" took file modes from the patch text and updated the mode
+  bits of the target tree even when the patch was not about mode changes.
+
+* "git bisect view" on Cygwin did not launch gitk
+
+* "git checkout $tree" did not trigger an error.
+
+* "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake.
+
+* "git describe --all" complained when a commit is described with a tag,
+  which was nonsense.
+
+* "git diff --no-index --" did not trigger no-index (aka "use git-diff as
+  a replacement of diff on untracked files") behaviour.
+
+* "git format-patch -1 HEAD" on a root commit failed to produce patch
+  text.
+
+* "git fsck branch" did not work as advertised; instead it behaved the same
+  way as "git fsck".
+
+* "git log --pretty=format:%s" did not handle a multi-line subject the
+  same way as built-in log listers (i.e. shortlog, --pretty=oneline, etc.)
+
+* "git daemon", and "git merge-file" are more careful when freopen fails
+  and barf, instead of going on and writing to unopened filehandle.
+
+* "git http-push" did not like some RFC 4918 compliant DAV server
+  responses.
+
+* "git merge -s recursive" mistakenly overwritten an untracked file in the
+  work tree upon delete/modify conflict.
+
+* "git merge -s recursive" didn't leave the index unmerged for entries with
+  rename/delete conflicts.
+
+* "git merge -s recursive" clobbered untracked files in the work tree.
+
+* "git mv -k" with more than one erroneous paths misbehaved.
+
+* "git read-tree -m -u" hence branch switching incorrectly lost a
+  subdirectory in rare cases.
+
+* "git rebase -i" issued an unnecessary error message upon a user error of
+  marking the first commit to be "squash"ed.
+
+* "git shortlog" did not format a commit message with multi-line
+  subject correctly.
+
+Many documentation updates.
diff --git a/Documentation/RelNotes-1.6.1.2.txt b/Documentation/RelNotes-1.6.1.2.txt
new file mode 100644
index 0000000..be37cbb
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.2.txt
@@ -0,0 +1,39 @@
+GIT v1.6.1.2 Release Notes
+==========================
+
+Fixes since v1.6.1.1
+--------------------
+
+* The logic for rename detection in internal diff used by commands like
+  "git diff" and "git blame" has been optimized to avoid loading the same
+  blob repeatedly.
+
+* We did not allow writing out a blob that is larger than 2GB for no good
+  reason.
+
+* "git format-patch -o $dir", when $dir is a relative directory, used it
+  as relative to the root of the work tree, not relative to the current
+  directory.
+
+* v1.6.1 introduced an optimization for "git push" into a repository (A)
+  that borrows its objects from another repository (B) to avoid sending
+  objects that are available in repository B, when they are not yet used
+  by repository A.  However the code on the "git push" sender side was
+  buggy and did not work when repository B had new objects that are not
+  known by the sender.  This caused pushing into a "forked" repository
+  served by v1.6.1 software using "git push" from v1.6.1 sometimes did not
+  work.  The bug was purely on the "git push" sender side, and has been
+  corrected.
+
+* "git status -v" did not paint its diff output in colour even when
+  color.ui configuration was set.
+
+* "git ls-tree" learned --full-tree option to help Porcelain scripts that
+  want to always see the full path regardless of the current working
+  directory.
+
+* "git grep" incorrectly searched in work tree paths even when they are
+  marked as assume-unchanged.  It now searches in the index entries.
+
+* "git gc" with no grace period needlessly ejected packed but unreachable
+  objects in their loose form, only to delete them right away.
diff --git a/Documentation/RelNotes-1.6.1.3.txt b/Documentation/RelNotes-1.6.1.3.txt
new file mode 100644
index 0000000..6f0bde1
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.3.txt
@@ -0,0 +1,32 @@
+GIT v1.6.1.3 Release Notes
+==========================
+
+Fixes since v1.6.1.2
+--------------------
+
+* "git diff --binary | git apply" pipeline did not work well when
+  a binary blob is changed to a symbolic link.
+
+* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
+  not work as expected.
+
+* "git grep" did not pass the -I (ignore binary) option when
+  calling out an external grep program.
+
+* "git log" and friends include HEAD to the set of starting points
+  when --all is given.  This makes a difference when you are not
+  on any branch.
+
+* "git mv" to move an untracked file to overwrite a tracked
+  contents misbehaved.
+
+* "git merge -s octopus" with many potential merge bases did not
+  work correctly.
+
+* RPM binary package installed the html manpages in a wrong place.
+
+Also includes minor documentation fixes and updates.
+
+
+--
+git shortlog --no-merges v1.6.1.2-33-gc789350..
diff --git a/Documentation/RelNotes-1.6.1.4.txt b/Documentation/RelNotes-1.6.1.4.txt
new file mode 100644
index 0000000..a9f1a6b
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.4.txt
@@ -0,0 +1,19 @@
+GIT v1.6.1.4 Release Notes
+==========================
+
+Fixes since v1.6.1.3
+--------------------
+
+* "git fast-export" produced wrong output with some parents missing from
+  commits, when the history is clock-skewed.
+
+* "git fast-import" sometimes failed to read back objects it just wrote
+  out and aborted, because it failed to flush stale cached data.
+
+* "git repack" did not error out when necessary object was missing in the
+  repository.
+
+Also includes minor documentation fixes and updates.
+
+--
+git shortlog --no-merges v1.6.1.3..
diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt
new file mode 100644
index 0000000..adb7cca
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.txt
@@ -0,0 +1,286 @@
+GIT v1.6.1 Release Notes
+========================
+
+Updates since v1.6.0
+--------------------
+
+When some commands (e.g. "git log", "git diff") spawn pager internally, we
+used to make the pager the parent process of the git command that produces
+output.  This meant that the exit status of the whole thing comes from the
+pager, not the underlying git command.  We swapped the order of the
+processes around and you will see the exit code from the command from now
+on.
+
+(subsystems)
+
+* gitk can call out to git-gui to view "git blame" output; git-gui in turn
+  can run gitk from its blame view.
+
+* Various git-gui updates including updated translations.
+
+* Various gitweb updates from repo.or.cz installation.
+
+* Updates to emacs bindings.
+
+(portability)
+
+* A few test scripts used nonportable "grep" that did not work well on
+  some platforms, e.g. Solaris.
+
+* Sample pre-auto-gc script has OS X support.
+
+* Makefile has support for (ancient) FreeBSD 4.9.
+
+(performance)
+
+* Many operations that are lstat(3) heavy can be told to pre-execute
+  necessary lstat(3) in parallel before their main operations, which
+  potentially gives much improved performance for cold-cache cases or in
+  environments with weak metadata caching (e.g. NFS).
+
+* The underlying diff machinery to produce textual output has been
+  optimized, which would result in faster "git blame" processing.
+
+* Most of the test scripts (but not the ones that try to run servers)
+  can be run in parallel.
+
+* Bash completion of refnames in a repository with massive number of
+  refs has been optimized.
+
+* Cygwin port uses native stat/lstat implementations when applicable,
+  which leads to improved performance.
+
+* "git push" pays attention to alternate repositories to avoid sending
+  unnecessary objects.
+
+* "git svn" can rebuild an out-of-date rev_map file.
+
+(usability, bells and whistles)
+
+* When you mistype a command name, git helpfully suggests what it guesses
+  you might have meant to say.  help.autocorrect configuration can be set
+  to a non-zero value to accept the suggestion when git can uniquely
+  guess.
+
+* The packfile machinery hopefully is more robust when dealing with
+  corrupt packs if redundant objects involved in the corruption are
+  available elsewhere.
+
+* "git add -N path..." adds the named paths as an empty blob, so that
+  subsequent "git diff" will show a diff as if they are creation events.
+
+* "git add" gained a built-in synonym for people who want to say "stage
+  changes" instead of "add contents to the staging area" which amounts
+  to the same thing.
+
+* "git apply" learned --include=paths option, similar to the existing
+  --exclude=paths option.
+
+* "git bisect" is careful about a user mistake and suggests testing of
+  merge base first when good is not a strict ancestor of bad.
+
+* "git bisect skip" can take a range of commits.
+
+* "git blame" re-encodes the commit metainfo to UTF-8 from i18n.commitEncoding
+  by default.
+
+* "git check-attr --stdin" can check attributes for multiple paths.
+
+* "git checkout --track origin/hack" used to be a syntax error.  It now
+  DWIMs to create a corresponding local branch "hack", i.e. acts as if you
+  said "git checkout --track -b hack origin/hack".
+
+* "git checkout --ours/--theirs" can be used to check out one side of a
+  conflicting merge during conflict resolution.
+
+* "git checkout -m" can be used to recreate the initial conflicted state
+  during conflict resolution.
+
+* "git cherry-pick" can also utilize rerere for conflict resolution.
+
+* "git clone" learned to be verbose with -v
+
+* "git commit --author=$name" can look up author name from existing
+  commits.
+
+* output from "git commit" has been reworded in a more concise and yet
+  more informative way.
+
+* "git count-objects" reports the on-disk footprint for packfiles and
+  their corresponding idx files.
+
+* "git daemon" learned --max-connections=<count> option.
+
+* "git daemon" exports REMOTE_ADDR to record client address, so that
+  spawned programs can act differently on it.
+
+* "git describe --tags" favours closer lightweight tags than farther
+  annotated tags now.
+
+* "git diff" learned to mimic --suppress-blank-empty from GNU diff via a
+  configuration option.
+
+* "git diff" learned to put more sensible hunk headers for Python,
+  HTML and ObjC contents.
+
+* "git diff" learned to vary the a/ vs b/ prefix depending on what are
+  being compared, controlled by diff.mnemonicprefix configuration.
+
+* "git diff" learned --dirstat-by-file to count changed files, not number
+  of lines, when summarizing the global picture.
+
+* "git diff" learned "textconv" filters --- a binary or hard-to-read
+  contents can be munged into human readable form and the difference
+  between the results of the conversion can be viewed (obviously this
+  cannot produce a patch that can be applied, so this is disabled in
+  format-patch among other things).
+
+* "--cached" option to "git diff has an easier to remember synonym "--staged",
+  to ask "what is the difference between the given commit and the
+  contents staged in the index?"
+
+* "git for-each-ref" learned "refname:short" token that gives an
+  unambiguously abbreviated refname.
+
+* Auto-numbering of the subject lines is the default for "git
+  format-patch" now.
+
+* "git grep" learned to accept -z similar to GNU grep.
+
+* "git help" learned to use GIT_MAN_VIEWER environment variable before
+  using "man" program.
+
+* "git imap-send" can optionally talk SSL.
+
+* "git index-pack" is more careful against disk corruption while
+  completing a thin pack.
+
+* "git log --check" and "git log --exit-code" passes their underlying diff
+  status with their exit status code.
+
+* "git log" learned --simplify-merges, a milder variant of --full-history;
+  "gitk --simplify-merges" is easier to view than with --full-history.
+
+* "git log" learned "--source" to show what ref each commit was reached
+  from.
+
+* "git log" also learned "--simplify-by-decoration" to show the
+  birds-eye-view of the topology of the history.
+
+* "git log --pretty=format:" learned "%d" format element that inserts
+  names of tags that point at the commit.
+
+* "git merge --squash" and "git merge --no-ff" into an unborn branch are
+  noticed as user errors.
+
+* "git merge -s $strategy" can use a custom built strategy if you have a
+  command "git-merge-$strategy" on your $PATH.
+
+* "git pull" (and "git fetch") can be told to operate "-v"erbosely or
+  "-q"uietly.
+
+* "git push" can be told to reject deletion of refs with receive.denyDeletes
+  configuration.
+
+* "git rebase" honours pre-rebase hook; use --no-verify to bypass it.
+
+* "git rebase -p" uses interactive rebase machinery now to preserve the merges.
+
+* "git reflog expire branch" can be used in place of "git reflog expire
+  refs/heads/branch".
+
+* "git remote show $remote" lists remote branches one-per-line now.
+
+* "git send-email" can be given revision range instead of files and
+  maildirs on the command line, and automatically runs format-patch to
+  generate patches for the given revision range.
+
+* "git submodule foreach" subcommand allows you to iterate over checked
+  out submodules.
+
+* "git submodule sync" subcommands allows you to update the origin URL
+  recorded in submodule directories from the toplevel .gitmodules file.
+
+* "git svn branch" can create new branches on the other end.
+
+* "gitweb" can use more saner PATH_INFO based URL.
+
+(internal)
+
+* "git hash-object" learned to lie about the path being hashed, so that
+  correct gitattributes processing can be done while hashing contents
+  stored in a temporary file.
+
+* various callers of git-merge-recursive avoid forking it as an external
+  process.
+
+* Git class defined in "Git.pm" can be subclasses a bit more easily.
+
+* We used to link GNU regex library as a compatibility layer for some
+  platforms, but it turns out it is not necessary on most of them.
+
+* Some path handling routines used fixed number of buffers used alternately
+  but depending on the call depth, this arrangement led to hard to track
+  bugs.  This issue is being addressed.
+
+
+Fixes since v1.6.0
+------------------
+
+All of the fixes in v1.6.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+* Porcelains implemented as shell scripts were utterly confused when you
+  entered to a subdirectory of a work tree from sideways, following a
+  symbolic link (this may need to be backported to older releases later).
+
+* Tracking symbolic links would work better on filesystems whose lstat()
+  returns incorrect st_size value for them.
+
+* "git add" and "git update-index" incorrectly allowed adding S/F when S
+  is a tracked symlink that points at a directory D that has a path F in
+  it (we still need to fix a similar nonsense when S is a submodule and F
+  is a path in it).
+
+* "git am" after stopping at a broken patch lost --whitespace, -C, -p and
+  --3way options given from the command line initially.
+
+* "git diff --stdin" used to take two trees on a line and compared them,
+  but we dropped support for such a use case long time ago.  This has
+  been resurrected.
+
+* "git filter-branch" failed to rewrite a tag name with slashes in it.
+
+* "git http-push" did not understand URI scheme other than opaquelocktoken
+  when acquiring a lock from the server (this may need to be backported to
+  older releases later).
+
+* After "git rebase -p" stopped with conflicts while replaying a merge,
+ "git rebase --continue" did not work (may need to be backported to older
+  releases).
+
+* "git revert" records relative to which parent a revert was made when
+  reverting a merge.  Together with new documentation that explains issues
+  around reverting a merge and merging from the updated branch later, this
+  hopefully will reduce user confusion (this may need to be backported to
+  older releases later).
+
+* "git rm --cached" used to allow an empty blob that was added earlier to
+  be removed without --force, even when the file in the work tree has
+  since been modified.
+
+* "git push --tags --all $there" failed with generic usage message without
+  telling saying these two options are incompatible.
+
+* "git log --author/--committer" match used to potentially match the
+  timestamp part, exposing internal implementation detail.  Also these did
+  not work with --fixed-strings match at all.
+
+* "gitweb" did not mark non-ASCII characters imported from external HTML fragments
+  correctly.
+
+--
+exec >/var/tmp/1
+O=v1.6.1-rc3-74-gf66bc5f
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/RelNotes-1.6.2.1.txt b/Documentation/RelNotes-1.6.2.1.txt
new file mode 100644
index 0000000..dfa3641
--- /dev/null
+++ b/Documentation/RelNotes-1.6.2.1.txt
@@ -0,0 +1,19 @@
+GIT v1.6.2.1 Release Notes
+==========================
+
+Fixes since v1.6.2
+------------------
+
+* .gitignore learned to handle backslash as a quoting mechanism for
+  comment introduction character "#".
+
+* timestamp output in --date=relative mode used to display timestamps that
+  are long time ago in the default mode; it now uses "N years M months
+  ago", and "N years ago".
+
+* git-add -i/-p now works with non-ASCII pathnames.
+
+* "git hash-object -w" did not read from the configuration file from the
+  correct .git directory.
+
+* git-send-email learned to correctly handle multiple Cc: addresses.
diff --git a/Documentation/RelNotes-1.6.2.2.txt b/Documentation/RelNotes-1.6.2.2.txt
new file mode 100644
index 0000000..fafa998
--- /dev/null
+++ b/Documentation/RelNotes-1.6.2.2.txt
@@ -0,0 +1,45 @@
+GIT v1.6.2.2 Release Notes
+==========================
+
+Fixes since v1.6.2.1
+--------------------
+
+* A longstanding confusing description of what --pickaxe option of
+  git-diff does has been clarified in the documentation.
+
+* "git-blame -S" did not quite work near the commits that were given
+  on the command line correctly.
+
+* "git diff --pickaxe-regexp" did not count overlapping matches
+  correctly.
+
+* "git diff" did not feed files in work-tree representation to external
+  diff and textconv.
+
+* "git-fetch" in a repository that was not cloned from anywhere said
+  it cannot find 'origin', which was hard to understand for new people.
+
+* "git-format-patch --numbered-files --stdout" did not have to die of
+  incompatible options; it now simply ignores --numbered-files as no files
+  are produced anyway.
+
+* "git-ls-files --deleted" did not work well with GIT_DIR&GIT_WORK_TREE.
+
+* "git-read-tree A B C..." without -m option has been broken for a long
+  time.
+
+* git-send-email ignored --in-reply-to when --no-thread was given.
+
+* 'git-submodule add' did not tolerate extra slashes and ./ in the path it
+  accepted from the command line; it now is more lenient.
+
+* git-svn misbehaved when the project contained a path that began with
+  two dashes.
+
+* import-zips script (in contrib) did not compute the common directory
+  prefix correctly.
+
+* miscompilation of negated enum constants by old gcc (2.9) affected the
+  codepaths to spawn subprocesses.
+
+Many small documentation updates are included as well.
diff --git a/Documentation/RelNotes-1.6.2.3.txt b/Documentation/RelNotes-1.6.2.3.txt
new file mode 100644
index 0000000..4d3c1ac
--- /dev/null
+++ b/Documentation/RelNotes-1.6.2.3.txt
@@ -0,0 +1,22 @@
+GIT v1.6.2.3 Release Notes
+==========================
+
+Fixes since v1.6.2.2
+--------------------
+
+* Setting an octal mode value to core.sharedrepository configuration to
+  restrict access to the repository to group members did not work as
+  advertised.
+
+* A fairly large and trivial memory leak while rev-list shows list of
+  reachable objects has been identified and plugged.
+
+* "git-commit --interactive" did not abort when underlying "git-add -i"
+  signaled a failure.
+
+* git-repack (invoked from git-gc) did not work as nicely as it should in
+  a repository that borrows objects from neighbours via alternates
+  mechanism especially when some packs are marked with the ".keep" flag
+  to prevent them from being repacked.
+
+Many small documentation updates are included as well.
diff --git a/Documentation/RelNotes-1.6.2.4.txt b/Documentation/RelNotes-1.6.2.4.txt
new file mode 100644
index 0000000..21bf4f3
--- /dev/null
+++ b/Documentation/RelNotes-1.6.2.4.txt
@@ -0,0 +1,31 @@
+GIT v1.6.2.4 Release Notes
+==========================
+
+Fixes since v1.6.2.3
+--------------------
+
+* The configuration parser had a buffer overflow while parsing an overlong
+  value.
+
+* "git-checkout <tree-ish> <submodule>" did not update the index entry at
+  the named path; it now does.
+
+* "git init" segfaulted when given an overlong template location via
+  the --template= option.
+
+* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
+  deciding to descend into a subdirectory but they did not match the
+  individual paths correctly.  This caused pathspecs "abc/d ab" to match
+  "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
+  and then "ab" incorrectly matched "abc/0" when it shouldn't).
+
+* "git-merge-recursive" was broken when a submodule entry was involved in
+  a criss-cross merge situation.
+
+Many small documentation updates are included as well.
+
+---
+exec >/var/tmp/1
+echo O=$(git describe maint)
+O=v1.6.2.3-21-ga51609a
+git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt
new file mode 100644
index 0000000..ad060f4
--- /dev/null
+++ b/Documentation/RelNotes-1.6.2.txt
@@ -0,0 +1,164 @@
+GIT v1.6.2 Release Notes
+========================
+
+With the next major release, "git push" into a branch that is
+currently checked out will be refused by default.  You can choose
+what should happen upon such a push by setting the configuration
+variable receive.denyCurrentBranch in the receiving repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing.  Please refer to:
+
+  http://git.or.cz/gitwiki/GitFaq#non-bare
+  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+For a similar reason, "git push $there :$killed" to delete the branch
+$killed in a remote repository $there, if $killed branch is the current
+branch pointed at by its HEAD, gets a large warning.  You can choose what
+should happen upon such a push by setting the configuration variable
+receive.denyDeleteCurrent in the receiving repository.
+
+
+Updates since v1.6.1
+--------------------
+
+(subsystems)
+
+* git-svn updates.
+
+* gitweb updates, including a new patch view and RSS/Atom feed
+  improvements.
+
+* (contrib/emacs) git.el now has commands for checking out a branch,
+  creating a branch, cherry-picking and reverting commits; vc-git.el
+  is not shipped with git anymore (it is part of official Emacs).
+
+(performance)
+
+* pack-objects autodetects the number of CPUs available and uses threaded
+  version.
+
+(usability, bells and whistles)
+
+* automatic typo correction works on aliases as well
+
+* @{-1} is a way to refer to the last branch you were on.  This is
+  accepted not only where an object name is expected, but anywhere
+  a branch name is expected and acts as if you typed the branch name.
+  E.g. "git branch --track mybranch @{-1}", "git merge @{-1}", and
+  "git rev-parse --symbolic-full-name @{-1}" would work as expected.
+
+* When refs/remotes/origin/HEAD points at a remote tracking branch that
+  has been pruned away, many git operations issued warning when they
+  internally enumerated the refs.  We now warn only when you say "origin"
+  to refer to that pruned branch.
+
+* The location of .mailmap file can be configured, and its file format was
+  enhanced to allow mapping an incorrect e-mail field as well.
+
+* "git add -p" learned 'g'oto action to jump directly to a hunk.
+
+* "git add -p" learned to find a hunk with given text with '/'.
+
+* "git add -p" optionally can be told to work with just the command letter
+  without Enter.
+
+* when "git am" stops upon a patch that does not apply, it shows the
+  title of the offending patch.
+
+* "git am --directory=<dir>" and "git am --reject" passes these options
+  to underlying "git apply".
+
+* "git am" learned --ignore-date option.
+
+* "git blame" aligns author names better when they are spelled in
+  non US-ASCII encoding.
+
+* "git clone" now makes its best effort when cloning from an empty
+  repository to set up configuration variables to refer to the remote
+  repository.
+
+* "git checkout -" is a shorthand for "git checkout @{-1}".
+
+* "git cherry" defaults to whatever the current branch is tracking (if
+  exists) when the <upstream> argument is not given.
+
+* "git cvsserver" can be told not to add extra "via git-CVS emulator" to
+  the commit log message it serves via gitcvs.commitmsgannotation
+  configuration.
+
+* "git cvsserver" learned to handle 'noop' command some CVS clients seem
+  to expect to work.
+
+* "git diff" learned a new option --inter-hunk-context to coalesce close
+  hunks together and show context between them.
+
+* The definition of what constitutes a word for "git diff --color-words"
+  can be customized via gitattributes, command line or a configuration.
+
+* "git diff" learned --patience to run "patience diff" algorithm.
+
+* "git filter-branch" learned --prune-empty option that discards commits
+  that do not change the contents.
+
+* "git fsck" now checks loose objects in alternate object stores, instead
+  of misreporting them as missing.
+
+* "git gc --prune" was resurrected to allow "git gc --no-prune" and
+  giving non-default expiration period e.g. "git gc --prune=now".
+
+* "git grep -w" and "git grep" for fixed strings have been optimized.
+
+* "git mergetool" learned -y(--no-prompt) option to disable prompting.
+
+* "git rebase -i" can transplant a history down to root to elsewhere
+  with --root option.
+
+* "git reset --merge" is a new mode that works similar to the way
+  "git checkout" switches branches, taking the local changes while
+  switching to another commit.
+
+* "git submodule update" learned --no-fetch option.
+
+* "git tag" learned --contains that works the same way as the same option
+  from "git branch".
+
+
+Fixes since v1.6.1
+------------------
+
+All of the fixes in v1.6.1.X maintenance series are included in this
+release, unless otherwise noted.
+
+Here are fixes that this release has, but have not been backported to
+v1.6.1.X series.
+
+* "git-add sub/file" when sub is a submodule incorrectly added the path to
+  the superproject.
+
+* "git bundle" did not exclude annotated tags even when a range given
+  from the command line wanted to.
+
+* "git filter-branch" unnecessarily refused to work when you had
+  checked out a different commit from what is recorded in the superproject
+  index in a submodule.
+
+* "git filter-branch" incorrectly tried to update a nonexistent work tree
+  at the end when it is run in a bare repository.
+
+* "git gc" did not work if your repository was created with an ancient git
+  and never had any pack files in it before.
+
+* "git mergetool" used to ignore autocrlf and other attributes
+  based content rewriting.
+
+* branch switching and merges had a silly bug that did not validate
+  the correct directory when making sure an existing subdirectory is
+  clean.
+
+* "git -p cmd" when cmd is not a built-in one left the display in funny state
+  when killed in the middle.
diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt
new file mode 100644
index 0000000..839498c
--- /dev/null
+++ b/Documentation/RelNotes-1.6.3.txt
@@ -0,0 +1,181 @@
+GIT v1.6.3 Release Notes
+========================
+
+With the next major release, "git push" into a branch that is
+currently checked out will be refused by default.  You can choose
+what should happen upon such a push by setting the configuration
+variable receive.denyCurrentBranch in the receiving repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing.  Please refer to:
+
+  http://git.or.cz/gitwiki/GitFaq#non-bare
+  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+For a similar reason, "git push $there :$killed" to delete the branch
+$killed in a remote repository $there, if $killed branch is the current
+branch pointed at by its HEAD, gets a large warning.  You can choose what
+should happen upon such a push by setting the configuration variable
+receive.denyDeleteCurrent in the receiving repository.
+
+When the user does not tell "git push" what to push, it has always
+pushed matching refs.  For some people it is unexpected, and a new
+configuration variable push.default has been introduced to allow
+changing a different default behaviour.  To advertise the new feature,
+a big warning is issued if this is not configured and a git push without
+arguments is attempted.
+
+
+Updates since v1.6.2
+--------------------
+
+(subsystems)
+
+* various git-svn updates.
+
+(performance)
+
+* many uses of lstat(2) in the codepath for "git checkout" have been
+  optimized out.
+
+* pruning reflog entries that are unreachable from the tip of the ref
+  during "git reflog prune" (hence "git gc") was very inefficient.
+
+(usability, bells and whistles)
+
+* rsync:/path/to/repo can be used to run git over rsync for local
+  repositories.  It may not be useful in practice; meant primarily for
+  testing.
+
+* http transport learned to prompt and use password when fetching from or
+  pushing to http://user@host.xz/ URL.
+
+* (msysgit) progress output that is sent over the sideband protocol can
+  be handled appropriately in Windows console.
+
+* "--pretty=<style>" option to the log family of commands can now be
+  spelled as "--format=<style>".  In addition, --format=%formatstring
+  is a short-hand for --pretty=tformat:%formatstring.
+
+* "--oneline" is a synonym for "--pretty=oneline --abbrev-commit".
+
+* If you realize that you botched the patch when you are editing hunks
+  with the 'edit' action in git-add -i/-p, you can abort the editor to
+  tell git not to apply it.
+
+* The number of commits shown in "you are ahead/behind your upstream"
+  messages given by "git checkout" and "git status" used to count merge
+  commits; now it doesn't.
+
+* @{-1} is a new way to refer to the last branch you were on introduced in
+  1.6.2, but the initial implementation did not teach this to a few
+  commands.  Now the syntax works with "branch -m @{-1} newname".
+
+* git-archive learned --output=<file> option.
+
+* git-bisect shows not just the number of remaining commits whose goodness
+  is unknown, but also shows the estimated number of remaining rounds.
+
+* You can give --date=<format> option to git-blame.
+
+* "git-branch -r" shows HEAD symref that points at a remote branch in
+  interest of each tracked remote repository.
+
+* "git-branch -v -v" is a new way to get list of names for branches and the
+  "upstream" branch for them.
+
+* git-config learned -e option to open an editor to edit the config file
+  directly.
+
+* git-clone runs post-checkout hook when run without --no-checkout.
+
+* git-fast-export choked when seeing a tag that does not point at commit.
+
+* git-for-each-ref learned a new "upstream" token.
+
+* git-format-patch can be told to use attachment with a new configuration,
+  format.attach.
+
+* git-format-patch can be told to produce deep or shallow message threads.
+
+* git-format-patch can be told to always add sign-off with a configuration
+  variable.
+
+* git-format-patch learned format.headers configuration to add extra
+  header fields to the output.  This behaviour is similar to the existing
+  --add-header=<header> option of the command.
+
+* git-format-patch gives human readable names to the attached files, when
+  told to send patches as attachments.
+
+* git-grep learned to highlight the found substrings in color.
+
+* git-imap-send learned to work around Thunderbird's inability to easily
+  disable format=flowed with a new configuration, imap.preformattedHTML.
+
+* git-rebase can be told to rebase the series even if your branch is a
+  descendant of the commit you are rebasing onto with --force-rebase
+  option.
+
+* git-rebase can be told to report diffstat with the --stat option.
+
+* Output from git-remote command has been vastly improved.
+
+* "git remote update --prune $remote" updates from the named remote and
+  then prunes stale tracking branches.
+
+* git-send-email learned --confirm option to review the Cc: list before
+  sending the messages out.
+
+(developers)
+
+* Test scripts can be run under valgrind.
+
+* Test scripts can be run with installed git.
+
+* Makefile learned 'coverage' option to run the test suites with
+  coverage tracking enabled.
+
+* Building the manpages with docbook-xsl between 1.69.1 and 1.71.1 now
+  requires setting DOCBOOK_SUPPRESS_SP to work around a docbook-xsl bug.
+  This workaround used to be enabled by default, but causes problems
+  with newer versions of docbook-xsl.  In addition, there are a few more
+  knobs you can tweak to work around issues with various versions of the
+  docbook-xsl package.  See comments in Documentation/Makefile for details.
+
+Fixes since v1.6.2
+------------------
+
+All of the fixes in v1.6.2.X maintenance series are included in this
+release, unless otherwise noted.
+
+Here are fixes that this release has, but have not been backported to
+v1.6.2.X series.
+
+* The initial checkout did not read the attributes from the .gitattribute
+  file that is being checked out.
+
+* "git-checkout <tree-ish> <submodule>" did not update the index entry at
+  the named path; it now does.
+
+* git-gc spent excessive amount of time to decide if an object appears
+  in a locally existing pack (if needed, backport by merging 69e020a).
+
+* "git-ls-tree" and "git-diff-tree" used a pathspec correctly when
+  deciding to descend into a subdirectory but they did not match the
+  individual paths correctly.  This caused pathspecs "abc/d ab" to match
+  "abc/0" ("abc/d" made them decide to descend into the directory "abc/",
+  and then "ab" incorrectly matched "abc/0" when it shouldn't).
+
+* "git-merge-recursive" was broken when a submodule entry was involved in
+  a criss-cross merge situation.
+
+---
+exec >/var/tmp/1
+O=v1.6.2.3-497-g54a4749
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 34fdc83..8d818a2 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -71,7 +71,7 @@
 
 (1a) Try to be nice to older C compilers
 
-We try to support wide range of C compilers to compile
+We try to support a wide range of C compilers to compile
 git with. That means that you should not use C99 initializers, even
 if a lot of compilers grok it.
 
@@ -376,9 +376,36 @@
 
 (A Large Angry SCM)
 
+By default, Thunderbird will both wrap emails as well as flag them as
+being 'format=flowed', both of which will make the resulting email unusable
+by git.
+
 Here are some hints on how to successfully submit patches inline using
 Thunderbird.
 
+There are two different approaches.  One approach is to configure
+Thunderbird to not mangle patches.  The second approach is to use
+an external editor to keep Thunderbird from mangling the patches.
+
+Approach #1 (configuration):
+
+This recipe is current as of Thunderbird 2.0.0.19.  Three steps:
+  1.  Configure your mail server composition as plain text
+      Edit...Account Settings...Composition & Addressing,
+        uncheck 'Compose Messages in HTML'.
+  2.  Configure your general composition window to not wrap
+      Edit..Preferences..Composition, wrap plain text messages at 0
+  3.  Disable the use of format=flowed
+      Edit..Preferences..Advanced..Config Editor.  Search for:
+        mailnews.send_plaintext_flowed
+      toggle it to make sure it is set to 'false'.
+
+After that is done, you should be able to compose email as you
+otherwise would (cut + paste, git-format-patch | git-imap-send, etc),
+and the patches should not be mangled.
+
+Approach #2 (external editor):
+
 This recipe appears to work with the current [*1*] Thunderbird from Suse.
 
 The following Thunderbird extensions are needed:
@@ -464,6 +491,12 @@
 Gmail
 -----
 
+GMail does not appear to have any way to turn off line wrapping in the web
+interface, so this will mangle any emails that you send.  You can however
+use any IMAP email client to connect to the google imap server, and forward
+the emails through that.  Just make sure to disable line wrapping in that
+email client.  Alternatively, use "git send-email" instead.
+
 Submitting properly formatted patches via Gmail is simple now that
 IMAP support is available. First, edit your ~/.gitconfig to specify your
 account settings:
@@ -476,6 +509,9 @@
 	port = 993
 	sslverify = false
 
+You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error
+that the "Folder doesn't exist".
+
 Next, ensure that your Gmail settings are correct. In "Settings" the
 "Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
 
@@ -486,3 +522,4 @@
 
 Go to your Gmail account, open the Drafts folder, find the patch email, fill
 in the To: and CC: fields and send away!
+
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 2da867d..dc76e7f 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -7,6 +7,9 @@
 # Show GIT link as: <command>(<section>); if section is defined, else just show
 # the command.
 
+[macros]
+(?su)[\\]?(?P<name>linkgit):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
 [attributes]
 asterisk=&#42;
 plus=&#43;
@@ -24,7 +27,7 @@
 endif::backend-docbook[]
 
 ifdef::backend-docbook[]
-ifndef::docbook-xsl-172[]
+ifndef::git-asciidoc-no-roff[]
 # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
 # v1.72 breaks with this because it replaces dots not in roff requests.
 [listingblock]
@@ -39,16 +42,16 @@
 endif::doctype-manpage[]
 </literallayout>
 {title#}</example>
-endif::docbook-xsl-172[]
+endif::git-asciidoc-no-roff[]
 
-ifdef::docbook-xsl-172[]
+ifdef::git-asciidoc-no-roff[]
 ifdef::doctype-manpage[]
 # The following two small workarounds insert a simple paragraph after screen
 [listingblock]
 <example><title>{title}</title>
-<screen>
+<literallayout>
 |
-</screen><simpara></simpara>
+</literallayout><simpara></simpara>
 {title#}</example>
 
 [verseblock]
@@ -56,10 +59,11 @@
 {title%}<literallayout{id? id="{id}"}>
 {title#}<literallayout>
 |
-</literallayout><simpara></simpara>
+</literallayout>
 {title#}</para></formalpara>
+{title%}<simpara></simpara>
 endif::doctype-manpage[]
-endif::docbook-xsl-172[]
+endif::git-asciidoc-no-roff[]
 endif::backend-docbook[]
 
 ifdef::doctype-manpage[]
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 5428111..1625ffc 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -39,7 +39,14 @@
 	Show raw timestamp (Default: off).
 
 -S <revs-file>::
-	Use revs from revs-file instead of calling linkgit:git-rev-list[1].
+	Use revisions from revs-file instead of calling linkgit:git-rev-list[1].
+
+--reverse::
+	Walk history forward instead of backward. Instead of showing
+	the revision in which a line appeared, this shows the last
+	revision in which a line has existed. This requires a range of
+	revision like START..END where the path to blame exists in
+	START.
 
 -p::
 --porcelain::
@@ -49,6 +56,13 @@
 	Show the result incrementally in a format designed for
 	machine consumption.
 
+--encoding=<encoding>::
+	Specifies the encoding used to output author names
+	and commit summaries. Setting it to `none` makes blame
+	output unconverted data. For more information see the
+	discussion about encoding in the linkgit:git-log[1]
+	manual page.
+
 --contents <file>::
 	When <rev> is not specified, the command annotates the
 	changes starting backwards from the working tree copy.
@@ -56,11 +70,19 @@
 	tree copy has the contents of the named file (specify
 	`-` to make the command read from the standard input).
 
+--date <format>::
+	The value is one of the following alternatives:
+	{relative,local,default,iso,rfc,short}. If --date is not
+	provided, the value of the blame.date config variable is
+	used. If the blame.date config variable is also not set, the
+	iso format is used. For more information, See the discussion
+	of the --date option at linkgit:git-log[1].
+
 -M|<num>|::
 	Detect moving lines in the file as well.  When a commit
 	moves a block of lines in a file (e.g. the original file
 	has A and then B, and the commit changes it to B and
-	then A), traditional 'blame' algorithm typically blames
+	then A), the traditional 'blame' algorithm typically blames
 	the lines that were moved up (i.e. B) to the parent and
 	assigns blame to the lines that were moved down (i.e. A)
 	to the child commit.  With this option, both groups of lines
@@ -76,8 +98,8 @@
 	files that were modified in the same commit.  This is
 	useful when you reorganize your program and move code
 	around across files.  When this option is given twice,
-	the command looks for copies from all other files in the
-	parent for the commit that creates the file in addition.
+	the command additionally looks for copies from all other
+	files in the parent for the commit that creates the file.
 +
 <num> is optional but it is the lower bound on the number of
 alphanumeric characters that git must detect as moving
diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl
deleted file mode 100644
index 6a361a2..0000000
--- a/Documentation/callouts.xsl
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- callout.xsl: converts asciidoc callouts to man page format -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
-<xsl:template match="co">
-	<xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
-	<xsl:text>.sp&#10;</xsl:text>
-	<xsl:apply-templates/>
-	<xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
-	<xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
-	<xsl:apply-templates/>
-	<xsl:text>.br&#10;</xsl:text>
-</xsl:template>
-
-<!-- sorry, this is not about callouts, but attempts to work around
- spurious .sp at the tail of the line docbook stylesheets seem to add -->
-<xsl:template match="simpara">
-  <xsl:variable name="content">
-    <xsl:apply-templates/>
-  </xsl:variable>
-  <xsl:value-of select="normalize-space($content)"/>
-  <xsl:if test="not(ancestor::authorblurb) and
-                not(ancestor::personblurb)">
-    <xsl:text>&#10;&#10;</xsl:text>
-  </xsl:if>
-</xsl:template>
-
-</xsl:stylesheet>
diff --git a/Documentation/cat-texi.perl b/Documentation/cat-texi.perl
index dbc133c..828ec62 100755
--- a/Documentation/cat-texi.perl
+++ b/Documentation/cat-texi.perl
@@ -18,8 +18,12 @@
 
 printf '\input texinfo
 @setfilename gitman.info
-@documentencoding us-ascii
-@node Top,,%s
+@documentencoding UTF-8
+@dircategory Development
+@direntry
+* Git Man Pages: (gitman).  Manual pages for Git revision control system
+@end direntry
+@node Top,,, (dir)
 @top Git Manual Pages
 @documentlanguage en
 @menu
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 113d9d1..5319df5 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -25,7 +25,7 @@
 The file consists of sections and variables.  A section begins with
 the name of the section in square brackets and continues until the next
 section begins.  Section names are not case sensitive.  Only alphanumeric
-characters, '`-`' and '`.`' are allowed in section names.  Each variable
+characters, `-` and `.` are allowed in section names.  Each variable
 must belong to some section, which means that there must be section
 header before first setting of a variable.
 
@@ -39,7 +39,7 @@
 --------
 
 Subsection names can contain any characters except newline (doublequote
-'`"`' and backslash have to be escaped as '`\"`' and '`\\`',
+`"` and backslash have to be escaped as `\"` and `\\`,
 respectively) and are case sensitive.  Section header cannot span multiple
 lines.  Variables may belong directly to a section or to a given subsection.
 You can have `[section]` if you have `[section "subsection"]`, but you
@@ -53,7 +53,7 @@
 'name = value'.  If there is no equal sign on the line, the entire line
 is taken as 'name' and the variable is recognized as boolean "true".
 The variable names are case-insensitive and only alphanumeric
-characters and '`-`' are allowed.  There can be more than one value
+characters and `-` are allowed.  There can be more than one value
 for a given variable; we say then that variable is multivalued.
 
 Leading and trailing whitespace in a variable value is discarded.
@@ -69,15 +69,15 @@
 You need to enclose variable value in double quotes if you want to
 preserve leading or trailing whitespace, or if variable value contains
 beginning of comment characters (if it contains '#' or ';').
-Double quote '`"`' and backslash '`\`' characters in variable value must
-be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'.
+Double quote `"` and backslash `\` characters in variable value must
+be escaped: use `\"` for `"` and `\\` for `\`.
 
-The following escape sequences (beside '`\"`' and '`\\`') are recognized:
-'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB)
-and '`\b`' for backspace (BS).  No other char escape sequence, nor octal
+The following escape sequences (beside `\"` and `\\`) are recognized:
+`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
+and `\b` for backspace (BS).  No other char escape sequence, nor octal
 char sequences are valid.
 
-Variable value ending in a '`\`' is continued on the next line in the
+Variable value ending in a `\` is continued on the next line in the
 customary UNIX fashion.
 
 Some variables may require special value format.
@@ -117,6 +117,17 @@
 	the working copy are ignored; useful on broken filesystems like FAT.
 	See linkgit:git-update-index[1]. True by default.
 
+core.ignoreCygwinFSTricks::
+	This option is only used by Cygwin implementation of Git. If false,
+	the Cygwin stat() and lstat() functions are used. This may be useful
+	if your repository consists of a few separate directories joined in
+	one hierarchy using Cygwin mount. If true, Git uses native Win32 API
+	whenever it is possible and falls back to Cygwin functions only to
+	handle symbol links. The native mode is more than twice faster than
+	normal Cygwin l/stat() functions. True by default, unless core.filemode
+	is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's
+	POSIX emulation is required to support core.filemode.
+
 core.trustctime::
 	If false, the ctime differences between the index and the
 	working copy are ignored; useful when the inode change time
@@ -210,6 +221,11 @@
 Can be overridden by the 'GIT_PROXY_COMMAND' environment variable
 (which always applies universally, without the special "for"
 handling).
++
+The special string `none` can be used as the proxy command to
+specify that no proxy be used for a given domain pattern.
+This is useful for excluding servers inside a firewall from
+proxy use, while defaulting to a common proxy for external domains.
 
 core.ignoreStat::
 	If true, commands which modify both the working tree and the index
@@ -371,9 +387,9 @@
 	to override git's default settings this way, you need
 	to be explicit.  For example, to disable the S option
 	in a backward compatible manner, set `core.pager`
-	to "`less -+$LESS -FRX`".  This will be passed to the
+	to `less -+$LESS -FRX`.  This will be passed to the
 	shell by git, which will translate the final command to
-	"`LESS=FRSX less -+FRSX -FRX`".
+	`LESS=FRSX less -+FRSX -FRX`.
 
 core.whitespace::
 	A comma separated list of common whitespace problems to
@@ -402,6 +418,15 @@
 journalling (traditional UNIX filesystems) or that only journal metadata
 and not file contents (OS X's HFS+, or Linux ext3 with "data=writeback").
 
+core.preloadindex::
+	Enable parallel index preload for operations like 'git diff'
++
+This can speed up operations like 'git diff' and 'git status' especially
+on filesystems like NFS that have weak caching semantics and thus
+relatively high IO latencies.  With this set to 'true', git will do the
+index comparison to the filesystem data in parallel, allowing
+overlapping IO's.
+
 alias.*::
 	Command aliases for the linkgit:git[1] command wrapper - e.g.
 	after defining "alias.last = cat-file commit HEAD", the invocation
@@ -448,10 +473,14 @@
 	This option defaults to never.
 
 branch.<name>.remote::
-	When in branch <name>, it tells 'git-fetch' which remote to fetch.
-	If this option is not given, 'git-fetch' defaults to remote "origin".
+	When in branch <name>, it tells 'git-fetch' and 'git-push' which
+	remote to fetch from/push to.  It defaults to `origin` if no remote is
+	configured. `origin` is also used if you are not on any branch.
 
 branch.<name>.merge::
+	Defines, together with branch.<name>.remote, the upstream branch
+	for the given branch. It tells 'git-fetch'/'git-pull' which
+	branch to merge and can also affect 'git-push' (see push.default).
 	When in branch <name>, it tells 'git-fetch' the default
 	refspec to be marked for merging in FETCH_HEAD. The value is
 	handled like the remote part of a refspec, and must match a
@@ -528,6 +557,25 @@
 	whitespace errors). The values of these variables may be specified as
 	in color.branch.<slot>.
 
+color.grep::
+	When set to `always`, always highlight matches.  When `false` (or
+	`never`), never.  When set to `true` or `auto`, use color only
+	when the output is written to the terminal.  Defaults to `false`.
+
+color.grep.external::
+	The string value of this variable is passed to an external 'grep'
+	command as a command line option if match highlighting is turned
+	on.  If set to an empty string, no option is passed at all,
+	turning off coloring for external 'grep' calls; this is the default.
+	For GNU grep, set it to `--color=always` to highlight matches even
+	when a pager is used.
+
+color.grep.match::
+	Use customized color for matches.  The value of this variable
+	may be specified as in color.branch.<slot>.  It is passed using
+	the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
+	calling an external 'grep'.
+
 color.interactive::
 	When set to `always`, always use colors for interactive prompts
 	and displays (such as those used by "git-add --interactive").
@@ -536,8 +584,8 @@
 
 color.interactive.<slot>::
 	Use customized color for 'git-add --interactive'
-	output. `<slot>` may be `prompt`, `header`, or `help`, for
-	three distinct types of normal output from interactive
+	output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
+	four distinct types of normal output from interactive
 	programs.  The values of these variables may be specified as
 	in color.branch.<slot>.
 
@@ -590,6 +638,22 @@
 	you want to use an external diff program only on a subset of
 	your files, you	might want to use linkgit:gitattributes[5] instead.
 
+diff.mnemonicprefix::
+	If set, 'git-diff' uses a prefix pair that is different from the
+	standard "a/" and "b/" depending on what is being compared.  When
+	this configuration is in effect, reverse diff output also swaps
+	the order of the prefixes:
+'git-diff';;
+	compares the (i)ndex and the (w)ork tree;
+'git-diff HEAD';;
+	 compares a (c)ommit and the (w)ork tree;
+'git diff --cached';;
+	compares a (c)ommit and the (i)ndex;
+'git-diff HEAD:file1 file2';;
+	compares an (o)bject and a (w)ork tree entity;
+'git diff --no-index a b';;
+	compares two non-git things (1) and (2).
+
 diff.renameLimit::
 	The number of files to consider when performing the copy/rename
 	detection; equivalent to the 'git-diff' option '-l'.
@@ -599,6 +663,37 @@
 	will enable basic rename detection.  If set to "copies" or
 	"copy", it will detect copies, as well.
 
+diff.suppressBlankEmpty::
+	A boolean to inhibit the standard behavior of printing a space
+	before each empty output line. Defaults to false.
+
+diff.tool::
+	Controls which diff tool is used.  `diff.tool` overrides
+	`merge.tool` when used by linkgit:git-difftool[1] and has
+	the same valid values as `merge.tool` minus "tortoisemerge"
+	and plus "kompare".
+
+difftool.<tool>.path::
+	Override the path for the given tool.  This is useful in case
+	your tool is not in the PATH.
+
+difftool.<tool>.cmd::
+	Specify the command to invoke the specified diff tool.
+	The specified command is evaluated in shell with the following
+	variables available:  'LOCAL' is set to the name of the temporary
+	file containing the contents of the diff pre-image and 'REMOTE'
+	is set to the name of the temporary file containing the contents
+	of the diff post-image.
+
+difftool.prompt::
+	Prompt before each invocation of the diff tool.
+
+diff.wordRegex::
+	A POSIX Extended Regular Expression used to determine what is a "word"
+	when performing word-by-word difference calculations.  Character
+	sequences that match the regular expression are "words", all other
+	characters are *ignorable* whitespace.
+
 fetch.unpackLimit::
 	If the number of objects fetched over the git native
 	transfer is below this
@@ -611,10 +706,11 @@
 	`transfer.unpackLimit` is used instead.
 
 format.numbered::
-	A boolean which can enable sequence numbers in patch subjects.
-	Setting this option to "auto" will enable it only if there is
-	more than one patch.  See --numbered option in
-	linkgit:git-format-patch[1].
+	A boolean which can enable or disable sequence numbers in patch
+	subjects.  It defaults to "auto" which enables it only if there
+	is more than one patch.  It can be enabled or disabled for all
+	messages by setting it to "true" or "false".  See --numbered
+	option in linkgit:git-format-patch[1].
 
 format.headers::
 	Additional email headers to include in a patch to be submitted
@@ -630,6 +726,23 @@
 	See linkgit:git-log[1], linkgit:git-show[1],
 	linkgit:git-whatchanged[1].
 
+format.thread::
+	The default threading style for 'git-format-patch'.  Can be
+	either a boolean value, `shallow` or `deep`.  'Shallow'
+	threading makes every mail a reply to the head of the series,
+	where the head is chosen from the cover letter, the
+	`\--in-reply-to`, and the first patch mail, in this order.
+	'Deep' threading makes every mail a reply to the previous one.
+	A true boolean value is the same as `shallow`, and a false
+	value disables threading.
+
+format.signoff::
+    A boolean value which lets you enable the `-s/--signoff` option of
+    format-patch by default. *Note:* Adding the Signed-off-by: line to a
+    patch should be a conscious act and means that you certify you have
+    the rights to submit this work under the same open source license.
+    Please see the 'SubmittingPatches' document for further discussion.
+
 gc.aggressiveWindow::
 	The window size parameter used in the delta compression
 	algorithm used by 'git-gc --aggressive'.  This defaults
@@ -661,7 +774,9 @@
 
 gc.pruneexpire::
 	When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
-	Override the grace period with this config variable.
+	Override the grace period with this config variable.  The value
+	"now" may be used to disable this  grace period and always prune
+	unreachable objects immediately.
 
 gc.reflogexpire::
 	'git-reflog expire' removes reflog entries older than
@@ -682,6 +797,10 @@
 	kept for this many days when 'git-rerere gc' is run.
 	The default is 15 days.  See linkgit:git-rerere[1].
 
+gitcvs.commitmsgannotation::
+	Append this string to each commit message. Set to empty string
+	to disable this feature. Defaults to "via git-CVS emulator".
+
 gitcvs.enabled::
 	Whether the CVS server interface is enabled for this repository.
 	See linkgit:git-cvsserver[1].
@@ -752,6 +871,14 @@
 	Specifies how many context lines should be used in calls to diff
 	made by the linkgit:git-gui[1]. The default is "5".
 
+gui.encoding::
+	Specifies the default encoding to use for displaying of
+	file contents in linkgit:git-gui[1] and linkgit:gitk[1].
+	It can be overridden by setting the 'encoding' attribute
+	for relevant files (see linkgit:gitattributes[5]).
+	If this option is not set, the tools default to the
+	locale encoding.
+
 gui.matchtrackingbranch::
 	Determines if new branches created with linkgit:git-gui[1] should
 	default to tracking remote branches with matching names or
@@ -774,6 +901,73 @@
 	the linkgit:git-gui[1]. When set to "none" spell checking is turned
 	off.
 
+gui.fastcopyblame::
+	If true, 'git gui blame' uses '-C' instead of '-C -C' for original
+	location detection. It makes blame significantly faster on huge
+	repositories at the expense of less thorough copy detection.
+
+gui.copyblamethreshold::
+	Specifies the threshold to use in 'git gui blame' original location
+	detection, measured in alphanumeric characters. See the
+	linkgit:git-blame[1] manual for more information on copy detection.
+
+gui.blamehistoryctx::
+	Specifies the radius of history context in days to show in
+	linkgit:gitk[1] for the selected commit, when the `Show History
+	Context` menu item is invoked from 'git gui blame'. If this
+	variable is set to zero, the whole history is shown.
+
+guitool.<name>.cmd::
+	Specifies the shell command line to execute when the corresponding item
+	of the linkgit:git-gui[1] `Tools` menu is invoked. This option is
+	mandatory for every tool. The command is executed from the root of
+	the working directory, and in the environment it receives the name of
+	the tool as 'GIT_GUITOOL', the name of the currently selected file as
+	'FILENAME', and the name of the current branch as 'CUR_BRANCH' (if
+	the head is detached, 'CUR_BRANCH' is empty).
+
+guitool.<name>.needsfile::
+	Run the tool only if a diff is selected in the GUI. It guarantees
+	that 'FILENAME' is not empty.
+
+guitool.<name>.noconsole::
+	Run the command silently, without creating a window to display its
+	output.
+
+guitool.<name>.norescan::
+	Don't rescan the working directory for changes after the tool
+	finishes execution.
+
+guitool.<name>.confirm::
+	Show a confirmation dialog before actually running the tool.
+
+guitool.<name>.argprompt::
+	Request a string argument from the user, and pass it to the tool
+	through the 'ARGS' environment variable. Since requesting an
+	argument implies confirmation, the 'confirm' option has no effect
+	if this is enabled. If the option is set to 'true', 'yes', or '1',
+	the dialog uses a built-in generic prompt; otherwise the exact
+	value of the variable is used.
+
+guitool.<name>.revprompt::
+	Request a single valid revision from the user, and set the
+	'REVISION' environment variable. In other aspects this option
+	is similar to 'argprompt', and can be used together with it.
+
+guitool.<name>.revunmerged::
+	Show only unmerged branches in the 'revprompt' subdialog.
+	This is useful for tools similar to merge or rebase, but not
+	for things like checkout or reset.
+
+guitool.<name>.title::
+	Specifies the title to use for the prompt dialog. The default
+	is the tool name.
+
+guitool.<name>.prompt::
+	Specifies the general prompt string to display at the top of
+	the dialog, before subsections for 'argprompt' and 'revprompt'.
+	The default value includes the actual command.
+
 help.browser::
 	Specify the browser that will be used to display help in the
 	'web' format. See linkgit:git-help[1].
@@ -783,6 +977,15 @@
 	Values 'man', 'info', 'web' and 'html' are supported. 'man' is
 	the default. 'web' and 'html' are the same.
 
+help.autocorrect::
+	Automatically correct and execute mistyped commands after
+	waiting for the given number of deciseconds (0.1 sec). If more
+	than one command can be deduced from the entered text, nothing
+	will be executed.  If the value of this option is negative,
+	the corrected command will be executed immediately. If the
+	value is 0 - the command will be just shown but not executed.
+	This is the default.
+
 http.proxy::
 	Override the HTTP proxy, normally configured using the 'http_proxy'
 	environment variable (see linkgit:curl[1]).  This can be overridden
@@ -863,6 +1066,13 @@
 	The port number to bind the gitweb httpd to. See
 	linkgit:git-instaweb[1].
 
+interactive.singlekey::
+	In interactive programs, allow the user to provide one-letter
+	input with a single key (i.e., without hitting enter).
+	Currently this is used only by the `\--patch` mode of
+	linkgit:git-add[1].  Note that this setting is silently
+	ignored if portable keystroke input is not available.
+
 log.date::
 	Set default date-time mode for the log command. Setting log.date
 	value is similar to using 'git-log'\'s --date option. The value is one of the
@@ -875,6 +1085,14 @@
 	Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
 	normally hide the root commit will now show it. True by default.
 
+mailmap.file::
+	The location of an augmenting mailmap file. The default
+	mailmap, located in the root of the repository, is loaded
+	first, then the mailmap file pointed to by this variable.
+	The location of the mailmap file may be in a repository
+	subdirectory, or somewhere outside of the repository itself.
+	See linkgit:git-shortlog[1] and linkgit:git-blame[1].
+
 man.viewer::
 	Specify the programs that may be used to display help in the
 	'man' format. See linkgit:git-help[1].
@@ -919,6 +1137,16 @@
 	is set to `false` then this file is not preserved.  Defaults to
 	`true` (i.e. keep the backup files).
 
+mergetool.keepTemporaries::
+	When invoking a custom merge tool, git uses a set of temporary
+	files to pass to the tool. If the tool returns an error and this
+	variable is set to `true`, then these temporary files will be
+	preserved, otherwise they will be removed after the tool has
+	exited. Defaults to `false`.
+
+mergetool.prompt::
+	Prompt before each invocation of the merge resolution program.
+
 pack.window::
 	The size of the window used by linkgit:git-pack-objects[1] when no
 	window size is given on the command line. Defaults to 10.
@@ -989,7 +1217,7 @@
 	particular git subcommand when writing to a tty.  If
 	`\--paginate` or `\--no-pager` is specified on the command line,
 	it takes precedence over this option.  To disable pagination for
-	all commands, set `core.pager` or 'GIT_PAGER' to "`cat`".
+	all commands, set `core.pager` or `GIT_PAGER` to `cat`.
 
 pull.octopus::
 	The default merge strategy to use when pulling multiple branches
@@ -998,6 +1226,23 @@
 pull.twohead::
 	The default merge strategy to use when pulling a single branch.
 
+push.default::
+	Defines the action git push should take if no refspec is given
+	on the command line, no refspec is configured in the remote, and
+	no refspec is implied by any of the options given on the command
+	line. Possible values are:
++
+* `nothing` do not push anything.
+* `matching` push all matching branches.
+  All branches having the same name in both ends are considered to be
+  matching. This is the default.
+* `tracking` push the current branch to its upstream branch.
+* `current` push the current branch to a branch of the same name.
+
+rebase.stat::
+	Whether to show a diffstat of what changed upstream since the last
+	rebase. False by default.
+
 receive.fsckObjects::
 	If it is set to true, git-receive-pack will check all received
 	objects. It will abort in the case of a malformed object or a
@@ -1014,6 +1259,19 @@
 	especially on slow filesystems.  If not set, the value of
 	`transfer.unpackLimit` is used instead.
 
+receive.denyDeletes::
+	If set to true, git-receive-pack will deny a ref update that deletes
+	the ref. Use this to prevent such a ref deletion via a push.
+
+receive.denyCurrentBranch::
+	If set to true or "refuse", receive-pack will deny a ref update
+	to the currently checked out branch of a non-bare repository.
+	Such a push is potentially dangerous because it brings the HEAD
+	out of sync with the index and working tree. If set to "warn",
+	print a warning of such a push to stderr, but allow the push to
+	proceed. If set to false or "ignore", allow such pushes with no
+	message. Defaults to "warn".
+
 receive.denyNonFastForwards::
 	If set to true, git-receive-pack will deny a ref update which is
 	not a fast forward. Use this to prevent such an update via a push,
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 45885bb..9276fae 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -19,16 +19,12 @@
 
 ifndef::git-format-patch[]
 -p::
+-u::
 	Generate patch (see section on generating patches).
 	{git-diff? This is the default.}
 endif::git-format-patch[]
 
--u::
-	Synonym for "-p".
-
 -U<n>::
-	Shorthand for "--unified=<n>".
-
 --unified=<n>::
 	Generate diffs with <n> lines of context instead of
 	the usual three. Implies "-p".
@@ -40,6 +36,9 @@
 --patch-with-raw::
 	Synonym for "-p --raw".
 
+--patience::
+	Generate a diff using the "patience diff" algorithm.
+
 --stat[=width[,name-width]]::
 	Generate a diffstat.  You can override the default
 	output width for 80-column terminal by "--stat=width".
@@ -65,6 +64,9 @@
 	can be set with "--dirstat=limit". Changes in a child directory is not
 	counted for the parent directory, unless "--cumulative" is used.
 
+--dirstat-by-file[=limit]::
+	Same as --dirstat, but counts changed files instead of lines.
+
 --summary::
 	Output a condensed summary of extended header information
 	such as creations, renames and mode changes.
@@ -92,8 +94,22 @@
 	Turn off colored diff, even when the configuration file
 	gives the default to color output.
 
---color-words::
-	Show colored word diff, i.e. color words which have changed.
+--color-words[=<regex>]::
+	Show colored word diff, i.e., color words which have changed.
+	By default, words are separated by whitespace.
++
+When a <regex> is specified, every non-overlapping match of the
+<regex> is considered a word.  Anything between these matches is
+considered whitespace and ignored(!) for the purposes of finding
+differences.  You may want to append `|[^[:space:]]` to your regular
+expression to make sure that it matches all non-whitespace characters.
+A match that contains a newline is silently truncated(!) at the
+newline.
++
+The regex can also be set via a diff driver or configuration option, see
+linkgit:gitattributes[1] or linkgit:git-config[1].  Giving it explicitly
+overrides any diff driver or configuration setting.  Diff drivers
+override configuration settings.
 
 --no-renames::
 	Turn off rename detection, even when the configuration
@@ -106,9 +122,9 @@
 	--exit-code.
 
 --full-index::
-	Instead of the first handful characters, show full
-	object name of pre- and post-image blob on the "index"
-	line when generating a patch format output.
+	Instead of the first handful of characters, show the full
+	pre- and post-image blob object names on the "index"
+	line when generating patch format output.
 
 --binary::
 	In addition to --full-index, output "binary diff" that
@@ -117,7 +133,7 @@
 --abbrev[=<n>]::
 	Instead of showing the full 40-byte hexadecimal object
 	name in diff-raw format output and diff-tree header
-	lines, show only handful hexdigits prefix.  This is
+	lines, show only a partial prefix.  This is
 	independent of --full-index option above, which controls
 	the diff-patch output format.  Non default number of
 	digits can be specified with --abbrev=<n>.
@@ -160,7 +176,10 @@
 	number.
 
 -S<string>::
-	Look for differences that contain the change in <string>.
+	Look for differences that introduce or remove an instance of
+	<string>. Note that this is different than the string simply
+	appearing in diff output; see the 'pickaxe' entry in
+	linkgit:gitdiffcore[7] for more details.
 
 --pickaxe-all::
 	When -S finds a change, show all the changes in that
@@ -187,30 +206,28 @@
 	can name which subdirectory to make the output relative
 	to by giving a <path> as an argument.
 
+-a::
 --text::
 	Treat all files as text.
 
--a::
-	Shorthand for "--text".
-
 --ignore-space-at-eol::
 	Ignore changes in whitespace at EOL.
 
+-b::
 --ignore-space-change::
 	Ignore changes in amount of whitespace.  This ignores whitespace
 	at line end, and considers all other sequences of one or
 	more whitespace characters to be equivalent.
 
--b::
-	Shorthand for "--ignore-space-change".
-
+-w::
 --ignore-all-space::
 	Ignore whitespace when comparing lines.  This ignores
 	differences even if one line has whitespace where the other
 	line has none.
 
--w::
-	Shorthand for "--ignore-all-space".
+--inter-hunk-context=<lines>::
+	Show the context between diff hunks, up to the specified number
+	of lines, thereby fusing hunks that are close to each other.
 
 --exit-code::
 	Make the program exit with codes similar to diff(1).
diff --git a/Documentation/docbook-xsl.css b/Documentation/docbook-xsl.css
index b878b38..e11c8f0 100644
--- a/Documentation/docbook-xsl.css
+++ b/Documentation/docbook-xsl.css
@@ -16,6 +16,7 @@
 html body {
   margin: 1em 5% 1em 5%;
   line-height: 1.2;
+  font-family: sans-serif;
 }
 
 body div {
@@ -128,6 +129,15 @@
 
 tt.literal, code.literal {
   color: navy;
+  font-family: sans-serif;
+}
+
+code.literal:before { content: "'"; }
+code.literal:after { content: "'"; }
+
+em {
+  font-style: italic;
+  color: #064;
 }
 
 div.literallayout p {
@@ -137,7 +147,6 @@
 
 div.literallayout {
   font-family: monospace;
-#  margin: 0.5em 10% 0.5em 1em;
   margin: 0em;
   color: navy;
   border: 1px solid silver;
@@ -187,7 +196,8 @@
 }
 
 dt span.term {
-  font-style: italic;
+  font-style: normal;
+  color: navy;
 }
 
 div.variablelist dd p {
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 2b6d6c8..d938b42 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -9,8 +9,8 @@
 --------
 [verse]
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
-	  [--all | [--update | -u]] [--refresh] [--ignore-errors] [--]
-	  <filepattern>...
+	  [--all | [--update | -u]] [--intent-to-add | -N]
+	  [--refresh] [--ignore-errors] [--] <filepattern>...
 
 DESCRIPTION
 -----------
@@ -92,6 +92,15 @@
 	and add all untracked files that are not ignored by '.gitignore'
 	mechanism.
 
+
+-N::
+--intent-to-add::
+	Record only the fact that the path will be added later. An entry
+	for the path is placed in the index with no content. This is
+	useful for, among other things, showing the unstaged content of
+	such files with 'git diff' and committing them with 'git commit
+	-a'.
+
 --refresh::
 	Don't add the file(s), but only refresh their stat()
 	information in the index.
@@ -127,7 +136,7 @@
 ------------
 +
 Note that the asterisk `\*` is quoted from the shell in this
-example; this lets the command to include the files from
+example; this lets the command include the files from
 subdirectories of `Documentation/` directory.
 
 * Considers adding content from all git-*.sh scripts:
@@ -136,7 +145,7 @@
 $ git add git-*.sh
 ------------
 +
-Because this example lets shell expand the asterisk (i.e. you are
+Because this example lets the shell expand the asterisk (i.e. you are
 listing the files explicitly), it does not consider
 `subdir/git-foo.sh`.
 
@@ -189,8 +198,8 @@
 
 update::
 
-   This shows the status information and gives prompt
-   "Update>>".  When the prompt ends with double '>>', you can
+   This shows the status information and issues an "Update>>"
+   prompt.  When the prompt ends with double '>>', you can
    make more than one selection, concatenated with whitespace or
    comma.  Also you can say ranges.  E.g. "2-5 7,9" to choose
    2,3,4,5,7,9 from the list.  If the second number in a range is
@@ -229,15 +238,18 @@
 
 patch::
 
-  This lets you choose one path out of 'status' like selection.
-  After choosing the path, it presents diff between the index
+  This lets you choose one path out of a 'status' like selection.
+  After choosing the path, it presents the diff between the index
   and the working tree file and asks you if you want to stage
   the change of each hunk.  You can say:
 
        y - stage this hunk
        n - do not stage this hunk
+       q - quit, do not stage this hunk nor any of the remaining ones
        a - stage this and all the remaining hunks in the file
        d - do not stage this hunk nor any of the remaining hunks in the file
+       g - select a hunk to go to
+       / - search for a hunk matching the given regex
        j - leave this hunk undecided, see next undecided hunk
        J - leave this hunk undecided, see next hunk
        k - leave this hunk undecided, see previous undecided hunk
@@ -254,13 +266,6 @@
   This lets you review what will be committed (i.e. between
   HEAD and index).
 
-Bugs
-----
-The interactive mode does not work with files whose names contain
-characters that need C-quoting.  `core.quotepath` configuration can be
-used to work this limitation around to some degree, but backslash,
-double-quote and control characters will still have problems.
-
 SEE ALSO
 --------
 linkgit:git-status[1]
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index b9c6fac..1e71dd5 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -10,8 +10,10 @@
 --------
 [verse]
 'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
-	 [--3way] [--interactive]
-         [--whitespace=<option>] [-C<n>] [-p<n>]
+	 [--3way] [--interactive] [--committer-date-is-author-date]
+	 [--ignore-date]
+	 [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
+	 [--reject]
 	 [<mbox> | <Maildir>...]
 'git am' (--skip | --resolved | --abort)
 
@@ -25,8 +27,8 @@
 -------
 <mbox>|<Maildir>...::
 	The list of mailbox files to read patches from. If you do not
-	supply this argument, reads from the standard input. If you supply
-	directories, they'll be treated as Maildirs.
+	supply this argument, the command reads from the standard input.
+	If you supply directories, they will be treated as Maildirs.
 
 -s::
 --signoff::
@@ -46,7 +48,7 @@
 	preferred encoding if it is not UTF-8).
 +
 This was optional in prior versions of git, but now it is the
-default.   You could use `--no-utf8` to override this.
+default.   You can use `--no-utf8` to override this.
 
 --no-utf8::
 	Pass `-n` flag to 'git-mailinfo' (see
@@ -55,17 +57,15 @@
 -3::
 --3way::
 	When the patch does not apply cleanly, fall back on
-	3-way merge, if the patch records the identity of blobs
-	it is supposed to apply to, and we have those blobs
+	3-way merge if the patch records the identity of blobs
+	it is supposed to apply to and we have those blobs
 	available locally.
 
 --whitespace=<option>::
-	This flag is passed to the 'git-apply' (see linkgit:git-apply[1])
-	program that applies
-	the patch.
-
 -C<n>::
 -p<n>::
+--directory=<dir>::
+--reject::
 	These flags are passed to the 'git-apply' (see linkgit:git-apply[1])
 	program that applies
 	the patch.
@@ -74,6 +74,20 @@
 --interactive::
 	Run interactively.
 
+--committer-date-is-author-date::
+	By default the command records the date from the e-mail
+	message as the commit author date, and uses the time of
+	commit creation as the committer date. This allows the
+	user to lie about the committer date by using the same
+	timestamp as the author date.
+
+--ignore-date::
+	By default the command records the date from the e-mail
+	message as the commit author date, and uses the time of
+	commit creation as the committer date. This allows the
+	user to lie about author timestamp by using the same
+	timestamp as the committer date.
+
 --skip::
 	Skip the current patch.  This is only meaningful when
 	restarting an aborted patch.
@@ -107,18 +121,18 @@
 It is supposed to describe what the commit is about concisely as
 a one line text.
 
-The body of the message (iow, after a blank line that terminates
-RFC2822 headers) can begin with "Subject: " and "From: " lines
-that are different from those of the mail header, to override
-the values of these fields.
+The body of the message (the rest of the message after the blank line
+that terminates the RFC2822 headers) can begin with "Subject: " and
+"From: " lines that are different from those of the mail header,
+to override the values of these fields.
 
 The commit message is formed by the title taken from the
 "Subject: ", a blank line and the body of the message up to
-where the patch begins.  Excess whitespaces at the end of the
+where the patch begins.  Excess whitespace characters at the end of the
 lines are automatically stripped.
 
 The patch is expected to be inline, directly following the
-message.  Any line that is of form:
+message.  Any line that is of the form:
 
 * three-dashes and end-of-line, or
 * a line that begins with "diff -", or
@@ -127,18 +141,18 @@
 is taken as the beginning of a patch, and the commit log message
 is terminated before the first occurrence of such a line.
 
-When initially invoking it, you give it names of the mailboxes
-to crunch.  Upon seeing the first patch that does not apply, it
-aborts in the middle,.  You can recover from this in one of two ways:
+When initially invoking it, you give it the names of the mailboxes
+to process.  Upon seeing the first patch that does not apply, it
+aborts in the middle.  You can recover from this in one of two ways:
 
-. skip the current patch by re-running the command with '--skip'
+. skip the current patch by re-running the command with the '--skip'
   option.
 
 . hand resolve the conflict in the working directory, and update
-  the index file to bring it in a state that the patch should
-  have produced.  Then run the command with '--resolved' option.
+  the index file to bring it into a state that the patch should
+  have produced.  Then run the command with the '--resolved' option.
 
-The command refuses to process new mailboxes while `.git/rebase-apply`
+The command refuses to process new mailboxes while the `.git/rebase-apply`
 directory exists, so if you decide to start over from scratch,
 run `rm -f -r .git/rebase-apply` before running the command with mailbox
 names.
diff --git a/Documentation/git-annotate.txt b/Documentation/git-annotate.txt
index 0aba022..0590eec 100644
--- a/Documentation/git-annotate.txt
+++ b/Documentation/git-annotate.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-annotate - Annotate file lines with commit info
+git-annotate - Annotate file lines with commit information
 
 SYNOPSIS
 --------
@@ -12,11 +12,11 @@
 DESCRIPTION
 -----------
 Annotates each line in the given file with information from the commit
-which introduced the line. Optionally annotate from a given revision.
+which introduced the line. Optionally annotates from a given revision.
 
 The only difference between this command and linkgit:git-blame[1] is that
 they use slightly different output formats, and this command exists only
-for backward compatibility to support existing scripts, and provide more
+for backward compatibility to support existing scripts, and provide a more
 familiar command name for people coming from other SCM systems.
 
 OPTIONS
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 44e1968..9e5baa2 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -10,11 +10,12 @@
 --------
 [verse]
 'git apply' [--stat] [--numstat] [--summary] [--check] [--index]
-	  [--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]
+	  [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
 	  [--allow-binary-replacement | --binary] [--reject] [-z]
 	  [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
 	  [--whitespace=<nowarn|warn|fix|error|error-all>]
-	  [--exclude=PATH] [--directory=<root>] [--verbose] [<patch>...]
+	  [--exclude=PATH] [--include=PATH] [--directory=<root>]
+	  [--verbose] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -24,7 +25,7 @@
 OPTIONS
 -------
 <patch>...::
-	The files to read patch from.  '-' can be used to read
+	The files to read the patch from.  '-' can be used to read
 	from the standard input.
 
 --stat::
@@ -32,8 +33,8 @@
 	input.  Turns off "apply".
 
 --numstat::
-	Similar to \--stat, but shows number of added and
-	deleted lines in decimal notation and pathname without
+	Similar to \--stat, but shows the number of added and
+	deleted lines in decimal notation and the pathname without
 	abbreviation, to make it more machine friendly.  For
 	binary files, outputs two `-` instead of saying
 	`0 0`.  Turns off "apply".
@@ -59,15 +60,15 @@
 	causes the index file to be updated.
 
 --cached::
-	Apply a patch without touching the working tree. Instead, take the
-	cached data, apply the patch, and store the result in the index,
+	Apply a patch without touching the working tree. Instead take the
+	cached data, apply the patch, and store the result in the index
 	without using the working tree. This implies '--index'.
 
---build-fake-ancestor <file>::
+--build-fake-ancestor=<file>::
 	Newer 'git-diff' output has embedded 'index information'
 	for each blob to help identify the original version that
 	the patch applies to.  When this flag is given, and if
-	the original versions of the blobs is available locally,
+	the original versions of the blobs are available locally,
 	builds a temporary index containing those blobs.
 +
 When a pure mode change is encountered (which has no index information),
@@ -108,13 +109,13 @@
 	applying a diff generated with --unified=0. To bypass these
 	checks use '--unidiff-zero'.
 +
-Note, for the reasons stated above usage of context-free patches are
+Note, for the reasons stated above usage of context-free patches is
 discouraged.
 
 --apply::
 	If you use any of the options marked "Turns off
 	'apply'" above, 'git-apply' reads and outputs the
-	information you asked without actually applying the
+	requested information without actually applying the
 	patch.  Give this flag after those flags to also apply
 	the patch.
 
@@ -123,7 +124,7 @@
 	patch.  This can be used to extract the common part between
 	two files by first running 'diff' on them and applying
 	the result with this option, which would apply the
-	deletion part but not addition part.
+	deletion part but not the addition part.
 
 --allow-binary-replacement::
 --binary::
@@ -137,6 +138,17 @@
 	be useful when importing patchsets, where you want to exclude certain
 	files or directories.
 
+--include=<path-pattern>::
+	Apply changes to files matching the given path pattern. This can
+	be useful when importing patchsets, where you want to include certain
+	files or directories.
++
+When --exclude and --include patterns are used, they are examined in the
+order they appear on the command line, and the first match determines if a
+patch to each path is used.  A patch to a path that does not match any
+include/exclude pattern is used by default if there is no include pattern
+on the command line, and ignored if there is any include pattern.
+
 --whitespace=<action>::
 	When applying a patch, detect a new or modified line that has
 	whitespace errors.  What are considered whitespace errors is
@@ -150,7 +162,7 @@
 When `git-apply` is used for statistics and not applying a
 patch, it defaults to `nowarn`.
 +
-You can use different `<action>` to control this
+You can use different `<action>` values to control this
 behavior:
 +
 * `nowarn` turns off the trailing whitespace warning.
@@ -158,7 +170,7 @@
   patch as-is (default).
 * `fix` outputs warnings for a few such errors, and applies the
   patch after fixing them (`strip` is a synonym --- the tool
-  used to consider only trailing whitespaces as errors, and the
+  used to consider only trailing whitespace characters as errors, and the
   fix involved 'stripping' them, but modern gits do more).
 * `error` outputs warnings for a few such errors, and refuses
   to apply the patch.
@@ -183,7 +195,7 @@
 	adjusting the hunk headers appropriately).
 
 --directory=<root>::
-	Prepend <root> to all filenames.  If a "-p" argument was passed, too,
+	Prepend <root> to all filenames.  If a "-p" argument was also passed,
 	it is applied before prepending the new root.
 +
 For example, a patch that talks about updating `a/git-gui.sh` to `b/git-gui.sh`
@@ -209,7 +221,7 @@
 are not updated.
 
 If --index is not specified, then the submodule commits in the patch
-are ignored and only the absence of presence of the corresponding
+are ignored and only the absence or presence of the corresponding
 subdirectory is checked and (if possible) updated.
 
 Author
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index 41cbf9c..bc132c8 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -10,6 +10,7 @@
 --------
 [verse]
 'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+	      [--output=<file>] [--worktree-attributes]
 	      [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
 	      [path...]
 
@@ -22,7 +23,7 @@
 
 'git-archive' behaves differently when given a tree ID versus when
 given a commit ID or tag ID.  In the first case the current time is
-used as modification time of each file in the archive.  In the latter
+used as the modification time of each file in the archive.  In the latter
 case the commit time as recorded in the referenced commit object is
 used instead.  Additionally the commit ID is stored in a global
 extended pax header if the tar format is used; it can be extracted
@@ -47,12 +48,18 @@
 --prefix=<prefix>/::
 	Prepend <prefix>/ to each filename in the archive.
 
+--output=<file>::
+	Write the archive to <file> instead of stdout.
+
+--worktree-attributes::
+	Look for attributes in .gitattributes in working directory too.
+
 <extra>::
-	This can be any options that the archiver backend understand.
+	This can be any options that the archiver backend understands.
 	See next section.
 
 --remote=<repo>::
-	Instead of making a tar archive from local repository,
+	Instead of making a tar archive from the local repository,
 	retrieve a tar archive from a remote repository.
 
 --exec=<git-upload-archive>::
@@ -88,12 +95,24 @@
 	archiving user's umask will be used instead.  See umask(2) for
 	details.
 
+ATTRIBUTES
+----------
+
+export-ignore::
+	Files and directories with the attribute export-ignore won't be
+	added to archive files.  See linkgit:gitattributes[5] for details.
+
+export-subst::
+	If the attribute export-subst is set for a file then git will
+	expand several placeholders when adding this file to an archive.
+	See linkgit:gitattributes[5] for details.
+
 EXAMPLES
 --------
 git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
 
 	Create a tar archive that contains the contents of the
-	latest commit on the current branch, and extracts it in
+	latest commit on the current branch, and extract it in the
 	`/var/tmp/junk` directory.
 
 git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
@@ -110,6 +129,11 @@
 	Put everything in the current head's Documentation/ directory
 	into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
 
+
+SEE ALSO
+--------
+linkgit:gitattributes[5]
+
 Author
 ------
 Written by Franck Bui-Huu and Rene Scharfe.
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 39034ec..ffc02c7 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-bisect - Find the change that introduced a bug by binary search
+git-bisect - Find by binary search the change that introduced a bug
 
 
 SYNOPSIS
@@ -19,7 +19,7 @@
  git bisect start [<bad> [<good>...]] [--] [<paths>...]
  git bisect bad [<rev>]
  git bisect good [<rev>...]
- git bisect skip [<rev>...]
+ git bisect skip [(<rev>|<range>)...]
  git bisect reset [<branch>]
  git bisect visualize
  git bisect replay <logfile>
@@ -39,7 +39,8 @@
 Basic bisect commands: start, bad, good
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The way you use it is:
+Using the Linux kernel tree as an example, basic use of the bisect
+command is as follows:
 
 ------------------------------------------------
 $ git bisect start
@@ -48,61 +49,63 @@
                                  # tested that was good
 ------------------------------------------------
 
-When you give at least one bad and one good versions, it will bisect
-the revision tree and say something like:
+When you have specified at least one bad and one good version, the
+command bisects the revision tree and outputs something similar to
+the following:
 
 ------------------------------------------------
 Bisecting: 675 revisions left to test after this
 ------------------------------------------------
 
-and check out the state in the middle. Now, compile that kernel, and
-boot it. Now, let's say that this booted kernel works fine, then just
-do
+The state in the middle of the set of revisions is then checked out.
+You would now compile that kernel and boot it. If the booted kernel
+works correctly, you would then issue the following command:
 
 ------------------------------------------------
 $ git bisect good			# this one is good
 ------------------------------------------------
 
-which will now say
+The output of this command would be something similar to the following:
 
 ------------------------------------------------
 Bisecting: 337 revisions left to test after this
 ------------------------------------------------
 
-and you continue along, compiling that one, testing it, and depending
-on whether it is good or bad, you say "git bisect good" or "git bisect
-bad", and ask for the next bisection.
+You keep repeating this process, compiling the tree, testing it, and
+depending on whether it is good or bad issuing the command "git bisect good"
+or "git bisect bad" to ask for the next bisection.
 
-Until you have no more left, and you'll have been left with the first
-bad kernel rev in "refs/bisect/bad".
+Eventually there will be no more revisions left to bisect, and you
+will have been left with the first bad kernel revision in "refs/bisect/bad".
 
 Bisect reset
 ~~~~~~~~~~~~
 
-Oh, and then after you want to reset to the original head, do a
+To return to the original head after a bisect session, issue the
+following command:
 
 ------------------------------------------------
 $ git bisect reset
 ------------------------------------------------
 
-to get back to the original branch, instead of being on the bisection
-commit ("git bisect start" will do that for you too, actually: it will
-reset the bisection state).
+This resets the tree to the original branch instead of being on the
+bisection commit ("git bisect start" will also do that, as it resets
+the bisection state).
 
 Bisect visualize
 ~~~~~~~~~~~~~~~~
 
-During the bisection process, you can say
+To see the currently remaining suspects in 'gitk', issue the following
+command during the bisection process:
 
 ------------
 $ git bisect visualize
 ------------
 
-to see the currently remaining suspects in 'gitk'.  `visualize` is a bit
-too long to type and `view` is provided as a synonym.
+`view` may also be used as a synonym for `visualize`.
 
-If 'DISPLAY' environment variable is not set, 'git log' is used
-instead.  You can even give command line options such as `-p` and
+If the 'DISPLAY' environment variable is not set, 'git log' is used
+instead.  You can also give command line options such as `-p` and
 `--stat`.
 
 ------------
@@ -112,73 +115,94 @@
 Bisect log and bisect replay
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The good/bad input is logged, and
+After having marked revisions as good or bad, issue the following
+command to show what has been done so far:
 
 ------------
 $ git bisect log
 ------------
 
-shows what you have done so far. You can truncate its output somewhere
-and save it in a file, and run
+If you discover that you made a mistake in specifying the status of a
+revision, you can save the output of this command to a file, edit it to
+remove the incorrect entries, and then issue the following commands to
+return to a corrected state:
 
 ------------
+$ git bisect reset
 $ git bisect replay that-file
 ------------
 
-if you find later you made a mistake telling good/bad about a
-revision.
-
-Avoiding to test a commit
+Avoiding testing a commit
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If in a middle of bisect session, you know what the bisect suggested
-to try next is not a good one to test (e.g. the change the commit
+If, in the middle of a bisect session, you know that the next suggested
+revision is not a good one to test (e.g. the change the commit
 introduces is known not to work in your environment and you know it
 does not have anything to do with the bug you are chasing), you may
-want to find a near-by commit and try that instead.
+want to find a nearby commit and try that instead.
 
-It goes something like this:
+For example:
 
 ------------
-$ git bisect good/bad			# previous round was good/bad.
+$ git bisect good/bad			# previous round was good or bad.
 Bisecting: 337 revisions left to test after this
 $ git bisect visualize			# oops, that is uninteresting.
-$ git reset --hard HEAD~3		# try 3 revs before what
+$ git reset --hard HEAD~3		# try 3 revisions before what
 					# was suggested
 ------------
 
-Then compile and test the one you chose to try. After that, tell
-bisect what the result was as usual.
+Then compile and test the chosen revision, and afterwards mark
+the revision as good or bad in the usual manner.
 
 Bisect skip
 ~~~~~~~~~~~~
 
-Instead of choosing by yourself a nearby commit, you may just want git
-to do it for you using:
+Instead of choosing by yourself a nearby commit, you can ask git
+to do it for you by issuing the command:
 
 ------------
 $ git bisect skip                 # Current version cannot be tested
 ------------
 
 But computing the commit to test may be slower afterwards and git may
-eventually not be able to tell the first bad among a bad and one or
-more "skip"ped commits.
+eventually not be able to tell the first bad commit among a bad commit
+and one or more skipped commits.
+
+You can even skip a range of commits, instead of just one commit,
+using the "'<commit1>'..'<commit2>'" notation. For example:
+
+------------
+$ git bisect skip v2.5..v2.6
+------------
+
+This tells the bisect process that no commit after `v2.5`, up to and
+including `v2.6`, should be tested.
+
+Note that if you also want to skip the first commit of the range you
+would issue the command:
+
+------------
+$ git bisect skip v2.5 v2.5..v2.6
+------------
+
+This tells the bisect process that the commits between `v2.5` included
+and `v2.6` included should be skipped.
+
 
 Cutting down bisection by giving more parameters to bisect start
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-You can further cut down the number of trials if you know what part of
-the tree is involved in the problem you are tracking down, by giving
-paths parameters when you say `bisect start`, like this:
+You can further cut down the number of trials, if you know what part of
+the tree is involved in the problem you are tracking down, by specifying
+path parameters when issuing the `bisect start` command:
 
 ------------
 $ git bisect start -- arch/i386 include/asm-i386
 ------------
 
-If you know beforehand more than one good commits, you can narrow the
-bisect space down without doing the whole tree checkout every time you
-give good commits. You give the bad revision immediately after `start`
-and then you give all the good revisions you have:
+If you know beforehand more than one good commit, you can narrow the
+bisect space down by specifying all of the good commits immediately after
+the bad commit when issuing the `bisect start` command:
 
 ------------
 $ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 --
@@ -190,38 +214,38 @@
 ~~~~~~~~~~
 
 If you have a script that can tell if the current source code is good
-or bad, you can automatically bisect using:
+or bad, you can bisect by issuing the command:
 
 ------------
-$ git bisect run my_script
+$ git bisect run my_script arguments
 ------------
 
-Note that the "run" script (`my_script` in the above example) should
-exit with code 0 in case the current source code is good.  Exit with a
+Note that the script (`my_script` in the above example) should
+exit with code 0 if the current source code is good, and exit with a
 code between 1 and 127 (inclusive), except 125, if the current
 source code is bad.
 
-Any other exit code will abort the automatic bisect process. (A
-program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
-the value is chopped with "& 0377".)
+Any other exit code will abort the bisect process. It should be noted
+that a program that terminates via "exit(-1)" leaves $? = 255, (see the
+exit(3) manual page), as the value is chopped with "& 0377".
 
 The special exit code 125 should be used when the current source code
-cannot be tested. If the "run" script exits with this code, the current
-revision will be skipped, see `git bisect skip` above.
+cannot be tested. If the script exits with this code, the current
+revision will be skipped (see `git bisect skip` above).
 
-You may often find that during bisect you want to have near-constant
-tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
-"revision that does not have this commit needs this patch applied to
-work around other problem this bisection is not interested in")
-applied to the revision being tested.
+You may often find that during a bisect session you want to have
+temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a
+header file, or "revision that does not have this commit needs this
+patch applied to work around another problem this bisection is not
+interested in") applied to the revision being tested.
 
 To cope with such a situation, after the inner 'git bisect' finds the
-next revision to test, with the "run" script, you can apply that tweak
-before compiling, run the real test, and after the test decides if the
-revision (possibly with the needed tweaks) passed the test, rewind the
-tree to the pristine state.  Finally the "run" script can exit with
-the status of the real test to let the "git bisect run" command loop to
-determine the outcome.
+next revision to test, the script can apply the patch
+before compiling, run the real test, and afterwards decide if the
+revision (possibly with the needed patch) passed the test and then
+rewind the tree to the pristine state.  Finally the script should exit
+with the status of the real test to let the "git bisect run" command loop
+determine the eventual outcome of the bisect session.
 
 EXAMPLES
 --------
@@ -233,44 +257,60 @@
 $ git bisect run make                # "make" builds the app
 ------------
 
+* Automatically bisect a test failure between origin and HEAD:
++
+------------
+$ git bisect start HEAD origin --    # HEAD is bad, origin is good
+$ git bisect run make test           # "make test" builds and tests
+------------
+
 * Automatically bisect a broken test suite:
 +
 ------------
 $ cat ~/test.sh
 #!/bin/sh
-make || exit 125                   # this "skip"s broken builds
+make || exit 125                   # this skips broken builds
 make test                          # "make test" runs the test suite
 $ git bisect start v1.3 v1.1 --    # v1.3 is bad, v1.1 is good
 $ git bisect run ~/test.sh
 ------------
 +
 Here we use a "test.sh" custom script. In this script, if "make"
-fails, we "skip" the current commit.
+fails, we skip the current commit.
 +
-It's safer to use a custom script outside the repo to prevent
+It is safer to use a custom script outside the repository to prevent
 interactions between the bisect, make and test processes and the
 script.
 +
-And "make test" should "exit 0", if the test suite passes, and
-"exit 1" (for example) otherwise.
+"make test" should "exit 0", if the test suite passes, and
+"exit 1" otherwise.
 
 * Automatically bisect a broken test case:
 +
 ------------
 $ cat ~/test.sh
 #!/bin/sh
-make || exit 125                     # this "skip"s broken builds
+make || exit 125                     # this skips broken builds
 ~/check_test_case.sh                 # does the test case passes ?
 $ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
 $ git bisect run ~/test.sh
 ------------
 +
-Here "check_test_case.sh" should "exit 0", if the test case passes,
-and "exit 1" (for example) otherwise.
+Here "check_test_case.sh" should "exit 0" if the test case passes,
+and "exit 1" otherwise.
 +
-It's safer if both "test.sh" and "check_test_case.sh" scripts are
-outside the repo to prevent interactions between the bisect, make and
-test processes and the scripts.
+It is safer if both "test.sh" and "check_test_case.sh" scripts are
+outside the repository to prevent interactions between the bisect,
+make and test processes and the scripts.
+
+* Automatically bisect a broken test suite:
++
+------------
+$ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
+$ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
+------------
++
+Does the same as the previous example, but on a single line.
 
 Author
 ------
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index fba374d..8c7b7b0 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -10,7 +10,7 @@
 [verse]
 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [--incremental] [-L n,m]
             [-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
-            [<rev> | --contents <file>] [--] <file>
+	    [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
 DESCRIPTION
 -----------
@@ -18,9 +18,9 @@
 Annotates each line in the given file with information from the revision which
 last modified the line. Optionally, start annotating from the given revision.
 
-Also it can limit the range of lines annotated.
+The command can also limit the range of lines annotated.
 
-This report doesn't tell you anything about lines which have been deleted or
+The report does not tell you anything about lines which have been deleted or
 replaced; you need to use a tool such as 'git-diff' or the "pickaxe"
 interface briefly mentioned in the following paragraph.
 
@@ -48,26 +48,26 @@
 	lines between files (see `-C`) and lines moved within a
 	file (see `-M`).  The first number listed is the score.
 	This is the number of alphanumeric characters detected
-	to be moved between or within files.  This must be above
+	as having been moved between or within files.  This must be above
 	a certain threshold for 'git-blame' to consider those lines
 	of code to have been moved.
 
 -f::
 --show-name::
-	Show filename in the original commit.  By default
-	filename is shown if there is any line that came from a
-	file with different name, due to rename detection.
+	Show the filename in the original commit.  By default
+	the filename is shown if there is any line that came from a
+	file with a different name, due to rename detection.
 
 -n::
 --show-number::
-	Show line number in the original commit (Default: off).
+	Show the line number in the original commit (Default: off).
 
 -s::
-	Suppress author name and timestamp from the output.
+	Suppress the author name and timestamp from the output.
 
 -w::
-	Ignore whitespace when comparing parent's version and
-	child's to find where the lines came from.
+	Ignore whitespace when comparing the parent's version and
+	the child's to find where the lines came from.
 
 
 THE PORCELAIN FORMAT
@@ -79,17 +79,17 @@
 - 40-byte SHA-1 of the commit the line is attributed to;
 - the line number of the line in the original file;
 - the line number of the line in the final file;
-- on a line that starts a group of line from a different
+- on a line that starts a group of lines from a different
   commit than the previous one, the number of lines in this
   group.  On subsequent lines this field is absent.
 
 This header line is followed by the following information
 at least once for each commit:
 
-- author name ("author"), email ("author-mail"), time
+- the author name ("author"), email ("author-mail"), time
   ("author-time"), and timezone ("author-tz"); similarly
   for committer.
-- filename in the commit the line is attributed to.
+- the filename in the commit that the line is attributed to.
 - the first line of the commit log message ("summary").
 
 The contents of the actual line is output after the above
@@ -100,23 +100,23 @@
 SPECIFYING RANGES
 -----------------
 
-Unlike 'git-blame' and 'git-annotate' in older git, the extent
-of annotation can be limited to both line ranges and revision
+Unlike 'git-blame' and 'git-annotate' in older versions of git, the extent
+of the annotation can be limited to both line ranges and revision
 ranges.  When you are interested in finding the origin for
-ll. 40-60 for file `foo`, you can use `-L` option like these
+lines 40-60 for file `foo`, you can use the `-L` option like so
 (they mean the same thing -- both ask for 21 lines starting at
 line 40):
 
 	git blame -L 40,60 foo
 	git blame -L 40,+21 foo
 
-Also you can use regular expression to specify the line range.
+Also you can use a regular expression to specify the line range:
 
 	git blame -L '/^sub hello {/,/^}$/' foo
 
-would limit the annotation to the body of `hello` subroutine.
+which limits the annotation to the body of the `hello` subroutine.
 
-When you are not interested in changes older than the version
+When you are not interested in changes older than version
 v2.6.18, or changes older than 3 weeks, you can use revision
 range specifiers  similar to 'git-rev-list':
 
@@ -129,7 +129,7 @@
 weeks old in the above example) are blamed for that range
 boundary commit.
 
-A particularly useful way is to see if an added file have lines
+A particularly useful way is to see if an added file has lines
 created by copy-and-paste from existing files.  Sometimes this
 indicates that the developer was being sloppy and did not
 refactor the code properly.  You can first find the commit that
@@ -162,26 +162,32 @@
 +
 Line numbers count from 1.
 
-. The first time that commit shows up in the stream, it has various
+. The first time that a commit shows up in the stream, it has various
   other information about it printed out with a one-word tag at the
-  beginning of each line about that "extended commit info" (author,
-  email, committer, dates, summary etc).
+  beginning of each line describing the extra commit information (author,
+  email, committer, dates, summary, etc.).
 
-. Unlike Porcelain format, the filename information is always
+. Unlike the Porcelain format, the filename information is always
   given and terminates the entry:
 
 	"filename" <whitespace-quoted-filename-goes-here>
 +
-and thus it's really quite easy to parse for some line- and word-oriented
+and thus it is really quite easy to parse for some line- and word-oriented
 parser (which should be quite natural for most scripting languages).
 +
 [NOTE]
 For people who do parsing: to make it more robust, just ignore any
-lines in between the first and last one ("<sha1>" and "filename" lines)
-where you don't recognize the tag-words (or care about that particular
+lines between the first and last one ("<sha1>" and "filename" lines)
+where you do not recognize the tag words (or care about that particular
 one) at the beginning of the "extended information" lines. That way, if
 there is ever added information (like the commit encoding or extended
-commit commentary), a blame viewer won't ever care.
+commit commentary), a blame viewer will not care.
+
+
+MAPPING AUTHORS
+---------------
+
+include::mailmap.txt[]
 
 
 SEE ALSO
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 6103d62..cbd4275 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -18,19 +18,19 @@
 DESCRIPTION
 -----------
 
-With no arguments, existing branches are listed, the current branch will
+With no arguments, existing branches are listed and the current branch will
 be highlighted with an asterisk.  Option `-r` causes the remote-tracking
 branches to be listed, and option `-a` shows both.
 
-With `--contains`, shows only the branches that contains the named commit
-(in other words, the branches whose tip commits are descendant of the
+With `--contains`, shows only the branches that contain the named commit
+(in other words, the branches whose tip commits are descendants of the
 named commit).  With `--merged`, only branches merged into the named
 commit (i.e. the branches whose tip commits are reachable from the named
 commit) will be listed.  With `--no-merged` only branches not merged into
-the named commit will be listed.  Missing <commit> argument defaults to
-'HEAD' (i.e. the tip of the current branch).
+the named commit will be listed.  If the <commit> argument is missing it
+defaults to 'HEAD' (i.e. the tip of the current branch).
 
-In its second form, a new branch named <branchname> will be created.
+In the command's second form, a new branch named <branchname> will be created.
 It will start out with a head equal to the one given as <start-point>.
 If no <start-point> is given, the branch will be created with a head
 equal to that of the currently checked out branch.
@@ -57,9 +57,9 @@
 
 Use -r together with -d to delete remote-tracking branches. Note, that it
 only makes sense to delete remote-tracking branches if they no longer exist
-in remote repository or if 'git-fetch' was configured not to fetch
-them again. See also 'prune' subcommand of linkgit:git-remote[1] for way to
-clean up all obsolete remote-tracking branches.
+in the remote repository or if 'git-fetch' was configured not to fetch
+them again. See also the 'prune' subcommand of linkgit:git-remote[1] for a
+way to clean up all obsolete remote-tracking branches.
 
 
 OPTIONS
@@ -76,14 +76,14 @@
 	based sha1 expressions such as "<branchname>@\{yesterday}".
 
 -f::
-	Force the creation of a new branch even if it means deleting
-	a branch that already exists with the same name.
+	Reset <branchname> to <startpoint> if <branchname> exists
+	already. Without `-f` 'git-branch' refuses to change an existing branch.
 
 -m::
 	Move/rename a branch and the corresponding reflog.
 
 -M::
-	Move/rename a branch even if the new branchname already exists.
+	Move/rename a branch even if the new branch name already exists.
 
 --color::
 	Color branches to highlight current, local, and remote branches.
@@ -100,29 +100,34 @@
 
 -v::
 --verbose::
-	Show sha1 and commit subject line for each head.
+	Show sha1 and commit subject line for each head, along with
+	relationship to upstream branch (if any). If given twice, print
+	the name of the upstream branch, as well.
 
 --abbrev=<length>::
-	Alter minimum display length for sha1 in output listing,
-	default value is 7.
+	Alter the sha1's minimum display length in the output listing.
+	The default value is 7.
 
 --no-abbrev::
-	Display the full sha1s in output listing rather than abbreviating them.
+	Display the full sha1s in the output listing rather than abbreviating them.
 
 --track::
-	When creating a new branch, set up configuration so that 'git-pull'
-	will automatically retrieve data from the start point, which must be
-	a branch. Use this if you always pull from the same upstream branch
-	into the new branch, and if you don't want to use "git pull
-	<repository> <refspec>" explicitly. This behavior is the default
-	when the start point is a remote branch. Set the
-	branch.autosetupmerge configuration variable to `false` if you want
-	'git-checkout' and 'git-branch' to always behave as if '--no-track' were
-	given. Set it to `always` if you want this behavior when the
-	start-point is either a local or remote branch.
+	When creating a new branch, set up configuration to mark the
+	start-point branch as "upstream" from the new branch. This
+	configuration will tell git to show the relationship between the
+	two branches in `git status` and `git branch -v`. Furthermore,
+	it directs `git pull` without arguments to pull from the
+	upstream when the new branch is checked out.
++
+This behavior is the default when the start point is a remote branch.
+Set the branch.autosetupmerge configuration variable to `false` if you
+want `git checkout` and `git branch` to always behave as if '--no-track'
+were given. Set it to `always` if you want this behavior when the
+start-point is either a local or remote branch.
 
 --no-track::
-	Ignore the branch.autosetupmerge configuration variable.
+	Do not set up "upstream" configuration, even if the
+	branch.autosetupmerge configuration variable is true.
 
 --contains <commit>::
 	Only list branches which contain the specified commit.
@@ -149,13 +154,13 @@
 
 <newbranch>::
 	The new name for an existing branch. The same restrictions as for
-	<branchname> applies.
+	<branchname> apply.
 
 
 Examples
 --------
 
-Start development off of a known tag::
+Start development from a known tag::
 +
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
@@ -167,7 +172,7 @@
 <1> This step and the next one could be combined into a single step with
 "checkout -b my2.6.14 v2.6.14".
 
-Delete unneeded branch::
+Delete an unneeded branch::
 +
 ------------
 $ git clone git://git.kernel.org/.../git.git my.git
@@ -176,21 +181,21 @@
 $ git branch -D test                                    <2>
 ------------
 +
-<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or
-'pull' will create them again unless you configure them not to. See
-linkgit:git-fetch[1].
-<2> Delete "test" branch even if the "master" branch (or whichever branch is
-currently checked out) does not have all commits from test branch.
+<1> Delete the remote-tracking branches "todo", "html" and "man". The next
+'fetch' or 'pull' will create them again unless you configure them not to.
+See linkgit:git-fetch[1].
+<2> Delete the "test" branch even if the "master" branch (or whichever branch
+is currently checked out) does not have all commits from the test branch.
 
 
 Notes
 -----
 
-If you are creating a branch that you want to immediately checkout, it's
+If you are creating a branch that you want to checkout immediately, it is
 easier to use the git checkout command with its `-b` option to create
 a branch and check it out with a single command.
 
-The options `--contains`, `--merged` and `--no-merged` serves three related
+The options `--contains`, `--merged` and `--no-merged` serve three related
 but different purposes:
 
 - `--contains <commit>` is used to find all branches which will need
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index 1b66ab7..aee7e4a 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -19,13 +19,13 @@
 
 Some workflows require that one or more branches of development on one
 machine be replicated on another machine, but the two machines cannot
-be directly connected so the interactive git protocols (git, ssh,
-rsync, http) cannot be used.  This command provides support for
+be directly connected, and therefore the interactive git protocols (git,
+ssh, rsync, http) cannot be used.  This command provides support for
 'git-fetch' and 'git-pull' to operate by packaging objects and references
 in an archive at the originating machine, then importing those into
 another repository using 'git-fetch' and 'git-pull'
 after moving the archive by some means (i.e., by sneakernet).  As no
-direct connection between repositories exists, the user must specify a
+direct connection between the repositories exists, the user must specify a
 basis for the bundle that is held by the destination repository: the
 bundle assumes that all objects in the basis are already in the
 destination repository.
@@ -43,7 +43,7 @@
        bundle format itself as well as checking that the prerequisite
        commits exist and are fully linked in the current repository.
        'git-bundle' prints a list of missing commits, if any, and exits
-       with non-zero status.
+       with a non-zero status.
 
 list-heads <file>::
        Lists the references defined in the bundle.  If followed by a
@@ -53,14 +53,14 @@
 unbundle <file>::
        Passes the objects in the bundle to 'git-index-pack'
        for storage in the repository, then prints the names of all
-       defined references. If a reflist is given, only references
-       matching those in the given list are printed. This command is
+       defined references. If a list of references is given, only
+       references matching those in the list are printed. This command is
        really plumbing, intended to be called only by 'git-fetch'.
 
 [git-rev-list-args...]::
        A list of arguments, acceptable to 'git-rev-parse' and
-       'git-rev-list', that specify the specific objects and references
-       to transport.  For example, "master~10..master" causes the
+       'git-rev-list', that specifies the specific objects and references
+       to transport.  For example, `master\~10..master` causes the
        current master reference to be packaged along with all objects
        added since its 10th ancestor commit.  There is no explicit
        limit to the number of references and objects that may be
@@ -71,98 +71,134 @@
        A list of references used to limit the references reported as
        available. This is principally of use to 'git-fetch', which
        expects to receive only those references asked for and not
-       necessarily everything in the pack (in this case, 'git-bundle' is
-       acting like 'git-fetch-pack').
+       necessarily everything in the pack (in this case, 'git-bundle' acts
+       like 'git-fetch-pack').
 
 SPECIFYING REFERENCES
 ---------------------
 
 'git-bundle' will only package references that are shown by
 'git-show-ref': this includes heads, tags, and remote heads.  References
-such as master~1 cannot be packaged, but are perfectly suitable for
+such as `master\~1` cannot be packaged, but are perfectly suitable for
 defining the basis.  More than one reference may be packaged, and more
 than one basis can be specified.  The objects packaged are those not
 contained in the union of the given bases.  Each basis can be
-specified explicitly (e.g., ^master~10), or implicitly (e.g.,
-master~10..master, master --since=10.days.ago).
+specified explicitly (e.g. `^master\~10`), or implicitly (e.g.
+`master\~10..master`, `--since=10.days.ago master`).
 
 It is very important that the basis used be held by the destination.
-It is okay to err on the side of conservatism, causing the bundle file
-to contain objects already in the destination as these are ignored
+It is okay to err on the side of caution, causing the bundle file
+to contain objects already in the destination, as these are ignored
 when unpacking at the destination.
 
 EXAMPLE
 -------
 
-Assume two repositories exist as R1 on machine A, and R2 on machine B.
+Assume you want to transfer the history from a repository R1 on machine A
+to another repository R2 on machine B.
 For whatever reason, direct connection between A and B is not allowed,
-but we can move data from A to B via some mechanism (CD, email, etc).
-We want to update R2 with developments made on branch master in R1.
+but we can move data from A to B via some mechanism (CD, email, etc.).
+We want to update R2 with development made on the branch master in R1.
 
-To create the bundle you have to specify the basis. You have some options:
+To bootstrap the process, you can first create a bundle that does not have
+any basis. You can use a tag to remember up to what commit you last
+processed, in order to make it easy to later update the other repository
+with an incremental bundle:
 
-- Without basis.
-+
-This is useful when sending the whole history.
+----------------
+machineA$ cd R1
+machineA$ git bundle create file.bundle master
+machineA$ git tag -f lastR2bundle master
+----------------
 
-------------
-$ git bundle create mybundle master
-------------
+Then you transfer file.bundle to the target machine B. If you are creating
+the repository on machine B, then you can clone from the bundle as if it
+were a remote repository instead of creating an empty repository and then
+pulling or fetching objects from the bundle:
 
-- Using temporally tags.
-+
-We set a tag in R1 (lastR2bundle) after the previous such transport,
-and move it afterwards to help build the bundle.
+----------------
+machineB$ git clone /home/me/tmp/file.bundle R2
+----------------
 
-------------
-$ git bundle create mybundle master ^lastR2bundle
-$ git tag -f lastR2bundle master
-------------
-
-- Using a tag present in both repositories
-
-------------
-$ git bundle create mybundle master ^v1.0.0
-------------
-
-- A basis based on time.
-
-------------
-$ git bundle create mybundle master --since=10.days.ago
-------------
-
-- With a limit on the number of commits
-
-------------
-$ git bundle create mybundle master -n 10
-------------
-
-Then you move mybundle from A to B, and in R2 on B:
-
-------------
-$ git bundle verify mybundle
-$ git fetch mybundle master:localRef
-------------
-
-With something like this in the config in R2:
+This will define a remote called "origin" in the resulting repository that
+lets you fetch and pull from the bundle. The $GIT_DIR/config file in R2 will
+have an entry like this:
 
 ------------------------
-[remote "bundle"]
-    url = /home/me/tmp/file.bdl
+[remote "origin"]
+    url = /home/me/tmp/file.bundle
     fetch = refs/heads/*:refs/remotes/origin/*
 ------------------------
 
-You can first sneakernet the bundle file to ~/tmp/file.bdl and
-then these commands on machine B:
+To update the resulting mine.git repository, you can fetch or pull after
+replacing the bundle stored at /home/me/tmp/file.bundle with incremental
+updates.
 
-------------
-$ git ls-remote bundle
-$ git fetch bundle
-$ git pull bundle
-------------
+After working some more in the original repository, you can create an
+incremental bundle to update the other repository:
 
-would treat it as if it is talking with a remote side over the
-network.
+----------------
+machineA$ cd R1
+machineA$ git bundle create file.bundle lastR2bundle..master
+machineA$ git tag -f lastR2bundle master
+----------------
+
+You then transfer the bundle to the other machine to replace
+/home/me/tmp/file.bundle, and pull from it.
+
+----------------
+machineB$ cd R2
+machineB$ git pull
+----------------
+
+If you know up to what commit the intended recipient repository should
+have the necessary objects, you can use that knowledge to specify the
+basis, giving a cut-off point to limit the revisions and objects that go
+in the resulting bundle. The previous example used lastR2bundle tag
+for this purpose, but you can use any other options that you would give to
+the linkgit:git-log[1] command. Here are more examples:
+
+You can use a tag that is present in both:
+
+----------------
+$ git bundle create mybundle v1.0.0..master
+----------------
+
+You can use a basis based on time:
+
+----------------
+$ git bundle create mybundle --since=10.days master
+----------------
+
+You can use the number of commits:
+
+----------------
+$ git bundle create mybundle -10 master
+----------------
+
+You can run `git-bundle verify` to see if you can extract from a bundle
+that was created with a basis:
+
+----------------
+$ git bundle verify mybundle
+----------------
+
+This will list what commits you must have in order to extract from the
+bundle and will error out if you do not have them.
+
+A bundle from a recipient repository's point of view is just like a
+regular repository which it fetches or pulls from. You can, for example, map
+references when fetching:
+
+----------------
+$ git fetch mybundle master:localRef
+----------------
+
+You can also see what references it offers.
+
+----------------
+$ git ls-remote mybundle
+----------------
 
 Author
 ------
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 668f697..b191276 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-cat-file - Provide content or type/size information for repository objects
+git-cat-file - Provide content or type and size information for repository objects
 
 
 SYNOPSIS
@@ -14,19 +14,19 @@
 
 DESCRIPTION
 -----------
-In the first form, provides content or type of objects in the repository. The
-type is required unless '-t' or '-p' is used to find the object type, or '-s'
-is used to find the object size.
+In its first form, the command provides the content or the type of an object in
+the repository. The type is required unless '-t' or '-p' is used to find the
+object type, or '-s' is used to find the object size.
 
-In the second form, a list of object (separated by LFs) is provided on stdin,
-and the SHA1, type, and size of each object is printed on stdout.
+In the second form, a list of objects (separated by linefeeds) is provided on
+stdin, and the SHA1, type, and size of each object is printed on stdout.
 
 OPTIONS
 -------
 <object>::
 	The name of the object to show.
 	For a more complete list of ways to spell object names, see
-	"SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+	the "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
 
 -t::
 	Instead of the content, show the object type identified by
@@ -56,8 +56,8 @@
 	stdin. May not be combined with any other options or arguments.
 
 --batch-check::
-	Print the SHA1, type, and size of each object provided on stdin. May not be
-	combined with any other options or arguments.
+	Print the SHA1, type, and size of each object provided on stdin. May not
+	be combined with any other options or arguments.
 
 OUTPUT
 ------
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index 043274b..50824e3 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -8,17 +8,26 @@
 
 SYNOPSIS
 --------
+[verse]
 'git check-attr' attr... [--] pathname...
+'git check-attr' --stdin [-z] attr... < <list-of-paths>
 
 DESCRIPTION
 -----------
-For every pathname, this command will list if each attr is 'unspecified',
+For every pathname, this command will list if each attribute is 'unspecified',
 'set', or 'unset' as a gitattribute on that pathname.
 
 OPTIONS
 -------
+--stdin::
+	Read file names from stdin instead of from the command-line.
+
+-z::
+	Only meaningful with `--stdin`; paths are separated with a
+	NUL character instead of a linefeed character.
+
 \--::
-	Interpret all preceding arguments as attributes, and all following
+	Interpret all preceding arguments as attributes and all following
 	arguments as path names. If not supplied, only the first argument will
 	be treated as an attribute.
 
@@ -28,12 +37,12 @@
 The output is of the form:
 <path> COLON SP <attribute> COLON SP <info> LF
 
-Where <path> is the path of a file being queried, <attribute> is an attribute
+<path> is the path of a file being queried, <attribute> is an attribute
 being queried and <info> can be either:
 
 'unspecified';; when the attribute is not defined for the path.
-'unset';;	when the attribute is defined to false.
-'set';;		when the attribute is defined to true.
+'unset';;	when the attribute is defined as false.
+'set';;		when the attribute is defined as true.
 <value>;;	when a value has been assigned to the attribute.
 
 EXAMPLES
@@ -60,7 +69,7 @@
 org/example/MyClass.java: myAttr: set
 ---------------
 
-* Listing attribute for multiple files:
+* Listing an attribute for multiple files:
 ---------------
 $ git check-attr myAttr -- org/example/MyClass.java org/example/NoMyAttr.java
 org/example/MyClass.java: myAttr: set
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index 034223c..c1ce268 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -3,52 +3,70 @@
 
 NAME
 ----
-git-check-ref-format - Make sure ref name is well formed
+git-check-ref-format - Ensures that a reference name is well formed
 
 SYNOPSIS
 --------
+[verse]
 'git check-ref-format' <refname>
+'git check-ref-format' [--branch] <branchname-shorthand>
 
 DESCRIPTION
 -----------
-Checks if a given 'refname' is acceptable, and exits non-zero if
-it is not.
+Checks if a given 'refname' is acceptable, and exits with a non-zero
+status if it is not.
 
 A reference is used in git to specify branches and tags.  A
-branch head is stored under `$GIT_DIR/refs/heads` directory, and
-a tag is stored under `$GIT_DIR/refs/tags` directory.  git
-imposes the following rules on how refs are named:
+branch head is stored under the `$GIT_DIR/refs/heads` directory, and
+a tag is stored under the `$GIT_DIR/refs/tags` directory.  git
+imposes the following rules on how references are named:
 
-. It can include slash `/` for hierarchical (directory)
+. They can include slash `/` for hierarchical (directory)
   grouping, but no slash-separated component can begin with a
-  dot `.`;
+  dot `.`.
 
-. It cannot have two consecutive dots `..` anywhere;
+. They cannot have two consecutive dots `..` anywhere.
 
-. It cannot have ASCII control character (i.e. bytes whose
+. They cannot have ASCII control characters (i.e. bytes whose
   values are lower than \040, or \177 `DEL`), space, tilde `~`,
   caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
-  or open bracket `[` anywhere;
+  or open bracket `[` anywhere.
 
-. It cannot end with a slash `/`.
+. They cannot end with a slash `/` nor a dot `.`.
 
-These rules makes it easy for shell script based tools to parse
-refnames, pathname expansion by the shell when a refname is used
+. They cannot end with the sequence `.lock`.
+
+. They cannot contain a sequence `@{`.
+
+These rules make it easy for shell script based tools to parse
+reference names, pathname expansion by the shell when a reference name is used
 unquoted (by mistake), and also avoids ambiguities in certain
-refname expressions (see linkgit:git-rev-parse[1]).  Namely:
+reference name expressions (see linkgit:git-rev-parse[1]):
 
-. double-dot `..` are often used as in `ref1..ref2`, and in some
-  context this notation means `{caret}ref1 ref2` (i.e. not in
-  ref1 and in ref2).
+. A double-dot `..` is often used as in `ref1..ref2`, and in some
+  contexts this notation means `{caret}ref1 ref2` (i.e. not in
+  `ref1` and in `ref2`).
 
-. tilde `~` and caret `{caret}` are used to introduce postfix
+. A tilde `~` and caret `{caret}` are used to introduce the postfix
   'nth parent' and 'peel onion' operation.
 
-. colon `:` is used as in `srcref:dstref` to mean "use srcref\'s
+. A colon `:` is used as in `srcref:dstref` to mean "use srcref\'s
   value and store it in dstref" in fetch and push operations.
   It may also be used to select a specific object such as with
   'git-cat-file': "git cat-file blob v1.3.3:refs.c".
 
+. at-open-brace `@{` is used as a notation to access a reflog entry.
+
+With the `--branch` option, it expands a branch name shorthand and
+prints the name of the branch the shorthand refers to.
+
+EXAMPLE
+-------
+
+git check-ref-format --branch @{-1}::
+
+Print the name of the previous branch.
+
 
 GIT
 ---
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 19510de..ad4b31e 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -8,29 +8,39 @@
 SYNOPSIS
 --------
 [verse]
-'git checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>]
-'git checkout' [<tree-ish>] [--] <paths>...
+'git checkout' [-q] [-f] [-m] [<branch>]
+'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
 
 DESCRIPTION
 -----------
 
 When <paths> are not given, this command switches branches by
-updating the index and working tree to reflect the specified
-branch, <branch>, and updating HEAD to be <branch> or, if
-specified, <new_branch>.  Using -b will cause <new_branch> to
-be created; in this case you can use the --track or --no-track
-options, which will be passed to `git branch`.
+updating the index, working tree, and HEAD to reflect the specified
+branch.
+
+If `-b` is given, a new branch is created and checked out, as if
+linkgit:git-branch[1] were called; in this case you can
+use the --track or --no-track options, which will be passed to `git
+branch`.  As a convenience, --track without `-b` implies branch
+creation; see the description of --track below.
 
 When <paths> are given, this command does *not* switch
 branches.  It updates the named paths in the working tree from
-the index file (i.e. it runs `git checkout-index -f -u`), or
-from a named commit.  In
-this case, the `-f` and `-b` options are meaningless and giving
-either of them results in an error.  <tree-ish> argument can be
+the index file, or from a named <tree-ish> (most often a commit).  In
+this case, the `-b` and `--track` options are meaningless and giving
+either of them results in an error. The <tree-ish> argument can be
 used to specify a specific tree-ish (i.e. commit, tag or tree)
 to update the index for the given paths before updating the
 working tree.
 
+The index may contain unmerged entries after a failed merge.  By
+default, if you try to check out such an entry from the index, the
+checkout operation will fail and nothing will be checked out.
+Using -f will ignore these unmerged entries.  The contents from a
+specific side of the merge can be checked out of the index by
+using --ours or --theirs.  With -m, changes made to the working tree
+file can be discarded to recreate the original conflicted merge result.
 
 OPTIONS
 -------
@@ -38,38 +48,49 @@
 	Quiet, suppress feedback messages.
 
 -f::
-	Proceed even if the index or the working tree differs
-	from HEAD.  This is used to throw away local changes.
+	When switching branches, proceed even if the index or the
+	working tree differs from HEAD.  This is used to throw away
+	local changes.
++
+When checking out paths from the index, do not fail upon unmerged
+entries; instead, unmerged entries are ignored.
+
+--ours::
+--theirs::
+	When checking out paths from the index, check out stage #2
+	('ours') or #3 ('theirs') for unmerged paths.
 
 -b::
 	Create a new branch named <new_branch> and start it at
-	<branch>.  The new branch name must pass all checks defined
-	by linkgit:git-check-ref-format[1].  Some of these checks
-	may restrict the characters allowed in a branch name.
+	<start_point>; see linkgit:git-branch[1] for details.
 
 -t::
 --track::
-	When creating a new branch, set up configuration so that 'git-pull'
-	will automatically retrieve data from the start point, which must be
-	a branch. Use this if you always pull from the same upstream branch
-	into the new branch, and if you don't want to use "git pull
-	<repository> <refspec>" explicitly. This behavior is the default
-	when the start point is a remote branch. Set the
-	branch.autosetupmerge configuration variable to `false` if you want
-	'git-checkout' and 'git-branch' to always behave as if '--no-track' were
-	given. Set it to `always` if you want this behavior when the
-	start-point is either a local or remote branch.
+	When creating a new branch, set up "upstream" configuration. See
+	"--track" in linkgit:git-branch[1] for details.
++
+If no '-b' option is given, the name of the new branch will be
+derived from the remote branch.  If "remotes/" or "refs/remotes/"
+is prefixed it is stripped away, and then the part up to the
+next slash (which would be the nickname of the remote) is removed.
+This would tell us to use "hack" as the local branch when branching
+off of "origin/hack" (or "remotes/origin/hack", or even
+"refs/remotes/origin/hack").  If the given name has no slash, or the above
+guessing results in an empty name, the guessing is aborted.  You can
+explicitly give a name with '-b' in such a case.
 
 --no-track::
-	Ignore the branch.autosetupmerge configuration variable.
+	Do not set up "upstream" configuration, even if the
+	branch.autosetupmerge configuration variable is true.
 
 -l::
-	Create the new branch's reflog.  This activates recording of
-	all changes made to the branch ref, enabling use of date
-	based sha1 expressions such as "<branchname>@\{yesterday}".
+	Create the new branch's reflog; see linkgit:git-branch[1] for
+	details.
 
 -m::
-	If you have local modifications to one or more files that
+--merge::
+	When switching branches,
+	if you have local modifications to one or more files that
 	are different between the current branch and the branch to
 	which you are switching, the command refuses to switch
 	branches in order to preserve your modifications in context.
@@ -81,20 +102,39 @@
 paths are left unmerged, and you need to resolve the conflicts
 and mark the resolved paths with `git add` (or `git rm` if the merge
 should result in deletion of the path).
++
+When checking out paths from the index, this option lets you recreate
+the conflicted merge in the specified paths.
+
+--conflict=<style>::
+	The same as --merge option above, but changes the way the
+	conflicting hunks are presented, overriding the
+	merge.conflictstyle configuration variable.  Possible values are
+	"merge" (default) and "diff3" (in addition to what is shown by
+	"merge" style, shows the original contents).
+
+<branch>::
+	Branch to checkout; if it refers to a branch (i.e., a name that,
+	when prepended with "refs/heads/", is a valid ref), then that
+	branch is checked out. Otherwise, if it refers to a valid
+	commit, your HEAD becomes "detached" and you are no longer on
+	any branch (see below for details).
++
+As a special case, the `"@\{-N\}"` syntax for the N-th last branch
+checks out the branch (instead of detaching).  You may also specify
+`-` which is synonymous with `"@\{-1\}"`.
 
 <new_branch>::
 	Name for the new branch.
 
+<start_point>::
+	The name of a commit at which to start the new branch; see
+	linkgit:git-branch[1] for details. Defaults to HEAD.
+
 <tree-ish>::
 	Tree to checkout from (when paths are given). If not specified,
 	the index will be used.
 
-<branch>::
-	Branch to checkout (when no paths are given); may be any object
-	ID that resolves to a commit.  Defaults to HEAD.
-+
-When this parameter names a non-branch (but still a valid commit object),
-your HEAD becomes 'detached'.
 
 
 Detached HEAD
@@ -110,12 +150,12 @@
 ------------
 
 Earlier versions of git did not allow this and asked you to
-create a temporary branch using `-b` option, but starting from
+create a temporary branch using the `-b` option, but starting from
 version 1.5.0, the above command 'detaches' your HEAD from the
-current branch and directly point at the commit named by the tag
-(`v2.6.18` in the above example).
+current branch and directly points at the commit named by the tag
+(`v2.6.18` in the example above).
 
-You can use usual git commands while in this state.  You can use
+You can use all git commands while in this state.  You can use
 `git reset --hard $othercommit` to further move around, for
 example.  You can make changes and create a new commit on top of
 a detached HEAD.  You can even create a merge by using `git
@@ -160,7 +200,7 @@
 $ git checkout -- hello.c
 ------------
 
-. After working in a wrong branch, switching to the correct
+. After working in the wrong branch, switching to the correct
 branch would be done using:
 +
 ------------
@@ -168,7 +208,7 @@
 ------------
 +
 However, your "wrong" branch and correct "mytopic" branch may
-differ in files that you have locally modified, in which case,
+differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
 ------------
@@ -194,7 +234,6 @@
 ------------
 $ git checkout -m mytopic
 Auto-merging frotz
-merge: warning: conflicts during merge
 ERROR: Merge conflict in frotz
 fatal: merge program failed
 ------------
diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt
index 74d14c4..7deefda 100644
--- a/Documentation/git-cherry.txt
+++ b/Documentation/git-cherry.txt
@@ -7,7 +7,7 @@
 
 SYNOPSIS
 --------
-'git cherry' [-v] <upstream> [<head>] [<limit>]
+'git cherry' [-v] [<upstream> [<head> [<limit>]]]
 
 DESCRIPTION
 -----------
@@ -51,6 +51,7 @@
 
 <upstream>::
 	Upstream branch to compare against.
+	Defaults to the first tracked remote branch, if available.
 
 <head>::
 	Working branch; defaults to HEAD.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 307f252..4072f40 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -90,6 +90,11 @@
 	Operate quietly.  This flag is also passed to the `rsync'
 	command when given.
 
+--verbose::
+-v::
+	Display the progressbar, even in case the standard output is not
+	a terminal.
+
 --no-checkout::
 -n::
 	No checkout of HEAD is performed after the clone is complete.
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 5cce3a3..b5d81be 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -29,7 +29,8 @@
 
 3. by listing files as arguments to the 'commit' command, in which
    case the commit will ignore changes staged in the index, and instead
-   record the current content of the listed files;
+   record the current content of the listed files (which must already
+   be known to git);
 
 4. by using the -a switch with the 'commit' command to automatically
    "add" changes from all known files (i.e. all files that are already
@@ -75,8 +76,10 @@
 	read the message from the standard input.
 
 --author=<author>::
-	Override the author name used in the commit.  Use
-	`A U Thor <author@example.com>` format.
+	Override the author name used in the commit.  You can use the
+	standard `A U Thor <author@example.com>` format.  Otherwise,
+	an existing commit that matches the given string and its author
+	name is used.
 
 -m <msg>::
 --message=<msg>::
@@ -143,6 +146,10 @@
 ------
 but can be used to amend a merge commit.
 --
++
+You should understand the implications of rewriting history if you
+amend a commit that has already been published.  (See the "RECOVERING
+FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
 
 -i::
 --include::
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 19a8917..7131ee3 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -11,7 +11,7 @@
 [verse]
 'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
 'git config' [<file-option>] [type] --add name value
-'git config' [<file-option>] [type] --replace-all name [value [value_regex]]
+'git config' [<file-option>] [type] --replace-all name value [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
@@ -22,6 +22,7 @@
 'git config' [<file-option>] [-z|--null] -l | --list
 'git config' [<file-option>] --get-color name [default]
 'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
+'git config' [<file-option>] -e | --edit
 
 DESCRIPTION
 -----------
@@ -130,6 +131,10 @@
 	in the config file will cause the value to be multiplied
 	by 1024, 1048576, or 1073741824 prior to output.
 
+--bool-or-int::
+	'git-config' will ensure that the output matches the format of
+	either --bool or --int, as described above.
+
 -z::
 --null::
 	For all options that output values and/or keys, always
@@ -157,6 +162,11 @@
 	output.  The optional `default` parameter is used instead, if
 	there is no color configured for `name`.
 
+-e::
+--edit::
+	Opens an editor to modify the specified config file; either
+	'--system', '--global', or repository (default).
+
 [[FILES]]
 FILES
 -----
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index 75a8da1..6bc1c21 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -21,8 +21,9 @@
 --verbose::
 	In addition to the number of loose objects and disk
 	space consumed, it reports the number of in-pack
-	objects, number of packs, and number of objects that can be
-	removed by running `git prune-packed`.
+	objects, number of packs, disk space consumed by those packs,
+	and number of objects that can be removed by running
+	`git prune-packed`.
 
 
 Author
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index b7a8c10..d7bab13 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -24,6 +24,9 @@
 Splitting the CVS log into patch sets is done by 'cvsps'.
 At least version 2.1 is required.
 
+*WARNING:* for certain situations the import leads to incorrect results.
+Please see the section <<issues,ISSUES>> for further reference.
+
 You should *never* do any work of your own on the branches that are
 created by 'git-cvsimport'.  By default initial import will create and populate a
 "master" branch from the CVS repository's main branch which you're free
@@ -62,7 +65,7 @@
 -r <remote>::
 	The git remote to import this CVS repository into.
 	Moves all CVS branches into remotes/<remote>/<branch>
-	akin to the 'git-clone' "--use-separate-remote" option.
+	akin to the way 'git-clone' uses 'origin' by default.
 
 -o <branch-for-HEAD>::
 	When no remote is specified (via -r) the 'HEAD' branch
@@ -164,6 +167,39 @@
 Otherwise, success is indicated the Unix way, i.e. by simply exiting with
 a zero exit status.
 
+[[issues]]
+ISSUES
+------
+Problems related to timestamps:
+
+ * If timestamps of commits in the cvs repository are not stable enough
+   to be used for ordering commits changes may show up in the wrong
+   order.
+ * If any files were ever "cvs import"ed more than once (e.g., import of
+   more than one vendor release) the HEAD contains the wrong content.
+ * If the timestamp order of different files cross the revision order
+   within the commit matching time window the order of commits may be
+   wrong.
+
+Problems related to branches:
+
+ * Branches on which no commits have been made are not imported.
+ * All files from the branching point are added to a branch even if
+   never added in cvs.
+ * This applies to files added to the source branch *after* a daughter
+   branch was created: if previously no commit was made on the daughter
+   branch they will erroneously be added to the daughter branch in git.
+
+Problems related to tags:
+
+* Multiple tags on the same revision are not imported.
+
+If you suspect that any of these issues may apply to the repository you
+want to import consider using these alternative tools which proved to be
+more stable in practise:
+
+* cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org`
+* parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs`
 
 Author
 ------
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 4ba4b75..a85121c 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -9,8 +9,9 @@
 --------
 [verse]
 'git daemon' [--verbose] [--syslog] [--export-all]
-	     [--timeout=n] [--init-timeout=n] [--strict-paths]
-	     [--base-path=path] [--user-path | --user-path=path]
+	     [--timeout=n] [--init-timeout=n] [--max-connections=n]
+	     [--strict-paths] [--base-path=path] [--base-path-relaxed]
+	     [--user-path | --user-path=path]
 	     [--interpolated-path=pathtemplate]
 	     [--reuseaddr] [--detach] [--pid-file=file]
 	     [--enable=service] [--disable=service]
@@ -47,7 +48,7 @@
 	'git-daemon' will refuse to start when this option is enabled and no
 	whitelist is specified.
 
---base-path::
+--base-path=path::
 	Remap all the path requests as relative to the given path.
 	This is sort of "GIT root" - if you run 'git-daemon' with
 	'--base-path=/srv/git' on example.com, then if you later try to pull
@@ -80,8 +81,8 @@
 	Incompatible with --port, --listen, --user and --group options.
 
 --listen=host_or_ipaddr::
-	Listen on an a specific IP address or hostname.  IP addresses can
-	be either an IPv4 address or an IPV6 address if supported.  If IPv6
+	Listen on a specific IP address or hostname.  IP addresses can
+	be either an IPv4 address or an IPv6 address if supported.  If IPv6
 	is not supported, then --listen=hostname is also not supported and
 	--listen must be given an IPv4 address.
 	Incompatible with '--inetd' option.
@@ -89,15 +90,19 @@
 --port=n::
 	Listen on an alternative port.  Incompatible with '--inetd' option.
 
---init-timeout::
+--init-timeout=n::
 	Timeout between the moment the connection is established and the
 	client request is received (typically a rather low value, since
 	that should be basically immediate).
 
---timeout::
+--timeout=n::
 	Timeout for specific client sub-requests. This includes the time
-	it takes for the server to process the sub-request and time spent
-	waiting for next client's request.
+	it takes for the server to process the sub-request and the time spent
+	waiting for the next client's request.
+
+--max-connections=n::
+	Maximum number of concurrent clients, defaults to 32.  Set it to
+	zero for no limit.
 
 --syslog::
 	Log to syslog instead of stderr. Note that this option does not imply
@@ -105,9 +110,9 @@
 
 --user-path::
 --user-path=path::
-	Allow ~user notation to be used in requests.  When
+	Allow {tilde}user notation to be used in requests.  When
 	specified with no parameter, requests to
-	git://host/~alice/foo is taken as a request to access
+	git://host/{tilde}alice/foo is taken as a request to access
 	'foo' repository in the home directory of user `alice`.
 	If `--user-path=path` is specified, the same request is
 	taken as a request to access `path/foo` repository in
@@ -145,7 +150,7 @@
 	Enable/disable the service site-wide per default.  Note
 	that a service disabled site-wide can still be enabled
 	per repository if it is marked overridable and the
-	repository enables the service with an configuration
+	repository enables the service with a configuration
 	item.
 
 --allow-override=service::
@@ -265,6 +270,15 @@
 ----------------------------------------------------------------
 
 
+ENVIRONMENT
+-----------
+'git-daemon' will set REMOTE_ADDR to the IP address of the client
+that connected to it, if the IP address is available. REMOTE_ADDR will
+be available in the environment of hooks called when
+services are performed.
+
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 59a6fd1..b231dbb 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -18,6 +18,9 @@
 additional commits on top of the tagged object and the
 abbreviated object name of the most recent commit.
 
+By default (without --all or --tags) `git describe` only shows
+annotated tags.  For more information about creating annotated tags
+see the -a and -s options to linkgit:git-tag[1].
 
 OPTIONS
 -------
@@ -26,11 +29,13 @@
 
 --all::
 	Instead of using only the annotated tags, use any ref
-	found in `.git/refs/`.
+	found in `.git/refs/`.  This option enables matching
+	any known branch, remote branch, or lightweight tag.
 
 --tags::
 	Instead of using only the annotated tags, use any tag
-	found in `.git/refs/tags`.
+	found in `.git/refs/tags`.  This option enables matching
+	a lightweight (non-annotated) tag.
 
 --contains::
 	Instead of finding the tag that predates the commit, find
@@ -82,7 +87,7 @@
 	v1.0.4-14-g2414721
 
 i.e. the current head of my "parent" branch is based on v1.0.4,
-but since it has a handful commits on top of that,
+but since it has a few commits on top of that,
 describe has added the number of additional commits ("14") and
 an abbreviated object name for the commit itself ("2414721")
 at the end.
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 4e83067..23b7abd 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -49,13 +49,22 @@
 --stdin::
 	When '--stdin' is specified, the command does not take
 	<tree-ish> arguments from the command line.  Instead, it
-	reads either one <commit> or a list of <commit>
-	separated with a single space from its standard input.
+	reads lines containing either two <tree>, one <commit>, or a
+	list of <commit> from its standard input.  (Use a single space
+	as separator.)
 +
-When a single commit is given on one line of such input, it compares
-the commit with its parents.  The following flags further affects its
-behavior.  The remaining commits, when given, are used as if they are
+When two trees are given, it compares the first tree with the second.
+When a single commit is given, it compares the commit with its
+parents.  The remaining commits, when given, are used as if they are
 parents of the first commit.
++
+When comparing two trees, the ID of both trees (separated by a space
+and terminated by a newline) is printed before the difference.  When
+comparing commits, the ID of the first (or only) commit, followed by a
+newline, is printed.
++
+The following flags further affect the behavior when comparing
+commits (but not trees).
 
 -m::
 	By default, 'git-diff-tree --stdin' does not show
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index c53eba5..a2f192f 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -33,6 +33,7 @@
 	commit relative to the named <commit>.  Typically you
 	would want comparison with the latest commit, so if you
 	do not give <commit>, it defaults to HEAD.
+	--staged is a synonym of --cached.
 
 'git diff' [--options] <commit> [--] [<path>...]::
 
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
new file mode 100644
index 0000000..15b247b
--- /dev/null
+++ b/Documentation/git-difftool.txt
@@ -0,0 +1,105 @@
+git-difftool(1)
+===============
+
+NAME
+----
+git-difftool - Show changes using common diff tools
+
+SYNOPSIS
+--------
+'git difftool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<'git diff' options>]
+
+DESCRIPTION
+-----------
+'git-difftool' is a git command that allows you to compare and edit files
+between revisions using common diff tools.  'git difftool' is a frontend
+to 'git-diff' and accepts the same options and arguments.
+
+OPTIONS
+-------
+-y::
+--no-prompt::
+	Do not prompt before launching a diff tool.
+
+--prompt::
+	Prompt before each invocation of the diff tool.
+	This is the default behaviour; the option is provided to
+	override any configuration settings.
+
+-t <tool>::
+--tool=<tool>::
+	Use the diff tool specified by <tool>.
+	Valid merge tools are:
+	kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
+	ecmerge, diffuse and opendiff
++
+If a diff tool is not specified, 'git-difftool'
+will use the configuration variable `diff.tool`.  If the
+configuration variable `diff.tool` is not set, 'git-difftool'
+will pick a suitable default.
++
+You can explicitly provide a full path to the tool by setting the
+configuration variable `difftool.<tool>.path`. For example, you
+can configure the absolute path to kdiff3 by setting
+`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
+tool is available in PATH.
++
+Instead of running one of the known diff tools,
+'git-difftool' can be customized to run an alternative program
+by specifying the command line to invoke in a configuration
+variable `difftool.<tool>.cmd`.
++
+When 'git-difftool' is invoked with this tool (either through the
+`-t` or `--tool` option or the `diff.tool` configuration variable)
+the configured command line will be invoked with the following
+variables available: `$LOCAL` is set to the name of the temporary
+file containing the contents of the diff pre-image and `$REMOTE`
+is set to the name of the temporary file containing the contents
+of the diff post-image.  `$BASE` is provided for compatibility
+with custom merge tool commands and has the same value as `$LOCAL`.
+
+See linkgit:git-diff[1] for the full list of supported options.
+
+CONFIG VARIABLES
+----------------
+'git-difftool' falls back to 'git-mergetool' config variables when the
+difftool equivalents have not been defined.
+
+diff.tool::
+	The default diff tool to use.
+
+difftool.<tool>.path::
+	Override the path for the given tool.  This is useful in case
+	your tool is not in the PATH.
+
+difftool.<tool>.cmd::
+	Specify the command to invoke the specified diff tool.
++
+See the `--tool=<tool>` option above for more details.
+
+difftool.prompt::
+	Prompt before each invocation of the diff tool.
+
+SEE ALSO
+--------
+linkgit:git-diff[1]::
+	 Show changes between commits, commit and working tree, etc
+
+linkgit:git-mergetool[1]::
+	Run merge conflict resolution tools to resolve merge conflicts
+
+linkgit:git-config[1]::
+	 Get and set repository or global options
+
+
+AUTHOR
+------
+Written by David Aguilar <davvid@gmail.com>.
+
+Documentation
+--------------
+Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index 539decb..0c9eb56 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -15,7 +15,7 @@
 This program dumps the given revisions in a form suitable to be piped
 into 'git-fast-import'.
 
-You can use it as a human readable bundle replacement (see
+You can use it as a human-readable bundle replacement (see
 linkgit:git-bundle[1]), or as a kind of an interactive
 'git-filter-branch'.
 
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index b0e710d..ab527b5 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -31,12 +31,17 @@
 useful in the future for compensating for some git bugs or such,
 therefore such a usage is permitted.
 
+*NOTE*: This command honors `.git/info/grafts`. If you have any grafts
+defined, running this command will make them permanent.
+
 *WARNING*! The rewritten history will have different object names for all
 the objects and will not converge with the original branch.  You will not
 be able to easily push and distribute the rewritten branch on top of the
 original branch.  Please do not use this command if you do not know the
 full implications, and avoid using it anyway, if a simple single commit
-would suffice to fix your problem.
+would suffice to fix your problem.  (See the "RECOVERING FROM UPSTREAM
+REBASE" section in linkgit:git-rebase[1] for further information about
+rewriting published history.)
 
 Always verify that the rewritten version is correct: The original refs,
 if different from the rewritten ones, will be stored in the namespace
@@ -89,7 +94,9 @@
 --index-filter <command>::
 	This is the filter for rewriting the index.  It is similar to the
 	tree filter but does not check out the tree, which makes it much
-	faster.  For hairy cases, see linkgit:git-update-index[1].
+	faster.  Frequently used with `git rm \--cached
+	\--ignore-unmatch ...`, see EXAMPLES below.  For hairy
+	cases, see linkgit:git-update-index[1].
 
 --parent-filter <command>::
 	This is the filter for rewriting the commit's parent list.
@@ -120,6 +127,10 @@
 convenience functions, too.  For example, calling 'skip_commit "$@"'
 will leave out the current commit (but not its changes! If you want
 that, use 'git-rebase' instead).
++
+You can also use the 'git_commit_non_empty_tree "$@"' instead of
+'git commit-tree "$@"' if you don't wish to keep commits with a single parent
+and that makes no change to the tree.
 
 --tag-name-filter <command>::
 	This is the filter for rewriting tag names. When passed,
@@ -149,6 +160,16 @@
 	The result will contain that directory (and only that) as its
 	project root.
 
+--prune-empty::
+	Some kind of filters will generate empty commits, that left the tree
+	untouched.  This switch allow git-filter-branch to ignore such
+	commits.  Though, this switch only applies for commits that have one
+	and only one parent, it will hence keep merges points. Also, this
+	option is not compatible with the use of '--commit-filter'. Though you
+	just need to use the function 'git_commit_non_empty_tree "$@"' instead
+	of the 'git commit-tree "$@"' idiom in your commit filter to make that
+	happen.
+
 --original <namespace>::
 	Use this option to set the namespace where the original commits
 	will be stored. The default value is 'refs/original'.
@@ -188,10 +209,14 @@
 a simple `rm filename` will fail for that tree and commit.
 Thus you may instead want to use `rm -f filename` as the script.
 
-A significantly faster version:
+Using `\--index-filter` with 'git-rm' yields a significantly faster
+version.  Like with using `rm filename`, `git rm --cached filename`
+will fail if the file is absent from the tree of a commit.  If you
+want to "completely forget" a file, it does not matter when it entered
+history, so we also add `\--ignore-unmatch`:
 
 --------------------------------------------------------------------------
-git filter-branch --index-filter 'git rm --cached filename' HEAD
+git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
 --------------------------------------------------------------------------
 
 Now, you will get the rewritten history saved in HEAD.
@@ -318,6 +343,47 @@
 ---------------------------------------------------------------
 
 
+
+Checklist for Shrinking a Repository
+------------------------------------
+
+git-filter-branch is often used to get rid of a subset of files,
+usually with some combination of `\--index-filter` and
+`\--subdirectory-filter`.  People expect the resulting repository to
+be smaller than the original, but you need a few more steps to
+actually make it smaller, because git tries hard not to lose your
+objects until you tell it to.  First make sure that:
+
+* You really removed all variants of a filename, if a blob was moved
+  over its lifetime.  `git log \--name-only \--follow \--all \--
+  filename` can help you find renames.
+
+* You really filtered all refs: use `\--tag-name-filter cat \--
+  \--all` when calling git-filter-branch.
+
+Then there are two ways to get a smaller repository.  A safer way is
+to clone, that keeps your original intact.
+
+* Clone it with `git clone +++file:///path/to/repo+++`.  The clone
+  will not have the removed objects.  See linkgit:git-clone[1].  (Note
+  that cloning with a plain path just hardlinks everything!)
+
+If you really don't want to clone it, for whatever reasons, check the
+following points instead (in this order).  This is a very destructive
+approach, so *make a backup* or go back to cloning it.  You have been
+warned.
+
+* Remove the original refs backed up by git-filter-branch: say `git
+  for-each-ref \--format="%(refname)" refs/original/ | xargs -n 1 git
+  update-ref -d`.
+
+* Expire all reflogs with `git reflog expire \--expire=now \--all`.
+
+* Garbage collect all unreferenced objects with `git gc \--prune=now`
+  (or if your git-gc is not new enough to support arguments to
+  `\--prune`, use `git repack -ad; git prune` instead).
+
+
 Author
 ------
 Written by Petr "Pasky" Baudis <pasky@suse.cz>,
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index ebd7c5f..8dc873f 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -74,6 +74,9 @@
 
 refname::
 	The name of the ref (the part after $GIT_DIR/).
+	For a non-ambiguous short name of the ref append `:short`.
+	The option core.warnAmbiguousRefs is used to select the strict
+	abbreviation mode.
 
 objecttype::
 	The type of the object (`blob`, `tree`, `commit`, `tag`).
@@ -84,6 +87,11 @@
 objectname::
 	The object name (aka SHA-1).
 
+upstream::
+	The name of a local ref which can be considered ``upstream''
+	from the displayed ref. Respects `:short` in the same way as
+	`refname` above.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 7426109..eb2fbcf 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -10,7 +10,8 @@
 --------
 [verse]
 'git format-patch' [-k] [-o <dir> | --stdout] [--thread]
-		   [--attach[=<boundary>] | --inline[=<boundary>]]
+		   [--attach[=<boundary>] | --inline[=<boundary>] |
+		     [--no-attach]]
 		   [-s | --signoff] [<common diff options>]
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
@@ -39,15 +40,11 @@
    REVISIONS" section in linkgit:git-rev-parse[1]) means the
    commits in the specified range.
 
-A single commit, when interpreted as a <revision range>
-expression, means "everything that leads to that commit", but
-if you write 'git format-patch <commit>', the previous rule
-applies to that command line and you do not get "everything
-since the beginning of the time".  If you want to format
-everything since project inception to one commit, say "git
-format-patch \--root <commit>" to make it clear that it is the
-latter case.  If you want to format a single commit, you can do
-this with "git format-patch -1 <commit>".
+The first rule takes precedence in the case of a single <commit>.  To
+apply the second rule, i.e., format everything since the beginning of
+history up until <commit>, use the '\--root' option: "git format-patch
+\--root <commit>".  If you want to format only <commit> itself, you
+can do this with "git format-patch -1 <commit>".
 
 By default, each output file is numbered sequentially from 1, and uses the
 first line of the commit message (massaged for pathname safety) as
@@ -59,8 +56,10 @@
 If -o is specified, output files are created in <dir>.  Otherwise
 they are created in the current working directory.
 
-If -n is specified, instead of "[PATCH] Subject", the first line
-is formatted as "[PATCH n/m] Subject".
+By default, the subject of a single patch is "[PATCH] First Line" and
+the subject when multiple patches are output is "[PATCH n/m] First
+Line". To force 1/1 to be added for a single patch, use -n.  To omit
+patch numbers from the subject, use -N
 
 If given --thread, 'git-format-patch' will generate In-Reply-To and
 References headers to make the second and subsequent patch mails appear
@@ -82,7 +81,7 @@
 
 -n::
 --numbered::
-	Name output in '[PATCH n/m]' format.
+	Name output in '[PATCH n/m]' format, even with a single patch.
 
 -N::
 --no-numbered::
@@ -94,7 +93,6 @@
 --numbered-files::
 	Output file names will be a simple number sequence
 	without the default first line of the commit appended.
-	Mutually exclusive with the --stdout option.
 
 -k::
 --keep-subject::
@@ -115,15 +113,27 @@
 	which is the commit message and the patch itself in the
 	second part, with "Content-Disposition: attachment".
 
+--no-attach::
+	Disable the creation of an attachment, overriding the
+	configuration setting.
+
 --inline[=<boundary>]::
 	Create multipart/mixed attachment, the first part of
 	which is the commit message and the patch itself in the
 	second part, with "Content-Disposition: inline".
 
---thread::
+--thread[=<style>]::
 	Add In-Reply-To and References headers to make the second and
 	subsequent mails appear as replies to the first.  Also generates
 	the Message-Id header to reference.
++
+The optional <style> argument can be either `shallow` or `deep`.
+'Shallow' threading makes every mail a reply to the head of the
+series, where the head is chosen from the cover letter, the
+`\--in-reply-to`, and the first patch mail, in this order.  'Deep'
+threading makes every mail a reply to the previous one.  If not
+specified, defaults to the 'format.thread' configuration, or `shallow`
+if that is not set.
 
 --in-reply-to=Message-Id::
 	Make the first mail (or all the mails with --no-thread) appear as a
@@ -147,6 +157,11 @@
 	Add a "Cc:" header to the email headers. This is in addition
 	to any configured headers, and may be used multiple times.
 
+--add-header=<header>::
+	Add an arbitrary header to the email headers.  This is in addition
+	to any configured headers, and may be used multiple times.
+	For example, --add-header="Organization: git-foo"
+
 --cover-letter::
 	In addition to the patches, generate a cover letter file
 	containing the shortlog and the overall diffstat.  You can
@@ -168,11 +183,19 @@
 	applied.  By default the contents of changes in those files are
 	encoded in the patch.
 
+--root::
+	Treat the revision argument as a <revision range>, even if it
+	is just a single commit (that would normally be treated as a
+	<since>).  Note that root commits included in the specified
+	range are always formatted as creation patches, independently
+	of this flag.
+
 CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message
 in the repository configuration, new defaults for the subject prefix
-and file suffix, and number patches when outputting more than one.
+and file suffix, control attachements, and number patches when outputting
+more than one.
 
 ------------
 [format]
@@ -181,6 +204,8 @@
 	suffix = .txt
 	numbered = auto
 	cc = <email>
+	attach [ = mime-boundary-string ]
+	signoff = true
 ------------
 
 
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 7086eea..b292e98 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git gc' [--aggressive] [--auto] [--quiet]
+'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune]
 
 DESCRIPTION
 -----------
@@ -59,6 +59,14 @@
 'git-repack'. Setting `gc.autopacklimit` to 0 disables
 automatic consolidation of packs.
 
+--prune=<date>::
+	Prune loose objects older than date (default is 2 weeks ago,
+	overrideable by the config variable `gc.pruneExpire`).  This
+	option is on by default.
+
+--no-prune::
+	Do not prune any loose objects.
+
 --quiet::
 	Suppress all progress reports.
 
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index fa4d133..fccb82d 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -15,7 +15,9 @@
 	   [-E | --extended-regexp] [-G | --basic-regexp]
 	   [-F | --fixed-strings] [-n]
 	   [-l | --files-with-matches] [-L | --files-without-match]
+	   [-z | --null]
 	   [-c | --count] [--all-match]
+	   [--color | --no-color]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
 	   [-f <file>] [-e] <pattern>
 	   [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
@@ -94,11 +96,23 @@
 	For better compatibility with 'git-diff', --name-only is a
 	synonym for --files-with-matches.
 
+-z::
+--null::
+	Output \0 instead of the character that normally follows a
+	file name.
+
 -c::
 --count::
 	Instead of showing every matched line, show the number of
 	lines that match.
 
+--color::
+	Show colored matches.
+
+--no-color::
+	Turn off match highlighting, even when the configuration file
+	gives the default to color output.
+
 -[ABC] <context>::
 	Show `context` trailing (`A` -- after), or leading (`B`
 	-- before), or both (`C` -- context) lines, and place a
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
index 0e650f4..d0bc98b 100644
--- a/Documentation/git-gui.txt
+++ b/Documentation/git-gui.txt
@@ -65,9 +65,28 @@
 	example the file is read from the object database and not
 	the working directory.
 
+git gui blame --line=100 Makefile::
+
+	Loads annotations as described above and automatically
+	scrolls the view to center on line '100'.
+
 git gui citool::
 
 	Make one commit and return to the shell when it is complete.
+	This command returns a non-zero exit code if the window was
+	closed in any way other than by making a commit.
+
+git gui citool --amend::
+
+	Automatically enter the 'Amend Last Commit' mode of
+	the interface.
+
+git gui citool --nocommit::
+
+	Behave as normal citool, but instead of making a commit
+	simply terminate with a zero exit code. It still checks
+	that the index does not contain any unmerged entries, so
+	you can use it as a GUI version of linkgit:git-mergetool[1]
 
 git citool::
 
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index ac928e1..0af40cf 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -8,7 +8,9 @@
 
 SYNOPSIS
 --------
-'git hash-object' [-t <type>] [-w] [--stdin | --stdin-paths] [--] <file>...
+[verse]
+'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
+'git hash-object' [-t <type>] [-w] --stdin-paths < <list-of-paths>
 
 DESCRIPTION
 -----------
@@ -35,6 +37,22 @@
 --stdin-paths::
 	Read file names from stdin instead of from the command-line.
 
+--path::
+	Hash object as it were located at the given path. The location of
+	file does not directly influence on the hash value, but path is
+	used to determine what git filters should be applied to the object
+	before it can be placed to the object database, and, as result of
+	applying filters, the actual blob put into the object database may
+	differ from the given file. This option is mainly useful for hashing
+	temporary files located outside of the working directory or files
+	read from stdin.
+
+--no-filters::
+	Hash the contents as is, ignoring any input filter that would
+	have been chosen by the attributes mechanism, including crlf
+	conversion. If the file is read from standard input then this
+	is always implied, unless the --path option is given.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index f414583..d9b9c34 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -112,7 +112,9 @@
 will try to use konqueror first. But this may fail (for example if
 DISPLAY is not set) and in that case emacs' woman mode will be tried.
 
-If everything fails the 'man' program will be tried anyway.
+If everything fails, or if no viewer is configured, the viewer specified
+in the GIT_MAN_VIEWER environment variable will be tried.  If that
+fails too, the 'man' program will be tried anyway.
 
 man.<tool>.path
 ~~~~~~~~~~~~~~~
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index b3d8da3..d016daf 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-imap-send - Dump a mailbox from stdin into an imap folder
+git-imap-send - Send a collection of patches from stdin to an IMAP folder
 
 
 SYNOPSIS
@@ -13,9 +13,9 @@
 
 DESCRIPTION
 -----------
-This command uploads a mailbox generated with git-format-patch
-into an imap drafts folder.  This allows patches to be sent as
-other email is sent with mail clients that cannot read mailbox
+This command uploads a mailbox generated with 'git-format-patch'
+into an IMAP drafts folder.  This allows patches to be sent as
+other email is when using mail clients that cannot read mailbox
 files directly.
 
 Typical usage is something like:
@@ -26,23 +26,98 @@
 CONFIGURATION
 -------------
 
-'git-imap-send' requires the following values in the repository
-configuration file (shown with examples):
+To use the tool, imap.folder and either imap.tunnel or imap.host must be set
+to appropriate values.
+
+Variables
+~~~~~~~~~
+
+imap.folder::
+	The folder to drop the mails into, which is typically the Drafts
+	folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
+	"[Gmail]/Drafts". Required to use imap-send.
+
+imap.tunnel::
+	Command used to setup a tunnel to the IMAP server through which
+	commands will be piped instead of using a direct network connection
+	to the server. Required when imap.host is not set to use imap-send.
+
+imap.host::
+	A URL identifying the server. Use a `imap://` prefix for non-secure
+	connections and a `imaps://` prefix for secure connections.
+	Ignored when imap.tunnel is set, but required to use imap-send
+	otherwise.
+
+imap.user::
+	The username to use when logging in to the server.
+
+imap.pass::
+	The password to use when logging in to the server.
+
+imap.port::
+	An integer port number to connect to on the server.
+	Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
+	Ignored when imap.tunnel is set.
+
+imap.sslverify::
+	A boolean to enable/disable verification of the server certificate
+	used by the SSL/TLS connection. Default is `true`. Ignored when
+	imap.tunnel is set.
+
+imap.preformattedHTML::
+	A boolean to enable/disable the use of html encoding when sending
+	a patch.  An html encoded patch will be bracketed with <pre>
+	and have a content type of text/html.  Ironically, enabling this
+	option causes Thunderbird to send the patch as a plain/text,
+	format=fixed email.  Default is `false`.
+
+Examples
+~~~~~~~~
+
+Using tunnel mode:
 
 ..........................
 [imap]
-    Folder = "INBOX.Drafts"
-
-[imap]
-    Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
-
-[imap]
-    Host = imap.server.com
-    User = bob
-    Pass = pwd
-    Port = 143
+    folder = "INBOX.Drafts"
+    tunnel = "ssh -q -C user@example.com /usr/bin/imapd ./Maildir 2> /dev/null"
 ..........................
 
+Using direct mode:
+
+.........................
+[imap]
+    folder = "INBOX.Drafts"
+    host = imap://imap.example.com
+    user = bob
+    pass = p4ssw0rd
+..........................
+
+Using direct mode with SSL:
+
+.........................
+[imap]
+    folder = "INBOX.Drafts"
+    host = imaps://imap.example.com
+    user = bob
+    pass = p4ssw0rd
+    port = 123
+    sslverify = false
+..........................
+
+
+CAUTION
+-------
+It is still your responsibility to make sure that the email message
+sent by your email program meets the standards of your project.
+Many projects do not like patches to be attached.  Some mail
+agents will transform patches (e.g. wrap lines, send them as
+format=flowed) in ways that make them fail.  You will get angry
+flames ridiculing you if you don't check this.
+
+Thunderbird in particular is known to be problematic.  Thunderbird
+users may wish to visit this web page for more information:
+  http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
+
 
 BUGS
 ----
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 93a2a22..34cf4e5 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -40,6 +40,10 @@
 --decorate::
 	Print out the ref names of any commits that are shown.
 
+--source::
+	Print out the ref name given on the command line by which each
+	commit was reached.
+
 --full-diff::
 	Without this flag, "git log -p <path>..." shows commits that
 	touch the specified paths, and diffs about the same specified
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 9f85d60..057a021 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -126,7 +126,7 @@
 
 --abbrev[=<n>]::
 	Instead of showing the full 40-byte hexadecimal object
-	lines, show only handful hexdigits prefix.
+	lines, show only a partial prefix.
 	Non default number of digits can be specified with --abbrev=<n>.
 
 \--::
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index 4c7262f..f68e5c5 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
-	    [--name-only] [--name-status] [--full-name] [--abbrev=[<n>]]
+	    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev=[<n>]]
 	    <tree-ish> [paths...]
 
 DESCRIPTION
@@ -30,6 +30,8 @@
    'sub/dir' in 'HEAD').  You don't want to give a tree that is not at the
    root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that
    would result in asking for 'sub/sub/dir' in the 'HEAD' commit.
+   However, the current working directory can be ignored by passing
+   --full-tree option.
 
 OPTIONS
 -------
@@ -59,13 +61,17 @@
 
 --abbrev[=<n>]::
 	Instead of showing the full 40-byte hexadecimal object
-	lines, show only handful hexdigits prefix.
+	lines, show only a partial prefix.
 	Non default number of digits can be specified with --abbrev=<n>.
 
 --full-name::
 	Instead of showing the path names relative to the current working
 	directory, show the full path names.
 
+--full-tree::
+	Do not limit the listing to the current working directory.
+	Implies --full-name.
+
 paths::
 	When paths are given, show them (note that this isn't really raw
 	pathnames, but rather a list of patterns to match).  Otherwise
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 1a7ecbf..767486c 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -8,26 +8,80 @@
 
 SYNOPSIS
 --------
-'git merge-base' [--all] <commit> <commit>
+'git merge-base' [--all] <commit> <commit>...
 
 DESCRIPTION
 -----------
 
-'git-merge-base' finds as good a common ancestor as possible between
-the two commits. That is, given two commits A and B, `git merge-base A
-B` will output a commit which is reachable from both A and B through
-the parent relationship.
+'git-merge-base' finds best common ancestor(s) between two commits to use
+in a three-way merge.  One common ancestor is 'better' than another common
+ancestor if the latter is an ancestor of the former.  A common ancestor
+that does not have any better common ancestor is a 'best common
+ancestor', i.e. a 'merge base'.  Note that there can be more than one
+merge base for a pair of commits.
 
-Given a selection of equally good common ancestors it should not be
-relied on to decide in any particular way.
-
-The 'git-merge-base' algorithm is still in flux - use the source...
+Among the two commits to compute the merge base from, one is specified by
+the first commit argument on the command line; the other commit is a
+(possibly hypothetical) commit that is a merge across all the remaining
+commits on the command line.  As the most common special case, specifying only
+two commits on the command line means computing the merge base between
+the given two commits.
 
 OPTIONS
 -------
 --all::
-	Output all common ancestors for the two commits instead of
-	just one.
+	Output all merge bases for the commits, instead of just one.
+
+DISCUSSION
+----------
+
+Given two commits 'A' and 'B', `git merge-base A B` will output a commit
+which is reachable from both 'A' and 'B' through the parent relationship.
+
+For example, with this topology:
+
+		 o---o---o---B
+		/
+	---o---1---o---o---o---A
+
+the merge base between 'A' and 'B' is '1'.
+
+Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the
+merge base between 'A' and a hypothetical commit 'M', which is a merge
+between 'B' and 'C'.  For example, with this topology:
+
+	       o---o---o---o---C
+	      /
+	     /   o---o---o---B
+	    /   /
+	---2---1---o---o---o---A
+
+the result of `git merge-base A B C` is '1'.  This is because the
+equivalent topology with a merge commit 'M' between 'B' and 'C' is:
+
+
+	       o---o---o---o---o
+	      /                 \
+	     /   o---o---o---o---M
+	    /   /
+	---2---1---o---o---o---A
+
+and the result of `git merge-base A M` is '1'.  Commit '2' is also a
+common ancestor between 'A' and 'M', but '1' is a better common ancestor,
+because '2' is an ancestor of '1'.  Hence, '2' is not a merge base.
+
+When the history involves criss-cross merges, there can be more than one
+'best' common ancestor for two commits.  For example, with this topology:
+
+       ---1---o---A
+	   \ /
+	    X
+	   / \
+       ---2---o---o---B
+
+both '1' and '2' are merge-bases of A and B.  Neither one is better than
+the other (both are 'best' merge bases).  When the `--all` option is not given,
+it is unspecified which best one is output.
 
 Author
 ------
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 024ec01..3035373 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -15,17 +15,17 @@
 
 DESCRIPTION
 -----------
-'git-file-merge' incorporates all changes that lead from the `<base-file>`
+'git-merge-file' incorporates all changes that lead from the `<base-file>`
 to `<other-file>` into `<current-file>`. The result ordinarily goes into
 `<current-file>`. 'git-merge-file' is useful for combining separate changes
 to an original. Suppose `<base-file>` is the original, and both
-`<current-file>` and `<other-file>` are modifications of `<base-file>`.
-Then 'git-merge-file' combines both changes.
+`<current-file>` and `<other-file>` are modifications of `<base-file>`,
+then 'git-merge-file' combines both changes.
 
 A conflict occurs if both `<current-file>` and `<other-file>` have changes
 in a common segment of lines. If a conflict is found, 'git-merge-file'
-normally outputs a warning and brackets the conflict with <<<<<<< and
->>>>>>> lines. A typical conflict will look like this:
+normally outputs a warning and brackets the conflict with lines containing
+<<<<<<< and >>>>>>> markers. A typical conflict will look like this:
 
 	<<<<<<< A
 	lines in file A
@@ -60,7 +60,7 @@
 	`<current-file>`.
 
 -q::
-	Quiet;  do  not  warn about conflicts.
+	Quiet; do not warn about conflicts.
 
 
 EXAMPLES
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index ff088c5..123e6d0 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -29,11 +29,11 @@
 	Instead of stopping at the first failed merge, do all of them
 	in one shot - continue with merging even when previous merges
 	returned errors, and only return the error code after all the
-	merges are over.
+	merges.
 
 -q::
-	Do not complain about failed merge program (the merge program
-	failure usually indicates conflicts during merge). This is for
+	Do not complain about a failed merge program (a merge program
+	failure usually indicates conflicts during the merge). This is for
 	porcelains which might want to emit custom messages.
 
 If 'git-merge-index' is called with multiple <file>s (or -a) then it
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index dbb0c18..f869a7f 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -14,14 +14,14 @@
 -----------
 Reads three treeish, and output trivial merge results and
 conflicting stages to the standard output.  This is similar to
-what three-way read-tree -m does, but instead of storing the
+what three-way 'git read-tree -m' does, but instead of storing the
 results in the index, the command outputs the entries to the
 standard output.
 
 This is meant to be used by higher level scripts to compute
-merge results outside index, and stuff the results back into the
+merge results outside of the index, and stuff the results back into the
 index.  For this reason, the output from the command omits
-entries that match <branch1> tree.
+entries that match the <branch1> tree.
 
 Author
 ------
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 17a15ac..c04ae73 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -40,8 +40,8 @@
 include::merge-strategies.txt[]
 
 
-If you tried a merge which resulted in a complex conflicts and
-would want to start over, you can recover with 'git-reset'.
+If you tried a merge which resulted in complex conflicts and
+want to start over, you can recover with 'git-reset'.
 
 CONFIGURATION
 -------------
@@ -69,20 +69,20 @@
   simplest case, called "Already up-to-date."
 
 * `HEAD` is already contained in the merged commit. This is the
-  most common case especially when involved through 'git pull':
-  you are tracking an upstream repository, committed no local
+  most common case especially when invoked from 'git pull':
+  you are tracking an upstream repository, have committed no local
   changes and now you want to update to a newer upstream revision.
-  Your `HEAD` (and the index) is updated to at point the merged
+  Your `HEAD` (and the index) is updated to point at the merged
   commit, without creating an extra merge commit.  This is
   called "Fast-forward".
 
 * Both the merged commit and `HEAD` are independent and must be
-  tied together by a merge commit that has them both as its parents.
+  tied together by a merge commit that has both of them as its parents.
   The rest of this section describes this "True merge" case.
 
 The chosen merge strategy merges the two commits into a single
 new source tree.
-When things cleanly merge, these things happen:
+When things merge cleanly, this is what happens:
 
 1. The results are updated both in the index file and in your
    working tree;
@@ -91,16 +91,16 @@
 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
+file matches 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
+Because 1. involves only those paths differing 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:
+When there are conflicts, the following happens:
 
 1. `HEAD` stays the same.
 
@@ -111,28 +111,105 @@
    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 `<<< === >>>`.
+   tree files contain the result of the "merge" program; i.e. 3-way
+   merge results 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`.
 
+HOW CONFLICTS ARE PRESENTED
+---------------------------
+
+During a merge, the working tree files are updated to reflect the result
+of the merge.  Among the changes made to the common ancestor's version,
+non-overlapping ones (that is, you changed an area of the file while the
+other side left that area intact, or vice versa) are incorporated in the
+final result verbatim.  When both sides made changes to the same area,
+however, git cannot randomly pick one side over the other, and asks you to
+resolve it by leaving what both sides did to that area.
+
+By default, git uses the same style as that is used by "merge" program
+from the RCS suite to present such a conflicted hunk, like this:
+
+------------
+Here are lines that are either unchanged from the common
+ancestor, or cleanly resolved because only one side changed.
+<<<<<<< yours:sample.txt
+Conflict resolution is hard;
+let's go shopping.
+=======
+Git makes conflict resolution easy.
+>>>>>>> theirs:sample.txt
+And here is another line that is cleanly resolved or unmodified.
+------------
+
+The area where a pair of conflicting changes happened is marked with markers
+`<<<<<<<`, `=======`, and `>>>>>>>`.  The part before the `=======`
+is typically your side, and the part afterwards is typically their side.
+
+The default format does not show what the original said in the conflicting
+area.  You cannot tell how many lines are deleted and replaced with
+Barbie's remark on your side.  The only thing you can tell is that your
+side wants to say it is hard and you'd prefer to go shopping, while the
+other side wants to claim it is easy.
+
+An alternative style can be used by setting the "merge.conflictstyle"
+configuration variable to "diff3".  In "diff3" style, the above conflict
+may look like this:
+
+------------
+Here are lines that are either unchanged from the common
+ancestor, or cleanly resolved because only one side changed.
+<<<<<<< yours:sample.txt
+Conflict resolution is hard;
+let's go shopping.
+|||||||
+Conflict resolution is hard.
+=======
+Git makes conflict resolution easy.
+>>>>>>> theirs:sample.txt
+And here is another line that is cleanly resolved or unmodified.
+------------
+
+In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses
+another `|||||||` marker that is followed by the original text.  You can
+tell that the original just stated a fact, and your side simply gave in to
+that statement and gave up, while the other side tried to have a more
+positive attitude.  You can sometimes come up with a better resolution by
+viewing the original.
+
+
+HOW TO RESOLVE CONFLICTS
+------------------------
+
 After seeing a conflict, you can do two things:
 
- * Decide not to merge.  The only clean-up you need are to reset
+ * Decide not to merge.  The only clean-ups 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 --hard' 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 mergetool' can ease this task), 'git-add' or 'git-rm'
-   them, to make the index file contain what the merge result
-   should be, and run 'git-commit' to commit the result.
+ * Resolve the conflicts.  Git will mark the conflicts in
+   the working tree.  Edit the files into shape and
+   'git-add' them to the index.  Use 'git-commit' to seal the deal.
 
+You can work through the conflict with a number of tools:
+
+ * Use a mergetool.  'git mergetool' to launch a graphical
+   mergetool which will work you through the merge.
+
+ * Look at the diffs.  'git diff' will show a three-way diff,
+   highlighting changes from both the HEAD and remote versions.
+
+ * Look at the diffs on their own. 'git log --merge -p <path>'
+   will show diffs first for the HEAD version and then the
+   remote version.
+
+ * Look at the originals.  'git show :1:filename' shows the
+   common ancestor, 'git show :2:filename' shows the HEAD
+   version and 'git show :3:filename' shows the remote version.
 
 SEE ALSO
 --------
diff --git a/Documentation/git-mergetool--lib.txt b/Documentation/git-mergetool--lib.txt
new file mode 100644
index 0000000..78eb03f
--- /dev/null
+++ b/Documentation/git-mergetool--lib.txt
@@ -0,0 +1,54 @@
+git-mergetool--lib(1)
+=====================
+
+NAME
+----
+git-mergetool--lib - Common git merge tool shell scriptlets
+
+SYNOPSIS
+--------
+'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool--lib"'
+
+DESCRIPTION
+-----------
+
+This is not a command the end user would want to run.  Ever.
+This documentation is meant for people who are studying the
+Porcelain-ish scripts and/or are writing new ones.
+
+The 'git-mergetool--lib' scriptlet is designed to be sourced (using
+`.`) by other shell scripts to set up functions for working
+with git merge tools.
+
+Before sourcing 'git-mergetool--lib', your script must set `TOOL_MODE`
+to define the operation mode for the functions listed below.
+'diff' and 'merge' are valid values.
+
+FUNCTIONS
+---------
+get_merge_tool::
+	returns a merge tool.
+
+get_merge_tool_cmd::
+	returns the custom command for a merge tool.
+
+get_merge_tool_path::
+	returns the custom path for a merge tool.
+
+run_merge_tool::
+	launches a merge tool given the tool name and a true/false
+	flag to indicate whether a merge base is present.
+	'$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
+	for use by the merge tool.
+
+Author
+------
+Written by David Aguilar <davvid@gmail.com>
+
+Documentation
+--------------
+Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index e0b2703..ff9700d 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -7,7 +7,7 @@
 
 SYNOPSIS
 --------
-'git mergetool' [--tool=<tool>] [<file>]...
+'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>]...
 
 DESCRIPTION
 -----------
@@ -22,10 +22,12 @@
 
 OPTIONS
 -------
--t or --tool=<tool>::
+-t <tool>::
+--tool=<tool>::
 	Use the merge resolution program specified by <tool>.
 	Valid merge tools are:
-	kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+	kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
+	diffuse, tortoisemerge and opendiff
 +
 If a merge resolution program is not specified, 'git-mergetool'
 will use the configuration variable `merge.tool`.  If the
@@ -38,7 +40,7 @@
 `mergetool.kdiff3.path`. Otherwise, 'git-mergetool' assumes the
 tool is available in PATH.
 +
-Instead of running one of the known merge tool programs
+Instead of running one of the known merge tool programs,
 'git-mergetool' can be customized to run an alternative program
 by specifying the command line to invoke in a configuration
 variable `mergetool.<tool>.cmd`.
@@ -55,11 +57,21 @@
 merge resolution.
 +
 If the custom merge tool correctly indicates the success of a
-merge resolution with its exit code then the configuration
+merge resolution with its exit code, then the configuration
 variable `mergetool.<tool>.trustExitCode` can be set to `true`.
 Otherwise, 'git-mergetool' will prompt the user to indicate the
 success of the resolution after the custom tool has exited.
 
+-y::
+--no-prompt::
+	Don't prompt before each invocation of the merge resolution
+	program.
+
+--prompt::
+	Prompt before each invocation of the merge resolution program.
+	This is the default behaviour; the option is provided to
+	override any configuration settings.
+
 Author
 ------
 Written by Theodore Y Ts'o <tytso@mit.edu>
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
index a5244d3..1ee99c2 100644
--- a/Documentation/git-pack-refs.txt
+++ b/Documentation/git-pack-refs.txt
@@ -26,7 +26,7 @@
 traditional `$GIT_DIR/refs` hierarchy, it is looked up in this
 file and used if found.
 
-Subsequent updates to branches always creates new file under
+Subsequent updates to branches always create new files under
 `$GIT_DIR/refs` hierarchy.
 
 A recommended practice to deal with a repository with too many
@@ -35,7 +35,7 @@
 definition stationary and are not expected to change.  Branch
 heads will be packed with the initial `pack-refs --all`, but
 only the currently active branch heads will become unpacked,
-and next `pack-refs` (without `--all`) will leave them
+and the next `pack-refs` (without `--all`) will leave them
 unpacked.
 
 
diff --git a/Documentation/git-patch-id.txt b/Documentation/git-patch-id.txt
index 477785e..253fc0f 100644
--- a/Documentation/git-patch-id.txt
+++ b/Documentation/git-patch-id.txt
@@ -20,7 +20,7 @@
 
 When dealing with 'git-diff-tree' output, it takes advantage of
 the fact that the patch is prefixed with the object name of the
-commit, and outputs two 40-byte hexadecimal string.  The first
+commit, and outputs two 40-byte hexadecimal strings.  The first
 string is the patch ID, and the second string is the commit ID.
 This can be used to make a mapping from patch ID to commit ID.
 
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 54f1dab..da6055d 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-prune' [-n] [--expire <expire>] [--] [<head>...]
+'git-prune' [-n] [-v] [--expire <expire>] [--] [<head>...]
 
 DESCRIPTION
 -----------
@@ -34,6 +34,9 @@
 	Do not remove anything; just report what it would
 	remove.
 
+-v::
+	Report all removed objects.
+
 \--::
 	Do not interpret any more arguments as options.
 
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 6150b1b..fd53c49 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git push' [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
+'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]
 	   [--repo=<repository>] [-f | --force] [-v | --verbose]
 	   [<repository> <refspec>...]
 
@@ -24,40 +24,45 @@
 documentation for linkgit:git-receive-pack[1].
 
 
-OPTIONS
--------
+OPTIONS[[OPTIONS]]
+------------------
 <repository>::
 	The "remote" repository that is destination of a push
-	operation.  See the section <<URLS,GIT URLS>> below.
+	operation.  This parameter can be either a URL
+	(see the section <<URLS,GIT URLS>> below) or the name
+	of a remote (see the section <<REMOTES,REMOTES>> below).
 
 <refspec>...::
-	The canonical format of a <refspec> parameter is
-	`+?<src>:<dst>`; that is, an optional plus `{plus}`, followed
-	by the source ref, followed by a colon `:`, followed by
-	the destination ref.
+	The format of a <refspec> parameter is an optional plus
+	`{plus}`, followed by the source ref <src>, followed
+	by a colon `:`, followed by the destination ref <dst>.
+	It is used to specify with what <src> object the <dst> ref
+	in the remote repository is to be updated.
 +
-The <src> side represents the source branch (or arbitrary
-"SHA1 expression", such as `master~4` (four parents before the
-tip of `master` branch); see linkgit:git-rev-parse[1]) that you
-want to push.  The <dst> side represents the destination location.
+The <src> is often the name of the branch you would want to push, but
+it can be any arbitrary "SHA-1 expression", such as `master~4` or
+`HEAD` (see linkgit:git-rev-parse[1]).
 +
-The local ref that matches <src> is used
-to fast forward the remote ref that matches <dst> (or, if no <dst> was
-specified, the same ref that <src> referred to locally).  If
-the optional leading plus `+` is used, the remote ref is updated
-even if it does not result in a fast forward update.
+The <dst> tells which ref on the remote side is updated with this
+push. Arbitrary expressions cannot be used here, an actual ref must
+be named. If `:`<dst> is omitted, the same ref as <src> will be
+updated.
++
+The object referenced by <src> is used to update the <dst> reference
+on the remote side, but by default this is only allowed if the
+update can fast forward <dst>.  By having the optional leading `{plus}`,
+you can tell git to update the <dst> ref even when the update is not a
+fast forward.  This does *not* attempt to merge <src> into <dst>.  See
+EXAMPLES below for details.
 +
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
 +
-A parameter <ref> without a colon pushes the <ref> from the source
-repository to the destination repository under the same name.
-+
 Pushing an empty <src> allows you to delete the <dst> ref from
 the remote repository.
 +
-The special refspec `:` (or `+:` to allow non-fast forward updates)
-directs git to push "matching" heads: for every head that exists on
-the local side, the remote side is updated if a head of the same name
+The special refspec `:` (or `{plus}:` to allow non-fast forward updates)
+directs git to push "matching" branches: for every branch that exists on
+the local side, the remote side is updated if a branch of the same name
 already exists on the remote side.  This is the default operation mode
 if no explicit refspec is found (that is neither on the command line
 nor in any Push line of the corresponding remotes file---see below).
@@ -86,14 +91,12 @@
 	line.
 
 --receive-pack=<git-receive-pack>::
+--exec=<git-receive-pack>::
 	Path to the 'git-receive-pack' program on the remote
 	end.  Sometimes useful when pushing to a remote
 	repository over ssh, and you do not have the program in
 	a directory on the default $PATH.
 
---exec=<git-receive-pack>::
-	Same as \--receive-pack=<git-receive-pack>.
-
 -f::
 --force::
 	Usually, the command refuses to update a remote ref that is
@@ -184,6 +187,28 @@
 Examples
 --------
 
+git push::
+	Works like `git push <remote>`, where <remote> is the
+	current branch's remote (or `origin`, if no remote is
+	configured for the current branch).
+
+git push origin::
+	Without additional configuration, works like
+	`git push origin :`.
++
+The default behavior of this command when no <refspec> is given can be
+configured by setting the `push` option of the remote.
++
+For example, to default to pushing only the current branch to `origin`
+use `git config remote.origin.push HEAD`.  Any valid <refspec> (like
+the ones in the examples below) can be configured as the default for
+`git push origin`.
+
+git push origin :::
+	Push "matching" branches to `origin`. See
+	<refspec> in the <<OPTIONS,OPTIONS>> section above for a
+	description of "matching" branches.
+
 git push origin master::
 	Find a ref that matches `master` in the source repository
 	(most likely, it would find `refs/heads/master`), and update
@@ -191,9 +216,9 @@
 	with it.  If `master` did not exist remotely, it would be
 	created.
 
-git push origin :experimental::
-	Find a ref that matches `experimental` in the `origin` repository
-	(e.g. `refs/heads/experimental`), and delete it.
+git push origin HEAD::
+	A handy way to push the current branch to the same name on the
+	remote.
 
 git push origin master:satellite/master dev:satellite/dev::
 	Use the source ref that matches `master` (e.g. `refs/heads/master`)
@@ -201,6 +226,11 @@
 	`refs/remotes/satellite/master`) in the `origin` repository, then
 	do the same for `dev` and `satellite/dev`.
 
+git push origin HEAD:master::
+	Push the current branch to the remote ref matching `master` in the
+	`origin` repository. This form is convenient to push the current
+	branch without thinking about its local name.
+
 git push origin master:refs/heads/experimental::
 	Create the branch `experimental` in the `origin` repository
 	by copying the current `master` branch.  This form is only
@@ -208,6 +238,35 @@
 	the local name and the remote name are different; otherwise,
 	the ref name on its own will work.
 
+git push origin :experimental::
+	Find a ref that matches `experimental` in the `origin` repository
+	(e.g. `refs/heads/experimental`), and delete it.
+
+git push origin {plus}dev:master::
+	Update the origin repository's master branch with the dev branch,
+	allowing non-fast forward updates.  *This can leave unreferenced
+	commits dangling in the origin repository.*  Consider the
+	following situation, where a fast forward is not possible:
++
+----
+	    o---o---o---A---B  origin/master
+		     \
+		      X---Y---Z  dev
+----
++
+The above command would change the origin repository to
++
+----
+		      A---B  (unnamed branch)
+		     /
+	    o---o---o---X---Y---Z  master
+----
++
+Commits A and B would no longer belong to a branch with a symbolic name,
+and so would be unreachable.  As such, these commits would be removed by
+a `git gc` command on the origin repository.
+
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>, later rewritten in C
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 309deac..7160fa1 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -212,7 +212,7 @@
 
 Case #3 is slightly tricky and needs explanation.  The result from this
 rule logically should be to remove the path if the user staged the removal
-of the path and then swiching to a new branch.  That however will prevent
+of the path and then switching to a new branch.  That however will prevent
 the initial checkout from happening, so the rule is modified to use M (new
 tree) only when the contents of the index is empty.  Otherwise the removal
 of the path is kept as long as $H and $M are the same.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 59c1b02..3d5a066 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -8,10 +8,11 @@
 SYNOPSIS
 --------
 [verse]
-'git rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
-	[-s <strategy> | --strategy=<strategy>]
-	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
-	[--onto <newbase>] <upstream> [<branch>]
+'git rebase' [-i | --interactive] [options] [--onto <newbase>]
+	<upstream> [<branch>]
+'git rebase' [-i | --interactive] [options] --onto <newbase>
+	--root [<branch>]
+
 'git rebase' --continue | --skip | --abort
 
 DESCRIPTION
@@ -22,7 +23,8 @@
 
 All changes made by commits in the current branch but that are not
 in <upstream> are saved to a temporary area.  This is the same set
-of commits that would be shown by `git log <upstream>..HEAD`.
+of commits that would be shown by `git log <upstream>..HEAD` (or
+`git log HEAD`, if --root is specified).
 
 The current branch is reset to <upstream>, or <newbase> if the
 --onto option was supplied.  This has the exact same effect as
@@ -92,7 +94,7 @@
 from the latter branch, using `rebase --onto`.
 
 First let's assume your 'topic' is based on branch 'next'.
-For example feature developed in 'topic' depends on some
+For example, a feature developed in 'topic' depends on some
 functionality which is found in 'next'.
 
 ------------
@@ -103,9 +105,9 @@
                             o---o---o  topic
 ------------
 
-We would want to make 'topic' forked from branch 'master',
-for example because the functionality 'topic' branch depend on
-got merged into more stable 'master' branch, like this:
+We want to make 'topic' forked from branch 'master'; for example,
+because the functionality on which 'topic' depends was merged into the
+more stable 'master' branch. We want our tree to look like this:
 
 ------------
     o---o---o---o---o  master
@@ -190,6 +192,13 @@
 
     git rebase --abort
 
+CONFIGURATION
+-------------
+
+rebase.stat::
+	Whether to show a diffstat of what changed upstream since the last
+	rebase. False by default.
+
 OPTIONS
 -------
 <newbase>::
@@ -230,7 +239,18 @@
 
 -v::
 --verbose::
-	Display a diffstat of what changed upstream since the last rebase.
+	Be verbose. Implies --stat.
+
+--stat::
+	Show a diffstat of what changed upstream since the last rebase. The
+	diffstat is also controlled by the configuration option rebase.stat.
+
+-n::
+--no-stat::
+	Do not show a diffstat as part of the rebase process.
+
+--no-verify::
+	This option bypasses the pre-rebase hook.  See also linkgit:githooks[5].
 
 -C<n>::
 	Ensure at least <n> lines of surrounding context match before
@@ -238,9 +258,22 @@
 	context exist they all must match.  By default no context is
 	ever ignored.
 
---whitespace=<nowarn|warn|error|error-all|strip>::
+-f::
+--force-rebase::
+	Force the rebase even if the current branch is a descendant
+	of the commit you are rebasing onto.  Normally the command will
+	exit with the message "Current branch is up to date" in such a
+	situation.
+
+--whitespace=<option>::
 	This flag is passed to the 'git-apply' program
 	(see linkgit:git-apply[1]) that applies the patch.
+	Incompatible with the --interactive option.
+
+--committer-date-is-author-date::
+--ignore-date::
+	These flags are passed to 'git-am' to easily change the dates
+	of the rebased commits (see linkgit:git-am[1]).
 
 -i::
 --interactive::
@@ -250,18 +283,25 @@
 
 -p::
 --preserve-merges::
-	Instead of ignoring merges, try to recreate them.  This option
-	only works in interactive mode.
+	Instead of ignoring merges, try to recreate them.
+
+--root::
+	Rebase all commits reachable from <branch>, instead of
+	limiting them with an <upstream>.  This allows you to rebase
+	the root commit(s) on a branch.  Must be used with --onto, and
+	will skip changes already contained in <newbase> (instead of
+	<upstream>).  When used together with --preserve-merges, 'all'
+	root commits will be rewritten to have <newbase> as parent
+	instead.
 
 include::merge-strategies.txt[]
 
 NOTES
 -----
-When you rebase a branch, you are changing its history in a way that
-will cause problems for anyone who already has a copy of the branch
-in their repository and tries to pull updates from you.  You should
-understand the implications of using 'git-rebase' on a repository that
-you share.
+
+You should understand the implications of using 'git-rebase' on a
+repository that you share.  See also RECOVERING FROM UPSTREAM REBASE
+below.
 
 When the git-rebase command is run, it will first execute a "pre-rebase"
 hook if one exists.  You can use this hook to do sanity checks and
@@ -396,6 +436,127 @@
 after each commit, test, and amend the commit if fixes are necessary.
 
 
+RECOVERING FROM UPSTREAM REBASE
+-------------------------------
+
+Rebasing (or any other form of rewriting) a branch that others have
+based work on is a bad idea: anyone downstream of it is forced to
+manually fix their history.  This section explains how to do the fix
+from the downstream's point of view.  The real fix, however, would be
+to avoid rebasing the upstream in the first place.
+
+To illustrate, suppose you are in a situation where someone develops a
+'subsystem' branch, and you are working on a 'topic' that is dependent
+on this 'subsystem'.  You might end up with a history like the
+following:
+
+------------
+    o---o---o---o---o---o---o---o---o  master
+	 \
+	  o---o---o---o---o  subsystem
+			   \
+			    *---*---*  topic
+------------
+
+If 'subsystem' is rebased against 'master', the following happens:
+
+------------
+    o---o---o---o---o---o---o---o  master
+	 \			 \
+	  o---o---o---o---o	  o'--o'--o'--o'--o'  subsystem
+			   \
+			    *---*---*  topic
+------------
+
+If you now continue development as usual, and eventually merge 'topic'
+to 'subsystem', the commits from 'subsystem' will remain duplicated forever:
+
+------------
+    o---o---o---o---o---o---o---o  master
+	 \			 \
+	  o---o---o---o---o	  o'--o'--o'--o'--o'--M	 subsystem
+			   \			     /
+			    *---*---*-..........-*--*  topic
+------------
+
+Such duplicates are generally frowned upon because they clutter up
+history, making it harder to follow.  To clean things up, you need to
+transplant the commits on 'topic' to the new 'subsystem' tip, i.e.,
+rebase 'topic'.  This becomes a ripple effect: anyone downstream from
+'topic' is forced to rebase too, and so on!
+
+There are two kinds of fixes, discussed in the following subsections:
+
+Easy case: The changes are literally the same.::
+
+	This happens if the 'subsystem' rebase was a simple rebase and
+	had no conflicts.
+
+Hard case: The changes are not the same.::
+
+	This happens if the 'subsystem' rebase had conflicts, or used
+	`\--interactive` to omit, edit, or squash commits; or if the
+	upstream used one of `commit \--amend`, `reset`, or
+	`filter-branch`.
+
+
+The easy case
+~~~~~~~~~~~~~
+
+Only works if the changes (patch IDs based on the diff contents) on
+'subsystem' are literally the same before and after the rebase
+'subsystem' did.
+
+In that case, the fix is easy because 'git-rebase' knows to skip
+changes that are already present in the new upstream.  So if you say
+(assuming you're on 'topic')
+------------
+    $ git rebase subsystem
+------------
+you will end up with the fixed history
+------------
+    o---o---o---o---o---o---o---o  master
+				 \
+				  o'--o'--o'--o'--o'  subsystem
+						   \
+						    *---*---*  topic
+------------
+
+
+The hard case
+~~~~~~~~~~~~~
+
+Things get more complicated if the 'subsystem' changes do not exactly
+correspond to the ones before the rebase.
+
+NOTE: While an "easy case recovery" sometimes appears to be successful
+      even in the hard case, it may have unintended consequences.  For
+      example, a commit that was removed via `git rebase
+      \--interactive` will be **resurrected**!
+
+The idea is to manually tell 'git-rebase' "where the old 'subsystem'
+ended and your 'topic' began", that is, what the old merge-base
+between them was.  You will have to find a way to name the last commit
+of the old 'subsystem', for example:
+
+* With the 'subsystem' reflog: after 'git-fetch', the old tip of
+  'subsystem' is at `subsystem@\{1}`.  Subsequent fetches will
+  increase the number.  (See linkgit:git-reflog[1].)
+
+* Relative to the tip of 'topic': knowing that your 'topic' has three
+  commits, the old tip of 'subsystem' must be `topic~3`.
+
+You can then transplant the old `subsystem..topic` to the new tip by
+saying (for the reflog case, and assuming you are on 'topic' already):
+------------
+    $ git rebase --onto subsystem subsystem@{1}
+------------
+
+The ripple effect of a "hard case" recovery is especially bad:
+'everyone' downstream from 'topic' will now have to perform a "hard
+case" recovery too!
+
+
 Authors
 ------
 Written by Junio C Hamano <gitster@pobox.com> and
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index bb99810..9e2b4ea 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -11,10 +11,12 @@
 [verse]
 'git remote' [-v | --verbose]
 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
+'git remote rename' <old> <new>
 'git remote rm' <name>
+'git remote set-head' <name> [-a | -d | <branch>]
 'git remote show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
-'git remote update' [group]
+'git remote update' [-p | --prune] [group | remote]...
 
 DESCRIPTION
 -----------
@@ -52,8 +54,7 @@
 multiple branches without grabbing all branches.
 +
 With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
-up to point at remote's `<master>` branch instead of whatever
-branch the `HEAD` at the remote repository actually points at.
+up to point at remote's `<master>` branch. See also the set-head command.
 +
 In mirror mode, enabled with `\--mirror`, the refs will not be stored
 in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
@@ -61,11 +62,44 @@
 mode, furthermore, `git push` will always behave as if `\--mirror`
 was passed.
 
+'rename'::
+
+Rename the remote named <old> to <new>. All remote tracking branches and
+configuration settings for the remote are updated.
++
+In case <old> and <new> are the same, and <old> is a file under
+`$GIT_DIR/remotes` or `$GIT_DIR/branches`, the remote is converted to
+the configuration file format.
+
 'rm'::
 
 Remove the remote named <name>. All remote tracking branches and
 configuration settings for the remote are removed.
 
+'set-head'::
+
+Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for
+the named remote. Having a default branch for a remote is not required,
+but allows the name of the remote to be specified in lieu of a specific
+branch. For example, if the default branch for `origin` is set to
+`master`, then `origin` may be specified wherever you would normally
+specify `origin/master`.
++
+With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted.
++
+With `-a`, the remote is queried to determine its `HEAD`, then
+`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
+`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set
+`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
+only work if `refs/remotes/origin/next` already exists; if not it must be
+fetched first.
++
+Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git
+remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
+`refs/remotes/origin/master`. This will only work if
+`refs/remotes/origin/master` already exists; if not it must be fetched first.
++
+
 'show'::
 
 Gives some information about the remote <name>.
@@ -91,6 +125,8 @@
 remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See linkgit:git-config[1]).
++
+With `--prune` option, prune all the remotes that are updated.
 
 
 DISCUSSION
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 6abaeac..abb25d1 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git reset' [--mixed | --soft | --hard] [-q] [<commit>]
+'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
 'git reset' [-q] [<commit>] [--] <paths>...
 
 DESCRIPTION
@@ -45,6 +45,11 @@
 	switched to. Any changes to tracked files in the working tree
 	since <commit> are lost.
 
+--merge::
+	Resets the index to match the tree recorded by the named commit,
+	and updates the files that are different between the named commit
+	and the current commit in the working tree.
+
 -q::
 	Be quiet, only report errors.
 
@@ -82,7 +87,9 @@
 +
 <1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
 and you do not want to ever see them again.  Do *not* do this if
-you have already given these commits to somebody else.
+you have already given these commits to somebody else.  (See the
+"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
+the implications of doing so.)
 
 Undo a commit, making it a topic branch::
 +
@@ -128,7 +135,7 @@
 $ git pull                         <1>
 Auto-merging nitfol
 CONFLICT (content): Merge conflict in nitfol
-Automatic merge failed/prevented; fix up by hand
+Automatic merge failed; fix conflicts and then commit the result.
 $ git reset --hard                 <2>
 $ git pull . topic/branch          <3>
 Updating from 41223... to 13134...
@@ -150,6 +157,28 @@
 brings your index file and the working tree back to that state,
 and resets the tip of the branch to that commit.
 
+Undo a merge or pull inside a dirty work tree::
++
+------------
+$ git pull                         <1>
+Auto-merging nitfol
+Merge made by recursive.
+ nitfol                |   20 +++++----
+ ...
+$ git reset --merge ORIG_HEAD      <2>
+------------
++
+<1> Even if you may have local modifications in your
+working tree, you can safely say "git pull" when you know
+that the change in the other branch does not overlap with
+them.
+<2> After inspecting the result of the merge, you may find
+that the change in the other branch is unsatisfactory.  Running
+"git reset --hard ORIG_HEAD" will let you go back to where you
+were, but it will discard your local changes, which you do not
+want.  "git reset --merge" keeps your local changes.
+
+
 Interrupted workflow::
 +
 Suppose you are interrupted by an urgent fix request while you
@@ -175,6 +204,8 @@
 <3> At this point the index file still has all the WIP changes you
     committed as 'snapshot WIP'.  This updates the index to show your
     WIP files as uncommitted.
++
+See also linkgit:git-stash[1].
 
 Reset a single file in the index::
 +
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 2921da3..fba30b1 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -84,6 +84,11 @@
 	unfortunately named tag "master"), and show them as full
 	refnames (e.g. "refs/heads/master").
 
+--abbrev-ref[={strict|loose}]::
+	A non-ambiguous short name of the objects name.
+	The option core.warnAmbiguousRefs is used to select the strict
+	abbreviation mode.
+
 --all::
 	Show all refs found in `$GIT_DIR/refs`.
 
@@ -212,6 +217,9 @@
   reflog of the current branch. For example, if you are on the
   branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
 
+* The special construct '@\{-<n>\}' means the <n>th branch checked out
+  before the current one.
+
 * A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
@@ -296,18 +304,18 @@
 commit, following the commit ancestry chain.
 
 To exclude commits reachable from a commit, a prefix `{caret}`
-notation is used.  E.g. "`{caret}r1 r2`" means commits reachable
+notation is used.  E.g. `{caret}r1 r2` means commits reachable
 from `r2` but exclude the ones reachable from `r1`.
 
 This set operation appears so often that there is a shorthand
 for it.  When you have two commits `r1` and `r2` (named according
 to the syntax explained in SPECIFYING REVISIONS above), you can ask
 for commits that are reachable from r2 excluding those that are reachable
-from r1 by "`{caret}r1 r2`" and it can be written as "`r1..r2`".
+from r1 by `{caret}r1 r2` and it can be written as `r1..r2`.
 
-A similar notation "`r1\...r2`" is called symmetric difference
+A similar notation `r1\...r2` is called symmetric difference
 of `r1` and `r2` and is defined as
-"`r1 r2 --not $(git merge-base --all r1 r2)`".
+`r1 r2 --not $(git merge-base --all r1 r2)`.
 It is the set of commits that are reachable from either one of
 `r1` or `r2` but not from both.
 
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index caa0729..5e11758 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -44,6 +44,14 @@
 	option specifies the parent number (starting from 1) of
 	the mainline and allows revert to reverse the change
 	relative to the specified parent.
++
+Reverting a merge commit declares that you will never want the tree changes
+brought in by the merge.  As a result, later merges will only bring in tree
+changes introduced by commits that are not ancestors of the previously
+reverted merge.  This may or may not be what you want.
++
+See the link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for
+more details.
 
 --no-edit::
 	With this option, 'git-revert' will not start the commit
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 3c3e1b0..0b1f183 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,8 +8,7 @@
 
 SYNOPSIS
 --------
-'git send-email' [options] <file|directory> [... file|directory]
-
+'git send-email' [options] <file|directory|rev-list options>...
 
 
 DESCRIPTION
@@ -20,70 +19,110 @@
 specified on the command line, the user will be prompted with a ReadLine
 enabled interface to provide the necessary information.
 
+There are two formats accepted for patch files:
+
+1. mbox format files
++
+This is what linkgit:git-format-patch[1] generates.  Most headers and MIME
+formatting are ignored.
+
+2. The original format used by Greg Kroah-Hartman's 'send_lots_of_email.pl'
+script
++
+This format expects the first line of the file to contain the "Cc:" value
+and the "Subject:" of the message as the second line.
+
+
 OPTIONS
 -------
-The options available are:
 
---bcc::
-	Specify a "Bcc:" value for each email.
+Composing
+~~~~~~~~~
+
+--bcc=<address>::
+	Specify a "Bcc:" value for each email. Default is the value of
+	'sendemail.bcc'.
 +
 The --bcc option must be repeated for each user you want on the bcc list.
 
---cc::
+--cc=<address>::
 	Specify a starting "Cc:" value for each email.
+	Default is the value of 'sendemail.cc'.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
---cc-cmd::
-	Specify a command to execute once per patch file which
-	should generate patch file specific "Cc:" entries.
-	Output of this command must be single email address per line.
-	Default is the value of 'sendemail.cccmd' configuration value.
-
---chain-reply-to::
---no-chain-reply-to::
-	If this is set, each email will be sent as a reply to the previous
-	email sent.  If disabled with "--no-chain-reply-to", all emails after
-	the first will be sent as replies to the first email sent.  When using
-	this, it is recommended that the first file given be an overview of the
-	entire patch series.
-	Default is the value of the 'sendemail.chainreplyto' configuration
-	value; if that is unspecified, default to --chain-reply-to.
+--annotate::
+	Review each patch you're about to send in an editor. The setting
+	'sendemail.multiedit' defines if this will spawn one editor per patch
+	or one for all of them at once.
 
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
++
+When '--compose' is used, git send-email will use the From, Subject, and
+In-Reply-To headers specified in the message. If the body of the message
+(what you type after the headers and a blank line) only contains blank
+(or GIT: prefixed) lines the summary won't be sent, but From, Subject,
+and In-Reply-To headers will be used unless they are removed.
++
+Missing From or In-Reply-To headers will be prompted for.
 
---from::
+--from=<address>::
 	Specify the sender of the emails.  This will default to
 	the value GIT_COMMITTER_IDENT, as returned by "git var -l".
 	The user will still be prompted to confirm this entry.
 
---in-reply-to::
+--in-reply-to=<identifier>::
 	Specify the contents of the first In-Reply-To header.
 	Subsequent emails will refer to the previous email
 	instead of this if --chain-reply-to is set (the default)
 	Only necessary if --compose is also set.  If --compose
 	is not set, this will be prompted for.
 
---signed-off-by-cc::
---no-signed-off-by-cc::
-        If this is set, add emails found in Signed-off-by: or Cc: lines to the
-        cc list.
-        Default is the value of 'sendemail.signedoffcc' configuration value;
-        if that is unspecified, default to --signed-off-by-cc.
+--subject=<string>::
+	Specify the initial subject of the email thread.
+	Only necessary if --compose is also set.  If --compose
+	is not set, this will be prompted for.
 
---quiet::
-	Make git-send-email less verbose.  One line per email should be
-	all that is output.
+--to=<address>::
+	Specify the primary recipient of the emails generated. Generally, this
+	will be the upstream maintainer of the project involved. Default is the
+	value of the 'sendemail.to' configuration value; if that is unspecified,
+	this will be prompted for.
++
+The --to option must be repeated for each user you want on the to list.
 
---identity::
-	A configuration identity. When given, causes values in the
-	'sendemail.<identity>' subsection to take precedence over
-	values in the 'sendemail' section. The default identity is
-	the value of 'sendemail.identity'.
 
---smtp-server::
+Sending
+~~~~~~~
+
+--envelope-sender=<address>::
+	Specify the envelope sender used to send the emails.
+	This is useful if your default address is not the address that is
+	subscribed to a list. If you use the sendmail binary, you must have
+	suitable privileges for the -f parameter. Default is the value of
+	the 'sendemail.envelopesender' configuration variable; if that is
+	unspecified, choosing the envelope sender is left to your MTA.
+
+--smtp-encryption=<encryption>::
+	Specify the encryption to use, either 'ssl' or 'tls'.  Any other
+	value reverts to plain SMTP.  Default is the value of
+	'sendemail.smtpencryption'.
+
+--smtp-pass[=<password>]::
+	Password for SMTP-AUTH. The argument is optional: If no
+	argument is specified, then the empty string is used as
+	the password. Default is the value of 'sendemail.smtppass',
+	however '--smtp-pass' always overrides this value.
++
+Furthermore, passwords need not be specified in configuration files
+or on the command line. If a username has been specified (with
+'--smtp-user' or a 'sendemail.smtpuser'), but no password has been
+specified (with '--smtp-pass' or 'sendemail.smtppass'), then the
+user is prompted for a password while the input is masked for privacy.
+
+--smtp-server=<host>::
 	If set, specifies the outgoing SMTP server to use (e.g.
 	`smtp.example.com` or a raw IP address).  Alternatively it can
 	specify a full pathname of a sendmail-like program instead;
@@ -93,114 +132,129 @@
 	`/usr/lib/sendmail` if such program is available, or
 	`localhost` otherwise.
 
---smtp-server-port::
+--smtp-server-port=<port>::
 	Specifies a port different from the default port (SMTP
 	servers typically listen to smtp port 25 and ssmtp port
-	465).
-
---smtp-user::
-	Username for SMTP-AUTH. In place of this option, the following
-	configuration variables can be specified:
-+
---
-		* sendemail.smtpuser
-		* sendemail.<identity>.smtpuser (see sendemail.identity).
---
-+
-However, --smtp-user always overrides these variables.
-+
-If a username is not specified (with --smtp-user or a
-configuration variable), then authentication is not attempted.
-
---smtp-pass::
-	Password for SMTP-AUTH. The argument is optional: If no
-	argument is specified, then the empty string is used as
-	the password.
-+
-In place of this option, the following configuration variables
-can be specified:
-+
---
-		* sendemail.smtppass
-		* sendemail.<identity>.smtppass (see sendemail.identity).
---
-+
-However, --smtp-pass always overrides these variables.
-+
-Furthermore, passwords need not be specified in configuration files
-or on the command line. If a username has been specified (with
---smtp-user or a configuration variable), but no password has been
-specified (with --smtp-pass or a configuration variable), then the
-user is prompted for a password while the input is masked for privacy.
-
---smtp-encryption::
-	Specify the encryption to use, either 'ssl' or 'tls'.  Any other
-	value reverts to plain SMTP.  Default is the value of
-	'sendemail.smtpencryption'.
+	465). This can be set with 'sendemail.smtpserverport'.
 
 --smtp-ssl::
-	Legacy alias for '--smtp-encryption=ssl'.
+	Legacy alias for '--smtp-encryption ssl'.
 
---subject::
-	Specify the initial subject of the email thread.
-	Only necessary if --compose is also set.  If --compose
-	is not set, this will be prompted for.
+--smtp-user=<user>::
+	Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
+	if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
+	then authentication is not attempted.
 
---suppress-from::
---no-suppress-from::
-        If this is set, do not add the From: address to the cc: list.
-        Default is the value of 'sendemail.suppressfrom' configuration value;
-        if that is unspecified, default to --no-suppress-from.
 
---suppress-cc::
+Automating
+~~~~~~~~~~
+
+--cc-cmd=<command>::
+	Specify a command to execute once per patch file which
+	should generate patch file specific "Cc:" entries.
+	Output of this command must be single email address per line.
+	Default is the value of 'sendemail.cccmd' configuration value.
+
+--[no-]chain-reply-to=<identifier>::
+	If this is set, each email will be sent as a reply to the previous
+	email sent.  If disabled with "--no-chain-reply-to", all emails after
+	the first will be sent as replies to the first email sent.  When using
+	this, it is recommended that the first file given be an overview of the
+	entire patch series. Default is the value of the 'sendemail.chainreplyto'
+	configuration value; if that is unspecified, default to --chain-reply-to.
+
+--identity=<identity>::
+	A configuration identity. When given, causes values in the
+	'sendemail.<identity>' subsection to take precedence over
+	values in the 'sendemail' section. The default identity is
+	the value of 'sendemail.identity'.
+
+--[no-]signed-off-by-cc::
+	If this is set, add emails found in Signed-off-by: or Cc: lines to the
+	cc list. Default is the value of 'sendemail.signedoffbycc' configuration
+	value; if that is unspecified, default to --signed-off-by-cc.
+
+--suppress-cc=<category>::
 	Specify an additional category of recipients to suppress the
-	auto-cc of.  'self' will avoid including the sender, 'author' will
-	avoid including the patch author, 'cc' will avoid including anyone
-	mentioned in Cc lines in the patch, 'sob' will avoid including
-	anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid
-	running the --cc-cmd.  'all' will suppress all auto cc values.
-	Default is the value of 'sendemail.suppresscc' configuration value;
-	if that is unspecified, default to 'self' if --suppress-from is
-	specified, as well as 'sob' if --no-signed-off-cc is specified.
+	auto-cc of:
++
+--
+- 'author' will avoid including the patch author
+- 'self' will avoid including the sender
+- 'cc' will avoid including anyone mentioned in Cc lines in the patch header
+  except for self (use 'self' for that).
+- 'ccbody' will avoid including anyone mentioned in Cc lines in the
+  patch body (commit message) except for self (use 'self' for that).
+- 'sob' will avoid including anyone mentioned in Signed-off-by lines except
+   for self (use 'self' for that).
+- 'cccmd' will avoid running the --cc-cmd.
+- 'body' is equivalent to 'sob' + 'ccbody'
+- 'all' will suppress all auto cc values.
+--
++
+Default is the value of 'sendemail.suppresscc' configuration value; if
+that is unspecified, default to 'self' if --suppress-from is
+specified, as well as 'body' if --no-signed-off-cc is specified.
 
---thread::
---no-thread::
+--[no-]suppress-from::
+	If this is set, do not add the From: address to the cc: list.
+	Default is the value of 'sendemail.suppressfrom' configuration
+	value; if that is unspecified, default to --no-suppress-from.
+
+--[no-]thread::
 	If this is set, the In-Reply-To header will be set on each email sent.
 	If disabled with "--no-thread", no emails will have the In-Reply-To
-	header set.
-	Default is the value of the 'sendemail.thread' configuration value;
-	if that is unspecified, default to --thread.
+	header set. Default is the value of the 'sendemail.thread' configuration
+	value; if that is unspecified, default to --thread.
+
+
+Administering
+~~~~~~~~~~~~~
+
+--confirm=<mode>::
+	Confirm just before sending:
++
+--
+- 'always' will always confirm before sending
+- 'never' will never confirm before sending
+- 'cc' will confirm before sending when send-email has automatically
+  added addresses from the patch to the Cc list
+- 'compose' will confirm before sending the first message when using --compose.
+- 'auto' is equivalent to 'cc' + 'compose'
+--
++
+Default is the value of 'sendemail.confirm' configuration value; if that
+is unspecified, default to 'auto' unless any of the suppress options
+have been specified, in which case default to 'compose'.
 
 --dry-run::
 	Do everything except actually send the emails.
 
---envelope-sender::
-	Specify the envelope sender used to send the emails.
-	This is useful if your default address is not the address that is
-	subscribed to a list. If you use the sendmail binary, you must have
-	suitable privileges for the -f parameter.
-	Default is the value of the 'sendemail.envelopesender' configuration
-	variable; if that is unspecified, choosing the envelope sender is left
-	to your MTA.
+--quiet::
+	Make git-send-email less verbose.  One line per email should be
+	all that is output.
 
---to::
-	Specify the primary recipient of the emails generated.
-	Generally, this will be the upstream maintainer of the
-	project involved.
-	Default is the value of the 'sendemail.to' configuration value;
-	if that is unspecified, this will be prompted for.
+--[no-]validate::
+	Perform sanity checks on patches.
+	Currently, validation means the following:
 +
-The --to option must be repeated for each user you want on the to list.
+--
+		*	Warn of patches that contain lines longer than 998 characters; this
+			is due to SMTP limits as described by http://www.ietf.org/rfc/rfc2821.txt.
+--
++
+Default is the value of 'sendemail.validate'; if this is not set,
+default to '--validate'.
+
+--[no-]format-patch::
+	When an argument may be understood either as a reference or as a file name,
+	choose to understand it as a format-patch argument ('--format-patch')
+	or as a file name ('--no-format-patch'). By default, when such a conflict
+	occurs, git send-email will fail.
 
 
 CONFIGURATION
 -------------
-sendemail.identity::
-	The default configuration identity. When specified,
-	'sendemail.<identity>.<item>' will have higher precedence than
-	'sendemail.<item>'. This is useful to declare multiple SMTP
-	identities and to hoist sensitive authentication information
-	out of the repository and into the global configuration file.
 
 sendemail.aliasesfile::
 	To avoid typing long email addresses, point this to one or more
@@ -210,38 +264,17 @@
 	Format of the file(s) specified in sendemail.aliasesfile. Must be
 	one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
-sendemail.to::
-	Email address (or alias) to always send to.
+sendemail.multiedit::
+	If true (default), a single editor instance will be spawned to edit
+	files you have to edit (patches when '--annotate' is used, and the
+	summary when '--compose' is used). If false, files will be edited one
+	after the other, spawning a new editor each time.
 
-sendemail.cccmd::
-	Command to execute to generate per patch file specific "Cc:"s.
+sendemail.confirm::
+	Sets the default for whether to confirm before sending. Must be
+	one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm'
+	in the previous section for the meaning of these values.
 
-sendemail.bcc::
-	Email address (or alias) to always bcc.
-
-sendemail.chainreplyto::
-	Boolean value specifying the default to the '--chain_reply_to'
-	parameter.
-
-sendemail.smtpserver::
-	Default SMTP server to use.
-
-sendemail.smtpserverport::
-	Default SMTP server port to use.
-
-sendemail.smtpuser::
-	Default SMTP-AUTH username.
-
-sendemail.smtppass::
-	Default SMTP-AUTH password.
-
-sendemail.smtpencryption::
-	Default encryption method.  Use 'ssl' for SSL (and specify an
-	appropriate port), or 'tls' for TLS.  Takes precedence over
-	'smtpssl' if both are specified.
-
-sendemail.smtpssl::
-	Legacy boolean that sets 'smtpencryption=ssl' if enabled.
 
 Author
 ------
@@ -250,10 +283,12 @@
 git-send-email is originally based upon
 send_lots_of_email.pl by Greg Kroah-Hartman.
 
+
 Documentation
 --------------
 Documentation by Ryan Anderson
 
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-shell.txt b/Documentation/git-shell.txt
index ff420f8..0f3ad81 100644
--- a/Documentation/git-shell.txt
+++ b/Documentation/git-shell.txt
@@ -18,8 +18,9 @@
 The commands can be executed only by the '-c' option; the shell is not
 interactive.
 
-Currently, only the 'git-receive-pack' and 'git-upload-pack' commands
-are permitted to be called, with a single required argument.
+Currently, only four commands are permitted to be called, 'git-receive-pack'
+'git-upload-pack' and 'git-upload-archive' with a single required argument, or
+'cvs server' (to invoke 'git-cvsserver').
 
 Author
 ------
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index 7ccf31c..42463a9 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -45,19 +45,16 @@
 	and subsequent lines are indented by `indent2` spaces. `width`,
 	`indent1`, and `indent2` default to 76, 6 and 9 respectively.
 
-FILES
------
 
-If the file `.mailmap` exists, it will be used for mapping author
-email addresses to a real author name. One mapping per line, first
-the author name followed by the email address enclosed by
-'<' and '>'. Use hash '#' for comments. Example:
+MAPPING AUTHORS
+---------------
 
-------------
-# Keep alphabetized
-Adam Morrow <adam@localhost.localdomain>
-Eve Jones <eve@laptop.(none)>
-------------
+The `.mailmap` feature is used to coalesce together commits by the same
+person in the shortlog, where their name and/or email address was
+spelled differently.
+
+include::mailmap.txt[]
+
 
 Author
 ------
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index fb269ff..7e9ff37 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -99,12 +99,12 @@
 	will show the revisions given by "git rev-list {caret}master
 	topic1 topic2"
 
+-g::
 --reflog[=<n>[,<base>]] [<ref>]::
 	Shows <n> most recent ref-log entries for the given
 	ref.  If <base> is given, <n> entries going back from
 	that entry.  <base> can be specified as count or date.
-	`-g` can be used as a short-hand for this option.  When
-	no explicit <ref> parameter is given, it defaults to the
+	When no explicit <ref> parameter is given, it defaults to the
 	current branch (or `HEAD` if it is detached).
 
 Note that --more, --list, --independent and --merge-base options
@@ -172,7 +172,7 @@
 your topic branch, it is shown as well.
 
 ------------
-$ git show-branch --reflog='10,1 hour ago' --list master
+$ git show-branch --reflog="10,1 hour ago" --list master
 ------------
 
 shows 10 reflog entries going back from the tip as of 1 hour ago.
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
new file mode 100644
index 0000000..7f251a5
--- /dev/null
+++ b/Documentation/git-stage.txt
@@ -0,0 +1,19 @@
+git-stage(1)
+==============
+
+NAME
+----
+git-stage - Add file contents to the staging area
+
+
+SYNOPSIS
+--------
+[verse]
+'git stage' args...
+
+
+DESCRIPTION
+-----------
+
+This is a synonym for linkgit:git-add[1].  Please refer to the
+documentation of that command.
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index e6652a7..3b8df44 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -12,8 +12,10 @@
 'git submodule' [--quiet] add [-b branch] [--] <repository> <path>
 'git submodule' [--quiet] status [--cached] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] update [--init] [--] [<path>...]
+'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...]
 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach <command>
+'git submodule' [--quiet] sync [--] [<path>...]
 
 
 DESCRIPTION
@@ -123,6 +125,30 @@
 	in the submodule between the given super project commit and the
 	index or working tree (switched by --cached) are shown.
 
+foreach::
+	Evaluates an arbitrary shell command in each checked out submodule.
+	The command has access to the variables $path and $sha1:
+	$path is the name of the submodule directory relative to the
+	superproject, and $sha1 is the commit as recorded in the superproject.
+	Any submodules defined in the superproject but not checked out are
+	ignored by this command. Unless given --quiet, foreach prints the name
+	of each submodule before evaluating the command.
+	A non-zero return from the command in any submodule causes
+	the processing to terminate. This can be overridden by adding '|| :'
+	to the end of the command.
++
+As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will
+show the path and currently checked out commit for each submodule.
+
+sync::
+	Synchronizes submodules' remote URL configuration setting
+	to the value specified in .gitmodules.  This is useful when
+	submodule URLs change upstream and you need to update your local
+	repositories accordingly.
++
+"git submodule sync" synchronizes all submodules while
+"git submodule sync -- A" synchronizes submodule "A" only.
+
 OPTIONS
 -------
 -q::
@@ -146,6 +172,11 @@
 	(the default). This limit only applies to modified submodules. The
 	size is always limited to 1 for added/deleted/typechanged submodules.
 
+-N::
+--no-fetch::
+	This option is only valid for the update command.
+	Don't fetch new objects from the remote site.
+
 <path>...::
 	Paths to submodule(s). When specified this will restrict the command
 	to only operate on the submodules found at the specified paths.
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 5d6d30f..9229d45 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -85,6 +85,10 @@
 	specified, the prefix must include a trailing slash.
 	Setting a prefix is useful if you wish to track multiple
 	projects that share a common repository.
+--ignore-paths=<regex>;;
+	When passed to 'init' or 'clone' this regular expression will
+	be preserved as a config key.  See 'fetch' for a description
+	of '--ignore-paths'.
 
 'fetch'::
 	Fetch unfetched revisions from the Subversion remote we are
@@ -92,6 +96,41 @@
 	.git/config file may be specified as an optional command-line
 	argument.
 
+--localtime;;
+	Store Git commit times in the local timezone instead of UTC.  This
+	makes 'git-log' (even without --date=local) show the same times
+	that `svn log` would in the local timezone.
+
+--parent;;
+	Fetch only from the SVN parent of the current HEAD.
+
+This doesn't interfere with interoperating with the Subversion
+repository you cloned from, but if you wish for your local Git
+repository to be able to interoperate with someone else's local Git
+repository, either don't use this option or you should both use it in
+the same local timezone.
+
+--ignore-paths=<regex>;;
+	This allows one to specify a Perl regular expression that will
+	cause skipping of all matching paths from checkout from SVN.
+	The '--ignore-paths' option should match for every 'fetch'
+	(including automatic fetches due to 'clone', 'dcommit',
+	'rebase', etc) on a given repository.
+
+config key: svn-remote.<name>.ignore-paths
+
+	If the ignore-paths config key is set and the command
+	line option is also given, both regular expressions
+	will be used.
+
+Examples:
+
+	--ignore-paths="^doc" - skip "doc*" directory for every
+	    fetch.
+
+	--ignore-paths="^[^/]+/(?:branches|tags)" - skip
+	    "branches" and "tags" of first level directories.
+
 'clone'::
 	Runs 'init' and 'fetch'.  It will automatically create a
 	directory based on the basename of the URL passed to it;
@@ -145,10 +184,30 @@
 	reused if a user is later given access to an alternate transport
 	method (e.g. `svn+ssh://` or `https://`) for commit.
 
+config key: svn-remote.<name>.commiturl
+
+config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
+
 	Using this option for any other purpose (don't ask)
 	is very strongly discouraged.
 --
 
+'branch'::
+	Create a branch in the SVN repository.
+
+-m;;
+--message;;
+	Allows to specify the commit message.
+
+-t;;
+--tag;;
+	Create a tag by using the tags_subdir instead of the branches_subdir
+	specified during git svn init.
+
+'tag'::
+	Create a tag in the SVN repository. This is a shorthand for
+	'branch -t'.
+
 'log'::
 	This should make it easy to look up svn log messages when svn
 	users refer to -r/--revision numbers.
@@ -341,7 +400,8 @@
 
 -q::
 --quiet::
-	Make 'git-svn' less verbose.
+	Make 'git-svn' less verbose. Specify a second time to make it
+	even less verbose.
 
 --repack[=<n>]::
 --repack-flags=<flags>::
@@ -372,7 +432,8 @@
 -n::
 --dry-run::
 
-This can be used with the 'dcommit' and 'rebase' commands.
+This can be used with the 'dcommit', 'rebase', 'branch' and 'tag'
+commands.
 
 For 'dcommit', print out the series of git arguments that would show
 which diffs would be committed to SVN.
@@ -381,6 +442,9 @@
 repository associated with the current branch and the URL of svn
 repository that will be fetched from.
 
+For 'branch' and 'tag', display the urls that will be used for copying when
+creating the branch or tag.
+
 --
 
 ADVANCED OPTIONS
@@ -455,6 +519,14 @@
 	the repository with a public http:// or svn:// URL in the
 	metadata so users of it will see the public URL.
 
+svn.brokenSymlinkWorkaround::
+This disables potentially expensive checks to workaround broken symlinks
+checked into SVN by broken clients.  Set this option to "false" if you
+track a SVN repository with many empty blobs that are not symlinks.
+This option may be changed while "git-svn" is running and take effect on
+the next revision fetched.  If unset, git-svn assumes this option to be
+"true".
+
 --
 
 Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
@@ -498,6 +570,8 @@
 	git svn clone http://svn.example.com/project -T trunk -b branches -t tags
 # View all branches and tags you have cloned:
 	git branch -r
+# Create a new branch in SVN
+    git svn branch waldo
 # Reset your master to trunk (or any other branch, replacing 'trunk'
 # with the appropriate name):
 	git reset --hard remotes/trunk
@@ -614,9 +688,9 @@
 ------------------------------------------------------------------------
 [svn-remote "project-a"]
 	url = http://server.org/svn
+	fetch = trunk/project-a:refs/remotes/project-a/trunk
 	branches = branches/*/project-a:refs/remotes/project-a/branches/*
 	tags = tags/*/project-a:refs/remotes/project-a/tags/*
-	trunk = trunk/project-a:refs/remotes/project-a/trunk
 ------------------------------------------------------------------------
 
 Keep in mind that the '*' (asterisk) wildcard of the local ref
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 1f34948..fa73321 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -12,7 +12,7 @@
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
 	<name> [<commit> | <object>]
 'git tag' -d <name>...
-'git tag' [-n[<num>]] -l [<pattern>]
+'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
 'git tag' -v <name>...
 
 DESCRIPTION
@@ -69,9 +69,12 @@
 	List tags with names that match the given pattern (or all if no pattern is given).
 	Typing "git tag" without arguments, also lists all tags.
 
+--contains <commit>::
+	Only list tags which contain the specified commit.
+
 -m <msg>::
 	Use the given tag message (instead of prompting).
-	If multiple `-m` options are given, there values are
+	If multiple `-m` options are given, their values are
 	concatenated as separate paragraphs.
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
 	is given.
@@ -208,7 +211,7 @@
 
 A one-shot pull is a sign that a commit history is now crossing
 the boundary between one circle of people (e.g. "people who are
-primarily interested in networking part of the kernel") who may
+primarily interested in the networking part of the kernel") who may
 have their own set of tags (e.g. "this is the third release
 candidate from the networking group to be proposed for general
 consumption with 2.6.21 release") to another circle of people
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
index 7f7a45b..278cf73 100644
--- a/Documentation/git-web--browse.txt
+++ b/Documentation/git-web--browse.txt
@@ -26,6 +26,7 @@
 * lynx
 * dillo
 * open (this is the default under Mac OS X GUI)
+* start (this is the default under MinGW)
 
 Custom commands may also be specified.
 
diff --git a/Documentation/git.txt b/Documentation/git.txt
index df420ae..470fdc5 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]]
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
     [-p|--paginate|--no-pager]
     [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
     [--help] COMMAND [ARGS]
@@ -43,16 +43,37 @@
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.0.2/git.html[documentation for release 1.6.0.2]
+* link:v1.6.2.3/git.html[documentation for release 1.6.2.3]
 
 * release notes for
+  link:RelNotes-1.6.2.3.txt[1.6.2.3],
+  link:RelNotes-1.6.2.2.txt[1.6.2.2],
+  link:RelNotes-1.6.2.1.txt[1.6.2.1],
+  link:RelNotes-1.6.2.txt[1.6.2].
+
+* link:v1.6.1.3/git.html[documentation for release 1.6.1.3]
+
+* release notes for
+  link:RelNotes-1.6.1.3.txt[1.6.1.3],
+  link:RelNotes-1.6.1.2.txt[1.6.1.2],
+  link:RelNotes-1.6.1.1.txt[1.6.1.1],
+  link:RelNotes-1.6.1.txt[1.6.1].
+
+* link:v1.6.0.6/git.html[documentation for release 1.6.0.6]
+
+* release notes for
+  link:RelNotes-1.6.0.6.txt[1.6.0.6],
+  link:RelNotes-1.6.0.5.txt[1.6.0.5],
+  link:RelNotes-1.6.0.4.txt[1.6.0.4],
+  link:RelNotes-1.6.0.3.txt[1.6.0.3],
   link:RelNotes-1.6.0.2.txt[1.6.0.2],
   link:RelNotes-1.6.0.1.txt[1.6.0.1],
   link:RelNotes-1.6.0.txt[1.6.0].
 
-* link:v1.5.6.5/git.html[documentation for release 1.5.6.5]
+* link:v1.5.6.6/git.html[documentation for release 1.5.6.6]
 
 * release notes for
+  link:RelNotes-1.5.6.6.txt[1.5.6.6],
   link:RelNotes-1.5.6.5.txt[1.5.6.5],
   link:RelNotes-1.5.6.4.txt[1.5.6.4],
   link:RelNotes-1.5.6.3.txt[1.5.6.3],
@@ -60,18 +81,22 @@
   link:RelNotes-1.5.6.1.txt[1.5.6.1],
   link:RelNotes-1.5.6.txt[1.5.6].
 
-* link:v1.5.5.4/git.html[documentation for release 1.5.5.4]
+* link:v1.5.5.6/git.html[documentation for release 1.5.5.6]
 
 * release notes for
+  link:RelNotes-1.5.5.6.txt[1.5.5.6],
+  link:RelNotes-1.5.5.5.txt[1.5.5.5],
   link:RelNotes-1.5.5.4.txt[1.5.5.4],
   link:RelNotes-1.5.5.3.txt[1.5.5.3],
   link:RelNotes-1.5.5.2.txt[1.5.5.2],
   link:RelNotes-1.5.5.1.txt[1.5.5.1],
   link:RelNotes-1.5.5.txt[1.5.5].
 
-* link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
+* link:v1.5.4.7/git.html[documentation for release 1.5.4.7]
 
 * release notes for
+  link:RelNotes-1.5.4.7.txt[1.5.4.7],
+  link:RelNotes-1.5.4.6.txt[1.5.4.6],
   link:RelNotes-1.5.4.5.txt[1.5.4.5],
   link:RelNotes-1.5.4.4.txt[1.5.4.4],
   link:RelNotes-1.5.4.3.txt[1.5.4.3],
@@ -155,6 +180,10 @@
 	environment variable. If no path is given, 'git' will print
 	the current setting and then exit.
 
+--html-path::
+	Print the path to wherever your git HTML documentation is installed
+	and exit.
+
 -p::
 --paginate::
 	Pipe all output into 'less' (or if set, $PAGER).
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index c4aebc4..aaa073e 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -18,10 +18,10 @@
 
 Each line in `gitattributes` file is of form:
 
-	glob	attr1 attr2 ...
+	pattern	attr1 attr2 ...
 
-That is, a glob pattern followed by an attributes list,
-separated by whitespaces.  When the glob pattern matches the
+That is, a pattern followed by an attributes list,
+separated by whitespaces.  When the pattern matches the
 path in question, the attributes listed on the line are given to
 the path.
 
@@ -48,13 +48,14 @@
 
 Unspecified::
 
-	No glob pattern matches the path, and nothing says if
+	No pattern matches the path, and nothing says if
 	the path has or does not have the attribute, the
 	attribute for the path is said to be Unspecified.
 
-When more than one glob pattern matches the path, a later line
+When more than one pattern matches the path, a later line
 overrides an earlier line.  This overriding is done per
-attribute.
+attribute.  The rules how the pattern matches paths are the
+same as in `.gitignore` files; see linkgit:gitignore[5].
 
 When deciding what attributes are assigned to a path, git
 consults `$GIT_DIR/info/attributes` file (which has the highest
@@ -216,10 +217,12 @@
 `diff`
 ^^^^^^
 
-The attribute `diff` affects if 'git-diff' generates textual
-patch for the path or just says `Binary files differ`.  It also
-can affect what line is shown on the hunk header `@@ -k,l +n,m @@`
-line.
+The attribute `diff` affects how 'git' generates diffs for particular
+files. It can tell git whether to generate a textual patch for the path
+or to treat the path as a binary file.  It can also affect what line is
+shown on the hunk header `@@ -k,l +n,m @@` line, tell git to use an
+external command to generate the diff, or ask git to convert binary
+files to a text format before generating the diff.
 
 Set::
 
@@ -230,7 +233,8 @@
 Unset::
 
 	A path to which the `diff` attribute is unset will
-	generate `Binary files differ`.
+	generate `Binary files differ` (or a binary patch, if
+	binary patches are enabled).
 
 Unspecified::
 
@@ -241,21 +245,21 @@
 
 String::
 
-	Diff is shown using the specified custom diff driver.
-	The driver program is given its input using the same
-	calling convention as used for GIT_EXTERNAL_DIFF
-	program.  This name is also used for custom hunk header
-	selection.
+	Diff is shown using the specified diff driver.  Each driver may
+	specify one or more options, as described in the following
+	section. The options for the diff driver "foo" are defined
+	by the configuration variables in the "diff.foo" section of the
+	git config file.
 
 
-Defining a custom diff driver
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Defining an external diff driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The definition of a diff driver is done in `gitconfig`, not
 `gitattributes` file, so strictly speaking this manual page is a
 wrong place to talk about it.  However...
 
-To define a custom diff driver `jcdiff`, add a section to your
+To define an external diff driver `jcdiff`, add a section to your
 `$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
 
 ----------------------------------------------------------------
@@ -293,7 +297,8 @@
 
 Then, you would define a "diff.tex.xfuncname" configuration to
 specify a regular expression that matches a line that you would
-want to appear as the hunk header "TEXT", like this:
+want to appear as the hunk header "TEXT". Add a section to your
+`$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
 
 ------------------------
 [diff "tex"]
@@ -314,15 +319,83 @@
 
 - `bibtex` suitable for files with BibTeX coded references.
 
+- `cpp` suitable for source code in the C and C++ languages.
+
+- `html` suitable for HTML/XHTML documents.
+
 - `java` suitable for source code in the Java language.
 
+- `objc` suitable for source code in the Objective-C language.
+
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
+- `php` suitable for source code in the PHP language.
+
+- `python` suitable for source code in the Python language.
+
 - `ruby` suitable for source code in the Ruby language.
 
 - `tex` suitable for source code for LaTeX documents.
 
 
+Customizing word diff
+^^^^^^^^^^^^^^^^^^^^^
+
+You can customize the rules that `git diff --color-words` uses to
+split words in a line, by specifying an appropriate regular expression
+in the "diff.*.wordRegex" configuration variable.  For example, in TeX
+a backslash followed by a sequence of letters forms a command, but
+several such commands can be run together without intervening
+whitespace.  To separate them, use a regular expression in your
+`$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
+
+------------------------
+[diff "tex"]
+	wordRegex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+"
+------------------------
+
+A built-in pattern is provided for all languages listed in the
+previous section.
+
+
+Performing text diffs of binary files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Sometimes it is desirable to see the diff of a text-converted
+version of some binary files. For example, a word processor
+document can be converted to an ASCII text representation, and
+the diff of the text shown. Even though this conversion loses
+some information, the resulting diff is useful for human
+viewing (but cannot be applied directly).
+
+The `textconv` config option is used to define a program for
+performing such a conversion. The program should take a single
+argument, the name of a file to convert, and produce the
+resulting text on stdout.
+
+For example, to show the diff of the exif information of a
+file instead of the binary information (assuming you have the
+exif tool installed), add the following section to your
+`$GIT_DIR/config` file (or `$HOME/.gitconfig` file):
+
+------------------------
+[diff "jpg"]
+	textconv = exif
+------------------------
+
+NOTE: The text conversion is generally a one-way conversion;
+in this example, we lose the actual image contents and focus
+just on the text data. This means that diffs generated by
+textconv are _not_ suitable for applying. For this reason,
+only `git diff` and the `git log` family of commands (i.e.,
+log, whatchanged, show) will perform text conversion. `git
+format-patch` will never generate this output. If you want to
+send somebody a text-converted diff of a binary file (e.g.,
+because it quickly conveys the changes you have made), you
+should generate it separately and send it as a comment _in
+addition to_ the usual binary diff that you might send.
+
+
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -487,6 +560,23 @@
 commit hash.
 
 
+Viewing files in GUI tools
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`encoding`
+^^^^^^^^^^
+
+The value of this attribute specifies the character encoding that should
+be used by GUI tools (e.g. linkgit:gitk[1] and linkgit:git-gui[1]) to
+display the contents of the relevant file. Note that due to performance
+considerations linkgit:gitk[1] does not use this attribute unless you
+manually enable per-file encodings in its options.
+
+If this attribute is not set or has an invalid value, the value of the
+`gui.encoding` configuration variable is used instead
+(See linkgit:git-config[1]).
+
+
 USING ATTRIBUTE MACROS
 ----------------------
 
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 29e5929..be39ed7 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -46,20 +46,20 @@
 scripting git:
 
  * it's preferred to use the non dashed form of git commands, which means that
-   you should prefer `"git foo"` to `"git-foo"`.
+   you should prefer `git foo` to `git-foo`.
 
- * splitting short options to separate words (prefer `"git foo -a -b"`
-   to `"git foo -ab"`, the latter may not even work).
+ * splitting short options to separate words (prefer `git foo -a -b`
+   to `git foo -ab`, the latter may not even work).
 
  * when a command line option takes an argument, use the 'sticked' form.  In
-   other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short
-   options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"`
+   other words, write `git foo -oArg` instead of `git foo -o Arg` for short
+   options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg`
    for long options.  An option that takes optional option-argument must be
    written in the 'sticked' form.
 
  * when you give a revision parameter to a command, make sure the parameter is
    not ambiguous with a name of a file in the work tree.  E.g. do not write
-   `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work
+   `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work
    if you happen to have a file called `HEAD` in the work tree.
 
 
@@ -99,17 +99,17 @@
 
 Negating options
 ~~~~~~~~~~~~~~~~
-Options with long option names can be negated by prefixing `"--no-"`. For
-example, `"git branch"` has the option `"--track"` which is 'on' by default. You
-can use `"--no-track"` to override that behaviour. The same goes for `"--color"`
-and `"--no-color"`.
+Options with long option names can be negated by prefixing `--no-`. For
+example, `git branch` has the option `--track` which is 'on' by default. You
+can use `--no-track` to override that behaviour. The same goes for `--color`
+and `--no-color`.
 
 
 Aggregating short options
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 Commands that support the enhanced option parser allow you to aggregate short
-options. This means that you can for example use `"git rm -rf"` or
-`"git clean -fdx"`.
+options. This means that you can for example use `git rm -rf` or
+`git clean -fdx`.
 
 
 Separating argument from the option
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index 896cbdf..7ba5e58 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -899,7 +899,7 @@
 ----------------
 	Auto-merging hello
 	CONFLICT (content): Merge conflict in hello
-	Automatic merge failed; fix up by hand
+	Automatic merge failed; fix conflicts and then commit the result.
 ----------------
 
 It tells you that it did an "Automatic merge", which
@@ -993,7 +993,7 @@
 
 ----------------
 Updating from ae3a2da... to a80b4aa....
-Fast forward
+Fast forward (no commit created; -m option ignored)
  example |    1 +
  hello   |    1 +
  2 files changed, 2 insertions(+), 0 deletions(-)
@@ -1243,10 +1243,10 @@
 ------------
 
 In our example of only two files, we did not have unchanged
-files so only 'example' resulted in collapsing, but in real-life
-large projects, only small number of files change in one commit,
-and this 'collapsing' tends to trivially merge most of the paths
-fairly quickly, leaving only a handful the real changes in non-zero
+files so only 'example' resulted in collapsing.  But in real-life
+large projects, when only a small number of files change in one commit,
+this 'collapsing' tends to trivially merge most of the paths
+fairly quickly, leaving only a handful of real changes in non-zero
 stages.
 
 To look at only non-zero stages, use `\--unmerged` flag:
@@ -1265,9 +1265,8 @@
 
 ------------
 $ git merge-index git-merge-one-file hello
-Auto-merging hello.
-merge: warning: conflicts during merge
-ERROR: Merge conflict in hello.
+Auto-merging hello
+ERROR: Merge conflict in hello
 fatal: merge program failed
 ------------
 
@@ -1447,7 +1446,7 @@
 never.
 
 If you run `git repack` again at this point, it will say
-"Nothing to pack". Once you continue your development and
+"Nothing new to pack.". Once you continue your development and
 accumulate the changes, running `git repack` again will create a
 new pack, that contains objects created since you packed your
 repository the last time. We recommend that you pack your project
@@ -1693,6 +1692,7 @@
 linkgit:gittutorial[7],
 linkgit:gittutorial-2[7],
 linkgit:gitcvs-migration[7],
+linkgit:git-help[1],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 024abb2..1c73673 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -15,11 +15,15 @@
 
 Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
-'git-init' is run, a handful example hooks are copied in the
+'git-init' is run, a handful of example hooks are copied into the
 `hooks` directory of the new repository, but by default they are
 all disabled.  To enable a hook, rename it by removing its `.sample`
 suffix.
 
+NOTE: It is also a requirement for a given hook to be executable.
+However - in a freshly initialized repository - the `.sample` files are
+executable by default.
+
 This document describes the currently defined hooks.
 
 applypatch-msg
@@ -147,6 +151,10 @@
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
 This hook cannot affect the outcome of 'git-checkout'.
 
+It is also run after 'git-clone', unless the --no-checkout (-n) option is
+used. The first parameter given to the hook is the null-ref, the second the
+ref of the new HEAD and the flag is always 1.
+
 This hook can be used to perform repository validity checks, auto-display
 differences from the previous HEAD if different, or set working dir metadata
 properties.
diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt
index 5ef3687..cf465cb 100644
--- a/Documentation/gitk.txt
+++ b/Documentation/gitk.txt
@@ -47,7 +47,8 @@
 
 	After an attempt to merge stops with conflicts, show the commits on
 	the history between two branches (i.e. the HEAD and the MERGE_HEAD)
-	that modify the conflicted files.
+	that modify the conflicted files and do not exist on all the heads
+	being merged.
 
 --argscmd=<command>::
 	Command to be run each time gitk has to determine the list of
@@ -56,6 +57,11 @@
 	Use this instead of explicitly specifying <revs> if the set of
 	commits to show may vary between refreshes.
 
+--select-commit=<ref>::
+
+	Automatically select the specified commit after loading the graph.
+	Default behavior is equivalent to specifying '--select-commit=HEAD'.
+
 <revs>::
 
 	Limit the revisions to show. This can be either a single revision
@@ -68,7 +74,7 @@
 <path>...::
 
 	Limit commits to the ones touching files in the given paths. Note, to
-	avoid ambiguity wrt. revision names use "--" to separate the paths
+	avoid ambiguity with respect to revision names use "--" to separate the paths
 	from any preceding options.
 
 Examples
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index a969b3f..1befca9 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -134,7 +134,8 @@
 	Hooks are customization scripts used by various git
 	commands.  A handful of sample hooks are installed when
 	'git-init' is run, but all of them are disabled by
-	default.  To enable, they need to be made executable.
+	default.  To enable, the `.sample` suffix has to be
+	removed from the filename by renaming.
 	Read linkgit:githooks[5] for more details about
 	each hook.
 
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index 6609046..dc8fc3a 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -32,22 +32,27 @@
 $ echo 'hello world' > file.txt
 $ git add .
 $ git commit -a -m "initial commit"
-Created initial commit 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
+[master (root-commit) 54196cc] initial commit
+ 1 files changed, 1 insertions(+), 0 deletions(-)
  create mode 100644 file.txt
 $ echo 'hello world!' >file.txt
 $ git commit -a -m "add emphasis"
-Created commit c4d59f390b9cfd4318117afde11d601c1085f241
+[master c4d59f3] add emphasis
+ 1 files changed, 1 insertions(+), 1 deletions(-)
 ------------------------------------------------
 
-What are the 40 digits of hex that git responded to the commit with?
+What are the 7 digits of hex that git responded to the commit with?
 
 We saw in part one of the tutorial that commits have names like this.
 It turns out that every object in the git history is stored under
-such a 40-digit hex name.  That name is the SHA1 hash of the object's
+a 40-digit hex name.  That name is the SHA1 hash of the object's
 contents; among other things, this ensures that git will never store
 the same data twice (since identical data is given an identical SHA1
 name), and that the contents of a git object will never change (since
-that would change the object's name as well).
+that would change the object's name as well). The 7 char hex strings
+here are simply the abbreviation of such 40 character long strings.
+Abbreviations can be used everywhere where the 40 character strings
+can be used, so long as they are unambiguous.
 
 It is expected that the content of the commit object you created while
 following the example above generates a different SHA1 hash than
@@ -420,6 +425,7 @@
 linkgit:gitcvs-migration[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
+linkgit:git-help[1],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 384972c..c5d5596 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -26,6 +26,15 @@
 $ man git-log
 ------------------------------------------------
 
+or:
+
+------------------------------------------------
+$ git help log
+------------------------------------------------
+
+With the latter, you can use the manual viewer of your choice; see
+linkgit:git-help[1] for more information.
+
 It is a good idea to introduce yourself to git with your name and
 public email address before doing any operation.  The easiest
 way to do so is:
@@ -299,9 +308,7 @@
 
 This merges the changes from Bob's "master" branch into Alice's
 current branch.  If Alice has made her own changes in the meantime,
-then she may need to manually fix any conflicts.  (Note that the
-"master" argument in the above command is actually unnecessary, as it
-is the default.)
+then she may need to manually fix any conflicts.
 
 The "pull" command thus performs two operations: it fetches changes
 from a remote branch, then merges them into the current branch.
@@ -581,7 +588,7 @@
 then merged back together, the order in which 'git-log' presents
 those commits is meaningless.
 
-Most projects with multiple contributors (such as the linux kernel,
+Most projects with multiple contributors (such as the Linux kernel,
 or git itself) have frequent merges, and 'gitk' does a better job of
 visualizing their history.  For example,
 
@@ -633,7 +640,7 @@
 
   * linkgit:git-format-patch[1], linkgit:git-am[1]: These convert
     series of git commits into emailed patches, and vice versa,
-    useful for projects such as the linux kernel which rely heavily
+    useful for projects such as the Linux kernel which rely heavily
     on emailed patches.
 
   * linkgit:git-bisect[1]: When there is a regression in your
@@ -653,6 +660,7 @@
 linkgit:gitcvs-migration[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
+linkgit:git-help[1],
 link:everyday.html[Everyday git],
 link:user-manual.html[The Git User's Manual]
 
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
new file mode 100644
index 0000000..2b021e3
--- /dev/null
+++ b/Documentation/gitworkflows.txt
@@ -0,0 +1,364 @@
+gitworkflows(7)
+===============
+
+NAME
+----
+gitworkflows - An overview of recommended workflows with git
+
+SYNOPSIS
+--------
+git *
+
+
+DESCRIPTION
+-----------
+
+This document attempts to write down and motivate some of the workflow
+elements used for `git.git` itself.  Many ideas apply in general,
+though the full workflow is rarely required for smaller projects with
+fewer people involved.
+
+We formulate a set of 'rules' for quick reference, while the prose
+tries to motivate each of them.  Do not always take them literally;
+you should value good reasons for your actions higher than manpages
+such as this one.
+
+
+SEPARATE CHANGES
+----------------
+
+As a general rule, you should try to split your changes into small
+logical steps, and commit each of them.  They should be consistent,
+working independently of any later commits, pass the test suite, etc.
+This makes the review process much easier, and the history much more
+useful for later inspection and analysis, for example with
+linkgit:git-blame[1] and linkgit:git-bisect[1].
+
+To achieve this, try to split your work into small steps from the very
+beginning. It is always easier to squash a few commits together than
+to split one big commit into several.  Don't be afraid of making too
+small or imperfect steps along the way. You can always go back later
+and edit the commits with `git rebase \--interactive` before you
+publish them.  You can use `git stash save \--keep-index` to run the
+test suite independent of other uncommitted changes; see the EXAMPLES
+section of linkgit:git-stash[1].
+
+
+MANAGING BRANCHES
+-----------------
+
+There are two main tools that can be used to include changes from one
+branch on another: linkgit:git-merge[1] and
+linkgit:git-cherry-pick[1].
+
+Merges have many advantages, so we try to solve as many problems as
+possible with merges alone.  Cherry-picking is still occasionally
+useful; see "Merging upwards" below for an example.
+
+Most importantly, merging works at the branch level, while
+cherry-picking works at the commit level.  This means that a merge can
+carry over the changes from 1, 10, or 1000 commits with equal ease,
+which in turn means the workflow scales much better to a large number
+of contributors (and contributions).  Merges are also easier to
+understand because a merge commit is a "promise" that all changes from
+all its parents are now included.
+
+There is a tradeoff of course: merges require a more careful branch
+management.  The following subsections discuss the important points.
+
+
+Graduation
+~~~~~~~~~~
+
+As a given feature goes from experimental to stable, it also
+"graduates" between the corresponding branches of the software.
+`git.git` uses the following 'integration branches':
+
+* 'maint' tracks the commits that should go into the next "maintenance
+  release", i.e., update of the last released stable version;
+
+* 'master' tracks the commits that should go into the next release;
+
+* 'next' is intended as a testing branch for topics being tested for
+  stability for master.
+
+There is a fourth official branch that is used slightly differently:
+
+* 'pu' (proposed updates) is an integration branch for things that are
+  not quite ready for inclusion yet (see "Integration Branches"
+  below).
+
+Each of the four branches is usually a direct descendant of the one
+above it.
+
+Conceptually, the feature enters at an unstable branch (usually 'next'
+or 'pu'), and "graduates" to 'master' for the next release once it is
+considered stable enough.
+
+
+Merging upwards
+~~~~~~~~~~~~~~~
+
+The "downwards graduation" discussed above cannot be done by actually
+merging downwards, however, since that would merge 'all' changes on
+the unstable branch into the stable one.  Hence the following:
+
+.Merge upwards
+[caption="Rule: "]
+=====================================
+Always commit your fixes to the oldest supported branch that require
+them.  Then (periodically) merge the integration branches upwards into each
+other.
+=====================================
+
+This gives a very controlled flow of fixes.  If you notice that you
+have applied a fix to e.g. 'master' that is also required in 'maint',
+you will need to cherry-pick it (using linkgit:git-cherry-pick[1])
+downwards.  This will happen a few times and is nothing to worry about
+unless you do it very frequently.
+
+
+Topic branches
+~~~~~~~~~~~~~~
+
+Any nontrivial feature will require several patches to implement, and
+may get extra bugfixes or improvements during its lifetime.
+
+Committing everything directly on the integration branches leads to many
+problems: Bad commits cannot be undone, so they must be reverted one
+by one, which creates confusing histories and further error potential
+when you forget to revert part of a group of changes.  Working in
+parallel mixes up the changes, creating further confusion.
+
+Use of "topic branches" solves these problems.  The name is pretty
+self explanatory, with a caveat that comes from the "merge upwards"
+rule above:
+
+.Topic branches
+[caption="Rule: "]
+=====================================
+Make a side branch for every topic (feature, bugfix, ...). Fork it off
+at the oldest integration branch that you will eventually want to merge it
+into.
+=====================================
+
+Many things can then be done very naturally:
+
+* To get the feature/bugfix into an integration branch, simply merge
+  it.  If the topic has evolved further in the meantime, merge again.
+  (Note that you do not necessarily have to merge it to the oldest
+  integration branch first.  For example, you can first merge a bugfix
+  to 'next', give it some testing time, and merge to 'maint' when you
+  know it is stable.)
+
+* If you find you need new features from the branch 'other' to continue
+  working on your topic, merge 'other' to 'topic'.  (However, do not
+  do this "just habitually", see below.)
+
+* If you find you forked off the wrong branch and want to move it
+  "back in time", use linkgit:git-rebase[1].
+
+Note that the last point clashes with the other two: a topic that has
+been merged elsewhere should not be rebased.  See the section on
+RECOVERING FROM UPSTREAM REBASE in linkgit:git-rebase[1].
+
+We should point out that "habitually" (regularly for no real reason)
+merging an integration branch into your topics -- and by extension,
+merging anything upstream into anything downstream on a regular basis
+-- is frowned upon:
+
+.Merge to downstream only at well-defined points
+[caption="Rule: "]
+=====================================
+Do not merge to downstream except with a good reason: upstream API
+changes affect your branch; your branch no longer merges to upstream
+cleanly; etc.
+=====================================
+
+Otherwise, the topic that was merged to suddenly contains more than a
+single (well-separated) change.  The many resulting small merges will
+greatly clutter up history.  Anyone who later investigates the history
+of a file will have to find out whether that merge affected the topic
+in development.  An upstream might even inadvertently be merged into a
+"more stable" branch.  And so on.
+
+
+Throw-away integration
+~~~~~~~~~~~~~~~~~~~~~~
+
+If you followed the last paragraph, you will now have many small topic
+branches, and occasionally wonder how they interact.  Perhaps the
+result of merging them does not even work?  But on the other hand, we
+want to avoid merging them anywhere "stable" because such merges
+cannot easily be undone.
+
+The solution, of course, is to make a merge that we can undo: merge
+into a throw-away branch.
+
+.Throw-away integration branches
+[caption="Rule: "]
+=====================================
+To test the interaction of several topics, merge them into a
+throw-away branch.  You must never base any work on such a branch!
+=====================================
+
+If you make it (very) clear that this branch is going to be deleted
+right after the testing, you can even publish this branch, for example
+to give the testers a chance to work with it, or other developers a
+chance to see if their in-progress work will be compatible.  `git.git`
+has such an official throw-away integration branch called 'pu'.
+
+
+DISTRIBUTED WORKFLOWS
+---------------------
+
+After the last section, you should know how to manage topics.  In
+general, you will not be the only person working on the project, so
+you will have to share your work.
+
+Roughly speaking, there are two important workflows: merge and patch.
+The important difference is that the merge workflow can propagate full
+history, including merges, while patches cannot.  Both workflows can
+be used in parallel: in `git.git`, only subsystem maintainers use
+the merge workflow, while everyone else sends patches.
+
+Note that the maintainer(s) may impose restrictions, such as
+"Signed-off-by" requirements, that all commits/patches submitted for
+inclusion must adhere to.  Consult your project's documentation for
+more information.
+
+
+Merge workflow
+~~~~~~~~~~~~~~
+
+The merge workflow works by copying branches between upstream and
+downstream.  Upstream can merge contributions into the official
+history; downstream base their work on the official history.
+
+There are three main tools that can be used for this:
+
+* linkgit:git-push[1] copies your branches to a remote repository,
+  usually to one that can be read by all involved parties;
+
+* linkgit:git-fetch[1] that copies remote branches to your repository;
+  and
+
+* linkgit:git-pull[1] that does fetch and merge in one go.
+
+Note the last point.  Do 'not' use 'git-pull' unless you actually want
+to merge the remote branch.
+
+Getting changes out is easy:
+
+.Push/pull: Publishing branches/topics
+[caption="Recipe: "]
+=====================================
+`git push <remote> <branch>` and tell everyone where they can fetch
+from.
+=====================================
+
+You will still have to tell people by other means, such as mail.  (Git
+provides the linkgit:git-request-pull[1] to send preformatted pull
+requests to upstream maintainers to simplify this task.)
+
+If you just want to get the newest copies of the integration branches,
+staying up to date is easy too:
+
+.Push/pull: Staying up to date
+[caption="Recipe: "]
+=====================================
+Use `git fetch <remote>` or `git remote update` to stay up to date.
+=====================================
+
+Then simply fork your topic branches from the stable remotes as
+explained earlier.
+
+If you are a maintainer and would like to merge other people's topic
+branches to the integration branches, they will typically send a
+request to do so by mail.  Such a request looks like
+
+-------------------------------------
+Please pull from
+    <url> <branch>
+-------------------------------------
+
+In that case, 'git-pull' can do the fetch and merge in one go, as
+follows.
+
+.Push/pull: Merging remote topics
+[caption="Recipe: "]
+=====================================
+`git pull <url> <branch>`
+=====================================
+
+Occasionally, the maintainer may get merge conflicts when he tries to
+pull changes from downstream.  In this case, he can ask downstream to
+do the merge and resolve the conflicts themselves (perhaps they will
+know better how to resolve them).  It is one of the rare cases where
+downstream 'should' merge from upstream.
+
+
+Patch workflow
+~~~~~~~~~~~~~~
+
+If you are a contributor that sends changes upstream in the form of
+emails, you should use topic branches as usual (see above).  Then use
+linkgit:git-format-patch[1] to generate the corresponding emails
+(highly recommended over manually formatting them because it makes the
+maintainer's life easier).
+
+.format-patch/am: Publishing branches/topics
+[caption="Recipe: "]
+=====================================
+* `git format-patch -M upstream..topic` to turn them into preformatted
+  patch files
+* `git send-email --to=<recipient> <patches>`
+=====================================
+
+See the linkgit:git-format-patch[1] and linkgit:git-send-email[1]
+manpages for further usage notes.
+
+If the maintainer tells you that your patch no longer applies to the
+current upstream, you will have to rebase your topic (you cannot use a
+merge because you cannot format-patch merges):
+
+.format-patch/am: Keeping topics up to date
+[caption="Recipe: "]
+=====================================
+`git pull --rebase <url> <branch>`
+=====================================
+
+You can then fix the conflicts during the rebase.  Presumably you have
+not published your topic other than by mail, so rebasing it is not a
+problem.
+
+If you receive such a patch series (as maintainer, or perhaps as a
+reader of the mailing list it was sent to), save the mails to files,
+create a new topic branch and use 'git-am' to import the commits:
+
+.format-patch/am: Importing patches
+[caption="Recipe: "]
+=====================================
+`git am < patch`
+=====================================
+
+One feature worth pointing out is the three-way merge, which can help
+if you get conflicts: `git am -3` will use index information contained
+in patches to figure out the merge base.  See linkgit:git-am[1] for
+other options.
+
+
+SEE ALSO
+--------
+linkgit:gittutorial[7],
+linkgit:git-push[1],
+linkgit:git-pull[1],
+linkgit:git-merge[1],
+linkgit:git-rebase[1],
+linkgit:git-format-patch[1],
+linkgit:git-send-email[1],
+linkgit:git-am[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite.
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 9b4a4f4..572374f 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -183,7 +183,8 @@
 	and potentially aborted, and allow for a post-notification after the
 	operation is done. The hook scripts are found in the
 	`$GIT_DIR/hooks/` directory, and are enabled by simply
-	making them executable.
+	removing the `.sample` suffix from the filename. In earlier versions
+	of git you had to make them executable.
 
 [[def_index]]index::
 	A collection of files with stat information, whose contents are stored
@@ -261,7 +262,7 @@
 	'origin' is used for that purpose. New upstream updates
 	will be fetched into remote <<def_tracking_branch,tracking branches>> named
 	origin/name-of-upstream-branch, which you can see using
-	"`git branch -r`".
+	`git branch -r`.
 
 [[def_pack]]pack::
 	A set of objects which have been compressed into one file (to save space
@@ -448,6 +449,12 @@
 	An <<def_object,object>> which is not <<def_reachable,reachable>> from a
 	<<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
 
+[[def_upstream_branch]]upstream branch::
+	The default <<def_branch,branch>> that is merged into the branch in
+	question (or the branch in question is rebased onto). It is configured
+	via branch.<name>.remote and branch.<name>.merge. If the upstream branch
+	of 'A' is 'origin/B' sometimes we say "'A' is tracking 'origin/B'".
+
 [[def_working_tree]]working tree::
 	The tree of actual checked out files.  The working tree is
 	normally equal to the <<def_HEAD,HEAD>> plus any local changes
diff --git a/Documentation/howto/rebase-and-edit.txt b/Documentation/howto/rebase-and-edit.txt
deleted file mode 100644
index 554909f..0000000
--- a/Documentation/howto/rebase-and-edit.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-Date:	Sat, 13 Aug 2005 22:16:02 -0700 (PDT)
-From:	Linus Torvalds <torvalds@osdl.org>
-To:	Steve French <smfrench@austin.rr.com>
-cc:	git@vger.kernel.org
-Subject: Re: sending changesets from the middle of a git tree
-Abstract: In this article, Linus demonstrates how a broken commit
- in a sequence of commits can be removed by rewinding the head and
- reapplying selected changes.
-
-On Sat, 13 Aug 2005, Linus Torvalds wrote:
-
-> That's correct. Same things apply: you can move a patch over, and create a
-> new one with a modified comment, but basically the _old_ commit will be
-> immutable.
-
-Let me clarify.
-
-You can entirely _drop_ old branches, so commits may be immutable, but
-nothing forces you to keep them. Of course, when you drop a commit, you'll
-always end up dropping all the commits that depended on it, and if you
-actually got somebody else to pull that commit you can't drop it from
-_their_ repository, but undoing things is not impossible.
-
-For example, let's say that you've made a mess of things: you've committed
-three commits "old->a->b->c", and you notice that "a" was broken, but you
-want to save "b" and "c". What you can do is
-
-	# Create a branch "broken" that is the current code
-	# for reference
-	git branch broken
-
-	# Reset the main branch to three parents back: this
-	# effectively undoes the three top commits
-	git reset HEAD^^^
-	git checkout -f
-
-	# Check the result visually to make sure you know what's
-	# going on
-	gitk --all
-
-	# Re-apply the two top ones from "broken"
-	#
-	# First "parent of broken" (aka b):
-	git-diff-tree -p broken^ | git-apply --index
-	git commit --reedit=broken^
-
-	# Then "top of broken" (aka c):
-	git-diff-tree -p broken | git-apply --index
-	git commit --reedit=broken
-
-and you've now re-applied (and possibly edited the comments) the two
-commits b/c, and commit "a" is basically gone (it still exists in the
-"broken" branch, of course).
-
-Finally, check out the end result again:
-
-	# Look at the new commit history
-	gitk --all
-
-to see that everything looks sensible.
-
-And then, you can just remove the broken branch if you decide you really
-don't want it:
-
-	# remove 'broken' branch
-	git branch -d broken
-
-	# Prune old objects if you're really really sure
-	git prune
-
-And yeah, I'm sure there are other ways of doing this. And as usual, the
-above is totally untested, and I just wrote it down in this email, so if
-I've done something wrong, you'll have to figure it out on your own ;)
-
-			Linus
--
-To unsubscribe from this list: send the line "unsubscribe git" in
-the body of a message to majordomo@vger.kernel.org
-More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt
index d214d4b..74a1c0c 100644
--- a/Documentation/howto/rebase-from-internal-branch.txt
+++ b/Documentation/howto/rebase-from-internal-branch.txt
@@ -27,7 +27,7 @@
 I just have done a simpler one, this time using only the core
 GIT tools.
 
-I had a handful commits that were ahead of master in pu, and I
+I had a handful of commits that were ahead of master in pu, and I
 wanted to add some documentation bypassing my usual habit of
 placing new things in pu first.  At the beginning, the commit
 ancestry graph looked like this:
diff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt
new file mode 100644
index 0000000..3b4a390
--- /dev/null
+++ b/Documentation/howto/revert-a-faulty-merge.txt
@@ -0,0 +1,179 @@
+Date: Fri, 19 Dec 2008 00:45:19 -0800
+From: Linus Torvalds <torvalds@linux-foundation.org>, Junio C Hamano <gitster@pobox.com>
+Subject: Re: Odd merge behaviour involving reverts
+Abstract: Sometimes a branch that was already merged to the mainline
+ is later found to be faulty.  Linus and Junio give guidance on
+ recovering from such a premature merge and continuing development
+ after the offending branch is fixed.
+Message-ID: <7vocz8a6zk.fsf@gitster.siamese.dyndns.org>
+References: <alpine.LFD.2.00.0812181949450.14014@localhost.localdomain>
+
+Alan <alan@clueserver.org> said:
+
+    I have a master branch.  We have a branch off of that that some
+    developers are doing work on.  They claim it is ready. We merge it
+    into the master branch.  It breaks something so we revert the merge.
+    They make changes to the code.  they get it to a point where they say
+    it is ok and we merge again.
+
+    When examined, we find that code changes made before the revert are
+    not in the master branch, but code changes after are in the master
+    branch.
+
+and asked for help recovering from this situation.
+
+The history immediately after the "revert of the merge" would look like
+this:
+
+ ---o---o---o---M---x---x---W
+	       /
+       ---A---B
+
+where A and B are on the side development that was not so good, M is the
+merge that brings these premature changes into the mainline, x are changes
+unrelated to what the side branch did and already made on the mainline,
+and W is the "revert of the merge M" (doesn't W look M upside down?).
+IOW, "diff W^..W" is similar to "diff -R M^..M".
+
+Such a "revert" of a merge can be made with:
+
+    $ git revert -m 1 M
+
+After the developers of the side branch fix their mistakes, the history
+may look like this:
+
+ ---o---o---o---M---x---x---W---x
+	       /
+       ---A---B-------------------C---D
+
+where C and D are to fix what was broken in A and B, and you may already
+have some other changes on the mainline after W.
+
+If you merge the updated side branch (with D at its tip), none of the
+changes made in A nor B will be in the result, because they were reverted
+by W.  That is what Alan saw.
+
+Linus explains the situation:
+
+    Reverting a regular commit just effectively undoes what that commit
+    did, and is fairly straightforward. But reverting a merge commit also
+    undoes the _data_ that the commit changed, but it does absolutely
+    nothing to the effects on _history_ that the merge had.
+
+    So the merge will still exist, and it will still be seen as joining
+    the two branches together, and future merges will see that merge as
+    the last shared state - and the revert that reverted the merge brought
+    in will not affect that at all.
+
+    So a "revert" undoes the data changes, but it's very much _not_ an
+    "undo" in the sense that it doesn't undo the effects of a commit on
+    the repository history.
+
+    So if you think of "revert" as "undo", then you're going to always
+    miss this part of reverts. Yes, it undoes the data, but no, it doesn't
+    undo history.
+
+In such a situation, you would want to first revert the previous revert,
+which would make the history look like this:
+
+ ---o---o---o---M---x---x---W---x---Y
+	       /
+       ---A---B-------------------C---D
+
+where Y is the revert of W.  Such a "revert of the revert" can be done
+with:
+
+    $ git revert W
+
+This history would (ignoring possible conflicts between what W and W..Y
+changed) be equivalent to not having W nor Y at all in the history:
+
+ ---o---o---o---M---x---x-------x----
+	       /
+       ---A---B-------------------C---D
+
+and merging the side branch again will not have conflict arising from an
+earlier revert and revert of the revert.
+
+ ---o---o---o---M---x---x-------x-------*
+	       /                       /
+       ---A---B-------------------C---D
+
+Of course the changes made in C and D still can conflict with what was
+done by any of the x, but that is just a normal merge conflict.
+
+On the other hand, if the developers of the side branch discarded their
+faulty A and B, and redone the changes on top of the updated mainline
+after the revert, the history would have looked like this:
+
+ ---o---o---o---M---x---x---W---x---x
+	       /                 \
+       ---A---B                   A'--B'--C'
+
+If you reverted the revert in such a case as in the previous example:
+
+ ---o---o---o---M---x---x---W---x---x---Y---*
+	       /                 \         /
+       ---A---B                   A'--B'--C'
+
+where Y is the revert of W, A' and B' are rerolled A and B, and there may
+also be a further fix-up C' on the side branch.  "diff Y^..Y" is similar
+to "diff -R W^..W" (which in turn means it is similar to "diff M^..M"),
+and "diff A'^..C'" by definition would be similar but different from that,
+because it is a rerolled series of the earlier change.  There will be a
+lot of overlapping changes that result in conflicts.  So do not do "revert
+of revert" blindly without thinking..
+
+ ---o---o---o---M---x---x---W---x---x
+	       /                 \
+       ---A---B                   A'--B'--C'
+
+In the history with rebased side branch, W (and M) are behind the merge
+base of the updated branch and the tip of the mainline, and they should
+merge without the past faulty merge and its revert getting in the way.
+
+To recap, these are two very different scenarios, and they want two very
+different resolution strategies:
+
+ - If the faulty side branch was fixed by adding corrections on top, then
+   doing a revert of the previous revert would be the right thing to do.
+
+ - If the faulty side branch whose effects were discarded by an earlier
+   revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,
+   as you seem to have interpreted), then re-merging the result without
+   doing anything else fancy would be the right thing to do.
+
+However, there are things to keep in mind when reverting a merge (and
+reverting such a revert).
+
+For example, think about what reverting a merge (and then reverting the
+revert) does to bisectability. Ignore the fact that the revert of a revert
+is undoing it - just think of it as a "single commit that does a lot".
+Because that is what it does.
+
+When you have a problem you are chasing down, and you hit a "revert this
+merge", what you're hitting is essentially a single commit that contains
+all the changes (but obviously in reverse) of all the commits that got
+merged. So it's debugging hell, because now you don't have lots of small
+changes that you can try to pinpoint which _part_ of it changes.
+
+But does it all work? Sure it does. You can revert a merge, and from a
+purely technical angle, git did it very naturally and had no real
+troubles. It just considered it a change from "state before merge" to
+"state after merge", and that was it. Nothing complicated, nothing odd,
+nothing really dangerous. Git will do it without even thinking about it.
+
+So from a technical angle, there's nothing wrong with reverting a merge,
+but from a workflow angle it's something that you generally should try to
+avoid.
+
+If at all possible, for example, if you find a problem that got merged
+into the main tree, rather than revert the merge, try _really_ hard to
+bisect the problem down into the branch you merged, and just fix it, or
+try to revert the individual commit that caused it.
+
+Yes, it's more complex, and no, it's not always going to work (sometimes
+the answer is: "oops, I really shouldn't have merged it, because it wasn't
+ready yet, and I really need to undo _all_ of the merge"). So then you
+really should revert the merge, but when you want to re-do the merge, you
+now need to do it by reverting the revert.
diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt
index 4032748..622ee5c 100644
--- a/Documentation/howto/setup-git-server-over-http.txt
+++ b/Documentation/howto/setup-git-server-over-http.txt
@@ -143,7 +143,7 @@
        Require valid-user
     </Location>
 
-    Debian automatically reads all files under /etc/apach2/conf.d.
+    Debian automatically reads all files under /etc/apache2/conf.d.
 
 The password file can be somewhere else, but it has to be readable by
 Apache and preferably not readable by the world.
diff --git a/Documentation/i18n.txt b/Documentation/i18n.txt
index c673966..708da6c 100644
--- a/Documentation/i18n.txt
+++ b/Documentation/i18n.txt
@@ -37,9 +37,9 @@
 help other people who look at them later.  Lack of this header
 implies that the commit log message is encoded in UTF-8.
 
-. 'git-log', 'git-show' and friends looks at the `encoding`
-  header of a commit object, and tries to re-code the log
-  message into UTF-8 unless otherwise specified.  You can
+. 'git-log', 'git-show', 'git-blame' and friends look at the
+  `encoding` header of a commit object, and try to re-code the
+  log message into UTF-8 unless otherwise specified.  You can
   specify the desired output encoding with
   `i18n.logoutputencoding` in `.git/config` file, like this:
 +
diff --git a/Documentation/mailmap.txt b/Documentation/mailmap.txt
new file mode 100644
index 0000000..288f04e
--- /dev/null
+++ b/Documentation/mailmap.txt
@@ -0,0 +1,74 @@
+If the file `.mailmap` exists at the toplevel of the repository, or at
+the location pointed to by the mailmap.file configuration option, it
+is used to map author and committer names and email addresses to
+canonical real names and email addresses.
+
+In the simple form, each line in the file consists of the canonical
+real name of an author, whitespace, and an email address used in the
+commit (enclosed by '<' and '>') to map to the name. For example:
+--
+	Proper Name <commit@email.xx>
+--
+
+The more complex forms are:
+--
+	<proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace only the email part of a commit, and:
+--
+	Proper Name <proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a
+commit matching the specified commit email address, and:
+--
+	Proper Name <proper@email.xx> Commit Name <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a
+commit matching both the specified commit name and email address.
+
+Example 1: Your history contains commits by two authors, Jane
+and Joe, whose names appear in the repository under several forms:
+
+------------
+Joe Developer <joe@example.com>
+Joe R. Developer <joe@example.com>
+Jane Doe <jane@example.com>
+Jane Doe <jane@laptop.(none)>
+Jane D. <jane@desktop.(none)>
+------------
+
+Now suppose that Joe wants his middle name initial used, and Jane
+prefers her family name fully spelled out. A proper `.mailmap` file
+would look like:
+
+------------
+Jane Doe         <jane@desktop.(none)>
+Joe R. Developer <joe@example.com>
+------------
+
+Note how there is no need for an entry for <jane@laptop.(none)>, because the
+real name of that author is already correct.
+
+Example 2: Your repository contains commits from the following
+authors:
+
+------------
+nick1 <bugs@company.xx>
+nick2 <bugs@company.xx>
+nick2 <nick2@company.xx>
+santa <me@company.xx>
+claus <me@company.xx>
+CTO <cto@coompany.xx>
+------------
+
+Then you might want a `.mailmap` file that looks like:
+------------
+<cto@company.xx>                       <cto@coompany.xx>
+Some Dude <some@dude.xx>         nick1 <bugs@company.xx>
+Other Author <other@author.xx>   nick2 <bugs@company.xx>
+Other Author <other@author.xx>         <nick2@company.xx>
+Santa Claus <santa.claus@northpole.xx> <me@company.xx>
+------------
+
+Use hash '#' for comments that are either on their own line, or after
+the email address.
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
index 4065a3a..b4d315c 100644
--- a/Documentation/manpage-1.72.xsl
+++ b/Documentation/manpage-1.72.xsl
@@ -1,21 +1,14 @@
-<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<!-- manpage-1.72.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles peculiarities in docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
 
-<xsl:param name="man.output.quietly" select="1"/>
-<xsl:param name="refentry.meta.get.quietly" select="1"/>
+<xsl:import href="manpage-base.xsl"/>
 
-<xsl:template match="co">
-	<xsl:value-of select="concat('&#x2593;fB(',substring-after(@id,'-'),')&#x2593;fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
-	<xsl:text>&#x2302;sp&#10;</xsl:text>
-	<xsl:apply-templates/>
-	<xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
-	<xsl:value-of select="concat('&#x2593;fB',substring-after(@arearefs,'-'),'. &#x2593;fR')"/>
-	<xsl:apply-templates/>
-	<xsl:text>&#x2302;br&#10;</xsl:text>
-</xsl:template>
+<!-- these are the special values for the roff control characters
+     needed for docbook-xsl 1.72.0 -->
+<xsl:param name="git.docbook.backslash">&#x2593;</xsl:param>
+<xsl:param name="git.docbook.dot"      >&#x2302;</xsl:param>
 
 </xsl:stylesheet>
diff --git a/Documentation/manpage-base.xsl b/Documentation/manpage-base.xsl
new file mode 100644
index 0000000..a264fa6
--- /dev/null
+++ b/Documentation/manpage-base.xsl
@@ -0,0 +1,35 @@
+<!-- manpage-base.xsl:
+     special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- these params silence some output from xmlto -->
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
+<!-- convert asciidoc callouts to man page format;
+     git.docbook.backslash and git.docbook.dot params
+     must be supplied by another XSL file or other means -->
+<xsl:template match="co">
+	<xsl:value-of select="concat(
+			      $git.docbook.backslash,'fB(',
+			      substring-after(@id,'-'),')',
+			      $git.docbook.backslash,'fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+	<xsl:value-of select="$git.docbook.dot"/>
+	<xsl:text>sp&#10;</xsl:text>
+	<xsl:apply-templates/>
+	<xsl:text>&#10;</xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+	<xsl:value-of select="concat(
+			      $git.docbook.backslash,'fB',
+			      substring-after(@arearefs,'-'),
+			      '. ',$git.docbook.backslash,'fR')"/>
+	<xsl:apply-templates/>
+	<xsl:value-of select="$git.docbook.dot"/>
+	<xsl:text>br&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-bold-literal.xsl b/Documentation/manpage-bold-literal.xsl
new file mode 100644
index 0000000..608eb5d
--- /dev/null
+++ b/Documentation/manpage-bold-literal.xsl
@@ -0,0 +1,17 @@
+<!-- manpage-bold-literal.xsl:
+     special formatting for manpages rendered from asciidoc+docbook -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- render literal text as bold (instead of plain or monospace);
+     this makes literal text easier to distinguish in manpages
+     viewed on a tty -->
+<xsl:template match="literal">
+	<xsl:value-of select="$git.docbook.backslash"/>
+	<xsl:text>fB</xsl:text>
+	<xsl:apply-templates/>
+	<xsl:value-of select="$git.docbook.backslash"/>
+	<xsl:text>fR</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl
new file mode 100644
index 0000000..a48f5b1
--- /dev/null
+++ b/Documentation/manpage-normal.xsl
@@ -0,0 +1,13 @@
+<!-- manpage-normal.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles anything we want to keep away from docbook-xsl 1.72.0 -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<xsl:import href="manpage-base.xsl"/>
+
+<!-- these are the normal values for the roff control characters -->
+<xsl:param name="git.docbook.backslash">\</xsl:param>
+<xsl:param name="git.docbook.dot"	>.</xsl:param>
+
+</xsl:stylesheet>
diff --git a/Documentation/manpage-suppress-sp.xsl b/Documentation/manpage-suppress-sp.xsl
new file mode 100644
index 0000000..a63c763
--- /dev/null
+++ b/Documentation/manpage-suppress-sp.xsl
@@ -0,0 +1,21 @@
+<!-- manpage-suppress-sp.xsl:
+     special settings for manpages rendered from asciidoc+docbook
+     handles erroneous, inline .sp in manpage output of some
+     versions of docbook-xsl -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+		version="1.0">
+
+<!-- attempt to work around spurious .sp at the tail of the line
+     that some versions of docbook stylesheets seem to add -->
+<xsl:template match="simpara">
+  <xsl:variable name="content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+  <xsl:value-of select="normalize-space($content)"/>
+  <xsl:if test="not(ancestor::authorblurb) and
+                not(ancestor::personblurb)">
+    <xsl:text>&#10;&#10;</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
index c735788..4832bc7 100644
--- a/Documentation/merge-config.txt
+++ b/Documentation/merge-config.txt
@@ -1,6 +1,10 @@
-merge.stat::
-	Whether to print the diffstat between ORIG_HEAD and the merge result
-	at the end of the merge.  True by default.
+merge.conflictstyle::
+	Specify the style in which conflicted hunks are written out to
+	working tree files upon merge.  The default is "merge", which
+	shows a `<<<<<<<` conflict marker, changes made by one side,
+	a `=======` marker, changes made by the other side, and then
+	a `>>>>>>>` marker.  An alternate style, "diff3", adds a `|||||||`
+	marker and the original text before the `=======` marker.
 
 merge.log::
 	Whether to include summaries of merged commits in newly created
@@ -11,10 +15,15 @@
 	during a merge; if not specified, defaults to the value of
 	diff.renameLimit.
 
+merge.stat::
+	Whether to print the diffstat between ORIG_HEAD and the merge result
+	at the end of the merge.  True by default.
+
 merge.tool::
 	Controls which merge resolution program is used by
 	linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
-	"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+	"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
+	"diffuse", "ecmerge", "tortoisemerge", and
 	"opendiff".  Any other value is treated is custom merge tool
 	and there must be a corresponding mergetool.<tool>.cmd option.
 
@@ -24,10 +33,10 @@
 	message if conflicts were detected. Level 1 outputs only
 	conflicts, 2 outputs conflicts and file changes.  Level 5 and
 	above outputs debugging information.  The default is level 2.
-	Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
+	Can be overridden by the 'GIT_MERGE_VERBOSITY' environment variable.
 
 merge.<driver>.name::
-	Defines a human readable name for a custom low-level
+	Defines a human-readable name for a custom low-level
 	merge driver.  See linkgit:gitattributes[5] for details.
 
 merge.<driver>.driver::
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 007909a..637b53f 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -1,10 +1,18 @@
+-q::
+--quiet::
+	Operate quietly.
+
+-v::
+--verbose::
+	Be verbose.
+
 --stat::
 	Show a diffstat at the end of the merge. The diffstat is also
 	controlled by the configuration option merge.stat.
 
 -n::
 --no-stat::
-	Do not show diffstat at the end of the merge.
+	Do not show a diffstat at the end of the merge.
 
 --summary::
 --no-summary::
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index 1276f85..4365b7e 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -3,15 +3,15 @@
 
 resolve::
 	This can only resolve two heads (i.e. the current branch
-	and another branch you pulled from) using 3-way merge
+	and another branch you pulled from) using a 3-way merge
 	algorithm.  It tries to carefully detect criss-cross
 	merge ambiguities and is considered generally safe and
 	fast.
 
 recursive::
-	This can only resolve two heads using 3-way merge
-	algorithm.  When there are more than one common
-	ancestors that can be used for 3-way merge, it creates a
+	This can only resolve two heads using a 3-way merge
+	algorithm.  When there is more than one common
+	ancestor that can be used for 3-way merge, it creates a
 	merged tree of the common ancestors and uses that as
 	the reference tree for the 3-way merge.  This has been
 	reported to result in fewer merge conflicts without
@@ -22,11 +22,11 @@
 	pulling or merging one branch.
 
 octopus::
-	This resolves more than two-head case, but refuses to do
-	complex merge that needs manual resolution.  It is
+	This resolves cases with more than two heads, but refuses to do
+	a complex merge that needs manual resolution.  It is
 	primarily meant to be used for bundling topic branch
 	heads together.  This is the default merge strategy when
-	pulling or merging more than one branches.
+	pulling or merging more than one branch.
 
 ours::
 	This resolves any number of heads, but the result of the
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 388d492..2a845b1 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -30,7 +30,7 @@
 
 	  commit <sha1>
 	  Author: <author>
-	  Date: <author date>
+	  Date:   <author date>
 
 	      <title line>
 
@@ -49,9 +49,9 @@
 * 'fuller'
 
 	  commit <sha1>
-	  Author: <author>
+	  Author:     <author>
 	  AuthorDate: <author date>
-	  Commit: <committer>
+	  Commit:     <committer>
 	  CommitDate: <committer date>
 
 	       <title line>
@@ -101,28 +101,33 @@
 - '%P': parent hashes
 - '%p': abbreviated parent hashes
 - '%an': author name
-- '%aN': author name (respecting .mailmap)
+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ae': author email
+- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ad': author date (format respects --date= option)
 - '%aD': author date, RFC2822 style
 - '%ar': author date, relative
 - '%at': author date, UNIX timestamp
 - '%ai': author date, ISO 8601 format
 - '%cn': committer name
-- '%cN': committer name (respecting .mailmap)
+- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ce': committer email
+- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%cd': committer date
 - '%cD': committer date, RFC2822 style
 - '%cr': committer date, relative
 - '%ct': committer date, UNIX timestamp
 - '%ci': committer date, ISO 8601 format
+- '%d': ref names, like the --decorate option of linkgit:git-log[1]
 - '%e': encoding
 - '%s': subject
+- '%f': sanitized subject line, suitable for a filename
 - '%b': body
 - '%Cred': switch color to red
 - '%Cgreen': switch color to green
 - '%Cblue': switch color to blue
 - '%Creset': reset color
+- '%C(...)': color specification, as described in color.branch.* config option
 - '%m': left, right or boundary mark
 - '%n': newline
 - '%x00': print a byte from a hex code
@@ -148,3 +153,12 @@
 4da45be
 7134973
 ---------------------
++
+In addition, any unrecognized string that has a `%` in it is interpreted
+as if it has `tformat:` in front of it.  For example, these two are
+equivalent:
++
+---------------------
+$ git log -2 --pretty=tformat:%h 4da45bef
+$ git log -2 --pretty=%h 4da45bef
+---------------------
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 6d66c74..bff9499 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -1,4 +1,5 @@
 --pretty[='<format>']::
+--format[='<format>']::
 
 	Pretty-print the contents of the commit logs in a given format,
 	where '<format>' can be one of 'oneline', 'short', 'medium',
@@ -10,13 +11,17 @@
 
 --abbrev-commit::
 	Instead of showing the full 40-byte hexadecimal commit object
-	name, show only handful hexdigits prefix.  Non default number of
+	name, show only a partial prefix.  Non default number of
 	digits can be specified with "--abbrev=<n>" (which also modifies
 	diff output, if it is displayed).
 +
 This should make "--pretty=oneline" a whole lot more readable for
 people using 80-column terminals.
 
+--oneline::
+	This is a shorthand for "--pretty=oneline --abbrev-commit"
+	used together.
+
 --encoding[=<encoding>]::
 	The commit objects record the encoding used for the log message
 	in their encoding header; this option can be used to tell the
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index ebdd948..f9811f2 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -5,15 +5,14 @@
 	of a remote (see the section <<REMOTES,REMOTES>> below).
 
 <refspec>::
-	The canonical format of a <refspec> parameter is
-	`+?<src>:<dst>`; that is, an optional plus `{plus}`, followed
-	by the source ref, followed by a colon `:`, followed by
-	the destination ref.
+	The format of a <refspec> parameter is an optional plus
+	`{plus}`, followed by the source ref <src>, followed
+	by a colon `:`, followed by the destination ref <dst>.
 +
 The remote ref that matches <src>
 is fetched, and if <dst> is not empty string, the local
 ref that matches it is fast forwarded using <src>.
-Again, if the optional plus `+` is used, the local ref
+If the optional plus `+` is used, the local ref
 is updated even if it does not result in a fast forward
 update.
 +
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 1023ac2..11eec94 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -13,7 +13,7 @@
 
 	Synonym for `--date=relative`.
 
---date={relative,local,default,iso,rfc,short}::
+--date={relative,local,default,iso,rfc,short,raw}::
 
 	Only takes effect for dates shown in human-readable format, such
 	as when using "--pretty". `log.date` config variable sets a default
@@ -31,6 +31,8 @@
 +
 `--date=short` shows only date but not time, in `YYYY-MM-DD` format.
 +
+`--date=raw` shows the date in the internal raw git format `%s %z` format.
++
 `--date=default` shows timestamps in the original timezone
 (either committer's or author's).
 
@@ -138,38 +140,38 @@
 --
 
 -n 'number'::
---max-count='number'::
+--max-count=<number>::
 
 	Limit the number of commits output.
 
---skip='number'::
+--skip=<number>::
 
 	Skip 'number' commits before starting to show the commit output.
 
---since='date'::
---after='date'::
+--since=<date>::
+--after=<date>::
 
 	Show commits more recent than a specific date.
 
---until='date'::
---before='date'::
+--until=<date>::
+--before=<date>::
 
 	Show commits older than a specific date.
 
 ifdef::git-rev-list[]
---max-age='timestamp'::
---min-age='timestamp'::
+--max-age=<timestamp>::
+--min-age=<timestamp>::
 
 	Limit the commits output to specified time range.
 endif::git-rev-list[]
 
---author='pattern'::
---committer='pattern'::
+--author=<pattern>::
+--committer=<pattern>::
 
 	Limit the commits output to ones with author/committer
 	header lines that match the specified pattern (regular expression).
 
---grep='pattern'::
+--grep=<pattern>::
 
 	Limit the commits output to ones with log message that
 	matches the specified pattern (regular expression).
@@ -222,6 +224,21 @@
 	Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
 	command line as '<commit>'.
 
+--branches::
+
+	Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed
+	on the command line as '<commit>'.
+
+--tags::
+
+	Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed
+	on the command line as '<commit>'.
+
+--remotes::
+
+	Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
+	on the command line as '<commit>'.
+
 ifdef::git-rev-list[]
 --stdin::
 
@@ -285,8 +302,52 @@
 History Simplification
 ~~~~~~~~~~~~~~~~~~~~~~
 
-When optional paths are given, 'git rev-list' simplifies commits with
-various strategies, according to the options you have selected.
+Sometimes you are only interested in parts of the history, for example the
+commits modifying a particular <path>. But there are two parts of
+'History Simplification', one part is selecting the commits and the other
+is how to do it, as there are various strategies to simplify the history.
+
+The following options select the commits to be shown:
+
+<paths>::
+
+	Commits modifying the given <paths> are selected.
+
+--simplify-by-decoration::
+
+	Commits that are referred by some branch or tag are selected.
+
+Note that extra commits can be shown to give a meaningful history.
+
+The following options affect the way the simplification is performed:
+
+Default mode::
+
+	Simplifies the history to the simplest history explaining the
+	final state of the tree. Simplest because it prunes some side
+	branches if the end result is the same (i.e. merging branches
+	with the same content)
+
+--full-history::
+
+	As the default mode but does not prune some history.
+
+--dense::
+
+	Only the selected commits are shown, plus some to have a
+	meaningful history.
+
+--sparse::
+
+	All commits in the simplified history are shown.
+
+--simplify-merges::
+
+	Additional option to '--full-history' to remove some needless
+	merges from the resulting history, as there are no selected
+	commits contributing to this merge.
+
+A more detailed explanation follows.
 
 Suppose you specified `foo` as the <paths>.  We shall call commits
 that modify `foo` !TREESAME, and the rest TREESAME.  (In a diff
@@ -413,6 +474,56 @@
 one of the parents is TREESAME, we follow only that one, so the other
 sides of the merge are never walked.
 
+Finally, there is a fourth simplification mode available:
+
+--simplify-merges::
+
+	First, build a history graph in the same way that
+	'\--full-history' with parent rewriting does (see above).
++
+Then simplify each commit `C` to its replacement `C'` in the final
+history according to the following rules:
++
+--
+* Set `C'` to `C`.
++
+* Replace each parent `P` of `C'` with its simplification `P'`.  In
+  the process, drop parents that are ancestors of other parents, and
+  remove duplicates.
++
+* If after this parent rewriting, `C'` is a root or merge commit (has
+  zero or >1 parents), a boundary commit, or !TREESAME, it remains.
+  Otherwise, it is replaced with its only parent.
+--
++
+The effect of this is best shown by way of comparing to
+'\--full-history' with parent rewriting.  The example turns into:
++
+-----------------------------------------------------------------------
+	  .-A---M---N---O
+	 /     /       /
+	I     B       D
+	 \   /       /
+	  `---------'
+-----------------------------------------------------------------------
++
+Note the major differences in `N` and `P` over '\--full-history':
++
+--
+* `N`'s parent list had `I` removed, because it is an ancestor of the
+  other parent `M`.  Still, `N` remained because it is !TREESAME.
++
+* `P`'s parent list similarly had `I` removed.  `P` was then
+  removed completely, because it had one parent and is TREESAME.
+--
+
+The '\--simplify-by-decoration' option allows you to view only the
+big picture of the topology of the history, by omitting commits
+that are not referenced by tags.  Commits are marked as !TREESAME
+(in other words, kept after history simplification rules described
+above) if (1) they are referenced by tags, or (2) they change the
+contents of the paths given on the command line.  All other
+commits are marked as TREESAME (subject to be simplified away).
 
 ifdef::git-rev-list[]
 Bisection Helpers
@@ -457,11 +568,11 @@
 commits, ordered by their distance to the included and excluded
 commits. The farthest from them is displayed first. (This is the only
 one displayed by `--bisect`.)
-
++
 This is useful because it makes it easy to choose a good commit to
 test when you want to avoid to test some of them for some reason (they
 may not compile for example).
-
++
 This option can be used along with `--bisect-vars`, in this case,
 after all the sorted commit objects, there will be the same text as if
 `--bisect-vars` had been used alone.
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 539863b..e66ca9f 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -66,6 +66,12 @@
 non-option arguments in `argv[]`.
 `argc` is updated appropriately because of the assignment.
 +
+You can also pass NULL instead of a usage array as fourth parameter of
+parse_options(), to avoid displaying a help screen with usage info and
+option list.  This should only be done if necessary, e.g. to implement
+a limited parser for only a subset of the options that needs to be run
+before the full parser, which in turn shows the full help message.
++
 Flags are the bitwise-or of:
 
 `PARSE_OPT_KEEP_DASHDASH`::
@@ -77,6 +83,28 @@
 	Using this flag, processing is stopped at the first non-option
 	argument.
 
+`PARSE_OPT_KEEP_ARGV0`::
+	Keep the first argument, which contains the program name.  It's
+	removed from argv[] by default.
+
+`PARSE_OPT_KEEP_UNKNOWN`::
+	Keep unknown arguments instead of erroring out.  This doesn't
+	work for all combinations of arguments as users might expect
+	it to do.  E.g. if the first argument in `--unknown --known`
+	takes a value (which we can't know), the second one is
+	mistakenly interpreted as a known option.  Similarly, if
+	`PARSE_OPT_STOP_AT_NON_OPTION` is set, the second argument in
+	`--unknown value` will be mistakenly interpreted as a
+	non-option, not as a value belonging to the unknown option,
+	the parser early.  That's why parse_options() errors out if
+	both options are set.
+
+`PARSE_OPT_NO_INTERNAL_HELP`::
+	By default, parse_options() handles `-h`, `--help` and
+	`--help-all` internally, by showing a help screen.  This option
+	turns it off and allows one to add custom handlers for these
+	options, or to just leave them unknown.
+
 Data Structure
 --------------
 
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 75aa5d4..2efe7a4 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -30,7 +30,7 @@
 	start_command() followed by finish_command(). Takes a pointer
 	to a `struct child_process` that specifies the details.
 
-`run_command_v_opt`, `run_command_v_opt_cd`, `run_command_v_opt_cd_env`::
+`run_command_v_opt`, `run_command_v_opt_cd_env`::
 
 	Convenience functions that encapsulate a sequence of
 	start_command() followed by finish_command(). The argument argv
@@ -52,6 +52,21 @@
 	Wait for the completion of an asynchronous function that was
 	started with start_async().
 
+`run_hook`::
+
+	Run a hook.
+	The first argument is a pathname to an index file, or NULL
+	if the hook uses the default index file or no index is needed.
+	The second argument is the name of the hook.
+	The further arguments correspond to the hook arguments.
+	The last argument has to be NULL to terminate the arguments list.
+	If the hook does not exist or is not executable, the return
+	value will be zero.
+	If it is executable, the hook will be executed and the exit
+	status of the hook is returned.
+	On execution, .stdout_to_stderr and .no_stdin will be set.
+	(See below.)
+
 
 Data structures
 ---------------
diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index 4242dc0..7438149 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -133,8 +133,10 @@
 
 * Adding data to the buffer
 
-NOTE: All of these functions in this section will grow the buffer as
-      necessary.
+NOTE: All of the functions in this section will grow the buffer as necessary.
+If they fail for some reason other than memory shortage and the buffer hadn't
+been allocated before (i.e. the `struct strbuf` was set to `STRBUF_INIT`),
+then they will free() it.
 
 `strbuf_addch`::
 
@@ -205,6 +207,13 @@
 parameters to the callback, `strbuf_expand()` passes a context pointer,
 which can be used by the programmer of the callback as she sees fit.
 
+`strbuf_expand_dict_cb`::
+
+	Used as callback for `strbuf_expand()`, expects an array of
+	struct strbuf_expand_dict_entry as context, i.e. pairs of
+	placeholder and replacement string.  The array needs to be
+	terminated by an entry with placeholder set to NULL.
+
 `strbuf_addf`::
 
 	Add a formatted string to the buffer.
@@ -213,7 +222,7 @@
 
 	Read a given size of data from a FILE* pointer to the buffer.
 +
-NOTE: The buffer is rewinded if the read fails. If -1 is returned,
+NOTE: The buffer is rewound if the read fails. If -1 is returned,
 `errno` must be consulted, like you would do for `read(3)`.
 `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline()` has the
 same behaviour as well.
@@ -228,6 +237,11 @@
 	Read the contents of a file, specified by its path. The third argument
 	can be used to give a hint about the file size, to avoid reallocs.
 
+`strbuf_readlink`::
+
+	Read the target of a symbolic link, specified by its path.  The third
+	argument can be used to give a hint about the size, to avoid reallocs.
+
 `strbuf_getline`::
 
 	Read a line from a FILE* pointer. The second argument specifies the line
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index 6bdf034..48bb97f 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -135,7 +135,7 @@
 
 This will make all index entries racily clean.  The linux-2.6
 project, for example, there are over 20,000 files in the working
-tree.  On my Athron 64X2 3800+, after the above:
+tree.  On my Athlon 64 X2 3800+, after the above:
 
   $ /usr/bin/time git diff-files
   1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index fa34c67..5355ebc 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -6,10 +6,10 @@
 
 ===============================================================
 - rsync://host.xz/path/to/repo.git/
-- http://host.xz/path/to/repo.git/
-- https://host.xz/path/to/repo.git/
-- git://host.xz/path/to/repo.git/
-- git://host.xz/~user/path/to/repo.git/
+- http://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- https://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- git://host.xz{startsb}:port{endsb}/~user/path/to/repo.git/
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
 - ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
 - ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 2ae88c5..dbbeb7e 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -18,12 +18,22 @@
 Further chapters cover more specialized topics.
 
 Comprehensive reference documentation is available through the man
-pages.  For a command such as "git clone <repo>", just use
+pages, or linkgit:git-help[1] command.  For example, for the command
+"git clone <repo>", you can either use:
 
 ------------------------------------------------
 $ man git-clone
 ------------------------------------------------
 
+or:
+
+------------------------------------------------
+$ git help clone
+------------------------------------------------
+
+With the latter, you can use the manual viewer of your choice; see
+linkgit:git-help[1] for more information.
+
 See also <<git-quick-start>> for a brief overview of git commands,
 without any explanation.
 
@@ -49,7 +59,7 @@
 ------------------------------------------------
 	# git itself (approx. 10MB download):
 $ git clone git://git.kernel.org/pub/scm/git/git.git
-	# the linux kernel (approx. 150MB download):
+	# the Linux kernel (approx. 150MB download):
 $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
 ------------------------------------------------
 
@@ -178,7 +188,7 @@
 did, and why.
 
 Every commit has a 40-hexdigit id, sometimes called the "object name" or the
-"SHA1 id", shown on the first line of the "git-show" output.  You can usually
+"SHA-1 id", shown on the first line of the "git show" output.  You can usually
 refer to a commit by a shorter name, such as a tag or a branch name, but this
 longer name can also be useful.  Most importantly, it is a globally unique
 name for this commit: so if you tell somebody else the object name (for
@@ -297,7 +307,7 @@
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The git-checkout command normally expects a branch head, but will also
+The `git checkout` command normally expects a branch head, but will also
 accept an arbitrary commit; for example, you can check out the commit
 referenced by a tag:
 
@@ -310,7 +320,7 @@
 HEAD is now at 427abfa... Linux v2.6.17
 ------------------------------------------------
 
-The HEAD then refers to the SHA1 of the commit instead of to a branch,
+The HEAD then refers to the SHA-1 of the commit instead of to a branch,
 and git branch shows that you are no longer on a branch:
 
 ------------------------------------------------
@@ -390,7 +400,7 @@
 REVISIONS" section of linkgit:git-rev-parse[1].
 
 [[Updating-a-repository-With-git-fetch]]
-Updating a repository with git-fetch
+Updating a repository with git fetch
 ------------------------------------
 
 Eventually the developer cloned from will do additional work in her
@@ -417,7 +427,7 @@
 -------------------------------------------------
 
 New remote-tracking branches will be stored under the shorthand name
-that you gave "git-remote add", in this case linux-nfs:
+that you gave "git remote add", in this case linux-nfs:
 
 -------------------------------------------------
 $ git branch -r
@@ -506,7 +516,7 @@
 
 to return you to the branch you were on before.
 
-Note that the version which git-bisect checks out for you at each
+Note that the version which `git bisect` checks out for you at each
 point is just a suggestion, and you're free to try a different
 version if you think it would be a good idea.  For example,
 occasionally you may land on a commit that broke something unrelated;
@@ -582,11 +592,11 @@
 commits:
 
 Merges (to be discussed later), as well as operations such as
-git-reset, which change the currently checked-out commit, generally
+`git reset`, which change the currently checked-out commit, generally
 set ORIG_HEAD to the value HEAD had before the current operation.
 
-The git-fetch operation always stores the head of the last fetched
-branch in FETCH_HEAD.  For example, if you run git fetch without
+The `git fetch` operation always stores the head of the last fetched
+branch in FETCH_HEAD.  For example, if you run `git fetch` without
 specifying a local branch as the target of the operation
 
 -------------------------------------------------
@@ -729,7 +739,7 @@
 -------------------------------------------------
 
 Alternatively, you may often see this sort of thing done with the
-lower-level command linkgit:git-rev-list[1], which just lists the SHA1's
+lower-level command linkgit:git-rev-list[1], which just lists the SHA-1's
 of all the given commits:
 
 -------------------------------------------------
@@ -999,7 +1009,7 @@
 If you have some initial content (say, a tarball):
 
 -------------------------------------------------
-$ tar -xzvf project.tar.gz
+$ tar xzvf project.tar.gz
 $ cd project
 $ git init
 $ git add . # include everything below ./ in the first commit:
@@ -1063,9 +1073,9 @@
 
 shows the difference between the working tree and the index file.
 
-Note that "git-add" always adds just the current contents of a file
+Note that "git add" always adds just the current contents of a file
 to the index; further changes to the same file will be ignored unless
-you run git-add on the file again.
+you run `git add` on the file again.
 
 When you're ready, just run
 
@@ -1126,10 +1136,10 @@
 A project will often generate files that you do 'not' want to track with git.
 This typically includes files generated by a build process or temporary
 backup files made by your editor. Of course, 'not' tracking files with git
-is just a matter of 'not' calling "`git-add`" on them. But it quickly becomes
+is just a matter of 'not' calling `git add` on them. But it quickly becomes
 annoying to have these untracked files lying around; e.g. they make
-"`git add .`" practically useless, and they keep showing up in the output of
-"`git status`".
+`git add .` practically useless, and they keep showing up in the output of
+`git status`.
 
 You can tell git to ignore certain files by creating a file called .gitignore
 in the top level of your working directory, with contents such as:
@@ -1330,7 +1340,7 @@
 MERGE_HEAD, and which touch an unmerged file.
 
 You may also use linkgit:git-mergetool[1], which lets you merge the
-unmerged files using external tools such as emacs or kdiff3.
+unmerged files using external tools such as Emacs or kdiff3.
 
 Each time you resolve the conflicts in a file and update the index:
 
@@ -1339,7 +1349,7 @@
 -------------------------------------------------
 
 the different stages of that file will be "collapsed", after which
-git-diff will (by default) no longer show diffs for that file.
+`git diff` will (by default) no longer show diffs for that file.
 
 [[undoing-a-merge]]
 Undoing a merge
@@ -1436,7 +1446,7 @@
 
 If the problematic commit is the most recent commit, and you have not
 yet made that commit public, then you may just
-<<undoing-a-merge,destroy it using git-reset>>.
+<<undoing-a-merge,destroy it using `git reset`>>.
 
 Alternatively, you
 can edit the working directory and update the index to fix your
@@ -1464,7 +1474,7 @@
 
 In the process of undoing a previous bad change, you may find it
 useful to check out an older version of a particular file using
-linkgit:git-checkout[1].  We've used git-checkout before to switch
+linkgit:git-checkout[1].  We've used `git checkout` before to switch
 branches, but it has quite different behavior if it is given a path
 name: the command
 
@@ -1532,7 +1542,7 @@
 -------------------------------------------------
 
 to recompress the archive.  This can be very time-consuming, so
-you may prefer to run git-gc when you are not doing other work.
+you may prefer to run `git gc` when you are not doing other work.
 
 
 [[ensuring-reliability]]
@@ -1624,7 +1634,7 @@
 suppose you delete a branch, then realize you need the history it
 contained.  The reflog is also deleted; however, if you have not yet
 pruned the repository, then you may still be able to find the lost
-commits in the dangling objects that git-fsck reports.  See
+commits in the dangling objects that `git fsck` reports.  See
 <<dangling-objects>> for the details.
 
 -------------------------------------------------
@@ -1666,7 +1676,7 @@
 ===============================
 
 [[getting-updates-With-git-pull]]
-Getting updates with git-pull
+Getting updates with git pull
 -----------------------------
 
 After you clone a repository and make a few changes of your own, you
@@ -1712,7 +1722,7 @@
 <<fast-forwards,fast forward>>; instead, your branch will just be
 updated to point to the latest commit from the upstream branch.)
 
-The git-pull command can also be given "." as the "remote" repository,
+The `git pull` command can also be given "." as the "remote" repository,
 in which case it just merges in a branch from the current repository; so
 the commands
 
@@ -1785,7 +1795,7 @@
 Another way to submit changes to a project is to tell the maintainer
 of that project to pull the changes from your repository using
 linkgit:git-pull[1].  In the section "<<getting-updates-With-git-pull,
-Getting updates with git-pull>>" we described this as a way to get
+Getting updates with `git pull`>>" we described this as a way to get
 updates from the "main" repository, but it works just as well in the
 other direction.
 
@@ -1837,7 +1847,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Assume your personal repository is in the directory ~/proj.  We
-first create a new clone of the repository and tell git-daemon that it
+first create a new clone of the repository and tell `git daemon` that it
 is meant to be public:
 
 -------------------------------------------------
@@ -1868,10 +1878,10 @@
 Otherwise, all you need to do is start linkgit:git-daemon[1]; it will
 listen on port 9418.  By default, it will allow access to any directory
 that looks like a git directory and contains the magic file
-git-daemon-export-ok.  Passing some directory paths as git-daemon
+git-daemon-export-ok.  Passing some directory paths as `git daemon`
 arguments will further restrict the exports to those paths.
 
-You can also run git-daemon as an inetd service; see the
+You can also run `git daemon` as an inetd service; see the
 linkgit:git-daemon[1] man page for details.  (See especially the
 examples section.)
 
@@ -1932,7 +1942,7 @@
 $ git push ssh://yourserver.com/~you/proj.git master
 -------------------------------------------------
 
-As with git-fetch, git-push will complain if this does not result in a
+As with `git fetch`, `git push` will complain if this does not result in a
 <<fast-forwards,fast forward>>; see the following section for details on
 handling this case.
 
@@ -1942,7 +1952,7 @@
 will not be updated by the push.  This may lead to unexpected results if
 the branch you push to is the currently checked-out branch!
 
-As with git-fetch, you may also set up configuration options to
+As with `git fetch`, you may also set up configuration options to
 save typing; so, for example, after
 
 -------------------------------------------------
@@ -1978,13 +1988,13 @@
 
 This can happen, for example, if you:
 
-	- use `git-reset --hard` to remove already-published commits, or
-	- use `git-commit --amend` to replace already-published commits
+	- use `git reset --hard` to remove already-published commits, or
+	- use `git commit --amend` to replace already-published commits
 	  (as in <<fixing-a-mistake-by-rewriting-history>>), or
-	- use `git-rebase` to rebase any already-published commits (as
+	- use `git rebase` to rebase any already-published commits (as
 	  in <<using-git-rebase>>).
 
-You may force git-push to perform the update anyway by preceding the
+You may force `git push` to perform the update anyway by preceding the
 branch name with a plus sign:
 
 -------------------------------------------------
@@ -2026,7 +2036,7 @@
 
 	- Git's ability to quickly import and merge patches allows a
 	  single maintainer to process incoming changes even at very
-	  high rates.  And when that becomes too much, git-pull provides
+	  high rates.  And when that becomes too much, `git pull` provides
 	  an easy way for that maintainer to delegate this job to other
 	  maintainers while still allowing optional review of incoming
 	  changes.
@@ -2394,7 +2404,7 @@
 you are rewriting history.
 
 [[using-git-rebase]]
-Keeping a patch series up to date using git-rebase
+Keeping a patch series up to date using git rebase
 --------------------------------------------------
 
 Suppose that you create a branch "mywork" on a remote-tracking branch
@@ -2458,9 +2468,9 @@
 ................................................
 
 In the process, it may discover conflicts.  In that case it will stop
-and allow you to fix the conflicts; after fixing conflicts, use "git-add"
+and allow you to fix the conflicts; after fixing conflicts, use `git add`
 to update the index with those contents, and then, instead of
-running git-commit, just run
+running `git commit`, just run
 
 -------------------------------------------------
 $ git rebase --continue
@@ -2498,7 +2508,7 @@
 $ git tag bad mywork~5
 -------------------------------------------------
 
-(Either gitk or git-log may be useful for finding the commit.)
+(Either gitk or `git log` may be useful for finding the commit.)
 
 Then check out that commit, edit it, and rebase the rest of the series
 on top of it (note that we could check out the commit on a temporary
@@ -2539,12 +2549,12 @@
 
 and browse through the list of patches in the mywork branch using gitk,
 applying them (possibly in a different order) to mywork-new using
-cherry-pick, and possibly modifying them as you go using `commit --amend`.
+cherry-pick, and possibly modifying them as you go using `git commit --amend`.
 The linkgit:git-gui[1] command may also help as it allows you to
 individually select diff hunks for inclusion in the index (by
 right-clicking on the diff hunk and choosing "Stage Hunk for Commit").
 
-Another technique is to use git-format-patch to create a series of
+Another technique is to use `git format-patch` to create a series of
 patches, then reset the state to before the patches:
 
 -------------------------------------------------
@@ -2652,7 +2662,7 @@
 linkgit:git-bisect[1] identifies C as the culprit, how will you
 figure out that the problem is due to this change in semantics?
 
-When the result of a git-bisect is a non-merge commit, you should
+When the result of a `git bisect` is a non-merge commit, you should
 normally be able to discover the problem by examining just that commit.
 Developers can make this easy by breaking their changes into small
 self-contained commits.  That won't help in the case above, however,
@@ -2715,7 +2725,7 @@
 git fetch and fast-forwards
 ---------------------------
 
-In the previous example, when updating an existing branch, "git-fetch"
+In the previous example, when updating an existing branch, "git fetch"
 checks to make sure that the most recent commit on the remote
 branch is a descendant of the most recent commit on your copy of the
 branch before updating your copy of the branch to point at the new
@@ -2741,7 +2751,7 @@
             o--o--o <-- new head of the branch
 ................................................
 
-In this case, "git-fetch" will fail, and print out a warning.
+In this case, "git fetch" will fail, and print out a warning.
 
 In that case, you can still force git to update to the new head, as
 described in the following section.  However, note that in the
@@ -2750,7 +2760,7 @@
 them.
 
 [[forcing-fetch]]
-Forcing git-fetch to do non-fast-forward updates
+Forcing git fetch to do non-fast-forward updates
 ------------------------------------------------
 
 If git fetch fails because the new head of a branch is not a
@@ -2855,8 +2865,8 @@
 We already saw in <<understanding-commits>> that all commits are stored
 under a 40-digit "object name".  In fact, all the information needed to
 represent the history of a project is stored in objects with such names.
-In each case the name is calculated by taking the SHA1 hash of the
-contents of the object.  The SHA1 hash is a cryptographic hash function.
+In each case the name is calculated by taking the SHA-1 hash of the
+contents of the object.  The SHA-1 hash is a cryptographic hash function.
 What that means to us is that it is impossible to find two different
 objects with the same name.  This has a number of advantages; among
 others:
@@ -2867,10 +2877,10 @@
   same content stored in two repositories will always be stored under
   the same name.
 - Git can detect errors when it reads an object, by checking that the
-  object's name is still the SHA1 hash of its contents.
+  object's name is still the SHA-1 hash of its contents.
 
 (See <<object-details>> for the details of the object formatting and
-SHA1 calculation.)
+SHA-1 calculation.)
 
 There are four different types of objects: "blob", "tree", "commit", and
 "tag".
@@ -2916,9 +2926,9 @@
 
 As you can see, a commit is defined by:
 
-- a tree: The SHA1 name of a tree object (as defined below), representing
+- a tree: The SHA-1 name of a tree object (as defined below), representing
   the contents of a directory at a certain point in time.
-- parent(s): The SHA1 name of some number of commits which represent the
+- parent(s): The SHA-1 name of some number of commits which represent the
   immediately previous step(s) in the history of the project.  The
   example above has one parent; merge commits may have more than
   one.  A commit with no parents is called a "root" commit, and
@@ -2967,13 +2977,13 @@
 ------------------------------------------------
 
 As you can see, a tree object contains a list of entries, each with a
-mode, object type, SHA1 name, and name, sorted by name.  It represents
+mode, object type, SHA-1 name, and name, sorted by name.  It represents
 the contents of a single directory tree.
 
 The object type may be a blob, representing the contents of a file, or
 another tree, representing the contents of a subdirectory.  Since trees
-and blobs, like all other objects, are named by the SHA1 hash of their
-contents, two trees have the same SHA1 name if and only if their
+and blobs, like all other objects, are named by the SHA-1 hash of their
+contents, two trees have the same SHA-1 name if and only if their
 contents (including, recursively, the contents of all subdirectories)
 are identical.  This allows git to quickly determine the differences
 between two related tree objects, since it can ignore any entries with
@@ -3019,15 +3029,15 @@
 Trust
 ~~~~~
 
-If you receive the SHA1 name of a blob from one source, and its contents
+If you receive the SHA-1 name of a blob from one source, and its contents
 from another (possibly untrusted) source, you can still trust that those
-contents are correct as long as the SHA1 name agrees.  This is because
-the SHA1 is designed so that it is infeasible to find different contents
+contents are correct as long as the SHA-1 name agrees.  This is because
+the SHA-1 is designed so that it is infeasible to find different contents
 that produce the same hash.
 
-Similarly, you need only trust the SHA1 name of a top-level tree object
+Similarly, you need only trust the SHA-1 name of a top-level tree object
 to trust the contents of the entire directory that it refers to, and if
-you receive the SHA1 name of a commit from a trusted source, then you
+you receive the SHA-1 name of a commit from a trusted source, then you
 can easily verify the entire history of commits reachable through
 parents of that commit, and all of those contents of the trees referred
 to by those commits.
@@ -3039,7 +3049,7 @@
 commits tells others that they can trust the whole history.
 
 In other words, you can easily validate a whole archive by just
-sending out a single email that tells the people the name (SHA1 hash)
+sending out a single email that tells the people the name (SHA-1 hash)
 of the top commit, and digitally sign that email using something
 like GPG/PGP.
 
@@ -3080,7 +3090,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Newly created objects are initially created in a file named after the
-object's SHA1 hash (stored in .git/objects).
+object's SHA-1 hash (stored in .git/objects).
 
 Unfortunately this system becomes inefficient once a project has a
 lot of objects.  Try this on an old project:
@@ -3121,7 +3131,7 @@
 
 to remove any of the "loose" objects that are now contained in the
 pack.  This will also remove any unreferenced objects (which may be
-created when, for example, you use "git-reset" to remove a commit).
+created when, for example, you use "git reset" to remove a commit).
 You can verify that the loose objects are gone by looking at the
 .git/objects directory or by running
 
@@ -3150,7 +3160,7 @@
 pointer itself just doesn't, since you replaced it with another one.
 
 There are also other situations that cause dangling objects. For
-example, a "dangling blob" may arise because you did a "git-add" of a
+example, a "dangling blob" may arise because you did a "git add" of a
 file, but then, before you actually committed it and made it part of the
 bigger picture, you changed something else in that file and committed
 that *updated* thing--the old state that you added originally ends up
@@ -3200,7 +3210,7 @@
 almost always the result of either being a half-way mergebase (the blob
 will often even have the conflict markers from a merge in it, if you
 have had conflicting merges that you fixed up by hand), or simply
-because you interrupted a "git-fetch" with ^C or something like that,
+because you interrupted a "git fetch" with ^C or something like that,
 leaving _some_ of the new objects in the object database, but just
 dangling and useless.
 
@@ -3215,9 +3225,9 @@
 repository--it's kind of like doing a filesystem fsck recovery: you
 don't want to do that while the filesystem is mounted.
 
-(The same is true of "git-fsck" itself, btw, but since
-git-fsck never actually *changes* the repository, it just reports
-on what it found, git-fsck itself is never "dangerous" to run.
+(The same is true of "git fsck" itself, btw, but since
+`git fsck` never actually *changes* the repository, it just reports
+on what it found, `git fsck` itself is never 'dangerous' to run.
 Running it while somebody is actually changing the repository can cause
 confusing and scary messages, but it won't actually do anything bad. In
 contrast, running "git prune" while somebody is actively changing the
@@ -3287,7 +3297,7 @@
 ------------------------------------------------
 
 which will create and store a blob object with the contents of
-somedirectory/myfile, and output the sha1 of that object.  if you're
+somedirectory/myfile, and output the SHA-1 of that object.  if you're
 extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in
 which case you've guessed right, and the corruption is fixed!
 
@@ -3349,7 +3359,7 @@
 -----------
 
 The index is a binary file (generally kept in .git/index) containing a
-sorted list of path names, each with permissions and the SHA1 of a blob
+sorted list of path names, each with permissions and the SHA-1 of a blob
 object; linkgit:git-ls-files[1] can show you the contents of the index:
 
 -------------------------------------------------
@@ -3479,14 +3489,14 @@
 
 NOTE: Do not use local URLs here if you plan to publish your superproject!
 
-See what files `git-submodule` created:
+See what files `git submodule` created:
 
 -------------------------------------------------
 $ ls -a
 .  ..  .git  .gitmodules  a  b  c  d
 -------------------------------------------------
 
-The `git-submodule add <repo> <path>` command does a couple of things:
+The `git submodule add <repo> <path>` command does a couple of things:
 
 - It clones the submodule from <repo> to the given <path> under the
   current directory and by default checks out the master branch.
@@ -3532,7 +3542,7 @@
 $ git submodule init
 -------------------------------------------------
 
-Now use `git-submodule update` to clone the repositories and check out the
+Now use `git submodule update` to clone the repositories and check out the
 commits specified in the superproject:
 
 -------------------------------------------------
@@ -3542,8 +3552,8 @@
 .  ..  .git  a.txt
 -------------------------------------------------
 
-One major difference between `git-submodule update` and `git-submodule add` is
-that `git-submodule update` checks out a specific commit, rather than the tip
+One major difference between `git submodule update` and `git submodule add` is
+that `git submodule update` checks out a specific commit, rather than the tip
 of a branch. It's like checking out a tag: the head is detached, so you're not
 working on a branch.
 
@@ -3744,7 +3754,7 @@
 index.  Normal operation is just
 
 -------------------------------------------------
-$ git read-tree <sha1 of tree>
+$ git read-tree <SHA-1 of tree>
 -------------------------------------------------
 
 and your index file will now be equivalent to the tree that you saved
@@ -3759,7 +3769,7 @@
 files. This is not a very common operation, since normally you'd just
 keep your files updated, and rather than write to your working
 directory, you'd tell the index files about the changes in your
-working directory (i.e. `git-update-index`).
+working directory (i.e. `git update-index`).
 
 However, if you decide to jump to a new version, or check out somebody
 else's version, or just restore a previous tree, you'd populate your
@@ -3772,7 +3782,7 @@
 
 or, if you want to check out all of the index, use `-a`.
 
-NOTE! git-checkout-index normally refuses to overwrite old files, so
+NOTE! `git checkout-index` normally refuses to overwrite old files, so
 if you have an old version of the tree already checked out, you will
 need to use the "-f" flag ('before' the "-a" flag or the filename) to
 'force' the checkout.
@@ -3810,7 +3820,7 @@
 and then giving the reason for the commit on stdin (either through
 redirection from a pipe or file, or by just typing it at the tty).
 
-git-commit-tree will return the name of the object that represents
+`git commit-tree` will return the name of the object that represents
 that commit, and you should save it away for later use. Normally,
 you'd commit a new `HEAD` state, and while git doesn't care where you
 save the note about that state, in practice we tend to just write the
@@ -3879,7 +3889,7 @@
 
 to show its contents. NOTE! Trees have binary content, and as a result
 there is a special helper for showing that content, called
-`git-ls-tree`, which turns the binary content into a more easily
+`git ls-tree`, which turns the binary content into a more easily
 readable form.
 
 It's especially instructive to look at "commit" objects, since those
@@ -3968,13 +3978,13 @@
 ------------------------------------------------
 
 Each line of the `git ls-files --unmerged` output begins with
-the blob mode bits, blob SHA1, 'stage number', and the
+the blob mode bits, blob SHA-1, 'stage number', and the
 filename.  The 'stage number' is git's way to say which tree it
 came from: stage 1 corresponds to `$orig` tree, stage 2 `HEAD`
 tree, and stage3 `$target` tree.
 
 Earlier we said that trivial merges are done inside
-`git-read-tree -m`.  For example, if the file did not change
+`git read-tree -m`.  For example, if the file did not change
 from `$orig` to `HEAD` nor `$target`, or if the file changed
 from `$orig` to `HEAD` and `$orig` to `$target` the same way,
 obviously the final outcome is what is in `HEAD`.  What the
@@ -4001,20 +4011,20 @@
 $ git update-index hello.c
 -------------------------------------------------
 
-When a path is in the "unmerged" state, running `git-update-index` for
+When a path is in the "unmerged" state, running `git update-index` for
 that path tells git to mark the path resolved.
 
 The above is the description of a git merge at the lowest level,
 to help you understand what conceptually happens under the hood.
-In practice, nobody, not even git itself, runs `git-cat-file` three times
-for this.  There is a `git-merge-index` program that extracts the
+In practice, nobody, not even git itself, runs `git cat-file` three times
+for this.  There is a `git merge-index` program that extracts the
 stages to temporary files and calls a "merge" script on it:
 
 -------------------------------------------------
 $ git merge-index git-merge-one-file hello.c
 -------------------------------------------------
 
-and that is what higher level `git-merge -s resolve` is implemented with.
+and that is what higher level `git merge -s resolve` is implemented with.
 
 [[hacking-git]]
 Hacking git
@@ -4035,12 +4045,12 @@
 Regardless of object type, all objects share the following
 characteristics: they are all deflated with zlib, and have a header
 that not only specifies their type, but also provides size information
-about the data in the object.  It's worth noting that the SHA1 hash
+about the data in the object.  It's worth noting that the SHA-1 hash
 that is used to name the object is the hash of the original data
 plus this header, so `sha1sum` 'file' does not match the object name
 for 'file'.
 (Historical note: in the dawn of the age of git the hash
-was the sha1 of the 'compressed' object.)
+was the SHA-1 of the 'compressed' object.)
 
 As a result, the general consistency of an object can always be tested
 independently of the contents or the type of the object: all objects can
@@ -4051,7 +4061,7 @@
 
 The structured objects can further have their structure and
 connectivity to other objects verified. This is generally done with
-the `git-fsck` program, which generates a full dependency graph
+the `git fsck` program, which generates a full dependency graph
 of all objects, and verifies their internal consistency (in addition
 to just verifying their superficial consistency through the hash).
 
@@ -4110,7 +4120,7 @@
 This is just to get you into the groove for the most libified part of Git:
 the revision walker.
 
-Basically, the initial version of `git-log` was a shell script:
+Basically, the initial version of `git log` was a shell script:
 
 ----------------------------------------------------------------
 $ git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | \
@@ -4119,20 +4129,20 @@
 
 What does this mean?
 
-`git-rev-list` is the original version of the revision walker, which
+`git rev-list` is the original version of the revision walker, which
 _always_ printed a list of revisions to stdout.  It is still functional,
 and needs to, since most new Git programs start out as scripts using
-`git-rev-list`.
+`git rev-list`.
 
-`git-rev-parse` is not as important any more; it was only used to filter out
+`git rev-parse` is not as important any more; it was only used to filter out
 options that were relevant for the different plumbing commands that were
 called by the script.
 
-Most of what `git-rev-list` did is contained in `revision.c` and
+Most of what `git rev-list` did is contained in `revision.c` and
 `revision.h`.  It wraps the options in a struct named `rev_info`, which
 controls how and what revisions are walked, and more.
 
-The original job of `git-rev-parse` is now taken by the function
+The original job of `git rev-parse` is now taken by the function
 `setup_revisions()`, which parses the revisions and the common command line
 options for the revision walker. This information is stored in the struct
 `rev_info` for later consumption. You can do your own command line option
@@ -4145,7 +4155,7 @@
 `git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you
 no longer need to call `setup_pager()` directly).
 
-Nowadays, `git-log` is a builtin, which means that it is _contained_ in the
+Nowadays, `git log` is a builtin, which means that it is _contained_ in the
 command `git`.  The source side of a builtin is
 
 - a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`,
@@ -4161,7 +4171,7 @@
 _not_ named like the `.c` file in which they live have to be listed in
 `BUILT_INS` in the `Makefile`.
 
-`git-log` looks more complicated in C than it does in the original script,
+`git log` looks more complicated in C than it does in the original script,
 but that allows for a much greater flexibility and performance.
 
 Here again it is a good point to take a pause.
@@ -4172,9 +4182,9 @@
 So, think about something which you are interested in, say, "how can I
 access a blob just knowing the object name of it?".  The first step is to
 find a Git command with which you can do it.  In this example, it is either
-`git-show` or `git-cat-file`.
+`git show` or `git cat-file`.
 
-For the sake of clarity, let's stay with `git-cat-file`, because it
+For the sake of clarity, let's stay with `git cat-file`, because it
 
 - is plumbing, and
 
@@ -4188,7 +4198,7 @@
 ------------------------------------------------------------------
         git_config(git_default_config);
         if (argc != 3)
-                usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
+		usage("git cat-file [-t|-s|-e|-p|<type>] <sha1>");
         if (get_sha1(argv[2], sha1))
                 die("Not a valid object name %s", argv[2]);
 ------------------------------------------------------------------
@@ -4233,10 +4243,10 @@
 -----------------------------------
 
 Sometimes, you do not know where to look for a feature.  In many such cases,
-it helps to search through the output of `git log`, and then `git-show` the
+it helps to search through the output of `git log`, and then `git show` the
 corresponding commit.
 
-Example: If you know that there was some test case for `git-bundle`, but
+Example: If you know that there was some test case for `git bundle`, but
 do not remember where it was (yes, you _could_ `git grep bundle t/`, but that
 does not illustrate the point!):
 
@@ -4356,7 +4366,9 @@
 * remote example
   URL: git://example.com/project.git
   Tracked remote branches
-    master next ...
+    master
+    next
+    ...
 $ git fetch example		# update branches from example
 $ git branch -r			# list all remote branches
 -----------------------------------------------
@@ -4518,7 +4530,7 @@
 - Whenever possible, section headings should clearly describe the task
   they explain how to do, in language that requires no more knowledge
   than necessary: for example, "importing patches into a project" rather
-  than "the git-am command"
+  than "the `git am` command"
 
 Think about how to create a clear chapter dependency graph that will
 allow people to get to important topics without necessarily reading
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 6c7465c..97fc1e0 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.0.2.GIT
+DEF_VER=v1.6.2.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index 2bae53f..ae7f750 100644
--- a/INSTALL
+++ b/INSTALL
@@ -6,7 +6,7 @@
 to do a global install, you can do
 
 	$ make prefix=/usr all doc info ;# as yourself
-	# make prefix=/usr install install-doc install-info ;# as root
+	# make prefix=/usr install install-doc install-html install-info ;# as root
 
 (or prefix=/usr/local, of course).  Just like any program suite
 that uses $prefix, the built results have some paths encoded,
@@ -19,7 +19,7 @@
 	$ make configure ;# as yourself
 	$ ./configure --prefix=/usr ;# as yourself
 	$ make all doc ;# as yourself
-	# make install install-doc ;# as root
+	# make install install-doc install-html;# as root
 
 
 Issues of note:
@@ -89,13 +89,25 @@
    inclined to install the tools, the default build target
    ("make all") does _not_ build them.
 
+   "make doc" builds documentation in man and html formats; there are
+   also "make man", "make html" and "make info". Note that "make html"
+   requires asciidoc, but not xmlto. "make man" (and thus make doc)
+   requires both.
+
+   "make install-doc" installs documentation in man format only; there
+   are also "make install-man", "make install-html" and "make
+   install-info".
+
    Building and installing the info file additionally requires
    makeinfo and docbook2X.  Version 0.8.3 is known to work.
 
+   Building and installing the pdf file additionally requires
+   dblatex.  Version 0.2.7 with asciidoc >= 8.2.7 is known to work.
+
    The documentation is written for AsciiDoc 7, but "make
    ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
 
-   Alternatively, pre-formatted documentation are available in
+   Alternatively, pre-formatted documentation is available in
    "html" and "man" branches of the git repository itself.  For
    example, you could:
 
@@ -117,6 +129,13 @@
 
 	http://www.kernel.org/pub/software/scm/git/docs/
 
+   There are also "make quick-install-doc", "make quick-install-man"
+   and "make quick-install-html" which install preformatted man pages
+   and html documentation.
+   This does not require asciidoc/xmlto, but it only works from within
+   a cloned checkout of git.git with these two extra branches, and will
+   not work for the maintainer for obvious chicken-and-egg reasons.
+
    It has been reported that docbook-xsl version 1.72 and 1.73 are
    buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
    the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
diff --git a/Makefile b/Makefile
index 5de4247..57f9f26 100644
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,9 @@
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
 #
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
 # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
 #
 # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
@@ -90,6 +93,8 @@
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
+# Define NO_PTHREADS if you do not have or do not want to use Pthreads.
+#
 # Define NO_PREAD if you have a problem with pread() system call (e.g.
 # cygwin.dll before v1.5.22).
 #
@@ -121,9 +126,18 @@
 # randomly break unless your underlying filesystem supports those sub-second
 # times (my ext3 doesn't).
 #
+# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
+# "st_ctim"
+#
+# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
+# available.  This automatically turns USE_NSEC off.
+#
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
 #
+# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
+# field that counts the on-disk footprint in 512-byte blocks.
+#
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
 # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
@@ -131,6 +145,8 @@
 # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 # MakeMaker (e.g. using ActiveState under Cygwin).
 #
+# Define NO_PERL if you do not want Perl scripts or libraries at all.
+#
 # Define NO_TCLTK if you do not want Tcl/Tk GUI.
 #
 # The TCL_PATH variable governs the location of the Tcl interpreter
@@ -161,6 +177,7 @@
 uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
 uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
 uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
+uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
@@ -173,28 +190,32 @@
 # Among the variables below, these:
 #   gitexecdir
 #   template_dir
+#   mandir
+#   infodir
 #   htmldir
 #   ETC_GITCONFIG (but not sysconfdir)
-# can be specified as a relative path ../some/where/else (which must begin
-# with ../); this is interpreted as relative to $(bindir) and "git" at
+# can be specified as a relative path some/where/else;
+# this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
 # This can help installing the suite in a relocatable way.
 
 prefix = $(HOME)
-bindir = $(prefix)/bin
-mandir = $(prefix)/share/man
-infodir = $(prefix)/share/info
-gitexecdir = $(prefix)/libexec/git-core
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
-template_dir = $(sharedir)/git-core/templates
-htmldir=$(sharedir)/doc/git-doc
+template_dir = share/git-core/templates
+htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
+ETC_GITCONFIG = $(sysconfdir)/gitconfig
 else
 sysconfdir = $(prefix)/etc
+ETC_GITCONFIG = etc/gitconfig
 endif
 lib = lib
-ETC_GITCONFIG = $(sysconfdir)/gitconfig
 # DESTDIR=
 
 # default configuration for gitweb
@@ -215,7 +236,7 @@
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
 
-export prefix bindir sharedir htmldir sysconfdir
+export prefix bindir sharedir sysconfdir
 
 CC = gcc
 AR = ar
@@ -226,6 +247,7 @@
 RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
+PTHREAD_LIBS = -lpthread
 
 export TCL_PATH TCLTK_PATH
 
@@ -243,14 +265,28 @@
 BASIC_CFLAGS =
 BASIC_LDFLAGS =
 
+# Guard against environment variables
+BUILTIN_OBJS =
+BUILT_INS =
+COMPAT_CFLAGS =
+COMPAT_OBJS =
+LIB_H =
+LIB_OBJS =
+PROGRAMS =
+SCRIPT_PERL =
+SCRIPT_SH =
+TEST_PROGRAMS =
+
 SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
+SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
 SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
+SCRIPT_SH += git-mergetool--lib.sh
 SCRIPT_SH += git-parse-remote.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
@@ -264,6 +300,7 @@
 SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_PERL += git-add--interactive.perl
+SCRIPT_PERL += git-difftool.perl
 SCRIPT_PERL += git-archimport.perl
 SCRIPT_PERL += git-cvsexportcommit.perl
 SCRIPT_PERL += git-cvsimport.perl
@@ -291,8 +328,8 @@
 PROGRAMS += git-mktree$X
 PROGRAMS += git-pack-redundant$X
 PROGRAMS += git-patch-id$X
-PROGRAMS += git-receive-pack$X
 PROGRAMS += git-send-pack$X
+PROGRAMS += git-shell$X
 PROGRAMS += git-show-index$X
 PROGRAMS += git-unpack-file$X
 PROGRAMS += git-update-server-info$X
@@ -303,8 +340,8 @@
 # builtin-$C.o but is linked in as part of some other command.
 BUILT_INS += $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
-BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-cherry$X
+BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-get-tar-commit-id$X
@@ -313,6 +350,7 @@
 BUILT_INS += git-peek-remote$X
 BUILT_INS += git-repo-config$X
 BUILT_INS += git-show$X
+BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
 BUILT_INS += git-whatchanged$X
 
@@ -320,7 +358,10 @@
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
 # what 'all' will build but not install in gitexecdir
-OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
+OTHER_PROGRAMS = git$X
+ifndef NO_PERL
+OTHER_PROGRAMS += gitweb/gitweb.cgi
+endif
 
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
@@ -342,6 +383,7 @@
 LIB_H += cache.h
 LIB_H += cache-tree.h
 LIB_H += commit.h
+LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
@@ -354,6 +396,8 @@
 LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
+LIB_H += help.h
+LIB_H += levenshtein.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
@@ -365,7 +409,6 @@
 LIB_H += pack-revindex.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
-LIB_H += string-list.h
 LIB_H += pkt-line.h
 LIB_H += progress.h
 LIB_H += quote.h
@@ -377,12 +420,15 @@
 LIB_H += run-command.h
 LIB_H += sha1-lookup.h
 LIB_H += sideband.h
+LIB_H += sigchain.h
 LIB_H += strbuf.h
+LIB_H += string-list.h
 LIB_H += tag.h
 LIB_H += transport.h
 LIB_H += tree.h
 LIB_H += tree-walk.h
 LIB_H += unpack-trees.h
+LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += wt-status.h
 
@@ -394,6 +440,7 @@
 LIB_OBJS += archive-zip.o
 LIB_OBJS += attr.o
 LIB_OBJS += base85.o
+LIB_OBJS += bisect.o
 LIB_OBJS += blob.o
 LIB_OBJS += branch.o
 LIB_OBJS += bundle.o
@@ -415,8 +462,8 @@
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
 LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-no-index.o
 LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
 LIB_OBJS += diff.o
 LIB_OBJS += dir.o
 LIB_OBJS += editor.o
@@ -429,7 +476,7 @@
 LIB_OBJS += hash.o
 LIB_OBJS += help.o
 LIB_OBJS += ident.o
-LIB_OBJS += interpolate.o
+LIB_OBJS += levenshtein.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
@@ -437,6 +484,7 @@
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
+LIB_OBJS += merge-recursive.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
@@ -447,9 +495,9 @@
 LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
-LIB_OBJS += string-list.o
 LIB_OBJS += path.o
 LIB_OBJS += pkt-line.o
+LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
 LIB_OBJS += progress.o
 LIB_OBJS += quote.o
@@ -463,12 +511,14 @@
 LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
-LIB_OBJS += sha1_file.o
 LIB_OBJS += sha1-lookup.o
+LIB_OBJS += sha1_file.o
 LIB_OBJS += sha1_name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
+LIB_OBJS += sigchain.o
 LIB_OBJS += strbuf.o
+LIB_OBJS += string-list.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += trace.o
@@ -478,6 +528,7 @@
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
 LIB_OBJS += usage.o
+LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
 LIB_OBJS += walker.o
 LIB_OBJS += wrapper.o
@@ -490,6 +541,7 @@
 BUILTIN_OBJS += builtin-annotate.o
 BUILTIN_OBJS += builtin-apply.o
 BUILTIN_OBJS += builtin-archive.o
+BUILTIN_OBJS += builtin-bisect--helper.o
 BUILTIN_OBJS += builtin-blame.o
 BUILTIN_OBJS += builtin-branch.o
 BUILTIN_OBJS += builtin-bundle.o
@@ -518,6 +570,7 @@
 BUILTIN_OBJS += builtin-fsck.o
 BUILTIN_OBJS += builtin-gc.o
 BUILTIN_OBJS += builtin-grep.o
+BUILTIN_OBJS += builtin-help.o
 BUILTIN_OBJS += builtin-init-db.o
 BUILTIN_OBJS += builtin-log.o
 BUILTIN_OBJS += builtin-ls-files.o
@@ -538,6 +591,7 @@
 BUILTIN_OBJS += builtin-prune.o
 BUILTIN_OBJS += builtin-push.o
 BUILTIN_OBJS += builtin-read-tree.o
+BUILTIN_OBJS += builtin-receive-pack.o
 BUILTIN_OBJS += builtin-reflog.o
 BUILTIN_OBJS += builtin-remote.o
 BUILTIN_OBJS += builtin-rerere.o
@@ -575,9 +629,11 @@
 
 ifeq ($(uname_S),Linux)
 	NO_STRLCPY = YesPlease
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	NO_STRLCPY = YesPlease
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),UnixWare)
 	CC = cc
@@ -621,13 +677,15 @@
 ifeq ($(uname_S),Darwin)
 	NEEDS_SSL_WITH_CRYPTO = YesPlease
 	NEEDS_LIBICONV = YesPlease
-	ifneq ($(shell expr "$(uname_R)" : '9\.'),2)
+	ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
 		OLD_ICONV = UnfortunatelyYes
 	endif
-	NO_STRLCPY = YesPlease
+	ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
+		NO_STRLCPY = YesPlease
+	endif
 	NO_MEMMEM = YesPlease
-	COMPAT_CFLAGS += -Icompat/regex
-	COMPAT_OBJS += compat/regex/regex.o
+	THREADED_DELTA_SEARCH = YesPlease
+	USE_ST_TIMESPEC = YesPlease
 endif
 ifeq ($(uname_S),SunOS)
 	NEEDS_SOCKET = YesPlease
@@ -677,8 +735,13 @@
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
 	DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
-	COMPAT_CFLAGS += -Icompat/regex
-	COMPAT_OBJS += compat/regex/regex.o
+	USE_ST_TIMESPEC = YesPlease
+	THREADED_DELTA_SEARCH = YesPlease
+	ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
+		PTHREAD_LIBS = -pthread
+		NO_UINTMAX_T = YesPlease
+		NO_STRTOUMAX = YesPlease
+	endif
 endif
 ifeq ($(uname_S),OpenBSD)
 	NO_STRCASESTR = YesPlease
@@ -686,26 +749,31 @@
 	NEEDS_LIBICONV = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),NetBSD)
 	ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
 		NEEDS_LIBICONV = YesPlease
 	endif
 	BASIC_CFLAGS += -I/usr/pkg/include
-	BASIC_LDFLAGS += -L/usr/pkg/lib
-	ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
+	BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),AIX)
 	NO_STRCASESTR=YesPlease
 	NO_MEMMEM = YesPlease
 	NO_MKDTEMP = YesPlease
 	NO_STRLCPY = YesPlease
+	NO_NSEC = YesPlease
 	FREAD_READS_DIRECTORIES = UnfortunatelyYes
 	INTERNAL_QSORT = UnfortunatelyYes
 	NEEDS_LIBICONV=YesPlease
 	BASIC_CFLAGS += -D_LARGE_FILES
-	COMPAT_CFLAGS += -Icompat/regex
-	COMPAT_OBJS += compat/regex/regex.o
+	ifneq ($(shell expr "$(uname_V)" : '[1234]'),1)
+		THREADED_DELTA_SEARCH = YesPlease
+	else
+		NO_PTHREADS = YesPlease
+	endif
 endif
 ifeq ($(uname_S),GNU)
 	# GNU/Hurd
@@ -735,8 +803,10 @@
 	NO_SYS_SELECT_H = YesPlease
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 endif
+ifneq (,$(findstring CYGWIN,$(uname_S)))
+	COMPAT_OBJS += compat/cygwin.o
+endif
 ifneq (,$(findstring MINGW,$(uname_S)))
-	NO_MMAP = YesPlease
 	NO_PREAD = YesPlease
 	NO_OPENSSL = YesPlease
 	NO_CURL = YesPlease
@@ -747,6 +817,7 @@
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
 	NO_MEMMEM = YesPlease
+	NO_PTHREADS = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	OLD_ICONV = YesPlease
 	NO_C99_FORMAT = YesPlease
@@ -755,16 +826,17 @@
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 	NO_SVN_TESTS = YesPlease
 	NO_PERL_MAKEMAKER = YesPlease
+	RUNTIME_PREFIX = YesPlease
 	NO_POSIX_ONLY_PROGRAMS = YesPlease
+	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+	NO_NSEC = YesPlease
+	USE_WIN32_MMAP = YesPlease
 	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
 	COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 	COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
 	EXTLIBS += -lws2_32
 	X = .exe
-	gitexecdir = ../libexec/git-core
-	template_dir = ../share/git-core/templates/
-	ETC_GITCONFIG = ../etc/gitconfig
 endif
 ifneq (,$(findstring arm,$(uname_M)))
 	ARM_SHA1 = YesPlease
@@ -786,14 +858,17 @@
 			BASIC_LDFLAGS += -L/opt/local/lib
 		endif
 	endif
+	PTHREAD_LIBS =
 endif
 
-ifdef NO_R_TO_GCC_LINKER
-	# Some gcc does not accept and pass -R to the linker to specify
-	# the runtime dynamic library path.
-	CC_LD_DYNPATH = -Wl,-rpath=
-else
-	CC_LD_DYNPATH = -R
+ifndef CC_LD_DYNPATH
+	ifdef NO_R_TO_GCC_LINKER
+		# Some gcc does not accept and pass -R to the linker to specify
+		# the runtime dynamic library path.
+		CC_LD_DYNPATH = -Wl,-rpath,
+	else
+		CC_LD_DYNPATH = -R
+	endif
 endif
 
 ifdef NO_CURL
@@ -816,7 +891,12 @@
 		endif
 	endif
 	ifndef NO_EXPAT
-		EXPAT_LIBEXPAT = -lexpat
+		ifdef EXPATDIR
+			BASIC_CFLAGS += -I$(EXPATDIR)/include
+			EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
+		else
+			EXPAT_LIBEXPAT = -lexpat
+		endif
 	endif
 endif
 
@@ -829,7 +909,6 @@
 ifndef NO_POSIX_ONLY_PROGRAMS
 	PROGRAMS += git-daemon$X
 	PROGRAMS += git-imap-send$X
-	PROGRAMS += git-shell$X
 endif
 ifndef NO_OPENSSL
 	OPENSSL_LIBSSL = -lssl
@@ -870,6 +949,18 @@
 ifdef NO_D_INO_IN_DIRENT
 	BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
 endif
+ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+	BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
+endif
+ifdef USE_NSEC
+	BASIC_CFLAGS += -DUSE_NSEC
+endif
+ifdef USE_ST_TIMESPEC
+	BASIC_CFLAGS += -DUSE_ST_TIMESPEC
+endif
+ifdef NO_NSEC
+	BASIC_CFLAGS += -DNO_NSEC
+endif
 ifdef NO_C99_FORMAT
 	BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
@@ -917,6 +1008,11 @@
 ifdef NO_MMAP
 	COMPAT_CFLAGS += -DNO_MMAP
 	COMPAT_OBJS += compat/mmap.o
+else
+	ifdef USE_WIN32_MMAP
+		COMPAT_CFLAGS += -DUSE_WIN32_MMAP
+		COMPAT_OBJS += compat/win32mmap.o
+	endif
 endif
 ifdef NO_PREAD
 	COMPAT_CFLAGS += -DNO_PREAD
@@ -931,6 +1027,9 @@
 ifdef NO_IPV6
 	BASIC_CFLAGS += -DNO_IPV6
 endif
+ifdef NO_UINTMAX_T
+	BASIC_CFLAGS += -Duintmax_t=uint32_t
+endif
 ifdef NO_SOCKADDR_STORAGE
 ifdef NO_IPV6
 	BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
@@ -989,10 +1088,19 @@
 	COMPAT_CFLAGS += -DINTERNAL_QSORT
 	COMPAT_OBJS += compat/qsort.o
 endif
+ifdef RUNTIME_PREFIX
+	COMPAT_CFLAGS += -DRUNTIME_PREFIX
+endif
+
+ifdef NO_PTHREADS
+	THREADED_DELTA_SEARCH =
+	BASIC_CFLAGS += -DNO_PTHREADS
+else
+	EXTLIBS += $(PTHREAD_LIBS)
+endif
 
 ifdef THREADED_DELTA_SEARCH
 	BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
-	EXTLIBS += -lpthread
 	LIB_OBJS += thread-utils.o
 endif
 ifdef DIR_HAS_BSD_GROUP_SEMANTICS
@@ -1006,6 +1114,10 @@
 NO_TCLTK=NoThanks
 endif
 
+ifeq ($(PERL_PATH),)
+NO_PERL=NoThanks
+endif
+
 QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
 QUIET_SUBDIR1  =
 
@@ -1042,6 +1154,7 @@
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
+bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 mandir_SQ = $(subst ','\'',$(mandir))
 infodir_SQ = $(subst ','\'',$(infodir))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
@@ -1079,7 +1192,9 @@
 	$(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
 	$(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 endif
+ifndef NO_PERL
 	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
+endif
 	$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
 
 please_set_SHELL_PATH_to_a_more_modern_shell:
@@ -1092,13 +1207,14 @@
 
 git.o: git.c common-cmds.h GIT-CFLAGS
 	$(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
+		'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 		$(ALL_CFLAGS) -c $(filter %.c,$^)
 
 git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
 		$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-help.o: help.c common-cmds.h GIT-CFLAGS
+builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 		'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 		'-DGIT_MAN_PATH="$(mandir_SQ)"' \
@@ -1126,6 +1242,7 @@
 	chmod +x $@+ && \
 	mv $@+ $@
 
+ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
 perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
@@ -1185,6 +1302,15 @@
 	    $@.sh > $@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
+else # NO_PERL
+$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+	    -e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
+	    unimplemented.sh >$@+ && \
+	chmod +x $@+ && \
+	mv $@+ $@
+endif # NO_PERL
 
 configure: configure.ac
 	$(QUIET_GEN)$(RM) $@ $<+ && \
@@ -1207,7 +1333,12 @@
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
 exec_cmd.o: exec_cmd.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $<
+	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+		'-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
+		'-DBINDIR="$(bindir_relative_SQ)"' \
+		'-DPREFIX="$(prefix_SQ)"' \
+		$<
+
 builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
 
@@ -1225,7 +1356,9 @@
 git-%$X: %.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(LIB_FILE)
+git-imap-send$X: imap-send.o $(GITLIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+		$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
 
 http.o http-walker.o http-push.o transport.o: http.h
 
@@ -1241,7 +1374,7 @@
 	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
 
 XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
-	xdiff/xmerge.o
+	xdiff/xmerge.o xdiff/xpatience.o
 $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
 	xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
 
@@ -1252,9 +1385,18 @@
 doc:
 	$(MAKE) -C Documentation all
 
+man:
+	$(MAKE) -C Documentation man
+
+html:
+	$(MAKE) -C Documentation html
+
 info:
 	$(MAKE) -C Documentation info
 
+pdf:
+	$(MAKE) -C Documentation pdf
+
 TAGS:
 	$(RM) TAGS
 	$(FIND) . -name '*.[hcS]' -print | xargs etags -a
@@ -1284,6 +1426,8 @@
 GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
 	@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
 	@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
+	@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+	@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
@@ -1301,7 +1445,17 @@
 
 ### Testing rules
 
-TEST_PROGRAMS = test-chmtime$X test-dump-cache-tree$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X
+TEST_PROGRAMS += test-chmtime$X
+TEST_PROGRAMS += test-ctype$X
+TEST_PROGRAMS += test-date$X
+TEST_PROGRAMS += test-delta$X
+TEST_PROGRAMS += test-dump-cache-tree$X
+TEST_PROGRAMS += test-genrandom$X
+TEST_PROGRAMS += test-match-trees$X
+TEST_PROGRAMS += test-parse-options$X
+TEST_PROGRAMS += test-path-utils$X
+TEST_PROGRAMS += test-sha1$X
+TEST_PROGRAMS += test-sigchain$X
 
 all:: $(TEST_PROGRAMS)
 
@@ -1314,6 +1468,8 @@
 test: all
 	$(MAKE) -C t/ all
 
+test-ctype$X: ctype.o
+
 test-date$X: date.o ctype.o
 
 test-delta$X: diff-delta.o patch-delta.o
@@ -1345,17 +1501,17 @@
 
 ### Installation rules
 
-ifeq ($(firstword $(subst /, ,$(template_dir))),..)
-template_instdir = $(bindir)/$(template_dir)
-else
+ifneq ($(filter /%,$(firstword $(template_dir))),)
 template_instdir = $(template_dir)
+else
+template_instdir = $(prefix)/$(template_dir)
 endif
 export template_instdir
 
-ifeq ($(firstword $(subst /, ,$(gitexecdir))),..)
-gitexec_instdir = $(bindir)/$(gitexecdir)
-else
+ifneq ($(filter /%,$(firstword $(gitexecdir))),)
 gitexec_instdir = $(gitexecdir)
+else
+gitexec_instdir = $(prefix)/$(gitexecdir)
 endif
 gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
 export gitexec_instdir
@@ -1377,26 +1533,40 @@
 	bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
 	execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
 	{ $(RM) "$$execdir/git-add$X" && \
-		ln git-add$X "$$execdir/git-add$X" 2>/dev/null || \
-		cp git-add$X "$$execdir/git-add$X"; } && \
-	{ $(foreach p,$(filter-out git-add$X,$(BUILT_INS)), $(RM) "$$execdir/$p" && \
-		ln "$$execdir/git-add$X" "$$execdir/$p" 2>/dev/null || \
-		ln -s "git-add$X" "$$execdir/$p" 2>/dev/null || \
-		cp "$$execdir/git-add$X" "$$execdir/$p" || exit;) } && \
+		ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \
+		cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \
+	{ for p in $(filter-out git-add$X,$(BUILT_INS)); do \
+		$(RM) "$$execdir/$$p" && \
+		ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \
+		ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \
+		cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \
+	  done } && \
 	./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
 	$(MAKE) -C Documentation install
 
+install-man:
+	$(MAKE) -C Documentation install-man
+
 install-html:
 	$(MAKE) -C Documentation install-html
 
 install-info:
 	$(MAKE) -C Documentation install-info
 
+install-pdf:
+	$(MAKE) -C Documentation install-pdf
+
 quick-install-doc:
 	$(MAKE) -C Documentation quick-install
 
+quick-install-man:
+	$(MAKE) -C Documentation quick-install-man
+
+quick-install-html:
+	$(MAKE) -C Documentation quick-install-html
+
 
 
 ### Maintainer's dist rules
@@ -1460,9 +1630,11 @@
 	$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
 	$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
 	$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
-	$(RM) gitweb/gitweb.cgi
 	$(MAKE) -C Documentation/ clean
+ifndef NO_PERL
+	$(RM) gitweb/gitweb.cgi
 	$(MAKE) -C perl clean
+endif
 	$(MAKE) -C templates/ clean
 	$(MAKE) -C t/ clean
 ifndef NO_TCLTK
@@ -1535,3 +1707,27 @@
 check-builtins::
 	./check-builtins.sh
 
+### Test suite coverage testing
+#
+.PHONY: coverage coverage-clean coverage-build coverage-report
+
+coverage:
+	$(MAKE) coverage-build
+	$(MAKE) coverage-report
+
+coverage-clean:
+	rm -f *.gcda *.gcno
+
+COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
+COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
+
+coverage-build: coverage-clean
+	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
+	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
+		-j1 test
+
+coverage-report:
+	gcov -b *.c
+	grep '^function.*called 0 ' *.c.gcov \
+		| sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \
+		| tee coverage-untested-functions
diff --git a/README b/README
index 5fa41b7..c932ab3 100644
--- a/README
+++ b/README
@@ -24,10 +24,18 @@
 hackers around the net. It is currently maintained by Junio C Hamano.
 
 Please read the file INSTALL for installation instructions.
+
 See Documentation/gittutorial.txt to get started, then see
-Documentation/everyday.txt for a useful minimum set of commands,
-and "man git-commandname" for documentation of each command.
-CVS users may also want to read Documentation/cvs-migration.txt.
+Documentation/everyday.txt for a useful minimum set of commands, and
+Documentation/git-commandname.txt for documentation of each command.
+If git has been correctly installed, then the tutorial can also be
+read with "man gittutorial" or "git help tutorial", and the
+documentation of each command with "man git-commandname" or "git help
+commandname".
+
+CVS users may also want to read Documentation/gitcvs-migration.txt
+("man gitcvs-migration" or "git help cvs-migration" if git is
+installed).
 
 Many Git online resources are accessible from http://git.or.cz/
 including full documentation and Git related tools.
diff --git a/RelNotes b/RelNotes
index 009d8f0..dd8bc4b 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.0.6.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.3.txt
\ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 0d56124..649f34f 100644
--- a/abspath.c
+++ b/abspath.c
@@ -1,5 +1,16 @@
 #include "cache.h"
 
+/*
+ * Do not use this for inspecting *tracked* content.  When path is a
+ * symlink to a directory, we do not want to say it is a directory when
+ * dealing with tracked content in the working tree.
+ */
+int is_directory(const char *path)
+{
+	struct stat st;
+	return (!stat(path, &st) && S_ISDIR(st.st_mode));
+}
+
 /* We allow "recursive" symbolic links. Only within reason, though. */
 #define MAXDEPTH 5
 
@@ -17,7 +28,7 @@
 		die ("Too long path: %.*s", 60, path);
 
 	while (depth--) {
-		if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
+		if (!is_directory(buf)) {
 			char *last_slash = strrchr(buf, '/');
 			if (last_slash) {
 				*last_slash = '\0';
@@ -53,6 +64,8 @@
 			len = readlink(buf, next_buf, PATH_MAX);
 			if (len < 0)
 				die ("Invalid symlink: %s", buf);
+			if (PATH_MAX <= len)
+				die("symbolic link too long: %s", buf);
 			next_buf[len] = '\0';
 			buf = next_buf;
 			buf_index = 1 - buf_index;
diff --git a/archive-tar.c b/archive-tar.c
index 1302961..ba890eb 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -124,11 +124,10 @@
 		unsigned int mode, void *buffer, unsigned long size)
 {
 	struct ustar_header header;
-	struct strbuf ext_header;
+	struct strbuf ext_header = STRBUF_INIT;
 	int err = 0;
 
 	memset(&header, 0, sizeof(header));
-	strbuf_init(&ext_header, 0);
 
 	if (!sha1) {
 		*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
@@ -211,10 +210,9 @@
 static int write_global_extended_header(struct archiver_args *args)
 {
 	const unsigned char *sha1 = args->commit_sha1;
-	struct strbuf ext_header;
+	struct strbuf ext_header = STRBUF_INIT;
 	int err;
 
-	strbuf_init(&ext_header, 0);
 	strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
 	err = write_tar_entry(args, NULL, NULL, 0, 0, ext_header.buf,
 			ext_header.len);
diff --git a/archive.c b/archive.c
index 45d242b..b2b90d3 100644
--- a/archive.c
+++ b/archive.c
@@ -4,6 +4,7 @@
 #include "attr.h"
 #include "archive.h"
 #include "parse-options.h"
+#include "unpack-trees.h"
 
 static char const * const archive_usage[] = {
 	"git archive [options] <tree-ish> [path...]",
@@ -15,7 +16,7 @@
 
 #define USES_ZLIB_COMPRESSION 1
 
-const struct archiver {
+static const struct archiver {
 	const char *name;
 	write_archive_fn_t write_archive;
 	unsigned int flags;
@@ -29,11 +30,10 @@
                          struct strbuf *buf)
 {
 	char *to_free = NULL;
-	struct strbuf fmt;
+	struct strbuf fmt = STRBUF_INIT;
 
 	if (src == buf->buf)
 		to_free = strbuf_detach(buf, NULL);
-	strbuf_init(&fmt, 0);
 	for (;;) {
 		const char *b, *c;
 
@@ -65,10 +65,9 @@
 
 	buffer = read_sha1_file(sha1, type, sizep);
 	if (buffer && S_ISREG(mode)) {
-		struct strbuf buf;
+		struct strbuf buf = STRBUF_INIT;
 		size_t size = 0;
 
-		strbuf_init(&buf, 0);
 		strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
 		convert_to_working_tree(path, buf.buf, buf.len, &buf);
 		if (commit)
@@ -134,7 +133,7 @@
 		err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
 		if (err)
 			return err;
-		return READ_TREE_RECURSIVE;
+		return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 	}
 
 	buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,
@@ -152,6 +151,8 @@
 		write_archive_entry_fn_t write_entry)
 {
 	struct archiver_context context;
+	struct unpack_trees_options opts;
+	struct tree_desc t;
 	int err;
 
 	if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
@@ -170,6 +171,22 @@
 	context.args = args;
 	context.write_entry = write_entry;
 
+	/*
+	 * Setup index and instruct attr to read index only
+	 */
+	if (!args->worktree_attributes) {
+		memset(&opts, 0, sizeof(opts));
+		opts.index_only = 1;
+		opts.head_idx = -1;
+		opts.src_index = &the_index;
+		opts.dst_index = &the_index;
+		opts.fn = oneway_merge;
+		init_tree_desc(&t, args->tree->buffer, args->tree->size);
+		if (unpack_trees(1, &t, &opts))
+			return -1;
+		git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
+	}
+
 	err =  read_tree_recursive(args->tree, args->base, args->baselen, 0,
 			args->pathspec, write_archive_entry, &context);
 	if (err == READ_TREE_RECURSIVE)
@@ -255,15 +272,21 @@
 	const char *base = NULL;
 	const char *remote = NULL;
 	const char *exec = NULL;
+	const char *output = NULL;
 	int compression_level = -1;
 	int verbose = 0;
 	int i;
 	int list = 0;
+	int worktree_attributes = 0;
 	struct option opts[] = {
 		OPT_GROUP(""),
 		OPT_STRING(0, "format", &format, "fmt", "archive format"),
 		OPT_STRING(0, "prefix", &base, "prefix",
 			"prepend prefix to each pathname in the archive"),
+		OPT_STRING(0, "output", &output, "file",
+			"write the archive to this file"),
+		OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
+			"read .gitattributes in working directory"),
 		OPT__VERBOSE(&verbose),
 		OPT__COMPR('0', &compression_level, "store only", 0),
 		OPT__COMPR('1', &compression_level, "compress faster", 1),
@@ -292,6 +315,8 @@
 		die("Unexpected option --remote");
 	if (exec)
 		die("Option --exec can only be used together with --remote");
+	if (output)
+		die("Unexpected option --output");
 
 	if (!base)
 		base = "";
@@ -321,6 +346,7 @@
 	args->verbose = verbose;
 	args->base = base;
 	args->baselen = strlen(base);
+	args->worktree_attributes = worktree_attributes;
 
 	return argc;
 }
diff --git a/archive.h b/archive.h
index 0b15b35..038ac35 100644
--- a/archive.h
+++ b/archive.h
@@ -10,6 +10,7 @@
 	time_t time;
 	const char **pathspec;
 	unsigned int verbose : 1;
+	unsigned int worktree_attributes : 1;
 	int compression_level;
 };
 
diff --git a/arm/sha1.c b/arm/sha1.c
index 9e3ae03..c61ad4a 100644
--- a/arm/sha1.c
+++ b/arm/sha1.c
@@ -8,9 +8,9 @@
 #include <string.h>
 #include "sha1.h"
 
-extern void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
+extern void arm_sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
 
-void SHA1_Init(SHA_CTX *c)
+void arm_SHA1_Init(arm_SHA_CTX *c)
 {
 	c->len = 0;
 	c->hash[0] = 0x67452301;
@@ -20,7 +20,7 @@
 	c->hash[4] = 0xc3d2e1f0;
 }
 
-void SHA1_Update(SHA_CTX *c, const void *p, unsigned long n)
+void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n)
 {
 	uint32_t workspace[80];
 	unsigned int partial;
@@ -32,12 +32,12 @@
 		if (partial) {
 			done = 64 - partial;
 			memcpy(c->buffer + partial, p, done);
-			sha_transform(c->hash, c->buffer, workspace);
+			arm_sha_transform(c->hash, c->buffer, workspace);
 			partial = 0;
 		} else
 			done = 0;
 		while (n >= done + 64) {
-			sha_transform(c->hash, p + done, workspace);
+			arm_sha_transform(c->hash, p + done, workspace);
 			done += 64;
 		}
 	} else
@@ -46,7 +46,7 @@
 		memcpy(c->buffer + partial, p + done, n - done);
 }
 
-void SHA1_Final(unsigned char *hash, SHA_CTX *c)
+void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c)
 {
 	uint64_t bitlen;
 	uint32_t bitlen_hi, bitlen_lo;
@@ -57,7 +57,7 @@
 	bitlen = c->len << 3;
 	offset = c->len & 0x3f;
 	padlen = ((offset < 56) ? 56 : (64 + 56)) - offset;
-	SHA1_Update(c, padding, padlen);
+	arm_SHA1_Update(c, padding, padlen);
 
 	bitlen_hi = bitlen >> 32;
 	bitlen_lo = bitlen & 0xffffffff;
@@ -69,7 +69,7 @@
 	bits[5] = bitlen_lo >> 16;
 	bits[6] = bitlen_lo >> 8;
 	bits[7] = bitlen_lo;
-	SHA1_Update(c, bits, 8);
+	arm_SHA1_Update(c, bits, 8);
 
 	for (i = 0; i < 5; i++) {
 		uint32_t v = c->hash[i];
diff --git a/arm/sha1.h b/arm/sha1.h
index 3952646..b61b618 100644
--- a/arm/sha1.h
+++ b/arm/sha1.h
@@ -7,12 +7,17 @@
 
 #include <stdint.h>
 
-typedef struct sha_context {
+typedef struct {
 	uint64_t len;
 	uint32_t hash[5];
 	unsigned char buffer[64];
-} SHA_CTX;
+} arm_SHA_CTX;
 
-void SHA1_Init(SHA_CTX *c);
-void SHA1_Update(SHA_CTX *c, const void *p, unsigned long n);
-void SHA1_Final(unsigned char *hash, SHA_CTX *c);
+void arm_SHA1_Init(arm_SHA_CTX *c);
+void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n);
+void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c);
+
+#define git_SHA_CTX	arm_SHA_CTX
+#define git_SHA1_Init	arm_SHA1_Init
+#define git_SHA1_Update	arm_SHA1_Update
+#define git_SHA1_Final	arm_SHA1_Final
diff --git a/arm/sha1_arm.S b/arm/sha1_arm.S
index 8c1cb99..41e9263 100644
--- a/arm/sha1_arm.S
+++ b/arm/sha1_arm.S
@@ -10,7 +10,7 @@
  */
 
 	.text
-	.globl	sha_transform
+	.globl	arm_sha_transform
 
 /*
  * void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
@@ -18,7 +18,7 @@
  * note: the "data" pointer may be unaligned.
  */
 
-sha_transform:
+arm_sha_transform:
 
 	stmfd	sp!, {r4 - r8, lr}
 
diff --git a/attr.c b/attr.c
index 17f6a4d..f1ca4f5 100644
--- a/attr.c
+++ b/attr.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 #include "attr.h"
 
@@ -318,6 +319,9 @@
 	return res;
 }
 
+static enum git_attr_direction direction;
+static struct index_state *use_index;
+
 static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
 {
 	FILE *fp = fopen(path, "r");
@@ -340,9 +344,10 @@
 	unsigned long sz;
 	enum object_type type;
 	void *data;
+	struct index_state *istate = use_index ? use_index : &the_index;
 
 	len = strlen(path);
-	pos = cache_name_pos(path, len);
+	pos = index_name_pos(istate, path, len);
 	if (pos < 0) {
 		/*
 		 * We might be in the middle of a merge, in which
@@ -350,15 +355,15 @@
 		 */
 		int i;
 		for (i = -pos - 1;
-		     (pos < 0 && i < active_nr &&
-		      !strcmp(active_cache[i]->name, path));
+		     (pos < 0 && i < istate->cache_nr &&
+		      !strcmp(istate->cache[i]->name, path));
 		     i++)
-			if (ce_stage(active_cache[i]) == 2)
+			if (ce_stage(istate->cache[i]) == 2)
 				pos = i;
 	}
 	if (pos < 0)
 		return NULL;
-	data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
+	data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
 	if (!data || type != OBJ_BLOB) {
 		free(data);
 		return NULL;
@@ -366,27 +371,17 @@
 	return data;
 }
 
-static struct attr_stack *read_attr(const char *path, int macro_ok)
+static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
 {
 	struct attr_stack *res;
 	char *buf, *sp;
 	int lineno = 0;
 
-	res = read_attr_from_file(path, macro_ok);
-	if (res)
-		return res;
-
-	res = xcalloc(1, sizeof(*res));
-
-	/*
-	 * There is no checked out .gitattributes file there, but
-	 * we might have it in the index.  We allow operation in a
-	 * sparsely checked out work tree, so read from it.
-	 */
 	buf = read_index_data(path);
 	if (!buf)
-		return res;
+		return NULL;
 
+	res = xcalloc(1, sizeof(*res));
 	for (sp = buf; *sp; ) {
 		char *ep;
 		int more;
@@ -401,6 +396,32 @@
 	return res;
 }
 
+static struct attr_stack *read_attr(const char *path, int macro_ok)
+{
+	struct attr_stack *res;
+
+	if (direction == GIT_ATTR_CHECKOUT) {
+		res = read_attr_from_index(path, macro_ok);
+		if (!res)
+			res = read_attr_from_file(path, macro_ok);
+	}
+	else if (direction == GIT_ATTR_CHECKIN) {
+		res = read_attr_from_file(path, macro_ok);
+		if (!res)
+			/*
+			 * There is no checked out .gitattributes file there, but
+			 * we might have it in the index.  We allow operation in a
+			 * sparsely checked out work tree, so read from it.
+			 */
+			res = read_attr_from_index(path, macro_ok);
+	}
+	else
+		res = read_attr_from_index(path, macro_ok);
+	if (!res)
+		res = xcalloc(1, sizeof(*res));
+	return res;
+}
+
 #if DEBUG_ATTR
 static void debug_info(const char *what, struct attr_stack *elem)
 {
@@ -428,6 +449,15 @@
 #define debug_set(a,b,c,d) do { ; } while (0)
 #endif
 
+static void drop_attr_stack(void)
+{
+	while (attr_stack) {
+		struct attr_stack *elem = attr_stack;
+		attr_stack = elem->prev;
+		free_attr_elem(elem);
+	}
+}
+
 static void bootstrap_attr_stack(void)
 {
 	if (!attr_stack) {
@@ -438,7 +468,7 @@
 		elem->prev = attr_stack;
 		attr_stack = elem;
 
-		if (!is_bare_repository()) {
+		if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
 			elem = read_attr(GITATTRIBUTES_FILE, 1);
 			elem->origin = strdup("");
 			elem->prev = attr_stack;
@@ -505,7 +535,7 @@
 	/*
 	 * Read from parent directories and push them down
 	 */
-	if (!is_bare_repository()) {
+	if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
 		while (1) {
 			char *cp;
 
@@ -642,3 +672,16 @@
 
 	return 0;
 }
+
+void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
+{
+	enum git_attr_direction old = direction;
+
+	if (is_bare_repository() && new != GIT_ATTR_INDEX)
+		die("BUG: non-INDEX attr direction in a bare repo");
+
+	direction = new;
+	if (new != old)
+		drop_attr_stack();
+	use_index = istate;
+}
diff --git a/attr.h b/attr.h
index f1c2038..69b5767 100644
--- a/attr.h
+++ b/attr.h
@@ -31,4 +31,11 @@
 
 int git_checkattr(const char *path, int, struct git_attr_check *);
 
+enum git_attr_direction {
+	GIT_ATTR_CHECKIN,
+	GIT_ATTR_CHECKOUT,
+	GIT_ATTR_INDEX,
+};
+void git_attr_set_direction(enum git_attr_direction, struct index_state *);
+
 #endif /* ATTR_H */
diff --git a/bisect.c b/bisect.c
new file mode 100644
index 0000000..58f7e6f
--- /dev/null
+++ b/bisect.c
@@ -0,0 +1,556 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "refs.h"
+#include "list-objects.h"
+#include "quote.h"
+#include "sha1-lookup.h"
+#include "bisect.h"
+
+static unsigned char (*skipped_sha1)[20];
+static int skipped_sha1_nr;
+static int skipped_sha1_alloc;
+
+static const char **rev_argv;
+static int rev_argv_nr;
+static int rev_argv_alloc;
+
+/* bits #0-15 in revision.h */
+
+#define COUNTED		(1u<<16)
+
+/*
+ * This is a truly stupid algorithm, but it's only
+ * used for bisection, and we just don't care enough.
+ *
+ * We care just barely enough to avoid recursing for
+ * non-merge entries.
+ */
+static int count_distance(struct commit_list *entry)
+{
+	int nr = 0;
+
+	while (entry) {
+		struct commit *commit = entry->item;
+		struct commit_list *p;
+
+		if (commit->object.flags & (UNINTERESTING | COUNTED))
+			break;
+		if (!(commit->object.flags & TREESAME))
+			nr++;
+		commit->object.flags |= COUNTED;
+		p = commit->parents;
+		entry = p;
+		if (p) {
+			p = p->next;
+			while (p) {
+				nr += count_distance(p);
+				p = p->next;
+			}
+		}
+	}
+
+	return nr;
+}
+
+static void clear_distance(struct commit_list *list)
+{
+	while (list) {
+		struct commit *commit = list->item;
+		commit->object.flags &= ~COUNTED;
+		list = list->next;
+	}
+}
+
+#define DEBUG_BISECT 0
+
+static inline int weight(struct commit_list *elem)
+{
+	return *((int*)(elem->item->util));
+}
+
+static inline void weight_set(struct commit_list *elem, int weight)
+{
+	*((int*)(elem->item->util)) = weight;
+}
+
+static int count_interesting_parents(struct commit *commit)
+{
+	struct commit_list *p;
+	int count;
+
+	for (count = 0, p = commit->parents; p; p = p->next) {
+		if (p->item->object.flags & UNINTERESTING)
+			continue;
+		count++;
+	}
+	return count;
+}
+
+static inline int halfway(struct commit_list *p, int nr)
+{
+	/*
+	 * Don't short-cut something we are not going to return!
+	 */
+	if (p->item->object.flags & TREESAME)
+		return 0;
+	if (DEBUG_BISECT)
+		return 0;
+	/*
+	 * 2 and 3 are halfway of 5.
+	 * 3 is halfway of 6 but 2 and 4 are not.
+	 */
+	switch (2 * weight(p) - nr) {
+	case -1: case 0: case 1:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+#if !DEBUG_BISECT
+#define show_list(a,b,c,d) do { ; } while (0)
+#else
+static void show_list(const char *debug, int counted, int nr,
+		      struct commit_list *list)
+{
+	struct commit_list *p;
+
+	fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
+
+	for (p = list; p; p = p->next) {
+		struct commit_list *pp;
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+		enum object_type type;
+		unsigned long size;
+		char *buf = read_sha1_file(commit->object.sha1, &type, &size);
+		char *ep, *sp;
+
+		fprintf(stderr, "%c%c%c ",
+			(flags & TREESAME) ? ' ' : 'T',
+			(flags & UNINTERESTING) ? 'U' : ' ',
+			(flags & COUNTED) ? 'C' : ' ');
+		if (commit->util)
+			fprintf(stderr, "%3d", weight(p));
+		else
+			fprintf(stderr, "---");
+		fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
+		for (pp = commit->parents; pp; pp = pp->next)
+			fprintf(stderr, " %.*s", 8,
+				sha1_to_hex(pp->item->object.sha1));
+
+		sp = strstr(buf, "\n\n");
+		if (sp) {
+			sp += 2;
+			for (ep = sp; *ep && *ep != '\n'; ep++)
+				;
+			fprintf(stderr, " %.*s", (int)(ep - sp), sp);
+		}
+		fprintf(stderr, "\n");
+	}
+}
+#endif /* DEBUG_BISECT */
+
+static struct commit_list *best_bisection(struct commit_list *list, int nr)
+{
+	struct commit_list *p, *best;
+	int best_distance = -1;
+
+	best = list;
+	for (p = list; p; p = p->next) {
+		int distance;
+		unsigned flags = p->item->object.flags;
+
+		if (flags & TREESAME)
+			continue;
+		distance = weight(p);
+		if (nr - distance < distance)
+			distance = nr - distance;
+		if (distance > best_distance) {
+			best = p;
+			best_distance = distance;
+		}
+	}
+
+	return best;
+}
+
+struct commit_dist {
+	struct commit *commit;
+	int distance;
+};
+
+static int compare_commit_dist(const void *a_, const void *b_)
+{
+	struct commit_dist *a, *b;
+
+	a = (struct commit_dist *)a_;
+	b = (struct commit_dist *)b_;
+	if (a->distance != b->distance)
+		return b->distance - a->distance; /* desc sort */
+	return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
+}
+
+static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
+{
+	struct commit_list *p;
+	struct commit_dist *array = xcalloc(nr, sizeof(*array));
+	int cnt, i;
+
+	for (p = list, cnt = 0; p; p = p->next) {
+		int distance;
+		unsigned flags = p->item->object.flags;
+
+		if (flags & TREESAME)
+			continue;
+		distance = weight(p);
+		if (nr - distance < distance)
+			distance = nr - distance;
+		array[cnt].commit = p->item;
+		array[cnt].distance = distance;
+		cnt++;
+	}
+	qsort(array, cnt, sizeof(*array), compare_commit_dist);
+	for (p = list, i = 0; i < cnt; i++) {
+		struct name_decoration *r = xmalloc(sizeof(*r) + 100);
+		struct object *obj = &(array[i].commit->object);
+
+		sprintf(r->name, "dist=%d", array[i].distance);
+		r->next = add_decoration(&name_decoration, obj, r);
+		p->item = array[i].commit;
+		p = p->next;
+	}
+	if (p)
+		p->next = NULL;
+	free(array);
+	return list;
+}
+
+/*
+ * zero or positive weight is the number of interesting commits it can
+ * reach, including itself.  Especially, weight = 0 means it does not
+ * reach any tree-changing commits (e.g. just above uninteresting one
+ * but traversal is with pathspec).
+ *
+ * weight = -1 means it has one parent and its distance is yet to
+ * be computed.
+ *
+ * weight = -2 means it has more than one parent and its distance is
+ * unknown.  After running count_distance() first, they will get zero
+ * or positive distance.
+ */
+static struct commit_list *do_find_bisection(struct commit_list *list,
+					     int nr, int *weights,
+					     int find_all)
+{
+	int n, counted;
+	struct commit_list *p;
+
+	counted = 0;
+
+	for (n = 0, p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+
+		p->item->util = &weights[n++];
+		switch (count_interesting_parents(commit)) {
+		case 0:
+			if (!(flags & TREESAME)) {
+				weight_set(p, 1);
+				counted++;
+				show_list("bisection 2 count one",
+					  counted, nr, list);
+			}
+			/*
+			 * otherwise, it is known not to reach any
+			 * tree-changing commit and gets weight 0.
+			 */
+			break;
+		case 1:
+			weight_set(p, -1);
+			break;
+		default:
+			weight_set(p, -2);
+			break;
+		}
+	}
+
+	show_list("bisection 2 initialize", counted, nr, list);
+
+	/*
+	 * If you have only one parent in the resulting set
+	 * then you can reach one commit more than that parent
+	 * can reach.  So we do not have to run the expensive
+	 * count_distance() for single strand of pearls.
+	 *
+	 * However, if you have more than one parents, you cannot
+	 * just add their distance and one for yourself, since
+	 * they usually reach the same ancestor and you would
+	 * end up counting them twice that way.
+	 *
+	 * So we will first count distance of merges the usual
+	 * way, and then fill the blanks using cheaper algorithm.
+	 */
+	for (p = list; p; p = p->next) {
+		if (p->item->object.flags & UNINTERESTING)
+			continue;
+		if (weight(p) != -2)
+			continue;
+		weight_set(p, count_distance(p));
+		clear_distance(list);
+
+		/* Does it happen to be at exactly half-way? */
+		if (!find_all && halfway(p, nr))
+			return p;
+		counted++;
+	}
+
+	show_list("bisection 2 count_distance", counted, nr, list);
+
+	while (counted < nr) {
+		for (p = list; p; p = p->next) {
+			struct commit_list *q;
+			unsigned flags = p->item->object.flags;
+
+			if (0 <= weight(p))
+				continue;
+			for (q = p->item->parents; q; q = q->next) {
+				if (q->item->object.flags & UNINTERESTING)
+					continue;
+				if (0 <= weight(q))
+					break;
+			}
+			if (!q)
+				continue;
+
+			/*
+			 * weight for p is unknown but q is known.
+			 * add one for p itself if p is to be counted,
+			 * otherwise inherit it from q directly.
+			 */
+			if (!(flags & TREESAME)) {
+				weight_set(p, weight(q)+1);
+				counted++;
+				show_list("bisection 2 count one",
+					  counted, nr, list);
+			}
+			else
+				weight_set(p, weight(q));
+
+			/* Does it happen to be at exactly half-way? */
+			if (!find_all && halfway(p, nr))
+				return p;
+		}
+	}
+
+	show_list("bisection 2 counted all", counted, nr, list);
+
+	if (!find_all)
+		return best_bisection(list, nr);
+	else
+		return best_bisection_sorted(list, nr);
+}
+
+struct commit_list *find_bisection(struct commit_list *list,
+					  int *reaches, int *all,
+					  int find_all)
+{
+	int nr, on_list;
+	struct commit_list *p, *best, *next, *last;
+	int *weights;
+
+	show_list("bisection 2 entry", 0, 0, list);
+
+	/*
+	 * Count the number of total and tree-changing items on the
+	 * list, while reversing the list.
+	 */
+	for (nr = on_list = 0, last = NULL, p = list;
+	     p;
+	     p = next) {
+		unsigned flags = p->item->object.flags;
+
+		next = p->next;
+		if (flags & UNINTERESTING)
+			continue;
+		p->next = last;
+		last = p;
+		if (!(flags & TREESAME))
+			nr++;
+		on_list++;
+	}
+	list = last;
+	show_list("bisection 2 sorted", 0, nr, list);
+
+	*all = nr;
+	weights = xcalloc(on_list, sizeof(*weights));
+
+	/* Do the real work of finding bisection commit. */
+	best = do_find_bisection(list, nr, weights, find_all);
+	if (best) {
+		if (!find_all)
+			best->next = NULL;
+		*reaches = weight(best);
+	}
+	free(weights);
+	return best;
+}
+
+static int register_ref(const char *refname, const unsigned char *sha1,
+			int flags, void *cb_data)
+{
+	if (!strcmp(refname, "bad")) {
+		ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+		rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
+	} else if (!prefixcmp(refname, "good-")) {
+		const char *hex = sha1_to_hex(sha1);
+		char *good = xmalloc(strlen(hex) + 2);
+		*good = '^';
+		memcpy(good + 1, hex, strlen(hex) + 1);
+		ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+		rev_argv[rev_argv_nr++] = good;
+	} else if (!prefixcmp(refname, "skip-")) {
+		ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
+			   skipped_sha1_alloc);
+		hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
+	}
+
+	return 0;
+}
+
+static int read_bisect_refs(void)
+{
+	return for_each_ref_in("refs/bisect/", register_ref, NULL);
+}
+
+void read_bisect_paths(void)
+{
+	struct strbuf str = STRBUF_INIT;
+	const char *filename = git_path("BISECT_NAMES");
+	FILE *fp = fopen(filename, "r");
+
+	if (!fp)
+		die("Could not open file '%s': %s", filename, strerror(errno));
+
+	while (strbuf_getline(&str, fp, '\n') != EOF) {
+		char *quoted;
+		int res;
+
+		strbuf_trim(&str);
+		quoted = strbuf_detach(&str, NULL);
+		res = sq_dequote_to_argv(quoted, &rev_argv,
+					 &rev_argv_nr, &rev_argv_alloc);
+		if (res)
+			die("Badly quoted content in file '%s': %s",
+			    filename, quoted);
+	}
+
+	strbuf_release(&str);
+	fclose(fp);
+}
+
+static int skipcmp(const void *a, const void *b)
+{
+	return hashcmp(a, b);
+}
+
+static void prepare_skipped(void)
+{
+	qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
+}
+
+static const unsigned char *skipped_sha1_access(size_t index, void *table)
+{
+	unsigned char (*skipped)[20] = table;
+	return skipped[index];
+}
+
+static int lookup_skipped(unsigned char *sha1)
+{
+	return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
+			skipped_sha1_access);
+}
+
+struct commit_list *filter_skipped(struct commit_list *list,
+				   struct commit_list **tried,
+				   int show_all)
+{
+	struct commit_list *filtered = NULL, **f = &filtered;
+
+	*tried = NULL;
+
+	if (!skipped_sha1_nr)
+		return list;
+
+	prepare_skipped();
+
+	while (list) {
+		struct commit_list *next = list->next;
+		list->next = NULL;
+		if (0 <= lookup_skipped(list->item->object.sha1)) {
+			/* Move current to tried list */
+			*tried = list;
+			tried = &list->next;
+		} else {
+			if (!show_all)
+				return list;
+			/* Move current to filtered list */
+			*f = list;
+			f = &list->next;
+		}
+		list = next;
+	}
+
+	return filtered;
+}
+
+static void bisect_rev_setup(struct rev_info *revs, const char *prefix)
+{
+	init_revisions(revs, prefix);
+	revs->abbrev = 0;
+	revs->commit_format = CMIT_FMT_UNSPECIFIED;
+
+	/* argv[0] will be ignored by setup_revisions */
+	ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+	rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
+
+	if (read_bisect_refs())
+		die("reading bisect refs failed");
+
+	ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+	rev_argv[rev_argv_nr++] = xstrdup("--");
+
+	read_bisect_paths();
+
+	ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
+	rev_argv[rev_argv_nr++] = NULL;
+
+	setup_revisions(rev_argv_nr, rev_argv, revs, NULL);
+
+	revs->limited = 1;
+}
+
+int bisect_next_vars(const char *prefix)
+{
+	struct rev_info revs;
+	struct rev_list_info info;
+	int reaches = 0, all = 0;
+
+	memset(&info, 0, sizeof(info));
+	info.revs = &revs;
+	info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED;
+
+	bisect_rev_setup(&revs, prefix);
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	if (revs.tree_objects)
+		mark_edges_uninteresting(revs.commits, &revs, NULL);
+
+	revs.commits = find_bisection(revs.commits, &reaches, &all,
+				      !!skipped_sha1_nr);
+
+	return show_bisect_vars(&info, reaches, all);
+}
diff --git a/bisect.h b/bisect.h
new file mode 100644
index 0000000..fdba913
--- /dev/null
+++ b/bisect.h
@@ -0,0 +1,29 @@
+#ifndef BISECT_H
+#define BISECT_H
+
+extern struct commit_list *find_bisection(struct commit_list *list,
+					  int *reaches, int *all,
+					  int find_all);
+
+extern struct commit_list *filter_skipped(struct commit_list *list,
+					  struct commit_list **tried,
+					  int show_all);
+
+/* bisect_show_flags flags in struct rev_list_info */
+#define BISECT_SHOW_ALL		(1<<0)
+#define BISECT_SHOW_TRIED	(1<<1)
+#define BISECT_SHOW_STRINGED	(1<<2)
+
+struct rev_list_info {
+	struct rev_info *revs;
+	int bisect_show_flags;
+	int show_timestamp;
+	int hdr_termination;
+	const char *header_prefix;
+};
+
+extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
+
+extern int bisect_next_vars(const char *prefix);
+
+#endif
diff --git a/branch.c b/branch.c
index 6a75057..62030af 100644
--- a/branch.c
+++ b/branch.c
@@ -32,21 +32,59 @@
 	return 0;
 }
 
-static int should_setup_rebase(const struct tracking *tracking)
+static int should_setup_rebase(const char *origin)
 {
 	switch (autorebase) {
 	case AUTOREBASE_NEVER:
 		return 0;
 	case AUTOREBASE_LOCAL:
-		return tracking->remote == NULL;
+		return origin == NULL;
 	case AUTOREBASE_REMOTE:
-		return tracking->remote != NULL;
+		return origin != NULL;
 	case AUTOREBASE_ALWAYS:
 		return 1;
 	}
 	return 0;
 }
 
+void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
+{
+	struct strbuf key = STRBUF_INIT;
+	int rebasing = should_setup_rebase(origin);
+
+	strbuf_addf(&key, "branch.%s.remote", local);
+	git_config_set(key.buf, origin ? origin : ".");
+
+	strbuf_reset(&key);
+	strbuf_addf(&key, "branch.%s.merge", local);
+	git_config_set(key.buf, remote);
+
+	if (rebasing) {
+		strbuf_reset(&key);
+		strbuf_addf(&key, "branch.%s.rebase", local);
+		git_config_set(key.buf, "true");
+	}
+
+	if (flag & BRANCH_CONFIG_VERBOSE) {
+		strbuf_reset(&key);
+
+		strbuf_addstr(&key, origin ? "remote" : "local");
+
+		/* Are we tracking a proper "branch"? */
+		if (!prefixcmp(remote, "refs/heads/")) {
+			strbuf_addf(&key, " branch %s", remote + 11);
+			if (origin)
+				strbuf_addf(&key, " from %s", origin);
+		}
+		else
+			strbuf_addf(&key, " ref %s", remote);
+		printf("Branch %s set up to track %s%s.\n",
+		       local, key.buf,
+		       rebasing ? " by rebasing" : "");
+	}
+	strbuf_release(&key);
+}
+
 /*
  * This is called when new_ref is branched off of orig_ref, and tries
  * to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -55,7 +93,6 @@
 static int setup_tracking(const char *new_ref, const char *orig_ref,
                           enum branch_track track)
 {
-	char key[1024];
 	struct tracking tracking;
 
 	if (strlen(new_ref) > 1024 - 7 - 7 - 1)
@@ -80,19 +117,10 @@
 		return error("Not tracking: ambiguous information for ref %s",
 				orig_ref);
 
-	sprintf(key, "branch.%s.remote", new_ref);
-	git_config_set(key, tracking.remote ?  tracking.remote : ".");
-	sprintf(key, "branch.%s.merge", new_ref);
-	git_config_set(key, tracking.src ? tracking.src : orig_ref);
-	printf("Branch %s set up to track %s branch %s.\n", new_ref,
-		tracking.remote ? "remote" : "local", orig_ref);
-	if (should_setup_rebase(&tracking)) {
-		sprintf(key, "branch.%s.rebase", new_ref);
-		git_config_set(key, "true");
-		printf("This branch will rebase on pull.\n");
-	}
-	free(tracking.src);
+	install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
+			      tracking.src ? tracking.src : orig_ref);
 
+	free(tracking.src);
 	return 0;
 }
 
@@ -103,14 +131,14 @@
 	struct ref_lock *lock;
 	struct commit *commit;
 	unsigned char sha1[20];
-	char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
+	char *real_ref, msg[PATH_MAX + 20];
+	struct strbuf ref = STRBUF_INIT;
 	int forcing = 0;
 
-	snprintf(ref, sizeof ref, "refs/heads/%s", name);
-	if (check_ref_format(ref))
+	if (strbuf_check_branch_ref(&ref, name))
 		die("'%s' is not a valid branch name.", name);
 
-	if (resolve_ref(ref, sha1, 1, NULL)) {
+	if (resolve_ref(ref.buf, sha1, 1, NULL)) {
 		if (!force)
 			die("A branch named '%s' already exists.", name);
 		else if (!is_bare_repository() && !strcmp(head, name))
@@ -142,7 +170,7 @@
 		die("Not a valid branch point: '%s'.", start_name);
 	hashcpy(sha1, commit->object.sha1);
 
-	lock = lock_any_ref_for_update(ref, NULL, 0);
+	lock = lock_any_ref_for_update(ref.buf, NULL, 0);
 	if (!lock)
 		die("Failed to lock ref for update: %s.", strerror(errno));
 
@@ -162,6 +190,7 @@
 	if (write_ref_sha1(lock, sha1, msg) < 0)
 		die("Failed to write ref: %s.", strerror(errno));
 
+	strbuf_release(&ref);
 	free(real_ref);
 }
 
@@ -170,5 +199,6 @@
 	unlink(git_path("MERGE_HEAD"));
 	unlink(git_path("MERGE_RR"));
 	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("MERGE_MODE"));
 	unlink(git_path("SQUASH_MSG"));
 }
diff --git a/branch.h b/branch.h
index 9f0c2a2..eed817a 100644
--- a/branch.h
+++ b/branch.h
@@ -21,4 +21,11 @@
  */
 void remove_branch_state(void);
 
+/*
+ * Configure local branch "local" to merge remote branch "remote"
+ * taken from origin "origin".
+ */
+#define BRANCH_CONFIG_VERBOSE 01
+extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
+
 #endif
diff --git a/builtin-add.c b/builtin-add.c
index fc3f96e..cb67d2c 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -8,10 +8,6 @@
 #include "dir.h"
 #include "exec_cmd.h"
 #include "cache-tree.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "commit.h"
-#include "revision.h"
 #include "run-command.h"
 #include "parse-options.h"
 
@@ -19,9 +15,30 @@
 	"git add [options] [--] <filepattern>...",
 	NULL
 };
-static int patch_interactive = 0, add_interactive = 0;
+static int patch_interactive, add_interactive;
 static int take_worktree_changes;
 
+static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
+{
+	int num_unmatched = 0, i;
+
+	/*
+	 * Since we are walking the index as if we were walking the directory,
+	 * we have to mark the matched pathspec as seen; otherwise we will
+	 * mistakenly think that the user gave a pathspec that did not match
+	 * anything.
+	 */
+	for (i = 0; i < specs; i++)
+		if (!seen[i])
+			num_unmatched++;
+	if (!num_unmatched)
+		return;
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
+	}
+}
+
 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
 	char *seen;
@@ -41,6 +58,7 @@
 			*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;
+	fill_pathspec_matches(pathspec, seen, specs);
 
 	for (i = 0; i < specs; i++) {
 		if (!seen[i] && !file_exists(pathspec[i]))
@@ -50,6 +68,33 @@
         free(seen);
 }
 
+static void treat_gitlinks(const char **pathspec)
+{
+	int i;
+
+	if (!pathspec || !*pathspec)
+		return;
+
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (S_ISGITLINK(ce->ce_mode)) {
+			int len = ce_namelen(ce), j;
+			for (j = 0; pathspec[j]; j++) {
+				int len2 = strlen(pathspec[j]);
+				if (len2 <= len || pathspec[j][len] != '/' ||
+				    memcmp(ce->name, pathspec[j], len))
+					continue;
+				if (len2 == len + 1)
+					/* strip trailing slash */
+					pathspec[j] = xstrndup(ce->name, len);
+				else
+					die ("Path '%s' is in submodule '%.*s'",
+						pathspec[j], len, ce->name);
+			}
+		}
+	}
+}
+
 static void fill_directory(struct dir_struct *dir, const char **pathspec,
 		int ignored_too)
 {
@@ -59,7 +104,7 @@
 	/* Set up the default git porcelain excludes */
 	memset(dir, 0, sizeof(*dir));
 	if (!ignored_too) {
-		dir->collect_ignored = 1;
+		dir->flags |= DIR_COLLECT_IGNORED;
 		setup_standard_excludes(dir);
 	}
 
@@ -79,59 +124,6 @@
 		prune_directory(dir, pathspec, baselen);
 }
 
-struct update_callback_data
-{
-	int flags;
-	int add_errors;
-};
-
-static void update_callback(struct diff_queue_struct *q,
-			    struct diff_options *opt, void *cbdata)
-{
-	int i;
-	struct update_callback_data *data = cbdata;
-
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		const char *path = p->one->path;
-		switch (p->status) {
-		default:
-			die("unexpected diff status %c", p->status);
-		case DIFF_STATUS_UNMERGED:
-		case DIFF_STATUS_MODIFIED:
-		case DIFF_STATUS_TYPE_CHANGED:
-			if (add_file_to_cache(path, data->flags)) {
-				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
-					die("updating files failed");
-				data->add_errors++;
-			}
-			break;
-		case DIFF_STATUS_DELETED:
-			if (!(data->flags & ADD_CACHE_PRETEND))
-				remove_file_from_cache(path);
-			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
-				printf("remove '%s'\n", path);
-			break;
-		}
-	}
-}
-
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
-{
-	struct update_callback_data data;
-	struct rev_info rev;
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-	rev.prune_data = pathspec;
-	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = update_callback;
-	data.flags = flags;
-	data.add_errors = 0;
-	rev.diffopt.format_callback_data = &data;
-	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	return !!data.add_errors;
-}
-
 static void refresh(int verbose, const char **pathspec)
 {
 	char *seen;
@@ -153,6 +145,16 @@
 {
 	const char **pathspec = get_pathspec(prefix, argv);
 
+	if (pathspec) {
+		const char **p;
+		for (p = pathspec; *p; p++) {
+			if (has_symlink_leading_path(*p, strlen(*p))) {
+				int len = prefix ? strlen(prefix) : 0;
+				die("'%s' is beyond a symbolic link", *p + len);
+			}
+		}
+	}
+
 	return pathspec;
 }
 
@@ -191,7 +193,7 @@
 "The following paths are ignored by one of your .gitignore files:\n";
 
 static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove;
+static int ignore_add_errors, addremove, intent_to_add;
 
 static struct option builtin_add_options[] = {
 	OPT__DRY_RUN(&show_only),
@@ -201,6 +203,7 @@
 	OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
 	OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
 	OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
+	OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
 	OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
 	OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
 	OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
@@ -258,7 +261,7 @@
 
 	if (addremove && take_worktree_changes)
 		die("-A and -u are mutually incompatible");
-	if (addremove && !argc) {
+	if ((addremove || take_worktree_changes) && !argc) {
 		static const char *here[2] = { ".", NULL };
 		argc = 1;
 		argv = here;
@@ -271,33 +274,32 @@
 
 	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
 		 (show_only ? ADD_CACHE_PRETEND : 0) |
-		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
+		 (intent_to_add ? ADD_CACHE_INTENT : 0) |
+		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+		 (!(addremove || take_worktree_changes)
+		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
 
 	if (require_pathspec && argc == 0) {
 		fprintf(stderr, "Nothing specified, nothing added.\n");
 		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
 		return 0;
 	}
-	pathspec = get_pathspec(prefix, argv);
-
-	/*
-	 * If we are adding new files, we need to scan the working
-	 * tree to find the ones that match pathspecs; this needs
-	 * to be done before we read the index.
-	 */
-	if (add_new_files)
-		fill_directory(&dir, pathspec, ignored_too);
+	pathspec = validate_pathspec(argc, argv, prefix);
 
 	if (read_cache() < 0)
 		die("index file corrupt");
+	treat_gitlinks(pathspec);
+
+	if (add_new_files)
+		/* This picks up the paths that are not tracked */
+		fill_directory(&dir, pathspec, ignored_too);
 
 	if (refresh_only) {
 		refresh(verbose, pathspec);
 		goto finish;
 	}
 
-	if (take_worktree_changes || addremove)
-		exit_status |= add_files_to_cache(prefix, pathspec, flags);
+	exit_status |= add_files_to_cache(prefix, pathspec, flags);
 
 	if (add_new_files)
 		exit_status |= add_files(&dir, flags);
diff --git a/builtin-apply.c b/builtin-apply.c
index 7a1ff04..7b404ef 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -14,6 +14,7 @@
 #include "builtin.h"
 #include "string-list.h"
 #include "dir.h"
+#include "parse-options.h"
 
 /*
  *  --check turns on checking that the working tree matches the
@@ -45,9 +46,11 @@
 static int no_add;
 static const char *fake_ancestor;
 static int line_termination = '\n';
-static unsigned long p_context = ULONG_MAX;
-static const char apply_usage[] =
-"git apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...";
+static unsigned int p_context = UINT_MAX;
+static const char * const apply_usage[] = {
+	"git apply [options] [<patch>...]",
+	NULL
+};
 
 static enum ws_error_action {
 	nowarn_ws_error,
@@ -61,6 +64,8 @@
 static const char *patch_input_file;
 static const char *root;
 static int root_len;
+static int read_stdin = 1;
+static int options;
 
 static void parse_whitespace_option(const char *option)
 {
@@ -321,13 +326,12 @@
 	const char *start = line;
 
 	if (*line == '"') {
-		struct strbuf name;
+		struct strbuf name = STRBUF_INIT;
 
 		/*
 		 * Proposed "new-style" GNU patch/diff format; see
 		 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
 		 */
-		strbuf_init(&name, 0);
 		if (!unquote_c_style(&name, line, NULL)) {
 			char *cp;
 
@@ -631,7 +635,7 @@
 	memcpy(patch->new_sha1_prefix, line, len);
 	patch->new_sha1_prefix[len] = 0;
 	if (*ptr == ' ')
-		patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+		patch->old_mode = strtoul(ptr+1, NULL, 8);
 	return 0;
 }
 
@@ -675,11 +679,8 @@
 
 	if (*line == '"') {
 		const char *cp;
-		struct strbuf first;
-		struct strbuf sp;
-
-		strbuf_init(&first, 0);
-		strbuf_init(&sp, 0);
+		struct strbuf first = STRBUF_INIT;
+		struct strbuf sp = STRBUF_INIT;
 
 		if (unquote_c_style(&first, line, &second))
 			goto free_and_fail1;
@@ -741,10 +742,9 @@
 	 */
 	for (second = name; second < line + llen; second++) {
 		if (*second == '"') {
-			struct strbuf sp;
+			struct strbuf sp = STRBUF_INIT;
 			const char *np;
 
-			strbuf_init(&sp, 0);
 			if (unquote_c_style(&sp, second, NULL))
 				goto free_and_fail2;
 
@@ -1258,8 +1258,9 @@
 	stream.avail_in = size;
 	stream.next_out = out = xmalloc(inflated_size);
 	stream.avail_out = inflated_size;
-	inflateInit(&stream);
-	st = inflate(&stream, Z_FINISH);
+	git_inflate_init(&stream);
+	st = git_inflate(&stream, Z_FINISH);
+	git_inflate_end(&stream);
 	if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
 		free(out);
 		return NULL;
@@ -1515,11 +1516,10 @@
 
 static void show_stats(struct patch *patch)
 {
-	struct strbuf qname;
+	struct strbuf qname = STRBUF_INIT;
 	char *cp = patch->new_name ? patch->new_name : patch->old_name;
 	int max, add, del;
 
-	strbuf_init(&qname, 0);
 	quote_c_style(cp, &qname, NULL, 0);
 
 	/*
@@ -1565,10 +1565,8 @@
 {
 	switch (st->st_mode & S_IFMT) {
 	case S_IFLNK:
-		strbuf_grow(buf, st->st_size);
-		if (readlink(path, buf->buf, st->st_size) != st->st_size)
-			return -1;
-		strbuf_setlen(buf, st->st_size);
+		if (strbuf_readlink(buf, path, st->st_size) < 0)
+			return error("unable to read symlink %s", path);
 		return 0;
 	case S_IFREG:
 		if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
@@ -2273,6 +2271,25 @@
 	return NULL;
 }
 
+/*
+ * item->util in the filename table records the status of the path.
+ * Usually it points at a patch (whose result records the contents
+ * of it after applying it), but it could be PATH_WAS_DELETED for a
+ * path that a previously applied patch has already removed.
+ */
+ #define PATH_TO_BE_DELETED ((struct patch *) -2)
+#define PATH_WAS_DELETED ((struct patch *) -1)
+
+static int to_be_deleted(struct patch *patch)
+{
+	return patch == PATH_TO_BE_DELETED;
+}
+
+static int was_deleted(struct patch *patch)
+{
+	return patch == PATH_WAS_DELETED;
+}
+
 static void add_to_fn_table(struct patch *patch)
 {
 	struct string_list_item *item;
@@ -2293,23 +2310,36 @@
 	 */
 	if ((patch->new_name == NULL) || (patch->is_rename)) {
 		item = string_list_insert(patch->old_name, &fn_table);
-		item->util = (struct patch *) -1;
+		item->util = PATH_WAS_DELETED;
+	}
+}
+
+static void prepare_fn_table(struct patch *patch)
+{
+	/*
+	 * store information about incoming file deletion
+	 */
+	while (patch) {
+		if ((patch->new_name == NULL) || (patch->is_rename)) {
+			struct string_list_item *item;
+			item = string_list_insert(patch->old_name, &fn_table);
+			item->util = PATH_TO_BE_DELETED;
+		}
+		patch = patch->next;
 	}
 }
 
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	struct image image;
 	size_t len;
 	char *img;
 	struct patch *tpatch;
 
-	strbuf_init(&buf, 0);
-
 	if (!(patch->is_copy || patch->is_rename) &&
-	    ((tpatch = in_fn_table(patch->old_name)) != NULL)) {
-		if (tpatch == (struct patch *) -1) {
+	    (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
+		if (was_deleted(tpatch)) {
 			return error("patch %s has been renamed/deleted",
 				patch->old_name);
 		}
@@ -2364,7 +2394,7 @@
 		 * In such a case, path "new_name" does not exist as
 		 * far as git is concerned.
 		 */
-		if (has_symlink_leading_path(strlen(new_name), new_name))
+		if (has_symlink_leading_path(new_name, strlen(new_name)))
 			return 0;
 
 		return error("%s: already exists in working directory", new_name);
@@ -2403,10 +2433,9 @@
 	assert(patch->is_new <= 0);
 
 	if (!(patch->is_copy || patch->is_rename) &&
-	    (tpatch = in_fn_table(old_name)) != NULL) {
-		if (tpatch == (struct patch *) -1) {
+	    (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
+		if (was_deleted(tpatch))
 			return error("%s: has been deleted/renamed", old_name);
-		}
 		st_mode = tpatch->new_mode;
 	} else if (!cached) {
 		stat_ret = lstat(old_name, st);
@@ -2414,6 +2443,9 @@
 			return error("%s: %s", old_name, strerror(errno));
 	}
 
+	if (to_be_deleted(tpatch))
+		tpatch = NULL;
+
 	if (check_index && !tpatch) {
 		int pos = cache_name_pos(old_name, strlen(old_name));
 		if (pos < 0) {
@@ -2455,8 +2487,10 @@
 	if ((st_mode ^ patch->old_mode) & S_IFMT)
 		return error("%s: wrong type", old_name);
 	if (st_mode != patch->old_mode)
-		fprintf(stderr, "warning: %s has type %o, expected %o\n",
+		warning("%s has type %o, expected %o",
 			old_name, st_mode, patch->old_mode);
+	if (!patch->new_mode && !patch->is_delete)
+		patch->new_mode = st_mode;
 	return 0;
 
  is_new:
@@ -2473,6 +2507,7 @@
 	const char *new_name = patch->new_name;
 	const char *name = old_name ? old_name : new_name;
 	struct cache_entry *ce = NULL;
+	struct patch *tpatch;
 	int ok_if_exists;
 	int status;
 
@@ -2483,7 +2518,8 @@
 		return status;
 	old_name = patch->old_name;
 
-	if (in_fn_table(new_name) == (struct patch *) -1)
+	if ((tpatch = in_fn_table(new_name)) &&
+			(was_deleted(tpatch) || to_be_deleted(tpatch)))
 		/*
 		 * A type-change diff is always split into a patch to
 		 * delete old, immediately followed by a patch to
@@ -2535,6 +2571,7 @@
 {
 	int err = 0;
 
+	prepare_fn_table(patch);
 	while (patch) {
 		if (apply_verbosely)
 			say_patch_name(stderr,
@@ -2786,7 +2823,7 @@
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
 {
 	int fd;
-	struct strbuf nbuf;
+	struct strbuf nbuf = STRBUF_INIT;
 
 	if (S_ISGITLINK(mode)) {
 		struct stat st;
@@ -2805,7 +2842,6 @@
 	if (fd < 0)
 		return -1;
 
-	strbuf_init(&nbuf, 0);
 	if (convert_to_working_tree(path, buf, size, &nbuf)) {
 		size = nbuf.len;
 		buf  = nbuf.buf;
@@ -2935,8 +2971,7 @@
 	cnt = strlen(patch->new_name);
 	if (ARRAY_SIZE(namebuf) <= cnt + 5) {
 		cnt = ARRAY_SIZE(namebuf) - 5;
-		fprintf(stderr,
-			"warning: truncating .rej filename to %.*s.rej",
+		warning("truncating .rej filename to %.*s.rej",
 			cnt - 1, patch->new_name);
 	}
 	memcpy(namebuf, patch->new_name, cnt);
@@ -2996,29 +3031,45 @@
 
 static struct lock_file lock_file;
 
-static struct excludes {
-	struct excludes *next;
-	const char *path;
-} *excludes;
+static struct string_list limit_by_name;
+static int has_include;
+static void add_name_limit(const char *name, int exclude)
+{
+	struct string_list_item *it;
+
+	it = string_list_append(name, &limit_by_name);
+	it->util = exclude ? NULL : (void *) 1;
+}
 
 static int use_patch(struct patch *p)
 {
 	const char *pathname = p->new_name ? p->new_name : p->old_name;
-	struct excludes *x = excludes;
-	while (x) {
-		if (fnmatch(x->path, pathname, 0) == 0)
-			return 0;
-		x = x->next;
-	}
+	int i;
+
+	/* Paths outside are not touched regardless of "--include" */
 	if (0 < prefix_length) {
 		int pathlen = strlen(pathname);
 		if (pathlen <= prefix_length ||
 		    memcmp(prefix, pathname, prefix_length))
 			return 0;
 	}
-	return 1;
+
+	/* See if it matches any of exclude/include rule */
+	for (i = 0; i < limit_by_name.nr; i++) {
+		struct string_list_item *it = &limit_by_name.items[i];
+		if (!fnmatch(it->string, pathname, 0))
+			return (it->util != NULL);
+	}
+
+	/*
+	 * If we had any include, a path that does not match any rule is
+	 * not used.  Otherwise, we saw bunch of exclude rules (or none)
+	 * and such a path is used.
+	 */
+	return !has_include;
 }
 
+
 static void prefix_one(char **name)
 {
 	char *old_name = *name;
@@ -3051,13 +3102,12 @@
 static int apply_patch(int fd, const char *filename, int options)
 {
 	size_t offset;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	struct patch *list = NULL, **listp = &list;
 	int skipped_patch = 0;
 
 	/* FIXME - memory leak when using multiple patch files as inputs */
 	memset(&fn_table, 0, sizeof(struct string_list));
-	strbuf_init(&buf, 0);
 	patch_input_file = filename;
 	read_patch_file(&buf, fd);
 	offset = 0;
@@ -3131,149 +3181,160 @@
 	return git_default_config(var, value, cb);
 }
 
+static int option_parse_exclude(const struct option *opt,
+				const char *arg, int unset)
+{
+	add_name_limit(arg, 1);
+	return 0;
+}
+
+static int option_parse_include(const struct option *opt,
+				const char *arg, int unset)
+{
+	add_name_limit(arg, 0);
+	has_include = 1;
+	return 0;
+}
+
+static int option_parse_p(const struct option *opt,
+			  const char *arg, int unset)
+{
+	p_value = atoi(arg);
+	p_value_known = 1;
+	return 0;
+}
+
+static int option_parse_z(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		line_termination = '\n';
+	else
+		line_termination = 0;
+	return 0;
+}
+
+static int option_parse_whitespace(const struct option *opt,
+				   const char *arg, int unset)
+{
+	const char **whitespace_option = opt->value;
+
+	*whitespace_option = arg;
+	parse_whitespace_option(arg);
+	return 0;
+}
+
+static int option_parse_directory(const struct option *opt,
+				  const char *arg, int unset)
+{
+	root_len = strlen(arg);
+	if (root_len && arg[root_len - 1] != '/') {
+		char *new_root;
+		root = new_root = xmalloc(root_len + 2);
+		strcpy(new_root, arg);
+		strcpy(new_root + root_len++, "/");
+	} else
+		root = arg;
+	return 0;
+}
 
 int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 {
 	int i;
-	int read_stdin = 1;
-	int options = 0;
 	int errs = 0;
 	int is_not_gitdir;
+	int binary;
+	int force_apply = 0;
 
 	const char *whitespace_option = NULL;
 
+	struct option builtin_apply_options[] = {
+		{ OPTION_CALLBACK, 0, "exclude", NULL, "path",
+			"don't apply changes matching the given path",
+			0, option_parse_exclude },
+		{ OPTION_CALLBACK, 0, "include", NULL, "path",
+			"apply changes matching the given path",
+			0, option_parse_include },
+		{ OPTION_CALLBACK, 'p', NULL, NULL, "num",
+			"remove <num> leading slashes from traditional diff paths",
+			0, option_parse_p },
+		OPT_BOOLEAN(0, "no-add", &no_add,
+			"ignore additions made by the patch"),
+		OPT_BOOLEAN(0, "stat", &diffstat,
+			"instead of applying the patch, output diffstat for the input"),
+		{ OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary,
+		  NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
+		{ OPTION_BOOLEAN, 0, "binary", &binary,
+		  NULL, "old option, now no-op", PARSE_OPT_HIDDEN },
+		OPT_BOOLEAN(0, "numstat", &numstat,
+			"shows number of added and deleted lines in decimal notation"),
+		OPT_BOOLEAN(0, "summary", &summary,
+			"instead of applying the patch, output a summary for the input"),
+		OPT_BOOLEAN(0, "check", &check,
+			"instead of applying the patch, see if the patch is applicable"),
+		OPT_BOOLEAN(0, "index", &check_index,
+			"make sure the patch is applicable to the current index"),
+		OPT_BOOLEAN(0, "cached", &cached,
+			"apply a patch without touching the working tree"),
+		OPT_BOOLEAN(0, "apply", &force_apply,
+			"also apply the patch (use with --stat/--summary/--check)"),
+		OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file",
+			"build a temporary index based on embedded index information"),
+		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+			"paths are separated with NUL character",
+			PARSE_OPT_NOARG, option_parse_z },
+		OPT_INTEGER('C', NULL, &p_context,
+				"ensure at least <n> lines of context match"),
+		{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
+			"detect new or modified lines that have whitespace errors",
+			0, option_parse_whitespace },
+		OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
+			"apply the patch in reverse"),
+		OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
+			"don't expect at least one line of context"),
+		OPT_BOOLEAN(0, "reject", &apply_with_reject,
+			"leave the rejected hunks in corresponding *.rej files"),
+		OPT__VERBOSE(&apply_verbosely),
+		OPT_BIT(0, "inaccurate-eof", &options,
+			"tolerate incorrectly detected missing new-line at the end of file",
+			INACCURATE_EOF),
+		OPT_BIT(0, "recount", &options,
+			"do not trust the line counts in the hunk headers",
+			RECOUNT),
+		{ OPTION_CALLBACK, 0, "directory", NULL, "root",
+			"prepend <root> to all filenames",
+			0, option_parse_directory },
+		OPT_END()
+	};
+
 	prefix = setup_git_directory_gently(&is_not_gitdir);
 	prefix_length = prefix ? strlen(prefix) : 0;
 	git_config(git_apply_config, NULL);
 	if (apply_default_whitespace)
 		parse_whitespace_option(apply_default_whitespace);
 
-	for (i = 1; i < argc; i++) {
+	argc = parse_options(argc, argv, builtin_apply_options,
+			apply_usage, 0);
+	if (apply_with_reject)
+		apply = apply_verbosely = 1;
+	if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
+		apply = 0;
+	if (check_index && is_not_gitdir)
+		die("--index outside a repository");
+	if (cached) {
+		if (is_not_gitdir)
+			die("--cached outside a repository");
+		check_index = 1;
+	}
+	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
-		char *end;
 		int fd;
 
 		if (!strcmp(arg, "-")) {
 			errs |= apply_patch(0, "<stdin>", options);
 			read_stdin = 0;
 			continue;
-		}
-		if (!prefixcmp(arg, "--exclude=")) {
-			struct excludes *x = xmalloc(sizeof(*x));
-			x->path = arg + 10;
-			x->next = excludes;
-			excludes = x;
-			continue;
-		}
-		if (!prefixcmp(arg, "-p")) {
-			p_value = atoi(arg + 2);
-			p_value_known = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--no-add")) {
-			no_add = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--stat")) {
-			apply = 0;
-			diffstat = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--allow-binary-replacement") ||
-		    !strcmp(arg, "--binary")) {
-			continue; /* now no-op */
-		}
-		if (!strcmp(arg, "--numstat")) {
-			apply = 0;
-			numstat = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--summary")) {
-			apply = 0;
-			summary = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--check")) {
-			apply = 0;
-			check = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--index")) {
-			if (is_not_gitdir)
-				die("--index outside a repository");
-			check_index = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--cached")) {
-			if (is_not_gitdir)
-				die("--cached outside a repository");
-			check_index = 1;
-			cached = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--apply")) {
-			apply = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--build-fake-ancestor")) {
-			apply = 0;
-			if (++i >= argc)
-				die ("need a filename");
-			fake_ancestor = argv[i];
-			continue;
-		}
-		if (!strcmp(arg, "-z")) {
-			line_termination = 0;
-			continue;
-		}
-		if (!prefixcmp(arg, "-C")) {
-			p_context = strtoul(arg + 2, &end, 0);
-			if (*end != '\0')
-				die("unrecognized context count '%s'", arg + 2);
-			continue;
-		}
-		if (!prefixcmp(arg, "--whitespace=")) {
-			whitespace_option = arg + 13;
-			parse_whitespace_option(arg + 13);
-			continue;
-		}
-		if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
-			apply_in_reverse = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--unidiff-zero")) {
-			unidiff_zero = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--reject")) {
-			apply = apply_with_reject = apply_verbosely = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
-			apply_verbosely = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--inaccurate-eof")) {
-			options |= INACCURATE_EOF;
-			continue;
-		}
-		if (!strcmp(arg, "--recount")) {
-			options |= RECOUNT;
-			continue;
-		}
-		if (!prefixcmp(arg, "--directory=")) {
-			arg += strlen("--directory=");
-			root_len = strlen(arg);
-			if (root_len && arg[root_len - 1] != '/') {
-				char *new_root;
-				root = new_root = xmalloc(root_len + 2);
-				strcpy(new_root, arg);
-				strcpy(new_root + root_len++, "/");
-			} else
-				root = arg;
-			continue;
-		}
-		if (0 < prefix_length)
+		} else if (0 < prefix_length)
 			arg = prefix_filename(prefix, prefix_length, arg);
 
 		fd = open(arg, O_RDONLY);
@@ -3292,8 +3353,8 @@
 		    squelch_whitespace_errors < whitespace_error) {
 			int squelched =
 				whitespace_error - squelch_whitespace_errors;
-			fprintf(stderr, "warning: squelched %d "
-				"whitespace error%s\n",
+			warning("squelched %d "
+				"whitespace error%s",
 				squelched,
 				squelched == 1 ? "" : "s");
 		}
@@ -3303,12 +3364,12 @@
 			    whitespace_error == 1 ? "" : "s",
 			    whitespace_error == 1 ? "s" : "");
 		if (applied_after_fixing_ws && apply)
-			fprintf(stderr, "warning: %d line%s applied after"
-				" fixing whitespace errors.\n",
+			warning("%d line%s applied after"
+				" fixing whitespace errors.",
 				applied_after_fixing_ws,
 				applied_after_fixing_ws == 1 ? "" : "s");
 		else if (whitespace_error)
-			fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n",
+			warning("%d line%s add%s whitespace errors.",
 				whitespace_error,
 				whitespace_error == 1 ? "" : "s",
 				whitespace_error == 1 ? "s" : "");
diff --git a/builtin-archive.c b/builtin-archive.c
index 5ceec43..ab50ceb 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -5,44 +5,35 @@
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "parse-options.h"
 #include "pkt-line.h"
 #include "sideband.h"
 
-static int run_remote_archiver(const char *remote, int argc,
-			       const char **argv)
+static void create_output_file(const char *output_file)
+{
+	int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+	if (output_fd < 0)
+		die("could not create archive file: %s ", output_file);
+	if (output_fd != 1) {
+		if (dup2(output_fd, 1) < 0)
+			die("could not redirect output");
+		else
+			close(output_fd);
+	}
+}
+
+static int run_remote_archiver(int argc, const char **argv,
+			       const char *remote, const char *exec)
 {
 	char *url, buf[LARGE_PACKET_MAX];
 	int fd[2], i, len, rv;
 	struct child_process *conn;
-	const char *exec = "git-upload-archive";
-	int exec_at = 0, exec_value_at = 0;
-
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-		if (!prefixcmp(arg, "--exec=")) {
-			if (exec_at)
-				die("multiple --exec specified");
-			exec = arg + 7;
-			exec_at = i;
-		} else if (!strcmp(arg, "--exec")) {
-			if (exec_at)
-				die("multiple --exec specified");
-			if (i + 1 >= argc)
-				die("option --exec requires a value");
-			exec = argv[i + 1];
-			exec_at = i;
-			exec_value_at = ++i;
-		}
-	}
 
 	url = xstrdup(remote);
 	conn = git_connect(fd, url, exec, 0);
 
-	for (i = 1; i < argc; i++) {
-		if (i == exec_at || i == exec_value_at)
-			continue;
+	for (i = 1; i < argc; i++)
 		packet_write(fd[1], "argument %s\n", argv[i]);
-	}
 	packet_flush(fd[1]);
 
 	len = packet_read_line(fd[0], buf, sizeof(buf));
@@ -61,7 +52,7 @@
 		die("git archive: expected a flush");
 
 	/* Now, start reading from fd[0] and spit it out to stdout */
-	rv = recv_sideband("archive", fd[0], 1, 2);
+	rv = recv_sideband("archive", fd[0], 1);
 	close(fd[0]);
 	close(fd[1]);
 	rv |= finish_connect(conn);
@@ -69,51 +60,33 @@
 	return !!rv;
 }
 
-static const char *extract_remote_arg(int *ac, const char **av)
-{
-	int ix, iy, cnt = *ac;
-	int no_more_options = 0;
-	const char *remote = NULL;
-
-	for (ix = iy = 1; ix < cnt; ix++) {
-		const char *arg = av[ix];
-		if (!strcmp(arg, "--"))
-			no_more_options = 1;
-		if (!no_more_options) {
-			if (!prefixcmp(arg, "--remote=")) {
-				if (remote)
-					die("Multiple --remote specified");
-				remote = arg + 9;
-				continue;
-			} else if (!strcmp(arg, "--remote")) {
-				if (remote)
-					die("Multiple --remote specified");
-				if (++ix >= cnt)
-					die("option --remote requires a value");
-				remote = av[ix];
-				continue;
-			}
-			if (arg[0] != '-')
-				no_more_options = 1;
-		}
-		if (ix != iy)
-			av[iy] = arg;
-		iy++;
-	}
-	if (remote) {
-		av[--cnt] = NULL;
-		*ac = cnt;
-	}
-	return remote;
-}
+#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | 	\
+			     PARSE_OPT_KEEP_ARGV0 | 	\
+			     PARSE_OPT_KEEP_UNKNOWN |	\
+			     PARSE_OPT_NO_INTERNAL_HELP	)
 
 int cmd_archive(int argc, const char **argv, const char *prefix)
 {
+	const char *exec = "git-upload-archive";
+	const char *output = NULL;
 	const char *remote = NULL;
+	struct option local_opts[] = {
+		OPT_STRING(0, "output", &output, "file",
+			"write the archive to this file"),
+		OPT_STRING(0, "remote", &remote, "repo",
+			"retrieve the archive from remote repository <repo>"),
+		OPT_STRING(0, "exec", &exec, "cmd",
+			"path to the remote git-upload-archive command"),
+		OPT_END()
+	};
 
-	remote = extract_remote_arg(&argc, argv);
+	argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL);
+
+	if (output)
+		create_output_file(output);
+
 	if (remote)
-		return run_remote_archiver(remote, argc, argv);
+		return run_remote_archiver(argc, argv, remote, exec);
 
 	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
 
diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c
new file mode 100644
index 0000000..8fe7787
--- /dev/null
+++ b/builtin-bisect--helper.c
@@ -0,0 +1,27 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "bisect.h"
+
+static const char * const git_bisect_helper_usage[] = {
+	"git bisect--helper --next-vars",
+	NULL
+};
+
+int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+{
+	int next_vars = 0;
+	struct option options[] = {
+		OPT_BOOLEAN(0, "next-vars", &next_vars,
+			    "output next bisect step variables"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0);
+
+	if (!next_vars)
+		usage_with_options(git_bisect_helper_usage, options);
+
+	/* next-vars */
+	return bisect_next_vars(prefix);
+}
diff --git a/builtin-blame.c b/builtin-blame.c
index 101c416..83141fc 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1,5 +1,5 @@
 /*
- * Pickaxe
+ * Blame
  *
  * Copyright (c) 2006, Junio C Hamano
  */
@@ -19,6 +19,7 @@
 #include "string-list.h"
 #include "mailmap.h"
 #include "parse-options.h"
+#include "utf8.h"
 
 static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
 
@@ -39,6 +40,10 @@
 static int blank_boundary;
 static int incremental;
 static int xdl_opts = XDF_NEED_MINIMAL;
+
+static enum date_mode blame_date_mode = DATE_ISO8601;
+static size_t blame_date_width;
+
 static struct string_list mailmap;
 
 #ifndef DEBUG
@@ -73,6 +78,7 @@
  */
 struct origin {
 	int refcnt;
+	struct origin *previous;
 	struct commit *commit;
 	mmfile_t file;
 	unsigned char blob_sha1[20];
@@ -114,6 +120,8 @@
 static void origin_decref(struct origin *o)
 {
 	if (o && --o->refcnt <= 0) {
+		if (o->previous)
+			origin_decref(o->previous);
 		free(o->file.ptr);
 		free(o);
 	}
@@ -443,135 +451,6 @@
 }
 
 /*
- * Parsing of patch chunks...
- */
-struct chunk {
-	/* line number in postimage; up to but not including this
-	 * line is the same as preimage
-	 */
-	int same;
-
-	/* preimage line number after this chunk */
-	int p_next;
-
-	/* postimage line number after this chunk */
-	int t_next;
-};
-
-struct patch {
-	struct chunk *chunks;
-	int num;
-};
-
-struct blame_diff_state {
-	struct xdiff_emit_state xm;
-	struct patch *ret;
-	unsigned hunk_post_context;
-	unsigned hunk_in_pre_context : 1;
-};
-
-static void process_u_diff(void *state_, char *line, unsigned long len)
-{
-	struct blame_diff_state *state = state_;
-	struct chunk *chunk;
-	int off1, off2, len1, len2, num;
-
-	num = state->ret->num;
-	if (len < 4 || line[0] != '@' || line[1] != '@') {
-		if (state->hunk_in_pre_context && line[0] == ' ')
-			state->ret->chunks[num - 1].same++;
-		else {
-			state->hunk_in_pre_context = 0;
-			if (line[0] == ' ')
-				state->hunk_post_context++;
-			else
-				state->hunk_post_context = 0;
-		}
-		return;
-	}
-
-	if (num && state->hunk_post_context) {
-		chunk = &state->ret->chunks[num - 1];
-		chunk->p_next -= state->hunk_post_context;
-		chunk->t_next -= state->hunk_post_context;
-	}
-	state->ret->num = ++num;
-	state->ret->chunks = xrealloc(state->ret->chunks,
-				      sizeof(struct chunk) * num);
-	chunk = &state->ret->chunks[num - 1];
-	if (parse_hunk_header(line, len, &off1, &len1, &off2, &len2)) {
-		state->ret->num--;
-		return;
-	}
-
-	/* Line numbers in patch output are one based. */
-	off1--;
-	off2--;
-
-	chunk->same = len2 ? off2 : (off2 + 1);
-
-	chunk->p_next = off1 + (len1 ? len1 : 1);
-	chunk->t_next = chunk->same + len2;
-	state->hunk_in_pre_context = 1;
-	state->hunk_post_context = 0;
-}
-
-static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
-				    int context)
-{
-	struct blame_diff_state state;
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-	xdemitcb_t ecb;
-
-	xpp.flags = xdl_opts;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = context;
-	ecb.outf = xdiff_outf;
-	ecb.priv = &state;
-	memset(&state, 0, sizeof(state));
-	state.xm.consume = process_u_diff;
-	state.ret = xmalloc(sizeof(struct patch));
-	state.ret->chunks = NULL;
-	state.ret->num = 0;
-
-	xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb);
-
-	if (state.ret->num) {
-		struct chunk *chunk;
-		chunk = &state.ret->chunks[state.ret->num - 1];
-		chunk->p_next -= state.hunk_post_context;
-		chunk->t_next -= state.hunk_post_context;
-	}
-	return state.ret;
-}
-
-/*
- * Run diff between two origins and grab the patch output, so that
- * we can pass blame for lines origin is currently suspected for
- * to its parent.
- */
-static struct patch *get_patch(struct origin *parent, struct origin *origin)
-{
-	mmfile_t file_p, file_o;
-	struct patch *patch;
-
-	fill_origin_blob(parent, &file_p);
-	fill_origin_blob(origin, &file_o);
-	if (!file_p.ptr || !file_o.ptr)
-		return NULL;
-	patch = compare_buffer(&file_p, &file_o, 0);
-	num_get_patch++;
-	return patch;
-}
-
-static void free_patch(struct patch *p)
-{
-	free(p->chunks);
-	free(p);
-}
-
-/*
  * Link in a new blame entry to the scoreboard.  Entries that cover the
  * same line range have been removed from the scoreboard previously.
  */
@@ -817,6 +696,22 @@
 	}
 }
 
+struct blame_chunk_cb_data {
+	struct scoreboard *sb;
+	struct origin *target;
+	struct origin *parent;
+	long plno;
+	long tlno;
+};
+
+static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
+{
+	struct blame_chunk_cb_data *d = data;
+	blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
+	d->plno = p_next;
+	d->tlno = t_next;
+}
+
 /*
  * We are looking at the origin 'target' and aiming to pass blame
  * for the lines it is suspected to its parent.  Run diff to find
@@ -826,26 +721,28 @@
 				struct origin *target,
 				struct origin *parent)
 {
-	int i, last_in_target, plno, tlno;
-	struct patch *patch;
+	int last_in_target;
+	mmfile_t file_p, file_o;
+	struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
 
 	last_in_target = find_last_in_target(sb, target);
 	if (last_in_target < 0)
 		return 1; /* nothing remains for this target */
 
-	patch = get_patch(parent, target);
-	plno = tlno = 0;
-	for (i = 0; i < patch->num; i++) {
-		struct chunk *chunk = &patch->chunks[i];
+	fill_origin_blob(parent, &file_p);
+	fill_origin_blob(target, &file_o);
+	num_get_patch++;
 
-		blame_chunk(sb, tlno, plno, chunk->same, target, parent);
-		plno = chunk->p_next;
-		tlno = chunk->t_next;
-	}
+	memset(&xpp, 0, sizeof(xpp));
+	xpp.flags = xdl_opts;
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = 0;
+	xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
 	/* The rest (i.e. anything after tlno) are the same as the parent */
-	blame_chunk(sb, tlno, plno, last_in_target, target, parent);
+	blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
 
-	free_patch(patch);
 	return 0;
 }
 
@@ -937,6 +834,23 @@
 	}
 }
 
+struct handle_split_cb_data {
+	struct scoreboard *sb;
+	struct blame_entry *ent;
+	struct origin *parent;
+	struct blame_entry *split;
+	long plno;
+	long tlno;
+};
+
+static void handle_split_cb(void *data, long same, long p_next, long t_next)
+{
+	struct handle_split_cb_data *d = data;
+	handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
+	d->plno = p_next;
+	d->tlno = t_next;
+}
+
 /*
  * Find the lines from parent that are the same as ent so that
  * we can pass blames to it.  file_p has the blob contents for
@@ -951,8 +865,9 @@
 	const char *cp;
 	int cnt;
 	mmfile_t file_o;
-	struct patch *patch;
-	int i, plno, tlno;
+	struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
 
 	/*
 	 * Prepare mmfile that contains only the lines in ent.
@@ -967,24 +882,18 @@
 	}
 	file_o.size = cp - file_o.ptr;
 
-	patch = compare_buffer(file_p, &file_o, 1);
-
 	/*
 	 * file_o is a part of final image we are annotating.
 	 * file_p partially may match that image.
 	 */
+	memset(&xpp, 0, sizeof(xpp));
+	xpp.flags = xdl_opts;
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = 1;
 	memset(split, 0, sizeof(struct blame_entry [3]));
-	plno = tlno = 0;
-	for (i = 0; i < patch->num; i++) {
-		struct chunk *chunk = &patch->chunks[i];
-
-		handle_split(sb, ent, tlno, plno, chunk->same, parent, split);
-		plno = chunk->p_next;
-		tlno = chunk->t_next;
-	}
+	xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
 	/* remainder, if any, all match the preimage */
-	handle_split(sb, ent, tlno, plno, ent->num_lines, parent, split);
-	free_patch(patch);
+	handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
 }
 
 /*
@@ -1296,6 +1205,10 @@
 		struct origin *porigin = sg_origin[i];
 		if (!porigin)
 			continue;
+		if (!origin->previous) {
+			origin_incref(porigin);
+			origin->previous = porigin;
+		}
 		if (pass_blame_to_parent(sb, origin, porigin))
 			goto finish;
 	}
@@ -1362,11 +1275,12 @@
  * Parse author/committer line in the commit object buffer
  */
 static void get_ac_line(const char *inbuf, const char *what,
-			int bufsz, char *person, const char **mail,
+			int person_len, char *person,
+			int mail_len, char *mail,
 			unsigned long *time, const char **tz)
 {
 	int len, tzlen, maillen;
-	char *tmp, *endp, *timepos;
+	char *tmp, *endp, *timepos, *mailpos;
 
 	tmp = strstr(inbuf, what);
 	if (!tmp)
@@ -1377,10 +1291,11 @@
 		len = strlen(tmp);
 	else
 		len = endp - tmp;
-	if (bufsz <= len) {
+	if (person_len <= len) {
 	error_out:
 		/* Ugh */
-		*mail = *tz = "(unknown)";
+		*tz = "(unknown)";
+		strcpy(mail, *tz);
 		*time = 0;
 		return;
 	}
@@ -1403,9 +1318,10 @@
 	*tmp = 0;
 	while (*tmp != ' ')
 		tmp--;
-	*mail = tmp + 1;
+	mailpos = tmp + 1;
 	*tmp = 0;
 	maillen = timepos - tmp;
+	memcpy(mail, mailpos, maillen);
 
 	if (!mailmap.nr)
 		return;
@@ -1414,20 +1330,23 @@
 	 * mailmap expansion may make the name longer.
 	 * make room by pushing stuff down.
 	 */
-	tmp = person + bufsz - (tzlen + 1);
+	tmp = person + person_len - (tzlen + 1);
 	memmove(tmp, *tz, tzlen);
 	tmp[tzlen] = 0;
 	*tz = tmp;
 
-	tmp = tmp - (maillen + 1);
-	memmove(tmp, *mail, maillen);
-	tmp[maillen] = 0;
-	*mail = tmp;
-
 	/*
-	 * Now, convert e-mail using mailmap
+	 * Now, convert both name and e-mail using mailmap
 	 */
-	map_email(&mailmap, tmp + 1, person, tmp-person-1);
+	if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+		/* Add a trailing '>' to email, since map_user returns plain emails
+		   Note: It already has '<', since we replace from mail+1 */
+		mailpos = memchr(mail, '\0', mail_len);
+		if (mailpos && mailpos-mail < mail_len - 1) {
+			*mailpos = '>';
+			*(mailpos+1) = '\0';
+		}
+	}
 }
 
 static void get_commit_info(struct commit *commit,
@@ -1435,9 +1354,11 @@
 			    int detailed)
 {
 	int len;
-	char *tmp, *endp;
-	static char author_buf[1024];
-	static char committer_buf[1024];
+	char *tmp, *endp, *reencoded, *message;
+	static char author_name[1024];
+	static char author_mail[1024];
+	static char committer_name[1024];
+	static char committer_mail[1024];
 	static char summary_buf[1024];
 
 	/*
@@ -1453,24 +1374,33 @@
 			die("Cannot read commit %s",
 			    sha1_to_hex(commit->object.sha1));
 	}
-	ret->author = author_buf;
-	get_ac_line(commit->buffer, "\nauthor ",
-		    sizeof(author_buf), author_buf, &ret->author_mail,
+	reencoded = reencode_commit_message(commit, NULL);
+	message   = reencoded ? reencoded : commit->buffer;
+	ret->author = author_name;
+	ret->author_mail = author_mail;
+	get_ac_line(message, "\nauthor ",
+		    sizeof(author_name), author_name,
+		    sizeof(author_mail), author_mail,
 		    &ret->author_time, &ret->author_tz);
 
-	if (!detailed)
+	if (!detailed) {
+		free(reencoded);
 		return;
+	}
 
-	ret->committer = committer_buf;
-	get_ac_line(commit->buffer, "\ncommitter ",
-		    sizeof(committer_buf), committer_buf, &ret->committer_mail,
+	ret->committer = committer_name;
+	ret->committer_mail = committer_mail;
+	get_ac_line(message, "\ncommitter ",
+		    sizeof(committer_name), committer_name,
+		    sizeof(committer_mail), committer_mail,
 		    &ret->committer_time, &ret->committer_tz);
 
 	ret->summary = summary_buf;
-	tmp = strstr(commit->buffer, "\n\n");
+	tmp = strstr(message, "\n\n");
 	if (!tmp) {
 	error_out:
 		sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
+		free(reencoded);
 		return;
 	}
 	tmp += 2;
@@ -1482,6 +1412,7 @@
 		goto error_out;
 	memcpy(summary_buf, tmp, len);
 	summary_buf[len] = 0;
+	free(reencoded);
 }
 
 /*
@@ -1495,6 +1426,39 @@
 }
 
 /*
+ * Porcelain/Incremental format wants to show a lot of details per
+ * commit.  Instead of repeating this every line, emit it only once,
+ * the first time each commit appears in the output.
+ */
+static int emit_one_suspect_detail(struct origin *suspect)
+{
+	struct commit_info ci;
+
+	if (suspect->commit->object.flags & METAINFO_SHOWN)
+		return 0;
+
+	suspect->commit->object.flags |= METAINFO_SHOWN;
+	get_commit_info(suspect->commit, &ci, 1);
+	printf("author %s\n", ci.author);
+	printf("author-mail %s\n", ci.author_mail);
+	printf("author-time %lu\n", ci.author_time);
+	printf("author-tz %s\n", ci.author_tz);
+	printf("committer %s\n", ci.committer);
+	printf("committer-mail %s\n", ci.committer_mail);
+	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-tz %s\n", ci.committer_tz);
+	printf("summary %s\n", ci.summary);
+	if (suspect->commit->object.flags & UNINTERESTING)
+		printf("boundary\n");
+	if (suspect->previous) {
+		struct origin *prev = suspect->previous;
+		printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
+		write_name_quoted(prev->path, stdout, '\n');
+	}
+	return 1;
+}
+
+/*
  * The blame_entry is found to be guilty for the range.  Mark it
  * as such, and show it in incremental output.
  */
@@ -1509,22 +1473,7 @@
 		printf("%s %d %d %d\n",
 		       sha1_to_hex(suspect->commit->object.sha1),
 		       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
-		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
-			struct commit_info ci;
-			suspect->commit->object.flags |= METAINFO_SHOWN;
-			get_commit_info(suspect->commit, &ci, 1);
-			printf("author %s\n", ci.author);
-			printf("author-mail %s\n", ci.author_mail);
-			printf("author-time %lu\n", ci.author_time);
-			printf("author-tz %s\n", ci.author_tz);
-			printf("committer %s\n", ci.committer);
-			printf("committer-mail %s\n", ci.committer_mail);
-			printf("committer-time %lu\n", ci.committer_time);
-			printf("committer-tz %s\n", ci.committer_tz);
-			printf("summary %s\n", ci.summary);
-			if (suspect->commit->object.flags & UNINTERESTING)
-				printf("boundary\n");
-		}
+		emit_one_suspect_detail(suspect);
 		write_filename_info(suspect->path);
 		maybe_flush_or_die(stdout, "stdout");
 	}
@@ -1587,24 +1536,20 @@
 			       int show_raw_time)
 {
 	static char time_buf[128];
-	time_t t = time;
-	int minutes, tz;
-	struct tm *tm;
+	const char *time_str;
+	int time_len;
+	int tz;
 
 	if (show_raw_time) {
 		sprintf(time_buf, "%lu %s", time, tz_str);
-		return time_buf;
 	}
-
-	tz = atoi(tz_str);
-	minutes = tz < 0 ? -tz : tz;
-	minutes = (minutes / 100)*60 + (minutes % 100);
-	minutes = tz < 0 ? -minutes : minutes;
-	t = time + minutes * 60;
-	tm = gmtime(&t);
-
-	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
-	strcat(time_buf, tz_str);
+	else {
+		tz = atoi(tz_str);
+		time_str = show_date(time, tz, blame_date_mode);
+		time_len = strlen(time_str);
+		memcpy(time_buf, time_str, time_len);
+		memset(time_buf + time_len, ' ', blame_date_width - time_len);
+	}
 	return time_buf;
 }
 
@@ -1631,24 +1576,8 @@
 	       ent->s_lno + 1,
 	       ent->lno + 1,
 	       ent->num_lines);
-	if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
-		struct commit_info ci;
-		suspect->commit->object.flags |= METAINFO_SHOWN;
-		get_commit_info(suspect->commit, &ci, 1);
-		printf("author %s\n", ci.author);
-		printf("author-mail %s\n", ci.author_mail);
-		printf("author-time %lu\n", ci.author_time);
-		printf("author-tz %s\n", ci.author_tz);
-		printf("committer %s\n", ci.committer);
-		printf("committer-mail %s\n", ci.committer_mail);
-		printf("committer-time %lu\n", ci.committer_time);
-		printf("committer-tz %s\n", ci.committer_tz);
-		write_filename_info(suspect->path);
-		printf("summary %s\n", ci.summary);
-		if (suspect->commit->object.flags & UNINTERESTING)
-			printf("boundary\n");
-	}
-	else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH)
+	if (emit_one_suspect_detail(suspect) ||
+	    (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
 		write_filename_info(suspect->path);
 
 	cp = nth_line(sb, ent->lno);
@@ -1711,13 +1640,14 @@
 				printf(" %*d", max_orig_digits,
 				       ent->s_lno + 1 + cnt);
 
-			if (!(opt & OUTPUT_NO_AUTHOR))
-				printf(" (%-*.*s %10s",
-				       longest_author, longest_author,
-				       ci.author,
+			if (!(opt & OUTPUT_NO_AUTHOR)) {
+				int pad = longest_author - utf8_strwidth(ci.author);
+				printf(" (%s%*s %10s",
+				       ci.author, pad, "",
 				       format_time(ci.author_time,
 						   ci.author_tz,
 						   show_raw_time));
+			}
 			printf(" %*d) ",
 			       max_digits, ent->lno + 1 + cnt);
 		}
@@ -1848,7 +1778,7 @@
 		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
 			suspect->commit->object.flags |= METAINFO_SHOWN;
 			get_commit_info(suspect->commit, &ci, 1);
-			num = strlen(ci.author);
+			num = utf8_strwidth(ci.author);
 			if (longest_author < num)
 				longest_author = num;
 		}
@@ -1885,36 +1815,6 @@
 			baa = 1;
 		}
 	}
-	for (ent = sb->ent; ent; ent = ent->next) {
-		/* Mark the ones that haven't been checked */
-		if (0 < ent->suspect->refcnt)
-			ent->suspect->refcnt = -ent->suspect->refcnt;
-	}
-	for (ent = sb->ent; ent; ent = ent->next) {
-		/*
-		 * ... then pick each and see if they have the the
-		 * correct refcnt.
-		 */
-		int found;
-		struct blame_entry *e;
-		struct origin *suspect = ent->suspect;
-
-		if (0 < suspect->refcnt)
-			continue;
-		suspect->refcnt = -suspect->refcnt; /* Unmark */
-		for (found = 0, e = sb->ent; e; e = e->next) {
-			if (e->suspect != suspect)
-				continue;
-			found++;
-		}
-		if (suspect->refcnt != found) {
-			fprintf(stderr, "%s in %s has refcnt %d, not %d\n",
-				ent->suspect->path,
-				sha1_to_hex(ent->suspect->commit->object.sha1),
-				ent->suspect->refcnt, found);
-			baa = 2;
-		}
-	}
 	if (baa) {
 		int opt = 0160;
 		find_alignment(sb, &opt);
@@ -2054,6 +1954,12 @@
 		blank_boundary = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "blame.date")) {
+		if (!value)
+			return config_error_nonbool(var);
+		blame_date_mode = parse_date_format(value);
+		return 0;
+	}
 	return git_default_config(var, value, cb);
 }
 
@@ -2066,7 +1972,7 @@
 	struct commit *commit;
 	struct origin *origin;
 	unsigned char head_sha1[20];
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	const char *ident;
 	time_t now;
 	int size, len;
@@ -2086,11 +1992,9 @@
 
 	origin = make_origin(commit, path);
 
-	strbuf_init(&buf, 0);
 	if (!contents_from || strcmp("-", contents_from)) {
 		struct stat st;
 		const char *read_from;
-		unsigned long fin_size;
 
 		if (contents_from) {
 			if (stat(contents_from, &st) < 0)
@@ -2102,7 +2006,6 @@
 				die("Cannot lstat %s", path);
 			read_from = path;
 		}
-		fin_size = xsize_t(st.st_size);
 		mode = canon_mode(st.st_mode);
 		switch (st.st_mode & S_IFMT) {
 		case S_IFREG:
@@ -2110,9 +2013,8 @@
 				die("cannot open or read %s", read_from);
 			break;
 		case S_IFLNK:
-			if (readlink(read_from, buf.buf, buf.alloc) != fin_size)
+			if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
 				die("cannot readlink %s", read_from);
-			buf.len = fin_size;
 			break;
 		default:
 			die("unsupported file type %s", read_from);
@@ -2322,6 +2224,8 @@
 
 	git_config(git_blame_config, NULL);
 	init_revisions(&revs, NULL);
+	revs.date_mode = blame_date_mode;
+
 	save_commit_buffer = 0;
 	dashdash_pos = 0;
 
@@ -2346,8 +2250,39 @@
 parse_done:
 	argc = parse_options_end(&ctx);
 
-	if (cmd_is_annotate)
+	if (revs_file && read_ancestry(revs_file))
+		die("reading graft file %s failed: %s",
+		    revs_file, strerror(errno));
+
+	if (cmd_is_annotate) {
 		output_option |= OUTPUT_ANNOTATE_COMPAT;
+		blame_date_mode = DATE_ISO8601;
+	} else {
+		blame_date_mode = revs.date_mode;
+	}
+
+	/* The maximum width used to show the dates */
+	switch (blame_date_mode) {
+	case DATE_RFC2822:
+		blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
+		break;
+	case DATE_ISO8601:
+		blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
+		break;
+	case DATE_RAW:
+		blame_date_width = sizeof("1161298804 -0700");
+		break;
+	case DATE_SHORT:
+		blame_date_width = sizeof("2006-10-19");
+		break;
+	case DATE_RELATIVE:
+		/* "normal" is used as the fallback for "relative" */
+	case DATE_LOCAL:
+	case DATE_NORMAL:
+		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
+		break;
+	}
+	blame_date_width -= 1; /* strip the null */
 
 	if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
 		opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
@@ -2487,11 +2422,7 @@
 	sb.ent = ent;
 	sb.path = path;
 
-	if (revs_file && read_ancestry(revs_file))
-		die("reading graft file %s failed: %s",
-		    revs_file, strerror(errno));
-
-	read_mailmap(&mailmap, ".mailmap", NULL);
+	read_mailmap(&mailmap, NULL);
 
 	if (!incremental)
 		setup_pager();
diff --git a/builtin-branch.c b/builtin-branch.c
index 4b4abfd..91098ca 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -32,18 +32,18 @@
 
 static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
-	"\033[m",	/* reset */
-	"",		/* PLAIN (normal) */
-	"\033[31m",	/* REMOTE (red) */
-	"",		/* LOCAL (normal) */
-	"\033[32m",	/* CURRENT (green) */
+	GIT_COLOR_RESET,
+	GIT_COLOR_NORMAL,	/* PLAIN */
+	GIT_COLOR_RED,		/* REMOTE */
+	GIT_COLOR_NORMAL,	/* LOCAL */
+	GIT_COLOR_GREEN,	/* CURRENT */
 };
 enum color_branch {
-	COLOR_BRANCH_RESET = 0,
-	COLOR_BRANCH_PLAIN = 1,
-	COLOR_BRANCH_REMOTE = 2,
-	COLOR_BRANCH_LOCAL = 3,
-	COLOR_BRANCH_CURRENT = 4,
+	BRANCH_COLOR_RESET = 0,
+	BRANCH_COLOR_PLAIN = 1,
+	BRANCH_COLOR_REMOTE = 2,
+	BRANCH_COLOR_LOCAL = 3,
+	BRANCH_COLOR_CURRENT = 4,
 };
 
 static enum merge_filter {
@@ -56,15 +56,15 @@
 static int parse_branch_color_slot(const char *var, int ofs)
 {
 	if (!strcasecmp(var+ofs, "plain"))
-		return COLOR_BRANCH_PLAIN;
+		return BRANCH_COLOR_PLAIN;
 	if (!strcasecmp(var+ofs, "reset"))
-		return COLOR_BRANCH_RESET;
+		return BRANCH_COLOR_RESET;
 	if (!strcasecmp(var+ofs, "remote"))
-		return COLOR_BRANCH_REMOTE;
+		return BRANCH_COLOR_REMOTE;
 	if (!strcasecmp(var+ofs, "local"))
-		return COLOR_BRANCH_LOCAL;
+		return BRANCH_COLOR_LOCAL;
 	if (!strcasecmp(var+ofs, "current"))
-		return COLOR_BRANCH_CURRENT;
+		return BRANCH_COLOR_CURRENT;
 	die("bad config variable '%s'", var);
 }
 
@@ -97,9 +97,9 @@
 	unsigned char sha1[20];
 	char *name = NULL;
 	const char *fmt, *remote;
-	char section[PATH_MAX];
 	int i;
 	int ret = 0;
+	struct strbuf bname = STRBUF_INIT;
 
 	switch (kinds) {
 	case REF_REMOTE_BRANCH:
@@ -120,20 +120,21 @@
 		if (!head_rev)
 			die("Couldn't look up commit object for HEAD");
 	}
-	for (i = 0; i < argc; i++) {
-		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) {
+	for (i = 0; i < argc; i++, strbuf_release(&bname)) {
+		strbuf_branchname(&bname, argv[i]);
+		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
 			error("Cannot delete the branch '%s' "
-				"which you are currently on.", argv[i]);
+			      "which you are currently on.", bname.buf);
 			ret = 1;
 			continue;
 		}
 
 		free(name);
 
-		name = xstrdup(mkpath(fmt, argv[i]));
+		name = xstrdup(mkpath(fmt, bname.buf));
 		if (!resolve_ref(name, sha1, 1, NULL)) {
 			error("%sbranch '%s' not found.",
-					remote, argv[i]);
+					remote, bname.buf);
 			ret = 1;
 			continue;
 		}
@@ -153,23 +154,26 @@
 		if (!force &&
 		    !in_merge_bases(rev, &head_rev, 1)) {
 			error("The branch '%s' is not an ancestor of "
-				"your current HEAD.\n"
-				"If you are sure you want to delete it, "
-				"run 'git branch -D %s'.", argv[i], argv[i]);
+			      "your current HEAD.\n"
+			      "If you are sure you want to delete it, "
+			      "run 'git branch -D %s'.", bname.buf, bname.buf);
 			ret = 1;
 			continue;
 		}
 
 		if (delete_ref(name, sha1, 0)) {
 			error("Error deleting %sbranch '%s'", remote,
-			       argv[i]);
+			      bname.buf);
 			ret = 1;
 		} else {
-			printf("Deleted %sbranch %s.\n", remote, argv[i]);
-			snprintf(section, sizeof(section), "branch.%s",
-				 argv[i]);
-			if (git_config_rename_section(section, NULL) < 0)
+			struct strbuf buf = STRBUF_INIT;
+			printf("Deleted %sbranch %s (was %s).\n", remote,
+			       bname.buf,
+			       find_unique_abbrev(sha1, DEFAULT_ABBREV));
+			strbuf_addf(&buf, "branch.%s", bname.buf);
+			if (git_config_rename_section(buf.buf, NULL) < 0)
 				warning("Update of config-file failed");
+			strbuf_release(&buf);
 		}
 	}
 
@@ -180,7 +184,8 @@
 
 struct ref_item {
 	char *name;
-	unsigned int kind;
+	char *dest;
+	unsigned int kind, len;
 	struct commit *commit;
 };
 
@@ -192,19 +197,18 @@
 	int kinds;
 };
 
-static int has_commit(struct commit *commit, struct commit_list *with_commit)
+static char *resolve_symref(const char *src, const char *prefix)
 {
-	if (!with_commit)
-		return 1;
-	while (with_commit) {
-		struct commit *other;
+	unsigned char sha1[20];
+	int flag;
+	const char *dst, *cp;
 
-		other = with_commit->item;
-		with_commit = with_commit->next;
-		if (in_merge_bases(other, &commit, 1))
-			return 1;
-	}
-	return 0;
+	dst = resolve_ref(src, sha1, 0, &flag);
+	if (!(dst && (flag & REF_ISSYMREF)))
+		return NULL;
+	if (prefix && (cp = skip_prefix(dst, prefix)))
+		dst = cp;
+	return xstrdup(dst);
 }
 
 static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
@@ -212,17 +216,28 @@
 	struct ref_list *ref_list = (struct ref_list*)(cb_data);
 	struct ref_item *newitem;
 	struct commit *commit;
-	int kind;
-	int len;
+	int kind, i;
+	const char *prefix, *orig_refname = refname;
+
+	static struct {
+		int kind;
+		const char *prefix;
+		int pfxlen;
+	} ref_kind[] = {
+		{ REF_LOCAL_BRANCH, "refs/heads/", 11 },
+		{ REF_REMOTE_BRANCH, "refs/remotes/", 13 },
+	};
 
 	/* Detect kind */
-	if (!prefixcmp(refname, "refs/heads/")) {
-		kind = REF_LOCAL_BRANCH;
-		refname += 11;
-	} else if (!prefixcmp(refname, "refs/remotes/")) {
-		kind = REF_REMOTE_BRANCH;
-		refname += 13;
-	} else
+	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
+		prefix = ref_kind[i].prefix;
+		if (strncmp(refname, prefix, ref_kind[i].pfxlen))
+			continue;
+		kind = ref_kind[i].kind;
+		refname += ref_kind[i].pfxlen;
+		break;
+	}
+	if (ARRAY_SIZE(ref_kind) <= i)
 		return 0;
 
 	commit = lookup_commit_reference_gently(sha1, 1);
@@ -230,7 +245,7 @@
 		return error("branch '%s' does not point at a commit", refname);
 
 	/* Filter with with_commit if specified */
-	if (!has_commit(commit, ref_list->with_commit))
+	if (!is_descendant_of(commit, ref_list->with_commit))
 		return 0;
 
 	/* Don't add types the caller doesn't want */
@@ -253,9 +268,14 @@
 	newitem->name = xstrdup(refname);
 	newitem->kind = kind;
 	newitem->commit = commit;
-	len = strlen(newitem->name);
-	if (len > ref_list->maxwidth)
-		ref_list->maxwidth = len;
+	newitem->len = strlen(refname);
+	newitem->dest = resolve_symref(orig_refname, prefix);
+	/* adjust for "remotes/" */
+	if (newitem->kind == REF_REMOTE_BRANCH &&
+	    ref_list->kinds != REF_REMOTE_BRANCH)
+		newitem->len += 8;
+	if (newitem->len > ref_list->maxwidth)
+		ref_list->maxwidth = newitem->len;
 
 	return 0;
 }
@@ -264,8 +284,10 @@
 {
 	int i;
 
-	for (i = 0; i < ref_list->index; i++)
+	for (i = 0; i < ref_list->index; i++) {
 		free(ref_list->list[i].name);
+		free(ref_list->list[i].dest);
+	}
 	free(ref_list->list);
 }
 
@@ -279,19 +301,30 @@
 	return strcmp(c1->name, c2->name);
 }
 
-static void fill_tracking_info(char *stat, const char *branch_name)
+static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
+		int show_upstream_ref)
 {
 	int ours, theirs;
 	struct branch *branch = branch_get(branch_name);
 
-	if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
+	if (!stat_tracking_info(branch, &ours, &theirs)) {
+		if (branch && branch->merge && branch->merge[0]->dst &&
+		    show_upstream_ref)
+			strbuf_addf(stat, "[%s] ",
+			    shorten_unambiguous_ref(branch->merge[0]->dst, 0));
 		return;
+	}
+
+	strbuf_addch(stat, '[');
+	if (show_upstream_ref)
+		strbuf_addf(stat, "%s: ",
+			shorten_unambiguous_ref(branch->merge[0]->dst, 0));
 	if (!ours)
-		sprintf(stat, "[behind %d] ", theirs);
+		strbuf_addf(stat, "behind %d] ", theirs);
 	else if (!theirs)
-		sprintf(stat, "[ahead %d] ", ours);
+		strbuf_addf(stat, "ahead %d] ", ours);
 	else
-		sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
+		strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
 }
 
 static int matches_merge_filter(struct commit *commit)
@@ -306,40 +339,48 @@
 }
 
 static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
-			   int abbrev, int current)
+			   int abbrev, int current, char *prefix)
 {
 	char c;
 	int color;
 	struct commit *commit = item->commit;
+	struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
 
 	if (!matches_merge_filter(commit))
 		return;
 
 	switch (item->kind) {
 	case REF_LOCAL_BRANCH:
-		color = COLOR_BRANCH_LOCAL;
+		color = BRANCH_COLOR_LOCAL;
 		break;
 	case REF_REMOTE_BRANCH:
-		color = COLOR_BRANCH_REMOTE;
+		color = BRANCH_COLOR_REMOTE;
 		break;
 	default:
-		color = COLOR_BRANCH_PLAIN;
+		color = BRANCH_COLOR_PLAIN;
 		break;
 	}
 
 	c = ' ';
 	if (current) {
 		c = '*';
-		color = COLOR_BRANCH_CURRENT;
+		color = BRANCH_COLOR_CURRENT;
 	}
 
-	if (verbose) {
-		struct strbuf subject;
-		const char *sub = " **** invalid ref ****";
-		char stat[128];
+	strbuf_addf(&name, "%s%s", prefix, item->name);
+	if (verbose)
+		strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
+			    maxwidth, name.buf,
+			    branch_get_color(BRANCH_COLOR_RESET));
+	else
+		strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
+			    name.buf, branch_get_color(BRANCH_COLOR_RESET));
 
-		strbuf_init(&subject, 0);
-		stat[0] = '\0';
+	if (item->dest)
+		strbuf_addf(&out, " -> %s", item->dest);
+	else if (verbose) {
+		struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
+		const char *sub = " **** invalid ref ****";
 
 		commit = item->commit;
 		if (commit && !parse_commit(commit)) {
@@ -349,29 +390,27 @@
 		}
 
 		if (item->kind == REF_LOCAL_BRANCH)
-			fill_tracking_info(stat, item->name);
+			fill_tracking_info(&stat, item->name, verbose > 1);
 
-		printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
-		       maxwidth, item->name,
-		       branch_get_color(COLOR_BRANCH_RESET),
-		       find_unique_abbrev(item->commit->object.sha1, abbrev),
-		       stat, sub);
+		strbuf_addf(&out, " %s %s%s",
+			find_unique_abbrev(item->commit->object.sha1, abbrev),
+			stat.buf, sub);
+		strbuf_release(&stat);
 		strbuf_release(&subject);
-	} else {
-		printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
-		       branch_get_color(COLOR_BRANCH_RESET));
 	}
+	printf("%s\n", out.buf);
+	strbuf_release(&name);
+	strbuf_release(&out);
 }
 
 static int calc_maxwidth(struct ref_list *refs)
 {
-	int i, l, w = 0;
+	int i, w = 0;
 	for (i = 0; i < refs->index; i++) {
 		if (!matches_merge_filter(refs->list[i].commit))
 			continue;
-		l = strlen(refs->list[i].name);
-		if (l > w)
-			w = l;
+		if (refs->list[i].len > w)
+			w = refs->list[i].len;
 	}
 	return w;
 }
@@ -403,14 +442,17 @@
 	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
 	detached = (detached && (kinds & REF_LOCAL_BRANCH));
-	if (detached && head_commit && has_commit(head_commit, with_commit)) {
+	if (detached && head_commit &&
+	    is_descendant_of(head_commit, with_commit)) {
 		struct ref_item item;
 		item.name = xstrdup("(no branch)");
+		item.len = strlen(item.name);
 		item.kind = REF_LOCAL_BRANCH;
+		item.dest = NULL;
 		item.commit = head_commit;
-		if (strlen(item.name) > ref_list.maxwidth)
-			ref_list.maxwidth = strlen(item.name);
-		print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
+		if (item.len > ref_list.maxwidth)
+			ref_list.maxwidth = item.len;
+		print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, "");
 		free(item.name);
 	}
 
@@ -418,8 +460,11 @@
 		int current = !detached &&
 			(ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
 			!strcmp(ref_list.list[i].name, head);
+		char *prefix = (kinds != REF_REMOTE_BRANCH &&
+				ref_list.list[i].kind == REF_REMOTE_BRANCH)
+				? "remotes/" : "";
 		print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
-			       abbrev, current);
+			       abbrev, current, prefix);
 	}
 
 	free_ref_list(&ref_list);
@@ -427,58 +472,53 @@
 
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
-	char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
+	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
 	unsigned char sha1[20];
-	char oldsection[PATH_MAX], newsection[PATH_MAX];
+	struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
+	int recovery = 0;
 
 	if (!oldname)
 		die("cannot rename the current branch while not on any.");
 
-	if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
-		die("Old branchname too long");
+	if (strbuf_check_branch_ref(&oldref, oldname)) {
+		/*
+		 * Bad name --- this could be an attempt to rename a
+		 * ref that we used to allow to be created by accident.
+		 */
+		if (resolve_ref(oldref.buf, sha1, 1, NULL))
+			recovery = 1;
+		else
+			die("Invalid branch name: '%s'", oldname);
+	}
 
-	if (check_ref_format(oldref))
-		die("Invalid branch name: %s", oldref);
+	if (strbuf_check_branch_ref(&newref, newname))
+		die("Invalid branch name: '%s'", newname);
 
-	if (snprintf(newref, sizeof(newref), "refs/heads/%s", newname) > sizeof(newref))
-		die("New branchname too long");
+	if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
+		die("A branch named '%s' already exists.", newref.buf + 11);
 
-	if (check_ref_format(newref))
-		die("Invalid branch name: %s", newref);
+	strbuf_addf(&logmsg, "Branch: renamed %s to %s",
+		 oldref.buf, newref.buf);
 
-	if (resolve_ref(newref, sha1, 1, NULL) && !force)
-		die("A branch named '%s' already exists.", newname);
-
-	snprintf(logmsg, sizeof(logmsg), "Branch: renamed %s to %s",
-		 oldref, newref);
-
-	if (rename_ref(oldref, newref, logmsg))
+	if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
 		die("Branch rename failed");
+	strbuf_release(&logmsg);
+
+	if (recovery)
+		warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
 
 	/* no need to pass logmsg here as HEAD didn't really move */
-	if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL))
+	if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
 		die("Branch renamed to %s, but HEAD is not updated!", newname);
 
-	snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11);
-	snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11);
-	if (git_config_rename_section(oldsection, newsection) < 0)
+	strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
+	strbuf_release(&oldref);
+	strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
+	strbuf_release(&newref);
+	if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
 		die("Branch is renamed, but update of config-file failed");
-}
-
-static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
-{
-	unsigned char sha1[20];
-	struct commit *commit;
-
-	if (!arg)
-		return -1;
-	if (get_sha1(arg, sha1))
-		die("malformed object name %s", arg);
-	commit = lookup_commit_reference(sha1);
-	if (!commit)
-		die("no such commit %s", arg);
-	commit_list_insert(commit, opt->value);
-	return 0;
+	strbuf_release(&oldsection);
+	strbuf_release(&newsection);
 }
 
 static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
@@ -516,13 +556,13 @@
 			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
 			"print only branches that contain the commit",
 			PARSE_OPT_LASTARG_DEFAULT,
-			opt_parse_with_commit, (intptr_t)"HEAD",
+			parse_opt_with_commit, (intptr_t)"HEAD",
 		},
 		{
 			OPTION_CALLBACK, 0, "with", &with_commit, "commit",
 			"print only branches that contain the commit",
 			PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
-			opt_parse_with_commit, (intptr_t) "HEAD",
+			parse_opt_with_commit, (intptr_t) "HEAD",
 		},
 		OPT__ABBREV(&abbrev),
 
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index 3fba6b9..8fad19d 100644
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
@@ -137,7 +137,7 @@
 		break;
 
 	default:
-		die("git cat-file: unknown option: %s\n", exp_type);
+		die("git cat-file: unknown option: %s", exp_type);
 	}
 
 	if (!buf)
@@ -189,9 +189,8 @@
 
 static int batch_objects(int print_contents)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 
-	strbuf_init(&buf, 0);
 	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
 		int error = batch_one_object(buf.buf, print_contents);
 		if (error)
diff --git a/builtin-check-attr.c b/builtin-check-attr.c
index cb783fc..15a04b7 100644
--- a/builtin-check-attr.c
+++ b/builtin-check-attr.c
@@ -2,21 +2,84 @@
 #include "cache.h"
 #include "attr.h"
 #include "quote.h"
+#include "parse-options.h"
 
-static const char check_attr_usage[] =
-"git check-attr attr... [--] pathname...";
+static int stdin_paths;
+static const char * const check_attr_usage[] = {
+"git check-attr attr... [--] pathname...",
+"git check-attr --stdin attr... < <list-of-paths>",
+NULL
+};
+
+static int null_term_line;
+
+static const struct option check_attr_options[] = {
+	OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
+	OPT_BOOLEAN('z', NULL, &null_term_line,
+		"input paths are terminated by a null character"),
+	OPT_END()
+};
+
+static void check_attr(int cnt, struct git_attr_check *check,
+	const char** name, const char *file)
+{
+	int j;
+	if (git_checkattr(file, cnt, check))
+		die("git_checkattr died");
+	for (j = 0; j < cnt; j++) {
+		const char *value = check[j].value;
+
+		if (ATTR_TRUE(value))
+			value = "set";
+		else if (ATTR_FALSE(value))
+			value = "unset";
+		else if (ATTR_UNSET(value))
+			value = "unspecified";
+
+		quote_c_style(file, NULL, stdout, 0);
+		printf(": %s: %s\n", name[j], value);
+	}
+}
+
+static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
+	const char** name)
+{
+	struct strbuf buf, nbuf;
+	int line_termination = null_term_line ? 0 : '\n';
+
+	strbuf_init(&buf, 0);
+	strbuf_init(&nbuf, 0);
+	while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+		if (line_termination && buf.buf[0] == '"') {
+			strbuf_reset(&nbuf);
+			if (unquote_c_style(&nbuf, buf.buf, NULL))
+				die("line is badly quoted");
+			strbuf_swap(&buf, &nbuf);
+		}
+		check_attr(cnt, check, name, buf.buf);
+		maybe_flush_or_die(stdout, "attribute to stdout");
+	}
+	strbuf_release(&buf);
+	strbuf_release(&nbuf);
+}
 
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
 	struct git_attr_check *check;
 	int cnt, i, doubledash;
+	const char *errstr = NULL;
+
+	argc = parse_options(argc, argv, check_attr_options, check_attr_usage,
+		PARSE_OPT_KEEP_DASHDASH);
+	if (!argc)
+		usage_with_options(check_attr_usage, check_attr_options);
 
 	if (read_cache() < 0) {
 		die("invalid cache");
 	}
 
 	doubledash = -1;
-	for (i = 1; doubledash < 0 && i < argc; i++) {
+	for (i = 0; doubledash < 0 && i < argc; i++) {
 		if (!strcmp(argv[i], "--"))
 			doubledash = i;
 	}
@@ -24,41 +87,37 @@
 	/* If there is no double dash, we handle only one attribute */
 	if (doubledash < 0) {
 		cnt = 1;
-		doubledash = 1;
+		doubledash = 0;
 	} else
-		cnt = doubledash - 1;
+		cnt = doubledash;
 	doubledash++;
 
-	if (cnt <= 0 || argc < doubledash)
-		usage(check_attr_usage);
+	if (cnt <= 0)
+		errstr = "No attribute specified";
+	else if (stdin_paths && doubledash < argc)
+		errstr = "Can't specify files with --stdin";
+	if (errstr) {
+		error("%s", errstr);
+		usage_with_options(check_attr_usage, check_attr_options);
+	}
+
 	check = xcalloc(cnt, sizeof(*check));
 	for (i = 0; i < cnt; i++) {
 		const char *name;
 		struct git_attr *a;
-		name = argv[i + 1];
+		name = argv[i];
 		a = git_attr(name, strlen(name));
 		if (!a)
 			return error("%s: not a valid attribute name", name);
 		check[i].attr = a;
 	}
 
-	for (i = doubledash; i < argc; i++) {
-		int j;
-		if (git_checkattr(argv[i], cnt, check))
-			die("git_checkattr died");
-		for (j = 0; j < cnt; j++) {
-			const char *value = check[j].value;
-
-			if (ATTR_TRUE(value))
-				value = "set";
-			else if (ATTR_FALSE(value))
-				value = "unset";
-			else if (ATTR_UNSET(value))
-				value = "unspecified";
-
-			quote_c_style(argv[i], NULL, stdout, 0);
-			printf(": %s: %s\n", argv[j+1], value);
-		}
+	if (stdin_paths)
+		check_attr_stdin_paths(cnt, check, argv);
+	else {
+		for (i = doubledash; i < argc; i++)
+			check_attr(cnt, check, argv, argv[i]);
+		maybe_flush_or_die(stdout, "attribute to stdout");
 	}
 	return 0;
 }
diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c
index 701de43..f9381e0 100644
--- a/builtin-check-ref-format.c
+++ b/builtin-check-ref-format.c
@@ -5,9 +5,18 @@
 #include "cache.h"
 #include "refs.h"
 #include "builtin.h"
+#include "strbuf.h"
 
 int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 {
+	if (argc == 3 && !strcmp(argv[1], "--branch")) {
+		struct strbuf sb = STRBUF_INIT;
+
+		if (strbuf_check_branch_ref(&sb, argv[2]))
+			die("'%s' is not a valid branch name", argv[2]);
+		printf("%s\n", sb.buf + 11);
+		exit(0);
+	}
 	if (argc != 2)
 		usage("git check-ref-format refname");
 	return !!check_ref_format(argv[1]);
diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c
index 55b7aaf..0d534bc 100644
--- a/builtin-checkout-index.c
+++ b/builtin-checkout-index.c
@@ -40,6 +40,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "cache-tree.h"
+#include "parse-options.h"
 
 #define CHECKOUT_ALL 4
 static int line_termination = '\n';
@@ -153,11 +154,58 @@
 		exit(128);
 }
 
-static const char checkout_cache_usage[] =
-"git checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]|all] [--prefix=<string>] [--temp] [--] <file>...";
+static const char * const builtin_checkout_index_usage[] = {
+	"git checkout-index [options] [--] <file>...",
+	NULL
+};
 
 static struct lock_file lock_file;
 
+static int option_parse_u(const struct option *opt,
+			      const char *arg, int unset)
+{
+	int *newfd = opt->value;
+
+	state.refresh_cache = 1;
+	if (*newfd < 0)
+		*newfd = hold_locked_index(&lock_file, 1);
+	return 0;
+}
+
+static int option_parse_z(const struct option *opt,
+			  const char *arg, int unset)
+{
+	if (unset)
+		line_termination = '\n';
+	else
+		line_termination = 0;
+	return 0;
+}
+
+static int option_parse_prefix(const struct option *opt,
+			       const char *arg, int unset)
+{
+	state.base_dir = arg;
+	state.base_dir_len = strlen(arg);
+	return 0;
+}
+
+static int option_parse_stage(const struct option *opt,
+			      const char *arg, int unset)
+{
+	if (!strcmp(arg, "all")) {
+		to_tempfile = 1;
+		checkout_stage = CHECKOUT_ALL;
+	} else {
+		int ch = arg[0];
+		if ('1' <= ch && ch <= '3')
+			checkout_stage = arg[0] - '0';
+		else
+			die("stage should be between 1 and 3 or all");
+	}
+	return 0;
+}
+
 int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 {
 	int i;
@@ -165,6 +213,33 @@
 	int all = 0;
 	int read_from_stdin = 0;
 	int prefix_length;
+	int force = 0, quiet = 0, not_new = 0;
+	struct option builtin_checkout_index_options[] = {
+		OPT_BOOLEAN('a', "all", &all,
+			"checks out all files in the index"),
+		OPT_BOOLEAN('f', "force", &force,
+			"forces overwrite of existing files"),
+		OPT__QUIET(&quiet),
+		OPT_BOOLEAN('n', "no-create", &not_new,
+			"don't checkout new files"),
+		{ OPTION_CALLBACK, 'u', "index", &newfd, NULL,
+			"update stat information in the index file",
+			PARSE_OPT_NOARG, option_parse_u },
+		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+			"paths are separated with NUL character",
+			PARSE_OPT_NOARG, option_parse_z },
+		OPT_BOOLEAN(0, "stdin", &read_from_stdin,
+			"read list of paths from the standard input"),
+		OPT_BOOLEAN(0, "temp", &to_tempfile,
+			"write the content to temporary files"),
+		OPT_CALLBACK(0, "prefix", NULL, "string",
+			"when creating files, prepend <string>",
+			option_parse_prefix),
+		OPT_CALLBACK(0, "stage", NULL, NULL,
+			"copy out the files from named stage",
+			option_parse_stage),
+		OPT_END()
+	};
 
 	git_config(git_default_config, NULL);
 	state.base_dir = "";
@@ -174,72 +249,11 @@
 		die("invalid cache");
 	}
 
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (!strcmp(arg, "--")) {
-			i++;
-			break;
-		}
-		if (!strcmp(arg, "-a") || !strcmp(arg, "--all")) {
-			all = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-f") || !strcmp(arg, "--force")) {
-			state.force = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
-			state.quiet = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-n") || !strcmp(arg, "--no-create")) {
-			state.not_new = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-u") || !strcmp(arg, "--index")) {
-			state.refresh_cache = 1;
-			if (newfd < 0)
-				newfd = hold_locked_index(&lock_file, 1);
-			continue;
-		}
-		if (!strcmp(arg, "-z")) {
-			line_termination = 0;
-			continue;
-		}
-		if (!strcmp(arg, "--stdin")) {
-			if (i != argc - 1)
-				die("--stdin must be at the end");
-			read_from_stdin = 1;
-			i++; /* do not consider arg as a file name */
-			break;
-		}
-		if (!strcmp(arg, "--temp")) {
-			to_tempfile = 1;
-			continue;
-		}
-		if (!prefixcmp(arg, "--prefix=")) {
-			state.base_dir = arg+9;
-			state.base_dir_len = strlen(state.base_dir);
-			continue;
-		}
-		if (!prefixcmp(arg, "--stage=")) {
-			if (!strcmp(arg + 8, "all")) {
-				to_tempfile = 1;
-				checkout_stage = CHECKOUT_ALL;
-			} else {
-				int ch = arg[8];
-				if ('1' <= ch && ch <= '3')
-					checkout_stage = arg[8] - '0';
-				else
-					die("stage should be between 1 and 3 or all");
-			}
-			continue;
-		}
-		if (arg[0] == '-')
-			usage(checkout_cache_usage);
-		break;
-	}
+	argc = parse_options(argc, argv, builtin_checkout_index_options,
+			builtin_checkout_index_usage, 0);
+	state.force = force;
+	state.quiet = quiet;
+	state.not_new = not_new;
 
 	if (state.base_dir_len || to_tempfile) {
 		/* when --prefix is specified we do not
@@ -253,7 +267,7 @@
 	}
 
 	/* Check out named files first */
-	for ( ; i < argc; i++) {
+	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
 		const char *p;
 
@@ -268,13 +282,11 @@
 	}
 
 	if (read_from_stdin) {
-		struct strbuf buf, nbuf;
+		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
 
 		if (all)
 			die("git checkout-index: don't mix '--all' and '--stdin'");
 
-		strbuf_init(&buf, 0);
-		strbuf_init(&nbuf, 0);
 		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
 			const char *p;
 			if (line_termination && buf.buf[0] == '"') {
diff --git a/builtin-checkout.c b/builtin-checkout.c
index c107fd6..ee1edd4 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -13,6 +13,9 @@
 #include "diff.h"
 #include "revision.h"
 #include "remote.h"
+#include "blob.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
 
 static const char * const checkout_usage[] = {
 	"git checkout [options] <branch>",
@@ -20,26 +23,28 @@
 	NULL,
 };
 
+struct checkout_opts {
+	int quiet;
+	int merge;
+	int force;
+	int writeout_stage;
+	int writeout_error;
+
+	const char *new_branch;
+	int new_branch_log;
+	enum branch_track track;
+};
+
 static int post_checkout_hook(struct commit *old, struct commit *new,
 			      int changed)
 {
-	struct child_process proc;
-	const char *name = git_path("hooks/post-checkout");
-	const char *argv[5];
+	return run_hook(NULL, "post-checkout",
+			sha1_to_hex(old ? old->object.sha1 : null_sha1),
+			sha1_to_hex(new ? new->object.sha1 : null_sha1),
+			changed ? "1" : "0", NULL);
+	/* "new" can be NULL when checking out from the index before
+	   a commit exists. */
 
-	if (access(name, X_OK) < 0)
-		return 0;
-
-	memset(&proc, 0, sizeof(proc));
-	argv[0] = name;
-	argv[1] = xstrdup(sha1_to_hex(old ? old->object.sha1 : null_sha1));
-	argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
-	argv[3] = changed ? "1" : "0";
-	argv[4] = NULL;
-	proc.argv = argv;
-	proc.no_stdin = 1;
-	proc.stdout_to_stderr = 1;
-	return run_command(&proc);
 }
 
 static int update_some(const unsigned char *sha1, const char *base, int baselen,
@@ -48,9 +53,6 @@
 	int len;
 	struct cache_entry *ce;
 
-	if (S_ISGITLINK(mode))
-		return 0;
-
 	if (S_ISDIR(mode))
 		return READ_TREE_RECURSIVE;
 
@@ -84,8 +86,121 @@
 	return pos;
 }
 
+static int check_stage(int stage, struct cache_entry *ce, int pos)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return 0;
+		pos++;
+	}
+	return error("path '%s' does not have %s version",
+		     ce->name,
+		     (stage == 2) ? "our" : "their");
+}
 
-static int checkout_paths(struct tree *source_tree, const char **pathspec)
+static int check_all_stages(struct cache_entry *ce, int pos)
+{
+	if (ce_stage(ce) != 1 ||
+	    active_nr <= pos + 2 ||
+	    strcmp(active_cache[pos+1]->name, ce->name) ||
+	    ce_stage(active_cache[pos+1]) != 2 ||
+	    strcmp(active_cache[pos+2]->name, ce->name) ||
+	    ce_stage(active_cache[pos+2]) != 3)
+		return error("path '%s' does not have all three versions",
+			     ce->name);
+	return 0;
+}
+
+static int checkout_stage(int stage, struct cache_entry *ce, int pos,
+			  struct checkout *state)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return checkout_entry(active_cache[pos], state, NULL);
+		pos++;
+	}
+	return error("path '%s' does not have %s version",
+		     ce->name,
+		     (stage == 2) ? "our" : "their");
+}
+
+/* NEEDSWORK: share with merge-recursive */
+static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
+{
+	unsigned long size;
+	enum object_type type;
+
+	if (!hashcmp(sha1, null_sha1)) {
+		mm->ptr = xstrdup("");
+		mm->size = 0;
+		return;
+	}
+
+	mm->ptr = read_sha1_file(sha1, &type, &size);
+	if (!mm->ptr || type != OBJ_BLOB)
+		die("unable to read blob object %s", sha1_to_hex(sha1));
+	mm->size = size;
+}
+
+static int checkout_merged(int pos, struct checkout *state)
+{
+	struct cache_entry *ce = active_cache[pos];
+	const char *path = ce->name;
+	mmfile_t ancestor, ours, theirs;
+	int status;
+	unsigned char sha1[20];
+	mmbuffer_t result_buf;
+
+	if (ce_stage(ce) != 1 ||
+	    active_nr <= pos + 2 ||
+	    strcmp(active_cache[pos+1]->name, path) ||
+	    ce_stage(active_cache[pos+1]) != 2 ||
+	    strcmp(active_cache[pos+2]->name, path) ||
+	    ce_stage(active_cache[pos+2]) != 3)
+		return error("path '%s' does not have all 3 versions", path);
+
+	fill_mm(active_cache[pos]->sha1, &ancestor);
+	fill_mm(active_cache[pos+1]->sha1, &ours);
+	fill_mm(active_cache[pos+2]->sha1, &theirs);
+
+	status = ll_merge(&result_buf, path, &ancestor,
+			  &ours, "ours", &theirs, "theirs", 1);
+	free(ancestor.ptr);
+	free(ours.ptr);
+	free(theirs.ptr);
+	if (status < 0 || !result_buf.ptr) {
+		free(result_buf.ptr);
+		return error("path '%s': cannot merge", path);
+	}
+
+	/*
+	 * NEEDSWORK:
+	 * There is absolutely no reason to write this as a blob object
+	 * and create a phoney cache entry just to leak.  This hack is
+	 * primarily to get to the write_entry() machinery that massages
+	 * the contents to work-tree format and writes out which only
+	 * allows it for a cache entry.  The code in write_entry() needs
+	 * to be refactored to allow us to feed a <buffer, size, mode>
+	 * instead of a cache entry.  Such a refactoring would help
+	 * merge_recursive as well (it also writes the merge result to the
+	 * object database even when it may contain conflicts).
+	 */
+	if (write_sha1_file(result_buf.ptr, result_buf.size,
+			    blob_type, sha1))
+		die("Unable to add merge result for '%s'", path);
+	ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
+			      sha1,
+			      path, 2, 0);
+	if (!ce)
+		die("make_cache_entry failed for path '%s'", path);
+	status = checkout_entry(ce, state, NULL);
+	return status;
+}
+
+static int checkout_paths(struct tree *source_tree, const char **pathspec,
+			  struct checkout_opts *opts)
 {
 	int pos;
 	struct checkout state;
@@ -94,12 +209,14 @@
 	int flag;
 	struct commit *head;
 	int errs = 0;
-
+	int stage = opts->writeout_stage;
+	int merge = opts->merge;
 	int newfd;
 	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
 	newfd = hold_locked_index(lock_file, 1);
-	read_cache();
+	if (read_cache() < 0)
+		return error("corrupt index file");
 
 	if (source_tree)
 		read_tree_some(source_tree, pathspec);
@@ -110,7 +227,7 @@
 
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
-		pathspec_match(pathspec, ps_matched, ce->name, 0);
+		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
 	}
 
 	if (report_path_error(ps_matched, pathspec, 0))
@@ -119,11 +236,19 @@
 	/* Any unmerged paths? */
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
-		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+		if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
 			if (!ce_stage(ce))
 				continue;
-			errs = 1;
-			error("path '%s' is unmerged", ce->name);
+			if (opts->force) {
+				warning("path '%s' is unmerged", ce->name);
+			} else if (stage) {
+				errs |= check_stage(stage, ce, pos);
+			} else if (opts->merge) {
+				errs |= check_all_stages(ce, pos);
+			} else {
+				errs = 1;
+				error("path '%s' is unmerged", ce->name);
+			}
 			pos = skip_same_name(ce, pos) - 1;
 		}
 	}
@@ -136,11 +261,15 @@
 	state.refresh_cache = 1;
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
-		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+		if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
 			if (!ce_stage(ce)) {
 				errs |= checkout_entry(ce, &state, NULL);
 				continue;
 			}
+			if (stage)
+				errs |= checkout_stage(stage, ce, pos, &state);
+			else if (merge)
+				errs |= checkout_merged(pos, &state);
 			pos = skip_same_name(ce, pos) - 1;
 		}
 	}
@@ -163,14 +292,15 @@
 	init_revisions(&rev, NULL);
 	rev.abbrev = 0;
 	rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
+	if (diff_setup_done(&rev.diffopt) < 0)
+		die("diff_setup_done failed");
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
 }
 
 static void describe_detached_head(char *msg, struct commit *commit)
 {
-	struct strbuf sb;
-	strbuf_init(&sb, 0);
+	struct strbuf sb = STRBUF_INIT;
 	parse_commit(commit);
 	pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0);
 	fprintf(stderr, "%s %s... %s\n", msg,
@@ -178,17 +308,6 @@
 	strbuf_release(&sb);
 }
 
-struct checkout_opts {
-	int quiet;
-	int merge;
-	int force;
-	int writeout_error;
-
-	char *new_branch;
-	int new_branch_log;
-	enum branch_track track;
-};
-
 static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)
 {
 	struct unpack_trees_options opts;
@@ -230,10 +349,12 @@
 
 static void setup_branch_path(struct branch_info *branch)
 {
-	struct strbuf buf;
-	strbuf_init(&buf, 0);
-	strbuf_addstr(&buf, "refs/heads/");
-	strbuf_addstr(&buf, branch->name);
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_branchname(&buf, branch->name);
+	if (strcmp(buf.buf, branch->name))
+		branch->name = xstrdup(buf.buf);
+	strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
@@ -243,7 +364,9 @@
 	int ret;
 	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 	int newfd = hold_locked_index(lock_file, 1);
-	read_cache();
+
+	if (read_cache() < 0)
+		return error("corrupt index file");
 
 	if (opts->force) {
 		ret = reset_tree(new->commit->tree, opts, 1);
@@ -276,7 +399,7 @@
 		topts.verbose_update = !opts->quiet;
 		topts.fn = twoway_merge;
 		topts.dir = xcalloc(1, sizeof(*topts.dir));
-		topts.dir->show_ignored = 1;
+		topts.dir->flags |= DIR_SHOW_IGNORED;
 		topts.dir->exclude_per_dir = ".gitignore";
 		tree = parse_tree_indirect(old->commit->object.sha1);
 		init_tree_desc(&trees[0], tree->buffer, tree->size);
@@ -292,6 +415,7 @@
 			 */
 			struct tree *result;
 			struct tree *work;
+			struct merge_options o;
 			if (!opts->merge)
 				return 1;
 			parse_commit(old->commit);
@@ -310,13 +434,17 @@
 			 */
 
 			add_files_to_cache(NULL, NULL, 0);
-			work = write_tree_from_memory();
+			init_merge_options(&o);
+			o.verbosity = 0;
+			work = write_tree_from_memory(&o);
 
 			ret = reset_tree(new->commit->tree, opts, 1);
 			if (ret)
 				return ret;
-			merge_trees(new->commit->tree, work, old->commit->tree,
-				    new->name, "local", &result);
+			o.branch1 = new->name;
+			o.branch2 = "local";
+			merge_trees(&o, new->commit->tree, work,
+				old->commit->tree, &result);
 			ret = reset_tree(new->commit->tree, opts, 0);
 			if (ret)
 				return ret;
@@ -348,7 +476,7 @@
 				   struct branch_info *old,
 				   struct branch_info *new)
 {
-	struct strbuf msg;
+	struct strbuf msg = STRBUF_INIT;
 	const char *old_desc;
 	if (opts->new_branch) {
 		create_branch(old->name, opts->new_branch, new->name, 0,
@@ -357,7 +485,6 @@
 		setup_branch_path(new);
 	}
 
-	strbuf_init(&msg, 0);
 	old_desc = old->name;
 	if (!old_desc && old->commit)
 		old_desc = sha1_to_hex(old->commit->object.sha1);
@@ -368,10 +495,10 @@
 		create_symref("HEAD", new->path, msg.buf);
 		if (!opts->quiet) {
 			if (old->path && !strcmp(new->path, old->path))
-				fprintf(stderr, "Already on \"%s\"\n",
+				fprintf(stderr, "Already on '%s'\n",
 					new->name);
 			else
-				fprintf(stderr, "Switched to%s branch \"%s\"\n",
+				fprintf(stderr, "Switched to%s branch '%s'\n",
 					opts->new_branch ? " a new" : "",
 					new->name);
 		}
@@ -380,7 +507,7 @@
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path)
-				fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
+				fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
 			describe_detached_head("HEAD is now at", new->commit);
 		}
 	}
@@ -423,8 +550,8 @@
 
 	if (!old.commit && !opts->force) {
 		if (!opts->quiet) {
-			fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n");
-			fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name);
+			warning("You appear to be on a branch yet to be born.");
+			warning("Forcing checkout of %s.", new->name);
 		}
 		opts->force = 1;
 	}
@@ -439,6 +566,11 @@
 	return ret || opts->writeout_error;
 }
 
+static int git_checkout_config(const char *var, const char *value, void *cb)
+{
+	return git_xmerge_config(var, value, cb);
+}
+
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
@@ -446,14 +578,21 @@
 	const char *arg;
 	struct branch_info new;
 	struct tree *source_tree = NULL;
+	char *conflict_style = NULL;
 	struct option options[] = {
 		OPT__QUIET(&opts.quiet),
 		OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
 		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
 		OPT_SET_INT('t', "track",  &opts.track, "track",
 			BRANCH_TRACK_EXPLICIT),
+		OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
+			    2),
+		OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
+			    3),
 		OPT_BOOLEAN('f', NULL, &opts.force, "force"),
-		OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
+		OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
+		OPT_STRING(0, "conflict", &conflict_style, "style",
+			   "conflict style (merge or diff3)"),
 		OPT_END(),
 	};
 	int has_dash_dash;
@@ -461,15 +600,34 @@
 	memset(&opts, 0, sizeof(opts));
 	memset(&new, 0, sizeof(new));
 
-	git_config(git_default_config, NULL);
+	git_config(git_checkout_config, NULL);
 
-	opts.track = git_branch_track;
+	opts.track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	if (!opts.new_branch && (opts.track != git_branch_track))
-		die("git checkout: --track and --no-track require -b");
+	/* --track without -b should DWIM */
+	if (0 < opts.track && !opts.new_branch) {
+		const char *argv0 = argv[0];
+		if (!argc || !strcmp(argv0, "--"))
+			die ("--track needs a branch name");
+		if (!prefixcmp(argv0, "refs/"))
+			argv0 += 5;
+		if (!prefixcmp(argv0, "remotes/"))
+			argv0 += 8;
+		argv0 = strchr(argv0, '/');
+		if (!argv0 || !argv0[1])
+			die ("Missing branch name; try -b");
+		opts.new_branch = argv0 + 1;
+	}
+
+	if (opts.track == BRANCH_TRACK_UNSPECIFIED)
+		opts.track = git_branch_track;
+	if (conflict_style) {
+		opts.merge = 1; /* implied */
+		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+	}
 
 	if (opts.force && opts.merge)
 		die("git checkout: -f and -m are incompatible");
@@ -505,6 +663,9 @@
 		arg = argv[0];
 		has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
 
+		if (!strcmp(arg, "-"))
+			arg = "@{-1}";
+
 		if (get_sha1(arg, rev)) {
 			if (has_dash_dash)          /* case (1) */
 				die("invalid reference: %s", arg);
@@ -515,8 +676,8 @@
 		argv++;
 		argc--;
 
+		new.name = arg;
 		if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
-			new.name = arg;
 			setup_branch_path(&new);
 			if (resolve_ref(new.path, rev, 1, NULL))
 				new.commit = lookup_commit_reference(rev);
@@ -553,32 +714,35 @@
 			die("invalid path specification");
 
 		/* Checkout paths */
-		if (opts.new_branch || opts.force || opts.merge) {
+		if (opts.new_branch) {
 			if (argc == 1) {
-				die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
+				die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
 			} else {
-				die("git checkout: updating paths is incompatible with switching branches/forcing");
+				die("git checkout: updating paths is incompatible with switching branches.");
 			}
 		}
 
-		return checkout_paths(source_tree, pathspec);
+		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+			die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
+
+		return checkout_paths(source_tree, pathspec, &opts);
 	}
 
 	if (opts.new_branch) {
-		struct strbuf buf;
-		strbuf_init(&buf, 0);
-		strbuf_addstr(&buf, "refs/heads/");
-		strbuf_addstr(&buf, opts.new_branch);
+		struct strbuf buf = STRBUF_INIT;
+		if (strbuf_check_branch_ref(&buf, opts.new_branch))
+			die("git checkout: we do not like '%s' as a branch name.",
+			    opts.new_branch);
 		if (!get_sha1(buf.buf, rev))
 			die("git checkout: branch %s already exists", opts.new_branch);
-		if (check_ref_format(buf.buf))
-			die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
 		strbuf_release(&buf);
 	}
 
 	if (new.name && !new.commit) {
 		die("Cannot switch branch to a non-commit.");
 	}
+	if (opts.writeout_stage)
+		die("--ours/--theirs is incompatible with switching branches.");
 
 	return switch_branches(&opts, &new);
 }
diff --git a/builtin-clean.c b/builtin-clean.c
index 48bf29f..c5ad33d 100644
--- a/builtin-clean.c
+++ b/builtin-clean.c
@@ -31,11 +31,11 @@
 	int i;
 	int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
 	int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
-	struct strbuf directory;
+	struct strbuf directory = STRBUF_INIT;
 	struct dir_struct dir;
 	const char *path, *base;
 	static const char **pathspec;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -58,10 +58,9 @@
 
 	argc = parse_options(argc, argv, options, builtin_clean_usage, 0);
 
-	strbuf_init(&buf, 0);
 	memset(&dir, 0, sizeof(dir));
 	if (ignored_only)
-		dir.show_ignored = 1;
+		dir.flags |= DIR_SHOW_IGNORED;
 
 	if (ignored && ignored_only)
 		die("-x and -X cannot be used together");
@@ -70,7 +69,7 @@
 		die("clean.requireForce%s set and -n or -f not given; "
 		    "refusing to clean", config_set ? "" : " not");
 
-	dir.show_other_directories = 1;
+	dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
 	if (!ignored)
 		setup_standard_excludes(&dir);
@@ -88,7 +87,6 @@
 	if (baselen)
 		path = base = xmemdupz(*pathspec, baselen);
 	read_directory(&dir, path, base, baselen, pathspec);
-	strbuf_init(&directory, 0);
 
 	if (pathspec)
 		seen = xmalloc(argc > 0 ? argc : 1);
diff --git a/builtin-clone.c b/builtin-clone.c
index 5b40e07..880373f 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -19,6 +19,10 @@
 #include "strbuf.h"
 #include "dir.h"
 #include "pack-refs.h"
+#include "sigchain.h"
+#include "branch.h"
+#include "remote.h"
+#include "run-command.h"
 
 /*
  * Overall FIXMEs:
@@ -38,9 +42,11 @@
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
 static char *option_upload_pack = "git-upload-pack";
+static int option_verbose;
 
 static struct option builtin_clone_options[] = {
 	OPT__QUIET(&option_quiet),
+	OPT__VERBOSE(&option_verbose),
 	OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
 		    "don't create a checkout"),
 	OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
@@ -77,7 +83,7 @@
 	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
 		const char *path;
 		path = mkpath("%s%s", repo, suffix[i]);
-		if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
+		if (is_directory(path)) {
 			*is_bundle = 0;
 			return xstrdup(make_nonrelative_path(path));
 		}
@@ -132,21 +138,14 @@
 	}
 
 	if (is_bare) {
-		char *result = xmalloc(end - start + 5);
-		sprintf(result, "%.*s.git", (int)(end - start), start);
-		return result;
+		struct strbuf result = STRBUF_INIT;
+		strbuf_addf(&result, "%.*s.git", (int)(end - start), start);
+		return strbuf_detach(&result, 0);
 	}
 
 	return xstrndup(start, end - start);
 }
 
-static int is_directory(const char *path)
-{
-	struct stat buf;
-
-	return !stat(path, &buf) && S_ISDIR(buf.st_mode);
-}
-
 static void strip_trailing_slashes(char *dir)
 {
 	char *end = dir + strlen(dir);
@@ -188,36 +187,38 @@
 	free(ref_git_copy);
 }
 
-static void copy_or_link_directory(char *src, char *dest)
+static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
 {
 	struct dirent *de;
 	struct stat buf;
 	int src_len, dest_len;
 	DIR *dir;
 
-	dir = opendir(src);
+	dir = opendir(src->buf);
 	if (!dir)
-		die("failed to open %s\n", src);
+		die("failed to open %s", src->buf);
 
-	if (mkdir(dest, 0777)) {
+	if (mkdir(dest->buf, 0777)) {
 		if (errno != EEXIST)
-			die("failed to create directory %s\n", dest);
-		else if (stat(dest, &buf))
-			die("failed to stat %s\n", dest);
+			die("failed to create directory %s", dest->buf);
+		else if (stat(dest->buf, &buf))
+			die("failed to stat %s", dest->buf);
 		else if (!S_ISDIR(buf.st_mode))
-			die("%s exists and is not a directory\n", dest);
+			die("%s exists and is not a directory", dest->buf);
 	}
 
-	src_len = strlen(src);
-	src[src_len] = '/';
-	dest_len = strlen(dest);
-	dest[dest_len] = '/';
+	strbuf_addch(src, '/');
+	src_len = src->len;
+	strbuf_addch(dest, '/');
+	dest_len = dest->len;
 
 	while ((de = readdir(dir)) != NULL) {
-		strcpy(src + src_len + 1, de->d_name);
-		strcpy(dest + dest_len + 1, de->d_name);
-		if (stat(src, &buf)) {
-			warning ("failed to stat %s\n", src);
+		strbuf_setlen(src, src_len);
+		strbuf_addstr(src, de->d_name);
+		strbuf_setlen(dest, dest_len);
+		strbuf_addstr(dest, de->d_name);
+		if (stat(src->buf, &buf)) {
+			warning ("failed to stat %s\n", src->buf);
 			continue;
 		}
 		if (S_ISDIR(buf.st_mode)) {
@@ -226,17 +227,17 @@
 			continue;
 		}
 
-		if (unlink(dest) && errno != ENOENT)
-			die("failed to unlink %s\n", dest);
+		if (unlink(dest->buf) && errno != ENOENT)
+			die("failed to unlink %s", dest->buf);
 		if (!option_no_hardlinks) {
-			if (!link(src, dest))
+			if (!link(src->buf, dest->buf))
 				continue;
 			if (option_local)
-				die("failed to create link %s\n", dest);
+				die("failed to create link %s", dest->buf);
 			option_no_hardlinks = 1;
 		}
-		if (copy_file(dest, src, 0666))
-			die("failed to copy file to %s\n", dest);
+		if (copy_file(dest->buf, src->buf, 0666))
+			die("failed to copy file to %s", dest->buf);
 	}
 	closedir(dir);
 }
@@ -245,17 +246,19 @@
 				     const char *dest_repo)
 {
 	const struct ref *ret;
-	char src[PATH_MAX];
-	char dest[PATH_MAX];
+	struct strbuf src = STRBUF_INIT;
+	struct strbuf dest = STRBUF_INIT;
 	struct remote *remote;
 	struct transport *transport;
 
 	if (option_shared)
 		add_to_alternates_file(src_repo);
 	else {
-		snprintf(src, PATH_MAX, "%s/objects", src_repo);
-		snprintf(dest, PATH_MAX, "%s/objects", dest_repo);
-		copy_or_link_directory(src, dest);
+		strbuf_addf(&src, "%s/objects", src_repo);
+		strbuf_addf(&dest, "%s/objects", dest_repo);
+		copy_or_link_directory(&src, &dest);
+		strbuf_release(&src);
+		strbuf_release(&dest);
 	}
 
 	remote = remote_get(src_repo);
@@ -267,14 +270,13 @@
 
 static const char *junk_work_tree;
 static const char *junk_git_dir;
-pid_t junk_pid;
+static pid_t junk_pid;
 
 static void remove_junk(void)
 {
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 	if (getpid() != junk_pid)
 		return;
-	strbuf_init(&sb, 0);
 	if (junk_git_dir) {
 		strbuf_addstr(&sb, junk_git_dir);
 		remove_dir_recursively(&sb, 0);
@@ -290,47 +292,10 @@
 static void remove_junk_on_signal(int signo)
 {
 	remove_junk();
-	signal(SIGINT, SIG_DFL);
+	sigchain_pop(signo);
 	raise(signo);
 }
 
-static const struct ref *locate_head(const struct ref *refs,
-				     const struct ref *mapped_refs,
-				     const struct ref **remote_head_p)
-{
-	const struct ref *remote_head = NULL;
-	const struct ref *remote_master = NULL;
-	const struct ref *r;
-	for (r = refs; r; r = r->next)
-		if (!strcmp(r->name, "HEAD"))
-			remote_head = r;
-
-	for (r = mapped_refs; r; r = r->next)
-		if (!strcmp(r->name, "refs/heads/master"))
-			remote_master = r;
-
-	if (remote_head_p)
-		*remote_head_p = remote_head;
-
-	/* If there's no HEAD value at all, never mind. */
-	if (!remote_head)
-		return NULL;
-
-	/* If refs/heads/master could be right, it is. */
-	if (remote_master && !hashcmp(remote_master->old_sha1,
-				      remote_head->old_sha1))
-		return remote_master;
-
-	/* Look for another ref that points there */
-	for (r = mapped_refs; r; r = r->next)
-		if (r != remote_head &&
-		    !hashcmp(r->old_sha1, remote_head->old_sha1))
-			return r;
-
-	/* Nothing is the same */
-	return NULL;
-}
-
 static struct ref *write_remote_refs(const struct ref *refs,
 		struct refspec *refspec, const char *reflog)
 {
@@ -353,19 +318,20 @@
 
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
-	int use_local_hardlinks = 1;
-	int use_separate_remote = 1;
 	int is_bundle = 0;
 	struct stat buf;
 	const char *repo_name, *repo, *work_tree, *git_dir;
 	char *path, *dir;
+	int dest_exists;
 	const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
-	char branch_top[256], key[256], value[256];
-	struct strbuf reflog_msg;
+	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
+	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
 	char *src_ref_prefix = "refs/heads/";
+	int err = 0;
 
-	struct refspec refspec;
+	struct refspec *refspec;
+	const char *fetch_pattern;
 
 	junk_pid = getpid();
 
@@ -375,9 +341,6 @@
 	if (argc == 0)
 		die("You must specify a repository to clone.");
 
-	if (option_no_hardlinks)
-		use_local_hardlinks = 0;
-
 	if (option_mirror)
 		option_bare = 1;
 
@@ -386,7 +349,6 @@
 			die("--bare and --origin %s options are incompatible.",
 			    option_origin);
 		option_no_checkout = 1;
-		use_separate_remote = 0;
 	}
 
 	if (!option_origin)
@@ -408,10 +370,11 @@
 		dir = guess_dir_name(repo_name, is_bundle, option_bare);
 	strip_trailing_slashes(dir);
 
-	if (!stat(dir, &buf))
-		die("destination directory '%s' already exists.", dir);
+	dest_exists = !stat(dir, &buf);
+	if (dest_exists && !is_empty_dir(dir))
+		die("destination path '%s' already exists and is not "
+			"an empty directory.", dir);
 
-	strbuf_init(&reflog_msg, 0);
 	strbuf_addf(&reflog_msg, "clone: from %s", repo);
 
 	if (option_bare)
@@ -434,16 +397,16 @@
 		if (safe_create_leading_directories_const(work_tree) < 0)
 			die("could not create leading directories of '%s': %s",
 					work_tree, strerror(errno));
-		if (mkdir(work_tree, 0755))
+		if (!dest_exists && mkdir(work_tree, 0755))
 			die("could not create work tree dir '%s': %s.",
 					work_tree, strerror(errno));
 		set_git_work_tree(work_tree);
 	}
 	junk_git_dir = git_dir;
 	atexit(remove_junk);
-	signal(SIGINT, remove_junk_on_signal);
+	sigchain_push_common(remove_junk_on_signal);
 
-	setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);
+	setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);
 
 	if (safe_create_leading_directories_const(git_dir) < 0)
 		die("could not create leading directories of '%s'", git_dir);
@@ -466,35 +429,36 @@
 	if (option_bare) {
 		if (option_mirror)
 			src_ref_prefix = "refs/";
-		strcpy(branch_top, src_ref_prefix);
+		strbuf_addstr(&branch_top, src_ref_prefix);
 
 		git_config_set("core.bare", "true");
 	} else {
-		snprintf(branch_top, sizeof(branch_top),
-			 "refs/remotes/%s/", option_origin);
+		strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
 	}
 
+	strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
+
 	if (option_mirror || !option_bare) {
 		/* Configure the remote */
+		strbuf_addf(&key, "remote.%s.fetch", option_origin);
+		git_config_set_multivar(key.buf, value.buf, "^$", 0);
+		strbuf_reset(&key);
+
 		if (option_mirror) {
-			snprintf(key, sizeof(key),
-					"remote.%s.mirror", option_origin);
-			git_config_set(key, "true");
+			strbuf_addf(&key, "remote.%s.mirror", option_origin);
+			git_config_set(key.buf, "true");
+			strbuf_reset(&key);
 		}
 
-		snprintf(key, sizeof(key), "remote.%s.url", option_origin);
-		git_config_set(key, repo);
-
-		snprintf(key, sizeof(key), "remote.%s.fetch", option_origin);
-		snprintf(value, sizeof(value),
-				"+%s*:%s*", src_ref_prefix, branch_top);
-		git_config_set_multivar(key, value, "^$", 0);
+		strbuf_addf(&key, "remote.%s.url", option_origin);
+		git_config_set(key.buf, repo);
+		strbuf_reset(&key);
 	}
 
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = src_ref_prefix;
-	refspec.dst = branch_top;
+	fetch_pattern = value.buf;
+	refspec = parse_fetch_refspec(1, &fetch_pattern);
+
+	strbuf_reset(&value);
 
 	if (path && !is_bundle)
 		refs = clone_local(path, git_dir);
@@ -513,27 +477,42 @@
 
 		if (option_quiet)
 			transport->verbose = -1;
+		else if (option_verbose)
+			transport->progress = 1;
 
 		if (option_upload_pack)
 			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 					     option_upload_pack);
 
 		refs = transport_get_remote_refs(transport);
-		transport_fetch_refs(transport, refs);
+		if(refs)
+			transport_fetch_refs(transport, refs);
 	}
 
-	clear_extra_refs();
+	if (refs) {
+		clear_extra_refs();
 
-	mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
+		mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
 
-	head_points_at = locate_head(refs, mapped_refs, &remote_head);
+		remote_head = find_ref_by_name(refs, "HEAD");
+		head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+	}
+	else {
+		warning("You appear to have cloned an empty repository.");
+		head_points_at = NULL;
+		remote_head = NULL;
+		option_no_checkout = 1;
+		if (!option_bare)
+			install_branch_config(0, "master", option_origin,
+					      "refs/heads/master");
+	}
 
 	if (head_points_at) {
 		/* Local default branch link */
 		create_symref("HEAD", head_points_at->name, NULL);
 
 		if (!option_bare) {
-			struct strbuf head_ref;
+			struct strbuf head_ref = STRBUF_INIT;
 			const char *head = head_points_at->name;
 
 			if (!prefixcmp(head, "refs/heads/"))
@@ -546,8 +525,7 @@
 				   head_points_at->old_sha1,
 				   NULL, 0, DIE_ON_ERR);
 
-			strbuf_init(&head_ref, 0);
-			strbuf_addstr(&head_ref, branch_top);
+			strbuf_addstr(&head_ref, branch_top.buf);
 			strbuf_addstr(&head_ref, "HEAD");
 
 			/* Remote branch link */
@@ -555,10 +533,8 @@
 				      head_points_at->peer_ref->name,
 				      reflog_msg.buf);
 
-			snprintf(key, sizeof(key), "branch.%s.remote", head);
-			git_config_set(key, option_origin);
-			snprintf(key, sizeof(key), "branch.%s.merge", head);
-			git_config_set(key, head_points_at->name);
+			install_branch_config(0, head, option_origin,
+					      head_points_at->name);
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
@@ -605,9 +581,15 @@
 		if (write_cache(fd, active_cache, active_nr) ||
 		    commit_locked_index(lock_file))
 			die("unable to write new index file");
+
+		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+				sha1_to_hex(remote_head->old_sha1), "1", NULL);
 	}
 
 	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_top);
+	strbuf_release(&key);
+	strbuf_release(&value);
 	junk_pid = 0;
-	return 0;
+	return err;
 }
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index 9b84c48..0453425 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -46,8 +46,10 @@
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
 int commit_tree(const char *msg, unsigned char *tree,
-		struct commit_list *parents, unsigned char *ret)
+		struct commit_list *parents, unsigned char *ret,
+		const char *author)
 {
+	int result;
 	int encoding_is_utf8;
 	struct strbuf buffer;
 
@@ -73,7 +75,9 @@
 	}
 
 	/* Person/date information */
-	strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME));
+	if (!author)
+		author = git_author_info(IDENT_ERROR_ON_NO_NAME);
+	strbuf_addf(&buffer, "author %s\n", author);
 	strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
 	if (!encoding_is_utf8)
 		strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
@@ -86,7 +90,9 @@
 	if (encoding_is_utf8 && !is_utf8(buffer.buf))
 		fprintf(stderr, commit_utf8_warn);
 
-	return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+	result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+	strbuf_release(&buffer);
+	return result;
 }
 
 int cmd_commit_tree(int argc, const char **argv, const char *prefix)
@@ -120,7 +126,7 @@
 	if (strbuf_read(&buffer, 0, 0) < 0)
 		die("git commit-tree: read returned %s", strerror(errno));
 
-	if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1)) {
+	if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
 		printf("%s\n", sha1_to_hex(commit_sha1));
 		return 0;
 	}
diff --git a/builtin-commit.c b/builtin-commit.c
index fde7b89..81371b1 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -166,7 +166,7 @@
 		struct cache_entry *ce = active_cache[i];
 		if (ce->ce_flags & CE_UPDATE)
 			continue;
-		if (!pathspec_match(pattern, m, ce->name, 0))
+		if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
 			continue;
 		string_list_insert(ce->name, list);
 	}
@@ -226,18 +226,18 @@
 	if (interactive) {
 		if (interactive_add(argc, argv, prefix) != 0)
 			die("interactive add failed");
-		if (read_cache() < 0)
+		if (read_cache_preload(NULL) < 0)
 			die("index file corrupt");
 		commit_style = COMMIT_AS_IS;
 		return get_index_file();
 	}
 
-	if (read_cache() < 0)
-		die("index file corrupt");
-
 	if (*argv)
 		pathspec = get_pathspec(prefix, argv);
 
+	if (read_cache_preload(pathspec) < 0)
+		die("index file corrupt");
+
 	/*
 	 * Non partial, non as-is commit.
 	 *
@@ -321,7 +321,8 @@
 		die("unable to write new_index file");
 
 	fd = hold_lock_file_for_update(&false_lock,
-				       git_path("next-index-%d", getpid()),
+				       git_path("next-index-%"PRIuMAX,
+						(uintmax_t) getpid()),
 				       LOCK_DIE_ON_ERROR);
 
 	create_base_index();
@@ -361,40 +362,6 @@
 	return s.commitable;
 }
 
-static int run_hook(const char *index_file, const char *name, ...)
-{
-	struct child_process hook;
-	const char *argv[10], *env[2];
-	char index[PATH_MAX];
-	va_list args;
-	int i;
-
-	va_start(args, name);
-	argv[0] = git_path("hooks/%s", name);
-	i = 0;
-	do {
-		if (++i >= ARRAY_SIZE(argv))
-			die ("run_hook(): too many arguments");
-		argv[i] = va_arg(args, const char *);
-	} while (argv[i]);
-	va_end(args);
-
-	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-	env[0] = index;
-	env[1] = NULL;
-
-	if (access(argv[0], X_OK) < 0)
-		return 0;
-
-	memset(&hook, 0, sizeof(hook));
-	hook.argv = argv;
-	hook.no_stdin = 1;
-	hook.stdout_to_stderr = 1;
-	hook.env = env;
-
-	return run_command(&hook);
-}
-
 static int is_a_merge(const unsigned char *sha1)
 {
 	struct commit *commit = lookup_commit(sha1);
@@ -450,7 +417,7 @@
 {
 	struct stat statbuf;
 	int commitable, saved_color_setting;
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 	char *buffer;
 	FILE *fp;
 	const char *hook_arg1 = NULL;
@@ -460,7 +427,6 @@
 	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
 		return 0;
 
-	strbuf_init(&sb, 0);
 	if (message.len) {
 		strbuf_addbuf(&sb, &message);
 		hook_arg1 = "message";
@@ -513,10 +479,9 @@
 		stripspace(&sb, 0);
 
 	if (signoff) {
-		struct strbuf sob;
+		struct strbuf sob = STRBUF_INIT;
 		int i;
 
-		strbuf_init(&sob, 0);
 		strbuf_addstr(&sob, sign_off_header);
 		strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
 					     getenv("GIT_COMMITTER_EMAIL")));
@@ -597,7 +562,6 @@
 		commitable = run_status(fp, index_file, prefix, 1);
 		wt_status_use_color = saved_color_setting;
 	} else {
-		struct rev_info rev;
 		unsigned char sha1[20];
 		const char *parent = "HEAD";
 
@@ -609,16 +573,8 @@
 
 		if (get_sha1(parent, sha1))
 			commitable = !!active_nr;
-		else {
-			init_revisions(&rev, "");
-			rev.abbrev = 0;
-			setup_revisions(0, NULL, &rev, parent);
-			DIFF_OPT_SET(&rev.diffopt, QUIET);
-			DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-			run_diff_index(&rev, 1 /* cached */);
-
-			commitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
-		}
+		else
+			commitable = index_differs_from(parent, 0);
 	}
 
 	fclose(fp);
@@ -640,7 +596,7 @@
 		active_cache_tree = cache_tree();
 	if (cache_tree_update(active_cache_tree,
 			      active_cache, active_nr, 0, 0) < 0) {
-		error("Error building trees; the index is unmerged?");
+		error("Error building trees");
 		return 0;
 	}
 
@@ -668,20 +624,19 @@
 }
 
 /*
- * Find out if the message starting at position 'start' in the strbuf
- * contains only whitespace and Signed-off-by lines.
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
  */
-static int message_is_empty(struct strbuf *sb, int start)
+static int message_is_empty(struct strbuf *sb)
 {
-	struct strbuf tmpl;
+	struct strbuf tmpl = STRBUF_INIT;
 	const char *nl;
-	int eol, i;
+	int eol, i, start = 0;
 
 	if (cleanup_mode == CLEANUP_NONE && sb->len)
 		return 0;
 
 	/* See if the template is just a prefix of the message. */
-	strbuf_init(&tmpl, 0);
 	if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
 		stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
 		if (start + tmpl.len <= sb->len &&
@@ -711,6 +666,31 @@
 	return 1;
 }
 
+static const char *find_author_by_nickname(const char *name)
+{
+	struct rev_info revs;
+	struct commit *commit;
+	struct strbuf buf = STRBUF_INIT;
+	const char *av[20];
+	int ac = 0;
+
+	init_revisions(&revs, NULL);
+	strbuf_addf(&buf, "--author=%s", name);
+	av[++ac] = "--all";
+	av[++ac] = "-i";
+	av[++ac] = buf.buf;
+	av[++ac] = NULL;
+	setup_revisions(ac, av, &revs, NULL);
+	prepare_revision_walk(&revs);
+	commit = get_revision(&revs);
+	if (commit) {
+		strbuf_release(&buf);
+		format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
+		return strbuf_detach(&buf, NULL);
+	}
+	die("No existing author found with '%s'", name);
+}
+
 static int parse_and_validate_options(int argc, const char *argv[],
 				      const char * const usage[],
 				      const char *prefix)
@@ -721,6 +701,9 @@
 	logfile = parse_options_fix_filename(prefix, logfile);
 	template_file = parse_options_fix_filename(prefix, template_file);
 
+	if (force_author && !strchr(force_author, '>'))
+		force_author = find_author_by_nickname(force_author);
+
 	if (logfile || message.len || use_message)
 		use_editor = 0;
 	if (edit_flag)
@@ -840,6 +823,9 @@
 	if (wt_status_use_color == -1)
 		wt_status_use_color = git_use_color_default;
 
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
 	argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
 
 	index_file = prepare_index(argc, argv, prefix);
@@ -855,6 +841,9 @@
 {
 	struct rev_info rev;
 	struct commit *commit;
+	static const char *format = "format:%h] %s";
+	unsigned char junk_sha1[20];
+	const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
 
 	commit = lookup_commit(sha1);
 	if (!commit)
@@ -872,18 +861,24 @@
 
 	rev.verbose_header = 1;
 	rev.show_root_diff = 1;
-	get_commit_format("format:%h: %s", &rev);
+	get_commit_format(format, &rev);
 	rev.always_show_header = 0;
 	rev.diffopt.detect_rename = 1;
 	rev.diffopt.rename_limit = 100;
 	rev.diffopt.break_opt = 0;
 	diff_setup_done(&rev.diffopt);
 
-	printf("Created %scommit ", initial_commit ? "initial " : "");
+	printf("[%s%s ",
+		!prefixcmp(head, "refs/heads/") ?
+			head + 11 :
+			!strcmp(head, "HEAD") ?
+				"detached HEAD" :
+				head,
+		initial_commit ? " (root-commit)" : "");
 
 	if (!log_tree_commit(&rev, commit)) {
 		struct strbuf buf = STRBUF_INIT;
-		format_commit_message(commit, "%h: %s", &buf, DATE_NORMAL);
+		format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
 		printf("%s\n", buf.buf);
 		strbuf_release(&buf);
 	}
@@ -897,42 +892,22 @@
 	return git_status_config(k, v, cb);
 }
 
-static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
-
-static void add_parent(struct strbuf *sb, const unsigned char *sha1)
-{
-	struct object *obj = parse_object(sha1);
-	const char *parent = sha1_to_hex(sha1);
-	const char *cp;
-
-	if (!obj)
-		die("Unable to find commit parent %s", parent);
-	if (obj->type != OBJ_COMMIT)
-		die("Parent %s isn't a proper commit", parent);
-
-	for (cp = sb->buf; cp && (cp = strstr(cp, "\nparent ")); cp += 8) {
-		if (!memcmp(cp + 8, parent, 40) && cp[48] == '\n') {
-			error("duplicate parent %s ignored", parent);
-			return;
-		}
-	}
-	strbuf_addf(sb, "parent %s\n", parent);
-}
-
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
-	int header_len;
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
 	char *nl, *p;
 	unsigned char commit_sha1[20];
 	struct ref_lock *ref_lock;
+	struct commit_list *parents = NULL, **pptr = &parents;
+	struct stat statbuf;
+	int allow_fast_forward = 1;
 
 	git_config(git_commit_config, NULL);
 
+	if (wt_status_use_color == -1)
+		wt_status_use_color = git_use_color_default;
+
 	argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
 
 	index_file = prepare_index(argc, argv, prefix);
@@ -944,13 +919,6 @@
 		return 1;
 	}
 
-	/*
-	 * The commit object
-	 */
-	strbuf_init(&sb, 0);
-	strbuf_addf(&sb, "tree %s\n",
-		    sha1_to_hex(active_cache_tree->sha1));
-
 	/* Determine parents */
 	if (initial_commit) {
 		reflog_msg = "commit (initial)";
@@ -964,14 +932,13 @@
 			die("could not parse HEAD commit");
 
 		for (c = commit->parents; c; c = c->next)
-			add_parent(&sb, c->item->object.sha1);
+			pptr = &commit_list_insert(c->item, pptr)->next;
 	} else if (in_merge) {
-		struct strbuf m;
+		struct strbuf m = STRBUF_INIT;
 		FILE *fp;
 
 		reflog_msg = "commit (merge)";
-		add_parent(&sb, head_sha1);
-		strbuf_init(&m, 0);
+		pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
 		fp = fopen(git_path("MERGE_HEAD"), "r");
 		if (fp == NULL)
 			die("could not open %s for reading: %s",
@@ -980,24 +947,26 @@
 			unsigned char sha1[20];
 			if (get_sha1_hex(m.buf, sha1) < 0)
 				die("Corrupt MERGE_HEAD file (%s)", m.buf);
-			add_parent(&sb, sha1);
+			pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next;
 		}
 		fclose(fp);
 		strbuf_release(&m);
+		if (!stat(git_path("MERGE_MODE"), &statbuf)) {
+			if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
+				die("could not read MERGE_MODE: %s",
+						strerror(errno));
+			if (!strcmp(sb.buf, "no-ff"))
+				allow_fast_forward = 0;
+		}
+		if (allow_fast_forward)
+			parents = reduce_heads(parents);
 	} else {
 		reflog_msg = "commit";
-		strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+		pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
 	}
 
-	strbuf_addf(&sb, "author %s\n",
-		    fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME));
-	strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
-	if (!is_encoding_utf8(git_commit_encoding))
-		strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
-	strbuf_addch(&sb, '\n');
-
 	/* Finally, get the commit message */
-	header_len = sb.len;
+	strbuf_reset(&sb);
 	if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
 		rollback_index_files();
 		die("could not read commit message");
@@ -1012,16 +981,15 @@
 
 	if (cleanup_mode != CLEANUP_NONE)
 		stripspace(&sb, cleanup_mode == CLEANUP_ALL);
-	if (sb.len < header_len || message_is_empty(&sb, header_len)) {
+	if (message_is_empty(&sb)) {
 		rollback_index_files();
 		fprintf(stderr, "Aborting commit due to empty commit message.\n");
 		exit(1);
 	}
-	strbuf_addch(&sb, '\0');
-	if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
-		fprintf(stderr, commit_utf8_warn);
 
-	if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {
+	if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
+			fmt_ident(author_name, author_email, author_date,
+				IDENT_ERROR_ON_NO_NAME))) {
 		rollback_index_files();
 		die("failed to write commit object");
 	}
@@ -1030,12 +998,11 @@
 					   initial_commit ? NULL : head_sha1,
 					   0);
 
-	nl = strchr(sb.buf + header_len, '\n');
+	nl = strchr(sb.buf, '\n');
 	if (nl)
 		strbuf_setlen(&sb, nl + 1 - sb.buf);
 	else
 		strbuf_addch(&sb, '\n');
-	strbuf_remove(&sb, 0, header_len);
 	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
 	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
 
@@ -1050,6 +1017,7 @@
 
 	unlink(git_path("MERGE_HEAD"));
 	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("MERGE_MODE"));
 	unlink(git_path("SQUASH_MSG"));
 
 	if (commit_index_files())
diff --git a/builtin-config.c b/builtin-config.c
index f710162..d8da72c 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -1,9 +1,12 @@
 #include "builtin.h"
 #include "cache.h"
 #include "color.h"
+#include "parse-options.h"
 
-static const char git_config_set_usage[] =
-"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]";
+static const char *const builtin_config_usage[] = {
+	"git config [options]",
+	NULL
+};
 
 static char *key;
 static regex_t *key_regexp;
@@ -16,7 +19,67 @@
 static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
-static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW;
+
+static int use_global_config, use_system_config;
+static const char *given_config_file;
+static int actions, types;
+static const char *get_color_slot, *get_colorbool_slot;
+static int end_null;
+
+#define ACTION_GET (1<<0)
+#define ACTION_GET_ALL (1<<1)
+#define ACTION_GET_REGEXP (1<<2)
+#define ACTION_REPLACE_ALL (1<<3)
+#define ACTION_ADD (1<<4)
+#define ACTION_UNSET (1<<5)
+#define ACTION_UNSET_ALL (1<<6)
+#define ACTION_RENAME_SECTION (1<<7)
+#define ACTION_REMOVE_SECTION (1<<8)
+#define ACTION_LIST (1<<9)
+#define ACTION_EDIT (1<<10)
+#define ACTION_SET (1<<11)
+#define ACTION_SET_ALL (1<<12)
+#define ACTION_GET_COLOR (1<<13)
+#define ACTION_GET_COLORBOOL (1<<14)
+
+#define TYPE_BOOL (1<<0)
+#define TYPE_INT (1<<1)
+#define TYPE_BOOL_OR_INT (1<<2)
+
+static struct option builtin_config_options[] = {
+	OPT_GROUP("Config file location"),
+	OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
+	OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
+	OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
+	OPT_GROUP("Action"),
+	OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
+	OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
+	OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP),
+	OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL),
+	OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD),
+	OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET),
+	OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL),
+	OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION),
+	OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION),
+	OPT_BIT('l', "list", &actions, "list all", ACTION_LIST),
+	OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT),
+	OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"),
+	OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"),
+	OPT_GROUP("Type"),
+	OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
+	OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
+	OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
+	OPT_GROUP("Other"),
+	OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
+	OPT_END(),
+};
+
+static void check_argc(int argc, int min, int max) {
+	if (argc >= min && argc <= max)
+		return;
+	error("wrong number of arguments");
+	usage_with_options(builtin_config_usage, builtin_config_options);
+}
 
 static int show_all_config(const char *key_, const char *value_, void *cb)
 {
@@ -27,7 +90,7 @@
 	return 0;
 }
 
-static int show_config(const char* key_, const char* value_, void *cb)
+static int show_config(const char *key_, const char *value_, void *cb)
 {
 	char value[256];
 	const char *vptr = value;
@@ -49,11 +112,11 @@
 	}
 	if (seen && !do_all)
 		dup_error = 1;
-	if (type == T_INT)
+	if (types == TYPE_INT)
 		sprintf(value, "%d", git_config_int(key_, value_?value_:""));
-	else if (type == T_BOOL)
+	else if (types == TYPE_BOOL)
 		vptr = git_config_bool(key_, value_) ? "true" : "false";
-	else if (type == T_BOOL_OR_INT) {
+	else if (types == TYPE_BOOL_OR_INT) {
 		int is_bool, v;
 		v = git_config_bool_or_int(key_, value_, &is_bool);
 		if (is_bool)
@@ -74,7 +137,7 @@
 	return 0;
 }
 
-static int get_value(const char* key_, const char* regex_)
+static int get_value(const char *key_, const char *regex_)
 {
 	int ret = -1;
 	char *tl;
@@ -152,18 +215,18 @@
 	if (!value)
 		return NULL;
 
-	if (type == T_RAW)
+	if (types == 0)
 		normalized = xstrdup(value);
 	else {
 		normalized = xmalloc(64);
-		if (type == T_INT) {
+		if (types == TYPE_INT) {
 			int v = git_config_int(key, value);
 			sprintf(normalized, "%d", v);
 		}
-		else if (type == T_BOOL)
+		else if (types == TYPE_BOOL)
 			sprintf(normalized, "%s",
 				git_config_bool(key, value) ? "true" : "false");
-		else if (type == T_BOOL_OR_INT) {
+		else if (types == TYPE_BOOL_OR_INT) {
 			int is_bool, v;
 			v = git_config_bool_or_int(key, value, &is_bool);
 			if (!is_bool)
@@ -178,6 +241,7 @@
 
 static int get_color_found;
 static const char *get_color_slot;
+static const char *get_colorbool_slot;
 static char parsed_color[COLOR_MAXLEN];
 
 static int git_get_color_config(const char *var, const char *value, void *cb)
@@ -191,29 +255,8 @@
 	return 0;
 }
 
-static int get_color(int argc, const char **argv)
+static void get_color(const char *def_color)
 {
-	/*
-	 * grab the color setting for the given slot from the configuration,
-	 * or parse the default value if missing, and return ANSI color
-	 * escape sequence.
-	 *
-	 * e.g.
-	 * git config --get-color color.diff.whitespace "blue reverse"
-	 */
-	const char *def_color = NULL;
-
-	switch (argc) {
-	default:
-		usage(git_config_set_usage);
-	case 2:
-		def_color = argv[1];
-		/* fallthru */
-	case 1:
-		get_color_slot = argv[0];
-		break;
-	}
-
 	get_color_found = 0;
 	parsed_color[0] = '\0';
 	git_config(git_get_color_config, NULL);
@@ -222,7 +265,6 @@
 		color_parse(def_color, "command line", parsed_color);
 
 	fputs(parsed_color, stdout);
-	return 0;
 }
 
 static int stdout_is_tty;
@@ -231,7 +273,7 @@
 static int git_get_colorbool_config(const char *var, const char *value,
 		void *cb)
 {
-	if (!strcmp(var, get_color_slot)) {
+	if (!strcmp(var, get_colorbool_slot)) {
 		get_colorbool_found =
 			git_config_colorbool(var, value, stdout_is_tty);
 	}
@@ -246,183 +288,188 @@
 	return 0;
 }
 
-static int get_colorbool(int argc, const char **argv)
+static int get_colorbool(int print)
 {
-	/*
-	 * git config --get-colorbool <slot> [<stdout-is-tty>]
-	 *
-	 * returns "true" or "false" depending on how <slot>
-	 * is configured.
-	 */
-
-	if (argc == 2)
-		stdout_is_tty = git_config_bool("command line", argv[1]);
-	else if (argc == 1)
-		stdout_is_tty = isatty(1);
-	else
-		usage(git_config_set_usage);
 	get_colorbool_found = -1;
 	get_diff_color_found = -1;
-	get_color_slot = argv[0];
 	git_config(git_get_colorbool_config, NULL);
 
 	if (get_colorbool_found < 0) {
-		if (!strcmp(get_color_slot, "color.diff"))
+		if (!strcmp(get_colorbool_slot, "color.diff"))
 			get_colorbool_found = get_diff_color_found;
 		if (get_colorbool_found < 0)
 			get_colorbool_found = git_use_color_default;
 	}
 
-	if (argc == 1) {
-		return get_colorbool_found ? 0 : 1;
-	} else {
+	if (print) {
 		printf("%s\n", get_colorbool_found ? "true" : "false");
 		return 0;
-	}
+	} else
+		return get_colorbool_found ? 0 : 1;
 }
 
-int cmd_config(int argc, const char **argv, const char *prefix)
+int cmd_config(int argc, const char **argv, const char *unused_prefix)
 {
 	int nongit;
-	char* value;
-	const char *file = setup_git_directory_gently(&nongit);
+	char *value;
+	const char *prefix = setup_git_directory_gently(&nongit);
 
 	config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
 
-	while (1 < argc) {
-		if (!strcmp(argv[1], "--int"))
-			type = T_INT;
-		else if (!strcmp(argv[1], "--bool"))
-			type = T_BOOL;
-		else if (!strcmp(argv[1], "--bool-or-int"))
-			type = T_BOOL_OR_INT;
-		else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
-			if (argc != 2)
-				usage(git_config_set_usage);
-			if (git_config(show_all_config, NULL) < 0 &&
-					file && errno)
-				die("unable to read config file %s: %s", file,
-				    strerror(errno));
-			return 0;
-		}
-		else if (!strcmp(argv[1], "--global")) {
-			char *home = getenv("HOME");
-			if (home) {
-				char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-				config_exclusive_filename = user_config;
-			} else {
-				die("$HOME not set");
-			}
-		}
-		else if (!strcmp(argv[1], "--system"))
-			config_exclusive_filename = git_etc_gitconfig();
-		else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
-			if (argc < 3)
-				usage(git_config_set_usage);
-			if (!is_absolute_path(argv[2]) && file)
-				file = prefix_filename(file, strlen(file),
-						       argv[2]);
-			else
-				file = argv[2];
-			config_exclusive_filename = file;
-			argc--;
-			argv++;
-		}
-		else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
-			term = '\0';
-			delim = '\n';
-			key_delim = '\n';
-		}
-		else if (!strcmp(argv[1], "--rename-section")) {
-			int ret;
-			if (argc != 4)
-				usage(git_config_set_usage);
-			ret = git_config_rename_section(argv[2], argv[3]);
-			if (ret < 0)
-				return ret;
-			if (ret == 0) {
-				fprintf(stderr, "No such section!\n");
-				return 1;
-			}
-			return 0;
-		}
-		else if (!strcmp(argv[1], "--remove-section")) {
-			int ret;
-			if (argc != 3)
-				usage(git_config_set_usage);
-			ret = git_config_rename_section(argv[2], NULL);
-			if (ret < 0)
-				return ret;
-			if (ret == 0) {
-				fprintf(stderr, "No such section!\n");
-				return 1;
-			}
-			return 0;
-		} else if (!strcmp(argv[1], "--get-color")) {
-			return get_color(argc-2, argv+2);
-		} else if (!strcmp(argv[1], "--get-colorbool")) {
-			return get_colorbool(argc-2, argv+2);
-		} else
-			break;
-		argc--;
-		argv++;
+	argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (use_global_config + use_system_config + !!given_config_file > 1) {
+		error("only one config file at a time.");
+		usage_with_options(builtin_config_usage, builtin_config_options);
 	}
 
-	switch (argc) {
-	case 2:
-		return get_value(argv[1], NULL);
-	case 3:
-		if (!strcmp(argv[1], "--unset"))
-			return git_config_set(argv[2], NULL);
-		else if (!strcmp(argv[1], "--unset-all"))
-			return git_config_set_multivar(argv[2], NULL, NULL, 1);
-		else if (!strcmp(argv[1], "--get"))
-			return get_value(argv[2], NULL);
-		else if (!strcmp(argv[1], "--get-all")) {
-			do_all = 1;
-			return get_value(argv[2], NULL);
-		} else if (!strcmp(argv[1], "--get-regexp")) {
-			show_keys = 1;
-			use_key_regexp = 1;
-			do_all = 1;
-			return get_value(argv[2], NULL);
+	if (use_global_config) {
+		char *home = getenv("HOME");
+		if (home) {
+			char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
+			config_exclusive_filename = user_config;
 		} else {
-			value = normalize_value(argv[1], argv[2]);
-			return git_config_set(argv[1], value);
+			die("$HOME not set");
 		}
-	case 4:
-		if (!strcmp(argv[1], "--unset"))
-			return git_config_set_multivar(argv[2], NULL, argv[3], 0);
-		else if (!strcmp(argv[1], "--unset-all"))
-			return git_config_set_multivar(argv[2], NULL, argv[3], 1);
-		else if (!strcmp(argv[1], "--get"))
-			return get_value(argv[2], argv[3]);
-		else if (!strcmp(argv[1], "--get-all")) {
-			do_all = 1;
-			return get_value(argv[2], argv[3]);
-		} else if (!strcmp(argv[1], "--get-regexp")) {
-			show_keys = 1;
-			use_key_regexp = 1;
-			do_all = 1;
-			return get_value(argv[2], argv[3]);
-		} else if (!strcmp(argv[1], "--add")) {
-			value = normalize_value(argv[2], argv[3]);
-			return git_config_set_multivar(argv[2], value, "^$", 0);
-		} else if (!strcmp(argv[1], "--replace-all")) {
-			value = normalize_value(argv[2], argv[3]);
-			return git_config_set_multivar(argv[2], value, NULL, 1);
-		} else {
-			value = normalize_value(argv[1], argv[2]);
-			return git_config_set_multivar(argv[1], value, argv[3], 0);
-		}
-	case 5:
-		if (!strcmp(argv[1], "--replace-all")) {
-			value = normalize_value(argv[2], argv[3]);
-			return git_config_set_multivar(argv[2], value, argv[4], 1);
-		}
-	case 1:
-	default:
-		usage(git_config_set_usage);
 	}
+	else if (use_system_config)
+		config_exclusive_filename = git_etc_gitconfig();
+	else if (given_config_file) {
+		if (!is_absolute_path(given_config_file) && prefix)
+			config_exclusive_filename = prefix_filename(prefix,
+								    strlen(prefix),
+								    argv[2]);
+		else
+			config_exclusive_filename = given_config_file;
+	}
+
+	if (end_null) {
+		term = '\0';
+		delim = '\n';
+		key_delim = '\n';
+	}
+
+	if (HAS_MULTI_BITS(types)) {
+		error("only one type at a time.");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+
+	if (get_color_slot)
+	    actions |= ACTION_GET_COLOR;
+	if (get_colorbool_slot)
+	    actions |= ACTION_GET_COLORBOOL;
+
+	if ((get_color_slot || get_colorbool_slot) && types) {
+		error("--get-color and variable type are incoherent");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+
+	if (HAS_MULTI_BITS(actions)) {
+		error("only one action at a time.");
+		usage_with_options(builtin_config_usage, builtin_config_options);
+	}
+	if (actions == 0)
+		switch (argc) {
+		case 1: actions = ACTION_GET; break;
+		case 2: actions = ACTION_SET; break;
+		case 3: actions = ACTION_SET_ALL; break;
+		default:
+			usage_with_options(builtin_config_usage, builtin_config_options);
+		}
+
+	if (actions == ACTION_LIST) {
+		check_argc(argc, 0, 0);
+		if (git_config(show_all_config, NULL) < 0) {
+			if (config_exclusive_filename)
+				die("unable to read config file %s: %s",
+				    config_exclusive_filename, strerror(errno));
+			else
+				die("error processing config file(s)");
+		}
+	}
+	else if (actions == ACTION_EDIT) {
+		check_argc(argc, 0, 0);
+		git_config(git_default_config, NULL);
+		launch_editor(config_exclusive_filename ?
+			      config_exclusive_filename : git_path("config"),
+			      NULL, NULL);
+	}
+	else if (actions == ACTION_SET) {
+		check_argc(argc, 2, 2);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set(argv[0], value);
+	}
+	else if (actions == ACTION_SET_ALL) {
+		check_argc(argc, 2, 3);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set_multivar(argv[0], value, argv[2], 0);
+	}
+	else if (actions == ACTION_ADD) {
+		check_argc(argc, 2, 2);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set_multivar(argv[0], value, "^$", 0);
+	}
+	else if (actions == ACTION_REPLACE_ALL) {
+		check_argc(argc, 2, 3);
+		value = normalize_value(argv[0], argv[1]);
+		return git_config_set_multivar(argv[0], value, argv[2], 1);
+	}
+	else if (actions == ACTION_GET) {
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_ALL) {
+		do_all = 1;
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_GET_REGEXP) {
+		show_keys = 1;
+		use_key_regexp = 1;
+		do_all = 1;
+		check_argc(argc, 1, 2);
+		return get_value(argv[0], argv[1]);
+	}
+	else if (actions == ACTION_UNSET) {
+		check_argc(argc, 1, 2);
+		if (argc == 2)
+			return git_config_set_multivar(argv[0], NULL, argv[1], 0);
+		else
+			return git_config_set(argv[0], NULL);
+	}
+	else if (actions == ACTION_UNSET_ALL) {
+		check_argc(argc, 1, 2);
+		return git_config_set_multivar(argv[0], NULL, argv[1], 1);
+	}
+	else if (actions == ACTION_RENAME_SECTION) {
+		int ret;
+		check_argc(argc, 2, 2);
+		ret = git_config_rename_section(argv[0], argv[1]);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			die("No such section!");
+	}
+	else if (actions == ACTION_REMOVE_SECTION) {
+		int ret;
+		check_argc(argc, 1, 1);
+		ret = git_config_rename_section(argv[0], NULL);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			die("No such section!");
+	}
+	else if (actions == ACTION_GET_COLOR) {
+		get_color(argv[0]);
+	}
+	else if (actions == ACTION_GET_COLORBOOL) {
+		if (argc == 1)
+			stdout_is_tty = git_config_bool("command line", argv[0]);
+		else if (argc == 0)
+			stdout_is_tty = isatty(1);
+		return get_colorbool(argc != 0);
+	}
+
 	return 0;
 }
diff --git a/builtin-count-objects.c b/builtin-count-objects.c
index 91b5487..b814fe5 100644
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
@@ -5,6 +5,7 @@
  */
 
 #include "cache.h"
+#include "dir.h"
 #include "builtin.h"
 #include "parse-options.h"
 
@@ -21,9 +22,7 @@
 		const char *cp;
 		int bad = 0;
 
-		if ((ent->d_name[0] == '.') &&
-		    (ent->d_name[1] == 0 ||
-		     ((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
+		if (is_dot_or_dotdot(ent->d_name))
 			continue;
 		for (cp = ent->d_name; *cp; cp++) {
 			int ch = *cp;
@@ -43,7 +42,7 @@
 			if (lstat(path, &st) || !S_ISREG(st.st_mode))
 				bad = 1;
 			else
-				(*loose_size) += xsize_t(st.st_blocks);
+				(*loose_size) += xsize_t(on_disk_bytes(st));
 		}
 		if (bad) {
 			if (verbose) {
@@ -61,7 +60,7 @@
 		hex[40] = 0;
 		if (get_sha1_hex(hex, sha1))
 			die("internal error");
-		if (has_sha1_pack(sha1, NULL))
+		if (has_sha1_pack(sha1))
 			(*packed_loose)++;
 	}
 }
@@ -104,6 +103,7 @@
 	if (verbose) {
 		struct packed_git *p;
 		unsigned long num_pack = 0;
+		unsigned long size_pack = 0;
 		if (!packed_git)
 			prepare_packed_git();
 		for (p = packed_git; p; p = p->next) {
@@ -112,17 +112,19 @@
 			if (open_pack_index(p))
 				continue;
 			packed += p->num_objects;
+			size_pack += p->pack_size + p->index_size;
 			num_pack++;
 		}
 		printf("count: %lu\n", loose);
-		printf("size: %lu\n", loose_size / 2);
+		printf("size: %lu\n", loose_size / 1024);
 		printf("in-pack: %lu\n", packed);
 		printf("packs: %lu\n", num_pack);
+		printf("size-pack: %lu\n", size_pack / 1024);
 		printf("prune-packable: %lu\n", packed_loose);
 		printf("garbage: %lu\n", garbage);
 	}
 	else
 		printf("%lu objects, %lu kilobytes\n",
-		       loose, loose_size / 2);
+		       loose, loose_size / 1024);
 	return 0;
 }
diff --git a/builtin-describe.c b/builtin-describe.c
index ec404c8..3a007ed 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -15,8 +15,8 @@
 };
 
 static int debug;	/* Display lots of verbose info */
-static int all;	/* Default to annotated tags only */
-static int tags;	/* But allow any tags if --tags is specified */
+static int all;	/* Any valid ref can be used */
+static int tags;	/* Allow lightweight tags */
 static int longformat;
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
@@ -112,8 +112,6 @@
 {
 	struct possible_tag *a = (struct possible_tag *)a_;
 	struct possible_tag *b = (struct possible_tag *)b_;
-	if (a->name->prio != b->name->prio)
-		return b->name->prio - a->name->prio;
 	if (a->depth != b->depth)
 		return a->depth - b->depth;
 	if (a->found_order != b->found_order)
@@ -160,7 +158,7 @@
 		n->tag = lookup_tag(n->sha1);
 		if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
 			die("annotated tag %s not available", n->path);
-		if (strcmp(n->tag->tag, n->path))
+		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
 			warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
 	}
 
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index 2b578c7..5b64011 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -59,8 +59,8 @@
 	    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
 		rev.combine_merges = rev.dense_combined_merges = 1;
 
-	if (read_cache() < 0) {
-		perror("read_cache");
+	if (read_cache_preload(rev.diffopt.paths) < 0) {
+		perror("read_cache_preload");
 		return -1;
 	}
 	result = run_diff_files(&rev, options);
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index 415cb16..79cedb7 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -14,20 +14,10 @@
 	return log_tree_commit(&log_tree_opt, commit);
 }
 
-static int diff_tree_stdin(char *line)
+/* Diff one or more commits. */
+static int stdin_diff_commit(struct commit *commit, char *line, int len)
 {
-	int len = strlen(line);
 	unsigned char sha1[20];
-	struct commit *commit;
-
-	if (!len || line[len-1] != '\n')
-		return -1;
-	line[len-1] = 0;
-	if (get_sha1_hex(line, sha1))
-		return -1;
-	commit = lookup_commit(sha1);
-	if (!commit || parse_commit(commit))
-		return -1;
 	if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
 		/* Graft the fake parents locally to the commit */
 		int pos = 41;
@@ -52,6 +42,49 @@
 	return log_tree_commit(&log_tree_opt, commit);
 }
 
+/* Diff two trees. */
+static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+{
+	unsigned char sha1[20];
+	struct tree *tree2;
+	if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+		return error("Need exactly two trees, separated by a space");
+	tree2 = lookup_tree(sha1);
+	if (!tree2 || parse_tree(tree2))
+		return -1;
+	printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
+			  sha1_to_hex(tree2->object.sha1));
+	diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
+		       "", &log_tree_opt.diffopt);
+	log_tree_diff_flush(&log_tree_opt);
+	return 0;
+}
+
+static int diff_tree_stdin(char *line)
+{
+	int len = strlen(line);
+	unsigned char sha1[20];
+	struct object *obj;
+
+	if (!len || line[len-1] != '\n')
+		return -1;
+	line[len-1] = 0;
+	if (get_sha1_hex(line, sha1))
+		return -1;
+	obj = lookup_unknown_object(sha1);
+	if (!obj || !obj->parsed)
+		obj = parse_object(sha1);
+	if (!obj)
+		return -1;
+	if (obj->type == OBJ_COMMIT)
+		return stdin_diff_commit((struct commit *)obj, line, len);
+	if (obj->type == OBJ_TREE)
+		return stdin_diff_trees((struct tree *)obj, line, len);
+	error("Object %s is a %s, not a commit or tree",
+	      sha1_to_hex(sha1), typename(obj->type));
+	return -1;
+}
+
 static const char diff_tree_usage[] =
 "git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
 "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
@@ -69,7 +102,6 @@
 
 	init_revisions(opt, prefix);
 	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
-	nr_sha1 = 0;
 	opt->abbrev = 0;
 	opt->diff = 1;
 	argc = setup_revisions(argc, argv, opt, NULL);
diff --git a/builtin-diff.c b/builtin-diff.c
index 375a0d3..d75d69b 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -74,6 +74,8 @@
 	if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
 		die("'%s': not a regular file or symlink", path);
 
+	diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
+
 	if (blob[0].mode == S_IFINVALID)
 		blob[0].mode = canon_mode(st.st_mode);
 
@@ -116,7 +118,7 @@
 	int cached = 0;
 	while (1 < argc) {
 		const char *arg = argv[1];
-		if (!strcmp(arg, "--cached"))
+		if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
 			cached = 1;
 		else
 			usage(builtin_diff_usage);
@@ -132,8 +134,8 @@
 	    revs->max_count != -1 || revs->min_age != -1 ||
 	    revs->max_age != -1)
 		usage(builtin_diff_usage);
-	if (read_cache() < 0) {
-		perror("read_cache");
+	if (read_cache_preload(revs->diffopt.paths) < 0) {
+		perror("read_cache_preload");
 		return -1;
 	}
 	return run_diff_index(revs, cached);
@@ -232,8 +234,8 @@
 		revs->combine_merges = revs->dense_combined_merges = 1;
 
 	setup_work_tree();
-	if (read_cache() < 0) {
-		perror("read_cache");
+	if (read_cache_preload(revs->diffopt.paths) < 0) {
+		perror("read_cache_preload");
 		return -1;
 	}
 	result = run_diff_files(revs, options);
@@ -288,8 +290,9 @@
 	/* Otherwise, we are doing the usual "git" diff */
 	rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
 
-	/* Default to let external be used */
+	/* Default to let external and textconv be used */
 	DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+	DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
 
 	if (nongit)
 		die("Not a git repository");
@@ -320,7 +323,8 @@
 			const char *arg = argv[i];
 			if (!strcmp(arg, "--"))
 				break;
-			else if (!strcmp(arg, "--cached")) {
+			else if (!strcmp(arg, "--cached") ||
+				 !strcmp(arg, "--staged")) {
 				add_head_to_pending(&rev);
 				if (!rev.pending.nr)
 					die("No HEAD commit to compare with (yet)");
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index cdb7df5..6731713 100644
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -221,7 +221,8 @@
 	if (message)
 		message += 2;
 
-	if (commit->parents) {
+	if (commit->parents &&
+	    get_object_mark(&commit->parents->item->object) != 0) {
 		parse_commit(commit->parents->item);
 		diff_tree_sha1(commit->parents->item->tree->object.sha1,
 			       commit->tree->object.sha1, "", &rev->diffopt);
@@ -362,7 +363,10 @@
 			break;
 		case OBJ_TAG:
 			tag = (struct tag *)e->item;
+
+			/* handle nested tags */
 			while (tag && tag->object.type == OBJ_TAG) {
+				parse_object(tag->object.sha1);
 				string_list_append(full_name, extra_refs)->util = tag;
 				tag = (struct tag *)tag->tagged;
 			}
@@ -375,11 +379,17 @@
 			case OBJ_BLOB:
 				handle_object(tag->object.sha1);
 				continue;
+			default: /* OBJ_TAG (nested tags) is already handled */
+				warning("Tag points to object of unexpected type %s, skipping.",
+					typename(tag->object.type));
+				continue;
 			}
 			break;
 		default:
-			die ("Unexpected object of type %s",
-			     typename(e->item->type));
+			warning("%s: Unexpected object of type %s, skipping.",
+				e->name,
+				typename(e->item->type));
+			continue;
 		}
 		if (commit->util)
 			/* more than one name for the same object */
@@ -497,6 +507,9 @@
 		OPT_END()
 	};
 
+	if (argc == 1)
+		usage_with_options (fast_export_usage, options);
+
 	/* we handle encodings */
 	git_config(git_default_config, NULL);
 
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index 7460ab7..29356d2 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -2,11 +2,11 @@
 #include "cache.h"
 #include "refs.h"
 #include "commit.h"
+#include "sigchain.h"
 
 static char *get_stdin(void)
 {
-	struct strbuf buf;
-	strbuf_init(&buf, 0);
+	struct strbuf buf = STRBUF_INIT;
 	if (strbuf_read(&buf, 0, 1024) < 0) {
 		die("error reading standard input: %s", strerror(errno));
 	}
@@ -187,7 +187,7 @@
 static void remove_keep_on_signal(int signo)
 {
 	remove_keep();
-	signal(SIGINT, SIG_DFL);
+	sigchain_pop(signo);
 	raise(signo);
 }
 
@@ -246,7 +246,7 @@
 	char buffer[1024];
 	int err = 0;
 
-	signal(SIGINT, remove_keep_on_signal);
+	sigchain_push_common(remove_keep_on_signal);
 	atexit(remove_keep);
 
 	while (fgets(buffer, sizeof(buffer), stdin)) {
diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 22a5712..5d134be 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -216,9 +216,8 @@
 	if (args.depth > 0) {
 		char line[1024];
 		unsigned char sha1[20];
-		int len;
 
-		while ((len = packet_read_line(fd[0], line, sizeof(line)))) {
+		while (packet_read_line(fd[0], line, sizeof(line))) {
 			if (!prefixcmp(line, "shallow ")) {
 				if (get_sha1_hex(line + 8, sha1))
 					die("invalid shallow line: %s", line);
@@ -483,7 +482,7 @@
 {
 	int *xd = data;
 
-	return recv_sideband("fetch-pack", xd[0], fd, 2);
+	return recv_sideband("fetch-pack", xd[0], fd);
 }
 
 static int get_pack(int xd[2], char **pack_lockfile)
@@ -540,7 +539,7 @@
 			*av++ = "--fix-thin";
 		if (args.lock_pack || unpack_limit) {
 			int s = sprintf(keep_arg,
-					"--keep=fetch-pack %d on ", getpid());
+					"--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
 			if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
 				strcpy(keep_arg + s, "localhost");
 			*av++ = keep_arg;
@@ -606,7 +605,7 @@
 			/* When cloning, it is not unusual to have
 			 * no common commit.
 			 */
-			fprintf(stderr, "warning: no common commits\n");
+			warning("no common commits");
 
 	if (get_pack(fd, pack_lockfile))
 		die("git fetch-pack: fetch failed.");
@@ -735,7 +734,7 @@
 	conn = git_connect(fd, (char *)dest, args.uploadpack,
 			   args.verbose ? CONNECT_VERBOSE : 0);
 	if (conn) {
-		get_remote_heads(fd[0], &ref, 0, NULL, 0);
+		get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
 
 		ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
 		close(fd[0]);
@@ -801,15 +800,13 @@
 		int fd;
 
 		mtime.sec = st.st_mtime;
-#ifdef USE_NSEC
-		mtime.usec = st.st_mtim.usec;
-#endif
+		mtime.nsec = ST_MTIME_NSEC(st);
 		if (stat(shallow, &st)) {
 			if (mtime.sec)
 				die("shallow file was removed during fetch");
 		} else if (st.st_mtime != mtime.sec
 #ifdef USE_NSEC
-				|| st.st_mtim.usec != mtime.usec
+				|| ST_MTIME_NSEC(st) != mtime.nsec
 #endif
 			  )
 			die("shallow file was changed during fetch");
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 57c161d..3c998ea 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -10,6 +10,7 @@
 #include "transport.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "sigchain.h"
 
 static const char * const builtin_fetch_usage[] = {
 	"git fetch [options] [<repository> <refspec>...]",
@@ -22,7 +23,7 @@
 	TAGS_SET = 2
 };
 
-static int append, force, keep, update_head_ok, verbose, quiet;
+static int append, force, keep, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -30,8 +31,7 @@
 static struct transport *transport;
 
 static struct option builtin_fetch_options[] = {
-	OPT__QUIET(&quiet),
-	OPT__VERBOSE(&verbose),
+	OPT__VERBOSITY(&verbosity),
 	OPT_BOOLEAN('a', "append", &append,
 		    "append to .git/FETCH_HEAD instead of overwriting"),
 	OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
@@ -59,7 +59,7 @@
 static void unlock_pack_on_signal(int signo)
 {
 	unlock_pack();
-	signal(SIGINT, SIG_DFL);
+	sigchain_pop(signo);
 	raise(signo);
 }
 
@@ -192,17 +192,12 @@
 
 static int update_local_ref(struct ref *ref,
 			    const char *remote,
-			    int verbose,
 			    char *display)
 {
 	struct commit *current = NULL, *updated;
 	enum object_type type;
 	struct branch *current_branch = branch_get(NULL);
-	const char *pretty_ref = ref->name + (
-		!prefixcmp(ref->name, "refs/heads/") ? 11 :
-		!prefixcmp(ref->name, "refs/tags/") ? 10 :
-		!prefixcmp(ref->name, "refs/remotes/") ? 13 :
-		0);
+	const char *pretty_ref = prettify_ref(ref);
 
 	*display = 0;
 	type = sha1_object_info(ref->new_sha1, NULL);
@@ -210,7 +205,7 @@
 		die("object %s not found", sha1_to_hex(ref->new_sha1));
 
 	if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
-		if (verbose)
+		if (verbosity > 0)
 			sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
 				"[up to date]", REFCOL_WIDTH, remote,
 				pretty_ref);
@@ -366,18 +361,19 @@
 			note);
 
 		if (ref)
-			rc |= update_local_ref(ref, what, verbose, note);
+			rc |= update_local_ref(ref, what, note);
 		else
 			sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
 				SUMMARY_WIDTH, *kind ? kind : "branch",
 				 REFCOL_WIDTH, *what ? what : "HEAD");
 		if (*note) {
-			if (!shown_url) {
+			if (verbosity >= 0 && !shown_url) {
 				fprintf(stderr, "From %.*s\n",
 						url_len, url);
 				shown_url = 1;
 			}
-			fprintf(stderr, " %s\n", note);
+			if (verbosity >= 0)
+				fprintf(stderr, " %s\n", note);
 		}
 	}
 	fclose(fp);
@@ -521,8 +517,8 @@
 		     will_fetch(head, ref->old_sha1))) {
 			string_list_insert(ref_name, &new_refs);
 
-			rm = alloc_ref_from_str(ref_name);
-			rm->peer_ref = alloc_ref_from_str(ref_name);
+			rm = alloc_ref(ref_name);
+			rm->peer_ref = alloc_ref(ref_name);
 			hashcpy(rm->old_sha1, ref_sha1);
 
 			**tail = rm;
@@ -544,7 +540,8 @@
 	for (; ref_map; ref_map = ref_map->next)
 		if (ref_map->peer_ref && !strcmp(current_branch->refname,
 					ref_map->peer_ref->name))
-			die("Refusing to fetch into current branch");
+			die("Refusing to fetch into current branch %s "
+			    "of non-bare repository", current_branch->refname);
 }
 
 static int do_fetch(struct transport *transport,
@@ -608,7 +605,7 @@
 {
 	int r = transport_set_option(transport, name, value);
 	if (r < 0)
-		die("Option \"%s\" value \"%s\" is not valid for %s\n",
+		die("Option \"%s\" value \"%s\" is not valid for %s",
 			name, value, transport->url);
 	if (r > 0)
 		warning("Option \"%s\" is ignored for %s\n",
@@ -636,10 +633,13 @@
 	else
 		remote = remote_get(argv[0]);
 
+	if (!remote)
+		die("Where do you want to fetch from today?");
+
 	transport = transport_get(remote, remote->url[0]);
-	if (verbose >= 2)
+	if (verbosity >= 2)
 		transport->verbose = 1;
-	if (quiet)
+	if (verbosity < 0)
 		transport->verbose = -1;
 	if (upload_pack)
 		set_option(TRANS_OPT_UPLOADPACK, upload_pack);
@@ -648,9 +648,6 @@
 	if (depth)
 		set_option(TRANS_OPT_DEPTH, depth);
 
-	if (!transport->url)
-		die("Where do you want to fetch from today?");
-
 	if (argc > 1) {
 		int j = 0;
 		refs = xcalloc(argc + 1, sizeof(const char *));
@@ -673,7 +670,7 @@
 		ref_nr = j;
 	}
 
-	signal(SIGINT, unlock_pack_on_signal);
+	sigchain_push_common(unlock_pack_on_signal);
 	atexit(unlock_pack);
 	exit_code = do_fetch(transport,
 			parse_fetch_refspec(ref_nr, refs), ref_nr);
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index df02ba7..a788369 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -5,8 +5,10 @@
 #include "revision.h"
 #include "tag.h"
 
-static const char *fmt_merge_msg_usage =
-	"git fmt-merge-msg [--log] [--no-log] [--file <file>]";
+static const char * const fmt_merge_msg_usage[] = {
+	"git fmt-merge-msg [--log|--no-log] [--file <file>]",
+	NULL
+};
 
 static int merge_summary;
 
@@ -254,8 +256,7 @@
 
 int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
 	int limit = 20, i = 0, pos = 0;
-	char line[1024];
-	char *p = line, *sep = "";
+	char *sep = "";
 	unsigned char head_sha1[20];
 	const char *current_branch;
 
@@ -269,9 +270,8 @@
 	/* get a line */
 	while (pos < in->len) {
 		int len;
-		char *newline;
+		char *newline, *p = in->buf + pos;
 
-		p = in->buf + pos;
 		newline = strchr(p, '\n');
 		len = newline ? newline - p : strlen(p);
 		pos += len + !!newline;
@@ -347,46 +347,35 @@
 
 int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 {
+	const char *inpath = NULL;
+	struct option options[] = {
+		OPT_BOOLEAN(0, "log",     &merge_summary, "populate log with the shortlog"),
+		OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
+		OPT_STRING('F', "file",   &inpath, "file", "file to read from"),
+		OPT_END()
+	};
+
 	FILE *in = stdin;
-	struct strbuf input, output;
+	struct strbuf input = STRBUF_INIT, output = STRBUF_INIT;
 	int ret;
 
 	git_config(fmt_merge_msg_config, NULL);
+	argc = parse_options(argc, argv, options, fmt_merge_msg_usage, 0);
+	if (argc > 0)
+		usage_with_options(fmt_merge_msg_usage, options);
 
-	while (argc > 1) {
-		if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary"))
-			merge_summary = 1;
-		else if (!strcmp(argv[1], "--no-log")
-				|| !strcmp(argv[1], "--no-summary"))
-			merge_summary = 0;
-		else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
-			if (argc < 3)
-				die ("Which file?");
-			if (!strcmp(argv[2], "-"))
-				in = stdin;
-			else {
-				fclose(in);
-				in = fopen(argv[2], "r");
-				if (!in)
-					die("cannot open %s", argv[2]);
-			}
-			argc--; argv++;
-		} else
-			break;
-		argc--; argv++;
+	if (inpath && strcmp(inpath, "-")) {
+		in = fopen(inpath, "r");
+		if (!in)
+			die("cannot open %s", inpath);
 	}
 
-	if (argc > 1)
-		usage(fmt_merge_msg_usage);
-
-	strbuf_init(&input, 0);
 	if (strbuf_read(&input, fileno(in), 0) < 0)
 		die("could not read input file %s", strerror(errno));
-	strbuf_init(&output, 0);
 
 	ret = fmt_merge_msg(merge_summary, &input, &output);
 	if (ret)
 		return ret;
-	printf("%s", output.buf);
+	write_in_full(STDOUT_FILENO, output.buf, output.len);
 	return 0;
 }
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index 72c0878..91e8f95 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -8,6 +8,7 @@
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
+#include "remote.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -66,6 +67,7 @@
 	{ "subject" },
 	{ "body" },
 	{ "contents" },
+	{ "upstream" },
 };
 
 /*
@@ -568,12 +570,50 @@
 	for (i = 0; i < used_atom_cnt; i++) {
 		const char *name = used_atom[i];
 		struct atom_value *v = &ref->value[i];
-		if (!strcmp(name, "refname"))
-			v->s = ref->refname;
-		else if (!strcmp(name, "*refname")) {
-			int len = strlen(ref->refname);
+		int deref = 0;
+		const char *refname;
+		const char *formatp;
+
+		if (*name == '*') {
+			deref = 1;
+			name++;
+		}
+
+		if (!prefixcmp(name, "refname"))
+			refname = ref->refname;
+		else if(!prefixcmp(name, "upstream")) {
+			struct branch *branch;
+			/* only local branches may have an upstream */
+			if (prefixcmp(ref->refname, "refs/heads/"))
+				continue;
+			branch = branch_get(ref->refname + 11);
+
+			if (!branch || !branch->merge || !branch->merge[0] ||
+			    !branch->merge[0]->dst)
+				continue;
+			refname = branch->merge[0]->dst;
+		}
+		else
+			continue;
+
+		formatp = strchr(name, ':');
+		/* look for "short" refname format */
+		if (formatp) {
+			formatp++;
+			if (!strcmp(formatp, "short"))
+				refname = shorten_unambiguous_ref(refname,
+						      warn_ambiguous_refs);
+			else
+				die("unknown %.*s format %s",
+				    (int)(formatp - name), name, formatp);
+		}
+
+		if (!deref)
+			v->s = refname;
+		else {
+			int len = strlen(refname);
 			char *s = xmalloc(len + 4);
-			sprintf(s, "%s^{}", ref->refname);
+			sprintf(s, "%s^{}", refname);
 			v->s = s;
 		}
 	}
@@ -820,7 +860,6 @@
 		return -1;
 
 	*sort_tail = s = xcalloc(1, sizeof(*s));
-	sort_tail = &s->next;
 
 	if (*arg == '-') {
 		s->reverse = 1;
@@ -879,6 +918,9 @@
 		sort = default_sort();
 	sort_atom_limit = used_atom_cnt;
 
+	/* for warn_ambiguous_refs */
+	git_config(git_default_config, NULL);
+
 	memset(&cbdata, 0, sizeof(cbdata));
 	cbdata.grab_pattern = argv;
 	for_each_ref(grab_single_ref, &cbdata);
diff --git a/builtin-fsck.c b/builtin-fsck.c
index aa4b239..6436bc2 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -10,6 +10,7 @@
 #include "tree-walk.h"
 #include "fsck.h"
 #include "parse-options.h"
+#include "dir.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -22,6 +23,7 @@
 static int check_strict;
 static int keep_cache_objects;
 static unsigned char head_sha1[20];
+static const char *head_points_at;
 static int errors_found;
 static int write_lost_and_found;
 static int verbose;
@@ -158,7 +160,7 @@
 	 * do a full fsck
 	 */
 	if (!obj->parsed) {
-		if (has_sha1_pack(obj->sha1, NULL))
+		if (has_sha1_pack(obj->sha1))
 			return; /* it is in pack - forget about it */
 		printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
 		errors_found |= ERROR_REACHABLE;
@@ -222,12 +224,16 @@
 				char *buf = read_sha1_file(obj->sha1,
 						&type, &size);
 				if (buf) {
-					fwrite(buf, size, 1, f);
+					if (fwrite(buf, size, 1, f) != 1)
+						die("Could not write %s: %s",
+						    filename, strerror(errno));
 					free(buf);
 				}
 			} else
 				fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
-			fclose(f);
+			if (fclose(f))
+				die("Could not finish %s: %s",
+				    filename, strerror(errno));
 		}
 		return;
 	}
@@ -391,19 +397,12 @@
 	while ((de = readdir(dir)) != NULL) {
 		char name[100];
 		unsigned char sha1[20];
-		int len = strlen(de->d_name);
 
-		switch (len) {
-		case 2:
-			if (de->d_name[1] != '.')
-				break;
-		case 1:
-			if (de->d_name[0] != '.')
-				break;
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
-		case 38:
+		if (strlen(de->d_name) == 38) {
 			sprintf(name, "%02x", i);
-			memcpy(name+2, de->d_name, len+1);
+			memcpy(name+2, de->d_name, 39);
 			if (get_sha1_hex(name, sha1) < 0)
 				break;
 			add_sha1_list(sha1, DIRENT_SORT_HINT(de));
@@ -475,6 +474,8 @@
 
 static void get_default_heads(void)
 {
+	if (head_points_at && !is_null_sha1(head_sha1))
+		fsck_handle_ref("HEAD", head_sha1, 0, NULL);
 	for_each_ref(fsck_handle_ref, NULL);
 	if (include_reflogs)
 		for_each_reflog(fsck_handle_reflog, NULL);
@@ -514,14 +515,13 @@
 
 static int fsck_head_link(void)
 {
-	unsigned char sha1[20];
 	int flag;
 	int null_is_error = 0;
-	const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
 
 	if (verbose)
 		fprintf(stderr, "Checking HEAD link\n");
 
+	head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
 	if (!head_points_at)
 		return error("Invalid HEAD");
 	if (!strcmp(head_points_at, "HEAD"))
@@ -530,7 +530,7 @@
 	else if (prefixcmp(head_points_at, "refs/heads/"))
 		return error("HEAD points to something strange (%s)",
 			     head_points_at);
-	if (is_null_sha1(sha1)) {
+	if (is_null_sha1(head_sha1)) {
 		if (null_is_error)
 			return error("HEAD: detached HEAD points at nothing");
 		fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
@@ -586,6 +586,7 @@
 int cmd_fsck(int argc, const char **argv, const char *prefix)
 {
 	int i, heads;
+	struct alternate_object_database *alt;
 
 	errors_found = 0;
 
@@ -597,17 +598,19 @@
 
 	fsck_head_link();
 	fsck_object_dir(get_object_directory());
+
+	prepare_alt_odb();
+	for (alt = alt_odb_list; alt; alt = alt->next) {
+		char namebuf[PATH_MAX];
+		int namelen = alt->name - alt->base;
+		memcpy(namebuf, alt->base, namelen);
+		namebuf[namelen - 1] = 0;
+		fsck_object_dir(namebuf);
+	}
+
 	if (check_full) {
-		struct alternate_object_database *alt;
 		struct packed_git *p;
-		prepare_alt_odb();
-		for (alt = alt_odb_list; alt; alt = alt->next) {
-			char namebuf[PATH_MAX];
-			int namelen = alt->name - alt->base;
-			memcpy(namebuf, alt->base, namelen);
-			namebuf[namelen - 1] = 0;
-			fsck_object_dir(namebuf);
-		}
+
 		prepare_packed_git();
 		for (p = packed_git; p; p = p->next)
 			/* verify gives error messages itself */
@@ -626,8 +629,9 @@
 	heads = 0;
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
-		if (!get_sha1(arg, head_sha1)) {
-			struct object *obj = lookup_object(head_sha1);
+		unsigned char sha1[20];
+		if (!get_sha1(arg, sha1)) {
+			struct object *obj = lookup_object(sha1);
 
 			/* Error is printed by lookup_object(). */
 			if (!obj)
diff --git a/builtin-gc.c b/builtin-gc.c
index 53a0d43..fc556ed 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -23,10 +23,10 @@
 };
 
 static int pack_refs = 1;
-static int aggressive_window = -1;
+static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
-static char *prune_expire = "2.weeks.ago";
+static const char *prune_expire = "2.weeks.ago";
 
 #define MAX_ADD 10
 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
@@ -57,15 +57,12 @@
 		return 0;
 	}
 	if (!strcmp(var, "gc.pruneexpire")) {
-		if (!value)
-			return config_error_nonbool(var);
-		if (strcmp(value, "now")) {
+		if (value && strcmp(value, "now")) {
 			unsigned long now = approxidate("now");
 			if (approxidate(value) >= now)
 				return error("Invalid %s: '%s'", var, value);
 		}
-		prune_expire = xstrdup(value);
-		return 0;
+		return git_config_string(&prune_expire, var, value);
 	}
 	return git_default_config(var, value, cb);
 }
@@ -147,34 +144,6 @@
 	return gc_auto_pack_limit <= cnt;
 }
 
-static int run_hook(void)
-{
-	const char *argv[2];
-	struct child_process hook;
-	int ret;
-
-	argv[0] = git_path("hooks/pre-auto-gc");
-	argv[1] = NULL;
-
-	if (access(argv[0], X_OK) < 0)
-		return 0;
-
-	memset(&hook, 0, sizeof(hook));
-	hook.argv = argv;
-	hook.no_stdin = 1;
-	hook.stdout_to_stderr = 1;
-
-	ret = start_command(&hook);
-	if (ret) {
-		warning("Could not spawn %s", argv[0]);
-		return ret;
-	}
-	ret = finish_command(&hook);
-	if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
-		warning("%s exited due to uncaught signal", argv[0]);
-	return ret;
-}
-
 static int need_to_gc(void)
 {
 	/*
@@ -191,25 +160,29 @@
 	 * there is no need.
 	 */
 	if (too_many_packs())
-		append_option(argv_repack, "-A", MAX_ADD);
+		append_option(argv_repack,
+			      prune_expire && !strcmp(prune_expire, "now") ?
+			      "-a" : "-A",
+			      MAX_ADD);
 	else if (!too_many_loose_objects())
 		return 0;
 
-	if (run_hook())
+	if (run_hook(NULL, "pre-auto-gc", NULL))
 		return 0;
 	return 1;
 }
 
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
-	int prune = 0;
 	int aggressive = 0;
 	int auto_gc = 0;
 	int quiet = 0;
 	char buf[80];
 
 	struct option builtin_gc_options[] = {
-		OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
+		{ OPTION_STRING, 0, "prune", &prune_expire, "date",
+			"prune unreferenced objects",
+			PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
 		OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
 		OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
 		OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
@@ -227,6 +200,7 @@
 
 	if (aggressive) {
 		append_option(argv_repack, "-f", MAX_ADD);
+		append_option(argv_repack, "--depth=250", MAX_ADD);
 		if (aggressive_window > 0) {
 			sprintf(buf, "--window=%d", aggressive_window);
 			append_option(argv_repack, buf, MAX_ADD);
@@ -246,7 +220,10 @@
 			"run \"git gc\" manually. See "
 			"\"git help gc\" for more information.\n");
 	} else
-		append_option(argv_repack, "-A", MAX_ADD);
+		append_option(argv_repack,
+			      prune_expire && !strcmp(prune_expire, "now")
+			      ? "-a" : "-A",
+			      MAX_ADD);
 
 	if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
 		return error(FAILED_RUN, argv_pack_refs[0]);
@@ -257,9 +234,11 @@
 	if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
 		return error(FAILED_RUN, argv_repack[0]);
 
-	argv_prune[2] = prune_expire;
-	if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
-		return error(FAILED_RUN, argv_prune[0]);
+	if (prune_expire) {
+		argv_prune[2] = prune_expire;
+		if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
+			return error(FAILED_RUN, argv_prune[0]);
+	}
 
 	if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
 		return error(FAILED_RUN, argv_rerere[0]);
diff --git a/builtin-grep.c b/builtin-grep.c
index d3cc75e..89489dd 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -20,6 +20,30 @@
 #endif
 #endif
 
+static int builtin_grep;
+
+static int grep_config(const char *var, const char *value, void *cb)
+{
+	struct grep_opt *opt = cb;
+
+	if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) {
+		opt->color = git_config_colorbool(var, value, -1);
+		return 0;
+	}
+	if (!strcmp(var, "grep.color.external") ||
+	    !strcmp(var, "color.grep.external")) {
+		return git_config_string(&(opt->color_external), var, value);
+	}
+	if (!strcmp(var, "grep.color.match") ||
+	    !strcmp(var, "color.grep.match")) {
+		if (!value)
+			return config_error_nonbool(var);
+		color_parse(value, var, opt->color_match);
+		return 0;
+	}
+	return git_color_default_config(var, value, cb);
+}
+
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
  * pathname wildcards are allowed.
@@ -267,6 +291,21 @@
 	return status;
 }
 
+static void grep_add_color(struct strbuf *sb, const char *escape_seq)
+{
+	size_t orig_len = sb->len;
+
+	while (*escape_seq) {
+		if (*escape_seq == 'm')
+			strbuf_addch(sb, ';');
+		else if (*escape_seq != '\033' && *escape_seq  != '[')
+			strbuf_addch(sb, *escape_seq);
+		escape_seq++;
+	}
+	if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
+		strbuf_setlen(sb, sb->len - 1);
+}
+
 static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 {
 	int i, nr, argc, hit, len, status;
@@ -297,6 +336,9 @@
 		push_arg("-l");
 	if (opt->unmatch_name_only)
 		push_arg("-L");
+	if (opt->null_following_name)
+		/* in GNU grep git's "-z" translates to "-Z" */
+		push_arg("-Z");
 	if (opt->count)
 		push_arg("-c");
 	if (opt->post_context || opt->pre_context) {
@@ -334,6 +376,23 @@
 		push_arg("-e");
 		push_arg(p->pattern);
 	}
+	if (opt->color) {
+		struct strbuf sb = STRBUF_INIT;
+
+		grep_add_color(&sb, opt->color_match);
+		setenv("GREP_COLOR", sb.buf, 1);
+
+		strbuf_reset(&sb);
+		strbuf_addstr(&sb, "mt=");
+		grep_add_color(&sb, opt->color_match);
+		strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
+		setenv("GREP_COLORS", sb.buf, 1);
+
+		strbuf_release(&sb);
+
+		if (opt->color_external && strlen(opt->color_external) > 0)
+			push_arg(opt->color_external);
+	}
 
 	hit = 0;
 	argc = nr;
@@ -388,7 +447,7 @@
 	 * we grep through the checked-out files. It tends to
 	 * be a lot more optimized
 	 */
-	if (!cached) {
+	if (!cached && !builtin_grep) {
 		hit = external_grep(opt, paths, cached);
 		if (hit >= 0)
 			return hit;
@@ -401,7 +460,12 @@
 			continue;
 		if (!pathspec_matches(paths, ce->name))
 			continue;
-		if (cached) {
+		/*
+		 * If CE_VALID is on, we assume worktree file and its cache entry
+		 * are identical, even if worktree file has been modified, so use
+		 * cache version instead
+		 */
+		if (cached || (ce->ce_flags & CE_VALID)) {
 			if (ce_stage(ce))
 				continue;
 			hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
@@ -526,6 +590,12 @@
 	opt.pattern_tail = &opt.pattern_list;
 	opt.regflags = REG_NEWLINE;
 
+	strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
+	opt.color = -1;
+	git_config(grep_config, &opt);
+	if (opt.color == -1)
+		opt.color = git_use_color_default;
+
 	/*
 	 * If there is no -- then the paths must exist in the working
 	 * tree.  If there is no explicit pattern specified with -e or
@@ -544,6 +614,10 @@
 			cached = 1;
 			continue;
 		}
+		if (!strcmp("--no-ext-grep", arg)) {
+			builtin_grep = 1;
+			continue;
+		}
 		if (!strcmp("-a", arg) ||
 		    !strcmp("--text", arg)) {
 			opt.binary = GREP_BINARY_TEXT;
@@ -601,6 +675,11 @@
 			opt.unmatch_name_only = 1;
 			continue;
 		}
+		if (!strcmp("-z", arg) ||
+		    !strcmp("--null", arg)) {
+			opt.null_following_name = 1;
+			continue;
+		}
 		if (!strcmp("-c", arg) ||
 		    !strcmp("--count", arg)) {
 			opt.count = 1;
@@ -713,6 +792,14 @@
 			opt.relative = 0;
 			continue;
 		}
+		if (!strcmp("--color", arg)) {
+			opt.color = 1;
+			continue;
+		}
+		if (!strcmp("--no-color", arg)) {
+			opt.color = 0;
+			continue;
+		}
 		if (!strcmp("--", arg)) {
 			/* later processing wants to have this at argv[1] */
 			argv--;
@@ -738,6 +825,8 @@
 		}
 	}
 
+	if (opt.color && !opt.color_external)
+		builtin_grep = 1;
 	if (!opt.pattern_list)
 		die("no pattern given.");
 	if ((opt.regflags != REG_NEWLINE) && opt.fixed)
diff --git a/builtin-help.c b/builtin-help.c
new file mode 100644
index 0000000..9b57a74
--- /dev/null
+++ b/builtin-help.c
@@ -0,0 +1,463 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "common-cmds.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "help.h"
+
+static struct man_viewer_list {
+	struct man_viewer_list *next;
+	char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+	struct man_viewer_info_list *next;
+	const char *info;
+	char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+	HELP_FORMAT_MAN,
+	HELP_FORMAT_INFO,
+	HELP_FORMAT_WEB,
+};
+
+static int show_all = 0;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+			HELP_FORMAT_WEB),
+	OPT_SET_INT('i', "info", &help_format, "show info page",
+			HELP_FORMAT_INFO),
+	OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+	"git help [--all] [--man|--web|--info] [command]",
+	NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+	if (!strcmp(format, "man"))
+		return HELP_FORMAT_MAN;
+	if (!strcmp(format, "info"))
+		return HELP_FORMAT_INFO;
+	if (!strcmp(format, "web") || !strcmp(format, "html"))
+		return HELP_FORMAT_WEB;
+	die("unrecognized help format '%s'", format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+	struct man_viewer_info_list *viewer;
+
+	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+	{
+		if (!strcasecmp(name, viewer->name))
+			return viewer->info;
+	}
+	return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+	struct strbuf buffer = STRBUF_INIT;
+	struct child_process ec_process;
+	const char *argv_ec[] = { "emacsclient", "--version", NULL };
+	int version;
+
+	/* emacsclient prints its version number on stderr */
+	memset(&ec_process, 0, sizeof(ec_process));
+	ec_process.argv = argv_ec;
+	ec_process.err = -1;
+	ec_process.stdout_to_stderr = 1;
+	if (start_command(&ec_process)) {
+		fprintf(stderr, "Failed to start emacsclient.\n");
+		return -1;
+	}
+	strbuf_read(&buffer, ec_process.err, 20);
+	close(ec_process.err);
+
+	/*
+	 * Don't bother checking return value, because "emacsclient --version"
+	 * seems to always exits with code 1.
+	 */
+	finish_command(&ec_process);
+
+	if (prefixcmp(buffer.buf, "emacsclient")) {
+		fprintf(stderr, "Failed to parse emacsclient version.\n");
+		strbuf_release(&buffer);
+		return -1;
+	}
+
+	strbuf_remove(&buffer, 0, strlen("emacsclient"));
+	version = atoi(buffer.buf);
+
+	if (version < 22) {
+		fprintf(stderr,
+			"emacsclient version '%d' too old (< 22).\n",
+			version);
+		strbuf_release(&buffer);
+		return -1;
+	}
+
+	strbuf_release(&buffer);
+	return 0;
+}
+
+static void exec_woman_emacs(const char* path, const char *page)
+{
+	if (!check_emacsclient_version()) {
+		/* This works only with emacsclient version >= 22. */
+		struct strbuf man_page = STRBUF_INIT;
+
+		if (!path)
+			path = "emacsclient";
+		strbuf_addf(&man_page, "(woman \"%s\")", page);
+		execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+		warning("failed to exec '%s': %s", path, strerror(errno));
+	}
+}
+
+static void exec_man_konqueror(const char* path, const char *page)
+{
+	const char *display = getenv("DISPLAY");
+	if (display && *display) {
+		struct strbuf man_page = STRBUF_INIT;
+		const char *filename = "kfmclient";
+
+		/* It's simpler to launch konqueror using kfmclient. */
+		if (path) {
+			const char *file = strrchr(path, '/');
+			if (file && !strcmp(file + 1, "konqueror")) {
+				char *new = xstrdup(path);
+				char *dest = strrchr(new, '/');
+
+				/* strlen("konqueror") == strlen("kfmclient") */
+				strcpy(dest + 1, "kfmclient");
+				path = new;
+			}
+			if (file)
+				filename = file;
+		} else
+			path = "kfmclient";
+		strbuf_addf(&man_page, "man:%s(1)", page);
+		execlp(path, filename, "newTab", man_page.buf, NULL);
+		warning("failed to exec '%s': %s", path, strerror(errno));
+	}
+}
+
+static void exec_man_man(const char* path, const char *page)
+{
+	if (!path)
+		path = "man";
+	execlp(path, "man", page, NULL);
+	warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+	struct strbuf shell_cmd = STRBUF_INIT;
+	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+	execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+	warning("failed to exec '%s': %s", cmd, strerror(errno));
+}
+
+static void add_man_viewer(const char *name)
+{
+	struct man_viewer_list **p = &man_viewer_list;
+	size_t len = strlen(name);
+
+	while (*p)
+		p = &((*p)->next);
+	*p = xcalloc(1, (sizeof(**p) + len + 1));
+	strncpy((*p)->name, name, len);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+	return (!strncasecmp("man", name, len) ||
+		!strncasecmp("woman", name, len) ||
+		!strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+				   size_t len,
+				   const char *value)
+{
+	struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
+
+	strncpy(new->name, name, len);
+	new->info = xstrdup(value);
+	new->next = man_viewer_info_list;
+	man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+			       size_t len,
+			       const char *value)
+{
+	if (supported_man_viewer(name, len))
+		do_add_man_viewer_info(name, len, value);
+	else
+		warning("'%s': path for unsupported man viewer.\n"
+			"Please consider using 'man.<tool>.cmd' instead.",
+			name);
+
+	return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+			      size_t len,
+			      const char *value)
+{
+	if (supported_man_viewer(name, len))
+		warning("'%s': cmd for supported man viewer.\n"
+			"Please consider using 'man.<tool>.path' instead.",
+			name);
+	else
+		do_add_man_viewer_info(name, len, value);
+
+	return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+	const char *name = var + 4;
+	const char *subkey = strrchr(name, '.');
+
+	if (!subkey)
+		return error("Config with no key for man viewer: %s", name);
+
+	if (!strcmp(subkey, ".path")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_path(name, subkey - name, value);
+	}
+	if (!strcmp(subkey, ".cmd")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_cmd(name, subkey - name, value);
+	}
+
+	warning("'%s': unsupported man viewer sub key.", subkey);
+	return 0;
+}
+
+static int git_help_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "help.format")) {
+		if (!value)
+			return config_error_nonbool(var);
+		help_format = parse_help_format(value);
+		return 0;
+	}
+	if (!strcmp(var, "man.viewer")) {
+		if (!value)
+			return config_error_nonbool(var);
+		add_man_viewer(value);
+		return 0;
+	}
+	if (!prefixcmp(var, "man."))
+		return add_man_viewer_info(var, value);
+
+	return git_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+void list_common_cmds_help(void)
+{
+	int i, longest = 0;
+
+	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		if (longest < strlen(common_cmds[i].name))
+			longest = strlen(common_cmds[i].name);
+	}
+
+	puts("The most commonly used git commands are:");
+	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		printf("   %s   ", common_cmds[i].name);
+		mput_char(' ', longest - strlen(common_cmds[i].name));
+		puts(common_cmds[i].help);
+	}
+}
+
+static int is_git_command(const char *s)
+{
+	return is_in_cmdlist(&main_cmds, s) ||
+		is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+	size_t pre_len = strlen(prefix);
+	size_t cmd_len = strlen(cmd);
+	char *p = xmalloc(pre_len + cmd_len + 1);
+	memcpy(p, prefix, pre_len);
+	strcpy(p + pre_len, cmd);
+	return p;
+}
+
+static const char *cmd_to_page(const char *git_cmd)
+{
+	if (!git_cmd)
+		return "git";
+	else if (!prefixcmp(git_cmd, "git"))
+		return git_cmd;
+	else if (is_git_command(git_cmd))
+		return prepend("git-", git_cmd);
+	else
+		return prepend("git", git_cmd);
+}
+
+static void setup_man_path(void)
+{
+	struct strbuf new_path = STRBUF_INIT;
+	const char *old_path = getenv("MANPATH");
+
+	/* We should always put ':' after our path. If there is no
+	 * old_path, the ':' at the end will let 'man' to try
+	 * system-wide paths after ours to find the manual page. If
+	 * there is old_path, we need ':' as delimiter. */
+	strbuf_addstr(&new_path, system_path(GIT_MAN_PATH));
+	strbuf_addch(&new_path, ':');
+	if (old_path)
+		strbuf_addstr(&new_path, old_path);
+
+	setenv("MANPATH", new_path.buf, 1);
+
+	strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+	const char *info = get_man_viewer_info(name);
+
+	if (!strcasecmp(name, "man"))
+		exec_man_man(info, page);
+	else if (!strcasecmp(name, "woman"))
+		exec_woman_emacs(info, page);
+	else if (!strcasecmp(name, "konqueror"))
+		exec_man_konqueror(info, page);
+	else if (info)
+		exec_man_cmd(info, page);
+	else
+		warning("'%s': unknown man viewer.", name);
+}
+
+static void show_man_page(const char *git_cmd)
+{
+	struct man_viewer_list *viewer;
+	const char *page = cmd_to_page(git_cmd);
+	const char *fallback = getenv("GIT_MAN_VIEWER");
+
+	setup_man_path();
+	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+	{
+		exec_viewer(viewer->name, page); /* will return when unable */
+	}
+	if (fallback)
+		exec_viewer(fallback, page);
+	exec_viewer("man", page);
+	die("no man viewer handled the request");
+}
+
+static void show_info_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
+	execlp("info", "info", "gitman", page, NULL);
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+	struct stat st;
+	const char *html_path = system_path(GIT_HTML_PATH);
+
+	/* Check that we have a git documentation directory. */
+	if (stat(mkpath("%s/git.html", html_path), &st)
+	    || !S_ISREG(st.st_mode))
+		die("'%s': not a documentation directory.", html_path);
+
+	strbuf_init(page_path, 0);
+	strbuf_addf(page_path, "%s/%s.html", html_path, page);
+}
+
+/*
+ * If open_html is not defined in a platform-specific way (see for
+ * example compat/mingw.h), we use the script web--browse to display
+ * HTML.
+ */
+#ifndef open_html
+void open_html(const char *path)
+{
+	execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
+}
+#endif
+
+static void show_html_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	struct strbuf page_path; /* it leaks but we exec bellow */
+
+	get_html_page_path(&page_path, page);
+
+	open_html(page_path.buf);
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix)
+{
+	int nongit;
+	const char *alias;
+	load_command_list("git-", &main_cmds, &other_cmds);
+
+	setup_git_directory_gently(&nongit);
+	git_config(git_help_config, NULL);
+
+	argc = parse_options(argc, argv, builtin_help_options,
+			builtin_help_usage, 0);
+
+	if (show_all) {
+		printf("usage: %s\n\n", git_usage_string);
+		list_commands("git commands", &main_cmds, &other_cmds);
+		printf("%s\n", git_more_info_string);
+		return 0;
+	}
+
+	if (!argv[0]) {
+		printf("usage: %s\n\n", git_usage_string);
+		list_common_cmds_help();
+		printf("\n%s\n", git_more_info_string);
+		return 0;
+	}
+
+	alias = alias_lookup(argv[0]);
+	if (alias && !is_git_command(argv[0])) {
+		printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+		return 0;
+	}
+
+	switch (help_format) {
+	case HELP_FORMAT_MAN:
+		show_man_page(argv[0]);
+		break;
+	case HELP_FORMAT_INFO:
+		show_info_page(argv[0]);
+		break;
+	case HELP_FORMAT_WEB:
+		show_html_page(argv[0]);
+		break;
+	}
+
+	return 0;
+}
diff --git a/builtin-http-fetch.c b/builtin-http-fetch.c
index 03f34d7..f3e63d7 100644
--- a/builtin-http-fetch.c
+++ b/builtin-http-fetch.c
@@ -53,7 +53,7 @@
 	}
 	url = argv[arg];
 	if (url && url[strlen(url)-1] != '/') {
-		rewritten_url = malloc(strlen(url)+2);
+		rewritten_url = xmalloc(strlen(url)+2);
 		strcpy(rewritten_url, url);
 		strcat(rewritten_url, "/");
 		url = rewritten_url;
diff --git a/builtin-init-db.c b/builtin-init-db.c
index d30c3fe..d1fa12a 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -29,7 +29,7 @@
 		}
 	}
 	else if (share && adjust_shared_perm(dir))
-		die("Could not make %s writable by group\n", dir);
+		die("Could not make %s writable by group", dir);
 }
 
 static void copy_templates_1(char *path, int baselen,
@@ -122,16 +122,17 @@
 		template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
 	if (!template_dir[0])
 		return;
+	template_len = strlen(template_dir);
+	if (PATH_MAX <= (template_len+strlen("/config")))
+		die("insanely long template path %s", template_dir);
 	strcpy(template_path, template_dir);
-	template_len = strlen(template_path);
 	if (template_path[template_len-1] != '/') {
 		template_path[template_len++] = '/';
 		template_path[template_len] = 0;
 	}
 	dir = opendir(template_path);
 	if (!dir) {
-		fprintf(stderr, "warning: templates not found %s\n",
-			template_dir);
+		warning("templates not found %s", template_dir);
 		return;
 	}
 
@@ -144,8 +145,8 @@
 
 	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",
+		warning("not copying templates of "
+			"a wrong format version %d from '%s'",
 			repository_format_version,
 			template_dir);
 		closedir(dir);
@@ -195,6 +196,8 @@
 
 	git_config(git_default_config, NULL);
 	is_bare_repository_cfg = init_is_bare_repository;
+
+	/* reading existing config may have overwrote it */
 	if (init_shared_repository != -1)
 		shared_repository = init_shared_repository;
 
@@ -313,12 +316,15 @@
 		 * and compatibility values for PERM_GROUP and
 		 * PERM_EVERYBODY.
 		 */
-		if (shared_repository == PERM_GROUP)
+		if (shared_repository < 0)
+			/* force to the mode value */
+			sprintf(buf, "0%o", -shared_repository);
+		else if (shared_repository == PERM_GROUP)
 			sprintf(buf, "%d", OLD_PERM_GROUP);
 		else if (shared_repository == PERM_EVERYBODY)
 			sprintf(buf, "%d", OLD_PERM_EVERYBODY);
 		else
-			sprintf(buf, "0%o", shared_repository);
+			die("oops");
 		git_config_set("core.sharedrepository", buf);
 		git_config_set("receive.denyNonFastforwards", "true");
 	}
@@ -398,6 +404,9 @@
 			usage(init_db_usage);
 	}
 
+	if (init_shared_repository != -1)
+		shared_repository = init_shared_repository;
+
 	/*
 	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
 	 * without --bare.  Catch the error early.
diff --git a/builtin-log.c b/builtin-log.c
index db71e0d..5eaec5d 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -14,9 +14,10 @@
 #include "tag.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
-#include "refs.h"
 #include "run-command.h"
 #include "shortlog.h"
+#include "remote.h"
+#include "string-list.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -25,36 +26,10 @@
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
-static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
-{
-	int plen = strlen(prefix);
-	int nlen = strlen(name);
-	struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
-	memcpy(res->name, prefix, plen);
-	memcpy(res->name + plen, name, nlen + 1);
-	res->next = add_decoration(&name_decoration, obj, res);
-}
-
-static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct object *obj = parse_object(sha1);
-	if (!obj)
-		return 0;
-	add_name_decoration("", refname, obj);
-	while (obj->type == OBJ_TAG) {
-		obj = ((struct tag *)obj)->tagged;
-		if (!obj)
-			break;
-		add_name_decoration("tag: ", refname, obj);
-	}
-	return 0;
-}
-
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
 		      struct rev_info *rev)
 {
 	int i;
-	int decorate = 0;
 
 	rev->abbrev = DEFAULT_ABBREV;
 	rev->commit_format = CMIT_FMT_DEFAULT;
@@ -64,6 +39,7 @@
 	DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
 	rev->show_root_diff = default_show_root;
 	rev->subject_prefix = fmt_patch_subject_prefix;
+	DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
 
 	if (default_date_mode)
 		rev->date_mode = parse_date_format(default_date_mode);
@@ -80,9 +56,10 @@
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		if (!strcmp(arg, "--decorate")) {
-			if (!decorate)
-				for_each_ref(add_ref_decoration, NULL);
-			decorate = 1;
+			load_ref_decorations();
+			rev->show_decorations = 1;
+		} else if (!strcmp(arg, "--source")) {
+			rev->show_source = 1;
 		} else
 			die("unrecognized argument: %s", arg);
 	}
@@ -217,6 +194,11 @@
 	if (rev->early_output)
 		finish_early_output(rev);
 
+	/*
+	 * For --check and --exit-code, the exit code is based on CHECK_FAILED
+	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
+	 * retain that state information if replacing rev->diffopt in this loop
+	 */
 	while ((commit = get_revision(rev)) != NULL) {
 		log_tree_commit(rev, commit);
 		if (!rev->reflog_info) {
@@ -227,7 +209,11 @@
 		free_commit_list(commit->parents);
 		commit->parents = NULL;
 	}
-	return 0;
+	if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
+	    DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+		return 02;
+	}
+	return diff_result_code(&rev->diffopt, 0);
 }
 
 static int git_log_config(const char *var, const char *value, void *cb)
@@ -265,22 +251,13 @@
 
 static void show_tagger(char *buf, int len, struct rev_info *rev)
 {
-	char *email_end, *p;
-	unsigned long date;
-	int tz;
+	struct strbuf out = STRBUF_INIT;
 
-	email_end = memchr(buf, '>', len);
-	if (!email_end)
-		return;
-	p = ++email_end;
-	while (isspace(*p))
-		p++;
-	date = strtoul(p, &p, 10);
-	while (isspace(*p))
-		p++;
-	tz = (int)strtol(p, NULL, 10);
-	printf("Tagger: %.*s\nDate:   %s\n", (int)(email_end - buf), buf,
-	       show_date(date, tz, rev->date_mode));
+	pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
+		git_log_output_encoding ?
+		git_log_output_encoding: git_commit_encoding);
+	printf("%s\n", out.buf);
+	strbuf_release(&out);
 }
 
 static int show_object(const unsigned char *sha1, int show_tag_object,
@@ -440,17 +417,12 @@
 }
 
 /* format-patch */
-#define FORMAT_PATCH_NAME_MAX 64
-
-static int istitlechar(char c)
-{
-	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
-		(c >= '0' && c <= '9') || c == '.' || c == '_';
-}
 
 static const char *fmt_patch_suffix = ".patch";
 static int numbered = 0;
-static int auto_number = 0;
+static int auto_number = 1;
+
+static char *default_attach = NULL;
 
 static char **extra_hdr;
 static int extra_hdr_nr;
@@ -483,6 +455,11 @@
 	extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
 }
 
+#define THREAD_SHALLOW 1
+#define THREAD_DEEP 2
+static int thread = 0;
+static int do_signoff = 0;
+
 static int git_format_config(const char *var, const char *value, void *cb)
 {
 	if (!strcmp(var, "format.headers")) {
@@ -509,95 +486,63 @@
 			return 0;
 		}
 		numbered = git_config_bool(var, value);
+		auto_number = auto_number && numbered;
+		return 0;
+	}
+	if (!strcmp(var, "format.attach")) {
+		if (value && *value)
+			default_attach = xstrdup(value);
+		else
+			default_attach = xstrdup(git_version_string);
+		return 0;
+	}
+	if (!strcmp(var, "format.thread")) {
+		if (value && !strcasecmp(value, "deep")) {
+			thread = THREAD_DEEP;
+			return 0;
+		}
+		if (value && !strcasecmp(value, "shallow")) {
+			thread = THREAD_SHALLOW;
+			return 0;
+		}
+		thread = git_config_bool(var, value) && THREAD_SHALLOW;
+		return 0;
+	}
+	if (!strcmp(var, "format.signoff")) {
+		do_signoff = git_config_bool(var, value);
 		return 0;
 	}
 
 	return git_log_config(var, value, cb);
 }
 
-
-static const char *get_oneline_for_filename(struct commit *commit,
-					    int keep_subject)
-{
-	static char filename[PATH_MAX];
-	char *sol;
-	int len = 0;
-	int suffix_len = strlen(fmt_patch_suffix) + 1;
-
-	sol = strstr(commit->buffer, "\n\n");
-	if (!sol)
-		filename[0] = '\0';
-	else {
-		int j, space = 0;
-
-		sol += 2;
-		/* strip [PATCH] or [PATCH blabla] */
-		if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
-			char *eos = strchr(sol + 6, ']');
-			if (eos) {
-				while (isspace(*eos))
-					eos++;
-				sol = eos;
-			}
-		}
-
-		for (j = 0;
-		     j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
-			     len < sizeof(filename) - suffix_len &&
-			     sol[j] && sol[j] != '\n';
-		     j++) {
-			if (istitlechar(sol[j])) {
-				if (space) {
-					filename[len++] = '-';
-					space = 0;
-				}
-				filename[len++] = sol[j];
-				if (sol[j] == '.')
-					while (sol[j + 1] == '.')
-						j++;
-			} else
-				space = 1;
-		}
-		while (filename[len - 1] == '.'
-		       || filename[len - 1] == '-')
-			len--;
-		filename[len] = '\0';
-	}
-	return filename;
-}
-
 static FILE *realstdout = NULL;
 static const char *output_directory = NULL;
+static int outdir_offset;
 
-static int reopen_stdout(const char *oneline, int nr, int total)
+static int reopen_stdout(struct commit *commit, struct rev_info *rev)
 {
-	char filename[PATH_MAX];
-	int len = 0;
+	struct strbuf filename = STRBUF_INIT;
 	int suffix_len = strlen(fmt_patch_suffix) + 1;
 
 	if (output_directory) {
-		len = snprintf(filename, sizeof(filename), "%s",
-				output_directory);
-		if (len >=
-		    sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
+		strbuf_addstr(&filename, output_directory);
+		if (filename.len >=
+		    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
 			return error("name of output directory is too long");
-		if (filename[len - 1] != '/')
-			filename[len++] = '/';
+		if (filename.buf[filename.len - 1] != '/')
+			strbuf_addch(&filename, '/');
 	}
 
-	if (!oneline)
-		len += sprintf(filename + len, "%d", nr);
-	else {
-		len += sprintf(filename + len, "%04d-", nr);
-		len += snprintf(filename + len, sizeof(filename) - len - 1
-				- suffix_len, "%s", oneline);
-		strcpy(filename + len, fmt_patch_suffix);
-	}
+	get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
 
-	fprintf(realstdout, "%s\n", filename);
-	if (freopen(filename, "w", stdout) == NULL)
-		return error("Cannot open patch file %s",filename);
+	if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
+		fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
 
+	if (freopen(filename.buf, "w", stdout) == NULL)
+		return error("Cannot open patch file %s", filename.buf);
+
+	strbuf_release(&filename);
 	return 0;
 }
 
@@ -652,10 +597,9 @@
 	const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
 	const char *email_start = strrchr(committer, '<');
 	const char *email_end = strrchr(committer, '>');
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	if (!email_start || !email_end || email_start > email_end - 1)
 		die("Could not extract email from committer identity.");
-	strbuf_init(&buf, 0);
 	strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
 		    (unsigned long) time(NULL),
 		    (int)(email_end - email_start - 1), email_start + 1);
@@ -668,34 +612,52 @@
 			      int nr, struct commit **list, struct commit *head)
 {
 	const char *committer;
-	char *head_sha1;
 	const char *subject_start = NULL;
 	const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
 	const char *msg;
 	const char *extra_headers = rev->extra_headers;
 	struct shortlog log;
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 	int i;
 	const char *encoding = "utf-8";
 	struct diff_options opts;
 	int need_8bit_cte = 0;
+	struct commit *commit = NULL;
 
 	if (rev->commit_format != CMIT_FMT_EMAIL)
 		die("Cover letter needs email format");
 
-	if (!use_stdout && reopen_stdout(numbered_files ?
-				NULL : "cover-letter", 0, rev->total))
-		return;
-
-	head_sha1 = sha1_to_hex(head->object.sha1);
-
-	log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers,
-				&need_8bit_cte);
-
 	committer = git_committer_info(0);
 
+	if (!numbered_files) {
+		/*
+		 * We fake a commit for the cover letter so we get the filename
+		 * desired.
+		 */
+		commit = xcalloc(1, sizeof(*commit));
+		commit->buffer = xmalloc(400);
+		snprintf(commit->buffer, 400,
+			"tree 0000000000000000000000000000000000000000\n"
+			"parent %s\n"
+			"author %s\n"
+			"committer %s\n\n"
+			"cover letter\n",
+			sha1_to_hex(head->object.sha1), committer, committer);
+	}
+
+	if (!use_stdout && reopen_stdout(commit, rev))
+		return;
+
+	if (commit) {
+
+		free(commit->buffer);
+		free(commit);
+	}
+
+	log_write_email_headers(rev, head, &subject_start, &extra_headers,
+				&need_8bit_cte);
+
 	msg = body;
-	strbuf_init(&sb, 0);
 	pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
 		     encoding);
 	pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
@@ -757,6 +719,27 @@
 	return xmemdupz(a, z - a);
 }
 
+static const char *set_outdir(const char *prefix, const char *output_directory)
+{
+	if (output_directory && is_absolute_path(output_directory))
+		return output_directory;
+
+	if (!prefix || !*prefix) {
+		if (output_directory)
+			return output_directory;
+		/* The user did not explicitly ask for "./" */
+		outdir_offset = 2;
+		return "./";
+	}
+
+	outdir_offset = strlen(prefix);
+	if (!output_directory)
+		return prefix;
+
+	return xstrdup(prefix_filename(prefix, outdir_offset,
+				       output_directory));
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
 	struct commit *commit;
@@ -769,7 +752,6 @@
 	int numbered_files = 0;		/* _just_ numbers */
 	int subject_prefix = 0;
 	int ignore_if_in_upstream = 0;
-	int thread = 0;
 	int cover_letter = 0;
 	int boundary_count = 0;
 	int no_binary_diff = 0;
@@ -777,7 +759,7 @@
 	const char *in_reply_to = NULL;
 	struct patch_ids ids;
 	char *add_signoff = NULL;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 
 	git_config(git_format_config, NULL);
 	init_revisions(&rev, prefix);
@@ -790,6 +772,11 @@
 
 	rev.subject_prefix = fmt_patch_subject_prefix;
 
+	if (default_attach) {
+		rev.mime_boundary = default_attach;
+		rev.no_inline = 1;
+	}
+
 	/*
 	 * Parse the arguments before setup_revisions(), or something
 	 * like "git format-patch -o a123 HEAD^.." may fail; a123 is
@@ -836,13 +823,7 @@
 		}
 		else if (!strcmp(argv[i], "--signoff") ||
 			 !strcmp(argv[i], "-s")) {
-			const char *committer;
-			const char *endpos;
-			committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
-			endpos = strchr(committer, '>');
-			if (!endpos)
-				die("bogus committer info %s\n", committer);
-			add_signoff = xmemdupz(committer, endpos - committer + 1);
+			do_signoff = 1;
 		}
 		else if (!strcmp(argv[i], "--attach")) {
 			rev.mime_boundary = git_version_string;
@@ -852,6 +833,10 @@
 			rev.mime_boundary = argv[i] + 9;
 			rev.no_inline = 1;
 		}
+		else if (!strcmp(argv[i], "--no-attach")) {
+			rev.mime_boundary = NULL;
+			rev.no_inline = 0;
+		}
 		else if (!strcmp(argv[i], "--inline")) {
 			rev.mime_boundary = git_version_string;
 			rev.no_inline = 0;
@@ -862,8 +847,13 @@
 		}
 		else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
 			ignore_if_in_upstream = 1;
-		else if (!strcmp(argv[i], "--thread"))
-			thread = 1;
+		else if (!strcmp(argv[i], "--thread")
+			|| !strcmp(argv[i], "--thread=shallow"))
+			thread = THREAD_SHALLOW;
+		else if (!strcmp(argv[i], "--thread=deep"))
+			thread = THREAD_DEEP;
+		else if (!strcmp(argv[i], "--no-thread"))
+			thread = 0;
 		else if (!prefixcmp(argv[i], "--in-reply-to="))
 			in_reply_to = argv[i] + 14;
 		else if (!strcmp(argv[i], "--in-reply-to")) {
@@ -880,12 +870,22 @@
 			cover_letter = 1;
 		else if (!strcmp(argv[i], "--no-binary"))
 			no_binary_diff = 1;
+		else if (!prefixcmp(argv[i], "--add-header="))
+			add_header(argv[i] + 13);
 		else
 			argv[j++] = argv[i];
 	}
 	argc = j;
 
-	strbuf_init(&buf, 0);
+	if (do_signoff) {
+		const char *committer;
+		const char *endpos;
+		committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
+		endpos = strchr(committer, '>');
+		if (!endpos)
+			die("bogus committer info %s", committer);
+		add_signoff = xmemdupz(committer, endpos - committer + 1);
+	}
 
 	for (i = 0; i < extra_hdr_nr; i++) {
 		strbuf_addstr(&buf, extra_hdr[i]);
@@ -922,21 +922,20 @@
 		die ("-n and -k are mutually exclusive.");
 	if (keep_subject && subject_prefix)
 		die ("--subject-prefix and -k are mutually exclusive.");
-	if (numbered_files && use_stdout)
-		die ("--numbered-files and --stdout are mutually exclusive.");
 
 	argc = setup_revisions(argc, argv, &rev, "HEAD");
 	if (argc > 1)
 		die ("unrecognized argument: %s", argv[1]);
 
-	if (!rev.diffopt.output_format)
+	if (!rev.diffopt.output_format
+		|| rev.diffopt.output_format == DIFF_FORMAT_PATCH)
 		rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
 
 	if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
 		DIFF_OPT_SET(&rev.diffopt, BINARY);
 
-	if (!output_directory && !use_stdout)
-		output_directory = prefix;
+	if (!use_stdout)
+		output_directory = set_outdir(prefix, output_directory);
 
 	if (output_directory) {
 		if (use_stdout)
@@ -962,6 +961,13 @@
 		 * get_revision() to do the usual traversal.
 		 */
 	}
+
+	/*
+	 * We cannot move this anywhere earlier because we do want to
+	 * know if --root was given explicitly from the comand line.
+	 */
+	rev.show_root_diff = 1;
+
 	if (cover_letter) {
 		/* remember the range */
 		int i;
@@ -1008,8 +1014,14 @@
 		numbered = 1;
 	if (numbered)
 		rev.total = total + start_number - 1;
-	if (in_reply_to)
-		rev.ref_message_id = clean_message_id(in_reply_to);
+	if (in_reply_to || thread || cover_letter)
+		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to) {
+		const char *msgid = clean_message_id(in_reply_to);
+		string_list_append(msgid, rev.ref_message_ids);
+	}
+	rev.numbered_files = numbered_files;
+	rev.patch_suffix = fmt_patch_suffix;
 	if (cover_letter) {
 		if (thread)
 			gen_message_id(&rev, "cover");
@@ -1028,21 +1040,39 @@
 			/* Have we already had a message ID? */
 			if (rev.message_id) {
 				/*
-				 * If we've got the ID to be a reply
-				 * to, discard the current ID;
-				 * otherwise, make everything a reply
-				 * to that.
+				 * For deep threading: make every mail
+				 * a reply to the previous one, no
+				 * matter what other options are set.
+				 *
+				 * For shallow threading:
+				 *
+				 * Without --cover-letter and
+				 * --in-reply-to, make every mail a
+				 * reply to the one before.
+				 *
+				 * With --in-reply-to but no
+				 * --cover-letter, make every mail a
+				 * reply to the <reply-to>.
+				 *
+				 * With --cover-letter, make every
+				 * mail but the cover letter a reply
+				 * to the cover letter.  The cover
+				 * letter is a reply to the
+				 * --in-reply-to, if specified.
 				 */
-				if (rev.ref_message_id)
+				if (thread == THREAD_SHALLOW
+				    && rev.ref_message_ids->nr > 0
+				    && (!cover_letter || rev.nr > 1))
 					free(rev.message_id);
 				else
-					rev.ref_message_id = rev.message_id;
+					string_list_append(rev.message_id,
+							   rev.ref_message_ids);
 			}
 			gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
 		}
-		if (!use_stdout && reopen_stdout(numbered_files ? NULL :
-				get_oneline_for_filename(commit, keep_subject),
-				rev.nr, rev.total))
+
+		if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
+						 &rev))
 			die("Failed to create output files");
 		shown = log_tree_commit(&rev, commit);
 		free(commit->buffer);
@@ -1088,13 +1118,14 @@
 }
 
 static const char cherry_usage[] =
-"git cherry [-v] <upstream> [<head>] [<limit>]";
+"git cherry [-v] [<upstream> [<head> [<limit>]]]";
 int cmd_cherry(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info revs;
 	struct patch_ids ids;
 	struct commit *commit;
 	struct commit_list *list = NULL;
+	struct branch *current_branch;
 	const char *upstream;
 	const char *head = "HEAD";
 	const char *limit = NULL;
@@ -1117,7 +1148,17 @@
 		upstream = argv[1];
 		break;
 	default:
-		usage(cherry_usage);
+		current_branch = branch_get(NULL);
+		if (!current_branch || !current_branch->merge
+					|| !current_branch->merge[0]
+					|| !current_branch->merge[0]->dst) {
+			fprintf(stderr, "Could not find a tracked"
+					" remote branch, please"
+					" specify <upstream> manually.\n");
+			usage(cherry_usage);
+		}
+
+		upstream = current_branch->merge[0]->dst;
 	}
 
 	init_revisions(&revs, prefix);
@@ -1162,8 +1203,7 @@
 			sign = '-';
 
 		if (verbose) {
-			struct strbuf buf;
-			strbuf_init(&buf, 0);
+			struct strbuf buf = STRBUF_INIT;
 			pretty_print_commit(CMIT_FMT_ONELINE, commit,
 			                    &buf, 0, NULL, NULL, 0, 0);
 			printf("%c %s %s\n", sign,
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index f72eb85..88e2697 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -10,6 +10,7 @@
 #include "dir.h"
 #include "builtin.h"
 #include "tree.h"
+#include "parse-options.h"
 
 static int abbrev;
 static int show_deleted;
@@ -28,6 +29,7 @@
 static int error_unmatch;
 static char *ps_matched;
 static const char *with_tree;
+static int exc_given;
 
 static const char *tag_cached = "";
 static const char *tag_unmerged = "";
@@ -36,42 +38,6 @@
 static const char *tag_killed = "";
 static const char *tag_modified = "";
 
-
-/*
- * Match a pathspec against a filename. The first "skiplen" characters
- * are the common prefix
- */
-int pathspec_match(const char **spec, char *ps_matched,
-		   const char *filename, int skiplen)
-{
-	const char *m;
-
-	while ((m = *spec++) != NULL) {
-		int matchlen = strlen(m + skiplen);
-
-		if (!matchlen)
-			goto matched;
-		if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
-			if (m[skiplen + matchlen - 1] == '/')
-				goto matched;
-			switch (filename[skiplen + matchlen]) {
-			case '/': case '\0':
-				goto matched;
-			}
-		}
-		if (!fnmatch(m + skiplen, filename + skiplen, 0))
-			goto matched;
-		if (ps_matched)
-			ps_matched++;
-		continue;
-	matched:
-		if (ps_matched)
-			*ps_matched = 1;
-		return 1;
-	}
-	return 0;
-}
-
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
 	int len = prefix_len;
@@ -80,7 +46,7 @@
 	if (len >= ent->len)
 		die("git ls-files: internal error - directory entry not superset of prefix");
 
-	if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
+	if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
 		return;
 
 	fputs(tag, stdout);
@@ -156,7 +122,7 @@
 	if (len >= ce_namelen(ce))
 		die("git ls-files: internal error - cache entry not superset of prefix");
 
-	if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
+	if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
 		return;
 
 	if (tag && *tag && show_valid_bit &&
@@ -210,7 +176,8 @@
 		for (i = 0; i < active_nr; i++) {
 			struct cache_entry *ce = active_cache[i];
 			int dtype = ce_to_dtype(ce);
-			if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
+			if (excluded(dir, ce->name, &dtype) !=
+					!!(dir->flags & DIR_SHOW_IGNORED))
 				continue;
 			if (show_unmerged && !ce_stage(ce))
 				continue;
@@ -225,7 +192,8 @@
 			struct stat st;
 			int err;
 			int dtype = ce_to_dtype(ce);
-			if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
+			if (excluded(dir, ce->name, &dtype) !=
+					!!(dir->flags & DIR_SHOW_IGNORED))
 				continue;
 			if (ce->ce_flags & CE_UPDATE)
 				continue;
@@ -298,6 +266,21 @@
 	return max ? xmemdupz(prev, max) : NULL;
 }
 
+static void strip_trailing_slash_from_submodules(void)
+{
+	const char **p;
+
+	for (p = pathspec; *p != NULL; p++) {
+		int len = strlen(*p), pos;
+
+		if (len < 1 || (*p)[len - 1] != '/')
+			continue;
+		pos = cache_name_pos(*p, len - 1);
+		if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
+			*p = xstrndup(*p, len - 1);
+	}
+}
+
 /*
  * Read the tree specified with --with-tree option
  * (typically, HEAD) into stage #1 and then
@@ -395,156 +378,144 @@
 	return errors;
 }
 
-static const char ls_files_usage[] =
-	"git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
-	"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
-	"[ --exclude-per-directory=<filename> ] [--exclude-standard] "
-	"[--full-name] [--abbrev] [--] [<file>]*";
+static const char * const ls_files_usage[] = {
+	"git ls-files [options] [<file>]*",
+	NULL
+};
+
+static int option_parse_z(const struct option *opt,
+			  const char *arg, int unset)
+{
+	line_terminator = unset ? '\n' : '\0';
+
+	return 0;
+}
+
+static int option_parse_exclude(const struct option *opt,
+				const char *arg, int unset)
+{
+	struct exclude_list *list = opt->value;
+
+	exc_given = 1;
+	add_exclude(arg, "", 0, list);
+
+	return 0;
+}
+
+static int option_parse_exclude_from(const struct option *opt,
+				     const char *arg, int unset)
+{
+	struct dir_struct *dir = opt->value;
+
+	exc_given = 1;
+	add_excludes_from_file(dir, arg);
+
+	return 0;
+}
+
+static int option_parse_exclude_standard(const struct option *opt,
+					 const char *arg, int unset)
+{
+	struct dir_struct *dir = opt->value;
+
+	exc_given = 1;
+	setup_standard_excludes(dir);
+
+	return 0;
+}
 
 int cmd_ls_files(int argc, const char **argv, const char *prefix)
 {
-	int i;
-	int exc_given = 0, require_work_tree = 0;
+	int require_work_tree = 0, show_tag = 0;
 	struct dir_struct dir;
+	struct option builtin_ls_files_options[] = {
+		{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+			"paths are separated with NUL character",
+			PARSE_OPT_NOARG, option_parse_z },
+		OPT_BOOLEAN('t', NULL, &show_tag,
+			"identify the file status with tags"),
+		OPT_BOOLEAN('v', NULL, &show_valid_bit,
+			"use lowercase letters for 'assume unchanged' files"),
+		OPT_BOOLEAN('c', "cached", &show_cached,
+			"show cached files in the output (default)"),
+		OPT_BOOLEAN('d', "deleted", &show_deleted,
+			"show deleted files in the output"),
+		OPT_BOOLEAN('m', "modified", &show_modified,
+			"show modified files in the output"),
+		OPT_BOOLEAN('o', "others", &show_others,
+			"show other files in the output"),
+		OPT_BIT('i', "ignored", &dir.flags,
+			"show ignored files in the output",
+			DIR_SHOW_IGNORED),
+		OPT_BOOLEAN('s', "stage", &show_stage,
+			"show staged contents' object name in the output"),
+		OPT_BOOLEAN('k', "killed", &show_killed,
+			"show files on the filesystem that need to be removed"),
+		OPT_BIT(0, "directory", &dir.flags,
+			"show 'other' directories' name only",
+			DIR_SHOW_OTHER_DIRECTORIES),
+		OPT_BIT(0, "no-empty-directory", &dir.flags,
+			"don't show empty directories",
+			DIR_HIDE_EMPTY_DIRECTORIES),
+		OPT_BOOLEAN('u', "unmerged", &show_unmerged,
+			"show unmerged files in the output"),
+		{ OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
+			"skip files matching pattern",
+			0, option_parse_exclude },
+		{ OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
+			"exclude patterns are read from <file>",
+			0, option_parse_exclude_from },
+		OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file",
+			"read additional per-directory exclude patterns in <file>"),
+		{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
+			"add the standard git exclusions",
+			PARSE_OPT_NOARG, option_parse_exclude_standard },
+		{ OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+			"make the output relative to the project top directory",
+			PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
+		OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
+			"if any <file> is not in the index, treat this as an error"),
+		OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
+			"pretend that paths removed since <tree-ish> are still present"),
+		OPT__ABBREV(&abbrev),
+		OPT_END()
+	};
 
 	memset(&dir, 0, sizeof(dir));
 	if (prefix)
 		prefix_offset = strlen(prefix);
 	git_config(git_default_config, NULL);
 
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (!strcmp(arg, "--")) {
-			i++;
-			break;
-		}
-		if (!strcmp(arg, "-z")) {
-			line_terminator = 0;
-			continue;
-		}
-		if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
-			tag_cached = "H ";
-			tag_unmerged = "M ";
-			tag_removed = "R ";
-			tag_modified = "C ";
-			tag_other = "? ";
-			tag_killed = "K ";
-			if (arg[1] == 'v')
-				show_valid_bit = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
-			show_cached = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
-			show_deleted = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
-			show_modified = 1;
-			require_work_tree = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
-			show_others = 1;
-			require_work_tree = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
-			dir.show_ignored = 1;
-			require_work_tree = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
-			show_stage = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
-			show_killed = 1;
-			require_work_tree = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--directory")) {
-			dir.show_other_directories = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--no-empty-directory")) {
-			dir.hide_empty_directories = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
-			/* There's no point in showing unmerged unless
-			 * you also show the stage information.
-			 */
-			show_stage = 1;
-			show_unmerged = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-x") && i+1 < argc) {
-			exc_given = 1;
-			add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
-			continue;
-		}
-		if (!prefixcmp(arg, "--exclude=")) {
-			exc_given = 1;
-			add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
-			continue;
-		}
-		if (!strcmp(arg, "-X") && i+1 < argc) {
-			exc_given = 1;
-			add_excludes_from_file(&dir, argv[++i]);
-			continue;
-		}
-		if (!prefixcmp(arg, "--exclude-from=")) {
-			exc_given = 1;
-			add_excludes_from_file(&dir, arg+15);
-			continue;
-		}
-		if (!prefixcmp(arg, "--exclude-per-directory=")) {
-			exc_given = 1;
-			dir.exclude_per_dir = arg + 24;
-			continue;
-		}
-		if (!strcmp(arg, "--exclude-standard")) {
-			exc_given = 1;
-			setup_standard_excludes(&dir);
-			continue;
-		}
-		if (!strcmp(arg, "--full-name")) {
-			prefix_offset = 0;
-			continue;
-		}
-		if (!strcmp(arg, "--error-unmatch")) {
-			error_unmatch = 1;
-			continue;
-		}
-		if (!prefixcmp(arg, "--with-tree=")) {
-			with_tree = arg + 12;
-			continue;
-		}
-		if (!prefixcmp(arg, "--abbrev=")) {
-			abbrev = strtoul(arg+9, NULL, 10);
-			if (abbrev && abbrev < MINIMUM_ABBREV)
-				abbrev = MINIMUM_ABBREV;
-			else if (abbrev > 40)
-				abbrev = 40;
-			continue;
-		}
-		if (!strcmp(arg, "--abbrev")) {
-			abbrev = DEFAULT_ABBREV;
-			continue;
-		}
-		if (*arg == '-')
-			usage(ls_files_usage);
-		break;
+	argc = parse_options(argc, argv, builtin_ls_files_options,
+			ls_files_usage, 0);
+	if (show_tag || show_valid_bit) {
+		tag_cached = "H ";
+		tag_unmerged = "M ";
+		tag_removed = "R ";
+		tag_modified = "C ";
+		tag_other = "? ";
+		tag_killed = "K ";
 	}
+	if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
+		require_work_tree = 1;
+	if (show_unmerged)
+		/*
+		 * There's no point in showing unmerged unless
+		 * you also show the stage information.
+		 */
+		show_stage = 1;
+	if (dir.exclude_per_dir)
+		exc_given = 1;
 
 	if (require_work_tree && !is_inside_work_tree())
 		setup_work_tree();
 
-	pathspec = get_pathspec(prefix, argv + i);
+	pathspec = get_pathspec(prefix, argv);
+
+	/* be nice with submodule patsh ending in a slash */
+	read_cache();
+	if (pathspec)
+		strip_trailing_slash_from_submodules();
 
 	/* Verify that the pathspec matches the prefix */
 	if (pathspec)
@@ -558,7 +529,7 @@
 		ps_matched = xcalloc(1, num);
 	}
 
-	if (dir.show_ignored && !exc_given) {
+	if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) {
 		fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
 			argv[0]);
 		exit(1);
@@ -569,7 +540,6 @@
 	      show_killed | show_modified))
 		show_cached = 1;
 
-	read_cache();
 	if (prefix)
 		prune_cache(prefix);
 	if (with_tree) {
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index cb61717..22008df 100644
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
@@ -23,7 +23,7 @@
 static const char *ls_tree_prefix;
 
 static const char ls_tree_usage[] =
-	"git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
+	"git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [path...]";
 
 static int show_recursive(const char *base, int baselen, const char *pathname)
 {
@@ -60,7 +60,6 @@
 {
 	int retval = 0;
 	const char *type = blob_type;
-	unsigned long size;
 
 	if (S_ISGITLINK(mode)) {
 		/*
@@ -68,13 +67,8 @@
 		 *
 		 * Something similar to this incomplete example:
 		 *
-		if (show_subprojects(base, baselen, pathname)) {
-			struct child_process ls_tree;
-
-			ls_tree.dir = base;
-			ls_tree.argv = ls-tree;
-			start_command(&ls_tree);
-		}
+		if (show_subprojects(base, baselen, pathname))
+			retval = READ_TREE_RECURSIVE;
 		 *
 		 */
 		type = commit_type;
@@ -95,17 +89,20 @@
 
 	if (!(ls_options & LS_NAME_ONLY)) {
 		if (ls_options & LS_SHOW_SIZE) {
+			char size_text[24];
 			if (!strcmp(type, blob_type)) {
-				sha1_object_info(sha1, &size);
-				printf("%06o %s %s %7lu\t", mode, type,
-				       abbrev ? find_unique_abbrev(sha1, abbrev)
-				              : sha1_to_hex(sha1),
-				       size);
+				unsigned long size;
+				if (sha1_object_info(sha1, &size) == OBJ_BAD)
+					strcpy(size_text, "BAD");
+				else
+					snprintf(size_text, sizeof(size_text),
+						 "%lu", size);
 			} else
-				printf("%06o %s %s %7c\t", mode, type,
-				       abbrev ? find_unique_abbrev(sha1, abbrev)
-				              : sha1_to_hex(sha1),
-				       '-');
+				strcpy(size_text, "-");
+			printf("%06o %s %s %7s\t", mode, type,
+			       abbrev ? find_unique_abbrev(sha1, abbrev)
+				      : sha1_to_hex(sha1),
+			       size_text);
 		} else
 			printf("%06o %s %s\t", mode, type,
 			       abbrev ? find_unique_abbrev(sha1, abbrev)
@@ -156,6 +153,11 @@
 				chomp_prefix = 0;
 				break;
 			}
+			if (!strcmp(argv[1]+2, "full-tree")) {
+				ls_tree_prefix = prefix = NULL;
+				chomp_prefix = 0;
+				break;
+			}
 			if (!prefixcmp(argv[1]+2, "abbrev=")) {
 				abbrev = strtoul(argv[1]+9, NULL, 10);
 				if (abbrev && abbrev < MINIMUM_ABBREV)
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index dacc8ac..1eeeb4d 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -29,6 +29,9 @@
 #define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
 
+static void cleanup_space(struct strbuf *sb);
+
+
 static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
 {
 	struct strbuf *src = name;
@@ -109,11 +112,19 @@
 	strbuf_add(&email, at, el);
 	strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
 
-	/* The remainder is name.  It could be "John Doe <john.doe@xz>"
-	 * or "john.doe@xz (John Doe)", but we have removed the
-	 * email part, so trim from both ends, possibly removing
-	 * the () pair at the end.
+	/* The remainder is name.  It could be
+	 *
+	 * - "John Doe <john.doe@xz>"			(a), or
+	 * - "john.doe@xz (John Doe)"			(b), or
+	 * - "John (zzz) Doe <john.doe@xz> (Comment)"	(c)
+	 *
+	 * but we have removed the email part, so
+	 *
+	 * - remove extra spaces which could stay after email (case 'c'), and
+	 * - trim from both ends, possibly removing the () pair at the end
+	 *   (cases 'a' and 'b').
 	 */
+	cleanup_space(&f);
 	strbuf_trim(&f);
 	if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
 		strbuf_remove(&f, 0, 1);
@@ -487,7 +498,7 @@
 		return;
 	out = reencode_string(line->buf, metainfo_charset, charset);
 	if (!out)
-		die("cannot convert from %s to %s\n",
+		die("cannot convert from %s to %s",
 		    charset, metainfo_charset);
 	strbuf_attach(line, out, strlen(out), strlen(out));
 }
@@ -526,7 +537,6 @@
 				 */
 				strbuf_add(&outbuf, in, ep - in);
 			}
-			in = ep;
 		}
 		/* E.g.
 		 * ep : "=?iso-2022-jp?B?GyR...?= foo"
diff --git a/builtin-merge-base.c b/builtin-merge-base.c
index 3382b13..03fc1c2 100644
--- a/builtin-merge-base.c
+++ b/builtin-merge-base.c
@@ -1,10 +1,13 @@
 #include "builtin.h"
 #include "cache.h"
 #include "commit.h"
+#include "parse-options.h"
 
-static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_all)
+static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-	struct commit_list *result = get_merge_bases(rev1, rev2, 0);
+	struct commit_list *result;
+
+	result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
 
 	if (!result)
 		return 1;
@@ -19,8 +22,10 @@
 	return 0;
 }
 
-static const char merge_base_usage[] =
-"git merge-base [--all] <commit-id> <commit-id>";
+static const char * const merge_base_usage[] = {
+	"git merge-base [--all] <commit-id> <commit-id>...",
+	NULL
+};
 
 static struct commit *get_commit_reference(const char *arg)
 {
@@ -38,23 +43,21 @@
 
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
 {
-	struct commit *rev1, *rev2;
+	struct commit **rev;
+	int rev_nr = 0;
 	int show_all = 0;
 
+	struct option options[] = {
+		OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"),
+		OPT_END()
+	};
+
 	git_config(git_default_config, NULL);
-
-	while (1 < argc && argv[1][0] == '-') {
-		const char *arg = argv[1];
-		if (!strcmp(arg, "-a") || !strcmp(arg, "--all"))
-			show_all = 1;
-		else
-			usage(merge_base_usage);
-		argc--; argv++;
-	}
-	if (argc != 3)
-		usage(merge_base_usage);
-	rev1 = get_commit_reference(argv[1]);
-	rev2 = get_commit_reference(argv[2]);
-
-	return show_merge_base(rev1, rev2, show_all);
+	argc = parse_options(argc, argv, options, merge_base_usage, 0);
+	if (argc < 2)
+		usage_with_options(merge_base_usage, options);
+	rev = xmalloc(argc * sizeof(*rev));
+	while (argc-- > 0)
+		rev[rev_nr++] = get_commit_reference(*argv++);
+	return show_merge_base(rev, rev_nr, show_all);
 }
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 3605960..96edb97 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -2,57 +2,79 @@
 #include "cache.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
+#include "parse-options.h"
 
-static const char merge_file_usage[] =
-"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+static const char *const merge_file_usage[] = {
+	"git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2",
+	NULL
+};
+
+static int label_cb(const struct option *opt, const char *arg, int unset)
+{
+	static int label_count = 0;
+	const char **names = (const char **)opt->value;
+
+	if (label_count >= 3)
+		return error("too many labels on the command line");
+	names[label_count++] = arg;
+	return 0;
+}
 
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
-	const char *names[3];
+	const char *names[3] = { NULL, NULL, NULL };
 	mmfile_t mmfs[3];
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
 	int ret = 0, i = 0, to_stdout = 0;
+	int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
+	int merge_style = 0, quiet = 0;
+	int nongit;
 
-	while (argc > 4) {
-		if (!strcmp(argv[1], "-L") && i < 3) {
-			names[i++] = argv[2];
-			argc--;
-			argv++;
-		} else if (!strcmp(argv[1], "-p") ||
-				!strcmp(argv[1], "--stdout"))
-			to_stdout = 1;
-		else if (!strcmp(argv[1], "-q") ||
-				!strcmp(argv[1], "--quiet"))
-			freopen("/dev/null", "w", stderr);
-		else
-			usage(merge_file_usage);
-		argc--;
-		argv++;
+	struct option options[] = {
+		OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
+		OPT_SET_INT(0, "diff3", &merge_style, "use a diff3 based merge", XDL_MERGE_DIFF3),
+		OPT__QUIET(&quiet),
+		OPT_CALLBACK('L', NULL, names, "name",
+			     "set labels for file1/orig_file/file2", &label_cb),
+		OPT_END(),
+	};
+
+	prefix = setup_git_directory_gently(&nongit);
+	if (!nongit) {
+		/* Read the configuration file */
+		git_config(git_xmerge_config, NULL);
+		if (0 <= git_xmerge_style)
+			merge_style = git_xmerge_style;
 	}
 
-	if (argc != 4)
-		usage(merge_file_usage);
-
-	for (; i < 3; i++)
-		names[i] = argv[i + 1];
+	argc = parse_options(argc, argv, options, merge_file_usage, 0);
+	if (argc != 3)
+		usage_with_options(merge_file_usage, options);
+	if (quiet) {
+		if (!freopen("/dev/null", "w", stderr))
+			return error("failed to redirect stderr to /dev/null: "
+				     "%s\n", strerror(errno));
+	}
 
 	for (i = 0; i < 3; i++) {
-		if (read_mmfile(mmfs + i, argv[i + 1]))
+		if (!names[i])
+			names[i] = argv[i];
+		if (read_mmfile(mmfs + i, argv[i]))
 			return -1;
 		if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
 			return error("Cannot merge binary files: %s\n",
-					argv[i + 1]);
+					argv[i]);
 	}
 
 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-			&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
+			&xpp, merge_level | merge_style, &result);
 
 	for (i = 0; i < 3; i++)
 		free(mmfs[i].ptr);
 
 	if (ret >= 0) {
-		const char *filename = argv[1];
+		const char *filename = argv[0];
 		FILE *f = to_stdout ? stdout : fopen(filename, "wb");
 
 		if (!f)
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index b973865..703045b 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -1,1288 +1,8 @@
-/*
- * Recursive Merge algorithm stolen from git-merge-recursive.py by
- * Fredrik Kuivinen.
- * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
- */
 #include "cache.h"
-#include "cache-tree.h"
 #include "commit.h"
-#include "blob.h"
-#include "builtin.h"
-#include "tree-walk.h"
-#include "diff.h"
-#include "diffcore.h"
 #include "tag.h"
-#include "unpack-trees.h"
-#include "string-list.h"
-#include "xdiff-interface.h"
-#include "ll-merge.h"
-#include "interpolate.h"
-#include "attr.h"
-#include "dir.h"
 #include "merge-recursive.h"
 
-static int subtree_merge;
-
-static struct tree *shift_tree_object(struct tree *one, struct tree *two)
-{
-	unsigned char shifted[20];
-
-	/*
-	 * NEEDSWORK: this limits the recursion depth to hardcoded
-	 * value '2' to avoid excessive overhead.
-	 */
-	shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
-	if (!hashcmp(two->object.sha1, shifted))
-		return two;
-	return lookup_tree(shifted);
-}
-
-/*
- * A virtual commit has
- * - (const char *)commit->util set to the name, and
- * - *(int *)commit->object.sha1 set to the virtual id.
- */
-
-static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
-{
-	struct commit *commit = xcalloc(1, sizeof(struct commit));
-	static unsigned virtual_id = 1;
-	commit->tree = tree;
-	commit->util = (void*)comment;
-	*(int*)commit->object.sha1 = virtual_id++;
-	/* avoid warnings */
-	commit->object.parsed = 1;
-	return commit;
-}
-
-/*
- * Since we use get_tree_entry(), which does not put the read object into
- * the object pool, we cannot rely on a == b.
- */
-static int sha_eq(const unsigned char *a, const unsigned char *b)
-{
-	if (!a && !b)
-		return 2;
-	return a && b && hashcmp(a, b) == 0;
-}
-
-/*
- * Since we want to write the index eventually, we cannot reuse the index
- * for these (temporary) data.
- */
-struct stage_data
-{
-	struct
-	{
-		unsigned mode;
-		unsigned char sha[20];
-	} stages[4];
-	unsigned processed:1;
-};
-
-static struct string_list current_file_set = {NULL, 0, 0, 1};
-static struct string_list current_directory_set = {NULL, 0, 0, 1};
-
-static int call_depth = 0;
-static int verbosity = 2;
-static int diff_rename_limit = -1;
-static int merge_rename_limit = -1;
-static int buffer_output = 1;
-static struct strbuf obuf = STRBUF_INIT;
-
-static int show(int v)
-{
-	return (!call_depth && verbosity >= v) || verbosity >= 5;
-}
-
-static void flush_output(void)
-{
-	if (obuf.len) {
-		fputs(obuf.buf, stdout);
-		strbuf_reset(&obuf);
-	}
-}
-
-static void output(int v, const char *fmt, ...)
-{
-	int len;
-	va_list ap;
-
-	if (!show(v))
-		return;
-
-	strbuf_grow(&obuf, call_depth * 2 + 2);
-	memset(obuf.buf + obuf.len, ' ', call_depth * 2);
-	strbuf_setlen(&obuf, obuf.len + call_depth * 2);
-
-	va_start(ap, fmt);
-	len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap);
-	va_end(ap);
-
-	if (len < 0)
-		len = 0;
-	if (len >= strbuf_avail(&obuf)) {
-		strbuf_grow(&obuf, len + 2);
-		va_start(ap, fmt);
-		len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap);
-		va_end(ap);
-		if (len >= strbuf_avail(&obuf)) {
-			die("this should not happen, your snprintf is broken");
-		}
-	}
-	strbuf_setlen(&obuf, obuf.len + len);
-	strbuf_add(&obuf, "\n", 1);
-	if (!buffer_output)
-		flush_output();
-}
-
-static void output_commit_title(struct commit *commit)
-{
-	int i;
-	flush_output();
-	for (i = call_depth; i--;)
-		fputs("  ", stdout);
-	if (commit->util)
-		printf("virtual %s\n", (char *)commit->util);
-	else {
-		printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
-		if (parse_commit(commit) != 0)
-			printf("(bad commit)\n");
-		else {
-			const char *s;
-			int len;
-			for (s = commit->buffer; *s; s++)
-				if (*s == '\n' && s[1] == '\n') {
-					s += 2;
-					break;
-				}
-			for (len = 0; s[len] && '\n' != s[len]; len++)
-				; /* do nothing */
-			printf("%.*s\n", len, s);
-		}
-	}
-}
-
-static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
-		const char *path, int stage, int refresh, int options)
-{
-	struct cache_entry *ce;
-	ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
-	if (!ce)
-		return error("addinfo_cache failed for path '%s'", path);
-	return add_cache_entry(ce, options);
-}
-
-/*
- * This is a global variable which is used in a number of places but
- * only written to in the 'merge' function.
- *
- * index_only == 1    => Don't leave any non-stage 0 entries in the cache and
- *                       don't update the working directory.
- *               0    => Leave unmerged entries in the cache and update
- *                       the working directory.
- */
-static int index_only = 0;
-
-static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
-{
-	parse_tree(tree);
-	init_tree_desc(desc, tree->buffer, tree->size);
-}
-
-static int git_merge_trees(int index_only,
-			   struct tree *common,
-			   struct tree *head,
-			   struct tree *merge)
-{
-	int rc;
-	struct tree_desc t[3];
-	struct unpack_trees_options opts;
-
-	memset(&opts, 0, sizeof(opts));
-	if (index_only)
-		opts.index_only = 1;
-	else
-		opts.update = 1;
-	opts.merge = 1;
-	opts.head_idx = 2;
-	opts.fn = threeway_merge;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-
-	init_tree_desc_from_tree(t+0, common);
-	init_tree_desc_from_tree(t+1, head);
-	init_tree_desc_from_tree(t+2, merge);
-
-	rc = unpack_trees(3, t, &opts);
-	cache_tree_free(&active_cache_tree);
-	return rc;
-}
-
-struct tree *write_tree_from_memory(void)
-{
-	struct tree *result = NULL;
-
-	if (unmerged_cache()) {
-		int i;
-		output(0, "There are unmerged index entries:");
-		for (i = 0; i < active_nr; i++) {
-			struct cache_entry *ce = active_cache[i];
-			if (ce_stage(ce))
-				output(0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
-		}
-		return NULL;
-	}
-
-	if (!active_cache_tree)
-		active_cache_tree = cache_tree();
-
-	if (!cache_tree_fully_valid(active_cache_tree) &&
-	    cache_tree_update(active_cache_tree,
-			      active_cache, active_nr, 0, 0) < 0)
-		die("error building trees");
-
-	result = lookup_tree(active_cache_tree->sha1);
-
-	return result;
-}
-
-static int save_files_dirs(const unsigned char *sha1,
-		const char *base, int baselen, const char *path,
-		unsigned int mode, int stage, void *context)
-{
-	int len = strlen(path);
-	char *newpath = xmalloc(baselen + len + 1);
-	memcpy(newpath, base, baselen);
-	memcpy(newpath + baselen, path, len);
-	newpath[baselen + len] = '\0';
-
-	if (S_ISDIR(mode))
-		string_list_insert(newpath, &current_directory_set);
-	else
-		string_list_insert(newpath, &current_file_set);
-	free(newpath);
-
-	return READ_TREE_RECURSIVE;
-}
-
-static int get_files_dirs(struct tree *tree)
-{
-	int n;
-	if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs, NULL))
-		return 0;
-	n = current_file_set.nr + current_directory_set.nr;
-	return n;
-}
-
-/*
- * Returns an index_entry instance which doesn't have to correspond to
- * a real cache entry in Git's index.
- */
-static struct stage_data *insert_stage_data(const char *path,
-		struct tree *o, struct tree *a, struct tree *b,
-		struct string_list *entries)
-{
-	struct string_list_item *item;
-	struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-	get_tree_entry(o->object.sha1, path,
-			e->stages[1].sha, &e->stages[1].mode);
-	get_tree_entry(a->object.sha1, path,
-			e->stages[2].sha, &e->stages[2].mode);
-	get_tree_entry(b->object.sha1, path,
-			e->stages[3].sha, &e->stages[3].mode);
-	item = string_list_insert(path, entries);
-	item->util = e;
-	return e;
-}
-
-/*
- * Create a dictionary mapping file names to stage_data objects. The
- * dictionary contains one entry for every path with a non-zero stage entry.
- */
-static struct string_list *get_unmerged(void)
-{
-	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
-	int i;
-
-	unmerged->strdup_strings = 1;
-
-	for (i = 0; i < active_nr; i++) {
-		struct string_list_item *item;
-		struct stage_data *e;
-		struct cache_entry *ce = active_cache[i];
-		if (!ce_stage(ce))
-			continue;
-
-		item = string_list_lookup(ce->name, unmerged);
-		if (!item) {
-			item = string_list_insert(ce->name, unmerged);
-			item->util = xcalloc(1, sizeof(struct stage_data));
-		}
-		e = item->util;
-		e->stages[ce_stage(ce)].mode = ce->ce_mode;
-		hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
-	}
-
-	return unmerged;
-}
-
-struct rename
-{
-	struct diff_filepair *pair;
-	struct stage_data *src_entry;
-	struct stage_data *dst_entry;
-	unsigned processed:1;
-};
-
-/*
- * Get information of all renames which occurred between 'o_tree' and
- * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and
- * 'b_tree') to be able to associate the correct cache entries with
- * the rename information. 'tree' is always equal to either a_tree or b_tree.
- */
-static struct string_list *get_renames(struct tree *tree,
-					struct tree *o_tree,
-					struct tree *a_tree,
-					struct tree *b_tree,
-					struct string_list *entries)
-{
-	int i;
-	struct string_list *renames;
-	struct diff_options opts;
-
-	renames = xcalloc(1, sizeof(struct string_list));
-	diff_setup(&opts);
-	DIFF_OPT_SET(&opts, RECURSIVE);
-	opts.detect_rename = DIFF_DETECT_RENAME;
-	opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit :
-			    diff_rename_limit >= 0 ? diff_rename_limit :
-			    500;
-	opts.warn_on_too_large_rename = 1;
-	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-	if (diff_setup_done(&opts) < 0)
-		die("diff setup failed");
-	diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
-	diffcore_std(&opts);
-	for (i = 0; i < diff_queued_diff.nr; ++i) {
-		struct string_list_item *item;
-		struct rename *re;
-		struct diff_filepair *pair = diff_queued_diff.queue[i];
-		if (pair->status != 'R') {
-			diff_free_filepair(pair);
-			continue;
-		}
-		re = xmalloc(sizeof(*re));
-		re->processed = 0;
-		re->pair = pair;
-		item = string_list_lookup(re->pair->one->path, entries);
-		if (!item)
-			re->src_entry = insert_stage_data(re->pair->one->path,
-					o_tree, a_tree, b_tree, entries);
-		else
-			re->src_entry = item->util;
-
-		item = string_list_lookup(re->pair->two->path, entries);
-		if (!item)
-			re->dst_entry = insert_stage_data(re->pair->two->path,
-					o_tree, a_tree, b_tree, entries);
-		else
-			re->dst_entry = item->util;
-		item = string_list_insert(pair->one->path, renames);
-		item->util = re;
-	}
-	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-	diff_queued_diff.nr = 0;
-	diff_flush(&opts);
-	return renames;
-}
-
-static int update_stages(const char *path, struct diff_filespec *o,
-			 struct diff_filespec *a, struct diff_filespec *b,
-			 int clear)
-{
-	int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
-	if (clear)
-		if (remove_file_from_cache(path))
-			return -1;
-	if (o)
-		if (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))
-			return -1;
-	if (a)
-		if (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))
-			return -1;
-	if (b)
-		if (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))
-			return -1;
-	return 0;
-}
-
-static int remove_file(int clean, const char *path, int no_wd)
-{
-	int update_cache = index_only || clean;
-	int update_working_directory = !index_only && !no_wd;
-
-	if (update_cache) {
-		if (remove_file_from_cache(path))
-			return -1;
-	}
-	if (update_working_directory) {
-		if (remove_path(path))
-			return -1;
-	}
-	return 0;
-}
-
-static char *unique_path(const char *path, const char *branch)
-{
-	char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
-	int suffix = 0;
-	struct stat st;
-	char *p = newpath + strlen(path);
-	strcpy(newpath, path);
-	*(p++) = '~';
-	strcpy(p, branch);
-	for (; *p; ++p)
-		if ('/' == *p)
-			*p = '_';
-	while (string_list_has_string(&current_file_set, newpath) ||
-	       string_list_has_string(&current_directory_set, newpath) ||
-	       lstat(newpath, &st) == 0)
-		sprintf(p, "_%d", suffix++);
-
-	string_list_insert(newpath, &current_file_set);
-	return newpath;
-}
-
-static void flush_buffer(int fd, const char *buf, unsigned long size)
-{
-	while (size > 0) {
-		long ret = write_in_full(fd, buf, size);
-		if (ret < 0) {
-			/* Ignore epipe */
-			if (errno == EPIPE)
-				break;
-			die("merge-recursive: %s", strerror(errno));
-		} else if (!ret) {
-			die("merge-recursive: disk full?");
-		}
-		size -= ret;
-		buf += ret;
-	}
-}
-
-static int make_room_for_path(const char *path)
-{
-	int status;
-	const char *msg = "failed to create path '%s'%s";
-
-	status = safe_create_leading_directories_const(path);
-	if (status) {
-		if (status == -3) {
-			/* something else exists */
-			error(msg, path, ": perhaps a D/F conflict?");
-			return -1;
-		}
-		die(msg, path, "");
-	}
-
-	/* Successful unlink is good.. */
-	if (!unlink(path))
-		return 0;
-	/* .. and so is no existing file */
-	if (errno == ENOENT)
-		return 0;
-	/* .. but not some other error (who really cares what?) */
-	return error(msg, path, ": perhaps a D/F conflict?");
-}
-
-static void update_file_flags(const unsigned char *sha,
-			      unsigned mode,
-			      const char *path,
-			      int update_cache,
-			      int update_wd)
-{
-	if (index_only)
-		update_wd = 0;
-
-	if (update_wd) {
-		enum object_type type;
-		void *buf;
-		unsigned long size;
-
-		if (S_ISGITLINK(mode))
-			die("cannot read object %s '%s': It is a submodule!",
-			    sha1_to_hex(sha), path);
-
-		buf = read_sha1_file(sha, &type, &size);
-		if (!buf)
-			die("cannot read object %s '%s'", sha1_to_hex(sha), path);
-		if (type != OBJ_BLOB)
-			die("blob expected for %s '%s'", sha1_to_hex(sha), path);
-		if (S_ISREG(mode)) {
-			struct strbuf strbuf;
-			strbuf_init(&strbuf, 0);
-			if (convert_to_working_tree(path, buf, size, &strbuf)) {
-				free(buf);
-				size = strbuf.len;
-				buf = strbuf_detach(&strbuf, NULL);
-			}
-		}
-
-		if (make_room_for_path(path) < 0) {
-			update_wd = 0;
-			free(buf);
-			goto update_index;
-		}
-		if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
-			int fd;
-			if (mode & 0100)
-				mode = 0777;
-			else
-				mode = 0666;
-			fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
-			if (fd < 0)
-				die("failed to open %s: %s", path, strerror(errno));
-			flush_buffer(fd, buf, size);
-			close(fd);
-		} else if (S_ISLNK(mode)) {
-			char *lnk = xmemdupz(buf, size);
-			safe_create_leading_directories_const(path);
-			unlink(path);
-			symlink(lnk, path);
-			free(lnk);
-		} else
-			die("do not know what to do with %06o %s '%s'",
-			    mode, sha1_to_hex(sha), path);
-		free(buf);
-	}
- update_index:
-	if (update_cache)
-		add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
-}
-
-static void update_file(int clean,
-			const unsigned char *sha,
-			unsigned mode,
-			const char *path)
-{
-	update_file_flags(sha, mode, path, index_only || clean, !index_only);
-}
-
-/* Low level file merging, update and removal */
-
-struct merge_file_info
-{
-	unsigned char sha[20];
-	unsigned mode;
-	unsigned clean:1,
-		 merge:1;
-};
-
-static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
-{
-	unsigned long size;
-	enum object_type type;
-
-	if (!hashcmp(sha1, null_sha1)) {
-		mm->ptr = xstrdup("");
-		mm->size = 0;
-		return;
-	}
-
-	mm->ptr = read_sha1_file(sha1, &type, &size);
-	if (!mm->ptr || type != OBJ_BLOB)
-		die("unable to read blob object %s", sha1_to_hex(sha1));
-	mm->size = size;
-}
-
-static int merge_3way(mmbuffer_t *result_buf,
-		      struct diff_filespec *o,
-		      struct diff_filespec *a,
-		      struct diff_filespec *b,
-		      const char *branch1,
-		      const char *branch2)
-{
-	mmfile_t orig, src1, src2;
-	char *name1, *name2;
-	int merge_status;
-
-	name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
-	name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
-
-	fill_mm(o->sha1, &orig);
-	fill_mm(a->sha1, &src1);
-	fill_mm(b->sha1, &src2);
-
-	merge_status = ll_merge(result_buf, a->path, &orig,
-				&src1, name1, &src2, name2,
-				index_only);
-
-	free(name1);
-	free(name2);
-	free(orig.ptr);
-	free(src1.ptr);
-	free(src2.ptr);
-	return merge_status;
-}
-
-static struct merge_file_info merge_file(struct diff_filespec *o,
-		struct diff_filespec *a, struct diff_filespec *b,
-		const char *branch1, const char *branch2)
-{
-	struct merge_file_info result;
-	result.merge = 0;
-	result.clean = 1;
-
-	if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
-		result.clean = 0;
-		if (S_ISREG(a->mode)) {
-			result.mode = a->mode;
-			hashcpy(result.sha, a->sha1);
-		} else {
-			result.mode = b->mode;
-			hashcpy(result.sha, b->sha1);
-		}
-	} else {
-		if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
-			result.merge = 1;
-
-		/*
-		 * Merge modes
-		 */
-		if (a->mode == b->mode || a->mode == o->mode)
-			result.mode = b->mode;
-		else {
-			result.mode = a->mode;
-			if (b->mode != o->mode) {
-				result.clean = 0;
-				result.merge = 1;
-			}
-		}
-
-		if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, o->sha1))
-			hashcpy(result.sha, b->sha1);
-		else if (sha_eq(b->sha1, o->sha1))
-			hashcpy(result.sha, a->sha1);
-		else if (S_ISREG(a->mode)) {
-			mmbuffer_t result_buf;
-			int merge_status;
-
-			merge_status = merge_3way(&result_buf, o, a, b,
-						  branch1, branch2);
-
-			if ((merge_status < 0) || !result_buf.ptr)
-				die("Failed to execute internal merge");
-
-			if (write_sha1_file(result_buf.ptr, result_buf.size,
-					    blob_type, result.sha))
-				die("Unable to add %s to database",
-				    a->path);
-
-			free(result_buf.ptr);
-			result.clean = (merge_status == 0);
-		} else if (S_ISGITLINK(a->mode)) {
-			result.clean = 0;
-			hashcpy(result.sha, a->sha1);
-		} else if (S_ISLNK(a->mode)) {
-			hashcpy(result.sha, a->sha1);
-
-			if (!sha_eq(a->sha1, b->sha1))
-				result.clean = 0;
-		} else {
-			die("unsupported object type in the tree");
-		}
-	}
-
-	return result;
-}
-
-static void conflict_rename_rename(struct rename *ren1,
-				   const char *branch1,
-				   struct rename *ren2,
-				   const char *branch2)
-{
-	char *del[2];
-	int delp = 0;
-	const char *ren1_dst = ren1->pair->two->path;
-	const char *ren2_dst = ren2->pair->two->path;
-	const char *dst_name1 = ren1_dst;
-	const char *dst_name2 = ren2_dst;
-	if (string_list_has_string(&current_directory_set, ren1_dst)) {
-		dst_name1 = del[delp++] = unique_path(ren1_dst, branch1);
-		output(1, "%s is a directory in %s added as %s instead",
-		       ren1_dst, branch2, dst_name1);
-		remove_file(0, ren1_dst, 0);
-	}
-	if (string_list_has_string(&current_directory_set, ren2_dst)) {
-		dst_name2 = del[delp++] = unique_path(ren2_dst, branch2);
-		output(1, "%s is a directory in %s added as %s instead",
-		       ren2_dst, branch1, dst_name2);
-		remove_file(0, ren2_dst, 0);
-	}
-	if (index_only) {
-		remove_file_from_cache(dst_name1);
-		remove_file_from_cache(dst_name2);
-		/*
-		 * Uncomment to leave the conflicting names in the resulting tree
-		 *
-		 * update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1);
-		 * update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2);
-		 */
-	} else {
-		update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
-		update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
-	}
-	while (delp--)
-		free(del[delp]);
-}
-
-static void conflict_rename_dir(struct rename *ren1,
-				const char *branch1)
-{
-	char *new_path = unique_path(ren1->pair->two->path, branch1);
-	output(1, "Renamed %s to %s instead", ren1->pair->one->path, new_path);
-	remove_file(0, ren1->pair->two->path, 0);
-	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
-	free(new_path);
-}
-
-static void conflict_rename_rename_2(struct rename *ren1,
-				     const char *branch1,
-				     struct rename *ren2,
-				     const char *branch2)
-{
-	char *new_path1 = unique_path(ren1->pair->two->path, branch1);
-	char *new_path2 = unique_path(ren2->pair->two->path, branch2);
-	output(1, "Renamed %s to %s and %s to %s instead",
-	       ren1->pair->one->path, new_path1,
-	       ren2->pair->one->path, new_path2);
-	remove_file(0, ren1->pair->two->path, 0);
-	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
-	update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
-	free(new_path2);
-	free(new_path1);
-}
-
-static int process_renames(struct string_list *a_renames,
-			   struct string_list *b_renames,
-			   const char *a_branch,
-			   const char *b_branch)
-{
-	int clean_merge = 1, i, j;
-	struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
-	const struct rename *sre;
-
-	for (i = 0; i < a_renames->nr; i++) {
-		sre = a_renames->items[i].util;
-		string_list_insert(sre->pair->two->path, &a_by_dst)->util
-			= sre->dst_entry;
-	}
-	for (i = 0; i < b_renames->nr; i++) {
-		sre = b_renames->items[i].util;
-		string_list_insert(sre->pair->two->path, &b_by_dst)->util
-			= sre->dst_entry;
-	}
-
-	for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
-		int compare;
-		char *src;
-		struct string_list *renames1, *renames2, *renames2Dst;
-		struct rename *ren1 = NULL, *ren2 = NULL;
-		const char *branch1, *branch2;
-		const char *ren1_src, *ren1_dst;
-
-		if (i >= a_renames->nr) {
-			compare = 1;
-			ren2 = b_renames->items[j++].util;
-		} else if (j >= b_renames->nr) {
-			compare = -1;
-			ren1 = a_renames->items[i++].util;
-		} else {
-			compare = strcmp(a_renames->items[i].string,
-					b_renames->items[j].string);
-			if (compare <= 0)
-				ren1 = a_renames->items[i++].util;
-			if (compare >= 0)
-				ren2 = b_renames->items[j++].util;
-		}
-
-		/* TODO: refactor, so that 1/2 are not needed */
-		if (ren1) {
-			renames1 = a_renames;
-			renames2 = b_renames;
-			renames2Dst = &b_by_dst;
-			branch1 = a_branch;
-			branch2 = b_branch;
-		} else {
-			struct rename *tmp;
-			renames1 = b_renames;
-			renames2 = a_renames;
-			renames2Dst = &a_by_dst;
-			branch1 = b_branch;
-			branch2 = a_branch;
-			tmp = ren2;
-			ren2 = ren1;
-			ren1 = tmp;
-		}
-		src = ren1->pair->one->path;
-
-		ren1->dst_entry->processed = 1;
-		ren1->src_entry->processed = 1;
-
-		if (ren1->processed)
-			continue;
-		ren1->processed = 1;
-
-		ren1_src = ren1->pair->one->path;
-		ren1_dst = ren1->pair->two->path;
-
-		if (ren2) {
-			const char *ren2_src = ren2->pair->one->path;
-			const char *ren2_dst = ren2->pair->two->path;
-			/* Renamed in 1 and renamed in 2 */
-			if (strcmp(ren1_src, ren2_src) != 0)
-				die("ren1.src != ren2.src");
-			ren2->dst_entry->processed = 1;
-			ren2->processed = 1;
-			if (strcmp(ren1_dst, ren2_dst) != 0) {
-				clean_merge = 0;
-				output(1, "CONFLICT (rename/rename): "
-				       "Rename \"%s\"->\"%s\" in branch \"%s\" "
-				       "rename \"%s\"->\"%s\" in \"%s\"%s",
-				       src, ren1_dst, branch1,
-				       src, ren2_dst, branch2,
-				       index_only ? " (left unresolved)": "");
-				if (index_only) {
-					remove_file_from_cache(src);
-					update_file(0, ren1->pair->one->sha1,
-						    ren1->pair->one->mode, src);
-				}
-				conflict_rename_rename(ren1, branch1, ren2, branch2);
-			} else {
-				struct merge_file_info mfi;
-				remove_file(1, ren1_src, 1);
-				mfi = merge_file(ren1->pair->one,
-						 ren1->pair->two,
-						 ren2->pair->two,
-						 branch1,
-						 branch2);
-				if (mfi.merge || !mfi.clean)
-					output(1, "Renamed %s->%s", src, ren1_dst);
-
-				if (mfi.merge)
-					output(2, "Auto-merged %s", ren1_dst);
-
-				if (!mfi.clean) {
-					output(1, "CONFLICT (content): merge conflict in %s",
-					       ren1_dst);
-					clean_merge = 0;
-
-					if (!index_only)
-						update_stages(ren1_dst,
-							      ren1->pair->one,
-							      ren1->pair->two,
-							      ren2->pair->two,
-							      1 /* clear */);
-				}
-				update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
-			}
-		} else {
-			/* Renamed in 1, maybe changed in 2 */
-			struct string_list_item *item;
-			/* we only use sha1 and mode of these */
-			struct diff_filespec src_other, dst_other;
-			int try_merge, stage = a_renames == renames1 ? 3: 2;
-
-			remove_file(1, ren1_src, index_only || stage == 3);
-
-			hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
-			src_other.mode = ren1->src_entry->stages[stage].mode;
-			hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha);
-			dst_other.mode = ren1->dst_entry->stages[stage].mode;
-
-			try_merge = 0;
-
-			if (string_list_has_string(&current_directory_set, ren1_dst)) {
-				clean_merge = 0;
-				output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s "
-				       " directory %s added in %s",
-				       ren1_src, ren1_dst, branch1,
-				       ren1_dst, branch2);
-				conflict_rename_dir(ren1, branch1);
-			} else if (sha_eq(src_other.sha1, null_sha1)) {
-				clean_merge = 0;
-				output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s "
-				       "and deleted in %s",
-				       ren1_src, ren1_dst, branch1,
-				       branch2);
-				update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
-			} else if (!sha_eq(dst_other.sha1, null_sha1)) {
-				const char *new_path;
-				clean_merge = 0;
-				try_merge = 1;
-				output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. "
-				       "%s added in %s",
-				       ren1_src, ren1_dst, branch1,
-				       ren1_dst, branch2);
-				new_path = unique_path(ren1_dst, branch2);
-				output(1, "Added as %s instead", new_path);
-				update_file(0, dst_other.sha1, dst_other.mode, new_path);
-			} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
-				ren2 = item->util;
-				clean_merge = 0;
-				ren2->processed = 1;
-				output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. "
-				       "Renamed %s->%s in %s",
-				       ren1_src, ren1_dst, branch1,
-				       ren2->pair->one->path, ren2->pair->two->path, branch2);
-				conflict_rename_rename_2(ren1, branch1, ren2, branch2);
-			} else
-				try_merge = 1;
-
-			if (try_merge) {
-				struct diff_filespec *o, *a, *b;
-				struct merge_file_info mfi;
-				src_other.path = (char *)ren1_src;
-
-				o = ren1->pair->one;
-				if (a_renames == renames1) {
-					a = ren1->pair->two;
-					b = &src_other;
-				} else {
-					b = ren1->pair->two;
-					a = &src_other;
-				}
-				mfi = merge_file(o, a, b,
-						a_branch, b_branch);
-
-				if (mfi.clean &&
-				    sha_eq(mfi.sha, ren1->pair->two->sha1) &&
-				    mfi.mode == ren1->pair->two->mode)
-					/*
-					 * This messaged is part of
-					 * t6022 test. If you change
-					 * it update the test too.
-					 */
-					output(3, "Skipped %s (merged same as existing)", ren1_dst);
-				else {
-					if (mfi.merge || !mfi.clean)
-						output(1, "Renamed %s => %s", ren1_src, ren1_dst);
-					if (mfi.merge)
-						output(2, "Auto-merged %s", ren1_dst);
-					if (!mfi.clean) {
-						output(1, "CONFLICT (rename/modify): Merge conflict in %s",
-						       ren1_dst);
-						clean_merge = 0;
-
-						if (!index_only)
-							update_stages(ren1_dst,
-								      o, a, b, 1);
-					}
-					update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
-				}
-			}
-		}
-	}
-	string_list_clear(&a_by_dst, 0);
-	string_list_clear(&b_by_dst, 0);
-
-	return clean_merge;
-}
-
-static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
-{
-	return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
-}
-
-/* Per entry merge function */
-static int process_entry(const char *path, struct stage_data *entry,
-			 const char *branch1,
-			 const char *branch2)
-{
-	/*
-	printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
-	print_index_entry("\tpath: ", entry);
-	*/
-	int clean_merge = 1;
-	unsigned o_mode = entry->stages[1].mode;
-	unsigned a_mode = entry->stages[2].mode;
-	unsigned b_mode = entry->stages[3].mode;
-	unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
-	unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
-	unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
-
-	if (o_sha && (!a_sha || !b_sha)) {
-		/* Case A: Deleted in one */
-		if ((!a_sha && !b_sha) ||
-		    (sha_eq(a_sha, o_sha) && !b_sha) ||
-		    (!a_sha && sha_eq(b_sha, o_sha))) {
-			/* Deleted in both or deleted in one and
-			 * unchanged in the other */
-			if (a_sha)
-				output(2, "Removed %s", path);
-			/* do not touch working file if it did not exist */
-			remove_file(1, path, !a_sha);
-		} else {
-			/* Deleted in one and changed in the other */
-			clean_merge = 0;
-			if (!a_sha) {
-				output(1, "CONFLICT (delete/modify): %s deleted in %s "
-				       "and modified in %s. Version %s of %s left in tree.",
-				       path, branch1,
-				       branch2, branch2, path);
-				update_file(0, b_sha, b_mode, path);
-			} else {
-				output(1, "CONFLICT (delete/modify): %s deleted in %s "
-				       "and modified in %s. Version %s of %s left in tree.",
-				       path, branch2,
-				       branch1, branch1, path);
-				update_file(0, a_sha, a_mode, path);
-			}
-		}
-
-	} else if ((!o_sha && a_sha && !b_sha) ||
-		   (!o_sha && !a_sha && b_sha)) {
-		/* Case B: Added in one. */
-		const char *add_branch;
-		const char *other_branch;
-		unsigned mode;
-		const unsigned char *sha;
-		const char *conf;
-
-		if (a_sha) {
-			add_branch = branch1;
-			other_branch = branch2;
-			mode = a_mode;
-			sha = a_sha;
-			conf = "file/directory";
-		} else {
-			add_branch = branch2;
-			other_branch = branch1;
-			mode = b_mode;
-			sha = b_sha;
-			conf = "directory/file";
-		}
-		if (string_list_has_string(&current_directory_set, path)) {
-			const char *new_path = unique_path(path, add_branch);
-			clean_merge = 0;
-			output(1, "CONFLICT (%s): There is a directory with name %s in %s. "
-			       "Added %s as %s",
-			       conf, path, other_branch, path, new_path);
-			remove_file(0, path, 0);
-			update_file(0, sha, mode, new_path);
-		} else {
-			output(2, "Added %s", path);
-			update_file(1, sha, mode, path);
-		}
-	} else if (a_sha && b_sha) {
-		/* Case C: Added in both (check for same permissions) and */
-		/* case D: Modified in both, but differently. */
-		const char *reason = "content";
-		struct merge_file_info mfi;
-		struct diff_filespec o, a, b;
-
-		if (!o_sha) {
-			reason = "add/add";
-			o_sha = (unsigned char *)null_sha1;
-		}
-		output(2, "Auto-merged %s", path);
-		o.path = a.path = b.path = (char *)path;
-		hashcpy(o.sha1, o_sha);
-		o.mode = o_mode;
-		hashcpy(a.sha1, a_sha);
-		a.mode = a_mode;
-		hashcpy(b.sha1, b_sha);
-		b.mode = b_mode;
-
-		mfi = merge_file(&o, &a, &b,
-				 branch1, branch2);
-
-		clean_merge = mfi.clean;
-		if (mfi.clean)
-			update_file(1, mfi.sha, mfi.mode, path);
-		else if (S_ISGITLINK(mfi.mode))
-			output(1, "CONFLICT (submodule): Merge conflict in %s "
-			       "- needs %s", path, sha1_to_hex(b.sha1));
-		else {
-			output(1, "CONFLICT (%s): Merge conflict in %s",
-					reason, path);
-
-			if (index_only)
-				update_file(0, mfi.sha, mfi.mode, path);
-			else
-				update_file_flags(mfi.sha, mfi.mode, path,
-					      0 /* update_cache */, 1 /* update_working_directory */);
-		}
-	} else if (!o_sha && !a_sha && !b_sha) {
-		/*
-		 * this entry was deleted altogether. a_mode == 0 means
-		 * we had that path and want to actively remove it.
-		 */
-		remove_file(1, path, !a_mode);
-	} else
-		die("Fatal merge failure, shouldn't happen.");
-
-	return clean_merge;
-}
-
-int merge_trees(struct tree *head,
-		struct tree *merge,
-		struct tree *common,
-		const char *branch1,
-		const char *branch2,
-		struct tree **result)
-{
-	int code, clean;
-
-	if (subtree_merge) {
-		merge = shift_tree_object(head, merge);
-		common = shift_tree_object(head, common);
-	}
-
-	if (sha_eq(common->object.sha1, merge->object.sha1)) {
-		output(0, "Already uptodate!");
-		*result = head;
-		return 1;
-	}
-
-	code = git_merge_trees(index_only, common, head, merge);
-
-	if (code != 0)
-		die("merging of trees %s and %s failed",
-		    sha1_to_hex(head->object.sha1),
-		    sha1_to_hex(merge->object.sha1));
-
-	if (unmerged_cache()) {
-		struct string_list *entries, *re_head, *re_merge;
-		int i;
-		string_list_clear(&current_file_set, 1);
-		string_list_clear(&current_directory_set, 1);
-		get_files_dirs(head);
-		get_files_dirs(merge);
-
-		entries = get_unmerged();
-		re_head  = get_renames(head, common, head, merge, entries);
-		re_merge = get_renames(merge, common, head, merge, entries);
-		clean = process_renames(re_head, re_merge,
-				branch1, branch2);
-		for (i = 0; i < entries->nr; i++) {
-			const char *path = entries->items[i].string;
-			struct stage_data *e = entries->items[i].util;
-			if (!e->processed
-				&& !process_entry(path, e, branch1, branch2))
-				clean = 0;
-		}
-
-		string_list_clear(re_merge, 0);
-		string_list_clear(re_head, 0);
-		string_list_clear(entries, 1);
-
-	}
-	else
-		clean = 1;
-
-	if (index_only)
-		*result = write_tree_from_memory();
-
-	return clean;
-}
-
-static struct commit_list *reverse_commit_list(struct commit_list *list)
-{
-	struct commit_list *next = NULL, *current, *backup;
-	for (current = list; current; current = backup) {
-		backup = current->next;
-		current->next = next;
-		next = current;
-	}
-	return next;
-}
-
-/*
- * Merge the commits h1 and h2, return the resulting virtual
- * commit object and a flag indicating the cleanness of the merge.
- */
-int merge_recursive(struct commit *h1,
-		    struct commit *h2,
-		    const char *branch1,
-		    const char *branch2,
-		    struct commit_list *ca,
-		    struct commit **result)
-{
-	struct commit_list *iter;
-	struct commit *merged_common_ancestors;
-	struct tree *mrtree = mrtree;
-	int clean;
-
-	if (show(4)) {
-		output(4, "Merging:");
-		output_commit_title(h1);
-		output_commit_title(h2);
-	}
-
-	if (!ca) {
-		ca = get_merge_bases(h1, h2, 1);
-		ca = reverse_commit_list(ca);
-	}
-
-	if (show(5)) {
-		output(5, "found %u common ancestor(s):", commit_list_count(ca));
-		for (iter = ca; iter; iter = iter->next)
-			output_commit_title(iter->item);
-	}
-
-	merged_common_ancestors = pop_commit(&ca);
-	if (merged_common_ancestors == NULL) {
-		/* if there is no common ancestor, make an empty tree */
-		struct tree *tree = xcalloc(1, sizeof(struct tree));
-
-		tree->object.parsed = 1;
-		tree->object.type = OBJ_TREE;
-		pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
-		merged_common_ancestors = make_virtual_commit(tree, "ancestor");
-	}
-
-	for (iter = ca; iter; iter = iter->next) {
-		call_depth++;
-		/*
-		 * When the merge fails, the result contains files
-		 * with conflict markers. The cleanness flag is
-		 * ignored, it was never actually used, as result of
-		 * merge_trees has always overwritten it: the committed
-		 * "conflicts" were already resolved.
-		 */
-		discard_cache();
-		merge_recursive(merged_common_ancestors, iter->item,
-				"Temporary merge branch 1",
-				"Temporary merge branch 2",
-				NULL,
-				&merged_common_ancestors);
-		call_depth--;
-
-		if (!merged_common_ancestors)
-			die("merge returned no commit");
-	}
-
-	discard_cache();
-	if (!call_depth) {
-		read_cache();
-		index_only = 0;
-	} else
-		index_only = 1;
-
-	clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
-			    branch1, branch2, &mrtree);
-
-	if (index_only) {
-		*result = make_virtual_commit(mrtree, "merged tree");
-		commit_list_insert(h1, &(*result)->parents);
-		commit_list_insert(h2, &(*result)->parents->next);
-	}
-	flush_output();
-	return clean;
-}
-
 static const char *better_branch_name(const char *branch)
 {
 	static char githead_env[8 + 40 + 1];
@@ -1295,103 +15,58 @@
 	return name ? name : branch;
 }
 
-static struct commit *get_ref(const char *ref)
-{
-	unsigned char sha1[20];
-	struct object *object;
-
-	if (get_sha1(ref, sha1))
-		die("Could not resolve ref '%s'", ref);
-	object = deref_tag(parse_object(sha1), ref, strlen(ref));
-	if (!object)
-		return NULL;
-	if (object->type == OBJ_TREE)
-		return make_virtual_commit((struct tree*)object,
-			better_branch_name(ref));
-	if (object->type != OBJ_COMMIT)
-		return NULL;
-	if (parse_commit((struct commit *)object))
-		die("Could not parse commit '%s'", sha1_to_hex(object->sha1));
-	return (struct commit *)object;
-}
-
-static int merge_config(const char *var, const char *value, void *cb)
-{
-	if (!strcasecmp(var, "merge.verbosity")) {
-		verbosity = git_config_int(var, value);
-		return 0;
-	}
-	if (!strcasecmp(var, "diff.renamelimit")) {
-		diff_rename_limit = git_config_int(var, value);
-		return 0;
-	}
-	if (!strcasecmp(var, "merge.renamelimit")) {
-		merge_rename_limit = git_config_int(var, value);
-		return 0;
-	}
-	return git_default_config(var, value, cb);
-}
-
 int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
 {
-	static const char *bases[20];
-	static unsigned bases_count = 0;
-	int i, clean;
-	const char *branch1, *branch2;
-	struct commit *result, *h1, *h2;
-	struct commit_list *ca = NULL;
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-	int index_fd;
+	const unsigned char *bases[21];
+	unsigned bases_count = 0;
+	int i, failed;
+	unsigned char h1[20], h2[20];
+	struct merge_options o;
+	struct commit *result;
 
+	init_merge_options(&o);
 	if (argv[0]) {
 		int namelen = strlen(argv[0]);
 		if (8 < namelen &&
 		    !strcmp(argv[0] + namelen - 8, "-subtree"))
-			subtree_merge = 1;
+			o.subtree_merge = 1;
 	}
 
-	git_config(merge_config, NULL);
-	if (getenv("GIT_MERGE_VERBOSITY"))
-		verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
-
 	if (argc < 4)
-		die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
+		die("Usage: %s <base>... -- <head> <remote> ...", argv[0]);
 
 	for (i = 1; i < argc; ++i) {
 		if (!strcmp(argv[i], "--"))
 			break;
-		if (bases_count < sizeof(bases)/sizeof(*bases))
-			bases[bases_count++] = argv[i];
+		if (bases_count < ARRAY_SIZE(bases)-1) {
+			unsigned char *sha = xmalloc(20);
+			if (get_sha1(argv[i], sha))
+				die("Could not parse object '%s'", argv[i]);
+			bases[bases_count++] = sha;
+		}
+		else
+			warning("Cannot handle more than %zu bases. "
+				"Ignoring %s.", ARRAY_SIZE(bases)-1, argv[i]);
 	}
 	if (argc - i != 3) /* "--" "<head>" "<remote>" */
 		die("Not handling anything other than two heads merge.");
-	if (verbosity >= 5)
-		buffer_output = 0;
 
-	branch1 = argv[++i];
-	branch2 = argv[++i];
+	o.branch1 = argv[++i];
+	o.branch2 = argv[++i];
 
-	h1 = get_ref(branch1);
-	h2 = get_ref(branch2);
+	if (get_sha1(o.branch1, h1))
+		die("Could not resolve ref '%s'", o.branch1);
+	if (get_sha1(o.branch2, h2))
+		die("Could not resolve ref '%s'", o.branch2);
 
-	branch1 = better_branch_name(branch1);
-	branch2 = better_branch_name(branch2);
+	o.branch1 = better_branch_name(o.branch1);
+	o.branch2 = better_branch_name(o.branch2);
 
-	if (show(3))
-		printf("Merging %s with %s\n", branch1, branch2);
+	if (o.verbosity >= 3)
+		printf("Merging %s with %s\n", o.branch1, o.branch2);
 
-	index_fd = hold_locked_index(lock, 1);
-
-	for (i = 0; i < bases_count; i++) {
-		struct commit *ancestor = get_ref(bases[i]);
-		ca = commit_list_insert(ancestor, &ca);
-	}
-	clean = merge_recursive(h1, h2, branch1, branch2, ca, &result);
-
-	if (active_cache_changed &&
-	    (write_cache(index_fd, active_cache, active_nr) ||
-	     commit_locked_index(lock)))
-			die ("unable to write %s", get_index_file());
-
-	return clean ? 0: 1;
+	failed = merge_recursive_generic(&o, h1, h2, bases_count, bases, &result);
+	if (failed < 0)
+		return 128; /* die() error code */
+	return failed;
 }
diff --git a/builtin-merge.c b/builtin-merge.c
index d0bf1fc..6a51823 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -22,6 +22,8 @@
 #include "log-tree.h"
 #include "color.h"
 #include "rerere.h"
+#include "help.h"
+#include "merge-recursive.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -34,8 +36,8 @@
 };
 
 static const char * const builtin_merge_usage[] = {
-	"git-merge [options] <remote>...",
-	"git-merge [options] <msg> HEAD <remote>",
+	"git merge [options] <remote>...",
+	"git merge [options] <msg> HEAD <remote>",
 	NULL
 };
 
@@ -48,6 +50,7 @@
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char *branch;
+static int verbosity;
 
 static struct strategy all_strategy[] = {
 	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -77,7 +80,9 @@
 static struct strategy *get_strategy(const char *name)
 {
 	int i;
-	struct strbuf err;
+	struct strategy *ret;
+	static struct cmdnames main_cmds, other_cmds;
+	static int loaded;
 
 	if (!name)
 		return NULL;
@@ -86,12 +91,42 @@
 		if (!strcmp(name, all_strategy[i].name))
 			return &all_strategy[i];
 
-	strbuf_init(&err, 0);
-	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-		strbuf_addf(&err, " %s", all_strategy[i].name);
-	fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
-	fprintf(stderr, "Available strategies are:%s.\n", err.buf);
-	exit(1);
+	if (!loaded) {
+		struct cmdnames not_strategies;
+		loaded = 1;
+
+		memset(&not_strategies, 0, sizeof(struct cmdnames));
+		load_command_list("git-merge-", &main_cmds, &other_cmds);
+		for (i = 0; i < main_cmds.cnt; i++) {
+			int j, found = 0;
+			struct cmdname *ent = main_cmds.names[i];
+			for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+				if (!strncmp(ent->name, all_strategy[j].name, ent->len)
+						&& !all_strategy[j].name[ent->len])
+					found = 1;
+			if (!found)
+				add_cmdname(&not_strategies, ent->name, ent->len);
+			exclude_cmds(&main_cmds, &not_strategies);
+		}
+	}
+	if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+		fprintf(stderr, "Available strategies are:");
+		for (i = 0; i < main_cmds.cnt; i++)
+			fprintf(stderr, " %s", main_cmds.names[i]->name);
+		fprintf(stderr, ".\n");
+		if (other_cmds.cnt) {
+			fprintf(stderr, "Available custom strategies are:");
+			for (i = 0; i < other_cmds.cnt; i++)
+				fprintf(stderr, " %s", other_cmds.names[i]->name);
+			fprintf(stderr, ".\n");
+		}
+		exit(1);
+	}
+
+	ret = xcalloc(1, sizeof(struct strategy));
+	ret->name = xstrdup(name);
+	return ret;
 }
 
 static void append_strategy(struct strategy *s)
@@ -137,6 +172,7 @@
 	OPT_CALLBACK('m', "message", &merge_msg, "message",
 		"message to be used for the merge commit (if any)",
 		option_parse_message),
+	OPT__VERBOSITY(&verbosity),
 	OPT_END()
 };
 
@@ -145,6 +181,7 @@
 {
 	unlink(git_path("MERGE_HEAD"));
 	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("MERGE_MODE"));
 }
 
 static void save_state(void)
@@ -192,7 +229,7 @@
 
 static void restore_state(void)
 {
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 	const char *args[] = { "stash", "apply", NULL, NULL };
 
 	if (is_null_sha1(stash))
@@ -200,7 +237,6 @@
 
 	reset_hard(head, 1);
 
-	strbuf_init(&sb, 0);
 	args[2] = sha1_to_hex(stash);
 
 	/*
@@ -216,7 +252,8 @@
 /* This is called when no merge was necessary. */
 static void finish_up_to_date(const char *msg)
 {
-	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	if (verbosity >= 0)
+		printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
 	drop_save();
 }
 
@@ -224,7 +261,7 @@
 {
 	struct rev_info rev;
 	struct commit *commit;
-	struct strbuf out;
+	struct strbuf out = STRBUF_INIT;
 	struct commit_list *j;
 	int fd;
 
@@ -248,7 +285,6 @@
 	if (prepare_revision_walk(&rev))
 		die("revision walk setup failed");
 
-	strbuf_init(&out, 0);
 	strbuf_addstr(&out, "Squashed commit of the following:\n");
 	while ((commit = get_revision(&rev)) != NULL) {
 		strbuf_addch(&out, '\n');
@@ -257,56 +293,29 @@
 		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
 			NULL, NULL, rev.date_mode, 0);
 	}
-	write(fd, out.buf, out.len);
-	close(fd);
+	if (write(fd, out.buf, out.len) < 0)
+		die("Writing SQUASH_MSG: %s", strerror(errno));
+	if (close(fd))
+		die("Finishing SQUASH_MSG: %s", strerror(errno));
 	strbuf_release(&out);
 }
 
-static int run_hook(const char *name)
-{
-	struct child_process hook;
-	const char *argv[3], *env[2];
-	char index[PATH_MAX];
-
-	argv[0] = git_path("hooks/%s", name);
-	if (access(argv[0], X_OK) < 0)
-		return 0;
-
-	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
-	env[0] = index;
-	env[1] = NULL;
-
-	if (squash)
-		argv[1] = "1";
-	else
-		argv[1] = "0";
-	argv[2] = NULL;
-
-	memset(&hook, 0, sizeof(hook));
-	hook.argv = argv;
-	hook.no_stdin = 1;
-	hook.stdout_to_stderr = 1;
-	hook.env = env;
-
-	return run_command(&hook);
-}
-
 static void finish(const unsigned char *new_head, const char *msg)
 {
-	struct strbuf reflog_message;
+	struct strbuf reflog_message = STRBUF_INIT;
 
-	strbuf_init(&reflog_message, 0);
 	if (!msg)
 		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
 	else {
-		printf("%s\n", msg);
+		if (verbosity >= 0)
+			printf("%s\n", msg);
 		strbuf_addf(&reflog_message, "%s: %s",
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
 	if (squash) {
 		squash_message();
 	} else {
-		if (!merge_msg.len)
+		if (verbosity >= 0 && !merge_msg.len)
 			printf("No merge message -- not updating HEAD\n");
 		else {
 			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
@@ -336,7 +345,7 @@
 	}
 
 	/* Run a post-merge hook */
-	run_hook("post-merge");
+	run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
 
 	strbuf_release(&reflog_message);
 }
@@ -346,16 +355,19 @@
 {
 	struct object *remote_head;
 	unsigned char branch_head[20], buf_sha[20];
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf bname = STRBUF_INIT;
 	const char *ptr;
 	int len, early;
 
+	strbuf_branchname(&bname, remote);
+	remote = bname.buf;
+
 	memset(branch_head, 0, sizeof(branch_head));
 	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
 	if (!remote_head)
 		die("'%s' does not point to a commit", remote);
 
-	strbuf_init(&buf, 0);
 	strbuf_addstr(&buf, "refs/heads/");
 	strbuf_addstr(&buf, remote);
 	resolve_ref(buf.buf, branch_head, 0, 0);
@@ -363,7 +375,7 @@
 	if (!hashcmp(remote_head->sha1, branch_head)) {
 		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
 			sha1_to_hex(branch_head), remote);
-		return;
+		goto cleanup;
 	}
 
 	/* See if remote matches <name>^^^.. or <name>~<number> */
@@ -403,17 +415,17 @@
 				    sha1_to_hex(remote_head->sha1),
 				    truname.buf + 11,
 				    (early ? " (early part)" : ""));
-			return;
+			strbuf_release(&truname);
+			goto cleanup;
 		}
 	}
 
 	if (!strcmp(remote, "FETCH_HEAD") &&
 			!access(git_path("FETCH_HEAD"), R_OK)) {
 		FILE *fp;
-		struct strbuf line;
+		struct strbuf line = STRBUF_INIT;
 		char *ptr;
 
-		strbuf_init(&line, 0);
 		fp = fopen(git_path("FETCH_HEAD"), "r");
 		if (!fp)
 			die("could not open %s for reading: %s",
@@ -425,10 +437,13 @@
 			strbuf_remove(&line, ptr-line.buf+1, 13);
 		strbuf_addbuf(msg, &line);
 		strbuf_release(&line);
-		return;
+		goto cleanup;
 	}
 	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
 		sha1_to_hex(remote_head->sha1), remote);
+cleanup:
+	strbuf_release(&buf);
+	strbuf_release(&bname);
 }
 
 static int git_merge_config(const char *k, const char *v, void *cb)
@@ -511,30 +526,76 @@
 	const char **args;
 	int i = 0, ret;
 	struct commit_list *j;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
+	int index_fd;
+	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 
-	args = xmalloc((4 + commit_list_count(common) +
-			commit_list_count(remoteheads)) * sizeof(char *));
-	strbuf_init(&buf, 0);
-	strbuf_addf(&buf, "merge-%s", strategy);
-	args[i++] = buf.buf;
-	for (j = common; j; j = j->next)
-		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
-	args[i++] = "--";
-	args[i++] = head_arg;
-	for (j = remoteheads; j; j = j->next)
-		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
-	args[i] = NULL;
-	ret = run_command_v_opt(args, RUN_GIT_CMD);
-	strbuf_release(&buf);
-	i = 1;
-	for (j = common; j; j = j->next)
-		free((void *)args[i++]);
-	i += 2;
-	for (j = remoteheads; j; j = j->next)
-		free((void *)args[i++]);
-	free(args);
-	return -ret;
+	index_fd = hold_locked_index(lock, 1);
+	refresh_cache(REFRESH_QUIET);
+	if (active_cache_changed &&
+			(write_cache(index_fd, active_cache, active_nr) ||
+			 commit_locked_index(lock)))
+		return error("Unable to write index.");
+	rollback_lock_file(lock);
+
+	if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
+		int clean;
+		struct commit *result;
+		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		int index_fd;
+		struct commit_list *reversed = NULL;
+		struct merge_options o;
+
+		if (remoteheads->next) {
+			error("Not handling anything other than two heads merge.");
+			return 2;
+		}
+
+		init_merge_options(&o);
+		if (!strcmp(strategy, "subtree"))
+			o.subtree_merge = 1;
+
+		o.branch1 = head_arg;
+		o.branch2 = remoteheads->item->util;
+
+		for (j = common; j; j = j->next)
+			commit_list_insert(j->item, &reversed);
+
+		index_fd = hold_locked_index(lock, 1);
+		clean = merge_recursive(&o, lookup_commit(head),
+				remoteheads->item, reversed, &result);
+		if (active_cache_changed &&
+				(write_cache(index_fd, active_cache, active_nr) ||
+				 commit_locked_index(lock)))
+			die ("unable to write %s", get_index_file());
+		rollback_lock_file(lock);
+		return clean ? 0 : 1;
+	} else {
+		args = xmalloc((4 + commit_list_count(common) +
+					commit_list_count(remoteheads)) * sizeof(char *));
+		strbuf_addf(&buf, "merge-%s", strategy);
+		args[i++] = buf.buf;
+		for (j = common; j; j = j->next)
+			args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+		args[i++] = "--";
+		args[i++] = head_arg;
+		for (j = remoteheads; j; j = j->next)
+			args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+		args[i] = NULL;
+		ret = run_command_v_opt(args, RUN_GIT_CMD);
+		strbuf_release(&buf);
+		i = 1;
+		for (j = common; j; j = j->next)
+			free((void *)args[i++]);
+		i += 2;
+		for (j = remoteheads; j; j = j->next)
+			free((void *)args[i++]);
+		free(args);
+		discard_cache();
+		if (read_cache() < 0)
+			die("failed to read the cache");
+		return -ret;
+	}
 }
 
 static void count_diff_files(struct diff_queue_struct *q,
@@ -574,7 +635,7 @@
 	memset(&opts, 0, sizeof(opts));
 	memset(&t, 0, sizeof(t));
 	memset(&dir, 0, sizeof(dir));
-	dir.show_ignored = 1;
+	dir.flags |= DIR_SHOW_IGNORED;
 	dir.exclude_per_dir = ".gitignore";
 	opts.dir = &dir;
 
@@ -659,7 +720,7 @@
 	parent->next = xmalloc(sizeof(*parent->next));
 	parent->next->item = remoteheads->item;
 	parent->next->next = NULL;
-	commit_tree(merge_msg.buf, result_tree, parent, result_commit);
+	commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
 	finish(result_commit, "In-index merge");
 	drop_save();
 	return 0;
@@ -688,7 +749,7 @@
 	}
 	free_commit_list(remoteheads);
 	strbuf_addch(&merge_msg, '\n');
-	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
 	strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
 	finish(result_commit, buf.buf);
 	strbuf_release(&buf);
@@ -745,10 +806,6 @@
 	int cnt = 0;
 	struct rev_info rev;
 
-	discard_cache();
-	if (read_cache() < 0)
-		die("failed to read the cache");
-
 	/* Check how many files differ. */
 	init_revisions(&rev, "");
 	setup_revisions(0, NULL, &rev, NULL);
@@ -770,7 +827,7 @@
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
 	unsigned char result_tree[20];
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	const char *head_arg;
 	int flag, head_invalid = 0, i;
 	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
@@ -800,6 +857,8 @@
 
 	argc = parse_options(argc, argv, builtin_merge_options,
 			builtin_merge_usage, 0);
+	if (verbosity < 0)
+		show_diffstat = 0;
 
 	if (squash) {
 		if (!allow_fast_forward)
@@ -819,7 +878,6 @@
 	 * Traditional format never would have "-m" so it is an
 	 * additional safety measure to check for it.
 	 */
-	strbuf_init(&buf, 0);
 
 	if (!have_message && is_old_style_invocation(argc, argv)) {
 		strbuf_addstr(&merge_msg, argv[0]);
@@ -836,6 +894,11 @@
 		if (argc != 1)
 			die("Can merge only exactly one commit into "
 				"empty head");
+		if (squash)
+			die("Squash commit into empty head not supported yet");
+		if (!allow_fast_forward)
+			die("Non-fast-forward commit does not make sense into "
+			    "an empty head");
 		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
 		if (!remote_head)
 			die("%s - not something we can merge", argv[0]);
@@ -844,7 +907,7 @@
 		reset_hard(remote_head->sha1, 0);
 		return 0;
 	} else {
-		struct strbuf msg;
+		struct strbuf msg = STRBUF_INIT;
 
 		/* We are invoked directly as the first-class UI. */
 		head_arg = "HEAD";
@@ -857,7 +920,6 @@
 		 * codepath so we discard the error in this
 		 * loop.
 		 */
-		strbuf_init(&msg, 0);
 		for (i = 0; i < argc; i++)
 			merge_name(argv[i], &msg);
 		fmt_merge_msg(option_log, &msg, &merge_msg);
@@ -877,12 +939,14 @@
 
 	for (i = 0; i < argc; i++) {
 		struct object *o;
+		struct commit *commit;
 
 		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
 		if (!o)
 			die("%s - not something we can merge", argv[i]);
-		remotes = &commit_list_insert(lookup_commit(o->sha1),
-			remotes)->next;
+		commit = lookup_commit(o->sha1);
+		commit->util = (void *)argv[i];
+		remotes = &commit_list_insert(commit, remotes)->next;
 
 		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
 		setenv(buf.buf, argv[i], 1);
@@ -930,17 +994,17 @@
 			!common->next &&
 			!hashcmp(common->item->object.sha1, head)) {
 		/* Again the most common case of merging one remote. */
-		struct strbuf msg;
+		struct strbuf msg = STRBUF_INIT;
 		struct object *o;
 		char hex[41];
 
 		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
 
-		printf("Updating %s..%s\n",
-			hex,
-			find_unique_abbrev(remoteheads->item->object.sha1,
-			DEFAULT_ABBREV));
-		strbuf_init(&msg, 0);
+		if (verbosity >= 0)
+			printf("Updating %s..%s\n",
+				hex,
+				find_unique_abbrev(remoteheads->item->object.sha1,
+				DEFAULT_ABBREV));
 		strbuf_addstr(&msg, "Fast forward");
 		if (have_message)
 			strbuf_addstr(&msg,
@@ -1076,7 +1140,6 @@
 		}
 
 		/* Automerge succeeded. */
-		discard_cache();
 		write_tree_trivial(result_tree);
 		automerge_was_ok = 1;
 		break;
@@ -1136,6 +1199,15 @@
 			merge_msg.len)
 			die("Could not write to %s", git_path("MERGE_MSG"));
 		close(fd);
+		fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MODE"));
+		strbuf_reset(&buf);
+		if (!allow_fast_forward)
+			strbuf_addf(&buf, "no-ff");
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_MODE"));
+		close(fd);
 	}
 
 	if (merge_was_ok) {
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 7104145..419f29a 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -78,7 +78,7 @@
 static int window = 10;
 static uint32_t pack_size_limit, pack_size_limit_cfg;
 static int depth = 50;
-static int delta_search_threads = 1;
+static int delta_search_threads;
 static int pack_to_stdout;
 static int num_preferred_base;
 static struct progress *progress_state;
@@ -195,16 +195,16 @@
 	int st;
 
 	memset(&stream, 0, sizeof(stream));
-	inflateInit(&stream);
+	git_inflate_init(&stream);
 	do {
 		in = use_pack(p, w_curs, offset, &stream.avail_in);
 		stream.next_in = in;
 		stream.next_out = fakebuf;
 		stream.avail_out = sizeof(fakebuf);
-		st = inflate(&stream, Z_FINISH);
+		st = git_inflate(&stream, Z_FINISH);
 		offset += stream.next_in - in;
 	} while (st == Z_OK || st == Z_BUF_ERROR);
-	inflateEnd(&stream);
+	git_inflate_end(&stream);
 	return (st == Z_STREAM_END &&
 		stream.total_out == expect &&
 		stream.total_in == len) ? 0 : -1;
@@ -286,6 +286,7 @@
 				 */
 
 	if (!to_reuse) {
+		no_reuse:
 		if (!usable_delta) {
 			buf = read_sha1_file(entry->idx.sha1, &type, &size);
 			if (!buf)
@@ -367,46 +368,60 @@
 		struct revindex_entry *revidx;
 		off_t offset;
 
-		if (entry->delta) {
+		if (entry->delta)
 			type = (allow_ofs_delta && entry->delta->idx.offset) ?
 				OBJ_OFS_DELTA : OBJ_REF_DELTA;
-			reused_delta++;
-		}
 		hdrlen = encode_header(type, entry->size, header);
+
 		offset = entry->in_pack_offset;
 		revidx = find_pack_revindex(p, offset);
 		datalen = revidx[1].offset - offset;
 		if (!pack_to_stdout && p->index_version > 1 &&
-		    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
-			die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+		    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
+			error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+			unuse_pack(&w_curs);
+			goto no_reuse;
+		}
+
 		offset += entry->in_pack_header_size;
 		datalen -= entry->in_pack_header_size;
+		if (!pack_to_stdout && p->index_version == 1 &&
+		    check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
+			error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
+			unuse_pack(&w_curs);
+			goto no_reuse;
+		}
+
 		if (type == OBJ_OFS_DELTA) {
 			off_t ofs = entry->idx.offset - entry->delta->idx.offset;
 			unsigned pos = sizeof(dheader) - 1;
 			dheader[pos] = ofs & 127;
 			while (ofs >>= 7)
 				dheader[--pos] = 128 | (--ofs & 127);
-			if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
+			if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+				unuse_pack(&w_curs);
 				return 0;
+			}
 			sha1write(f, header, hdrlen);
 			sha1write(f, dheader + pos, sizeof(dheader) - pos);
 			hdrlen += sizeof(dheader) - pos;
+			reused_delta++;
 		} else if (type == OBJ_REF_DELTA) {
-			if (limit && hdrlen + 20 + datalen + 20 >= limit)
+			if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+				unuse_pack(&w_curs);
 				return 0;
+			}
 			sha1write(f, header, hdrlen);
 			sha1write(f, entry->delta->idx.sha1, 20);
 			hdrlen += 20;
+			reused_delta++;
 		} else {
-			if (limit && hdrlen + datalen + 20 >= limit)
+			if (limit && hdrlen + datalen + 20 >= limit) {
+				unuse_pack(&w_curs);
 				return 0;
+			}
 			sha1write(f, header, hdrlen);
 		}
-
-		if (!pack_to_stdout && p->index_version == 1 &&
-		    check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
-			die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
 		copy_pack_data(f, p, &w_curs, offset, datalen);
 		unuse_pack(&w_curs);
 		reused++;
@@ -473,9 +488,8 @@
 		} else {
 			char tmpname[PATH_MAX];
 			int fd;
-			snprintf(tmpname, sizeof(tmpname),
-				 "%s/pack/tmp_pack_XXXXXX", get_object_directory());
-			fd = xmkstemp(tmpname);
+			fd = odb_mkstemp(tmpname, sizeof(tmpname),
+					 "pack/tmp_pack_XXXXXX");
 			pack_tmp_name = xstrdup(tmpname);
 			f = sha1fd(fd, pack_tmp_name);
 		}
@@ -1017,9 +1031,11 @@
 		 * We want in_pack_type even if we do not reuse delta
 		 * since non-delta representations could still be reused.
 		 */
-		used = unpack_object_header_gently(buf, avail,
+		used = unpack_object_header_buffer(buf, avail,
 						   &entry->in_pack_type,
 						   &entry->size);
+		if (used == 0)
+			goto give_up;
 
 		/*
 		 * Determine if this is a delta and if so whether we can
@@ -1031,6 +1047,8 @@
 			/* Not a delta hence we've already got all we need. */
 			entry->type = entry->in_pack_type;
 			entry->in_pack_header_size = used;
+			if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
+				goto give_up;
 			unuse_pack(&w_curs);
 			return;
 		case OBJ_REF_DELTA:
@@ -1047,19 +1065,25 @@
 			ofs = c & 127;
 			while (c & 128) {
 				ofs += 1;
-				if (!ofs || MSB(ofs, 7))
-					die("delta base offset overflow in pack for %s",
-					    sha1_to_hex(entry->idx.sha1));
+				if (!ofs || MSB(ofs, 7)) {
+					error("delta base offset overflow in pack for %s",
+					      sha1_to_hex(entry->idx.sha1));
+					goto give_up;
+				}
 				c = buf[used_0++];
 				ofs = (ofs << 7) + (c & 127);
 			}
-			if (ofs >= entry->in_pack_offset)
-				die("delta base offset out of bound for %s",
-				    sha1_to_hex(entry->idx.sha1));
 			ofs = entry->in_pack_offset - ofs;
+			if (ofs <= 0 || ofs >= entry->in_pack_offset) {
+				error("delta base offset out of bound for %s",
+				      sha1_to_hex(entry->idx.sha1));
+				goto give_up;
+			}
 			if (reuse_delta && !entry->preferred_base) {
 				struct revindex_entry *revidx;
 				revidx = find_pack_revindex(p, ofs);
+				if (!revidx)
+					goto give_up;
 				base_ref = nth_packed_object_sha1(p, revidx->nr);
 			}
 			entry->in_pack_header_size = used + used_0;
@@ -1079,6 +1103,7 @@
 			 */
 			entry->type = entry->in_pack_type;
 			entry->delta = base_entry;
+			entry->delta_size = entry->size;
 			entry->delta_sibling = base_entry->delta_child;
 			base_entry->delta_child = entry;
 			unuse_pack(&w_curs);
@@ -1093,6 +1118,8 @@
 			 */
 			entry->size = get_size_from_delta(p, &w_curs,
 					entry->in_pack_offset + entry->in_pack_header_size);
+			if (entry->size == 0)
+				goto give_up;
 			unuse_pack(&w_curs);
 			return;
 		}
@@ -1102,6 +1129,7 @@
 		 * with sha1_object_info() to find about the object type
 		 * at this point...
 		 */
+		give_up:
 		unuse_pack(&w_curs);
 	}
 
@@ -1384,12 +1412,10 @@
 			int window, int depth, unsigned *processed)
 {
 	uint32_t i, idx = 0, count = 0;
-	unsigned int array_size = window * sizeof(struct unpacked);
 	struct unpacked *array;
 	unsigned long mem_usage = 0;
 
-	array = xmalloc(array_size);
-	memset(array, 0, array_size);
+	array = xcalloc(window, sizeof(struct unpacked));
 
 	for (;;) {
 		struct object_entry *entry;
@@ -1585,11 +1611,18 @@
 		find_deltas(list, &list_size, window, depth, processed);
 		return;
 	}
+	if (progress > pack_to_stdout)
+		fprintf(stderr, "Delta compression using up to %d threads.\n",
+				delta_search_threads);
 
 	/* Partition the work amongst work threads. */
 	for (i = 0; i < delta_search_threads; i++) {
 		unsigned sub_size = list_size / (delta_search_threads - i);
 
+		/* don't use too small segments or no deltas will be found */
+		if (sub_size < 2*window && i+1 < delta_search_threads)
+			sub_size = 0;
+
 		p[i].window = window;
 		p[i].depth = depth;
 		p[i].processed = processed;
@@ -1715,6 +1748,16 @@
 
 	get_object_details();
 
+	/*
+	 * If we're locally repacking then we need to be doubly careful
+	 * from now on in order to make sure no stealth corruption gets
+	 * propagated to the new pack.  Clients receiving streamed packs
+	 * should validate everything they get anyway so no need to incur
+	 * the additional cost here in that case.
+	 */
+	if (!pack_to_stdout)
+		do_check_packed_object_crc = 1;
+
 	if (!nr_objects || !window || !depth)
 		return;
 
@@ -1741,6 +1784,14 @@
 			if (entry->type < 0)
 				die("unable to get type of object %s",
 				    sha1_to_hex(entry->idx.sha1));
+		} else {
+			if (entry->type < 0) {
+				/*
+				 * This object is not found, but we
+				 * don't have to include it anyway.
+				 */
+				continue;
+			}
 		}
 
 		delta_list[n++] = entry;
@@ -1850,7 +1901,7 @@
 
 #define OBJECT_ADDED (1u<<20)
 
-static void show_commit(struct commit *commit)
+static void show_commit(struct commit *commit, void *data)
 {
 	add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
 	commit->object.flags |= OBJECT_ADDED;
@@ -1923,11 +1974,7 @@
 		const unsigned char *sha1;
 		struct object *o;
 
-		for (i = 0; i < revs->num_ignore_packed; i++) {
-			if (matches_pack_name(p, revs->ignore_packed[i]))
-				break;
-		}
-		if (revs->num_ignore_packed <= i)
+		if (!p->pack_local || p->pack_keep)
 			continue;
 		if (open_pack_index(p))
 			die("cannot open pack index");
@@ -1956,6 +2003,29 @@
 	free(in_pack.array);
 }
 
+static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
+{
+	static struct packed_git *last_found = (void *)1;
+	struct packed_git *p;
+
+	p = (last_found != (void *)1) ? last_found : packed_git;
+
+	while (p) {
+		if ((!p->pack_local || p->pack_keep) &&
+			find_pack_entry_one(sha1, p)) {
+			last_found = p;
+			return 1;
+		}
+		if (p == last_found)
+			p = packed_git;
+		else
+			p = p->next;
+		if (p == last_found)
+			p = p->next;
+	}
+	return 0;
+}
+
 static void loosen_unused_packed_objects(struct rev_info *revs)
 {
 	struct packed_git *p;
@@ -1963,11 +2033,7 @@
 	const unsigned char *sha1;
 
 	for (p = packed_git; p; p = p->next) {
-		for (i = 0; i < revs->num_ignore_packed; i++) {
-			if (matches_pack_name(p, revs->ignore_packed[i]))
-				break;
-		}
-		if (revs->num_ignore_packed <= i)
+		if (!p->pack_local || p->pack_keep)
 			continue;
 
 		if (open_pack_index(p))
@@ -1975,7 +2041,8 @@
 
 		for (i = 0; i < p->num_objects; i++) {
 			sha1 = nth_packed_object_sha1(p, i);
-			if (!locate_object_entry(sha1))
+			if (!locate_object_entry(sha1) &&
+				!has_sha1_pack_kept_or_nonlocal(sha1))
 				if (force_object_loose(sha1, p->mtime))
 					die("unable to force loose object");
 		}
@@ -2012,7 +2079,7 @@
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
 	mark_edges_uninteresting(revs.commits, &revs, show_edge);
-	traverse_commit_list(&revs, show_commit, show_object);
+	traverse_commit_list(&revs, show_commit, show_object, NULL);
 
 	if (keep_unreachable)
 		add_objects_in_unpacked_packs(&revs);
@@ -2165,7 +2232,6 @@
 			continue;
 		}
 		if (!strcmp("--unpacked", arg) ||
-		    !prefixcmp(arg, "--unpacked=") ||
 		    !strcmp("--reflog", arg) ||
 		    !strcmp("--all", arg)) {
 			use_internal_rev_list = 1;
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
index 10cb8df..2d5b2cd 100644
--- a/builtin-prune-packed.c
+++ b/builtin-prune-packed.c
@@ -23,7 +23,7 @@
 		memcpy(hex+2, de->d_name, 38);
 		if (get_sha1_hex(hex, sha1))
 			continue;
-		if (!has_sha1_pack(sha1, NULL))
+		if (!has_sha1_pack(sha1))
 			continue;
 		memcpy(pathname + len, de->d_name, 38);
 		if (opts & DRY_RUN)
diff --git a/builtin-prune.c b/builtin-prune.c
index 1663f8b..545e9c1 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -5,12 +5,14 @@
 #include "builtin.h"
 #include "reachable.h"
 #include "parse-options.h"
+#include "dir.h"
 
 static const char * const prune_usage[] = {
-	"git prune [-n] [--expire <time>] [--] [<head>...]",
+	"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
 	NULL
 };
 static int show_only;
+static int verbose;
 static unsigned long expire;
 
 static int prune_tmp_object(const char *path, const char *filename)
@@ -39,11 +41,12 @@
 		if (st.st_mtime > expire)
 			return 0;
 	}
-	if (show_only) {
+	if (show_only || verbose) {
 		enum object_type type = sha1_object_info(sha1, NULL);
 		printf("%s %s\n", sha1_to_hex(sha1),
 		       (type > 0) ? typename(type) : "unknown");
-	} else
+	}
+	if (!show_only)
 		unlink(fullpath);
 	return 0;
 }
@@ -59,19 +62,12 @@
 	while ((de = readdir(dir)) != NULL) {
 		char name[100];
 		unsigned char sha1[20];
-		int len = strlen(de->d_name);
 
-		switch (len) {
-		case 2:
-			if (de->d_name[1] != '.')
-				break;
-		case 1:
-			if (de->d_name[0] != '.')
-				break;
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
-		case 38:
+		if (strlen(de->d_name) == 38) {
 			sprintf(name, "%02x", i);
-			memcpy(name+2, de->d_name, len+1);
+			memcpy(name+2, de->d_name, 39);
 			if (get_sha1_hex(name, sha1) < 0)
 				break;
 
@@ -135,6 +131,8 @@
 	const struct option options[] = {
 		OPT_BOOLEAN('n', NULL, &show_only,
 			    "do not remove, show only"),
+		OPT_BOOLEAN('v', NULL, &verbose,
+			"report pruned objects"),
 		OPT_DATE(0, "expire", &expire,
 			 "expire objects older than <time>"),
 		OPT_END()
diff --git a/builtin-push.c b/builtin-push.c
index f5cc762..2eabcd3 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -48,30 +48,108 @@
 	}
 }
 
+static void setup_push_tracking(void)
+{
+	struct strbuf refspec = STRBUF_INIT;
+	struct branch *branch = branch_get(NULL);
+	if (!branch)
+		die("You are not currently on a branch.");
+	if (!branch->merge_nr)
+		die("The current branch %s is not tracking anything.",
+		    branch->name);
+	if (branch->merge_nr != 1)
+		die("The current branch %s is tracking multiple branches, "
+		    "refusing to push.", branch->name);
+	strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
+	add_refspec(refspec.buf);
+}
+
+static const char *warn_unconfigured_push_msg[] = {
+	"You did not specify any refspecs to push, and the current remote",
+	"has not configured any push refspecs. The default action in this",
+	"case is to push all matching refspecs, that is, all branches",
+	"that exist both locally and remotely will be updated.  This may",
+	"not necessarily be what you want to happen.",
+	"",
+	"You can specify what action you want to take in this case, and",
+	"avoid seeing this message again, by configuring 'push.default' to:",
+	"  'nothing'  : Do not push anything",
+	"  'matching' : Push all matching branches (default)",
+	"  'tracking' : Push the current branch to whatever it is tracking",
+	"  'current'  : Push the current branch"
+};
+
+static void warn_unconfigured_push(void)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++)
+		warning("%s", warn_unconfigured_push_msg[i]);
+}
+
+static void setup_default_push_refspecs(void)
+{
+	git_config(git_default_config, NULL);
+	switch (push_default) {
+	case PUSH_DEFAULT_UNSPECIFIED:
+		warn_unconfigured_push();
+		/* fallthrough */
+
+	case PUSH_DEFAULT_MATCHING:
+		add_refspec(":");
+		break;
+
+	case PUSH_DEFAULT_TRACKING:
+		setup_push_tracking();
+		break;
+
+	case PUSH_DEFAULT_CURRENT:
+		add_refspec("HEAD");
+		break;
+
+	case PUSH_DEFAULT_NOTHING:
+		die("You didn't specify any refspecs to push, and "
+		    "push.default is \"nothing\".");
+		break;
+	}
+}
+
 static int do_push(const char *repo, int flags)
 {
 	int i, errs;
 	struct remote *remote = remote_get(repo);
 
-	if (!remote)
-		die("bad repository '%s'", repo);
+	if (!remote) {
+		if (repo)
+			die("bad repository '%s'", repo);
+		die("No destination configured to push to.");
+	}
 
 	if (remote->mirror)
 		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
-	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
-		return -1;
+	if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
+		if (!strcmp(*refspec, "refs/tags/*"))
+			return error("--all and --tags are incompatible");
+		return error("--all can't be combined with refspecs");
+	}
+
+	if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
+		if (!strcmp(*refspec, "refs/tags/*"))
+			return error("--mirror and --tags are incompatible");
+		return error("--mirror can't be combined with refspecs");
+	}
 
 	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
 				(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
 		return error("--all and --mirror are incompatible");
 	}
 
-	if (!refspec
-		&& !(flags & TRANSPORT_PUSH_ALL)
-		&& remote->push_refspec_nr) {
-		refspec = remote->push_refspec;
-		refspec_nr = remote->push_refspec_nr;
+	if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
+		if (remote->push_refspec_nr) {
+			refspec = remote->push_refspec;
+			refspec_nr = remote->push_refspec_nr;
+		} else if (!(flags & TRANSPORT_PUSH_MIRROR))
+			setup_default_push_refspecs();
 	}
 	errs = 0;
 	for (i = 0; i < remote->url_nr; i++) {
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 38fef34..8e02738 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -170,7 +170,7 @@
 				die("more than one --exclude-per-directory are given.");
 
 			dir = xcalloc(1, sizeof(*opts.dir));
-			dir->show_ignored = 1;
+			dir->flags |= DIR_SHOW_IGNORED;
 			dir->exclude_per_dir = arg + 24;
 			opts.dir = dir;
 			/* We do not need to nor want to do read-directory
diff --git a/receive-pack.c b/builtin-receive-pack.c
similarity index 67%
rename from receive-pack.c
rename to builtin-receive-pack.c
index f0145bd..a970b39 100644
--- a/receive-pack.c
+++ b/builtin-receive-pack.c
@@ -6,21 +6,54 @@
 #include "exec_cmd.h"
 #include "commit.h"
 #include "object.h"
+#include "remote.h"
+#include "transport.h"
 
-static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
+static const char receive_pack_usage[] = "git receive-pack <git-dir>";
 
-static int deny_non_fast_forwards = 0;
+enum deny_action {
+	DENY_UNCONFIGURED,
+	DENY_IGNORE,
+	DENY_WARN,
+	DENY_REFUSE,
+};
+
+static int deny_deletes;
+static int deny_non_fast_forwards;
+static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
+static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
 static int report_status;
+static const char *head_name;
 
 static char capabilities[] = " report-status delete-refs ";
 static int capabilities_sent;
 
+static enum deny_action parse_deny_action(const char *var, const char *value)
+{
+	if (value) {
+		if (!strcasecmp(value, "ignore"))
+			return DENY_IGNORE;
+		if (!strcasecmp(value, "warn"))
+			return DENY_WARN;
+		if (!strcasecmp(value, "refuse"))
+			return DENY_REFUSE;
+	}
+	if (git_config_bool(var, value))
+		return DENY_REFUSE;
+	return DENY_IGNORE;
+}
+
 static int receive_pack_config(const char *var, const char *value, void *cb)
 {
+	if (strcmp(var, "receive.denydeletes") == 0) {
+		deny_deletes = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (strcmp(var, "receive.denynonfastforwards") == 0) {
 		deny_non_fast_forwards = git_config_bool(var, value);
 		return 0;
@@ -41,6 +74,16 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "receive.denycurrentbranch")) {
+		deny_current_branch = parse_deny_action(var, value);
+		return 0;
+	}
+
+	if (strcmp(var, "receive.denydeletecurrent") == 0) {
+		deny_delete_current = parse_deny_action(var, value);
+		return 0;
+	}
+
 	return git_default_config(var, value, cb);
 }
 
@@ -101,7 +144,7 @@
 	}
 }
 
-static int run_hook(const char *hook_name)
+static int run_receive_hook(const char *hook_name)
 {
 	static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
 	struct command *cmd;
@@ -165,6 +208,71 @@
 	return hook_status(run_command(&proc), update_hook);
 }
 
+static int is_ref_checked_out(const char *ref)
+{
+	if (is_bare_repository())
+		return 0;
+
+	if (!head_name)
+		return 0;
+	return !strcmp(head_name, ref);
+}
+
+static char *warn_unconfigured_deny_msg[] = {
+	"Updating the currently checked out branch may cause confusion,",
+	"as the index and work tree do not reflect changes that are in HEAD.",
+	"As a result, you may see the changes you just pushed into it",
+	"reverted when you run 'git diff' over there, and you may want",
+	"to run 'git reset --hard' before starting to work to recover.",
+	"",
+	"You can set 'receive.denyCurrentBranch' configuration variable to",
+	"'refuse' in the remote repository to forbid pushing into its",
+	"current branch."
+	"",
+	"To allow pushing into the current branch, you can set it to 'ignore';",
+	"but this is not recommended unless you arranged to update its work",
+	"tree to match what you pushed in some other way.",
+	"",
+	"To squelch this message, you can set it to 'warn'.",
+	"",
+	"Note that the default will change in a future version of git",
+	"to refuse updating the current branch unless you have the",
+	"configuration variable set to either 'ignore' or 'warn'."
+};
+
+static void warn_unconfigured_deny(void)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++)
+		warning("%s", warn_unconfigured_deny_msg[i]);
+}
+
+static char *warn_unconfigured_deny_delete_current_msg[] = {
+	"Deleting the current branch can cause confusion by making the next",
+	"'git clone' not check out any file.",
+	"",
+	"You can set 'receive.denyDeleteCurrent' configuration variable to",
+	"'refuse' in the remote repository to disallow deleting the current",
+	"branch.",
+	"",
+	"You can set it to 'ignore' to allow such a delete without a warning.",
+	"",
+	"To make this warning message less loud, you can set it to 'warn'.",
+	"",
+	"Note that the default will change in a future version of git",
+	"to refuse deleting the current branch unless you have the",
+	"configuration variable set to either 'ignore' or 'warn'."
+};
+
+static void warn_unconfigured_deny_delete_current(void)
+{
+	int i;
+	for (i = 0;
+	     i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
+	     i++)
+		warning("%s", warn_unconfigured_deny_delete_current_msg[i]);
+}
+
 static const char *update(struct command *cmd)
 {
 	const char *name = cmd->ref_name;
@@ -178,11 +286,51 @@
 		return "funny refname";
 	}
 
+	if (is_ref_checked_out(name)) {
+		switch (deny_current_branch) {
+		case DENY_IGNORE:
+			break;
+		case DENY_UNCONFIGURED:
+		case DENY_WARN:
+			warning("updating the current branch");
+			if (deny_current_branch == DENY_UNCONFIGURED)
+				warn_unconfigured_deny();
+			break;
+		case DENY_REFUSE:
+			error("refusing to update checked out branch: %s", name);
+			return "branch is currently checked out";
+		}
+	}
+
 	if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
 		error("unpack should have generated %s, "
 		      "but I can't find it!", sha1_to_hex(new_sha1));
 		return "bad pack";
 	}
+
+	if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
+		if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
+			error("denying ref deletion for %s", name);
+			return "deletion prohibited";
+		}
+
+		if (!strcmp(name, head_name)) {
+			switch (deny_delete_current) {
+			case DENY_IGNORE:
+				break;
+			case DENY_WARN:
+			case DENY_UNCONFIGURED:
+				if (deny_delete_current == DENY_UNCONFIGURED)
+					warn_unconfigured_deny_delete_current();
+				warning("deleting the current branch");
+				break;
+			case DENY_REFUSE:
+				error("refusing to delete the current branch: %s", name);
+				return "deletion of the current branch prohibited";
+			}
+		}
+	}
+
 	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
 	    !is_null_sha1(old_sha1) &&
 	    !prefixcmp(name, "refs/heads/")) {
@@ -276,6 +424,7 @@
 static void execute_commands(const char *unpacker_error)
 {
 	struct command *cmd = commands;
+	unsigned char sha1[20];
 
 	if (unpacker_error) {
 		while (cmd) {
@@ -285,7 +434,7 @@
 		return;
 	}
 
-	if (run_hook(pre_receive_hook)) {
+	if (run_receive_hook(pre_receive_hook)) {
 		while (cmd) {
 			cmd->error_string = "pre-receive hook declined";
 			cmd = cmd->next;
@@ -293,6 +442,8 @@
 		return;
 	}
 
+	head_name = resolve_ref("HEAD", sha1, 0, NULL);
+
 	while (cmd) {
 		cmd->error_string = update(cmd);
 		cmd = cmd->next;
@@ -407,7 +558,7 @@
 		char keep_arg[256];
 		struct child_process ip;
 
-		s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
+		s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
 		if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
 			strcpy(keep_arg + s, "localhost");
 
@@ -462,14 +613,53 @@
 	return 1;
 }
 
-int main(int argc, char **argv)
+static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
+{
+	char *other;
+	size_t len;
+	struct remote *remote;
+	struct transport *transport;
+	const struct ref *extra;
+
+	e->name[-1] = '\0';
+	other = xstrdup(make_absolute_path(e->base));
+	e->name[-1] = '/';
+	len = strlen(other);
+
+	while (other[len-1] == '/')
+		other[--len] = '\0';
+	if (len < 8 || memcmp(other + len - 8, "/objects", 8))
+		return 0;
+	/* Is this a git repository with refs? */
+	memcpy(other + len - 8, "/refs", 6);
+	if (!is_directory(other))
+		return 0;
+	other[len - 8] = '\0';
+	remote = remote_get(other);
+	transport = transport_get(remote, other);
+	for (extra = transport_get_remote_refs(transport);
+	     extra;
+	     extra = extra->next) {
+		add_extra_ref(".have", extra->old_sha1, 0);
+	}
+	transport_disconnect(transport);
+	free(other);
+	return 0;
+}
+
+static void add_alternate_refs(void)
+{
+	foreach_alt_odb(add_refs_from_alternate, NULL);
+}
+
+int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	char *dir = NULL;
 
 	argv++;
 	for (i = 1; i < argc; i++) {
-		char *arg = *argv++;
+		const char *arg = *argv++;
 
 		if (*arg == '-') {
 			/* Do flag handling here */
@@ -477,7 +667,7 @@
 		}
 		if (dir)
 			usage(receive_pack_usage);
-		dir = arg;
+		dir = xstrdup(arg);
 	}
 	if (!dir)
 		usage(receive_pack_usage);
@@ -485,7 +675,7 @@
 	setup_path();
 
 	if (!enter_repo(dir, 0))
-		die("'%s': unable to chdir or not a git archive", dir);
+		die("'%s' does not appear to be a git repository", dir);
 
 	if (is_repository_shallow())
 		die("attempt to push into a shallow repository");
@@ -497,7 +687,9 @@
 	else if (0 <= receive_unpack_limit)
 		unpack_limit = receive_unpack_limit;
 
+	add_alternate_refs();
 	write_head_info();
+	clear_extra_refs();
 
 	/* EOF */
 	packet_flush(1);
@@ -513,7 +705,7 @@
 			unlink(pack_lockfile);
 		if (report_status)
 			report(unpack_status);
-		run_hook(post_receive_hook);
+		run_receive_hook(post_receive_hook);
 		run_update_post_hook(commands);
 	}
 	return 0;
diff --git a/builtin-reflog.c b/builtin-reflog.c
index da96da3..249ad2a 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -52,6 +52,7 @@
 
 #define INCOMPLETE	(1u<<10)
 #define STUDYING	(1u<<11)
+#define REACHABLE	(1u<<12)
 
 static int tree_is_complete(const unsigned char *sha1)
 {
@@ -209,6 +210,70 @@
 	return 1;
 }
 
+static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+{
+	/*
+	 * We may or may not have the commit yet - if not, look it
+	 * up using the supplied sha1.
+	 */
+	if (!commit) {
+		if (is_null_sha1(sha1))
+			return 0;
+
+		commit = lookup_commit_reference_gently(sha1, 1);
+
+		/* Not a commit -- keep it */
+		if (!commit)
+			return 0;
+	}
+
+	/* Reachable from the current ref?  Don't prune. */
+	if (commit->object.flags & REACHABLE)
+		return 0;
+	if (in_merge_bases(commit, &cb->ref_commit, 1))
+		return 0;
+
+	/* We can't reach it - prune it. */
+	return 1;
+}
+
+static void mark_reachable(struct commit *commit, unsigned long expire_limit)
+{
+	/*
+	 * We need to compute if commit on either side of an reflog
+	 * entry is reachable from the tip of the ref for all entries.
+	 * Mark commits that are reachable from the tip down to the
+	 * time threashold first; we know a commit marked thusly is
+	 * reachable from the tip without running in_merge_bases()
+	 * at all.
+	 */
+	struct commit_list *pending = NULL;
+
+	commit_list_insert(commit, &pending);
+	while (pending) {
+		struct commit_list *entry = pending;
+		struct commit_list *parent;
+		pending = entry->next;
+		commit = entry->item;
+		free(entry);
+		if (commit->object.flags & REACHABLE)
+			continue;
+		if (parse_commit(commit))
+			continue;
+		commit->object.flags |= REACHABLE;
+		if (commit->date < expire_limit)
+			continue;
+		parent = commit->parents;
+		while (parent) {
+			commit = parent->item;
+			parent = parent->next;
+			if (commit->object.flags & REACHABLE)
+				continue;
+			commit_list_insert(commit, &pending);
+		}
+	}
+}
+
 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 		const char *email, unsigned long timestamp, int tz,
 		const char *message, void *cb_data)
@@ -230,12 +295,7 @@
 	if (timestamp < cb->cmd->expire_unreachable) {
 		if (!cb->ref_commit)
 			goto prune;
-		if (!old && !is_null_sha1(osha1))
-			old = lookup_commit_reference_gently(osha1, 1);
-		if (!new && !is_null_sha1(nsha1))
-			new = lookup_commit_reference_gently(nsha1, 1);
-		if ((old && !in_merge_bases(old, &cb->ref_commit, 1)) ||
-		    (new && !in_merge_bases(new, &cb->ref_commit, 1)))
+		if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
 			goto prune;
 	}
 
@@ -288,7 +348,11 @@
 	cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
 	cb.ref = ref;
 	cb.cmd = cmd;
+	if (cb.ref_commit)
+		mark_reachable(cb.ref_commit, cmd->expire_total);
 	for_each_reflog_ent(ref, expire_reflog_ent, &cb);
+	if (cb.ref_commit)
+		clear_commit_marks(cb.ref_commit, REACHABLE);
  finish:
 	if (cb.newlog) {
 		if (fclose(cb.newlog)) {
@@ -540,11 +604,11 @@
 		free(collected.e);
 	}
 
-	while (i < argc) {
-		const char *ref = argv[i++];
+	for (; i < argc; i++) {
+		char *ref;
 		unsigned char sha1[20];
-		if (!resolve_ref(ref, sha1, 1, NULL)) {
-			status |= error("%s points nowhere!", ref);
+		if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
+			status |= error("%s points nowhere!", argv[i]);
 			continue;
 		}
 		set_reflog_expiry_param(&cb, explicit_expiry, ref);
diff --git a/builtin-remote.c b/builtin-remote.c
index 5af4e64..ca7c639 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -8,18 +8,25 @@
 #include "refs.h"
 
 static const char * const builtin_remote_usage[] = {
-	"git remote",
-	"git remote add <name> <url>",
+	"git remote [-v | --verbose]",
+	"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
+	"git remote rename <old> <new>",
 	"git remote rm <name>",
-	"git remote show <name>",
-	"git remote prune <name>",
-	"git remote update [group]",
+	"git remote set-head <name> [-a | -d | <branch>]",
+	"git remote show [-n] <name>",
+	"git remote prune [-n | --dry-run] <name>",
+	"git remote [-v | --verbose] update [-p | --prune] [group]",
 	NULL
 };
 
+#define GET_REF_STATES (1<<0)
+#define GET_HEAD_NAMES (1<<1)
+#define GET_PUSH_REF_STATES (1<<2)
+
 static int verbose;
 
 static int show_all(void);
+static int prune_remote(const char *remote, int dry_run);
 
 static inline int postfixcmp(const char *string, const char *postfix)
 {
@@ -41,7 +48,11 @@
 
 static int fetch_remote(const char *name)
 {
-	const char *argv[] = { "fetch", name, NULL };
+	const char *argv[] = { "fetch", name, NULL, NULL };
+	if (verbose) {
+		argv[1] = "-v";
+		argv[2] = name;
+	}
 	printf("Updating %s\n", name);
 	if (run_command_v_opt(argv, RUN_GIT_CMD))
 		return error("Could not fetch %s", name);
@@ -54,7 +65,7 @@
 	struct string_list track = { NULL, 0, 0 };
 	const char *master = NULL;
 	struct remote *remote;
-	struct strbuf buf, buf2;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
 	const char *name, *url;
 	int i;
 
@@ -81,9 +92,6 @@
 			remote->fetch_refspec_nr))
 		die("remote %s already exists.", name);
 
-	strbuf_init(&buf, 0);
-	strbuf_init(&buf2, 0);
-
 	strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
 	if (!valid_fetch_refspec(buf2.buf))
 		die("'%s' is not a valid remote name", name);
@@ -141,8 +149,9 @@
 }
 
 struct branch_info {
-	char *remote;
+	char *remote_name;
 	struct string_list merge;
+	int rebase;
 };
 
 static struct string_list branch_list;
@@ -159,10 +168,11 @@
 static int config_read_branches(const char *key, const char *value, void *cb)
 {
 	if (!prefixcmp(key, "branch.")) {
+		const char *orig_key = key;
 		char *name;
 		struct string_list_item *item;
 		struct branch_info *info;
-		enum { REMOTE, MERGE } type;
+		enum { REMOTE, MERGE, REBASE } type;
 
 		key += 7;
 		if (!postfixcmp(key, ".remote")) {
@@ -171,6 +181,9 @@
 		} else if (!postfixcmp(key, ".merge")) {
 			name = xstrndup(key, strlen(key) - 6);
 			type = MERGE;
+		} else if (!postfixcmp(key, ".rebase")) {
+			name = xstrndup(key, strlen(key) - 7);
+			type = REBASE;
 		} else
 			return 0;
 
@@ -180,10 +193,10 @@
 			item->util = xcalloc(sizeof(struct branch_info), 1);
 		info = item->util;
 		if (type == REMOTE) {
-			if (info->remote)
-				warning("more than one branch.%s", key);
-			info->remote = xstrdup(value);
-		} else {
+			if (info->remote_name)
+				warning("more than one %s", orig_key);
+			info->remote_name = xstrdup(value);
+		} else if (type == MERGE) {
 			char *space = strchr(value, ' ');
 			value = abbrev_branch(value);
 			while (space) {
@@ -194,7 +207,8 @@
 				space = strchr(value, ' ');
 			}
 			string_list_append(xstrdup(value), &info->merge);
-		}
+		} else
+			info->rebase = git_config_bool(orig_key, value);
 	}
 	return 0;
 }
@@ -204,12 +218,12 @@
 	if (branch_list.nr)
 		return;
 	git_config(config_read_branches, NULL);
-	sort_string_list(&branch_list);
 }
 
 struct ref_states {
 	struct remote *remote;
-	struct string_list new, stale, tracked;
+	struct string_list new, stale, tracked, heads, push;
+	int queried;
 };
 
 static int handle_one_branch(const char *refname,
@@ -225,10 +239,8 @@
 		const char *name = abbrev_branch(refspec.src);
 		/* symbolic refs pointing nowhere were handled already */
 		if ((flags & REF_ISSYMREF) ||
-				unsorted_string_list_has_string(&states->tracked,
-					name) ||
-				unsorted_string_list_has_string(&states->new,
-					name))
+		    string_list_has_string(&states->tracked, name) ||
+		    string_list_has_string(&states->new, name))
 			return 0;
 		item = string_list_append(name, &states->stale);
 		item->util = xstrdup(refname);
@@ -236,39 +248,154 @@
 	return 0;
 }
 
-static int get_ref_states(const struct ref *ref, struct ref_states *states)
+static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
 {
 	struct ref *fetch_map = NULL, **tail = &fetch_map;
+	struct ref *ref;
 	int i;
 
 	for (i = 0; i < states->remote->fetch_refspec_nr; i++)
-		if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1))
+		if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
 			die("Could not get fetch map for refspec %s",
 				states->remote->fetch_refspec[i]);
 
 	states->new.strdup_strings = states->tracked.strdup_strings = 1;
 	for (ref = fetch_map; ref; ref = ref->next) {
-		struct string_list *target = &states->tracked;
 		unsigned char sha1[20];
-		void *util = NULL;
-
 		if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
-			target = &states->new;
-		else {
-			target = &states->tracked;
-			if (hashcmp(sha1, ref->new_sha1))
-				util = &states;
-		}
-		string_list_append(abbrev_branch(ref->name), target)->util = util;
+			string_list_append(abbrev_branch(ref->name), &states->new);
+		else
+			string_list_append(abbrev_branch(ref->name), &states->tracked);
 	}
 	free_refs(fetch_map);
 
+	sort_string_list(&states->new);
+	sort_string_list(&states->tracked);
 	for_each_ref(handle_one_branch, states);
 	sort_string_list(&states->stale);
 
 	return 0;
 }
 
+struct push_info {
+	char *dest;
+	int forced;
+	enum {
+		PUSH_STATUS_CREATE = 0,
+		PUSH_STATUS_DELETE,
+		PUSH_STATUS_UPTODATE,
+		PUSH_STATUS_FASTFORWARD,
+		PUSH_STATUS_OUTOFDATE,
+		PUSH_STATUS_NOTQUERIED,
+	} status;
+};
+
+static int get_push_ref_states(const struct ref *remote_refs,
+	struct ref_states *states)
+{
+	struct remote *remote = states->remote;
+	struct ref *ref, *local_refs, *push_map, **push_tail;
+	if (remote->mirror)
+		return 0;
+
+	local_refs = get_local_heads();
+	ref = push_map = copy_ref_list(remote_refs);
+	while (ref->next)
+		ref = ref->next;
+	push_tail = &ref->next;
+
+	match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr,
+		   remote->push_refspec, MATCH_REFS_NONE);
+
+	states->push.strdup_strings = 1;
+	for (ref = push_map; ref; ref = ref->next) {
+		struct string_list_item *item;
+		struct push_info *info;
+
+		if (!ref->peer_ref)
+			continue;
+		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+
+		item = string_list_append(abbrev_branch(ref->peer_ref->name),
+					  &states->push);
+		item->util = xcalloc(sizeof(struct push_info), 1);
+		info = item->util;
+		info->forced = ref->force;
+		info->dest = xstrdup(abbrev_branch(ref->name));
+
+		if (is_null_sha1(ref->new_sha1)) {
+			info->status = PUSH_STATUS_DELETE;
+		} else if (!hashcmp(ref->old_sha1, ref->new_sha1))
+			info->status = PUSH_STATUS_UPTODATE;
+		else if (is_null_sha1(ref->old_sha1))
+			info->status = PUSH_STATUS_CREATE;
+		else if (has_sha1_file(ref->old_sha1) &&
+			 ref_newer(ref->new_sha1, ref->old_sha1))
+			info->status = PUSH_STATUS_FASTFORWARD;
+		else
+			info->status = PUSH_STATUS_OUTOFDATE;
+	}
+	free_refs(local_refs);
+	free_refs(push_map);
+	return 0;
+}
+
+static int get_push_ref_states_noquery(struct ref_states *states)
+{
+	int i;
+	struct remote *remote = states->remote;
+	struct string_list_item *item;
+	struct push_info *info;
+
+	if (remote->mirror)
+		return 0;
+
+	states->push.strdup_strings = 1;
+	if (!remote->push_refspec_nr) {
+		item = string_list_append("(matching)", &states->push);
+		info = item->util = xcalloc(sizeof(struct push_info), 1);
+		info->status = PUSH_STATUS_NOTQUERIED;
+		info->dest = xstrdup(item->string);
+	}
+	for (i = 0; i < remote->push_refspec_nr; i++) {
+		struct refspec *spec = remote->push + i;
+		if (spec->matching)
+			item = string_list_append("(matching)", &states->push);
+		else if (strlen(spec->src))
+			item = string_list_append(spec->src, &states->push);
+		else
+			item = string_list_append("(delete)", &states->push);
+
+		info = item->util = xcalloc(sizeof(struct push_info), 1);
+		info->forced = spec->force;
+		info->status = PUSH_STATUS_NOTQUERIED;
+		info->dest = xstrdup(spec->dst ? spec->dst : item->string);
+	}
+	return 0;
+}
+
+static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
+{
+	struct ref *ref, *matches;
+	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
+	struct refspec refspec;
+
+	refspec.force = 0;
+	refspec.pattern = 1;
+	refspec.src = refspec.dst = "refs/heads/*";
+	states->heads.strdup_strings = 1;
+	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
+	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
+				    fetch_map, 1);
+	for(ref = matches; ref; ref = ref->next)
+		string_list_append(abbrev_branch(ref->name), &states->heads);
+
+	free_refs(fetch_map);
+	free_refs(matches);
+
+	return 0;
+}
+
 struct known_remote {
 	struct known_remote *next;
 	struct remote *remote;
@@ -296,7 +423,7 @@
 
 struct branches_for_remote {
 	struct remote *remote;
-	struct string_list *branches;
+	struct string_list *branches, *skipped;
 	struct known_remotes *keep;
 };
 
@@ -321,6 +448,16 @@
 			return 0;
 	}
 
+	/* don't delete non-remote refs */
+	if (prefixcmp(refname, "refs/remotes")) {
+		/* advise user how to delete local branches */
+		if (!prefixcmp(refname, "refs/heads/"))
+			string_list_append(abbrev_branch(refname),
+					   branches->skipped);
+		/* silently skip over other non-remote refs */
+		return 0;
+	}
+
 	/* make sure that symrefs are deleted */
 	if (flags & REF_ISSYMREF)
 		return unlink(git_path("%s", refname));
@@ -332,6 +469,190 @@
 	return 0;
 }
 
+struct rename_info {
+	const char *old;
+	const char *new;
+	struct string_list *remote_branches;
+};
+
+static int read_remote_branches(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct rename_info *rename = cb_data;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	int flag;
+	unsigned char orig_sha1[20];
+	const char *symref;
+
+	strbuf_addf(&buf, "refs/remotes/%s", rename->old);
+	if(!prefixcmp(refname, buf.buf)) {
+		item = string_list_append(xstrdup(refname), rename->remote_branches);
+		symref = resolve_ref(refname, orig_sha1, 1, &flag);
+		if (flag & REF_ISSYMREF)
+			item->util = xstrdup(symref);
+		else
+			item->util = NULL;
+	}
+
+	return 0;
+}
+
+static int migrate_file(struct remote *remote)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int i;
+	char *path = NULL;
+
+	strbuf_addf(&buf, "remote.%s.url", remote->name);
+	for (i = 0; i < remote->url_nr; i++)
+		if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
+			return error("Could not append '%s' to '%s'",
+					remote->url[i], buf.buf);
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.push", remote->name);
+	for (i = 0; i < remote->push_refspec_nr; i++)
+		if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
+			return error("Could not append '%s' to '%s'",
+					remote->push_refspec[i], buf.buf);
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
+	for (i = 0; i < remote->fetch_refspec_nr; i++)
+		if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
+			return error("Could not append '%s' to '%s'",
+					remote->fetch_refspec[i], buf.buf);
+	if (remote->origin == REMOTE_REMOTES)
+		path = git_path("remotes/%s", remote->name);
+	else if (remote->origin == REMOTE_BRANCHES)
+		path = git_path("branches/%s", remote->name);
+	if (path && unlink(path))
+		warning("failed to remove '%s'", path);
+	return 0;
+}
+
+static int mv(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct remote *oldremote, *newremote;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
+	struct string_list remote_branches = { NULL, 0, 0, 0 };
+	struct rename_info rename;
+	int i;
+
+	if (argc != 3)
+		usage_with_options(builtin_remote_usage, options);
+
+	rename.old = argv[1];
+	rename.new = argv[2];
+	rename.remote_branches = &remote_branches;
+
+	oldremote = remote_get(rename.old);
+	if (!oldremote)
+		die("No such remote: %s", rename.old);
+
+	if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
+		return migrate_file(oldremote);
+
+	newremote = remote_get(rename.new);
+	if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
+		die("remote %s already exists.", rename.new);
+
+	strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
+	if (!valid_fetch_refspec(buf.buf))
+		die("'%s' is not a valid remote name", rename.new);
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s", rename.old);
+	strbuf_addf(&buf2, "remote.%s", rename.new);
+	if (git_config_rename_section(buf.buf, buf2.buf) < 1)
+		return error("Could not rename config section '%s' to '%s'",
+				buf.buf, buf2.buf);
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "remote.%s.fetch", rename.new);
+	if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
+		return error("Could not remove config section '%s'", buf.buf);
+	for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
+		char *ptr;
+
+		strbuf_reset(&buf2);
+		strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
+		ptr = strstr(buf2.buf, rename.old);
+		if (ptr)
+			strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
+					rename.new, strlen(rename.new));
+		if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+			return error("Could not append '%s'", buf.buf);
+	}
+
+	read_branches();
+	for (i = 0; i < branch_list.nr; i++) {
+		struct string_list_item *item = branch_list.items + i;
+		struct branch_info *info = item->util;
+		if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
+			strbuf_reset(&buf);
+			strbuf_addf(&buf, "branch.%s.remote", item->string);
+			if (git_config_set(buf.buf, rename.new)) {
+				return error("Could not set '%s'", buf.buf);
+			}
+		}
+	}
+
+	/*
+	 * First remove symrefs, then rename the rest, finally create
+	 * the new symrefs.
+	 */
+	for_each_ref(read_remote_branches, &rename);
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+		int flag = 0;
+		unsigned char sha1[20];
+
+		resolve_ref(item->string, sha1, 1, &flag);
+		if (!(flag & REF_ISSYMREF))
+			continue;
+		if (delete_ref(item->string, NULL, REF_NODEREF))
+			die("deleting '%s' failed", item->string);
+	}
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+
+		if (item->util)
+			continue;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
+				rename.new, strlen(rename.new));
+		strbuf_reset(&buf2);
+		strbuf_addf(&buf2, "remote: renamed %s to %s",
+				item->string, buf.buf);
+		if (rename_ref(item->string, buf.buf, buf2.buf))
+			die("renaming '%s' failed", item->string);
+	}
+	for (i = 0; i < remote_branches.nr; i++) {
+		struct string_list_item *item = remote_branches.items + i;
+
+		if (!item->util)
+			continue;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, item->string);
+		strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
+				rename.new, strlen(rename.new));
+		strbuf_reset(&buf2);
+		strbuf_addstr(&buf2, item->util);
+		strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
+				rename.new, strlen(rename.new));
+		strbuf_reset(&buf3);
+		strbuf_addf(&buf3, "remote: renamed %s to %s",
+				item->string, buf.buf);
+		if (create_symref(buf.buf, buf2.buf, buf3.buf))
+			die("creating '%s' failed", buf.buf);
+	}
+	return 0;
+}
+
 static int remove_branches(struct string_list *branches)
 {
 	int i, result = 0;
@@ -352,11 +673,14 @@
 		OPT_END()
 	};
 	struct remote *remote;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	struct known_remotes known_remotes = { NULL, NULL };
 	struct string_list branches = { NULL, 0, 0, 1 };
-	struct branches_for_remote cb_data = { NULL, &branches, &known_remotes };
-	int i;
+	struct string_list skipped = { NULL, 0, 0, 1 };
+	struct branches_for_remote cb_data = {
+		NULL, &branches, &skipped, &known_remotes
+	};
+	int i, result;
 
 	if (argc != 2)
 		usage_with_options(builtin_remote_usage, options);
@@ -368,7 +692,6 @@
 	known_remotes.to_delete = remote;
 	for_each_remote(add_known_remote, &known_remotes);
 
-	strbuf_init(&buf, 0);
 	strbuf_addf(&buf, "remote.%s", remote->name);
 	if (git_config_rename_section(buf.buf, NULL) < 1)
 		return error("Could not remove config section '%s'", buf.buf);
@@ -377,7 +700,7 @@
 	for (i = 0; i < branch_list.nr; i++) {
 		struct string_list_item *item = branch_list.items + i;
 		struct branch_info *info = item->util;
-		if (info->remote && !strcmp(info->remote, remote->name)) {
+		if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
 			const char *keys[] = { "remote", "merge", NULL }, **k;
 			for (k = keys; *k; k++) {
 				strbuf_reset(&buf);
@@ -397,29 +720,59 @@
 	 * refs, which are invalidated when deleting a branch.
 	 */
 	cb_data.remote = remote;
-	i = for_each_ref(add_branch_for_removal, &cb_data);
+	result = for_each_ref(add_branch_for_removal, &cb_data);
 	strbuf_release(&buf);
 
-	if (!i)
-		i = remove_branches(&branches);
+	if (!result)
+		result = remove_branches(&branches);
 	string_list_clear(&branches, 1);
 
-	return i;
+	if (skipped.nr) {
+		fprintf(stderr, skipped.nr == 1 ?
+			"Note: A non-remote branch was not removed; "
+			"to delete it, use:\n" :
+			"Note: Non-remote branches were not removed; "
+			"to delete them, use:\n");
+		for (i = 0; i < skipped.nr; i++)
+			fprintf(stderr, "  git branch -d %s\n",
+				skipped.items[i].string);
+	}
+	string_list_clear(&skipped, 0);
+
+	return result;
 }
 
-static void show_list(const char *title, struct string_list *list,
-		      const char *extra_arg)
+void clear_push_info(void *util, const char *string)
 {
-	int i;
+	struct push_info *info = util;
+	free(info->dest);
+	free(info);
+}
 
-	if (!list->nr)
-		return;
+static void free_remote_ref_states(struct ref_states *states)
+{
+	string_list_clear(&states->new, 0);
+	string_list_clear(&states->stale, 0);
+	string_list_clear(&states->tracked, 0);
+	string_list_clear(&states->heads, 0);
+	string_list_clear_func(&states->push, clear_push_info);
+}
 
-	printf(title, list->nr > 1 ? "es" : "", extra_arg);
-	printf("\n    ");
-	for (i = 0; i < list->nr; i++)
-		printf("%s%s", i ? " " : "", list->items[i].string);
-	printf("\n");
+static int append_ref_to_tracked_list(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct ref_states *states = cb_data;
+	struct refspec refspec;
+
+	if (flags & REF_ISSYMREF)
+		return 0;
+
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (!remote_find_tracking(states->remote, &refspec))
+		string_list_append(abbrev_branch(refspec.src), &states->tracked);
+
+	return 0;
 }
 
 static int get_remote_ref_states(const char *name,
@@ -427,7 +780,7 @@
 				 int query)
 {
 	struct transport *transport;
-	const struct ref *ref;
+	const struct ref *remote_refs;
 
 	states->remote = remote_get(name);
 	if (!states->remote)
@@ -438,105 +791,336 @@
 	if (query) {
 		transport = transport_get(NULL, states->remote->url_nr > 0 ?
 			states->remote->url[0] : NULL);
-		ref = transport_get_remote_refs(transport);
+		remote_refs = transport_get_remote_refs(transport);
 		transport_disconnect(transport);
 
-		get_ref_states(ref, states);
+		states->queried = 1;
+		if (query & GET_REF_STATES)
+			get_ref_states(remote_refs, states);
+		if (query & GET_HEAD_NAMES)
+			get_head_names(remote_refs, states);
+		if (query & GET_PUSH_REF_STATES)
+			get_push_ref_states(remote_refs, states);
+	} else {
+		for_each_ref(append_ref_to_tracked_list, states);
+		sort_string_list(&states->tracked);
+		get_push_ref_states_noquery(states);
 	}
 
 	return 0;
 }
 
-static int append_ref_to_tracked_list(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
+struct show_info {
+	struct string_list *list;
+	struct ref_states *states;
+	int width, width2;
+	int any_rebase;
+};
+
+int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
 {
-	struct ref_states *states = cb_data;
-	struct refspec refspec;
+	struct show_info *info = cb_data;
+	int n = strlen(item->string);
+	if (n > info->width)
+		info->width = n;
+	string_list_insert(item->string, info->list);
+	return 0;
+}
 
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
-	if (!remote_find_tracking(states->remote, &refspec))
-		string_list_append(abbrev_branch(refspec.src), &states->tracked);
+int show_remote_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *info = cb_data;
+	struct ref_states *states = info->states;
+	const char *name = item->string;
 
+	if (states->queried) {
+		const char *fmt = "%s";
+		const char *arg = "";
+		if (string_list_has_string(&states->new, name)) {
+			fmt = " new (next fetch will store in remotes/%s)";
+			arg = states->remote->name;
+		} else if (string_list_has_string(&states->tracked, name))
+			arg = " tracked";
+		else if (string_list_has_string(&states->stale, name))
+			arg = " stale (use 'git remote prune' to remove)";
+		else
+			arg = " ???";
+		printf("    %-*s", info->width, name);
+		printf(fmt, arg);
+		printf("\n");
+	} else
+		printf("    %s\n", name);
+
+	return 0;
+}
+
+int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct ref_states *states = show_info->states;
+	struct branch_info *branch_info = branch_item->util;
+	struct string_list_item *item;
+	int n;
+
+	if (!branch_info->merge.nr || !branch_info->remote_name ||
+	    strcmp(states->remote->name, branch_info->remote_name))
+		return 0;
+	if ((n = strlen(branch_item->string)) > show_info->width)
+		show_info->width = n;
+	if (branch_info->rebase)
+		show_info->any_rebase = 1;
+
+	item = string_list_insert(branch_item->string, show_info->list);
+	item->util = branch_info;
+
+	return 0;
+}
+
+int show_local_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct branch_info *branch_info = item->util;
+	struct string_list *merge = &branch_info->merge;
+	const char *also;
+	int i;
+
+	if (branch_info->rebase && branch_info->merge.nr > 1) {
+		error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
+			item->string);
+		return 0;
+	}
+
+	printf("    %-*s ", show_info->width, item->string);
+	if (branch_info->rebase) {
+		printf("rebases onto remote %s\n", merge->items[0].string);
+		return 0;
+	} else if (show_info->any_rebase) {
+		printf(" merges with remote %s\n", merge->items[0].string);
+		also = "    and with remote";
+	} else {
+		printf("merges with remote %s\n", merge->items[0].string);
+		also = "   and with remote";
+	}
+	for (i = 1; i < merge->nr; i++)
+		printf("    %-*s %s %s\n", show_info->width, "", also,
+		       merge->items[i].string);
+
+	return 0;
+}
+
+int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct push_info *push_info = push_item->util;
+	struct string_list_item *item;
+	int n;
+	if ((n = strlen(push_item->string)) > show_info->width)
+		show_info->width = n;
+	if ((n = strlen(push_info->dest)) > show_info->width2)
+		show_info->width2 = n;
+	item = string_list_append(push_item->string, show_info->list);
+	item->util = push_item->util;
+	return 0;
+}
+
+/*
+ * Sorting comparison for a string list that has push_info
+ * structs in its util field
+ */
+static int cmp_string_with_push(const void *va, const void *vb)
+{
+	const struct string_list_item *a = va;
+	const struct string_list_item *b = vb;
+	const struct push_info *a_push = a->util;
+	const struct push_info *b_push = b->util;
+	int cmp = strcmp(a->string, b->string);
+	return cmp ? cmp : strcmp(a_push->dest, b_push->dest);
+}
+
+int show_push_info_item(struct string_list_item *item, void *cb_data)
+{
+	struct show_info *show_info = cb_data;
+	struct push_info *push_info = item->util;
+	char *src = item->string, *status = NULL;
+
+	switch (push_info->status) {
+	case PUSH_STATUS_CREATE:
+		status = "create";
+		break;
+	case PUSH_STATUS_DELETE:
+		status = "delete";
+		src = "(none)";
+		break;
+	case PUSH_STATUS_UPTODATE:
+		status = "up to date";
+		break;
+	case PUSH_STATUS_FASTFORWARD:
+		status = "fast forwardable";
+		break;
+	case PUSH_STATUS_OUTOFDATE:
+		status = "local out of date";
+		break;
+	case PUSH_STATUS_NOTQUERIED:
+		break;
+	}
+	if (status)
+		printf("    %-*s %s to %-*s (%s)\n", show_info->width, src,
+			push_info->forced ? "forces" : "pushes",
+			show_info->width2, push_info->dest, status);
+	else
+		printf("    %-*s %s to %s\n", show_info->width, src,
+			push_info->forced ? "forces" : "pushes",
+			push_info->dest);
 	return 0;
 }
 
 static int show(int argc, const char **argv)
 {
-	int no_query = 0, result = 0;
+	int no_query = 0, result = 0, query_flag = 0;
 	struct option options[] = {
 		OPT_GROUP("show specific options"),
 		OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
 		OPT_END()
 	};
 	struct ref_states states;
+	struct string_list info_list = { NULL, 0, 0, 0 };
+	struct show_info info;
 
 	argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
 	if (argc < 1)
 		return show_all();
 
+	if (!no_query)
+		query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
+
 	memset(&states, 0, sizeof(states));
+	memset(&info, 0, sizeof(info));
+	info.states = &states;
+	info.list = &info_list;
 	for (; argc; argc--, argv++) {
 		int i;
 
-		get_remote_ref_states(*argv, &states, !no_query);
+		get_remote_ref_states(*argv, &states, query_flag);
 
 		printf("* remote %s\n  URL: %s\n", *argv,
 			states.remote->url_nr > 0 ?
 				states.remote->url[0] : "(no URL)");
-
-		for (i = 0; i < branch_list.nr; i++) {
-			struct string_list_item *branch = branch_list.items + i;
-			struct branch_info *info = branch->util;
-			int j;
-
-			if (!info->merge.nr || strcmp(*argv, info->remote))
-				continue;
-			printf("  Remote branch%s merged with 'git pull' "
-				"while on branch %s\n   ",
-				info->merge.nr > 1 ? "es" : "",
-				branch->string);
-			for (j = 0; j < info->merge.nr; j++)
-				printf(" %s", info->merge.items[j].string);
-			printf("\n");
-		}
-
-		if (!no_query) {
-			show_list("  New remote branch%s (next fetch "
-				"will store in remotes/%s)",
-				&states.new, states.remote->name);
-			show_list("  Stale tracking branch%s (use 'git remote "
-				"prune')", &states.stale, "");
-		}
-
 		if (no_query)
-			for_each_ref(append_ref_to_tracked_list, &states);
-		show_list("  Tracked remote branch%s", &states.tracked, "");
-
-		if (states.remote->push_refspec_nr) {
-			printf("  Local branch%s pushed with 'git push'\n   ",
-				states.remote->push_refspec_nr > 1 ?
-					"es" : "");
-			for (i = 0; i < states.remote->push_refspec_nr; i++) {
-				struct refspec *spec = states.remote->push + i;
-				printf(" %s%s%s%s", spec->force ? "+" : "",
-				       abbrev_branch(spec->src),
-				       spec->dst ? ":" : "",
-				       spec->dst ? abbrev_branch(spec->dst) : "");
-			}
-			printf("\n");
+			printf("  HEAD branch: (not queried)\n");
+		else if (!states.heads.nr)
+			printf("  HEAD branch: (unknown)\n");
+		else if (states.heads.nr == 1)
+			printf("  HEAD branch: %s\n", states.heads.items[0].string);
+		else {
+			printf("  HEAD branch (remote HEAD is ambiguous,"
+			       " may be one of the following):\n");
+			for (i = 0; i < states.heads.nr; i++)
+				printf("    %s\n", states.heads.items[i].string);
 		}
 
-		/* NEEDSWORK: free remote */
-		string_list_clear(&states.new, 0);
-		string_list_clear(&states.stale, 0);
-		string_list_clear(&states.tracked, 0);
+		/* remote branch info */
+		info.width = 0;
+		for_each_string_list(add_remote_to_show_info, &states.new, &info);
+		for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
+		for_each_string_list(add_remote_to_show_info, &states.stale, &info);
+		if (info.list->nr)
+			printf("  Remote branch%s:%s\n",
+			       info.list->nr > 1 ? "es" : "",
+				no_query ? " (status not queried)" : "");
+		for_each_string_list(show_remote_info_item, info.list, &info);
+		string_list_clear(info.list, 0);
+
+		/* git pull info */
+		info.width = 0;
+		info.any_rebase = 0;
+		for_each_string_list(add_local_to_show_info, &branch_list, &info);
+		if (info.list->nr)
+			printf("  Local branch%s configured for 'git pull':\n",
+			       info.list->nr > 1 ? "es" : "");
+		for_each_string_list(show_local_info_item, info.list, &info);
+		string_list_clear(info.list, 0);
+
+		/* git push info */
+		if (states.remote->mirror)
+			printf("  Local refs will be mirrored by 'git push'\n");
+
+		info.width = info.width2 = 0;
+		for_each_string_list(add_push_to_show_info, &states.push, &info);
+		qsort(info.list->items, info.list->nr,
+			sizeof(*info.list->items), cmp_string_with_push);
+		if (info.list->nr)
+			printf("  Local ref%s configured for 'git push'%s:\n",
+				info.list->nr > 1 ? "s" : "",
+				no_query ? " (status not queried)" : "");
+		for_each_string_list(show_push_info_item, info.list, &info);
+		string_list_clear(info.list, 0);
+
+		free_remote_ref_states(&states);
 	}
 
 	return result;
 }
 
+static int set_head(int argc, const char **argv)
+{
+	int i, opt_a = 0, opt_d = 0, result = 0;
+	struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
+	char *head_name = NULL;
+
+	struct option options[] = {
+		OPT_GROUP("set-head specific options"),
+		OPT_BOOLEAN('a', "auto", &opt_a,
+			    "set refs/remotes/<name>/HEAD according to remote"),
+		OPT_BOOLEAN('d', "delete", &opt_d,
+			    "delete refs/remotes/<name>/HEAD"),
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
+	if (argc)
+		strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
+
+	if (!opt_a && !opt_d && argc == 2) {
+		head_name = xstrdup(argv[1]);
+	} else if (opt_a && !opt_d && argc == 1) {
+		struct ref_states states;
+		memset(&states, 0, sizeof(states));
+		get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
+		if (!states.heads.nr)
+			result |= error("Cannot determine remote HEAD");
+		else if (states.heads.nr > 1) {
+			result |= error("Multiple remote HEAD branches. "
+					"Please choose one explicitly with:");
+			for (i = 0; i < states.heads.nr; i++)
+				fprintf(stderr, "  git remote set-head %s %s\n",
+					argv[0], states.heads.items[i].string);
+		} else
+			head_name = xstrdup(states.heads.items[0].string);
+		free_remote_ref_states(&states);
+	} else if (opt_d && !opt_a && argc == 1) {
+		if (delete_ref(buf.buf, NULL, REF_NODEREF))
+			result |= error("Could not delete %s", buf.buf);
+	} else
+		usage_with_options(builtin_remote_usage, options);
+
+	if (head_name) {
+		unsigned char sha1[20];
+		strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
+		/* make sure it's valid */
+		if (!resolve_ref(buf2.buf, sha1, 1, NULL))
+			result |= error("Not a valid ref: %s", buf2.buf);
+		else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+			result |= error("Could not setup %s", buf.buf);
+		if (opt_a)
+			printf("%s/HEAD set to %s\n", argv[0], head_name);
+		free(head_name);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&buf2);
+	return result;
+}
+
 static int prune(int argc, const char **argv)
 {
 	int dry_run = 0, result = 0;
@@ -545,43 +1129,49 @@
 		OPT__DRY_RUN(&dry_run),
 		OPT_END()
 	};
-	struct ref_states states;
 
 	argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
 	if (argc < 1)
 		usage_with_options(builtin_remote_usage, options);
 
+	for (; argc; argc--, argv++)
+		result |= prune_remote(*argv, dry_run);
+
+	return result;
+}
+
+static int prune_remote(const char *remote, int dry_run)
+{
+	int result = 0, i;
+	struct ref_states states;
+	const char *dangling_msg = dry_run
+		? " %s will become dangling!\n"
+		: " %s has become dangling!\n";
+
 	memset(&states, 0, sizeof(states));
-	for (; argc; argc--, argv++) {
-		int i;
+	get_remote_ref_states(remote, &states, GET_REF_STATES);
 
-		get_remote_ref_states(*argv, &states, 1);
-
-		if (states.stale.nr) {
-			printf("Pruning %s\n", *argv);
-			printf("URL: %s\n",
-			       states.remote->url_nr
-			       ? states.remote->url[0]
-			       : "(no URL)");
-		}
-
-		for (i = 0; i < states.stale.nr; i++) {
-			const char *refname = states.stale.items[i].util;
-
-			if (!dry_run)
-				result |= delete_ref(refname, NULL, 0);
-
-			printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
-			       abbrev_ref(refname, "refs/remotes/"));
-		}
-
-		/* NEEDSWORK: free remote */
-		string_list_clear(&states.new, 0);
-		string_list_clear(&states.stale, 0);
-		string_list_clear(&states.tracked, 0);
+	if (states.stale.nr) {
+		printf("Pruning %s\n", remote);
+		printf("URL: %s\n",
+		       states.remote->url_nr
+		       ? states.remote->url[0]
+		       : "(no URL)");
 	}
 
+	for (i = 0; i < states.stale.nr; i++) {
+		const char *refname = states.stale.items[i].util;
+
+		if (!dry_run)
+			result |= delete_ref(refname, NULL, 0);
+
+		printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
+		       abbrev_ref(refname, "refs/remotes/"));
+		warn_dangling_symref(dangling_msg, refname);
+	}
+
+	free_remote_ref_states(&states);
 	return result;
 }
 
@@ -589,7 +1179,7 @@
 {
 	struct string_list *list = priv;
 	if (!remote->skip_default_update)
-		string_list_append(xstrdup(remote->name), list);
+		string_list_append(remote->name, list);
 	return 0;
 }
 
@@ -598,16 +1188,18 @@
 	struct string_list *list;
 } remote_group;
 
-static int get_remote_group(const char *key, const char *value, void *cb)
+static int get_remote_group(const char *key, const char *value, void *num_hits)
 {
 	if (!prefixcmp(key, "remotes.") &&
 			!strcmp(key + 8, remote_group.name)) {
 		/* split list by white space */
 		int space = strcspn(value, " \t\n");
 		while (*value) {
-			if (space > 1)
+			if (space > 1) {
 				string_list_append(xstrndup(value, space),
 						remote_group.list);
+				++*((int *)num_hits);
+			}
 			value += space + (value[space] != '\0');
 			space = strcspn(value, " \t\n");
 		}
@@ -618,10 +1210,18 @@
 
 static int update(int argc, const char **argv)
 {
-	int i, result = 0;
+	int i, result = 0, prune = 0;
 	struct string_list list = { NULL, 0, 0, 0 };
 	static const char *default_argv[] = { NULL, "default", NULL };
+	struct option options[] = {
+		OPT_GROUP("update specific options"),
+		OPT_BOOLEAN('p', "prune", &prune,
+			    "prune remotes after fecthing"),
+		OPT_END()
+	};
 
+	argc = parse_options(argc, argv, options, builtin_remote_usage,
+			     PARSE_OPT_KEEP_ARGV0);
 	if (argc < 2) {
 		argc = 2;
 		argv = default_argv;
@@ -629,15 +1229,28 @@
 
 	remote_group.list = &list;
 	for (i = 1; i < argc; i++) {
+		int groups_found = 0;
 		remote_group.name = argv[i];
-		result = git_config(get_remote_group, NULL);
+		result = git_config(get_remote_group, &groups_found);
+		if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) {
+			struct remote *remote;
+			if (!remote_is_configured(argv[i]))
+				die("No such remote or remote group: %s",
+				    argv[i]);
+			remote = remote_get(argv[i]);
+			string_list_append(remote->name, remote_group.list);
+		}
 	}
 
 	if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
 		result = for_each_remote(get_one_remote_for_update, &list);
 
-	for (i = 0; i < list.nr; i++)
-		result |= fetch_remote(list.items[i].string);
+	for (i = 0; i < list.nr; i++) {
+		int err = fetch_remote(list.items[i].string);
+		result |= err;
+		if (!err && prune)
+			result |= prune_remote(list.items[i].string, 0);
+	}
 
 	/* all names were strdup()ed or strndup()ed */
 	list.strdup_strings = 1;
@@ -700,8 +1313,12 @@
 		result = show_all();
 	else if (!strcmp(argv[0], "add"))
 		result = add(argc, argv);
+	else if (!strcmp(argv[0], "rename"))
+		result = mv(argc, argv);
 	else if (!strcmp(argv[0], "rm"))
 		result = rm(argc, argv);
+	else if (!strcmp(argv[0], "set-head"))
+		result = set_head(argc, argv);
 	else if (!strcmp(argv[0], "show"))
 		result = show(argc, argv);
 	else if (!strcmp(argv[0], "prune"))
diff --git a/builtin-rerere.c b/builtin-rerere.c
index dd4573f..020af73 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "dir.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff/xdiff.h"
@@ -12,28 +13,17 @@
 static int cutoff_noresolve = 15;
 static int cutoff_resolve = 60;
 
-static const char *rr_path(const char *name, const char *file)
-{
-	return git_path("rr-cache/%s/%s", name, file);
-}
-
 static time_t rerere_created_at(const char *name)
 {
 	struct stat st;
-	return stat(rr_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
-}
-
-static int has_resolution(const char *name)
-{
-	struct stat st;
-	return !stat(rr_path(name, "postimage"), &st);
+	return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
 
 static void unlink_rr_item(const char *name)
 {
-	unlink(rr_path(name, "thisimage"));
-	unlink(rr_path(name, "preimage"));
-	unlink(rr_path(name, "postimage"));
+	unlink(rerere_path(name, "thisimage"));
+	unlink(rerere_path(name, "preimage"));
+	unlink(rerere_path(name, "postimage"));
 	rmdir(git_path("rr-cache/%s", name));
 }
 
@@ -59,17 +49,15 @@
 	git_config(git_rerere_gc_config, NULL);
 	dir = opendir(git_path("rr-cache"));
 	while ((e = readdir(dir))) {
-		const char *name = e->d_name;
-		if (name[0] == '.' &&
-		    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
+		if (is_dot_or_dotdot(e->d_name))
 			continue;
-		then = rerere_created_at(name);
+		then = rerere_created_at(e->d_name);
 		if (!then)
 			continue;
-		cutoff = (has_resolution(name)
+		cutoff = (has_rerere_resolution(e->d_name)
 			  ? cutoff_resolve : cutoff_noresolve);
 		if (then < now - cutoff * 86400)
-			string_list_append(name, &to_remove);
+			string_list_append(e->d_name, &to_remove);
 	}
 	for (i = 0; i < to_remove.nr; i++)
 		unlink_rr_item(to_remove.items[i].string);
@@ -98,6 +86,7 @@
 
 	printf("--- a/%s\n+++ b/%s\n", label1, label2);
 	fflush(stdout);
+	memset(&xpp, 0, sizeof(xpp));
 	xpp.flags = XDF_NEED_MINIMAL;
 	memset(&xecfg, 0, sizeof(xecfg));
 	xecfg.ctxlen = 3;
@@ -124,7 +113,7 @@
 	if (!strcmp(argv[1], "clear")) {
 		for (i = 0; i < merge_rr.nr; i++) {
 			const char *name = (const char *)merge_rr.items[i].util;
-			if (!has_resolution(name))
+			if (!has_rerere_resolution(name))
 				unlink_rr_item(name);
 		}
 		unlink(git_path("rr-cache/MERGE_RR"));
@@ -137,7 +126,7 @@
 		for (i = 0; i < merge_rr.nr; i++) {
 			const char *path = merge_rr.items[i].string;
 			const char *name = (const char *)merge_rr.items[i].util;
-			diff_two(rr_path(name, "preimage"), path, path, path);
+			diff_two(rerere_path(name, "preimage"), path, path, path);
 		}
 	else
 		usage(git_rerere_usage);
diff --git a/builtin-reset.c b/builtin-reset.c
index 9514b77..c0cb915 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -20,11 +20,14 @@
 #include "parse-options.h"
 
 static const char * const git_reset_usage[] = {
-	"git reset [--mixed | --soft | --hard] [-q] [<commit>]",
+	"git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
 	"git reset [--mixed] <commit> [--] <paths>...",
 	NULL
 };
 
+enum reset_type { MIXED, SOFT, HARD, MERGE, NONE };
+static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL };
+
 static char *args_to_str(const char **argv)
 {
 	char *buf = NULL;
@@ -49,7 +52,7 @@
 	return !access(git_path("MERGE_HEAD"), F_OK);
 }
 
-static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int quiet)
+static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
 {
 	int i = 0;
 	const char *args[6];
@@ -57,9 +60,17 @@
 	args[i++] = "read-tree";
 	if (!quiet)
 		args[i++] = "-v";
-	args[i++] = "--reset";
-	if (is_hard_reset)
+	switch (reset_type) {
+	case MERGE:
 		args[i++] = "-u";
+		args[i++] = "-m";
+		break;
+	case HARD:
+		args[i++] = "-u";
+		/* fallthrough */
+	default:
+		args[i++] = "--reset";
+	}
 	args[i++] = sha1_to_hex(sha1);
 	args[i] = NULL;
 
@@ -169,9 +180,6 @@
 		warning("Reflog action message too long: %.*s...", 50, buf);
 }
 
-enum reset_type { MIXED, SOFT, HARD, NONE };
-static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
-
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -186,6 +194,8 @@
 		OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
 		OPT_SET_INT(0, "hard", &reset_type,
 				"reset HEAD, index and working tree", HARD),
+		OPT_SET_INT(0, "merge", &reset_type,
+				"reset HEAD, index and working tree", MERGE),
 		OPT_BOOLEAN('q', NULL, &quiet,
 				"disable showing new HEAD in hard reset and progress message"),
 		OPT_END()
@@ -266,7 +276,7 @@
 		if (is_merge() || read_cache() < 0 || unmerged_cache())
 			die("Cannot do a soft reset in the middle of a merge.");
 	}
-	else if (reset_index_file(sha1, (reset_type == HARD), quiet))
+	else if (reset_index_file(sha1, reset_type, quiet))
 		die("Could not reset index file to revision '%s'.", rev);
 
 	/* Any resets update HEAD to the head being switched to,
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index aa3c962..38a8f23 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -1,20 +1,12 @@
 #include "cache.h"
-#include "refs.h"
-#include "tag.h"
 #include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "tree-walk.h"
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
 #include "builtin.h"
 #include "log-tree.h"
 #include "graph.h"
-
-/* bits #0-15 in revision.h */
-
-#define COUNTED		(1u<<16)
+#include "bisect.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -50,74 +42,69 @@
 "    --bisect-all"
 ;
 
-static struct rev_info revs;
-
-static int bisect_list;
-static int show_timestamp;
-static int hdr_termination;
-static const char *header_prefix;
-
-static void finish_commit(struct commit *commit);
-static void show_commit(struct commit *commit)
+static void finish_commit(struct commit *commit, void *data);
+static void show_commit(struct commit *commit, void *data)
 {
-	graph_show_commit(revs.graph);
+	struct rev_list_info *info = data;
+	struct rev_info *revs = info->revs;
 
-	if (show_timestamp)
+	graph_show_commit(revs->graph);
+
+	if (info->show_timestamp)
 		printf("%lu ", commit->date);
-	if (header_prefix)
-		fputs(header_prefix, stdout);
+	if (info->header_prefix)
+		fputs(info->header_prefix, stdout);
 
-	if (!revs.graph) {
+	if (!revs->graph) {
 		if (commit->object.flags & BOUNDARY)
 			putchar('-');
 		else if (commit->object.flags & UNINTERESTING)
 			putchar('^');
-		else if (revs.left_right) {
+		else if (revs->left_right) {
 			if (commit->object.flags & SYMMETRIC_LEFT)
 				putchar('<');
 			else
 				putchar('>');
 		}
 	}
-	if (revs.abbrev_commit && revs.abbrev)
-		fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
+	if (revs->abbrev_commit && revs->abbrev)
+		fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
 		      stdout);
 	else
 		fputs(sha1_to_hex(commit->object.sha1), stdout);
-	if (revs.print_parents) {
+	if (revs->print_parents) {
 		struct commit_list *parents = commit->parents;
 		while (parents) {
 			printf(" %s", sha1_to_hex(parents->item->object.sha1));
 			parents = parents->next;
 		}
 	}
-	if (revs.children.name) {
+	if (revs->children.name) {
 		struct commit_list *children;
 
-		children = lookup_decoration(&revs.children, &commit->object);
+		children = lookup_decoration(&revs->children, &commit->object);
 		while (children) {
 			printf(" %s", sha1_to_hex(children->item->object.sha1));
 			children = children->next;
 		}
 	}
-	show_decorations(commit);
-	if (revs.commit_format == CMIT_FMT_ONELINE)
+	show_decorations(revs, commit);
+	if (revs->commit_format == CMIT_FMT_ONELINE)
 		putchar(' ');
 	else
 		putchar('\n');
 
-	if (revs.verbose_header && commit->buffer) {
-		struct strbuf buf;
-		strbuf_init(&buf, 0);
-		pretty_print_commit(revs.commit_format, commit,
-				    &buf, revs.abbrev, NULL, NULL,
-				    revs.date_mode, 0);
-		if (revs.graph) {
+	if (revs->verbose_header && commit->buffer) {
+		struct strbuf buf = STRBUF_INIT;
+		pretty_print_commit(revs->commit_format, commit,
+				    &buf, revs->abbrev, NULL, NULL,
+				    revs->date_mode, 0);
+		if (revs->graph) {
 			if (buf.len) {
-				if (revs.commit_format != CMIT_FMT_ONELINE)
-					graph_show_oneline(revs.graph);
+				if (revs->commit_format != CMIT_FMT_ONELINE)
+					graph_show_oneline(revs->graph);
 
-				graph_show_commit_msg(revs.graph, &buf);
+				graph_show_commit_msg(revs->graph, &buf);
 
 				/*
 				 * Add a newline after the commit message.
@@ -135,7 +122,7 @@
 				 * format doesn't explicitly end in a newline.)
 				 */
 				if (buf.len && buf.buf[buf.len - 1] == '\n')
-					graph_show_padding(revs.graph);
+					graph_show_padding(revs->graph);
 				putchar('\n');
 			} else {
 				/*
@@ -143,23 +130,23 @@
 				 * the rest of the graph output for this
 				 * commit.
 				 */
-				if (graph_show_remainder(revs.graph))
+				if (graph_show_remainder(revs->graph))
 					putchar('\n');
 			}
 		} else {
 			if (buf.len)
-				printf("%s%c", buf.buf, hdr_termination);
+				printf("%s%c", buf.buf, info->hdr_termination);
 		}
 		strbuf_release(&buf);
 	} else {
-		if (graph_show_remainder(revs.graph))
+		if (graph_show_remainder(revs->graph))
 			putchar('\n');
 	}
 	maybe_flush_or_die(stdout, "stdout");
-	finish_commit(commit);
+	finish_commit(commit, data);
 }
 
-static void finish_commit(struct commit *commit)
+static void finish_commit(struct commit *commit, void *data)
 {
 	if (commit->parents) {
 		free_commit_list(commit->parents);
@@ -199,389 +186,122 @@
 	printf("-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
+static inline int log2i(int n)
+{
+	int log2 = 0;
+
+	for (; n > 1; n >>= 1)
+		log2++;
+
+	return log2;
+}
+
+static inline int exp2i(int n)
+{
+	return 1 << n;
+}
+
 /*
- * This is a truly stupid algorithm, but it's only
- * used for bisection, and we just don't care enough.
+ * Estimate the number of bisect steps left (after the current step)
  *
- * We care just barely enough to avoid recursing for
- * non-merge entries.
+ * For any x between 0 included and 2^n excluded, the probability for
+ * n - 1 steps left looks like:
+ *
+ * P(2^n + x) == (2^n - x) / (2^n + x)
+ *
+ * and P(2^n + x) < 0.5 means 2^n < 3x
  */
-static int count_distance(struct commit_list *entry)
+static int estimate_bisect_steps(int all)
 {
-	int nr = 0;
+	int n, x, e;
 
-	while (entry) {
-		struct commit *commit = entry->item;
-		struct commit_list *p;
-
-		if (commit->object.flags & (UNINTERESTING | COUNTED))
-			break;
-		if (!(commit->object.flags & TREESAME))
-			nr++;
-		commit->object.flags |= COUNTED;
-		p = commit->parents;
-		entry = p;
-		if (p) {
-			p = p->next;
-			while (p) {
-				nr += count_distance(p);
-				p = p->next;
-			}
-		}
-	}
-
-	return nr;
-}
-
-static void clear_distance(struct commit_list *list)
-{
-	while (list) {
-		struct commit *commit = list->item;
-		commit->object.flags &= ~COUNTED;
-		list = list->next;
-	}
-}
-
-#define DEBUG_BISECT 0
-
-static inline int weight(struct commit_list *elem)
-{
-	return *((int*)(elem->item->util));
-}
-
-static inline void weight_set(struct commit_list *elem, int weight)
-{
-	*((int*)(elem->item->util)) = weight;
-}
-
-static int count_interesting_parents(struct commit *commit)
-{
-	struct commit_list *p;
-	int count;
-
-	for (count = 0, p = commit->parents; p; p = p->next) {
-		if (p->item->object.flags & UNINTERESTING)
-			continue;
-		count++;
-	}
-	return count;
-}
-
-static inline int halfway(struct commit_list *p, int nr)
-{
-	/*
-	 * Don't short-cut something we are not going to return!
-	 */
-	if (p->item->object.flags & TREESAME)
+	if (all < 3)
 		return 0;
-	if (DEBUG_BISECT)
-		return 0;
-	/*
-	 * 2 and 3 are halfway of 5.
-	 * 3 is halfway of 6 but 2 and 4 are not.
-	 */
-	switch (2 * weight(p) - nr) {
-	case -1: case 0: case 1:
+
+	n = log2i(all);
+	e = exp2i(n);
+	x = all - e;
+
+	return (e < 3 * x) ? n : n - 1;
+}
+
+static void show_tried_revs(struct commit_list *tried, int stringed)
+{
+	printf("bisect_tried='");
+	for (;tried; tried = tried->next) {
+		char *format = tried->next ? "%s|" : "%s";
+		printf(format, sha1_to_hex(tried->item->object.sha1));
+	}
+	printf(stringed ? "' &&\n" : "'\n");
+}
+
+int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
+{
+	int cnt, flags = info->bisect_show_flags;
+	char hex[41] = "", *format;
+	struct commit_list *tried;
+	struct rev_info *revs = info->revs;
+
+	if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
 		return 1;
-	default:
-		return 0;
-	}
-}
 
-#if !DEBUG_BISECT
-#define show_list(a,b,c,d) do { ; } while (0)
-#else
-static void show_list(const char *debug, int counted, int nr,
-		      struct commit_list *list)
-{
-	struct commit_list *p;
-
-	fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
-
-	for (p = list; p; p = p->next) {
-		struct commit_list *pp;
-		struct commit *commit = p->item;
-		unsigned flags = commit->object.flags;
-		enum object_type type;
-		unsigned long size;
-		char *buf = read_sha1_file(commit->object.sha1, &type, &size);
-		char *ep, *sp;
-
-		fprintf(stderr, "%c%c%c ",
-			(flags & TREESAME) ? ' ' : 'T',
-			(flags & UNINTERESTING) ? 'U' : ' ',
-			(flags & COUNTED) ? 'C' : ' ');
-		if (commit->util)
-			fprintf(stderr, "%3d", weight(p));
-		else
-			fprintf(stderr, "---");
-		fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
-		for (pp = commit->parents; pp; pp = pp->next)
-			fprintf(stderr, " %.*s", 8,
-				sha1_to_hex(pp->item->object.sha1));
-
-		sp = strstr(buf, "\n\n");
-		if (sp) {
-			sp += 2;
-			for (ep = sp; *ep && *ep != '\n'; ep++)
-				;
-			fprintf(stderr, " %.*s", (int)(ep - sp), sp);
-		}
-		fprintf(stderr, "\n");
-	}
-}
-#endif /* DEBUG_BISECT */
-
-static struct commit_list *best_bisection(struct commit_list *list, int nr)
-{
-	struct commit_list *p, *best;
-	int best_distance = -1;
-
-	best = list;
-	for (p = list; p; p = p->next) {
-		int distance;
-		unsigned flags = p->item->object.flags;
-
-		if (flags & TREESAME)
-			continue;
-		distance = weight(p);
-		if (nr - distance < distance)
-			distance = nr - distance;
-		if (distance > best_distance) {
-			best = p;
-			best_distance = distance;
-		}
-	}
-
-	return best;
-}
-
-struct commit_dist {
-	struct commit *commit;
-	int distance;
-};
-
-static int compare_commit_dist(const void *a_, const void *b_)
-{
-	struct commit_dist *a, *b;
-
-	a = (struct commit_dist *)a_;
-	b = (struct commit_dist *)b_;
-	if (a->distance != b->distance)
-		return b->distance - a->distance; /* desc sort */
-	return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
-}
-
-static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
-{
-	struct commit_list *p;
-	struct commit_dist *array = xcalloc(nr, sizeof(*array));
-	int cnt, i;
-
-	for (p = list, cnt = 0; p; p = p->next) {
-		int distance;
-		unsigned flags = p->item->object.flags;
-
-		if (flags & TREESAME)
-			continue;
-		distance = weight(p);
-		if (nr - distance < distance)
-			distance = nr - distance;
-		array[cnt].commit = p->item;
-		array[cnt].distance = distance;
-		cnt++;
-	}
-	qsort(array, cnt, sizeof(*array), compare_commit_dist);
-	for (p = list, i = 0; i < cnt; i++) {
-		struct name_decoration *r = xmalloc(sizeof(*r) + 100);
-		struct object *obj = &(array[i].commit->object);
-
-		sprintf(r->name, "dist=%d", array[i].distance);
-		r->next = add_decoration(&name_decoration, obj, r);
-		p->item = array[i].commit;
-		p = p->next;
-	}
-	if (p)
-		p->next = NULL;
-	free(array);
-	return list;
-}
-
-/*
- * zero or positive weight is the number of interesting commits it can
- * reach, including itself.  Especially, weight = 0 means it does not
- * reach any tree-changing commits (e.g. just above uninteresting one
- * but traversal is with pathspec).
- *
- * weight = -1 means it has one parent and its distance is yet to
- * be computed.
- *
- * weight = -2 means it has more than one parent and its distance is
- * unknown.  After running count_distance() first, they will get zero
- * or positive distance.
- */
-static struct commit_list *do_find_bisection(struct commit_list *list,
-					     int nr, int *weights,
-					     int find_all)
-{
-	int n, counted;
-	struct commit_list *p;
-
-	counted = 0;
-
-	for (n = 0, p = list; p; p = p->next) {
-		struct commit *commit = p->item;
-		unsigned flags = commit->object.flags;
-
-		p->item->util = &weights[n++];
-		switch (count_interesting_parents(commit)) {
-		case 0:
-			if (!(flags & TREESAME)) {
-				weight_set(p, 1);
-				counted++;
-				show_list("bisection 2 count one",
-					  counted, nr, list);
-			}
-			/*
-			 * otherwise, it is known not to reach any
-			 * tree-changing commit and gets weight 0.
-			 */
-			break;
-		case 1:
-			weight_set(p, -1);
-			break;
-		default:
-			weight_set(p, -2);
-			break;
-		}
-	}
-
-	show_list("bisection 2 initialize", counted, nr, list);
+	revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
 
 	/*
-	 * If you have only one parent in the resulting set
-	 * then you can reach one commit more than that parent
-	 * can reach.  So we do not have to run the expensive
-	 * count_distance() for single strand of pearls.
-	 *
-	 * However, if you have more than one parents, you cannot
-	 * just add their distance and one for yourself, since
-	 * they usually reach the same ancestor and you would
-	 * end up counting them twice that way.
-	 *
-	 * So we will first count distance of merges the usual
-	 * way, and then fill the blanks using cheaper algorithm.
+	 * revs->commits can reach "reaches" commits among
+	 * "all" commits.  If it is good, then there are
+	 * (all-reaches) commits left to be bisected.
+	 * On the other hand, if it is bad, then the set
+	 * to bisect is "reaches".
+	 * A bisect set of size N has (N-1) commits further
+	 * to test, as we already know one bad one.
 	 */
-	for (p = list; p; p = p->next) {
-		if (p->item->object.flags & UNINTERESTING)
-			continue;
-		if (weight(p) != -2)
-			continue;
-		weight_set(p, count_distance(p));
-		clear_distance(list);
+	cnt = all - reaches;
+	if (cnt < reaches)
+		cnt = reaches;
 
-		/* Does it happen to be at exactly half-way? */
-		if (!find_all && halfway(p, nr))
-			return p;
-		counted++;
+	if (revs->commits)
+		strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
+
+	if (flags & BISECT_SHOW_ALL) {
+		traverse_commit_list(revs, show_commit, show_object, info);
+		printf("------\n");
 	}
 
-	show_list("bisection 2 count_distance", counted, nr, list);
+	if (flags & BISECT_SHOW_TRIED)
+		show_tried_revs(tried, flags & BISECT_SHOW_STRINGED);
+	format = (flags & BISECT_SHOW_STRINGED) ?
+		"bisect_rev=%s &&\n"
+		"bisect_nr=%d &&\n"
+		"bisect_good=%d &&\n"
+		"bisect_bad=%d &&\n"
+		"bisect_all=%d &&\n"
+		"bisect_steps=%d\n"
+		:
+		"bisect_rev=%s\n"
+		"bisect_nr=%d\n"
+		"bisect_good=%d\n"
+		"bisect_bad=%d\n"
+		"bisect_all=%d\n"
+		"bisect_steps=%d\n";
+	printf(format,
+	       hex,
+	       cnt - 1,
+	       all - reaches - 1,
+	       reaches - 1,
+	       all,
+	       estimate_bisect_steps(all));
 
-	while (counted < nr) {
-		for (p = list; p; p = p->next) {
-			struct commit_list *q;
-			unsigned flags = p->item->object.flags;
-
-			if (0 <= weight(p))
-				continue;
-			for (q = p->item->parents; q; q = q->next) {
-				if (q->item->object.flags & UNINTERESTING)
-					continue;
-				if (0 <= weight(q))
-					break;
-			}
-			if (!q)
-				continue;
-
-			/*
-			 * weight for p is unknown but q is known.
-			 * add one for p itself if p is to be counted,
-			 * otherwise inherit it from q directly.
-			 */
-			if (!(flags & TREESAME)) {
-				weight_set(p, weight(q)+1);
-				counted++;
-				show_list("bisection 2 count one",
-					  counted, nr, list);
-			}
-			else
-				weight_set(p, weight(q));
-
-			/* Does it happen to be at exactly half-way? */
-			if (!find_all && halfway(p, nr))
-				return p;
-		}
-	}
-
-	show_list("bisection 2 counted all", counted, nr, list);
-
-	if (!find_all)
-		return best_bisection(list, nr);
-	else
-		return best_bisection_sorted(list, nr);
-}
-
-static struct commit_list *find_bisection(struct commit_list *list,
-					  int *reaches, int *all,
-					  int find_all)
-{
-	int nr, on_list;
-	struct commit_list *p, *best, *next, *last;
-	int *weights;
-
-	show_list("bisection 2 entry", 0, 0, list);
-
-	/*
-	 * Count the number of total and tree-changing items on the
-	 * list, while reversing the list.
-	 */
-	for (nr = on_list = 0, last = NULL, p = list;
-	     p;
-	     p = next) {
-		unsigned flags = p->item->object.flags;
-
-		next = p->next;
-		if (flags & UNINTERESTING)
-			continue;
-		p->next = last;
-		last = p;
-		if (!(flags & TREESAME))
-			nr++;
-		on_list++;
-	}
-	list = last;
-	show_list("bisection 2 sorted", 0, nr, list);
-
-	*all = nr;
-	weights = xcalloc(on_list, sizeof(*weights));
-
-	/* Do the real work of finding bisection commit. */
-	best = do_find_bisection(list, nr, weights, find_all);
-	if (best) {
-		if (!find_all)
-			best->next = NULL;
-		*reaches = weight(best);
-	}
-	free(weights);
-	return best;
+	return 0;
 }
 
 int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
-	struct commit_list *list;
+	struct rev_info revs;
+	struct rev_list_info info;
 	int i;
 	int read_from_stdin = 0;
+	int bisect_list = 0;
 	int bisect_show_vars = 0;
 	int bisect_find_all = 0;
 	int quiet = 0;
@@ -592,6 +312,9 @@
 	revs.commit_format = CMIT_FMT_UNSPECIFIED;
 	argc = setup_revisions(argc, argv, &revs, NULL);
 
+	memset(&info, 0, sizeof(info));
+	info.revs = &revs;
+
 	quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
 	for (i = 1 ; i < argc; i++) {
 		const char *arg = argv[i];
@@ -601,7 +324,7 @@
 			continue;
 		}
 		if (!strcmp(arg, "--timestamp")) {
-			show_timestamp = 1;
+			info.show_timestamp = 1;
 			continue;
 		}
 		if (!strcmp(arg, "--bisect")) {
@@ -611,6 +334,8 @@
 		if (!strcmp(arg, "--bisect-all")) {
 			bisect_list = 1;
 			bisect_find_all = 1;
+			info.bisect_show_flags = BISECT_SHOW_ALL;
+			revs.show_decorations = 1;
 			continue;
 		}
 		if (!strcmp(arg, "--bisect-vars")) {
@@ -629,19 +354,17 @@
 	}
 	if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
 		/* The command line has a --pretty  */
-		hdr_termination = '\n';
+		info.hdr_termination = '\n';
 		if (revs.commit_format == CMIT_FMT_ONELINE)
-			header_prefix = "";
+			info.header_prefix = "";
 		else
-			header_prefix = "commit ";
+			info.header_prefix = "commit ";
 	}
 	else if (revs.verbose_header)
 		/* Only --header was specified */
 		revs.commit_format = CMIT_FMT_RAW;
 
-	list = revs.commits;
-
-	if ((!list &&
+	if ((!revs.commits &&
 	     (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
 	      !revs.pending.nr)) ||
 	    revs.diff)
@@ -662,47 +385,15 @@
 
 		revs.commits = find_bisection(revs.commits, &reaches, &all,
 					      bisect_find_all);
-		if (bisect_show_vars) {
-			int cnt;
-			char hex[41];
-			if (!revs.commits)
-				return 1;
-			/*
-			 * revs.commits can reach "reaches" commits among
-			 * "all" commits.  If it is good, then there are
-			 * (all-reaches) commits left to be bisected.
-			 * On the other hand, if it is bad, then the set
-			 * to bisect is "reaches".
-			 * A bisect set of size N has (N-1) commits further
-			 * to test, as we already know one bad one.
-			 */
-			cnt = all - reaches;
-			if (cnt < reaches)
-				cnt = reaches;
-			strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
 
-			if (bisect_find_all) {
-				traverse_commit_list(&revs, show_commit, show_object);
-				printf("------\n");
-			}
-
-			printf("bisect_rev=%s\n"
-			       "bisect_nr=%d\n"
-			       "bisect_good=%d\n"
-			       "bisect_bad=%d\n"
-			       "bisect_all=%d\n",
-			       hex,
-			       cnt - 1,
-			       all - reaches - 1,
-			       reaches - 1,
-			       all);
-			return 0;
-		}
+		if (bisect_show_vars)
+			return show_bisect_vars(&info, reaches, all);
 	}
 
 	traverse_commit_list(&revs,
-		quiet ? finish_commit : show_commit,
-		quiet ? finish_object : show_object);
+			     quiet ? finish_commit : show_commit,
+			     quiet ? finish_object : show_object,
+			     &info);
 
 	return 0;
 }
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 9aa049e..22c6d6a 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -26,6 +26,8 @@
 #define SHOW_SYMBOLIC_FULL 2
 static int symbolic;
 static int abbrev;
+static int abbrev_ref;
+static int abbrev_ref_strict;
 static int output_sq;
 
 /*
@@ -109,8 +111,8 @@
 		return;
 	def = NULL;
 
-	if (symbolic && name) {
-		if (symbolic == SHOW_SYMBOLIC_FULL) {
+	if ((symbolic || abbrev_ref) && name) {
+		if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
 			unsigned char discard[20];
 			char *full;
 
@@ -125,6 +127,9 @@
 				 */
 				break;
 			case 1: /* happy */
+				if (abbrev_ref)
+					full = shorten_unambiguous_ref(full,
+						abbrev_ref_strict);
 				show_with_type(type, full);
 				break;
 			default: /* ambiguous */
@@ -307,19 +312,17 @@
 		OPT_END(),
 	};
 
-	struct strbuf sb, parsed;
+	struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
 	const char **usage = NULL;
 	struct option *opts = NULL;
 	int onb = 0, osz = 0, unb = 0, usz = 0;
 
-	strbuf_init(&parsed, 0);
 	strbuf_addstr(&parsed, "set --");
 	argc = parse_options(argc, argv, parseopt_opts, parseopt_usage,
 	                     PARSE_OPT_KEEP_DASHDASH);
 	if (argc < 1 || strcmp(argv[0], "--"))
 		usage_with_options(parseopt_usage, parseopt_opts);
 
-	strbuf_init(&sb, 0);
 	/* get the usage up to the first line with a -- on it */
 	for (;;) {
 		if (strbuf_getline(&sb, stdin, '\n') == EOF)
@@ -508,6 +511,20 @@
 				symbolic = SHOW_SYMBOLIC_FULL;
 				continue;
 			}
+			if (!prefixcmp(arg, "--abbrev-ref") &&
+			    (!arg[12] || arg[12] == '=')) {
+				abbrev_ref = 1;
+				abbrev_ref_strict = warn_ambiguous_refs;
+				if (arg[12] == '=') {
+					if (!strcmp(arg + 13, "strict"))
+						abbrev_ref_strict = 1;
+					else if (!strcmp(arg + 13, "loose"))
+						abbrev_ref_strict = 0;
+					else
+						die("unknown mode for %s", arg);
+				}
+				continue;
+			}
 			if (!strcmp(arg, "--all")) {
 				for_each_ref(show_reference, NULL);
 				continue;
diff --git a/builtin-revert.c b/builtin-revert.c
index 74845ef..3f2614e 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -11,6 +11,8 @@
 #include "cache-tree.h"
 #include "diff.h"
 #include "revision.h"
+#include "rerere.h"
+#include "merge-recursive.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -200,36 +202,6 @@
 			sha1_to_hex(commit->object.sha1));
 }
 
-static int merge_recursive(const char *base_sha1,
-		const char *head_sha1, const char *head_name,
-		const char *next_sha1, const char *next_name)
-{
-	char buffer[256];
-	const char *argv[6];
-	int i = 0;
-
-	sprintf(buffer, "GITHEAD_%s", head_sha1);
-	setenv(buffer, head_name, 1);
-	sprintf(buffer, "GITHEAD_%s", next_sha1);
-	setenv(buffer, next_name, 1);
-
-	/*
-	 * This three way merge is an interesting one.  We are at
-	 * $head, and would want to apply the change between $commit
-	 * and $prev on top of us (when reverting), or the change between
-	 * $prev and $commit on top of us (when cherry-picking or replaying).
-	 */
-	argv[i++] = "merge-recursive";
-	if (base_sha1)
-		argv[i++] = base_sha1;
-	argv[i++] = "--";
-	argv[i++] = head_sha1;
-	argv[i++] = next_sha1;
-	argv[i++] = NULL;
-
-	return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
-}
-
 static char *help_msg(const unsigned char *sha1)
 {
 	static char helpbuf[1024];
@@ -251,25 +223,27 @@
 	return helpbuf;
 }
 
-static int index_is_dirty(void)
+static struct tree *empty_tree(void)
 {
-	struct rev_info rev;
-	init_revisions(&rev, NULL);
-	setup_revisions(0, NULL, &rev, "HEAD");
-	DIFF_OPT_SET(&rev.diffopt, QUIET);
-	DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-	run_diff_index(&rev, 1);
-	return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+	struct tree *tree = xcalloc(1, sizeof(struct tree));
+
+	tree->object.parsed = 1;
+	tree->object.type = OBJ_TREE;
+	pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
+	return tree;
 }
 
 static int revert_or_cherry_pick(int argc, const char **argv)
 {
 	unsigned char head[20];
 	struct commit *base, *next, *parent;
-	int i;
+	int i, index_fd, clean;
 	char *oneline, *reencoded_message = NULL;
 	const char *message, *encoding;
 	char *defmsg = git_pathdup("MERGE_MSG");
+	struct merge_options o;
+	struct tree *result, *next_tree, *base_tree, *head_tree;
+	static struct lock_file index_lock;
 
 	git_config(git_default_config, NULL);
 	me = action == REVERT ? "revert" : "cherry-pick";
@@ -280,6 +254,8 @@
 	if (action == REVERT && !no_replay)
 		die("revert is incompatible with replay");
 
+	if (read_cache() < 0)
+		die("git %s: failed to read the index", me);
 	if (no_commit) {
 		/*
 		 * We do not intend to commit immediately.  We just want to
@@ -292,12 +268,12 @@
 	} else {
 		if (get_sha1("HEAD", head))
 			die ("You do not have a valid HEAD");
-		if (read_cache() < 0)
-			die("could not read the index");
-		if (index_is_dirty())
+		if (index_differs_from("HEAD", 0))
 			die ("Dirty index: cannot %s", me);
-		discard_cache();
 	}
+	discard_cache();
+
+	index_fd = hold_locked_index(&index_lock, 1);
 
 	if (!commit->parents) {
 		if (action == REVERT)
@@ -331,6 +307,10 @@
 		die ("Cannot get commit message for %s",
 				sha1_to_hex(commit->object.sha1));
 
+	if (parent && parse_commit(parent) < 0)
+		die("%s: cannot parse parent commit %s",
+		    me, sha1_to_hex(parent->object.sha1));
+
 	/*
 	 * "commit" is an existing commit.  We would want to apply
 	 * the difference it introduces since its first parent "prev"
@@ -361,6 +341,11 @@
 		add_to_msg(oneline_body + 1);
 		add_to_msg("\"\n\nThis reverts commit ");
 		add_to_msg(sha1_to_hex(commit->object.sha1));
+
+		if (commit->parents->next) {
+			add_to_msg(", reversing\nchanges made to ");
+			add_to_msg(sha1_to_hex(parent->object.sha1));
+		}
 		add_to_msg(".\n");
 	} else {
 		base = parent;
@@ -374,13 +359,27 @@
 		}
 	}
 
-	if (merge_recursive(base == NULL ?
-				NULL : sha1_to_hex(base->object.sha1),
-				sha1_to_hex(head), "HEAD",
-				sha1_to_hex(next->object.sha1), oneline) ||
-			write_cache_as_tree(head, 0, NULL)) {
+	read_cache();
+	init_merge_options(&o);
+	o.branch1 = "HEAD";
+	o.branch2 = oneline;
+
+	head_tree = parse_tree_indirect(head);
+	next_tree = next ? next->tree : empty_tree();
+	base_tree = base ? base->tree : empty_tree();
+
+	clean = merge_trees(&o,
+			    head_tree,
+			    next_tree, base_tree, &result);
+
+	if (active_cache_changed &&
+	    (write_cache(index_fd, active_cache, active_nr) ||
+	     commit_locked_index(&index_lock)))
+		die("%s: Unable to write new index file", me);
+	rollback_lock_file(&index_lock);
+
+	if (!clean) {
 		add_to_msg("\nConflicts:\n\n");
-		read_cache();
 		for (i = 0; i < active_nr;) {
 			struct cache_entry *ce = active_cache[i++];
 			if (ce_stage(ce)) {
@@ -396,6 +395,7 @@
 			die ("Error wrapping up %s", defmsg);
 		fprintf(stderr, "Automatic %s failed.%s\n",
 			me, help_msg(commit->object.sha1));
+		rerere();
 		exit(1);
 	}
 	if (commit_lock_file(&msg_file) < 0)
diff --git a/builtin-rm.c b/builtin-rm.c
index e06640c..269d608 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -31,7 +31,8 @@
 
 static int check_local_mod(unsigned char *head, int index_only)
 {
-	/* items in list are already sorted in the cache order,
+	/*
+	 * Items in list are already sorted in the cache order,
 	 * so we could do this a lot more efficiently by using
 	 * tree_desc based traversal if we wanted to, but I am
 	 * lazy, and who cares if removal of files is a tad
@@ -58,8 +59,7 @@
 
 		if (lstat(ce->name, &st) < 0) {
 			if (errno != ENOENT)
-				fprintf(stderr, "warning: '%s': %s",
-					ce->name, strerror(errno));
+				warning("'%s': %s", ce->name, strerror(errno));
 			/* It already vanished from the working tree */
 			continue;
 		}
@@ -71,24 +71,55 @@
 			 */
 			continue;
 		}
+
+		/*
+		 * "rm" of a path that has changes need to be treated
+		 * carefully not to allow losing local changes
+		 * accidentally.  A local change could be (1) file in
+		 * work tree is different since the index; and/or (2)
+		 * the user staged a content that is different from
+		 * the current commit in the index.
+		 *
+		 * In such a case, you would need to --force the
+		 * removal.  However, "rm --cached" (remove only from
+		 * the index) is safe if the index matches the file in
+		 * the work tree or the HEAD commit, as it means that
+		 * the content being removed is available elsewhere.
+		 */
+
+		/*
+		 * Is the index different from the file in the work tree?
+		 */
 		if (ce_match_stat(ce, &st, 0))
 			local_changes = 1;
+
+		/*
+		 * Is the index different from the HEAD commit?  By
+		 * definition, before the very initial commit,
+		 * anything staged in the index is treated by the same
+		 * way as changed from the HEAD.
+		 */
 		if (no_head
 		     || get_tree_entry(head, name, sha1, &mode)
 		     || ce->ce_mode != create_ce_mode(mode)
 		     || hashcmp(ce->sha1, sha1))
 			staged_changes = 1;
 
-		if (local_changes && staged_changes)
-			errs = error("'%s' has staged content different "
-				     "from both the file and the HEAD\n"
-				     "(use -f to force removal)", name);
+		/*
+		 * If the index does not match the file in the work
+		 * tree and if it does not match the HEAD commit
+		 * either, (1) "git rm" without --cached definitely
+		 * will lose information; (2) "git rm --cached" will
+		 * lose information unless it is about removing an
+		 * "intent to add" entry.
+		 */
+		if (local_changes && staged_changes) {
+			if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
+				errs = error("'%s' has staged content different "
+					     "from both the file and the HEAD\n"
+					     "(use -f to force removal)", name);
+		}
 		else if (!index_only) {
-			/* It's not dangerous to "git rm --cached" a
-			 * file if the index matches the file or the
-			 * HEAD, since it means the deleted content is
-			 * still available somewhere.
-			 */
 			if (staged_changes)
 				errs = error("'%s' has changes staged in the index\n"
 					     "(use --cached to keep the file, "
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index e428eac..d5a1c48 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "commit.h"
-#include "tag.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "run-command.h"
@@ -11,14 +10,26 @@
 "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
 "  --all and explicit <ref> specification are mutually exclusive.";
 
-static struct send_pack_args args = {
-	/* .receivepack = */ "git-receive-pack",
-};
+static struct send_pack_args args;
+
+static int feed_object(const unsigned char *sha1, int fd, int negative)
+{
+	char buf[42];
+
+	if (negative && !has_sha1_file(sha1))
+		return 1;
+
+	memcpy(buf + negative, sha1_to_hex(sha1), 40);
+	if (negative)
+		buf[0] = '^';
+	buf[40 + negative] = '\n';
+	return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
+}
 
 /*
  * Make a pack stream and spit it out into file descriptor fd
  */
-static int pack_objects(int fd, struct ref *refs)
+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
 {
 	/*
 	 * The child becomes pack-objects --revs; we feed
@@ -34,8 +45,9 @@
 		NULL,
 	};
 	struct child_process po;
+	int i;
 
-	if (args.use_thin_pack)
+	if (args->use_thin_pack)
 		argv[4] = "--thin";
 	memset(&po, 0, sizeof(po));
 	po.argv = argv;
@@ -49,25 +61,17 @@
 	 * We feed the pack-objects we just spawned with revision
 	 * parameters by writing to the pipe.
 	 */
-	while (refs) {
-		char buf[42];
+	for (i = 0; i < extra->nr; i++)
+		if (!feed_object(extra->array[i], po.in, 1))
+			break;
 
+	while (refs) {
 		if (!is_null_sha1(refs->old_sha1) &&
-		    has_sha1_file(refs->old_sha1)) {
-			memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
-			buf[0] = '^';
-			buf[41] = '\n';
-			if (!write_or_whine(po.in, buf, 42,
-						"send-pack: send refs"))
-				break;
-		}
-		if (!is_null_sha1(refs->new_sha1)) {
-			memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
-			buf[40] = '\n';
-			if (!write_or_whine(po.in, buf, 41,
-						"send-pack: send refs"))
-				break;
-		}
+		    !feed_object(refs->old_sha1, po.in, 1))
+			break;
+		if (!is_null_sha1(refs->new_sha1) &&
+		    !feed_object(refs->new_sha1, po.in, 0))
+			break;
 		refs = refs->next;
 	}
 
@@ -77,82 +81,6 @@
 	return 0;
 }
 
-static void unmark_and_free(struct commit_list *list, unsigned int mark)
-{
-	while (list) {
-		struct commit_list *temp = list;
-		temp->item->object.flags &= ~mark;
-		list = temp->next;
-		free(temp);
-	}
-}
-
-static int ref_newer(const unsigned char *new_sha1,
-		     const unsigned char *old_sha1)
-{
-	struct object *o;
-	struct commit *old, *new;
-	struct commit_list *list, *used;
-	int found = 0;
-
-	/* Both new and old must be commit-ish and new is descendant of
-	 * old.  Otherwise we require --force.
-	 */
-	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != OBJ_COMMIT)
-		return 0;
-	old = (struct commit *) o;
-
-	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != OBJ_COMMIT)
-		return 0;
-	new = (struct commit *) o;
-
-	if (parse_commit(new) < 0)
-		return 0;
-
-	used = list = NULL;
-	commit_list_insert(new, &list);
-	while (list) {
-		new = pop_most_recent_commit(&list, 1);
-		commit_list_insert(new, &used);
-		if (new == old) {
-			found = 1;
-			break;
-		}
-	}
-	unmark_and_free(list, 1);
-	unmark_and_free(used, 1);
-	return found;
-}
-
-static struct ref *local_refs, **local_tail;
-static struct ref *remote_refs, **remote_tail;
-
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct ref *ref;
-	int len;
-
-	/* we already know it starts with refs/ to get here */
-	if (check_ref_format(refname + 5))
-		return 0;
-
-	len = strlen(refname) + 1;
-	ref = xcalloc(1, sizeof(*ref) + len);
-	hashcpy(ref->new_sha1, sha1);
-	memcpy(ref->name, refname, len);
-	*local_tail = ref;
-	local_tail = &ref->next;
-	return 0;
-}
-
-static void get_local_heads(void)
-{
-	local_tail = &local_refs;
-	for_each_ref(one_local_ref, NULL);
-}
-
 static int receive_status(int in, struct ref *refs)
 {
 	struct ref *hint;
@@ -240,16 +168,6 @@
 	}
 }
 
-static const char *prettify_ref(const struct ref *ref)
-{
-	const char *name = ref->name;
-	return name + (
-		!prefixcmp(name, "refs/heads/") ? 11 :
-		!prefixcmp(name, "refs/tags/") ? 10 :
-		!prefixcmp(name, "refs/remotes/") ? 13 :
-		0);
-}
-
 #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
 static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
@@ -378,44 +296,29 @@
 	return 0;
 }
 
-static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
+int send_pack(struct send_pack_args *args,
+	      int fd[], struct child_process *conn,
+	      struct ref *remote_refs,
+	      struct extra_have_objects *extra_have)
 {
+	int in = fd[0];
+	int out = fd[1];
 	struct ref *ref;
 	int new_refs;
 	int ask_for_status_report = 0;
 	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
-	int flags = MATCH_REFS_NONE;
 	int ret;
 
-	if (args.send_all)
-		flags |= MATCH_REFS_ALL;
-	if (args.send_mirror)
-		flags |= MATCH_REFS_MIRROR;
-
-	/* No funny business with the matcher */
-	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
-	get_local_heads();
-
 	/* Does the other end support the reporting? */
 	if (server_supports("report-status"))
 		ask_for_status_report = 1;
 	if (server_supports("delete-refs"))
 		allow_deleting_refs = 1;
 
-	/* match them up */
-	if (!remote_tail)
-		remote_tail = &remote_refs;
-	if (match_refs(local_refs, remote_refs, &remote_tail,
-		       nr_refspec, refspec, flags)) {
-		close(out);
-		return -1;
-	}
-
 	if (!remote_refs) {
 		fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
 			"Perhaps you should specify a branch such as 'master'.\n");
-		close(out);
 		return 0;
 	}
 
@@ -427,7 +330,7 @@
 
 		if (ref->peer_ref)
 			hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		else if (!args.send_mirror)
+		else if (!args->send_mirror)
 			continue;
 
 		ref->deletion = is_null_sha1(ref->new_sha1);
@@ -466,7 +369,7 @@
 		    (!has_sha1_file(ref->old_sha1)
 		      || !ref_newer(ref->new_sha1, ref->old_sha1));
 
-		if (ref->nonfastforward && !ref->force && !args.force_update) {
+		if (ref->nonfastforward && !ref->force && !args->force_update) {
 			ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
 			continue;
 		}
@@ -474,7 +377,7 @@
 		if (!ref->deletion)
 			new_refs++;
 
-		if (!args.dry_run) {
+		if (!args->dry_run) {
 			char *old_hex = sha1_to_hex(ref->old_sha1);
 			char *new_hex = sha1_to_hex(ref->new_sha1);
 
@@ -495,27 +398,19 @@
 	}
 
 	packet_flush(out);
-	if (new_refs && !args.dry_run) {
-		if (pack_objects(out, remote_refs) < 0)
+	if (new_refs && !args->dry_run) {
+		if (pack_objects(out, remote_refs, extra_have, args) < 0) {
+			for (ref = remote_refs; ref; ref = ref->next)
+				ref->status = REF_STATUS_NONE;
 			return -1;
+		}
 	}
-	else
-		close(out);
 
 	if (expect_status_report)
 		ret = receive_status(in, remote_refs);
 	else
 		ret = 0;
 
-	print_push_status(dest, remote_refs);
-
-	if (!args.dry_run && remote) {
-		for (ref = remote_refs; ref; ref = ref->next)
-			update_tracking_ref(remote, ref);
-	}
-
-	if (!refs_pushed(remote_refs))
-		fprintf(stderr, "Everything up-to-date\n");
 	if (ret < 0)
 		return ret;
 	for (ref = remote_refs; ref; ref = ref->next) {
@@ -564,11 +459,19 @@
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
 {
-	int i, nr_heads = 0;
-	const char **heads = NULL;
+	int i, nr_refspecs = 0;
+	const char **refspecs = NULL;
 	const char *remote_name = NULL;
 	struct remote *remote = NULL;
 	const char *dest = NULL;
+	int fd[2];
+	struct child_process *conn;
+	struct extra_have_objects extra_have;
+	struct ref *remote_refs, **remote_tail, *local_refs;
+	int ret;
+	int send_all = 0;
+	const char *receivepack = "git-receive-pack";
+	int flags;
 
 	argv++;
 	for (i = 1; i < argc; i++, argv++) {
@@ -576,11 +479,11 @@
 
 		if (*arg == '-') {
 			if (!prefixcmp(arg, "--receive-pack=")) {
-				args.receivepack = arg + 15;
+				receivepack = arg + 15;
 				continue;
 			}
 			if (!prefixcmp(arg, "--exec=")) {
-				args.receivepack = arg + 7;
+				receivepack = arg + 7;
 				continue;
 			}
 			if (!prefixcmp(arg, "--remote=")) {
@@ -588,7 +491,7 @@
 				continue;
 			}
 			if (!strcmp(arg, "--all")) {
-				args.send_all = 1;
+				send_all = 1;
 				continue;
 			}
 			if (!strcmp(arg, "--dry-run")) {
@@ -617,8 +520,8 @@
 			dest = arg;
 			continue;
 		}
-		heads = (const char **) argv;
-		nr_heads = argc - i;
+		refspecs = (const char **) argv;
+		nr_refspecs = argc - i;
 		break;
 	}
 	if (!dest)
@@ -627,8 +530,8 @@
 	 * --all and --mirror are incompatible; neither makes sense
 	 * with any refspecs.
 	 */
-	if ((heads && (args.send_all || args.send_mirror)) ||
-					(args.send_all && args.send_mirror))
+	if ((refspecs && (send_all || args.send_mirror)) ||
+	    (send_all && args.send_mirror))
 		usage(send_pack_usage);
 
 	if (remote_name) {
@@ -639,24 +542,50 @@
 		}
 	}
 
-	return send_pack(&args, dest, remote, nr_heads, heads);
-}
+	conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0);
 
-int send_pack(struct send_pack_args *my_args,
-	      const char *dest, struct remote *remote,
-	      int nr_heads, const char **heads)
-{
-	int fd[2], ret;
-	struct child_process *conn;
+	memset(&extra_have, 0, sizeof(extra_have));
 
-	memcpy(&args, my_args, sizeof(args));
+	get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
+			 &extra_have);
 
-	verify_remote_names(nr_heads, heads);
+	verify_remote_names(nr_refspecs, refspecs);
 
-	conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
-	ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
+	local_refs = get_local_heads();
+
+	flags = MATCH_REFS_NONE;
+
+	if (send_all)
+		flags |= MATCH_REFS_ALL;
+	if (args.send_mirror)
+		flags |= MATCH_REFS_MIRROR;
+
+	/* match them up */
+	remote_tail = &remote_refs;
+	while (*remote_tail)
+		remote_tail = &((*remote_tail)->next);
+	if (match_refs(local_refs, remote_refs, &remote_tail,
+		       nr_refspecs, refspecs, flags)) {
+		return -1;
+	}
+
+	ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
+
+	close(fd[1]);
 	close(fd[0]);
-	/* do_send_pack always closes fd[1] */
+
 	ret |= finish_connect(conn);
-	return !!ret;
+
+	print_push_status(dest, remote_refs);
+
+	if (!args.dry_run && remote) {
+		struct ref *ref;
+		for (ref = remote_refs; ref; ref = ref->next)
+			update_tracking_ref(remote, ref);
+	}
+
+	if (!ret && !refs_pushed(remote_refs))
+		fprintf(stderr, "Everything up-to-date\n");
+
+	return ret;
 }
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index d03f14f..b28091b 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -29,6 +29,9 @@
 		return -1;
 }
 
+const char *format_subject(struct strbuf *sb, const char *msg,
+			   const char *line_separator);
+
 static void insert_one_record(struct shortlog *log,
 			      const char *author,
 			      const char *oneline)
@@ -36,11 +39,12 @@
 	const char *dot3 = log->common_repo_prefix;
 	char *buffer, *p;
 	struct string_list_item *item;
-	struct string_list *onelines;
 	char namebuf[1024];
+	char emailbuf[1024];
 	size_t len;
 	const char *eol;
 	const char *boemail, *eoemail;
+	struct strbuf subject = STRBUF_INIT;
 
 	boemail = strchr(author, '<');
 	if (!boemail)
@@ -48,7 +52,19 @@
 	eoemail = strchr(boemail, '>');
 	if (!eoemail)
 		return;
-	if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
+
+	/* copy author name to namebuf, to support matching on both name and email */
+	memcpy(namebuf, author, boemail - author);
+	len = boemail - author;
+	while(len > 0 && isspace(namebuf[len-1]))
+		len--;
+	namebuf[len] = 0;
+
+	/* copy email name to emailbuf, to allow email replacement as well */
+	memcpy(emailbuf, boemail+1, eoemail - boemail);
+	emailbuf[eoemail - boemail - 1] = 0;
+
+	if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
 		while (author < boemail && isspace(*author))
 			author++;
 		for (len = 0;
@@ -64,16 +80,13 @@
 
 	if (log->email) {
 		size_t room = sizeof(namebuf) - len - 1;
-		int maillen = eoemail - boemail + 1;
-		snprintf(namebuf + len, room, " %.*s", maillen, boemail);
+		int maillen = strlen(emailbuf);
+		snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
 	}
 
-	buffer = xstrdup(namebuf);
-	item = string_list_insert(buffer, &log->list);
+	item = string_list_insert(namebuf, &log->list);
 	if (item->util == NULL)
 		item->util = xcalloc(1, sizeof(struct string_list));
-	else
-		free(buffer);
 
 	/* Skip any leading whitespace, including any blank lines. */
 	while (*oneline && isspace(*oneline))
@@ -88,10 +101,8 @@
 	}
 	while (*oneline && isspace(*oneline) && *oneline != '\n')
 		oneline++;
-	len = eol - oneline;
-	while (len && isspace(oneline[len-1]))
-		len--;
-	buffer = xmemdupz(oneline, len);
+	format_subject(&subject, oneline, " ");
+	buffer = strbuf_detach(&subject, NULL);
 
 	if (dot3) {
 		int dot3len = strlen(dot3);
@@ -104,16 +115,7 @@
 		}
 	}
 
-	onelines = item->util;
-	if (onelines->nr >= onelines->alloc) {
-		onelines->alloc = alloc_nr(onelines->nr);
-		onelines->items = xrealloc(onelines->items,
-				onelines->alloc
-				* sizeof(struct string_list_item));
-	}
-
-	onelines->items[onelines->nr].util = NULL;
-	onelines->items[onelines->nr++].string = buffer;
+	string_list_append(buffer, item->util);
 }
 
 static void read_from_stdin(struct shortlog *log)
@@ -229,7 +231,7 @@
 {
 	memset(log, 0, sizeof(*log));
 
-	read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
+	read_mailmap(&log->mailmap, &log->common_repo_prefix);
 
 	log->list.strdup_strings = 1;
 	log->wrap = DEFAULT_WRAPLEN;
@@ -258,6 +260,7 @@
 	struct parse_opt_ctx_t ctx;
 
 	prefix = setup_git_directory_gently(&nongit);
+	git_config(git_default_config, NULL);
 	shortlog_init(&log);
 	init_revisions(&rev, prefix);
 	parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
@@ -323,13 +326,12 @@
 		}
 
 		onelines->strdup_strings = 1;
-		string_list_clear(onelines, 1);
+		string_list_clear(onelines, 0);
 		free(onelines);
 		log->list.items[i].util = NULL;
 	}
 
 	log->list.strdup_strings = 1;
 	string_list_clear(&log->list, 1);
-	log->mailmap.strdup_strings = 1;
-	string_list_clear(&log->mailmap, 1);
+	clear_mailmap(&log->mailmap);
 }
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 233eed4..828e6f8 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -259,11 +259,10 @@
 
 static void show_one_commit(struct commit *commit, int no_name)
 {
-	struct strbuf pretty;
+	struct strbuf pretty = STRBUF_INIT;
 	const char *pretty_str = "(unavailable)";
 	struct commit_name *name = commit->util;
 
-	strbuf_init(&pretty, 0);
 	if (commit->object.parsed) {
 		pretty_print_commit(CMIT_FMT_ONELINE, commit,
 				    &pretty, 0, NULL, NULL, 0, 0);
@@ -366,8 +365,7 @@
 				return 0;
 	}
 	if (MAX_REVS <= ref_name_cnt) {
-		fprintf(stderr, "warning: ignoring %s; "
-			"cannot handle more than %d refs\n",
+		warning("ignoring %s; cannot handle more than %d refs",
 			refname, MAX_REVS);
 		return 0;
 	}
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 572b114..dc76c50 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -140,7 +140,7 @@
 				continue;
 		}
 		if (check_ref_format(ref)) {
-			fprintf(stderr, "warning: ref '%s' ignored\n", ref);
+			warning("ref '%s' ignored", ref);
 			continue;
 		}
 		if (!string_list_has_string(&existing_refs, ref)) {
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index c0b2130..d6e3896 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -70,14 +70,13 @@
 
 int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	int strip_comments = 0;
 
 	if (argc > 1 && (!strcmp(argv[1], "-s") ||
 				!strcmp(argv[1], "--strip-comments")))
 		strip_comments = 1;
 
-	strbuf_init(&buf, 0);
 	if (strbuf_read(&buf, 0, 1024) < 0)
 		die("could not read the input");
 
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
index bfc78bb..6ae6bcc 100644
--- a/builtin-symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -44,6 +44,9 @@
 		check_symref(argv[0], quiet);
 		break;
 	case 2:
+		if (!strcmp(argv[0], "HEAD") &&
+		    prefixcmp(argv[1], "refs/"))
+			die("Refusing to point HEAD outside of refs/");
 		create_symref(argv[0], argv[1], msg);
 		break;
 	default:
diff --git a/builtin-tag.c b/builtin-tag.c
index 2cdefb1..01e7374 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -26,6 +26,7 @@
 struct tag_filter {
 	const char *pattern;
 	int lines;
+	struct commit_list *with_commit;
 };
 
 #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
@@ -42,6 +43,16 @@
 		char *buf, *sp, *eol;
 		size_t len;
 
+		if (filter->with_commit) {
+			struct commit *commit;
+
+			commit = lookup_commit_reference_gently(sha1, 1);
+			if (!commit)
+				return 0;
+			if (!is_descendant_of(commit, filter->with_commit))
+				return 0;
+		}
+
 		if (!filter->lines) {
 			printf("%s\n", refname);
 			return 0;
@@ -79,7 +90,8 @@
 	return 0;
 }
 
-static int list_tags(const char *pattern, int lines)
+static int list_tags(const char *pattern, int lines,
+			struct commit_list *with_commit)
 {
 	struct tag_filter filter;
 
@@ -88,6 +100,7 @@
 
 	filter.pattern = pattern;
 	filter.lines = lines;
+	filter.with_commit = with_commit;
 
 	for_each_tag_ref(show_reference, (void *) &filter);
 
@@ -350,7 +363,7 @@
 
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	unsigned char object[20], prev[20];
 	char ref[PATH_MAX];
 	const char *object_ref, *tag;
@@ -360,6 +373,7 @@
 		list = 0, delete = 0, verify = 0;
 	const char *msgfile = NULL, *keyid = NULL;
 	struct msg_arg msg = { 0, STRBUF_INIT };
+	struct commit_list *with_commit = NULL;
 	struct option options[] = {
 		OPT_BOOLEAN('l', NULL, &list, "list tag names"),
 		{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
@@ -378,6 +392,14 @@
 		OPT_STRING('u', NULL, &keyid, "key-id",
 					"use another key to sign the tag"),
 		OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+
+		OPT_GROUP("Tag listing options"),
+		{
+			OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
+			"print only tags that contain the commit",
+			PARSE_OPT_LASTARG_DEFAULT,
+			parse_opt_with_commit, (intptr_t)"HEAD",
+		},
 		OPT_END()
 	};
 
@@ -402,15 +424,17 @@
 	if (list + delete + verify > 1)
 		usage_with_options(git_tag_usage, options);
 	if (list)
-		return list_tags(argv[0], lines == -1 ? 0 : lines);
+		return list_tags(argv[0], lines == -1 ? 0 : lines,
+				 with_commit);
 	if (lines != -1)
 		die("-n option is only allowed with -l.");
+	if (with_commit)
+		die("--contains option is only allowed with -l.");
 	if (delete)
 		return for_each_tag_name(argv, delete_tag);
 	if (verify)
 		return for_each_tag_name(argv, verify_tag);
 
-	strbuf_init(&buf, 0);
 	if (msg.given || msgfile) {
 		if (msg.given && msgfile)
 			die("only one -F or -m option is allowed.");
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
index 0713bca..f88e721 100644
--- a/builtin-tar-tree.c
+++ b/builtin-tar-tree.c
@@ -24,7 +24,7 @@
 	 * 	git archive --format-tar --prefix=basedir tree-ish
 	 */
 	int i;
-	const char **nargv = xcalloc(sizeof(*nargv), argc + 2);
+	const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
 	char *basedir_arg;
 	int nargc = 0;
 
@@ -36,6 +36,13 @@
 		argv++;
 		argc--;
 	}
+
+	/*
+	 * Because it's just a compatibility wrapper, tar-tree supports only
+	 * the old behaviour of reading attributes from the work tree.
+	 */
+	nargv[nargc++] = "--worktree-attributes";
+
 	switch (argc) {
 	default:
 		usage(tar_tree_usage);
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 40b20f2..9a77323 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -19,7 +19,7 @@
 static unsigned char buffer[4096];
 static unsigned int offset, len;
 static off_t consumed_bytes;
-static SHA_CTX ctx;
+static git_SHA_CTX ctx;
 
 /*
  * When running under --strict mode, objects whose reachability are
@@ -59,7 +59,7 @@
 	if (min > sizeof(buffer))
 		die("cannot fill %d bytes", min);
 	if (offset) {
-		SHA1_Update(&ctx, buffer, offset);
+		git_SHA1_Update(&ctx, buffer, offset);
 		memmove(buffer, buffer + offset, len);
 		offset = 0;
 	}
@@ -99,10 +99,10 @@
 	stream.avail_out = size;
 	stream.next_in = fill(1);
 	stream.avail_in = len;
-	inflateInit(&stream);
+	git_inflate_init(&stream);
 
 	for (;;) {
-		int ret = inflate(&stream, 0);
+		int ret = git_inflate(&stream, 0);
 		use(len - stream.avail_in);
 		if (stream.total_out == size && ret == Z_STREAM_END)
 			break;
@@ -118,7 +118,7 @@
 		stream.next_in = fill(1);
 		stream.avail_in = len;
 	}
-	inflateEnd(&stream);
+	git_inflate_end(&stream);
 	return buf;
 }
 
@@ -370,6 +370,8 @@
 			base_offset = (base_offset << 7) + (c & 127);
 		}
 		base_offset = obj_list[nr].offset - base_offset;
+		if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
+			die("offset value out of bound for delta base object");
 
 		delta_data = get_data(delta_size);
 		if (dry_run || !delta_data) {
@@ -477,8 +479,7 @@
 
 	if (!quiet)
 		progress = start_progress("Unpacking objects", nr_objects);
-	obj_list = xmalloc(nr_objects * sizeof(*obj_list));
-	memset(obj_list, 0, nr_objects * sizeof(*obj_list));
+	obj_list = xcalloc(nr_objects, sizeof(*obj_list));
 	for (i = 0; i < nr_objects; i++) {
 		unpack_one(i);
 		display_progress(progress, i + 1);
@@ -539,10 +540,10 @@
 		/* We don't take any non-flag arguments now.. Maybe some day */
 		usage(unpack_usage);
 	}
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 	unpack_all();
-	SHA1_Update(&ctx, buffer, offset);
-	SHA1_Final(sha1, &ctx);
+	git_SHA1_Update(&ctx, buffer, offset);
+	git_SHA1_Final(sha1, &ctx);
 	if (strict)
 		write_rest();
 	if (hashcmp(fill(20), sha1))
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 9d19c51..1fde893 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -194,6 +194,10 @@
 	int len;
 	struct stat st;
 
+	len = strlen(path);
+	if (has_symlink_leading_path(path, len))
+		return error("'%s' is beyond a symbolic link", path);
+
 	/*
 	 * First things first: get the stat information, to decide
 	 * what to do about the pathname!
@@ -201,7 +205,6 @@
 	if (lstat(path, &st) < 0)
 		return process_lstat_error(path, errno);
 
-	len = strlen(path);
 	if (S_ISDIR(st.st_mode))
 		return process_directory(path, len, &st);
 
@@ -215,7 +218,7 @@
 	struct cache_entry *ce;
 
 	if (!verify_path(path))
-		return -1;
+		return error("Invalid path '%s'", path);
 
 	len = strlen(path);
 	size = cache_entry_size(len);
@@ -294,11 +297,9 @@
 
 static void read_index_info(int line_termination)
 {
-	struct strbuf buf;
-	struct strbuf uq;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf uq = STRBUF_INIT;
 
-	strbuf_init(&buf, 0);
-	strbuf_init(&uq, 0);
 	while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
 		char *ptr, *tab;
 		char *path_name;
@@ -485,7 +486,7 @@
 static void read_head_pointers(void)
 {
 	if (read_ref("HEAD", head_sha1))
-		die("No HEAD -- no initial commit yet?\n");
+		die("No HEAD -- no initial commit yet?");
 	if (read_ref("MERGE_HEAD", merge_head_sha1)) {
 		fprintf(stderr, "Not in the middle of a merge.\n");
 		exit(0);
@@ -714,10 +715,8 @@
 			free((char*)p);
 	}
 	if (read_from_stdin) {
-		struct strbuf buf, nbuf;
+		struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
 
-		strbuf_init(&buf, 0);
-		strbuf_init(&nbuf, 0);
 		setup_work_tree();
 		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
 			const char *p;
@@ -743,8 +742,7 @@
 		if (newfd < 0) {
 			if (refresh_flags & REFRESH_QUIET)
 				exit(128);
-			die("unable to create '%s.lock': %s",
-			    get_index_file(), strerror(lock_error));
+			unable_to_lock_index_die(get_index_file(), lock_error);
 		}
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(lock_file))
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
index a9b02fa..0206b41 100644
--- a/builtin-upload-archive.c
+++ b/builtin-upload-archive.c
@@ -35,7 +35,7 @@
 	strcpy(buf, argv[1]); /* enter-repo smudges its argument */
 
 	if (!enter_repo(buf, 0))
-		die("not a git archive");
+		die("'%s' does not appear to be a git repository", buf);
 
 	/* put received options in sent_argv[] */
 	sent_argc = 1;
diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c
index 25a29f1..0ee0a9a 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -107,7 +107,7 @@
 	return err;
 }
 
-static const char verify_pack_usage[] = "git-verify-pack [-v] <pack>...";
+static const char verify_pack_usage[] = "git verify-pack [-v] <pack>...";
 
 int cmd_verify_pack(int argc, const char **argv, const char *prefix)
 {
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 52a3c01..9d64050 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -42,7 +42,7 @@
 		die("%s: error reading the index", me);
 		break;
 	case WRITE_TREE_UNMERGED_INDEX:
-		die("%s: error building trees; the index is unmerged?", me);
+		die("%s: error building trees", me);
 		break;
 	case WRITE_TREE_PREFIX_ERROR:
 		die("%s: prefix %s not found", me, prefix);
diff --git a/builtin.h b/builtin.h
index f3502d3..425ff8e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -11,19 +11,21 @@
 extern const char git_more_info_string[];
 
 extern void list_common_cmds_help(void);
-extern void help_unknown_cmd(const char *cmd);
+extern const char *help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
 extern int read_line_with_nul(char *buf, int size, FILE *file);
 extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
 	struct strbuf *out);
 extern int commit_tree(const char *msg, unsigned char *tree,
-		struct commit_list *parents, unsigned char *ret);
+		struct commit_list *parents, unsigned char *ret,
+		const char *author);
 extern int check_pager_config(const char *cmd);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
 extern int cmd_archive(int argc, const char **argv, const char *prefix);
+extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_blame(int argc, const char **argv, const char *prefix);
 extern int cmd_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_bundle(int argc, const char **argv, const char *prefix);
@@ -78,6 +80,7 @@
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
diff --git a/bundle.c b/bundle.c
index daecd8e..d0dd818 100644
--- a/bundle.c
+++ b/bundle.c
@@ -167,6 +167,32 @@
 	return list_refs(&header->references, argc, argv);
 }
 
+static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
+{
+	unsigned long size;
+	enum object_type type;
+	char *buf, *line, *lineend;
+	unsigned long date;
+
+	if (revs->max_age == -1 && revs->min_age == -1)
+		return 1;
+
+	buf = read_sha1_file(tag->sha1, &type, &size);
+	if (!buf)
+		return 1;
+	line = memmem(buf, size, "\ntagger ", 8);
+	if (!line++)
+		return 1;
+	lineend = memchr(line, buf + size - line, '\n');
+	line = memchr(line, lineend ? lineend - line : buf + size - line, '>');
+	if (!line++)
+		return 1;
+	date = strtoul(line, NULL, 10);
+	free(buf);
+	return (revs->max_age == -1 || revs->max_age < date) &&
+		(revs->min_age == -1 || revs->min_age > date);
+}
+
 int create_bundle(struct bundle_header *header, const char *path,
 		int argc, const char **argv)
 {
@@ -240,6 +266,8 @@
 		return error("unrecognized argument: %s'", argv[i]);
 	}
 
+	object_array_remove_duplicates(&revs.pending);
+
 	for (i = 0; i < revs.pending.nr; i++) {
 		struct object_array_entry *e = revs.pending.objects + i;
 		unsigned char sha1[20];
@@ -255,6 +283,12 @@
 			flag = 0;
 		display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
+		if (e->item->type == OBJ_TAG &&
+				!is_tag_in_date_range(e->item, &revs)) {
+			e->item->flags |= UNINTERESTING;
+			continue;
+		}
+
 		/*
 		 * Make sure the refs we wrote out is correct; --max-count and
 		 * other limiting options could have prevented all the tips
diff --git a/cache-tree.c b/cache-tree.c
index 5f8ee87..3d8f218 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -155,13 +155,17 @@
 	funny = 0;
 	for (i = 0; i < entries; i++) {
 		struct cache_entry *ce = cache[i];
-		if (ce_stage(ce)) {
+		if (ce_stage(ce) || (ce->ce_flags & CE_INTENT_TO_ADD)) {
 			if (10 < ++funny) {
 				fprintf(stderr, "...\n");
 				break;
 			}
-			fprintf(stderr, "%s: unmerged (%s)\n",
-				ce->name, sha1_to_hex(ce->sha1));
+			if (ce_stage(ce))
+				fprintf(stderr, "%s: unmerged (%s)\n",
+					ce->name, sha1_to_hex(ce->sha1));
+			else
+				fprintf(stderr, "%s: not added yet\n",
+					ce->name);
 		}
 	}
 	if (funny)
diff --git a/cache.h b/cache.h
index 099a32e..ab1294d 100644
--- a/cache.h
+++ b/cache.h
@@ -6,12 +6,22 @@
 #include "hash.h"
 
 #include SHA1_HEADER
-#include <zlib.h>
+#ifndef git_SHA_CTX
+#define git_SHA_CTX	SHA_CTX
+#define git_SHA1_Init	SHA1_Init
+#define git_SHA1_Update	SHA1_Update
+#define git_SHA1_Final	SHA1_Final
+#endif
 
+#include <zlib.h>
 #if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
 #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
 #endif
 
+void git_inflate_init(z_streamp strm);
+void git_inflate_end(z_streamp strm);
+int git_inflate(z_streamp strm, int flush);
+
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
 #define DTYPE(de)	((de)->d_type)
 #else
@@ -109,9 +119,29 @@
 	char name[FLEX_ARRAY]; /* more */
 };
 
+/*
+ * This struct is used when CE_EXTENDED bit is 1
+ * The struct must match ondisk_cache_entry exactly from
+ * ctime till flags
+ */
+struct ondisk_cache_entry_extended {
+	struct cache_time ctime;
+	struct cache_time mtime;
+	unsigned int dev;
+	unsigned int ino;
+	unsigned int mode;
+	unsigned int uid;
+	unsigned int gid;
+	unsigned int size;
+	unsigned char sha1[20];
+	unsigned short flags;
+	unsigned short flags2;
+	char name[FLEX_ARRAY]; /* more */
+};
+
 struct cache_entry {
-	unsigned int ce_ctime;
-	unsigned int ce_mtime;
+	struct cache_time ce_ctime;
+	struct cache_time ce_mtime;
 	unsigned int ce_dev;
 	unsigned int ce_ino;
 	unsigned int ce_mode;
@@ -126,10 +156,19 @@
 
 #define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
+#define CE_EXTENDED  (0x4000)
 #define CE_VALID     (0x8000)
 #define CE_STAGESHIFT 12
 
-/* In-memory only */
+/*
+ * Range 0xFFFF0000 in ce_flags is divided into
+ * two parts: in-memory flags and on-disk ones.
+ * Flags in CE_EXTENDED_FLAGS will get saved on-disk
+ * if you want to save a new flag, add it in
+ * CE_EXTENDED_FLAGS
+ *
+ * In-memory only flags
+ */
 #define CE_UPDATE    (0x10000)
 #define CE_REMOVE    (0x20000)
 #define CE_UPTODATE  (0x40000)
@@ -139,6 +178,25 @@
 #define CE_UNHASHED  (0x200000)
 
 /*
+ * Extended on-disk flags
+ */
+#define CE_INTENT_TO_ADD 0x20000000
+/* CE_EXTENDED2 is for future extension */
+#define CE_EXTENDED2 0x80000000
+
+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
+
+/*
+ * Safeguard to avoid saving wrong flags:
+ *  - CE_EXTENDED2 won't get saved until its semantic is known
+ *  - Bits in 0x0000FFFF have been saved in ce_flags already
+ *  - Bits in 0x003F0000 are currently in-memory flags
+ */
+#if CE_EXTENDED_FLAGS & 0x803FFFFF
+#error "CE_EXTENDED_FLAGS out of range"
+#endif
+
+/*
  * Copy the sha1 and stat state of a cache entry from one to
  * another. But we never change the name, or the hash state!
  */
@@ -170,7 +228,9 @@
 }
 
 #define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
+#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
+			    ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
+			    ondisk_cache_entry_size(ce_namelen(ce)))
 #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
@@ -213,14 +273,16 @@
 	(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 	S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 
-#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
-#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
+#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
+#define cache_entry_size(len) flexible_size(cache_entry,len)
+#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
+#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
 
 struct index_state {
 	struct cache_entry **cache;
 	unsigned int cache_nr, cache_alloc, cache_changed;
 	struct cache_tree *cache_tree;
-	time_t timestamp;
+	struct cache_time timestamp;
 	void *alloc;
 	unsigned name_hash_initialized : 1,
 		 initialized : 1;
@@ -255,6 +317,7 @@
 
 #define read_cache() read_index(&the_index)
 #define read_cache_from(path) read_index_from(&the_index, (path))
+#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 #define is_cache_unborn() is_index_unborn(&the_index)
 #define read_cache_unmerged() read_index_unmerged(&the_index)
 #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
@@ -314,6 +377,7 @@
 extern int is_inside_git_dir(void);
 extern char *git_work_tree_cfg;
 extern int is_inside_work_tree(void);
+extern int have_git_dir(void);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_index_file(void);
@@ -360,10 +424,11 @@
 
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
+extern int read_index_preload(struct index_state *, const char **pathspec);
 extern int read_index_from(struct index_state *, const char *path);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
-extern int write_index(const struct index_state *, int newfd);
+extern int write_index(struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
@@ -373,14 +438,18 @@
 #define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
 #define ADD_CACHE_SKIP_DFCHECK 4	/* Ok to skip DF conflict checks */
 #define ADD_CACHE_JUST_APPEND 8		/* Append only; tree.c::read_tree() */
+#define ADD_CACHE_NEW_ONLY 16		/* Do not replace existing ones */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 extern int remove_index_entry_at(struct index_state *, int pos);
+extern void remove_marked_cache_entries(struct index_state *istate);
 extern int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_VERBOSE 1
 #define ADD_CACHE_PRETEND 2
 #define ADD_CACHE_IGNORE_ERRORS	4
+#define ADD_CACHE_IGNORE_REMOVAL 8
+#define ADD_CACHE_INTENT 16
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
@@ -396,7 +465,6 @@
 
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
@@ -417,6 +485,7 @@
 };
 #define LOCK_DIE_ON_ERROR 1
 #define LOCK_NODEREF 2
+extern NORETURN void unable_to_lock_index_die(const char *path, int err);
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
@@ -448,6 +517,7 @@
 extern size_t delta_base_cache_limit;
 extern int auto_crlf;
 extern int fsync_object_files;
+extern int core_preload_index;
 
 enum safe_crlf {
 	SAFE_CRLF_FALSE = 0,
@@ -458,6 +528,7 @@
 extern enum safe_crlf safe_crlf;
 
 enum branch_track {
+	BRANCH_TRACK_UNSPECIFIED = -1,
 	BRANCH_TRACK_NEVER = 0,
 	BRANCH_TRACK_REMOTE,
 	BRANCH_TRACK_ALWAYS,
@@ -471,8 +542,17 @@
 	AUTOREBASE_ALWAYS,
 };
 
+enum push_default_type {
+	PUSH_DEFAULT_UNSPECIFIED = -1,
+	PUSH_DEFAULT_NOTHING = 0,
+	PUSH_DEFAULT_MATCHING,
+	PUSH_DEFAULT_TRACKING,
+	PUSH_DEFAULT_CURRENT,
+};
+
 extern enum branch_track git_branch_track;
 extern enum rebase_setup_type autorebase;
+extern enum push_default_type push_default;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -517,6 +597,13 @@
 {
 	memset(hash, 0, 20);
 }
+extern int is_empty_blob_sha1(const unsigned char *sha1);
+
+#define EMPTY_TREE_SHA1_HEX \
+	"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
+#define EMPTY_TREE_SHA1_BIN \
+	 "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
+	 "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
@@ -536,7 +623,8 @@
 	PERM_EVERYBODY      = 0664,
 };
 int git_config_perm(const char *var, const char *value);
-int adjust_shared_perm(const char *path);
+int set_shared_perm(const char *path, int mode);
+#define adjust_shared_perm(path) set_shared_perm((path), 0)
 int safe_create_leading_directories(char *path);
 int safe_create_leading_directories_const(const char *path);
 char *enter_repo(char *path, int strict);
@@ -544,11 +632,13 @@
 {
 	return path[0] == '/' || has_dos_drive_prefix(path);
 }
+int is_directory(const char *);
 const char *make_absolute_path(const char *path);
 const char *make_nonrelative_path(const char *path);
 const char *make_relative_path(const char *abs, const char *base);
-int normalize_absolute_path(char *buf, const char *path);
+int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, const char *prefix_list);
+char *strip_path_suffix(const char *path, const char *suffix);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -558,14 +648,14 @@
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 
-/* just like read_sha1_file(), but non fatal in presence of bad objects */
-extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
+/* global flag to enable extra checks when accessing packed objects */
+extern int do_check_packed_object_crc;
 
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
 extern int move_temp_to_file(const char *tmpfile, const char *filename);
 
-extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
+extern int has_sha1_pack(const unsigned char *sha1);
 extern int has_sha1_file(const unsigned char *sha1);
 extern int has_loose_object_nonlocal(const unsigned char *sha1);
 
@@ -590,6 +680,7 @@
 extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
+extern int interpret_branch_name(const char *str, struct strbuf *);
 
 extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 extern const char *ref_rev_parse_rules[];
@@ -616,7 +707,8 @@
 	DATE_SHORT,
 	DATE_LOCAL,
 	DATE_ISO8601,
-	DATE_RFC2822
+	DATE_RFC2822,
+	DATE_RAW
 };
 
 const char *show_date(unsigned long time, int timezone, enum date_mode mode);
@@ -643,7 +735,13 @@
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
-extern int has_symlink_leading_path(int len, const char *name);
+extern int has_symlink_leading_path(const char *name, int len);
+extern int has_symlink_or_noent_leading_path(const char *name, int len);
+extern int has_dirs_only_path(const char *name, int len, int prefix_len);
+extern void invalidate_lstat_cache(const char *name, int len);
+extern void clear_lstat_cache(void);
+extern void schedule_dir_for_removal(const char *name, int len);
+extern void remove_scheduled_dirs(void);
 
 extern struct alternate_object_database {
 	struct alternate_object_database *next;
@@ -652,6 +750,8 @@
 } *alt_odb_list;
 extern void prepare_alt_odb(void);
 extern void add_to_alternates_file(const char *reference);
+typedef int alt_odb_fn(struct alternate_object_database *, void *);
+extern void foreach_alt_odb(alt_odb_fn, void*);
 
 struct pack_window {
 	struct pack_window *next;
@@ -714,14 +814,18 @@
 #define REF_HEADS	(1u << 1)
 #define REF_TAGS	(1u << 2)
 
-extern struct ref *find_ref_by_name(struct ref *list, const char *name);
+extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
 
 #define CONNECT_VERBOSE       (1u << 0)
 extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 extern int finish_connect(struct child_process *conn);
 extern int path_match(const char *path, int nr, char **match);
 extern int get_ack(int fd, unsigned char *result_sha1);
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
+struct extra_have_objects {
+	int nr, alloc;
+	unsigned char (*array)[20];
+};
+extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
 extern int server_supports(const char *feature);
 
 extern struct packed_git *parse_pack_index(unsigned char *sha1);
@@ -745,10 +849,9 @@
 extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
 extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
 extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
-extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
+extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
-extern int matches_pack_name(struct packed_git *p, const char *name);
 
 /* Dumb servers support */
 extern int update_server_info(int);
@@ -757,7 +860,6 @@
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
 extern int git_config(config_fn_t fn, void *);
-extern int git_parse_long(const char *, long *);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
@@ -781,6 +883,7 @@
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
+extern const char *git_mailmap_file;
 
 /* IO helper functions */
 extern void maybe_flush_or_die(FILE *, const char *);
@@ -856,7 +959,6 @@
 extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 
 /* ls-files */
-int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
diff --git a/color.c b/color.c
index fc0b72a..62977f4 100644
--- a/color.c
+++ b/color.c
@@ -1,8 +1,6 @@
 #include "cache.h"
 #include "color.h"
 
-#define COLOR_RESET "\033[m"
-
 int git_use_color_default = 0;
 
 static int parse_color(const char *name, int len)
@@ -41,29 +39,40 @@
 
 void color_parse(const char *value, const char *var, char *dst)
 {
+	color_parse_mem(value, strlen(value), var, dst);
+}
+
+void color_parse_mem(const char *value, int value_len, const char *var,
+		char *dst)
+{
 	const char *ptr = value;
+	int len = value_len;
 	int attr = -1;
 	int fg = -2;
 	int bg = -2;
 
-	if (!strcasecmp(value, "reset")) {
-		strcpy(dst, "\033[m");
+	if (!strncasecmp(value, "reset", len)) {
+		strcpy(dst, GIT_COLOR_RESET);
 		return;
 	}
 
 	/* [fg [bg]] [attr] */
-	while (*ptr) {
+	while (len > 0) {
 		const char *word = ptr;
-		int val, len = 0;
+		int val, wordlen = 0;
 
-		while (word[len] && !isspace(word[len]))
-			len++;
+		while (len > 0 && !isspace(word[wordlen])) {
+			wordlen++;
+			len--;
+		}
 
-		ptr = word + len;
-		while (*ptr && isspace(*ptr))
+		ptr = word + wordlen;
+		while (len > 0 && isspace(*ptr)) {
 			ptr++;
+			len--;
+		}
 
-		val = parse_color(word, len);
+		val = parse_color(word, wordlen);
 		if (val >= -1) {
 			if (fg == -2) {
 				fg = val;
@@ -75,7 +84,7 @@
 			}
 			goto bad;
 		}
-		val = parse_attr(word, len);
+		val = parse_attr(word, wordlen);
 		if (val < 0 || attr != -1)
 			goto bad;
 		attr = val;
@@ -115,7 +124,7 @@
 	*dst = 0;
 	return;
 bad:
-	die("bad config value '%s' for variable '%s'", value, var);
+	die("bad color value '%.*s' for variable '%s'", value_len, value, var);
 }
 
 int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
@@ -164,7 +173,7 @@
 		r += fprintf(fp, "%s", color);
 	r += vfprintf(fp, fmt, args);
 	if (*color)
-		r += fprintf(fp, "%s", COLOR_RESET);
+		r += fprintf(fp, "%s", GIT_COLOR_RESET);
 	if (trail)
 		r += fprintf(fp, "%s", trail);
 	return r;
@@ -191,3 +200,31 @@
 	va_end(args);
 	return r;
 }
+
+/*
+ * This function splits the buffer by newlines and colors the lines individually.
+ *
+ * Returns 0 on success.
+ */
+int color_fwrite_lines(FILE *fp, const char *color,
+		size_t count, const char *buf)
+{
+	if (!*color)
+		return fwrite(buf, count, 1, fp) != 1;
+	while (count) {
+		char *p = memchr(buf, '\n', count);
+		if (p != buf && (fputs(color, fp) < 0 ||
+				fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+				fputs(GIT_COLOR_RESET, fp) < 0))
+			return -1;
+		if (!p)
+			return 0;
+		if (fputc('\n', fp) < 0)
+			return -1;
+		count -= p + 1 - buf;
+		buf = p + 1;
+	}
+	return 0;
+}
+
+
diff --git a/color.h b/color.h
index 6cf5c88..18abeb7 100644
--- a/color.h
+++ b/color.h
@@ -4,6 +4,17 @@
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 #define COLOR_MAXLEN 24
 
+#define GIT_COLOR_NORMAL	""
+#define GIT_COLOR_RESET		"\033[m"
+#define GIT_COLOR_BOLD		"\033[1m"
+#define GIT_COLOR_RED		"\033[31m"
+#define GIT_COLOR_GREEN		"\033[32m"
+#define GIT_COLOR_YELLOW	"\033[33m"
+#define GIT_COLOR_BLUE		"\033[34m"
+#define GIT_COLOR_MAGENTA	"\033[35m"
+#define GIT_COLOR_CYAN		"\033[36m"
+#define GIT_COLOR_BG_RED	"\033[41m"
+
 /*
  * This variable stores the value of color.ui
  */
@@ -16,8 +27,10 @@
 int git_color_default_config(const char *var, const char *value, void *cb);
 
 int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
-void color_parse(const char *var, const char *value, char *dst);
+void color_parse(const char *value, const char *var, char *dst);
+void color_parse_mem(const char *value, int len, const char *var, char *dst);
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
+int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
 
 #endif /* COLOR_H */
diff --git a/combine-diff.c b/combine-diff.c
index aa9d79e..066ce84 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -143,8 +143,6 @@
 }
 
 struct combine_diff_state {
-	struct xdiff_emit_state xm;
-
 	unsigned int lno;
 	int ob, on, nb, nn;
 	unsigned long nmask;
@@ -215,19 +213,18 @@
 
 	parent_file.ptr = grab_blob(parent, &sz);
 	parent_file.size = sz;
+	memset(&xpp, 0, sizeof(xpp));
 	xpp.flags = XDF_NEED_MINIMAL;
 	memset(&xecfg, 0, sizeof(xecfg));
-	ecb.outf = xdiff_outf;
-	ecb.priv = &state;
 	memset(&state, 0, sizeof(state));
-	state.xm.consume = consume_line;
 	state.nmask = nmask;
 	state.sline = sline;
 	state.lno = 1;
 	state.num_parent = num_parent;
 	state.n = n;
 
-	xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+	xdi_diff_outf(&parent_file, result_file, consume_line, &state,
+		      &xpp, &xecfg, &ecb);
 	free(parent_file.ptr);
 
 	/* Assign line numbers for this parent.
@@ -529,7 +526,6 @@
 		return; /* result deleted */
 
 	while (1) {
-		struct sline *sl = &sline[lno];
 		unsigned long hunk_end;
 		unsigned long rlines;
 		const char *hunk_comment = NULL;
@@ -595,7 +591,7 @@
 			struct lline *ll;
 			int j;
 			unsigned long p_mask;
-			sl = &sline[lno++];
+			struct sline *sl = &sline[lno++];
 			ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head;
 			while (ll) {
 				fputs(c_old, stdout);
@@ -687,9 +683,13 @@
 	int i, show_hunks;
 	int working_tree_file = is_null_sha1(elem->sha1);
 	int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+	const char *a_prefix, *b_prefix;
 	mmfile_t result_file;
 
 	context = opt->context;
+	a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
+	b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+
 	/* Read the result of merge first */
 	if (!working_tree_file)
 		result = grab_blob(elem->sha1, &result_size);
@@ -702,19 +702,17 @@
 			goto deleted_file;
 
 		if (S_ISLNK(st.st_mode)) {
-			size_t len = xsize_t(st.st_size);
-			result_size = len;
-			result = xmalloc(len + 1);
-			if (result_size != readlink(elem->path, result, len)) {
+			struct strbuf buf = STRBUF_INIT;
+
+			if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) {
 				error("readlink(%s): %s", elem->path,
 				      strerror(errno));
 				return;
 			}
-			result[len] = 0;
+			result_size = buf.len;
+			result = strbuf_detach(&buf, NULL);
 			elem->mode = canon_mode(st.st_mode);
-		}
-		else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
-			 !fstat(fd, &st)) {
+		} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
 			size_t len = xsize_t(st.st_size);
 			ssize_t done;
 			int is_file, i;
@@ -742,9 +740,8 @@
 
 			/* If not a fake symlink, apply filters, e.g. autocrlf */
 			if (is_file) {
-				struct strbuf buf;
+				struct strbuf buf = STRBUF_INIT;
 
-				strbuf_init(&buf, 0);
 				if (convert_to_git(elem->path, result, len, &buf, safe_crlf)) {
 					free(result);
 					result = strbuf_detach(&buf, &len);
@@ -865,13 +862,13 @@
 			dump_quoted_path("--- ", "", "/dev/null",
 					 c_meta, c_reset);
 		else
-			dump_quoted_path("--- ", opt->a_prefix, elem->path,
+			dump_quoted_path("--- ", a_prefix, elem->path,
 					 c_meta, c_reset);
 		if (deleted)
 			dump_quoted_path("+++ ", "", "/dev/null",
 					 c_meta, c_reset);
 		else
-			dump_quoted_path("+++ ", opt->b_prefix, elem->path,
+			dump_quoted_path("+++ ", b_prefix, elem->path,
 					 c_meta, c_reset);
 		dump_sline(sline, cnt, num_parent,
 			   DIFF_OPT_TST(opt, COLOR_DIFF));
diff --git a/command-list.txt b/command-list.txt
index 3583a33..fb03a2e 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -33,6 +33,7 @@
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
+git-difftool                            ancillaryinterrogators
 git-fast-export				ancillarymanipulators
 git-fast-import				ancillarymanipulators
 git-fetch                               mainporcelain common
diff --git a/commit.c b/commit.c
index dc0c5bf..aa3b35b 100644
--- a/commit.c
+++ b/commit.c
@@ -160,7 +160,7 @@
 	return graft;
 }
 
-int read_graft_file(const char *graft_file)
+static int read_graft_file(const char *graft_file)
 {
 	FILE *fp = fopen(graft_file, "r");
 	char buf[1024];
@@ -705,6 +705,21 @@
 	return get_merge_bases_many(one, 1, &two, cleanup);
 }
 
+int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
+{
+	if (!with_commit)
+		return 1;
+	while (with_commit) {
+		struct commit *other;
+
+		other = with_commit->item;
+		with_commit = with_commit->next;
+		if (in_merge_bases(other, &commit, 1))
+			return 1;
+	}
+	return 0;
+}
+
 int in_merge_bases(struct commit *commit, struct commit **reference, int num)
 {
 	struct commit_list *bases, *b;
diff --git a/commit.h b/commit.h
index ecdd573..ba9f638 100644
--- a/commit.h
+++ b/commit.h
@@ -65,6 +65,8 @@
 
 extern int non_ascii(int);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+extern char *reencode_commit_message(const struct commit *commit,
+				     const char **encoding_p);
 extern void get_commit_format(const char *arg, struct rev_info *);
 extern void format_commit_message(const struct commit *commit,
 				  const void *format, struct strbuf *sb,
@@ -118,10 +120,10 @@
 
 struct commit_graft *read_graft_line(char *buf, int len);
 int register_commit_graft(struct commit_graft *, int);
-int read_graft_file(const char *graft_file);
 struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
+extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
 extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 extern int register_shallow(const unsigned char *sha1);
@@ -131,6 +133,7 @@
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
 		int depth, int shallow_flag, int not_shallow_flag);
 
+int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit **, int);
 
 extern int interactive_add(int argc, const char **argv, const char *prefix);
diff --git a/compat/cygwin.c b/compat/cygwin.c
new file mode 100644
index 0000000..ebac148
--- /dev/null
+++ b/compat/cygwin.c
@@ -0,0 +1,143 @@
+#define WIN32_LEAN_AND_MEAN
+#include "../git-compat-util.h"
+#include "win32.h"
+#include "../cache.h" /* to read configuration */
+
+static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
+{
+	long long winTime = ((long long)ft->dwHighDateTime << 32) +
+			ft->dwLowDateTime;
+	winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
+	/* convert 100-nsecond interval to seconds and nanoseconds */
+	ts->tv_sec = (time_t)(winTime/10000000);
+	ts->tv_nsec = (long)(winTime - ts->tv_sec*10000000LL) * 100;
+}
+
+#define size_to_blocks(s) (((s)+511)/512)
+
+/* do_stat is a common implementation for cygwin_lstat and cygwin_stat.
+ *
+ * To simplify its logic, in the case of cygwin symlinks, this implementation
+ * falls back to the cygwin version of stat/lstat, which is provided as the
+ * last argument.
+ */
+static int do_stat(const char *file_name, struct stat *buf, stat_fn_t cygstat)
+{
+	WIN32_FILE_ATTRIBUTE_DATA fdata;
+
+	if (file_name[0] == '/')
+		return cygstat (file_name, buf);
+
+	if (!(errno = get_file_attr(file_name, &fdata))) {
+		/*
+		 * If the system attribute is set and it is not a directory then
+		 * it could be a symbol link created in the nowinsymlinks mode.
+		 * Normally, Cygwin works in the winsymlinks mode, so this situation
+		 * is very unlikely. For the sake of simplicity of our code, let's
+		 * Cygwin to handle it.
+		 */
+		if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
+		    !(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+			return cygstat(file_name, buf);
+
+		/* fill out the stat structure */
+		buf->st_dev = buf->st_rdev = 0; /* not used by Git */
+		buf->st_ino = 0;
+		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+		buf->st_nlink = 1;
+		buf->st_uid = buf->st_gid = 0;
+#ifdef __CYGWIN_USE_BIG_TYPES__
+		buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) +
+			fdata.nFileSizeLow;
+#else
+		buf->st_size = (off_t)fdata.nFileSizeLow;
+#endif
+		buf->st_blocks = size_to_blocks(buf->st_size);
+		filetime_to_timespec(&fdata.ftLastAccessTime, &buf->st_atim);
+		filetime_to_timespec(&fdata.ftLastWriteTime, &buf->st_mtim);
+		filetime_to_timespec(&fdata.ftCreationTime, &buf->st_ctim);
+		return 0;
+	} else if (errno == ENOENT) {
+		/*
+		 * In the winsymlinks mode (which is the default), Cygwin
+		 * emulates symbol links using Windows shortcut files. These
+		 * files are formed by adding .lnk extension. So, if we have
+		 * not found the specified file name, it could be that it is
+		 * a symbol link. Let's Cygwin to deal with that.
+		 */
+		return cygstat(file_name, buf);
+	}
+	return -1;
+}
+
+/* We provide our own lstat/stat functions, since the provided Cygwin versions
+ * of these functions are too slow. These stat functions are tailored for Git's
+ * usage, and therefore they are not meant to be complete and correct emulation
+ * of lstat/stat functionality.
+ */
+static int cygwin_lstat(const char *path, struct stat *buf)
+{
+	return do_stat(path, buf, lstat);
+}
+
+static int cygwin_stat(const char *path, struct stat *buf)
+{
+	return do_stat(path, buf, stat);
+}
+
+
+/*
+ * At start up, we are trying to determine whether Win32 API or cygwin stat
+ * functions should be used. The choice is determined by core.ignorecygwinfstricks.
+ * Reading this option is not always possible immediately as git_dir may be
+ * not be set yet. So until it is set, use cygwin lstat/stat functions.
+ * However, if core.filemode is set, we must use the Cygwin posix
+ * stat/lstat as the Windows stat fuctions do not determine posix filemode.
+ *
+ * Note that git_cygwin_config() does NOT call git_default_config() and this
+ * is deliberate.  Many commands read from config to establish initial
+ * values in variables and later tweak them from elsewhere (e.g. command line).
+ * init_stat() is called lazily on demand, typically much late in the program,
+ * and calling git_default_config() from here would break such variables.
+ */
+static int native_stat = 1;
+static int core_filemode;
+
+static int git_cygwin_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "core.ignorecygwinfstricks"))
+		native_stat = git_config_bool(var, value);
+	else if (!strcmp(var, "core.filemode"))
+		core_filemode = git_config_bool(var, value);
+	return 0;
+}
+
+static int init_stat(void)
+{
+	if (have_git_dir()) {
+		git_config(git_cygwin_config, NULL);
+		if (!core_filemode && native_stat) {
+			cygwin_stat_fn = cygwin_stat;
+			cygwin_lstat_fn = cygwin_lstat;
+		} else {
+			cygwin_stat_fn = stat;
+			cygwin_lstat_fn = lstat;
+		}
+		return 0;
+	}
+	return 1;
+}
+
+static int cygwin_stat_stub(const char *file_name, struct stat *buf)
+{
+	return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf);
+}
+
+static int cygwin_lstat_stub(const char *file_name, struct stat *buf)
+{
+	return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
+}
+
+stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
+stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
+
diff --git a/compat/cygwin.h b/compat/cygwin.h
new file mode 100644
index 0000000..a3229f5
--- /dev/null
+++ b/compat/cygwin.h
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+typedef int (*stat_fn_t)(const char*, struct stat*);
+extern stat_fn_t cygwin_stat_fn;
+extern stat_fn_t cygwin_lstat_fn;
+
+#define stat(path, buf) (*cygwin_stat_fn)(path, buf)
+#define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)
diff --git a/compat/memmem.c b/compat/memmem.c
index cd0d877..56bcb42 100644
--- a/compat/memmem.c
+++ b/compat/memmem.c
@@ -5,6 +5,8 @@
 {
 	const char *begin = haystack;
 	const char *last_possible = begin + haystack_len - needle_len;
+	const char *tail = needle;
+	char point;
 
 	/*
 	 * The first occurrence of the empty string is deemed to occur at
@@ -20,8 +22,9 @@
 	if (haystack_len < needle_len)
 		return NULL;
 
+	point = *tail++;
 	for (; begin <= last_possible; begin++) {
-		if (!memcmp(begin, needle, needle_len))
+		if (*begin == point && !memcmp(begin + 1, tail, needle_len - 1))
 			return (void *)begin;
 	}
 
diff --git a/compat/mingw.c b/compat/mingw.c
index 45733f9..2839d9d 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1,8 +1,122 @@
 #include "../git-compat-util.h"
+#include "win32.h"
 #include "../strbuf.h"
 
 unsigned int _CRT_fmode = _O_BINARY;
 
+static int err_win_to_posix(DWORD winerr)
+{
+	int error = ENOSYS;
+	switch(winerr) {
+	case ERROR_ACCESS_DENIED: error = EACCES; break;
+	case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
+	case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
+	case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
+	case ERROR_ALREADY_EXISTS: error = EEXIST; break;
+	case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
+	case ERROR_BAD_COMMAND: error = EIO; break;
+	case ERROR_BAD_DEVICE: error = ENODEV; break;
+	case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
+	case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
+	case ERROR_BAD_FORMAT: error = ENOEXEC; break;
+	case ERROR_BAD_LENGTH: error = EINVAL; break;
+	case ERROR_BAD_PATHNAME: error = ENOENT; break;
+	case ERROR_BAD_PIPE: error = EPIPE; break;
+	case ERROR_BAD_UNIT: error = ENODEV; break;
+	case ERROR_BAD_USERNAME: error = EINVAL; break;
+	case ERROR_BROKEN_PIPE: error = EPIPE; break;
+	case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
+	case ERROR_BUSY: error = EBUSY; break;
+	case ERROR_BUSY_DRIVE: error = EBUSY; break;
+	case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
+	case ERROR_CANNOT_MAKE: error = EACCES; break;
+	case ERROR_CANTOPEN: error = EIO; break;
+	case ERROR_CANTREAD: error = EIO; break;
+	case ERROR_CANTWRITE: error = EIO; break;
+	case ERROR_CRC: error = EIO; break;
+	case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
+	case ERROR_DEVICE_IN_USE: error = EBUSY; break;
+	case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
+	case ERROR_DIRECTORY: error = EINVAL; break;
+	case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
+	case ERROR_DISK_CHANGE: error = EIO; break;
+	case ERROR_DISK_FULL: error = ENOSPC; break;
+	case ERROR_DRIVE_LOCKED: error = EBUSY; break;
+	case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
+	case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
+	case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
+	case ERROR_FILE_EXISTS: error = EEXIST; break;
+	case ERROR_FILE_INVALID: error = ENODEV; break;
+	case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
+	case ERROR_GEN_FAILURE: error = EIO; break;
+	case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
+	case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
+	case ERROR_INVALID_ACCESS: error = EACCES; break;
+	case ERROR_INVALID_ADDRESS: error = EFAULT; break;
+	case ERROR_INVALID_BLOCK: error = EFAULT; break;
+	case ERROR_INVALID_DATA: error = EINVAL; break;
+	case ERROR_INVALID_DRIVE: error = ENODEV; break;
+	case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
+	case ERROR_INVALID_FLAGS: error = EINVAL; break;
+	case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
+	case ERROR_INVALID_HANDLE: error = EBADF; break;
+	case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
+	case ERROR_INVALID_NAME: error = EINVAL; break;
+	case ERROR_INVALID_OWNER: error = EINVAL; break;
+	case ERROR_INVALID_PARAMETER: error = EINVAL; break;
+	case ERROR_INVALID_PASSWORD: error = EPERM; break;
+	case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
+	case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
+	case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
+	case ERROR_INVALID_WORKSTATION: error = EACCES; break;
+	case ERROR_IO_DEVICE: error = EIO; break;
+	case ERROR_IO_INCOMPLETE: error = EINTR; break;
+	case ERROR_LOCKED: error = EBUSY; break;
+	case ERROR_LOCK_VIOLATION: error = EACCES; break;
+	case ERROR_LOGON_FAILURE: error = EACCES; break;
+	case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
+	case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
+	case ERROR_MORE_DATA: error = EPIPE; break;
+	case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
+	case ERROR_NOACCESS: error = EFAULT; break;
+	case ERROR_NONE_MAPPED: error = EINVAL; break;
+	case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
+	case ERROR_NOT_READY: error = EAGAIN; break;
+	case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
+	case ERROR_NO_DATA: error = EPIPE; break;
+	case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
+	case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
+	case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
+	case ERROR_OPEN_FAILED: error = EIO; break;
+	case ERROR_OPEN_FILES: error = EBUSY; break;
+	case ERROR_OPERATION_ABORTED: error = EINTR; break;
+	case ERROR_OUTOFMEMORY: error = ENOMEM; break;
+	case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
+	case ERROR_PATH_BUSY: error = EBUSY; break;
+	case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
+	case ERROR_PIPE_BUSY: error = EBUSY; break;
+	case ERROR_PIPE_CONNECTED: error = EPIPE; break;
+	case ERROR_PIPE_LISTENING: error = EPIPE; break;
+	case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
+	case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
+	case ERROR_READ_FAULT: error = EIO; break;
+	case ERROR_SEEK: error = EIO; break;
+	case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
+	case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
+	case ERROR_SHARING_VIOLATION: error = EACCES; break;
+	case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
+	case ERROR_SWAPERROR: error = ENOENT; break;
+	case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
+	case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
+	case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
+	case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
+	case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
+	case ERROR_WRITE_FAULT: error = EIO; break;
+	case ERROR_WRITE_PROTECT: error = EROFS; break;
+	}
+	return error;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
@@ -31,12 +145,6 @@
 	return (time_t)winTime;
 }
 
-static inline size_t size_to_blocks(size_t s)
-{
-	return (s+511)/512;
-}
-
-extern int _getdrive( void );
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
  * this case, we strip the trailing slashes and stat again.
@@ -45,46 +153,20 @@
 {
 	WIN32_FILE_ATTRIBUTE_DATA fdata;
 
-	if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
-		int fMode = S_IREAD;
-		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-			fMode |= S_IFDIR;
-		else
-			fMode |= S_IFREG;
-		if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
-			fMode |= S_IWRITE;
-
+	if (!(errno = get_file_attr(file_name, &fdata))) {
 		buf->st_ino = 0;
 		buf->st_gid = 0;
 		buf->st_uid = 0;
-		buf->st_mode = fMode;
-		buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-		buf->st_blocks = size_to_blocks(buf->st_size);
-		buf->st_dev = _getdrive() - 1;
+		buf->st_nlink = 1;
+		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+		buf->st_size = fdata.nFileSizeLow |
+			(((off_t)fdata.nFileSizeHigh)<<32);
+		buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
-		errno = 0;
 		return 0;
 	}
-
-	switch (GetLastError()) {
-	case ERROR_ACCESS_DENIED:
-	case ERROR_SHARING_VIOLATION:
-	case ERROR_LOCK_VIOLATION:
-	case ERROR_SHARING_BUFFER_EXCEEDED:
-		errno = EACCES;
-		break;
-	case ERROR_BUFFER_OVERFLOW:
-		errno = ENAMETOOLONG;
-		break;
-	case ERROR_NOT_ENOUGH_MEMORY:
-		errno = ENOMEM;
-		break;
-	default:
-		errno = ENOENT;
-		break;
-	}
 	return -1;
 }
 
@@ -94,7 +176,7 @@
  * complete. Note that Git stat()s are redirected to mingw_lstat()
  * too, since Windows doesn't really handle symlinks that well.
  */
-int mingw_lstat(const char *file_name, struct mingw_stat *buf)
+int mingw_lstat(const char *file_name, struct stat *buf)
 {
 	int namelen;
 	static char alt_name[PATH_MAX];
@@ -122,8 +204,7 @@
 }
 
 #undef fstat
-#undef stat
-int mingw_fstat(int fd, struct mingw_stat *buf)
+int mingw_fstat(int fd, struct stat *buf)
 {
 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
 	BY_HANDLE_FILE_INFORMATION fdata;
@@ -133,39 +214,18 @@
 		return -1;
 	}
 	/* direct non-file handles to MS's fstat() */
-	if (GetFileType(fh) != FILE_TYPE_DISK) {
-		struct stat st;
-		if (fstat(fd, &st))
-			return -1;
-		buf->st_ino = st.st_ino;
-		buf->st_gid = st.st_gid;
-		buf->st_uid = st.st_uid;
-		buf->st_mode = st.st_mode;
-		buf->st_size = st.st_size;
-		buf->st_blocks = size_to_blocks(buf->st_size);
-		buf->st_dev = st.st_dev;
-		buf->st_atime = st.st_atime;
-		buf->st_mtime = st.st_mtime;
-		buf->st_ctime = st.st_ctime;
-		return 0;
-	}
+	if (GetFileType(fh) != FILE_TYPE_DISK)
+		return _fstati64(fd, buf);
 
 	if (GetFileInformationByHandle(fh, &fdata)) {
-		int fMode = S_IREAD;
-		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-			fMode |= S_IFDIR;
-		else
-			fMode |= S_IFREG;
-		if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
-			fMode |= S_IWRITE;
-
 		buf->st_ino = 0;
 		buf->st_gid = 0;
 		buf->st_uid = 0;
-		buf->st_mode = fMode;
-		buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-		buf->st_blocks = size_to_blocks(buf->st_size);
-		buf->st_dev = _getdrive() - 1;
+		buf->st_nlink = 1;
+		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+		buf->st_size = fdata.nFileSizeLow |
+			(((off_t)fdata.nFileSizeHigh)<<32);
+		buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
@@ -283,8 +343,13 @@
 {
 	int i, pending;
 
-	if (timeout != -1)
+	if (timeout >= 0) {
+		if (nfds == 0) {
+			Sleep(timeout);
+			return 0;
+		}
 		return errno = EINVAL, error("poll timeout not supported");
+	}
 
 	/* When there is only one fd to wait for, then we pretend that
 	 * input is available and let the actual wait happen when the
@@ -392,7 +457,7 @@
 	const char *p = arg;
 	if (!*p) force_quotes = 1;
 	while (*p) {
-		if (isspace(*p) || *p == '*' || *p == '?' || *p == '{')
+		if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'')
 			force_quotes = 1;
 		else if (*p == '"')
 			n++;
@@ -586,12 +651,16 @@
 		 * would normally create a console window. But
 		 * since we'll be redirecting std streams, we do
 		 * not need the console.
+		 * It is necessary to use DETACHED_PROCESS
+		 * instead of CREATE_NO_WINDOW to make ssh
+		 * recognize that it has no console.
 		 */
-		flags = CREATE_NO_WINDOW;
+		flags = DETACHED_PROCESS;
 	} else {
 		/* There is already a console. If we specified
-		 * CREATE_NO_WINDOW here, too, Windows would
+		 * DETACHED_PROCESS here, too, Windows would
 		 * disassociate the child from the console.
+		 * The same is true for CREATE_NO_WINDOW.
 		 * Go figure!
 		 */
 		flags = 0;
@@ -1049,3 +1118,24 @@
 	printf("Launching default browser to display HTML ...\n");
 	ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
 }
+
+int link(const char *oldpath, const char *newpath)
+{
+	typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+	static T create_hard_link = NULL;
+	if (!create_hard_link) {
+		create_hard_link = (T) GetProcAddress(
+			GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
+		if (!create_hard_link)
+			create_hard_link = (T)-1;
+	}
+	if (create_hard_link == (T)-1) {
+		errno = ENOSYS;
+		return -1;
+	}
+	if (!create_hard_link(newpath, oldpath, NULL)) {
+		errno = err_win_to_posix(GetLastError());
+		return -1;
+	}
+	return 0;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index a52e657..762eb14 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -21,12 +21,12 @@
 #define WEXITSTATUS(x) ((x) & 0xff)
 #define WIFSIGNALED(x) ((unsigned)(x) > 259)
 
-#define SIGKILL 0
-#define SIGCHLD 0
-#define SIGPIPE 0
-#define SIGHUP 0
-#define SIGQUIT 0
-#define SIGALRM 100
+#define SIGHUP 1
+#define SIGQUIT 3
+#define SIGKILL 9
+#define SIGPIPE 13
+#define SIGALRM 14
+#define SIGCHLD 17
 
 #define F_GETFD 1
 #define F_SETFD 2
@@ -67,8 +67,6 @@
 { errno = ENOSYS; return -1; }
 static inline int symlink(const char *oldpath, const char *newpath)
 { errno = ENOSYS; return -1; }
-static inline int link(const char *oldpath, const char *newpath)
-{ errno = ENOSYS; return -1; }
 static inline int fchmod(int fildes, mode_t mode)
 { errno = ENOSYS; return -1; }
 static inline int fork(void)
@@ -134,6 +132,7 @@
 struct passwd *getpwuid(int uid);
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
+int link(const char *oldpath, const char *newpath);
 
 /*
  * replacements of existing functions
@@ -160,24 +159,22 @@
 int mingw_rename(const char*, const char*);
 #define rename mingw_rename
 
+#ifdef USE_WIN32_MMAP
+int mingw_getpagesize(void);
+#define getpagesize mingw_getpagesize
+#endif
+
 /* Use mingw_lstat() instead of lstat()/stat() and
  * mingw_fstat() instead of fstat() on Windows.
- * struct stat is redefined because it lacks the st_blocks member.
  */
-struct mingw_stat {
-	unsigned st_mode;
-	time_t st_mtime, st_atime, st_ctime;
-	unsigned st_dev, st_ino, st_uid, st_gid;
-	size_t st_size;
-	size_t st_blocks;
-};
-int mingw_lstat(const char *file_name, struct mingw_stat *buf);
-int mingw_fstat(int fd, struct mingw_stat *buf);
+#define off_t off64_t
+#define stat _stati64
+#define lseek _lseeki64
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define stat mingw_stat
-static inline int mingw_stat(const char *file_name, struct mingw_stat *buf)
-{ return mingw_lstat(file_name, buf); }
+#define _stati64(x,y) mingw_lstat(x,y)
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
 #define utime mingw_utime
diff --git a/compat/win32.h b/compat/win32.h
new file mode 100644
index 0000000..c26384e
--- /dev/null
+++ b/compat/win32.h
@@ -0,0 +1,34 @@
+/* common Win32 functions for MinGW and Cygwin */
+#include <windows.h>
+
+static inline int file_attr_to_st_mode (DWORD attr)
+{
+	int fMode = S_IREAD;
+	if (attr & FILE_ATTRIBUTE_DIRECTORY)
+		fMode |= S_IFDIR;
+	else
+		fMode |= S_IFREG;
+	if (!(attr & FILE_ATTRIBUTE_READONLY))
+		fMode |= S_IWRITE;
+	return fMode;
+}
+
+static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
+{
+	if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
+		return 0;
+
+	switch (GetLastError()) {
+	case ERROR_ACCESS_DENIED:
+	case ERROR_SHARING_VIOLATION:
+	case ERROR_LOCK_VIOLATION:
+	case ERROR_SHARING_BUFFER_EXCEEDED:
+		return EACCES;
+	case ERROR_BUFFER_OVERFLOW:
+		return ENAMETOOLONG;
+	case ERROR_NOT_ENOUGH_MEMORY:
+		return ENOMEM;
+	default:
+		return ENOENT;
+	}
+}
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
new file mode 100644
index 0000000..779d796
--- /dev/null
+++ b/compat/win32mmap.c
@@ -0,0 +1,53 @@
+#include "../git-compat-util.h"
+
+/*
+ * Note that this doesn't return the actual pagesize, but
+ * the allocation granularity. If future Windows specific git code
+ * needs the real getpagesize function, we need to find another solution.
+ */
+int mingw_getpagesize(void)
+{
+	SYSTEM_INFO si;
+	GetSystemInfo(&si);
+	return si.dwAllocationGranularity;
+}
+
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
+{
+	HANDLE hmap;
+	void *temp;
+	size_t len;
+	struct stat st;
+	uint64_t o = offset;
+	uint32_t l = o & 0xFFFFFFFF;
+	uint32_t h = (o >> 32) & 0xFFFFFFFF;
+
+	if (!fstat(fd, &st))
+		len = xsize_t(st.st_size);
+	else
+		die("mmap: could not determine filesize");
+
+	if ((length + offset) > len)
+		length = len - offset;
+
+	if (!(flags & MAP_PRIVATE))
+		die("Invalid usage of mmap when built with USE_WIN32_MMAP");
+
+	hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_WRITECOPY,
+		0, 0, 0);
+
+	if (!hmap)
+		return MAP_FAILED;
+
+	temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start);
+
+	if (!CloseHandle(hmap))
+		warning("unable to close file mapping handle\n");
+
+	return temp ? temp : MAP_FAILED;
+}
+
+int git_munmap(void *start, size_t length)
+{
+	return !UnmapViewOfFile(start);
+}
diff --git a/compat/winansi.c b/compat/winansi.c
index e2d96df..44dc293 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -18,8 +18,6 @@
 
  This file is git-specific. Therefore, this file does not attempt
  to implement any codes that are not used by git.
-
- TODO: K
 */
 
 static HANDLE console;
@@ -79,6 +77,20 @@
 	SetConsoleTextAttribute(console, attributes);
 }
 
+static void erase_in_line(void)
+{
+	CONSOLE_SCREEN_BUFFER_INFO sbi;
+
+	if (!console)
+		return;
+
+	GetConsoleScreenBufferInfo(console, &sbi);
+	FillConsoleOutputCharacterA(console, ' ',
+		sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
+		NULL);
+}
+
+
 static const char *set_attr(const char *str)
 {
 	const char *func;
@@ -218,7 +230,7 @@
 		set_console_attr();
 		break;
 	case 'K':
-		/* TODO */
+		erase_in_line();
 		break;
 	default:
 		/* Unsupported code */
diff --git a/config.c b/config.c
index 82807c8..2d70398 100644
--- a/config.c
+++ b/config.c
@@ -51,7 +51,7 @@
 
 	for (;;) {
 		int c = get_next_char();
-		if (len >= sizeof(value))
+		if (len >= sizeof(value) - 1)
 			return NULL;
 		if (c == '\n') {
 			if (quote)
@@ -205,8 +205,27 @@
 	int baselen = 0;
 	static char var[MAXNAME];
 
+	/* U+FEFF Byte Order Mark in UTF8 */
+	static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
+	const unsigned char *bomptr = utf8_bom;
+
 	for (;;) {
 		int c = get_next_char();
+		if (bomptr && *bomptr) {
+			/* We are at the file beginning; skip UTF8-encoded BOM
+			 * if present. Sane editors won't put this in on their
+			 * own, but e.g. Windows Notepad will do it happily. */
+			if ((unsigned char) c == *bomptr) {
+				bomptr++;
+				continue;
+			} else {
+				/* Do not tolerate partial BOM. */
+				if (bomptr != utf8_bom)
+					break;
+				/* No BOM at file beginning. Cool. */
+				bomptr = NULL;
+			}
+		}
 		if (c == '\n') {
 			if (config_file_eof)
 				return 0;
@@ -255,7 +274,7 @@
 	return 0;
 }
 
-int git_parse_long(const char *value, long *ret)
+static int git_parse_long(const char *value, long *ret)
 {
 	if (value && *value) {
 		char *end;
@@ -291,7 +310,7 @@
 
 int git_config_int(const char *name, const char *value)
 {
-	long ret;
+	long ret = 0;
 	if (!git_parse_long(value, &ret))
 		die_bad_config(name);
 	return ret;
@@ -471,6 +490,11 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.preloadindex")) {
+		core_preload_index = git_config_bool(var, value);
+		return 0;
+	}
+
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
 }
@@ -541,6 +565,40 @@
 	return 0;
 }
 
+static int git_default_push_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "push.default")) {
+		if (!value)
+			return config_error_nonbool(var);
+		else if (!strcmp(value, "nothing"))
+			push_default = PUSH_DEFAULT_NOTHING;
+		else if (!strcmp(value, "matching"))
+			push_default = PUSH_DEFAULT_MATCHING;
+		else if (!strcmp(value, "tracking"))
+			push_default = PUSH_DEFAULT_TRACKING;
+		else if (!strcmp(value, "current"))
+			push_default = PUSH_DEFAULT_CURRENT;
+		else {
+			error("Malformed value for %s: %s", var, value);
+			return error("Must be one of nothing, matching, "
+				     "tracking or current.");
+		}
+		return 0;
+	}
+
+	/* Add other config variables here and to Documentation/config.txt. */
+	return 0;
+}
+
+static int git_default_mailmap_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "mailmap.file"))
+		return git_config_string(&git_mailmap_file, var, value);
+
+	/* Add other config variables here and to Documentation/config.txt. */
+	return 0;
+}
+
 int git_default_config(const char *var, const char *value, void *dummy)
 {
 	if (!prefixcmp(var, "core."))
@@ -555,6 +613,12 @@
 	if (!prefixcmp(var, "branch."))
 		return git_default_branch_config(var, value);
 
+	if (!prefixcmp(var, "push."))
+		return git_default_push_config(var, value);
+
+	if (!prefixcmp(var, "mailmap."))
+		return git_default_mailmap_config(var, value);
+
 	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
 		pager_use_color = git_config_bool(var,value);
 		return 0;
@@ -608,31 +672,37 @@
 
 int git_config(config_fn_t fn, void *data)
 {
-	int ret = 0;
+	int ret = 0, found = 0;
 	char *repo_config = NULL;
 	const char *home = NULL;
 
-	/* $GIT_CONFIG makes git read _only_ the given config file,
-	 * $GIT_CONFIG_LOCAL will make it process it in addition to the
-	 * global config file, the same way it would the per-repository
-	 * config file otherwise. */
+	/* Setting $GIT_CONFIG makes git read _only_ the given config file. */
 	if (config_exclusive_filename)
 		return git_config_from_file(fn, config_exclusive_filename, data);
-	if (git_config_system() && !access(git_etc_gitconfig(), R_OK))
+	if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
 		ret += git_config_from_file(fn, git_etc_gitconfig(),
 					    data);
+		found += 1;
+	}
 
 	home = getenv("HOME");
 	if (git_config_global() && home) {
 		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-		if (!access(user_config, R_OK))
+		if (!access(user_config, R_OK)) {
 			ret += git_config_from_file(fn, user_config, data);
+			found += 1;
+		}
 		free(user_config);
 	}
 
 	repo_config = git_pathdup("config");
-	ret += git_config_from_file(fn, repo_config, data);
+	if (!access(repo_config, R_OK)) {
+		ret += git_config_from_file(fn, repo_config, data);
+		found += 1;
+	}
 	free(repo_config);
+	if (found == 0)
+		return -1;
 	return ret;
 }
 
@@ -734,9 +804,8 @@
 {
 	const char *dot;
 	int i, success;
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 
-	strbuf_init(&sb, 0);
 	dot = memchr(key, '.', store.baselen);
 	if (dot) {
 		strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
@@ -761,7 +830,7 @@
 	int i, success;
 	int length = strlen(key + store.baselen + 1);
 	const char *quote = "";
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 
 	/*
 	 * Check to see if the value needs to be surrounded with a dq pair.
@@ -778,7 +847,6 @@
 	if (i && value[i - 1] == ' ')
 		quote = "\"";
 
-	strbuf_init(&sb, 0);
 	strbuf_addf(&sb, "\t%.*s = %s",
 		    length, key + store.baselen + 1, quote);
 
diff --git a/config.mak.in b/config.mak.in
index b776149..7cce0c1 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -3,6 +3,8 @@
 
 CC = @CC@
 CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+CC_LD_DYNPATH = @CC_LD_DYNPATH@
 AR = @AR@
 TAR = @TAR@
 #INSTALL = @INSTALL@		# needs install-sh or install.sh in sources
@@ -11,9 +13,9 @@
 prefix = @prefix@
 exec_prefix = @exec_prefix@
 bindir = @bindir@
-gitexecdir = @libexecdir@/git-core/
+gitexecdir = @libexecdir@/git-core
 datarootdir = @datarootdir@
-template_dir = @datadir@/git-core/templates/
+template_dir = @datadir@/git-core/templates
 
 mandir=@mandir@
 
@@ -39,6 +41,7 @@
 NO_STRCASESTR=@NO_STRCASESTR@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
+NO_UINTMAX_T=@NO_UINTMAX_T@
 NO_STRTOUMAX=@NO_STRTOUMAX@
 NO_SETENV=@NO_SETENV@
 NO_UNSETENV=@NO_UNSETENV@
@@ -48,3 +51,6 @@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
 FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
 SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
+NO_PTHREADS=@NO_PTHREADS@
+THREADED_DELTA_SEARCH=@THREADED_DELTA_SEARCH@
+PTHREAD_LIBS=@PTHREAD_LIBS@
diff --git a/configure.ac b/configure.ac
index 7c2856e..4e728bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,8 @@
 	if test "$withval" = "yes"; then \
 		AC_MSG_WARN([You should provide path for --with-$1=PATH]); \
 	else \
+		m4_toupper($1)_PATH=$withval; \
+		AC_MSG_NOTICE([Setting m4_toupper($1)_PATH to $withval]); \
 		GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \
 	fi; \
 fi; \
@@ -61,10 +63,48 @@
 	m4_toupper(NO_$1)=; \
 else \
 	m4_toupper(NO_$1)=; \
+	m4_toupper($1)DIR=$withval; \
+	AC_MSG_NOTICE([Setting m4_toupper($1)DIR to $withval]); \
 	GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
 fi \
 ])# GIT_PARSE_WITH
 
+dnl
+dnl GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
+dnl -----------------------------------------
+dnl Similar to AC_CHECK_FUNC, but on systems that do not generate
+dnl warnings for missing prototypes (e.g. FreeBSD when compiling without
+dnl -Wall), it does not work.  By looking for function definition in
+dnl libraries, this problem can be worked around.
+AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[
+  AC_SEARCH_LIBS([$1],,
+  [$2],[$3])
+],[$3])])
+
+dnl
+dnl GIT_STASH_FLAGS(BASEPATH_VAR)
+dnl -----------------------------
+dnl Allow for easy stashing of LDFLAGS and CPPFLAGS before running
+dnl tests that may want to take user settings into account.
+AC_DEFUN([GIT_STASH_FLAGS],[
+if test -n "$1"; then
+   old_CPPFLAGS="$CPPFLAGS"
+   old_LDFLAGS="$LDFLAGS"
+   CPPFLAGS="-I$1/include $CPPFLAGS"
+   LDFLAGS="-L$1/$lib $LDFLAGS"
+fi
+])
+
+dnl
+dnl GIT_UNSTASH_FLAGS(BASEPATH_VAR)
+dnl -----------------------------
+dnl Restore the stashed *FLAGS values.
+AC_DEFUN([GIT_UNSTASH_FLAGS],[
+if test -n "$1"; then
+   CPPFLAGS="$old_CPPFLAGS"
+   LDFLAGS="$old_LDFLAGS"
+fi
+])
 
 ## Site configuration related to programs (before tests)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
@@ -76,9 +116,124 @@
  [if test "$withval" = "no" || test "$withval" = "yes"; then \
 	AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
 else \
+	lib=$withval; \
+	AC_MSG_NOTICE([Setting lib to '$lib']); \
 	GIT_CONF_APPEND_LINE(lib=$withval); \
 fi; \
 ],[])
+
+if test -z "$lib"; then
+   AC_MSG_NOTICE([Setting lib to 'lib' (the default)])
+   lib=lib
+fi
+
+AC_ARG_ENABLE([pthreads],
+ [AS_HELP_STRING([--enable-pthreads=FLAGS],
+  [FLAGS is the value to pass to the compiler to enable POSIX Threads.]
+  [The default if FLAGS is not specified is to try first -pthread]
+  [and then -lpthread.]
+  [--without-pthreads will disable threading.])],
+[
+if test "x$enableval" = "xyes"; then
+   AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads])
+elif test "x$enableval" != "xno"; then
+   PTHREAD_CFLAGS=$enableval
+   AC_MSG_NOTICE([Setting '$PTHREAD_CFLAGS' as the FLAGS to enable POSIX Threads])
+else
+   AC_MSG_NOTICE([POSIX Threads will be disabled.])
+   NO_PTHREADS=YesPlease
+   USER_NOPTHREAD=1
+fi],
+[
+   AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads.])
+])
+
+## Site configuration (override autodetection)
+## --with-PACKAGE[=ARG] and --without-PACKAGE
+AC_MSG_NOTICE([CHECKS for site configuration])
+#
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# 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
+# choice) has very fast version optimized for i586.
+#
+# Define PPC_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for PowerPC.
+#
+# Define ARM_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for ARM.
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# This also implies MOZILLA_SHA1.
+#
+# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(openssl,
+AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
+AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
+GIT_PARSE_WITH(openssl))
+#
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(curl,
+AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)])
+AS_HELP_STRING([],           [ARG can be also prefix for curl library and headers]),
+GIT_PARSE_WITH(curl))
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+#
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+AC_ARG_WITH(expat,
+AS_HELP_STRING([--with-expat],
+[support git-push using http:// and https:// transports via WebDAV (default is YES)])
+AS_HELP_STRING([],            [ARG can be also prefix for expat library and headers]),
+GIT_PARSE_WITH(expat))
+#
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want GIT to link against any libraries
+# installed there.  If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
+#
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want GIT to
+# link against any libraries installed there.  If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_ICONV if your libc does not properly support iconv.
+AC_ARG_WITH(iconv,
+AS_HELP_STRING([--without-iconv],
+[if your architecture doesn't properly support iconv])
+AS_HELP_STRING([--with-iconv=PATH],
+[PATH is prefix for libiconv library and headers])
+AS_HELP_STRING([],
+[used only if you need linking with libiconv]),
+GIT_PARSE_WITH(iconv))
+
+## --enable-FEATURE[=ARG] and --disable-FEATURE
+#
+# Define USE_NSEC below if you want git to care about sub-second file mtimes
+# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
+# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
+# randomly break unless your underlying filesystem supports those sub-second
+# times (my ext3 doesn't).
+#
+# Define USE_STDEV below if you want git to care about the underlying device
+# change being considered an inode change from the update-index perspective.
+
 #
 # Define SHELL_PATH to provide path to shell.
 GIT_ARG_SET_PATH(shell)
@@ -103,6 +258,38 @@
 AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
+# which switch to pass runtime path to dynamic libraries to the linker
+AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
+   SAVE_LDFLAGS="${LDFLAGS}"
+   LDFLAGS="${SAVE_LDFLAGS} -R /"
+   AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_dashr=yes], [git_cv_ld_dashr=no])
+   LDFLAGS="${SAVE_LDFLAGS}"
+])
+if test "$git_cv_ld_dashr" = "yes"; then
+   AC_SUBST(CC_LD_DYNPATH, [-R])
+else
+   AC_CACHE_CHECK([if linker supports -Wl,-rpath,], git_cv_ld_wl_rpath, [
+      SAVE_LDFLAGS="${LDFLAGS}"
+      LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/"
+      AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_wl_rpath=yes], [git_cv_ld_wl_rpath=no])
+      LDFLAGS="${SAVE_LDFLAGS}"
+   ])
+   if test "$git_cv_ld_wl_rpath" = "yes"; then
+      AC_SUBST(CC_LD_DYNPATH, [-Wl,-rpath,])
+   else
+      AC_CACHE_CHECK([if linker supports -rpath], git_cv_ld_rpath, [
+         SAVE_LDFLAGS="${LDFLAGS}"
+         LDFLAGS="${SAVE_LDFLAGS} -rpath /"
+         AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_rpath=yes], [git_cv_ld_rpath=no])
+         LDFLAGS="${SAVE_LDFLAGS}"
+      ])
+      if test "$git_cv_ld_rpath" = "yes"; then
+         AC_SUBST(CC_LD_DYNPATH, [-rpath])
+      else
+         AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+      fi
+   fi
+fi
 #AC_PROG_INSTALL		# needs install-sh or install.sh in sources
 AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
@@ -125,7 +312,7 @@
 AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
 if test -n "$ASCIIDOC"; then
 	AC_MSG_CHECKING([for asciidoc version])
-	asciidoc_version=`$ASCIIDOC --version 2>&1`
+	asciidoc_version=`$ASCIIDOC --version 2>/dev/null`
 	case "${asciidoc_version}" in
 	asciidoc' '8*)
 		ASCIIDOC8=YesPlease
@@ -149,33 +336,57 @@
 #
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+
+GIT_STASH_FLAGS($OPENSSLDIR)
+
 AC_CHECK_LIB([crypto], [SHA1_Init],
 [NEEDS_SSL_WITH_CRYPTO=],
 [AC_CHECK_LIB([ssl], [SHA1_Init],
  [NEEDS_SSL_WITH_CRYPTO=YesPlease
   NEEDS_SSL_WITH_CRYPTO=],
  [NO_OPENSSL=YesPlease])])
+
+GIT_UNSTASH_FLAGS($OPENSSLDIR)
+
 AC_SUBST(NEEDS_SSL_WITH_CRYPTO)
 AC_SUBST(NO_OPENSSL)
+
 #
 # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
+
+GIT_STASH_FLAGS($CURLDIR)
+
 AC_CHECK_LIB([curl], [curl_global_init],
 [NO_CURL=],
 [NO_CURL=YesPlease])
+
+GIT_UNSTASH_FLAGS($CURLDIR)
+
 AC_SUBST(NO_CURL)
+
 #
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
+
+GIT_STASH_FLAGS($EXPATDIR)
+
 AC_CHECK_LIB([expat], [XML_ParserCreate],
 [NO_EXPAT=],
 [NO_EXPAT=YesPlease])
+
+GIT_UNSTASH_FLAGS($EXPATDIR)
+
 AC_SUBST(NO_EXPAT)
+
 #
 # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin and
 # some Solaris installations).
 # Define NO_ICONV if neither libc nor libiconv support iconv.
+
+GIT_STASH_FLAGS($ICONVDIR)
+
 AC_DEFUN([ICONVTEST_SRC], [
 #include <iconv.h>
 
@@ -185,25 +396,46 @@
 	return 0;
 }
 ])
-AC_MSG_CHECKING([for iconv in -lc])
-AC_LINK_IFELSE(ICONVTEST_SRC,
+
+if test -n "$ICONVDIR"; then
+   lib_order="-liconv -lc"
+else
+   lib_order="-lc -liconv"
+fi
+
+NO_ICONV=YesPlease
+
+for l in $lib_order; do
+    if test "$l" = "-liconv"; then
+       NEEDS_LIBICONV=YesPlease
+    else
+       NEEDS_LIBICONV=
+    fi
+
+    old_LIBS="$LIBS"
+    LIBS="$LIBS $l"
+    AC_MSG_CHECKING([for iconv in $l])
+    AC_LINK_IFELSE(ICONVTEST_SRC,
 	[AC_MSG_RESULT([yes])
-	NEEDS_LIBICONV=],
-	[AC_MSG_RESULT([no])
-	old_LIBS="$LIBS"
-	LIBS="$LIBS -liconv"
-	AC_MSG_CHECKING([for iconv in -liconv])
-	AC_LINK_IFELSE(ICONVTEST_SRC,
-		[AC_MSG_RESULT([yes])
-		NEEDS_LIBICONV=YesPlease],
-		[AC_MSG_RESULT([no])
-		NO_ICONV=YesPlease])
-	LIBS="$old_LIBS"])
+	NO_ICONV=
+	break],
+	[AC_MSG_RESULT([no])])
+    LIBS="$old_LIBS"
+done
+
+#in case of break
+LIBS="$old_LIBS"
+
+GIT_UNSTASH_FLAGS($ICONVDIR)
+
 AC_SUBST(NEEDS_LIBICONV)
 AC_SUBST(NO_ICONV)
-test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv"
+
 #
 # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
+
+GIT_STASH_FLAGS($ZLIB_PATH)
+
 AC_DEFUN([ZLIBTEST_SRC], [
 #include <zlib.h>
 
@@ -221,7 +453,11 @@
 	[AC_MSG_RESULT([no])
 	NO_DEFLATE_BOUND=yes])
 LIBS="$old_LIBS"
+
+GIT_UNSTASH_FLAGS($ZLIB_PATH)
+
 AC_SUBST(NO_DEFLATE_BOUND)
+
 #
 # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
 # Patrick Mauritz).
@@ -255,13 +491,18 @@
 	return 0;
 }
 ]])
+
+GIT_STASH_FLAGS($ICONVDIR)
+
 AC_MSG_CHECKING([for old iconv()])
 AC_COMPILE_IFELSE(OLDICONVTEST_SRC,
 	[AC_MSG_RESULT([no])],
 	[AC_MSG_RESULT([yes])
 	OLD_ICONV=UnfortunatelyYes])
-AC_SUBST(OLD_ICONV)
 
+GIT_UNSTASH_FLAGS($ICONVDIR)
+
+AC_SUBST(OLD_ICONV)
 
 ## Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
@@ -293,7 +534,7 @@
 #
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 AC_CHECK_TYPE([struct addrinfo],[
- AC_CHECK_FUNC([getaddrinfo],
+ GIT_CHECK_FUNC([getaddrinfo],
   [NO_IPV6=],
   [NO_IPV6=YesPlease])
 ],[NO_IPV6=YesPlease],[
@@ -387,43 +628,51 @@
 AC_MSG_NOTICE([CHECKS for library functions])
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
-AC_CHECK_FUNC(strcasestr,
+GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
 [NO_STRCASESTR=YesPlease])
 AC_SUBST(NO_STRCASESTR)
 #
 # Define NO_MEMMEM if you don't have memmem.
-AC_CHECK_FUNC(memmem,
+GIT_CHECK_FUNC(memmem,
 [NO_MEMMEM=],
 [NO_MEMMEM=YesPlease])
 AC_SUBST(NO_MEMMEM)
 #
 # Define NO_STRLCPY if you don't have strlcpy.
-AC_CHECK_FUNC(strlcpy,
+GIT_CHECK_FUNC(strlcpy,
 [NO_STRLCPY=],
 [NO_STRLCPY=YesPlease])
 AC_SUBST(NO_STRLCPY)
 #
+# Define NO_UINTMAX_T if your platform does not have uintmax_t
+AC_CHECK_TYPE(uintmax_t,
+[NO_UINTMAX_T=],
+[NO_UINTMAX_T=YesPlease],[
+#include <inttypes.h>
+])
+AC_SUBST(NO_UINTMAX_T)
+#
 # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
-AC_CHECK_FUNC(strtoumax,
+GIT_CHECK_FUNC(strtoumax,
 [NO_STRTOUMAX=],
 [NO_STRTOUMAX=YesPlease])
 AC_SUBST(NO_STRTOUMAX)
 #
 # Define NO_SETENV if you don't have setenv in the C library.
-AC_CHECK_FUNC(setenv,
+GIT_CHECK_FUNC(setenv,
 [NO_SETENV=],
 [NO_SETENV=YesPlease])
 AC_SUBST(NO_SETENV)
 #
 # Define NO_UNSETENV if you don't have unsetenv in the C library.
-AC_CHECK_FUNC(unsetenv,
+GIT_CHECK_FUNC(unsetenv,
 [NO_UNSETENV=],
 [NO_UNSETENV=YesPlease])
 AC_SUBST(NO_UNSETENV)
 #
 # Define NO_MKDTEMP if you don't have mkdtemp in the C library.
-AC_CHECK_FUNC(mkdtemp,
+GIT_CHECK_FUNC(mkdtemp,
 [NO_MKDTEMP=],
 [NO_MKDTEMP=YesPlease])
 AC_SUBST(NO_MKDTEMP)
@@ -439,93 +688,69 @@
 #
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
+#
+# Define NO_PTHREADS if we do not have pthreads
+#
+# Define PTHREAD_LIBS to the linker flag used for Pthread support and define
+# THREADED_DELTA_SEARCH if Pthreads are available.
+AC_DEFUN([PTHREADTEST_SRC], [
+#include <pthread.h>
 
-## Site configuration (override autodetection)
-## --with-PACKAGE[=ARG] and --without-PACKAGE
-AC_MSG_NOTICE([CHECKS for site configuration])
-#
-# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
-# tests.  These tests take up a significant amount of the total test time
-# but are not needed unless you plan to talk to SVN repos.
-#
-# 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
-# choice) has very fast version optimized for i586.
-#
-# Define PPC_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for PowerPC.
-#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
-# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
-#
-# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-AC_ARG_WITH(openssl,
-AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
-AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
-GIT_PARSE_WITH(openssl))
-#
-# Define NO_CURL if you do not have curl installed.  git-http-pull and
-# git-http-push are not built, and you cannot use http:// and https://
-# transports.
-#
-# Define CURLDIR=/foo/bar if your curl header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-AC_ARG_WITH(curl,
-AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)])
-AS_HELP_STRING([],           [ARG can be also prefix for curl library and headers]),
-GIT_PARSE_WITH(curl))
-#
-# Define NO_EXPAT if you do not have expat installed.  git-http-push is
-# not built, and you cannot push using http:// and https:// transports.
-#
-# Define EXPATDIR=/foo/bar if your expat header and library files are in
-# /foo/bar/include and /foo/bar/lib directories.
-AC_ARG_WITH(expat,
-AS_HELP_STRING([--with-expat],
-[support git-push using http:// and https:// transports via WebDAV (default is YES)])
-AS_HELP_STRING([],            [ARG can be also prefix for expat library and headers]),
-GIT_PARSE_WITH(expat))
-#
-# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
-# installed in /sw, but don't want GIT to link against any libraries
-# installed there.  If defined you may specify your own (or Fink's)
-# include directories and library directories by defining CFLAGS
-# and LDFLAGS appropriately.
-#
-# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
-# have DarwinPorts installed in /opt/local, but don't want GIT to
-# link against any libraries installed there.  If defined you may
-# specify your own (or DarwinPort's) include directories and
-# library directories by defining CFLAGS and LDFLAGS appropriately.
-#
-# Define NO_MMAP if you want to avoid mmap.
-#
-# Define NO_ICONV if your libc does not properly support iconv.
-AC_ARG_WITH(iconv,
-AS_HELP_STRING([--without-iconv],
-[if your architecture doesn't properly support iconv])
-AS_HELP_STRING([--with-iconv=PATH],
-[PATH is prefix for libiconv library and headers])
-AS_HELP_STRING([],
-[used only if you need linking with libiconv]),
-GIT_PARSE_WITH(iconv))
+int main(void)
+{
+	pthread_mutex_t test_mutex;
+	return (0);
+}
+])
 
-## --enable-FEATURE[=ARG] and --disable-FEATURE
-#
-# Define USE_NSEC below if you want git to care about sub-second file mtimes
-# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
-# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
-# randomly break unless your underlying filesystem supports those sub-second
-# times (my ext3 doesn't).
-#
-# Define USE_STDEV below if you want git to care about the underlying device
-# change being considered an inode change from the update-index perspective.
+dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM(
+dnl   [[#include <pthread.h>]],
+dnl   [[pthread_mutex_t test_mutex;]]
+dnl )])
 
+NO_PTHREADS=UnfortunatelyYes
+THREADED_DELTA_SEARCH=
+PTHREAD_LIBS=
+
+if test -n "$USER_NOPTHREAD"; then
+   AC_MSG_NOTICE([Skipping POSIX Threads at user request.])
+# handle these separately since PTHREAD_CFLAGS could be '-lpthreads
+# -D_REENTRANT' or some such.
+elif test -z "$PTHREAD_CFLAGS"; then
+  for opt in -pthread -lpthread; do
+     old_CFLAGS="$CFLAGS"
+     CFLAGS="$opt $CFLAGS"
+     AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
+     AC_LINK_IFELSE(PTHREADTEST_SRC,
+	[AC_MSG_RESULT([yes])
+		NO_PTHREADS=
+		PTHREAD_LIBS="$opt"
+		THREADED_DELTA_SEARCH=YesPlease
+		break
+	],
+	[AC_MSG_RESULT([no])])
+      CFLAGS="$old_CFLAGS"
+  done
+else
+  old_CFLAGS="$CFLAGS"
+  CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
+  AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS'])
+  AC_LINK_IFELSE(PTHREADTEST_SRC,
+	[AC_MSG_RESULT([yes])
+		NO_PTHREADS=
+		PTHREAD_LIBS="$PTHREAD_CFLAGS"
+		THREADED_DELTA_SEARCH=YesPlease
+	],
+	[AC_MSG_RESULT([no])])
+
+  CFLAGS="$old_CFLAGS"
+fi
+
+CFLAGS="$old_CFLAGS"
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(NO_PTHREADS)
+AC_SUBST(THREADED_DELTA_SEARCH)
 
 ## Output files
 AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"])
diff --git a/connect.c b/connect.c
index dd96f8e..7636bf9 100644
--- a/connect.c
+++ b/connect.c
@@ -41,12 +41,20 @@
 	return check_ref(ref->name, strlen(ref->name), flags);
 }
 
+static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1)
+{
+	ALLOC_GROW(extra->array, extra->nr + 1, extra->alloc);
+	hashcpy(&(extra->array[extra->nr][0]), sha1);
+	extra->nr++;
+}
+
 /*
  * Read all the refs from the other end
  */
 struct ref **get_remote_heads(int in, struct ref **list,
 			      int nr_match, char **match,
-			      unsigned int flags)
+			      unsigned int flags,
+			      struct extra_have_objects *extra_have)
 {
 	*list = NULL;
 	for (;;) {
@@ -62,6 +70,9 @@
 		if (buffer[len-1] == '\n')
 			buffer[--len] = 0;
 
+		if (len > 4 && !prefixcmp(buffer, "ERR "))
+			die("remote error: %s", buffer + 4);
+
 		if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
 			die("protocol error: expected sha/ref, got '%s'", buffer);
 		name = buffer + 41;
@@ -72,13 +83,18 @@
 			server_capabilities = xstrdup(name + name_len + 1);
 		}
 
+		if (extra_have &&
+		    name_len == 5 && !memcmp(".have", name, 5)) {
+			add_extra_have(extra_have, old_sha1);
+			continue;
+		}
+
 		if (!check_ref(name, name_len, flags))
 			continue;
 		if (nr_match && !path_match(name, nr_match, match))
 			continue;
-		ref = alloc_ref(name_len + 1);
+		ref = alloc_ref(buffer + 41);
 		hashcpy(ref->old_sha1, old_sha1);
-		memcpy(ref->name, buffer + 41, name_len + 1);
 		*list = ref;
 		list = &ref->next;
 	}
@@ -299,7 +315,7 @@
 		/* Not numeric */
 		struct servent *se = getservbyname(port,"tcp");
 		if ( !se )
-			die("Unknown port %s\n", port);
+			die("Unknown port %s", port);
 		nport = se->s_port;
 	}
 
@@ -357,8 +373,6 @@
 
 
 static char *git_proxy_command;
-static const char *rhost_name;
-static int rhost_len;
 
 static int git_proxy_command_options(const char *var, const char *value,
 		void *cb)
@@ -367,6 +381,8 @@
 		const char *for_pos;
 		int matchlen = -1;
 		int hostlen;
+		const char *rhost_name = cb;
+		int rhost_len = strlen(rhost_name);
 
 		if (git_proxy_command)
 			return 0;
@@ -410,11 +426,8 @@
 
 static int git_use_proxy(const char *host)
 {
-	rhost_name = host;
-	rhost_len = strlen(host);
 	git_proxy_command = getenv("GIT_PROXY_COMMAND");
-	git_config(git_proxy_command_options, NULL);
-	rhost_name = NULL;
+	git_config(git_proxy_command_options, (void*)host);
 	return (git_proxy_command && *git_proxy_command);
 }
 
@@ -464,8 +477,8 @@
 	char *p = strchr(host, ':');
 
 	if (p) {
-		strtol(p+1, &end, 10);
-		if (*end == '\0') {
+		long port = strtol(p + 1, &end, 10);
+		if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) {
 			*p = '\0';
 			return p+1;
 		}
@@ -491,7 +504,7 @@
 				  const char *prog, int flags)
 {
 	char *url = xstrdup(url_orig);
-	char *host, *path = url;
+	char *host, *path;
 	char *end;
 	int c;
 	struct child_process *conn;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8fc01fb..1a90cb8 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1,3 +1,4 @@
+#!bash
 #
 # bash completion support for core Git.
 #
@@ -33,6 +34,12 @@
 #       are currently in a git repository.  The %s token will be
 #       the name of the current branch.
 #
+#       In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
+#       value, unstaged (*) and staged (+) changes will be shown next
+#       to the branch name.  You can configure this per-repository
+#       with the bash.showDirtyState variable, which defaults to true
+#       once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#
 # To submit patches:
 #
 #    *) Read Documentation/SubmittingPatches
@@ -50,10 +57,12 @@
 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
 esac
 
+# __gitdir accepts 0 or 1 arguments (i.e., location)
+# returns location of .git repo
 __gitdir ()
 {
-	if [ -z "$1" ]; then
-		if [ -n "$__git_dir" ]; then
+	if [ -z "${1-}" ]; then
+		if [ -n "${__git_dir-}" ]; then
 			echo "$__git_dir"
 		elif [ -d .git ]; then
 			echo .git
@@ -67,58 +76,81 @@
 	fi
 }
 
+# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
+# returns text to add to bash PS1 prompt (includes branch name)
 __git_ps1 ()
 {
-	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local g="$(__gitdir)"
 	if [ -n "$g" ]; then
 		local r
 		local b
-		if [ -d "$g/rebase-apply" ]
-		then
-			if test -f "$g/rebase-apply/rebasing"
-			then
+		if [ -d "$g/rebase-apply" ]; then
+			if [ -f "$g/rebase-apply/rebasing" ]; then
 				r="|REBASE"
-			elif test -f "$g/rebase-apply/applying"
-			then
+		elif [ -f "$g/rebase-apply/applying" ]; then
 				r="|AM"
 			else
 				r="|AM/REBASE"
 			fi
 			b="$(git symbolic-ref HEAD 2>/dev/null)"
-		elif [ -f "$g/rebase-merge/interactive" ]
-		then
+		elif [ -f "$g/rebase-merge/interactive" ]; then
 			r="|REBASE-i"
 			b="$(cat "$g/rebase-merge/head-name")"
-		elif [ -d "$g/rebase-merge" ]
-		then
+		elif [ -d "$g/rebase-merge" ]; then
 			r="|REBASE-m"
 			b="$(cat "$g/rebase-merge/head-name")"
-		elif [ -f "$g/MERGE_HEAD" ]
-		then
+		elif [ -f "$g/MERGE_HEAD" ]; then
 			r="|MERGING"
 			b="$(git symbolic-ref HEAD 2>/dev/null)"
 		else
-			if [ -f "$g/BISECT_LOG" ]
-			then
+			if [ -f "$g/BISECT_LOG" ]; then
 				r="|BISECTING"
 			fi
-			if ! b="$(git symbolic-ref HEAD 2>/dev/null)"
-			then
-				if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
-				then
-					b="$(cut -c1-7 "$g/HEAD")..."
+			if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then
+				if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then
+					if [ -r "$g/HEAD" ]; then
+						b="$(cut -c1-7 "$g/HEAD")..."
+					fi
 				fi
 			fi
 		fi
 
-		if [ -n "$1" ]; then
-			printf "$1" "${b##refs/heads/}$r"
-		else
-			printf " (%s)" "${b##refs/heads/}$r"
+		local w
+		local i
+		local c
+
+		if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+			if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then
+				c="BARE:"
+			else
+				b="GIT_DIR!"
+			fi
+		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+					git diff --no-ext-diff --ignore-submodules \
+						--quiet --exit-code || w="*"
+					if git rev-parse --quiet --verify HEAD >/dev/null; then
+						git diff-index --cached --quiet \
+							--ignore-submodules HEAD -- || i="+"
+					else
+						i="#"
+					fi
+				fi
+			fi
+		fi
+
+		if [ -n "$b" ]; then
+			if [ -n "${1-}" ]; then
+				printf "$1" "$c${b##refs/heads/}$w$i$r"
+			else
+				printf " (%s)" "$c${b##refs/heads/}$w$i$r"
+			fi
 		fi
 	fi
 }
 
+# __gitcomp_1 requires 2 arguments
 __gitcomp_1 ()
 {
 	local c IFS=' '$'\t'$'\n'
@@ -131,6 +163,8 @@
 	done
 }
 
+# __gitcomp accepts 1, 2, 3, or 4 arguments
+# generates completion reply with compgen
 __gitcomp ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -143,25 +177,23 @@
 		;;
 	*)
 		local IFS=$'\n'
-		COMPREPLY=($(compgen -P "$2" \
-			-W "$(__gitcomp_1 "$1" "$4")" \
+		COMPREPLY=($(compgen -P "${2-}" \
+			-W "$(__gitcomp_1 "${1-}" "${4-}")" \
 			-- "$cur"))
 		;;
 	esac
 }
 
+# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
 __git_heads ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "$1")"
+	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
 	if [ -d "$dir" ]; then
-		for i in $(git --git-dir="$dir" \
-			for-each-ref --format='%(refname)' \
-			refs/heads ); do
-			echo "${i#refs/heads/}"
-		done
+		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+			refs/heads
 		return
 	fi
-	for i in $(git ls-remote "$1" 2>/dev/null); do
+	for i in $(git ls-remote "${1-}" 2>/dev/null); do
 		case "$is_hash,$i" in
 		y,*) is_hash=n ;;
 		n,*^{}) is_hash=y ;;
@@ -171,18 +203,16 @@
 	done
 }
 
+# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
 __git_tags ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "$1")"
+	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
 	if [ -d "$dir" ]; then
-		for i in $(git --git-dir="$dir" \
-			for-each-ref --format='%(refname)' \
-			refs/tags ); do
-			echo "${i#refs/tags/}"
-		done
+		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+			refs/tags
 		return
 	fi
-	for i in $(git ls-remote "$1" 2>/dev/null); do
+	for i in $(git ls-remote "${1-}" 2>/dev/null); do
 		case "$is_hash,$i" in
 		y,*) is_hash=n ;;
 		n,*^{}) is_hash=y ;;
@@ -192,21 +222,25 @@
 	done
 }
 
+# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
 __git_refs ()
 {
-	local cmd i is_hash=y dir="$(__gitdir "$1")"
+	local i is_hash=y dir="$(__gitdir "${1-}")"
+	local cur="${COMP_WORDS[COMP_CWORD]}" format refs
 	if [ -d "$dir" ]; then
-		if [ -e "$dir/HEAD" ]; then echo HEAD; fi
-		for i in $(git --git-dir="$dir" \
-			for-each-ref --format='%(refname)' \
-			refs/tags refs/heads refs/remotes); do
-			case "$i" in
-				refs/tags/*)    echo "${i#refs/tags/}" ;;
-				refs/heads/*)   echo "${i#refs/heads/}" ;;
-				refs/remotes/*) echo "${i#refs/remotes/}" ;;
-				*)              echo "$i" ;;
-			esac
-		done
+		case "$cur" in
+		refs|refs/*)
+			format="refname"
+			refs="${cur%/*}"
+			;;
+		*)
+			if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+			format="refname:short"
+			refs="refs/tags refs/heads refs/remotes"
+			;;
+		esac
+		git --git-dir="$dir" for-each-ref --format="%($format)" \
+			$refs
 		return
 	fi
 	for i in $(git ls-remote "$dir" 2>/dev/null); do
@@ -221,6 +255,7 @@
 	done
 }
 
+# __git_refs2 requires 1 argument (to pass to __git_refs)
 __git_refs2 ()
 {
 	local i
@@ -229,6 +264,7 @@
 	done
 }
 
+# __git_refs_remotes requires 1 argument (to pass to ls-remote)
 __git_refs_remotes ()
 {
 	local cmd i is_hash=y
@@ -267,7 +303,7 @@
 
 __git_merge_strategies ()
 {
-	if [ -n "$__git_merge_strategylist" ]; then
+	if [ -n "${__git_merge_strategylist-}" ]; then
 		echo "$__git_merge_strategylist"
 		return
 	fi
@@ -351,9 +387,88 @@
 	esac
 }
 
+__git_complete_remote_or_refspec ()
+{
+	local cmd="${COMP_WORDS[1]}"
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		--all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+		-*) ;;
+		*) remote="$i"; break ;;
+		esac
+		c=$((++c))
+	done
+	if [ -z "$remote" ]; then
+		__gitcomp "$(__git_remotes)"
+		return
+	fi
+	if [ $no_complete_refspec = 1 ]; then
+		COMPREPLY=()
+		return
+	fi
+	[ "$remote" = "." ] && remote=
+	case "$cur" in
+	*:*)
+		case "$COMP_WORDBREAKS" in
+		*:*) : great ;;
+		*)   pfx="${cur%%:*}:" ;;
+		esac
+		cur="${cur#*:}"
+		lhs=0
+		;;
+	+*)
+		pfx="+"
+		cur="${cur#+}"
+		;;
+	esac
+	case "$cmd" in
+	fetch)
+		if [ $lhs = 1 ]; then
+			__gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+		else
+			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		fi
+		;;
+	pull)
+		if [ $lhs = 1 ]; then
+			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+		else
+			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		fi
+		;;
+	push)
+		if [ $lhs = 1 ]; then
+			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		else
+			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+		fi
+		;;
+	esac
+}
+
+__git_complete_strategy ()
+{
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-s|--strategy)
+		__gitcomp "$(__git_merge_strategies)"
+		return 0
+	esac
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--strategy=*)
+		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+		return 0
+		;;
+	esac
+	return 1
+}
+
 __git_all_commands ()
 {
-	if [ -n "$__git_all_commandlist" ]; then
+	if [ -n "${__git_all_commandlist-}" ]; then
 		echo "$__git_all_commandlist"
 		return
 	fi
@@ -371,7 +486,7 @@
 
 __git_porcelain_commands ()
 {
-	if [ -n "$__git_porcelain_commandlist" ]; then
+	if [ -n "${__git_porcelain_commandlist-}" ]; then
 		echo "$__git_porcelain_commandlist"
 		return
 	fi
@@ -473,6 +588,7 @@
 	done
 }
 
+# __git_aliased_command requires 1 argument
 __git_aliased_command ()
 {
 	local word cmdline=$(git --git-dir="$(__gitdir)" \
@@ -485,6 +601,7 @@
 	done
 }
 
+# __git_find_subcommand requires 1 argument
 __git_find_subcommand ()
 {
 	local word subcommand c=1
@@ -529,7 +646,8 @@
 		;;
 	--*)
 		__gitcomp "
-			--signoff --utf8 --binary --3way --interactive
+			--3way --committer-date-is-author-date --ignore-date
+			--interactive --keep --no-utf8 --signoff --utf8
 			--whitespace=
 			"
 		return
@@ -566,7 +684,7 @@
 	--*)
 		__gitcomp "
 			--interactive --refresh --patch --update --dry-run
-			--ignore-errors
+			--ignore-errors --intent-to-add
 			"
 		return
 	esac
@@ -631,7 +749,6 @@
 	done
 
 	case "${COMP_WORDS[COMP_CWORD]}" in
-	--*=*)	COMPREPLY=() ;;
 	--*)
 		__gitcomp "
 			--color --no-color --verbose --abbrev= --no-abbrev
@@ -762,6 +879,20 @@
 	__gitcomp "$(__git_refs)"
 }
 
+__git_diff_common_options="--stat --numstat --shortstat --summary
+			--patch-with-stat --name-only --name-status --color
+			--no-color --color-words --no-renames --check
+			--full-index --binary --abbrev --diff-filter=
+			--find-copies-harder
+			--text --ignore-space-at-eol --ignore-space-change
+			--ignore-all-space --exit-code --quiet --ext-diff
+			--no-ext-diff
+			--no-prefix --src-prefix= --dst-prefix=
+			--inter-hunk-context=
+			--patience
+			--raw
+"
+
 _git_diff ()
 {
 	__git_has_doubledash && return
@@ -769,16 +900,9 @@
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
-		__gitcomp "--cached --stat --numstat --shortstat --summary
-			--patch-with-stat --name-only --name-status --color
-			--no-color --color-words --no-renames --check
-			--full-index --binary --abbrev --diff-filter=
-			--find-copies-harder --pickaxe-all --pickaxe-regex
-			--text --ignore-space-at-eol --ignore-space-change
-			--ignore-all-space --exit-code --quiet --ext-diff
-			--no-ext-diff
-			--no-prefix --src-prefix= --dst-prefix=
+		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
 			--base --ours --theirs
+			$__git_diff_common_options
 			"
 		return
 		;;
@@ -786,46 +910,68 @@
 	__git_complete_file
 }
 
+__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
+			tkdiff vimdiff gvimdiff xxdiff
+"
+
+_git_difftool ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--tool=*)
+		__gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
+		return
+		;;
+	--*)
+		__gitcomp "--tool="
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+__git_fetch_options="
+	--quiet --verbose --append --upload-pack --force --keep --depth=
+	--tags --no-tags
+"
+
 _git_fetch ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-
-	if [ "$COMP_CWORD" = 2 ]; then
-		__gitcomp "$(__git_remotes)"
-	else
-		case "$cur" in
-		*:*)
-			local pfx=""
-			case "$COMP_WORDBREAKS" in
-			*:*) : great ;;
-			*)   pfx="${cur%%:*}:" ;;
-			esac
-			__gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
-			;;
-		*)
-			__gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
-			;;
-		esac
-	fi
+	case "$cur" in
+	--*)
+		__gitcomp "$__git_fetch_options"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
 }
 
 _git_format_patch ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
+	--thread=*)
+		__gitcomp "
+			deep shallow
+			" "" "${cur##--thread=}"
+		return
+		;;
 	--*)
 		__gitcomp "
-			--stdout --attach --thread
+			--stdout --attach --no-attach --thread --thread=
 			--output-directory
 			--numbered --start-number
 			--numbered-files
 			--keep-subject
 			--signoff
-			--in-reply-to=
+			--in-reply-to= --cc=
 			--full-index --binary
 			--not --all
 			--cover-letter
 			--no-prefix --src-prefix= --dst-prefix=
+			--inline --suffix= --ignore-if-in-upstream
+			--subject-prefix=
 			"
 		return
 		;;
@@ -833,6 +979,21 @@
 	__git_complete_revlist
 }
 
+_git_fsck ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--tags --root --unreachable --cache --no-reflogs --full
+			--strict --verbose --lost-found
+			"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
 _git_gc ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -881,6 +1042,7 @@
 		attributes cli core-tutorial cvs-migration
 		diffcore gitk glossary hooks ignore modules
 		repository-layout tutorial tutorial-2
+		workflows
 		"
 }
 
@@ -932,6 +1094,29 @@
 	__git_complete_file
 }
 
+# Options that go well for log, shortlog and gitk
+__git_log_common_options="
+	--not --all
+	--branches --tags --remotes
+	--first-parent --no-merges
+	--max-count=
+	--max-age= --since= --after=
+	--min-age= --until= --before=
+"
+# Options that go well for log and gitk (not shortlog)
+__git_log_gitk_options="
+	--dense --sparse --full-history
+	--simplify-merges --simplify-by-decoration
+	--left-right
+"
+# Options that go well for log and shortlog (not gitk)
+__git_log_shortlog_options="
+	--author= --committer= --grep=
+	--all-match
+"
+
+__git_log_pretty_formats="oneline short medium full fuller email raw format:"
+
 _git_log ()
 {
 	__git_has_doubledash && return
@@ -944,11 +1129,15 @@
 	fi
 	case "$cur" in
 	--pretty=*)
-		__gitcomp "
-			oneline short medium full fuller email raw
+		__gitcomp "$__git_log_pretty_formats
 			" "" "${cur##--pretty=}"
 		return
 		;;
+	--format=*)
+		__gitcomp "$__git_log_pretty_formats
+			" "" "${cur##--format=}"
+		return
+		;;
 	--date=*)
 		__gitcomp "
 			relative iso8601 rfc2822 short local default
@@ -957,23 +1146,22 @@
 		;;
 	--*)
 		__gitcomp "
-			--max-count= --max-age= --since= --after=
-			--min-age= --before= --until=
+			$__git_log_common_options
+			$__git_log_shortlog_options
+			$__git_log_gitk_options
 			--root --topo-order --date-order --reverse
-			--no-merges --follow
+			--follow
 			--abbrev-commit --abbrev=
 			--relative-date --date=
-			--author= --committer= --grep=
-			--all-match
-			--pretty= --name-status --name-only --raw
-			--not --all
-			--left-right --cherry-pick
+			--pretty= --format= --oneline
+			--cherry-pick
 			--graph
-			--stat --numstat --shortstat
-			--decorate --diff-filter=
-			--color-words --walk-reflogs
-			--parents --children --full-history
+			--decorate
+			--walk-reflogs
+			--parents --children
 			$merge
+			$__git_diff_common_options
+			--pickaxe-all --pickaxe-regex
 			"
 		return
 		;;
@@ -981,23 +1169,19 @@
 	__git_complete_revlist
 }
 
+__git_merge_options="
+	--no-commit --no-stat --log --no-log --squash --strategy
+	--commit --stat --no-squash --ff --no-ff
+"
+
 _git_merge ()
 {
+	__git_complete_strategy && return
+
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
-	-s|--strategy)
-		__gitcomp "$(__git_merge_strategies)"
-		return
-	esac
 	case "$cur" in
-	--strategy=*)
-		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-		return
-		;;
 	--*)
-		__gitcomp "
-			--no-commit --no-stat --log --no-log --squash --strategy
-			"
+		__gitcomp "$__git_merge_options"
 		return
 	esac
 	__gitcomp "$(__git_refs)"
@@ -1008,10 +1192,7 @@
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--tool=*)
-		__gitcomp "
-			kdiff3 tkdiff meld xxdiff emerge
-			vimdiff gvimdiff ecmerge opendiff
-			" "" "${cur##--tool=}"
+		__gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
 		return
 		;;
 	--*)
@@ -1046,40 +1227,44 @@
 
 _git_pull ()
 {
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	__git_complete_strategy && return
 
-	if [ "$COMP_CWORD" = 2 ]; then
-		__gitcomp "$(__git_remotes)"
-	else
-		__gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
-	fi
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--rebase --no-rebase
+			$__git_merge_options
+			$__git_fetch_options
+		"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
 }
 
 _git_push ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-
-	if [ "$COMP_CWORD" = 2 ]; then
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	--repo)
 		__gitcomp "$(__git_remotes)"
-	else
-		case "$cur" in
-		*:*)
-			local pfx=""
-			case "$COMP_WORDBREAKS" in
-			*:*) : great ;;
-			*)   pfx="${cur%%:*}:" ;;
-			esac
-
-			__gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
-			;;
-		+*)
-			__gitcomp "$(__git_refs)" + "${cur#+}"
-			;;
-		*)
-			__gitcomp "$(__git_refs)"
-			;;
-		esac
-	fi
+		return
+	esac
+	case "$cur" in
+	--repo=*)
+		__gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--all --mirror --tags --dry-run --force --verbose
+			--receive-pack= --repo=
+		"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
 }
 
 _git_rebase ()
@@ -1089,16 +1274,8 @@
 		__gitcomp "--continue --skip --abort"
 		return
 	fi
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
-	-s|--strategy)
-		__gitcomp "$(__git_merge_strategies)"
-		return
-	esac
+	__git_complete_strategy && return
 	case "$cur" in
-	--strategy=*)
-		__gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
-		return
-		;;
 	--*)
 		__gitcomp "--onto --merge --strategy --interactive"
 		return
@@ -1111,13 +1288,14 @@
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
-		__gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose
-			--dry-run --envelope-sender --from --identity
+		__gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
+			--compose --dry-run --envelope-sender --from --identity
 			--in-reply-to --no-chain-reply-to --no-signed-off-by-cc
 			--no-suppress-from --no-thread --quiet
 			--signed-off-by-cc --smtp-pass --smtp-server
 			--smtp-server-port --smtp-ssl --smtp-user --subject
-			--suppress-cc --suppress-from --thread --to"
+			--suppress-cc --suppress-from --thread --to
+			--validate --no-validate"
 		return
 		;;
 	esac
@@ -1155,13 +1333,17 @@
 		__gitcomp "$(__git_merge_strategies)"
 		return
 		;;
-	color.branch|color.diff|color.status)
+	color.branch|color.diff|color.interactive|color.status|color.ui)
 		__gitcomp "always never auto"
 		return
 		;;
+	color.pager)
+		__gitcomp "false true"
+		return
+		;;
 	color.*.*)
 		__gitcomp "
-			black red green yellow blue magenta cyan white
+			normal black red green yellow blue magenta cyan white
 			bold dim ul blink reverse
 			"
 		return
@@ -1185,7 +1367,7 @@
 	branch.*.*)
 		local pfx="${cur%.*}."
 		cur="${cur##*.}"
-		__gitcomp "remote merge" "$pfx" "$cur"
+		__gitcomp "remote merge mergeoptions" "$pfx" "$cur"
 		return
 		;;
 	branch.*)
@@ -1198,7 +1380,7 @@
 		local pfx="${cur%.*}."
 		cur="${cur##*.}"
 		__gitcomp "
-			url fetch push skipDefaultUpdate
+			url proxy fetch push mirror skipDefaultUpdate
 			receivepack uploadpack tagopt
 			" "$pfx" "$cur"
 		return
@@ -1212,92 +1394,168 @@
 	esac
 	__gitcomp "
 		apply.whitespace
-		core.fileMode
-		core.gitProxy
-		core.ignoreStat
-		core.preferSymlinkRefs
-		core.logAllRefUpdates
-		core.loosecompression
-		core.repositoryFormatVersion
-		core.sharedRepository
-		core.warnAmbiguousRefs
-		core.compression
-		core.packedGitWindowSize
-		core.packedGitLimit
+		branch.autosetupmerge
+		branch.autosetuprebase
 		clean.requireForce
 		color.branch
 		color.branch.current
 		color.branch.local
-		color.branch.remote
 		color.branch.plain
+		color.branch.remote
 		color.diff
-		color.diff.plain
-		color.diff.meta
-		color.diff.frag
-		color.diff.old
-		color.diff.new
 		color.diff.commit
+		color.diff.frag
+		color.diff.meta
+		color.diff.new
+		color.diff.old
+		color.diff.plain
 		color.diff.whitespace
+		color.interactive
+		color.interactive.header
+		color.interactive.help
+		color.interactive.prompt
 		color.pager
 		color.status
-		color.status.header
 		color.status.added
 		color.status.changed
+		color.status.header
+		color.status.nobranch
 		color.status.untracked
+		color.status.updated
+		color.ui
+		commit.template
+		core.autocrlf
+		core.bare
+		core.compression
+		core.deltaBaseCacheLimit
+		core.editor
+		core.excludesfile
+		core.fileMode
+		core.fsyncobjectfiles
+		core.gitProxy
+		core.ignoreCygwinFSTricks
+		core.ignoreStat
+		core.logAllRefUpdates
+		core.loosecompression
+		core.packedGitLimit
+		core.packedGitWindowSize
+		core.pager
+		core.preferSymlinkRefs
+		core.preloadindex
+		core.quotepath
+		core.repositoryFormatVersion
+		core.safecrlf
+		core.sharedRepository
+		core.symlinks
+		core.trustctime
+		core.warnAmbiguousRefs
+		core.whitespace
+		core.worktree
+		diff.autorefreshindex
+		diff.external
+		diff.mnemonicprefix
 		diff.renameLimit
+		diff.renameLimit.
 		diff.renames
 		fetch.unpackLimit
 		format.headers
-		format.subjectprefix
-		gitcvs.enabled
-		gitcvs.logfile
-		gitcvs.allbinary
-		gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass
-		gitcvs.dbtablenameprefix
+		format.numbered
+		format.pretty
+		format.suffix
+		gc.aggressiveWindow
+		gc.auto
+		gc.autopacklimit
 		gc.packrefs
+		gc.pruneexpire
 		gc.reflogexpire
 		gc.reflogexpireunreachable
 		gc.rerereresolved
 		gc.rerereunresolved
-		http.sslVerify
-		http.sslCert
-		http.sslKey
-		http.sslCAInfo
-		http.sslCAPath
-		http.maxRequests
+		gitcvs.allbinary
+		gitcvs.dbTableNamePrefix
+		gitcvs.dbdriver
+		gitcvs.dbname
+		gitcvs.dbpass
+		gitcvs.dbuser
+		gitcvs.enabled
+		gitcvs.logfile
+		gitcvs.usecrlfattr
+		gui.blamehistoryctx
+		gui.commitmsgwidth
+		gui.copyblamethreshold
+		gui.diffcontext
+		gui.encoding
+		gui.fastcopyblame
+		gui.matchtrackingbranch
+		gui.newbranchtemplate
+		gui.pruneduringfetch
+		gui.spellingdictionary
+		gui.trustmtime
+		help.autocorrect
+		help.browser
+		help.format
 		http.lowSpeedLimit
 		http.lowSpeedTime
+		http.maxRequests
 		http.noEPSV
+		http.proxy
+		http.sslCAInfo
+		http.sslCAPath
+		http.sslCert
+		http.sslKey
+		http.sslVerify
 		i18n.commitEncoding
 		i18n.logOutputEncoding
+		instaweb.browser
+		instaweb.httpd
+		instaweb.local
+		instaweb.modulepath
+		instaweb.port
+		log.date
 		log.showroot
+		man.viewer
+		merge.conflictstyle
+		merge.log
+		merge.renameLimit
+		merge.stat
 		merge.tool
-		merge.summary
 		merge.verbosity
-		pack.window
-		pack.depth
-		pack.windowMemory
+		mergetool.keepBackup
 		pack.compression
-		pack.deltaCacheSize
 		pack.deltaCacheLimit
+		pack.deltaCacheSize
+		pack.depth
+		pack.indexVersion
+		pack.packSizeLimit
+		pack.threads
+		pack.window
+		pack.windowMemory
 		pull.octopus
 		pull.twohead
-		repack.useDeltaBaseOffset
+		receive.denyCurrentBranch
+		receive.denyDeletes
+		receive.denyNonFastForwards
+		receive.fsckObjects
+		receive.unpackLimit
+		repack.usedeltabaseoffset
+		rerere.autoupdate
+		rerere.enabled
 		showbranch.default
+		status.relativePaths
+		status.showUntrackedFiles
 		tar.umask
 		transfer.unpackLimit
-		receive.unpackLimit
-		receive.denyNonFastForwards
-		user.name
 		user.email
+		user.name
 		user.signingkey
+		web.browser
 		branch. remote.
 	"
 }
 
 _git_remote ()
 {
-	local subcommands="add rm show prune update"
+	local subcommands="add rename rm show prune update set-head"
 	local subcommand="$(__git_find_subcommand "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
@@ -1305,7 +1563,7 @@
 	fi
 
 	case "$subcommand" in
-	rm|show|prune)
+	rename|rm|show|prune)
 		__gitcomp "$(__git_remotes)"
 		;;
 	update)
@@ -1333,7 +1591,7 @@
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--*)
-		__gitcomp "--mixed --hard --soft"
+		__gitcomp "--merge --mixed --hard --soft"
 		return
 		;;
 	esac
@@ -1374,12 +1632,8 @@
 	case "$cur" in
 	--*)
 		__gitcomp "
-			--max-count= --max-age= --since= --after=
-			--min-age= --before= --until=
-			--no-merges
-			--author= --committer= --grep=
-			--all-match
-			--not --all
+			$__git_log_common_options
+			$__git_log_shortlog_options
 			--numbered --summary
 			"
 		return
@@ -1395,13 +1649,19 @@
 	local cur="${COMP_WORDS[COMP_CWORD]}"
 	case "$cur" in
 	--pretty=*)
-		__gitcomp "
-			oneline short medium full fuller email raw
+		__gitcomp "$__git_log_pretty_formats
 			" "" "${cur##--pretty=}"
 		return
 		;;
+	--format=*)
+		__gitcomp "$__git_log_pretty_formats
+			" "" "${cur##--format=}"
+		return
+		;;
 	--*)
-		__gitcomp "--pretty="
+		__gitcomp "--pretty= --format=
+			$__git_diff_common_options
+			"
 		return
 		;;
 	esac
@@ -1457,7 +1717,7 @@
 {
 	__git_has_doubledash && return
 
-	local subcommands="add status init update"
+	local subcommands="add status init update summary foreach sync"
 	if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
 		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$cur" in
@@ -1477,7 +1737,8 @@
 	local subcommands="
 		init fetch clone rebase dcommit log find-rev
 		set-tree commit-diff info create-ignore propget
-		proplist show-ignore show-externals
+		proplist show-ignore show-externals branch tag blame
+		migrate
 		"
 	local subcommand="$(__git_find_subcommand "$subcommands")"
 	if [ -z "$subcommand" ]; then
@@ -1488,13 +1749,15 @@
 			--follow-parent --authors-file= --repack=
 			--no-metadata --use-svm-props --use-svnsync-props
 			--log-window-size= --no-checkout --quiet
-			--repack-flags --user-log-author $remote_opts
+			--repack-flags --use-log-author --localtime
+			--ignore-paths= $remote_opts
 			"
 		local init_opts="
 			--template= --shared= --trunk= --tags=
 			--branches= --stdlayout --minimize-url
 			--no-metadata --use-svm-props --use-svnsync-props
-			--rewrite-root= $remote_opts
+			--rewrite-root= --prefix= --use-log-author
+			--add-author-from $remote_opts
 			"
 		local cmt_opts="
 			--edit --rmdir --find-copies-harder --copy-similarity=
@@ -1514,7 +1777,8 @@
 		dcommit,--*)
 			__gitcomp "
 				--merge --strategy= --verbose --dry-run
-				--fetch-all --no-rebase $cmt_opts $fc_opts
+				--fetch-all --no-rebase --commit-url
+				--revision $cmt_opts $fc_opts
 				"
 			;;
 		set-tree,--*)
@@ -1528,13 +1792,13 @@
 			__gitcomp "
 				--limit= --revision= --verbose --incremental
 				--oneline --show-commit --non-recursive
-				--authors-file=
+				--authors-file= --color
 				"
 			;;
 		rebase,--*)
 			__gitcomp "
 				--merge --verbose --strategy= --local
-				--fetch-all $fc_opts
+				--fetch-all --dry-run $fc_opts
 				"
 			;;
 		commit-diff,--*)
@@ -1543,6 +1807,21 @@
 		info,--*)
 			__gitcomp "--url"
 			;;
+		branch,--*)
+			__gitcomp "--dry-run --message --tag"
+			;;
+		tag,--*)
+			__gitcomp "--dry-run --message"
+			;;
+		blame,--*)
+			__gitcomp "--git-format"
+			;;
+		migrate,--*)
+			__gitcomp "
+				--config-dir= --ignore-paths= --minimize
+				--no-auth-cache --username=
+				"
+			;;
 		*)
 			COMPREPLY=()
 			;;
@@ -1602,7 +1881,6 @@
 
 	if [ -z "$command" ]; then
 		case "${COMP_WORDS[COMP_CWORD]}" in
-		--*=*) COMPREPLY=() ;;
 		--*)   __gitcomp "
 			--paginate
 			--no-pager
@@ -1610,6 +1888,7 @@
 			--bare
 			--version
 			--exec-path
+			--html-path
 			--work-tree=
 			--help
 			"
@@ -1639,8 +1918,10 @@
 	config)      _git_config ;;
 	describe)    _git_describe ;;
 	diff)        _git_diff ;;
+	difftool)    _git_difftool ;;
 	fetch)       _git_fetch ;;
 	format-patch) _git_format_patch ;;
+	fsck)        _git_fsck ;;
 	gc)          _git_gc ;;
 	grep)        _git_grep ;;
 	help)        _git_help ;;
@@ -1666,6 +1947,7 @@
 	show)        _git_show ;;
 	show-branch) _git_show_branch ;;
 	stash)       _git_stash ;;
+	stage)       _git_add ;;
 	submodule)   _git_submodule ;;
 	svn)         _git_svn ;;
 	tag)         _git_tag ;;
@@ -1679,27 +1961,34 @@
 	__git_has_doubledash && return
 
 	local cur="${COMP_WORDS[COMP_CWORD]}"
-	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local g="$(__gitdir)"
 	local merge=""
 	if [ -f "$g/MERGE_HEAD" ]; then
 		merge="--merge"
 	fi
 	case "$cur" in
 	--*)
-		__gitcomp "--not --all $merge"
+		__gitcomp "
+			$__git_log_common_options
+			$__git_log_gitk_options
+			$merge
+			"
 		return
 		;;
 	esac
 	__git_complete_revlist
 }
 
-complete -o default -o nospace -F _git git
-complete -o default -o nospace -F _gitk gitk
+complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
+	|| complete -o default -o nospace -F _git git
+complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
+	|| complete -o default -o nospace -F _gitk gitk
 
 # The following are necessary only for Cygwin, and only are needed
 # when the user has tab-completed the executable name and consequently
 # included the '.exe' suffix.
 #
 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
-complete -o default -o nospace -F _git git.exe
+complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
+	|| complete -o default -o nospace -F _git git.exe
 fi
diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile
index a48540a..24d9312 100644
--- a/contrib/emacs/Makefile
+++ b/contrib/emacs/Makefile
@@ -2,7 +2,7 @@
 
 EMACS = emacs
 
-ELC = git.elc vc-git.elc git-blame.elc
+ELC = git.elc git-blame.elc
 INSTALL ?= install
 INSTALL_ELC = $(INSTALL) -m 644
 prefix ?= $(HOME)
diff --git a/contrib/emacs/README b/contrib/emacs/README
new file mode 100644
index 0000000..82368bd
--- /dev/null
+++ b/contrib/emacs/README
@@ -0,0 +1,39 @@
+This directory contains various modules for Emacs support.
+
+To make the modules available to Emacs, you should add this directory
+to your load-path, and then require the modules you want. This can be
+done by adding to your .emacs something like this:
+
+  (add-to-list 'load-path ".../git/contrib/emacs")
+  (require 'git)
+  (require 'git-blame)
+
+
+The following modules are available:
+
+* git.el:
+
+  Status manager that displays the state of all the files of the
+  project, and provides easy access to the most frequently used git
+  commands. The user interface is as far as possible compatible with
+  the pcl-cvs mode. It can be started with `M-x git-status'.
+
+* git-blame.el:
+
+  Emacs implementation of incremental git-blame.  When you turn it on
+  while viewing a file, the editor buffer will be updated by setting
+  the background of individual lines to a color that reflects which
+  commit it comes from.  And when you move around the buffer, a
+  one-line summary will be shown in the echo area.
+
+* vc-git.el:
+
+  This file used to contain the VC-mode backend for git, but it is no
+  longer distributed with git. It is now maintained as part of Emacs
+  and included in standard Emacs distributions starting from version
+  22.2.
+
+  If you have an earlier Emacs version, upgrading to Emacs 22 is
+  recommended, since the VC mode in older Emacs is not generic enough
+  to be able to support git in a reasonable manner, and no attempt has
+  been made to backport vc-git.el.
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index c1cf1cb..eace9c1 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -1,6 +1,6 @@
 ;;; git.el --- A user interface for git
 
-;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard <julliard@winehq.org>
 
 ;; Version: 1.0
 
@@ -34,15 +34,21 @@
 ;; To start: `M-x git-status'
 ;;
 ;; TODO
-;;  - portability to XEmacs
 ;;  - diff against other branch
 ;;  - renaming files from the status buffer
 ;;  - creating tags
 ;;  - fetch/pull
-;;  - switching branches
 ;;  - revlist browser
 ;;  - git-show-branch browser
-;;  - menus
+;;
+
+;;; Compatibility:
+;;
+;; This file works on GNU Emacs 21 or later. It may work on older
+;; versions but this is not guaranteed.
+;;
+;; It may work on XEmacs 21, provided that you first install the ewoc
+;; and log-edit packages.
 ;;
 
 (eval-when-compile (require 'cl))
@@ -173,7 +179,7 @@
 (defconst git-log-msg-separator "--- log message follows this line ---")
 
 (defvar git-log-edit-font-lock-keywords
-  `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$"
+  `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$"
      (1 font-lock-keyword-face)
      (2 font-lock-function-name-face))
     (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
@@ -183,11 +189,9 @@
   "Build a list of NAME=VALUE strings from a list of environment strings."
   (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env))
 
-(defun git-call-process-env (buffer env &rest args)
+(defun git-call-process (buffer &rest args)
   "Wrapper for call-process that sets environment strings."
-  (let ((process-environment (append (git-get-env-strings env)
-                                     process-environment)))
-    (apply #'call-process "git" nil buffer nil args)))
+  (apply #'call-process "git" nil buffer nil args))
 
 (defun git-call-process-display-error (&rest args)
   "Wrapper for call-process that displays error messages."
@@ -197,17 +201,26 @@
                (let ((default-directory dir)
                      (buffer-read-only nil))
                  (erase-buffer)
-                 (eq 0 (apply 'call-process "git" nil (list buffer t) nil args))))))
+                 (eq 0 (apply #'git-call-process (list buffer t) args))))))
     (unless ok (display-message-or-buffer buffer))
     ok))
 
-(defun git-call-process-env-string (env &rest args)
-  "Wrapper for call-process that sets environment strings,
-and returns the process output as a string, or nil if the git failed."
+(defun git-call-process-string (&rest args)
+  "Wrapper for call-process that returns the process output as a string,
+or nil if the git command failed."
   (with-temp-buffer
-    (and (eq 0 (apply #' git-call-process-env t env args))
+    (and (eq 0 (apply #'git-call-process t args))
          (buffer-string))))
 
+(defun git-call-process-string-display-error (&rest args)
+  "Wrapper for call-process that displays error message and returns
+the process output as a string, or nil if the git command failed."
+  (with-temp-buffer
+    (if (eq 0 (apply #'git-call-process (list t t) args))
+        (buffer-string)
+      (display-message-or-buffer (current-buffer))
+      nil)))
+
 (defun git-run-process-region (buffer start end program args)
   "Run a git process with a buffer region as input."
   (let ((output-buffer (current-buffer))
@@ -215,7 +228,7 @@
     (with-current-buffer buffer
       (cd dir)
       (apply #'call-process-region start end program
-             nil (list output-buffer nil) nil args))))
+             nil (list output-buffer t) nil args))))
 
 (defun git-run-command-buffer (buffer-name &rest args)
   "Run a git command, sending the output to a buffer named BUFFER-NAME."
@@ -226,19 +239,21 @@
       (let ((default-directory dir)
             (buffer-read-only nil))
         (erase-buffer)
-        (apply #'git-call-process-env buffer nil args)))
+        (apply #'git-call-process buffer args)))
     (message "Running git %s...done" (car args))
     buffer))
 
 (defun git-run-command-region (buffer start end env &rest args)
   "Run a git command with specified buffer region as input."
-  (unless (eq 0 (if env
-                    (git-run-process-region
-                     buffer start end "env"
-                     (append (git-get-env-strings env) (list "git") args))
+  (with-temp-buffer
+    (if (eq 0 (if env
                   (git-run-process-region
-                   buffer start end "git" args)))
-    (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
+                   buffer start end "env"
+                   (append (git-get-env-strings env) (list "git") args))
+                (git-run-process-region buffer start end "git" args)))
+        (buffer-string)
+      (display-message-or-buffer (current-buffer))
+      nil)))
 
 (defun git-run-hook (hook env &rest args)
   "Run a git hook and display its output if any."
@@ -327,7 +342,7 @@
   (let ((cdup (with-output-to-string
                 (with-current-buffer standard-output
                   (cd dir)
-                  (unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup"))
+                  (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup"))
                     (error "cannot find top-level git tree for %s." dir))))))
     (expand-file-name (concat (file-name-as-directory dir)
                               (car (split-string cdup "\n"))))))
@@ -348,8 +363,8 @@
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
-    (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
-  (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
+    (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+  (git-update-status-files (list (file-relative-name ignore-name)))))
 
 ; propertize definition for XEmacs, stolen from erc-compat
 (eval-when-compile
@@ -367,38 +382,52 @@
 (defun git-rev-parse (rev)
   "Parse a revision name and return its SHA1."
   (git-get-string-sha1
-   (git-call-process-env-string nil "rev-parse" rev)))
+   (git-call-process-string "rev-parse" rev)))
 
 (defun git-config (key)
   "Retrieve the value associated to KEY in the git repository config file."
-  (let ((str (git-call-process-env-string nil "config" key)))
+  (let ((str (git-call-process-string "config" key)))
     (and str (car (split-string str "\n")))))
 
 (defun git-symbolic-ref (ref)
   "Wrapper for the git-symbolic-ref command."
-  (let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
+  (let ((str (git-call-process-string "symbolic-ref" ref)))
     (and str (car (split-string str "\n")))))
 
 (defun git-update-ref (ref newval &optional oldval reason)
   "Update a reference by calling git-update-ref."
   (let ((args (and oldval (list oldval))))
-    (push newval args)
+    (when newval (push newval args))
     (push ref args)
     (when reason
      (push reason args)
      (push "-m" args))
+    (unless newval (push "-d" args))
     (apply 'git-call-process-display-error "update-ref" args)))
 
+(defun git-for-each-ref (&rest specs)
+  "Return a list of refs using git-for-each-ref.
+Each entry is a cons of (SHORT-NAME . FULL-NAME)."
+  (let (refs)
+    (with-temp-buffer
+      (apply #'git-call-process t "for-each-ref" "--format=%(refname)" specs)
+      (goto-char (point-min))
+      (while (re-search-forward "^[^/\n]+/[^/\n]+/\\(.+\\)$" nil t)
+	(push (cons (match-string 1) (match-string 0)) refs)))
+    (nreverse refs)))
+
 (defun git-read-tree (tree &optional index-file)
   "Read a tree into the index file."
-  (apply #'git-call-process-env nil
-         (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil)
-         "read-tree" (if tree (list tree))))
+  (let ((process-environment
+         (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
+    (apply 'git-call-process-display-error "read-tree" (if tree (list tree)))))
 
 (defun git-write-tree (&optional index-file)
   "Call git-write-tree and return the resulting tree SHA1 as a string."
-  (git-get-string-sha1
-   (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree")))
+  (let ((process-environment
+         (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment)))
+    (git-get-string-sha1
+     (git-call-process-string-display-error "write-tree"))))
 
 (defun git-commit-tree (buffer tree head)
   "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
@@ -424,11 +453,11 @@
             (when (re-search-forward "^Date: +\\(.*\\)$" nil t)
               (setq author-date (match-string 1)))
             (goto-char (point-min))
-            (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t)
-              (unless (string-equal head (match-string 1))
-                (setq subject "commit (merge): ")
+            (when (re-search-forward "^Merge: +\\(.*\\)" nil t)
+              (setq subject "commit (merge): ")
+              (dolist (parent (split-string (match-string 1) " +" t))
                 (push "-p" args)
-                (push (match-string 1) args))))
+                (push parent args))))
         (setq log-start (point-min)))
       (setq log-end (point-max))
       (goto-char log-start)
@@ -437,22 +466,20 @@
       (setq coding-system-for-write buffer-file-coding-system))
     (let ((commit
            (git-get-string-sha1
-            (with-output-to-string
-              (with-current-buffer standard-output
-                (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
-                             ("GIT_AUTHOR_EMAIL" . ,author-email)
-                             ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
-                             ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
-                  (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
-                  (apply #'git-run-command-region
-                         buffer log-start log-end env
-                         "commit-tree" tree (nreverse args))))))))
-      (and (git-update-ref "HEAD" commit head subject)
-           commit))))
+            (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
+                         ("GIT_AUTHOR_EMAIL" . ,author-email)
+                         ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
+                         ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
+              (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
+              (apply #'git-run-command-region
+                     buffer log-start log-end env
+                     "commit-tree" tree (nreverse args))))))
+      (when commit (git-update-ref "HEAD" commit head subject))
+      commit)))
 
 (defun git-empty-db-p ()
   "Check if the git db is empty (no commit done yet)."
-  (not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD"))))
+  (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD"))))
 
 (defun git-get-merge-heads ()
   "Retrieve the merge heads from the MERGE_HEAD file if present."
@@ -468,7 +495,7 @@
 (defun git-get-commit-description (commit)
   "Get a one-line description of COMMIT."
   (let ((coding-system-for-read (git-get-logoutput-coding-system)))
-    (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
+    (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit)))
       (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
           (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
         descr))))
@@ -487,14 +514,11 @@
   old-perm new-perm   ;; permission flags
   rename-state        ;; rename or copy state
   orig-name           ;; original name for renames or copies
+  needs-update        ;; whether file needs to be updated
   needs-refresh)      ;; whether file needs to be refreshed
 
 (defvar git-status nil)
 
-(defun git-clear-status (status)
-  "Remove everything from the status list."
-  (ewoc-filter status (lambda (info) nil)))
-
 (defun git-set-fileinfo-state (info state)
   "Set the state of a file info."
   (unless (eq (git-fileinfo->state info) state)
@@ -502,24 +526,26 @@
 	  (git-fileinfo->new-perm info) (git-fileinfo->old-perm info)
           (git-fileinfo->rename-state info) nil
           (git-fileinfo->orig-name info) nil
+          (git-fileinfo->needs-update info) nil
           (git-fileinfo->needs-refresh info) t)))
 
 (defun git-status-filenames-map (status func files &rest args)
-  "Apply FUNC to the status files names in the FILES list."
+  "Apply FUNC to the status files names in the FILES list.
+The list must be sorted."
   (when files
-    (setq files (sort files #'string-lessp))
     (let ((file (pop files))
           (node (ewoc-nth status 0)))
       (while (and file node)
-        (let ((info (ewoc-data node)))
-          (if (string-lessp (git-fileinfo->name info) file)
+        (let* ((info (ewoc-data node))
+               (name (git-fileinfo->name info)))
+          (if (string-lessp name file)
               (setq node (ewoc-next status node))
-            (if (string-equal (git-fileinfo->name info) file)
+            (if (string-equal name file)
                 (apply func info args))
             (setq file (pop files))))))))
 
 (defun git-set-filenames-state (status files state)
-  "Set the state of a list of named files."
+  "Set the state of a list of named files. The list must be sorted"
   (when files
     (git-status-filenames-map status #'git-set-fileinfo-state files state)
     (unless state  ;; delete files whose state has been set to nil
@@ -553,29 +579,29 @@
   (let* ((old-type (lsh (or old-perm 0) -9))
 	 (new-type (lsh (or new-perm 0) -9))
 	 (str (case new-type
-		(?\100  ;; file
+		(64  ;; file
 		 (case old-type
-		   (?\100 nil)
-		   (?\120 "   (type change symlink -> file)")
-		   (?\160 "   (type change subproject -> file)")))
-		 (?\120  ;; symlink
+		   (64 nil)
+		   (80 "   (type change symlink -> file)")
+		   (112 "   (type change subproject -> file)")))
+		 (80  ;; symlink
 		  (case old-type
-		    (?\100 "   (type change file -> symlink)")
-		    (?\160 "   (type change subproject -> symlink)")
+		    (64 "   (type change file -> symlink)")
+		    (112 "   (type change subproject -> symlink)")
 		    (t "   (symlink)")))
-		  (?\160  ;; subproject
+		  (112  ;; subproject
 		   (case old-type
-		     (?\100 "   (type change file -> subproject)")
-		     (?\120 "   (type change symlink -> subproject)")
+		     (64 "   (type change file -> subproject)")
+		     (80 "   (type change symlink -> subproject)")
 		     (t "   (subproject)")))
-                  (?\110 nil)  ;; directory (internal, not a real git state)
-		  (?\000  ;; deleted or unknown
+                  (72 nil)  ;; directory (internal, not a real git state)
+		  (0  ;; deleted or unknown
 		   (case old-type
-		     (?\120 "   (symlink)")
-		     (?\160 "   (subproject)")))
+		     (80 "   (symlink)")
+		     (112 "   (subproject)")))
 		  (t (format "   (unknown type %o)" new-type)))))
     (cond (str (propertize str 'face 'git-status-face))
-          ((eq new-type ?\110) "/")
+          ((eq new-type 72) "/")
           (t ""))))
 
 (defun git-rename-as-string (info)
@@ -612,39 +638,52 @@
 		    (git-file-type-as-string old-perm new-perm)
 		    (git-rename-as-string info)))))
 
-(defun git-insert-info-list (status infolist)
-  "Insert a list of file infos in the status buffer, replacing existing ones if any."
-  (setq infolist (sort infolist
-                       (lambda (info1 info2)
-                         (string-lessp (git-fileinfo->name info1)
-                                       (git-fileinfo->name info2)))))
-  (let ((info (pop infolist))
-        (node (ewoc-nth status 0)))
+(defun git-update-node-fileinfo (node info)
+  "Update the fileinfo of the specified node. The names are assumed to match already."
+  (let ((data (ewoc-data node)))
+    (setf
+     ;; preserve the marked flag
+     (git-fileinfo->marked info) (git-fileinfo->marked data)
+     (git-fileinfo->needs-update data) nil)
+    (when (not (equal info data))
+      (setf (git-fileinfo->needs-refresh info) t
+            (ewoc-data node) info))))
+
+(defun git-insert-info-list (status infolist files)
+  "Insert a sorted list of file infos in the status buffer, replacing existing ones if any."
+  (let* ((info (pop infolist))
+         (node (ewoc-nth status 0))
+         (name (and info (git-fileinfo->name info)))
+         remaining)
     (while info
-      (cond ((not node)
-	     (setq node (ewoc-enter-last status info))
-             (setq info (pop infolist)))
-            ((string-lessp (git-fileinfo->name (ewoc-data node))
-                           (git-fileinfo->name info))
-             (setq node (ewoc-next status node)))
-            ((string-equal (git-fileinfo->name (ewoc-data node))
-                           (git-fileinfo->name info))
-              ;; preserve the marked flag
-              (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
-	      (setf (git-fileinfo->needs-refresh info) t)
-              (setf (ewoc-data node) info)
-              (setq info (pop infolist)))
-            (t
-	     (setq node (ewoc-enter-before status node info))
-             (setq info (pop infolist)))))))
+      (let ((nodename (and node (git-fileinfo->name (ewoc-data node)))))
+        (while (and files (string-lessp (car files) name))
+          (push (pop files) remaining))
+        (when (and files (string-equal (car files) name))
+          (setq files (cdr files)))
+        (cond ((not nodename)
+               (setq node (ewoc-enter-last status info))
+               (setq info (pop infolist))
+               (setq name (and info (git-fileinfo->name info))))
+              ((string-lessp nodename name)
+               (setq node (ewoc-next status node)))
+              ((string-equal nodename name)
+               ;; preserve the marked flag
+               (git-update-node-fileinfo node info)
+               (setq info (pop infolist))
+               (setq name (and info (git-fileinfo->name info))))
+              (t
+               (setq node (ewoc-enter-before status node info))
+               (setq info (pop infolist))
+               (setq name (and info (git-fileinfo->name info)))))))
+    (nconc (nreverse remaining) files)))
 
 (defun git-run-diff-index (status files)
   "Run git-diff-index on FILES and parse the results into STATUS.
 Return the list of files that haven't been handled."
-  (let ((remaining (copy-sequence files))
-	infolist)
+  (let (infolist)
     (with-temp-buffer
-      (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+      (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files)
       (goto-char (point-min))
       (while (re-search-forward
 	      ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
@@ -659,11 +698,12 @@
                   (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
                 (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
                 (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
-            (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
-	  (setq remaining (delete name remaining))
-	  (when new-name (setq remaining (delete new-name remaining))))))
-    (git-insert-info-list status infolist)
-    remaining))
+            (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)))))
+    (setq infolist (sort (nreverse infolist)
+                         (lambda (info1 info2)
+                           (string-lessp (git-fileinfo->name info1)
+                                         (git-fileinfo->name info2)))))
+    (git-insert-info-list status infolist files)))
 
 (defun git-find-status-file (status file)
   "Find a given file in the status ewoc and return its node."
@@ -677,42 +717,40 @@
 Return the list of files that haven't been handled."
   (let (infolist)
     (with-temp-buffer
-      (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
+      (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
       (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1)
         (let ((name (match-string 1)))
           (push (git-create-fileinfo default-state name 0
                                      (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0))
-                infolist)
-          (setq files (delete name files)))))
-    (git-insert-info-list status infolist)
-    files))
+                infolist))))
+    (setq infolist (nreverse infolist))  ;; assume it is sorted already
+    (git-insert-info-list status infolist files)))
 
 (defun git-run-ls-files-cached (status files default-state)
   "Run git-ls-files -c on FILES and parse the results into STATUS.
 Return the list of files that haven't been handled."
-  (let ((remaining (copy-sequence files))
-	infolist)
+  (let (infolist)
     (with-temp-buffer
-      (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files)
+      (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files)
       (goto-char (point-min))
       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
 	(let* ((new-perm (string-to-number (match-string 1) 8))
 	       (old-perm (if (eq default-state 'added) 0 new-perm))
 	       (name (match-string 2)))
-	  (push (git-create-fileinfo default-state name old-perm new-perm) infolist)
-	  (setq remaining (delete name remaining)))))
-    (git-insert-info-list status infolist)
-    remaining))
+	  (push (git-create-fileinfo default-state name old-perm new-perm) infolist))))
+    (setq infolist (nreverse infolist))  ;; assume it is sorted already
+    (git-insert-info-list status infolist files)))
 
 (defun git-run-ls-unmerged (status files)
   "Run git-ls-files -u on FILES and parse the results into STATUS."
   (with-temp-buffer
-    (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
+    (apply #'git-call-process t "ls-files" "-z" "-u" "--" files)
     (goto-char (point-min))
     (let (unmerged-files)
       (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
         (push (match-string 1) unmerged-files))
+      (setq unmerged-files (nreverse unmerged-files))  ;; assume it is sorted already
       (git-set-filenames-state status unmerged-files 'unmerged))))
 
 (defun git-get-exclude-files ()
@@ -732,12 +770,19 @@
            (concat "--exclude-per-directory=" git-per-dir-ignore-file)
            (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
 
-(defun git-update-status-files (files &optional default-state)
-  "Update the status of FILES from the index."
+(defun git-update-status-files (&optional files mark-files)
+  "Update the status of FILES from the index.
+The FILES list must be sorted."
   (unless git-status (error "Not in git-status buffer."))
-  (when (or git-show-uptodate files)
-    (git-run-ls-files-cached git-status files 'uptodate))
-  (let* ((remaining-files
+  ;; set the needs-update flag on existing files
+  (if files
+      (git-status-filenames-map
+       git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files)
+    (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status)
+    (git-call-process nil "update-index" "--refresh")
+    (when git-show-uptodate
+      (git-run-ls-files-cached git-status nil 'uptodate)))
+  (let ((remaining-files
           (if (git-empty-db-p) ; we need some special handling for an empty db
 	      (git-run-ls-files-cached git-status files 'added)
             (git-run-diff-index git-status files))))
@@ -746,13 +791,17 @@
       (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
     (when (or remaining-files (and git-show-ignored (not files)))
       (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
-    (git-set-filenames-state git-status remaining-files default-state)
+    (unless files
+      (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update))))
+    (when remaining-files
+      (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate)))
+    (git-set-filenames-state git-status remaining-files nil)
+    (when mark-files (git-mark-files git-status files))
     (git-refresh-files)
     (git-refresh-ewoc-hf git-status)))
 
 (defun git-mark-files (status files)
   "Mark all the specified FILES, and unmark the others."
-  (setq files (sort files #'string-lessp))
   (let ((file (and files (pop files)))
         (node (ewoc-nth status 0)))
     (while node
@@ -778,13 +827,13 @@
       (list (ewoc-data (ewoc-locate git-status)))))
 
 (defun git-marked-files-state (&rest states)
-  "Return marked files that are in the specified states."
+  "Return a sorted list of marked files that are in the specified states."
   (let ((files (git-marked-files))
         result)
     (dolist (info files)
       (when (memq (git-fileinfo->state info) states)
         (push info result)))
-    result))
+    (nreverse result)))
 
 (defun git-refresh-files ()
   "Refresh all files that need it and clear the needs-refresh flag."
@@ -824,19 +873,18 @@
 
 (defun git-update-index (index-file files)
   "Run git-update-index on a list of files."
-  (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file))))
+  (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file)))
+                                     process-environment))
         added deleted modified)
     (dolist (info files)
       (case (git-fileinfo->state info)
         ('added (push info added))
         ('deleted (push info deleted))
         ('modified (push info modified))))
-    (when added
-      (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
-    (when deleted
-      (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
-    (when modified
-      (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
+    (and
+     (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added)))
+     (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted)))
+     (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified))))))
 
 (defun git-run-pre-commit-hook ()
   "Run the pre-commit hook if any."
@@ -862,33 +910,30 @@
           (message "You cannot commit unmerged files, resolve them first.")
         (unwind-protect
             (let ((files (git-marked-files-state 'added 'deleted 'modified))
-                  head head-tree)
+                  head tree head-tree)
               (unless (git-empty-db-p)
                 (setq head (git-rev-parse "HEAD")
                       head-tree (git-rev-parse "HEAD^{tree}")))
-              (if files
-                  (progn
-                    (message "Running git commit...")
-                    (git-read-tree head-tree index-file)
-                    (git-update-index nil files)         ;update both the default index
-                    (git-update-index index-file files)  ;and the temporary one
-                    (let ((tree (git-write-tree index-file)))
-                      (if (or (not (string-equal tree head-tree))
-                              (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
-                          (let ((commit (git-commit-tree buffer tree head)))
-                            (when commit
-                              (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
-                              (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
-                              (with-current-buffer buffer (erase-buffer))
-                              (git-update-status-files (git-get-filenames files) 'uptodate)
-                              (git-call-process-env nil nil "rerere")
-                              (git-call-process-env nil nil "gc" "--auto")
-                              (git-refresh-files)
-                              (git-refresh-ewoc-hf git-status)
-                              (message "Committed %s." commit)
-                              (git-run-hook "post-commit" nil)))
-                        (message "Commit aborted."))))
-                (message "No files to commit.")))
+              (message "Running git commit...")
+              (when
+                  (and
+                   (git-read-tree head-tree index-file)
+                   (git-update-index nil files)         ;update both the default index
+                   (git-update-index index-file files)  ;and the temporary one
+                   (setq tree (git-write-tree index-file)))
+                (if (or (not (string-equal tree head-tree))
+                        (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? "))
+                    (let ((commit (git-commit-tree buffer tree head)))
+                      (when commit
+                        (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
+                        (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+                        (with-current-buffer buffer (erase-buffer))
+                        (git-update-status-files (git-get-filenames files))
+                        (git-call-process nil "rerere")
+                        (git-call-process nil "gc" "--auto")
+                        (message "Committed %s." commit)
+                        (git-run-hook "post-commit" nil)))
+                  (message "Commit aborted."))))
           (delete-file index-file))))))
 
 
@@ -990,6 +1035,11 @@
       (setq node (ewoc-prev git-status node)))
     (ewoc-goto-node git-status last)))
 
+(defun git-insert-file (file)
+  "Insert file(s) into the git-status buffer."
+  (interactive "fInsert file: ")
+  (git-update-status-files (list (file-relative-name file))))
+
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
@@ -998,7 +1048,7 @@
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
     (when (apply 'git-call-process-display-error "update-index" "--add" "--" files)
-      (git-update-status-files files 'uptodate)
+      (git-update-status-files files)
       (git-success-message "Added" files))))
 
 (defun git-ignore-file ()
@@ -1008,7 +1058,7 @@
     (unless files
       (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
     (dolist (f files) (git-append-to-ignore f))
-    (git-update-status-files files 'ignored)
+    (git-update-status-files files)
     (git-success-message "Ignored" files)))
 
 (defun git-remove-file ()
@@ -1018,7 +1068,9 @@
     (unless files
       (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
     (if (yes-or-no-p
-         (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
+         (if (cdr files)
+             (format "Remove %d files? " (length files))
+           (format "Remove %s? " (car files))))
         (progn
           (dolist (name files)
             (ignore-errors
@@ -1026,7 +1078,7 @@
                   (delete-directory name)
                 (delete-file name))))
           (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files)
-            (git-update-status-files files nil)
+            (git-update-status-files files)
             (git-success-message "Removed" files)))
       (message "Aborting"))))
 
@@ -1037,7 +1089,9 @@
         added modified)
     (when (and files
                (yes-or-no-p
-                (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" ""))))
+                (if (cdr files)
+                    (format "Revert %d files? " (length files))
+                  (format "Revert %s? " (git-fileinfo->name (car files))))))
       (dolist (info files)
         (case (git-fileinfo->state info)
           ('added (push (git-fileinfo->name info) added))
@@ -1053,13 +1107,14 @@
                  (or (not added)
                      (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added))
                  (or (not modified)
-                     (apply 'git-call-process-display-error "checkout" "HEAD" modified)))))
-        (git-update-status-files (append added modified) 'uptodate)
+                     (apply 'git-call-process-display-error "checkout" "HEAD" modified))))
+            (names (git-get-filenames files)))
+        (git-update-status-files names)
         (when ok
           (dolist (file modified)
             (let ((buffer (get-file-buffer file)))
               (when buffer (with-current-buffer buffer (revert-buffer t t t)))))
-          (git-success-message "Reverted" (git-get-filenames files)))))))
+          (git-success-message "Reverted" names))))))
 
 (defun git-resolve-file ()
   "Resolve conflicts in marked file(s)."
@@ -1067,7 +1122,7 @@
   (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
       (when (apply 'git-call-process-display-error "update-index" "--" files)
-        (git-update-status-files files 'uptodate)
+        (git-update-status-files files)
         (git-success-message "Resolved" files)))))
 
 (defun git-remove-handled ()
@@ -1225,11 +1280,10 @@
       (goto-char (point-max))
       (insert sign-off "\n"))))
 
-(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg)
+(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg)
   "Setup the log buffer for a commit."
   (unless git-status (error "Not in git-status buffer."))
-  (let ((merge-heads (git-get-merge-heads))
-        (dir default-directory)
+  (let ((dir default-directory)
         (committer-name (git-get-committer-name))
         (committer-email (git-get-committer-email))
         (sign-off git-append-signed-off-by))
@@ -1243,9 +1297,8 @@
                 (or author-email committer-email)
                 (if date (format "Date: %s\n" date) "")
                 (if merge-heads
-                    (format "Parent: %s\n%s\n"
-                            (git-rev-parse "HEAD")
-                            (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n"))
+                    (format "Merge: %s\n"
+                            (mapconcat 'identity merge-heads " "))
                   ""))
         'face 'git-header-face)
        (propertize git-log-msg-separator 'face 'git-separator-face)
@@ -1285,22 +1338,25 @@
             (goto-char (point-min))
             (when (re-search-forward "^Date: \\(.*\\)$" nil t)
               (setq date (match-string 1)))))
-        (git-setup-log-buffer buffer author-name author-email subject date))
+        (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date))
       (if (boundp 'log-edit-diff-function)
 	  (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
 					 (log-edit-diff-function . git-log-edit-diff)) buffer)
 	(log-edit 'git-do-commit nil 'git-log-edit-files buffer))
       (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+      (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[ 	]*$"))
       (setq buffer-file-coding-system coding-system)
       (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
 
 (defun git-setup-commit-buffer (commit)
   "Setup the commit buffer with the contents of COMMIT."
-  (let (author-name author-email subject date msg)
+  (let (parents author-name author-email subject date msg)
     (with-temp-buffer
       (let ((coding-system (git-get-logoutput-coding-system)))
-        (git-call-process-env t nil "log" "-1" "--pretty=medium" commit)
+        (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit)
         (goto-char (point-min))
+        (when (re-search-forward "^Merge: *\\(.*\\)$" nil t)
+          (setq parents (cdr (split-string (match-string 1) " +"))))
         (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
           (setq author-name (match-string 1))
           (setq author-email (match-string 2)))
@@ -1312,18 +1368,48 @@
         (setq subject (pop msg))
         (while (and msg (zerop (length (car msg))) (pop msg)))))
     (git-setup-log-buffer (get-buffer-create "*git-commit*")
-                          author-name author-email subject date
+                          parents author-name author-email subject date
                           (mapconcat #'identity msg "\n"))))
 
 (defun git-get-commit-files (commit)
-  "Retrieve the list of files modified by COMMIT."
+  "Retrieve a sorted list of files modified by COMMIT."
   (let (files)
     (with-temp-buffer
-      (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
+      (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit)
       (goto-char (point-min))
       (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
         (push (match-string 1) files)))
-    files))
+    (sort files #'string-lessp)))
+
+(defun git-read-commit-name (prompt &optional default)
+  "Ask for a commit name, with completion for local branch, remote branch and tag."
+  (completing-read prompt
+                   (list* "HEAD" "ORIG_HEAD" "FETCH_HEAD" (mapcar #'car (git-for-each-ref)))
+		   nil nil nil nil default))
+
+(defun git-checkout (branch &optional merge)
+  "Checkout a branch, tag, or any commit.
+Use a prefix arg if git should merge while checking out."
+  (interactive
+   (list (git-read-commit-name "Checkout: ")
+         current-prefix-arg))
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((args (list branch "--")))
+    (when merge (push "-m" args))
+    (when (apply #'git-call-process-display-error "checkout" args)
+      (git-update-status-files))))
+
+(defun git-branch (branch)
+  "Create a branch from the current HEAD and switch to it."
+  (interactive (list (git-read-commit-name "Branch: ")))
+  (unless git-status (error "Not in git-status buffer."))
+  (if (git-rev-parse (concat "refs/heads/" branch))
+      (if (yes-or-no-p (format "Branch %s already exists, replace it? " branch))
+          (and (git-call-process-display-error "branch" "-f" branch)
+               (git-call-process-display-error "checkout" branch))
+        (message "Canceled."))
+    (git-call-process-display-error "checkout" "-b" branch))
+    (git-refresh-ewoc-hf git-status))
 
 (defun git-amend-commit ()
   "Undo the last commit on HEAD, and set things up to commit an
@@ -1333,13 +1419,52 @@
   (when (git-empty-db-p) (error "No commit to amend."))
   (let* ((commit (git-rev-parse "HEAD"))
          (files (git-get-commit-files commit)))
-    (when (git-call-process-display-error "reset" "--soft" "HEAD^")
-      (git-update-status-files (copy-sequence files) 'uptodate)
-      (git-mark-files git-status files)
-      (git-refresh-files)
+    (when (if (git-rev-parse "HEAD^")
+              (git-call-process-display-error "reset" "--soft" "HEAD^")
+            (and (git-update-ref "ORIG_HEAD" commit)
+                 (git-update-ref "HEAD" nil commit)))
+      (git-update-status-files files t)
       (git-setup-commit-buffer commit)
       (git-commit-file))))
 
+(defun git-cherry-pick-commit (arg)
+  "Cherry-pick a commit."
+  (interactive (list (git-read-commit-name "Cherry-pick commit: ")))
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((commit (git-rev-parse (concat arg "^0"))))
+    (unless commit (error "Not a valid commit '%s'." arg))
+    (when (git-rev-parse (concat commit "^2"))
+      (error "Cannot cherry-pick a merge commit."))
+    (let ((files (git-get-commit-files commit))
+          (ok (git-call-process-display-error "cherry-pick" "-n" commit)))
+      (git-update-status-files files ok)
+      (with-current-buffer (git-setup-commit-buffer commit)
+        (goto-char (point-min))
+        (if (re-search-forward "^\n*Signed-off-by:" nil t 1)
+            (goto-char (match-beginning 0))
+          (goto-char (point-max)))
+        (insert "(cherry picked from commit " commit ")\n"))
+      (when ok (git-commit-file)))))
+
+(defun git-revert-commit (arg)
+  "Revert a commit."
+  (interactive (list (git-read-commit-name "Revert commit: ")))
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((commit (git-rev-parse (concat arg "^0"))))
+    (unless commit (error "Not a valid commit '%s'." arg))
+    (when (git-rev-parse (concat commit "^2"))
+      (error "Cannot revert a merge commit."))
+    (let ((files (git-get-commit-files commit))
+          (subject (git-get-commit-description commit))
+          (ok (git-call-process-display-error "revert" "-n" commit)))
+      (git-update-status-files files ok)
+      (when (string-match "^[0-9a-f]+ - \\(.*\\)$" subject)
+        (setq subject (match-string 1 subject)))
+      (git-setup-log-buffer (get-buffer-create "*git-commit*")
+                            (git-get-merge-heads) nil nil (format "Revert \"%s\"" subject) nil
+                            (format "This reverts commit %s.\n" commit))
+      (when ok (git-commit-file)))))
+
 (defun git-find-file ()
   "Visit the current file in its own buffer."
   (interactive)
@@ -1377,27 +1502,10 @@
 (defun git-refresh-status ()
   "Refresh the git status buffer."
   (interactive)
-  (let* ((status git-status)
-         (pos (ewoc-locate status))
-         (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
-         (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
-    (unless status (error "Not in git-status buffer."))
-    (message "Refreshing git status...")
-    (git-call-process-env nil nil "update-index" "--refresh")
-    (git-clear-status status)
-    (git-update-status-files nil)
-    ; restore file marks
-    (when marked-files
-      (git-status-filenames-map status
-                                (lambda (info)
-                                        (setf (git-fileinfo->marked info) t)
-                                        (setf (git-fileinfo->needs-refresh info) t))
-                                marked-files)
-      (git-refresh-files))
-    ; move point to the current file name if any
-    (message "Refreshing git status...done")
-    (let ((node (and cur-name (git-find-status-file status cur-name))))
-      (when node (ewoc-goto-node status node)))))
+  (unless git-status (error "Not in git-status buffer."))
+  (message "Refreshing git status...")
+  (git-update-status-files)
+  (message "Refreshing git status...done"))
 
 (defun git-status-quit ()
   "Quit git-status mode."
@@ -1434,6 +1542,7 @@
     (define-key map "\r"  'git-find-file)
     (define-key map "g"   'git-refresh-status)
     (define-key map "i"   'git-ignore-file)
+    (define-key map "I"   'git-insert-file)
     (define-key map "l"   'git-log-file)
     (define-key map "m"   'git-mark-file)
     (define-key map "M"   'git-mark-all)
@@ -1455,6 +1564,10 @@
     (define-key map "\M-\C-?" 'git-unmark-all)
     ; the commit submap
     (define-key commit-map "\C-a" 'git-amend-commit)
+    (define-key commit-map "\C-b" 'git-branch)
+    (define-key commit-map "\C-o" 'git-checkout)
+    (define-key commit-map "\C-p" 'git-cherry-pick-commit)
+    (define-key commit-map "\C-v" 'git-revert-commit)
     ; the diff submap
     (define-key diff-map "b" 'git-diff-file-base)
     (define-key diff-map "c" 'git-diff-file-combined)
@@ -1475,6 +1588,10 @@
     `("Git"
       ["Refresh" git-refresh-status t]
       ["Commit" git-commit-file t]
+      ["Checkout..." git-checkout t]
+      ["New Branch..." git-branch t]
+      ["Cherry-pick Commit..." git-cherry-pick-commit t]
+      ["Revert Commit..." git-revert-commit t]
       ("Merge"
 	["Next Unmerged File" git-next-unmerged-file t]
 	["Prev Unmerged File" git-prev-unmerged-file t]
@@ -1490,6 +1607,7 @@
       ["Revert File" git-revert-file t]
       ["Ignore File" git-ignore-file t]
       ["Remove File" git-remove-file t]
+      ["Insert File" git-insert-file t]
       "--------"
       ["Find File" git-find-file t]
       ["View File" git-view-file t]
@@ -1576,8 +1694,8 @@
         (let ((filename (file-relative-name file dir)))
           ; skip files located inside the .git directory
           (unless (string-match "^\\.git/" filename)
-            (git-call-process-env nil nil "add" "--refresh" "--" filename)
-            (git-update-status-files (list filename) 'uptodate)))))))
+            (git-call-process nil "add" "--refresh" "--" filename)
+            (git-update-status-files (list filename))))))))
 
 (defun git-help ()
   "Display help for Git mode."
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
deleted file mode 100644
index b8f6be5..0000000
--- a/contrib/emacs/vc-git.el
+++ /dev/null
@@ -1,216 +0,0 @@
-;;; vc-git.el --- VC backend for the git version control system
-
-;; Copyright (C) 2006 Alexandre Julliard
-
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be
-;; useful, but WITHOUT ANY WARRANTY; without even the implied
-;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-;; PURPOSE.  See the GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public
-;; License along with this program; if not, write to the Free
-;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-;; MA 02111-1307 USA
-
-;;; Commentary:
-
-;; This file contains a VC backend for the git version control
-;; system.
-;;
-;; To install: put this file on the load-path and add GIT to the list
-;; of supported backends in `vc-handled-backends'; the following line,
-;; placed in your ~/.emacs, will accomplish this:
-;;
-;;     (add-to-list 'vc-handled-backends 'GIT)
-;;
-;; TODO
-;;  - changelog generation
-;;  - working with revisions other than HEAD
-;;
-
-(eval-when-compile (require 'cl))
-
-(defvar git-commits-coding-system 'utf-8
-  "Default coding system for git commits.")
-
-(defun vc-git--run-command-string (file &rest args)
-  "Run a git command on FILE and return its output as string."
-  (let* ((ok t)
-         (str (with-output-to-string
-                (with-current-buffer standard-output
-                  (unless (eq 0 (apply #'call-process "git" nil '(t nil) nil
-                                       (append args (list (file-relative-name file)))))
-                    (setq ok nil))))))
-    (and ok str)))
-
-(defun vc-git--run-command (file &rest args)
-  "Run a git command on FILE, discarding any output."
-  (let ((name (file-relative-name file)))
-    (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name))))))
-
-(defun vc-git-registered (file)
-  "Check whether FILE is registered with git."
-  (with-temp-buffer
-    (let* ((dir (file-name-directory file))
-           (name (file-relative-name file dir)))
-      (and (ignore-errors
-             (when dir (cd dir))
-             (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
-           (let ((str (buffer-string)))
-             (and (> (length str) (length name))
-                  (string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))
-
-(defun vc-git-state (file)
-  "git-specific version of `vc-state'."
-  (let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--")))
-    (if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff))
-        'edited
-      'up-to-date)))
-
-(defun vc-git-workfile-version (file)
-  "git-specific version of `vc-workfile-version'."
-  (let ((str (with-output-to-string
-               (with-current-buffer standard-output
-                 (call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD")))))
-    (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
-        (match-string 2 str)
-      str)))
-
-(defun vc-git-symbolic-commit (commit)
-  "Translate COMMIT string into symbolic form.
-Returns nil if not possible."
-  (and commit
-       (with-temp-buffer
-	 (and
-	  (zerop
-	   (call-process "git" nil '(t nil) nil "name-rev"
-			 "--name-only" "--tags"
-			 commit))
-	  (goto-char (point-min))
-	  (= (forward-line 2) 1)
-	  (bolp)
-	  (buffer-substring-no-properties (point-min) (1- (point-max)))))))
-
-(defun vc-git-previous-version (file rev)
-  "git-specific version of `vc-previous-version'."
-  (let ((default-directory (file-name-directory (expand-file-name file)))
-	(file (file-name-nondirectory file)))
-    (vc-git-symbolic-commit
-     (with-temp-buffer
-       (and
-	(zerop
-	 (call-process "git" nil '(t nil) nil "rev-list"
-		       "-2" rev "--" file))
-	(goto-char (point-max))
-	(bolp)
-	(zerop (forward-line -1))
-	(not (bobp))
-	(buffer-substring-no-properties
-	   (point)
-	   (1- (point-max))))))))
-
-(defun vc-git-next-version (file rev)
-  "git-specific version of `vc-next-version'."
-  (let* ((default-directory (file-name-directory
-			     (expand-file-name file)))
-	(file (file-name-nondirectory file))
-	(current-rev
-	 (with-temp-buffer
-	   (and
-	    (zerop
-	     (call-process "git" nil '(t nil) nil "rev-list"
-			   "-1" rev "--" file))
-	    (goto-char (point-max))
-	    (bolp)
-	    (zerop (forward-line -1))
-	    (bobp)
-	    (buffer-substring-no-properties
-	     (point)
-	     (1- (point-max)))))))
-    (and current-rev
-	 (vc-git-symbolic-commit
-	  (with-temp-buffer
-	    (and
-	     (zerop
-	      (call-process "git" nil '(t nil) nil "rev-list"
-			    "HEAD" "--" file))
-	     (goto-char (point-min))
-	     (search-forward current-rev nil t)
-	     (zerop (forward-line -1))
-	     (buffer-substring-no-properties
-	      (point)
-	      (progn (forward-line 1) (1- (point))))))))))
-
-(defun vc-git-revert (file &optional contents-done)
-  "Revert FILE to the version stored in the git repository."
-  (if contents-done
-      (vc-git--run-command file "update-index" "--")
-    (vc-git--run-command file "checkout" "HEAD")))
-
-(defun vc-git-checkout-model (file)
-  'implicit)
-
-(defun vc-git-workfile-unchanged-p (file)
-  (let ((sha1 (vc-git--run-command-string file "hash-object" "--"))
-        (head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--")))
-    (and head
-         (string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head)
-         (string= (car (split-string sha1 "\n")) (match-string 1 head)))))
-
-(defun vc-git-register (file &optional rev comment)
-  "Register FILE into the git version-control system."
-  (vc-git--run-command file "update-index" "--add" "--"))
-
-(defun vc-git-print-log (file &optional buffer)
-  (let ((name (file-relative-name file))
-        (coding-system-for-read git-commits-coding-system))
-    (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
-
-(defun vc-git-diff (file &optional rev1 rev2 buffer)
-  (let ((name (file-relative-name file))
-        (buf (or buffer "*vc-diff*")))
-    (if (and rev1 rev2)
-        (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
-      (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
-    ; git-diff-index doesn't set exit status like diff does
-    (if (vc-git-workfile-unchanged-p file) 0 1)))
-
-(defun vc-git-checkin (file rev comment)
-  (let ((coding-system-for-write git-commits-coding-system))
-    (vc-git--run-command file "commit" "-m" comment "--only" "--")))
-
-(defun vc-git-checkout (file &optional editable rev destfile)
-  (if destfile
-      (let ((fullname (substring
-                       (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--")
-                       0 -1))
-            (coding-system-for-read 'no-conversion)
-            (coding-system-for-write 'no-conversion))
-        (with-temp-file destfile
-          (eq 0 (call-process "git" nil t nil "cat-file" "blob"
-                              (concat (or rev "HEAD") ":" fullname)))))
-    (vc-git--run-command file "checkout" (or rev "HEAD"))))
-
-(defun vc-git-annotate-command (file buf &optional rev)
-  ; FIXME: rev is ignored
-  (let ((name (file-relative-name file)))
-    (call-process "git" nil buf nil "blame" name)))
-
-(defun vc-git-annotate-time ()
-  (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t)
-       (vc-annotate-convert-time
-        (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
-
-;; Not really useful since we can't do anything with the revision yet
-;;(defun vc-annotate-extract-revision-at-line ()
-;;  (save-excursion
-;;    (move-beginning-of-line 1)
-;;    (and (looking-at "[0-9a-f]+")
-;;         (buffer-substring (match-beginning 0) (match-end 0)))))
-
-(provide 'vc-git)
diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl
index a13bb6a..4576c4a 100755
--- a/contrib/examples/git-svnimport.perl
+++ b/contrib/examples/git-svnimport.perl
@@ -287,9 +287,9 @@
 my $last_branch;
 my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
-	system("git-init");
+	system("git init");
 	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-	system("git-read-tree");
+	system("git read-tree");
 	die "Cannot init an empty tree: $?\n" if $?;
 
 	$last_branch = $opt_o;
@@ -303,7 +303,7 @@
 	-f "$git_dir/svn2git"
 		or die "'$git_dir/svn2git' does not exist.\n".
 		       "You need that file for incremental imports.\n";
-	open(F, "git-symbolic-ref HEAD |") or
+	open(F, "git symbolic-ref HEAD |") or
 		die "Cannot run git-symbolic-ref: $!\n";
 	chomp ($last_branch = <F>);
 	$last_branch = basename($last_branch);
@@ -331,7 +331,7 @@
 				"$git_dir/refs/heads/$opt_o") == 0;
 
 	# populate index
-	system('git-read-tree', $last_rev);
+	system('git', 'read-tree', $last_rev);
 	die "read-tree failed: $?\n" if $?;
 
 	# Get the last import timestamps
@@ -399,7 +399,7 @@
 	my $pid = open(my $F, '-|');
 	die $! unless defined $pid;
 	if (!$pid) {
-	    exec("git-hash-object", "-w", $name)
+	    exec("git", "hash-object", "-w", $name)
 		or die "Cannot create object: $!\n";
 	}
 	my $sha = <$F>;
@@ -423,7 +423,7 @@
 		my $pid = open(my $F, '-|');
 		die $! unless defined $pid;
 		if (!$pid) {
-			exec("git-hash-object", "-w", $name)
+			exec("git", "hash-object", "-w", $name)
 			    or die "Cannot create object: $!\n";
 		}
 		my $sha = <$F>;
@@ -547,7 +547,7 @@
 	my $pid = open my $f,'-|';
 	die $! unless defined $pid;
 	if (!$pid) {
-		exec("git-ls-tree","-r","-z",$gitrev,$srcpath)
+		exec("git","ls-tree","-r","-z",$gitrev,$srcpath)
 			or die $!;
 	}
 	local $/ = "\0";
@@ -634,7 +634,7 @@
 
 	my $rev;
 	if($revision > $opt_s and defined $parent) {
-		open(H,'-|',"git-rev-parse","--verify",$parent);
+		open(H,'-|',"git","rev-parse","--verify",$parent);
 		$rev = <H>;
 		close(H) or do {
 			print STDERR "$revision: cannot find commit '$parent'!\n";
@@ -671,7 +671,7 @@
 		unlink($git_index);
 	} elsif ($rev ne $last_rev) {
 		print "Switching from $last_rev to $rev ($branch)\n" if $opt_v;
-		system("git-read-tree", $rev);
+		system("git", "read-tree", $rev);
 		die "read-tree failed for $rev: $?\n" if $?;
 		$last_rev = $rev;
 	}
@@ -740,7 +740,7 @@
 			my $pid = open my $F, "-|";
 			die "$!" unless defined $pid;
 			if (!$pid) {
-				exec("git-ls-files", "-z", @o1) or die $!;
+				exec("git", "ls-files", "-z", @o1) or die $!;
 			}
 			@o1 = ();
 			local $/ = "\0";
@@ -758,7 +758,7 @@
 					@o2 = @o1;
 					@o1 = ();
 				}
-				system("git-update-index","--force-remove","--",@o2);
+				system("git","update-index","--force-remove","--",@o2);
 				die "Cannot remove files: $?\n" if $?;
 			}
 		}
@@ -770,7 +770,7 @@
 				@n2 = @new;
 				@new = ();
 			}
-			system("git-update-index","--add",
+			system("git","update-index","--add",
 				(map { ('--cacheinfo', @$_) } @n2));
 			die "Cannot add files: $?\n" if $?;
 		}
@@ -778,7 +778,7 @@
 		my $pid = open(C,"-|");
 		die "Cannot fork: $!" unless defined $pid;
 		unless($pid) {
-			exec("git-write-tree");
+			exec("git","write-tree");
 			die "Cannot exec git-write-tree: $!\n";
 		}
 		chomp(my $tree = <C>);
@@ -830,7 +830,7 @@
 				"GIT_COMMITTER_NAME=$committer_name",
 				"GIT_COMMITTER_EMAIL=$committer_email",
 				"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
-				"git-commit-tree", $tree,@par);
+				"git", "commit-tree", $tree,@par);
 			die "Cannot exec git-commit-tree: $!\n";
 		}
 		$pw->writer();
@@ -874,7 +874,7 @@
 
 		$dest =~ tr/_/\./ if $opt_u;
 
-		system('git-tag', '-f', $dest, $cid) == 0
+		system('git', 'tag', '-f', $dest, $cid) == 0
 			or die "Cannot create tag $dest: $!\n";
 
 		print "Created tag '$dest' on '$branch'\n" if $opt_v;
@@ -937,7 +937,7 @@
 	my $pid = fork();
 	die "Fork: $!\n" unless defined $pid;
 	unless($pid) {
-		exec("git-repack", "-d")
+		exec("git", "repack", "-d")
 			or die "Cannot repack: $!\n";
 	}
 	waitpid($pid, 0);
@@ -958,7 +958,7 @@
 	system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
 		if $forward_master;
 	unless ($opt_i) {
-		system('git-read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD');
+		system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD');
 		die "read-tree failed: $?\n" if $?;
 	}
 } else {
@@ -966,7 +966,7 @@
 	print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0);
 	system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
 		unless -f "$git_dir/refs/heads/master";
-	system('git-update-ref', 'HEAD', "$orig_branch");
+	system('git', 'update-ref', 'HEAD', "$orig_branch");
 	unless ($opt_i) {
 		system('git checkout');
 		die "checkout failed: $?\n" if $?;
diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt
index 71aad8b..3bb871e 100644
--- a/contrib/examples/git-svnimport.txt
+++ b/contrib/examples/git-svnimport.txt
@@ -114,9 +114,9 @@
 -R <repack_each_revs>::
 	Specify how often git repository should be repacked.
 +
-The default value is 1000. git-svnimport will do import in chunks of 1000
-revisions, after each chunk git repository will be repacked. To disable
-this behavior specify some big value here which is mote than number of
+The default value is 1000. git-svnimport will do imports in chunks of 1000
+revisions, after each chunk the git repository will be repacked. To disable
+this behavior specify some large value here which is greater than the number of
 revisions to import.
 
 -P <path_from_trunk>::
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 2b122d3..342529d 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -245,7 +245,22 @@
 def p4Where(depotPath):
     if not depotPath.endswith("/"):
         depotPath += "/"
-    output = p4Cmd("where %s..." % depotPath)
+    depotPath = depotPath + "..."
+    outputList = p4CmdList("where %s" % depotPath)
+    output = None
+    for entry in outputList:
+        if "depotFile" in entry:
+            if entry["depotFile"] == depotPath:
+                output = entry
+                break
+        elif "data" in entry:
+            data = entry.get("data")
+            space = data.find(" ")
+            if data[:space] == depotPath:
+                output = entry
+                break
+    if output == None:
+        return ""
     if output["code"] == "error":
         return ""
     clientPath = ""
@@ -316,8 +331,11 @@
                             stderr=subprocess.PIPE, stdout=subprocess.PIPE);
     return proc.wait() == 0;
 
+_gitConfig = {}
 def gitConfig(key):
-    return read_pipe("git config %s" % key, ignore_error=True).strip()
+    if not _gitConfig.has_key(key):
+        _gitConfig[key] = read_pipe("git config %s" % key, ignore_error=True).strip()
+    return _gitConfig[key]
 
 def p4BranchesInGit(branchesAreInRemotes = True):
     branches = {}
@@ -424,13 +442,14 @@
     output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
                                                         for p in depotPaths]))
 
-    changes = []
+    changes = {}
     for line in output:
-        changeNum = line.split(" ")[1]
-        changes.append(int(changeNum))
+	changeNum = int(line.split(" ")[1])
+	changes[changeNum] = True
 
-    changes.sort()
-    return changes
+    changelist = changes.keys()
+    changelist.sort()
+    return changelist
 
 class Command:
     def __init__(self):
@@ -708,6 +727,7 @@
                 newdiff = newdiff.replace("\n", "\r\n")
             tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
             tmpFile.close()
+            mtime = os.stat(fileName).st_mtime
             defaultEditor = "vi"
             if platform.system() == "Windows":
                 defaultEditor = "notepad"
@@ -716,15 +736,29 @@
             else:
                 editor = os.environ.get("EDITOR", defaultEditor);
             system(editor + " " + fileName)
-            tmpFile = open(fileName, "rb")
-            message = tmpFile.read()
-            tmpFile.close()
-            os.remove(fileName)
-            submitTemplate = message[:message.index(separatorLine)]
-            if self.isWindows:
-                submitTemplate = submitTemplate.replace("\r\n", "\n")
 
-            p4_write_pipe("submit -i", submitTemplate)
+            response = "y"
+            if os.stat(fileName).st_mtime <= mtime:
+                response = "x"
+                while response != "y" and response != "n":
+                    response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
+
+            if response == "y":
+                tmpFile = open(fileName, "rb")
+                message = tmpFile.read()
+                tmpFile.close()
+                submitTemplate = message[:message.index(separatorLine)]
+                if self.isWindows:
+                    submitTemplate = submitTemplate.replace("\r\n", "\n")
+                p4_write_pipe("submit -i", submitTemplate)
+            else:
+                for f in editedFiles:
+                    p4_system("revert \"%s\"" % f);
+                for f in filesToAdd:
+                    p4_system("revert \"%s\"" % f);
+                    system("rm %s" %f)
+
+            os.remove(fileName)
         else:
             fileName = "submit.txt"
             file = open(fileName, "w+")
@@ -931,7 +965,7 @@
 
             if includeFile:
                 filesForCommit.append(f)
-                if f['action'] != 'delete':
+                if f['action'] not in ('delete', 'purge'):
                     filesToRead.append(f)
 
         filedata = []
@@ -950,11 +984,11 @@
         while j < len(filedata):
             stat = filedata[j]
             j += 1
-            text = [];
+            text = ''
             while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
-                text.append(filedata[j]['data'])
+                text += filedata[j]['data']
+                del filedata[j]['data']
                 j += 1
-            text = ''.join(text)
 
             if not stat.has_key('depotFile'):
                 sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
@@ -1023,7 +1057,7 @@
                 continue
 
             relPath = self.stripRepoPath(file['path'], branchPrefixes)
-            if file["action"] == "delete":
+            if file["action"] in ("delete", "purge"):
                 self.gitStream.write("D %s\n" % relPath)
             else:
                 data = file['data']
@@ -1062,7 +1096,7 @@
 
                 cleanedFiles = {}
                 for info in files:
-                    if info["action"] == "delete":
+                    if info["action"] in ("delete", "purge"):
                         continue
                     cleanedFiles[info["depotFile"]] = info["rev"]
 
@@ -1108,7 +1142,7 @@
 
         s = ''
         for (key, val) in self.users.items():
-            s += "%s\t%s\n" % (key, val)
+	    s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
 
         open(self.getUserCacheFilename(), "wb").write(s)
         self.userMapFromPerforceServer = True
@@ -1385,7 +1419,7 @@
             if change > newestRevision:
                 newestRevision = change
 
-            if info["action"] == "delete":
+            if info["action"] in ("delete", "purge"):
                 # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
                 #fileCnt = fileCnt + 1
                 continue
@@ -1733,8 +1767,12 @@
         if not P4Sync.run(self, depotPaths):
             return False
         if self.branch != "master":
-            if gitBranchExists("refs/remotes/p4/master"):
-                system("git branch master refs/remotes/p4/master")
+            if self.importIntoRemotes:
+                masterbranch = "refs/remotes/p4/master"
+            else:
+                masterbranch = "refs/heads/p4/master"
+            if gitBranchExists(masterbranch):
+                system("git branch master %s" % masterbranch)
                 system("git checkout -f")
             else:
                 print "Could not detect main branch. No checkout/master branch created."
diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl
index 23aeb25..6309d14 100755
--- a/contrib/fast-import/import-tars.perl
+++ b/contrib/fast-import/import-tars.perl
@@ -14,13 +14,18 @@
 
 my $branch_name = 'import-tars';
 my $branch_ref = "refs/heads/$branch_name";
-my $committer_name = 'T Ar Creator';
-my $committer_email = 'tar@example.com';
+my $author_name = $ENV{'GIT_AUTHOR_NAME'} || 'T Ar Creator';
+my $author_email = $ENV{'GIT_AUTHOR_EMAIL'} || 'tar@example.com';
+my $committer_name = $ENV{'GIT_COMMITTER_NAME'} || `git config --get user.name`;
+my $committer_email = $ENV{'GIT_COMMITTER_EMAIL'} || `git config --get user.email`;
+
+chomp($committer_name, $committer_email);
 
 open(FI, '|-', 'git', 'fast-import', '--quiet')
 	or die "Unable to start git fast-import: $!\n";
 foreach my $tar_file (@ARGV)
 {
+	my $commit_time = time;
 	$tar_file =~ m,([^/]+)$,;
 	my $tar_name = $1;
 
@@ -39,7 +44,7 @@
 		die "Unrecognized compression format: $tar_file\n";
 	}
 
-	my $commit_time = 0;
+	my $author_time = 0;
 	my $next_mark = 1;
 	my $have_top_dir = 1;
 	my ($top_dir, %files);
@@ -92,7 +97,7 @@
 		}
 		$files{$path} = [$next_mark++, $mode];
 
-		$commit_time = $mtime if $mtime > $commit_time;
+		$author_time = $mtime if $mtime > $author_time;
 		$path =~ m,^([^/]+)/,;
 		$top_dir = $1 unless $top_dir;
 		$have_top_dir = 0 if $top_dir ne $1;
@@ -100,6 +105,7 @@
 
 	print FI <<EOF;
 commit $branch_ref
+author $author_name <$author_email> $author_time +0000
 committer $committer_name <$committer_email> $commit_time +0000
 data <<END_OF_COMMIT_MESSAGE
 Imported from $tar_file.
@@ -119,7 +125,7 @@
 	print FI <<EOF;
 tag $tar_name
 from $branch_ref
-tagger $committer_name <$committer_email> $commit_time +0000
+tagger $author_name <$author_email> $author_time +0000
 data <<END_OF_TAG_MESSAGE
 Package $tar_name
 END_OF_TAG_MESSAGE
diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh
new file mode 100755
index 0000000..c364dda
--- /dev/null
+++ b/contrib/git-resurrect.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
+LONG_USAGE="git-resurrect attempts to find traces of a branch tip
+called <name>, and tries to resurrect it.  Currently, the reflog is
+searched for checkout messages, and with -r also merge messages.  With
+-m and -t, the history of all refs is scanned for Merge <name> into
+other/Merge <other> into <name> (respectively) commit subjects, which
+is rather slow but allows you to resurrect other people's topic
+branches."
+
+OPTIONS_SPEC="\
+git resurrect $USAGE
+--
+b,branch=            save branch as <newname> instead of <name>
+a,all                same as -l -r -m -t
+k,keep-going         full rev-list scan (instead of first match)
+l,reflog             scan reflog for checkouts (enabled by default)
+r,reflog-merges      scan for merges recorded in reflog
+m,merges             scan for merges into other branches (slow)
+t,merge-targets      scan for merges of other branches into <name>
+n,dry-run            don't recreate the branch"
+
+. git-sh-setup
+
+search_reflog () {
+        sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
+                < "$GIT_DIR"/logs/HEAD
+}
+
+search_reflog_merges () {
+	git rev-parse $(
+		sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
+			< "$GIT_DIR"/logs/HEAD
+	)
+}
+
+_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+
+search_merges () {
+        git rev-list --all --grep="Merge branch '$1'" \
+                --pretty=tformat:"%P %s" |
+        sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}"
+}
+
+search_merge_targets () {
+	git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
+		--pretty=tformat:"%H %s" --all |
+	sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} "
+}
+
+dry_run=
+early_exit=q
+scan_reflog=t
+scan_reflog_merges=
+scan_merges=
+scan_merge_targets=
+new_name=
+
+while test "$#" != 0; do
+	case "$1" in
+	    -b|--branch)
+		shift
+		new_name="$1"
+		;;
+	    -n|--dry-run)
+		dry_run=t
+		;;
+	    --no-dry-run)
+		dry_run=
+		;;
+	    -k|--keep-going)
+		early_exit=
+		;;
+	    --no-keep-going)
+		early_exit=q
+		;;
+	    -m|--merges)
+		scan_merges=t
+		;;
+	    --no-merges)
+		scan_merges=
+		;;
+	    -l|--reflog)
+		scan_reflog=t
+		;;
+	    --no-reflog)
+		scan_reflog=
+		;;
+	    -r|--reflog_merges)
+		scan_reflog_merges=t
+		;;
+	    --no-reflog_merges)
+		scan_reflog_merges=
+		;;
+	    -t|--merge-targets)
+		scan_merge_targets=t
+		;;
+	    --no-merge-targets)
+		scan_merge_targets=
+		;;
+	    -a|--all)
+		scan_reflog=t
+		scan_reflog_merges=t
+		scan_merges=t
+		scan_merge_targets=t
+		;;
+	    --)
+		shift
+		break
+		;;
+	    *)
+		usage
+		;;
+	esac
+	shift
+done
+
+test "$#" = 1 || usage
+
+all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
+if test -z "$all_strategies"; then
+	die "must enable at least one of -lrmt"
+fi
+
+branch="$1"
+test -z "$new_name" && new_name="$branch"
+
+if test ! -z "$scan_reflog"; then
+	if test -r "$GIT_DIR"/logs/HEAD; then
+		candidates="$(search_reflog $branch)"
+	else
+		die 'reflog scanning requested, but' \
+			'$GIT_DIR/logs/HEAD not readable'
+	fi
+fi
+if test ! -z "$scan_reflog_merges"; then
+	if test -r "$GIT_DIR"/logs/HEAD; then
+		candidates="$candidates $(search_reflog_merges $branch)"
+	else
+		die 'reflog scanning requested, but' \
+			'$GIT_DIR/logs/HEAD not readable'
+	fi
+fi
+if test ! -z "$scan_merges"; then
+	candidates="$candidates $(search_merges $branch)"
+fi
+if test ! -z "$scan_merge_targets"; then
+	candidates="$candidates $(search_merge_targets $branch)"
+fi
+
+candidates="$(git rev-parse $candidates | sort -u)"
+
+if test -z "$candidates"; then
+	hint=
+	test "z$all_strategies" != "ztttt" \
+		&& hint=" (maybe try again with -a)"
+	die "no candidates for $branch found$hint"
+fi
+
+echo "** Candidates for $branch **"
+for cmt in $candidates; do
+	git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
+done \
+| sort -n | cut -d: -f2-
+
+newest="$(git rev-list -1 $candidates)"
+if test ! -z "$dry_run"; then
+	printf "** Most recent: "
+	git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then
+	printf "** Restoring $new_name to "
+	git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+	git branch $new_name $newest
+else
+	printf "Most recent: "
+	git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+	echo "** $new_name already exists, doing nothing"
+fi
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 4136895..60cbab6 100644
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -38,6 +38,12 @@
 # hooks.emailprefix
 #   All emails have their subjects prefixed with this prefix, or "[SCM]"
 #   if emailprefix is unset, to aid filtering
+# hooks.showrev
+#   The shell command used to format each revision in the email, with
+#   "%s" replaced with the commit id.  Defaults to "git rev-list -1
+#   --pretty %s", displaying the commit id, author, date and log
+#   message.  To list full patches separated by a blank line, you
+#   could set this to "git show -C %s; echo".
 #
 # Notes
 # -----
@@ -224,13 +230,7 @@
 	echo ""
 
 	echo $LOGBEGIN
-	# This shows all log entries that are not already covered by
-	# another ref - i.e. commits that are now accessible from this
-	# ref that were previously not accessible
-	# (see generate_update_branch_email for the explanation of this
-	# command)
-	git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
-	git rev-list --pretty --stdin $newrev
+	show_new_revisions
 	echo $LOGEND
 }
 
@@ -390,8 +390,7 @@
 
 		echo ""
 		echo $LOGBEGIN
-		git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
-		git rev-list --pretty --stdin $oldrev..$newrev
+		show_new_revisions
 
 		# XXX: Need a way of detecting whether git rev-list actually
 		# outputted anything, so that we can issue a "no new
@@ -591,6 +590,47 @@
 	echo $LOGEND
 }
 
+
+# --------------- Miscellaneous utilities
+
+#
+# Show new revisions as the user would like to see them in the email.
+#
+show_new_revisions()
+{
+	# This shows all log entries that are not already covered by
+	# another ref - i.e. commits that are now accessible from this
+	# ref that were previously not accessible
+	# (see generate_update_branch_email for the explanation of this
+	# command)
+
+	# Revision range passed to rev-list differs for new vs. updated
+	# branches.
+	if [ "$change_type" = create ]
+	then
+		# Show all revisions exclusive to this (new) branch.
+		revspec=$newrev
+	else
+		# Branch update; show revisions not part of $oldrev.
+		revspec=$oldrev..$newrev
+	fi
+
+	other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |
+	    grep -F -v $refname)
+	git rev-parse --not $other_branches |
+	if [ -z "$custom_showrev" ]
+	then
+		git rev-list --pretty --stdin $revspec
+	else
+		git rev-list --stdin $revspec |
+		while read onerev
+		do
+			eval $(printf "$custom_showrev" $onerev)
+		done
+	fi
+}
+
+
 send_mail()
 {
 	if [ -n "$envelopesender" ]; then
@@ -627,6 +667,7 @@
 announcerecipients=$(git config hooks.announcelist)
 envelopesender=$(git config hooks.envelopesender)
 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
+custom_showrev=$(git config hooks.showrev)
 
 # --- Main loop
 # Allow dual mode: run from the command line just like the update hook, or
diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery
index 0096f57..1f914c9 100644
--- a/contrib/hooks/pre-auto-gc-battery
+++ b/contrib/hooks/pre-auto-gc-battery
@@ -1,9 +1,9 @@
 #!/bin/sh
 #
 # An example hook script to verify if you are on battery, in case you
-# are running Linux. Called by git-gc --auto with no arguments. The hook
-# should exit with non-zero status after issuing an appropriate message
-# if it wants to stop the auto repacking.
+# are running Linux or OS X. Called by git-gc --auto with no arguments.
+# The hook should exit with non-zero status after issuing an appropriate
+# message if it wants to stop the auto repacking.
 #
 # This hook is stored in the contrib/hooks directory. Your distribution
 # may have put this somewhere else. If you want to use this hook, you
@@ -30,6 +30,13 @@
 elif grep -q '0x01$' /proc/apm 2>/dev/null
 then
 	exit 0
+elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null
+then
+	exit 0
+elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt |
+	grep -q "Currently drawing from 'AC Power'"
+then
+	exit 0
 fi
 
 echo "Auto packing deferred; not on AC"
diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh
new file mode 100755
index 0000000..2cfe1b9
--- /dev/null
+++ b/contrib/rerere-train.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# Copyright (c) 2008, Nanako Shiraishi
+# Prime rerere database from existing merge commits
+
+me=rerere-train
+USAGE="$me rev-list-args"
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
+. git-sh-setup
+require_work_tree
+cd_to_toplevel
+
+# Remember original branch
+branch=$(git symbolic-ref -q HEAD) ||
+original_HEAD=$(git rev-parse --verify HEAD) || {
+	echo >&2 "Not on any branch and no commit yet?"
+	exit 1
+}
+
+mkdir -p "$GIT_DIR/rr-cache" || exit
+
+git rev-list --parents "$@" |
+while read commit parent1 other_parents
+do
+	if test -z "$other_parents"
+	then
+		# Skip non-merges
+		continue
+	fi
+	git checkout -q "$parent1^0"
+	if git merge $other_parents >/dev/null 2>&1
+	then
+		# Cleanly merges
+		continue
+	fi
+	if test -s "$GIT_DIR/MERGE_RR"
+	then
+		git show -s --pretty=format:"Learning from %h %s" "$commit"
+		git rerere
+		git checkout -q $commit -- .
+		git rerere
+	fi
+	git reset -q --hard
+done
+
+if test -z "$branch"
+then
+	git checkout "$original_HEAD"
+else
+	git checkout "${branch#refs/heads/}"
+fi
diff --git a/contrib/vim/README b/contrib/vim/README
index 9e7881f..fca1e17 100644
--- a/contrib/vim/README
+++ b/contrib/vim/README
@@ -1,8 +1,32 @@
-To syntax highlight git's commit messages, you need to:
-  1. Copy syntax/gitcommit.vim to vim's syntax directory:
-     $ mkdir -p $HOME/.vim/syntax
-     $ cp syntax/gitcommit.vim $HOME/.vim/syntax
-  2. Auto-detect the editing of git commit files:
-     $ cat >>$HOME/.vimrc <<'EOF'
-     autocmd BufNewFile,BufRead COMMIT_EDITMSG set filetype=gitcommit
-     EOF
+Syntax highlighting for git commit messages, config files, etc. is
+included with the vim distribution as of vim 7.2, and should work
+automatically.
+
+If you have an older version of vim, you can get the latest syntax
+files from the vim project:
+
+  http://ftp.vim.org/pub/vim/runtime/syntax/git.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim
+  http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim
+
+These files are also available via FTP at the same location.
+
+To install:
+
+  1. Copy these files to vim's syntax directory $HOME/.vim/syntax
+  2. To auto-detect the editing of various git-related filetypes:
+	$ cat >>$HOME/.vim/filetype.vim <<'EOF'
+	autocmd BufNewFile,BufRead *.git/COMMIT_EDITMSG    setf gitcommit
+	autocmd BufNewFile,BufRead *.git/config,.gitconfig setf gitconfig
+	autocmd BufNewFile,BufRead git-rebase-todo         setf gitrebase
+	autocmd BufNewFile,BufRead .msg.[0-9]*
+		\ if getline(1) =~ '^From.*# This line is ignored.$' |
+		\   setf gitsendemail |
+		\ endif
+	autocmd BufNewFile,BufRead *.git/**
+		\ if getline(1) =~ '^\x\{40\}\>\|^ref: ' |
+		\   setf git |
+		\ endif
+	EOF
diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim
deleted file mode 100644
index 332121b..0000000
--- a/contrib/vim/syntax/gitcommit.vim
+++ /dev/null
@@ -1,18 +0,0 @@
-syn region gitLine start=/^#/ end=/$/
-syn region gitCommit start=/^# Changes to be committed:$/ end=/^#$/ contains=gitHead,gitCommitFile
-syn region gitHead contained start=/^#   (.*)/ end=/^#$/
-syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile
-syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile
-
-syn match gitCommitFile contained /^#\t.*/hs=s+2
-syn match gitChangedFile contained /^#\t.*/hs=s+2
-syn match gitUntrackedFile contained /^#\t.*/hs=s+2
-
-hi def link gitLine Comment
-hi def link gitCommit Comment
-hi def link gitChanged Comment
-hi def link gitHead Comment
-hi def link gitUntracked Comment
-hi def link gitCommitFile Type
-hi def link gitChangedFile Constant
-hi def link gitUntrackedFile Constant
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 7959eab..993cacf 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -22,7 +22,7 @@
 # want to make sure that what is pointed to has a .git directory ...
 git_dir=$(cd "$orig_git" 2>/dev/null &&
   git rev-parse --git-dir 2>/dev/null) ||
-  die "\"$orig_git\" is not a git repository!"
+  die "Not a git repository: \"$orig_git\""
 
 case "$git_dir" in
 .git)
diff --git a/convert.c b/convert.c
index 78efed8..1816e97 100644
--- a/convert.c
+++ b/convert.c
@@ -281,7 +281,7 @@
 	 * (child --> cmd) --> us
 	 */
 	int ret = 1;
-	struct strbuf nbuf;
+	struct strbuf nbuf = STRBUF_INIT;
 	struct async async;
 	struct filter_params params;
 
@@ -299,7 +299,6 @@
 	if (start_async(&async))
 		return 0;	/* error was already reported */
 
-	strbuf_init(&nbuf, 0);
 	if (strbuf_read(&nbuf, async.out, len) < 0) {
 		error("read from external filter %s failed", cmd);
 		ret = 0;
diff --git a/csum-file.c b/csum-file.c
index cfc1ac4..2ddb12a 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -11,10 +11,8 @@
 #include "progress.h"
 #include "csum-file.h"
 
-static void flush(struct sha1file *f, unsigned int count)
+static void flush(struct sha1file *f, void * buf, unsigned int count)
 {
-	void *buf = f->buffer;
-
 	for (;;) {
 		int ret = xwrite(f->fd, buf, count);
 		if (ret > 0) {
@@ -37,8 +35,8 @@
 	unsigned offset = f->offset;
 
 	if (offset) {
-		SHA1_Update(&f->ctx, f->buffer, offset);
-		flush(f, offset);
+		git_SHA1_Update(&f->ctx, f->buffer, offset);
+		flush(f, f->buffer, offset);
 		f->offset = 0;
 	}
 }
@@ -48,12 +46,12 @@
 	int fd;
 
 	sha1flush(f);
-	SHA1_Final(f->buffer, &f->ctx);
+	git_SHA1_Final(f->buffer, &f->ctx);
 	if (result)
 		hashcpy(result, f->buffer);
 	if (flags & (CSUM_CLOSE | CSUM_FSYNC)) {
 		/* write checksum and close fd */
-		flush(f, 20);
+		flush(f, f->buffer, 20);
 		if (flags & CSUM_FSYNC)
 			fsync_or_die(f->fd, f->name);
 		if (close(f->fd))
@@ -68,21 +66,30 @@
 
 int sha1write(struct sha1file *f, void *buf, unsigned int count)
 {
-	if (f->do_crc)
-		f->crc32 = crc32(f->crc32, buf, count);
 	while (count) {
 		unsigned offset = f->offset;
 		unsigned left = sizeof(f->buffer) - offset;
 		unsigned nr = count > left ? left : count;
+		void *data;
 
-		memcpy(f->buffer + offset, buf, nr);
+		if (f->do_crc)
+			f->crc32 = crc32(f->crc32, buf, nr);
+
+		if (nr == sizeof(f->buffer)) {
+			/* process full buffer directly without copy */
+			data = buf;
+		} else {
+			memcpy(f->buffer + offset, buf, nr);
+			data = f->buffer;
+		}
+
 		count -= nr;
 		offset += nr;
 		buf = (char *) buf + nr;
 		left -= nr;
 		if (!left) {
-			SHA1_Update(&f->ctx, f->buffer, offset);
-			flush(f, offset);
+			git_SHA1_Update(&f->ctx, data, offset);
+			flush(f, data, offset);
 			offset = 0;
 		}
 		f->offset = offset;
@@ -104,7 +111,7 @@
 	f->tp = tp;
 	f->name = name;
 	f->do_crc = 0;
-	SHA1_Init(&f->ctx);
+	git_SHA1_Init(&f->ctx);
 	return f;
 }
 
diff --git a/csum-file.h b/csum-file.h
index 01f13b5..294add2 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -7,7 +7,7 @@
 struct sha1file {
 	int fd;
 	unsigned int offset;
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	off_t total;
 	struct progress *tp;
 	const char *name;
diff --git a/ctype.c b/ctype.c
index d2bd38e..b90ec00 100644
--- a/ctype.c
+++ b/ctype.c
@@ -5,23 +5,22 @@
  */
 #include "cache.h"
 
-/* Just so that no insane platform contaminate namespace with these symbols */
-#undef SS
-#undef AA
-#undef DD
-
-#define SS GIT_SPACE
-#define AA GIT_ALPHA
-#define DD GIT_DIGIT
+enum {
+	S = GIT_SPACE,
+	A = GIT_ALPHA,
+	D = GIT_DIGIT,
+	G = GIT_GLOB_SPECIAL,	/* *, ?, [, \\ */
+	R = GIT_REGEX_SPECIAL,	/* $, (, ), +, ., ^, {, | * */
+};
 
 unsigned char sane_ctype[256] = {
-	 0,  0,  0,  0,  0,  0,  0,  0,  0, SS, SS,  0,  0, SS,  0,  0,		/* 0-15 */
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 16-15 */
-	SS,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 32-15 */
-	DD, DD, DD, DD, DD, DD, DD, DD, DD, DD,  0,  0,  0,  0,  0,  0,		/* 48-15 */
-	 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,		/* 64-15 */
-	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,		/* 80-15 */
-	 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,		/* 96-15 */
-	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,		/* 112-15 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  16.. 31 */
+	S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0,		/*  32.. 47 */
+	D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,		/*  48.. 63 */
+	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  64.. 79 */
+	A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0,		/*  80.. 95 */
+	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  96..111 */
+	A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0,		/* 112..127 */
 	/* Nothing in the 128.. range */
 };
diff --git a/daemon.c b/daemon.c
index 8dcde73..13401f1 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,7 +1,6 @@
 #include "cache.h"
 #include "pkt-line.h"
 #include "exec_cmd.h"
-#include "interpolate.h"
 
 #include <syslog.h>
 
@@ -16,12 +15,11 @@
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
-static int child_handler_pipe[2];
 
 static const char daemon_usage[] =
 "git daemon [--verbose] [--syslog] [--export-all]\n"
-"           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [--base-path-relaxed]\n"
+"           [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
+"           [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
 "           [--user-path | --user-path=path]\n"
 "           [--interpolated-path=path]\n"
 "           [--reuseaddr] [--detach] [--pid-file=file]\n"
@@ -55,61 +53,26 @@
 static unsigned int timeout;
 static unsigned int init_timeout;
 
-/*
- * Static table for now.  Ugh.
- * Feel free to make dynamic as needed.
- */
-#define INTERP_SLOT_HOST	(0)
-#define INTERP_SLOT_CANON_HOST	(1)
-#define INTERP_SLOT_IP		(2)
-#define INTERP_SLOT_PORT	(3)
-#define INTERP_SLOT_DIR		(4)
-#define INTERP_SLOT_PERCENT	(5)
-
-static struct interp interp_table[] = {
-	{ "%H", 0},
-	{ "%CH", 0},
-	{ "%IP", 0},
-	{ "%P", 0},
-	{ "%D", 0},
-	{ "%%", 0},
-};
-
+static char *hostname;
+static char *canon_hostname;
+static char *ip_address;
+static char *tcp_port;
 
 static void logreport(int priority, const char *err, va_list params)
 {
-	/* We should do a single write so that it is atomic and output
-	 * of several processes do not get intermingled. */
-	char buf[1024];
-	int buflen;
-	int maxlen, msglen;
-
-	/* sizeof(buf) should be big enough for "[pid] \n" */
-	buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
-
-	maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
-	msglen = vsnprintf(buf + buflen, maxlen, err, params);
-
 	if (log_syslog) {
+		char buf[1024];
+		vsnprintf(buf, sizeof(buf), err, params);
 		syslog(priority, "%s", buf);
-		return;
+	} else {
+		/*
+		 * Since stderr is set to linebuffered mode, the
+		 * logging of different processes will not overlap
+		 */
+		fprintf(stderr, "[%"PRIuMAX"] ", (uintmax_t)getpid());
+		vfprintf(stderr, err, params);
+		fputc('\n', stderr);
 	}
-
-	/* maxlen counted our own LF but also counts space given to
-	 * vsnprintf for the terminating NUL.  We want to make sure that
-	 * we have space for our own LF and NUL after the "meat" of the
-	 * message, so truncate it at maxlen - 1.
-	 */
-	if (msglen > maxlen - 1)
-		msglen = maxlen - 1;
-	else if (msglen < 0)
-		msglen = 0; /* Protect against weird return values. */
-	buflen += msglen;
-
-	buf[buflen++] = '\n';
-	buf[buflen] = '\0';
-
-	write_in_full(2, buf, buflen);
 }
 
 static void logerror(const char *err, ...)
@@ -183,15 +146,14 @@
 	}
 }
 
-static char *path_ok(struct interp *itable)
+static char *path_ok(char *directory)
 {
 	static char rpath[PATH_MAX];
 	static char interp_path[PATH_MAX];
-	int retried_path = 0;
 	char *path;
 	char *dir;
 
-	dir = itable[INTERP_SLOT_DIR].value;
+	dir = directory;
 
 	if (avoid_alias(dir)) {
 		logerror("'%s': aliased", dir);
@@ -221,14 +183,27 @@
 		}
 	}
 	else if (interpolated_path && saw_extended_args) {
+		struct strbuf expanded_path = STRBUF_INIT;
+		struct strbuf_expand_dict_entry dict[] = {
+			{ "H", hostname },
+			{ "CH", canon_hostname },
+			{ "IP", ip_address },
+			{ "P", tcp_port },
+			{ "D", directory },
+			{ "%", "%" },
+			{ NULL }
+		};
+
 		if (*dir != '/') {
 			/* Allow only absolute */
 			logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
 			return NULL;
 		}
 
-		interpolate(interp_path, PATH_MAX, interpolated_path,
-			    interp_table, ARRAY_SIZE(interp_table));
+		strbuf_expand(&expanded_path, interpolated_path,
+				strbuf_expand_dict_cb, &dict);
+		strlcpy(interp_path, expanded_path.buf, PATH_MAX);
+		strbuf_release(&expanded_path);
 		loginfo("Interpolated dir '%s'", interp_path);
 
 		dir = interp_path;
@@ -243,25 +218,18 @@
 		dir = rpath;
 	}
 
-	do {
-		path = enter_repo(dir, strict_paths);
-		if (path)
-			break;
-
+	path = enter_repo(dir, strict_paths);
+	if (!path && base_path && base_path_relaxed) {
 		/*
 		 * if we fail and base_path_relaxed is enabled, try without
 		 * prefixing the base path
 		 */
-		if (base_path && base_path_relaxed && !retried_path) {
-			dir = itable[INTERP_SLOT_DIR].value;
-			retried_path = 1;
-			continue;
-		}
-		break;
-	} while (1);
+		dir = directory;
+		path = enter_repo(dir, strict_paths);
+	}
 
 	if (!path) {
-		logerror("'%s': unable to chdir or not a git archive", dir);
+		logerror("'%s' does not appear to be a git repository", dir);
 		return NULL;
 	}
 
@@ -319,14 +287,12 @@
 	return 0;
 }
 
-static int run_service(struct interp *itable, struct daemon_service *service)
+static int run_service(char *dir, struct daemon_service *service)
 {
 	const char *path;
 	int enabled = service->enabled;
 
-	loginfo("Request %s for '%s'",
-		service->name,
-		itable[INTERP_SLOT_DIR].value);
+	loginfo("Request %s for '%s'", service->name, dir);
 
 	if (!enabled && !service->overridable) {
 		logerror("'%s': service not enabled.", service->name);
@@ -334,7 +300,7 @@
 		return -1;
 	}
 
-	if (!(path = path_ok(itable)))
+	if (!(path = path_ok(dir)))
 		return -1;
 
 	/*
@@ -431,11 +397,18 @@
 	die("No such service %s", name);
 }
 
+static char *xstrdup_tolower(const char *str)
+{
+	char *p, *dup = xstrdup(str);
+	for (p = dup; *p; p++)
+		*p = tolower(*p);
+	return dup;
+}
+
 /*
  * Separate the "extra args" information as supplied by the client connection.
- * Any resulting data is squirreled away in the given interpolation table.
  */
-static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
+static void parse_extra_args(char *extra_args, int buflen)
 {
 	char *val;
 	int vallen;
@@ -453,35 +426,23 @@
 				if (port) {
 					*port = 0;
 					port++;
-					interp_set_entry(table, INTERP_SLOT_PORT, port);
+					free(tcp_port);
+					tcp_port = xstrdup(port);
 				}
-				interp_set_entry(table, INTERP_SLOT_HOST, host);
+				free(hostname);
+				hostname = xstrdup_tolower(host);
 			}
 
 			/* On to the next one */
 			extra_args = val + vallen;
 		}
 	}
-}
-
-static void fill_in_extra_table_entries(struct interp *itable)
-{
-	char *hp;
-
-	/*
-	 * Replace literal host with lowercase-ized hostname.
-	 */
-	hp = interp_table[INTERP_SLOT_HOST].value;
-	if (!hp)
-		return;
-	for ( ; *hp; hp++)
-		*hp = tolower(*hp);
 
 	/*
 	 * Locate canonical hostname and its IP address.
 	 */
+	if (hostname) {
 #ifndef NO_IPV6
-	{
 		struct addrinfo hints;
 		struct addrinfo *ai, *ai0;
 		int gai;
@@ -490,30 +451,28 @@
 		memset(&hints, 0, sizeof(hints));
 		hints.ai_flags = AI_CANONNAME;
 
-		gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0);
+		gai = getaddrinfo(hostname, 0, &hints, &ai0);
 		if (!gai) {
 			for (ai = ai0; ai; ai = ai->ai_next) {
 				struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
 
 				inet_ntop(AF_INET, &sin_addr->sin_addr,
 					  addrbuf, sizeof(addrbuf));
-				interp_set_entry(interp_table,
-						 INTERP_SLOT_CANON_HOST, ai->ai_canonname);
-				interp_set_entry(interp_table,
-						 INTERP_SLOT_IP, addrbuf);
+				free(canon_hostname);
+				canon_hostname = xstrdup(ai->ai_canonname);
+				free(ip_address);
+				ip_address = xstrdup(addrbuf);
 				break;
 			}
 			freeaddrinfo(ai0);
 		}
-	}
 #else
-	{
 		struct hostent *hent;
 		struct sockaddr_in sa;
 		char **ap;
 		static char addrbuf[HOST_NAME_MAX + 1];
 
-		hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value);
+		hent = gethostbyname(hostname);
 
 		ap = hent->h_addr_list;
 		memset(&sa, 0, sizeof sa);
@@ -524,10 +483,12 @@
 		inet_ntop(hent->h_addrtype, &sa.sin_addr,
 			  addrbuf, sizeof(addrbuf));
 
-		interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name);
-		interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf);
-	}
+		free(canon_hostname);
+		canon_hostname = xstrdup(hent->h_name);
+		free(ip_address);
+		ip_address = xstrdup(addrbuf);
 #endif
+	}
 }
 
 
@@ -557,6 +518,10 @@
 #endif
 		}
 		loginfo("Connection from %s:%d", addrbuf, port);
+		setenv("REMOTE_ADDR", addrbuf, 1);
+	}
+	else {
+		unsetenv("REMOTE_ADDR");
 	}
 
 	alarm(init_timeout ? init_timeout : timeout);
@@ -573,16 +538,14 @@
 		pktlen--;
 	}
 
-	/*
-	 * Initialize the path interpolation table for this connection.
-	 */
-	interp_clear_table(interp_table, ARRAY_SIZE(interp_table));
-	interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%");
+	free(hostname);
+	free(canon_hostname);
+	free(ip_address);
+	free(tcp_port);
+	hostname = canon_hostname = ip_address = tcp_port = NULL;
 
-	if (len != pktlen) {
-	    parse_extra_args(interp_table, line + len + 1, pktlen - len - 1);
-	    fill_in_extra_table_entries(interp_table);
-	}
+	if (len != pktlen)
+		parse_extra_args(line + len + 1, pktlen - len - 1);
 
 	for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
 		struct daemon_service *s = &(daemon_service[i]);
@@ -594,9 +557,7 @@
 			 * Note: The directory here is probably context sensitive,
 			 * and might depend on the actual service being performed.
 			 */
-			interp_set_entry(interp_table,
-					 INTERP_SLOT_DIR, line + namelen + 5);
-			return run_service(interp_table, s);
+			return run_service(line + namelen + 5, s);
 		}
 	}
 
@@ -604,169 +565,107 @@
 	return -1;
 }
 
+static int max_connections = 32;
 
-/*
- * We count spawned/reaped separately, just to avoid any
- * races when updating them from signals. The SIGCHLD handler
- * will only update children_reaped, and the fork logic will
- * only update children_spawned.
- *
- * MAX_CHILDREN should be a power-of-two to make the modulus
- * operation cheap. It should also be at least twice
- * the maximum number of connections we will ever allow.
- */
-#define MAX_CHILDREN 128
-
-static int max_connections = 25;
-
-/* These are updated by the signal handler */
-static volatile unsigned int children_reaped;
-static pid_t dead_child[MAX_CHILDREN];
-
-/* These are updated by the main loop */
-static unsigned int children_spawned;
-static unsigned int children_deleted;
+static unsigned int live_children;
 
 static struct child {
+	struct child *next;
 	pid_t pid;
-	int addrlen;
 	struct sockaddr_storage address;
-} live_child[MAX_CHILDREN];
+} *firstborn;
 
-static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
+static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
 {
-	live_child[idx].pid = pid;
-	live_child[idx].addrlen = addrlen;
-	memcpy(&live_child[idx].address, addr, addrlen);
+	struct child *newborn, **cradle;
+
+	/*
+	 * This must be xcalloc() -- we'll compare the whole sockaddr_storage
+	 * but individual address may be shorter.
+	 */
+	newborn = xcalloc(1, sizeof(*newborn));
+	live_children++;
+	newborn->pid = pid;
+	memcpy(&newborn->address, addr, addrlen);
+	for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
+		if (!memcmp(&(*cradle)->address, &newborn->address,
+			    sizeof(newborn->address)))
+			break;
+	newborn->next = *cradle;
+	*cradle = newborn;
 }
 
-/*
- * Walk from "deleted" to "spawned", and remove child "pid".
- *
- * We move everything up by one, since the new "deleted" will
- * be one higher.
- */
-static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
+static void remove_child(pid_t pid)
 {
-	struct child n;
+	struct child **cradle, *blanket;
 
-	deleted %= MAX_CHILDREN;
-	spawned %= MAX_CHILDREN;
-	if (live_child[deleted].pid == pid) {
-		live_child[deleted].pid = -1;
-		return;
-	}
-	n = live_child[deleted];
-	for (;;) {
-		struct child m;
-		deleted = (deleted + 1) % MAX_CHILDREN;
-		if (deleted == spawned)
-			die("could not find dead child %d\n", pid);
-		m = live_child[deleted];
-		live_child[deleted] = n;
-		if (m.pid == pid)
-			return;
-		n = m;
-	}
+	for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
+		if (blanket->pid == pid) {
+			*cradle = blanket->next;
+			live_children--;
+			free(blanket);
+			break;
+		}
 }
 
 /*
  * This gets called if the number of connections grows
  * past "max_connections".
  *
- * We _should_ start off by searching for connections
- * from the same IP, and if there is some address wth
- * multiple connections, we should kill that first.
- *
- * As it is, we just "randomly" kill 25% of the connections,
- * and our pseudo-random generator sucks too. I have no
- * shame.
- *
- * Really, this is just a place-holder for a _real_ algorithm.
+ * We kill the newest connection from a duplicate IP.
  */
-static void kill_some_children(int signo, unsigned start, unsigned stop)
+static void kill_some_child(void)
 {
-	start %= MAX_CHILDREN;
-	stop %= MAX_CHILDREN;
-	while (start != stop) {
-		if (!(start & 3))
-			kill(live_child[start].pid, signo);
-		start = (start + 1) % MAX_CHILDREN;
-	}
+	const struct child *blanket, *next;
+
+	if (!(blanket = firstborn))
+		return;
+
+	for (; (next = blanket->next); blanket = next)
+		if (!memcmp(&blanket->address, &next->address,
+			    sizeof(next->address))) {
+			kill(blanket->pid, SIGTERM);
+			break;
+		}
 }
 
 static void check_dead_children(void)
 {
-	unsigned spawned, reaped, deleted;
+	int status;
+	pid_t pid;
 
-	spawned = children_spawned;
-	reaped = children_reaped;
-	deleted = children_deleted;
-
-	while (deleted < reaped) {
-		pid_t pid = dead_child[deleted % MAX_CHILDREN];
-		const char *dead = pid < 0 ? " (with error)" : "";
-
-		if (pid < 0)
-			pid = -pid;
-
-		/* XXX: Custom logging, since we don't wanna getpid() */
-		if (verbose) {
-			if (log_syslog)
-				syslog(LOG_INFO, "[%d] Disconnected%s",
-						pid, dead);
-			else
-				fprintf(stderr, "[%d] Disconnected%s\n",
-						pid, dead);
-		}
-		remove_child(pid, deleted, spawned);
-		deleted++;
-	}
-	children_deleted = deleted;
-}
-
-static void check_max_connections(void)
-{
-	for (;;) {
-		int active;
-		unsigned spawned, deleted;
-
-		check_dead_children();
-
-		spawned = children_spawned;
-		deleted = children_deleted;
-
-		active = spawned - deleted;
-		if (active <= max_connections)
-			break;
-
-		/* Kill some unstarted connections with SIGTERM */
-		kill_some_children(SIGTERM, deleted, spawned);
-		if (active <= max_connections << 1)
-			break;
-
-		/* If the SIGTERM thing isn't helping use SIGKILL */
-		kill_some_children(SIGKILL, deleted, spawned);
-		sleep(1);
+	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+		const char *dead = "";
+		remove_child(pid);
+		if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0))
+			dead = " (with error)";
+		loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
 	}
 }
 
 static void handle(int incoming, struct sockaddr *addr, int addrlen)
 {
-	pid_t pid = fork();
+	pid_t pid;
 
-	if (pid) {
-		unsigned idx;
-
-		close(incoming);
-		if (pid < 0)
+	if (max_connections && live_children >= max_connections) {
+		kill_some_child();
+		sleep(1);  /* give it some time to die */
+		check_dead_children();
+		if (live_children >= max_connections) {
+			close(incoming);
+			logerror("Too many children, dropping connection");
 			return;
+		}
+	}
 
-		idx = children_spawned % MAX_CHILDREN;
-		children_spawned++;
-		add_child(idx, pid, addr, addrlen);
+	if ((pid = fork())) {
+		close(incoming);
+		if (pid < 0) {
+			logerror("Couldn't fork %s", strerror(errno));
+			return;
+		}
 
-		check_max_connections();
+		add_child(pid, addr, addrlen);
 		return;
 	}
 
@@ -779,21 +678,11 @@
 
 static void child_handler(int signo)
 {
-	for (;;) {
-		int status;
-		pid_t pid = waitpid(-1, &status, WNOHANG);
-
-		if (pid > 0) {
-			unsigned reaped = children_reaped;
-			if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
-				pid = -pid;
-			dead_child[reaped % MAX_CHILDREN] = pid;
-			children_reaped = reaped + 1;
-			write(child_handler_pipe[1], &status, 1);
-			continue;
-		}
-		break;
-	}
+	/*
+	 * Otherwise empty handler because systemcalls will get interrupted
+	 * upon signal receipt
+	 * SysV needs the handler to be rearmed
+	 */
 	signal(SIGCHLD, child_handler);
 }
 
@@ -827,7 +716,7 @@
 
 	gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
 	if (gai)
-		die("getaddrinfo() failed: %s\n", gai_strerror(gai));
+		die("getaddrinfo() failed: %s", gai_strerror(gai));
 
 	for (ai = ai0; ai; ai = ai->ai_next) {
 		int sockfd;
@@ -836,7 +725,7 @@
 		if (sockfd < 0)
 			continue;
 		if (sockfd >= FD_SETSIZE) {
-			error("too large socket descriptor.");
+			logerror("Socket descriptor too large");
 			close(sockfd);
 			continue;
 		}
@@ -936,35 +825,28 @@
 	struct pollfd *pfd;
 	int i;
 
-	if (pipe(child_handler_pipe) < 0)
-		die ("Could not set up pipe for child handler");
-
-	pfd = xcalloc(socknum + 1, sizeof(struct pollfd));
+	pfd = xcalloc(socknum, sizeof(struct pollfd));
 
 	for (i = 0; i < socknum; i++) {
 		pfd[i].fd = socklist[i];
 		pfd[i].events = POLLIN;
 	}
-	pfd[socknum].fd = child_handler_pipe[0];
-	pfd[socknum].events = POLLIN;
 
 	signal(SIGCHLD, child_handler);
 
 	for (;;) {
 		int i;
 
-		if (poll(pfd, socknum + 1, -1) < 0) {
+		check_dead_children();
+
+		if (poll(pfd, socknum, -1) < 0) {
 			if (errno != EINTR) {
-				error("poll failed, resuming: %s",
+				logerror("Poll failed, resuming: %s",
 				      strerror(errno));
 				sleep(1);
 			}
 			continue;
 		}
-		if (pfd[socknum].revents & POLLIN) {
-			read(child_handler_pipe[0], &i, 1);
-			check_dead_children();
-		}
 
 		for (i = 0; i < socknum; i++) {
 			if (pfd[i].revents & POLLIN) {
@@ -1022,7 +904,7 @@
 	FILE *f = fopen(path, "w");
 	if (!f)
 		die("cannot open pid file %s: %s", path, strerror(errno));
-	if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0)
+	if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
 		die("failed to write pid file %s: %s", path, strerror(errno));
 }
 
@@ -1055,21 +937,14 @@
 	gid_t gid = 0;
 	int i;
 
-	/* Without this we cannot rely on waitpid() to tell
-	 * what happened to our children.
-	 */
-	signal(SIGCHLD, SIG_DFL);
+	git_extract_argv0_path(argv[0]);
 
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
 		if (!prefixcmp(arg, "--listen=")) {
-		    char *p = arg + 9;
-		    char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
-		    while (*p)
-			*ph++ = tolower(*p++);
-		    *ph = 0;
-		    continue;
+			listen_addr = xstrdup_tolower(arg + 9);
+			continue;
 		}
 		if (!prefixcmp(arg, "--port=")) {
 			char *end;
@@ -1105,6 +980,12 @@
 			init_timeout = atoi(arg+15);
 			continue;
 		}
+		if (!prefixcmp(arg, "--max-connections=")) {
+			max_connections = atoi(arg+18);
+			if (max_connections < 0)
+				max_connections = 0;	        /* unlimited */
+			continue;
+		}
 		if (!strcmp(arg, "--strict-paths")) {
 			strict_paths = 1;
 			continue;
@@ -1178,9 +1059,11 @@
 	}
 
 	if (log_syslog) {
-		openlog("git-daemon", 0, LOG_DAEMON);
+		openlog("git-daemon", LOG_PID, LOG_DAEMON);
 		set_die_routine(daemon_die);
-	}
+	} else
+		/* avoid splitting a message in the middle */
+		setvbuf(stderr, NULL, _IOLBF, 0);
 
 	if (inetd_mode && (group_name || user_name))
 		die("--user and --group are incompatible with --inetd");
@@ -1212,20 +1095,18 @@
 	if (strict_paths && (!ok_paths || !*ok_paths))
 		die("option --strict-paths requires a whitelist");
 
-	if (base_path) {
-		struct stat st;
-
-		if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
-			die("base-path '%s' does not exist or "
-			    "is not a directory", base_path);
-	}
+	if (base_path && !is_directory(base_path))
+		die("base-path '%s' does not exist or is not a directory",
+		    base_path);
 
 	if (inetd_mode) {
 		struct sockaddr_storage ss;
 		struct sockaddr *peer = (struct sockaddr *)&ss;
 		socklen_t slen = sizeof(ss);
 
-		freopen("/dev/null", "w", stderr);
+		if (!freopen("/dev/null", "w", stderr))
+			die("failed to redirect stderr to /dev/null: %s",
+			    strerror(errno));
 
 		if (getpeername(0, peer, &slen))
 			peer = NULL;
@@ -1233,8 +1114,10 @@
 		return execute(peer);
 	}
 
-	if (detach)
+	if (detach) {
 		daemonize();
+		loginfo("Ready to rumble");
+	}
 	else
 		sanitize_stdfds();
 
diff --git a/date.c b/date.c
index 950b88f..1165d30 100644
--- a/date.c
+++ b/date.c
@@ -89,6 +89,11 @@
 	struct tm *tm;
 	static char timebuf[200];
 
+	if (mode == DATE_RAW) {
+		snprintf(timebuf, sizeof(timebuf), "%lu %+05d", time, tz);
+		return timebuf;
+	}
+
 	if (mode == DATE_RELATIVE) {
 		unsigned long diff;
 		struct timeval now;
@@ -128,7 +133,25 @@
 			snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
 			return timebuf;
 		}
-		/* Else fall back on absolute format.. */
+		/* Give years and months for 5 years or so */
+		if (diff < 1825) {
+			unsigned long years = (diff + 183) / 365;
+			unsigned long months = (diff % 365 + 15) / 30;
+			int n;
+			n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
+					years, (years > 1 ? "s" : ""));
+			if (months)
+				snprintf(timebuf + n, sizeof(timebuf) - n,
+					", %lu month%s ago",
+					months, (months > 1 ? "s" : ""));
+			else
+				snprintf(timebuf + n, sizeof(timebuf) - n,
+					" ago");
+			return timebuf;
+		}
+		/* Otherwise, just years. Centuries is probably overkill. */
+		snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365);
+		return timebuf;
 	}
 
 	if (mode == DATE_LOCAL)
@@ -615,6 +638,8 @@
 		return DATE_LOCAL;
 	else if (!strcmp(format, "default"))
 		return DATE_NORMAL;
+	else if (!strcmp(format, "raw"))
+		return DATE_RAW;
 	else
 		die("unknown date format %s", format);
 }
diff --git a/diff-lib.c b/diff-lib.c
index e7eaff9..a310fb2 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -31,7 +31,7 @@
 			return -1;
 		return 1;
 	}
-	if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+	if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
 		return 1;
 	if (S_ISDIR(st->st_mode)) {
 		unsigned char sub[20];
@@ -61,12 +61,12 @@
 	int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
 	unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
 			      ? CE_MATCH_RACY_IS_DIRTY : 0);
-	char symcache[PATH_MAX];
+
+	diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
 
 	if (diff_unmerged_stage < 0)
 		diff_unmerged_stage = 2;
 	entries = active_nr;
-	symcache[0] = '\0';
 	for (i = 0; i < entries; i++) {
 		struct stat st;
 		unsigned int oldmode, newmode;
@@ -196,11 +196,6 @@
  * diff-index
  */
 
-struct oneway_unpack_data {
-	struct rev_info *revs;
-	char symcache[PATH_MAX];
-};
-
 /* A file entry went away or appeared */
 static void diff_index_show_file(struct rev_info *revs,
 				 const char *prefix,
@@ -214,8 +209,7 @@
 static int get_stat_data(struct cache_entry *ce,
 			 const unsigned char **sha1p,
 			 unsigned int *modep,
-			 int cached, int match_missing,
-			 struct oneway_unpack_data *cbdata)
+			 int cached, int match_missing)
 {
 	const unsigned char *sha1 = ce->sha1;
 	unsigned int mode = ce->ce_mode;
@@ -246,25 +240,24 @@
 	return 0;
 }
 
-static void show_new_file(struct oneway_unpack_data *cbdata,
+static void show_new_file(struct rev_info *revs,
 			  struct cache_entry *new,
 			  int cached, int match_missing)
 {
 	const unsigned char *sha1;
 	unsigned int mode;
-	struct rev_info *revs = cbdata->revs;
 
 	/*
 	 * New file in the index: it might actually be different in
 	 * the working copy.
 	 */
-	if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0)
+	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
 		return;
 
 	diff_index_show_file(revs, "+", new, sha1, mode);
 }
 
-static int show_modified(struct oneway_unpack_data *cbdata,
+static int show_modified(struct rev_info *revs,
 			 struct cache_entry *old,
 			 struct cache_entry *new,
 			 int report_missing,
@@ -272,9 +265,8 @@
 {
 	unsigned int mode, oldmode;
 	const unsigned char *sha1;
-	struct rev_info *revs = cbdata->revs;
 
-	if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) {
+	if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
 		if (report_missing)
 			diff_index_show_file(revs, "-", old,
 					     old->sha1, old->ce_mode);
@@ -342,8 +334,7 @@
 	struct cache_entry *idx,
 	struct cache_entry *tree)
 {
-	struct oneway_unpack_data *cbdata = o->unpack_data;
-	struct rev_info *revs = cbdata->revs;
+	struct rev_info *revs = o->unpack_data;
 	int match_missing, cached;
 
 	/*
@@ -366,7 +357,7 @@
 	 * Something added to the tree?
 	 */
 	if (!tree) {
-		show_new_file(cbdata, idx, cached, match_missing);
+		show_new_file(revs, idx, cached, match_missing);
 		return;
 	}
 
@@ -379,7 +370,7 @@
 	}
 
 	/* Show difference between old and new */
-	show_modified(cbdata, tree, idx, 1, cached, match_missing);
+	show_modified(revs, tree, idx, 1, cached, match_missing);
 }
 
 static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -416,8 +407,7 @@
 {
 	struct cache_entry *idx = src[0];
 	struct cache_entry *tree = src[1];
-	struct oneway_unpack_data *cbdata = o->unpack_data;
-	struct rev_info *revs = cbdata->revs;
+	struct rev_info *revs = o->unpack_data;
 
 	if (idx && ce_stage(idx))
 		skip_same_name(idx, o);
@@ -444,7 +434,6 @@
 	const char *tree_name;
 	struct unpack_trees_options opts;
 	struct tree_desc t;
-	struct oneway_unpack_data unpack_cb;
 
 	mark_merge_entries();
 
@@ -454,14 +443,12 @@
 	if (!tree)
 		return error("bad tree object %s", tree_name);
 
-	unpack_cb.revs = revs;
-	unpack_cb.symcache[0] = '\0';
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
 	opts.index_only = cached;
 	opts.merge = 1;
 	opts.fn = oneway_diff;
-	opts.unpack_data = &unpack_cb;
+	opts.unpack_data = revs;
 	opts.src_index = &the_index;
 	opts.dst_index = NULL;
 
@@ -469,6 +456,7 @@
 	if (unpack_trees(1, &t, &opts))
 		exit(128);
 
+	diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
 	return 0;
@@ -483,7 +471,6 @@
 	struct cache_entry *last = NULL;
 	struct unpack_trees_options opts;
 	struct tree_desc t;
-	struct oneway_unpack_data unpack_cb;
 
 	/*
 	 * This is used by git-blame to run diff-cache internally;
@@ -512,14 +499,12 @@
 	if (!tree)
 		die("bad tree object %s", sha1_to_hex(tree_sha1));
 
-	unpack_cb.revs = &revs;
-	unpack_cb.symcache[0] = '\0';
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
 	opts.index_only = 1;
 	opts.merge = 1;
 	opts.fn = oneway_diff;
-	opts.unpack_data = &unpack_cb;
+	opts.unpack_data = &revs;
 	opts.src_index = &the_index;
 	opts.dst_index = &the_index;
 
@@ -528,3 +513,18 @@
 		exit(128);
 	return 0;
 }
+
+int index_differs_from(const char *def, int diff_flags)
+{
+	struct rev_info rev;
+
+	init_revisions(&rev, NULL);
+	setup_revisions(0, NULL, &rev, def);
+	DIFF_OPT_SET(&rev.diffopt, QUIET);
+	DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+	rev.diffopt.flags |= diff_flags;
+	run_diff_index(&rev, 1);
+	if (rev.pending.alloc)
+		free(rev.pending.objects);
+	return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
+}
diff --git a/diff-no-index.c b/diff-no-index.c
index 7d68b7f..42c1dd8 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -38,9 +38,13 @@
 
 	if (!path || !strcmp(path, "/dev/null"))
 		*mode = 0;
+#ifdef _WIN32
+	else if (!strcasecmp(path, "nul"))
+		*mode = 0;
+#endif
 	else if (!strcmp(path, "-"))
 		*mode = create_ce_mode(0666);
-	else if (stat(path, &st))
+	else if (lstat(path, &st))
 		return error("Could not access '%s'", path);
 	else
 		*mode = st.st_mode;
@@ -173,8 +177,10 @@
 
 	/* Were we asked to do --no-index explicitly? */
 	for (i = 1; i < argc; i++) {
-		if (!strcmp(argv[i], "--"))
-			return;
+		if (!strcmp(argv[i], "--")) {
+			i++;
+			break;
+		}
 		if (!strcmp(argv[i], "--no-index"))
 			no_index = 1;
 		if (argv[i][0] != '-')
@@ -198,22 +204,17 @@
 		die("git diff %s takes two paths",
 		    no_index ? "--no-index" : "[--no-index]");
 
-	/*
-	 * If the user asked for our exit code then don't start a
-	 * pager or we would end up reporting its exit code instead.
-	 */
-	if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
-		setup_pager();
-
 	diff_setup(&revs->diffopt);
-	if (!revs->diffopt.output_format)
-		revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 	for (i = 1; i < argc - 2; ) {
 		int j;
 		if (!strcmp(argv[i], "--no-index"))
 			i++;
-		else if (!strcmp(argv[1], "-q"))
+		else if (!strcmp(argv[i], "-q")) {
 			options |= DIFF_SILENT_ON_REMOVED;
+			i++;
+		}
+		else if (!strcmp(argv[i], "--"))
+			i++;
 		else {
 			j = diff_opt_parse(&revs->diffopt, argv + i, argc - i);
 			if (!j)
@@ -222,6 +223,13 @@
 		}
 	}
 
+	/*
+	 * If the user asked for our exit code then don't start a
+	 * pager or we would end up reporting its exit code instead.
+	 */
+	if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
+		setup_pager();
+
 	if (prefix) {
 		int len = strlen(prefix);
 
@@ -241,6 +249,9 @@
 	else
 		revs->diffopt.paths = argv + argc - 2;
 	revs->diffopt.nr_paths = 2;
+	revs->diffopt.skip_stat_unmatch = 1;
+	if (!revs->diffopt.output_format)
+		revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 
 	DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
 	DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
@@ -252,6 +263,7 @@
 	if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
 		       revs->diffopt.paths[1]))
 		exit(1);
+	diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
 
diff --git a/diff.c b/diff.c
index 6fea3c0..3ac7168 100644
--- a/diff.c
+++ b/diff.c
@@ -11,6 +11,8 @@
 #include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
+#include "userdiff.h"
+#include "sigchain.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -20,21 +22,27 @@
 
 static int diff_detect_rename_default;
 static int diff_rename_limit_default = 200;
+static int diff_suppress_blank_empty;
 int diff_use_color_default = -1;
+static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
+static int diff_mnemonic_prefix;
 
 static char diff_colors[][COLOR_MAXLEN] = {
-	"\033[m",	/* reset */
-	"",		/* PLAIN (normal) */
-	"\033[1m",	/* METAINFO (bold) */
-	"\033[36m",	/* FRAGINFO (cyan) */
-	"\033[31m",	/* OLD (red) */
-	"\033[32m",	/* NEW (green) */
-	"\033[33m",	/* COMMIT (yellow) */
-	"\033[41m",	/* WHITESPACE (red background) */
+	GIT_COLOR_RESET,
+	GIT_COLOR_NORMAL,	/* PLAIN */
+	GIT_COLOR_BOLD,		/* METAINFO */
+	GIT_COLOR_CYAN,		/* FRAGINFO */
+	GIT_COLOR_RED,		/* OLD */
+	GIT_COLOR_GREEN,	/* NEW */
+	GIT_COLOR_YELLOW,	/* COMMIT */
+	GIT_COLOR_BG_RED,	/* WHITESPACE */
 };
 
+static void diff_filespec_load_driver(struct diff_filespec *one);
+static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+
 static int parse_diff_color_slot(const char *var, int ofs)
 {
 	if (!strcasecmp(var+ofs, "plain"))
@@ -54,78 +62,13 @@
 	die("bad config variable '%s'", var);
 }
 
-static struct ll_diff_driver {
-	const char *name;
-	struct ll_diff_driver *next;
-	const char *cmd;
-} *user_diff, **user_diff_tail;
-
-/*
- * Currently there is only "diff.<drivername>.command" variable;
- * because there are "diff.color.<slot>" variables, we are parsing
- * this in a bit convoluted way to allow low level diff driver
- * called "color".
- */
-static int parse_lldiff_command(const char *var, const char *ep, const char *value)
+static int git_config_rename(const char *var, const char *value)
 {
-	const char *name;
-	int namelen;
-	struct ll_diff_driver *drv;
-
-	name = var + 5;
-	namelen = ep - name;
-	for (drv = user_diff; drv; drv = drv->next)
-		if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
-			break;
-	if (!drv) {
-		drv = xcalloc(1, sizeof(struct ll_diff_driver));
-		drv->name = xmemdupz(name, namelen);
-		if (!user_diff_tail)
-			user_diff_tail = &user_diff;
-		*user_diff_tail = drv;
-		user_diff_tail = &(drv->next);
-	}
-
-	return git_config_string(&(drv->cmd), var, value);
-}
-
-/*
- * 'diff.<what>.funcname' attribute can be specified in the configuration
- * to define a customized regexp to find the beginning of a function to
- * be used for hunk header lines of "diff -p" style output.
- */
-struct funcname_pattern_entry {
-	char *name;
-	char *pattern;
-	int cflags;
-};
-static struct funcname_pattern_list {
-	struct funcname_pattern_list *next;
-	struct funcname_pattern_entry e;
-} *funcname_pattern_list;
-
-static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
-{
-	const char *name;
-	int namelen;
-	struct funcname_pattern_list *pp;
-
-	name = var + 5; /* "diff." */
-	namelen = ep - name;
-
-	for (pp = funcname_pattern_list; pp; pp = pp->next)
-		if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
-			break;
-	if (!pp) {
-		pp = xcalloc(1, sizeof(*pp));
-		pp->e.name = xmemdupz(name, namelen);
-		pp->next = funcname_pattern_list;
-		funcname_pattern_list = pp;
-	}
-	free(pp->e.pattern);
-	pp->e.pattern = xstrdup(value);
-	pp->e.cflags = cflags;
-	return 0;
+	if (!value)
+		return DIFF_DETECT_RENAME;
+	if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy"))
+		return  DIFF_DETECT_COPY;
+	return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
 }
 
 /*
@@ -141,27 +84,21 @@
 		return 0;
 	}
 	if (!strcmp(var, "diff.renames")) {
-		if (!value)
-			diff_detect_rename_default = DIFF_DETECT_RENAME;
-		else if (!strcasecmp(value, "copies") ||
-			 !strcasecmp(value, "copy"))
-			diff_detect_rename_default = DIFF_DETECT_COPY;
-		else if (git_config_bool(var,value))
-			diff_detect_rename_default = DIFF_DETECT_RENAME;
+		diff_detect_rename_default = git_config_rename(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "diff.autorefreshindex")) {
 		diff_auto_refresh_index = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "diff.mnemonicprefix")) {
+		diff_mnemonic_prefix = git_config_bool(var, value);
+		return 0;
+	}
 	if (!strcmp(var, "diff.external"))
 		return git_config_string(&external_diff_cmd_cfg, var, value);
-	if (!prefixcmp(var, "diff.")) {
-		const char *ep = strrchr(var, '.');
-
-		if (ep != var + 4 && !strcmp(ep, ".command"))
-			return parse_lldiff_command(var, ep, value);
-	}
+	if (!strcmp(var, "diff.wordregex"))
+		return git_config_string(&diff_word_regex_cfg, var, value);
 
 	return git_diff_basic_config(var, value, cb);
 }
@@ -173,6 +110,12 @@
 		return 0;
 	}
 
+	switch (userdiff_config(var, value)) {
+		case 0: break;
+		case -1: return -1;
+		default: return 0;
+	}
+
 	if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
 		int slot = parse_diff_color_slot(var, 11);
 		if (!value)
@@ -181,21 +124,12 @@
 		return 0;
 	}
 
-	if (!prefixcmp(var, "diff.")) {
-		const char *ep = strrchr(var, '.');
-		if (ep != var + 4) {
-			if (!strcmp(ep, ".funcname")) {
-				if (!value)
-					return config_error_nonbool(var);
-				return parse_funcname_pattern(var, ep, value,
-					0);
-			} else if (!strcmp(ep, ".xfuncname")) {
-				if (!value)
-					return config_error_nonbool(var);
-				return parse_funcname_pattern(var, ep, value,
-					REG_EXTENDED);
-			}
-		}
+	/* like GNU diff's --suppress-blank-empty option  */
+	if (!strcmp(var, "diff.suppressblankempty") ||
+			/* for backwards compatibility */
+			!strcmp(var, "diff.suppress-blank-empty")) {
+		diff_suppress_blank_empty = git_config_bool(var, value);
+		return 0;
 	}
 
 	return git_color_default_config(var, value, cb);
@@ -205,9 +139,8 @@
 {
 	int need_one = quote_c_style(one, NULL, NULL, 1);
 	int need_two = quote_c_style(two, NULL, NULL, 1);
-	struct strbuf res;
+	struct strbuf res = STRBUF_INIT;
 
-	strbuf_init(&res, 0);
 	if (need_one + need_two) {
 		strbuf_addch(&res, '"');
 		quote_c_style(one, &res, NULL, 1);
@@ -241,6 +174,33 @@
 	char tmp_path[PATH_MAX];
 } diff_temp[2];
 
+static struct diff_tempfile *claim_diff_tempfile(void) {
+	int i;
+	for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
+		if (!diff_temp[i].name)
+			return diff_temp + i;
+	die("BUG: diff is failing to clean up its tempfiles");
+}
+
+static int remove_tempfile_installed;
+
+static void remove_tempfile(void)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
+		if (diff_temp[i].name == diff_temp[i].tmp_path)
+			unlink(diff_temp[i].name);
+		diff_temp[i].name = NULL;
+	}
+}
+
+static void remove_tempfile_on_signal(int signo)
+{
+	remove_tempfile();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
 static int count_lines(const char *data, int size)
 {
 	int count, ch, completely_empty = 1, nl_just_seen = 0;
@@ -305,6 +265,8 @@
 			      const char *name_b,
 			      struct diff_filespec *one,
 			      struct diff_filespec *two,
+			      const char *textconv_one,
+			      const char *textconv_two,
 			      struct diff_options *o)
 {
 	int lc_a, lc_b;
@@ -316,6 +278,17 @@
 	const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
 	const char *reset = diff_get_color(color_diff, DIFF_RESET);
 	static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
+	const char *a_prefix, *b_prefix;
+	const char *data_one, *data_two;
+	size_t size_one, size_two;
+
+	if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
+		a_prefix = o->b_prefix;
+		b_prefix = o->a_prefix;
+	} else {
+		a_prefix = o->a_prefix;
+		b_prefix = o->b_prefix;
+	}
 
 	name_a += (*name_a == '/');
 	name_b += (*name_b == '/');
@@ -324,13 +297,32 @@
 
 	strbuf_reset(&a_name);
 	strbuf_reset(&b_name);
-	quote_two_c_style(&a_name, o->a_prefix, name_a, 0);
-	quote_two_c_style(&b_name, o->b_prefix, name_b, 0);
+	quote_two_c_style(&a_name, a_prefix, name_a, 0);
+	quote_two_c_style(&b_name, b_prefix, name_b, 0);
 
 	diff_populate_filespec(one, 0);
 	diff_populate_filespec(two, 0);
-	lc_a = count_lines(one->data, one->size);
-	lc_b = count_lines(two->data, two->size);
+	if (textconv_one) {
+		data_one = run_textconv(textconv_one, one, &size_one);
+		if (!data_one)
+			die("unable to read files to diff");
+	}
+	else {
+		data_one = one->data;
+		size_one = one->size;
+	}
+	if (textconv_two) {
+		data_two = run_textconv(textconv_two, two, &size_two);
+		if (!data_two)
+			die("unable to read files to diff");
+	}
+	else {
+		data_two = two->data;
+		size_two = two->size;
+	}
+
+	lc_a = count_lines(data_one, size_one);
+	lc_b = count_lines(data_two, size_two);
 	fprintf(o->file,
 		"%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
 		metainfo, a_name.buf, name_a_tab, reset,
@@ -340,9 +332,9 @@
 	print_line_count(o->file, lc_b);
 	fprintf(o->file, " @@%s\n", reset);
 	if (lc_a)
-		copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset);
+		copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset);
 	if (lc_b)
-		copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset);
+		copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset);
 }
 
 static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@ -354,6 +346,7 @@
 	}
 	else if (diff_populate_filespec(one, 0))
 		return -1;
+
 	mf->ptr = one->data;
 	mf->size = one->size;
 	return 0;
@@ -362,83 +355,138 @@
 struct diff_words_buffer {
 	mmfile_t text;
 	long alloc;
-	long current; /* output pointer */
-	int suppressed_newline;
+	struct diff_words_orig {
+		const char *begin, *end;
+	} *orig;
+	int orig_nr, orig_alloc;
 };
 
 static void diff_words_append(char *line, unsigned long len,
 		struct diff_words_buffer *buffer)
 {
-	if (buffer->text.size + len > buffer->alloc) {
-		buffer->alloc = (buffer->text.size + len) * 3 / 2;
-		buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
-	}
+	ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
 	line++;
 	len--;
 	memcpy(buffer->text.ptr + buffer->text.size, line, len);
 	buffer->text.size += len;
+	buffer->text.ptr[buffer->text.size] = '\0';
 }
 
 struct diff_words_data {
-	struct xdiff_emit_state xm;
 	struct diff_words_buffer minus, plus;
+	const char *current_plus;
 	FILE *file;
+	regex_t *word_regex;
 };
 
-static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
-		int suppress_newline)
-{
-	const char *ptr;
-	int eol = 0;
-
-	if (len == 0)
-		return;
-
-	ptr  = buffer->text.ptr + buffer->current;
-	buffer->current += len;
-
-	if (ptr[len - 1] == '\n') {
-		eol = 1;
-		len--;
-	}
-
-	fputs(diff_get_color(1, color), file);
-	fwrite(ptr, len, 1, file);
-	fputs(diff_get_color(1, DIFF_RESET), file);
-
-	if (eol) {
-		if (suppress_newline)
-			buffer->suppressed_newline = 1;
-		else
-			putc('\n', file);
-	}
-}
-
 static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 {
 	struct diff_words_data *diff_words = priv;
+	int minus_first, minus_len, plus_first, plus_len;
+	const char *minus_begin, *minus_end, *plus_begin, *plus_end;
 
-	if (diff_words->minus.suppressed_newline) {
-		if (line[0] != '+')
-			putc('\n', diff_words->file);
-		diff_words->minus.suppressed_newline = 0;
+	if (line[0] != '@' || parse_hunk_header(line, len,
+			&minus_first, &minus_len, &plus_first, &plus_len))
+		return;
+
+	/* POSIX requires that first be decremented by one if len == 0... */
+	if (minus_len) {
+		minus_begin = diff_words->minus.orig[minus_first].begin;
+		minus_end =
+			diff_words->minus.orig[minus_first + minus_len - 1].end;
+	} else
+		minus_begin = minus_end =
+			diff_words->minus.orig[minus_first].end;
+
+	if (plus_len) {
+		plus_begin = diff_words->plus.orig[plus_first].begin;
+		plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end;
+	} else
+		plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
+
+	if (diff_words->current_plus != plus_begin)
+		fwrite(diff_words->current_plus,
+				plus_begin - diff_words->current_plus, 1,
+				diff_words->file);
+	if (minus_begin != minus_end)
+		color_fwrite_lines(diff_words->file,
+				diff_get_color(1, DIFF_FILE_OLD),
+				minus_end - minus_begin, minus_begin);
+	if (plus_begin != plus_end)
+		color_fwrite_lines(diff_words->file,
+				diff_get_color(1, DIFF_FILE_NEW),
+				plus_end - plus_begin, plus_begin);
+
+	diff_words->current_plus = plus_end;
+}
+
+/* This function starts looking at *begin, and returns 0 iff a word was found. */
+static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
+		int *begin, int *end)
+{
+	if (word_regex && *begin < buffer->size) {
+		regmatch_t match[1];
+		if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+			char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
+					'\n', match[0].rm_eo - match[0].rm_so);
+			*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
+			*begin += match[0].rm_so;
+			return *begin >= *end;
+		}
+		return -1;
 	}
 
-	len--;
-	switch (line[0]) {
-		case '-':
-			print_word(diff_words->file,
-				   &diff_words->minus, len, DIFF_FILE_OLD, 1);
-			break;
-		case '+':
-			print_word(diff_words->file,
-				   &diff_words->plus, len, DIFF_FILE_NEW, 0);
-			break;
-		case ' ':
-			print_word(diff_words->file,
-				   &diff_words->plus, len, DIFF_PLAIN, 0);
-			diff_words->minus.current += len;
-			break;
+	/* find the next word */
+	while (*begin < buffer->size && isspace(buffer->ptr[*begin]))
+		(*begin)++;
+	if (*begin >= buffer->size)
+		return -1;
+
+	/* find the end of the word */
+	*end = *begin + 1;
+	while (*end < buffer->size && !isspace(buffer->ptr[*end]))
+		(*end)++;
+
+	return 0;
+}
+
+/*
+ * This function splits the words in buffer->text, stores the list with
+ * newline separator into out, and saves the offsets of the original words
+ * in buffer->orig.
+ */
+static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
+		regex_t *word_regex)
+{
+	int i, j;
+	long alloc = 0;
+
+	out->size = 0;
+	out->ptr = NULL;
+
+	/* fake an empty "0th" word */
+	ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
+	buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr;
+	buffer->orig_nr = 1;
+
+	for (i = 0; i < buffer->text.size; i++) {
+		if (find_word_boundaries(&buffer->text, word_regex, &i, &j))
+			return;
+
+		/* store original boundaries */
+		ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
+				buffer->orig_alloc);
+		buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i;
+		buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j;
+		buffer->orig_nr++;
+
+		/* store one word */
+		ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc);
+		memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
+		out->ptr[out->size + j - i] = '\n';
+		out->size += j - i + 1;
+
+		i = j - 1;
 	}
 }
 
@@ -449,46 +497,41 @@
 	xdemitconf_t xecfg;
 	xdemitcb_t ecb;
 	mmfile_t minus, plus;
-	int i;
 
+	/* special case: only removal */
+	if (!diff_words->plus.text.size) {
+		color_fwrite_lines(diff_words->file,
+			diff_get_color(1, DIFF_FILE_OLD),
+			diff_words->minus.text.size, diff_words->minus.text.ptr);
+		diff_words->minus.text.size = 0;
+		return;
+	}
+
+	diff_words->current_plus = diff_words->plus.text.ptr;
+
+	memset(&xpp, 0, sizeof(xpp));
 	memset(&xecfg, 0, sizeof(xecfg));
-	minus.size = diff_words->minus.text.size;
-	minus.ptr = xmalloc(minus.size);
-	memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
-	for (i = 0; i < minus.size; i++)
-		if (isspace(minus.ptr[i]))
-			minus.ptr[i] = '\n';
-	diff_words->minus.current = 0;
-
-	plus.size = diff_words->plus.text.size;
-	plus.ptr = xmalloc(plus.size);
-	memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
-	for (i = 0; i < plus.size; i++)
-		if (isspace(plus.ptr[i]))
-			plus.ptr[i] = '\n';
-	diff_words->plus.current = 0;
-
+	diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
+	diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
 	xpp.flags = XDF_NEED_MINIMAL;
-	xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
-	ecb.outf = xdiff_outf;
-	ecb.priv = diff_words;
-	diff_words->xm.consume = fn_out_diff_words_aux;
-	xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
+	/* as only the hunk header will be parsed, we need a 0-context */
+	xecfg.ctxlen = 0;
+	xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
+		      &xpp, &xecfg, &ecb);
 	free(minus.ptr);
 	free(plus.ptr);
+	if (diff_words->current_plus != diff_words->plus.text.ptr +
+			diff_words->plus.text.size)
+		fwrite(diff_words->current_plus,
+			diff_words->plus.text.ptr + diff_words->plus.text.size
+			- diff_words->current_plus, 1,
+			diff_words->file);
 	diff_words->minus.text.size = diff_words->plus.text.size = 0;
-
-	if (diff_words->minus.suppressed_newline) {
-		putc('\n', diff_words->file);
-		diff_words->minus.suppressed_newline = 0;
-	}
 }
 
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
 
 struct emit_callback {
-	struct xdiff_emit_state xm;
 	int nparents, color_diff;
 	unsigned ws_rule;
 	sane_truncate_fn truncate;
@@ -507,7 +550,10 @@
 			diff_words_show(ecbdata->diff_words);
 
 		free (ecbdata->diff_words->minus.text.ptr);
+		free (ecbdata->diff_words->minus.orig);
 		free (ecbdata->diff_words->plus.text.ptr);
+		free (ecbdata->diff_words->plus.orig);
+		free(ecbdata->diff_words->word_regex);
 		free(ecbdata->diff_words);
 		ecbdata->diff_words = NULL;
 	}
@@ -598,6 +644,12 @@
 		ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
 	}
 
+	if (diff_suppress_blank_empty
+	    && len == 2 && line[0] == ' ' && line[1] == '\n') {
+		line[0] = '\n';
+		len = 1;
+	}
+
 	/* This is not really necessary for now because
 	 * this codepath only deals with two-way diffs.
 	 */
@@ -661,7 +713,7 @@
 {
 	const char *old = a;
 	const char *new = b;
-	struct strbuf name;
+	struct strbuf name = STRBUF_INIT;
 	int pfx_length, sfx_length;
 	int len_a = strlen(a);
 	int len_b = strlen(b);
@@ -669,7 +721,6 @@
 	int qlen_a = quote_c_style(a, NULL, NULL, 0);
 	int qlen_b = quote_c_style(b, NULL, NULL, 0);
 
-	strbuf_init(&name, 0);
 	if (qlen_a || qlen_b) {
 		quote_c_style(a, &name, NULL, 0);
 		strbuf_addstr(&name, " => ");
@@ -726,8 +777,6 @@
 }
 
 struct diffstat_t {
-	struct xdiff_emit_state xm;
-
 	int nr;
 	int alloc;
 	struct diffstat_file {
@@ -814,8 +863,7 @@
 		return;
 
 	if (!file->is_renamed) {
-		struct strbuf buf;
-		strbuf_init(&buf, 0);
+		struct strbuf buf = STRBUF_INIT;
 		if (quote_c_style(file->name, &buf, NULL, 0)) {
 			pname = strbuf_detach(&buf, NULL);
 		} else {
@@ -830,7 +878,7 @@
 
 static void show_stats(struct diffstat_t* data, struct diff_options *options)
 {
-	int i, len, add, del, total, adds = 0, dels = 0;
+	int i, len, add, del, adds = 0, dels = 0;
 	int max_change = 0, max_len = 0;
 	int total_files = data->nr;
 	int width, name_width;
@@ -933,14 +981,12 @@
 		 */
 		add = added;
 		del = deleted;
-		total = add + del;
 		adds += add;
 		dels += del;
 
 		if (width <= max_change) {
 			add = scale_linear(add, width, max_change);
 			del = scale_linear(del, width, max_change);
-			total = add + del;
 		}
 		show_name(options->file, prefix, name, len, reset, set);
 		fprintf(options->file, "%5d%s", added + deleted,
@@ -1122,9 +1168,13 @@
 		/*
 		 * Original minus copied is the removed material,
 		 * added is the new material.  They are both damages
-		 * made to the preimage.
+		 * made to the preimage. In --dirstat-by-file mode, count
+		 * damaged files, not damaged lines. This is done by
+		 * counting only a single damaged line per file.
 		 */
 		damage = (p->one->size - copied) + added;
+		if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
+			damage = 1;
 
 		ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
 		dir.files[dir.nr].name = name;
@@ -1157,7 +1207,6 @@
 }
 
 struct checkdiff_t {
-	struct xdiff_emit_state xm;
 	const char *filename;
 	int lineno;
 	struct diff_options *o;
@@ -1332,123 +1381,61 @@
 	emit_binary_diff_body(file, two, one);
 }
 
-static void setup_diff_attr_check(struct git_attr_check *check)
+static void diff_filespec_load_driver(struct diff_filespec *one)
 {
-	static struct git_attr *attr_diff;
-
-	if (!attr_diff) {
-		attr_diff = git_attr("diff", 4);
-	}
-	check[0].attr = attr_diff;
-}
-
-static void diff_filespec_check_attr(struct diff_filespec *one)
-{
-	struct git_attr_check attr_diff_check;
-	int check_from_data = 0;
-
-	if (one->checked_attr)
-		return;
-
-	setup_diff_attr_check(&attr_diff_check);
-	one->is_binary = 0;
-	one->funcname_pattern_ident = NULL;
-
-	if (!git_checkattr(one->path, 1, &attr_diff_check)) {
-		const char *value;
-
-		/* binaryness */
-		value = attr_diff_check.value;
-		if (ATTR_TRUE(value))
-			;
-		else if (ATTR_FALSE(value))
-			one->is_binary = 1;
-		else
-			check_from_data = 1;
-
-		/* funcname pattern ident */
-		if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
-			;
-		else
-			one->funcname_pattern_ident = value;
-	}
-
-	if (check_from_data) {
-		if (!one->data && DIFF_FILE_VALID(one))
-			diff_populate_filespec(one, 0);
-
-		if (one->data)
-			one->is_binary = buffer_is_binary(one->data, one->size);
-	}
+	if (!one->driver)
+		one->driver = userdiff_find_by_path(one->path);
+	if (!one->driver)
+		one->driver = userdiff_find_by_name("default");
 }
 
 int diff_filespec_is_binary(struct diff_filespec *one)
 {
-	diff_filespec_check_attr(one);
+	if (one->is_binary == -1) {
+		diff_filespec_load_driver(one);
+		if (one->driver->binary != -1)
+			one->is_binary = one->driver->binary;
+		else {
+			if (!one->data && DIFF_FILE_VALID(one))
+				diff_populate_filespec(one, 0);
+			if (one->data)
+				one->is_binary = buffer_is_binary(one->data,
+						one->size);
+			if (one->is_binary == -1)
+				one->is_binary = 0;
+		}
+	}
 	return one->is_binary;
 }
 
-static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
+static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
 {
-	struct funcname_pattern_list *pp;
-
-	for (pp = funcname_pattern_list; pp; pp = pp->next)
-		if (!strcmp(ident, pp->e.name))
-			return &pp->e;
-	return NULL;
+	diff_filespec_load_driver(one);
+	return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
-static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
-	{ "java",
-	  "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-	  "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
-	  REG_EXTENDED },
-	{ "pascal",
-	  "^((procedure|function|constructor|destructor|interface|"
-		"implementation|initialization|finalization)[ \t]*.*)$"
-	  "|"
-	  "^(.*=[ \t]*(class|record).*)$",
-	  REG_EXTENDED },
-	{ "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
-	  REG_EXTENDED },
-	{ "tex",
-	  "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
-	  REG_EXTENDED },
-	{ "ruby", "^[ \t]*((class|module|def)[ \t].*)$",
-	  REG_EXTENDED },
-};
-
-static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
+static const char *userdiff_word_regex(struct diff_filespec *one)
 {
-	const char *ident;
-	const struct funcname_pattern_entry *pe;
-	int i;
+	diff_filespec_load_driver(one);
+	return one->driver->word_regex;
+}
 
-	diff_filespec_check_attr(one);
-	ident = one->funcname_pattern_ident;
+void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
+{
+	if (!options->a_prefix)
+		options->a_prefix = a;
+	if (!options->b_prefix)
+		options->b_prefix = b;
+}
 
-	if (!ident)
-		/*
-		 * If the config file has "funcname.default" defined, that
-		 * regexp is used; otherwise NULL is returned and xemit uses
-		 * the built-in default.
-		 */
-		return funcname_pattern("default");
-
-	/* Look up custom "funcname.$ident" regexp from config. */
-	pe = funcname_pattern(ident);
-	if (pe)
-		return pe;
-
-	/*
-	 * And define built-in fallback patterns here.  Note that
-	 * these can be overridden by the user's config settings.
-	 */
-	for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
-		if (!strcmp(ident, builtin_funcname_pattern[i].name))
-			return &builtin_funcname_pattern[i];
-
-	return NULL;
+static const char *get_textconv(struct diff_filespec *one)
+{
+	if (!DIFF_FILE_VALID(one))
+		return NULL;
+	if (!S_ISREG(one->mode))
+		return NULL;
+	diff_filespec_load_driver(one);
+	return one->driver->textconv;
 }
 
 static void builtin_diff(const char *name_a,
@@ -1464,13 +1451,29 @@
 	char *a_one, *b_two;
 	const char *set = diff_get_color_opt(o, DIFF_METAINFO);
 	const char *reset = diff_get_color_opt(o, DIFF_RESET);
+	const char *a_prefix, *b_prefix;
+	const char *textconv_one = NULL, *textconv_two = NULL;
+
+	if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+		textconv_one = get_textconv(one);
+		textconv_two = get_textconv(two);
+	}
+
+	diff_set_mnemonic_prefix(o, "a/", "b/");
+	if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+		a_prefix = o->b_prefix;
+		b_prefix = o->a_prefix;
+	} else {
+		a_prefix = o->a_prefix;
+		b_prefix = o->b_prefix;
+	}
 
 	/* Never use a non-valid filename anywhere if at all possible */
 	name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
 	name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
 
-	a_one = quote_two(o->a_prefix, name_a + (*name_a == '/'));
-	b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
+	a_one = quote_two(a_prefix, name_a + (*name_a == '/'));
+	b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
 	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
 	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
 	fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
@@ -1498,8 +1501,11 @@
 		 */
 		if ((one->mode ^ two->mode) & S_IFMT)
 			goto free_ab_and_return;
-		if (complete_rewrite) {
-			emit_rewrite_diff(name_a, name_b, one, two, o);
+		if (complete_rewrite &&
+		    (textconv_one || !diff_filespec_is_binary(one)) &&
+		    (textconv_two || !diff_filespec_is_binary(two))) {
+			emit_rewrite_diff(name_a, name_b, one, two,
+						textconv_one, textconv_two, o);
 			o->found_changes = 1;
 			goto free_ab_and_return;
 		}
@@ -1509,7 +1515,8 @@
 		die("unable to read files to diff");
 
 	if (!DIFF_OPT_TST(o, TEXT) &&
-	    (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
+	    ( (diff_filespec_is_binary(one) && !textconv_one) ||
+	      (diff_filespec_is_binary(two) && !textconv_two) )) {
 		/* Quite common confusing case */
 		if (mf1.size == mf2.size &&
 		    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1528,12 +1535,28 @@
 		xdemitconf_t xecfg;
 		xdemitcb_t ecb;
 		struct emit_callback ecbdata;
-		const struct funcname_pattern_entry *pe;
+		const struct userdiff_funcname *pe;
+
+		if (textconv_one) {
+			size_t size;
+			mf1.ptr = run_textconv(textconv_one, one, &size);
+			if (!mf1.ptr)
+				die("unable to read files to diff");
+			mf1.size = size;
+		}
+		if (textconv_two) {
+			size_t size;
+			mf2.ptr = run_textconv(textconv_two, two, &size);
+			if (!mf2.ptr)
+				die("unable to read files to diff");
+			mf2.size = size;
+		}
 
 		pe = diff_funcname_pattern(one);
 		if (!pe)
 			pe = diff_funcname_pattern(two);
 
+		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
 		memset(&ecbdata, 0, sizeof(ecbdata));
 		ecbdata.label_path = lbl;
@@ -1543,6 +1566,7 @@
 		ecbdata.file = o->file;
 		xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
 		xecfg.ctxlen = o->context;
+		xecfg.interhunkctxlen = o->interhunkcontext;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
 		if (pe)
 			xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
@@ -1552,17 +1576,34 @@
 			xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 		else if (!prefixcmp(diffopts, "-u"))
 			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-		ecb.outf = xdiff_outf;
-		ecb.priv = &ecbdata;
-		ecbdata.xm.consume = fn_out_consume;
 		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
 			ecbdata.diff_words =
 				xcalloc(1, sizeof(struct diff_words_data));
 			ecbdata.diff_words->file = o->file;
+			if (!o->word_regex)
+				o->word_regex = userdiff_word_regex(one);
+			if (!o->word_regex)
+				o->word_regex = userdiff_word_regex(two);
+			if (!o->word_regex)
+				o->word_regex = diff_word_regex_cfg;
+			if (o->word_regex) {
+				ecbdata.diff_words->word_regex = (regex_t *)
+					xmalloc(sizeof(regex_t));
+				if (regcomp(ecbdata.diff_words->word_regex,
+						o->word_regex,
+						REG_EXTENDED | REG_NEWLINE))
+					die ("Invalid regular expression: %s",
+							o->word_regex);
+			}
 		}
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
+			      &xpp, &xecfg, &ecb);
 		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
 			free_diff_words_data(&ecbdata);
+		if (textconv_one)
+			free(mf1.ptr);
+		if (textconv_two)
+			free(mf2.ptr);
 	}
 
  free_ab_and_return:
@@ -1609,11 +1650,11 @@
 		xdemitconf_t xecfg;
 		xdemitcb_t ecb;
 
+		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
 		xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
-		ecb.outf = xdiff_outf;
-		ecb.priv = diffstat;
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
+			      &xpp, &xecfg, &ecb);
 	}
 
  free_and_return:
@@ -1634,7 +1675,6 @@
 		return;
 
 	memset(&data, 0, sizeof(data));
-	data.xm.consume = checkdiff_consume;
 	data.filename = name_b ? name_b : name_a;
 	data.lineno = 0;
 	data.o = o;
@@ -1657,12 +1697,12 @@
 		xdemitconf_t xecfg;
 		xdemitcb_t ecb;
 
+		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
 		xecfg.ctxlen = 1; /* at least one context line */
 		xpp.flags = XDF_NEED_MINIMAL;
-		ecb.outf = xdiff_outf;
-		ecb.priv = &data;
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
+			      &xpp, &xecfg, &ecb);
 
 		if ((data.ws_rule & WS_TRAILING_SPACE) &&
 		    data.trailing_blanks_start) {
@@ -1687,6 +1727,7 @@
 	spec->path = (char *)(spec + 1);
 	memcpy(spec->path, path, namelen+1);
 	spec->count = 1;
+	spec->is_binary = -1;
 	return spec;
 }
 
@@ -1719,7 +1760,8 @@
 	struct stat st;
 	int pos, len;
 
-	/* We do not read the cache ourselves here, because the
+	/*
+	 * We do not read the cache ourselves here, because the
 	 * benchmark with my previous version that always reads cache
 	 * shows that it makes things worse for diff-tree comparing
 	 * two linux-2.6 kernel trees in an already checked out work
@@ -1743,7 +1785,7 @@
 	 * objects however would tend to be slower as they need
 	 * to be individually opened and inflated.
 	 */
-	if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL))
+	if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
 		return 0;
 
 	len = strlen(name);
@@ -1760,6 +1802,13 @@
 		return 0;
 
 	/*
+	 * If ce is marked as "assume unchanged", there is no
+	 * guarantee that work tree matches what we are looking for.
+	 */
+	if (ce->ce_flags & CE_VALID)
+		return 0;
+
+	/*
 	 * If ce matches the file in the work tree, we can reuse it.
 	 */
 	if (ce_uptodate(ce) ||
@@ -1771,10 +1820,9 @@
 
 static int populate_from_stdin(struct diff_filespec *s)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	size_t size = 0;
 
-	strbuf_init(&buf, 0);
 	if (strbuf_read(&buf, 0, 0) < 0)
 		return error("error while reading from stdin %s",
 				     strerror(errno));
@@ -1826,7 +1874,7 @@
 
 	if (!s->sha1_valid ||
 	    reuse_worktree_file(s->path, s->sha1, 0)) {
-		struct strbuf buf;
+		struct strbuf buf = STRBUF_INIT;
 		struct stat st;
 		int fd;
 
@@ -1846,19 +1894,18 @@
 		s->size = xsize_t(st.st_size);
 		if (!s->size)
 			goto empty;
-		if (size_only)
-			return 0;
 		if (S_ISLNK(st.st_mode)) {
-			int ret;
-			s->data = xmalloc(s->size);
-			s->should_free = 1;
-			ret = readlink(s->path, s->data, s->size);
-			if (ret < 0) {
-				free(s->data);
+			struct strbuf sb = STRBUF_INIT;
+
+			if (strbuf_readlink(&sb, s->path, s->size))
 				goto err_empty;
-			}
+			s->size = sb.len;
+			s->data = strbuf_detach(&sb, NULL);
+			s->should_free = 1;
 			return 0;
 		}
+		if (size_only)
+			return 0;
 		fd = open(s->path, O_RDONLY);
 		if (fd < 0)
 			goto err_empty;
@@ -1869,7 +1916,6 @@
 		/*
 		 * Convert from working tree format to canonical git format
 		 */
-		strbuf_init(&buf, 0);
 		if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
 			size_t size = 0;
 			munmap(s->data, s->size);
@@ -1911,17 +1957,23 @@
 	s->cnt_data = NULL;
 }
 
-static void prep_temp_blob(struct diff_tempfile *temp,
+static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
 			   void *blob,
 			   unsigned long size,
 			   const unsigned char *sha1,
 			   int mode)
 {
 	int fd;
+	struct strbuf buf = STRBUF_INIT;
 
 	fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX");
 	if (fd < 0)
 		die("unable to create temp-file: %s", strerror(errno));
+	if (convert_to_working_tree(path,
+			(const char *)blob, (size_t)size, &buf)) {
+		blob = buf.buf;
+		size = buf.len;
+	}
 	if (write_in_full(fd, blob, size) != size)
 		die("unable to write temp-file");
 	close(fd);
@@ -1929,12 +1981,14 @@
 	strcpy(temp->hex, sha1_to_hex(sha1));
 	temp->hex[40] = 0;
 	sprintf(temp->mode, "%06o", mode);
+	strbuf_release(&buf);
 }
 
-static void prepare_temp_file(const char *name,
-			      struct diff_tempfile *temp,
-			      struct diff_filespec *one)
+static struct diff_tempfile *prepare_temp_file(const char *name,
+		struct diff_filespec *one)
 {
+	struct diff_tempfile *temp = claim_diff_tempfile();
+
 	if (!DIFF_FILE_VALID(one)) {
 	not_a_valid_file:
 		/* A '-' entry produces this for file-2, and
@@ -1943,7 +1997,13 @@
 		temp->name = "/dev/null";
 		strcpy(temp->hex, ".");
 		strcpy(temp->mode, ".");
-		return;
+		return temp;
+	}
+
+	if (!remove_tempfile_installed) {
+		atexit(remove_tempfile);
+		sigchain_push_common(remove_tempfile_on_signal);
+		remove_tempfile_installed = 1;
 	}
 
 	if (!one->sha1_valid ||
@@ -1957,13 +2017,12 @@
 		if (S_ISLNK(st.st_mode)) {
 			int ret;
 			char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
-			size_t sz = xsize_t(st.st_size);
-			if (sizeof(buf) <= st.st_size)
-				die("symlink too long: %s", name);
-			ret = readlink(name, buf, sz);
+			ret = readlink(name, buf, sizeof(buf));
 			if (ret < 0)
 				die("readlink(%s)", name);
-			prep_temp_blob(temp, buf, sz,
+			if (ret == sizeof(buf))
+				die("symlink too long: %s", name);
+			prep_temp_blob(name, temp, buf, ret,
 				       (one->sha1_valid ?
 					one->sha1 : null_sha1),
 				       (one->sha1_valid ?
@@ -1984,32 +2043,15 @@
 			 */
 			sprintf(temp->mode, "%06o", one->mode);
 		}
-		return;
+		return temp;
 	}
 	else {
 		if (diff_populate_filespec(one, 0))
 			die("cannot read data blob for %s", one->path);
-		prep_temp_blob(temp, one->data, one->size,
+		prep_temp_blob(name, temp, one->data, one->size,
 			       one->sha1, one->mode);
 	}
-}
-
-static void remove_tempfile(void)
-{
-	int i;
-
-	for (i = 0; i < 2; i++)
-		if (diff_temp[i].name == diff_temp[i].tmp_path) {
-			unlink(diff_temp[i].name);
-			diff_temp[i].name = NULL;
-		}
-}
-
-static void remove_tempfile_on_signal(int signo)
-{
-	remove_tempfile();
-	signal(SIGINT, SIG_DFL);
-	raise(signo);
+	return temp;
 }
 
 /* An external diff command takes:
@@ -2027,34 +2069,22 @@
 			      int complete_rewrite)
 {
 	const char *spawn_arg[10];
-	struct diff_tempfile *temp = diff_temp;
 	int retval;
-	static int atexit_asked = 0;
-	const char *othername;
 	const char **arg = &spawn_arg[0];
 
-	othername = (other? other : name);
 	if (one && two) {
-		prepare_temp_file(name, &temp[0], one);
-		prepare_temp_file(othername, &temp[1], two);
-		if (! atexit_asked &&
-		    (temp[0].name == temp[0].tmp_path ||
-		     temp[1].name == temp[1].tmp_path)) {
-			atexit_asked = 1;
-			atexit(remove_tempfile);
-		}
-		signal(SIGINT, remove_tempfile_on_signal);
-	}
-
-	if (one && two) {
+		struct diff_tempfile *temp_one, *temp_two;
+		const char *othername = (other ? other : name);
+		temp_one = prepare_temp_file(name, one);
+		temp_two = prepare_temp_file(othername, two);
 		*arg++ = pgm;
 		*arg++ = name;
-		*arg++ = temp[0].name;
-		*arg++ = temp[0].hex;
-		*arg++ = temp[0].mode;
-		*arg++ = temp[1].name;
-		*arg++ = temp[1].hex;
-		*arg++ = temp[1].mode;
+		*arg++ = temp_one->name;
+		*arg++ = temp_one->hex;
+		*arg++ = temp_one->mode;
+		*arg++ = temp_two->name;
+		*arg++ = temp_two->hex;
+		*arg++ = temp_two->mode;
 		if (other) {
 			*arg++ = other;
 			*arg++ = xfrm_msg;
@@ -2073,29 +2103,6 @@
 	}
 }
 
-static const char *external_diff_attr(const char *name)
-{
-	struct git_attr_check attr_diff_check;
-
-	if (!name)
-		return NULL;
-
-	setup_diff_attr_check(&attr_diff_check);
-	if (!git_checkattr(name, 1, &attr_diff_check)) {
-		const char *value = attr_diff_check.value;
-		if (!ATTR_TRUE(value) &&
-		    !ATTR_FALSE(value) &&
-		    !ATTR_UNSET(value)) {
-			struct ll_diff_driver *drv;
-
-			for (drv = user_diff; drv; drv = drv->next)
-				if (!strcmp(drv->name, value))
-					return drv->cmd;
-		}
-	}
-	return NULL;
-}
-
 static int similarity_index(struct diff_filepair *p)
 {
 	return p->score * 100 / MAX_SCORE;
@@ -2179,9 +2186,9 @@
 	if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
 		pgm = NULL;
 	else {
-		const char *cmd = external_diff_attr(attr_path);
-		if (cmd)
-			pgm = cmd;
+		struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
+		if (drv && drv->external)
+			pgm = drv->external;
 	}
 
 	if (pgm) {
@@ -2208,7 +2215,7 @@
 			if (lstat(one->path, &st) < 0)
 				die("stat %s", one->path);
 			if (index_path(one->sha1, one->path, &st, 0))
-				die("cannot hash %s\n", one->path);
+				die("cannot hash %s", one->path);
 		}
 	}
 	else
@@ -2335,19 +2342,18 @@
 	options->break_opt = -1;
 	options->rename_limit = -1;
 	options->dirstat_percent = 3;
-	DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
 	options->context = 3;
 
 	options->change = diff_change;
 	options->add_remove = diff_addremove;
 	if (diff_use_color_default > 0)
 		DIFF_OPT_SET(options, COLOR_DIFF);
-	else
-		DIFF_OPT_CLR(options, COLOR_DIFF);
 	options->detect_rename = diff_detect_rename_default;
 
-	options->a_prefix = "a/";
-	options->b_prefix = "b/";
+	if (!diff_mnemonic_prefix) {
+		options->a_prefix = "a/";
+		options->b_prefix = "b/";
+	}
 }
 
 int diff_setup_done(struct diff_options *options)
@@ -2504,6 +2510,10 @@
 	else if (!strcmp(arg, "--cumulative")) {
 		options->output_format |= DIFF_FORMAT_DIRSTAT;
 		DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+	} else if (opt_arg(arg, 0, "dirstat-by-file",
+			   &options->dirstat_percent)) {
+		options->output_format |= DIFF_FORMAT_DIRSTAT;
+		DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
 	}
 	else if (!strcmp(arg, "--check"))
 		options->output_format |= DIFF_FORMAT_CHECKDIFF;
@@ -2573,11 +2583,13 @@
 
 	/* xdiff options */
 	else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
-		options->xdl_opts |= XDF_IGNORE_WHITESPACE;
+		DIFF_XDL_SET(options, IGNORE_WHITESPACE);
 	else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
-		options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+		DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
 	else if (!strcmp(arg, "--ignore-space-at-eol"))
-		options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
+		DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+	else if (!strcmp(arg, "--patience"))
+		DIFF_XDL_SET(options, PATIENCE_DIFF);
 
 	/* flags options */
 	else if (!strcmp(arg, "--binary")) {
@@ -2598,8 +2610,15 @@
 		DIFF_OPT_SET(options, COLOR_DIFF);
 	else if (!strcmp(arg, "--no-color"))
 		DIFF_OPT_CLR(options, COLOR_DIFF);
-	else if (!strcmp(arg, "--color-words"))
-		options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+	else if (!strcmp(arg, "--color-words")) {
+		DIFF_OPT_SET(options, COLOR_DIFF);
+		DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+	}
+	else if (!prefixcmp(arg, "--color-words=")) {
+		DIFF_OPT_SET(options, COLOR_DIFF);
+		DIFF_OPT_SET(options, COLOR_DIFF_WORDS);
+		options->word_regex = arg + 14;
+	}
 	else if (!strcmp(arg, "--exit-code"))
 		DIFF_OPT_SET(options, EXIT_WITH_STATUS);
 	else if (!strcmp(arg, "--quiet"))
@@ -2608,6 +2627,10 @@
 		DIFF_OPT_SET(options, ALLOW_EXTERNAL);
 	else if (!strcmp(arg, "--no-ext-diff"))
 		DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
+	else if (!strcmp(arg, "--textconv"))
+		DIFF_OPT_SET(options, ALLOW_TEXTCONV);
+	else if (!strcmp(arg, "--no-textconv"))
+		DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
 	else if (!strcmp(arg, "--ignore-submodules"))
 		DIFF_OPT_SET(options, IGNORE_SUBMODULES);
 
@@ -2641,6 +2664,9 @@
 		options->b_prefix = arg + 13;
 	else if (!strcmp(arg, "--no-prefix"))
 		options->a_prefix = options->b_prefix = "";
+	else if (opt_arg(arg, '\0', "inter-hunk-context",
+			 &options->interhunkcontext))
+		;
 	else if (!prefixcmp(arg, "--output=")) {
 		options->file = fopen(arg + strlen("--output="), "w");
 		options->close_file = 1;
@@ -3060,8 +3086,7 @@
 }
 
 struct patch_id_t {
-	struct xdiff_emit_state xm;
-	SHA_CTX *ctx;
+	git_SHA_CTX *ctx;
 	int patchlen;
 };
 
@@ -3089,7 +3114,7 @@
 
 	new_len = remove_space(line, len);
 
-	SHA1_Update(data->ctx, line, new_len);
+	git_SHA1_Update(data->ctx, line, new_len);
 	data->patchlen += new_len;
 }
 
@@ -3098,14 +3123,13 @@
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
 	int i;
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	struct patch_id_t data;
 	char buffer[PATH_MAX * 4 + 20];
 
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 	memset(&data, 0, sizeof(struct patch_id_t));
 	data.ctx = &ctx;
-	data.xm.consume = patch_id_consume;
 
 	for (i = 0; i < q->nr; i++) {
 		xpparam_t xpp;
@@ -3115,6 +3139,7 @@
 		struct diff_filepair *p = q->queue[i];
 		int len1, len2;
 
+		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
 		if (p->status == 0)
 			return error("internal diff status error");
@@ -3165,17 +3190,16 @@
 					len2, p->two->path,
 					len1, p->one->path,
 					len2, p->two->path);
-		SHA1_Update(&ctx, buffer, len1);
+		git_SHA1_Update(&ctx, buffer, len1);
 
 		xpp.flags = XDF_NEED_MINIMAL;
 		xecfg.ctxlen = 3;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
-		ecb.outf = xdiff_outf;
-		ecb.priv = &data;
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
+			      &xpp, &xecfg, &ecb);
 	}
 
-	SHA1_Final(sha1, &ctx);
+	git_SHA1_Final(sha1, &ctx);
 	return 0;
 }
 
@@ -3249,7 +3273,6 @@
 		struct diffstat_t diffstat;
 
 		memset(&diffstat, 0, sizeof(struct diffstat_t));
-		diffstat.xm.consume = diffstat_consume;
 		for (i = 0; i < q->nr; i++) {
 			struct diff_filepair *p = q->queue[i];
 			if (check_pair_status(p))
@@ -3546,3 +3569,32 @@
 	fill_filespec(one, sha1, mode);
 	diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
 }
+
+static char *run_textconv(const char *pgm, struct diff_filespec *spec,
+		size_t *outsize)
+{
+	struct diff_tempfile *temp;
+	const char *argv[3];
+	const char **arg = argv;
+	struct child_process child;
+	struct strbuf buf = STRBUF_INIT;
+
+	temp = prepare_temp_file(spec->path, spec);
+	*arg++ = pgm;
+	*arg++ = temp->name;
+	*arg = NULL;
+
+	memset(&child, 0, sizeof(child));
+	child.argv = argv;
+	child.out = -1;
+	if (start_command(&child) != 0 ||
+	    strbuf_read(&buf, child.out, 0) < 0 ||
+	    finish_command(&child) != 0) {
+		remove_tempfile();
+		error("error running textconv command '%s'", pgm);
+		return NULL;
+	}
+	remove_tempfile();
+
+	return strbuf_detach(&buf, outsize);
+}
diff --git a/diff.h b/diff.h
index 7f53beb..6616877 100644
--- a/diff.h
+++ b/diff.h
@@ -64,9 +64,14 @@
 #define DIFF_OPT_RELATIVE_NAME       (1 << 17)
 #define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
 #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
+#define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
+#define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
 #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
+#define DIFF_XDL_TST(opts, flag)    ((opts)->xdl_opts & XDF_##flag)
+#define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
+#define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
 
 struct diff_options {
 	const char *filter;
@@ -76,6 +81,7 @@
 	const char *a_prefix, *b_prefix;
 	unsigned flags;
 	int context;
+	int interhunkcontext;
 	int break_opt;
 	int detect_rename;
 	int skip_stat_unmatch;
@@ -95,6 +101,7 @@
 
 	int stat_width;
 	int stat_name_width;
+	const char *word_regex;
 
 	/* this is set by diffcore for DIFF_FORMAT_PATCH */
 	int found_changes;
@@ -160,6 +167,8 @@
 
 extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
 
+void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
+
 extern void diff_addremove(struct diff_options *,
 			   int addremove,
 			   unsigned mode,
@@ -259,4 +268,6 @@
 
 extern void diff_no_index(struct rev_info *, int, const char **, int, const char *);
 
+extern int index_differs_from(const char *def, int diff_flags);
+
 #endif /* DIFF_H */
diff --git a/diffcore-break.c b/diffcore-break.c
index 31cdcfe..d7097bb 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -45,7 +45,7 @@
 	 * The value we return is 1 if we want the pair to be broken,
 	 * or 0 if we do not.
 	 */
-	unsigned long delta_size, base_size, max_size;
+	unsigned long delta_size, max_size;
 	unsigned long src_copied, literal_added, src_removed;
 
 	*merge_score_p = 0; /* assume no deletion --- "do not break"
@@ -64,7 +64,6 @@
 	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
 		return 0; /* error but caught downstream */
 
-	base_size = ((src->size < dst->size) ? src->size : dst->size);
 	max_size = ((src->size > dst->size) ? src->size : dst->size);
 	if (max_size < MINIMUM_BREAK_SIZE)
 		return 0; /* we do not break too small filepair */
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index af9fffe..d0ef839 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -10,7 +10,7 @@
 			     regex_t *regexp)
 {
 	unsigned int cnt;
-	unsigned long offset, sz;
+	unsigned long sz;
 	const char *data;
 	if (diff_populate_filespec(one, 0))
 		return 0;
@@ -25,23 +25,23 @@
 		regmatch_t regmatch;
 		int flags = 0;
 
+		assert(data[sz] == '\0');
 		while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
 			flags |= REG_NOTBOL;
-			data += regmatch.rm_so;
-			if (*data) data++;
+			data += regmatch.rm_eo;
+			if (*data && regmatch.rm_so == regmatch.rm_eo)
+				data++;
 			cnt++;
 		}
 
 	} else { /* Classic exact string match */
-		/* Yes, I've heard of strstr(), but the thing is *data may
-		 * not be NUL terminated.  Sue me.
-		 */
-		for (offset = 0; offset + len <= sz; offset++) {
-			/* we count non-overlapping occurrences of needle */
-			if (!memcmp(needle, data + offset, len)) {
-				offset += len - 1;
-				cnt++;
-			}
+		while (sz) {
+			const char *found = memmem(data, sz, needle, len);
+			if (!found)
+				break;
+			sz -= found - data + len;
+			data = found + len;
+			cnt++;
 		}
 	}
 	diff_free_filespec_data(one);
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 168a95b..0b0d6b8 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -153,9 +153,9 @@
 	 * is a possible size - we really should have a flag to
 	 * say whether the size is valid or not!)
 	 */
-	if (!src->cnt_data && diff_populate_filespec(src, 0))
+	if (!src->cnt_data && diff_populate_filespec(src, 1))
 		return 0;
-	if (!dst->cnt_data && diff_populate_filespec(dst, 0))
+	if (!dst->cnt_data && diff_populate_filespec(dst, 1))
 		return 0;
 
 	max_size = ((src->size > dst->size) ? src->size : dst->size);
@@ -173,6 +173,11 @@
 	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 		return 0;
 
+	if (!src->cnt_data && diff_populate_filespec(src, 0))
+		return 0;
+	if (!dst->cnt_data && diff_populate_filespec(dst, 0))
+		return 0;
+
 	delta_limit = (unsigned long)
 		(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
 	if (diffcore_count_changes(src, dst,
diff --git a/diffcore.h b/diffcore.h
index 1ebfdae..5b63458 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -22,6 +22,8 @@
 
 #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */
 
+struct userdiff_driver;
+
 struct diff_filespec {
 	unsigned char sha1[20];
 	char *path;
@@ -40,8 +42,10 @@
 #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
 	unsigned should_free : 1; /* data should be free()'ed */
 	unsigned should_munmap : 1; /* data should be munmap()'ed */
-	unsigned checked_attr : 1;
-	unsigned is_binary : 1; /* data should be considered "binary" */
+
+	struct userdiff_driver *driver;
+	/* data should be considered "binary"; -1 means "don't know yet" */
+	int is_binary;
 };
 
 extern struct diff_filespec *alloc_filespec(const char *);
diff --git a/dir.c b/dir.c
index 04a4b98..c91ebfb 100644
--- a/dir.c
+++ b/dir.c
@@ -52,11 +52,6 @@
 	return prefix;
 }
 
-static inline int special_char(unsigned char c1)
-{
-	return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\';
-}
-
 /*
  * Does 'match' matches the given name?
  * A match is found if
@@ -80,7 +75,7 @@
 	for (;;) {
 		unsigned char c1 = *match;
 		unsigned char c2 = *name;
-		if (special_char(c1))
+		if (c1 == '\0' || is_glob_special(c1))
 			break;
 		if (c1 != c2)
 			return 0;
@@ -113,25 +108,28 @@
  * and a mark is left in seen[] array for pathspec element that
  * actually matched anything.
  */
-int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
+int match_pathspec(const char **pathspec, const char *name, int namelen,
+		int prefix, char *seen)
 {
-	int retval;
-	const char *match;
+	int i, retval = 0;
+
+	if (!pathspec)
+		return 1;
 
 	name += prefix;
 	namelen -= prefix;
 
-	for (retval = 0; (match = *pathspec++) != NULL; seen++) {
+	for (i = 0; pathspec[i] != NULL; i++) {
 		int how;
-		if (retval && *seen == MATCHED_EXACTLY)
+		const char *match = pathspec[i] + prefix;
+		if (seen && seen[i] == MATCHED_EXACTLY)
 			continue;
-		match += prefix;
 		how = match_one(match, name, namelen);
 		if (how) {
 			if (retval < how)
 				retval = how;
-			if (*seen < how)
-				*seen = how;
+			if (seen && seen[i] < how)
+				seen[i] = how;
 		}
 	}
 	return retval;
@@ -387,7 +385,7 @@
 	return ent;
 }
 
-struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
+static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
 	if (cache_name_exists(pathname, len, ignore_case))
 		return NULL;
@@ -396,7 +394,7 @@
 	return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
 }
 
-struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
 {
 	if (cache_name_pos(pathname, len) >= 0)
 		return NULL;
@@ -489,14 +487,14 @@
 		return recurse_into_directory;
 
 	case index_gitdir:
-		if (dir->show_other_directories)
+		if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
 			return ignore_directory;
 		return show_directory;
 
 	case index_nonexistent:
-		if (dir->show_other_directories)
+		if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
 			break;
-		if (!dir->no_gitlinks) {
+		if (!(dir->flags & DIR_NO_GITLINKS)) {
 			unsigned char sha1[20];
 			if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
 				return show_directory;
@@ -505,7 +503,7 @@
 	}
 
 	/* This is the "show_other_directories" case */
-	if (!dir->hide_empty_directories)
+	if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
 		return show_directory;
 	if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify))
 		return ignore_directory;
@@ -590,10 +588,8 @@
 			int len, dtype;
 			int exclude;
 
-			if ((de->d_name[0] == '.') &&
-			    (de->d_name[1] == 0 ||
-			     !strcmp(de->d_name + 1, ".") ||
-			     !strcmp(de->d_name + 1, "git")))
+			if (is_dot_or_dotdot(de->d_name) ||
+			     !strcmp(de->d_name, ".git"))
 				continue;
 			len = strlen(de->d_name);
 			/* Ignore overly long pathnames! */
@@ -605,7 +601,7 @@
 
 			dtype = DTYPE(de);
 			exclude = excluded(dir, fullname, &dtype);
-			if (exclude && dir->collect_ignored
+			if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
 			    && in_pathspec(fullname, baselen + len, simplify))
 				dir_add_ignored(dir, fullname, baselen + len);
 
@@ -613,7 +609,7 @@
 			 * Excluded? If we don't explicitly want to show
 			 * ignored files, ignore it
 			 */
-			if (exclude && !dir->show_ignored)
+			if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
 				continue;
 
 			if (dtype == DT_UNKNOWN)
@@ -625,7 +621,7 @@
 			 * even if we don't ignore them, since the
 			 * directory may contain files that we do..
 			 */
-			if (!exclude && dir->show_ignored) {
+			if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
 				if (dtype != DT_DIR)
 					continue;
 			}
@@ -638,7 +634,8 @@
 				len++;
 				switch (treat_directory(dir, fullname, baselen + len, simplify)) {
 				case show_directory:
-					if (exclude != dir->show_ignored)
+					if (exclude != !!(dir->flags
+							& DIR_SHOW_IGNORED))
 						continue;
 					break;
 				case recurse_into_directory:
@@ -680,17 +677,12 @@
  */
 static int simple_length(const char *match)
 {
-	const char special[256] = {
-		[0] = 1, ['?'] = 1,
-		['\\'] = 1, ['*'] = 1,
-		['['] = 1
-	};
 	int len = -1;
 
 	for (;;) {
 		unsigned char c = *match++;
 		len++;
-		if (special[c])
+		if (c == '\0' || is_glob_special(c))
 			return len;
 	}
 }
@@ -727,8 +719,12 @@
 
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
-	struct path_simplify *simplify = create_simplify(pathspec);
+	struct path_simplify *simplify;
 
+	if (has_symlink_leading_path(path, strlen(path)))
+		return dir->nr;
+
+	simplify = create_simplify(pathspec);
 	read_directory_recursive(dir, path, base, baselen, 0, simplify);
 	free_simplify(simplify);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
@@ -785,6 +781,25 @@
 	return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
 }
 
+int is_empty_dir(const char *path)
+{
+	DIR *dir = opendir(path);
+	struct dirent *e;
+	int ret = 1;
+
+	if (!dir)
+		return 0;
+
+	while ((e = readdir(dir)) != NULL)
+		if (!is_dot_or_dotdot(e->d_name)) {
+			ret = 0;
+			break;
+		}
+
+	closedir(dir);
+	return ret;
+}
+
 int remove_dir_recursively(struct strbuf *path, int only_empty)
 {
 	DIR *dir = opendir(path->buf);
@@ -799,10 +814,8 @@
 	len = path->len;
 	while ((e = readdir(dir)) != NULL) {
 		struct stat st;
-		if ((e->d_name[0] == '.') &&
-		    ((e->d_name[1] == 0) ||
-		     ((e->d_name[1] == '.') && e->d_name[2] == 0)))
-			continue; /* "." and ".." */
+		if (is_dot_or_dotdot(e->d_name))
+			continue;
 
 		strbuf_setlen(path, len);
 		strbuf_addstr(path, e->d_name);
diff --git a/dir.h b/dir.h
index 278ee42..541286a 100644
--- a/dir.h
+++ b/dir.h
@@ -34,11 +34,13 @@
 struct dir_struct {
 	int nr, alloc;
 	int ignored_nr, ignored_alloc;
-	unsigned int show_ignored:1,
-		     show_other_directories:1,
-		     hide_empty_directories:1,
-		     no_gitlinks:1,
-		     collect_ignored:1;
+	enum {
+		DIR_SHOW_IGNORED = 1<<0,
+		DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
+		DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
+		DIR_NO_GITLINKS = 1<<3,
+		DIR_COLLECT_IGNORED = 1<<4
+	} flags;
 	struct dir_entry **entries;
 	struct dir_entry **ignored;
 
@@ -73,11 +75,19 @@
 extern void add_exclude(const char *string, const char *base,
 			int baselen, struct exclude_list *which);
 extern int file_exists(const char *);
-extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);
 
 extern char *get_relative_cwd(char *buffer, int size, const char *dir);
 extern int is_inside_dir(const char *dir);
 
+static inline int is_dot_or_dotdot(const char *name)
+{
+	return (name[0] == '.' &&
+		(name[1] == '\0' ||
+		 (name[1] == '.' && name[2] == '\0')));
+}
+
+extern int is_empty_dir(const char *dir);
+
 extern void setup_standard_excludes(struct dir_struct *dir);
 extern int remove_dir_recursively(struct strbuf *path, int only_empty);
 
diff --git a/editor.c b/editor.c
index eebc3e9..4d469d0 100644
--- a/editor.c
+++ b/editor.c
@@ -26,9 +26,8 @@
 		int i = 0;
 		int failed;
 		const char *args[6];
-		struct strbuf arg0;
+		struct strbuf arg0 = STRBUF_INIT;
 
-		strbuf_init(&arg0, 0);
 		if (strcspn(editor, "$ \t'") != len) {
 			/* there are specials */
 			strbuf_addf(&arg0, "%s \"$@\"", editor);
diff --git a/entry.c b/entry.c
index aa2ee46..5daacc2 100644
--- a/entry.c
+++ b/entry.c
@@ -1,42 +1,37 @@
 #include "cache.h"
 #include "blob.h"
+#include "dir.h"
 
-static void create_directories(const char *path, const struct checkout *state)
+static void create_directories(const char *path, int path_len,
+			       const struct checkout *state)
 {
-	int len = strlen(path);
-	char *buf = xmalloc(len + 1);
-	const char *slash = path;
+	char *buf = xmalloc(path_len + 1);
+	int len = 0;
 
-	while ((slash = strchr(slash+1, '/')) != NULL) {
-		struct stat st;
-		int stat_status;
-
-		len = slash - path;
-		memcpy(buf, path, len);
+	while (len < path_len) {
+		do {
+			buf[len] = path[len];
+			len++;
+		} while (len < path_len && path[len] != '/');
+		if (len >= path_len)
+			break;
 		buf[len] = 0;
 
-		if (len <= state->base_dir_len)
-			/*
-			 * checkout-index --prefix=<dir>; <dir> is
-			 * allowed to be a symlink to an existing
-			 * directory.
-			 */
-			stat_status = stat(buf, &st);
-		else
-			/*
-			 * if there currently is a symlink, we would
-			 * want to replace it with a real directory.
-			 */
-			stat_status = lstat(buf, &st);
-
-		if (!stat_status && S_ISDIR(st.st_mode))
+		/*
+		 * For 'checkout-index --prefix=<dir>', <dir> is
+		 * allowed to be a symlink to an existing directory,
+		 * and we set 'state->base_dir_len' below, such that
+		 * we test the path components of the prefix with the
+		 * stat() function instead of the lstat() function.
+		 */
+		if (has_dirs_only_path(buf, len, state->base_dir_len))
 			continue; /* ok, it is already a directory. */
 
 		/*
-		 * We know stat_status == 0 means something exists
-		 * there and this mkdir would fail, but that is an
-		 * error codepath; we do not care, as we unlink and
-		 * mkdir again in such a case.
+		 * If this mkdir() would fail, it could be that there
+		 * is already a symlink or something else exists
+		 * there, therefore we then try to unlink it and try
+		 * one more time to create the directory.
 		 */
 		if (mkdir(buf, 0777)) {
 			if (errno == EEXIST && state->force &&
@@ -62,9 +57,7 @@
 	*name++ = '/';
 	while ((de = readdir(dir)) != NULL) {
 		struct stat st;
-		if ((de->d_name[0] == '.') &&
-		    ((de->d_name[1] == 0) ||
-		     ((de->d_name[1] == '.') && de->d_name[2] == 0)))
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
 		strcpy(name, de->d_name);
 		if (lstat(pathbuf, &st))
@@ -85,7 +78,7 @@
 	return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
 }
 
-static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
+static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
 {
 	enum object_type type;
 	void *new = read_sha1_file(ce->sha1, &type, size);
@@ -100,36 +93,52 @@
 
 static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
 {
-	int fd;
-	long wrote;
+	unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
+	int fd, ret, fstat_done = 0;
+	char *new;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned long size;
+	size_t wrote, newsize = 0;
+	struct stat st;
 
-	switch (ce->ce_mode & S_IFMT) {
-		char *new;
-		struct strbuf buf;
-		unsigned long size;
-
+	switch (ce_mode_s_ifmt) {
 	case S_IFREG:
-		new = read_blob_entry(ce, path, &size);
+	case S_IFLNK:
+		new = read_blob_entry(ce, &size);
 		if (!new)
 			return error("git checkout-index: unable to read sha1 file of %s (%s)",
 				path, sha1_to_hex(ce->sha1));
 
+		if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
+			ret = symlink(new, path);
+			free(new);
+			if (ret)
+				return error("git checkout-index: unable to create symlink %s (%s)",
+					     path, strerror(errno));
+			break;
+		}
+
 		/*
 		 * Convert from git internal format to working tree format
 		 */
-		strbuf_init(&buf, 0);
-		if (convert_to_working_tree(ce->name, new, size, &buf)) {
-			size_t newsize = 0;
+		if (ce_mode_s_ifmt == S_IFREG &&
+		    convert_to_working_tree(ce->name, new, size, &buf)) {
 			free(new);
 			new = strbuf_detach(&buf, &newsize);
 			size = newsize;
 		}
 
 		if (to_tempfile) {
-			strcpy(path, ".merge_file_XXXXXX");
+			if (ce_mode_s_ifmt == S_IFREG)
+				strcpy(path, ".merge_file_XXXXXX");
+			else
+				strcpy(path, ".merge_link_XXXXXX");
 			fd = mkstemp(path);
-		} else
+		} else if (ce_mode_s_ifmt == S_IFREG) {
 			fd = create_file(path, ce->ce_mode);
+		} else {
+			fd = create_file(path, 0666);
+		}
 		if (fd < 0) {
 			free(new);
 			return error("git checkout-index: unable to create file %s (%s)",
@@ -137,41 +146,16 @@
 		}
 
 		wrote = write_in_full(fd, new, size);
+		/* use fstat() only when path == ce->name */
+		if (state->refresh_cache && !to_tempfile && !state->base_dir_len) {
+			fstat(fd, &st);
+			fstat_done = 1;
+		}
 		close(fd);
 		free(new);
 		if (wrote != size)
 			return error("git checkout-index: unable to write file %s", path);
 		break;
-	case S_IFLNK:
-		new = read_blob_entry(ce, path, &size);
-		if (!new)
-			return error("git checkout-index: unable to read sha1 file of %s (%s)",
-				path, sha1_to_hex(ce->sha1));
-		if (to_tempfile || !has_symlinks) {
-			if (to_tempfile) {
-				strcpy(path, ".merge_link_XXXXXX");
-				fd = mkstemp(path);
-			} else
-				fd = create_file(path, 0666);
-			if (fd < 0) {
-				free(new);
-				return error("git checkout-index: unable to create "
-						 "file %s (%s)", path, strerror(errno));
-			}
-			wrote = write_in_full(fd, new, size);
-			close(fd);
-			free(new);
-			if (wrote != size)
-				return error("git checkout-index: unable to write file %s",
-					path);
-		} else {
-			wrote = symlink(new, path);
-			free(new);
-			if (wrote)
-				return error("git checkout-index: unable to create "
-						 "symlink %s (%s)", path, strerror(errno));
-		}
-		break;
 	case S_IFGITLINK:
 		if (to_tempfile)
 			return error("git checkout-index: cannot create temporary subproject %s", path);
@@ -183,8 +167,8 @@
 	}
 
 	if (state->refresh_cache) {
-		struct stat st;
-		lstat(ce->name, &st);
+		if (!fstat_done)
+			lstat(ce->name, &st);
 		fill_stat_cache_info(ce, &st);
 	}
 	return 0;
@@ -201,6 +185,7 @@
 
 	memcpy(path, state->base_dir, len);
 	strcpy(path + len, ce->name);
+	len += ce_namelen(ce);
 
 	if (!lstat(path, &st)) {
 		unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
@@ -229,6 +214,6 @@
 			return error("unable to unlink old '%s' (%s)", path, strerror(errno));
 	} else if (state->not_new)
 		return 0;
-	create_directories(path, state);
+	create_directories(path, len, state);
 	return write_entry(ce, path, state, 0);
 }
diff --git a/environment.c b/environment.c
index 9ebf485..4696885 100644
--- a/environment.c
+++ b/environment.c
@@ -42,6 +42,10 @@
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
+enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
+
+/* Parallel index stat data preload? */
+int core_preload_index = 0;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
@@ -80,6 +84,11 @@
 	return is_bare_repository_cfg && !get_git_work_tree();
 }
 
+int have_git_dir(void)
+{
+	return !!git_dir;
+}
+
 const char *get_git_dir(void)
 {
 	if (!git_dir)
diff --git a/exec_cmd.c b/exec_cmd.c
index ce6741e..408e4e5 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -9,22 +9,62 @@
 
 const char *system_path(const char *path)
 {
-	if (!is_absolute_path(path) && argv0_path) {
-		struct strbuf d = STRBUF_INIT;
-		strbuf_addf(&d, "%s/%s", argv0_path, path);
-		path = strbuf_detach(&d, NULL);
+#ifdef RUNTIME_PREFIX
+	static const char *prefix;
+#else
+	static const char *prefix = PREFIX;
+#endif
+	struct strbuf d = STRBUF_INIT;
+
+	if (is_absolute_path(path))
+		return path;
+
+#ifdef RUNTIME_PREFIX
+	assert(argv0_path);
+	assert(is_absolute_path(argv0_path));
+
+	if (!prefix &&
+	    !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) &&
+	    !(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
+	    !(prefix = strip_path_suffix(argv0_path, "git"))) {
+		prefix = PREFIX;
+		fprintf(stderr, "RUNTIME_PREFIX requested, "
+				"but prefix computation failed.  "
+				"Using static fallback '%s'.\n", prefix);
 	}
+#endif
+
+	strbuf_addf(&d, "%s/%s", prefix, path);
+	path = strbuf_detach(&d, NULL);
 	return path;
 }
 
-void git_set_argv0_path(const char *path)
+const char *git_extract_argv0_path(const char *argv0)
 {
-	argv0_path = path;
+	const char *slash;
+
+	if (!argv0 || !*argv0)
+		return NULL;
+	slash = argv0 + strlen(argv0);
+
+	while (argv0 <= slash && !is_dir_sep(*slash))
+		slash--;
+
+	if (slash >= argv0) {
+		argv0_path = xstrndup(argv0, slash - argv0);
+		return slash + 1;
+	}
+
+	return argv0;
 }
 
 void git_set_argv_exec_path(const char *exec_path)
 {
 	argv_exec_path = exec_path;
+	/*
+	 * Propagate this setting to external programs.
+	 */
+	setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
 }
 
 
@@ -59,13 +99,9 @@
 void setup_path(void)
 {
 	const char *old_path = getenv("PATH");
-	struct strbuf new_path;
+	struct strbuf new_path = STRBUF_INIT;
 
-	strbuf_init(&new_path, 0);
-
-	add_path(&new_path, argv_exec_path);
-	add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
-	add_path(&new_path, system_path(GIT_EXEC_PATH));
+	add_path(&new_path, git_exec_path());
 	add_path(&new_path, argv0_path);
 
 	if (old_path)
diff --git a/exec_cmd.h b/exec_cmd.h
index 594f961..e2b546b 100644
--- a/exec_cmd.h
+++ b/exec_cmd.h
@@ -2,8 +2,8 @@
 #define GIT_EXEC_CMD_H
 
 extern void git_set_argv_exec_path(const char *exec_path);
-extern void git_set_argv0_path(const char *path);
-extern const char* git_exec_path(void);
+extern const char *git_extract_argv0_path(const char *path);
+extern const char *git_exec_path(void);
 extern void setup_path(void);
 extern const char **prepare_git_cmd(const char **argv);
 extern int execv_git_cmd(const char **argv); /* NULL terminated */
diff --git a/fast-import.c b/fast-import.c
index 5bc9ce2..23c496d 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1,4 +1,5 @@
 /*
+(See Documentation/git-fast-import.txt for maintained documentation.)
 Format of STDIN stream:
 
   stream ::= cmd*;
@@ -18,8 +19,8 @@
 
   new_commit ::= 'commit' sp ref_str lf
     mark?
-    ('author' sp name '<' email '>' when lf)?
-    'committer' sp name '<' email '>' when lf
+    ('author' sp name sp '<' email '>' sp when lf)?
+    'committer' sp name sp '<' email '>' sp when lf
     commit_msg
     ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
     ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
@@ -43,7 +44,7 @@
 
   new_tag ::= 'tag' sp tag_str lf
     'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
-    ('tagger' sp name '<' email '>' when lf)?
+    ('tagger' sp name sp '<' email '>' sp when lf)?
     tag_msg;
   tag_msg ::= data;
 
@@ -150,6 +151,7 @@
 #include "refs.h"
 #include "csum-file.h"
 #include "quote.h"
+#include "exec_cmd.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -376,7 +378,7 @@
 
 static void write_crash_report(const char *err)
 {
-	char *loc = git_path("fast_import_crash_%d", getpid());
+	char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
 	FILE *rpt = fopen(loc, "w");
 	struct branch *b;
 	unsigned long lu;
@@ -390,8 +392,8 @@
 	fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
 
 	fprintf(rpt, "fast-import crash report:\n");
-	fprintf(rpt, "    fast-import process: %d\n", getpid());
-	fprintf(rpt, "    parent process     : %d\n", getppid());
+	fprintf(rpt, "    fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
+	fprintf(rpt, "    parent process     : %"PRIuMAX"\n", (uintmax_t) getppid());
 	fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
 	fputc('\n', rpt);
 
@@ -554,6 +556,10 @@
 	struct mem_pool *p;
 	void *r;
 
+	/* round up to a 'uintmax_t' alignment */
+	if (len & (sizeof(uintmax_t) - 1))
+		len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
+
 	for (p = mem_pool; p; p = p->next_pool)
 		if ((p->end - p->next_free >= len))
 			break;
@@ -572,9 +578,6 @@
 	}
 
 	r = p->next_free;
-	/* round out to a 'uintmax_t' alignment */
-	if (len & (sizeof(uintmax_t) - 1))
-		len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
 	p->next_free += len;
 	return r;
 }
@@ -815,9 +818,8 @@
 	struct pack_header hdr;
 	int pack_fd;
 
-	snprintf(tmpfile, sizeof(tmpfile),
-		"%s/pack/tmp_pack_XXXXXX", get_object_directory());
-	pack_fd = xmkstemp(tmpfile);
+	pack_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
+			      "pack/tmp_pack_XXXXXX");
 	p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
 	strcpy(p->pack_name, tmpfile);
 	p->pack_fd = pack_fd;
@@ -845,7 +847,7 @@
 static char *create_index(void)
 {
 	static char tmpfile[PATH_MAX];
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	struct sha1file *f;
 	struct object_entry **idx, **c, **last, *e;
 	struct object_entry_pool *o;
@@ -867,7 +869,7 @@
 	/* Generate the fan-out array. */
 	c = idx;
 	for (i = 0; i < 256; i++) {
-		struct object_entry **next = c;;
+		struct object_entry **next = c;
 		while (next < last) {
 			if ((*next)->sha1[0] != i)
 				break;
@@ -877,22 +879,21 @@
 		c = next;
 	}
 
-	snprintf(tmpfile, sizeof(tmpfile),
-		"%s/pack/tmp_idx_XXXXXX", get_object_directory());
-	idx_fd = xmkstemp(tmpfile);
+	idx_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
+			     "pack/tmp_idx_XXXXXX");
 	f = sha1fd(idx_fd, tmpfile);
 	sha1write(f, array, 256 * sizeof(int));
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 	for (c = idx; c != last; c++) {
 		uint32_t offset = htonl((*c)->offset);
 		sha1write(f, &offset, 4);
 		sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
-		SHA1_Update(&ctx, (*c)->sha1, 20);
+		git_SHA1_Update(&ctx, (*c)->sha1, 20);
 	}
 	sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
 	sha1close(f, NULL, CSUM_FSYNC);
 	free(idx);
-	SHA1_Final(pack_data->sha1, &ctx);
+	git_SHA1_Final(pack_data->sha1, &ctx);
 	return tmpfile;
 }
 
@@ -902,12 +903,7 @@
 	static const char *keep_msg = "fast-import";
 	int keep_fd;
 
-	chmod(pack_data->pack_name, 0444);
-	chmod(curr_index_name, 0444);
-
-	snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
-		 get_object_directory(), sha1_to_hex(pack_data->sha1));
-	keep_fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+	keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
 	if (keep_fd < 0)
 		die("cannot create keep file");
 	write_or_die(keep_fd, keep_msg, strlen(keep_msg));
@@ -1036,15 +1032,15 @@
 	unsigned char hdr[96];
 	unsigned char sha1[20];
 	unsigned long hdrlen, deltalen;
-	SHA_CTX c;
+	git_SHA_CTX c;
 	z_stream s;
 
 	hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
 		(unsigned long)dat->len) + 1;
-	SHA1_Init(&c);
-	SHA1_Update(&c, hdr, hdrlen);
-	SHA1_Update(&c, dat->buf, dat->len);
-	SHA1_Final(sha1, &c);
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, hdr, hdrlen);
+	git_SHA1_Update(&c, dat->buf, dat->len);
+	git_SHA1_Final(sha1, &c);
 	if (sha1out)
 		hashcpy(sha1out, sha1);
 
@@ -1747,19 +1743,20 @@
 static int validate_raw_date(const char *src, char *result, int maxlen)
 {
 	const char *orig_src = src;
-	char *endp, sign;
+	char *endp;
+
+	errno = 0;
 
 	strtoul(src, &endp, 10);
-	if (endp == src || *endp != ' ')
+	if (errno || endp == src || *endp != ' ')
 		return -1;
 
 	src = endp + 1;
 	if (*src != '-' && *src != '+')
 		return -1;
-	sign = *src;
 
 	strtoul(src + 1, &endp, 10);
-	if (endp == src || *endp || (endp - orig_src) >= maxlen)
+	if (errno || endp == src || *endp || (endp - orig_src) >= maxlen)
 		return -1;
 
 	strcpy(result, orig_src);
@@ -2403,6 +2400,8 @@
 {
 	unsigned int i, show_stats = 1;
 
+	git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 	git_config(git_pack_config, NULL);
 	if (!pack_compression_seen && core_compression_seen)
diff --git a/fsck.c b/fsck.c
index ab64c18..511b82c 100644
--- a/fsck.c
+++ b/fsck.c
@@ -148,20 +148,17 @@
 	struct tree_desc desc;
 	unsigned o_mode;
 	const char *o_name;
-	const unsigned char *o_sha1;
 
 	init_tree_desc(&desc, item->buffer, item->size);
 
 	o_mode = 0;
 	o_name = NULL;
-	o_sha1 = NULL;
 
 	while (desc.size) {
 		unsigned mode;
 		const char *name;
-		const unsigned char *sha1;
 
-		sha1 = tree_entry_extract(&desc, &name, &mode);
+		tree_entry_extract(&desc, &name, &mode);
 
 		if (strchr(name, '/'))
 			has_full_path = 1;
@@ -207,7 +204,6 @@
 
 		o_mode = mode;
 		o_name = name;
-		o_sha1 = sha1;
 	}
 
 	retval = 0;
@@ -307,9 +303,8 @@
 {
 	va_list ap;
 	int len;
-	struct strbuf sb;
+	struct strbuf sb = STRBUF_INIT;
 
-	strbuf_init(&sb, 0);
 	strbuf_addf(&sb, "object %s:", obj->sha1?sha1_to_hex(obj->sha1):"(null)");
 
 	va_start(ap, fmt);
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b0223c3..60dd1b5 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -3,6 +3,8 @@
 use strict;
 use Git;
 
+binmode(STDOUT, ":raw");
+
 my $repo = Git->repository();
 
 my $menu_use_color = $repo->get_colorbool('color.interactive');
@@ -12,6 +14,13 @@
 		$repo->get_color('color.interactive.header', 'bold'),
 		$repo->get_color('color.interactive.help', 'red bold'),
 	) : ();
+my $error_color = ();
+if ($menu_use_color) {
+	my $help_color_spec = ($repo->config('color.interactive.help') or
+				'red bold');
+	$error_color = $repo->get_color('color.interactive.error',
+					$help_color_spec);
+}
 
 my $diff_use_color = $repo->get_colorbool('color.diff');
 my ($fraginfo_color) =
@@ -33,6 +42,17 @@
 
 my $normal_color = $repo->get_color("", "reset");
 
+my $use_readkey = 0;
+sub ReadMode;
+sub ReadKey;
+if ($repo->config_bool("interactive.singlekey")) {
+	eval {
+		require Term::ReadKey;
+		Term::ReadKey->import;
+		$use_readkey = 1;
+	};
+}
+
 sub colored {
 	my $color = shift;
 	my $string = join("", @_);
@@ -73,6 +93,47 @@
 }
 chomp($GIT_DIR);
 
+my %cquote_map = (
+ "b" => chr(8),
+ "t" => chr(9),
+ "n" => chr(10),
+ "v" => chr(11),
+ "f" => chr(12),
+ "r" => chr(13),
+ "\\" => "\\",
+ "\042" => "\042",
+);
+
+sub unquote_path {
+	local ($_) = @_;
+	my ($retval, $remainder);
+	if (!/^\042(.*)\042$/) {
+		return $_;
+	}
+	($_, $retval) = ($1, "");
+	while (/^([^\\]*)\\(.*)$/) {
+		$remainder = $2;
+		$retval .= $1;
+		for ($remainder) {
+			if (/^([0-3][0-7][0-7])(.*)$/) {
+				$retval .= chr(oct($1));
+				$_ = $2;
+				last;
+			}
+			if (/^([\\\042btnvfr])(.*)$/) {
+				$retval .= $cquote_map{$1};
+				$_ = $2;
+				last;
+			}
+			# This is malformed -- just return it as-is for now.
+			return $_[0];
+		}
+		$_ = $remainder;
+	}
+	$retval .= $_;
+	return $retval;
+}
+
 sub refresh {
 	my $fh;
 	open $fh, 'git update-index --refresh |'
@@ -86,7 +147,7 @@
 sub list_untracked {
 	map {
 		chomp $_;
-		$_;
+		unquote_path($_);
 	}
 	run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
@@ -123,7 +184,8 @@
 
 	if (@ARGV) {
 		@tracked = map {
-			chomp $_; $_;
+			chomp $_;
+			unquote_path($_);
 		} run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);
 		return if (!@tracked);
 	}
@@ -135,6 +197,7 @@
 		if (($add, $del, $file) =
 		    /^([-\d]+)	([-\d]+)	(.*)/) {
 			my ($change, $bin);
+			$file = unquote_path($file);
 			if ($add eq '-' && $del eq '-') {
 				$change = 'binary';
 				$bin = 1;
@@ -150,6 +213,7 @@
 		}
 		elsif (($adddel, $file) =
 		       /^ (create|delete) mode [0-7]+ (.*)$/) {
+			$file = unquote_path($file);
 			$data{$file}{INDEX_ADDDEL} = $adddel;
 		}
 	}
@@ -157,6 +221,7 @@
 	for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) {
 		if (($add, $del, $file) =
 		    /^([-\d]+)	([-\d]+)	(.*)/) {
+			$file = unquote_path($file);
 			if (!exists $data{$file}) {
 				$data{$file} = +{
 					INDEX => 'unchanged',
@@ -178,6 +243,7 @@
 		}
 		elsif (($adddel, $file) =
 		       /^ (create|delete) mode [0-7]+ (.*)$/) {
+			$file = unquote_path($file);
 			$data{$file}{FILE_ADDDEL} = $adddel;
 		}
 	}
@@ -284,7 +350,8 @@
 			}
 			%search = %{$search{$letter}};
 		}
-		if ($soft_limit && $j + 1 > $soft_limit) {
+		if (ord($letters[0]) > 127 ||
+		    ($soft_limit && $j + 1 > $soft_limit)) {
 			$prefix = undef;
 			$remainder = $ret;
 		}
@@ -325,6 +392,10 @@
 	return "$prompt_color$prefix$normal_color$remainder";
 }
 
+sub error_msg {
+	print STDERR colored $error_color, @_;
+}
+
 sub list_and_choose {
 	my ($opts, @stuff) = @_;
 	my (@chosen, @return);
@@ -420,12 +491,12 @@
 			else {
 				$bottom = $top = find_unique($choice, @stuff);
 				if (!defined $bottom) {
-					print "Huh ($choice)?\n";
+					error_msg "Huh ($choice)?\n";
 					next TOPLOOP;
 				}
 			}
 			if ($opts->{SINGLETON} && $bottom != $top) {
-				print "Huh ($choice)?\n";
+				error_msg "Huh ($choice)?\n";
 				next TOPLOOP;
 			}
 			for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -549,11 +620,12 @@
 	if ($diff_use_color) {
 		@colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
 	}
-	my (@hunk) = { TEXT => [], DISPLAY => [] };
+	my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
 
 	for (my $i = 0; $i < @diff; $i++) {
 		if ($diff[$i] =~ /^@@ /) {
-			push @hunk, { TEXT => [], DISPLAY => [] };
+			push @hunk, { TEXT => [], DISPLAY => [],
+				TYPE => 'hunk' };
 		}
 		push @{$hunk[-1]{TEXT}}, $diff[$i];
 		push @{$hunk[-1]{DISPLAY}},
@@ -565,8 +637,8 @@
 sub parse_diff_header {
 	my $src = shift;
 
-	my $head = { TEXT => [], DISPLAY => [] };
-	my $mode = { TEXT => [], DISPLAY => [] };
+	my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
+	my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
 
 	for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
 		my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
@@ -613,6 +685,7 @@
 		my $this = +{
 			TEXT => [],
 			DISPLAY => [],
+			TYPE => 'hunk',
 			OLD => $o_ofs,
 			NEW => $n_ofs,
 			OCNT => 0,
@@ -731,6 +804,10 @@
 		|| $ENV{VISUAL} || $ENV{EDITOR} || "vi";
 	system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
 
+	if ($? != 0) {
+		return undef;
+	}
+
 	open $fh, '<', $hunkfile
 		or die "failed to open hunk edit file for reading: " . $!;
 	my @newtext = grep { !/^#/ } <$fh>;
@@ -758,11 +835,32 @@
 	return close $fh;
 }
 
+sub _restore_terminal_and_die {
+	ReadMode 'restore';
+	print "\n";
+	exit 1;
+}
+
+sub prompt_single_character {
+	if ($use_readkey) {
+		local $SIG{TERM} = \&_restore_terminal_and_die;
+		local $SIG{INT} = \&_restore_terminal_and_die;
+		ReadMode 'cbreak';
+		my $key = ReadKey 0;
+		ReadMode 'restore';
+		print "$key" if defined $key;
+		print "\n";
+		return $key;
+	} else {
+		return <STDIN>;
+	}
+}
+
 sub prompt_yesno {
 	my ($prompt) = @_;
 	while (1) {
 		print colored $prompt_color, $prompt;
-		my $line = <STDIN>;
+		my $line = prompt_single_character;
 		return 0 if $line =~ /^n/i;
 		return 1 if $line =~ /^y/i;
 	}
@@ -777,7 +875,11 @@
 		if (!defined $text) {
 			return undef;
 		}
-		my $newhunk = { TEXT => $text, USE => 1 };
+		my $newhunk = {
+			TEXT => $text,
+			TYPE => $hunk->[$ix]->{TYPE},
+			USE => 1
+		};
 		if (diff_applies($head,
 				 @{$hunk}[0..$ix-1],
 				 $newhunk,
@@ -798,8 +900,11 @@
 	print colored $help_color, <<\EOF ;
 y - stage this hunk
 n - do not stage this hunk
+q - quit, do not stage this hunk nor any of the remaining ones
 a - stage this and all the remaining hunks in the file
 d - do not stage this hunk nor any of the remaining hunks in the file
+g - select a hunk to go to
+/ - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
 J - leave this hunk undecided, see next hunk
 k - leave this hunk undecided, see previous undecided hunk
@@ -832,11 +937,53 @@
 					@mods);
 	}
 	for (@them) {
-		patch_update_file($_->{VALUE});
+		return 0 if patch_update_file($_->{VALUE});
 	}
 }
 
+# Generate a one line summary of a hunk.
+sub summarize_hunk {
+	my $rhunk = shift;
+	my $summary = $rhunk->{TEXT}[0];
+
+	# Keep the line numbers, discard extra context.
+	$summary =~ s/@@(.*?)@@.*/$1 /s;
+	$summary .= " " x (20 - length $summary);
+
+	# Add some user context.
+	for my $line (@{$rhunk->{TEXT}}) {
+		if ($line =~ m/^[+-].*\w/) {
+			$summary .= $line;
+			last;
+		}
+	}
+
+	chomp $summary;
+	return substr($summary, 0, 80) . "\n";
+}
+
+
+# Print a one-line summary of each hunk in the array ref in
+# the first argument, starting wih the index in the 2nd.
+sub display_hunks {
+	my ($hunks, $i) = @_;
+	my $ctr = 0;
+	$i ||= 0;
+	for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {
+		my $status = " ";
+		if (defined $hunks->[$i]{USE}) {
+			$status = $hunks->[$i]{USE} ? "+" : "-";
+		}
+		printf "%s%2d: %s",
+			$status,
+			$i + 1,
+			summarize_hunk($hunks->[$i]);
+	}
+	return $i;
+}
+
 sub patch_update_file {
+	my $quit = 0;
 	my ($ix, $num);
 	my $path = shift;
 	my ($head, @hunk) = parse_diff($path);
@@ -846,32 +993,7 @@
 	}
 
 	if (@{$mode->{TEXT}}) {
-		while (1) {
-			print @{$mode->{DISPLAY}};
-			print colored $prompt_color,
-				"Stage mode change [y/n/a/d/?]? ";
-			my $line = <STDIN>;
-			if ($line =~ /^y/i) {
-				$mode->{USE} = 1;
-				last;
-			}
-			elsif ($line =~ /^n/i) {
-				$mode->{USE} = 0;
-				last;
-			}
-			elsif ($line =~ /^a/i) {
-				$_->{USE} = 1 foreach ($mode, @hunk);
-				last;
-			}
-			elsif ($line =~ /^d/i) {
-				$_->{USE} = 0 foreach ($mode, @hunk);
-				last;
-			}
-			else {
-				help_patch_cmd('');
-				next;
-			}
-		}
+		unshift @hunk, $mode;
 	}
 
 	$num = scalar @hunk;
@@ -887,22 +1009,25 @@
 		for ($i = 0; $i < $ix; $i++) {
 			if (!defined $hunk[$i]{USE}) {
 				$prev = 1;
-				$other .= '/k';
+				$other .= ',k';
 				last;
 			}
 		}
 		if ($ix) {
-			$other .= '/K';
+			$other .= ',K';
 		}
 		for ($i = $ix + 1; $i < $num; $i++) {
 			if (!defined $hunk[$i]{USE}) {
 				$next = 1;
-				$other .= '/j';
+				$other .= ',j';
 				last;
 			}
 		}
 		if ($ix < $num - 1) {
-			$other .= '/J';
+			$other .= ',J';
+		}
+		if ($num > 1) {
+			$other .= ',g';
 		}
 		for ($i = 0; $i < $num; $i++) {
 			if (!defined $hunk[$i]{USE}) {
@@ -912,15 +1037,20 @@
 		}
 		last if (!$undecided);
 
-		if (hunk_splittable($hunk[$ix]{TEXT})) {
-			$other .= '/s';
+		if ($hunk[$ix]{TYPE} eq 'hunk' &&
+		    hunk_splittable($hunk[$ix]{TEXT})) {
+			$other .= ',s';
 		}
-		$other .= '/e';
+		if ($hunk[$ix]{TYPE} eq 'hunk') {
+			$other .= ',e';
+		}
 		for (@{$hunk[$ix]{DISPLAY}}) {
 			print;
 		}
-		print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? ";
-		my $line = <STDIN>;
+		print colored $prompt_color, 'Stage ',
+		  ($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'),
+		  " [y,n,a,d,/$other,?]? ";
+		my $line = prompt_single_character;
 		if ($line) {
 			if ($line =~ /^y/i) {
 				$hunk[$ix]{USE} = 1;
@@ -937,6 +1067,31 @@
 				}
 				next;
 			}
+			elsif ($other =~ /g/ && $line =~ /^g(.*)/) {
+				my $response = $1;
+				my $no = $ix > 10 ? $ix - 10 : 0;
+				while ($response eq '') {
+					my $extra = "";
+					$no = display_hunks(\@hunk, $no);
+					if ($no < $num) {
+						$extra = " (<ret> to see more)";
+					}
+					print "go to which hunk$extra? ";
+					$response = <STDIN>;
+					if (!defined $response) {
+						$response = '';
+					}
+					chomp $response;
+				}
+				if ($response !~ /^\s*\d+\s*$/) {
+					error_msg "Invalid number: '$response'\n";
+				} elsif (0 < $response && $response <= $num) {
+					$ix = $response - 1;
+				} else {
+					error_msg "Sorry, only $num hunks available.\n";
+				}
+				next;
+			}
 			elsif ($line =~ /^d/i) {
 				while ($ix < $num) {
 					if (!defined $hunk[$ix]{USE}) {
@@ -946,30 +1101,86 @@
 				}
 				next;
 			}
-			elsif ($other =~ /K/ && $line =~ /^K/) {
-				$ix--;
-				next;
-			}
-			elsif ($other =~ /J/ && $line =~ /^J/) {
-				$ix++;
-				next;
-			}
-			elsif ($other =~ /k/ && $line =~ /^k/) {
-				while (1) {
-					$ix--;
-					last if (!$ix ||
-						 !defined $hunk[$ix]{USE});
-				}
-				next;
-			}
-			elsif ($other =~ /j/ && $line =~ /^j/) {
-				while (1) {
+			elsif ($line =~ /^q/i) {
+				while ($ix < $num) {
+					if (!defined $hunk[$ix]{USE}) {
+						$hunk[$ix]{USE} = 0;
+					}
 					$ix++;
-					last if ($ix >= $num ||
-						 !defined $hunk[$ix]{USE});
+				}
+				$quit = 1;
+				next;
+			}
+			elsif ($line =~ m|^/(.*)|) {
+				my $regex = $1;
+				if ($1 eq "") {
+					print colored $prompt_color, "search for regex? ";
+					$regex = <STDIN>;
+					if (defined $regex) {
+						chomp $regex;
+					}
+				}
+				my $search_string;
+				eval {
+					$search_string = qr{$regex}m;
+				};
+				if ($@) {
+					my ($err,$exp) = ($@, $1);
+					$err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
+					error_msg "Malformed search regexp $exp: $err\n";
+					next;
+				}
+				my $iy = $ix;
+				while (1) {
+					my $text = join ("", @{$hunk[$iy]{TEXT}});
+					last if ($text =~ $search_string);
+					$iy++;
+					$iy = 0 if ($iy >= $num);
+					if ($ix == $iy) {
+						error_msg "No hunk matches the given pattern\n";
+						last;
+					}
+				}
+				$ix = $iy;
+				next;
+			}
+			elsif ($line =~ /^K/) {
+				if ($other =~ /K/) {
+					$ix--;
+				}
+				else {
+					error_msg "No previous hunk\n";
 				}
 				next;
 			}
+			elsif ($line =~ /^J/) {
+				if ($other =~ /J/) {
+					$ix++;
+				}
+				else {
+					error_msg "No next hunk\n";
+				}
+				next;
+			}
+			elsif ($line =~ /^k/) {
+				if ($other =~ /k/) {
+					while (1) {
+						$ix--;
+						last if (!$ix ||
+							 !defined $hunk[$ix]{USE});
+					}
+				}
+				else {
+					error_msg "No previous hunk\n";
+				}
+				next;
+			}
+			elsif ($line =~ /^j/) {
+				if ($other !~ /j/) {
+					error_msg "No next hunk\n";
+					next;
+				}
+			}
 			elsif ($other =~ /s/ && $line =~ /^s/) {
 				my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
 				if (1 < @split) {
@@ -980,7 +1191,7 @@
 				$num = scalar @hunk;
 				next;
 			}
-			elsif ($line =~ /^e/) {
+			elsif ($other =~ /e/ && $line =~ /^e/) {
 				my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
 				if (defined $newhunk) {
 					splice @hunk, $ix, 1, $newhunk;
@@ -1001,9 +1212,6 @@
 
 	my $n_lofs = 0;
 	my @result = ();
-	if ($mode->{USE}) {
-		push @result, @{$mode->{TEXT}};
-	}
 	for (@hunk) {
 		if ($_->{USE}) {
 			push @result, @{$_->{TEXT}};
@@ -1026,6 +1234,7 @@
 	}
 
 	print "\n";
+	return $quit;
 }
 
 sub diff_cmd {
diff --git a/git-am.sh b/git-am.sh
index aa60261..774383f 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -8,21 +8,24 @@
 git am [options] [<mbox>|<Maildir>...]
 git am [options] (--resolved | --skip | --abort)
 --
-d,dotest=       (removed -- do not use)
 i,interactive   run interactively
-b,binary        (historical option -- no-op)
+b,binary*       (historical option -- no-op)
 3,3way          allow fall back on 3way merging if needed
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
 k,keep          pass -k flag to git-mailinfo
 whitespace=     pass it through git-apply
+directory=      pass it through git-apply
 C=              pass it through git-apply
 p=              pass it through git-apply
+reject          pass it through git-apply
 resolvemsg=     override error message when patch failure occurs
 r,resolved      to be used after a patch failure
 skip            skip the current patch
 abort           restore the original branch and abort the patching operation.
-rebasing        (internal use for git-rebase)"
+committer-date-is-author-date    lie about committer date
+ignore-date     use current timestamp for author date
+rebasing*       (internal use for git-rebase)"
 
 . git-sh-setup
 prefix=$(git rev-parse --show-prefix)
@@ -33,6 +36,21 @@
 git var GIT_COMMITTER_IDENT >/dev/null ||
 	die "You need to set your committer info first"
 
+if git rev-parse --verify -q HEAD >/dev/null
+then
+	HAS_HEAD=yes
+else
+	HAS_HEAD=
+fi
+
+sq () {
+	for sqarg
+	do
+		printf "%s" "$sqarg" |
+		sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/'
+	done
+}
+
 stop_here () {
     echo "$1" >"$dotest/next"
     exit 1
@@ -124,6 +142,8 @@
 sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
 resolvemsg= resume=
 git_apply_opt=
+committer_date_is_author_date=
+ignore_date=
 
 while test $# != 0
 do
@@ -155,10 +175,16 @@
 		;;
 	--resolvemsg)
 		shift; resolvemsg=$1 ;;
-	--whitespace)
-		git_apply_opt="$git_apply_opt $1=$2"; shift ;;
+	--whitespace|--directory)
+		git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
 	-C|-p)
-		git_apply_opt="$git_apply_opt $1$2"; shift ;;
+		git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
+	--reject)
+		git_apply_opt="$git_apply_opt $1" ;;
+	--committer-date-is-author-date)
+		committer_date_is_author_date=t ;;
+	--ignore-date)
+		ignore_date=t ;;
 	--)
 		shift; break ;;
 	*)
@@ -192,7 +218,7 @@
 		# unreliable -- stdin could be /dev/null for example
 		# and the caller did not intend to feed us a patch but
 		# wanted to continue unattended.
-		tty -s
+		test -t 0
 		;;
 	*)
 		false
@@ -202,6 +228,9 @@
 	resume=yes
 
 	case "$skip,$abort" in
+	t,t)
+		die "Please make up your mind. --skip or --abort?"
+		;;
 	t,)
 		git rerere clear
 		git read-tree --reset -u HEAD HEAD
@@ -210,12 +239,19 @@
 		git update-ref ORIG_HEAD $orig_head
 		;;
 	,t)
+		if test -f "$dotest/rebasing"
+		then
+			exec git rebase --abort
+		fi
 		git rerere clear
-		git read-tree --reset -u HEAD ORIG_HEAD
-		git reset ORIG_HEAD
+		test -f "$dotest/dirtyindex" || {
+			git read-tree --reset -u HEAD ORIG_HEAD
+			git reset ORIG_HEAD
+		}
 		rm -fr "$dotest"
 		exit ;;
 	esac
+	rm -f "$dotest/dirtyindex"
 else
 	# Make sure we are not given --skip, --resolved, nor --abort
 	test "$skip$resolved$abort" = "" ||
@@ -247,10 +283,11 @@
 		exit 1
 	}
 
-	# -s, -u, -k and --whitespace flags are kept for the
-	# resuming session after a patch failure.
-	# -3 and -i can and must be given when resuming.
-	echo " $ws" >"$dotest/whitespace"
+	# -s, -u, -k, --whitespace, -3, -C and -p flags are kept
+	# for the resuming session after a patch failure.
+	# -i can and must be given when resuming.
+	echo " $git_apply_opt" >"$dotest/apply-opt"
+	echo "$threeway" >"$dotest/threeway"
 	echo "$sign" >"$dotest/sign"
 	echo "$utf8" >"$dotest/utf8"
 	echo "$keep" >"$dotest/keep"
@@ -260,16 +297,27 @@
 		: >"$dotest/rebasing"
 	else
 		: >"$dotest/applying"
-		git update-ref ORIG_HEAD HEAD
+		if test -n "$HAS_HEAD"
+		then
+			git update-ref ORIG_HEAD HEAD
+		else
+			git update-ref -d ORIG_HEAD >/dev/null 2>&1
+		fi
 	fi
 fi
 
 case "$resolved" in
 '')
-	files=$(git diff-index --cached --name-only HEAD --) || exit
-	if [ "$files" ]; then
-	   echo "Dirty index: cannot apply patches (dirty: $files)" >&2
-	   exit 1
+	case "$HAS_HEAD" in
+	'')
+		files=$(git ls-files) ;;
+	?*)
+		files=$(git diff-index --cached --name-only HEAD --) ;;
+	esac || exit
+	if test "$files"
+	then
+		test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
+		die "Dirty index: cannot apply patches (dirty: $files)"
 	fi
 esac
 
@@ -283,7 +331,11 @@
 then
 	keep=-k
 fi
-ws=`cat "$dotest/whitespace"`
+if test "$(cat "$dotest/threeway")" = t
+then
+	threeway=t
+fi
+git_apply_opt=$(cat "$dotest/apply-opt")
 if test "$(cat "$dotest/sign")" = t
 then
 	SIGNOFF=`git var GIT_COMMITTER_IDENT | sed -e '
@@ -454,7 +506,7 @@
 
 	case "$resolved" in
 	'')
-		git apply $git_apply_opt --index "$dotest/patch"
+		eval 'git apply '"$git_apply_opt"' --index "$dotest/patch"'
 		apply_status=$?
 		;;
 	t)
@@ -496,7 +548,7 @@
 	fi
 	if test $apply_status != 0
 	then
-		echo Patch failed at $msgnum.
+		printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
 		stop_here_user_resolve $this
 	fi
 
@@ -506,8 +558,21 @@
 	fi
 
 	tree=$(git write-tree) &&
-	parent=$(git rev-parse --verify HEAD) &&
-	commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
+	commit=$(
+		if test -n "$ignore_date"
+		then
+			GIT_AUTHOR_DATE=
+		fi
+		parent=$(git rev-parse --verify -q HEAD) ||
+		echo >&2 "applying to an empty history"
+
+		if test -n "$committer_date_is_author_date"
+		then
+			GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+			export GIT_COMMITTER_DATE
+		fi &&
+		git commit-tree $tree ${parent:+-p $parent} <"$dotest/final-commit"
+	) &&
 	git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
 	stop_here $this
 
diff --git a/git-bisect.sh b/git-bisect.sh
index b95dbbb..24712ff 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -9,7 +9,7 @@
         mark <rev> a known-bad revision.
 git bisect good [<rev>...]
         mark <rev>... known-good revisions.
-git bisect skip [<rev>...]
+git bisect skip [(<rev>|<range>)...]
         mark <rev>... untestable revisions.
 git bisect next
         find next bisection to test and check it out.
@@ -77,7 +77,7 @@
 	then
 		# Reset to the rev from where we started.
 		start_head=$(cat "$GIT_DIR/BISECT_START")
-		git checkout "$start_head" || exit
+		git checkout "$start_head" -- || exit
 	else
 		# Get rev from where we start.
 		case "$head" in
@@ -172,6 +172,40 @@
 	test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
 
+is_expected_rev() {
+	test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
+	test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
+}
+
+mark_expected_rev() {
+	echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV"
+}
+
+check_expected_revs() {
+	for _rev in "$@"; do
+		if ! is_expected_rev "$_rev"; then
+			rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
+			rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
+			return
+		fi
+	done
+}
+
+bisect_skip() {
+        all=''
+	for arg in "$@"
+	do
+	    case "$arg" in
+            *..*)
+                revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;;
+            *)
+                revs=$(sq "$arg") ;;
+	    esac
+            all="$all $revs"
+        done
+        eval bisect_state 'skip' $all
+}
+
 bisect_state() {
 	bisect_autostart
 	state=$1
@@ -181,7 +215,8 @@
 	1,bad|1,good|1,skip)
 		rev=$(git rev-parse --verify HEAD) ||
 			die "Bad rev input: HEAD"
-		bisect_write "$state" "$rev" ;;
+		bisect_write "$state" "$rev"
+		check_expected_revs "$rev" ;;
 	2,bad|*,good|*,skip)
 		shift
 		eval=''
@@ -191,7 +226,8 @@
 				die "Bad rev input: $rev"
 			eval="$eval bisect_write '$state' '$sha'; "
 		done
-		eval "$eval" ;;
+		eval "$eval"
+		check_expected_revs "$@" ;;
 	*,bad)
 		die "'git bisect bad' can take only one argument." ;;
 	*)
@@ -243,144 +279,162 @@
 	bisect_next_check && bisect_next || :
 }
 
-eval_rev_list() {
-	_eval="$1"
-
-	eval $_eval
-	res=$?
-
-	if [ $res -ne 0 ]; then
-		echo >&2 "'git rev-list --bisect-vars' failed:"
-		echo >&2 "maybe you mistake good and bad revs?"
-		exit $res
-	fi
-
-	return $res
-}
-
-filter_skipped() {
-	_eval="$1"
-	_skip="$2"
-
-	if [ -z "$_skip" ]; then
-		eval_rev_list "$_eval" | {
-			while read line
-			do
-				echo "$line &&"
-			done
-			echo ':'
-		}
-		return
-	fi
-
-	# Let's parse the output of:
-	# "git rev-list --bisect-vars --bisect-all ..."
-	eval_rev_list "$_eval" | {
-		VARS= FOUND= TRIED=
-		while read hash line
-		do
-			case "$VARS,$FOUND,$TRIED,$hash" in
-			1,*,*,*)
-				# "bisect_foo=bar" read from rev-list output.
-				echo "$hash &&"
-				;;
-			,*,*,---*)
-				# Separator
-				;;
-			,,,bisect_rev*)
-				# We had nothing to search.
-				echo "bisect_rev= &&"
-				VARS=1
-				;;
-			,,*,bisect_rev*)
-				# We did not find a good bisect rev.
-				# This should happen only if the "bad"
-				# commit is also a "skip" commit.
-				echo "bisect_rev='$TRIED' &&"
-				VARS=1
-				;;
-			,,*,*)
-				# We are searching.
-				TRIED="${TRIED:+$TRIED|}$hash"
-				case "$_skip" in
-				*$hash*) ;;
-				*)
-					echo "bisect_rev=$hash &&"
-					echo "bisect_tried='$TRIED' &&"
-					FOUND=1
-					;;
-				esac
-				;;
-			,1,*,bisect_rev*)
-				# We have already found a rev to be tested.
-				VARS=1
-				;;
-			,1,*,*)
-				;;
-			*)
-				# Unexpected input
-				echo "die 'filter_skipped error'"
-				die "filter_skipped error " \
-				    "VARS: '$VARS' " \
-				    "FOUND: '$FOUND' " \
-				    "TRIED: '$TRIED' " \
-				    "hash: '$hash' " \
-				    "line: '$line'"
-				;;
-			esac
-		done
-		echo ':'
-	}
-}
-
 exit_if_skipped_commits () {
 	_tried=$1
-	if expr "$_tried" : ".*[|].*" > /dev/null ; then
+	_bad=$2
+	if test -n "$_tried" ; then
 		echo "There are only 'skip'ped commit left to test."
 		echo "The first bad commit could be any of:"
 		echo "$_tried" | tr '[|]' '[\012]'
+		test -n "$_bad" && echo "$_bad"
 		echo "We cannot bisect more!"
 		exit 2
 	fi
 }
 
+bisect_checkout() {
+	_rev="$1"
+	_msg="$2"
+	echo "Bisecting: $_msg"
+	mark_expected_rev "$_rev"
+	git checkout -q "$_rev" -- || exit
+	git show-branch "$_rev"
+}
+
+is_among() {
+	_rev="$1"
+	_list="$2"
+	case "$_list" in *$_rev*) return 0 ;; esac
+	return 1
+}
+
+handle_bad_merge_base() {
+	_badmb="$1"
+	_good="$2"
+	if is_expected_rev "$_badmb"; then
+		cat >&2 <<EOF
+The merge base $_badmb is bad.
+This means the bug has been fixed between $_badmb and [$_good].
+EOF
+		exit 3
+	else
+		cat >&2 <<EOF
+Some good revs are not ancestor of the bad rev.
+git bisect cannot work properly in this case.
+Maybe you mistake good and bad revs?
+EOF
+		exit 1
+	fi
+}
+
+handle_skipped_merge_base() {
+	_mb="$1"
+	_bad="$2"
+	_good="$3"
+	cat >&2 <<EOF
+Warning: the merge base between $_bad and [$_good] must be skipped.
+So we cannot be sure the first bad commit is between $_mb and $_bad.
+We continue anyway.
+EOF
+}
+
+#
+# "check_merge_bases" checks that merge bases are not "bad".
+#
+# - If one is "good", that's good, we have nothing to do.
+# - If one is "bad", it means the user assumed something wrong
+# and we must exit.
+# - If one is "skipped", we can't know but we should warn.
+# - If we don't know, we should check it out and ask the user to test.
+#
+# In the last case we will return 1, and otherwise 0.
+#
+check_merge_bases() {
+	_bad="$1"
+	_good="$2"
+	_skip="$3"
+	for _mb in $(git merge-base --all $_bad $_good)
+	do
+		if is_among "$_mb" "$_good"; then
+			continue
+		elif test "$_mb" = "$_bad"; then
+			handle_bad_merge_base "$_bad" "$_good"
+		elif is_among "$_mb" "$_skip"; then
+			handle_skipped_merge_base "$_mb" "$_bad" "$_good"
+		else
+			bisect_checkout "$_mb" "a merge base must be tested"
+			return 1
+		fi
+	done
+	return 0
+}
+
+#
+# "check_good_are_ancestors_of_bad" checks that all "good" revs are
+# ancestor of the "bad" rev.
+#
+# If that's not the case, we need to check the merge bases.
+# If a merge base must be tested by the user we return 1 and
+# otherwise 0.
+#
+check_good_are_ancestors_of_bad() {
+	test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
+		return
+
+	_bad="$1"
+	_good=$(echo $2 | sed -e 's/\^//g')
+	_skip="$3"
+
+	# Bisecting with no good rev is ok
+	test -z "$_good" && return
+
+	_side=$(git rev-list $_good ^$_bad)
+	if test -n "$_side"; then
+		# Return if a checkout was done
+		check_merge_bases "$_bad" "$_good" "$_skip" || return
+	fi
+
+	: > "$GIT_DIR/BISECT_ANCESTORS_OK"
+
+	return 0
+}
+
 bisect_next() {
 	case "$#" in 0) ;; *) usage ;; esac
 	bisect_autostart
 	bisect_next_check good
 
-	skip=$(git for-each-ref --format='%(objectname)' \
-		"refs/bisect/skip-*" | tr '\012' ' ') || exit
-
-	BISECT_OPT=''
-	test -n "$skip" && BISECT_OPT='--bisect-all'
-
+	# Get bad, good and skipped revs
 	bad=$(git rev-parse --verify refs/bisect/bad) &&
 	good=$(git for-each-ref --format='^%(objectname)' \
 		"refs/bisect/good-*" | tr '\012' ' ') &&
-	eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
-	eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
-	eval=$(filter_skipped "$eval" "$skip") &&
+	skip=$(git for-each-ref --format='%(objectname)' \
+		"refs/bisect/skip-*" | tr '\012' ' ') || exit
+
+	# Maybe some merge bases must be tested first
+	check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
+	# Return now if a checkout has already been done
+	test "$?" -eq "1" && return
+
+	# Get bisection information
+	eval=$(eval "git bisect--helper --next-vars") &&
 	eval "$eval" || exit
 
 	if [ -z "$bisect_rev" ]; then
+		# We should exit here only if the "bad"
+		# commit is also a "skip" commit (see above).
+		exit_if_skipped_commits "$bisect_tried"
 		echo "$bad was both good and bad"
 		exit 1
 	fi
 	if [ "$bisect_rev" = "$bad" ]; then
-		exit_if_skipped_commits "$bisect_tried"
+		exit_if_skipped_commits "$bisect_tried" "$bad"
 		echo "$bisect_rev is first bad commit"
 		git diff-tree --pretty $bisect_rev
 		exit 0
 	fi
 
-	# We should exit here only if the "bad"
-	# commit is also a "skip" commit (see above).
-	exit_if_skipped_commits "$bisect_rev"
-
-	echo "Bisecting: $bisect_nr revisions left to test after this"
-	git checkout -q "$bisect_rev" || exit
-	git show-branch "$bisect_rev"
+	bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)"
 }
 
 bisect_visualize() {
@@ -388,7 +442,7 @@
 
 	if test $# = 0
 	then
-		case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
+		case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
 		'')	set git log ;;
 		set*)	set gitk ;;
 		esac
@@ -417,7 +471,7 @@
 	*)
 	    usage ;;
 	esac
-	git checkout "$branch" && bisect_clean_state
+	git checkout "$branch" -- && bisect_clean_state
 }
 
 bisect_clean_state() {
@@ -427,6 +481,8 @@
 	do
 		git update-ref -d $ref $hash || exit
 	done
+	rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
+	rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
 	rm -f "$GIT_DIR/BISECT_LOG" &&
 	rm -f "$GIT_DIR/BISECT_NAMES" &&
 	rm -f "$GIT_DIR/BISECT_RUN" &&
@@ -523,8 +579,10 @@
         git bisect -h ;;
     start)
         bisect_start "$@" ;;
-    bad|good|skip)
+    bad|good)
         bisect_state "$cmd" "$@" ;;
+    skip)
+        bisect_skip "$@" ;;
     next)
         # Not sure we want "next" at the UI level anymore.
         bisect_next "$@" ;;
diff --git a/git-compat-util.h b/git-compat-util.h
index cf89cdf..f09f244 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -85,6 +85,7 @@
 #undef _XOPEN_SOURCE
 #include <grp.h>
 #define _XOPEN_SOURCE 600
+#include "compat/cygwin.h"
 #else
 #undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
 #include <grp.h>
@@ -99,6 +100,11 @@
 #include <iconv.h>
 #endif
 
+#ifndef NO_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
 /* On most systems <limits.h> would have given us this, but
  * not on some systems (e.g. GNU/Hurd).
  */
@@ -149,10 +155,7 @@
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
-extern void set_usage_routine(void (*routine)(const char *err) NORETURN);
 extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
-extern void set_error_routine(void (*routine)(const char *err, va_list params));
-extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
 extern time_t tm_to_time_t(const struct tm *tm);
@@ -163,7 +166,7 @@
 	return strncmp(str, prefix, len) ? NULL : str + len;
 }
 
-#ifdef NO_MMAP
+#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
 
 #ifndef PROT_READ
 #define PROT_READ 1
@@ -177,13 +180,19 @@
 extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
 extern int git_munmap(void *start, size_t length);
 
+#else /* NO_MMAP || USE_WIN32_MMAP */
+
+#include <sys/mman.h>
+
+#endif /* NO_MMAP || USE_WIN32_MMAP */
+
+#ifdef NO_MMAP
+
 /* This value must be multiple of (pagesize * 2) */
 #define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
 
 #else /* NO_MMAP */
 
-#include <sys/mman.h>
-
 /* This value must be multiple of (pagesize * 2) */
 #define DEFAULT_PACKED_GIT_WINDOW_SIZE \
 	(sizeof(void*) >= 8 \
@@ -192,6 +201,12 @@
 
 #endif /* NO_MMAP */
 
+#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+#define on_disk_bytes(st) ((st).st_size)
+#else
+#define on_disk_bytes(st) ((st).st_blocks * 512)
+#endif
+
 #define DEFAULT_PACKED_GIT_LIMIT \
 	((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
 
@@ -294,6 +309,8 @@
 extern int xdup(int fd);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
+extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
+extern int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1);
 
 static inline size_t xsize_t(off_t len)
 {
@@ -308,6 +325,7 @@
 }
 
 /* Sane ctype - no locale, and works with signed chars */
+#undef isascii
 #undef isspace
 #undef isdigit
 #undef isalpha
@@ -318,11 +336,16 @@
 #define GIT_SPACE 0x01
 #define GIT_DIGIT 0x02
 #define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define isascii(x) (((x) & ~0x7f) == 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
+#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
 
@@ -371,4 +394,18 @@
 # define FORCE_DIR_SET_GID 0
 #endif
 
+#ifdef NO_NSEC
+#undef USE_NSEC
+#define ST_CTIME_NSEC(st) 0
+#define ST_MTIME_NSEC(st) 0
+#else
+#ifdef USE_ST_TIMESPEC
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
+#else
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
+#endif
+#endif
+
 #endif
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index b0a805c..ab6cea3 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -76,6 +76,7 @@
     'history'         => \&req_CATCHALL,
     'watchers'        => \&req_EMPTY,
     'editors'         => \&req_EMPTY,
+    'noop'            => \&req_EMPTY,
     'annotate'        => \&req_annotate,
     'Global_option'   => \&req_Globaloption,
     #'annotate'        => \&req_CATCHALL,
@@ -1358,7 +1359,13 @@
     # write our commit message out if we have one ...
     my ( $msg_fh, $msg_filename ) = tempfile( DIR => $TEMP_DIR );
     print $msg_fh $state->{opt}{m};# if ( exists ( $state->{opt}{m} ) );
-    print $msg_fh "\n\nvia git-CVS emulator\n";
+    if ( defined ( $cfg->{gitcvs}{commitmsgannotation} ) ) {
+        if ($cfg->{gitcvs}{commitmsgannotation} !~ /^\s*$/ ) {
+            print $msg_fh "\n\n".$cfg->{gitcvs}{commitmsgannotation}."\n"
+        }
+    } else {
+        print $msg_fh "\n\nvia git-CVS emulator\n";
+    }
     close $msg_fh;
 
     my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
@@ -1407,14 +1414,14 @@
 		close $pipe || die "bad pipe: $! $?";
 	}
 
+    $updater->update();
+
 	### Then hooks/post-update
 	$hook = $ENV{GIT_DIR}.'hooks/post-update';
 	if (-x $hook) {
 		system($hook, "refs/heads/$state->{module}");
 	}
 
-    $updater->update();
-
     # foreach file specified on the command line ...
     foreach my $filename ( @committedfiles )
     {
@@ -2527,12 +2534,18 @@
     return $fh;
 }
 
-# Generate a CVS author name from Git author information, by taking
-# the first eight characters of the user part of the email address.
+# Generate a CVS author name from Git author information, by taking the local
+# part of the email address and replacing characters not in the Portable
+# Filename Character Set (see IEEE Std 1003.1-2001, 3.276) by underscores. CVS
+# Login names are Unix login names, which should be restricted to this
+# character set.
 sub cvs_author
 {
     my $author_line = shift;
-    (my $author) = $author_line =~ /<([^>@]{1,8})/;
+    (my $author) = $author_line =~ /<([^@>]*)/;
+
+    $author =~ s/[^-a-zA-Z0-9_.]/_/g;
+    $author =~ s/^-/_/;
 
     $author;
 }
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
new file mode 100755
index 0000000..57e8e32
--- /dev/null
+++ b/git-difftool--helper.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
+# This script is typically launched by using the 'git difftool'
+# convenience command.
+#
+# Copyright (c) 2009 David Aguilar
+
+# Load common functions from git-mergetool--lib
+TOOL_MODE=diff
+. git-mergetool--lib
+
+# difftool.prompt controls the default prompt/no-prompt behavior
+# and is overridden with $GIT_DIFFTOOL*_PROMPT.
+should_prompt () {
+	prompt=$(git config --bool difftool.prompt || echo true)
+	if test "$prompt" = true; then
+		test -z "$GIT_DIFFTOOL_NO_PROMPT"
+	else
+		test -n "$GIT_DIFFTOOL_PROMPT"
+	fi
+}
+
+# Sets up shell variables and runs a merge tool
+launch_merge_tool () {
+	# Merged is the filename as it appears in the work tree
+	# Local is the contents of a/filename
+	# Remote is the contents of b/filename
+	# Custom merge tool commands might use $BASE so we provide it
+	MERGED="$1"
+	LOCAL="$2"
+	REMOTE="$3"
+	BASE="$1"
+
+	# $LOCAL and $REMOTE are temporary files so prompt
+	# the user with the real $MERGED name before launching $merge_tool.
+	if should_prompt; then
+		printf "\nViewing: '$MERGED'\n"
+		printf "Hit return to launch '%s': " "$merge_tool"
+		read ans
+	fi
+
+	# Run the appropriate merge tool command
+	run_merge_tool "$merge_tool"
+}
+
+# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
+test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
+test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
+
+if test -z "$merge_tool"; then
+	merge_tool="$(get_merge_tool)" || exit
+fi
+
+# Launch the merge tool on each path provided by 'git diff'
+while test $# -gt 6
+do
+	launch_merge_tool "$1" "$2" "$5"
+	shift 7
+done
diff --git a/git-difftool.perl b/git-difftool.perl
new file mode 100755
index 0000000..948ff7f
--- /dev/null
+++ b/git-difftool.perl
@@ -0,0 +1,85 @@
+#!/usr/bin/env perl
+# Copyright (c) 2009 David Aguilar
+#
+# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
+# git-difftool--helper script.
+#
+# This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
+# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
+# are exported for use by git-difftool--helper.
+#
+# Any arguments that are unknown to this script are forwarded to 'git diff'.
+
+use strict;
+use warnings;
+use Cwd qw(abs_path);
+use File::Basename qw(dirname);
+
+my $DIR = abs_path(dirname($0));
+
+
+sub usage
+{
+	print << 'USAGE';
+usage: git difftool [--tool=<tool>] [-y|--no-prompt] ["git diff" options]
+USAGE
+	exit 1;
+}
+
+sub setup_environment
+{
+	$ENV{PATH} = "$DIR:$ENV{PATH}";
+	$ENV{GIT_PAGER} = '';
+	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
+}
+
+sub exe
+{
+	my $exe = shift;
+	if ($^O eq 'MSWin32' || $^O eq 'msys') {
+		return "$exe.exe";
+	}
+	return $exe;
+}
+
+sub generate_command
+{
+	my @command = (exe('git'), 'diff');
+	my $skip_next = 0;
+	my $idx = -1;
+	for my $arg (@ARGV) {
+		$idx++;
+		if ($skip_next) {
+			$skip_next = 0;
+			next;
+		}
+		if ($arg eq '-t' || $arg eq '--tool') {
+			usage() if $#ARGV <= $idx;
+			$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
+			$skip_next = 1;
+			next;
+		}
+		if ($arg =~ /^--tool=/) {
+			$ENV{GIT_DIFF_TOOL} = substr($arg, 7);
+			next;
+		}
+		if ($arg eq '-y' || $arg eq '--no-prompt') {
+			$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+			delete $ENV{GIT_DIFFTOOL_PROMPT};
+			next;
+		}
+		if ($arg eq '--prompt') {
+			$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+			delete $ENV{GIT_DIFFTOOL_NO_PROMPT};
+			next;
+		}
+		if ($arg eq '-h' || $arg eq '--help') {
+			usage();
+		}
+		push @command, $arg;
+	}
+	return @command
+}
+
+setup_environment();
+exec(generate_command());
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index a324cf0..b90d3df 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -40,6 +40,16 @@
 	done;
 }
 
+# if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
+# it will skip commits that leave the tree untouched, commit the other.
+git_commit_non_empty_tree()
+{
+	if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
+		map "$3"
+	else
+		git commit-tree "$@"
+	fi
+}
 # override die(): this version puts in an extra line break, so that
 # the progress is still visible
 
@@ -98,7 +108,7 @@
 . git-sh-setup
 
 if [ "$(is_bare_repository)" = false ]; then
-	git diff-files --quiet &&
+	git diff-files --ignore-submodules --quiet &&
 	git diff-index --cached --quiet HEAD -- ||
 	die "Cannot rewrite branch(es) with a dirty working directory."
 fi
@@ -109,11 +119,12 @@
 filter_index=
 filter_parent=
 filter_msg=cat
-filter_commit='git commit-tree "$@"'
+filter_commit=
 filter_tag_name=
 filter_subdir=
 orig_namespace=refs/original/
 force=
+prune_empty=
 while :
 do
 	case "$1" in
@@ -126,6 +137,11 @@
 		force=t
 		continue
 		;;
+	--prune-empty)
+		shift
+		prune_empty=t
+		continue
+		;;
 	-*)
 		;;
 	*)
@@ -176,6 +192,17 @@
 	esac
 done
 
+case "$prune_empty,$filter_commit" in
+,)
+	filter_commit='git commit-tree "$@"';;
+t,)
+	filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
+,*)
+	;;
+*)
+	die "Cannot set --prune-empty and --filter-commit at the same time"
+esac
+
 case "$force" in
 t)
 	rm -rf "$tempdir"
@@ -193,13 +220,21 @@
 # Remove tempdir on exit
 trap 'cd ../..; rm -rf "$tempdir"' 0
 
+ORIG_GIT_DIR="$GIT_DIR"
+ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
+ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
+GIT_WORK_TREE=.
+export GIT_DIR GIT_WORK_TREE
+
 # Make sure refs/original is empty
-git for-each-ref > "$tempdir"/backup-refs
+git for-each-ref > "$tempdir"/backup-refs || exit
 while read sha1 type name
 do
 	case "$force,$name" in
 	,$orig_namespace*)
-		die "Namespace $orig_namespace not empty"
+		die "Cannot create a new backup.
+A previous backup already exists in $orig_namespace
+Force overwriting the backup with -f"
 	;;
 	t,$orig_namespace*)
 		git update-ref -d "$name" $sha1
@@ -207,15 +242,10 @@
 	esac
 done < "$tempdir"/backup-refs
 
-ORIG_GIT_DIR="$GIT_DIR"
-ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
-ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
-GIT_WORK_TREE=.
-export GIT_DIR GIT_WORK_TREE
-
 # The refs should be updated if their heads were rewritten
-git rev-parse --no-flags --revs-only --symbolic-full-name --default HEAD "$@" |
-sed -e '/^^/d' >"$tempdir"/heads
+git rev-parse --no-flags --revs-only --symbolic-full-name \
+	--default HEAD "$@" > "$tempdir"/raw-heads || exit
+sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads
 
 test -s "$tempdir"/heads ||
 	die "Which ref do you want to rewrite?"
@@ -224,19 +254,17 @@
 export GIT_INDEX_FILE
 git read-tree || die "Could not seed the index"
 
-ret=0
-
 # map old->new commit ids for rewriting parents
 mkdir ../map || die "Could not create map/ directory"
 
 case "$filter_subdir" in
 "")
 	git rev-list --reverse --topo-order --default HEAD \
-		--parents "$@"
+		--parents --simplify-merges "$@"
 	;;
 *)
 	git rev-list --reverse --topo-order --default HEAD \
-		--parents "$@" -- "$filter_subdir"
+		--parents --simplify-merges "$@" -- "$filter_subdir"
 esac > ../revs || die "Could not get the commits"
 commits=$(wc -l <../revs | tr -d " ")
 
@@ -244,10 +272,10 @@
 
 # Rewrite the commits
 
-i=0
+git_filter_branch__commit_count=0
 while read commit parents; do
-	i=$(($i+1))
-	printf "\rRewrite $commit ($i/$commits)"
+	git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
+	printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)"
 
 	case "$filter_subdir" in
 	"")
@@ -256,7 +284,7 @@
 	*)
 		# The commit may not have the subdirectory at all
 		err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
-			if ! git rev-parse --verify $commit:"$filter_subdir" 2>/dev/null
+			if ! git rev-parse -q --verify $commit:"$filter_subdir"
 			then
 				rm -f "$GIT_INDEX_FILE"
 			else
@@ -288,10 +316,11 @@
 			die "tree filter failed: $filter_tree"
 
 		(
-			git diff-index -r --name-only $commit
+			git diff-index -r --name-only $commit &&
 			git ls-files --others
-		) |
-		git update-index --add --replace --remove --stdin
+		) > "$tempdir"/tree-state || exit
+		git update-index --add --replace --remove --stdin \
+			< "$tempdir"/tree-state || exit
 	fi
 
 	eval "$filter_index" < /dev/null ||
@@ -312,29 +341,26 @@
 		eval "$filter_msg" > ../message ||
 			die "msg filter failed: $filter_msg"
 	@SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
-		$(git write-tree) $parentstr < ../message > ../map/$commit
+		$(git write-tree) $parentstr < ../message > ../map/$commit ||
+			die "could not write rewritten commit"
 done <../revs
 
 # In case of a subdirectory filter, it is possible that a specified head
 # is not in the set of rewritten commits, because it was pruned by the
-# revision walker.  Fix it by mapping these heads to the next rewritten
-# ancestor(s), i.e. the boundaries in the set of rewritten commits.
+# revision walker.  Fix it by mapping these heads to the unique nearest
+# ancestor that survived the pruning.
 
-# NEEDSWORK: we should sort the unmapped refs topologically first
-while read ref
-do
-	sha1=$(git rev-parse "$ref"^0)
-	test -f "$workdir"/../map/$sha1 && continue
-	# Assign the boundarie(s) in the set of rewritten commits
-	# as the replacement commit(s).
-	# (This would look a bit nicer if --not --stdin worked.)
-	for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") |
-		git rev-list $ref --boundary --stdin |
-		sed -n "s/^-//p")
+if test "$filter_subdir"
+then
+	while read ref
 	do
-		map $p >> "$workdir"/../map/$sha1
-	done
-done < "$tempdir"/heads
+		sha1=$(git rev-parse "$ref"^0)
+		test -f "$workdir"/../map/$sha1 && continue
+		ancestor=$(git rev-list --simplify-merges -1 \
+				$ref -- "$filter_subdir")
+		test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
+	done < "$tempdir"/heads
+fi
 
 # Finally update the refs
 
@@ -384,7 +410,8 @@
 			die "Could not rewrite $ref"
 	;;
 	esac
-	git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1
+	git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 ||
+		 exit
 done < "$tempdir"/heads
 
 # TODO: This should possibly go, with the semantics that all positive given
@@ -416,15 +443,17 @@
 		echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
 
 		if [ "$type" = "tag" ]; then
-			new_sha1=$(git cat-file tag "$ref" |
+			new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
+						"$new_sha1" "$new_ref"
+				git cat-file tag "$ref" |
 				sed -n \
 				    -e "1,/^$/{
-					  s/^object .*/object $new_sha1/
-					  s/^type .*/type commit/
-					  s/^tag .*/tag $new_ref/
+					  /^object /d
+					  /^type /d
+					  /^tag /d
 					}" \
 				    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
-				    -e 'p' |
+				    -e 'p' ) |
 				git mktag) ||
 				die "Could not create new tag object for $ref"
 			if git cat-file tag "$ref" | \
@@ -444,20 +473,21 @@
 
 trap - 0
 
+unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+test -z "$ORIG_GIT_DIR" || {
+	GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
+}
+test -z "$ORIG_GIT_WORK_TREE" || {
+	GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
+	export GIT_WORK_TREE
+}
+test -z "$ORIG_GIT_INDEX_FILE" || {
+	GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
+	export GIT_INDEX_FILE
+}
+
 if [ "$(is_bare_repository)" = false ]; then
-	unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
-	test -z "$ORIG_GIT_DIR" || {
-		GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
-	}
-	test -z "$ORIG_GIT_WORK_TREE" || {
-		GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
-		export GIT_WORK_TREE
-	}
-	test -z "$ORIG_GIT_INDEX_FILE" || {
-		GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
-		export GIT_INDEX_FILE
-	}
-	git read-tree -u -m HEAD
+	git read-tree -u -m HEAD || exit
 fi
 
-exit $ret
+exit 0
diff --git a/git-gui/.gitattributes b/git-gui/.gitattributes
new file mode 100644
index 0000000..f96112d
--- /dev/null
+++ b/git-gui/.gitattributes
@@ -0,0 +1,3 @@
+*           encoding=US-ASCII
+git-gui.sh  encoding=UTF-8
+/po/*.po    encoding=UTF-8
diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN
index 4e709eb..b3f937e 100755
--- a/git-gui/GIT-VERSION-GEN
+++ b/git-gui/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.11.GITGUI
+DEF_VER=0.12.GITGUI
 
 LF='
 '
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 55765c8..3ad8a21 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -285,6 +285,7 @@
 install: all
 	$(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1)
 	$(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
+	$(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 	$(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true
 ifdef GITGUI_WINDOWS_WRAPPER
 	$(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
@@ -302,6 +303,7 @@
 uninstall:
 	$(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 	$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1)
+	$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1)
 	$(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true
 ifdef GITGUI_WINDOWS_WRAPPER
 	$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1)
diff --git a/git-gui/git-gui--askpass b/git-gui/git-gui--askpass
new file mode 100755
index 0000000..12e117e
--- /dev/null
+++ b/git-gui/git-gui--askpass
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Tcl ignores the next line -*- tcl -*- \
+exec wish "$0" -- "$@"
+
+# This is a trivial implementation of an SSH_ASKPASS handler.
+# Git-gui uses this script if none are already configured.
+
+set answer {}
+set yesno  0
+set rc     255
+
+if {$argc < 1} {
+	set prompt "Enter your OpenSSH passphrase:"
+} else {
+	set prompt [join $argv " "]
+	if {[regexp -nocase {\(yes\/no\)\?\s*$} $prompt]} {
+		set yesno 1
+	}
+}
+
+message .m -text $prompt -justify center -aspect 4000
+pack .m -side top -fill x -padx 20 -pady 20 -expand 1
+
+entry .e -textvariable answer -width 50
+pack .e -side top -fill x -padx 10 -pady 10
+
+if {!$yesno} {
+	.e configure -show "*"
+}
+
+frame .b
+button .b.ok     -text OK     -command finish
+button .b.cancel -text Cancel -command {destroy .}
+
+pack .b.ok -side left -expand 1
+pack .b.cancel -side right -expand 1
+pack .b -side bottom -fill x -padx 10 -pady 10
+
+bind . <Visibility> {focus -force .e}
+bind . <Key-Return> finish
+bind . <Key-Escape> {destroy .}
+bind . <Destroy>    {exit $rc}
+
+proc finish {} {
+	if {$::yesno} {
+		if {$::answer ne "yes" && $::answer ne "no"} {
+			tk_messageBox -icon error -title "Error" -type ok \
+				-message "Only 'yes' or 'no' input allowed."
+			return
+		}
+	}
+
+	set ::rc 0
+	puts $::answer
+	destroy .
+}
+
+wm title . "OpenSSH"
+tk::PlaceWindow .
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 86402d4..e018e07 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -521,6 +521,19 @@
 	}
 }
 
+proc gitattr {path attr default} {
+	if {[catch {set r [git check-attr $attr -- $path]}]} {
+		set r unspecified
+	} else {
+		set r [join [lrange [split $r :] 2 end] :]
+		regsub {^ } $r {} r
+	}
+	if {$r eq {unspecified}} {
+		return $default
+	}
+	return $r
+}
+
 proc sq {value} {
 	regsub -all ' $value "'\\''" value
 	return "'$value'"
@@ -578,6 +591,34 @@
 
 if {[is_Windows]} {
 	wm iconbitmap . -default $oguilib/git-gui.ico
+	set ::tk::AlwaysShowSelection 1
+
+	# Spoof an X11 display for SSH
+	if {![info exists env(DISPLAY)]} {
+		set env(DISPLAY) :9999
+	}
+} else {
+	catch {
+		image create photo gitlogo -width 16 -height 16
+
+		gitlogo put #33CC33 -to  7  0  9  2
+		gitlogo put #33CC33 -to  4  2 12  4
+		gitlogo put #33CC33 -to  7  4  9  6
+		gitlogo put #CC3333 -to  4  6 12  8
+		gitlogo put gray26  -to  4  9  6 10
+		gitlogo put gray26  -to  3 10  6 12
+		gitlogo put gray26  -to  8  9 13 11
+		gitlogo put gray26  -to  8 11 10 12
+		gitlogo put gray26  -to 11 11 13 14
+		gitlogo put gray26  -to  3 12  5 14
+		gitlogo put gray26  -to  5 13
+		gitlogo put gray26  -to 10 13
+		gitlogo put gray26  -to  4 14 12 15
+		gitlogo put gray26  -to  5 15 11 16
+		gitlogo redither
+
+		wm iconphoto . -default gitlogo
+	}
 }
 
 ######################################################################
@@ -657,17 +698,21 @@
 }
 
 set default_config(branch.autosetupmerge) true
+set default_config(merge.tool) {}
+set default_config(merge.keepbackup) true
 set default_config(merge.diffstat) true
 set default_config(merge.summary) false
 set default_config(merge.verbosity) 2
 set default_config(user.name) {}
 set default_config(user.email) {}
 
+set default_config(gui.encoding) [encoding system]
 set default_config(gui.matchtrackingbranch) false
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.fastcopyblame) false
 set default_config(gui.copyblamethreshold) 40
+set default_config(gui.blamehistoryctx) 7
 set default_config(gui.diffcontext) 5
 set default_config(gui.commitmsgwidth) 75
 set default_config(gui.newbranchtemplate) {}
@@ -895,19 +940,25 @@
 }
 
 proc load_config {include_global} {
-	global repo_config global_config default_config
+	global repo_config global_config system_config default_config
 
 	if {$include_global} {
+		_parse_config system_config --system
 		_parse_config global_config --global
 	}
 	_parse_config repo_config
 
 	foreach name [array names default_config] {
+		if {[catch {set v $system_config($name)}]} {
+			set system_config($name) $default_config($name)
+		}
+	}
+	foreach name [array names system_config] {
 		if {[catch {set v $global_config($name)}]} {
-			set global_config($name) $default_config($name)
+			set global_config($name) $system_config($name)
 		}
 		if {[catch {set v $repo_config($name)}]} {
-			set repo_config($name) $default_config($name)
+			set repo_config($name) $system_config($name)
 		}
 	}
 }
@@ -945,17 +996,51 @@
 }
 citool {
 	enable_option singlecommit
+	enable_option retcode
 
 	disable_option multicommit
 	disable_option branch
 	disable_option transport
+
+	while {[llength $argv] > 0} {
+		set a [lindex $argv 0]
+		switch -- $a {
+		--amend {
+			enable_option initialamend
+		}
+		--nocommit {
+			enable_option nocommit
+			enable_option nocommitmsg
+		}
+		--commitmsg {
+			disable_option nocommitmsg
+		}
+		default {
+			break
+		}
+		}
+
+		set argv [lrange $argv 1 end]
+	}
 }
 }
 
 ######################################################################
 ##
+## execution environment
+
+set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
+
+# Suggest our implementation of askpass, if none is set
+if {![info exists env(SSH_ASKPASS)]} {
+	set env(SSH_ASKPASS) [gitexec git-gui--askpass]
+}
+
+######################################################################
+##
 ## repository setup
 
+set picked 0
 if {[catch {
 		set _gitdir $env(GIT_DIR)
 		set _prefix {}
@@ -967,6 +1052,7 @@
 	load_config 1
 	apply_config
 	choose_repository::pick
+	set picked 1
 }
 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
 	catch {set _gitdir [exec cygpath --windows $_gitdir]}
@@ -1020,8 +1106,12 @@
 set is_detached 0
 set current_diff_path {}
 set is_3way_diff 0
+set is_conflict_diff 0
 set selected_commit_type new
 
+set nullid "0000000000000000000000000000000000000000"
+set nullid2 "0000000000000000000000000000000000000001"
+
 ######################################################################
 ##
 ## task management
@@ -1102,6 +1192,20 @@
 	return $empty_tree
 }
 
+proc force_amend {} {
+	global selected_commit_type
+	global HEAD PARENT MERGE_HEAD commit_type
+
+	repository_state newType newHEAD newMERGE_HEAD
+	set HEAD $newHEAD
+	set PARENT $newHEAD
+	set MERGE_HEAD $newMERGE_HEAD
+	set commit_type $newType
+
+	set selected_commit_type amend
+	do_select_commit_type
+}
+
 proc rescan {after {honor_trustmtime 1}} {
 	global HEAD PARENT MERGE_HEAD commit_type
 	global ui_index ui_workdir ui_comm
@@ -1128,6 +1232,7 @@
 		|| [string trim [$ui_comm get 0.0 end]] eq {})} {
 		if {[string match amend* $commit_type]} {
 		} elseif {[load_message GITGUI_MSG]} {
+		} elseif {[run_prepare_commit_msg_hook]} {
 		} elseif {[load_message MERGE_MSG]} {
 		} elseif {[load_message SQUASH_MSG]} {
 		}
@@ -1227,6 +1332,70 @@
 	return 0
 }
 
+proc run_prepare_commit_msg_hook {} {
+	global pch_error
+
+	# prepare-commit-msg requires PREPARE_COMMIT_MSG exist.  From git-gui
+	# it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
+	# empty file but existant file.
+
+	set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
+
+	if {[file isfile [gitdir MERGE_MSG]]} {
+		set pcm_source "merge"
+		set fd_mm [open [gitdir MERGE_MSG] r]
+		puts -nonewline $fd_pcm [read $fd_mm]
+		close $fd_mm
+	} elseif {[file isfile [gitdir SQUASH_MSG]]} {
+		set pcm_source "squash"
+		set fd_sm [open [gitdir SQUASH_MSG] r]
+		puts -nonewline $fd_pcm [read $fd_sm]
+		close $fd_sm
+	} else {
+		set pcm_source ""
+	}
+
+	close $fd_pcm
+
+	set fd_ph [githook_read prepare-commit-msg \
+			[gitdir PREPARE_COMMIT_MSG] $pcm_source]
+	if {$fd_ph eq {}} {
+		catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+		return 0;
+	}
+
+	ui_status [mc "Calling prepare-commit-msg hook..."]
+	set pch_error {}
+
+	fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
+	fileevent $fd_ph readable \
+		[list prepare_commit_msg_hook_wait $fd_ph]
+
+	return 1;
+}
+
+proc prepare_commit_msg_hook_wait {fd_ph} {
+	global pch_error
+
+	append pch_error [read $fd_ph]
+	fconfigure $fd_ph -blocking 1
+	if {[eof $fd_ph]} {
+		if {[catch {close $fd_ph}]} {
+			ui_status [mc "Commit declined by prepare-commit-msg hook."]
+			hook_failed_popup prepare-commit-msg $pch_error
+			catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+			exit 1
+		} else {
+			load_message PREPARE_COMMIT_MSG
+		}
+		set pch_error {}
+		catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+		return
+        }
+	fconfigure $fd_ph -blocking 0
+	catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+}
+
 proc read_diff_index {fd after} {
 	global buf_rdi
 
@@ -1322,8 +1491,8 @@
 	prune_selection
 	unlock_index
 	display_all_files
-	if {$current_diff_path ne {}} reshow_diff
-	uplevel #0 $after
+	if {$current_diff_path ne {}} { reshow_diff $after }
+	if {$current_diff_path eq {}} { select_first_diff $after }
 }
 
 proc prune_selection {} {
@@ -1619,6 +1788,15 @@
    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
 } -maskdata $filemask
 
+image create bitmap file_statechange -background white -foreground green -data {
+#define file_merge_width 14
+#define file_merge_height 15
+static unsigned char file_statechange_bits[] = {
+   0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
+   0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
+   0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
+} -maskdata $filemask
+
 set ui_index .vpane.files.index.list
 set ui_workdir .vpane.files.workdir.list
 
@@ -1627,12 +1805,14 @@
 set all_icons(M$ui_index)   file_fulltick
 set all_icons(D$ui_index)   file_removed
 set all_icons(U$ui_index)   file_merge
+set all_icons(T$ui_index)   file_statechange
 
 set all_icons(_$ui_workdir) file_plain
 set all_icons(M$ui_workdir) file_mod
 set all_icons(D$ui_workdir) file_question
 set all_icons(U$ui_workdir) file_merge
 set all_icons(O$ui_workdir) file_plain
+set all_icons(T$ui_workdir) file_statechange
 
 set max_status_desc 0
 foreach i {
@@ -1643,6 +1823,9 @@
 		{MM {mc "Portions staged for commit"}}
 		{MD {mc "Staged for commit, missing"}}
 
+		{_T {mc "File type changed, not staged"}}
+		{T_ {mc "File type changed, staged"}}
+
 		{_O {mc "Untracked, not staged"}}
 		{A_ {mc "Staged for commit"}}
 		{AM {mc "Portions staged for commit"}}
@@ -1652,10 +1835,12 @@
 		{D_ {mc "Staged for removal"}}
 		{DO {mc "Staged for removal, still present"}}
 
+		{_U {mc "Requires merge resolution"}}
 		{U_ {mc "Requires merge resolution"}}
 		{UU {mc "Requires merge resolution"}}
 		{UM {mc "Requires merge resolution"}}
 		{UD {mc "Requires merge resolution"}}
+		{UT {mc "Requires merge resolution"}}
 	} {
 	set text [eval [lindex $i 1]]
 	if {$max_status_desc < [string length $text]} {
@@ -1729,12 +1914,33 @@
 	}
 }
 
-set is_quitting 0
+proc do_explore {} {
+	set explorer {}
+	if {[is_Cygwin] || [is_Windows]} {
+		set explorer "explorer.exe"
+	} elseif {[is_MacOSX]} {
+		set explorer "open"
+	} else {
+		# freedesktop.org-conforming system is our best shot
+		set explorer "xdg-open"
+	}
+	eval exec $explorer [file dirname [gitdir]] &
+}
 
-proc do_quit {} {
+set is_quitting 0
+set ret_code    1
+
+proc terminate_me {win} {
+	global ret_code
+	if {$win ne {.}} return
+	exit $ret_code
+}
+
+proc do_quit {{rc {1}}} {
 	global ui_comm is_quitting repo_config commit_type
 	global GITGUI_BCK_exists GITGUI_BCK_i
 	global ui_comm_spell
+	global ret_code
 
 	if {$is_quitting} return
 	set is_quitting 1
@@ -1789,6 +1995,7 @@
 		}
 	}
 
+	set ret_code $rc
 	destroy .
 }
 
@@ -1796,13 +2003,137 @@
 	rescan ui_ready
 }
 
+proc ui_do_rescan {} {
+	rescan {force_first_diff ui_ready}
+}
+
 proc do_commit {} {
 	commit_tree
 }
 
-proc next_diff {} {
+proc next_diff {{after {}}} {
 	global next_diff_p next_diff_w next_diff_i
-	show_diff $next_diff_p $next_diff_w $next_diff_i
+	show_diff $next_diff_p $next_diff_w {} {} $after
+}
+
+proc find_anchor_pos {lst name} {
+	set lid [lsearch -sorted -exact $lst $name]
+
+	if {$lid == -1} {
+		set lid 0
+		foreach lname $lst {
+			if {$lname >= $name} break
+			incr lid
+		}
+	}
+
+	return $lid
+}
+
+proc find_file_from {flist idx delta path mmask} {
+	global file_states
+
+	set len [llength $flist]
+	while {$idx >= 0 && $idx < $len} {
+		set name [lindex $flist $idx]
+
+		if {$name ne $path && [info exists file_states($name)]} {
+			set state [lindex $file_states($name) 0]
+
+			if {$mmask eq {} || [regexp $mmask $state]} {
+				return $idx
+			}
+		}
+
+		incr idx $delta
+	}
+
+	return {}
+}
+
+proc find_next_diff {w path {lno {}} {mmask {}}} {
+	global next_diff_p next_diff_w next_diff_i
+	global file_lists ui_index ui_workdir
+
+	set flist $file_lists($w)
+	if {$lno eq {}} {
+		set lno [find_anchor_pos $flist $path]
+	} else {
+		incr lno -1
+	}
+
+	if {$mmask ne {} && ![regexp {(^\^)|(\$$)} $mmask]} {
+		if {$w eq $ui_index} {
+			set mmask "^$mmask"
+		} else {
+			set mmask "$mmask\$"
+		}
+	}
+
+	set idx [find_file_from $flist $lno 1 $path $mmask]
+	if {$idx eq {}} {
+		incr lno -1
+		set idx [find_file_from $flist $lno -1 $path $mmask]
+	}
+
+	if {$idx ne {}} {
+		set next_diff_w $w
+		set next_diff_p [lindex $flist $idx]
+		set next_diff_i [expr {$idx+1}]
+		return 1
+	} else {
+		return 0
+	}
+}
+
+proc next_diff_after_action {w path {lno {}} {mmask {}}} {
+	global current_diff_path
+
+	if {$path ne $current_diff_path} {
+		return {}
+	} elseif {[find_next_diff $w $path $lno $mmask]} {
+		return {next_diff;}
+	} else {
+		return {reshow_diff;}
+	}
+}
+
+proc select_first_diff {after} {
+	global ui_workdir
+
+	if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
+	    [find_next_diff $ui_workdir {} 1 {[^O]$}]} {
+		next_diff $after
+	} else {
+		uplevel #0 $after
+	}
+}
+
+proc force_first_diff {after} {
+	global ui_workdir current_diff_path file_states
+
+	if {[info exists file_states($current_diff_path)]} {
+		set state [lindex $file_states($current_diff_path) 0]
+	} else {
+		set state {OO}
+	}
+
+	set reselect 0
+	if {[string first {U} $state] >= 0} {
+		# Already a conflict, do nothing
+	} elseif {[find_next_diff $ui_workdir $current_diff_path {} {^_?U}]} {
+		set reselect 1
+	} elseif {[string index $state 1] ne {O}} {
+		# Already a diff & no conflicts, do nothing
+	} elseif {[find_next_diff $ui_workdir $current_diff_path {} {[^O]$}]} {
+		set reselect 1
+	}
+
+	if {$reselect} {
+		next_diff $after
+	} else {
+		uplevel #0 $after
+	}
 }
 
 proc toggle_or_diff {w x y} {
@@ -1823,34 +2154,31 @@
 	$ui_index tag remove in_sel 0.0 end
 	$ui_workdir tag remove in_sel 0.0 end
 
+	# Determine the state of the file
+	if {[info exists file_states($path)]} {
+		set state [lindex $file_states($path) 0]
+	} else {
+		set state {__}
+	}
+
+	# Restage the file, or simply show the diff
 	if {$col == 0 && $y > 1} {
-		set i [expr {$lno-1}]
-		set ll [expr {[llength $file_lists($w)]-1}]
-
-		if {$i == $ll && $i == 0} {
-			set after {reshow_diff;}
-		} else {
-			global next_diff_p next_diff_w next_diff_i
-
-			set next_diff_w $w
-
-			if {$i < $ll} {
-				set i [expr {$i + 1}]
-				set next_diff_i $i
-			} else {
-				set next_diff_i $i
-				set i [expr {$i - 1}]
-			}
-
-			set next_diff_p [lindex $file_lists($w) $i]
-
-			if {$next_diff_p ne {} && $current_diff_path ne {}} {
-				set after {next_diff;}
-			} else {
-				set after {}
-			}
+		# Conflicts need special handling
+		if {[string first {U} $state] >= 0} {
+			# $w must always be $ui_workdir, but...
+			if {$w ne $ui_workdir} { set lno {} }
+			merge_stage_workdir $path $lno
+			return
 		}
 
+		if {[string index $state 1] eq {O}} {
+			set mmask {}
+		} else {
+			set mmask {[^O]}
+		}
+
+		set after [next_diff_after_action $w $path $lno $mmask]
+
 		if {$w eq $ui_index} {
 			update_indexinfo \
 				"Unstaging [short_path $path] from commit" \
@@ -1961,6 +2289,9 @@
 	.mbar add cascade -label [mc Merge] -menu .mbar.merge
 	.mbar add cascade -label [mc Remote] -menu .mbar.remote
 }
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+	.mbar add cascade -label [mc Tools] -menu .mbar.tools
+}
 . configure -menu .mbar
 
 # -- Repository Menu
@@ -1968,6 +2299,11 @@
 menu .mbar.repository
 
 .mbar.repository add command \
+	-label [mc "Explore Working Copy"] \
+	-command {do_explore}
+.mbar.repository add separator
+
+.mbar.repository add command \
 	-label [mc "Browse Current Branch's Files"] \
 	-command {browser::new $current_branch}
 set ui_browse_current [.mbar.repository index last]
@@ -2091,29 +2427,39 @@
 
 # -- Commit Menu
 #
+proc commit_btn_caption {} {
+	if {[is_enabled nocommit]} {
+		return [mc "Done"]
+	} else {
+		return [mc Commit@@verb]
+	}
+}
+
 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 	menu .mbar.commit
 
-	.mbar.commit add radiobutton \
-		-label [mc "New Commit"] \
-		-command do_select_commit_type \
-		-variable selected_commit_type \
-		-value new
-	lappend disable_on_lock \
-		[list .mbar.commit entryconf [.mbar.commit index last] -state]
+	if {![is_enabled nocommit]} {
+		.mbar.commit add radiobutton \
+			-label [mc "New Commit"] \
+			-command do_select_commit_type \
+			-variable selected_commit_type \
+			-value new
+		lappend disable_on_lock \
+			[list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-	.mbar.commit add radiobutton \
-		-label [mc "Amend Last Commit"] \
-		-command do_select_commit_type \
-		-variable selected_commit_type \
-		-value amend
-	lappend disable_on_lock \
-		[list .mbar.commit entryconf [.mbar.commit index last] -state]
+		.mbar.commit add radiobutton \
+			-label [mc "Amend Last Commit"] \
+			-command do_select_commit_type \
+			-variable selected_commit_type \
+			-value amend
+		lappend disable_on_lock \
+			[list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-	.mbar.commit add separator
+		.mbar.commit add separator
+	}
 
 	.mbar.commit add command -label [mc Rescan] \
-		-command do_rescan \
+		-command ui_do_rescan \
 		-accelerator F5
 	lappend disable_on_lock \
 		[list .mbar.commit entryconf [.mbar.commit index last] -state]
@@ -2152,11 +2498,13 @@
 
 	.mbar.commit add separator
 
-	.mbar.commit add command -label [mc "Sign Off"] \
-		-command do_signoff \
-		-accelerator $M1T-S
+	if {![is_enabled nocommitmsg]} {
+		.mbar.commit add command -label [mc "Sign Off"] \
+			-command do_signoff \
+			-accelerator $M1T-S
+	}
 
-	.mbar.commit add command -label [mc Commit@@verb] \
+	.mbar.commit add command -label [commit_btn_caption] \
 		-command do_commit \
 		-accelerator $M1T-Return
 	lappend disable_on_lock \
@@ -2184,11 +2532,15 @@
 	menu .mbar.remote
 
 	.mbar.remote add command \
+		-label [mc "Add..."] \
+		-command remote_add::dialog \
+		-accelerator $M1T-A
+	.mbar.remote add command \
 		-label [mc "Push..."] \
 		-command do_push_anywhere \
 		-accelerator $M1T-P
 	.mbar.remote add command \
-		-label [mc "Delete..."] \
+		-label [mc "Delete Branch..."] \
 		-command remote_branch_delete::dialog
 }
 
@@ -2214,6 +2566,20 @@
 		-command do_options
 }
 
+# -- Tools Menu
+#
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+	set tools_menubar .mbar.tools
+	menu $tools_menubar
+	$tools_menubar add separator
+	$tools_menubar add command -label [mc "Add..."] -command tools_add::dialog
+	$tools_menubar add command -label [mc "Remove..."] -command tools_remove::dialog
+	set tools_tailcnt 3
+	if {[array names repo_config guitool.*.cmd] ne {}} {
+		tools_populate_all
+	}
+}
+
 # -- Help Menu
 #
 .mbar add cascade -label [mc Help] -menu .mbar.help
@@ -2224,8 +2590,7 @@
 		-command do_about
 }
 
-set browser {}
-catch {set browser $repo_config(instaweb.browser)}
+
 set doc_path [file dirname [gitexec]]
 set doc_path [file join $doc_path Documentation index.html]
 
@@ -2233,34 +2598,23 @@
 	set doc_path [exec cygpath --mixed $doc_path]
 }
 
-if {$browser eq {}} {
-	if {[is_MacOSX]} {
-		set browser open
-	} elseif {[is_Cygwin]} {
-		set program_files [file dirname [exec cygpath --windir]]
-		set program_files [file join $program_files {Program Files}]
-		set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
-		set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
-		if {[file exists $firefox]} {
-			set browser $firefox
-		} elseif {[file exists $ie]} {
-			set browser $ie
-		}
-		unset program_files firefox ie
-	}
-}
-
 if {[file isfile $doc_path]} {
 	set doc_url "file:$doc_path"
 } else {
 	set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
 }
 
-if {$browser ne {}} {
-	.mbar.help add command -label [mc "Online Documentation"] \
-		-command [list exec $browser $doc_url &]
+proc start_browser {url} {
+	git "web--browse" $url
 }
-unset browser doc_path doc_url
+
+.mbar.help add command -label [mc "Online Documentation"] \
+	-command [list start_browser $doc_url]
+
+.mbar.help add command -label [mc "Show SSH Key"] \
+	-command do_ssh_key
+
+unset doc_path doc_url
 
 # -- Standard bindings
 #
@@ -2276,20 +2630,39 @@
 	exit 1
 }
 
+proc normalize_relpath {path} {
+	set elements {}
+	foreach item [file split $path] {
+		if {$item eq {.}} continue
+		if {$item eq {..} && [llength $elements] > 0
+		    && [lindex $elements end] ne {..}} {
+			set elements [lrange $elements 0 end-1]
+			continue
+		}
+		lappend elements $item
+	}
+	return [eval file join $elements]
+}
+
 # -- Not a normal commit type invocation?  Do that instead!
 #
 switch -- $subcommand {
 browser -
 blame {
-	set subcommand_args {rev? path}
+	if {$subcommand eq "blame"} {
+		set subcommand_args {[--line=<num>] rev? path}
+	} else {
+		set subcommand_args {rev? path}
+	}
 	if {$argv eq {}} usage
 	set head {}
 	set path {}
+	set jump_spec {}
 	set is_path 0
 	foreach a $argv {
 		if {$is_path || [file exists $_prefix$a]} {
 			if {$path ne {}} usage
-			set path $_prefix$a
+			set path [normalize_relpath $_prefix$a]
 			break
 		} elseif {$a eq {--}} {
 			if {$path ne {}} {
@@ -2298,6 +2671,9 @@
 				set path {}
 			}
 			set is_path 1
+		} elseif {[regexp {^--line=(\d+)$} $a a lnum]} {
+			if {$jump_spec ne {} || $head ne {}} usage
+			set jump_spec [list $lnum]
 		} elseif {$head eq {}} {
 			if {$head ne {}} usage
 			set head $a
@@ -2309,7 +2685,7 @@
 	unset is_path
 
 	if {$head ne {} && $path eq {}} {
-		set path $_prefix$head
+		set path [normalize_relpath $_prefix$head]
 		set head {}
 	}
 
@@ -2329,6 +2705,7 @@
 
 	switch -- $subcommand {
 	browser {
+		if {$jump_spec ne {}} usage
 		if {$head eq {}} {
 			if {$path ne {} && [file isdirectory $path]} {
 				set head $current_branch
@@ -2344,7 +2721,7 @@
 			puts stderr [mc "fatal: cannot stat path %s: No such file or directory" $path]
 			exit 1
 		}
-		blame::new $head $path
+		blame::new $head $path $jump_spec
 	}
 	}
 	return
@@ -2460,7 +2837,7 @@
 pack .vpane.lower.commarea.buttons -side left -fill y
 
 button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \
-	-command do_rescan
+	-command ui_do_rescan
 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
 lappend disable_on_lock \
 	{.vpane.lower.commarea.buttons.rescan conf -state}
@@ -2471,19 +2848,23 @@
 lappend disable_on_lock \
 	{.vpane.lower.commarea.buttons.incall conf -state}
 
-button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
-	-command do_signoff
-pack .vpane.lower.commarea.buttons.signoff -side top -fill x
+if {![is_enabled nocommitmsg]} {
+	button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
+		-command do_signoff
+	pack .vpane.lower.commarea.buttons.signoff -side top -fill x
+}
 
-button .vpane.lower.commarea.buttons.commit -text [mc Commit@@verb] \
+button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
 	-command do_commit
 pack .vpane.lower.commarea.buttons.commit -side top -fill x
 lappend disable_on_lock \
 	{.vpane.lower.commarea.buttons.commit conf -state}
 
-button .vpane.lower.commarea.buttons.push -text [mc Push] \
-	-command do_push_anywhere
-pack .vpane.lower.commarea.buttons.push -side top -fill x
+if {![is_enabled nocommit]} {
+	button .vpane.lower.commarea.buttons.push -text [mc Push] \
+		-command do_push_anywhere
+	pack .vpane.lower.commarea.buttons.push -side top -fill x
+}
 
 # -- Commit Message Buffer
 #
@@ -2491,20 +2872,24 @@
 frame .vpane.lower.commarea.buffer.header
 set ui_comm .vpane.lower.commarea.buffer.t
 set ui_coml .vpane.lower.commarea.buffer.header.l
-radiobutton .vpane.lower.commarea.buffer.header.new \
-	-text [mc "New Commit"] \
-	-command do_select_commit_type \
-	-variable selected_commit_type \
-	-value new
-lappend disable_on_lock \
-	[list .vpane.lower.commarea.buffer.header.new conf -state]
-radiobutton .vpane.lower.commarea.buffer.header.amend \
-	-text [mc "Amend Last Commit"] \
-	-command do_select_commit_type \
-	-variable selected_commit_type \
-	-value amend
-lappend disable_on_lock \
-	[list .vpane.lower.commarea.buffer.header.amend conf -state]
+
+if {![is_enabled nocommit]} {
+	radiobutton .vpane.lower.commarea.buffer.header.new \
+		-text [mc "New Commit"] \
+		-command do_select_commit_type \
+		-variable selected_commit_type \
+		-value new
+	lappend disable_on_lock \
+		[list .vpane.lower.commarea.buffer.header.new conf -state]
+	radiobutton .vpane.lower.commarea.buffer.header.amend \
+		-text [mc "Amend Last Commit"] \
+		-command do_select_commit_type \
+		-variable selected_commit_type \
+		-value amend
+	lappend disable_on_lock \
+		[list .vpane.lower.commarea.buffer.header.amend conf -state]
+}
+
 label $ui_coml \
 	-anchor w \
 	-justify left
@@ -2522,8 +2907,11 @@
 }
 trace add variable commit_type write trace_commit_type
 pack $ui_coml -side left -fill x
-pack .vpane.lower.commarea.buffer.header.amend -side right
-pack .vpane.lower.commarea.buffer.header.new -side right
+
+if {![is_enabled nocommit]} {
+	pack .vpane.lower.commarea.buffer.header.amend -side right
+	pack .vpane.lower.commarea.buffer.header.new -side right
+}
 
 text $ui_comm -background white -foreground black \
 	-borderwidth 1 \
@@ -2689,6 +3077,59 @@
 
 # -- Diff Body Context Menu
 #
+
+proc create_common_diff_popup {ctxm} {
+	$ctxm add command \
+		-label [mc "Show Less Context"] \
+		-command show_less_context
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add command \
+		-label [mc "Show More Context"] \
+		-command show_more_context
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add separator
+	$ctxm add command \
+		-label [mc Refresh] \
+		-command reshow_diff
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add command \
+		-label [mc Copy] \
+		-command {tk_textCopy $ui_diff}
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add command \
+		-label [mc "Select All"] \
+		-command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add command \
+		-label [mc "Copy All"] \
+		-command {
+			$ui_diff tag add sel 0.0 end
+			tk_textCopy $ui_diff
+			$ui_diff tag remove sel 0.0 end
+		}
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add separator
+	$ctxm add command \
+		-label [mc "Decrease Font Size"] \
+		-command {incr_font_size font_diff -1}
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add command \
+		-label [mc "Increase Font Size"] \
+		-command {incr_font_size font_diff 1}
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add separator
+	set emenu $ctxm.enc
+	menu $emenu
+	build_encoding_menu $emenu [list force_diff_encoding]
+	$ctxm add cascade \
+		-label [mc "Encoding"] \
+		-menu $emenu
+	lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+	$ctxm add separator
+	$ctxm add command -label [mc "Options..."] \
+		-command do_options
+}
+
 set ctxm .vpane.lower.diff.body.ctxm
 menu $ctxm -tearoff 0
 $ctxm add command \
@@ -2702,71 +3143,65 @@
 set ui_diff_applyline [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
 $ctxm add separator
-$ctxm add command \
-	-label [mc "Show Less Context"] \
-	-command show_less_context
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add command \
-	-label [mc "Show More Context"] \
-	-command show_more_context
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add separator
-$ctxm add command \
-	-label [mc Refresh] \
-	-command reshow_diff
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add command \
-	-label [mc Copy] \
-	-command {tk_textCopy $ui_diff}
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add command \
-	-label [mc "Select All"] \
-	-command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add command \
-	-label [mc "Copy All"] \
-	-command {
-		$ui_diff tag add sel 0.0 end
-		tk_textCopy $ui_diff
-		$ui_diff tag remove sel 0.0 end
-	}
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add separator
-$ctxm add command \
-	-label [mc "Decrease Font Size"] \
-	-command {incr_font_size font_diff -1}
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add command \
-	-label [mc "Increase Font Size"] \
-	-command {incr_font_size font_diff 1}
-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
-$ctxm add separator
-$ctxm add command -label [mc "Options..."] \
-	-command do_options
-proc popup_diff_menu {ctxm x y X Y} {
+create_common_diff_popup $ctxm
+
+set ctxmmg .vpane.lower.diff.body.ctxmmg
+menu $ctxmmg -tearoff 0
+$ctxmmg add command \
+	-label [mc "Run Merge Tool"] \
+	-command {merge_resolve_tool}
+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
+$ctxmmg add separator
+$ctxmmg add command \
+	-label [mc "Use Remote Version"] \
+	-command {merge_resolve_one 3}
+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
+$ctxmmg add command \
+	-label [mc "Use Local Version"] \
+	-command {merge_resolve_one 2}
+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
+$ctxmmg add command \
+	-label [mc "Revert To Base"] \
+	-command {merge_resolve_one 1}
+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
+$ctxmmg add separator
+create_common_diff_popup $ctxmmg
+
+proc popup_diff_menu {ctxm ctxmmg x y X Y} {
 	global current_diff_path file_states
 	set ::cursorX $x
 	set ::cursorY $y
-	if {$::ui_index eq $::current_diff_side} {
-		set l [mc "Unstage Hunk From Commit"]
-		set t [mc "Unstage Line From Commit"]
+	if {[info exists file_states($current_diff_path)]} {
+		set state [lindex $file_states($current_diff_path) 0]
 	} else {
-		set l [mc "Stage Hunk For Commit"]
-		set t [mc "Stage Line For Commit"]
+		set state {__}
 	}
-	if {$::is_3way_diff
-		|| $current_diff_path eq {}
-		|| ![info exists file_states($current_diff_path)]
-		|| {_O} eq [lindex $file_states($current_diff_path) 0]} {
-		set s disabled
+	if {[string first {U} $state] >= 0} {
+		tk_popup $ctxmmg $X $Y
 	} else {
-		set s normal
+		if {$::ui_index eq $::current_diff_side} {
+			set l [mc "Unstage Hunk From Commit"]
+			set t [mc "Unstage Line From Commit"]
+		} else {
+			set l [mc "Stage Hunk For Commit"]
+			set t [mc "Stage Line For Commit"]
+		}
+		if {$::is_3way_diff
+			|| $current_diff_path eq {}
+			|| {__} eq $state
+			|| {_O} eq $state
+			|| {_T} eq $state
+			|| {T_} eq $state} {
+			set s disabled
+		} else {
+			set s normal
+		}
+		$ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
+		$ctxm entryconf $::ui_diff_applyline -state $s -label $t
+		tk_popup $ctxm $X $Y
 	}
-	$ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
-	$ctxm entryconf $::ui_diff_applyline -state $s -label $t
-	tk_popup $ctxm $X $Y
 }
-bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]
+bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg %x %y %X %Y]
 
 # -- Status Bar
 #
@@ -2842,9 +3277,9 @@
 	bind . <$M1B-Key-P> do_push_anywhere
 }
 
-bind .   <Key-F5>     do_rescan
-bind .   <$M1B-Key-r> do_rescan
-bind .   <$M1B-Key-R> do_rescan
+bind .   <Key-F5>     ui_do_rescan
+bind .   <$M1B-Key-r> ui_do_rescan
+bind .   <$M1B-Key-R> ui_do_rescan
 bind .   <$M1B-Key-s> do_signoff
 bind .   <$M1B-Key-S> do_signoff
 bind .   <$M1B-Key-t> do_add_selection
@@ -2894,7 +3329,6 @@
 		{^GIT_PAGER$} -
 		{^GIT_TRACE$} -
 		{^GIT_CONFIG$} -
-		{^GIT_CONFIG_LOCAL$} -
 		{^GIT_(AUTHOR|COMMITTER)_DATE$} {
 			append msg " - $name\n"
 			incr ignored_env
@@ -2931,8 +3365,7 @@
 	load_all_remotes
 
 	set n [.mbar.remote index end]
-	populate_push_menu
-	populate_fetch_menu
+	populate_remotes_menu
 	set n [expr {[.mbar.remote index end] - $n}]
 	if {$n > 0} {
 		if {[.mbar.remote type 0] eq "tearoff"} { incr n }
@@ -3022,7 +3455,23 @@
 if {![winfo ismapped .]} {
 	wm deiconify .
 }
-after 1 do_rescan
+after 1 {
+	if {[is_enabled initialamend]} {
+		force_amend
+	} else {
+		do_rescan
+	}
+
+	if {[is_enabled nocommitmsg]} {
+		$ui_comm configure -state disabled -background gray
+	}
+}
 if {[is_enabled multicommit]} {
 	after 1000 hint_gc
 }
+if {[is_enabled retcode]} {
+	bind . <Destroy> {+terminate_me %W}
+}
+if {$picked && [is_config_true gui.autoexplore]} {
+	do_explore
+}
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl
index b6e42cb..1f3b08f 100644
--- a/git-gui/lib/blame.tcl
+++ b/git-gui/lib/blame.tcl
@@ -21,9 +21,11 @@
 field w_asim     ; # text column: annotations (simple computation)
 field w_file     ; # text column: actual file data
 field w_cviewer  ; # pane showing commit message
+field finder     ; # find mini-dialog frame
 field status     ; # status mega-widget instance
 field old_height ; # last known height of $w.file_pane
 
+
 # Tk UI colors
 #
 variable active_color #c0edc5
@@ -58,8 +60,8 @@
 field tooltip_timer     {} ; # Current timer event for our tooltip
 field tooltip_commit    {} ; # Commit(s) in tooltip
 
-constructor new {i_commit i_path} {
-	global cursor_ptr
+constructor new {i_commit i_path i_jump} {
+	global cursor_ptr M1B M1T have_tk85
 	variable active_color
 	variable group_colors
 
@@ -69,6 +71,8 @@
 	make_toplevel top w
 	wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]]
 
+	set font_w [font measure font_diff "0"]
+
 	frame $w.header -background gold
 	label $w.header.commit_l \
 		-text [mc "Commit:"] \
@@ -114,9 +118,9 @@
 	pack $w_path -fill x -side right
 	pack $w.header.path_l -side right
 
-	panedwindow $w.file_pane -orient vertical
-	frame $w.file_pane.out
-	frame $w.file_pane.cm
+	panedwindow $w.file_pane -orient vertical -borderwidth 0 -sashwidth 3
+	frame $w.file_pane.out -relief flat -borderwidth 1
+	frame $w.file_pane.cm -relief sunken -borderwidth 1
 	$w.file_pane add $w.file_pane.out \
 		-sticky nsew \
 		-minsize 100 \
@@ -197,6 +201,11 @@
 		-width 80 \
 		-xscrollcommand [list $w.file_pane.out.sbx set] \
 		-font font_diff
+	if {$have_tk85} {
+		$w_file configure -inactiveselectbackground darkblue
+	}
+	$w_file tag conf found \
+		-background yellow
 
 	set w_columns [list $w_amov $w_asim $w_line $w_file]
 
@@ -217,6 +226,11 @@
 		-weight 1
 	grid rowconfigure $w.file_pane.out 0 -weight 1
 
+	set finder [::searchbar::new \
+		$w.file_pane.out.ff $w_file \
+		-column [expr {[llength $w_columns] - 1}] \
+		]
+
 	set w_cviewer $w.file_pane.cm.t
 	text $w_cviewer \
 		-background white \
@@ -256,18 +270,41 @@
 	$w.ctxm add command \
 		-label [mc "Copy Commit"] \
 		-command [cb _copycommit]
+	$w.ctxm add separator
+	$w.ctxm add command \
+		-label [mc "Find Text..."] \
+		-accelerator F7 \
+		-command [list searchbar::show $finder]
+	menu $w.ctxm.enc
+	build_encoding_menu $w.ctxm.enc [cb _setencoding]
+	$w.ctxm add cascade \
+		-label [mc "Encoding"] \
+		-menu $w.ctxm.enc
 	$w.ctxm add command \
 		-label [mc "Do Full Copy Detection"] \
 		-command [cb _fullcopyblame]
+	$w.ctxm add separator
+	$w.ctxm add command \
+		-label [mc "Show History Context"] \
+		-command [cb _gitkcommit]
+	$w.ctxm add command \
+		-label [mc "Blame Parent Commit"] \
+		-command [cb _blameparent]
 
 	foreach i $w_columns {
 		for {set g 0} {$g < [llength $group_colors]} {incr g} {
 			$i tag conf color$g -background [lindex $group_colors $g]
 		}
 
+		if {$i eq $w_file} {
+			$w_file tag raise found
+		}
+		$i tag raise sel
+
 		$i conf -cursor $cursor_ptr
-		$i conf -yscrollcommand [list many2scrollbar \
-			$w_columns yview $w.file_pane.out.sby]
+		$i conf -yscrollcommand \
+			"[list ::searchbar::scrolled $finder]
+			 [list many2scrollbar $w_columns yview $w.file_pane.out.sby]"
 		bind $i <Button-1> "
 			[cb _hide_tooltip]
 			[cb _click $i @%x,%y]
@@ -284,7 +321,7 @@
 			tk_popup $w.ctxm %X %Y
 		"
 		bind $i <Shift-Tab> "[list focus $w_cviewer];break"
-		bind $i <Tab>       "[list focus $w_cviewer];break"
+		bind $i <Tab>       "[cb _focus_search $w_cviewer];break"
 	}
 
 	foreach i [concat $w_columns $w_cviewer] {
@@ -300,10 +337,15 @@
 		bind $i <Control-Key-f> {catch {%W yview scroll  1 pages};break}
 	}
 
-	bind $w_cviewer <Shift-Tab> "[list focus $w_file];break"
+	bind $w_cviewer <Shift-Tab> "[cb _focus_search $w_file];break"
 	bind $w_cviewer <Tab>       "[list focus $w_file];break"
-	bind $w_cviewer <Button-1> [list focus $w_cviewer]
-	bind $w_file    <Visibility> [list focus $w_file]
+	bind $w_cviewer <Button-1>   [list focus $w_cviewer]
+	bind $w_file    <Visibility> [cb _focus_search $w_file]
+	bind $top       <F7>         [list searchbar::show $finder]
+	bind $top       <Escape>     [list searchbar::hide $finder]
+	bind $top       <F3>         [list searchbar::find_next $finder]
+	bind $top       <Shift-F3>   [list searchbar::find_prev $finder]
+	catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] }
 
 	grid configure $w.header -sticky ew
 	grid configure $w.file_pane -sticky nsew
@@ -315,9 +357,14 @@
 
 	set req_w [winfo reqwidth  $top]
 	set req_h [winfo reqheight $top]
-	set scr_h [expr {[winfo screenheight $top] - 100}]
-	if {$req_w < 600} {set req_w 600}
+	set scr_w [expr {[winfo screenwidth $top] - 40}]
+	set scr_h [expr {[winfo screenheight $top] - 120}]
+	set opt_w [expr {$font_w * (80 + 5*3 + 3)}]
+	if {$req_w < $opt_w} {set req_w $opt_w}
+	if {$req_w > $scr_w} {set req_w $scr_w}
+	set opt_h [expr {$req_w*4/3}]
 	if {$req_h < $scr_h} {set req_h $scr_h}
+	if {$req_h > $opt_h} {set req_h $opt_h}
 	set g "${req_w}x${req_h}"
 	wm geometry $top $g
 	update
@@ -325,14 +372,29 @@
 	set old_height [winfo height $w.file_pane]
 	$w.file_pane sash place 0 \
 		[lindex [$w.file_pane sash coord 0] 0] \
-		[expr {int($old_height * 0.70)}]
+		[expr {int($old_height * 0.80)}]
 	bind $w.file_pane <Configure> \
 	"if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
 
 	wm protocol $top WM_DELETE_WINDOW "destroy $top"
-	bind $top <Destroy> [cb _kill]
+	bind $top <Destroy> [cb _handle_destroy %W]
 
-	_load $this {}
+	_load $this $i_jump
+}
+
+method _focus_search {win} {
+	if {[searchbar::visible $finder]} {
+		focus [searchbar::editor $finder]
+	} else {
+		focus $win
+	}
+}
+
+method _handle_destroy {win} {
+	if {$win eq $w} {
+		_kill $this
+		delete_this
+	}
 }
 
 method _kill {} {
@@ -393,7 +455,10 @@
 	} else {
 		set fd [git_read cat-file blob "$commit:$path"]
 	}
-	fconfigure $fd -blocking 0 -translation lf -encoding binary
+	fconfigure $fd \
+		-blocking 0 \
+		-translation lf \
+		-encoding [get_path_encoding $path]
 	fileevent $fd readable [cb _read_file $fd $jump]
 	set current_fd $fd
 }
@@ -494,7 +559,7 @@
 } ifdeleted { catch {close $fd} }
 
 method _exec_blame {cur_w cur_d options cur_s} {
-	lappend options --incremental
+	lappend options --incremental --encoding=utf-8
 	if {$commit eq {}} {
 		lappend options --contents $path
 	} else {
@@ -502,7 +567,7 @@
 	}
 	lappend options -- $path
 	set fd [eval git_read --nice blame $options]
-	fconfigure $fd -blocking 0 -translation lf -encoding binary
+	fconfigure $fd -blocking 0 -translation lf -encoding utf-8
 	fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
 	set current_fd $fd
 	set blame_lines 0
@@ -782,24 +847,42 @@
 	_showcommit $this $cur_w $lno
 }
 
+method _setencoding {enc} {
+	force_path_encoding $path $enc
+	_load $this [list \
+		$highlight_column \
+		$highlight_line \
+		[lindex [$w_file xview] 0] \
+		[lindex [$w_file yview] 0] \
+		]
+}
+
 method _load_commit {cur_w cur_d pos} {
 	upvar #0 $cur_d line_data
 	set lno [lindex [split [$cur_w index $pos] .] 0]
 	set dat [lindex $line_data $lno]
 	if {$dat ne {}} {
-		lappend history [list \
-			$commit $path \
-			$highlight_column \
-			$highlight_line \
-			[lindex [$w_file xview] 0] \
-			[lindex [$w_file yview] 0] \
-			]
-		set commit [lindex $dat 0]
-		set path   [lindex $dat 1]
-		_load $this [list [lindex $dat 2]]
+		_load_new_commit $this  \
+			[lindex $dat 0] \
+			[lindex $dat 1] \
+			[list [lindex $dat 2]]
 	}
 }
 
+method _load_new_commit {new_commit new_path jump} {
+	lappend history [list \
+		$commit $path \
+		$highlight_column \
+		$highlight_line \
+		[lindex [$w_file xview] 0] \
+		[lindex [$w_file yview] 0] \
+		]
+
+	set commit $new_commit
+	set path   $new_path
+	_load $this $jump
+}
+
 method _showcommit {cur_w lno} {
 	global repo_config
 	variable active_color
@@ -832,6 +915,10 @@
 		foreach i $w_columns {
 			$i tag conf g$cmit -background $active_color
 			$i tag raise g$cmit
+			if {$i eq $w_file} {
+				$w_file tag raise found
+			}
+			$i tag raise sel
 		}
 
 		set author_name {}
@@ -853,9 +940,8 @@
 			catch {
 				set fd [git_read cat-file commit $cmit]
 				fconfigure $fd -encoding binary -translation lf
-				if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
-					set enc utf-8
-				}
+				# By default commits are assumed to be in utf-8
+				set enc utf-8
 				while {[gets $fd line] > 0} {
 					if {[string match {encoding *} $line]} {
 						set enc [string tolower [string range $line 9 end]]
@@ -867,12 +953,6 @@
 				set enc [tcl_encoding $enc]
 				if {$enc ne {}} {
 					set msg [encoding convertfrom $enc $msg]
-					set author_name [encoding convertfrom $enc $author_name]
-					set committer_name [encoding convertfrom $enc $committer_name]
-					set header($cmit,author) $author_name
-					set header($cmit,committer) $committer_name
-					set header($cmit,summary) \
-					[encoding convertfrom $enc $header($cmit,summary)]
 				}
 				set msg [string trim $msg]
 			}
@@ -905,10 +985,14 @@
 	}
 }
 
-method _copycommit {} {
+method _get_click_amov_info {} {
 	set pos @$::cursorX,$::cursorY
 	set lno [lindex [split [$::cursorW index $pos] .] 0]
-	set dat [lindex $amov_data $lno]
+	return [lindex $amov_data $lno]
+}
+
+method _copycommit {} {
+	set dat [_get_click_amov_info $this]
 	if {$dat ne {}} {
 		clipboard clear
 		clipboard append \
@@ -918,6 +1002,147 @@
 	}
 }
 
+method _format_offset_date {base offset} {
+	set exval [expr {$base + $offset*24*60*60}]
+	return [clock format $exval -format {%Y-%m-%d}]
+}
+
+method _gitkcommit {} {
+	global nullid
+
+	set dat [_get_click_amov_info $this]
+	if {$dat ne {}} {
+		set cmit [lindex $dat 0]
+
+		# If the line belongs to the working copy, use HEAD instead
+		if {$cmit eq $nullid} {
+			if {[catch {set cmit [git rev-parse --verify HEAD]} err]} {
+				error_popup [strcat [mc "Cannot find HEAD commit:"] "\n\n$err"]
+				return;
+			}
+		}
+
+		set radius [get_config gui.blamehistoryctx]
+		set cmdline [list --select-commit=$cmit]
+
+                if {$radius > 0} {
+			set author_time {}
+			set committer_time {}
+
+			catch {set author_time $header($cmit,author-time)}
+			catch {set committer_time $header($cmit,committer-time)}
+
+			if {$committer_time eq {}} {
+				set committer_time $author_time
+			}
+
+			set after_time [_format_offset_date $this $committer_time [expr {-$radius}]]
+			set before_time [_format_offset_date $this $committer_time $radius]
+
+			lappend cmdline --after=$after_time --before=$before_time
+		}
+
+		lappend cmdline $cmit
+
+		set base_rev "HEAD"
+		if {$commit ne {}} {
+			set base_rev $commit
+		}
+
+		if {$base_rev ne $cmit} {
+			lappend cmdline $base_rev
+		}
+
+		do_gitk $cmdline
+	}
+}
+
+method _blameparent {} {
+	global nullid
+
+	set dat [_get_click_amov_info $this]
+	if {$dat ne {}} {
+		set cmit [lindex $dat 0]
+		set new_path [lindex $dat 1]
+
+		# Allow using Blame Parent on lines modified in the working copy
+		if {$cmit eq $nullid} {
+			set parent_ref "HEAD"
+		} else {
+			set parent_ref "$cmit^"
+		}
+		if {[catch {set cparent [git rev-parse --verify $parent_ref]} err]} {
+			error_popup [strcat [mc "Cannot find parent commit:"] "\n\n$err"]
+			return;
+		}
+
+		_kill $this
+
+		# Generate a diff between the commit and its parent,
+		# and use the hunks to update the line number.
+		# Request zero context to simplify calculations.
+		if {$cmit eq $nullid} {
+			set diffcmd [list diff-index --unified=0 $cparent -- $new_path]
+		} else {
+			set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
+		}
+		if {[catch {set fd [eval git_read $diffcmd]} err]} {
+			$status stop [mc "Unable to display parent"]
+			error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
+			return
+		}
+
+		set r_orig_line [lindex $dat 2]
+
+		fconfigure $fd \
+			-blocking 0 \
+			-encoding binary \
+			-translation binary
+		fileevent $fd readable [cb _read_diff_load_commit \
+			$fd $cparent $new_path $r_orig_line]
+		set current_fd $fd
+	}
+}
+
+method _read_diff_load_commit {fd cparent new_path tline} {
+	if {$fd ne $current_fd} {
+		catch {close $fd}
+		return
+	}
+
+	while {[gets $fd line] >= 0} {
+		if {[regexp {^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@} $line line \
+			old_line osz old_size new_line nsz new_size]} {
+
+			if {$osz eq {}} { set old_size 1 }
+			if {$nsz eq {}} { set new_size 1 }
+
+			if {$new_line <= $tline} {
+				if {[expr {$new_line + $new_size}] > $tline} {
+					# Target line within the hunk
+					set line_shift [expr {
+						($new_size-$old_size)*($tline-$new_line)/$new_size
+						}]
+				} else {
+					set line_shift [expr {$new_size-$old_size}]
+				}
+
+				set r_orig_line [expr {$r_orig_line - $line_shift}]
+			}
+		}
+	}
+
+	if {[eof $fd]} {
+		close $fd;
+		set current_fd {}
+
+		_load_new_commit $this  \
+			$cparent        \
+			$new_path       \
+			[list $r_orig_line]
+	}
+} ifdeleted { catch {close $fd} }
+
 method _show_tooltip {cur_w pos} {
 	if {$tooltip_wm ne {}} {
 		_open_tooltip $this $cur_w
diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl
index ab470d1..0410cc6 100644
--- a/git-gui/lib/browser.tcl
+++ b/git-gui/lib/browser.tcl
@@ -151,7 +151,7 @@
 				append p [lindex $n 1]
 			}
 			append p $name
-			blame::new $browser_commit $p
+			blame::new $browser_commit $p {}
 		}
 		}
 	}
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index 3180786..f9ff62a 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -43,12 +43,18 @@
 			$w.mbar.apple add command \
 				-label [mc "About %s" [appname]] \
 				-command do_about
+			$w.mbar.apple add command \
+				-label [mc "Show SSH Key"] \
+				-command do_ssh_key
 		} else {
 			$w.mbar add cascade -label [mc Help] -menu $w.mbar.help
 			menu $w.mbar.help
 			$w.mbar.help add command \
 				-label [mc "About %s" [appname]] \
 				-command do_about
+			$w.mbar.help add command \
+				-label [mc "Show SSH Key"] \
+				-command do_ssh_key
 		}
 
 		wm protocol $top WM_DELETE_WINDOW exit
@@ -381,7 +387,8 @@
 	label $w_body.where.l -text [mc "Directory:"]
 	entry $w_body.where.t \
 		-textvariable @local_path \
-		-font font_diff \
+		-borderwidth 1 \
+		-relief sunken \
 		-width 50
 	button $w_body.where.b \
 		-text [mc "Browse"] \
@@ -463,20 +470,22 @@
 	frame $w_body.args
 	pack $args -fill both
 
-	label $args.origin_l -text [mc "URL:"]
+	label $args.origin_l -text [mc "Source Location:"]
 	entry $args.origin_t \
 		-textvariable @origin_url \
-		-font font_diff \
+		-borderwidth 1 \
+		-relief sunken \
 		-width 50
 	button $args.origin_b \
 		-text [mc "Browse"] \
 		-command [cb _open_origin]
 	grid $args.origin_l $args.origin_t $args.origin_b -sticky ew
 
-	label $args.where_l -text [mc "Directory:"]
+	label $args.where_l -text [mc "Target Directory:"]
 	entry $args.where_t \
 		-textvariable @local_path \
-		-font font_diff \
+		-borderwidth 1 \
+		-relief sunken \
 		-width 50
 	button $args.where_b \
 		-text [mc "Browse"] \
@@ -979,7 +988,8 @@
 	label $w_body.where.l -text [mc "Repository:"]
 	entry $w_body.where.t \
 		-textvariable @local_path \
-		-font font_diff \
+		-borderwidth 1 \
+		-relief sunken \
 		-width 50
 	button $w_body.where.b \
 		-text [mc "Browse"] \
diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl
index 40a7103..9cc8410 100644
--- a/git-gui/lib/commit.tcl
+++ b/git-gui/lib/commit.tcl
@@ -27,9 +27,8 @@
 	if {[catch {
 			set fd [git_read cat-file commit $curHEAD]
 			fconfigure $fd -encoding binary -translation lf
-			if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
-				set enc utf-8
-			}
+			# By default commits are assumed to be in utf-8
+			set enc utf-8
 			while {[gets $fd line] > 0} {
 				if {[string match {parent *} $line]} {
 					lappend parents [string range $line 7 end]
@@ -149,7 +148,9 @@
 		_? {continue}
 		A? -
 		D? -
+		T_ -
 		M? {set files_ready 1}
+		_U -
 		U? {
 			error_popup [mc "Unmerged files cannot be committed.
 
@@ -166,7 +167,7 @@
 		}
 		}
 	}
-	if {!$files_ready && ![string match *merge $curType]} {
+	if {!$files_ready && ![string match *merge $curType] && ![is_enabled nocommit]} {
 		info_popup [mc "No changes to commit.
 
 You must stage at least 1 file before you can commit.
@@ -175,6 +176,8 @@
 		return
 	}
 
+	if {[is_enabled nocommitmsg]} { do_quit 0 }
+
 	# -- A message is required.
 	#
 	set msg [string trim [$ui_comm get 1.0 end]]
@@ -204,12 +207,14 @@
 	if {$use_enc ne {}} {
 		fconfigure $msg_wt -encoding $use_enc
 	} else {
-		puts stderr [mc "warning: Tcl does not support encoding '%s'." $enc]
+		error_popup [mc "warning: Tcl does not support encoding '%s'." $enc]
 		fconfigure $msg_wt -encoding utf-8
 	}
 	puts $msg_wt $msg
 	close $msg_wt
 
+	if {[is_enabled nocommit]} { do_quit 0 }
+
 	# -- Run the pre-commit hook.
 	#
 	set fd_ph [githook_read pre-commit]
@@ -408,7 +413,7 @@
 		set ::GITGUI_BCK_exists 0
 	}
 
-	if {[is_enabled singlecommit]} do_quit
+	if {[is_enabled singlecommit]} { do_quit 0 }
 
 	# -- Update in memory status
 	#
@@ -428,6 +433,7 @@
 		__ -
 		A_ -
 		M_ -
+		T_ -
 		D_ {
 			unset file_states($path)
 			catch {unset selected_paths($path)}
diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl
index 1970b60..bbbf15c 100644
--- a/git-gui/lib/diff.tcl
+++ b/git-gui/lib/diff.tcl
@@ -16,7 +16,7 @@
 	$ui_workdir tag remove in_diff 0.0 end
 }
 
-proc reshow_diff {} {
+proc reshow_diff {{after {}}} {
 	global file_states file_lists
 	global current_diff_path current_diff_side
 	global ui_diff
@@ -24,13 +24,28 @@
 	set p $current_diff_path
 	if {$p eq {}} {
 		# No diff is being shown.
-	} elseif {$current_diff_side eq {}
-		|| [catch {set s $file_states($p)}]
-		|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
+	} elseif {$current_diff_side eq {}} {
 		clear_diff
+	} elseif {[catch {set s $file_states($p)}]
+		|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
+
+		if {[find_next_diff $current_diff_side $p {} {[^O]}]} {
+			next_diff $after
+		} else {
+			clear_diff
+		}
 	} else {
 		set save_pos [lindex [$ui_diff yview] 0]
-		show_diff $p $current_diff_side {} $save_pos
+		show_diff $p $current_diff_side {} $save_pos $after
+	}
+}
+
+proc force_diff_encoding {enc} {
+	global current_diff_path
+	
+	if {$current_diff_path ne {}} {
+		force_path_encoding $current_diff_path $enc
+		reshow_diff
 	}
 }
 
@@ -54,11 +69,12 @@
 	rescan ui_ready 0
 }
 
-proc show_diff {path w {lno {}} {scroll_pos {}}} {
+proc show_diff {path w {lno {}} {scroll_pos {}} {callback {}}} {
 	global file_states file_lists
-	global is_3way_diff diff_active repo_config
+	global is_3way_diff is_conflict_diff diff_active repo_config
 	global ui_diff ui_index ui_workdir
 	global current_diff_path current_diff_side current_diff_header
+	global current_diff_queue
 
 	if {$diff_active || ![lock_index read]} return
 
@@ -71,21 +87,84 @@
 	}
 	if {$lno >= 1} {
 		$w tag add in_diff $lno.0 [expr {$lno + 1}].0
+		$w see $lno.0
 	}
 
 	set s $file_states($path)
 	set m [lindex $s 0]
-	set is_3way_diff 0
-	set diff_active 1
+	set is_conflict_diff 0
 	set current_diff_path $path
 	set current_diff_side $w
-	set current_diff_header {}
+	set current_diff_queue {}
 	ui_status [mc "Loading diff of %s..." [escape_path $path]]
 
+	set cont_info [list $scroll_pos $callback]
+
+	if {[string first {U} $m] >= 0} {
+		merge_load_stages $path [list show_unmerged_diff $cont_info]
+	} elseif {$m eq {_O}} {
+		show_other_diff $path $w $m $cont_info
+	} else {
+		start_show_diff $cont_info
+	}
+}
+
+proc show_unmerged_diff {cont_info} {
+	global current_diff_path current_diff_side
+	global merge_stages ui_diff is_conflict_diff
+	global current_diff_queue
+
+	if {$merge_stages(2) eq {}} {
+		set is_conflict_diff 1
+		lappend current_diff_queue \
+			[list [mc "LOCAL: deleted\nREMOTE:\n"] d======= \
+			    [list ":1:$current_diff_path" ":3:$current_diff_path"]]
+	} elseif {$merge_stages(3) eq {}} {
+		set is_conflict_diff 1
+		lappend current_diff_queue \
+			[list [mc "REMOTE: deleted\nLOCAL:\n"] d======= \
+			    [list ":1:$current_diff_path" ":2:$current_diff_path"]]
+	} elseif {[lindex $merge_stages(1) 0] eq {120000}
+		|| [lindex $merge_stages(2) 0] eq {120000}
+		|| [lindex $merge_stages(3) 0] eq {120000}} {
+		set is_conflict_diff 1
+		lappend current_diff_queue \
+			[list [mc "LOCAL:\n"] d======= \
+			    [list ":1:$current_diff_path" ":2:$current_diff_path"]]
+		lappend current_diff_queue \
+			[list [mc "REMOTE:\n"] d======= \
+			    [list ":1:$current_diff_path" ":3:$current_diff_path"]]
+	} else {
+		start_show_diff $cont_info
+		return
+	}
+
+	advance_diff_queue $cont_info
+}
+
+proc advance_diff_queue {cont_info} {
+	global current_diff_queue ui_diff
+
+	set item [lindex $current_diff_queue 0]
+	set current_diff_queue [lrange $current_diff_queue 1 end]
+
+	$ui_diff conf -state normal
+	$ui_diff insert end [lindex $item 0] [lindex $item 1]
+	$ui_diff conf -state disabled
+
+	start_show_diff $cont_info [lindex $item 2]
+}
+
+proc show_other_diff {path w m cont_info} {
+	global file_states file_lists
+	global is_3way_diff diff_active repo_config
+	global ui_diff ui_index ui_workdir
+	global current_diff_path current_diff_side current_diff_header
+
 	# - Git won't give us the diff, there's nothing to compare to!
 	#
 	if {$m eq {_O}} {
-		set max_sz [expr {128 * 1024}]
+		set max_sz 100000
 		set type unknown
 		if {[catch {
 				set type [file type $path]
@@ -101,7 +180,9 @@
 				}
 				file {
 					set fd [open $path r]
-					fconfigure $fd -eofchar {}
+					fconfigure $fd \
+						-eofchar {} \
+						-encoding [get_path_encoding $path]
 					set content [read $fd $max_sz]
 					close $fd
 					set sz [file size $path]
@@ -137,36 +218,57 @@
 				d_@
 		} else {
 			if {$sz > $max_sz} {
-				$ui_diff insert end \
-"* Untracked file is $sz bytes.
-* Showing only first $max_sz bytes.
-" d_@
+				$ui_diff insert end [mc \
+"* Untracked file is %d bytes.
+* Showing only first %d bytes.
+" $sz $max_sz] d_@
 			}
 			$ui_diff insert end $content
 			if {$sz > $max_sz} {
-				$ui_diff insert end "
-* Untracked file clipped here by [appname].
+				$ui_diff insert end [mc "
+* Untracked file clipped here by %s.
 * To see the entire file, use an external editor.
-" d_@
+" [appname]] d_@
 			}
 		}
 		$ui_diff conf -state disabled
 		set diff_active 0
 		unlock_index
+		set scroll_pos [lindex $cont_info 0]
 		if {$scroll_pos ne {}} {
 			update
 			$ui_diff yview moveto $scroll_pos
 		}
 		ui_ready
+		set callback [lindex $cont_info 1]
+		if {$callback ne {}} {
+			eval $callback
+		}
 		return
 	}
+}
+
+proc start_show_diff {cont_info {add_opts {}}} {
+	global file_states file_lists
+	global is_3way_diff diff_active repo_config
+	global ui_diff ui_index ui_workdir
+	global current_diff_path current_diff_side current_diff_header
+
+	set path $current_diff_path
+	set w $current_diff_side
+
+	set s $file_states($path)
+	set m [lindex $s 0]
+	set is_3way_diff 0
+	set diff_active 1
+	set current_diff_header {}
 
 	set cmd [list]
 	if {$w eq $ui_index} {
 		lappend cmd diff-index
 		lappend cmd --cached
 	} elseif {$w eq $ui_workdir} {
-		if {[string index $m 0] eq {U}} {
+		if {[string first {U} $m] >= 0} {
 			lappend cmd diff
 		} else {
 			lappend cmd diff-files
@@ -181,8 +283,12 @@
 	if {$w eq $ui_index} {
 		lappend cmd [PARENT]
 	}
-	lappend cmd --
-	lappend cmd $path
+	if {$add_opts ne {}} {
+		eval lappend cmd $add_opts
+	} else {
+		lappend cmd --
+		lappend cmd $path
+	}
 
 	if {[catch {set fd [eval git_read --nice $cmd]} err]} {
 		set diff_active 0
@@ -195,14 +301,15 @@
 	set ::current_diff_inheader 1
 	fconfigure $fd \
 		-blocking 0 \
-		-encoding binary \
-		-translation binary
-	fileevent $fd readable [list read_diff $fd $scroll_pos]
+		-encoding [get_path_encoding $path] \
+		-translation lf
+	fileevent $fd readable [list read_diff $fd $cont_info]
 }
 
-proc read_diff {fd scroll_pos} {
+proc read_diff {fd cont_info} {
 	global ui_diff diff_active
-	global is_3way_diff current_diff_header
+	global is_3way_diff is_conflict_diff current_diff_header
+	global current_diff_queue
 
 	$ui_diff conf -state normal
 	while {[gets $fd line] >= 0} {
@@ -249,6 +356,7 @@
 			{--} {set tags d_--}
 			{++} {
 				if {[regexp {^\+\+([<>]{7} |={7})} $line _g op]} {
+					set is_conflict_diff 1
 					set line [string replace $line 0 1 {  }]
 					set tags d$op
 				} else {
@@ -268,7 +376,7 @@
 			{-} {set tags d_-}
 			{+} {
 				if {[regexp {^\+([<>]{7} |={7})} $line _g op]} {
-					set line [string replace $line 0 0 { }]
+					set is_conflict_diff 1
 					set tags d$op
 				} else {
 					set tags d_+
@@ -290,8 +398,15 @@
 
 	if {[eof $fd]} {
 		close $fd
+
+		if {$current_diff_queue ne {}} {
+			advance_diff_queue $cont_info
+			return
+		}
+
 		set diff_active 0
 		unlock_index
+		set scroll_pos [lindex $cont_info 0]
 		if {$scroll_pos ne {}} {
 			update
 			$ui_diff yview moveto $scroll_pos
@@ -301,6 +416,10 @@
 		if {[$ui_diff index end] eq {2.0}} {
 			handle_empty_diff
 		}
+		set callback [lindex $cont_info 1]
+		if {$callback ne {}} {
+			eval $callback
+		}
 	}
 }
 
@@ -341,8 +460,9 @@
 	}
 
 	if {[catch {
+		set enc [get_path_encoding $current_diff_path]
 		set p [eval git_write $apply_cmd]
-		fconfigure $p -translation binary -encoding binary
+		fconfigure $p -translation binary -encoding $enc
 		puts -nonewline $p $current_diff_header
 		puts -nonewline $p [$ui_diff get $s_lno $e_lno]
 		close $p} err]} {
@@ -370,10 +490,9 @@
 	}
 	unlock_index
 	display_file $current_diff_path $mi
+	# This should trigger shift to the next changed file
 	if {$o eq {_}} {
-		clear_diff
-	} else {
-		set current_diff_path $current_diff_path
+		reshow_diff
 	}
 }
 
@@ -511,8 +630,9 @@
 	set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch"
 
 	if {[catch {
+		set enc [get_path_encoding $current_diff_path]
 		set p [eval git_write $apply_cmd]
-		fconfigure $p -translation binary -encoding binary
+		fconfigure $p -translation binary -encoding $enc
 		puts -nonewline $p $current_diff_header
 		puts -nonewline $p $patch
 		close $p} err]} {
diff --git a/git-gui/lib/encoding.tcl b/git-gui/lib/encoding.tcl
index 7f06b0d..32668fc 100644
--- a/git-gui/lib/encoding.tcl
+++ b/git-gui/lib/encoding.tcl
@@ -206,7 +206,7 @@
     { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
     { GBK CP936 MS936 windows-936 }
     { JIS_Encoding csJISEncoding }
-    { Shift_JIS MS_Kanji csShiftJIS }
+    { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
     { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
       EUC-JP }
     { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
@@ -240,37 +240,227 @@
     { Big5 csBig5 }
 }
 
-proc tcl_encoding {enc} {
-    global encoding_aliases
-    set names [encoding names]
-    set lcnames [string tolower $names]
-    set enc [string tolower $enc]
-    set i [lsearch -exact $lcnames $enc]
-    if {$i < 0} {
-	# look for "isonnn" instead of "iso-nnn" or "iso_nnn"
-	if {[regsub {^iso[-_]} $enc iso encx]} {
-	    set i [lsearch -exact $lcnames $encx]
+set encoding_groups {
+    {"" ""
+	{"Unicode" UTF-8}
+	{"Western" ISO-8859-1}}
+    {we "West European"
+	{"Western" ISO-8859-15 CP-437 CP-850 MacRoman CP-1252 Windows-1252}
+	{"Celtic" ISO-8859-14}
+	{"Greek" ISO-8859-14 ISO-8859-7 CP-737 CP-869 MacGreek CP-1253 Windows-1253}
+	{"Icelandic" MacIceland MacIcelandic CP-861}
+	{"Nordic" ISO-8859-10 CP-865}
+	{"Portuguese" CP-860}
+	{"South European" ISO-8859-3}}
+    {ee "East European"
+	{"Baltic" CP-775 ISO-8859-4 ISO-8859-13 CP-1257 Windows-1257}
+	{"Central European" CP-852 ISO-8859-2 MacCE CP-1250 Windows-1250}
+	{"Croatian" MacCroatian}
+	{"Cyrillic" CP-855 ISO-8859-5 ISO-IR-111 KOI8-R MacCyrillic CP-1251 Windows-1251}
+	{"Russian" CP-866}
+	{"Ukrainian" KOI8-U MacUkraine MacUkrainian}
+	{"Romanian" ISO-8859-16 MacRomania MacRomanian}}
+    {ea "East Asian"
+	{"Generic" ISO-2022}
+	{"Chinese Simplified" GB2312 GB1988 GB12345 GB2312-RAW GBK EUC-CN GB18030 HZ ISO-2022-CN}
+	{"Chinese Traditional" Big5 Big5-HKSCS EUC-TW CP-950}
+	{"Japanese" EUC-JP ISO-2022-JP Shift-JIS JIS-0212 JIS-0208 JIS-0201 CP-932 MacJapan}
+	{"Korean" EUC-KR UHC JOHAB ISO-2022-KR CP-949 KSC5601}}
+    {sa "SE & SW Asian"
+	{"Armenian" ARMSCII-8}
+	{"Georgian" GEOSTD8}
+	{"Thai" TIS-620 ISO-8859-11 CP-874 Windows-874 MacThai}
+	{"Turkish" CP-857 CP857 ISO-8859-9 MacTurkish CP-1254 Windows-1254}
+	{"Vietnamese" TCVN VISCII VPS CP-1258 Windows-1258}
+	{"Hindi" MacDevanagari}
+	{"Gujarati" MacGujarati}
+	{"Gurmukhi" MacGurmukhi}}
+    {me "Middle Eastern"
+	{"Arabic" ISO-8859-6 Windows-1256 CP-1256 CP-864 MacArabic}
+	{"Farsi" MacFarsi}
+	{"Hebrew" ISO-8859-8-I Windows-1255 CP-1255 ISO-8859-8 CP-862 MacHebrew}}
+    {mi "Misc"
+	{"7-bit" ASCII}
+	{"16-bit" Unicode}
+	{"Legacy" CP-863 EBCDIC}
+	{"Symbol" Symbol Dingbats MacDingbats MacCentEuro}}
+}
+
+proc build_encoding_table {} {
+	global encoding_aliases encoding_lookup_table
+
+	# Prepare the lookup list; cannot use lsort -nocase because
+	# of compatibility issues with older Tcl (e.g. in msysgit)
+	set names [list]
+	foreach item [encoding names] {
+		lappend names [list [string tolower $item] $item]
 	}
-    }
-    if {$i < 0} {
-	foreach l $encoding_aliases {
-	    set ll [string tolower $l]
-	    if {[lsearch -exact $ll $enc] < 0} continue
-	    # look through the aliases for one that tcl knows about
-	    foreach e $ll {
-		set i [lsearch -exact $lcnames $e]
-		if {$i < 0} {
-		    if {[regsub {^iso[-_]} $e iso ex]} {
-			set i [lsearch -exact $lcnames $ex]
-		    }
+	set names [lsort -ascii -index 0 $names]
+	# neither can we use lsearch -index
+	set lnames [list]
+	foreach item $names {
+		lappend lnames [lindex $item 0]
+	}
+
+	foreach grp $encoding_aliases {
+		set target {}
+		foreach item $grp {
+			set i [lsearch -sorted -ascii $lnames \
+					[string tolower $item]]
+			if {$i >= 0} {
+				set target [lindex $names $i 1]
+				break
+			}
 		}
-		if {$i >= 0} break
-	    }
-	    break
+		if {$target eq {}} continue
+		foreach item $grp {
+			set encoding_lookup_table([string tolower $item]) $target
+		}
 	}
-    }
-    if {$i >= 0} {
-	return [lindex $names $i]
-    }
-    return {}
+
+	foreach item $names {
+		set encoding_lookup_table([lindex $item 0]) [lindex $item 1]
+	}
+}
+
+proc tcl_encoding {enc} {
+	global encoding_lookup_table
+	if {$enc eq {}} {
+		return {}
+	}
+	if {![info exists encoding_lookup_table]} {
+		build_encoding_table
+	}
+	set enc [string tolower $enc]
+	if {![info exists encoding_lookup_table($enc)]} {
+		# look for "isonnn" instead of "iso-nnn" or "iso_nnn"
+		if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
+			set enc $encx
+		}
+	}
+	if {[info exists encoding_lookup_table($enc)]} {
+		return $encoding_lookup_table($enc)
+	} else {
+		return {}
+	}
+}
+
+proc force_path_encoding {path enc} {
+	global path_encoding_overrides last_encoding_override
+
+	set enc [tcl_encoding $enc]
+	if {$enc eq {}} {
+		catch { unset last_encoding_override }
+		catch { unset path_encoding_overrides($path) }
+	} else {
+		set last_encoding_override $enc
+		if {$path ne {}} {
+			set path_encoding_overrides($path) $enc
+		}
+	}
+}
+
+proc get_path_encoding {path} {
+	global path_encoding_overrides last_encoding_override
+
+	if {[info exists last_encoding_override]} {
+		set tcl_enc $last_encoding_override
+	} else {
+		set tcl_enc [tcl_encoding [get_config gui.encoding]]
+	}
+	if {$tcl_enc eq {}} {
+		set tcl_enc [encoding system]
+	}
+	if {$path ne {}} {
+		if {[info exists path_encoding_overrides($path)]} {
+			set enc2 $path_encoding_overrides($path)
+		} else {
+			set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
+		}
+		if {$enc2 ne {}} {
+			set tcl_enc $enc2
+		}
+	}
+	return $tcl_enc
+}
+
+proc build_encoding_submenu {parent grp cmd} {
+	global used_encodings
+
+	set mid [lindex $grp 0]
+	set gname [mc [lindex $grp 1]]
+
+	set smenu {}
+	foreach subset [lrange $grp 2 end] {
+		set name [mc [lindex $subset 0]]
+
+		foreach enc [lrange $subset 1 end] {
+			set tcl_enc [tcl_encoding $enc]
+			if {$tcl_enc eq {}} continue
+
+			if {$smenu eq {}} {
+				if {$mid eq {}} {
+					set smenu $parent
+				} else {
+					set smenu "$parent.$mid"
+					menu $smenu
+					$parent add cascade \
+						-label $gname \
+						-menu $smenu
+				}
+			}
+
+			if {$name ne {}} {
+				set lbl "$name ($enc)"
+			} else {
+				set lbl $enc
+			}
+			$smenu add command \
+				-label $lbl \
+				-command [concat $cmd [list $tcl_enc]]
+
+			lappend used_encodings $tcl_enc
+		}
+	}
+}
+
+proc popup_btn_menu {m b} {
+	tk_popup $m [winfo pointerx $b] [winfo pointery $b]
+}
+
+proc build_encoding_menu {emenu cmd {nodef 0}} {
+	$emenu configure -postcommand \
+		[list do_build_encoding_menu $emenu $cmd $nodef]
+}
+
+proc do_build_encoding_menu {emenu cmd {nodef 0}} {
+	global used_encodings encoding_groups
+
+	$emenu configure -postcommand {}
+
+	if {!$nodef} {
+		$emenu add command \
+			-label [mc "Default"] \
+			-command [concat $cmd [list {}]]
+	}
+	set sysenc [encoding system]
+	$emenu add command \
+		-label [mc "System (%s)" $sysenc] \
+		-command [concat $cmd [list $sysenc]]
+
+	# Main encoding tree
+	set used_encodings [list identity]
+	$emenu add separator
+	foreach grp $encoding_groups {
+		build_encoding_submenu $emenu $grp $cmd
+	}
+
+	# Add unclassified encodings
+	set unused_grp [list [mc Other]]
+	foreach enc [encoding names] {
+		if {[lsearch -exact $used_encodings $enc] < 0} {
+			lappend unused_grp $enc
+		}
+	}
+	build_encoding_submenu $emenu [list other [mc Other] $unused_grp] $cmd
 }
diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl
index 3c1fce7..d33896a 100644
--- a/git-gui/lib/index.tcl
+++ b/git-gui/lib/index.tcl
@@ -99,6 +99,7 @@
 		switch -glob -- [lindex $s 0] {
 		A? {set new _O}
 		M? {set new _M}
+		T_ {set new _T}
 		D_ {set new _D}
 		D? {set new _?}
 		?? {continue}
@@ -162,6 +163,8 @@
 		?D {set new D_}
 		_O -
 		AM {set new A_}
+		_T {set new T_}
+		_U -
 		U? {
 			if {[file exists $path]} {
 				set new M_
@@ -231,6 +234,7 @@
 		switch -glob -- [lindex $file_states($path) 0] {
 		U? {continue}
 		?M -
+		?T -
 		?D {
 			puts -nonewline $fd "[encoding convertto $path]\0"
 			display_file $path ?_
@@ -252,6 +256,7 @@
 		switch -glob -- [lindex $file_states($path) 0] {
 		A? -
 		M? -
+		T_ -
 		D? {
 			lappend pathList $path
 			if {$path eq $current_diff_path} {
@@ -293,10 +298,18 @@
 	set after {}
 	foreach path $paths {
 		switch -glob -- [lindex $file_states($path) 0] {
+		_U -
+		U? {
+			if {$path eq $current_diff_path} {
+				unlock_index
+				merge_stage_workdir $path
+				return
+			}
+		}
 		_O -
 		?M -
 		?D -
-		U? {
+		?T {
 			lappend pathList $path
 			if {$path eq $current_diff_path} {
 				set after {reshow_diff;}
@@ -336,6 +349,7 @@
 		switch -glob -- [lindex $file_states($path) 0] {
 		U? {continue}
 		?M -
+		?T -
 		?D {lappend paths $path}
 		}
 	}
@@ -353,6 +367,7 @@
 		switch -glob -- [lindex $file_states($path) 0] {
 		U? {continue}
 		?M -
+		?T -
 		?D {
 			lappend pathList $path
 			if {$path eq $current_diff_path} {
@@ -409,11 +424,11 @@
 
 	if {[array size selected_paths] > 0} {
 		revert_helper \
-			{Reverting selected files} \
+			[mc "Reverting selected files"] \
 			[array names selected_paths]
 	} elseif {$current_diff_path ne {}} {
 		revert_helper \
-			"Reverting [short_path $current_diff_path]" \
+			[mc "Reverting %s" [short_path $current_diff_path]] \
 			[list $current_diff_path]
 	}
 }
diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl
index 5c01875..283e491 100644
--- a/git-gui/lib/merge.tcl
+++ b/git-gui/lib/merge.tcl
@@ -40,6 +40,7 @@
 		_O {
 			continue; # and pray it works!
 		}
+		_U -
 		U? {
 			error_popup [mc "You are in the middle of a conflicted merge.
 
diff --git a/git-gui/lib/mergetool.tcl b/git-gui/lib/mergetool.tcl
new file mode 100644
index 0000000..eb2b4b5
--- /dev/null
+++ b/git-gui/lib/mergetool.tcl
@@ -0,0 +1,393 @@
+# git-gui merge conflict resolution
+# parts based on git-mergetool (c) 2006 Theodore Y. Ts'o
+
+proc merge_resolve_one {stage} {
+	global current_diff_path
+
+	switch -- $stage {
+		1 { set targetquestion [mc "Force resolution to the base version?"] }
+		2 { set targetquestion [mc "Force resolution to this branch?"] }
+		3 { set targetquestion [mc "Force resolution to the other branch?"] }
+	}
+
+	set op_question [strcat $targetquestion "\n" \
+[mc "Note that the diff shows only conflicting changes.
+
+%s will be overwritten.
+
+This operation can be undone only by restarting the merge." \
+		[short_path $current_diff_path]]]
+
+	if {[ask_popup $op_question] eq {yes}} {
+		merge_load_stages $current_diff_path [list merge_force_stage $stage]
+	}
+}
+
+proc merge_stage_workdir {path {lno {}}} {
+	global current_diff_path diff_active
+	global current_diff_side ui_workdir
+
+	if {$diff_active} return
+
+	if {$path ne $current_diff_path || $ui_workdir ne $current_diff_side} {
+		show_diff $path $ui_workdir $lno {} [list do_merge_stage_workdir $path]
+	} else {
+		do_merge_stage_workdir $path
+	}
+}
+
+proc do_merge_stage_workdir {path} {
+	global current_diff_path is_conflict_diff
+
+	if {$path ne $current_diff_path} return;
+
+	if {$is_conflict_diff} {
+		if {[ask_popup [mc "File %s seems to have unresolved conflicts, still stage?" \
+				[short_path $path]]] ne {yes}} {
+			return
+		}
+	}
+
+	merge_add_resolution $path
+}
+
+proc merge_add_resolution {path} {
+	global current_diff_path ui_workdir
+
+	set after [next_diff_after_action $ui_workdir $path {} {^_?U}]
+
+	update_index \
+		[mc "Adding resolution for %s" [short_path $path]] \
+		[list $path] \
+		[concat $after [list ui_ready]]
+}
+
+proc merge_force_stage {stage} {
+	global current_diff_path merge_stages
+
+	if {$merge_stages($stage) ne {}} {
+		git checkout-index -f --stage=$stage -- $current_diff_path
+	} else {
+		file delete -- $current_diff_path
+	}
+
+	merge_add_resolution $current_diff_path
+}
+
+proc merge_load_stages {path cont} {
+	global merge_stages_fd merge_stages merge_stages_buf
+
+	if {[info exists merge_stages_fd]} {
+		catch { kill_file_process $merge_stages_fd }
+		catch { close $merge_stages_fd }
+	}
+
+	set merge_stages(0) {}
+	set merge_stages(1) {}
+	set merge_stages(2) {}
+	set merge_stages(3) {}
+	set merge_stages_buf {}
+
+	set merge_stages_fd [eval git_read ls-files -u -z -- $path]
+
+	fconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary
+	fileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont]
+}
+
+proc read_merge_stages {fd cont} {
+	global merge_stages_buf merge_stages_fd merge_stages
+
+	append merge_stages_buf [read $fd]
+	set pck [split $merge_stages_buf "\0"]
+	set merge_stages_buf [lindex $pck end]
+
+	if {[eof $fd] && $merge_stages_buf ne {}} {
+		lappend pck {}
+		set merge_stages_buf {}
+	}
+
+	foreach p [lrange $pck 0 end-1] {
+		set fcols [split $p "\t"]
+		set cols  [split [lindex $fcols 0] " "]
+		set stage [lindex $cols 2]
+		
+		set merge_stages($stage) [lrange $cols 0 1]
+	}
+
+	if {[eof $fd]} {
+		close $fd
+		unset merge_stages_fd
+		eval $cont
+	}
+}
+
+proc merge_resolve_tool {} {
+	global current_diff_path
+
+	merge_load_stages $current_diff_path [list merge_resolve_tool2]
+}
+
+proc merge_resolve_tool2 {} {
+	global current_diff_path merge_stages
+
+	# Validate the stages
+	if {$merge_stages(2) eq {} ||
+	    [lindex $merge_stages(2) 0] eq {120000} ||
+	    [lindex $merge_stages(2) 0] eq {160000} ||
+	    $merge_stages(3) eq {} ||
+	    [lindex $merge_stages(3) 0] eq {120000} ||
+	    [lindex $merge_stages(3) 0] eq {160000}
+	} {
+		error_popup [mc "Cannot resolve deletion or link conflicts using a tool"]
+		return
+	}
+
+	if {![file exists $current_diff_path]} {
+		error_popup [mc "Conflict file does not exist"]
+		return
+	}
+
+	# Determine the tool to use
+	set tool [get_config merge.tool]
+	if {$tool eq {}} { set tool meld }
+
+	set merge_tool_path [get_config "mergetool.$tool.path"]
+	if {$merge_tool_path eq {}} {
+		switch -- $tool {
+		emerge { set merge_tool_path "emacs" }
+		araxis { set merge_tool_path "compare" }
+		default { set merge_tool_path $tool }
+		}
+	}
+
+	# Make file names
+	set filebase [file rootname $current_diff_path]
+	set fileext  [file extension $current_diff_path]
+	set basename [lindex [file split $current_diff_path] end]
+
+	set MERGED   $current_diff_path
+	set BASE     "./$MERGED.BASE$fileext"
+	set LOCAL    "./$MERGED.LOCAL$fileext"
+	set REMOTE   "./$MERGED.REMOTE$fileext"
+	set BACKUP   "./$MERGED.BACKUP$fileext"
+
+	set base_stage $merge_stages(1)
+
+	# Build the command line
+	switch -- $tool {
+	kdiff3 {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Base)" \
+				--L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE"]
+		} else {
+			set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Local)" \
+				--L2 "$MERGED (Remote)" -o "$MERGED" "$LOCAL" "$REMOTE"]
+		}
+	}
+	tkdiff {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"]
+		} else {
+			set cmdline [list "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"]
+		}
+	}
+	meld {
+		set cmdline [list "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"]
+	}
+	gvimdiff {
+		set cmdline [list "$merge_tool_path" -f "$LOCAL" "$MERGED" "$REMOTE"]
+	}
+	xxdiff {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" -X --show-merged-pane \
+					    -R {Accel.SaveAsMerged: "Ctrl-S"} \
+					    -R {Accel.Search: "Ctrl+F"} \
+					    -R {Accel.SearchForward: "Ctrl-G"} \
+					    --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"]
+		} else {
+			set cmdline [list "$merge_tool_path" -X --show-merged-pane \
+					    -R {Accel.SaveAsMerged: "Ctrl-S"} \
+					    -R {Accel.Search: "Ctrl+F"} \
+					    -R {Accel.SearchForward: "Ctrl-G"} \
+					    --merged-file "$MERGED" "$LOCAL" "$REMOTE"]
+		}
+	}
+	opendiff {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"]
+		} else {
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED"]
+		}
+	}
+	ecmerge {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"]
+		} else {
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"]
+		}
+	}
+	emerge {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" -f emerge-files-with-ancestor-command \
+					"$LOCAL" "$REMOTE" "$BASE" "$basename"]
+		} else {
+			set cmdline [list "$merge_tool_path" -f emerge-files-command \
+					"$LOCAL" "$REMOTE" "$basename"]
+		}
+	}
+	winmerge {
+		if {$base_stage ne {}} {
+			# This tool does not support 3-way merges.
+			# Use the 'conflict file' resolution feature instead.
+			set cmdline [list "$merge_tool_path" -e -ub "$MERGED"]
+		} else {
+			set cmdline [list "$merge_tool_path" -e -ub -wl \
+				-dl "Theirs File" -dr "Mine File" "$REMOTE" "$LOCAL" "$MERGED"]
+		}
+	}
+	araxis {
+		if {$base_stage ne {}} {
+			set cmdline [list "$merge_tool_path" -wait -merge -3 -a1 \
+				-title1:"'$MERGED (Base)'" -title2:"'$MERGED (Local)'" \
+				-title3:"'$MERGED (Remote)'" \
+				"$BASE" "$LOCAL" "$REMOTE" "$MERGED"]
+		} else {
+			set cmdline [list "$merge_tool_path" -wait -2 \
+				 -title1:"'$MERGED (Local)'" -title2:"'$MERGED (Remote)'" \
+				 "$LOCAL" "$REMOTE" "$MERGED"]
+		}
+	}
+	p4merge {
+		set cmdline [list "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"]
+	}
+	vimdiff {
+		error_popup [mc "Not a GUI merge tool: '%s'" $tool]
+		return
+	}
+	default {
+		error_popup [mc "Unsupported merge tool '%s'" $tool]
+		return
+	}
+	}
+
+	merge_tool_start $cmdline $MERGED $BACKUP [list $BASE $LOCAL $REMOTE]
+}
+
+proc delete_temp_files {files} {
+	foreach fname $files {
+		file delete $fname
+	}
+}
+
+proc merge_tool_get_stages {target stages} {
+	global merge_stages
+
+	set i 1
+	foreach fname $stages {
+		if {$merge_stages($i) eq {}} {
+			file delete $fname
+			catch { close [open $fname w] }
+		} else {
+			# A hack to support autocrlf properly
+			git checkout-index -f --stage=$i -- $target
+			file rename -force -- $target $fname
+		}
+		incr i
+	}
+}
+
+proc merge_tool_start {cmdline target backup stages} {
+	global merge_stages mtool_target mtool_tmpfiles mtool_fd mtool_mtime
+
+	if {[info exists mtool_fd]} {
+		if {[ask_popup [mc "Merge tool is already running, terminate it?"]] eq {yes}} {
+			catch { kill_file_process $mtool_fd }
+			catch { close $mtool_fd }
+			unset mtool_fd
+
+			set old_backup [lindex $mtool_tmpfiles end]
+			file rename -force -- $old_backup $mtool_target
+			delete_temp_files $mtool_tmpfiles
+		} else {
+			return
+		}
+	}
+
+	# Save the original file
+	file rename -force -- $target $backup
+
+	# Get the blobs; it destroys $target
+	if {[catch {merge_tool_get_stages $target $stages} err]} {
+		file rename -force -- $backup $target
+		delete_temp_files $stages
+		error_popup [mc "Error retrieving versions:\n%s" $err]
+		return
+	}
+
+	# Restore the conflict file
+	file copy -force -- $backup $target
+
+	# Initialize global state
+	set mtool_target $target
+	set mtool_mtime [file mtime $target]
+	set mtool_tmpfiles $stages
+
+	lappend mtool_tmpfiles $backup
+
+	# Force redirection to avoid interpreting output on stderr
+	# as an error, and launch the tool
+	lappend cmdline {2>@1}
+
+	if {[catch { set mtool_fd [_open_stdout_stderr $cmdline] } err]} {
+		delete_temp_files $mtool_tmpfiles
+		error_popup [mc "Could not start the merge tool:\n\n%s" $err]
+		return
+	}
+
+	ui_status [mc "Running merge tool..."]
+
+	fconfigure $mtool_fd -blocking 0 -translation binary -encoding binary
+	fileevent $mtool_fd readable [list read_mtool_output $mtool_fd]
+}
+
+proc read_mtool_output {fd} {
+	global mtool_fd mtool_tmpfiles
+
+	read $fd
+	if {[eof $fd]} {
+		unset mtool_fd
+
+		fconfigure $fd -blocking 1
+		merge_tool_finish $fd
+	}
+}
+
+proc merge_tool_finish {fd} {
+	global mtool_tmpfiles mtool_target mtool_mtime
+
+	set backup [lindex $mtool_tmpfiles end]
+	set failed 0
+
+	# Check the return code
+	if {[catch {close $fd} err]} {
+		set failed 1
+		if {$err ne {child process exited abnormally}} {
+			error_popup [strcat [mc "Merge tool failed."] "\n\n$err"]
+		}
+	}
+
+	# Finish
+	if {$failed} {
+		file rename -force -- $backup $mtool_target
+		delete_temp_files $mtool_tmpfiles
+		ui_status [mc "Merge tool failed."]
+	} else {
+		if {[is_config_true merge.keepbackup]} {
+			file rename -force -- $backup "$mtool_target.orig"
+		}
+
+		delete_temp_files $mtool_tmpfiles
+
+		reshow_diff
+	}
+}
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl
index 5e1346e..1d55b49 100644
--- a/git-gui/lib/option.tcl
+++ b/git-gui/lib/option.tcl
@@ -1,9 +1,31 @@
 # git-gui options editor
 # Copyright (C) 2006, 2007 Shawn Pearce
 
+proc config_check_encodings {} {
+	global repo_config_new global_config_new
+
+	set enc $global_config_new(gui.encoding)
+	if {$enc eq {}} {
+		set global_config_new(gui.encoding) [encoding system]
+	} elseif {[tcl_encoding $enc] eq {}} {
+		error_popup [mc "Invalid global encoding '%s'" $enc]
+		return 0
+	}
+
+	set enc $repo_config_new(gui.encoding)
+	if {$enc eq {}} {
+		set repo_config_new(gui.encoding) [encoding system]
+	} elseif {[tcl_encoding $enc] eq {}} {
+		error_popup [mc "Invalid repo encoding '%s'" $enc]
+		return 0
+	}
+
+	return 1
+}
+
 proc save_config {} {
 	global default_config font_descs
-	global repo_config global_config
+	global repo_config global_config system_config
 	global repo_config_new global_config_new
 	global ui_comm_spell
 
@@ -27,7 +49,7 @@
 	foreach name [array names default_config] {
 		set value $global_config_new($name)
 		if {$value ne $global_config($name)} {
-			if {$value eq $default_config($name)} {
+			if {$value eq $system_config($name)} {
 				catch {git config --global --unset $name}
 			} else {
 				regsub -all "\[{}\]" $value {"} value
@@ -119,15 +141,18 @@
 		{b merge.summary {mc "Summarize Merge Commits"}}
 		{i-1..5 merge.verbosity {mc "Merge Verbosity"}}
 		{b merge.diffstat {mc "Show Diffstat After Merge"}}
+		{t merge.tool {mc "Use Merge Tool"}}
 
 		{b gui.trustmtime  {mc "Trust File Modification Timestamps"}}
 		{b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
 		{b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
 		{b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}}
 		{i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}}
+		{i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}}
 		{i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
 		{i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
 		{t gui.newbranchtemplate {mc "New Branch Name Template"}}
+		{c gui.encoding {mc "Default File Contents Encoding"}}
 		} {
 		set type [lindex $option 0]
 		set name [lindex $option 1]
@@ -157,6 +182,7 @@
 				pack $w.$f.$optid.v -side right -anchor e -padx 5
 				pack $w.$f.$optid -side top -anchor w -fill x
 			}
+			c -
 			t {
 				frame $w.$f.$optid
 				label $w.$f.$optid.l -text "$text:"
@@ -169,6 +195,16 @@
 				pack $w.$f.$optid.v -side left -anchor w \
 					-fill x -expand 1 \
 					-padx 5
+				if {$type eq {c}} {
+					menu $w.$f.$optid.m
+					build_encoding_menu $w.$f.$optid.m \
+						[list set ${f}_config_new($name)] 1
+					button $w.$f.$optid.b \
+						-text [mc "Change"] \
+						-command [list popup_btn_menu \
+							$w.$f.$optid.m $w.$f.$optid.b]
+					pack $w.$f.$optid.b -side left -anchor w
+				}
 				pack $w.$f.$optid -side top -anchor w -fill x
 			}
 			}
@@ -248,17 +284,17 @@
 }
 
 proc do_restore_defaults {} {
-	global font_descs default_config repo_config
+	global font_descs default_config repo_config system_config
 	global repo_config_new global_config_new
 
 	foreach name [array names default_config] {
-		set repo_config_new($name) $default_config($name)
-		set global_config_new($name) $default_config($name)
+		set repo_config_new($name) $system_config($name)
+		set global_config_new($name) $system_config($name)
 	}
 
 	foreach option $font_descs {
 		set name [lindex $option 0]
-		set repo_config(gui.$name) $default_config(gui.$name)
+		set repo_config(gui.$name) $system_config(gui.$name)
 	}
 	apply_config
 
@@ -273,6 +309,7 @@
 }
 
 proc do_save_config {w} {
+	if {![config_check_encodings]} return
 	if {[catch {save_config} err]} {
 		error_popup [strcat [mc "Failed to completely save options:"] "\n\n$err"]
 	}
diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl
index 0e86dda..b92b429 100644
--- a/git-gui/lib/remote.tcl
+++ b/git-gui/lib/remote.tcl
@@ -132,91 +132,145 @@
 	set all_remotes [lsort -unique $all_remotes]
 }
 
-proc populate_fetch_menu {} {
-	global all_remotes repo_config
-
+proc add_fetch_entry {r} {
+	global repo_config
 	set remote_m .mbar.remote
 	set fetch_m $remote_m.fetch
 	set prune_m $remote_m.prune
-
-	foreach r $all_remotes {
-		set enable 0
-		if {![catch {set a $repo_config(remote.$r.url)}]} {
-			if {![catch {set a $repo_config(remote.$r.fetch)}]} {
-				set enable 1
-			}
-		} else {
-			catch {
-				set fd [open [gitdir remotes $r] r]
-				while {[gets $fd n] >= 0} {
-					if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
-						set enable 1
-						break
-					}
+	set remove_m $remote_m.remove
+	set enable 0
+	if {![catch {set a $repo_config(remote.$r.url)}]} {
+		if {![catch {set a $repo_config(remote.$r.fetch)}]} {
+			set enable 1
+		}
+	} else {
+		catch {
+			set fd [open [gitdir remotes $r] r]
+			while {[gets $fd n] >= 0} {
+				if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
+					set enable 1
+					break
 				}
-				close $fd
 			}
+			close $fd
+		}
+	}
+
+	if {$enable} {
+		if {![winfo exists $fetch_m]} {
+			menu $remove_m
+			$remote_m insert 0 cascade \
+				-label [mc "Remove Remote"] \
+				-menu $remove_m
+
+			menu $prune_m
+			$remote_m insert 0 cascade \
+				-label [mc "Prune from"] \
+				-menu $prune_m
+
+			menu $fetch_m
+			$remote_m insert 0 cascade \
+				-label [mc "Fetch from"] \
+				-menu $fetch_m
 		}
 
-		if {$enable} {
-			if {![winfo exists $fetch_m]} {
-				menu $prune_m
-				$remote_m insert 0 cascade \
-					-label [mc "Prune from"] \
-					-menu $prune_m
-
-				menu $fetch_m
-				$remote_m insert 0 cascade \
-					-label [mc "Fetch from"] \
-					-menu $fetch_m
-			}
-
-			$fetch_m add command \
-				-label $r \
-				-command [list fetch_from $r]
-			$prune_m add command \
-				-label $r \
-				-command [list prune_from $r]
-		}
+		$fetch_m add command \
+			-label $r \
+			-command [list fetch_from $r]
+		$prune_m add command \
+			-label $r \
+			-command [list prune_from $r]
+		$remove_m add command \
+			-label $r \
+			-command [list remove_remote $r]
 	}
 }
 
-proc populate_push_menu {} {
-	global all_remotes repo_config
-
+proc add_push_entry {r} {
+	global repo_config
 	set remote_m .mbar.remote
 	set push_m $remote_m.push
-
-	foreach r $all_remotes {
-		set enable 0
-		if {![catch {set a $repo_config(remote.$r.url)}]} {
-			if {![catch {set a $repo_config(remote.$r.push)}]} {
-				set enable 1
-			}
-		} else {
-			catch {
-				set fd [open [gitdir remotes $r] r]
-				while {[gets $fd n] >= 0} {
-					if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
-						set enable 1
-						break
-					}
-				}
-				close $fd
-			}
+	set enable 0
+	if {![catch {set a $repo_config(remote.$r.url)}]} {
+		if {![catch {set a $repo_config(remote.$r.push)}]} {
+			set enable 1
 		}
-
-		if {$enable} {
-			if {![winfo exists $push_m]} {
-				menu $push_m
-				$remote_m insert 0 cascade \
-					-label [mc "Push to"] \
-					-menu $push_m
+	} else {
+		catch {
+			set fd [open [gitdir remotes $r] r]
+			while {[gets $fd n] >= 0} {
+				if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
+					set enable 1
+					break
+				}
 			}
-
-			$push_m add command \
-				-label $r \
-				-command [list push_to $r]
+			close $fd
 		}
 	}
+
+	if {$enable} {
+		if {![winfo exists $push_m]} {
+			menu $push_m
+			$remote_m insert 0 cascade \
+				-label [mc "Push to"] \
+				-menu $push_m
+		}
+
+		$push_m add command \
+			-label $r \
+			-command [list push_to $r]
+	}
+}
+
+proc populate_remotes_menu {} {
+	global all_remotes
+
+	foreach r $all_remotes {
+		add_fetch_entry $r
+		add_push_entry $r
+	}
+}
+
+proc add_single_remote {name location} {
+	global all_remotes repo_config
+	lappend all_remotes $name
+
+	git remote add $name $location
+
+	# XXX: Better re-read the config so that we will never get out
+	# of sync with git remote implementation?
+	set repo_config(remote.$name.url) $location
+	set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*"
+
+	add_fetch_entry $name
+	add_push_entry $name
+}
+
+proc delete_from_menu {menu name} {
+	if {[winfo exists $menu]} {
+		$menu delete $name
+	}
+}
+
+proc remove_remote {name} {
+	global all_remotes repo_config
+
+	git remote rm $name
+
+	catch {
+		# Missing values are ok
+		unset repo_config(remote.$name.url)
+		unset repo_config(remote.$name.fetch)
+		unset repo_config(remote.$name.push)
+	}
+
+	set i [lsearch -exact all_remotes $name]
+	lreplace all_remotes $i $i
+
+	set remote_m .mbar.remote
+	delete_from_menu $remote_m.fetch $name
+	delete_from_menu $remote_m.prune $name
+	delete_from_menu $remote_m.remove $name
+	# Not all remotes are in the push menu
+	catch { delete_from_menu $remote_m.push $name }
 }
diff --git a/git-gui/lib/remote_add.tcl b/git-gui/lib/remote_add.tcl
new file mode 100644
index 0000000..fb29422
--- /dev/null
+++ b/git-gui/lib/remote_add.tcl
@@ -0,0 +1,191 @@
+# git-gui remote adding support
+# Copyright (C) 2008 Petr Baudis
+
+class remote_add {
+
+field w              ; # widget path
+field w_name         ; # new remote name widget
+field w_loc          ; # new remote location widget
+
+field name         {}; # name of the remote the user has chosen
+field location     {}; # location of the remote the user has chosen
+
+field opt_action fetch; # action to do after registering the remote locally
+
+constructor dialog {} {
+	global repo_config
+
+	make_toplevel top w
+	wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+	}
+
+	label $w.header -text [mc "Add New Remote"] -font font_uibold
+	pack $w.header -side top -fill x
+
+	frame $w.buttons
+	button $w.buttons.create -text [mc Add] \
+		-default active \
+		-command [cb _add]
+	pack $w.buttons.create -side right
+	button $w.buttons.cancel -text [mc Cancel] \
+		-command [list destroy $w]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	labelframe $w.desc -text [mc "Remote Details"]
+
+	label $w.desc.name_l -text [mc "Name:"]
+	set w_name $w.desc.name_t
+	entry $w_name \
+		-borderwidth 1 \
+		-relief sunken \
+		-width 40 \
+		-textvariable @name \
+		-validate key \
+		-validatecommand [cb _validate_name %d %S]
+	grid $w.desc.name_l $w_name -sticky we -padx {0 5}
+
+	label $w.desc.loc_l -text [mc "Location:"]
+	set w_loc $w.desc.loc_t
+	entry $w_loc \
+		-borderwidth 1 \
+		-relief sunken \
+		-width 40 \
+		-textvariable @location
+	grid $w.desc.loc_l $w_loc -sticky we -padx {0 5}
+
+	grid columnconfigure $w.desc 1 -weight 1
+	pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+	labelframe $w.action -text [mc "Further Action"]
+
+	radiobutton $w.action.fetch \
+		-text [mc "Fetch Immediately"] \
+		-value fetch \
+		-variable @opt_action
+	pack $w.action.fetch -anchor nw
+
+	radiobutton $w.action.push \
+		-text [mc "Initialize Remote Repository and Push"] \
+		-value push \
+		-variable @opt_action
+	pack $w.action.push -anchor nw
+
+	radiobutton $w.action.none \
+		-text [mc "Do Nothing Else Now"] \
+		-value none \
+		-variable @opt_action
+	pack $w.action.none -anchor nw
+
+	grid columnconfigure $w.action 1 -weight 1
+	pack $w.action -anchor nw -fill x -pady 5 -padx 5
+
+	bind $w <Visibility> [cb _visible]
+	bind $w <Key-Escape> [list destroy $w]
+	bind $w <Key-Return> [cb _add]\;break
+	tkwait window $w
+}
+
+method _add {} {
+	global repo_config env
+	global M1B
+
+	if {$name eq {}} {
+		tk_messageBox \
+			-icon error \
+			-type ok \
+			-title [wm title $w] \
+			-parent $w \
+			-message [mc "Please supply a remote name."]
+		focus $w_name
+		return
+	}
+
+	# XXX: We abuse check-ref-format here, but
+	# that should be ok.
+	if {[catch {git check-ref-format "remotes/$name"}]} {
+		tk_messageBox \
+			-icon error \
+			-type ok \
+			-title [wm title $w] \
+			-parent $w \
+			-message [mc "'%s' is not an acceptable remote name." $name]
+		focus $w_name
+		return
+	}
+
+	if {[catch {add_single_remote $name $location}]} {
+		tk_messageBox \
+			-icon error \
+			-type ok \
+			-title [wm title $w] \
+			-parent $w \
+			-message [mc "Failed to add remote '%s' of location '%s'." $name $location]
+		focus $w_name
+		return
+	}
+
+	switch -- $opt_action {
+	fetch {
+		set c [console::new \
+			[mc "fetch %s" $name] \
+			[mc "Fetching the %s" $name]]
+		console::exec $c [list git fetch $name]
+	}
+	push {
+		set cmds [list]
+
+		# Parse the location
+		if { [regexp {(?:git\+)?ssh://([^/]+)(/.+)} $location xx host path]
+		     || [regexp {([^:][^:]+):(.+)} $location xx host path]} {
+			set ssh ssh
+			if {[info exists env(GIT_SSH)]} {
+				set ssh $env(GIT_SSH)
+			}
+			lappend cmds [list exec $ssh $host mkdir -p $location && git --git-dir=$path init --bare]
+		} elseif { ! [regexp {://} $location xx] } {
+			lappend cmds [list exec mkdir -p $location]
+			lappend cmds [list exec git --git-dir=$location init --bare]
+		} else {
+			tk_messageBox \
+				-icon error \
+				-type ok \
+				-title [wm title $w] \
+				-parent $w \
+				-message [mc "Do not know how to initialize repository at location '%s'." $location]
+			destroy $w
+			return
+		}
+
+		set c [console::new \
+			[mc "push %s" $name] \
+			[mc "Setting up the %s (at %s)" $name $location]]
+
+		lappend cmds [list exec git push -v --all $name]
+		console::chain $c $cmds
+	}
+	none {
+	}
+	}
+
+	destroy $w
+}
+
+method _validate_name {d S} {
+	if {$d == 1} {
+		if {[regexp {[~^:?*\[\0- ]} $S]} {
+			return 0
+		}
+	}
+	return 1
+}
+
+method _visible {} {
+	grab $w
+	$w_name icursor end
+	focus $w_name
+}
+
+}
diff --git a/git-gui/lib/remote_branch_delete.tcl b/git-gui/lib/remote_branch_delete.tcl
index c7b8148..89eb0f7 100644
--- a/git-gui/lib/remote_branch_delete.tcl
+++ b/git-gui/lib/remote_branch_delete.tcl
@@ -26,12 +26,12 @@
 	global all_remotes M1B
 
 	make_toplevel top w
-	wm title $top [append "[appname] ([reponame]): " [mc "Delete Remote Branch"]]
+	wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]]
 	if {$top ne {.}} {
 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
 	}
 
-	label $w.header -text [mc "Delete Remote Branch"] -font font_uibold
+	label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold
 	pack $w.header -side top -fill x
 
 	frame $w.buttons
@@ -63,7 +63,7 @@
 		set urltype url
 	}
 	radiobutton $w.dest.url_r \
-		-text [mc "Arbitrary URL:"] \
+		-text [mc "Arbitrary Location:"] \
 		-value url \
 		-variable @urltype
 	entry $w.dest.url_t \
diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl
new file mode 100644
index 0000000..b371e9a
--- /dev/null
+++ b/git-gui/lib/search.tcl
@@ -0,0 +1,198 @@
+# incremental search panel
+# based on code from gitk, Copyright (C) Paul Mackerras
+
+class searchbar {
+
+field w
+field ctext
+
+field searchstring   {}
+field casesensitive  1
+field searchdirn     -forwards
+
+field smarktop
+field smarkbot
+
+constructor new {i_w i_text args} {
+	set w      $i_w
+	set ctext  $i_text
+
+	frame  $w
+	label  $w.l       -text [mc Find:]
+	entry  $w.ent -textvariable ${__this}::searchstring -background lightgreen
+	button $w.bn      -text [mc Next] -command [cb find_next]
+	button $w.bp      -text [mc Prev] -command [cb find_prev]
+	checkbutton $w.cs -text [mc Case-Sensitive] \
+		-variable ${__this}::casesensitive -command [cb _incrsearch]
+	pack   $w.l   -side left
+	pack   $w.cs  -side right
+	pack   $w.bp  -side right
+	pack   $w.bn  -side right
+	pack   $w.ent -side left -expand 1 -fill x
+
+	eval grid conf $w -sticky we $args
+	grid remove $w
+
+	trace add variable searchstring write [cb _incrsearch_cb]
+	
+	bind $w <Destroy> [list delete_this $this]
+	return $this
+}
+
+method show {} {
+	if {![visible $this]} {
+		grid $w
+	}
+	focus -force $w.ent
+}
+
+method hide {} {
+	if {[visible $this]} {
+		focus $ctext
+		grid remove $w
+	}
+}
+
+method visible {} {
+	return [winfo ismapped $w]
+}
+
+method editor {} {
+	return $w.ent
+}
+
+method _get_new_anchor {} {
+	# use start of selection if it is visible,
+	# or the bounds of the visible area
+	set top    [$ctext index @0,0]
+	set bottom [$ctext index @0,[winfo height $ctext]]
+	set sel    [$ctext tag ranges sel]
+	if {$sel ne {}} {
+		set spos [lindex $sel 0]
+		if {[lindex $spos 0] >= [lindex $top 0] &&
+		    [lindex $spos 0] <= [lindex $bottom 0]} {
+			return $spos
+		}
+	}
+	if {$searchdirn eq "-forwards"} {
+		return $top
+	} else {
+		return $bottom
+	}
+}
+
+method _get_wrap_anchor {dir} {
+	if {$dir eq "-forwards"} {
+		return 1.0
+	} else {
+		return end
+	}
+}
+
+method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
+	set cmd [list $ctext search]
+	if {$mlenvar ne {}} {
+		upvar $mlenvar mlen
+		lappend cmd -count mlen
+	}
+	if {!$casesensitive} {
+		lappend cmd -nocase
+	}
+	if {$dir eq {}} {
+		set dir $searchdirn
+	}
+	lappend cmd $dir -- $searchstring
+	if {$endbound ne {}} {
+		set here [eval $cmd [list $start] [list $endbound]]
+	} else {
+		set here [eval $cmd [list $start]]
+		if {$here eq {}} {
+			set here [eval $cmd [_get_wrap_anchor $this $dir]]
+		}
+	}
+	return $here
+}
+
+method _incrsearch_cb {name ix op} {
+	after idle [cb _incrsearch]
+}
+
+method _incrsearch {} {
+	$ctext tag remove found 1.0 end
+	if {[catch {$ctext index anchor}]} {
+		$ctext mark set anchor [_get_new_anchor $this]
+	}
+	if {$searchstring ne {}} {
+		set here [_do_search $this anchor mlen]
+		if {$here ne {}} {
+			$ctext see $here
+			$ctext tag remove sel 1.0 end
+			$ctext tag add sel $here "$here + $mlen c"
+			$w.ent configure -background lightgreen
+			_set_marks $this 1
+		} else {
+			$w.ent configure -background lightpink
+		}
+	}
+}
+
+method find_prev {} {
+	find_next $this -backwards
+}
+
+method find_next {{dir -forwards}} {
+	focus $w.ent
+	$w.ent icursor end
+	set searchdirn $dir
+	$ctext mark unset anchor
+	if {$searchstring ne {}} {
+		set start [_get_new_anchor $this]
+		if {$dir eq "-forwards"} {
+			set start "$start + 1c"
+		}
+		set match [_do_search $this $start mlen]
+		$ctext tag remove sel 1.0 end
+		if {$match ne {}} {
+			$ctext see $match
+			$ctext tag add sel $match "$match + $mlen c"
+		}
+	}
+}
+
+method _mark_range {first last} {
+	set mend $first.0
+	while {1} {
+		set match [_do_search $this $mend mlen -forwards $last.end]
+		if {$match eq {}} break
+		set mend "$match + $mlen c"
+		$ctext tag add found $match $mend
+	}
+}
+
+method _set_marks {doall} {
+	set topline [lindex [split [$ctext index @0,0] .] 0]
+	set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
+	if {$doall || $botline < $smarktop || $topline > $smarkbot} {
+		# no overlap with previous
+		_mark_range $this $topline $botline
+		set smarktop $topline
+		set smarkbot $botline
+	} else {
+		if {$topline < $smarktop} {
+			_mark_range $this $topline [expr {$smarktop-1}]
+			set smarktop $topline
+		}
+		if {$botline > $smarkbot} {
+			_mark_range $this [expr {$smarkbot+1}] $botline
+			set smarkbot $botline
+		}
+	}
+}
+
+method scrolled {} {
+	if {$searchstring ne {}} {
+		after idle [cb _set_marks 0]
+	}
+}
+
+}
\ No newline at end of file
diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl
index a479b2f..e612030 100644
--- a/git-gui/lib/spellcheck.tcl
+++ b/git-gui/lib/spellcheck.tcl
@@ -314,6 +314,7 @@
 method _read {} {
 	while {[gets $s_fd line] >= 0} {
 		set lineno [lindex $s_pending 0 0]
+		set line [string trim $line]
 
 		if {$s_clear} {
 			$w_text tag remove misspelled "$lineno.0" "$lineno.end"
diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl
new file mode 100644
index 0000000..82a1a80
--- /dev/null
+++ b/git-gui/lib/sshkey.tcl
@@ -0,0 +1,126 @@
+# git-gui about git-gui dialog
+# Copyright (C) 2006, 2007 Shawn Pearce
+
+proc find_ssh_key {} {
+	foreach name {~/.ssh/id_dsa.pub ~/.ssh/id_rsa.pub ~/.ssh/identity.pub} {
+		if {[file exists $name]} {
+			set fh    [open $name r]
+			set cont  [read $fh]
+			close $fh
+			return [list $name $cont]
+		}
+	}
+
+	return {}
+}
+
+proc do_ssh_key {} {
+	global sshkey_title have_tk85 sshkey_fd
+
+	set w .sshkey_dialog
+	if {[winfo exists $w]} {
+		raise $w
+		return
+	}
+
+	toplevel $w
+	wm transient $w .
+
+	set finfo [find_ssh_key]
+	if {$finfo eq {}} {
+		set sshkey_title [mc "No keys found."]
+		set gen_state   normal
+	} else {
+		set sshkey_title [mc "Found a public key in: %s" [lindex $finfo 0]]
+		set gen_state   disabled
+	}
+
+	frame $w.header -relief flat
+	label $w.header.lbl -textvariable sshkey_title -anchor w
+	button $w.header.gen -text [mc "Generate Key"] \
+		-command [list make_ssh_key $w] -state $gen_state
+	pack $w.header.lbl -side left -expand 1 -fill x
+	pack $w.header.gen -side right
+	pack $w.header -fill x -pady 5 -padx 5
+
+	text $w.contents -width 60 -height 10 -wrap char -relief sunken
+	pack $w.contents -fill both -expand 1
+	if {$have_tk85} {
+		$w.contents configure -inactiveselectbackground darkblue
+	}
+
+	frame $w.buttons
+	button $w.buttons.close -text [mc Close] \
+		-default active -command [list destroy $w]
+	pack $w.buttons.close -side right
+	button $w.buttons.copy -text [mc "Copy To Clipboard"] \
+		-command [list tk_textCopy $w.contents]
+	pack $w.buttons.copy -side left
+	pack $w.buttons -side bottom -fill x -pady 5 -padx 5
+
+	if {$finfo ne {}} {
+		$w.contents insert end [lindex $finfo 1] sel
+	}
+	$w.contents configure -state disabled
+
+	bind $w <Visibility> "grab $w; focus $w.buttons.close"
+	bind $w <Key-Escape> "destroy $w"
+	bind $w <Key-Return> "destroy $w"
+	bind $w <Destroy> kill_sshkey
+	wm title $w [mc "Your OpenSSH Public Key"]
+	tk::PlaceWindow $w widget .
+	tkwait window $w
+}
+
+proc make_ssh_key {w} {
+	global sshkey_title sshkey_output sshkey_fd
+
+	set sshkey_title [mc "Generating..."]
+	$w.header.gen configure -state disabled
+
+	set cmdline [list sh -c {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}]
+
+	if {[catch { set sshkey_fd [_open_stdout_stderr $cmdline] } err]} {
+		error_popup [mc "Could not start ssh-keygen:\n\n%s" $err]
+		return
+	}
+
+	set sshkey_output {}
+	fconfigure $sshkey_fd -blocking 0
+	fileevent $sshkey_fd readable [list read_sshkey_output $sshkey_fd $w]
+}
+
+proc kill_sshkey {} {
+	global sshkey_fd
+	if {![info exists sshkey_fd]} return
+	catch { kill_file_process $sshkey_fd }
+	catch { close $sshkey_fd }
+}
+
+proc read_sshkey_output {fd w} {
+	global sshkey_fd sshkey_output sshkey_title
+
+	set sshkey_output "$sshkey_output[read $fd]"
+	if {![eof $fd]} return
+
+	fconfigure $fd -blocking 1
+	unset sshkey_fd
+
+	$w.contents configure -state normal
+	if {[catch {close $fd} err]} {
+		set sshkey_title [mc "Generation failed."]
+		$w.contents insert end $err
+		$w.contents insert end "\n"
+		$w.contents insert end $sshkey_output
+	} else {
+		set finfo [find_ssh_key]
+		if {$finfo eq {}} {
+			set sshkey_title [mc "Generation succeded, but no keys found."]
+			$w.contents insert end $sshkey_output
+		} else {
+			set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]]
+			$w.contents insert end [lindex $finfo 1] sel
+		}
+	}
+	$w.contents configure -state disable
+}
diff --git a/git-gui/lib/tools.tcl b/git-gui/lib/tools.tcl
new file mode 100644
index 0000000..6ae63b6
--- /dev/null
+++ b/git-gui/lib/tools.tcl
@@ -0,0 +1,159 @@
+# git-gui Tools menu implementation
+
+proc tools_list {} {
+	global repo_config
+
+	set names {}
+	foreach item [array names repo_config guitool.*.cmd] {
+		lappend names [string range $item 8 end-4]
+	}
+	return [lsort $names]
+}
+
+proc tools_populate_all {} {
+	global tools_menubar tools_menutbl
+	global tools_tailcnt
+
+	set mbar_end [$tools_menubar index end]
+	set mbar_base [expr {$mbar_end - $tools_tailcnt}]
+	if {$mbar_base >= 0} {
+		$tools_menubar delete 0 $mbar_base
+	}
+
+	array unset tools_menutbl
+
+	foreach fullname [tools_list] {
+		tools_populate_one $fullname
+	}
+}
+
+proc tools_create_item {parent args} {
+	global tools_menubar tools_tailcnt
+	if {$parent eq $tools_menubar} {
+		set pos [expr {[$parent index end]-$tools_tailcnt+1}]
+		eval [list $parent insert $pos] $args
+	} else {
+		eval [list $parent add] $args
+	}
+}
+
+proc tools_populate_one {fullname} {
+	global tools_menubar tools_menutbl tools_id
+
+	if {![info exists tools_id]} {
+		set tools_id 0
+	}
+
+	set names [split $fullname '/']
+	set parent $tools_menubar
+	for {set i 0} {$i < [llength $names]-1} {incr i} {
+		set subname [join [lrange $names 0 $i] '/']
+		if {[info exists tools_menutbl($subname)]} {
+			set parent $tools_menutbl($subname)
+		} else {
+			set subid $parent.t$tools_id
+			tools_create_item $parent cascade \
+					-label [lindex $names $i] -menu $subid
+			menu $subid
+			set tools_menutbl($subname) $subid
+			set parent $subid
+			incr tools_id
+		}
+	}
+
+	tools_create_item $parent command \
+		-label [lindex $names end] \
+		-command [list tools_exec $fullname]
+}
+
+proc tools_exec {fullname} {
+	global repo_config env current_diff_path
+	global current_branch is_detached
+
+	if {[is_config_true "guitool.$fullname.needsfile"]} {
+		if {$current_diff_path eq {}} {
+			error_popup [mc "Running %s requires a selected file." $fullname]
+			return
+		}
+	}
+
+	catch { unset env(ARGS) }
+	catch { unset env(REVISION) }
+
+	if {[get_config "guitool.$fullname.revprompt"] ne {} ||
+	    [get_config "guitool.$fullname.argprompt"] ne {}} {
+		set dlg [tools_askdlg::dialog $fullname]
+		if {![tools_askdlg::execute $dlg]} {
+			return
+		}
+	} elseif {[is_config_true "guitool.$fullname.confirm"]} {
+		if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} {
+			return
+		}
+	}
+
+	set env(GIT_GUITOOL) $fullname
+	set env(FILENAME) $current_diff_path
+	if {$is_detached} {
+		set env(CUR_BRANCH) ""
+	} else {
+		set env(CUR_BRANCH) $current_branch
+	}
+
+	set cmdline $repo_config(guitool.$fullname.cmd)
+	if {[is_config_true "guitool.$fullname.noconsole"]} {
+		tools_run_silent [list sh -c $cmdline] \
+				 [list tools_complete $fullname {}]
+	} else {
+		regsub {/} $fullname { / } title
+		set w [console::new \
+			[mc "Tool: %s" $title] \
+			[mc "Running: %s" $cmdline]]
+		console::exec $w [list sh -c $cmdline] \
+				 [list tools_complete $fullname $w]
+	}
+
+	unset env(GIT_GUITOOL)
+	unset env(FILENAME)
+	unset env(CUR_BRANCH)
+	catch { unset env(ARGS) }
+	catch { unset env(REVISION) }
+}
+
+proc tools_run_silent {cmd after} {
+	lappend cmd 2>@1
+	set fd [_open_stdout_stderr $cmd]
+
+	fconfigure $fd -blocking 0 -translation binary
+	fileevent $fd readable [list tools_consume_input $fd $after]
+}
+
+proc tools_consume_input {fd after} {
+	read $fd
+	if {[eof $fd]} {
+		fconfigure $fd -blocking 1
+		if {[catch {close $fd}]} {
+			uplevel #0 $after 0
+		} else {
+			uplevel #0 $after 1
+		}
+	}
+}
+
+proc tools_complete {fullname w {ok 1}} {
+	if {$w ne {}} {
+		console::done $w $ok
+	}
+
+	if {$ok} {
+		set msg [mc "Tool completed succesfully: %s" $fullname]
+	} else {
+		set msg [mc "Tool failed: %s" $fullname]
+	}
+
+	if {[is_config_true "guitool.$fullname.norescan"]} {
+		ui_status $msg
+	} else {
+		rescan [list ui_status $msg]
+	}
+}
diff --git a/git-gui/lib/tools_dlg.tcl b/git-gui/lib/tools_dlg.tcl
new file mode 100644
index 0000000..5f7f08e
--- /dev/null
+++ b/git-gui/lib/tools_dlg.tcl
@@ -0,0 +1,421 @@
+# git-gui Tools menu dialogs
+
+class tools_add {
+
+field w              ; # widget path
+field w_name         ; # new remote name widget
+field w_cmd          ; # new remote location widget
+
+field name         {}; # name of the tool
+field command      {}; # command to execute
+field add_global    0; # add to the --global config
+field no_console    0; # disable using the console
+field needs_file    0; # ensure filename is set
+field confirm       0; # ask for confirmation
+field ask_branch    0; # ask for a revision
+field ask_args      0; # ask for additional args
+
+constructor dialog {} {
+	global repo_config
+
+	make_toplevel top w
+	wm title $top [append "[appname] ([reponame]): " [mc "Add Tool"]]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
+	}
+
+	label $w.header -text [mc "Add New Tool Command"] -font font_uibold
+	pack $w.header -side top -fill x
+
+	frame $w.buttons
+	checkbutton $w.buttons.global \
+		-text [mc "Add globally"] \
+		-variable @add_global
+	pack $w.buttons.global -side left -padx 5
+	button $w.buttons.create -text [mc Add] \
+		-default active \
+		-command [cb _add]
+	pack $w.buttons.create -side right
+	button $w.buttons.cancel -text [mc Cancel] \
+		-command [list destroy $w]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	labelframe $w.desc -text [mc "Tool Details"]
+
+	label $w.desc.name_cmnt -anchor w\
+		-text [mc "Use '/' separators to create a submenu tree:"]
+	grid x $w.desc.name_cmnt -sticky we -padx {0 5} -pady {0 2}
+	label $w.desc.name_l -text [mc "Name:"]
+	set w_name $w.desc.name_t
+	entry $w_name \
+		-borderwidth 1 \
+		-relief sunken \
+		-width 40 \
+		-textvariable @name \
+		-validate key \
+		-validatecommand [cb _validate_name %d %S]
+	grid $w.desc.name_l $w_name -sticky we -padx {0 5}
+
+	label $w.desc.cmd_l -text [mc "Command:"]
+	set w_cmd $w.desc.cmd_t
+	entry $w_cmd \
+		-borderwidth 1 \
+		-relief sunken \
+		-width 40 \
+		-textvariable @command
+	grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3}
+
+	grid columnconfigure $w.desc 1 -weight 1
+	pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+	checkbutton $w.confirm \
+		-text [mc "Show a dialog before running"] \
+		-variable @confirm -command [cb _check_enable_dlg]
+
+	labelframe $w.dlg -labelwidget $w.confirm
+
+	checkbutton $w.dlg.askbranch \
+		-text [mc "Ask the user to select a revision (sets \$REVISION)"] \
+		-variable @ask_branch -state disabled
+	pack $w.dlg.askbranch -anchor w -padx 15
+
+	checkbutton $w.dlg.askargs \
+		-text [mc "Ask the user for additional arguments (sets \$ARGS)"] \
+		-variable @ask_args -state disabled
+	pack $w.dlg.askargs -anchor w -padx 15
+
+	pack $w.dlg -anchor nw -fill x -pady {0 8} -padx 5
+
+	checkbutton $w.noconsole \
+		-text [mc "Don't show the command output window"] \
+		-variable @no_console
+	pack $w.noconsole -anchor w -padx 5
+
+	checkbutton $w.needsfile \
+		-text [mc "Run only if a diff is selected (\$FILENAME not empty)"] \
+		-variable @needs_file
+	pack $w.needsfile -anchor w -padx 5
+
+	bind $w <Visibility> [cb _visible]
+	bind $w <Key-Escape> [list destroy $w]
+	bind $w <Key-Return> [cb _add]\;break
+	tkwait window $w
+}
+
+method _check_enable_dlg {} {
+	if {$confirm} {
+		$w.dlg.askbranch configure -state normal
+		$w.dlg.askargs configure -state normal
+	} else {
+		$w.dlg.askbranch configure -state disabled
+		$w.dlg.askargs configure -state disabled
+	}
+}
+
+method _add {} {
+	global repo_config
+
+	if {$name eq {}} {
+		error_popup [mc "Please supply a name for the tool."]
+		focus $w_name
+		return
+	}
+
+	set item "guitool.$name.cmd"
+
+	if {[info exists repo_config($item)]} {
+		error_popup [mc "Tool '%s' already exists." $name]
+		focus $w_name
+		return
+	}
+
+	set cmd [list git config]
+	if {$add_global} { lappend cmd --global }
+	set items {}
+	if {$no_console} { lappend items "guitool.$name.noconsole" }
+	if {$needs_file} { lappend items "guitool.$name.needsfile" }
+	if {$confirm} {
+		if {$ask_args}   { lappend items "guitool.$name.argprompt" }
+		if {$ask_branch} { lappend items "guitool.$name.revprompt" }
+		if {!$ask_args && !$ask_branch} {
+			lappend items "guitool.$name.confirm"
+		}
+	}
+
+	if {[catch {
+		eval $cmd [list $item $command]
+		foreach citem $items { eval $cmd [list $citem yes] }
+	    } err]} {
+		error_popup [mc "Could not add tool:\n%s" $err]
+	} else {
+		set repo_config($item) $command
+		foreach citem $items { set repo_config($citem) yes }
+
+		tools_populate_all
+	}
+
+	destroy $w
+}
+
+method _validate_name {d S} {
+	if {$d == 1} {
+		if {[regexp {[~?*&\[\0\"\\\{]} $S]} {
+			return 0
+		}
+	}
+	return 1
+}
+
+method _visible {} {
+	grab $w
+	$w_name icursor end
+	focus $w_name
+}
+
+}
+
+class tools_remove {
+
+field w              ; # widget path
+field w_names        ; # name list
+
+constructor dialog {} {
+	global repo_config global_config system_config
+
+	load_config 1
+
+	make_toplevel top w
+	wm title $top [append "[appname] ([reponame]): " [mc "Remove Tool"]]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
+	}
+
+	label $w.header -text [mc "Remove Tool Commands"] -font font_uibold
+	pack $w.header -side top -fill x
+
+	frame $w.buttons
+	button $w.buttons.create -text [mc Remove] \
+		-default active \
+		-command [cb _remove]
+	pack $w.buttons.create -side right
+	button $w.buttons.cancel -text [mc Cancel] \
+		-command [list destroy $w]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	frame $w.list
+	set w_names $w.list.l
+	listbox $w_names \
+		-height 10 \
+		-width 30 \
+		-selectmode extended \
+		-exportselection false \
+		-yscrollcommand [list $w.list.sby set]
+	scrollbar $w.list.sby -command [list $w.list.l yview]
+	pack $w.list.sby -side right -fill y
+	pack $w.list.l -side left -fill both -expand 1
+	pack $w.list -fill both -expand 1 -pady 5 -padx 5
+
+	set local_cnt 0
+	foreach fullname [tools_list] {
+		# Cannot delete system tools
+		if {[info exists system_config(guitool.$fullname.cmd)]} continue
+
+		$w_names insert end $fullname
+		if {![info exists global_config(guitool.$fullname.cmd)]} {
+			$w_names itemconfigure end -foreground blue
+			incr local_cnt
+		}
+	}
+
+	if {$local_cnt > 0} {
+		label $w.colorlbl -foreground blue \
+			-text [mc "(Blue denotes repository-local tools)"]
+		pack $w.colorlbl -fill x -pady 5 -padx 5
+	}
+
+	bind $w <Visibility> [cb _visible]
+	bind $w <Key-Escape> [list destroy $w]
+	bind $w <Key-Return> [cb _remove]\;break
+	tkwait window $w
+}
+
+method _remove {} {
+	foreach i [$w_names curselection] {
+		set name [$w_names get $i]
+
+		catch { git config --remove-section guitool.$name }
+		catch { git config --global --remove-section guitool.$name }
+	}
+
+	load_config 0
+	tools_populate_all
+
+	destroy $w
+}
+
+method _visible {} {
+	grab $w
+	focus $w_names
+}
+
+}
+
+class tools_askdlg {
+
+field w              ; # widget path
+field w_rev        {}; # revision browser
+field w_args       {}; # arguments
+
+field is_ask_args   0; # has arguments field
+field is_ask_revs   0; # has revision browser
+
+field is_ok         0; # ok to start
+field argstr       {}; # arguments
+
+constructor dialog {fullname} {
+	global M1B
+
+	set title [get_config "guitool.$fullname.title"]
+	if {$title eq {}} {
+		regsub {/} $fullname { / } title
+	}
+
+	make_toplevel top w -autodelete 0
+	wm title $top [append "[appname] ([reponame]): " $title]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
+	}
+
+	set prompt [get_config "guitool.$fullname.prompt"]
+	if {$prompt eq {}} {
+		set command [get_config "guitool.$fullname.cmd"]
+		set prompt [mc "Run Command: %s" $command]
+	}
+
+	label $w.header -text $prompt -font font_uibold
+	pack $w.header -side top -fill x
+
+	set argprompt [get_config "guitool.$fullname.argprompt"]
+	set revprompt [get_config "guitool.$fullname.revprompt"]
+
+	set is_ask_args [expr {$argprompt ne {}}]
+	set is_ask_revs [expr {$revprompt ne {}}]
+
+	if {$is_ask_args} {
+		if {$argprompt eq {yes} || $argprompt eq {true} || $argprompt eq {1}} {
+			set argprompt [mc "Arguments"]
+		}
+
+		labelframe $w.arg -text $argprompt
+
+		set w_args $w.arg.txt
+		entry $w_args \
+			-borderwidth 1 \
+			-relief sunken \
+			-width 40 \
+			-textvariable @argstr
+		pack $w_args -padx 5 -pady 5 -fill both
+		pack $w.arg -anchor nw -fill both -pady 5 -padx 5
+	}
+
+	if {$is_ask_revs} {
+		if {$revprompt eq {yes} || $revprompt eq {true} || $revprompt eq {1}} {
+			set revprompt [mc "Revision"]
+		}
+
+		if {[is_config_true "guitool.$fullname.revunmerged"]} {
+			set w_rev [::choose_rev::new_unmerged $w.rev $revprompt]
+		} else {
+			set w_rev [::choose_rev::new $w.rev $revprompt]
+		}
+
+		pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+	}
+
+	frame $w.buttons
+	if {$is_ask_revs} {
+		button $w.buttons.visualize \
+			-text [mc Visualize] \
+			-command [cb _visualize]
+		pack $w.buttons.visualize -side left
+	}
+	button $w.buttons.ok \
+		-text [mc OK] \
+		-command [cb _start]
+	pack $w.buttons.ok -side right
+	button $w.buttons.cancel \
+		-text [mc "Cancel"] \
+		-command [cb _cancel]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	bind $w <$M1B-Key-Return> [cb _start]
+	bind $w <Key-Return> [cb _start]
+	bind $w <Key-Escape> [cb _cancel]
+	wm protocol $w WM_DELETE_WINDOW [cb _cancel]
+
+	bind $w <Visibility> [cb _visible]
+	return $this
+}
+
+method execute {} {
+	tkwait window $w
+	set rv $is_ok
+	delete_this
+	return $rv
+}
+
+method _visible {} {
+	grab $w
+	if {$is_ask_args} {
+		focus $w_args
+	} elseif {$is_ask_revs} {
+		$w_rev focus_filter
+	}
+}
+
+method _cancel {} {
+	wm protocol $w WM_DELETE_WINDOW {}
+	destroy $w
+}
+
+method _rev {} {
+	if {[catch {$w_rev commit_or_die}]} {
+		return {}
+	}
+	return [$w_rev get]
+}
+
+method _visualize {} {
+	global current_branch
+	set rev [_rev $this]
+	if {$rev ne {}} {
+		do_gitk [list --left-right "$current_branch...$rev"]
+	}
+}
+
+method _start {} {
+	global env
+
+	if {$is_ask_revs} {
+		set name [_rev $this]
+		if {$name eq {}} {
+			return
+		}
+		set env(REVISION) $name
+	}
+
+	if {$is_ask_args} {
+		set env(ARGS) $argstr
+	}
+
+	set is_ok 1
+	_cancel $this
+}
+
+}
diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl
index 8e6a9d0..b18d9c7 100644
--- a/git-gui/lib/transport.tcl
+++ b/git-gui/lib/transport.tcl
@@ -33,10 +33,15 @@
 proc start_push_anywhere_action {w} {
 	global push_urltype push_remote push_url push_thin push_tags
 	global push_force
+	global repo_config
 
+	set is_mirror 0
 	set r_url {}
 	switch -- $push_urltype {
-	remote {set r_url $push_remote}
+	remote {
+		set r_url $push_remote
+		catch {set is_mirror $repo_config(remote.$push_remote.mirror)}
+	}
 	url {set r_url $push_url}
 	}
 	if {$r_url eq {}} return
@@ -53,23 +58,29 @@
 		lappend cmd --tags
 	}
 	lappend cmd $r_url
-	set cnt 0
-	foreach i [$w.source.l curselection] {
-		set b [$w.source.l get $i]
-		lappend cmd "refs/heads/$b:refs/heads/$b"
-		incr cnt
-	}
-	if {$cnt == 0} {
-		return
-	} elseif {$cnt == 1} {
-		set unit branch
+	if {$is_mirror} {
+		set cons [console::new \
+			[mc "push %s" $r_url] \
+			[mc "Mirroring to %s" $r_url]]
 	} else {
-		set unit branches
-	}
+		set cnt 0
+		foreach i [$w.source.l curselection] {
+			set b [$w.source.l get $i]
+			lappend cmd "refs/heads/$b:refs/heads/$b"
+			incr cnt
+		}
+		if {$cnt == 0} {
+			return
+		} elseif {$cnt == 1} {
+			set unit branch
+		} else {
+			set unit branches
+		}
 
-	set cons [console::new \
-		[mc "push %s" $r_url] \
-		[mc "Pushing %s %s to %s" $cnt $unit $r_url]]
+		set cons [console::new \
+			[mc "push %s" $r_url] \
+			[mc "Pushing %s %s to %s" $cnt $unit $r_url]]
+	}
 	console::exec $cons $cmd
 	destroy $w
 }
@@ -135,7 +146,7 @@
 		set push_urltype url
 	}
 	radiobutton $w.dest.url_r \
-		-text [mc "Arbitrary URL:"] \
+		-text [mc "Arbitrary Location:"] \
 		-value url \
 		-variable push_urltype
 	entry $w.dest.url_t \
diff --git a/git-gui/po/de.po b/git-gui/po/de.po
index fa43947..a6f730b 100644
--- a/git-gui/po/de.po
+++ b/git-gui/po/de.po
@@ -7,8 +7,8 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 08:58+0200\n"
-"PO-Revision-Date: 2008-08-02 09:09+0200\n"
+"POT-Creation-Date: 2008-12-06 20:51+0100\n"
+"PO-Revision-Date: 2008-12-06 21:22+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -86,7 +86,15 @@
 msgid "Scanning for modified files ..."
 msgstr "Nach geänderten Dateien suchen..."
 
-#: git-gui.sh:1324 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Aufrufen der Eintragen-Vorbereiten-Kontrolle..."
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Eintragen abgelehnt durch Eintragen-Vorbereiten-Kontrolle (»prepare-commit hook«)."
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Bereit."
 
@@ -110,7 +118,15 @@
 msgid "Staged for commit, missing"
 msgstr "Bereitgestellt zum Eintragen, fehlend"
 
-#: git-gui.sh:1597
+#: git-gui.sh:1658
+msgid "File type changed, not staged"
+msgstr "Dateityp geändert, nicht bereitgestellt"
+
+#: git-gui.sh:1659
+msgid "File type changed, staged"
+msgstr "Dateityp geändert, bereitgestellt"
+
+#: git-gui.sh:1661
 msgid "Untracked, not staged"
 msgstr "Nicht unter Versionskontrolle, nicht bereitgestellt"
 
@@ -162,7 +178,15 @@
 msgid "Remote"
 msgstr "Andere Archive"
 
-#: git-gui.sh:1879
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Werkzeuge"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Arbeitskopie im Dateimanager"
+
+#: git-gui.sh:2247
 msgid "Browse Current Branch's Files"
 msgstr "Aktuellen Zweig durchblättern"
 
@@ -259,7 +283,15 @@
 msgid "Reset..."
 msgstr "Zurücksetzen..."
 
-#: git-gui.sh:2002 git-gui.sh:2389
+#: git-gui.sh:2372
+msgid "Done"
+msgstr "Fertig"
+
+#: git-gui.sh:2374
+msgid "Commit@@verb"
+msgstr "Eintragen"
+
+#: git-gui.sh:2383 git-gui.sh:2786
 msgid "New Commit"
 msgstr "Neue Version"
 
@@ -299,11 +331,7 @@
 msgid "Sign Off"
 msgstr "Abzeichnen"
 
-#: git-gui.sh:2053 git-gui.sh:2372
-msgid "Commit@@verb"
-msgstr "Eintragen"
-
-#: git-gui.sh:2064
+#: git-gui.sh:2458
 msgid "Local Merge..."
 msgstr "Lokales Zusammenführen..."
 
@@ -311,11 +339,19 @@
 msgid "Abort Merge..."
 msgstr "Zusammenführen abbrechen..."
 
-#: git-gui.sh:2081
+#: git-gui.sh:2475
+msgid "Add..."
+msgstr "Hinzufügen..."
+
+#: git-gui.sh:2479
 msgid "Push..."
 msgstr "Versenden..."
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
+#: git-gui.sh:2483
+msgid "Delete Branch..."
+msgstr "Zweig löschen..."
+
+#: git-gui.sh:2493 git-gui.sh:2515 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
@@ -329,7 +365,11 @@
 msgid "Options..."
 msgstr "Optionen..."
 
-#: git-gui.sh:2113 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Entfernen..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Hilfe"
 
@@ -337,7 +377,11 @@
 msgid "Online Documentation"
 msgstr "Online-Dokumentation"
 
-#: git-gui.sh:2238
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "SSH-Schlüssel anzeigen"
+
+#: git-gui.sh:2707
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
@@ -396,15 +440,7 @@
 msgid "File:"
 msgstr "Datei:"
 
-#: git-gui.sh:2589
-msgid "Apply/Reverse Hunk"
-msgstr "Kontext anwenden/umkehren"
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr "Zeile anwenden/umkehren"
-
-#: git-gui.sh:2711
+#: git-gui.sh:2834
 msgid "Refresh"
 msgstr "Aktualisieren"
 
@@ -416,7 +452,35 @@
 msgid "Increase Font Size"
 msgstr "Schriftgröße vergrößern"
 
-#: git-gui.sh:2646
+#: git-gui.sh:3033 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Zeichenkodierung"
+
+#: git-gui.sh:3044
+msgid "Apply/Reverse Hunk"
+msgstr "Kontext anwenden/umkehren"
+
+#: git-gui.sh:2875
+msgid "Apply/Reverse Line"
+msgstr "Zeile anwenden/umkehren"
+
+#: git-gui.sh:2885
+msgid "Run Merge Tool"
+msgstr "Zusammenführungswerkzeug"
+
+#: git-gui.sh:2890
+msgid "Use Remote Version"
+msgstr "Entfernte Version benutzen"
+
+#: git-gui.sh:2894
+msgid "Use Local Version"
+msgstr "Lokale Version benutzen"
+
+#: git-gui.sh:2898
+msgid "Revert To Base"
+msgstr "Ursprüngliche Version benutzen"
+
+#: git-gui.sh:3091
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
@@ -494,11 +558,23 @@
 msgid "Copy Commit"
 msgstr "Version kopieren"
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Text suchen..."
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr "Volle Kopie-Erkennung"
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:263
+msgid "Show History Context"
+msgstr "Historien-Kontext anzeigen"
+
+#: lib/blame.tcl:266
+msgid "Blame Parent Commit"
+msgstr "Elternversion annotieren"
+
+#: lib/blame.tcl:394
 #, tcl-format
 msgid "Reading %s..."
 msgstr "%s lesen..."
@@ -547,7 +623,23 @@
 msgid "Original File:"
 msgstr "Ursprüngliche Datei:"
 
-#: lib/blame.tcl:925
+#: lib/blame.tcl:1021
+msgid "Cannot find HEAD commit:"
+msgstr "Zweigspitze (»HEAD«) kann nicht gefunden werden:"
+
+#: lib/blame.tcl:1076
+msgid "Cannot find parent commit:"
+msgstr "Elternversion kann nicht gefunden werden:"
+
+#: lib/blame.tcl:1001
+msgid "Unable to display parent"
+msgstr "Elternversion kann nicht angezeigt werden"
+
+#: lib/blame.tcl:1002 lib/diff.tcl:191
+msgid "Error loading diff:"
+msgstr "Fehler beim Laden des Vergleichs:"
+
+#: lib/blame.tcl:1142
 msgid "Originally By:"
 msgstr "Ursprünglich von:"
 
@@ -970,7 +1062,7 @@
 msgid "Failed to create repository %s:"
 msgstr "Projektarchiv »%s« konnte nicht erstellt werden:"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "Verzeichnis:"
 
@@ -979,12 +1071,12 @@
 msgid "Git Repository"
 msgstr "Git Projektarchiv"
 
-#: lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Verzeichnis »%s« existiert bereits."
 
-#: lib/choose_repository.tcl:441
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Datei »%s« existiert bereits."
@@ -993,11 +1085,15 @@
 msgid "Clone"
 msgstr "Klonen"
 
-#: lib/choose_repository.tcl:468
-msgid "URL:"
-msgstr "URL:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Herkunft:"
 
-#: lib/choose_repository.tcl:489
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Zielverzeichnis:"
+
+#: lib/choose_repository.tcl:490
 msgid "Clone Type:"
 msgstr "Art des Klonens:"
 
@@ -1477,7 +1573,31 @@
 msgid "Loading diff of %s..."
 msgstr "Vergleich von »%s« laden..."
 
-#: lib/diff.tcl:114 lib/diff.tcl:184
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"LOKAL: gelöscht\n"
+"ANDERES:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"ANDERES: gelöscht\n"
+"LOKAL:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "LOKAL:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "ANDERES:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Datei »%s« kann nicht angezeigt werden"
@@ -1494,11 +1614,27 @@
 msgid "* Binary file (not showing content)."
 msgstr "* Binärdatei (Inhalt wird nicht angezeigt)"
 
-#: lib/diff.tcl:185
-msgid "Error loading diff:"
-msgstr "Fehler beim Laden des Vergleichs:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Datei nicht unter Versionskontrolle, Dateigröße %d Bytes.\n"
+"* Nur erste %d Bytes werden angezeigt.\n"
 
-#: lib/diff.tcl:303
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Datei nicht unter Versionskontrolle, hier abgeschnitten durch %s.\n"
+"* Zum Ansehen der vollständigen Datei externen Editor benutzen.\n"
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr ""
 "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
@@ -1515,6 +1651,19 @@
 msgid "Failed to stage selected line."
 msgstr "Fehler beim Bereitstellen der gewählten Zeile."
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Voreinstellung"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Systemweit (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Andere"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "Fehler"
@@ -1586,6 +1735,15 @@
 msgid "Do Nothing"
 msgstr "Nichts tun"
 
+#: lib/index.tcl:419
+msgid "Reverting selected files"
+msgstr "Änderungen in gewählten Dateien verwerfen"
+
+#: lib/index.tcl:423
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Änderungen in %s verwerfen"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1730,7 +1888,107 @@
 msgid "Abort completed.  Ready."
 msgstr "Abbruch durchgeführt. Bereit."
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:14
+msgid "Force resolution to the base version?"
+msgstr "Konflikt durch Basisversion ersetzen?"
+
+#: lib/mergetool.tcl:15
+msgid "Force resolution to this branch?"
+msgstr "Konflikt durch diesen Zweig ersetzen?"
+
+#: lib/mergetool.tcl:16
+msgid "Force resolution to the other branch?"
+msgstr "Konflikt durch anderen Zweig ersetzen?"
+
+#: lib/mergetool.tcl:20
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Hinweis: Der Vergleich zeigt nur konfliktverursachende Änderungen an.\n"
+"\n"
+"»%s« wird überschrieben.\n"
+"\n"
+"Diese Operation kann nur rückgängig gemacht werden, wenn die\n"
+"Zusammenführung erneut gestartet wird."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Datei »%s« hat nicht aufgelöste Konflikte. Trotzdem bereitstellen?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Auflösung hinzugefügt für %s"
+
+#: lib/mergetool.tcl:119
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Konflikte durch gelöschte Dateien oder symbolische Links können nicht durch "
+"das Zusamenführungswerkzeug gelöst werden."
+
+#: lib/mergetool.tcl:124
+msgid "Conflict file does not exist"
+msgstr "Konflikt-Datei existiert nicht"
+
+#: lib/mergetool.tcl:236
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Kein GUI Zusammenführungswerkzeug: »%s«"
+
+#: lib/mergetool.tcl:240
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Unbekanntes Zusammenführungswerkzeug: »%s«"
+
+#: lib/mergetool.tcl:275
+msgid "Merge tool is already running, terminate it?"
+msgstr "Zusammenführungswerkzeug läuft bereits. Soll es abgebrochen werden?"
+
+#: lib/mergetool.tcl:295
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Fehler beim Abrufen der Dateiversionen:\n"
+"%s"
+
+#: lib/mergetool.tcl:315
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Zusammenführungswerkzeug konnte nicht gestartet werden:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:319
+msgid "Running merge tool..."
+msgstr "Zusammenführungswerkzeug starten..."
+
+#: lib/mergetool.tcl:347 lib/mergetool.tcl:363
+msgid "Merge tool failed."
+msgstr "Zusammenführungswerkzeug fehlgeschlagen."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Ungültige globale Zeichenkodierung »%s«"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Ungültige Archiv-Zeichenkodierung »%s«"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Voreinstellungen wiederherstellen"
 
@@ -1767,7 +2025,11 @@
 msgid "Show Diffstat After Merge"
 msgstr "Vergleichsstatistik nach Zusammenführen anzeigen"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:122
+msgid "Use Merge Tool"
+msgstr "Zusammenführungswerkzeug"
+
+#: lib/option.tcl:124
 msgid "Trust File Modification Timestamps"
 msgstr "Auf Dateiänderungsdatum verlassen"
 
@@ -1788,6 +2050,10 @@
 msgstr "Mindestzahl Zeichen für Kopie-Annotieren"
 
 #: lib/option.tcl:128
+msgid "Blame History Context Radius (days)"
+msgstr "Anzahl Tage für Historien-Kontext"
+
+#: lib/option.tcl:129
 msgid "Number of Diff Context Lines"
 msgstr "Anzahl der Kontextzeilen beim Vergleich"
 
@@ -1799,7 +2065,15 @@
 msgid "New Branch Name Template"
 msgstr "Namensvorschlag für neue Zweige"
 
-#: lib/option.tcl:192
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Voreingestellte Zeichenkodierung"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Ändern"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Wörterbuch Rechtschreibprüfung:"
 
@@ -1824,9 +2098,86 @@
 msgid "Failed to completely save options:"
 msgstr "Optionen konnten nicht gespeichert werden:"
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Anderes Archiv hinzufügen"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Neues anderes Archiv hinzufügen"
+
+#: lib/remote_add.tcl:28
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Einzelheiten des anderen Archivs"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Adresse:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Weitere Aktion jetzt"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Gleich anfordern"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Anderes Archiv initialisieren und dahin versenden"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Nichts tun"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Bitte geben Sie einen Namen des anderen Archivs an."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "»%s« ist kein zulässiger Name eines anderen Archivs."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Fehler beim Hinzufügen des anderen Archivs »%s« aus Herkunftsort »%s«."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "»%s« anfordern"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "»%s« anfordern"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr ""
+"Initialisieren eines anderen Archivs an Adresse »%s« ist nicht möglich."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "»%s« versenden..."
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Einrichten von »%s« an »%s«"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Zweig in anderem Projektarchiv löschen"
+msgid "Delete Branch Remotely"
+msgstr "Zweig in anderem Archiv löschen"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
@@ -1837,8 +2188,8 @@
 msgstr "Anderes Archiv:"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "Archiv-URL:"
+msgid "Arbitrary Location:"
+msgstr "Adresse:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1910,7 +2261,11 @@
 msgid "Scanning %s..."
 msgstr "»%s« laden..."
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Anderes Archiv entfernen"
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr "Aufräumen von"
 
@@ -1922,6 +2277,22 @@
 msgid "Push to"
 msgstr "Versenden nach"
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Suchen:"
+
+#: lib/search.tcl:22
+msgid "Next"
+msgstr "Nächster"
+
+#: lib/search.tcl:23
+msgid "Prev"
+msgstr "Voriger"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Groß-/Kleinschreibung unterscheiden"
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "Fehler beim Schreiben der Verknüpfung:"
@@ -1967,15 +2338,181 @@
 msgid "Spell Checker Failed"
 msgstr "Rechtschreibprüfung fehlgeschlagen"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Keine Schlüssel gefunden."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Öffentlicher Schlüssel gefunden in: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Schlüssel erzeugen"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "In Zwischenablage kopieren"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Ihr OpenSSH öffenlicher Schlüssel"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Erzeugen..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Konnte »ssh-keygen« nicht starten:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "Schlüsselerzeugung fehlgeschlagen."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "Schlüsselerzeugung erfolgreich, aber keine Schlüssel gefunden."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Ihr Schlüssel ist abgelegt in: %s"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s ... %*i von %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Werkzeug hinzufügen"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Neues Kommando für Werkzeug hinzufügen"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Global hinzufügen"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Einzelheiten des Werkzeugs"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Benutzen Sie einen Schrägstrich »/«, um Untermenüs zu erstellen:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Kommando:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Bestätigungsfrage vor Starten anzeigen"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Benutzer nach Version fragen (setzt $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Benutzer nach zusätzlichen Argumenten fragen (setzt $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Kein Ausgabefenster zeigen"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Nur starten, wenn ein Vergleich gewählt ist ($FILENAME ist nicht leer)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Bitte geben Sie einen Werkzeugnamen an."
+
+#: lib/tools_dlg.tcl:129
 #, tcl-format
-msgid "fetch %s"
-msgstr "»%s« anfordern"
+msgid "Tool '%s' already exists."
+msgstr "Werkzeug »%s« existiert bereits."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Werkzeug konnte nicht hinzugefügt werden:\n"
+"\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Werkzeug entfernen"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Werkzeugkommandos entfernen"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Entfernen"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Werkzeuge für lokales Archiv werden in Blau angezeigt)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Kommando aufrufen: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Argumente"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "Ok"
+
+#: lib/tools.tcl:75
+#, tcl-format
+msgid "Running %s requires a selected file."
+msgstr "Um »%s« zu starten, muss eine Datei ausgewählt sein."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Wollen Sie %s wirklich starten?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Werkzeug: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Starten: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "Werkzeug erfolgreich abgeschlossen: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Werkzeug fehlgeschlagen: %s"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1992,17 +2529,17 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Übernahmezweige aufräumen und entfernen, die in »%s« gelöscht wurden"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "»%s« versenden..."
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr "Änderungen nach »%s« versenden"
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Spiegeln nach %s"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "%s %s nach %s versenden"
diff --git a/git-gui/po/fr.po b/git-gui/po/fr.po
index 26b866f..45773ab 100644
--- a/git-gui/po/fr.po
+++ b/git-gui/po/fr.po
@@ -1,4 +1,4 @@
-# translation of fr.po to Français
+# translation of fr.po to French
 # Translation of git-gui to French.
 # Copyright (C) 2008 Shawn Pearce, et al.
 # This file is distributed under the same license as the git package.
@@ -9,43 +9,43 @@
 msgstr ""
 "Project-Id-Version: fr\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 14:45-0700\n"
-"PO-Revision-Date: 2008-08-11 17:12-0400\n"
-"Last-Translator: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>\n"
-"Language-Team: Français <fr@li.org>\n"
+"POT-Creation-Date: 2008-11-16 13:56-0800\n"
+"PO-Revision-Date: 2008-11-20 10:20+0100\n"
+"Last-Translator: Christian Couder <chriscool@tuxfamily.org>\n"
+"Language-Team: French\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: KBabel 1.11.4\n"
 "Plural-Forms:  nplurals=2; plural=(n > 1);\n"
 
-#: git-gui.sh:41 git-gui.sh:688 git-gui.sh:702 git-gui.sh:715 git-gui.sh:798
-#: git-gui.sh:817
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr "git-gui: erreur fatale"
 
-#: git-gui.sh:644
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Police invalide spécifiée dans %s :"
 
-#: git-gui.sh:674
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr "Police principale"
 
-#: git-gui.sh:675
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr "Police diff/console"
 
-#: git-gui.sh:689
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr "Impossible de trouver git dans PATH."
 
-#: git-gui.sh:716
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr "Impossible de parser la version de Git :"
 
-#: git-gui.sh:734
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -64,381 +64,446 @@
 "\n"
 "Peut'on considérer que '%s' est en version 1.5.0 ?\n"
 
-#: git-gui.sh:972
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr "Impossible de trouver le répertoire git :"
 
-#: git-gui.sh:979
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr "Impossible d'aller à la racine du répertoire de travail :"
 
-#: git-gui.sh:986
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr "Impossible d'utiliser le répertoire .git:"
 
-#: git-gui.sh:991
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr "Aucun répertoire de travail"
 
-#: git-gui.sh:1138 lib/checkout_op.tcl:305
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr "Rafraichissement du status des fichiers..."
 
-#: git-gui.sh:1194
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr "Recherche de fichiers modifiés..."
 
-#: git-gui.sh:1369 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Lancement de l'action de préparation du message de commit..."
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Commit refusé par l'action de préparation du message de commit."
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Prêt."
 
-#: git-gui.sh:1635
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr "Non modifié"
 
-#: git-gui.sh:1637
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr "Modifié, pas indexé"
 
-#: git-gui.sh:1638 git-gui.sh:1643
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr "Indexé"
 
-#: git-gui.sh:1639 git-gui.sh:1644
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr "Portions indexées"
 
-#: git-gui.sh:1640 git-gui.sh:1645
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr "Indexés, manquant"
 
-#: git-gui.sh:1642
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "Le type de fichier a changé, non indexé"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "Le type de fichier a changé, indexé"
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr "Non versionné, non indexé"
 
-#: git-gui.sh:1647
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr "Manquant"
 
-#: git-gui.sh:1648
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr "Indexé pour suppression"
 
-#: git-gui.sh:1649
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr "Indexé pour suppression, toujours présent"
 
-#: git-gui.sh:1651 git-gui.sh:1652 git-gui.sh:1653 git-gui.sh:1654
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr "Nécessite la résolution d'une fusion"
 
-#: git-gui.sh:1689
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr "Lancement de gitk... un instant..."
 
-#: git-gui.sh:1698
+#: git-gui.sh:1887
 msgid "Couldn't find gitk in PATH"
 msgstr "Impossible de trouver gitk dans PATH."
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Dépôt"
 
-#: git-gui.sh:1949
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr "Edition"
 
-#: git-gui.sh:1951 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Branche"
 
-#: git-gui.sh:1954 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Commit"
 
-#: git-gui.sh:1957 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Fusionner"
 
-#: git-gui.sh:1958 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Dépôt distant"
 
-#: git-gui.sh:1967
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Outils"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Explorer la copie de travail"
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr "Naviguer dans la branche courante"
 
-#: git-gui.sh:1971
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr "Naviguer dans la branche..."
 
-#: git-gui.sh:1976
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr "Visualiser historique branche courante"
 
-#: git-gui.sh:1980
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr "Voir l'historique de toutes les branches"
 
-#: git-gui.sh:1987
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Naviguer l'arborescence de %s"
 
-#: git-gui.sh:1989
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Voir l'historique de la branche: %s"
 
-#: git-gui.sh:1994 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Statistiques du dépôt"
 
-#: git-gui.sh:1997 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Comprimer le dépôt"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr "Vérifier le dépôt"
 
-#: git-gui.sh:2007 git-gui.sh:2011 git-gui.sh:2015 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Créer icône sur bureau"
 
-#: git-gui.sh:2023 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Quitter"
 
-#: git-gui.sh:2031
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr "Défaire"
 
-#: git-gui.sh:2034
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr "Refaire"
 
-#: git-gui.sh:2038 git-gui.sh:2545
+#: git-gui.sh:2378 git-gui.sh:2923
 msgid "Cut"
 msgstr "Couper"
 
-#: git-gui.sh:2041 git-gui.sh:2548 git-gui.sh:2622 git-gui.sh:2715
+#: git-gui.sh:2381 git-gui.sh:2926 git-gui.sh:3000 git-gui.sh:3082
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Copier"
 
-#: git-gui.sh:2044 git-gui.sh:2551
+#: git-gui.sh:2384 git-gui.sh:2929
 msgid "Paste"
 msgstr "Coller"
 
-#: git-gui.sh:2047 git-gui.sh:2554 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2932 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Supprimer"
 
-#: git-gui.sh:2051 git-gui.sh:2558 git-gui.sh:2719 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2936 git-gui.sh:3086 lib/console.tcl:71
 msgid "Select All"
 msgstr "Tout sélectionner"
 
-#: git-gui.sh:2060
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr "Créer..."
 
-#: git-gui.sh:2066
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr "Charger (checkout)..."
 
-#: git-gui.sh:2072
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr "Renommer..."
 
-#: git-gui.sh:2077 git-gui.sh:2187
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr "Supprimer..."
 
-#: git-gui.sh:2082
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr "Réinitialiser..."
 
-#: git-gui.sh:2094 git-gui.sh:2491
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "Effectué"
+
+#: git-gui.sh:2434
+msgid "Commit@@verb"
+msgstr "Commiter@@verb"
+
+#: git-gui.sh:2443 git-gui.sh:2864
 msgid "New Commit"
 msgstr "Nouveau commit"
 
-#: git-gui.sh:2102 git-gui.sh:2498
+#: git-gui.sh:2451 git-gui.sh:2871
 msgid "Amend Last Commit"
 msgstr "Corriger dernier commit"
 
-#: git-gui.sh:2111 git-gui.sh:2458 lib/remote_branch_delete.tcl:99
+#: git-gui.sh:2461 git-gui.sh:2825 lib/remote_branch_delete.tcl:99
 msgid "Rescan"
 msgstr "Recharger modifs."
 
-#: git-gui.sh:2117
+#: git-gui.sh:2467
 msgid "Stage To Commit"
 msgstr "Indexer"
 
-#: git-gui.sh:2123
+#: git-gui.sh:2473
 msgid "Stage Changed Files To Commit"
 msgstr "Indexer toutes modifications"
 
-#: git-gui.sh:2129
+#: git-gui.sh:2479
 msgid "Unstage From Commit"
 msgstr "Désindexer"
 
-#: git-gui.sh:2134 lib/index.tcl:395
+#: git-gui.sh:2484 lib/index.tcl:410
 msgid "Revert Changes"
 msgstr "Annuler les modifications (revert)"
 
-#: git-gui.sh:2141 git-gui.sh:2702
+#: git-gui.sh:2491 git-gui.sh:3069
 msgid "Show Less Context"
 msgstr "Montrer moins de contexte"
 
-#: git-gui.sh:2145 git-gui.sh:2706
+#: git-gui.sh:2495 git-gui.sh:3073
 msgid "Show More Context"
 msgstr "Montrer plus de contexte"
 
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
+#: git-gui.sh:2502 git-gui.sh:2838 git-gui.sh:2947
 msgid "Sign Off"
 msgstr "Signer"
 
-#: git-gui.sh:2155 git-gui.sh:2474
-msgid "Commit@@verb"
-msgstr "Commiter"
-
-#: git-gui.sh:2166
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr "Fusion locale..."
 
-#: git-gui.sh:2171
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr "Abandonner fusion..."
 
-#: git-gui.sh:2183
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "Ajouter..."
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr "Pousser..."
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "Supprimer branche..."
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "À propos de %s"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr "Préférences..."
 
-#: git-gui.sh:2209 git-gui.sh:2740
+#: git-gui.sh:2565 git-gui.sh:3115
 msgid "Options..."
 msgstr "Options..."
 
-#: git-gui.sh:2215 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Supprimer..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Aide"
 
-#: git-gui.sh:2256
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr "Documentation en ligne"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "Montrer clé SSH"
+
+#: git-gui.sh:2707
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "erreur fatale : pas d'infos sur le chemin %s : Fichier ou répertoire "
 "inexistant"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2740
 msgid "Current Branch:"
 msgstr "Branche courante :"
 
-#: git-gui.sh:2394
+#: git-gui.sh:2761
 msgid "Staged Changes (Will Commit)"
 msgstr "Modifs. indexées (pour commit)"
 
-#: git-gui.sh:2414
+#: git-gui.sh:2781
 msgid "Unstaged Changes"
 msgstr "Modifs. non indexées"
 
-#: git-gui.sh:2464
+#: git-gui.sh:2831
 msgid "Stage Changed"
 msgstr "Indexer modifs."
 
-#: git-gui.sh:2480 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2850 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "Pousser"
 
-#: git-gui.sh:2510
+#: git-gui.sh:2885
 msgid "Initial Commit Message:"
 msgstr "Message de commit initial :"
 
-#: git-gui.sh:2511
+#: git-gui.sh:2886
 msgid "Amended Commit Message:"
 msgstr "Message de commit corrigé :"
 
-#: git-gui.sh:2512
+#: git-gui.sh:2887
 msgid "Amended Initial Commit Message:"
 msgstr "Message de commit initial corrigé :"
 
-#: git-gui.sh:2513
+#: git-gui.sh:2888
 msgid "Amended Merge Commit Message:"
 msgstr "Message de commit de fusion corrigé :"
 
-#: git-gui.sh:2514
+#: git-gui.sh:2889
 msgid "Merge Commit Message:"
 msgstr "Message de commit de fusion :"
 
-#: git-gui.sh:2515
+#: git-gui.sh:2890
 msgid "Commit Message:"
 msgstr "Message de commit :"
 
-#: git-gui.sh:2561 git-gui.sh:2723 lib/console.tcl:73
+#: git-gui.sh:2939 git-gui.sh:3090 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Copier tout"
 
-#: git-gui.sh:2585 lib/blame.tcl:100
+#: git-gui.sh:2963 lib/blame.tcl:104
 msgid "File:"
 msgstr "Fichier :"
 
-#: git-gui.sh:2691
-msgid "Apply/Reverse Hunk"
-msgstr "Appliquer/Inverser section"
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr "Appliquer/Inverser la ligne"
-
-#: git-gui.sh:2711
+#: git-gui.sh:3078
 msgid "Refresh"
 msgstr "Rafraichir"
 
-#: git-gui.sh:2732
+#: git-gui.sh:3099
 msgid "Decrease Font Size"
 msgstr "Diminuer la police"
 
-#: git-gui.sh:2736
+#: git-gui.sh:3103
 msgid "Increase Font Size"
 msgstr "Agrandir la police"
 
-#: git-gui.sh:2747
+#: git-gui.sh:3111 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Encodage"
+
+#: git-gui.sh:3122
+msgid "Apply/Reverse Hunk"
+msgstr "Appliquer/Inverser section"
+
+#: git-gui.sh:3127
+msgid "Apply/Reverse Line"
+msgstr "Appliquer/Inverser la ligne"
+
+#: git-gui.sh:3137
+msgid "Run Merge Tool"
+msgstr "Lancer outil de merge"
+
+#: git-gui.sh:3142
+msgid "Use Remote Version"
+msgstr "Utiliser la version distante"
+
+#: git-gui.sh:3146
+msgid "Use Local Version"
+msgstr "Utiliser la version locale"
+
+#: git-gui.sh:3150
+msgid "Revert To Base"
+msgstr "Revenir à la version de base"
+
+#: git-gui.sh:3169
 msgid "Unstage Hunk From Commit"
 msgstr "Désindexer la section"
 
-#: git-gui.sh:2748
+#: git-gui.sh:3170
 msgid "Unstage Line From Commit"
 msgstr "Désindexer la ligne"
 
-#: git-gui.sh:2750
+#: git-gui.sh:3172
 msgid "Stage Hunk For Commit"
 msgstr "Indexer la section"
 
-#: git-gui.sh:2751
+#: git-gui.sh:3173
 msgid "Stage Line For Commit"
 msgstr "Indexer la ligne"
 
-#: git-gui.sh:2771
+#: git-gui.sh:3196
 msgid "Initializing..."
 msgstr "Initialisation..."
 
-#: git-gui.sh:2876
+#: git-gui.sh:3301
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -455,7 +520,7 @@
 "sous-processus de Git lancés par %s\n"
 "\n"
 
-#: git-gui.sh:2906
+#: git-gui.sh:3331
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -465,7 +530,7 @@
 "Ceci est du à un problème connu avec\n"
 "le binaire Tcl distribué par Cygwin."
 
-#: git-gui.sh:2911
+#: git-gui.sh:3336
 #, tcl-format
 msgid ""
 "\n"
@@ -486,80 +551,108 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - une interface graphique utilisateur pour Git"
 
-#: lib/blame.tcl:70
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "Visionneur de fichier"
 
-#: lib/blame.tcl:74
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "Commit :"
 
-#: lib/blame.tcl:257
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "Copier commit"
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Chercher texte..."
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr "Lancer la détection approfondie des copies"
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Montrer l'historique"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "Blâmer le commit parent"
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Lecture de %s..."
 
-#: lib/blame.tcl:492
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "Chargement des annotations de suivi des copies/déplacements..."
 
-#: lib/blame.tcl:512
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "lignes annotées"
 
-#: lib/blame.tcl:704
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "Chargement des annotations d'emplacement original"
 
-#: lib/blame.tcl:707
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "Annotation terminée."
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr "Occupé"
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr "Annotation en cours d'exécution."
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr "Recherche de copie approfondie en cours..."
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "Chargement des annotations..."
 
-#: lib/blame.tcl:883
+#: lib/blame.tcl:964
 msgid "Author:"
 msgstr "Auteur :"
 
-#: lib/blame.tcl:887
+#: lib/blame.tcl:968
 msgid "Committer:"
 msgstr "Commiteur :"
 
-#: lib/blame.tcl:892
+#: lib/blame.tcl:973
 msgid "Original File:"
 msgstr "Fichier original :"
 
-#: lib/blame.tcl:1006
+#: lib/blame.tcl:1021
+msgid "Cannot find HEAD commit:"
+msgstr "Impossible de trouver le commit HEAD:"
+
+#: lib/blame.tcl:1076
+msgid "Cannot find parent commit:"
+msgstr "Impossible de trouver le commit parent:"
+
+#: lib/blame.tcl:1091
+msgid "Unable to display parent"
+msgstr "Impossible d'afficher le parent"
+
+#: lib/blame.tcl:1092 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "Erreur lors du chargement des différences :"
+
+#: lib/blame.tcl:1232
 msgid "Originally By:"
 msgstr "A l'origine par :"
 
-#: lib/blame.tcl:1012
+#: lib/blame.tcl:1238
 msgid "In File:"
 msgstr "Dans le fichier :"
 
-#: lib/blame.tcl:1017
+#: lib/blame.tcl:1243
 msgid "Copied Or Moved Here By:"
 msgstr "Copié ou déplacé ici par :"
 
@@ -573,16 +666,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:97
 msgid "Cancel"
 msgstr "Annuler"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "Révision"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:244
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "Options"
 
@@ -602,7 +697,7 @@
 msgid "Create New Branch"
 msgstr "Créer nouvelle branche"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr "Créer"
 
@@ -610,7 +705,7 @@
 msgid "Branch Name"
 msgstr "Nom de branche"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "Nom :"
 
@@ -755,9 +850,9 @@
 msgid "Browse Branch Files"
 msgstr "Naviguer dans les fichiers de le branche"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:472 lib/choose_repository.tcl:482
-#: lib/choose_repository.tcl:985
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr "Naviguer"
 
@@ -772,6 +867,7 @@
 msgstr "erreur fatale : Impossible de résoudre %s"
 
 #: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Fermer"
 
@@ -884,7 +980,7 @@
 msgid "Reset '%s'?"
 msgstr "Réinitialiser '%s' ?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Visualiser"
 
@@ -934,225 +1030,229 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr "Créer nouveau dépôt"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "Nouveau..."
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:458
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr "Cloner dépôt existant"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "Cloner..."
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:974
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr "Ouvrir dépôt existant"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "Ouvrir..."
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "Dépôt récemment utilisés"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "Ouvrir dépôt récent :"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "La création du dépôt %s a échouée :"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "Répertoire :"
 
-#: lib/choose_repository.tcl:410 lib/choose_repository.tcl:535
-#: lib/choose_repository.tcl:1007
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr "Dépôt Git"
 
-#: lib/choose_repository.tcl:435
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Le répertoire %s existe déjà."
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Le fichier %s existe déjà."
 
-#: lib/choose_repository.tcl:453
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr "Cloner"
 
-#: lib/choose_repository.tcl:466
-msgid "URL:"
-msgstr "URL :"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Emplacement source:"
 
-#: lib/choose_repository.tcl:487
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Répertoire cible:"
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr "Type de clonage :"
 
-#: lib/choose_repository.tcl:493
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (rapide, semi-redondant, liens durs)"
 
-#: lib/choose_repository.tcl:499
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Copy complète (plus lent, sauvegarde redondante)"
 
-#: lib/choose_repository.tcl:505
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Partagé (le plus rapide, non recommandé, pas de sauvegarde)"
 
-#: lib/choose_repository.tcl:541 lib/choose_repository.tcl:588
-#: lib/choose_repository.tcl:734 lib/choose_repository.tcl:804
-#: lib/choose_repository.tcl:1013 lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "'%s' n'est pas un dépôt Git."
 
-#: lib/choose_repository.tcl:577
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr "Standard n'est disponible que pour un dépôt local."
 
-#: lib/choose_repository.tcl:581
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr "Partagé n'est disponible que pour un dépôt local."
 
-#: lib/choose_repository.tcl:602
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "L'emplacement %s existe déjà."
 
-#: lib/choose_repository.tcl:613
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr "La configuration de l'origine a échouée."
 
-#: lib/choose_repository.tcl:625
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr "Décompte des objets"
 
-#: lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr "paniers"
 
-#: lib/choose_repository.tcl:650
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Impossible de copier 'objects/info/alternates' : %s"
 
-#: lib/choose_repository.tcl:686
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Il n'y a rien à cloner depuis %s."
 
-#: lib/choose_repository.tcl:688 lib/choose_repository.tcl:902
-#: lib/choose_repository.tcl:914
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr "La branche 'master' n'a pas été initialisée."
 
-#: lib/choose_repository.tcl:701
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Les liens durs ne sont pas supportés. Une copie sera effectuée à la place."
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Clonage depuis %s"
 
-#: lib/choose_repository.tcl:744
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr "Copie des objets"
 
-#: lib/choose_repository.tcl:745
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:769
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Impossible de copier l'objet : %s"
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr "Liaison des objets"
 
-#: lib/choose_repository.tcl:780
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr "objets"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Impossible créer un lien dur pour l'objet : %s"
 
-#: lib/choose_repository.tcl:843
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Impossible de récupérer les branches et objets. Voir la sortie console pour "
 "plus de détails."
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 "Impossible de récupérer les marques (tags). Voir la sortie console pour plus "
 "de détails."
 
-#: lib/choose_repository.tcl:878
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Impossible de déterminer HEAD. Voir la sortie console pour plus de détails."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Impossible de nettoyer %s"
 
-#: lib/choose_repository.tcl:893
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr "Le clonage a échoué."
 
-#: lib/choose_repository.tcl:900
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr "Aucune branche par défaut n'a été obtenue."
 
-#: lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Impossible de résoudre %s comme commit."
 
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr "Création du répertoire de travail"
 
-#: lib/choose_repository.tcl:924 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr "fichiers"
 
-#: lib/choose_repository.tcl:953
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr "Chargement initial du fichier échoué."
 
-#: lib/choose_repository.tcl:969
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr "Ouvrir"
 
-#: lib/choose_repository.tcl:979
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr "Dépôt :"
 
-#: lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Impossible d'ouvrir le dépôt %s :"
@@ -1254,7 +1354,7 @@
 "\n"
 "Cela va être fait tout de suite automatiquement.\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:156
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1267,7 +1367,7 @@
 "Le fichier %s a des conflicts de fusion. Vous devez les résoudre et pré-"
 "commiter le fichier avant de pouvoir commiter.\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:164
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1278,7 +1378,7 @@
 "\n"
 "Le fichier %s ne peut pas être commité par ce programme.\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:172
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1288,7 +1388,7 @@
 "\n"
 "Vous devez indexer au moins 1 fichier avant de pouvoir commiter.\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:187
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1306,45 +1406,45 @@
 "- Deuxième ligne : rien.\n"
 "- Lignes suivantes : Décrire pourquoi ces modifications sont bonnes.\n"
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:211
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr "attention : Tcl ne supporte pas l'encodage '%s'."
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:227
 msgid "Calling pre-commit hook..."
 msgstr "Lancement de l'action d'avant-commit..."
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:242
 msgid "Commit declined by pre-commit hook."
 msgstr "Commit refusé par l'action d'avant-commit."
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:265
 msgid "Calling commit-msg hook..."
 msgstr "Lancement de l'action \"message de commit\"..."
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:280
 msgid "Commit declined by commit-msg hook."
 msgstr "Commit refusé par l'action \"message de commit\"."
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:293
 msgid "Committing changes..."
 msgstr "Commit des modifications..."
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:309
 msgid "write-tree failed:"
 msgstr "write-tree a échoué :"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:310 lib/commit.tcl:354 lib/commit.tcl:374
 msgid "Commit failed."
 msgstr "Le commit a échoué."
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:327
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Le commit %s semble être corrompu"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:332
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1359,19 +1459,19 @@
 "\n"
 "Une resynchronisation va être lancée tout de suite automatiquement.\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:339
 msgid "No changes to commit."
 msgstr "Pas de modifications à commiter."
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:353
 msgid "commit-tree failed:"
 msgstr "commit-tree a échoué :"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:373
 msgid "update-ref failed:"
 msgstr "update-ref a échoué"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:461
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Commit créé %s : %s"
@@ -1448,7 +1548,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Date invalide de Git : %s"
 
-#: lib/diff.tcl:44
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1471,48 +1571,101 @@
 "Une resynchronisation va être lancée automatiquement pour trouver d'autres "
 "fichiers qui pourraient se trouver dans le même état."
 
-#: lib/diff.tcl:83
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Chargement des différences de %s..."
 
-#: lib/diff.tcl:116 lib/diff.tcl:190
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"LOCAL: supprimé\n"
+"DISTANT:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"DISTANT: supprimé\n"
+"LOCAL:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "LOCAL:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "DISTANT:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Impossible d'afficher %s"
 
-#: lib/diff.tcl:117
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr "Erreur lors du chargement du fichier :"
 
-#: lib/diff.tcl:124
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr "Dépôt Git (sous projet)"
 
-#: lib/diff.tcl:136
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr "* Fichier binaire (pas d'apperçu du contenu)."
 
-#: lib/diff.tcl:191
-msgid "Error loading diff:"
-msgstr "Erreur lors du chargement des différences :"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Le fichier non suivi fait %d octets.\n"
+"* On montre seulement les premiers %d octets.\n"
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Fichier suivi raccourcis ici de %s.\n"
+"* Pour voir le fichier entier, utiliser un éditeur externe.\n"
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr "Échec lors de la désindexation de la section sélectionnée."
 
-#: lib/diff.tcl:320
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr "Échec lors de l'indexation de la section."
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:509
 msgid "Failed to unstage selected line."
 msgstr "Échec lors de la désindexation de la ligne sélectionnée."
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:517
 msgid "Failed to stage selected line."
 msgstr "Échec lors de l'indexation de la ligne."
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Défaut"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Système (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Autre"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "erreur"
@@ -1549,40 +1702,49 @@
 msgid "Unlock Index"
 msgstr "Déverouiller l'index"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Désindexation de: %s"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr "Prêt à être commité."
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr "Ajout de %s"
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Annuler les modifications dans le fichier %s ? "
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Annuler les modifications dans ces %i fichiers ?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Toutes les modifications non-indexées seront définitivement perdues par "
 "l'annulation."
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr "Ne rien faire"
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "Annuler modifications dans fichiers selectionnés"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Annulation des modifications dans %s"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1612,7 +1774,7 @@
 "\n"
 "Cela va être fait tout de suite automatiquement\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1630,7 +1792,7 @@
 "terminer la fusion courante. Seulement à ce moment là sera-t-il possible "
 "d'effectuer une nouvelle fusion.\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1648,34 +1810,34 @@
 "faisait comme cela, vous éviterez de devoir éventuellement abandonner une "
 "fusion ayant échouée.\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s de %s"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr "Fusion de %s et %s..."
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr "La fusion s'est faite avec succès."
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "La fusion a echouée. Il est nécessaire de résoudre les conflicts."
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Fusion dans %s"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr "Révision à fusionner"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1685,7 +1847,7 @@
 "\n"
 "Vous devez finir de corriger ce commit.\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1700,7 +1862,7 @@
 "\n"
 "Abandonner quand même la fusion courante ?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1715,131 +1877,323 @@
 "\n"
 "Réinitialiser quand même les modifications courantes ?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "Abandon"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "fichiers réinitialisés"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "L'abandon a échoué."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "Abandon teminé. Prêt."
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Forcer la résolution à la version de base ?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Forcer la résolution à cette branche ?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Forcer la résolution à l'autre branche ?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Noter que le diff ne montre que les modifications en conflict.\n"
+"\n"
+"%s sera écrasé.\n"
+"\n"
+"Cette opération ne peut être défaite qu'en relançant la fusion."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Le fichier %s semble avoir des conflicts non résolus, indéxer quand même ?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Ajouter une résolution pour %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr "Impossible de résoudre la suppression ou de relier des conflicts en utilisant un outil"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Le fichier en conflict n'existe pas."
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "'%s' n'est pas un outil graphique pour fusionner des fichiers."
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Outil de fusion '%s' non supporté"
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "L'outil de fusion tourne déjà, faut-il le terminer ?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Erreur lors de la récupération des versions:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossible de lancer l'outil de fusion:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "Lancement de l'outil de fusion..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "L'outil de fusion a échoué."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Encodage global invalide '%s'"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Encodage de dépôt invalide '%s'"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Remettre les valeurs par défaut"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "Sauvegarder"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr "Dépôt: %s"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "Globales (tous les dépôts)"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "Nom d'utilisateur"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "Adresse email"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr "Résumer les commits de fusion"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr "Fusion bavarde"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr "Montrer statistiques de diff après fusion"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Utiliser outil de fusion"
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "Faire confiance aux dates de modification de fichiers "
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Purger les branches de suivi pendant la récupération"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "Faire correspondre les branches de suivi"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr "Annoter les copies seulement sur fichiers modifiés"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr "Minimum de caratères pour annoter une copie"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "Distance de blâme dans l'historique (jours)"
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "Nombre de lignes de contexte dans les diffs"
 
-#: lib/option.tcl:129
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr "Largeur du texte de message de commit"
 
-#: lib/option.tcl:130
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "Nouveau modèle de nom de branche"
 
-#: lib/option.tcl:194
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Encodage du contenu des fichiers par défaut"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Modifier"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Dictionnaire d'orthographe :"
 
-#: lib/option.tcl:218
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr "Modifier les polices"
 
-#: lib/option.tcl:222
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "Choisir %s"
 
-#: lib/option.tcl:228
+#: lib/option.tcl:264
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:242
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "Préférences"
 
-#: lib/option.tcl:277
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "La sauvegarde complète des options a échouée :"
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Supprimer dépôt distant"
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr "Purger de"
 
-#: lib/remote.tcl:170
+#: lib/remote.tcl:173
 msgid "Fetch from"
 msgstr "Récupérer de"
 
-#: lib/remote.tcl:213
+#: lib/remote.tcl:215
 msgid "Push to"
 msgstr "Pousser vers"
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Ajouter dépôt distant"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Ajouter nouveau dépôt distant"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "Ajouter"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Détails des dépôts distants"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Emplacement:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Action supplémentaire"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Récupérer immédiatement"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Initialiser dépôt distant et pousser"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Ne rien faire d'autre maintenant"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Merci de fournir un nom de dépôt distant."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "'%s' n'est pas un nom de dépôt distant acceptable."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Échec de l'ajout du dépôt distant '%s' à l'emplacement '%s'."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "récupérer %s"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Récupération de %s"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Pas de méthode connue pour initialiser le dépôt à l'emplacement '%s'."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr "pousser %s"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Mise en place de %s (à %s)"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Supprimer branche distante"
+msgid "Delete Branch Remotely"
+msgstr "Supprimer branche à distance"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
@@ -1850,8 +2204,8 @@
 msgstr "Branche distante :"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "URL arbitraire :"
+msgid "Arbitrary Location:"
+msgstr "Emplacement arbitraire :"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1921,6 +2275,22 @@
 msgid "Scanning %s..."
 msgstr "Synchronisation de %s..."
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Chercher :"
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Suivant"
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Précédant"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Sensible à la casse"
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "Impossible d'écrire le raccourcis :"
@@ -1958,23 +2328,188 @@
 msgid "No Suggestions"
 msgstr "Aucune suggestion"
 
-#: lib/spellcheck.tcl:387
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "EOF inattendue envoyée par le vérificateur d'orthographe"
 
-#: lib/spellcheck.tcl:391
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "Le vérificateur d'orthographe a échoué"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Aucune clé trouvée."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Clé publique trouvée dans : %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Générer une clé"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Copier dans le presse papier"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Votre clé publique Open SSH"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Génération..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossible de lancer ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "La génération a échoué."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "La génération a réussi, mais aucune clé n'a été trouvée."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Votre clé est dans : %s"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s ... %*i de %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
-msgstr "récupérer %s"
+msgid "Running %s requires a selected file."
+msgstr "Lancer %s nécessite qu'un fichier soit sélectionné."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Êtes vous sûr de vouloir lancer %s ?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Outil : %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Lancement de : %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "L'outil a terminé avec succès : %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "L'outil a échoué : %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Ajouter outil"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Ajouter nouvelle commande d'outil"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Ajouter globalement"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Détails sur l'outil"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Utiliser les séparateurs '/' pour créer un arbre de sous menus :"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Commande :"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Montrer une boîte de dialogue avant le lancement"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Demander à l'utilisateur de sélectionner une révision (change $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Demander à l'utilisateur des arguments supplémentaires (change $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Ne pas montrer la fenêtre de sortie des commandes"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Lancer seulement si un diff est selectionné ($FILENAME non vide)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Merci de fournir un nom pour l'outil."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "L'outil '%s' existe déjà."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Impossible d'ajouter l'outil:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Supprimer l'outil"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Supprimer des commandes d'outil"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Supprimer"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Le bleu indique des outils locaux au dépôt)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Lancer commande : %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Arguments"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1991,11 +2526,6 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Nettoyer les branches de suivi supprimées de %s"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "pousser %s"
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
diff --git a/git-gui/po/git-gui.pot b/git-gui/po/git-gui.pot
index e295000..15aea0d 100644
--- a/git-gui/po/git-gui.pot
+++ b/git-gui/po/git-gui.pot
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 14:45-0700\n"
+"POT-Creation-Date: 2008-12-08 08:31-0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,33 +16,33 @@
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:688 git-gui.sh:702 git-gui.sh:715 git-gui.sh:798
-#: git-gui.sh:817
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr ""
 
-#: git-gui.sh:644
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr ""
 
-#: git-gui.sh:674
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr ""
 
-#: git-gui.sh:675
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr ""
 
-#: git-gui.sh:689
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr ""
 
-#: git-gui.sh:716
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr ""
 
-#: git-gui.sh:734
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -54,379 +54,444 @@
 "Assume '%s' is version 1.5.0?\n"
 msgstr ""
 
-#: git-gui.sh:972
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr ""
 
-#: git-gui.sh:979
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr ""
 
-#: git-gui.sh:986
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr ""
 
-#: git-gui.sh:991
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr ""
 
-#: git-gui.sh:1138 lib/checkout_op.tcl:305
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr ""
 
-#: git-gui.sh:1194
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr ""
 
-#: git-gui.sh:1369 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr ""
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr ""
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr ""
 
-#: git-gui.sh:1635
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr ""
 
-#: git-gui.sh:1637
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr ""
 
-#: git-gui.sh:1638 git-gui.sh:1643
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr ""
 
-#: git-gui.sh:1639 git-gui.sh:1644
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr ""
 
-#: git-gui.sh:1640 git-gui.sh:1645
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr ""
 
-#: git-gui.sh:1642
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr ""
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr ""
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr ""
 
-#: git-gui.sh:1647
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr ""
 
-#: git-gui.sh:1648
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr ""
 
-#: git-gui.sh:1649
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr ""
 
-#: git-gui.sh:1651 git-gui.sh:1652 git-gui.sh:1653 git-gui.sh:1654
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr ""
 
-#: git-gui.sh:1689
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr ""
 
-#: git-gui.sh:1698
+#: git-gui.sh:1887
 msgid "Couldn't find gitk in PATH"
 msgstr ""
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr ""
 
-#: git-gui.sh:1949
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr ""
 
-#: git-gui.sh:1951 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr ""
 
-#: git-gui.sh:1954 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr ""
 
-#: git-gui.sh:1957 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr ""
 
-#: git-gui.sh:1958 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr ""
 
-#: git-gui.sh:1967
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr ""
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr ""
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr ""
 
-#: git-gui.sh:1971
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr ""
 
-#: git-gui.sh:1976
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr ""
 
-#: git-gui.sh:1980
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr ""
 
-#: git-gui.sh:1987
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr ""
 
-#: git-gui.sh:1989
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr ""
 
-#: git-gui.sh:1994 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr ""
 
-#: git-gui.sh:1997 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr ""
 
-#: git-gui.sh:2000
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr ""
 
-#: git-gui.sh:2007 git-gui.sh:2011 git-gui.sh:2015 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr ""
 
-#: git-gui.sh:2023 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr ""
 
-#: git-gui.sh:2031
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr ""
 
-#: git-gui.sh:2034
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr ""
 
-#: git-gui.sh:2038 git-gui.sh:2545
+#: git-gui.sh:2378 git-gui.sh:2937
 msgid "Cut"
 msgstr ""
 
-#: git-gui.sh:2041 git-gui.sh:2548 git-gui.sh:2622 git-gui.sh:2715
+#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr ""
 
-#: git-gui.sh:2044 git-gui.sh:2551
+#: git-gui.sh:2384 git-gui.sh:2943
 msgid "Paste"
 msgstr ""
 
-#: git-gui.sh:2047 git-gui.sh:2554 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr ""
 
-#: git-gui.sh:2051 git-gui.sh:2558 git-gui.sh:2719 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
 msgid "Select All"
 msgstr ""
 
-#: git-gui.sh:2060
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr ""
 
-#: git-gui.sh:2066
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr ""
 
-#: git-gui.sh:2072
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr ""
 
-#: git-gui.sh:2077 git-gui.sh:2187
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr ""
 
-#: git-gui.sh:2082
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr ""
 
-#: git-gui.sh:2094 git-gui.sh:2491
-msgid "New Commit"
+#: git-gui.sh:2432
+msgid "Done"
 msgstr ""
 
-#: git-gui.sh:2102 git-gui.sh:2498
-msgid "Amend Last Commit"
-msgstr ""
-
-#: git-gui.sh:2111 git-gui.sh:2458 lib/remote_branch_delete.tcl:99
-msgid "Rescan"
-msgstr ""
-
-#: git-gui.sh:2117
-msgid "Stage To Commit"
-msgstr ""
-
-#: git-gui.sh:2123
-msgid "Stage Changed Files To Commit"
-msgstr ""
-
-#: git-gui.sh:2129
-msgid "Unstage From Commit"
-msgstr ""
-
-#: git-gui.sh:2134 lib/index.tcl:395
-msgid "Revert Changes"
-msgstr ""
-
-#: git-gui.sh:2141 git-gui.sh:2702
-msgid "Show Less Context"
-msgstr ""
-
-#: git-gui.sh:2145 git-gui.sh:2706
-msgid "Show More Context"
-msgstr ""
-
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
-msgid "Sign Off"
-msgstr ""
-
-#: git-gui.sh:2155 git-gui.sh:2474
+#: git-gui.sh:2434
 msgid "Commit@@verb"
 msgstr ""
 
-#: git-gui.sh:2166
+#: git-gui.sh:2443 git-gui.sh:2878
+msgid "New Commit"
+msgstr ""
+
+#: git-gui.sh:2451 git-gui.sh:2885
+msgid "Amend Last Commit"
+msgstr ""
+
+#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr ""
+
+#: git-gui.sh:2467
+msgid "Stage To Commit"
+msgstr ""
+
+#: git-gui.sh:2473
+msgid "Stage Changed Files To Commit"
+msgstr ""
+
+#: git-gui.sh:2479
+msgid "Unstage From Commit"
+msgstr ""
+
+#: git-gui.sh:2484 lib/index.tcl:410
+msgid "Revert Changes"
+msgstr ""
+
+#: git-gui.sh:2491 git-gui.sh:3083
+msgid "Show Less Context"
+msgstr ""
+
+#: git-gui.sh:2495 git-gui.sh:3087
+msgid "Show More Context"
+msgstr ""
+
+#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+msgid "Sign Off"
+msgstr ""
+
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr ""
 
-#: git-gui.sh:2171
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr ""
 
-#: git-gui.sh:2183
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr ""
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr ""
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr ""
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr ""
 
-#: git-gui.sh:2201
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr ""
 
-#: git-gui.sh:2209 git-gui.sh:2740
+#: git-gui.sh:2565 git-gui.sh:3129
 msgid "Options..."
 msgstr ""
 
-#: git-gui.sh:2215 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr ""
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr ""
 
-#: git-gui.sh:2256
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr ""
 
-#: git-gui.sh:2340
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr ""
+
+#: git-gui.sh:2721
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 
-#: git-gui.sh:2373
+#: git-gui.sh:2754
 msgid "Current Branch:"
 msgstr ""
 
-#: git-gui.sh:2394
+#: git-gui.sh:2775
 msgid "Staged Changes (Will Commit)"
 msgstr ""
 
-#: git-gui.sh:2414
+#: git-gui.sh:2795
 msgid "Unstaged Changes"
 msgstr ""
 
-#: git-gui.sh:2464
+#: git-gui.sh:2845
 msgid "Stage Changed"
 msgstr ""
 
-#: git-gui.sh:2480 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr ""
 
-#: git-gui.sh:2510
+#: git-gui.sh:2899
 msgid "Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2511
+#: git-gui.sh:2900
 msgid "Amended Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2512
+#: git-gui.sh:2901
 msgid "Amended Initial Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2513
+#: git-gui.sh:2902
 msgid "Amended Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2514
+#: git-gui.sh:2903
 msgid "Merge Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2515
+#: git-gui.sh:2904
 msgid "Commit Message:"
 msgstr ""
 
-#: git-gui.sh:2561 git-gui.sh:2723 lib/console.tcl:73
+#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
 msgid "Copy All"
 msgstr ""
 
-#: git-gui.sh:2585 lib/blame.tcl:100
+#: git-gui.sh:2977 lib/blame.tcl:104
 msgid "File:"
 msgstr ""
 
-#: git-gui.sh:2691
-msgid "Apply/Reverse Hunk"
-msgstr ""
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr ""
-
-#: git-gui.sh:2711
+#: git-gui.sh:3092
 msgid "Refresh"
 msgstr ""
 
-#: git-gui.sh:2732
+#: git-gui.sh:3113
 msgid "Decrease Font Size"
 msgstr ""
 
-#: git-gui.sh:2736
+#: git-gui.sh:3117
 msgid "Increase Font Size"
 msgstr ""
 
-#: git-gui.sh:2747
+#: git-gui.sh:3125 lib/blame.tcl:281
+msgid "Encoding"
+msgstr ""
+
+#: git-gui.sh:3136
+msgid "Apply/Reverse Hunk"
+msgstr ""
+
+#: git-gui.sh:3141
+msgid "Apply/Reverse Line"
+msgstr ""
+
+#: git-gui.sh:3151
+msgid "Run Merge Tool"
+msgstr ""
+
+#: git-gui.sh:3156
+msgid "Use Remote Version"
+msgstr ""
+
+#: git-gui.sh:3160
+msgid "Use Local Version"
+msgstr ""
+
+#: git-gui.sh:3164
+msgid "Revert To Base"
+msgstr ""
+
+#: git-gui.sh:3183
 msgid "Unstage Hunk From Commit"
 msgstr ""
 
-#: git-gui.sh:2748
+#: git-gui.sh:3184
 msgid "Unstage Line From Commit"
 msgstr ""
 
-#: git-gui.sh:2750
+#: git-gui.sh:3186
 msgid "Stage Hunk For Commit"
 msgstr ""
 
-#: git-gui.sh:2751
+#: git-gui.sh:3187
 msgid "Stage Line For Commit"
 msgstr ""
 
-#: git-gui.sh:2771
+#: git-gui.sh:3210
 msgid "Initializing..."
 msgstr ""
 
-#: git-gui.sh:2876
+#: git-gui.sh:3315
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -437,14 +502,14 @@
 "\n"
 msgstr ""
 
-#: git-gui.sh:2906
+#: git-gui.sh:3345
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
 "Tcl binary distributed by Cygwin."
 msgstr ""
 
-#: git-gui.sh:2911
+#: git-gui.sh:3350
 #, tcl-format
 msgid ""
 "\n"
@@ -459,80 +524,108 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr ""
 
-#: lib/blame.tcl:70
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr ""
 
-#: lib/blame.tcl:74
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr ""
 
-#: lib/blame.tcl:257
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr ""
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr ""
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr ""
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr ""
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr ""
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr ""
 
-#: lib/blame.tcl:492
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr ""
 
-#: lib/blame.tcl:512
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr ""
 
-#: lib/blame.tcl:704
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr ""
 
-#: lib/blame.tcl:707
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr ""
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr ""
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr ""
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr ""
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr ""
 
-#: lib/blame.tcl:883
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr ""
 
-#: lib/blame.tcl:887
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr ""
 
-#: lib/blame.tcl:892
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr ""
 
-#: lib/blame.tcl:1006
+#: lib/blame.tcl:1020
+msgid "Cannot find HEAD commit:"
+msgstr ""
+
+#: lib/blame.tcl:1075
+msgid "Cannot find parent commit:"
+msgstr ""
+
+#: lib/blame.tcl:1090
+msgid "Unable to display parent"
+msgstr ""
+
+#: lib/blame.tcl:1091 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr ""
+
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr ""
 
-#: lib/blame.tcl:1012
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr ""
 
-#: lib/blame.tcl:1017
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr ""
 
@@ -546,16 +639,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr ""
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr ""
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:244
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr ""
 
@@ -575,7 +670,7 @@
 msgid "Create New Branch"
 msgstr ""
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr ""
 
@@ -583,7 +678,7 @@
 msgid "Branch Name"
 msgstr ""
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr ""
 
@@ -723,9 +818,9 @@
 msgid "Browse Branch Files"
 msgstr ""
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:472 lib/choose_repository.tcl:482
-#: lib/choose_repository.tcl:985
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr ""
 
@@ -740,6 +835,7 @@
 msgstr ""
 
 #: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr ""
 
@@ -836,7 +932,7 @@
 msgid "Reset '%s'?"
 msgstr ""
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr ""
 
@@ -877,221 +973,225 @@
 msgid "Git Gui"
 msgstr ""
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr ""
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:458
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr ""
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:974
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr ""
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr ""
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr ""
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr ""
 
-#: lib/choose_repository.tcl:410 lib/choose_repository.tcl:535
-#: lib/choose_repository.tcl:1007
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr ""
 
-#: lib/choose_repository.tcl:435
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:453
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr ""
 
-#: lib/choose_repository.tcl:466
-msgid "URL:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
 msgstr ""
 
-#: lib/choose_repository.tcl:487
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr ""
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr ""
 
-#: lib/choose_repository.tcl:493
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr ""
 
-#: lib/choose_repository.tcl:499
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:505
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr ""
 
-#: lib/choose_repository.tcl:541 lib/choose_repository.tcl:588
-#: lib/choose_repository.tcl:734 lib/choose_repository.tcl:804
-#: lib/choose_repository.tcl:1013 lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:577
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:581
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr ""
 
-#: lib/choose_repository.tcl:602
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr ""
 
-#: lib/choose_repository.tcl:613
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr ""
 
-#: lib/choose_repository.tcl:625
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:650
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:686
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr ""
 
-#: lib/choose_repository.tcl:688 lib/choose_repository.tcl:902
-#: lib/choose_repository.tcl:914
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr ""
 
-#: lib/choose_repository.tcl:701
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr ""
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:744
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:745
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr ""
 
-#: lib/choose_repository.tcl:769
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:780
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr ""
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:843
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:878
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr ""
 
-#: lib/choose_repository.tcl:893
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:900
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr ""
 
-#: lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr ""
 
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr ""
 
-#: lib/choose_repository.tcl:924 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr ""
 
-#: lib/choose_repository.tcl:953
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr ""
 
-#: lib/choose_repository.tcl:969
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr ""
 
-#: lib/choose_repository.tcl:979
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr ""
 
-#: lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr ""
@@ -1154,19 +1254,19 @@
 "current merge activity.\n"
 msgstr ""
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr ""
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr ""
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr ""
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:132
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1176,7 +1276,7 @@
 "The rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:155
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1185,7 +1285,7 @@
 "before committing.\n"
 msgstr ""
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:163
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1193,14 +1293,14 @@
 "File %s cannot be committed by this program.\n"
 msgstr ""
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:171
 msgid ""
 "No changes to commit.\n"
 "\n"
 "You must stage at least 1 file before you can commit.\n"
 msgstr ""
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:186
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1211,45 +1311,45 @@
 "- Remaining lines: Describe why this change is good.\n"
 msgstr ""
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:210
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr ""
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:226
 msgid "Calling pre-commit hook..."
 msgstr ""
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:241
 msgid "Commit declined by pre-commit hook."
 msgstr ""
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:264
 msgid "Calling commit-msg hook..."
 msgstr ""
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:279
 msgid "Commit declined by commit-msg hook."
 msgstr ""
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:292
 msgid "Committing changes..."
 msgstr ""
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:308
 msgid "write-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
 msgid "Commit failed."
 msgstr ""
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:326
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr ""
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:331
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1258,19 +1358,19 @@
 "A rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:338
 msgid "No changes to commit."
 msgstr ""
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:352
 msgid "commit-tree failed:"
 msgstr ""
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:372
 msgid "update-ref failed:"
 msgstr ""
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:460
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr ""
@@ -1339,7 +1439,7 @@
 msgid "Invalid date from Git: %s"
 msgstr ""
 
-#: lib/diff.tcl:44
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1353,48 +1453,92 @@
 "the same state."
 msgstr ""
 
-#: lib/diff.tcl:83
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr ""
 
-#: lib/diff.tcl:116 lib/diff.tcl:190
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr ""
 
-#: lib/diff.tcl:117
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr ""
 
-#: lib/diff.tcl:124
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr ""
 
-#: lib/diff.tcl:136
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr ""
 
-#: lib/diff.tcl:191
-msgid "Error loading diff:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
 msgstr ""
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:320
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr ""
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:509
 msgid "Failed to unstage selected line."
 msgstr ""
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:517
 msgid "Failed to stage selected line."
 msgstr ""
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr ""
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr ""
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr ""
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr ""
@@ -1429,38 +1573,47 @@
 msgid "Unlock Index"
 msgstr ""
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr ""
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr ""
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr ""
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr ""
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr ""
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr ""
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr ""
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr ""
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1478,7 +1631,7 @@
 "The rescan will be automatically started now.\n"
 msgstr ""
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1489,7 +1642,7 @@
 "merge.  Only then can you begin another merge.\n"
 msgstr ""
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1500,41 +1653,41 @@
 "will help you abort a failed merge, should the need arise.\n"
 msgstr ""
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr ""
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr ""
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr ""
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr ""
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr ""
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr ""
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
 "You must finish amending this commit.\n"
 msgstr ""
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1543,7 +1696,7 @@
 "Continue with aborting the current merge?"
 msgstr ""
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1552,142 +1705,325 @@
 "Continue with resetting the current changes?"
 msgstr ""
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr ""
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr ""
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr ""
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr ""
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr ""
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr ""
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr ""
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr ""
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr ""
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr ""
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr ""
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr ""
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr ""
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr ""
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr ""
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr ""
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr ""
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr ""
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr ""
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr ""
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr ""
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr ""
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr ""
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr ""
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr ""
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr ""
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr ""
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr ""
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr ""
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr ""
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr ""
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr ""
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr ""
 
-#: lib/option.tcl:129
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr ""
 
-#: lib/option.tcl:130
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr ""
 
-#: lib/option.tcl:194
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr ""
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr ""
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr ""
 
-#: lib/option.tcl:218
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr ""
 
-#: lib/option.tcl:222
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr ""
 
-#: lib/option.tcl:228
+#: lib/option.tcl:264
 msgid "pt."
 msgstr ""
 
-#: lib/option.tcl:242
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr ""
 
-#: lib/option.tcl:277
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr ""
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr ""
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr ""
 
-#: lib/remote.tcl:170
+#: lib/remote.tcl:173
 msgid "Fetch from"
 msgstr ""
 
-#: lib/remote.tcl:213
+#: lib/remote.tcl:215
 msgid "Push to"
 msgstr ""
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr ""
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr ""
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr ""
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr ""
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr ""
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr ""
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr ""
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr ""
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr ""
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr ""
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr ""
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr ""
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr ""
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr ""
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr ""
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr ""
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr ""
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
+msgid "Delete Branch Remotely"
 msgstr ""
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
 msgstr ""
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr ""
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+msgid "Arbitrary Location:"
 msgstr ""
 
 #: lib/remote_branch_delete.tcl:84
@@ -1750,6 +2086,22 @@
 msgid "Scanning %s..."
 msgstr ""
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr ""
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr ""
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr ""
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr ""
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr ""
@@ -1787,22 +2139,182 @@
 msgid "No Suggestions"
 msgstr ""
 
-#: lib/spellcheck.tcl:387
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr ""
 
-#: lib/spellcheck.tcl:391
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr ""
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr ""
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr ""
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr ""
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr ""
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr ""
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr ""
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr ""
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr ""
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr ""
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr ""
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
+msgid "Running %s requires a selected file."
+msgstr ""
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr ""
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr ""
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr ""
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr ""
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr ""
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr ""
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr ""
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr ""
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr ""
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr ""
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr ""
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr ""
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr ""
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr ""
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr ""
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr ""
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr ""
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr ""
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr ""
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr ""
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr ""
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
 msgstr ""
 
 #: lib/transport.tcl:7
@@ -1820,45 +2332,45 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr ""
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr ""
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr ""
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr ""
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr ""
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr ""
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr ""
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr ""
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr ""
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr ""
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr ""
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr ""
diff --git a/git-gui/po/hu.po b/git-gui/po/hu.po
index 28760ed..f761b64 100644
--- a/git-gui/po/hu.po
+++ b/git-gui/po/hu.po
@@ -7,8 +7,8 @@
 msgstr ""
 "Project-Id-Version: git-gui-i 18n\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-14 07:18+0100\n"
-"PO-Revision-Date: 2008-03-14 17:24+0100\n"
+"POT-Creation-Date: 2008-12-08 08:31-0800\n"
+"PO-Revision-Date: 2008-12-10 15:00+0100\n"
 "Last-Translator: Miklos Vajna <vmiklos@frugalware.org>\n"
 "Language-Team: Hungarian\n"
 "MIME-Version: 1.0\n"
@@ -16,33 +16,33 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744
-#: git-gui.sh:763
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr "git-gui: végzetes hiba"
 
-#: git-gui.sh:593
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Érvénytelen font lett megadva itt: %s:"
 
-#: git-gui.sh:620
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr "Fő betűtípus"
 
-#: git-gui.sh:621
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr "Diff/konzol betűtípus"
 
-#: git-gui.sh:635
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr "A git nem található a PATH-ban."
 
-#: git-gui.sh:662
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr "Nem értelmezhető a Git verzió sztring:"
 
-#: git-gui.sh:680
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,379 +61,445 @@
 "\n"
 "Feltételezhetjük, hogy a(z) '%s' verziója legalább 1.5.0?\n"
 
-#: git-gui.sh:918
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr "A Git könyvtár nem található:"
 
-#: git-gui.sh:925
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr "Nem lehet a munkakönyvtár tetejére lépni:"
 
-#: git-gui.sh:932
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr "Nem használható vicces .git könyvtár:"
 
-#: git-gui.sh:937
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr "Nincs munkakönyvtár"
 
-#: git-gui.sh:1084 lib/checkout_op.tcl:283
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr "A fájlok státuszának frissítése..."
 
-#: git-gui.sh:1149
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr "Módosított fájlok keresése ..."
 
-#: git-gui.sh:1324 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr "A prepare-commit-msg hurok meghívása..."
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "A commitot megakadályozta a prepare-commit-msg hurok."
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Kész."
 
-#: git-gui.sh:1590
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr "Nem módosított"
 
-#: git-gui.sh:1592
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr "Módosított, de nem kiválasztott"
 
-#: git-gui.sh:1593 git-gui.sh:1598
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr "Kiválasztva commitolásra"
 
-#: git-gui.sh:1594 git-gui.sh:1599
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr "Részek kiválasztva commitolásra"
 
-#: git-gui.sh:1595 git-gui.sh:1600
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr "Kiválasztva commitolásra, hiányzó"
 
-#: git-gui.sh:1597
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "Fájl típus megváltozott, nem kiválasztott"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "A fájltípus megváltozott, kiválasztott"
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr "Nem követett, nem kiválasztott"
 
-#: git-gui.sh:1602
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr "Hiányzó"
 
-#: git-gui.sh:1603
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr "Kiválasztva eltávolításra"
 
-#: git-gui.sh:1604
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr "Kiválasztva eltávolításra, jelenleg is elérhető"
 
-#: git-gui.sh:1606 git-gui.sh:1607 git-gui.sh:1608 git-gui.sh:1609
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr "Merge feloldás szükséges"
 
-#: git-gui.sh:1644
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr "A gitk indítása... várjunk..."
 
-#: git-gui.sh:1653
-#, tcl-format
-msgid ""
-"Unable to start gitk:\n"
-"\n"
-"%s does not exist"
-msgstr ""
-"A gitk indítása sikertelen:\n"
-"\n"
-"A(z) %s nem létezik"
+#: git-gui.sh:1887
+msgid "Couldn't find gitk in PATH"
+msgstr "A gitk nem található a PATH-ban."
 
-#: git-gui.sh:1860 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Repó"
 
-#: git-gui.sh:1861
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr "Szerkesztés"
 
-#: git-gui.sh:1863 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Branch"
 
-#: git-gui.sh:1866 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Commit@@főnév"
 
-#: git-gui.sh:1869 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Merge"
 
-#: git-gui.sh:1870 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Távoli"
 
-#: git-gui.sh:1879
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Eszközök"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Munkamásolat felfedezése"
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr "A jelenlegi branch fájljainak böngészése"
 
-#: git-gui.sh:1883
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr "A branch fájljainak böngészése..."
 
-#: git-gui.sh:1888
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr "A jelenlegi branch történetének vizualizálása"
 
-#: git-gui.sh:1892
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr "Az összes branch történetének vizualizálása"
 
-#: git-gui.sh:1899
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "A(z) %s branch fájljainak böngészése"
 
-#: git-gui.sh:1901
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "A(z) %s branch történetének vizualizálása"
 
-#: git-gui.sh:1906 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Adatbázis statisztikák"
 
-#: git-gui.sh:1909 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Adatbázis tömörítése"
 
-#: git-gui.sh:1912
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr "Adatbázis ellenőrzése"
 
-#: git-gui.sh:1919 git-gui.sh:1923 git-gui.sh:1927 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Asztal ikon létrehozása"
 
-#: git-gui.sh:1932 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Kilépés"
 
-#: git-gui.sh:1939
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr "Visszavonás"
 
-#: git-gui.sh:1942
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr "Mégis"
 
-#: git-gui.sh:1946 git-gui.sh:2443
+#: git-gui.sh:2378 git-gui.sh:2937
 msgid "Cut"
 msgstr "Kivágás"
 
-#: git-gui.sh:1949 git-gui.sh:2446 git-gui.sh:2520 git-gui.sh:2614
+#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Másolás"
 
-#: git-gui.sh:1952 git-gui.sh:2449
+#: git-gui.sh:2384 git-gui.sh:2943
 msgid "Paste"
 msgstr "Beillesztés"
 
-#: git-gui.sh:1955 git-gui.sh:2452 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Törlés"
 
-#: git-gui.sh:1959 git-gui.sh:2456 git-gui.sh:2618 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
 msgid "Select All"
 msgstr "Mindent kiválaszt"
 
-#: git-gui.sh:1968
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr "Létrehozás..."
 
-#: git-gui.sh:1974
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr "Checkout..."
 
-#: git-gui.sh:1980
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr "Átnevezés..."
 
-#: git-gui.sh:1985 git-gui.sh:2085
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr "Törlés..."
 
-#: git-gui.sh:1990
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr "Visszaállítás..."
 
-#: git-gui.sh:2002 git-gui.sh:2389
-msgid "New Commit"
-msgstr "Új commit"
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "Kész"
 
-#: git-gui.sh:2010 git-gui.sh:2396
-msgid "Amend Last Commit"
-msgstr "Utolsó commit javítása"
-
-#: git-gui.sh:2019 git-gui.sh:2356 lib/remote_branch_delete.tcl:99
-msgid "Rescan"
-msgstr "Keresés újra"
-
-#: git-gui.sh:2025
-msgid "Stage To Commit"
-msgstr "Kiválasztás commitolásra"
-
-#: git-gui.sh:2031
-msgid "Stage Changed Files To Commit"
-msgstr "Módosított fájlok kiválasztása commitolásra"
-
-#: git-gui.sh:2037
-msgid "Unstage From Commit"
-msgstr "Commitba való kiválasztás visszavonása"
-
-#: git-gui.sh:2042 lib/index.tcl:395
-msgid "Revert Changes"
-msgstr "Változtatások visszaállítása"
-
-#: git-gui.sh:2049 git-gui.sh:2368 git-gui.sh:2467
-msgid "Sign Off"
-msgstr "Aláír"
-
-#: git-gui.sh:2053 git-gui.sh:2372
+#: git-gui.sh:2434
 msgid "Commit@@verb"
 msgstr "Commit@@ige"
 
-#: git-gui.sh:2064
+#: git-gui.sh:2443 git-gui.sh:2878
+msgid "New Commit"
+msgstr "Új commit"
+
+#: git-gui.sh:2451 git-gui.sh:2885
+msgid "Amend Last Commit"
+msgstr "Utolsó commit javítása"
+
+#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "Keresés újra"
+
+#: git-gui.sh:2467
+msgid "Stage To Commit"
+msgstr "Kiválasztás commitolásra"
+
+#: git-gui.sh:2473
+msgid "Stage Changed Files To Commit"
+msgstr "Módosított fájlok kiválasztása commitolásra"
+
+#: git-gui.sh:2479
+msgid "Unstage From Commit"
+msgstr "Commitba való kiválasztás visszavonása"
+
+#: git-gui.sh:2484 lib/index.tcl:410
+msgid "Revert Changes"
+msgstr "Változtatások visszaállítása"
+
+#: git-gui.sh:2491 git-gui.sh:3083
+msgid "Show Less Context"
+msgstr "Kevesebb környezet mutatása"
+
+#: git-gui.sh:2495 git-gui.sh:3087
+msgid "Show More Context"
+msgstr "Több környezet mutatása"
+
+#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+msgid "Sign Off"
+msgstr "Aláír"
+
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr "Helyi merge..."
 
-#: git-gui.sh:2069
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr "Merge megszakítása..."
 
-#: git-gui.sh:2081
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "Hozzáadás..."
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr "Push..."
 
-#: git-gui.sh:2092 lib/choose_repository.tcl:41
-msgid "Apple"
-msgstr "Apple"
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "Branch törlése..."
 
-#: git-gui.sh:2095 git-gui.sh:2117 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Névjegy: %s"
 
-#: git-gui.sh:2099
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr "Beállítások..."
 
-#: git-gui.sh:2107 git-gui.sh:2639
+#: git-gui.sh:2565 git-gui.sh:3129
 msgid "Options..."
 msgstr "Opciók..."
 
-#: git-gui.sh:2113 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Eltávolítás..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Segítség"
 
-#: git-gui.sh:2154
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr "Online dokumentáció"
 
-#: git-gui.sh:2238
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "SSH kulcs mutatása"
+
+#: git-gui.sh:2721
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "végzetes hiba: nem érhető el a(z) %s útvonal: Nincs ilyen fájl vagy könyvtár"
 
-#: git-gui.sh:2271
+#: git-gui.sh:2754
 msgid "Current Branch:"
 msgstr "Jelenlegi branch:"
 
-#: git-gui.sh:2292
+#: git-gui.sh:2775
 msgid "Staged Changes (Will Commit)"
 msgstr "Kiválasztott változtatások (commitolva lesz)"
 
-#: git-gui.sh:2312
+#: git-gui.sh:2795
 msgid "Unstaged Changes"
 msgstr "Kiválasztatlan változtatások"
 
-#: git-gui.sh:2362
+#: git-gui.sh:2845
 msgid "Stage Changed"
 msgstr "Változtatások kiválasztása"
 
-#: git-gui.sh:2378 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Push"
 
-#: git-gui.sh:2408
+#: git-gui.sh:2899
 msgid "Initial Commit Message:"
 msgstr "Kezdeti commit üzenet:"
 
-#: git-gui.sh:2409
+#: git-gui.sh:2900
 msgid "Amended Commit Message:"
 msgstr "Javító commit üzenet:"
 
-#: git-gui.sh:2410
+#: git-gui.sh:2901
 msgid "Amended Initial Commit Message:"
 msgstr "Kezdeti javító commit üzenet:"
 
-#: git-gui.sh:2411
+#: git-gui.sh:2902
 msgid "Amended Merge Commit Message:"
 msgstr "Javító merge commit üzenet:"
 
-#: git-gui.sh:2412
+#: git-gui.sh:2903
 msgid "Merge Commit Message:"
 msgstr "Merge commit üzenet:"
 
-#: git-gui.sh:2413
+#: git-gui.sh:2904
 msgid "Commit Message:"
 msgstr "Commit üzenet:"
 
-#: git-gui.sh:2459 git-gui.sh:2622 lib/console.tcl:73
+#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Összes másolása"
 
-#: git-gui.sh:2483 lib/blame.tcl:107
+#: git-gui.sh:2977 lib/blame.tcl:104
 msgid "File:"
 msgstr "Fájl:"
 
-#: git-gui.sh:2589
-msgid "Apply/Reverse Hunk"
-msgstr "Hunk alkalmazása/visszaállítása"
-
-#: git-gui.sh:2595
-msgid "Show Less Context"
-msgstr "Kevesebb környezet mutatása"
-
-#: git-gui.sh:2602
-msgid "Show More Context"
-msgstr "Több környezet mutatása"
-
-#: git-gui.sh:2610
+#: git-gui.sh:3092
 msgid "Refresh"
 msgstr "Frissítés"
 
-#: git-gui.sh:2631
+#: git-gui.sh:3113
 msgid "Decrease Font Size"
 msgstr "Font méret csökkentése"
 
-#: git-gui.sh:2635
+#: git-gui.sh:3117
 msgid "Increase Font Size"
 msgstr "Fönt méret növelése"
 
-#: git-gui.sh:2646
+#: git-gui.sh:3125 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Kódolás"
+
+#: git-gui.sh:3136
+msgid "Apply/Reverse Hunk"
+msgstr "Hunk alkalmazása/visszaállítása"
+
+#: git-gui.sh:3141
+msgid "Apply/Reverse Line"
+msgstr "Sor alkalmazása/visszaállítása"
+
+#: git-gui.sh:3151
+msgid "Run Merge Tool"
+msgstr "Merge eszköz futtatása"
+
+#: git-gui.sh:3156
+msgid "Use Remote Version"
+msgstr "Távoli verzió használata"
+
+#: git-gui.sh:3160
+msgid "Use Local Version"
+msgstr "Helyi verzió használata"
+
+#: git-gui.sh:3164
+msgid "Revert To Base"
+msgstr "Visszaállítás az alaphoz"
+
+#: git-gui.sh:3183
 msgid "Unstage Hunk From Commit"
 msgstr "Hunk törlése commitból"
 
-#: git-gui.sh:2648
+#: git-gui.sh:3184
+msgid "Unstage Line From Commit"
+msgstr "A sor kiválasztásának törlése"
+
+#: git-gui.sh:3186
 msgid "Stage Hunk For Commit"
 msgstr "Hunk kiválasztása commitba"
 
-#: git-gui.sh:2667
+#: git-gui.sh:3187
+msgid "Stage Line For Commit"
+msgstr "Sor kiválasztása commitba"
+
+#: git-gui.sh:3210
 msgid "Initializing..."
 msgstr "Inicializálás..."
 
-#: git-gui.sh:2762
+#: git-gui.sh:3315
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -450,7 +516,7 @@
 "indított folyamatok által:\n"
 "\n"
 
-#: git-gui.sh:2792
+#: git-gui.sh:3345
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -460,7 +526,7 @@
 "Ez a Cygwin által terjesztett Tcl binárisban\n"
 "lévő ismert hiba miatt van."
 
-#: git-gui.sh:2797
+#: git-gui.sh:3350
 #, tcl-format
 msgid ""
 "\n"
@@ -481,64 +547,108 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - egy grafikus felület a Githez."
 
-#: lib/blame.tcl:77
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "Fájl néző"
 
-#: lib/blame.tcl:81
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "Commit:"
 
-#: lib/blame.tcl:264
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "Commit másolása"
 
-#: lib/blame.tcl:384
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Szöveg keresése..."
+
+#: lib/blame.tcl:284
+msgid "Do Full Copy Detection"
+msgstr "Teljes másolat-érzékelés bekapcsolása"
+
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Történeti környezet mutatása"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "Szülő commit vizsgálata"
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "A(z) %s olvasása..."
 
-#: lib/blame.tcl:488
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "A másolást/átnevezést követő annotációk betöltése..."
 
-#: lib/blame.tcl:508
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "sor annotálva"
 
-#: lib/blame.tcl:689
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "Az eredeti hely annotációk betöltése..."
 
-#: lib/blame.tcl:692
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "Az annotáció kész."
 
-#: lib/blame.tcl:746
+#: lib/blame.tcl:802
+msgid "Busy"
+msgstr "Elfoglalt"
+
+#: lib/blame.tcl:803
+msgid "Annotation process is already running."
+msgstr "Az annotációs folyamat már fut."
+
+#: lib/blame.tcl:842
+msgid "Running thorough copy detection..."
+msgstr "Futtatás másolás-érzékelésen keresztül..."
+
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "Az annotáció betöltése..."
 
-#: lib/blame.tcl:802
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "Szerző:"
 
-#: lib/blame.tcl:806
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "Commiter:"
 
-#: lib/blame.tcl:811
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "Eredeti fájl:"
 
-#: lib/blame.tcl:925
+#: lib/blame.tcl:1020
+msgid "Cannot find HEAD commit:"
+msgstr "Nem található a HEAD commit:"
+
+#: lib/blame.tcl:1075
+msgid "Cannot find parent commit:"
+msgstr "Nem található a szülő commit:"
+
+#: lib/blame.tcl:1090
+msgid "Unable to display parent"
+msgstr "Nem lehet megjeleníteni a szülőt"
+
+#: lib/blame.tcl:1091 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "Hiba a diff betöltése közben:"
+
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "Eredeti szerző:"
 
-#: lib/blame.tcl:931
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "Ebben a fájlban:"
 
-#: lib/blame.tcl:936
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "Ide másolta vagy helyezte:"
 
@@ -552,16 +662,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr "Mégsem"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "Revízió"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:242
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "Opciók"
 
@@ -581,7 +693,7 @@
 msgid "Create New Branch"
 msgstr "Új branch létrehozása"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr "Létrehozás"
 
@@ -589,7 +701,7 @@
 msgid "Branch Name"
 msgstr "Branch neve"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "Név:"
 
@@ -613,7 +725,7 @@
 msgid "Fast Forward Only"
 msgstr "Csak fast forward"
 
-#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
 msgid "Reset"
 msgstr "Visszaállítás"
 
@@ -703,7 +815,7 @@
 msgid "Please select a branch to rename."
 msgstr "Válasszunk ki egy átnevezendő branchet."
 
-#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
 #, tcl-format
 msgid "Branch '%s' already exists."
 msgstr "A(z) '%s' branch már létezik."
@@ -734,32 +846,39 @@
 msgid "Browse Branch Files"
 msgstr "A branch fájljainak böngészése"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:474 lib/choose_repository.tcl:484
-#: lib/choose_repository.tcl:987
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr "Böngészés"
 
-#: lib/checkout_op.tcl:79
+#: lib/checkout_op.tcl:84
 #, tcl-format
 msgid "Fetching %s from %s"
 msgstr "A(z) %s letöltése innen: %s"
 
-#: lib/checkout_op.tcl:127
+#: lib/checkout_op.tcl:132
 #, tcl-format
 msgid "fatal: Cannot resolve %s"
 msgstr "végzetes: Nem lehet feloldani a következőt: %s"
 
-#: lib/checkout_op.tcl:140 lib/console.tcl:81 lib/database.tcl:31
+#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Bezárás"
 
-#: lib/checkout_op.tcl:169
+#: lib/checkout_op.tcl:174
 #, tcl-format
 msgid "Branch '%s' does not exist."
 msgstr "A(z) '%s' branch nem létezik."
 
-#: lib/checkout_op.tcl:206
+#: lib/checkout_op.tcl:193
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr ""
+"Nem sikerült beállítani az egyszerűsített git-pull-t a(z) '%s' számára."
+
+#: lib/checkout_op.tcl:228
 #, tcl-format
 msgid ""
 "Branch '%s' already exists.\n"
@@ -772,21 +891,21 @@
 "Nem lehet fast-forwardolni a következőhöz: %s.\n"
 "Egy merge szükséges."
 
-#: lib/checkout_op.tcl:220
+#: lib/checkout_op.tcl:242
 #, tcl-format
 msgid "Merge strategy '%s' not supported."
 msgstr "A(z) '%s' merge strategy nem támogatott."
 
-#: lib/checkout_op.tcl:239
+#: lib/checkout_op.tcl:261
 #, tcl-format
 msgid "Failed to update '%s'."
 msgstr "Nem sikerült frissíteni a következőt: '%s'."
 
-#: lib/checkout_op.tcl:251
+#: lib/checkout_op.tcl:273
 msgid "Staging area (index) is already locked."
 msgstr "A kiválasztási terület (index) már zárolva van."
 
-#: lib/checkout_op.tcl:266
+#: lib/checkout_op.tcl:288
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -803,30 +922,30 @@
 "\n"
 "Az újrakeresés most automatikusan el fog indulni.\n"
 
-#: lib/checkout_op.tcl:322
+#: lib/checkout_op.tcl:344
 #, tcl-format
 msgid "Updating working directory to '%s'..."
 msgstr "A munkkönyvtár frissiítése a következőre: '%s'..."
 
-#: lib/checkout_op.tcl:323
+#: lib/checkout_op.tcl:345
 msgid "files checked out"
 msgstr "fájl frissítve"
 
-#: lib/checkout_op.tcl:353
+#: lib/checkout_op.tcl:375
 #, tcl-format
 msgid "Aborted checkout of '%s' (file level merging is required)."
 msgstr "A(z) '%s' checkoutja megszakítva (fájlszintű merge-ölés szükséges)."
 
-#: lib/checkout_op.tcl:354
+#: lib/checkout_op.tcl:376
 msgid "File level merge required."
 msgstr "Fájlszintű merge-ölés szükséges."
 
-#: lib/checkout_op.tcl:358
+#: lib/checkout_op.tcl:380
 #, tcl-format
 msgid "Staying on branch '%s'."
 msgstr "Jelenleg a(z) '%s' branchen."
 
-#: lib/checkout_op.tcl:429
+#: lib/checkout_op.tcl:451
 msgid ""
 "You are no longer on a local branch.\n"
 "\n"
@@ -838,31 +957,31 @@
 "Ha egy branchen szeretnénk lenni, hozzunk létre egyet az 'Ez a leválasztott "
 "checkout'-ból."
 
-#: lib/checkout_op.tcl:446 lib/checkout_op.tcl:450
+#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
 #, tcl-format
 msgid "Checked out '%s'."
 msgstr "'%s' kifejtve."
 
-#: lib/checkout_op.tcl:478
+#: lib/checkout_op.tcl:500
 #, tcl-format
 msgid "Resetting '%s' to '%s' will lose the following commits:"
 msgstr ""
 "A(z) '%s' -> '%s' visszaállítás a következő commitok elvesztését jelenti:"
 
-#: lib/checkout_op.tcl:500
+#: lib/checkout_op.tcl:522
 msgid "Recovering lost commits may not be easy."
 msgstr "Az elveszett commitok helyreállítása nem biztos, hogy egyszerű."
 
-#: lib/checkout_op.tcl:505
+#: lib/checkout_op.tcl:527
 #, tcl-format
 msgid "Reset '%s'?"
 msgstr "Visszaállítjuk a következőt: '%s'?"
 
-#: lib/checkout_op.tcl:510 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Vizualizálás"
 
-#: lib/checkout_op.tcl:578
+#: lib/checkout_op.tcl:600
 #, tcl-format
 msgid ""
 "Failed to set current branch.\n"
@@ -907,223 +1026,227 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr "Új repó létrehozása"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "Új..."
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:460
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr "Létező repó másolása"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "Másolás..."
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:976
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr "Létező könyvtár megnyitása"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "Meggyitás..."
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "Legutóbbi repók"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "Legutóbbi repók megnyitása:"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Nem sikerült letrehozni a(z) %s repót:"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:478
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "Könyvtár:"
 
-#: lib/choose_repository.tcl:412 lib/choose_repository.tcl:537
-#: lib/choose_repository.tcl:1011
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr "Git repó"
 
-#: lib/choose_repository.tcl:437
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "A(z) '%s' könyvtár már létezik."
 
-#: lib/choose_repository.tcl:441
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "A(z) '%s' fájl már létezik."
 
-#: lib/choose_repository.tcl:455
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr "Bezárás"
 
-#: lib/choose_repository.tcl:468
-msgid "URL:"
-msgstr "URL:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Forrás helye:"
 
-#: lib/choose_repository.tcl:489
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Cél könyvtár:"
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr "Másolás típusa:"
 
-#: lib/choose_repository.tcl:495
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Általános (Gyors, félig-redundáns, hardlinkek)"
 
-#: lib/choose_repository.tcl:501
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Teljes másolás (Lassabb, redundáns biztonsági mentés)"
 
-#: lib/choose_repository.tcl:507
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Megosztott (Leggyorsabb, nem ajánlott, nincs mentés)"
 
-#: lib/choose_repository.tcl:543 lib/choose_repository.tcl:590
-#: lib/choose_repository.tcl:736 lib/choose_repository.tcl:806
-#: lib/choose_repository.tcl:1017 lib/choose_repository.tcl:1025
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Nem Git repó: %s"
 
-#: lib/choose_repository.tcl:579
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr "A standard csak helyi repókra érhető el."
 
-#: lib/choose_repository.tcl:583
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr "A megosztott csak helyi repókra érhető el."
 
-#: lib/choose_repository.tcl:604
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "A(z) '%s' hely már létezik."
 
-#: lib/choose_repository.tcl:615
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr "Nem sikerült beállítani az origint"
 
-#: lib/choose_repository.tcl:627
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr "Objektumok számolása"
 
-#: lib/choose_repository.tcl:628
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr "vödrök"
 
-#: lib/choose_repository.tcl:652
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Nem sikerült másolni az objects/info/alternates-t: %s"
 
-#: lib/choose_repository.tcl:688
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Semmi másolni való nincs innen: %s"
 
-#: lib/choose_repository.tcl:690 lib/choose_repository.tcl:904
-#: lib/choose_repository.tcl:916
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr "A 'master' branch nincs inicializálva."
 
-#: lib/choose_repository.tcl:703
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Nem érhetőek el hardlinkek.  Másolás használata."
 
-#: lib/choose_repository.tcl:715
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Másolás innen: %s"
 
-#: lib/choose_repository.tcl:746
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr "Objektumok másolása"
 
-#: lib/choose_repository.tcl:747
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:771
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Nem sikerült másolni az objektumot: %s"
 
-#: lib/choose_repository.tcl:781
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr "Objektumok összefűzése"
 
-#: lib/choose_repository.tcl:782
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr "objektum"
 
-#: lib/choose_repository.tcl:790
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Nem sikerült hardlinkelni az objektumot: %s"
 
-#: lib/choose_repository.tcl:845
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Nem sikerült letölteni a branch-eket és az objektumokat.  Bővebben a "
 "konzolos kimenetben."
 
-#: lib/choose_repository.tcl:856
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Nem sikerült letölteni a tageket.  Bővebben a konzolos kimenetben."
 
-#: lib/choose_repository.tcl:880
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Nem sikerült megállapítani a HEAD-et.  Bővebben a konzolos kimenetben."
 
-#: lib/choose_repository.tcl:889
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Nem sikerült tiszítani: %s."
 
-#: lib/choose_repository.tcl:895
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr "A másolás nem sikerült."
 
-#: lib/choose_repository.tcl:902
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr "Nincs alapértelmezett branch."
 
-#: lib/choose_repository.tcl:913
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Nem sikerült felöldani a(z) %s objektumot commitként."
 
-#: lib/choose_repository.tcl:925
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr "Munkakönyvtár létrehozása"
 
-#: lib/choose_repository.tcl:926 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr "fájl"
 
-#: lib/choose_repository.tcl:955
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr "A kezdeti fájl-kibontás sikertelen."
 
-#: lib/choose_repository.tcl:971
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr "Megnyitás"
 
-#: lib/choose_repository.tcl:981
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr "Repó:"
 
-#: lib/choose_repository.tcl:1031
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Nem sikerült megnyitni a(z) %s repót:"
@@ -1194,19 +1317,19 @@
 "A jelenlegi merge még nem teljesen fejeződött be. Csak akkor javíthat egy "
 "előbbi commitot, hogyha megszakítja a jelenlegi merge folyamatot.\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "Hiba a javítandó commit adat betöltése közben:"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "Nem sikerült megállapítani az azonosítót:"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "Érvénytelen GIT_COMMITTER_IDENT:"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:132
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1223,7 +1346,7 @@
 "\n"
 "Az újrakeresés most automatikusan el fog indulni.\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:155
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1236,7 +1359,7 @@
 "A(z) %s fájlban ütközések vannak. Egyszer azokat ki kell javítani, majd "
 "hozzá ki kell választani a fájlt mielőtt commitolni lehetne.\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:163
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1247,7 +1370,7 @@
 "\n"
 "A(z) %s fájlt nem tudja ez a program commitolni.\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:171
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1257,7 +1380,7 @@
 "\n"
 "Legalább egy fájl ki kell választani, hogy commitolni lehessen.\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:186
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1275,45 +1398,45 @@
 "- Második sor: Üres\n"
 "- A többi sor: Leírja, hogy miért jó ez a változtatás.\n"
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:210
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr "figyelmeztetés: a Tcl nem támogatja a(z) '%s' kódolást."
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:226
 msgid "Calling pre-commit hook..."
 msgstr "A pre-commit hurok meghívása..."
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:241
 msgid "Commit declined by pre-commit hook."
 msgstr "A commitot megakadályozta a pre-commit hurok. "
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:264
 msgid "Calling commit-msg hook..."
 msgstr "A commit-msg hurok meghívása..."
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:279
 msgid "Commit declined by commit-msg hook."
 msgstr "A commiot megakadályozta a commit-msg hurok."
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:292
 msgid "Committing changes..."
 msgstr "A változtatások commitolása..."
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:308
 msgid "write-tree failed:"
 msgstr "a write-tree sikertelen:"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
 msgid "Commit failed."
 msgstr "A commit nem sikerült."
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:326
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "A(z) %s commit sérültnek tűnik"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:331
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1327,19 +1450,19 @@
 "\n"
 "Az újrakeresés most automatikusan el fog indulni.\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:338
 msgid "No changes to commit."
 msgstr "Nincs commitolandó változtatás."
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:352
 msgid "commit-tree failed:"
 msgstr "a commit-tree sikertelen:"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:372
 msgid "update-ref failed:"
 msgstr "az update-ref sikertelen:"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:460
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Létrejött a %s commit: %s"
@@ -1414,7 +1537,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Érvénytelen dátum a Git-től: %s"
 
-#: lib/diff.tcl:42
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1436,40 +1559,101 @@
 "\n"
 "Egy újrakeresés fog indulni a hasonló állapotú fájlok megtalálása érdekében."
 
-#: lib/diff.tcl:81
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "A(z) %s diff-jének betöltése..."
 
-#: lib/diff.tcl:114 lib/diff.tcl:184
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"HELYI: törölve\n"
+"TÁVOLI:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"TÁVOLI: törölve\n"
+"HELYI:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "HELYI:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "TÁVOLI:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Nem lehet megjeleníteni a következőt: %s"
 
-#: lib/diff.tcl:115
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr "Hiba a fájl betöltése közben:"
 
-#: lib/diff.tcl:122
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr "Git repó (alprojekt)"
 
-#: lib/diff.tcl:134
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr "* Bináris fájl (tartalom elrejtése)."
 
-#: lib/diff.tcl:185
-msgid "Error loading diff:"
-msgstr "Hiba a diff betöltése közben:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Nem követett fájl %d bájttal.\n"
+"* Csak az első %d bájt mutatása.\n"
 
-#: lib/diff.tcl:303
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Nem követett fájlt levágta a(z) %s.\n"
+"* A teljes tartalom megjelenítéséhez használjunk külső szövegszerkesztőt.\n"
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr "Nem visszavonni a hunk kiválasztását."
 
-#: lib/diff.tcl:310
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr "Nem sikerült kiválasztani a hunkot."
 
+#: lib/diff.tcl:509
+msgid "Failed to unstage selected line."
+msgstr "Nem sikerült visszavonni a sor kiválasztását."
+
+#: lib/diff.tcl:517
+msgid "Failed to stage selected line."
+msgstr "Nem sikerült kiválasztani a sort."
+
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Alapértelmezés"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Rendszer (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Más"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "hiba"
@@ -1506,40 +1690,49 @@
 msgid "Unlock Index"
 msgstr "Index zárolásának feloldása"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "A(z) %s commitba való kiválasztásának visszavonása"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr "Commitolásra kész."
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr "A(z) %s hozzáadása..."
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Visszaállítja a változtatásokat a(z) %s fájlban?"
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Visszaállítja a változtatásokat ebben e %i fájlban?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Minden nem kiválasztott változtatás el fog veszni ezáltal a visszaállítás "
 "által."
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr "Ne csináljunk semmit"
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "A kiválasztott fájlok visszaállítása"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "%s visszaállítása"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1568,7 +1761,7 @@
 "\n"
 "Az újrakeresés most automatikusan el fog indulni.\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1585,7 +1778,7 @@
 "Fel kell oldanunk őket, kiválasztani a fájlt, és commitolni hogy befejezzük "
 "a jelenlegi merge-t. Csak ezután kezdhetünk el egy újabbat.\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1602,34 +1795,34 @@
 "Először be kell fejeznünk a jelenlegi commitot, hogy elkezdhessünk egy merge-"
 "t. Ez segíteni fog, hogy félbeszakíthassunk egy merge-t.\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s / %s"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr "A(z) %s és a(z) %s merge-ölése..."
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr "A merge sikeresen befejeződött."
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "A merge sikertelen. Fel kell oldanunk az ütközéseket."
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Merge-ölés a következőbe: %s"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr "Merge-ölni szándékozott revízió"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1639,7 +1832,7 @@
 "\n"
 "Be kell fejeznünk ennek a commitnak a javítását.\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1654,7 +1847,7 @@
 "\n"
 "Folytatjuk a jelenlegi merge megszakítását?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1669,123 +1862,338 @@
 "\n"
 "Folytatjuk a jelenlegi módosítások visszavonását?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "Félbeszakítás"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "fájl visszaállítva"
 
-#: lib/merge.tcl:265
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "A félbeszakítás nem sikerült."
 
-#: lib/merge.tcl:267
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "A megkeszakítás befejeződött. Kész."
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Feloldás erőltetése az alap verzióhoz?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Feloldás erőltetése ehhez a branch-hez?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Feloldás erőltetése a másik branch-hez?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Megjegyzés: csak az ütköző különbségek látszanak.\n"
+"\n"
+"A(z) %s felül lesz írva.\n"
+"\n"
+"Ez a művelet csak a merge újraindításával lesz visszavonható."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+"A(z) %s fájl nem feloldott ütközéseket tartalmaz, mégis legyen kiválasztva?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Feloldás hozzáadása a(z) %s számára"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr "Nem lehet feloldani törlési vagy link ütközést egy eszközzel"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "A konfiklus-fájl nem létezik."
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Nem GUI merge eszköz: %s"
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "A(z) '%s' merge eszköz nem támogatott"
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "A merge eszköz már fut, le legyen állítva?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Hiba a verziók kinyerése közben:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"A merge eszköz indítása sikertelen:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "A merge eszköz futtatása..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "A merge eszköz nem sikerült."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Érvénytelen globális kódolás '%s'"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Érvénytelen repó kódolás '%s'"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Alapértelmezés visszaállítása"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "Mentés"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr "%s Repó"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "Globális (minden repó)"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "Felhasználónév"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "Email cím"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr "A merge commitok összegzése"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr "Merge beszédesség"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr "Diffstat mutatása merge után"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Merge eszköz használata"
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "A fájl módosítási dátumok megbízhatóak"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr "A követő branchek eltávolítása letöltés alatt"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "A követő branchek egyeztetése"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
+msgid "Blame Copy Only On Changed Files"
+msgstr "A blame másolás bekapcsolása csak megváltozott fájlokra"
+
+#: lib/option.tcl:150
+msgid "Minimum Letters To Blame Copy On"
+msgstr "Minimum betűszám blame másolás-érzékeléshez"
+
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "Blame történet környezet sugár (napokban)"
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "A diff környezeti sorok száma"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr "Commit üzenet szövegének szélessége"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "Új branch név sablon"
 
-#: lib/option.tcl:192
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Alapértelmezett fájltartalom-kódolás"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Megváltoztatás"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Helyesírás-ellenőrző szótár:"
 
-#: lib/option.tcl:216
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr "Betűtípus megváltoztatása"
 
-#: lib/option.tcl:220
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "%s választása"
 
-#: lib/option.tcl:226
+#: lib/option.tcl:264
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:240
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "Beállítások"
 
-#: lib/option.tcl:275
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "Nem sikerült teljesen elmenteni a beállításokat:"
 
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Remote eltávolítása"
+
+#: lib/remote.tcl:168
+msgid "Prune from"
+msgstr "Törlés innen"
+
+# tcl-format
+#: lib/remote.tcl:173
+msgid "Fetch from"
+msgstr "Letöltés innen"
+
+#: lib/remote.tcl:215
+msgid "Push to"
+msgstr "Push ide"
+
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Remote hozzáadása"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Új remote hozzáadása"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "Hozzáadás"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Remote részletei"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Hely:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Következő művelet"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Letöltés most"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Távoli repó inicializálása és push"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Ne csináljunk semmit"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Adjunk megy egy remote nevet."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "A(z) '%s' nem egy elfogadható remote név."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Nem sikerült a(t) '%s' remote hozzáadása innen: '%s'."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "a(z) %s letöltése"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "A(z) %s letöltése"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Nem tudni, hogy hogy kell a(z) '%s' helyen repót inicializálni."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "%s push-olása"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "A(z) %s beállítása itt: %s"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Távoli branch törlése"
+msgid "Delete Branch Remotely"
+msgstr "Távoli Branch törlése"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
 msgstr "Forrás repó"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "Távoli:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "Tetszőleges URL:"
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+msgid "Arbitrary Location:"
+msgstr "Önkényes hely:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1854,18 +2262,21 @@
 msgid "Scanning %s..."
 msgstr "Keresés itt: %s..."
 
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr "Törlés innen"
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Keresés:"
 
-# tcl-format
-#: lib/remote.tcl:170
-msgid "Fetch from"
-msgstr "Letöltés innen"
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Következő"
 
-#: lib/remote.tcl:213
-msgid "Push to"
-msgstr "Push ide"
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Előző"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Kisbetű-nagybetű számít"
 
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
@@ -1900,27 +2311,194 @@
 msgid "Unrecognized spell checker"
 msgstr "Ismeretlen helyesírás-ellenőrző"
 
-#: lib/spellcheck.tcl:180
+#: lib/spellcheck.tcl:186
 msgid "No Suggestions"
 msgstr "Nincs javaslat"
 
-#: lib/spellcheck.tcl:381
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "Nem várt EOF a helyesírás-ellenőrzőtől"
 
-#: lib/spellcheck.tcl:385
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "A helyesírás-ellenőrzés sikertelen"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Nincsenek kulcsok."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Nyilvános kulcs található ebben: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Kulcs generálása"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Másolás vágólapra"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Az OpenSSH publikus kulcsunk"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Generálás..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Az ssh-keygen indítása sikertelen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "A generálás nem sikerült."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "A generálás sikeres, de egy kulcs se található."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "A kulcsunk itt van: %s"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s ... %*i / %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
-msgstr "a(z) %s letöltése"
+msgid "Running %s requires a selected file."
+msgstr "A(z) %s futtatása egy kiválasztott fájlt igényel."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Biztos benne, hogy futtatni kívánja: %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Eszköz: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Futtatás: %s..."
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "Az eszköz sikeresen befejeződött: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Az eszköz sikertelen: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Eszköz hozzáadása"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Új eszköz-parancs hozzáadása"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Globális hozzáadás"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Eszköz részletei"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Használjunk '/' szeparátorokat almenü-fa létrehozásához:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Parancs:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Parancsablak mutatása futtatás előtt"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr ""
+"Megkéri a felhasználót, hogy válasszon ki egy revíziót (a $REVISION-t "
+"állítja)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Megkérdezi a felhasználót további argumentumokért (a $ARGS-ot állítja)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Ne mutassa a parancs kimeneti ablakát"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Futtatás csak ha egy diff ki van választva (a $FILENAME nem üres)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Adjunk meg egy eszköz nevet."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "A(z) '%s' eszköz már létezik."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Az eszköz nem hozzáadható:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Eszköz eltávolítása"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Eszköz parancsok eltávolítása"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Eltávolítás"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Kék jelzi a repó-specifikus eszközöket)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Parancs futtatása: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Argumentumok"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1937,72 +2515,81 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr "A %s repóból törölt követő branchek törlése"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "%s push-olása"
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr "Változások pusholása ide: %s"
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Tükrözés a következő helyre: %s"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "Pusholás: %s %s, ide: %s"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr "Branchek pusholása"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "Forrás branchek"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "Cél repó"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "Átviteli opciók"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr ""
 "Létező branch felülírásának erőltetése (lehet, hogy el fog dobni "
 "változtatásokat)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Vékony csomagok használata (lassú hálózati kapcsolatok számára)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "Tageket is"
 
+#~ msgid ""
+#~ "Unable to start gitk:\n"
+#~ "\n"
+#~ "%s does not exist"
+#~ msgstr ""
+#~ "A gitk indítása sikertelen:\n"
+#~ "\n"
+#~ "A(z) %s nem létezik"
+
+#~ msgid "Apple"
+#~ msgstr "Apple"
+
+#~ msgid "URL:"
+#~ msgstr "URL:"
+
+#~ msgid "Delete Remote Branch"
+#~ msgstr "Távoli branch törlése"
+
 #~ msgid "Not connected to aspell"
 #~ msgstr "Nincs kapcsolat az aspellhez"
 
-#~ msgid "Cannot find the git directory:"
-#~ msgstr "Nem található a git könyvtár:"
-
 #~ msgid "Unstaged Changes (Will Not Be Committed)"
 #~ msgstr "Nem kiválasztott változtatások (nem lesz commitolva)"
 
 #~ msgid "Push to %s..."
 #~ msgstr "Pusholás ide: %s..."
 
-#~ msgid "Add To Commit"
-#~ msgstr "Hozzáadás a commithoz"
-
 #~ msgid "Add Existing To Commit"
 #~ msgstr "Hozzáadás létező commithoz"
 
-#~ msgid "Running miga..."
-#~ msgstr "A miga futtatása..."
-
 #~ msgid "Add Existing"
 #~ msgstr "Létező hozzáadása"
 
diff --git a/git-gui/po/it.po b/git-gui/po/it.po
index 3db4fb6..294e595 100644
--- a/git-gui/po/it.po
+++ b/git-gui/po/it.po
@@ -9,41 +9,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 14:45-0700\n"
-"PO-Revision-Date: 2008-08-03 16:04+0200\n"
+"POT-Creation-Date: 2008-12-08 08:31-0800\n"
+"PO-Revision-Date: 2008-12-09 13:04+0100\n"
 "Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:688 git-gui.sh:702 git-gui.sh:715 git-gui.sh:798
-#: git-gui.sh:817
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr "git-gui: errore grave"
 
-#: git-gui.sh:644
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Caratteri non validi specificati in %s:"
 
-#: git-gui.sh:674
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr "Caratteri principali"
 
-#: git-gui.sh:675
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr "Caratteri per confronti e terminale"
 
-#: git-gui.sh:689
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr "Impossibile trovare git nel PATH"
 
-#: git-gui.sh:716
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr "Impossibile determinare la versione di Git:"
 
-#: git-gui.sh:734
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -62,381 +62,446 @@
 "\n"
 "Assumere che '%s' sia alla versione 1.5.0?\n"
 
-#: git-gui.sh:972
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr "Non trovo la directory di git: "
 
-#: git-gui.sh:979
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr "Impossibile spostarsi sulla directory principale del progetto:"
 
-#: git-gui.sh:986
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr "Impossibile usare una .git directory strana:"
 
-#: git-gui.sh:991
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr "Nessuna directory di lavoro"
 
-#: git-gui.sh:1138 lib/checkout_op.tcl:305
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr "Controllo dello stato dei file in corso..."
 
-#: git-gui.sh:1194
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr "Ricerca di file modificati in corso..."
 
-#: git-gui.sh:1369 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Avvio prepare-commit-msg hook..."
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Revisione rifiutata dal prepare-commit-msg hook."
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Pronto."
 
-#: git-gui.sh:1635
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr "Non modificato"
 
-#: git-gui.sh:1637
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr "Modificato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1638 git-gui.sh:1643
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr "Preparato per una nuova revisione"
 
-#: git-gui.sh:1639 git-gui.sh:1644
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr "Parti preparate per una nuova revisione"
 
-#: git-gui.sh:1640 git-gui.sh:1645
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr "Preparato per una nuova revisione, mancante"
 
-#: git-gui.sh:1642
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "Tipo di file modificato, non preparato per una nuova revisione"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "Tipo di file modificato, preparato per una nuova revisione"
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr "Non tracciato, non preparato per una nuova revisione"
 
-#: git-gui.sh:1647
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr "Mancante"
 
-#: git-gui.sh:1648
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr "Preparato per la rimozione"
 
-#: git-gui.sh:1649
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr "Preparato alla rimozione, ancora presente"
 
-#: git-gui.sh:1651 git-gui.sh:1652 git-gui.sh:1653 git-gui.sh:1654
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr "Richiede risoluzione dei conflitti"
 
-#: git-gui.sh:1689
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr "Avvio di gitk... attendere..."
 
-#: git-gui.sh:1698
+#: git-gui.sh:1887
 msgid "Couldn't find gitk in PATH"
 msgstr "Impossibile trovare gitk nel PATH"
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Archivio"
 
-#: git-gui.sh:1949
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr "Modifica"
 
-#: git-gui.sh:1951 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Ramo"
 
-#: git-gui.sh:1954 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Revisione"
 
-#: git-gui.sh:1957 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Fusione (Merge)"
 
-#: git-gui.sh:1958 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "Remoto"
 
-#: git-gui.sh:1967
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Strumenti"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Esplora copia di lavoro"
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr "Esplora i file del ramo attuale"
 
-#: git-gui.sh:1971
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr "Esplora i file del ramo..."
 
-#: git-gui.sh:1976
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr "Visualizza la cronologia del ramo attuale"
 
-#: git-gui.sh:1980
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr "Visualizza la cronologia di tutti i rami"
 
-#: git-gui.sh:1987
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Esplora i file di %s"
 
-#: git-gui.sh:1989
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualizza la cronologia di %s"
 
-#: git-gui.sh:1994 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Statistiche dell'archivio"
 
-#: git-gui.sh:1997 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Comprimi l'archivio"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr "Verifica l'archivio"
 
-#: git-gui.sh:2007 git-gui.sh:2011 git-gui.sh:2015 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Crea icona desktop"
 
-#: git-gui.sh:2023 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Esci"
 
-#: git-gui.sh:2031
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr "Annulla"
 
-#: git-gui.sh:2034
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr "Ripeti"
 
-#: git-gui.sh:2038 git-gui.sh:2545
+#: git-gui.sh:2378 git-gui.sh:2937
 msgid "Cut"
 msgstr "Taglia"
 
-#: git-gui.sh:2041 git-gui.sh:2548 git-gui.sh:2622 git-gui.sh:2715
+#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Copia"
 
-#: git-gui.sh:2044 git-gui.sh:2551
+#: git-gui.sh:2384 git-gui.sh:2943
 msgid "Paste"
 msgstr "Incolla"
 
-#: git-gui.sh:2047 git-gui.sh:2554 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Elimina"
 
-#: git-gui.sh:2051 git-gui.sh:2558 git-gui.sh:2719 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
 msgid "Select All"
 msgstr "Seleziona tutto"
 
-#: git-gui.sh:2060
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr "Crea..."
 
-#: git-gui.sh:2066
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr "Attiva..."
 
-#: git-gui.sh:2072
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr "Rinomina"
 
-#: git-gui.sh:2077 git-gui.sh:2187
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr "Elimina..."
 
-#: git-gui.sh:2082
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr "Ripristina..."
 
-#: git-gui.sh:2094 git-gui.sh:2491
-msgid "New Commit"
-msgstr "Nuova revisione"
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "Fatto"
 
-#: git-gui.sh:2102 git-gui.sh:2498
-msgid "Amend Last Commit"
-msgstr "Correggi l'ultima revisione"
-
-#: git-gui.sh:2111 git-gui.sh:2458 lib/remote_branch_delete.tcl:99
-msgid "Rescan"
-msgstr "Analizza nuovamente"
-
-#: git-gui.sh:2117
-msgid "Stage To Commit"
-msgstr "Prepara per una nuova revisione"
-
-#: git-gui.sh:2123
-msgid "Stage Changed Files To Commit"
-msgstr "Prepara i file modificati per una nuova revisione"
-
-#: git-gui.sh:2129
-msgid "Unstage From Commit"
-msgstr "Annulla preparazione"
-
-#: git-gui.sh:2134 lib/index.tcl:395
-msgid "Revert Changes"
-msgstr "Annulla modifiche"
-
-#: git-gui.sh:2141 git-gui.sh:2702
-msgid "Show Less Context"
-msgstr "Mostra meno contesto"
-
-#: git-gui.sh:2145 git-gui.sh:2706
-msgid "Show More Context"
-msgstr "Mostra più contesto"
-
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
-msgid "Sign Off"
-msgstr "Sign Off"
-
-#: git-gui.sh:2155 git-gui.sh:2474
+#: git-gui.sh:2434
 msgid "Commit@@verb"
 msgstr "Nuova revisione"
 
-#: git-gui.sh:2166
+#: git-gui.sh:2443 git-gui.sh:2878
+msgid "New Commit"
+msgstr "Nuova revisione"
+
+#: git-gui.sh:2451 git-gui.sh:2885
+msgid "Amend Last Commit"
+msgstr "Correggi l'ultima revisione"
+
+#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "Analizza nuovamente"
+
+#: git-gui.sh:2467
+msgid "Stage To Commit"
+msgstr "Prepara per una nuova revisione"
+
+#: git-gui.sh:2473
+msgid "Stage Changed Files To Commit"
+msgstr "Prepara i file modificati per una nuova revisione"
+
+#: git-gui.sh:2479
+msgid "Unstage From Commit"
+msgstr "Annulla preparazione"
+
+#: git-gui.sh:2484 lib/index.tcl:410
+msgid "Revert Changes"
+msgstr "Annulla modifiche"
+
+#: git-gui.sh:2491 git-gui.sh:3083
+msgid "Show Less Context"
+msgstr "Mostra meno contesto"
+
+#: git-gui.sh:2495 git-gui.sh:3087
+msgid "Show More Context"
+msgstr "Mostra più contesto"
+
+#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+msgid "Sign Off"
+msgstr "Sign Off"
+
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr "Fusione locale..."
 
-#: git-gui.sh:2171
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr "Interrompi fusione..."
 
-#: git-gui.sh:2183
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "Aggiungi..."
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr "Propaga..."
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "Elimina ramo..."
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Informazioni su %s"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr "Preferenze..."
 
-#: git-gui.sh:2209 git-gui.sh:2740
+#: git-gui.sh:2565 git-gui.sh:3129
 msgid "Options..."
 msgstr "Opzioni..."
 
-#: git-gui.sh:2215 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Rimuovi..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Aiuto"
 
-#: git-gui.sh:2256
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr "Documentazione sul web"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "Mostra chave SSH"
+
+#: git-gui.sh:2721
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "errore grave: impossibile effettuare lo stat del path %s: file o directory "
 "non trovata"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2754
 msgid "Current Branch:"
 msgstr "Ramo attuale:"
 
-#: git-gui.sh:2394
+#: git-gui.sh:2775
 msgid "Staged Changes (Will Commit)"
 msgstr "Modifiche preparate (saranno nella nuova revisione)"
 
-#: git-gui.sh:2414
+#: git-gui.sh:2795
 msgid "Unstaged Changes"
 msgstr "Modifiche non preparate"
 
-#: git-gui.sh:2464
+#: git-gui.sh:2845
 msgid "Stage Changed"
 msgstr "Prepara modificati"
 
-#: git-gui.sh:2480 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Propaga (Push)"
 
-#: git-gui.sh:2510
+#: git-gui.sh:2899
 msgid "Initial Commit Message:"
 msgstr "Messaggio di revisione iniziale:"
 
-#: git-gui.sh:2511
+#: git-gui.sh:2900
 msgid "Amended Commit Message:"
 msgstr "Messaggio di revisione corretto:"
 
-#: git-gui.sh:2512
+#: git-gui.sh:2901
 msgid "Amended Initial Commit Message:"
 msgstr "Messaggio iniziale di revisione corretto:"
 
-#: git-gui.sh:2513
+#: git-gui.sh:2902
 msgid "Amended Merge Commit Message:"
 msgstr "Messaggio di fusione corretto:"
 
-#: git-gui.sh:2514
+#: git-gui.sh:2903
 msgid "Merge Commit Message:"
 msgstr "Messaggio di fusione:"
 
-#: git-gui.sh:2515
+#: git-gui.sh:2904
 msgid "Commit Message:"
 msgstr "Messaggio di revisione:"
 
-#: git-gui.sh:2561 git-gui.sh:2723 lib/console.tcl:73
+#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Copia tutto"
 
-#: git-gui.sh:2585 lib/blame.tcl:100
+#: git-gui.sh:2977 lib/blame.tcl:104
 msgid "File:"
 msgstr "File:"
 
-#: git-gui.sh:2691
-msgid "Apply/Reverse Hunk"
-msgstr "Applica/Inverti sezione"
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr "Applica/Inverti riga"
-
-#: git-gui.sh:2711
+#: git-gui.sh:3092
 msgid "Refresh"
 msgstr "Rinfresca"
 
-#: git-gui.sh:2732
+#: git-gui.sh:3113
 msgid "Decrease Font Size"
 msgstr "Diminuisci dimensione caratteri"
 
-#: git-gui.sh:2736
+#: git-gui.sh:3117
 msgid "Increase Font Size"
 msgstr "Aumenta dimensione caratteri"
 
-#: git-gui.sh:2747
+#: git-gui.sh:3125 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Codifica"
+
+#: git-gui.sh:3136
+msgid "Apply/Reverse Hunk"
+msgstr "Applica/Inverti sezione"
+
+#: git-gui.sh:3141
+msgid "Apply/Reverse Line"
+msgstr "Applica/Inverti riga"
+
+#: git-gui.sh:3151
+msgid "Run Merge Tool"
+msgstr "Avvia programma esterno per la risoluzione dei conflitti"
+
+#: git-gui.sh:3156
+msgid "Use Remote Version"
+msgstr "Usa versione remota"
+
+#: git-gui.sh:3160
+msgid "Use Local Version"
+msgstr "Usa versione locale"
+
+#: git-gui.sh:3164
+msgid "Revert To Base"
+msgstr "Ritorna alla revisione comune"
+
+#: git-gui.sh:3183
 msgid "Unstage Hunk From Commit"
 msgstr "Annulla preparazione della sezione per una nuova revisione"
 
-#: git-gui.sh:2748
+#: git-gui.sh:3184
 msgid "Unstage Line From Commit"
 msgstr "Annulla preparazione della linea per una nuova revisione"
 
-#: git-gui.sh:2750
+#: git-gui.sh:3186
 msgid "Stage Hunk For Commit"
 msgstr "Prepara sezione per una nuova revisione"
 
-#: git-gui.sh:2751
+#: git-gui.sh:3187
 msgid "Stage Line For Commit"
 msgstr "Prepara linea per una nuova revisione"
 
-#: git-gui.sh:2771
+#: git-gui.sh:3210
 msgid "Initializing..."
 msgstr "Inizializzazione..."
 
-#: git-gui.sh:2876
+#: git-gui.sh:3315
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -453,7 +518,7 @@
 "da %s:\n"
 "\n"
 
-#: git-gui.sh:2906
+#: git-gui.sh:3345
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -463,7 +528,7 @@
 "Ciò è dovuto a un problema conosciuto\n"
 "causato dall'eseguibile Tcl distribuito da Cygwin."
 
-#: git-gui.sh:2911
+#: git-gui.sh:3350
 #, tcl-format
 msgid ""
 "\n"
@@ -483,80 +548,108 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - un'interfaccia grafica per Git."
 
-#: lib/blame.tcl:70
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "Mostra file"
 
-#: lib/blame.tcl:74
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "Revisione:"
 
-#: lib/blame.tcl:257
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "Copia revisione"
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Trova testo..."
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr "Ricerca accurata delle copie"
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Mostra contesto nella cronologia"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "Annota la revisione precedente"
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Lettura di %s..."
 
-#: lib/blame.tcl:492
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "Caricamento annotazioni per copie/spostamenti..."
 
-#: lib/blame.tcl:512
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "linee annotate"
 
-#: lib/blame.tcl:704
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "Caricamento annotazioni per posizione originaria..."
 
-#: lib/blame.tcl:707
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "Annotazione completata."
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr "Occupato"
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr "Il processo di annotazione è già in corso."
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr "Ricerca accurata delle copie in corso..."
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "Caricamento annotazioni..."
 
-#: lib/blame.tcl:883
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "Autore:"
 
-#: lib/blame.tcl:887
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "Revisione creata da:"
 
-#: lib/blame.tcl:892
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "File originario:"
 
-#: lib/blame.tcl:1006
+#: lib/blame.tcl:1020
+msgid "Cannot find HEAD commit:"
+msgstr "Impossibile trovare la revisione HEAD:"
+
+#: lib/blame.tcl:1075
+msgid "Cannot find parent commit:"
+msgstr "Impossibile trovare la revisione precedente:"
+
+#: lib/blame.tcl:1090
+msgid "Unable to display parent"
+msgstr "Impossibile visualizzare la revisione precedente"
+
+#: lib/blame.tcl:1091 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "Errore nel caricamento delle differenze:"
+
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "In origine da:"
 
-#: lib/blame.tcl:1012
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "Nel file:"
 
-#: lib/blame.tcl:1017
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "Copiato o spostato qui da:"
 
@@ -570,16 +663,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr "Annulla"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "Revisione"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:244
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "Opzioni"
 
@@ -599,7 +694,7 @@
 msgid "Create New Branch"
 msgstr "Crea nuovo ramo"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr "Crea"
 
@@ -607,7 +702,7 @@
 msgid "Branch Name"
 msgstr "Nome del ramo"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "Nome:"
 
@@ -753,9 +848,9 @@
 msgid "Browse Branch Files"
 msgstr "Esplora i file del ramo"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:472 lib/choose_repository.tcl:482
-#: lib/choose_repository.tcl:985
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr "Esplora"
 
@@ -770,6 +865,7 @@
 msgstr "errore grave: impossibile risolvere %s"
 
 #: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Chiudi"
 
@@ -883,7 +979,7 @@
 msgid "Reset '%s'?"
 msgstr "Ripristinare '%s'?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Visualizza"
 
@@ -933,226 +1029,230 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr "Crea nuovo archivio"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "Nuovo..."
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:458
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr "Clona archivio esistente"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "Clona..."
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:974
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr "Apri archivio esistente"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "Apri..."
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "Archivi recenti"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "Apri archivio recente:"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Impossibile creare l'archivio %s:"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "Directory:"
 
-#: lib/choose_repository.tcl:410 lib/choose_repository.tcl:535
-#: lib/choose_repository.tcl:1007
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr "Archivio Git"
 
-#: lib/choose_repository.tcl:435
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "La directory %s esiste già."
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Il file %s esiste già."
 
-#: lib/choose_repository.tcl:453
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr "Clona"
 
-#: lib/choose_repository.tcl:466
-msgid "URL:"
-msgstr "URL:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Posizione sorgente:"
 
-#: lib/choose_repository.tcl:487
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Directory di destinazione:"
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr "Tipo di clone:"
 
-#: lib/choose_repository.tcl:493
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (veloce, semi-ridondante, con hardlink)"
 
-#: lib/choose_repository.tcl:499
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Copia completa (più lento, backup ridondante)"
 
-#: lib/choose_repository.tcl:505
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Shared (il più veloce, non raccomandato, nessun backup)"
 
-#: lib/choose_repository.tcl:541 lib/choose_repository.tcl:588
-#: lib/choose_repository.tcl:734 lib/choose_repository.tcl:804
-#: lib/choose_repository.tcl:1013 lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "%s non è un archivio Git."
 
-#: lib/choose_repository.tcl:577
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr "Standard è disponibile solo per archivi locali."
 
-#: lib/choose_repository.tcl:581
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr "Shared è disponibile solo per archivi locali."
 
-#: lib/choose_repository.tcl:602
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Il file/directory %s esiste già."
 
-#: lib/choose_repository.tcl:613
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr "Impossibile configurare origin"
 
-#: lib/choose_repository.tcl:625
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr "Calcolo oggetti"
 
-#: lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr ""
 
-#: lib/choose_repository.tcl:650
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Impossibile copiare oggetti/info/alternate: %s"
 
-#: lib/choose_repository.tcl:686
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Niente da clonare da %s."
 
-#: lib/choose_repository.tcl:688 lib/choose_repository.tcl:902
-#: lib/choose_repository.tcl:914
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr "Il ramo 'master' non è stato inizializzato."
 
-#: lib/choose_repository.tcl:701
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Impossibile utilizzare gli hardlink. Si ricorrerà alla copia."
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Clonazione da %s"
 
-#: lib/choose_repository.tcl:744
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr "Copia degli oggetti"
 
-#: lib/choose_repository.tcl:745
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:769
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Impossibile copiare oggetto: %s"
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr "Collegamento oggetti"
 
-#: lib/choose_repository.tcl:780
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr "oggetti"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Hardlink impossibile sull'oggetto: %s"
 
-#: lib/choose_repository.tcl:843
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr ""
 "Impossibile recuperare rami e oggetti. Controllare i dettagli forniti dalla "
 "console."
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr ""
 "Impossibile recuperare le etichette. Controllare i dettagli forniti dalla "
 "console."
 
-#: lib/choose_repository.tcl:878
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr ""
 "Impossibile determinare HEAD. Controllare i dettagli forniti dalla console."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Impossibile ripulire %s"
 
-#: lib/choose_repository.tcl:893
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr "Clonazione non riuscita."
 
-#: lib/choose_repository.tcl:900
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr "Non è stato trovato un ramo predefinito."
 
-#: lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Impossibile risolvere %s come una revisione."
 
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr "Creazione directory di lavoro"
 
-#: lib/choose_repository.tcl:924 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr "file"
 
-#: lib/choose_repository.tcl:953
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr "Attivazione iniziale non riuscita."
 
-#: lib/choose_repository.tcl:969
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr "Apri"
 
-#: lib/choose_repository.tcl:979
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr "Archivio:"
 
-#: lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Impossibile accedere all'archivio %s:"
@@ -1224,19 +1324,19 @@
 "completata. Non puoi correggere la revisione precedente a meno che prima tu "
 "non interrompa l'operazione di fusione in corso.\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "Errore durante il caricamento dei dati della revisione da correggere:"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "Impossibile ottenere la tua identità:"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "GIT_COMMITTER_IDENT non valida:"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:132
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1253,7 +1353,7 @@
 "\n"
 "La nuova analisi comincerà ora.\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:155
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1266,7 +1366,7 @@
 "Il file %s presenta dei conflitti. Devi risolverli e preparare il file per "
 "creare una nuova revisione prima di effettuare questa azione.\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:163
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1277,7 +1377,7 @@
 "\n"
 "Questo programma non può creare una revisione contenente il file %s.\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:171
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1288,7 +1388,7 @@
 "Devi preparare per una nuova revisione almeno 1 file prima di effettuare "
 "questa operazione.\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:186
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1306,45 +1406,45 @@
 "- Seconda linea: vuota.\n"
 "- Terza linea: spiega a cosa serve la tua modifica.\n"
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:210
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr "attenzione: Tcl non supporta la codifica '%s'."
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:226
 msgid "Calling pre-commit hook..."
 msgstr "Avvio pre-commit hook..."
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:241
 msgid "Commit declined by pre-commit hook."
 msgstr "Revisione rifiutata dal pre-commit hook."
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:264
 msgid "Calling commit-msg hook..."
 msgstr "Avvio commit-msg hook..."
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:279
 msgid "Commit declined by commit-msg hook."
 msgstr "Revisione rifiutata dal commit-msg hook."
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:292
 msgid "Committing changes..."
 msgstr "Archiviazione modifiche..."
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:308
 msgid "write-tree failed:"
 msgstr "write-tree non riuscito:"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
 msgid "Commit failed."
 msgstr "Impossibile creare una nuova revisione."
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:326
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "La revisione %s sembra essere danneggiata"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:331
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1358,19 +1458,19 @@
 "\n"
 "Si procederà subito ad una nuova analisi.\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:338
 msgid "No changes to commit."
 msgstr "Nessuna modifica per la nuova revisione."
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:352
 msgid "commit-tree failed:"
 msgstr "commit-tree non riuscito:"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:372
 msgid "update-ref failed:"
 msgstr "update-ref non riuscito:"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:460
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Creata revisione %s: %s"
@@ -1445,7 +1545,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Git ha restituito una data non valida: %s"
 
-#: lib/diff.tcl:44
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1468,48 +1568,101 @@
 "Si procederà automaticamente ad una nuova analisi per trovare altri file che "
 "potrebbero avere lo stesso stato."
 
-#: lib/diff.tcl:83
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Caricamento delle differenze di %s..."
 
-#: lib/diff.tcl:116 lib/diff.tcl:190
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"LOCALE: cancellato\n"
+"REMOTO:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"REMOTO: cancellato\n"
+"LOCALE:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "LOCALE:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "REMOTO:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Impossibile visualizzare %s"
 
-#: lib/diff.tcl:117
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr "Errore nel caricamento del file:"
 
-#: lib/diff.tcl:124
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr "Archivio Git (sottoprogetto)"
 
-#: lib/diff.tcl:136
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr "* File binario (il contenuto non sarà mostrato)."
 
-#: lib/diff.tcl:191
-msgid "Error loading diff:"
-msgstr "Errore nel caricamento delle differenze:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Il file non tracciato è di %d byte.\n"
+"* Saranno visualizzati solo i primi %d byte.\n"
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* %s non visualizza completamente questo file non tracciato.\n"
+"* Per visualizzare il file completo, usare un programma esterno.\n"
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr "Impossibile rimuovere la sezione scelta dalla nuova revisione."
 
-#: lib/diff.tcl:320
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr "Impossibile preparare la sezione scelta per una nuova revisione."
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:509
 msgid "Failed to unstage selected line."
 msgstr "Impossibile rimuovere la riga scelta dalla nuova revisione."
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:517
 msgid "Failed to stage selected line."
 msgstr "Impossibile preparare la riga scelta per una nuova revisione."
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Predefinito"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Codifica di sistema (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Altro"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "errore"
@@ -1547,40 +1700,49 @@
 msgid "Unlock Index"
 msgstr "Sblocca l'accesso all'indice"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "%s non farà parte della prossima revisione"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr "Pronto per creare una nuova revisione."
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr "Aggiunta di %s in corso"
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Annullare le modifiche nel file %s?"
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Annullare le modifiche in questi %i file?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Tutte le modifiche non preparate per una nuova revisione saranno perse per "
 "sempre."
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr "Non fare niente"
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "Annullo le modifiche nei file selezionati"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Annullo le modifiche in %s"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1608,7 +1770,7 @@
 "\n"
 "La nuova analisi comincerà ora.\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1626,7 +1788,7 @@
 "infine crearla per completare la fusione attuale. Solo a questo punto potrai "
 "iniziare un'altra fusione.\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1644,34 +1806,34 @@
 "una fusione. In questo modo sarà più facile interrompere una fusione non "
 "riuscita, nel caso ce ne fosse bisogno.\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s di %s"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr "Fusione di %s e %s in corso..."
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr "Fusione completata con successo."
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Fusione non riuscita. Bisogna risolvere i conflitti."
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Fusione in %s"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr "Revisione da fondere"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1681,7 +1843,7 @@
 "\n"
 "Bisogna finire di correggere questa revisione.\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1696,7 +1858,7 @@
 "\n"
 "Continuare con l'interruzione della fusione attuale?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1704,151 +1866,352 @@
 "\n"
 "Continue with resetting the current changes?"
 msgstr ""
-"Ripristinare la revisione corrente e annullare le modifiche?\n"
+"Ripristinare la revisione attuale e annullare le modifiche?\n"
 "\n"
 "L'annullamento delle modifiche causerà la perdita di *TUTTE* le modifiche "
 "non ancora presenti nell'archivio.\n"
 "\n"
 "Continuare con l'annullamento delle modifiche attuali?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "Interruzione"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "ripristino file"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "Interruzione non riuscita."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "Interruzione completata. Pronto."
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Imporre la risoluzione alla revisione comune?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Imporre la risoluzione al ramo attuale?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Imporre la risoluzione all'altro ramo?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Si stanno mostrando solo le modifiche con conflitti.\n"
+"\n"
+"%s sarà sovrascritto.\n"
+"\n"
+"Questa operazione può essere modificata solo ricominciando la fusione."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+"Il file %s sembra contenere conflitti non risolti, preparare per la prossima "
+"revisione?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr ""
+"La risoluzione dei conflitti per %s è preparata per la prossima revisione"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Non è possibile risolvere i conflitti per cancellazioni o link con un "
+"programma esterno"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Non esiste un file con conflitti."
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "'%s' non è una GUI per la risoluzione dei conflitti."
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Il programma '%s' non è supportato"
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "La risoluzione dei conflitti è già avviata, terminarla?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Errore: revisione non trovata:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossibile avviare la risoluzione dei conflitti:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "Avvio del programma per la risoluzione dei conflitti in corso..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "Risoluzione dei conflitti non riuscita."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr ""
+"La codifica dei caratteri '%s' specificata per tutti gli archivi non è valida"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr ""
+"La codifica dei caratteri '%s' specificata per l'archivio attuale  non è "
+"valida"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Ripristina valori predefiniti"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "Salva"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr "Archivio di %s"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "Tutti gli archivi"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "Nome utente"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "Indirizzo Email"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr "Riepilogo nelle revisioni di fusione"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr "Prolissità della fusione"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr "Mostra statistiche delle differenze dopo la fusione"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Programma da utilizzare per la risoluzione dei conflitti"
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "Fidati delle date di modifica dei file"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr ""
 "Effettua potatura dei duplicati locali di rami remoti durante il recupero"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "Appaia duplicati locali di rami remoti"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr "Ricerca copie solo nei file modificati"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr "Numero minimo di lettere che attivano la ricerca delle copie"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "Giorni di contesto nella cronologia delle annotazioni"
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "Numero di linee di contesto nelle differenze"
 
-#: lib/option.tcl:129
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr "Larghezza del messaggio di revisione"
 
-#: lib/option.tcl:130
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "Modello per il nome di un nuovo ramo"
 
-#: lib/option.tcl:194
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Codifica predefinita per il contenuto dei file"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Cambia"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Lingua dizionario:"
 
-#: lib/option.tcl:218
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr "Cambia caratteri"
 
-#: lib/option.tcl:222
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "Scegli %s"
 
-#: lib/option.tcl:228
+#: lib/option.tcl:264
 msgid "pt."
 msgstr "pt."
 
-#: lib/option.tcl:242
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "Preferenze"
 
-#: lib/option.tcl:277
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "Impossibile salvare completamente le opzioni:"
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Rimuovi archivio remoto"
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr "Effettua potatura da"
 
-#: lib/remote.tcl:170
+#: lib/remote.tcl:173
 msgid "Fetch from"
 msgstr "Recupera da"
 
-#: lib/remote.tcl:213
+#: lib/remote.tcl:215
 msgid "Push to"
 msgstr "Propaga verso"
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Aggiungi archivio remoto"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Aggiungi nuovo archivio remoto"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "Aggiungi"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Dettagli sull'archivio remoto"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Posizione:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Altra azione"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Recupera subito"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Inizializza l'archivio remoto e propaga"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Non fare altro"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Inserire un nome per l'archivio remoto."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "'%s' non è utilizzabile come nome di archivio remoto."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Impossibile aggiungere l'archivio remoto '%s' posto in '%s'."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "recupera da %s"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Recupero %s"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Impossibile inizializzare l'archivio posto in '%s'."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "propaga verso %s"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Imposto %s (in %s)"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Cancella ramo remoto"
+msgid "Delete Branch Remotely"
+msgstr "Elimina ramo remoto"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
 msgstr "Da archivio"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
 msgstr "Remoto:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "URL specifico:"
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+msgid "Arbitrary Location:"
+msgstr "Posizione specifica:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1918,6 +2281,22 @@
 msgid "Scanning %s..."
 msgstr "Analisi in corso %s..."
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Trova:"
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Succ"
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Prec"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Distingui maiuscole"
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "Impossibile scrivere shortcut:"
@@ -1955,23 +2334,189 @@
 msgid "No Suggestions"
 msgstr "Nessun suggerimento"
 
-#: lib/spellcheck.tcl:387
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "Il correttore ortografico ha mandato un EOF inaspettato"
 
-#: lib/spellcheck.tcl:391
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "Errore nel correttore ortografico"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Chiavi non trovate."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Chiave pubblica trovata in: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Crea chiave"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Copia negli appunti"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "La tua chiave pubblica OpenSSH"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Creazione chiave in corso..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossibile avviare ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "Errore durante la creazione della chiave."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "La chiave è stata creata con successo, ma non è stata trovata."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "La chiave è in: %s"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%1$s ... %6$s: %2$*i di %4$*i (%7$3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
-msgstr "recupera da %s"
+msgid "Running %s requires a selected file."
+msgstr "Bisogna selezionare un file prima di eseguire %s."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Vuoi davvero eseguire %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Strumento: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Eseguo: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "Il programma esterno è terminato con successo: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Il programma esterno ha riportato un errore: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Aggiungi strumento"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Aggiungi un nuovo comando"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Aggiungi per tutti gli archivi"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Dettagli sullo strumento"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Utilizza il separatore '/' per creare un albero di sottomenu:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Comando:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Mostra una finestra di dialogo prima dell'avvio"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Chiedi all'utente di scegliere una revisione (imposta $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Chiedi all'utente di fornire argomenti aggiuntivi (imposta $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Non mostrare la finestra di comando"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Avvia solo se è selezionata una differenza ($FILENAME non è vuoto)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Bisogna dare un nome allo strumento."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "Lo strumento '%s' esiste già."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Impossibile aggiungere lo strumento:\n"
+"\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Rimuovi strumento"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Rimuovi i comandi dello strumento"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Rimuovi"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Il colore blu indica strumenti per l'archivio locale)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Avvia il comando: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Argomenti"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1988,45 +2533,45 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Effettua potatura dei duplicati locali di rami remoti cancellati da %s"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "propaga verso %s"
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr "Propagazione modifiche a %s"
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Mirroring verso %s"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "Propagazione %s %s a %s"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
 msgstr "Propaga rami"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "Rami di origine"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "Archivio di destinazione"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "Opzioni di trasferimento"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr "Sovrascrivi ramo esistente (alcune modifiche potrebbero essere perse)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Utilizza 'thin pack' (per connessioni lente)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "Includi etichette"
diff --git a/git-gui/po/ja.po b/git-gui/po/ja.po
index 5db44a4..09d60be 100644
--- a/git-gui/po/ja.po
+++ b/git-gui/po/ja.po
@@ -8,41 +8,41 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 14:45-0700\n"
-"PO-Revision-Date: 2008-08-03 17:00+0900\n"
+"POT-Creation-Date: 2008-12-08 08:31-0800\n"
+"PO-Revision-Date: 2008-12-09 06:27+0900\n"
 "Last-Translator: しらいし ななこ <nanako3@lavabit.com>\n"
 "Language-Team: Japanese\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:688 git-gui.sh:702 git-gui.sh:715 git-gui.sh:798
-#: git-gui.sh:817
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr "git-gui: 致命的なエラー"
 
-#: git-gui.sh:644
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "%s に無効なフォントが指定されています:"
 
-#: git-gui.sh:674
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr "主フォント"
 
-#: git-gui.sh:675
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr "diff/コンソール・フォント"
 
-#: git-gui.sh:689
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr "PATH 中に git が見つかりません"
 
-#: git-gui.sh:716
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr "Git バージョン名が理解できません:"
 
-#: git-gui.sh:734
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,381 +61,446 @@
 "\n"
 "'%s' はバージョン 1.5.0 と思って良いですか?\n"
 
-#: git-gui.sh:972
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr "Git ディレクトリが見つかりません:"
 
-#: git-gui.sh:979
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr "作業ディレクトリの最上位に移動できません"
 
-#: git-gui.sh:986
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr "変な .git ディレクトリは使えません"
 
-#: git-gui.sh:991
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr "作業ディレクトリがありません"
 
-#: git-gui.sh:1138 lib/checkout_op.tcl:305
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr "ファイル状態を更新しています…"
 
-#: git-gui.sh:1194
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr "変更されたファイルをスキャンしています…"
 
-#: git-gui.sh:1369 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr "prepare-commit-msg フックを実行中・・・"
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "prepare-commit-msg フックがコミットを拒否しました"
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "準備完了"
 
-#: git-gui.sh:1635
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr "変更無し"
 
-#: git-gui.sh:1637
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr "変更あり、コミット未予定"
 
-#: git-gui.sh:1638 git-gui.sh:1643
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr "コミット予定済"
 
-#: git-gui.sh:1639 git-gui.sh:1644
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr "部分的にコミット予定済"
 
-#: git-gui.sh:1640 git-gui.sh:1645
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr "コミット予定済、ファイル無し"
 
-#: git-gui.sh:1642
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "ファイル型変更、コミット未予定"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "ファイル型変更、コミット予定済"
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr "管理外、コミット未予定"
 
-#: git-gui.sh:1647
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr "ファイル無し"
 
-#: git-gui.sh:1648
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr "削除予定済"
 
-#: git-gui.sh:1649
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr "削除予定済、ファイル未削除"
 
-#: git-gui.sh:1651 git-gui.sh:1652 git-gui.sh:1653 git-gui.sh:1654
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr "要マージ解決"
 
-#: git-gui.sh:1689
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr "gitk を起動中…お待ち下さい…"
 
-#: git-gui.sh:1698
+#: git-gui.sh:1887
 msgid "Couldn't find gitk in PATH"
 msgstr "PATH 中に gitk が見つかりません"
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "リポジトリ"
 
-#: git-gui.sh:1949
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr "編集"
 
-#: git-gui.sh:1951 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "ブランチ"
 
-#: git-gui.sh:1954 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "コミット"
 
-#: git-gui.sh:1957 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "マージ"
 
-#: git-gui.sh:1958 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
 msgstr "リモート"
 
-#: git-gui.sh:1967
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "ツール"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "ワーキングコピーをブラウズ"
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr "現在のブランチのファイルを見る"
 
-#: git-gui.sh:1971
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr "ブランチのファイルを見る…"
 
-#: git-gui.sh:1976
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr "現在のブランチの履歴を見る"
 
-#: git-gui.sh:1980
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr "全てのブランチの履歴を見る"
 
-#: git-gui.sh:1987
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "ブランチ %s のファイルを見る"
 
-#: git-gui.sh:1989
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "ブランチ %s の履歴を見る"
 
-#: git-gui.sh:1994 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "データベース統計"
 
-#: git-gui.sh:1997 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "データベース圧縮"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr "データベース検証"
 
-#: git-gui.sh:2007 git-gui.sh:2011 git-gui.sh:2015 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "デスクトップ・アイコンを作る"
 
-#: git-gui.sh:2023 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "終了"
 
-#: git-gui.sh:2031
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr "元に戻す"
 
-#: git-gui.sh:2034
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr "やり直し"
 
-#: git-gui.sh:2038 git-gui.sh:2545
+#: git-gui.sh:2378 git-gui.sh:2923
 msgid "Cut"
 msgstr "切り取り"
 
-#: git-gui.sh:2041 git-gui.sh:2548 git-gui.sh:2622 git-gui.sh:2715
+#: git-gui.sh:2381 git-gui.sh:2926 git-gui.sh:3000 git-gui.sh:3082
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "コピー"
 
-#: git-gui.sh:2044 git-gui.sh:2551
+#: git-gui.sh:2384 git-gui.sh:2929
 msgid "Paste"
 msgstr "貼り付け"
 
-#: git-gui.sh:2047 git-gui.sh:2554 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2932 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "削除"
 
-#: git-gui.sh:2051 git-gui.sh:2558 git-gui.sh:2719 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2936 git-gui.sh:3086 lib/console.tcl:71
 msgid "Select All"
 msgstr "全て選択"
 
-#: git-gui.sh:2060
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr "作成…"
 
-#: git-gui.sh:2066
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr "チェックアウト"
 
-#: git-gui.sh:2072
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr "名前変更…"
 
-#: git-gui.sh:2077 git-gui.sh:2187
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr "削除…"
 
-#: git-gui.sh:2082
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr "リセット…"
 
-#: git-gui.sh:2094 git-gui.sh:2491
-msgid "New Commit"
-msgstr "新規コミット"
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "完了"
 
-#: git-gui.sh:2102 git-gui.sh:2498
-msgid "Amend Last Commit"
-msgstr "最新コミットを訂正"
-
-#: git-gui.sh:2111 git-gui.sh:2458 lib/remote_branch_delete.tcl:99
-msgid "Rescan"
-msgstr "再スキャン"
-
-#: git-gui.sh:2117
-msgid "Stage To Commit"
-msgstr "コミット予定する"
-
-#: git-gui.sh:2123
-msgid "Stage Changed Files To Commit"
-msgstr "変更されたファイルをコミット予定"
-
-#: git-gui.sh:2129
-msgid "Unstage From Commit"
-msgstr "コミットから降ろす"
-
-#: git-gui.sh:2134 lib/index.tcl:395
-msgid "Revert Changes"
-msgstr "変更を元に戻す"
-
-#: git-gui.sh:2141 git-gui.sh:2702
-msgid "Show Less Context"
-msgstr "文脈を少なく"
-
-#: git-gui.sh:2145 git-gui.sh:2706
-msgid "Show More Context"
-msgstr "文脈を多く"
-
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
-msgid "Sign Off"
-msgstr "署名"
-
-#: git-gui.sh:2155 git-gui.sh:2474
+#: git-gui.sh:2434
 msgid "Commit@@verb"
 msgstr "コミット"
 
-#: git-gui.sh:2166
+#: git-gui.sh:2443 git-gui.sh:2864
+msgid "New Commit"
+msgstr "新規コミット"
+
+#: git-gui.sh:2451 git-gui.sh:2871
+msgid "Amend Last Commit"
+msgstr "最新コミットを訂正"
+
+#: git-gui.sh:2461 git-gui.sh:2825 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "再スキャン"
+
+#: git-gui.sh:2467
+msgid "Stage To Commit"
+msgstr "コミット予定する"
+
+#: git-gui.sh:2473
+msgid "Stage Changed Files To Commit"
+msgstr "変更されたファイルをコミット予定"
+
+#: git-gui.sh:2479
+msgid "Unstage From Commit"
+msgstr "コミットから降ろす"
+
+#: git-gui.sh:2484 lib/index.tcl:410
+msgid "Revert Changes"
+msgstr "変更を元に戻す"
+
+#: git-gui.sh:2491 git-gui.sh:3069
+msgid "Show Less Context"
+msgstr "文脈を少なく"
+
+#: git-gui.sh:2495 git-gui.sh:3073
+msgid "Show More Context"
+msgstr "文脈を多く"
+
+#: git-gui.sh:2502 git-gui.sh:2838 git-gui.sh:2947
+msgid "Sign Off"
+msgstr "署名"
+
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr "ローカル・マージ…"
 
-#: git-gui.sh:2171
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr "マージ中止…"
 
-#: git-gui.sh:2183
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "追加"
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr "プッシュ…"
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "ブランチ削除..."
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "%s について"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr "設定…"
 
-#: git-gui.sh:2209 git-gui.sh:2740
+#: git-gui.sh:2565 git-gui.sh:3115
 msgid "Options..."
 msgstr "オプション…"
 
-#: git-gui.sh:2215 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "削除..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "ヘルプ"
 
-#: git-gui.sh:2256
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr "オンライン・ドキュメント"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "SSH キーを表示"
+
+#: git-gui.sh:2707
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "致命的: パス %s が stat できません。そのようなファイルやディレクトリはありま"
 "せん"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2740
 msgid "Current Branch:"
 msgstr "現在のブランチ"
 
-#: git-gui.sh:2394
+#: git-gui.sh:2761
 msgid "Staged Changes (Will Commit)"
 msgstr "ステージングされた(コミット予定済の)変更"
 
-#: git-gui.sh:2414
+#: git-gui.sh:2781
 msgid "Unstaged Changes"
 msgstr "コミット予定に入っていない変更"
 
-#: git-gui.sh:2464
+#: git-gui.sh:2831
 msgid "Stage Changed"
 msgstr "変更をコミット予定に入れる"
 
-#: git-gui.sh:2480 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2850 lib/transport.tcl:93 lib/transport.tcl:182
 msgid "Push"
 msgstr "プッシュ"
 
-#: git-gui.sh:2510
+#: git-gui.sh:2885
 msgid "Initial Commit Message:"
 msgstr "最初のコミットメッセージ:"
 
-#: git-gui.sh:2511
+#: git-gui.sh:2886
 msgid "Amended Commit Message:"
 msgstr "訂正したコミットメッセージ:"
 
-#: git-gui.sh:2512
+#: git-gui.sh:2887
 msgid "Amended Initial Commit Message:"
 msgstr "訂正した最初のコミットメッセージ:"
 
-#: git-gui.sh:2513
+#: git-gui.sh:2888
 msgid "Amended Merge Commit Message:"
 msgstr "訂正したマージコミットメッセージ:"
 
-#: git-gui.sh:2514
+#: git-gui.sh:2889
 msgid "Merge Commit Message:"
 msgstr "マージコミットメッセージ:"
 
-#: git-gui.sh:2515
+#: git-gui.sh:2890
 msgid "Commit Message:"
 msgstr "コミットメッセージ:"
 
-#: git-gui.sh:2561 git-gui.sh:2723 lib/console.tcl:73
+#: git-gui.sh:2939 git-gui.sh:3090 lib/console.tcl:73
 msgid "Copy All"
 msgstr "全てコピー"
 
-#: git-gui.sh:2585 lib/blame.tcl:100
+#: git-gui.sh:2963 lib/blame.tcl:104
 msgid "File:"
 msgstr "ファイル:"
 
-#: git-gui.sh:2691
-msgid "Apply/Reverse Hunk"
-msgstr "パッチを適用/取り消す"
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr "パッチ行を適用/取り消す"
-
-#: git-gui.sh:2711
+#: git-gui.sh:3078
 msgid "Refresh"
 msgstr "再読み込み"
 
-#: git-gui.sh:2732
+#: git-gui.sh:3099
 msgid "Decrease Font Size"
 msgstr "フォントを小さく"
 
-#: git-gui.sh:2736
+#: git-gui.sh:3103
 msgid "Increase Font Size"
 msgstr "フォントを大きく"
 
-#: git-gui.sh:2747
+#: git-gui.sh:3111 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "エンコーディング"
+
+#: git-gui.sh:3122
+msgid "Apply/Reverse Hunk"
+msgstr "パッチを適用/取り消す"
+
+#: git-gui.sh:3127
+msgid "Apply/Reverse Line"
+msgstr "パッチ行を適用/取り消す"
+
+#: git-gui.sh:3137
+msgid "Run Merge Tool"
+msgstr "マージツールを起動"
+
+#: git-gui.sh:3142
+msgid "Use Remote Version"
+msgstr "リモートの方を採用"
+
+#: git-gui.sh:3146
+msgid "Use Local Version"
+msgstr "ローカルの方を採用"
+
+#: git-gui.sh:3150
+msgid "Revert To Base"
+msgstr "ベース版を採用"
+
+#: git-gui.sh:3169
 msgid "Unstage Hunk From Commit"
 msgstr "パッチをコミット予定から外す"
 
-#: git-gui.sh:2748
+#: git-gui.sh:3170
 msgid "Unstage Line From Commit"
 msgstr "コミット予定から行を外す"
 
-#: git-gui.sh:2750
+#: git-gui.sh:3172
 msgid "Stage Hunk For Commit"
 msgstr "パッチをコミット予定に加える"
 
-#: git-gui.sh:2751
+#: git-gui.sh:3173
 msgid "Stage Line For Commit"
 msgstr "パッチ行をコミット予定に加える"
 
-#: git-gui.sh:2771
+#: git-gui.sh:3196
 msgid "Initializing..."
 msgstr "初期化しています…"
 
-#: git-gui.sh:2876
+#: git-gui.sh:3301
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -450,7 +515,7 @@
 "以下の環境変数は %s が起動する Git サブプロセスによって無視されるでしょう:\n"
 "\n"
 
-#: git-gui.sh:2906
+#: git-gui.sh:3331
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -460,7 +525,7 @@
 "これは Cygwin で配布されている Tcl バイナリに\n"
 "関しての既知の問題によります"
 
-#: git-gui.sh:2911
+#: git-gui.sh:3336
 #, tcl-format
 msgid ""
 "\n"
@@ -479,80 +544,108 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr "Git のグラフィカルUI git-gui"
 
-#: lib/blame.tcl:70
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "ファイルピューワ"
 
-#: lib/blame.tcl:74
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "コミット:"
 
-#: lib/blame.tcl:257
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "コミットをコピー"
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "テキストを検索"
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr "コピー検知"
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "文脈を見せる"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "親コミットを註釈"
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "%s を読んでいます…"
 
-#: lib/blame.tcl:492
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "コピー・移動追跡データを読んでいます…"
 
-#: lib/blame.tcl:512
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "行を注釈しました"
 
-#: lib/blame.tcl:704
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "元位置行の注釈データを読んでいます…"
 
-#: lib/blame.tcl:707
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "注釈完了しました"
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr "実行中"
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr "すでに blame プロセスを実行中です。"
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr "コピー検知を実行中…"
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "注釈を読み込んでいます…"
 
-#: lib/blame.tcl:883
+#: lib/blame.tcl:964
 msgid "Author:"
 msgstr "作者:"
 
-#: lib/blame.tcl:887
+#: lib/blame.tcl:968
 msgid "Committer:"
 msgstr "コミット者:"
 
-#: lib/blame.tcl:892
+#: lib/blame.tcl:973
 msgid "Original File:"
 msgstr "元ファイル"
 
-#: lib/blame.tcl:1006
+#: lib/blame.tcl:1021
+msgid "Cannot find HEAD commit:"
+msgstr "HEAD コミットが見つかりません"
+
+#: lib/blame.tcl:1076
+msgid "Cannot find parent commit:"
+msgstr "親コミットが見つかりません:"
+
+#: lib/blame.tcl:1091
+msgid "Unable to display parent"
+msgstr "親を表示できません"
+
+#: lib/blame.tcl:1092 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "diff を読む際のエラーです:"
+
+#: lib/blame.tcl:1232
 msgid "Originally By:"
 msgstr "原作者:"
 
-#: lib/blame.tcl:1012
+#: lib/blame.tcl:1238
 msgid "In File:"
 msgstr "ファイル:"
 
-#: lib/blame.tcl:1017
+#: lib/blame.tcl:1243
 msgid "Copied Or Moved Here By:"
 msgstr "複写・移動者:"
 
@@ -566,16 +659,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:97
 msgid "Cancel"
 msgstr "中止"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "リビジョン"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:244
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "オプション"
 
@@ -595,7 +690,7 @@
 msgid "Create New Branch"
 msgstr "ブランチを新規作成"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr "作成"
 
@@ -603,7 +698,7 @@
 msgid "Branch Name"
 msgstr "ブランチ名"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "名前:"
 
@@ -748,9 +843,9 @@
 msgid "Browse Branch Files"
 msgstr "現在のブランチのファイルを見る"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:472 lib/choose_repository.tcl:482
-#: lib/choose_repository.tcl:985
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr "ブラウズ"
 
@@ -765,6 +860,7 @@
 msgstr "致命的エラー: %s を解決できません"
 
 #: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "閉じる"
 
@@ -875,7 +971,7 @@
 msgid "Reset '%s'?"
 msgstr "'%s' をリセットしますか?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "可視化"
 
@@ -923,221 +1019,225 @@
 msgid "Git Gui"
 msgstr "Git GUI"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr "新しいリポジトリを作る"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "新規…"
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:458
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr "既存リポジトリを複製する"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "複製…"
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:974
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr "既存リポジトリを開く"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "開く…"
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "最近使ったリポジトリ"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "最近使ったリポジトリを開く"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "リポジトリ %s を作製できません:"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "ディレクトリ:"
 
-#: lib/choose_repository.tcl:410 lib/choose_repository.tcl:535
-#: lib/choose_repository.tcl:1007
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr "GIT リポジトリ"
 
-#: lib/choose_repository.tcl:435
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "ディレクトリ '%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "ファイル '%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:453
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr "複製"
 
-#: lib/choose_repository.tcl:466
-msgid "URL:"
-msgstr "URL:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "ソースの位置"
 
-#: lib/choose_repository.tcl:487
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "先ディレクトリ:"
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr "複製方式:"
 
-#: lib/choose_repository.tcl:493
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "標準(高速・中冗長度・ハードリンク)"
 
-#: lib/choose_repository.tcl:499
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "全複写(低速・冗長バックアップ)"
 
-#: lib/choose_repository.tcl:505
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "共有(最高速・非推奨・バックアップ無し)"
 
-#: lib/choose_repository.tcl:541 lib/choose_repository.tcl:588
-#: lib/choose_repository.tcl:734 lib/choose_repository.tcl:804
-#: lib/choose_repository.tcl:1013 lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Git リポジトリではありません: %s"
 
-#: lib/choose_repository.tcl:577
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr "標準方式は同一計算機上のリポジトリにのみ使えます。"
 
-#: lib/choose_repository.tcl:581
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr "共有方式は同一計算機上のリポジトリにのみ使えます。"
 
-#: lib/choose_repository.tcl:602
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "'%s' は既に存在します。"
 
-#: lib/choose_repository.tcl:613
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr "origin を設定できませんでした"
 
-#: lib/choose_repository.tcl:625
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr "オブジェクトを数えています"
 
-#: lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr "バケツ"
 
-#: lib/choose_repository.tcl:650
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "objects/info/alternates を複写できません: %s"
 
-#: lib/choose_repository.tcl:686
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "%s から複製する内容はありません"
 
-#: lib/choose_repository.tcl:688 lib/choose_repository.tcl:902
-#: lib/choose_repository.tcl:914
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr "'master' ブランチが初期化されていません"
 
-#: lib/choose_repository.tcl:701
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "ハードリンクが作れないので、コピーします"
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "%s から複製しています"
 
-#: lib/choose_repository.tcl:744
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr "オブジェクトを複写しています"
 
-#: lib/choose_repository.tcl:745
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:769
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "オブジェクトを複写できません: %s"
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr "オブジェクトを連結しています"
 
-#: lib/choose_repository.tcl:780
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr "オブジェクト"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "オブジェクトをハードリンクできません: %s"
 
-#: lib/choose_repository.tcl:843
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr "ブランチやオブジェクトを取得できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "タグを取得できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:878
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "HEAD を確定できません。コンソール出力を見て下さい"
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "%s を掃除できません"
 
-#: lib/choose_repository.tcl:893
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr "複写に失敗しました。"
 
-#: lib/choose_repository.tcl:900
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr "デフォールト・ブランチが取得されませんでした"
 
-#: lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "%s をコミットとして解釈できません"
 
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr "作業ディレクトリを作成しています"
 
-#: lib/choose_repository.tcl:924 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr "ファイル"
 
-#: lib/choose_repository.tcl:953
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr "初期チェックアウトに失敗しました"
 
-#: lib/choose_repository.tcl:969
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr "開く"
 
-#: lib/choose_repository.tcl:979
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr "リポジトリ:"
 
-#: lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "リポジトリ %s を開けません:"
@@ -1236,7 +1336,7 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:156
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1249,7 +1349,7 @@
 "ファイル %s にはマージ衝突が残っています。まず解決してコミット予定に加える必"
 "要があります。\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:164
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1260,7 +1360,7 @@
 "\n"
 "ファイル %s は本プログラムではコミットできません。\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:172
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1270,7 +1370,7 @@
 "\n"
 "最低一つの変更をコミット予定に加えてからコミットして下さい。\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:187
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1288,45 +1388,45 @@
 "- 第2行: 空白\n"
 "- 残りの行: なぜ、この変更が良い変更か、の説明。\n"
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:211
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr "警告: Tcl はエンコーディング '%s' をサポートしていません"
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:227
 msgid "Calling pre-commit hook..."
 msgstr "コミット前フックを実行中・・・"
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:242
 msgid "Commit declined by pre-commit hook."
 msgstr "コミット前フックがコミットを拒否しました"
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:265
 msgid "Calling commit-msg hook..."
 msgstr "コミット・メッセージ・フックを実行中・・・"
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:280
 msgid "Commit declined by commit-msg hook."
 msgstr "コミット・メッセージ・フックがコミットを拒否しました"
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:293
 msgid "Committing changes..."
 msgstr "変更点をコミット中・・・"
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:309
 msgid "write-tree failed:"
 msgstr "write-tree が失敗しました:"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:310 lib/commit.tcl:354 lib/commit.tcl:374
 msgid "Commit failed."
 msgstr "コミットに失敗しました。"
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:327
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "コミット %s は壊れています"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:332
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1340,19 +1440,19 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:339
 msgid "No changes to commit."
 msgstr "コミットする変更がありません。"
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:353
 msgid "commit-tree failed:"
 msgstr "commit-tree が失敗しました:"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:373
 msgid "update-ref failed:"
 msgstr "update-ref が失敗しました:"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:461
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "コミット %s を作成しました: %s"
@@ -1427,7 +1527,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Git から出た無効な日付: %s"
 
-#: lib/diff.tcl:44
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1449,48 +1549,102 @@
 "\n"
 "同様な状態のファイルを探すために、自動的に再スキャンを開始します。"
 
-#: lib/diff.tcl:83
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "%s の変更点をロード中…"
 
-#: lib/diff.tcl:116 lib/diff.tcl:190
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"LOCAL: 削除\n"
+"Remote:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"REMOTE: 削除\n"
+"LOCAL:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "LOCAL:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "REMOTE\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "%s を表示できません"
 
-#: lib/diff.tcl:117
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr "ファイルを読む際のエラーです:"
 
-#: lib/diff.tcl:124
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr "Git リポジトリ(サブプロジェクト)"
 
-#: lib/diff.tcl:136
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr "* バイナリファイル(内容は表示しません)"
 
-#: lib/diff.tcl:191
-msgid "Error loading diff:"
-msgstr "diff を読む際のエラーです:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* 管理外のファイルの大きさは %d バイトです。\n"
+"* 最初の %d バイトだけ表示しています。\n"
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"\n"
+"* %s は管理外のファイルをここで切りおとしました。\n"
+"* 全体を見るには外部エディタを使ってください。\n"
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr "選択されたパッチをコミット予定から外せません。"
 
-#: lib/diff.tcl:320
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr "選択されたパッチをコミット予定に加えられません。"
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:509
 msgid "Failed to unstage selected line."
 msgstr "選択されたパッチ行をコミット予定から外せません。"
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:517
 msgid "Failed to stage selected line."
 msgstr "選択されたパッチ行をコミット予定に加えられません。"
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "デフォールト"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "システム (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "その他"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "エラー"
@@ -1527,38 +1681,47 @@
 msgid "Unlock Index"
 msgstr "インデックスのロック解除"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "コミットから '%s' を降ろす"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr "コミット準備完了"
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr "コミットに %s を加えています"
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "ファイル %s にした変更を元に戻しますか?"
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "これら %i 個のファイルにした変更を元に戻しますか?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr "変更を元に戻すとコミット予定していない変更は全て失われます。"
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr "何もしない"
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "選択されたファイルにした変更を元に戻します"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "%s にした変更を元に戻します"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1585,7 +1748,7 @@
 "\n"
 "自動的に再スキャンを開始します。\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1602,7 +1765,7 @@
 "このファイルの衝突を解決し、コミット予定に加えて、コミットすることでマージを"
 "完了します。そうやって始めて、新たなマージを開始できるようになります。\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1619,34 +1782,34 @@
 "現在のコミットを完了してからマージを開始して下さい。そうする方がマージに失敗"
 "したときの回復が楽です。\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s の %s ブランチ"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr "%s と %s をマージ中・・・"
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr "マージが完了しました"
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "マージが失敗しました。衝突の解決が必要です。"
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "%s にマージ"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr "マージするリビジョン"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1656,7 +1819,7 @@
 "\n"
 "まず今のコミット訂正を完了させて下さい。\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1670,7 +1833,7 @@
 "\n"
 "マージを中断してよろしいですか?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1684,131 +1847,323 @@
 "\n"
 "リセットしてよろしいですか?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "中断しています"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "リセットしたファイル"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "中断に失敗しました。"
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "中断完了。"
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "共通の版を使いますか?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "自分の側の版を使いますか?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "相手制の版を使いますか?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"競合する変更点だけが表示されていることに注意してください。\n"
+"\n"
+"%s は上書きされます。\n"
+"\n"
+"やり直すにはマージ全体をやり直してください。"
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "ファイル %s には解決していない競合部分がまだあるようですが、いいですか?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "%s への解決をステージします"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr "ツールでは削除やリンク競合は扱えません"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "競合ファイルは存在しません。"
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "GUI マージツールではありません: %s"
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "マージツール '%s' はサポートしていません"
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "マージツールはすでに起動しています。終了しますか?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"版の取り出し時にエラーが出ました:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"マージツールが起動できません:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "マージツールを実行しています..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "マージツールが失敗しました。"
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "全体エンコーディングに 無効な %s が指定されています"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "リポジトリエンコーディングに 無効な %s が指定されています"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "既定値に戻す"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "保存"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr "%s リポジトリ"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "大域(全てのリポジトリ)"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "ユーザ名"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "電子メールアドレス"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr "マージコミットの要約"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr "マージの冗長度"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr "マージ後に diffstat を表示"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "マージツールを使用"
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "ファイル変更時刻を信頼する"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr "フェッチ中にトラッキングブランチを刈る"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "トラッキングブランチを合わせる"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr "変更されたファイルのみコピー検知を行なう"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr "コピーを検知する最少文字数"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "註釈する履歴半径(日数)"
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "diff の文脈行数"
 
-#: lib/option.tcl:129
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr "コミットメッセージのテキスト幅"
 
-#: lib/option.tcl:130
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "新しいブランチ名のテンプレート"
 
-#: lib/option.tcl:194
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "ファイル内容のデフォールトエンコーディング"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "変更"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "スペルチェック辞書"
 
-#: lib/option.tcl:218
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr "フォントを変更"
 
-#: lib/option.tcl:222
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "%s を選択"
 
-#: lib/option.tcl:228
+#: lib/option.tcl:264
 msgid "pt."
 msgstr "ポイント"
 
-#: lib/option.tcl:242
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "設定"
 
-#: lib/option.tcl:277
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "完全にオプションを保存できません:"
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "リモートを削除"
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr "から刈込む…"
 
-#: lib/remote.tcl:170
+#: lib/remote.tcl:173
 msgid "Fetch from"
 msgstr "取得元"
 
-#: lib/remote.tcl:213
+#: lib/remote.tcl:215
 msgid "Push to"
 msgstr "プッシュ先"
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "リモートを追加"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "リモートを新規に追加"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "追加"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "リモートの詳細"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "場所:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "その他の動作"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "即座に取得"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "リモートレポジトリを初期化してプッシュ"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "何もしない"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "リモート名を指定して下さい。"
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "'%s' はリモート名に使えません。"
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "場所 '%2$s' のリモート '%1$s'の名前変更に失敗しました。"
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "%s を取得"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "%s からフェッチしています"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "リポジトリ '%s' を初期化できません。"
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr "%s をプッシュ"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "%2$s にある %1$s をセットアップします"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "リモート・ブランチを削除"
+msgid "Delete Branch Remotely"
+msgstr "遠隔でブランチ削除"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
@@ -1819,8 +2174,8 @@
 msgstr "リモート:"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "任意の URL:"
+msgid "Arbitrary Location:"
+msgstr "任意の位置:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1890,6 +2245,22 @@
 msgid "Scanning %s..."
 msgstr "%s をスキャンしています…"
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "検索:"
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "次"
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "前"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "大文字小文字を区別"
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "ショートカットが書けません:"
@@ -1927,23 +2298,188 @@
 msgid "No Suggestions"
 msgstr "提案なし"
 
-#: lib/spellcheck.tcl:387
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "スペルチェッカーが予想外の EOF を返しました"
 
-#: lib/spellcheck.tcl:391
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "スペルチェック失敗"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "キーがありません。"
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "公開鍵がありました: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "鍵を生成"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "クリップボードにコピー"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "あなたの OpenSSH 公開鍵"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "生成中..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"ssh-keygen を起動できません:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "生成に失敗しました。"
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "生成には成功しましたが、鍵が見つかりません。"
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "あなたの鍵は %s にあります"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%1$s ... %4$*i %6$s 中の %2$*i (%7$3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
-msgstr "%s を取得"
+msgid "Running %s requires a selected file."
+msgstr "ファイルを選択してから %s を起動してください。"
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "本当に %s を起動しますか?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "ツール: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "実行中: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "ツールが完了しました: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "ツールが失敗しました: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "ツールの追加"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "新規ツールコマンドの追加"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "全体に追加"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "ツールの詳細"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "'/' でサブメニューを区切ります:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "コマンド:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "起動する前にダイアログを表示"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "ユーザにコミットを一つ選ばせる ($REVISION にセットします)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "ユーザに他の引数を追加させる ($ARGS にセットします)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "コマンドからの出力ウィンドウを見せない"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "パッチが選ばれているときだけ動かす($FILENAME が空でない)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "ツール名を指定して下さい。"
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "ツール '%s' は既に存在します。"
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"ツールを追加できません:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "ツールの削除"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "ツールコマンドの削除"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "削除"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(青色はローカルレポジトリのツールです)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "コマンドを起動: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "引数"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1960,17 +2496,17 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr "%s から削除されたトラッキング・ブランチを刈っています"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "%s をプッシュ"
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr "%s へ変更をプッシュしています"
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "%s へミラーしています"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "%3$s へ %1$s %2$s をプッシュしています"
diff --git a/git-gui/po/nb.po b/git-gui/po/nb.po
new file mode 100644
index 0000000..1c5137d
--- /dev/null
+++ b/git-gui/po/nb.po
@@ -0,0 +1,2484 @@
+# Norwegian (Bokmål) translation of git-gui.
+# Copyright (C) 2007-2008 Shawn Pearce, et al.
+# This file is distributed under the same license as the git-gui package.
+#
+# Fredrik Skolmli <fredrik@frsk.net>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: nb\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-11-16 13:56-0800\n"
+"PO-Revision-Date: 2008-12-03 16:05+0100\n"
+"Last-Translator: Fredrik Skolmli <fredrik@frsk.net>\n"
+"Language-Team: Norwegian Bokmål\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
+msgid "git-gui: fatal error"
+msgstr "git-gui: Kritisk feil"
+
+#: git-gui.sh:689
+#, tcl-format
+msgid "Invalid font specified in %s:"
+msgstr "Ugyldig font spesifisert i %s:"
+
+#: git-gui.sh:723
+msgid "Main Font"
+msgstr "Hovedskrifttype"
+
+#: git-gui.sh:724
+msgid "Diff/Console Font"
+msgstr "Diff-/Konsollskrifttype"
+
+#: git-gui.sh:738
+msgid "Cannot find git in PATH."
+msgstr "Kan ikke finne git i PATH"
+
+#: git-gui.sh:765
+msgid "Cannot parse Git version string:"
+msgstr "Kan ikke tyde Git's oppgitte versjon:"
+
+#: git-gui.sh:783
+#, tcl-format
+msgid ""
+"Git version cannot be determined.\n"
+"\n"
+"%s claims it is version '%s'.\n"
+"\n"
+"%s requires at least Git 1.5.0 or later.\n"
+"\n"
+"Assume '%s' is version 1.5.0?\n"
+msgstr ""
+"Kan ikke avgjøre hvilken Git-versjon du har.\n"
+"\n"
+"%s sier versjonen er '%s'.\n"
+"\n"
+"%s krever Git versjon 1.5.0 eller nyere.\n"
+"\n"
+"Anta at '%s' er versjon 1.5.0?\n"
+
+#: git-gui.sh:1062
+msgid "Git directory not found:"
+msgstr "Git-katalog ikke funnet:"
+
+#: git-gui.sh:1069
+msgid "Cannot move to top of working directory:"
+msgstr "Kan ikke gå til toppen av arbeidskatalogen:"
+
+#: git-gui.sh:1076
+msgid "Cannot use funny .git directory:"
+msgstr ""
+
+#: git-gui.sh:1081
+msgid "No working directory"
+msgstr "Ingen arbeidskatalog"
+
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
+msgid "Refreshing file status..."
+msgstr "Oppdaterer filstatus..."
+
+#: git-gui.sh:1303
+msgid "Scanning for modified files ..."
+msgstr "Søker etter endrede filer..."
+
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr ""
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr ""
+
+#: git-gui.sh:1542 lib/browser.tcl:246
+msgid "Ready."
+msgstr "Klar."
+
+#: git-gui.sh:1819
+msgid "Unmodified"
+msgstr "Uendret"
+
+#: git-gui.sh:1821
+msgid "Modified, not staged"
+msgstr "Endret, ikke køet"
+
+#: git-gui.sh:1822 git-gui.sh:1830
+msgid "Staged for commit"
+msgstr "Køet for innsjekking"
+
+#: git-gui.sh:1823 git-gui.sh:1831
+msgid "Portions staged for commit"
+msgstr "Delvis køet for innsjekking"
+
+#: git-gui.sh:1824 git-gui.sh:1832
+msgid "Staged for commit, missing"
+msgstr "Klar for innsjekking, fraværende"
+
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "Filtype endret, ikke køet"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "Filtype endret, køet"
+
+#: git-gui.sh:1829
+msgid "Untracked, not staged"
+msgstr "Usporet, ikke køet"
+
+#: git-gui.sh:1834
+msgid "Missing"
+msgstr "Fraværende"
+
+#: git-gui.sh:1835
+msgid "Staged for removal"
+msgstr "Køet for fjerning"
+
+#: git-gui.sh:1836
+msgid "Staged for removal, still present"
+msgstr "Køet for fjerning, fortsatt tilstede"
+
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
+msgid "Requires merge resolution"
+msgstr "Sammenslåingen krever konflikthåndtering"
+
+#: git-gui.sh:1878
+msgid "Starting gitk... please wait..."
+msgstr "Starter gitk... Vennligst vent..."
+
+#: git-gui.sh:1887
+msgid "Couldn't find gitk in PATH"
+msgstr "Kunne ikke finne gitk i PATH"
+
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
+msgid "Repository"
+msgstr "Arkiv"
+
+#: git-gui.sh:2281
+msgid "Edit"
+msgstr "Redigere"
+
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
+msgid "Branch"
+msgstr "Gren"
+
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
+msgid "Commit@@noun"
+msgstr "Innsjekking"
+
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
+msgid "Merge"
+msgstr "Sammenslåing"
+
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
+msgid "Remote"
+msgstr "Fjernarkiv"
+
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Verktøy"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Utforsk arbeidskopien"
+
+#: git-gui.sh:2307
+msgid "Browse Current Branch's Files"
+msgstr "Utforsk denne grens filer"
+
+#: git-gui.sh:2311
+msgid "Browse Branch Files..."
+msgstr "Bla igjennom filer på gren..."
+
+#: git-gui.sh:2316
+msgid "Visualize Current Branch's History"
+msgstr "Visualiser denne grens historikk"
+
+#: git-gui.sh:2320
+msgid "Visualize All Branch History"
+msgstr "Visualiser alle greners historikk"
+
+#: git-gui.sh:2327
+#, tcl-format
+msgid "Browse %s's Files"
+msgstr "Bla i filene til %s"
+
+#: git-gui.sh:2329
+#, tcl-format
+msgid "Visualize %s's History"
+msgstr "Visualiser historien til %s"
+
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
+msgid "Database Statistics"
+msgstr "Databasestatistikk"
+
+#: git-gui.sh:2337 lib/database.tcl:34
+msgid "Compress Database"
+msgstr "Kompress databasen"
+
+#: git-gui.sh:2340
+msgid "Verify Database"
+msgstr "Verifiser databasen"
+
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
+#: lib/shortcut.tcl:39 lib/shortcut.tcl:71
+msgid "Create Desktop Icon"
+msgstr "Lag skrivebordsikon"
+
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
+msgid "Quit"
+msgstr "Avslutt"
+
+#: git-gui.sh:2371
+msgid "Undo"
+msgstr "Angre"
+
+#: git-gui.sh:2374
+msgid "Redo"
+msgstr "Gjør om"
+
+#: git-gui.sh:2378 git-gui.sh:2923
+msgid "Cut"
+msgstr "Klipp ut"
+
+#: git-gui.sh:2381 git-gui.sh:2926 git-gui.sh:3000 git-gui.sh:3082
+#: lib/console.tcl:69
+msgid "Copy"
+msgstr "Kopier"
+
+#: git-gui.sh:2384 git-gui.sh:2929
+msgid "Paste"
+msgstr "Lim inn"
+
+#: git-gui.sh:2387 git-gui.sh:2932 lib/branch_delete.tcl:26
+#: lib/remote_branch_delete.tcl:38
+msgid "Delete"
+msgstr "Slett"
+
+#: git-gui.sh:2391 git-gui.sh:2936 git-gui.sh:3086 lib/console.tcl:71
+msgid "Select All"
+msgstr "Velg alle"
+
+#: git-gui.sh:2400
+msgid "Create..."
+msgstr "Opprett..."
+
+#: git-gui.sh:2406
+msgid "Checkout..."
+msgstr "Sjekk ut..."
+
+#: git-gui.sh:2412
+msgid "Rename..."
+msgstr "Endre navn..."
+
+#: git-gui.sh:2417
+msgid "Delete..."
+msgstr "Slett..."
+
+#: git-gui.sh:2422
+msgid "Reset..."
+msgstr "Tilbakestill..."
+
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "Ferdig"
+
+#: git-gui.sh:2434
+msgid "Commit@@verb"
+msgstr "Sjekk inn"
+
+#: git-gui.sh:2443 git-gui.sh:2864
+msgid "New Commit"
+msgstr "Ny innsjekking"
+
+#: git-gui.sh:2451 git-gui.sh:2871
+msgid "Amend Last Commit"
+msgstr "Legg til forrige innsjekking"
+
+#: git-gui.sh:2461 git-gui.sh:2825 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "Søk på ny"
+
+#: git-gui.sh:2467
+msgid "Stage To Commit"
+msgstr "Legg til i innsjekkingskøen"
+
+#: git-gui.sh:2473
+msgid "Stage Changed Files To Commit"
+msgstr "Legg til endrede filer i innsjekkingskøen"
+
+#: git-gui.sh:2479
+msgid "Unstage From Commit"
+msgstr "Fjern fra innsjekkingskøen"
+
+#: git-gui.sh:2484 lib/index.tcl:410
+msgid "Revert Changes"
+msgstr "Tilbakestill endringer"
+
+#: git-gui.sh:2491 git-gui.sh:3069
+msgid "Show Less Context"
+msgstr "Vis mindre innhold"
+
+#: git-gui.sh:2495 git-gui.sh:3073
+msgid "Show More Context"
+msgstr "Vis mer innhold"
+
+#: git-gui.sh:2502 git-gui.sh:2838 git-gui.sh:2947
+msgid "Sign Off"
+msgstr "Signér"
+
+#: git-gui.sh:2518
+msgid "Local Merge..."
+msgstr "Lokal sammenslåing..."
+
+#: git-gui.sh:2523
+msgid "Abort Merge..."
+msgstr "Avbryt sammenslåing..."
+
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "Legg til..."
+
+#: git-gui.sh:2539
+msgid "Push..."
+msgstr "Send..."
+
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "Fjern gren..."
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
+#, tcl-format
+msgid "About %s"
+msgstr "Om %s"
+
+#: git-gui.sh:2557
+msgid "Preferences..."
+msgstr "Innstillinger..."
+
+#: git-gui.sh:2565 git-gui.sh:3115
+msgid "Options..."
+msgstr "Alternativer..."
+
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Fjern..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
+msgid "Help"
+msgstr "Hjelp"
+
+#: git-gui.sh:2611
+msgid "Online Documentation"
+msgstr "Online dokumentasjon"
+
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "Vis SSH-nøkkel"
+
+#: git-gui.sh:2707
+#, tcl-format
+msgid "fatal: cannot stat path %s: No such file or directory"
+msgstr ""
+"kritisk: kunne ikke finne status for sti %s: Ingen slik fil eller katalog"
+
+#: git-gui.sh:2740
+msgid "Current Branch:"
+msgstr "Nåværende gren:"
+
+#: git-gui.sh:2761
+msgid "Staged Changes (Will Commit)"
+msgstr "Køede endringer (til innsjekking)"
+
+#: git-gui.sh:2781
+msgid "Unstaged Changes"
+msgstr "Ukøede endringer"
+
+#: git-gui.sh:2831
+msgid "Stage Changed"
+msgstr "Kø endret"
+
+#: git-gui.sh:2850 lib/transport.tcl:93 lib/transport.tcl:182
+msgid "Push"
+msgstr "Send"
+
+#: git-gui.sh:2885
+msgid "Initial Commit Message:"
+msgstr "Innledende innsjekkingsmelding:"
+
+#: git-gui.sh:2886
+msgid "Amended Commit Message:"
+msgstr "Utdypt innsjekkingsmelding"
+
+#: git-gui.sh:2887
+msgid "Amended Initial Commit Message:"
+msgstr "Utdypt innledende innsjekkingsmelding:"
+
+#: git-gui.sh:2888
+msgid "Amended Merge Commit Message:"
+msgstr "Utdypt innsjekkingsmelding for sammenslåing:"
+
+#: git-gui.sh:2889
+msgid "Merge Commit Message:"
+msgstr "Revisjonsmelding for sammenslåing:"
+
+#: git-gui.sh:2890
+msgid "Commit Message:"
+msgstr "Revisjonsmelding:"
+
+#: git-gui.sh:2939 git-gui.sh:3090 lib/console.tcl:73
+msgid "Copy All"
+msgstr "Kopier alle"
+
+#: git-gui.sh:2963 lib/blame.tcl:104
+msgid "File:"
+msgstr "Fil:"
+
+#: git-gui.sh:3078
+msgid "Refresh"
+msgstr "Oppdater"
+
+#: git-gui.sh:3099
+msgid "Decrease Font Size"
+msgstr "Gjør teksten mindre"
+
+#: git-gui.sh:3103
+msgid "Increase Font Size"
+msgstr "Gjør teksten større"
+
+#: git-gui.sh:3111 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Tekstkoding"
+
+#: git-gui.sh:3122
+msgid "Apply/Reverse Hunk"
+msgstr "Bruk/tilbakestill del"
+
+#: git-gui.sh:3127
+msgid "Apply/Reverse Line"
+msgstr "Bruk/tilbakestill linje"
+
+#: git-gui.sh:3137
+msgid "Run Merge Tool"
+msgstr "Start sammenslåingsprosess"
+
+#: git-gui.sh:3142
+msgid "Use Remote Version"
+msgstr "Bruk versjon fra fjernarkiv"
+
+#: git-gui.sh:3146
+msgid "Use Local Version"
+msgstr "Bruk lokal versjon"
+
+#: git-gui.sh:3150
+msgid "Revert To Base"
+msgstr "Tilbakestill til baseversjonen"
+
+#: git-gui.sh:3169
+msgid "Unstage Hunk From Commit"
+msgstr "Fjern delen fra innsjekkingskøen"
+
+#: git-gui.sh:3170
+msgid "Unstage Line From Commit"
+msgstr "Fjern linjen fra innsjekkingskøen"
+
+#: git-gui.sh:3172
+msgid "Stage Hunk For Commit"
+msgstr "Legg del i innsjekkingskøen"
+
+#: git-gui.sh:3173
+msgid "Stage Line For Commit"
+msgstr "Legg til linje i innsjekkingskøen"
+
+#: git-gui.sh:3196
+msgid "Initializing..."
+msgstr "Initsialiserer..."
+
+#: git-gui.sh:3301
+#, tcl-format
+msgid ""
+"Possible environment issues exist.\n"
+"\n"
+"The following environment variables are probably\n"
+"going to be ignored by any Git subprocess run\n"
+"by %s:\n"
+"\n"
+msgstr ""
+
+#: git-gui.sh:3331
+msgid ""
+"\n"
+"This is due to a known issue with the\n"
+"Tcl binary distributed by Cygwin."
+msgstr ""
+
+#: git-gui.sh:3336
+#, tcl-format
+msgid ""
+"\n"
+"\n"
+"A good replacement for %s\n"
+"is placing values for the user.name and\n"
+"user.email settings into your personal\n"
+"~/.gitconfig file.\n"
+msgstr ""
+
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui - Et grafisk brukergrensesnitt for Git."
+
+#: lib/blame.tcl:72
+msgid "File Viewer"
+msgstr "Filviser"
+
+#: lib/blame.tcl:78
+msgid "Commit:"
+msgstr "Innsjekking:"
+
+#: lib/blame.tcl:271
+msgid "Copy Commit"
+msgstr "Kopier innsjekking"
+
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Søk etter tekst..."
+
+#: lib/blame.tcl:284
+msgid "Do Full Copy Detection"
+msgstr "Gjennomfør full deteksjon av kopieringer"
+
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Vis historikkens innhold"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr ""
+
+#: lib/blame.tcl:450
+#, tcl-format
+msgid "Reading %s..."
+msgstr "Leser %s..."
+
+#: lib/blame.tcl:557
+msgid "Loading copy/move tracking annotations..."
+msgstr ""
+
+#: lib/blame.tcl:577
+msgid "lines annotated"
+msgstr ""
+
+#: lib/blame.tcl:769
+msgid "Loading original location annotations..."
+msgstr ""
+
+#: lib/blame.tcl:772
+msgid "Annotation complete."
+msgstr ""
+
+#: lib/blame.tcl:802
+msgid "Busy"
+msgstr "Opptatt"
+
+#: lib/blame.tcl:803
+msgid "Annotation process is already running."
+msgstr ""
+
+#: lib/blame.tcl:842
+msgid "Running thorough copy detection..."
+msgstr "Kjører kopidetektering..."
+
+#: lib/blame.tcl:910
+msgid "Loading annotation..."
+msgstr ""
+
+#: lib/blame.tcl:964
+msgid "Author:"
+msgstr "Forfatter:"
+
+#: lib/blame.tcl:968
+msgid "Committer:"
+msgstr "Innsjekker:"
+
+#: lib/blame.tcl:973
+msgid "Original File:"
+msgstr "Opprinnelig fil:"
+
+#: lib/blame.tcl:1021
+msgid "Cannot find HEAD commit:"
+msgstr "Finner ikke HEAD's innsjekking:"
+
+#: lib/blame.tcl:1076
+msgid "Cannot find parent commit:"
+msgstr "Kan ikke finne innsjekkingens forelder:"
+
+#: lib/blame.tcl:1091
+msgid "Unable to display parent"
+msgstr "Kan ikke vise forelder"
+
+#: lib/blame.tcl:1092 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "Feil ved innlasting av forskjell:"
+
+#: lib/blame.tcl:1232
+msgid "Originally By:"
+msgstr "Opprinnelig av:"
+
+#: lib/blame.tcl:1238
+msgid "In File:"
+msgstr "I fil:"
+
+#: lib/blame.tcl:1243
+msgid "Copied Or Moved Here By:"
+msgstr "Kopiert eller flyttet hit av:"
+
+#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
+msgid "Checkout Branch"
+msgstr "Sjekk ut gren"
+
+#: lib/branch_checkout.tcl:23
+msgid "Checkout"
+msgstr "Utsjekking"
+
+#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
+#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:97
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
+msgid "Revision"
+msgstr "Revisjon"
+
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
+msgid "Options"
+msgstr "Valg"
+
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
+msgid "Fetch Tracking Branch"
+msgstr "Hent sporet gren"
+
+#: lib/branch_checkout.tcl:44
+msgid "Detach From Local Branch"
+msgstr "Koble bort lokal gren"
+
+#: lib/branch_create.tcl:22
+msgid "Create Branch"
+msgstr "Opprett gren"
+
+#: lib/branch_create.tcl:27
+msgid "Create New Branch"
+msgstr "Opprett ny gren"
+
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
+msgid "Create"
+msgstr "Opprett"
+
+#: lib/branch_create.tcl:40
+msgid "Branch Name"
+msgstr "Navn på gren"
+
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
+msgid "Name:"
+msgstr "Navn:"
+
+#: lib/branch_create.tcl:58
+msgid "Match Tracking Branch Name"
+msgstr "Bruk navn på sporet gren"
+
+#: lib/branch_create.tcl:66
+msgid "Starting Revision"
+msgstr "Starter revisjon"
+
+#: lib/branch_create.tcl:72
+msgid "Update Existing Branch:"
+msgstr "Oppdater eksisterende gren:"
+
+#: lib/branch_create.tcl:75
+msgid "No"
+msgstr "Nei"
+
+#: lib/branch_create.tcl:80
+msgid "Fast Forward Only"
+msgstr "Kun hurtigfremspoling"
+
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:536
+msgid "Reset"
+msgstr "Tilbakestill"
+
+#: lib/branch_create.tcl:97
+msgid "Checkout After Creation"
+msgstr "Sjekk ut etter oppretting"
+
+#: lib/branch_create.tcl:131
+msgid "Please select a tracking branch."
+msgstr "Velg en gren som skal følges."
+
+#: lib/branch_create.tcl:140
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr "Den fulgte grenen %s er ikke en gren i fjernarkivet."
+
+#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
+msgid "Please supply a branch name."
+msgstr "Angi et navn for grenen."
+
+#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
+#, tcl-format
+msgid "'%s' is not an acceptable branch name."
+msgstr "'%s' kan ikke brukes som navn på en gren."
+
+#: lib/branch_delete.tcl:15
+msgid "Delete Branch"
+msgstr "Fjern gren"
+
+#: lib/branch_delete.tcl:20
+msgid "Delete Local Branch"
+msgstr "Fjern lokal gren"
+
+#: lib/branch_delete.tcl:37
+msgid "Local Branches"
+msgstr "Lokale grener"
+
+#: lib/branch_delete.tcl:52
+msgid "Delete Only If Merged Into"
+msgstr "Fjern kun ved sammenslåing"
+
+#: lib/branch_delete.tcl:54
+msgid "Always (Do not perform merge test.)"
+msgstr "Alltid (Ikke utfør sammenslåingstest.)"
+
+#: lib/branch_delete.tcl:103
+#, tcl-format
+msgid "The following branches are not completely merged into %s:"
+msgstr "Følgende grener er ikke fullstendig slått sammen med %s:"
+
+#: lib/branch_delete.tcl:115
+msgid ""
+"Recovering deleted branches is difficult. \n"
+"\n"
+" Delete the selected branches?"
+msgstr ""
+"Gjenoppretting av fjernede grener er vanskelig. \n"
+"\n"
+" Fjern valgte grener?"
+
+#: lib/branch_delete.tcl:141
+#, tcl-format
+msgid ""
+"Failed to delete branches:\n"
+"%s"
+msgstr ""
+"Kunne ikke fjerne grener:\n"
+"%s"
+
+#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
+msgid "Rename Branch"
+msgstr "Gi gren nytt navn"
+
+#: lib/branch_rename.tcl:26
+msgid "Rename"
+msgstr "Endre navn"
+
+#: lib/branch_rename.tcl:36
+msgid "Branch:"
+msgstr "Gren:"
+
+#: lib/branch_rename.tcl:39
+msgid "New Name:"
+msgstr "Nytt navn:"
+
+#: lib/branch_rename.tcl:75
+msgid "Please select a branch to rename."
+msgstr "Vennligst velg grenen du vil endre navn på."
+
+#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:201
+#, tcl-format
+msgid "Branch '%s' already exists."
+msgstr "Grenen '%s' eksisterer allerede."
+
+#: lib/branch_rename.tcl:117
+#, tcl-format
+msgid "Failed to rename '%s'."
+msgstr "Kunne ikke endre navnet '%s'."
+
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "Starter..."
+
+#: lib/browser.tcl:26
+msgid "File Browser"
+msgstr "Utforsker"
+
+#: lib/browser.tcl:126 lib/browser.tcl:143
+#, tcl-format
+msgid "Loading %s..."
+msgstr "Laster %s..."
+
+#: lib/browser.tcl:187
+msgid "[Up To Parent]"
+msgstr "[Opp til forelder]"
+
+#: lib/browser.tcl:267 lib/browser.tcl:273
+msgid "Browse Branch Files"
+msgstr "Bla igjennom grenens filer"
+
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
+msgid "Browse"
+msgstr "Bla igjennom"
+
+#: lib/checkout_op.tcl:84
+#, tcl-format
+msgid "Fetching %s from %s"
+msgstr "Henter %s fra %s"
+
+#: lib/checkout_op.tcl:132
+#, tcl-format
+msgid "fatal: Cannot resolve %s"
+msgstr "kritisk: Kan ikke åpne %s"
+
+#: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
+msgid "Close"
+msgstr "Lukk"
+
+#: lib/checkout_op.tcl:174
+#, tcl-format
+msgid "Branch '%s' does not exist."
+msgstr "Grenen '%s' eksisterer ikke."
+
+#: lib/checkout_op.tcl:193
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr "Kunne ikke konfigurere forenklet git-pull for '%s'."
+
+#: lib/checkout_op.tcl:228
+#, tcl-format
+msgid ""
+"Branch '%s' already exists.\n"
+"\n"
+"It cannot fast-forward to %s.\n"
+"A merge is required."
+msgstr ""
+"Grenen '%s' eksisterer allerede.\n"
+"\n"
+"Den kan ikke hurtigfremspoles til %s.\n"
+"En sammenslåing er påkrevd."
+
+#: lib/checkout_op.tcl:242
+#, tcl-format
+msgid "Merge strategy '%s' not supported."
+msgstr "Sammenslåingsstrategien '%s' er ikke støttet."
+
+#: lib/checkout_op.tcl:261
+#, tcl-format
+msgid "Failed to update '%s'."
+msgstr "Kunne ikke oppdatere '%s'."
+
+#: lib/checkout_op.tcl:273
+msgid "Staging area (index) is already locked."
+msgstr "Køområdet (index) er allerede låst."
+
+#: lib/checkout_op.tcl:288
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before the current branch can be changed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+
+#: lib/checkout_op.tcl:344
+#, tcl-format
+msgid "Updating working directory to '%s'..."
+msgstr "Oppdaterer arbeidskatalogen til '%s'..."
+
+#: lib/checkout_op.tcl:345
+msgid "files checked out"
+msgstr "filer sjekket ut"
+
+#: lib/checkout_op.tcl:375
+#, tcl-format
+msgid "Aborted checkout of '%s' (file level merging is required)."
+msgstr "Avbrøt utsjekkingen av '%s' (sammenslåing på filnivå kreves)."
+
+#: lib/checkout_op.tcl:376
+msgid "File level merge required."
+msgstr "Sammenslåing på filnivå kreves"
+
+#: lib/checkout_op.tcl:380
+#, tcl-format
+msgid "Staying on branch '%s'."
+msgstr "Blir stående på grenen '%s'."
+
+#: lib/checkout_op.tcl:451
+msgid ""
+"You are no longer on a local branch.\n"
+"\n"
+"If you wanted to be on a branch, create one now starting from 'This Detached "
+"Checkout'."
+msgstr ""
+
+#: lib/checkout_op.tcl:468 lib/checkout_op.tcl:472
+#, tcl-format
+msgid "Checked out '%s'."
+msgstr "Sjekket ut '%s'."
+
+#: lib/checkout_op.tcl:500
+#, tcl-format
+msgid "Resetting '%s' to '%s' will lose the following commits:"
+msgstr ""
+"Tilbakestilling av '%s' til '%s' vil medføre tap av følgende innsjekkinger:"
+
+#: lib/checkout_op.tcl:522
+msgid "Recovering lost commits may not be easy."
+msgstr ""
+"Det vil kanskje ikke være så enkelt å gjenopprette en tapt innsjekking."
+
+#: lib/checkout_op.tcl:527
+#, tcl-format
+msgid "Reset '%s'?"
+msgstr "Tilbakestill '%s'?"
+
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
+msgid "Visualize"
+msgstr "Visualiser"
+
+#: lib/checkout_op.tcl:600
+#, tcl-format
+msgid ""
+"Failed to set current branch.\n"
+"\n"
+"This working directory is only partially switched.  We successfully updated "
+"your files, but failed to update an internal Git file.\n"
+"\n"
+"This should not have occurred.  %s will now close and give up."
+msgstr ""
+
+#: lib/choose_font.tcl:39
+msgid "Select"
+msgstr "Velg"
+
+#: lib/choose_font.tcl:53
+msgid "Font Family"
+msgstr "Skrifttype-familie"
+
+#: lib/choose_font.tcl:74
+msgid "Font Size"
+msgstr "Skriftstørrelse"
+
+#: lib/choose_font.tcl:91
+msgid "Font Example"
+msgstr "Skrifteksempel"
+
+#: lib/choose_font.tcl:103
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Dette er en eksempeltekst.\n"
+"Hvis du liker hvordan teksten ser ut, kan du velge dette som din skrifttype."
+
+#: lib/choose_repository.tcl:28
+msgid "Git Gui"
+msgstr "Git Gui"
+
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
+msgid "Create New Repository"
+msgstr "Opprett nytt arkiv"
+
+#: lib/choose_repository.tcl:93
+msgid "New..."
+msgstr "Ny..."
+
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
+msgid "Clone Existing Repository"
+msgstr "Klon eksistererende arkiv"
+
+#: lib/choose_repository.tcl:106
+msgid "Clone..."
+msgstr "Klon..."
+
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
+msgid "Open Existing Repository"
+msgstr "Åpne eksistererende arkiv"
+
+#: lib/choose_repository.tcl:119
+msgid "Open..."
+msgstr "Åpne..."
+
+#: lib/choose_repository.tcl:132
+msgid "Recent Repositories"
+msgstr "Nylig brukte arkiv"
+
+#: lib/choose_repository.tcl:138
+msgid "Open Recent Repository:"
+msgstr "Åpne nylig brukt arkiv:"
+
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
+#, tcl-format
+msgid "Failed to create repository %s:"
+msgstr "Kunne ikke opprette arkivet %s:"
+
+#: lib/choose_repository.tcl:387
+msgid "Directory:"
+msgstr "Mappe:"
+
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
+msgid "Git Repository"
+msgstr "Git arkiv"
+
+#: lib/choose_repository.tcl:442
+#, tcl-format
+msgid "Directory %s already exists."
+msgstr "Mappen %s eksisterer allerede."
+
+#: lib/choose_repository.tcl:446
+#, tcl-format
+msgid "File %s already exists."
+msgstr "Filen %s eksisterer allerede."
+
+#: lib/choose_repository.tcl:460
+msgid "Clone"
+msgstr "Klon"
+
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Kildeplassering:"
+
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Destinasjonsmappe:"
+
+#: lib/choose_repository.tcl:496
+msgid "Clone Type:"
+msgstr "Klontype:"
+
+#: lib/choose_repository.tcl:502
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Standard (rask, delvis redundant, hardlinker)"
+
+#: lib/choose_repository.tcl:508
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Full kopi (tregere, redundant sikkerhetskopi)"
+
+#: lib/choose_repository.tcl:514
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Delt (raskest, ikke anbefalt, ingen sikkerhetskopiering)"
+
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Ikke et Git-arkiv: %s"
+
+#: lib/choose_repository.tcl:586
+msgid "Standard only available for local repository."
+msgstr "Standard er kun tilgjengelig for lokalt arkiv."
+
+#: lib/choose_repository.tcl:590
+msgid "Shared only available for local repository."
+msgstr "Delt er kun tilgjengelig for lokalt arkiv."
+
+#: lib/choose_repository.tcl:611
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Stedet %s eksisterer allerede."
+
+#: lib/choose_repository.tcl:622
+msgid "Failed to configure origin"
+msgstr "Kunne ikke konfigurere kildeoppføring"
+
+#: lib/choose_repository.tcl:634
+msgid "Counting objects"
+msgstr "Teller objekter"
+
+#: lib/choose_repository.tcl:635
+msgid "buckets"
+msgstr "bøtter"
+
+#: lib/choose_repository.tcl:659
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Kunne ikke kopiere objekter/informasjon/alternativt: %s"
+
+#: lib/choose_repository.tcl:695
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Ingenting å klone fra %s."
+
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
+msgid "The 'master' branch has not been initialized."
+msgstr "Grenen 'master' har ikke blitt initsialisert."
+
+#: lib/choose_repository.tcl:710
+msgid "Hardlinks are unavailable.  Falling back to copying."
+msgstr "Harde linker er utilgjengelig. Går tilbake til kopiering."
+
+#: lib/choose_repository.tcl:722
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "Kloner fra %s"
+
+#: lib/choose_repository.tcl:753
+msgid "Copying objects"
+msgstr "Kopierer objekter"
+
+#: lib/choose_repository.tcl:754
+msgid "KiB"
+msgstr "kB"
+
+#: lib/choose_repository.tcl:778
+#, tcl-format
+msgid "Unable to copy object: %s"
+msgstr "Kunne ikke kopiere objekt: %s"
+
+#: lib/choose_repository.tcl:788
+msgid "Linking objects"
+msgstr "Lenker objekter"
+
+#: lib/choose_repository.tcl:789
+msgid "objects"
+msgstr "objekter"
+
+#: lib/choose_repository.tcl:797
+#, tcl-format
+msgid "Unable to hardlink object: %s"
+msgstr "Kunne ikke opprette hardlink med objektet: %s"
+
+#: lib/choose_repository.tcl:852
+msgid "Cannot fetch branches and objects.  See console output for details."
+msgstr "Kunne ikke hente grener og objekter. Se utdata i konsoll for detaljer."
+
+#: lib/choose_repository.tcl:863
+msgid "Cannot fetch tags.  See console output for details."
+msgstr "Kunne ikke hente tagger. Se utdata i konsoll for detaljer."
+
+#: lib/choose_repository.tcl:887
+msgid "Cannot determine HEAD.  See console output for details."
+msgstr "Kan ikke bestemme HEAD. Se utdata i konsoll for detaljer."
+
+#: lib/choose_repository.tcl:896
+#, tcl-format
+msgid "Unable to cleanup %s"
+msgstr "Kunne ikke rydde opp %s"
+
+#: lib/choose_repository.tcl:902
+msgid "Clone failed."
+msgstr "Kloning feilet."
+
+#: lib/choose_repository.tcl:909
+msgid "No default branch obtained."
+msgstr "Ingen standardgren hentet."
+
+#: lib/choose_repository.tcl:920
+#, tcl-format
+msgid "Cannot resolve %s as a commit."
+msgstr "Kan ikke finne %s som en innsjekking."
+
+#: lib/choose_repository.tcl:932
+msgid "Creating working directory"
+msgstr "Oppretter arbeidskatalog"
+
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
+msgid "files"
+msgstr "filer"
+
+#: lib/choose_repository.tcl:962
+msgid "Initial file checkout failed."
+msgstr "Initsialiserende utsjekking feilet."
+
+#: lib/choose_repository.tcl:978
+msgid "Open"
+msgstr "Åpne"
+
+#: lib/choose_repository.tcl:988
+msgid "Repository:"
+msgstr "Arkiv:"
+
+#: lib/choose_repository.tcl:1037
+#, tcl-format
+msgid "Failed to open repository %s:"
+msgstr "Kunne ikke åpne arkivet %s:"
+
+#: lib/choose_rev.tcl:53
+msgid "This Detached Checkout"
+msgstr "Denne frakoblede utsjekkingen"
+
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Revisjonsuttrykk:"
+
+#: lib/choose_rev.tcl:74
+msgid "Local Branch"
+msgstr "Lokal gren"
+
+#: lib/choose_rev.tcl:79
+msgid "Tracking Branch"
+msgstr "Sporet gren"
+
+#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
+msgid "Tag"
+msgstr "Tag"
+
+#: lib/choose_rev.tcl:317
+#, tcl-format
+msgid "Invalid revision: %s"
+msgstr "Ugyldig revisjon: %s"
+
+#: lib/choose_rev.tcl:338
+msgid "No revision selected."
+msgstr "Ingen revisjoner valgt."
+
+#: lib/choose_rev.tcl:346
+msgid "Revision expression is empty."
+msgstr "Revisjonsuttrykk er tomt."
+
+#: lib/choose_rev.tcl:531
+msgid "Updated"
+msgstr "Oppdatert"
+
+#: lib/choose_rev.tcl:559
+msgid "URL"
+msgstr "URL"
+
+#: lib/commit.tcl:9
+msgid ""
+"There is nothing to amend.\n"
+"\n"
+"You are about to create the initial commit.  There is no commit before this "
+"to amend.\n"
+msgstr ""
+"Det er ingenting å legge til.\n"
+"\n"
+"Du er i ferd med å lage den initsialiserende revisjonen. Det er ingen "
+"tidligere revisjoner å tilføye.\n"
+
+#: lib/commit.tcl:18
+msgid ""
+"Cannot amend while merging.\n"
+"\n"
+"You are currently in the middle of a merge that has not been fully "
+"completed.  You cannot amend the prior commit unless you first abort the "
+"current merge activity.\n"
+msgstr ""
+"Kan ikke tilføye under sammenslåing.\n"
+"\n"
+"Du er for øyeblikket under en pågående sammenslåing som ikke er fullført. Du "
+"kan ikke tilføye en tidligere revisjon med mindre du først avbryter denne "
+"sammenslåingen.\n"
+
+#: lib/commit.tcl:49
+msgid "Error loading commit data for amend:"
+msgstr "Feil ved innhenting av revisjonsdata for tilføying:"
+
+#: lib/commit.tcl:76
+msgid "Unable to obtain your identity:"
+msgstr "Kunne ikke avgjøre din identitet:"
+
+#: lib/commit.tcl:81
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "Ugyldig GIT_COMMITTER_IDENT:"
+
+#: lib/commit.tcl:133
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before another commit can be created.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+
+#: lib/commit.tcl:156
+#, tcl-format
+msgid ""
+"Unmerged files cannot be committed.\n"
+"\n"
+"File %s has merge conflicts.  You must resolve them and stage the file "
+"before committing.\n"
+msgstr ""
+
+#: lib/commit.tcl:164
+#, tcl-format
+msgid ""
+"Unknown file state %s detected.\n"
+"\n"
+"File %s cannot be committed by this program.\n"
+msgstr ""
+"Ukjent filstatus %s er funnet.\n"
+"\n"
+"Filen %s kan ikke sjekkes inn av dette programmet.\n"
+
+#: lib/commit.tcl:172
+msgid ""
+"No changes to commit.\n"
+"\n"
+"You must stage at least 1 file before you can commit.\n"
+msgstr ""
+"Ingen endringer å sjekke inn.\n"
+"\n"
+"Du må køe minst en fil før du kan sjekke inn noe.\n"
+
+#: lib/commit.tcl:187
+msgid ""
+"Please supply a commit message.\n"
+"\n"
+"A good commit message has the following format:\n"
+"\n"
+"- First line: Describe in one sentence what you did.\n"
+"- Second line: Blank\n"
+"- Remaining lines: Describe why this change is good.\n"
+msgstr ""
+"Vennligst angi en revisjonsmelding.\n"
+"\n"
+"En god melding har følgende format:\n"
+"\n"
+"- Første linje: En beskrivelse av hva du har gjort i én setning.\n"
+"- Andre linje: Blank\n"
+"- Resterende linjer: Forklar hvorfor denne endringen er bra.\n"
+
+#: lib/commit.tcl:211
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "advarsel: Tcl støtter ikke denne tegnkodingen '%s'."
+
+#: lib/commit.tcl:227
+msgid "Calling pre-commit hook..."
+msgstr ""
+
+#: lib/commit.tcl:242
+msgid "Commit declined by pre-commit hook."
+msgstr ""
+
+#: lib/commit.tcl:265
+msgid "Calling commit-msg hook..."
+msgstr ""
+
+#: lib/commit.tcl:280
+msgid "Commit declined by commit-msg hook."
+msgstr ""
+
+#: lib/commit.tcl:293
+msgid "Committing changes..."
+msgstr "Sjekker inn endringer..."
+
+#: lib/commit.tcl:309
+msgid "write-tree failed:"
+msgstr "Skriving til tre feilet:"
+
+#: lib/commit.tcl:310 lib/commit.tcl:354 lib/commit.tcl:374
+msgid "Commit failed."
+msgstr "Innsjekking feilet."
+
+#: lib/commit.tcl:327
+#, tcl-format
+msgid "Commit %s appears to be corrupt"
+msgstr "Revisjon %s ser ut til å være korrupt"
+
+#: lib/commit.tcl:332
+msgid ""
+"No changes to commit.\n"
+"\n"
+"No files were modified by this commit and it was not a merge commit.\n"
+"\n"
+"A rescan will be automatically started now.\n"
+msgstr ""
+"Ingen endringer til innsjekking.\n"
+"\n"
+"Ingen filer ble endret av denne revisjonen, og det var ikke en revisjon fra "
+"en sammenslåing.\n"
+"\n"
+"Et nytt søk vil bli startet automatisk.\n"
+
+#: lib/commit.tcl:339
+msgid "No changes to commit."
+msgstr "Ingen endringer til innsekking."
+
+#: lib/commit.tcl:353
+msgid "commit-tree failed:"
+msgstr "commit-tree feilet:"
+
+#: lib/commit.tcl:373
+msgid "update-ref failed:"
+msgstr "update-ref feilet:"
+
+#: lib/commit.tcl:461
+#, tcl-format
+msgid "Created commit %s: %s"
+msgstr "Opprettet innsjekking %s: %s"
+
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "Jobber... Vennligst vent..."
+
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "Suksess"
+
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Feil: Kommandoen feilet"
+
+#: lib/database.tcl:43
+msgid "Number of loose objects"
+msgstr "Antall løse objekter"
+
+#: lib/database.tcl:44
+msgid "Disk space used by loose objects"
+msgstr "Diskplass brukt av løse objekter"
+
+#: lib/database.tcl:45
+msgid "Number of packed objects"
+msgstr "Antall pakkede objekter"
+
+#: lib/database.tcl:46
+msgid "Number of packs"
+msgstr "Antall pakker"
+
+#: lib/database.tcl:47
+msgid "Disk space used by packed objects"
+msgstr "Diskplass brukt av pakkede objekter"
+
+#: lib/database.tcl:48
+msgid "Packed objects waiting for pruning"
+msgstr "Pakkede objekter som avventer fjerning"
+
+#: lib/database.tcl:49
+msgid "Garbage files"
+msgstr "Avfallsfiler"
+
+#: lib/database.tcl:72
+msgid "Compressing the object database"
+msgstr "Komprimerer objektdatabasen"
+
+#: lib/database.tcl:83
+msgid "Verifying the object database with fsck-objects"
+msgstr "Verifiserer objektdatabasen med fsck-objects"
+
+#: lib/database.tcl:108
+#, tcl-format
+msgid ""
+"This repository currently has approximately %i loose objects.\n"
+"\n"
+"To maintain optimal performance it is strongly recommended that you compress "
+"the database when more than %i loose objects exist.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"Dette arkivet inneholder omtrent %i 'løse' objekter.\n"
+"\n"
+"For å sikre en optimal ytelse er det sterkt anbefalt at du komprimerer "
+"databasen når det er flere enn %i 'løse' objekter i den.\n"
+"\n"
+"Komprimere databasen nå?"
+
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Ugyldig dato fra Git: %s"
+
+#: lib/diff.tcl:59
+#, tcl-format
+msgid ""
+"No differences detected.\n"
+"\n"
+"%s has no changes.\n"
+"\n"
+"The modification date of this file was updated by another application, but "
+"the content within the file was not changed.\n"
+"\n"
+"A rescan will be automatically started to find other files which may have "
+"the same state."
+msgstr ""
+"Ingen forandringer funnet.\n"
+"\n"
+"%s har ingen endringer.\n"
+"\n"
+"Tidsstempelet for endring på denne filen ble oppdatert av en annen "
+" applikasjon, men innholdet er uendret.\n"
+"\n"
+"En gjennomsøking vil nå starte automatisk for å se om andre filer har "
+"status."
+
+#: lib/diff.tcl:99
+#, tcl-format
+msgid "Loading diff of %s..."
+msgstr "Laster inn forskjellene av %s..."
+
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr "LOKAL: slettet\n"
+"FJERN:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr "FJERN: slettet\n"
+"LOKAL:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "LOKAL:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "FJERN:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
+#, tcl-format
+msgid "Unable to display %s"
+msgstr "Kan ikke vise %s"
+
+#: lib/diff.tcl:198
+msgid "Error loading file:"
+msgstr "Feil ved lesing av fil: %s"
+
+#: lib/diff.tcl:205
+msgid "Git Repository (subproject)"
+msgstr "Git-arkiv (underprosjekt)"
+
+#: lib/diff.tcl:217
+msgid "* Binary file (not showing content)."
+msgstr "* Binærfil (viser ikke innhold)"
+
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Usporet fil er %d bytes.\n"
+"* Viser bare %d første bytes.\n"
+
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Usporede filer klippet her av %s.\n"
+"* For å se hele filen, bruk et eksternt redigeringsverktøy.\n"
+
+#: lib/diff.tcl:436
+msgid "Failed to unstage selected hunk."
+msgstr "Kunne ikke fjerne den valgte delen fra innsjekkingskøen."
+
+#: lib/diff.tcl:443
+msgid "Failed to stage selected hunk."
+msgstr "Kunne ikke legge til den valgte delen i innsjekkingskøen."
+
+#: lib/diff.tcl:509
+msgid "Failed to unstage selected line."
+msgstr "Kunne ikke fjerne den valgte linjen fra innsjekkingskøen."
+
+#: lib/diff.tcl:517
+msgid "Failed to stage selected line."
+msgstr "Kunne ikke legge til den valgte linjen i innsjekkingskøen."
+
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Standard"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Systemets (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Andre"
+
+#: lib/error.tcl:20 lib/error.tcl:114
+msgid "error"
+msgstr "feil"
+
+#: lib/error.tcl:36
+msgid "warning"
+msgstr "advarsel"
+
+#: lib/error.tcl:94
+msgid "You must correct the above errors before committing."
+msgstr "Du må rette de ovenstående feilene før innsjekking."
+
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Kunne ikke låse opp indexen."
+
+#: lib/index.tcl:15
+msgid "Index Error"
+msgstr "Feil på index"
+
+#: lib/index.tcl:21
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr ""
+"Oppdatering av Git's index mislyktes. Et nytt søk vil bli startet for å "
+"resynkronisere git-gui."
+
+#: lib/index.tcl:27
+msgid "Continue"
+msgstr "Fortsett"
+
+#: lib/index.tcl:31
+msgid "Unlock Index"
+msgstr "Lås opp index"
+
+#: lib/index.tcl:287
+#, tcl-format
+msgid "Unstaging %s from commit"
+msgstr "Fjerner %s fra innsjekkingskøen"
+
+#: lib/index.tcl:326
+msgid "Ready to commit."
+msgstr "Klar til innsjekking."
+
+#: lib/index.tcl:339
+#, tcl-format
+msgid "Adding %s"
+msgstr "Legger til %s"
+
+#: lib/index.tcl:396
+#, tcl-format
+msgid "Revert changes in file %s?"
+msgstr "Reverter endringene i filen %s?"
+
+#: lib/index.tcl:398
+#, tcl-format
+msgid "Revert changes in these %i files?"
+msgstr "Reverter endringene i disse %i filene?"
+
+#: lib/index.tcl:406
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr "Endringer som ikke ligger i innsjekkingskøen vil bli tapt av denne "
+"reverteringen"
+
+#: lib/index.tcl:409
+msgid "Do Nothing"
+msgstr "Ikke gjør noe"
+
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "Reverterer valgte filer"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Reverterer %s"
+
+#: lib/merge.tcl:13
+msgid ""
+"Cannot merge while amending.\n"
+"\n"
+"You must finish amending this commit before starting any type of merge.\n"
+msgstr ""
+"Kunne ikke slå sammen under utvidelse.\n"
+"\n"
+"Du må først fullføre utvidelsen av denne revisjonen før du kan starte en "
+"sammenslåing.\n"
+
+#: lib/merge.tcl:27
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before a merge can be performed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+
+#: lib/merge.tcl:45
+#, tcl-format
+msgid ""
+"You are in the middle of a conflicted merge.\n"
+"\n"
+"File %s has merge conflicts.\n"
+"\n"
+"You must resolve them, stage the file, and commit to complete the current "
+"merge.  Only then can you begin another merge.\n"
+msgstr ""
+
+#: lib/merge.tcl:55
+#, tcl-format
+msgid ""
+"You are in the middle of a change.\n"
+"\n"
+"File %s is modified.\n"
+"\n"
+"You should complete the current commit before starting a merge.  Doing so "
+"will help you abort a failed merge, should the need arise.\n"
+msgstr ""
+
+#: lib/merge.tcl:107
+#, tcl-format
+msgid "%s of %s"
+msgstr "%s av %s"
+
+#: lib/merge.tcl:120
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "Slår sammen %s og %s"
+
+#: lib/merge.tcl:131
+msgid "Merge completed successfully."
+msgstr "Vellykket sammenslåing fullført."
+
+#: lib/merge.tcl:133
+msgid "Merge failed.  Conflict resolution is required."
+msgstr "Sammenslåing feilet. Håndtering av konflikten kreves."
+
+#: lib/merge.tcl:158
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Slå sammen inn i %s"
+
+#: lib/merge.tcl:177
+msgid "Revision To Merge"
+msgstr "Revisjon til sammenslåing"
+
+#: lib/merge.tcl:212
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Kan ikke avbryte under utvidelse av revisjon.\n"
+"\n"
+"Du må fullføre utvidelsen av denne revisjonen.\n"
+
+#: lib/merge.tcl:222
+msgid ""
+"Abort merge?\n"
+"\n"
+"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with aborting the current merge?"
+msgstr ""
+"Avbryt sammenslåing?\n"
+"\n"
+"Avbryting av pågående sammenslåing vil føre til at *alle* endringer som ikke "
+" er sjekket inn, vil gå tapt.\n"
+"\n"
+"Fortsette med å avbryte den pågående sammenslåingen?"
+
+#: lib/merge.tcl:228
+msgid ""
+"Reset changes?\n"
+"\n"
+"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with resetting the current changes?"
+msgstr ""
+"Nullstill endringer?\n"
+"\n"
+"Nullstilling av endringer vil føre til at *alle* endringer som ikke er "
+"sjekket inn går tapt.\n"
+"\n"
+"Fortsette med nullstilling av endringer?"
+
+#: lib/merge.tcl:239
+msgid "Aborting"
+msgstr "Avbryter"
+
+#: lib/merge.tcl:239
+msgid "files reset"
+msgstr "filer tilbakestilt"
+
+#: lib/merge.tcl:267
+msgid "Abort failed."
+msgstr "Avbryting feilet."
+
+#: lib/merge.tcl:269
+msgid "Abort completed.  Ready."
+msgstr "Avbryting fullført. Klar."
+
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Tving håndtering til opprinnelig versjon?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Tving håndtering i denne grenen?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Tving håndtering i den andre grenen?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Merk deg at endringsvisningen kun viser motstridende endringer.\n"
+"\n"
+"%s vil bli overskrevet.\n"
+"\n"
+"Denne operasjonen kan kun bli angret ved å starte sammenslåingen på ny."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Filen %s ser ut til å ha uløste konflikter, skal filen likevel køes?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Legger til løsninge på konflikt for %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Konfliktfil eksisterer ikke"
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr ""
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr ""
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr ""
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Kunne ikke hente versjoner:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr ""
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr ""
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr ""
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr ""
+
+#: lib/option.tcl:117
+msgid "Restore Defaults"
+msgstr "Gjennopprett standardverdier"
+
+#: lib/option.tcl:121
+msgid "Save"
+msgstr "Lagre"
+
+#: lib/option.tcl:131
+#, tcl-format
+msgid "%s Repository"
+msgstr "%s arkiv"
+
+#: lib/option.tcl:132
+msgid "Global (All Repositories)"
+msgstr "Globalt (alle arkiv)"
+
+#: lib/option.tcl:138
+msgid "User Name"
+msgstr "Navn"
+
+#: lib/option.tcl:139
+msgid "Email Address"
+msgstr "Epost-adresse"
+
+#: lib/option.tcl:141
+msgid "Summarize Merge Commits"
+msgstr "Oppsummer innsjekkinger fra sammenslåinger"
+
+#: lib/option.tcl:142
+msgid "Merge Verbosity"
+msgstr "Detaljenivå på sammenslåing"
+
+#: lib/option.tcl:143
+msgid "Show Diffstat After Merge"
+msgstr "Vis endringsstatistikk etter sammenslåing"
+
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Bruk sammenslåingsverktøy"
+
+#: lib/option.tcl:146
+msgid "Trust File Modification Timestamps"
+msgstr "Stol på filers tid for endring"
+
+#: lib/option.tcl:147
+msgid "Prune Tracking Branches During Fetch"
+msgstr ""
+
+#: lib/option.tcl:148
+msgid "Match Tracking Branches"
+msgstr ""
+
+#: lib/option.tcl:149
+msgid "Blame Copy Only On Changed Files"
+msgstr ""
+
+#: lib/option.tcl:150
+msgid "Minimum Letters To Blame Copy On"
+msgstr ""
+
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr ""
+
+#: lib/option.tcl:152
+msgid "Number of Diff Context Lines"
+msgstr "Antall linjer sammenhengende endringer"
+
+#: lib/option.tcl:153
+msgid "Commit Message Text Width"
+msgstr "Tekstbredde for vindu til innsjekkingsmeldinger"
+
+#: lib/option.tcl:154
+msgid "New Branch Name Template"
+msgstr "Mal for navn på nye grener"
+
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Standard tekstenkoding for innhold i filer"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Endre"
+
+#: lib/option.tcl:230
+msgid "Spelling Dictionary:"
+msgstr "Stavebokordlister:"
+
+#: lib/option.tcl:254
+msgid "Change Font"
+msgstr "Endre skrifttype"
+
+#: lib/option.tcl:258
+#, tcl-format
+msgid "Choose %s"
+msgstr "Velg %s"
+
+#: lib/option.tcl:264
+msgid "pt."
+msgstr "pt."
+
+#: lib/option.tcl:278
+msgid "Preferences"
+msgstr "Egenskaper"
+
+#: lib/option.tcl:314
+msgid "Failed to completely save options:"
+msgstr "Kunne ikke lagre alternativ:"
+
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Fjern fjernarkiv"
+
+#: lib/remote.tcl:168
+msgid "Prune from"
+msgstr "Fjern fra"
+
+#: lib/remote.tcl:173
+msgid "Fetch from"
+msgstr "Hent fra"
+
+#: lib/remote.tcl:215
+msgid "Push to"
+msgstr "Send til"
+
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Legg til fjernarkiv"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Legg til nytt fjernarkiv"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "Legg til"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Detaljer for fjernarkiv"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Lokasjon:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Videre handling"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Hent umiddelbart"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Initsialiser og send til fjernarkiv"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Ikke gjør mer nå"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Vennligst angi et navn for fjernarkivet."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "'%s' er ikke et tillatt navn for et fjernarkiv."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Kunne ikke legge til fjernarkivet '%s' på '%s'."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "hent %s"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Henter %s"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Vet ikke hvordan arkiv på '%s' skal opprettes."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr "send %s"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Initsialiserer %s (på %s)"
+
+#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+msgid "Delete Branch Remotely"
+msgstr "Fjern gren fra fjernarkiv"
+
+#: lib/remote_branch_delete.tcl:47
+msgid "From Repository"
+msgstr "Fra arkiv"
+
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+msgid "Remote:"
+msgstr "Fjernarkiv:"
+
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
+msgid "Arbitrary Location:"
+msgstr "Vilkårlig lokasjon:"
+
+#: lib/remote_branch_delete.tcl:84
+msgid "Branches"
+msgstr "Grener"
+
+#: lib/remote_branch_delete.tcl:109
+msgid "Delete Only If"
+msgstr "Slett kun hvis"
+
+#: lib/remote_branch_delete.tcl:111
+msgid "Merged Into:"
+msgstr "Slått sammen i:"
+
+#: lib/remote_branch_delete.tcl:119
+msgid "Always (Do not perform merge checks)"
+msgstr "Alltid (Ikke utfør sammenslåingskontroll)"
+
+#: lib/remote_branch_delete.tcl:152
+msgid "A branch is required for 'Merged Into'."
+msgstr "En gren kreves for 'sammenslåing i'."
+
+#: lib/remote_branch_delete.tcl:184
+#, tcl-format
+msgid ""
+"The following branches are not completely merged into %s:\n"
+"\n"
+" - %s"
+msgstr ""
+"Følgende grener er ikke fullestendig sammenslått med %s:\n"
+"\n"
+" - %s"
+
+#: lib/remote_branch_delete.tcl:189
+#, tcl-format
+msgid ""
+"One or more of the merge tests failed because you have not fetched the "
+"necessary commits.  Try fetching from %s first."
+msgstr ""
+"En eller flere av testene som blir kjørt under sammenslåing feilet fordi du"
+"ikke har hentet inn de nødvendige innsjekkingene. Prøv å hent disse fra %s"
+"først"
+
+#: lib/remote_branch_delete.tcl:207
+msgid "Please select one or more branches to delete."
+msgstr "Velg en eller flere grener som skal fjernes."
+
+#: lib/remote_branch_delete.tcl:216
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Gjenoppretting av fjernede grener er vanskelig.\n"
+"\n"
+"Fjern den merkede grenen?"
+
+#: lib/remote_branch_delete.tcl:226
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "Fjerner grenene fra %s"
+
+#: lib/remote_branch_delete.tcl:286
+msgid "No repository selected."
+msgstr "Ingen arkiv valgt."
+
+#: lib/remote_branch_delete.tcl:291
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "Søker %s..."
+
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Finn:"
+
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Neste"
+
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Forrige"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Skiller på store og små bokstaver"
+
+#: lib/shortcut.tcl:20 lib/shortcut.tcl:61
+msgid "Cannot write shortcut:"
+msgstr "Kan ikke opprette snarvei:"
+
+#: lib/shortcut.tcl:136
+msgid "Cannot write icon:"
+msgstr "Kan ikke opprette ikon:"
+
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Stavekontrolleren er ikke støttet"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Stavekontroll er ikke tilgjengelig"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Ugyldig stavekontroll-konfigurasjon"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Reverterer ordbok til %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Stavekontrollen feilet stille under oppstart"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Stavekontrolleren er ukjent"
+
+#: lib/spellcheck.tcl:186
+msgid "No Suggestions"
+msgstr "Ingen forslag"
+
+#: lib/spellcheck.tcl:388
+msgid "Unexpected EOF from spell checker"
+msgstr "Uventet slutt på filen fra stavekontrollen"
+
+#: lib/spellcheck.tcl:392
+msgid "Spell Checker Failed"
+msgstr "Stavekontroll mislyktes"
+
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Ingen nøkler funnet."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Funnet en offentlig nøkkel i: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Generer nøkkel"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Kopier til utklippstavlen"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Din offentlige OpenSSH-nøkkel"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Genererer..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Kunne ikke starte ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "Generering feilet."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "Generering vellykket, men ingen nøkler er funnet."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Nøkkelen din ligger i: %s"
+
+#: lib/status_bar.tcl:83
+#, tcl-format
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s ... %*i av %*i %s (%3i%%)"
+
+#: lib/tools.tcl:75
+#, tcl-format
+msgid "Running %s requires a selected file."
+msgstr "Å kjøre %s krever at en fil er valgt"
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Er du sikker på at du vil kjøre %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Verktøy: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Kjører: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "Verktøyet ble fullført med suksess: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Verktøy feilet: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Legg til verktøy"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Legg til ny verktøykommando"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Legg til globalt"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Verktøydetaljer"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Bruk '/'-separator for å lage undermenyer:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Kommando:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Vis en dialog før start"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Spør brukeren om å velge en revisjon (setter $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Spør brukeren for ytterligere paramtere (setter $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Ikke vis kommandoens utdata i vinduet"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Kjør kun om forskjellene er markert ($FILENAME er ikke tom)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Vennligst angi et navn for dette verktøyet."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "Verktøyet '%s' eksisterer allerede."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Kunne ikke legge til verktøyet:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Fjern verktøyet"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Fjern verktøyskommandoen"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Fjern"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Blue angir lokale verktøy til arkivet)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Kjør kommando: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Argumenter"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
+
+#: lib/transport.tcl:7
+#, tcl-format
+msgid "Fetching new changes from %s"
+msgstr "Henter nye endringer fra %s"
+
+#: lib/transport.tcl:18
+#, tcl-format
+msgid "remote prune %s"
+msgstr "slett fjernarkiv %s"
+
+#: lib/transport.tcl:19
+#, tcl-format
+msgid "Pruning tracking branches deleted from %s"
+msgstr "Fjrner sporing av grener slettet fra %s"
+
+#: lib/transport.tcl:26
+#, tcl-format
+msgid "Pushing changes to %s"
+msgstr "Sender endringer til %s"
+
+#: lib/transport.tcl:72
+#, tcl-format
+msgid "Pushing %s %s to %s"
+msgstr "Sender %s %s til %s"
+
+#: lib/transport.tcl:89
+msgid "Push Branches"
+msgstr "Send grener"
+
+#: lib/transport.tcl:103
+msgid "Source Branches"
+msgstr "Kildegrener"
+
+#: lib/transport.tcl:120
+msgid "Destination Repository"
+msgstr "Destinasjonsarkiv"
+
+#: lib/transport.tcl:158
+msgid "Transfer Options"
+msgstr "Overføringsalternativer"
+
+#: lib/transport.tcl:160
+msgid "Force overwrite existing branch (may discard changes)"
+msgstr "Tving overskrivning av eksisterende gren (kan forkaste endringer)"
+
+#: lib/transport.tcl:164
+msgid "Use thin pack (for slow network connections)"
+msgstr "Bruk tynne pakker (for tregere nettverkstilkoblinger)"
+
+#: lib/transport.tcl:168
+msgid "Include tags"
+msgstr "Inkluder tagger"
diff --git a/git-gui/po/sv.po b/git-gui/po/sv.po
index 0196ba8..167654c 100644
--- a/git-gui/po/sv.po
+++ b/git-gui/po/sv.po
@@ -1,48 +1,48 @@
 # Swedish translation of git-gui.
-# Copyright (C) 2007 Shawn Pearce, et al.
+# Copyright (C) 2007-2008 Shawn Pearce, et al.
 # This file is distributed under the same license as the git-gui package.
 #
-# Peter Karlsson <peter@softwolves.pp.se>, 2007-2008.
+# Peter Krefting <peter@softwolves.pp.se>, 2007-2008.
 # Mikael Magnusson <mikachu@gmail.com>, 2008.
 msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-03 01:34+0200\n"
-"PO-Revision-Date: 2008-08-03 01:45+0200\n"
-"Last-Translator: Mikael Magnusson <mikachu@gmail.com>\n"
+"POT-Creation-Date: 2008-12-08 08:31-0800\n"
+"PO-Revision-Date: 2008-12-10 09:49+0100\n"
+"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: git-gui.sh:41 git-gui.sh:688 git-gui.sh:702 git-gui.sh:715 git-gui.sh:798
-#: git-gui.sh:817
+#: git-gui.sh:41 git-gui.sh:737 git-gui.sh:751 git-gui.sh:764 git-gui.sh:847
+#: git-gui.sh:866
 msgid "git-gui: fatal error"
 msgstr "git-gui: ödesdigert fel"
 
-#: git-gui.sh:644
+#: git-gui.sh:689
 #, tcl-format
 msgid "Invalid font specified in %s:"
 msgstr "Ogiltigt teckensnitt angivet i %s:"
 
-#: git-gui.sh:674
+#: git-gui.sh:723
 msgid "Main Font"
 msgstr "Huvudteckensnitt"
 
-#: git-gui.sh:675
+#: git-gui.sh:724
 msgid "Diff/Console Font"
 msgstr "Diff/konsolteckensnitt"
 
-#: git-gui.sh:689
+#: git-gui.sh:738
 msgid "Cannot find git in PATH."
 msgstr "Hittar inte git i PATH."
 
-#: git-gui.sh:716
+#: git-gui.sh:765
 msgid "Cannot parse Git version string:"
 msgstr "Kan inte tolka versionssträng från Git:"
 
-#: git-gui.sh:734
+#: git-gui.sh:783
 #, tcl-format
 msgid ""
 "Git version cannot be determined.\n"
@@ -61,380 +61,449 @@
 "\n"
 "Anta att \"%s\" är version 1.5.0?\n"
 
-#: git-gui.sh:972
+#: git-gui.sh:1062
 msgid "Git directory not found:"
 msgstr "Git-katalogen hittades inte:"
 
-#: git-gui.sh:979
+#: git-gui.sh:1069
 msgid "Cannot move to top of working directory:"
 msgstr "Kan inte gå till början på arbetskatalogen:"
 
-#: git-gui.sh:986
+#: git-gui.sh:1076
 msgid "Cannot use funny .git directory:"
 msgstr "Kan inte använda underlig .git-katalog:"
 
-#: git-gui.sh:991
+#: git-gui.sh:1081
 msgid "No working directory"
 msgstr "Ingen arbetskatalog"
 
-#: git-gui.sh:1138 lib/checkout_op.tcl:305
+#: git-gui.sh:1247 lib/checkout_op.tcl:305
 msgid "Refreshing file status..."
 msgstr "Uppdaterar filstatus..."
 
-#: git-gui.sh:1194
+#: git-gui.sh:1303
 msgid "Scanning for modified files ..."
 msgstr "Söker efter ändrade filer..."
 
-#: git-gui.sh:1369 lib/browser.tcl:246
+#: git-gui.sh:1367
+msgid "Calling prepare-commit-msg hook..."
+msgstr ""
+"Anropar kroken för förberedelse av incheckningsmeddelande (prepare-commit-"
+"msg)..."
+
+#: git-gui.sh:1384
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr ""
+"Incheckningen avvisades av kroken för förberedelse av incheckningsmeddelande "
+"(prepare-commit-msg)."
+
+#: git-gui.sh:1542 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Klar."
 
-#: git-gui.sh:1635
+#: git-gui.sh:1819
 msgid "Unmodified"
 msgstr "Oförändrade"
 
-#: git-gui.sh:1637
+#: git-gui.sh:1821
 msgid "Modified, not staged"
 msgstr "Förändrade, ej köade"
 
-#: git-gui.sh:1638 git-gui.sh:1643
+#: git-gui.sh:1822 git-gui.sh:1830
 msgid "Staged for commit"
 msgstr "Köade för incheckning"
 
-#: git-gui.sh:1639 git-gui.sh:1644
+#: git-gui.sh:1823 git-gui.sh:1831
 msgid "Portions staged for commit"
 msgstr "Delar köade för incheckning"
 
-#: git-gui.sh:1640 git-gui.sh:1645
+#: git-gui.sh:1824 git-gui.sh:1832
 msgid "Staged for commit, missing"
 msgstr "Köade för incheckning, saknade"
 
-#: git-gui.sh:1642
+#: git-gui.sh:1826
+msgid "File type changed, not staged"
+msgstr "Filtyp ändrad, ej köade"
+
+#: git-gui.sh:1827
+msgid "File type changed, staged"
+msgstr "Filtyp ändrad, köade"
+
+#: git-gui.sh:1829
 msgid "Untracked, not staged"
 msgstr "Ej spårade, ej köade"
 
-#: git-gui.sh:1647
+#: git-gui.sh:1834
 msgid "Missing"
 msgstr "Saknade"
 
-#: git-gui.sh:1648
+#: git-gui.sh:1835
 msgid "Staged for removal"
 msgstr "Köade för borttagning"
 
-#: git-gui.sh:1649
+#: git-gui.sh:1836
 msgid "Staged for removal, still present"
 msgstr "Köade för borttagning, fortfarande närvarande"
 
-#: git-gui.sh:1651 git-gui.sh:1652 git-gui.sh:1653 git-gui.sh:1654
+#: git-gui.sh:1838 git-gui.sh:1839 git-gui.sh:1840 git-gui.sh:1841
+#: git-gui.sh:1842 git-gui.sh:1843
 msgid "Requires merge resolution"
 msgstr "Kräver konflikthantering efter sammanslagning"
 
-#: git-gui.sh:1689
+#: git-gui.sh:1878
 msgid "Starting gitk... please wait..."
 msgstr "Startar gitk... vänta..."
 
-#: git-gui.sh:1698
+#: git-gui.sh:1887
 msgid "Couldn't find gitk in PATH"
 msgstr "Hittar inte gitk i PATH."
 
-#: git-gui.sh:1948 lib/choose_repository.tcl:36
+#: git-gui.sh:2280 lib/choose_repository.tcl:36
 msgid "Repository"
 msgstr "Arkiv"
 
-#: git-gui.sh:1949
+#: git-gui.sh:2281
 msgid "Edit"
 msgstr "Redigera"
 
-#: git-gui.sh:1951 lib/choose_rev.tcl:561
+#: git-gui.sh:2283 lib/choose_rev.tcl:561
 msgid "Branch"
 msgstr "Gren"
 
-#: git-gui.sh:1954 lib/choose_rev.tcl:548
+#: git-gui.sh:2286 lib/choose_rev.tcl:548
 msgid "Commit@@noun"
 msgstr "Incheckning"
 
-#: git-gui.sh:1957 lib/merge.tcl:120 lib/merge.tcl:149 lib/merge.tcl:167
+#: git-gui.sh:2289 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
 msgid "Merge"
 msgstr "Slå ihop"
 
-#: git-gui.sh:1958 lib/choose_rev.tcl:557
+#: git-gui.sh:2290 lib/choose_rev.tcl:557
 msgid "Remote"
-msgstr "Fjärr"
+msgstr "Fjärrarkiv"
 
-#: git-gui.sh:1967
+#: git-gui.sh:2293
+msgid "Tools"
+msgstr "Verktyg"
+
+#: git-gui.sh:2302
+msgid "Explore Working Copy"
+msgstr "Utforska arbetskopia"
+
+#: git-gui.sh:2307
 msgid "Browse Current Branch's Files"
 msgstr "Bläddra i grenens filer"
 
-#: git-gui.sh:1971
+#: git-gui.sh:2311
 msgid "Browse Branch Files..."
 msgstr "Bläddra filer på gren..."
 
-#: git-gui.sh:1976
+#: git-gui.sh:2316
 msgid "Visualize Current Branch's History"
 msgstr "Visualisera grenens historik"
 
-#: git-gui.sh:1980
+#: git-gui.sh:2320
 msgid "Visualize All Branch History"
 msgstr "Visualisera alla grenars historik"
 
-#: git-gui.sh:1987
+#: git-gui.sh:2327
 #, tcl-format
 msgid "Browse %s's Files"
 msgstr "Bläddra i filer för %s"
 
-#: git-gui.sh:1989
+#: git-gui.sh:2329
 #, tcl-format
 msgid "Visualize %s's History"
 msgstr "Visualisera historik för %s"
 
-#: git-gui.sh:1994 lib/database.tcl:27 lib/database.tcl:67
+#: git-gui.sh:2334 lib/database.tcl:27 lib/database.tcl:67
 msgid "Database Statistics"
 msgstr "Databasstatistik"
 
-#: git-gui.sh:1997 lib/database.tcl:34
+#: git-gui.sh:2337 lib/database.tcl:34
 msgid "Compress Database"
 msgstr "Komprimera databas"
 
-#: git-gui.sh:2000
+#: git-gui.sh:2340
 msgid "Verify Database"
 msgstr "Verifiera databas"
 
-#: git-gui.sh:2007 git-gui.sh:2011 git-gui.sh:2015 lib/shortcut.tcl:7
+#: git-gui.sh:2347 git-gui.sh:2351 git-gui.sh:2355 lib/shortcut.tcl:7
 #: lib/shortcut.tcl:39 lib/shortcut.tcl:71
 msgid "Create Desktop Icon"
 msgstr "Skapa skrivbordsikon"
 
-#: git-gui.sh:2023 lib/choose_repository.tcl:177 lib/choose_repository.tcl:185
+#: git-gui.sh:2363 lib/choose_repository.tcl:183 lib/choose_repository.tcl:191
 msgid "Quit"
 msgstr "Avsluta"
 
-#: git-gui.sh:2031
+#: git-gui.sh:2371
 msgid "Undo"
 msgstr "Ångra"
 
-#: git-gui.sh:2034
+#: git-gui.sh:2374
 msgid "Redo"
 msgstr "Gör om"
 
-#: git-gui.sh:2038 git-gui.sh:2545
+#: git-gui.sh:2378 git-gui.sh:2937
 msgid "Cut"
 msgstr "Klipp ut"
 
-#: git-gui.sh:2041 git-gui.sh:2548 git-gui.sh:2622 git-gui.sh:2715
+#: git-gui.sh:2381 git-gui.sh:2940 git-gui.sh:3014 git-gui.sh:3096
 #: lib/console.tcl:69
 msgid "Copy"
 msgstr "Kopiera"
 
-#: git-gui.sh:2044 git-gui.sh:2551
+#: git-gui.sh:2384 git-gui.sh:2943
 msgid "Paste"
 msgstr "Klistra in"
 
-#: git-gui.sh:2047 git-gui.sh:2554 lib/branch_delete.tcl:26
+#: git-gui.sh:2387 git-gui.sh:2946 lib/branch_delete.tcl:26
 #: lib/remote_branch_delete.tcl:38
 msgid "Delete"
 msgstr "Ta bort"
 
-#: git-gui.sh:2051 git-gui.sh:2558 git-gui.sh:2719 lib/console.tcl:71
+#: git-gui.sh:2391 git-gui.sh:2950 git-gui.sh:3100 lib/console.tcl:71
 msgid "Select All"
 msgstr "Markera alla"
 
-#: git-gui.sh:2060
+#: git-gui.sh:2400
 msgid "Create..."
 msgstr "Skapa..."
 
-#: git-gui.sh:2066
+#: git-gui.sh:2406
 msgid "Checkout..."
 msgstr "Checka ut..."
 
-#: git-gui.sh:2072
+#: git-gui.sh:2412
 msgid "Rename..."
 msgstr "Byt namn..."
 
-#: git-gui.sh:2077 git-gui.sh:2187
+#: git-gui.sh:2417
 msgid "Delete..."
 msgstr "Ta bort..."
 
-#: git-gui.sh:2082
+#: git-gui.sh:2422
 msgid "Reset..."
 msgstr "Återställ..."
 
-#: git-gui.sh:2094 git-gui.sh:2491
-msgid "New Commit"
-msgstr "Ny incheckning"
+#: git-gui.sh:2432
+msgid "Done"
+msgstr "Färdig"
 
-#: git-gui.sh:2102 git-gui.sh:2498
-msgid "Amend Last Commit"
-msgstr "Lägg till föregående incheckning"
-
-#: git-gui.sh:2111 git-gui.sh:2458 lib/remote_branch_delete.tcl:99
-msgid "Rescan"
-msgstr "Sök på nytt"
-
-#: git-gui.sh:2117
-msgid "Stage To Commit"
-msgstr "Köa för incheckning"
-
-#: git-gui.sh:2123
-msgid "Stage Changed Files To Commit"
-msgstr "Köa ändrade filer för incheckning"
-
-#: git-gui.sh:2129
-msgid "Unstage From Commit"
-msgstr "Ta bort från incheckningskö"
-
-#: git-gui.sh:2134 lib/index.tcl:395
-msgid "Revert Changes"
-msgstr "Återställ ändringar"
-
-#: git-gui.sh:2141 git-gui.sh:2702
-msgid "Show Less Context"
-msgstr "Visa mindre sammanhang"
-
-#: git-gui.sh:2145 git-gui.sh:2706
-msgid "Show More Context"
-msgstr "Visa mer sammanhang"
-
-#: git-gui.sh:2151 git-gui.sh:2470 git-gui.sh:2569
-msgid "Sign Off"
-msgstr "Skriv under"
-
-#: git-gui.sh:2155 git-gui.sh:2474
+#: git-gui.sh:2434
 msgid "Commit@@verb"
 msgstr "Checka in"
 
-#: git-gui.sh:2166
+#: git-gui.sh:2443 git-gui.sh:2878
+msgid "New Commit"
+msgstr "Ny incheckning"
+
+#: git-gui.sh:2451 git-gui.sh:2885
+msgid "Amend Last Commit"
+msgstr "Lägg till föregående incheckning"
+
+#: git-gui.sh:2461 git-gui.sh:2839 lib/remote_branch_delete.tcl:99
+msgid "Rescan"
+msgstr "Sök på nytt"
+
+#: git-gui.sh:2467
+msgid "Stage To Commit"
+msgstr "Köa för incheckning"
+
+#: git-gui.sh:2473
+msgid "Stage Changed Files To Commit"
+msgstr "Köa ändrade filer för incheckning"
+
+#: git-gui.sh:2479
+msgid "Unstage From Commit"
+msgstr "Ta bort från incheckningskö"
+
+#: git-gui.sh:2484 lib/index.tcl:410
+msgid "Revert Changes"
+msgstr "Återställ ändringar"
+
+#: git-gui.sh:2491 git-gui.sh:3083
+msgid "Show Less Context"
+msgstr "Visa mindre sammanhang"
+
+#: git-gui.sh:2495 git-gui.sh:3087
+msgid "Show More Context"
+msgstr "Visa mer sammanhang"
+
+#: git-gui.sh:2502 git-gui.sh:2852 git-gui.sh:2961
+msgid "Sign Off"
+msgstr "Skriv under"
+
+#: git-gui.sh:2518
 msgid "Local Merge..."
 msgstr "Lokal sammanslagning..."
 
-#: git-gui.sh:2171
+#: git-gui.sh:2523
 msgid "Abort Merge..."
 msgstr "Avbryt sammanslagning..."
 
-#: git-gui.sh:2183
+#: git-gui.sh:2535 git-gui.sh:2575
+msgid "Add..."
+msgstr "Lägg till..."
+
+#: git-gui.sh:2539
 msgid "Push..."
 msgstr "Sänd..."
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
-#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
+#: git-gui.sh:2543
+msgid "Delete Branch..."
+msgstr "Ta bort gren..."
+
+#: git-gui.sh:2553 git-gui.sh:2589 lib/about.tcl:14
+#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:53
 #, tcl-format
 msgid "About %s"
 msgstr "Om %s"
 
-#: git-gui.sh:2201
+#: git-gui.sh:2557
 msgid "Preferences..."
 msgstr "Inställningar..."
 
-#: git-gui.sh:2209 git-gui.sh:2740
+#: git-gui.sh:2565 git-gui.sh:3129
 msgid "Options..."
 msgstr "Alternativ..."
 
-#: git-gui.sh:2215 lib/choose_repository.tcl:47
+#: git-gui.sh:2576
+msgid "Remove..."
+msgstr "Ta bort..."
+
+#: git-gui.sh:2585 lib/choose_repository.tcl:50
 msgid "Help"
 msgstr "Hjälp"
 
-#: git-gui.sh:2256
+#: git-gui.sh:2611
 msgid "Online Documentation"
 msgstr "Webbdokumentation"
 
-#: git-gui.sh:2340
+#: git-gui.sh:2614 lib/choose_repository.tcl:47 lib/choose_repository.tcl:56
+msgid "Show SSH Key"
+msgstr "Visa SSH-nyckel"
+
+#: git-gui.sh:2721
 #, tcl-format
 msgid "fatal: cannot stat path %s: No such file or directory"
 msgstr ""
 "ödesdigert: kunde inte ta status på sökvägen %s: Fil eller katalog saknas"
 
-#: git-gui.sh:2373
+#: git-gui.sh:2754
 msgid "Current Branch:"
 msgstr "Aktuell gren:"
 
-#: git-gui.sh:2394
+#: git-gui.sh:2775
 msgid "Staged Changes (Will Commit)"
 msgstr "Köade ändringar (kommer att checkas in)"
 
-#: git-gui.sh:2414
+#: git-gui.sh:2795
 msgid "Unstaged Changes"
 msgstr "Oköade ändringar"
 
-#: git-gui.sh:2464
+#: git-gui.sh:2845
 msgid "Stage Changed"
 msgstr "Köa ändrade"
 
-#: git-gui.sh:2480 lib/transport.tcl:93 lib/transport.tcl:182
+#: git-gui.sh:2864 lib/transport.tcl:104 lib/transport.tcl:193
 msgid "Push"
 msgstr "Sänd"
 
-#: git-gui.sh:2510
+#: git-gui.sh:2899
 msgid "Initial Commit Message:"
 msgstr "Inledande incheckningsmeddelande:"
 
-#: git-gui.sh:2511
+#: git-gui.sh:2900
 msgid "Amended Commit Message:"
 msgstr "Utökat incheckningsmeddelande:"
 
-#: git-gui.sh:2512
+#: git-gui.sh:2901
 msgid "Amended Initial Commit Message:"
 msgstr "Utökat inledande incheckningsmeddelande:"
 
-#: git-gui.sh:2513
+#: git-gui.sh:2902
 msgid "Amended Merge Commit Message:"
 msgstr "Utökat incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:2514
+#: git-gui.sh:2903
 msgid "Merge Commit Message:"
 msgstr "Incheckningsmeddelande för sammanslagning:"
 
-#: git-gui.sh:2515
+#: git-gui.sh:2904
 msgid "Commit Message:"
 msgstr "Incheckningsmeddelande:"
 
-#: git-gui.sh:2561 git-gui.sh:2723 lib/console.tcl:73
+#: git-gui.sh:2953 git-gui.sh:3104 lib/console.tcl:73
 msgid "Copy All"
 msgstr "Kopiera alla"
 
-#: git-gui.sh:2585 lib/blame.tcl:100
+#: git-gui.sh:2977 lib/blame.tcl:104
 msgid "File:"
 msgstr "Fil:"
 
-#: git-gui.sh:2691
-msgid "Apply/Reverse Hunk"
-msgstr "Använd/återställ del"
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr "Använd/återställ rad"
-
-#: git-gui.sh:2711
+#: git-gui.sh:3092
 msgid "Refresh"
 msgstr "Uppdatera"
 
-#: git-gui.sh:2732
+#: git-gui.sh:3113
 msgid "Decrease Font Size"
 msgstr "Minska teckensnittsstorlek"
 
-#: git-gui.sh:2736
+#: git-gui.sh:3117
 msgid "Increase Font Size"
 msgstr "Öka teckensnittsstorlek"
 
-#: git-gui.sh:2747
+#: git-gui.sh:3125 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Teckenkodning"
+
+#: git-gui.sh:3136
+msgid "Apply/Reverse Hunk"
+msgstr "Använd/återställ del"
+
+#: git-gui.sh:3141
+msgid "Apply/Reverse Line"
+msgstr "Använd/återställ rad"
+
+#: git-gui.sh:3151
+msgid "Run Merge Tool"
+msgstr "Starta verktyg för sammanslagning"
+
+#: git-gui.sh:3156
+msgid "Use Remote Version"
+msgstr "Använd versionen från fjärrarkivet"
+
+#: git-gui.sh:3160
+msgid "Use Local Version"
+msgstr "Använd lokala versionen"
+
+#: git-gui.sh:3164
+msgid "Revert To Base"
+msgstr "Återställ till basversionen"
+
+#: git-gui.sh:3183
 msgid "Unstage Hunk From Commit"
 msgstr "Ta bort del ur incheckningskö"
 
-#: git-gui.sh:2748
+#: git-gui.sh:3184
 msgid "Unstage Line From Commit"
 msgstr "Ta bort rad ur incheckningskö"
 
-#: git-gui.sh:2750
+#: git-gui.sh:3186
 msgid "Stage Hunk For Commit"
 msgstr "Ställ del i incheckningskö"
 
-#: git-gui.sh:2751
+#: git-gui.sh:3187
 msgid "Stage Line For Commit"
 msgstr "Ställ rad i incheckningskö"
 
-#: git-gui.sh:2771
+#: git-gui.sh:3210
 msgid "Initializing..."
 msgstr "Initierar..."
 
-#: git-gui.sh:2876
+#: git-gui.sh:3315
 #, tcl-format
 msgid ""
 "Possible environment issues exist.\n"
@@ -451,7 +520,7 @@
 "av %s:\n"
 "\n"
 
-#: git-gui.sh:2906
+#: git-gui.sh:3345
 msgid ""
 "\n"
 "This is due to a known issue with the\n"
@@ -461,7 +530,7 @@
 "Detta beror på ett känt problem med\n"
 "Tcl-binären som följer med Cygwin."
 
-#: git-gui.sh:2911
+#: git-gui.sh:3350
 #, tcl-format
 msgid ""
 "\n"
@@ -482,80 +551,108 @@
 msgid "git-gui - a graphical user interface for Git."
 msgstr "git-gui - ett grafiskt användargränssnitt för Git."
 
-#: lib/blame.tcl:70
+#: lib/blame.tcl:72
 msgid "File Viewer"
 msgstr "Filvisare"
 
-#: lib/blame.tcl:74
+#: lib/blame.tcl:78
 msgid "Commit:"
 msgstr "Incheckning:"
 
-#: lib/blame.tcl:257
+#: lib/blame.tcl:271
 msgid "Copy Commit"
 msgstr "Kopiera incheckning"
 
-#: lib/blame.tcl:260
+#: lib/blame.tcl:275
+msgid "Find Text..."
+msgstr "Sök text..."
+
+#: lib/blame.tcl:284
 msgid "Do Full Copy Detection"
 msgstr "Gör full kopieringsigenkänning"
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:288
+msgid "Show History Context"
+msgstr "Visa historiksammanhang"
+
+#: lib/blame.tcl:291
+msgid "Blame Parent Commit"
+msgstr "Klandra föräldraincheckning"
+
+#: lib/blame.tcl:450
 #, tcl-format
 msgid "Reading %s..."
 msgstr "Läser %s..."
 
-#: lib/blame.tcl:492
+#: lib/blame.tcl:557
 msgid "Loading copy/move tracking annotations..."
 msgstr "Läser annoteringar för kopiering/flyttning..."
 
-#: lib/blame.tcl:512
+#: lib/blame.tcl:577
 msgid "lines annotated"
 msgstr "rader annoterade"
 
-#: lib/blame.tcl:704
+#: lib/blame.tcl:769
 msgid "Loading original location annotations..."
 msgstr "Läser in annotering av originalplacering..."
 
-#: lib/blame.tcl:707
+#: lib/blame.tcl:772
 msgid "Annotation complete."
 msgstr "Annotering fullbordad."
 
-#: lib/blame.tcl:737
+#: lib/blame.tcl:802
 msgid "Busy"
 msgstr "Upptagen"
 
-#: lib/blame.tcl:738
+#: lib/blame.tcl:803
 msgid "Annotation process is already running."
 msgstr "Annoteringsprocess körs redan."
 
-#: lib/blame.tcl:777
+#: lib/blame.tcl:842
 msgid "Running thorough copy detection..."
 msgstr "Kör grundlig kopieringsigenkänning..."
 
-#: lib/blame.tcl:827
+#: lib/blame.tcl:910
 msgid "Loading annotation..."
 msgstr "Läser in annotering..."
 
-#: lib/blame.tcl:883
+#: lib/blame.tcl:963
 msgid "Author:"
 msgstr "Författare:"
 
-#: lib/blame.tcl:887
+#: lib/blame.tcl:967
 msgid "Committer:"
 msgstr "Incheckare:"
 
-#: lib/blame.tcl:892
+#: lib/blame.tcl:972
 msgid "Original File:"
 msgstr "Ursprunglig fil:"
 
-#: lib/blame.tcl:1006
+#: lib/blame.tcl:1020
+msgid "Cannot find HEAD commit:"
+msgstr "Hittar inte incheckning för HEAD:"
+
+#: lib/blame.tcl:1075
+msgid "Cannot find parent commit:"
+msgstr "Hittar inte föräldraincheckning:"
+
+#: lib/blame.tcl:1090
+msgid "Unable to display parent"
+msgstr "Kan inte visa förälder"
+
+#: lib/blame.tcl:1091 lib/diff.tcl:297
+msgid "Error loading diff:"
+msgstr "Fel vid inläsning av differens:"
+
+#: lib/blame.tcl:1231
 msgid "Originally By:"
 msgstr "Ursprungligen av:"
 
-#: lib/blame.tcl:1012
+#: lib/blame.tcl:1237
 msgid "In File:"
 msgstr "I filen:"
 
-#: lib/blame.tcl:1017
+#: lib/blame.tcl:1242
 msgid "Copied Or Moved Here By:"
 msgstr "Kopierad eller flyttad hit av:"
 
@@ -569,16 +666,18 @@
 
 #: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35
 #: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:282
-#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:171
-#: lib/option.tcl:103 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97
+#: lib/checkout_op.tcl:544 lib/choose_font.tcl:43 lib/merge.tcl:172
+#: lib/option.tcl:125 lib/remote_add.tcl:32 lib/remote_branch_delete.tcl:42
+#: lib/tools_dlg.tcl:40 lib/tools_dlg.tcl:204 lib/tools_dlg.tcl:352
+#: lib/transport.tcl:108
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: lib/branch_checkout.tcl:32 lib/browser.tcl:287
+#: lib/branch_checkout.tcl:32 lib/browser.tcl:287 lib/tools_dlg.tcl:328
 msgid "Revision"
 msgstr "Revision"
 
-#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:244
+#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:280
 msgid "Options"
 msgstr "Alternativ"
 
@@ -598,7 +697,7 @@
 msgid "Create New Branch"
 msgstr "Skapa ny gren"
 
-#: lib/branch_create.tcl:31 lib/choose_repository.tcl:371
+#: lib/branch_create.tcl:31 lib/choose_repository.tcl:377
 msgid "Create"
 msgstr "Skapa"
 
@@ -606,7 +705,7 @@
 msgid "Branch Name"
 msgstr "Namn på gren"
 
-#: lib/branch_create.tcl:43
+#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
 msgid "Name:"
 msgstr "Namn:"
 
@@ -751,9 +850,9 @@
 msgid "Browse Branch Files"
 msgstr "Bläddra filer på grenen"
 
-#: lib/browser.tcl:278 lib/choose_repository.tcl:387
-#: lib/choose_repository.tcl:472 lib/choose_repository.tcl:482
-#: lib/choose_repository.tcl:985
+#: lib/browser.tcl:278 lib/choose_repository.tcl:394
+#: lib/choose_repository.tcl:480 lib/choose_repository.tcl:491
+#: lib/choose_repository.tcl:995
 msgid "Browse"
 msgstr "Bläddra"
 
@@ -768,6 +867,7 @@
 msgstr "ödesdigert: Kunde inte slå upp %s"
 
 #: lib/checkout_op.tcl:145 lib/console.tcl:81 lib/database.tcl:31
+#: lib/sshkey.tcl:53
 msgid "Close"
 msgstr "Stäng"
 
@@ -879,7 +979,7 @@
 msgid "Reset '%s'?"
 msgstr "Återställa \"%s\"?"
 
-#: lib/checkout_op.tcl:532 lib/merge.tcl:163
+#: lib/checkout_op.tcl:532 lib/merge.tcl:164 lib/tools_dlg.tcl:343
 msgid "Visualize"
 msgstr "Visualisera"
 
@@ -928,221 +1028,225 @@
 msgid "Git Gui"
 msgstr "Git Gui"
 
-#: lib/choose_repository.tcl:81 lib/choose_repository.tcl:376
+#: lib/choose_repository.tcl:87 lib/choose_repository.tcl:382
 msgid "Create New Repository"
 msgstr "Skapa nytt arkiv"
 
-#: lib/choose_repository.tcl:87
+#: lib/choose_repository.tcl:93
 msgid "New..."
 msgstr "Nytt..."
 
-#: lib/choose_repository.tcl:94 lib/choose_repository.tcl:458
+#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:465
 msgid "Clone Existing Repository"
 msgstr "Klona befintligt arkiv"
 
-#: lib/choose_repository.tcl:100
+#: lib/choose_repository.tcl:106
 msgid "Clone..."
 msgstr "Klona..."
 
-#: lib/choose_repository.tcl:107 lib/choose_repository.tcl:974
+#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:983
 msgid "Open Existing Repository"
 msgstr "Öppna befintligt arkiv"
 
-#: lib/choose_repository.tcl:113
+#: lib/choose_repository.tcl:119
 msgid "Open..."
 msgstr "Öppna..."
 
-#: lib/choose_repository.tcl:126
+#: lib/choose_repository.tcl:132
 msgid "Recent Repositories"
 msgstr "Senaste arkiven"
 
-#: lib/choose_repository.tcl:132
+#: lib/choose_repository.tcl:138
 msgid "Open Recent Repository:"
 msgstr "Öppna tidigare arkiv:"
 
-#: lib/choose_repository.tcl:296 lib/choose_repository.tcl:303
-#: lib/choose_repository.tcl:310
+#: lib/choose_repository.tcl:302 lib/choose_repository.tcl:309
+#: lib/choose_repository.tcl:316
 #, tcl-format
 msgid "Failed to create repository %s:"
 msgstr "Kunde inte skapa arkivet %s:"
 
-#: lib/choose_repository.tcl:381 lib/choose_repository.tcl:476
+#: lib/choose_repository.tcl:387
 msgid "Directory:"
 msgstr "Katalog:"
 
-#: lib/choose_repository.tcl:410 lib/choose_repository.tcl:535
-#: lib/choose_repository.tcl:1007
+#: lib/choose_repository.tcl:417 lib/choose_repository.tcl:544
+#: lib/choose_repository.tcl:1017
 msgid "Git Repository"
 msgstr "Gitarkiv"
 
-#: lib/choose_repository.tcl:435
+#: lib/choose_repository.tcl:442
 #, tcl-format
 msgid "Directory %s already exists."
 msgstr "Katalogen %s finns redan."
 
-#: lib/choose_repository.tcl:439
+#: lib/choose_repository.tcl:446
 #, tcl-format
 msgid "File %s already exists."
 msgstr "Filen %s finns redan."
 
-#: lib/choose_repository.tcl:453
+#: lib/choose_repository.tcl:460
 msgid "Clone"
 msgstr "Klona"
 
-#: lib/choose_repository.tcl:466
-msgid "URL:"
-msgstr "Webbadress:"
+#: lib/choose_repository.tcl:473
+msgid "Source Location:"
+msgstr "Plats för källkod:"
 
-#: lib/choose_repository.tcl:487
+#: lib/choose_repository.tcl:484
+msgid "Target Directory:"
+msgstr "Målkatalog:"
+
+#: lib/choose_repository.tcl:496
 msgid "Clone Type:"
 msgstr "Typ av klon:"
 
-#: lib/choose_repository.tcl:493
+#: lib/choose_repository.tcl:502
 msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
 msgstr "Standard (snabb, semiredundant, hårda länkar)"
 
-#: lib/choose_repository.tcl:499
+#: lib/choose_repository.tcl:508
 msgid "Full Copy (Slower, Redundant Backup)"
 msgstr "Full kopia (långsammare, redundant säkerhetskopia)"
 
-#: lib/choose_repository.tcl:505
+#: lib/choose_repository.tcl:514
 msgid "Shared (Fastest, Not Recommended, No Backup)"
 msgstr "Delad (snabbast, rekommenderas ej, ingen säkerhetskopia)"
 
-#: lib/choose_repository.tcl:541 lib/choose_repository.tcl:588
-#: lib/choose_repository.tcl:734 lib/choose_repository.tcl:804
-#: lib/choose_repository.tcl:1013 lib/choose_repository.tcl:1021
+#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597
+#: lib/choose_repository.tcl:743 lib/choose_repository.tcl:813
+#: lib/choose_repository.tcl:1023 lib/choose_repository.tcl:1031
 #, tcl-format
 msgid "Not a Git repository: %s"
 msgstr "Inte ett Gitarkiv: %s"
 
-#: lib/choose_repository.tcl:577
+#: lib/choose_repository.tcl:586
 msgid "Standard only available for local repository."
 msgstr "Standard är endast tillgängligt för lokala arkiv."
 
-#: lib/choose_repository.tcl:581
+#: lib/choose_repository.tcl:590
 msgid "Shared only available for local repository."
 msgstr "Delat är endast tillgängligt för lokala arkiv."
 
-#: lib/choose_repository.tcl:602
+#: lib/choose_repository.tcl:611
 #, tcl-format
 msgid "Location %s already exists."
 msgstr "Platsen %s finns redan."
 
-#: lib/choose_repository.tcl:613
+#: lib/choose_repository.tcl:622
 msgid "Failed to configure origin"
 msgstr "Kunde inte konfigurera ursprung"
 
-#: lib/choose_repository.tcl:625
+#: lib/choose_repository.tcl:634
 msgid "Counting objects"
 msgstr "Räknar objekt"
 
-#: lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:635
 msgid "buckets"
 msgstr "hinkar"
 
-#: lib/choose_repository.tcl:650
+#: lib/choose_repository.tcl:659
 #, tcl-format
 msgid "Unable to copy objects/info/alternates: %s"
 msgstr "Kunde inte kopiera objekt/info/alternativ: %s"
 
-#: lib/choose_repository.tcl:686
+#: lib/choose_repository.tcl:695
 #, tcl-format
 msgid "Nothing to clone from %s."
 msgstr "Ingenting att klona från %s."
 
-#: lib/choose_repository.tcl:688 lib/choose_repository.tcl:902
-#: lib/choose_repository.tcl:914
+#: lib/choose_repository.tcl:697 lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:923
 msgid "The 'master' branch has not been initialized."
 msgstr "Grenen \"master\" har inte initierats."
 
-#: lib/choose_repository.tcl:701
+#: lib/choose_repository.tcl:710
 msgid "Hardlinks are unavailable.  Falling back to copying."
 msgstr "Hårda länkar är inte tillgängliga. Faller tillbaka på kopiering."
 
-#: lib/choose_repository.tcl:713
+#: lib/choose_repository.tcl:722
 #, tcl-format
 msgid "Cloning from %s"
 msgstr "Klonar från %s"
 
-#: lib/choose_repository.tcl:744
+#: lib/choose_repository.tcl:753
 msgid "Copying objects"
 msgstr "Kopierar objekt"
 
-#: lib/choose_repository.tcl:745
+#: lib/choose_repository.tcl:754
 msgid "KiB"
 msgstr "KiB"
 
-#: lib/choose_repository.tcl:769
+#: lib/choose_repository.tcl:778
 #, tcl-format
 msgid "Unable to copy object: %s"
 msgstr "Kunde inte kopiera objekt: %s"
 
-#: lib/choose_repository.tcl:779
+#: lib/choose_repository.tcl:788
 msgid "Linking objects"
 msgstr "Länkar objekt"
 
-#: lib/choose_repository.tcl:780
+#: lib/choose_repository.tcl:789
 msgid "objects"
 msgstr "objekt"
 
-#: lib/choose_repository.tcl:788
+#: lib/choose_repository.tcl:797
 #, tcl-format
 msgid "Unable to hardlink object: %s"
 msgstr "Kunde inte hårdlänka objekt: %s"
 
-#: lib/choose_repository.tcl:843
+#: lib/choose_repository.tcl:852
 msgid "Cannot fetch branches and objects.  See console output for details."
 msgstr "Kunde inte hämta grenar och objekt. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:854
+#: lib/choose_repository.tcl:863
 msgid "Cannot fetch tags.  See console output for details."
 msgstr "Kunde inte hämta taggar. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:878
+#: lib/choose_repository.tcl:887
 msgid "Cannot determine HEAD.  See console output for details."
 msgstr "Kunde inte avgöra HEAD. Se konsolutdata för detaljer."
 
-#: lib/choose_repository.tcl:887
+#: lib/choose_repository.tcl:896
 #, tcl-format
 msgid "Unable to cleanup %s"
 msgstr "Kunde inte städa upp %s"
 
-#: lib/choose_repository.tcl:893
+#: lib/choose_repository.tcl:902
 msgid "Clone failed."
 msgstr "Kloning misslyckades."
 
-#: lib/choose_repository.tcl:900
+#: lib/choose_repository.tcl:909
 msgid "No default branch obtained."
 msgstr "Hämtade ingen standardgren."
 
-#: lib/choose_repository.tcl:911
+#: lib/choose_repository.tcl:920
 #, tcl-format
 msgid "Cannot resolve %s as a commit."
 msgstr "Kunde inte slå upp %s till någon incheckning."
 
-#: lib/choose_repository.tcl:923
+#: lib/choose_repository.tcl:932
 msgid "Creating working directory"
 msgstr "Skapar arbetskatalog"
 
-#: lib/choose_repository.tcl:924 lib/index.tcl:65 lib/index.tcl:127
-#: lib/index.tcl:193
+#: lib/choose_repository.tcl:933 lib/index.tcl:65 lib/index.tcl:128
+#: lib/index.tcl:196
 msgid "files"
 msgstr "filer"
 
-#: lib/choose_repository.tcl:953
+#: lib/choose_repository.tcl:962
 msgid "Initial file checkout failed."
 msgstr "Inledande filutcheckning misslyckades."
 
-#: lib/choose_repository.tcl:969
+#: lib/choose_repository.tcl:978
 msgid "Open"
 msgstr "Öppna"
 
-#: lib/choose_repository.tcl:979
+#: lib/choose_repository.tcl:988
 msgid "Repository:"
 msgstr "Arkiv:"
 
-#: lib/choose_repository.tcl:1027
+#: lib/choose_repository.tcl:1037
 #, tcl-format
 msgid "Failed to open repository %s:"
 msgstr "Kunde inte öppna arkivet %s:"
@@ -1214,19 +1318,19 @@
 "utöka tidigare incheckningar om du inte först avbryter den pågående "
 "sammanslagningen.\n"
 
-#: lib/commit.tcl:49
+#: lib/commit.tcl:48
 msgid "Error loading commit data for amend:"
 msgstr "Fel vid inläsning av incheckningsdata för utökning:"
 
-#: lib/commit.tcl:76
+#: lib/commit.tcl:75
 msgid "Unable to obtain your identity:"
 msgstr "Kunde inte hämta din identitet:"
 
-#: lib/commit.tcl:81
+#: lib/commit.tcl:80
 msgid "Invalid GIT_COMMITTER_IDENT:"
 msgstr "Felaktig GIT_COMMITTER_IDENT:"
 
-#: lib/commit.tcl:133
+#: lib/commit.tcl:132
 msgid ""
 "Last scanned state does not match repository state.\n"
 "\n"
@@ -1242,7 +1346,7 @@
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
-#: lib/commit.tcl:154
+#: lib/commit.tcl:155
 #, tcl-format
 msgid ""
 "Unmerged files cannot be committed.\n"
@@ -1255,7 +1359,7 @@
 "Filen %s har sammanslagningskonflikter. Du måste lösa dem och köa filen "
 "innan du checkar in den.\n"
 
-#: lib/commit.tcl:162
+#: lib/commit.tcl:163
 #, tcl-format
 msgid ""
 "Unknown file state %s detected.\n"
@@ -1266,7 +1370,7 @@
 "\n"
 "Filen %s kan inte checkas in av programmet.\n"
 
-#: lib/commit.tcl:170
+#: lib/commit.tcl:171
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1276,7 +1380,7 @@
 "\n"
 "Du måste köa åtminstone en fil innan du kan checka in.\n"
 
-#: lib/commit.tcl:183
+#: lib/commit.tcl:186
 msgid ""
 "Please supply a commit message.\n"
 "\n"
@@ -1294,45 +1398,45 @@
 "- Andra raden: Tom\n"
 "- Följande rader: Beskriv varför det här är en bra ändring.\n"
 
-#: lib/commit.tcl:207
+#: lib/commit.tcl:210
 #, tcl-format
 msgid "warning: Tcl does not support encoding '%s'."
 msgstr "varning: Tcl stöder inte teckenkodningen \"%s\"."
 
-#: lib/commit.tcl:221
+#: lib/commit.tcl:226
 msgid "Calling pre-commit hook..."
-msgstr "Anropar krok före incheckning..."
+msgstr "Anropar kroken före incheckning (pre-commit)..."
 
-#: lib/commit.tcl:236
+#: lib/commit.tcl:241
 msgid "Commit declined by pre-commit hook."
-msgstr "Incheckningen avvisades av krok före incheckning."
+msgstr "Incheckningen avvisades av kroken före incheckning (pre-commit)."
 
-#: lib/commit.tcl:259
+#: lib/commit.tcl:264
 msgid "Calling commit-msg hook..."
-msgstr "Anropar krok för incheckningsmeddelande..."
+msgstr "Anropar kroken för incheckningsmeddelande (commit-msg)..."
 
-#: lib/commit.tcl:274
+#: lib/commit.tcl:279
 msgid "Commit declined by commit-msg hook."
-msgstr "Incheckning avvisad av krok för incheckningsmeddelande."
+msgstr "Incheckning avvisad av kroken för incheckningsmeddelande (commit-msg)."
 
-#: lib/commit.tcl:287
+#: lib/commit.tcl:292
 msgid "Committing changes..."
 msgstr "Checkar in ändringar..."
 
-#: lib/commit.tcl:303
+#: lib/commit.tcl:308
 msgid "write-tree failed:"
 msgstr "write-tree misslyckades:"
 
-#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368
+#: lib/commit.tcl:309 lib/commit.tcl:353 lib/commit.tcl:373
 msgid "Commit failed."
 msgstr "Incheckningen misslyckades."
 
-#: lib/commit.tcl:321
+#: lib/commit.tcl:326
 #, tcl-format
 msgid "Commit %s appears to be corrupt"
 msgstr "Incheckningen %s verkar vara trasig"
 
-#: lib/commit.tcl:326
+#: lib/commit.tcl:331
 msgid ""
 "No changes to commit.\n"
 "\n"
@@ -1346,19 +1450,19 @@
 "\n"
 "En sökning kommer att startas automatiskt nu.\n"
 
-#: lib/commit.tcl:333
+#: lib/commit.tcl:338
 msgid "No changes to commit."
 msgstr "Inga ändringar att checka in."
 
-#: lib/commit.tcl:347
+#: lib/commit.tcl:352
 msgid "commit-tree failed:"
 msgstr "commit-tree misslyckades:"
 
-#: lib/commit.tcl:367
+#: lib/commit.tcl:372
 msgid "update-ref failed:"
 msgstr "update-ref misslyckades:"
 
-#: lib/commit.tcl:454
+#: lib/commit.tcl:460
 #, tcl-format
 msgid "Created commit %s: %s"
 msgstr "Skapade incheckningen %s: %s"
@@ -1433,7 +1537,7 @@
 msgid "Invalid date from Git: %s"
 msgstr "Ogiltigt datum från Git: %s"
 
-#: lib/diff.tcl:44
+#: lib/diff.tcl:59
 #, tcl-format
 msgid ""
 "No differences detected.\n"
@@ -1456,48 +1560,101 @@
 "En sökning kommer automatiskt att startas för att hitta andra filer som kan "
 "vara i samma tillstånd."
 
-#: lib/diff.tcl:83
+#: lib/diff.tcl:99
 #, tcl-format
 msgid "Loading diff of %s..."
 msgstr "Läser differens för %s..."
 
-#: lib/diff.tcl:116 lib/diff.tcl:190
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"LOKAL: borttagen\n"
+"FJÄRR:\n"
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"FJÄRR: borttagen\n"
+"LOKAL:\n"
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr "LOKAL:\n"
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr "FJÄRR:\n"
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Kan inte visa %s"
 
-#: lib/diff.tcl:117
+#: lib/diff.tcl:198
 msgid "Error loading file:"
 msgstr "Fel vid läsning av fil:"
 
-#: lib/diff.tcl:124
+#: lib/diff.tcl:205
 msgid "Git Repository (subproject)"
 msgstr "Gitarkiv (underprojekt)"
 
-#: lib/diff.tcl:136
+#: lib/diff.tcl:217
 msgid "* Binary file (not showing content)."
 msgstr "* Binärfil (visar inte innehållet)."
 
-#: lib/diff.tcl:191
-msgid "Error loading diff:"
-msgstr "Fel vid inläsning av differens:"
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* Den ospårade filen är %d byte.\n"
+"* Visar endast inledande %d byte.\n"
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Den ospårade filen klipptes här av %s.\n"
+"* För att se hela filen, använd ett externt redigeringsprogram.\n"
+
+#: lib/diff.tcl:436
 msgid "Failed to unstage selected hunk."
 msgstr "Kunde inte ta bort den valda delen från kön."
 
-#: lib/diff.tcl:320
+#: lib/diff.tcl:443
 msgid "Failed to stage selected hunk."
 msgstr "Kunde inte lägga till den valda delen till kön."
 
-#: lib/diff.tcl:386
+#: lib/diff.tcl:509
 msgid "Failed to unstage selected line."
 msgstr "Kunde inte ta bort den valda raden från kön."
 
-#: lib/diff.tcl:394
+#: lib/diff.tcl:517
 msgid "Failed to stage selected line."
 msgstr "Kunde inte lägga till den valda raden till kön."
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Standard"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Systemets (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Annan"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "fel"
@@ -1534,39 +1691,48 @@
 msgid "Unlock Index"
 msgstr "Lås upp index"
 
-#: lib/index.tcl:282
+#: lib/index.tcl:287
 #, tcl-format
 msgid "Unstaging %s from commit"
 msgstr "Tar bort %s för incheckningskön"
 
-#: lib/index.tcl:313
+#: lib/index.tcl:326
 msgid "Ready to commit."
 msgstr "Redo att checka in."
 
-#: lib/index.tcl:326
+#: lib/index.tcl:339
 #, tcl-format
 msgid "Adding %s"
 msgstr "Lägger till %s"
 
-#: lib/index.tcl:381
+#: lib/index.tcl:396
 #, tcl-format
 msgid "Revert changes in file %s?"
 msgstr "Återställ ändringarna i filen %s?"
 
-#: lib/index.tcl:383
+#: lib/index.tcl:398
 #, tcl-format
 msgid "Revert changes in these %i files?"
 msgstr "Återställ ändringarna i dessa %i filer?"
 
-#: lib/index.tcl:391
+#: lib/index.tcl:406
 msgid "Any unstaged changes will be permanently lost by the revert."
 msgstr ""
 "Alla oköade ändringar kommer permanent gå förlorade vid återställningen."
 
-#: lib/index.tcl:394
+#: lib/index.tcl:409
 msgid "Do Nothing"
 msgstr "Gör ingenting"
 
+#: lib/index.tcl:427
+msgid "Reverting selected files"
+msgstr "Återställer valda filer"
+
+#: lib/index.tcl:431
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Återställer %s"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1594,7 +1760,7 @@
 "\n"
 "Sökningen kommer att startas automatiskt nu.\n"
 
-#: lib/merge.tcl:44
+#: lib/merge.tcl:45
 #, tcl-format
 msgid ""
 "You are in the middle of a conflicted merge.\n"
@@ -1611,7 +1777,7 @@
 "Du måste lösa dem, köa filen och checka in för att fullborda den aktuella "
 "sammanslagningen. När du gjort det kan du påbörja en ny sammanslagning.\n"
 
-#: lib/merge.tcl:54
+#: lib/merge.tcl:55
 #, tcl-format
 msgid ""
 "You are in the middle of a change.\n"
@@ -1629,34 +1795,34 @@
 "sammanslagning. Om du gör det blir det enklare att avbryta en misslyckad "
 "sammanslagning, om det skulle vara nödvändigt.\n"
 
-#: lib/merge.tcl:106
+#: lib/merge.tcl:107
 #, tcl-format
 msgid "%s of %s"
 msgstr "%s av %s"
 
-#: lib/merge.tcl:119
+#: lib/merge.tcl:120
 #, tcl-format
 msgid "Merging %s and %s..."
 msgstr "Slår ihop %s och %s..."
 
-#: lib/merge.tcl:130
+#: lib/merge.tcl:131
 msgid "Merge completed successfully."
 msgstr "Sammanslagningen avslutades framgångsrikt."
 
-#: lib/merge.tcl:132
+#: lib/merge.tcl:133
 msgid "Merge failed.  Conflict resolution is required."
 msgstr "Sammanslagningen misslyckades. Du måste lösa konflikterna."
 
-#: lib/merge.tcl:157
+#: lib/merge.tcl:158
 #, tcl-format
 msgid "Merge Into %s"
 msgstr "Slå ihop i %s"
 
-#: lib/merge.tcl:176
+#: lib/merge.tcl:177
 msgid "Revision To Merge"
 msgstr "Revisioner att slå ihop"
 
-#: lib/merge.tcl:211
+#: lib/merge.tcl:212
 msgid ""
 "Cannot abort while amending.\n"
 "\n"
@@ -1666,7 +1832,7 @@
 "\n"
 "Du måste göra dig färdig med att utöka incheckningen.\n"
 
-#: lib/merge.tcl:221
+#: lib/merge.tcl:222
 msgid ""
 "Abort merge?\n"
 "\n"
@@ -1681,7 +1847,7 @@
 "\n"
 "Gå vidare med att avbryta den aktuella sammanslagningen?"
 
-#: lib/merge.tcl:227
+#: lib/merge.tcl:228
 msgid ""
 "Reset changes?\n"
 "\n"
@@ -1696,131 +1862,336 @@
 "\n"
 "Gå vidare med att återställa de aktuella ändringarna?"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "Aborting"
 msgstr "Avbryter"
 
-#: lib/merge.tcl:238
+#: lib/merge.tcl:239
 msgid "files reset"
 msgstr "filer återställda"
 
-#: lib/merge.tcl:266
+#: lib/merge.tcl:267
 msgid "Abort failed."
 msgstr "Misslyckades avbryta."
 
-#: lib/merge.tcl:268
+#: lib/merge.tcl:269
 msgid "Abort completed.  Ready."
 msgstr "Avbrytning fullbordad. Redo."
 
-#: lib/option.tcl:95
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Tvinga lösning att använda basversionen?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Tvinga lösning att använda den aktuella grenen?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Tvinga lösning att använda den andra grenen?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Observera att diffen endast visar de ändringar som står i konflikt.\n"
+"\n"
+"%s kommer att skrivas över.\n"
+"\n"
+"Du måste starta om sammanslagningen för att göra den här operationen ogjord."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Filen %s verkar innehålla olösta konflikter. Vill du köa ändå?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Lägger till lösning för %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr "Kan inte lösa borttagnings- eller länkkonflikter med ett verktyg"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Konfliktfil existerar inte"
+
+#: lib/mergetool.tcl:264
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Inte ett grafiskt verktyg för sammanslagning: %s"
+
+#: lib/mergetool.tcl:268
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Verktyget \"%s\" för sammanslagning stöds inte"
+
+#: lib/mergetool.tcl:303
+msgid "Merge tool is already running, terminate it?"
+msgstr "Verktyget för sammanslagning körs redan. Vill du avsluta det?"
+
+#: lib/mergetool.tcl:323
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Fel vid hämtning av versioner:\n"
+"%s"
+
+#: lib/mergetool.tcl:343
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Kunde inte starta verktyg för sammanslagning:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:347
+msgid "Running merge tool..."
+msgstr "Kör verktyg för sammanslagning..."
+
+#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
+msgid "Merge tool failed."
+msgstr "Verktyget för sammanslagning misslyckades."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Den globala teckenkodningen \"%s\" är ogiltig"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Arkivets teckenkodning \"%s\" är ogiltig"
+
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Återställ standardvärden"
 
-#: lib/option.tcl:99
+#: lib/option.tcl:121
 msgid "Save"
 msgstr "Spara"
 
-#: lib/option.tcl:109
+#: lib/option.tcl:131
 #, tcl-format
 msgid "%s Repository"
 msgstr "Arkivet %s"
 
-#: lib/option.tcl:110
+#: lib/option.tcl:132
 msgid "Global (All Repositories)"
 msgstr "Globalt (alla arkiv)"
 
-#: lib/option.tcl:116
+#: lib/option.tcl:138
 msgid "User Name"
 msgstr "Användarnamn"
 
-#: lib/option.tcl:117
+#: lib/option.tcl:139
 msgid "Email Address"
 msgstr "E-postadress"
 
-#: lib/option.tcl:119
+#: lib/option.tcl:141
 msgid "Summarize Merge Commits"
 msgstr "Summera sammanslagningsincheckningar"
 
-#: lib/option.tcl:120
+#: lib/option.tcl:142
 msgid "Merge Verbosity"
 msgstr "Pratsamhet för sammanslagningar"
 
-#: lib/option.tcl:121
+#: lib/option.tcl:143
 msgid "Show Diffstat After Merge"
 msgstr "Visa diffstatistik efter sammanslagning"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:144
+msgid "Use Merge Tool"
+msgstr "Använd verktyg för sammanslagning"
+
+#: lib/option.tcl:146
 msgid "Trust File Modification Timestamps"
 msgstr "Lita på filändringstidsstämplar"
 
-#: lib/option.tcl:124
+#: lib/option.tcl:147
 msgid "Prune Tracking Branches During Fetch"
 msgstr "Städa spårade grenar vid hämtning"
 
-#: lib/option.tcl:125
+#: lib/option.tcl:148
 msgid "Match Tracking Branches"
 msgstr "Matcha spårade grenar"
 
-#: lib/option.tcl:126
+#: lib/option.tcl:149
 msgid "Blame Copy Only On Changed Files"
 msgstr "Klandra kopiering bara i ändrade filer"
 
-#: lib/option.tcl:127
+#: lib/option.tcl:150
 msgid "Minimum Letters To Blame Copy On"
 msgstr "Minsta antal tecken att klandra kopiering för"
 
-#: lib/option.tcl:128
+#: lib/option.tcl:151
+msgid "Blame History Context Radius (days)"
+msgstr "Historikradie för klandring (dagar)"
+
+#: lib/option.tcl:152
 msgid "Number of Diff Context Lines"
 msgstr "Antal rader sammanhang i differenser"
 
-#: lib/option.tcl:129
+#: lib/option.tcl:153
 msgid "Commit Message Text Width"
 msgstr "Textbredd för incheckningsmeddelande"
 
-#: lib/option.tcl:130
+#: lib/option.tcl:154
 msgid "New Branch Name Template"
 msgstr "Mall för namn på nya grenar"
 
-#: lib/option.tcl:194
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Standardteckenkodning för filinnehåll"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Ändra"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Stavningsordlista:"
 
-#: lib/option.tcl:218
+#: lib/option.tcl:254
 msgid "Change Font"
 msgstr "Byt teckensnitt"
 
-#: lib/option.tcl:222
+#: lib/option.tcl:258
 #, tcl-format
 msgid "Choose %s"
 msgstr "Välj %s"
 
-#: lib/option.tcl:228
+#: lib/option.tcl:264
 msgid "pt."
 msgstr "p."
 
-#: lib/option.tcl:242
+#: lib/option.tcl:278
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: lib/option.tcl:277
+#: lib/option.tcl:314
 msgid "Failed to completely save options:"
 msgstr "Misslyckades med att helt spara alternativ:"
 
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Ta bort fjärrarkiv"
+
+#: lib/remote.tcl:168
+msgid "Prune from"
+msgstr "Ta bort från"
+
+#: lib/remote.tcl:173
+msgid "Fetch from"
+msgstr "Hämta från"
+
+#: lib/remote.tcl:215
+msgid "Push to"
+msgstr "Sänd till"
+
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Lägg till fjärrarkiv"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Lägg till nytt fjärrarkiv"
+
+#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
+msgid "Add"
+msgstr "Lägg till"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Detaljer för fjärrarkiv"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Plats:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Ytterligare åtgärd"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Hämta omedelbart"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Initiera fjärrarkiv och sänd till"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Gör ingent mer nu"
+
+#: lib/remote_add.tcl:101
+msgid "Please supply a remote name."
+msgstr "Ange ett namn för fjärrarkivet."
+
+#: lib/remote_add.tcl:114
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "\"%s\" kan inte användas som namn på fjärrarkivet."
+
+#: lib/remote_add.tcl:125
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Kunde inte lägga till fjärrarkivet \"%s\" på platsen \"%s\"."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "hämta %s"
+
+#: lib/remote_add.tcl:134
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Hämtar %s"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Vet inte hur arkivet på platsen \"%s\" skall initieras."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
+#: lib/transport.tcl:81
+#, tcl-format
+msgid "push %s"
+msgstr "sänd %s"
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Konfigurerar %s (på %s)"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Ta bort fjärrgren"
+msgid "Delete Branch Remotely"
+msgstr "Ta bort gren från fjärrarkiv"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
 msgstr "Från arkiv"
 
-#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123
+#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:134
 msgid "Remote:"
-msgstr "Fjärr:"
+msgstr "Fjärrarkiv:"
 
-#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "Godtycklig webbadress:"
+#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:149
+msgid "Arbitrary Location:"
+msgstr "Godtycklig plats:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -1890,17 +2261,21 @@
 msgid "Scanning %s..."
 msgstr "Söker %s..."
 
-#: lib/remote.tcl:165
-msgid "Prune from"
-msgstr "Ta bort från"
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Sök:"
 
-#: lib/remote.tcl:170
-msgid "Fetch from"
-msgstr "Hämta från"
+#: lib/search.tcl:23
+msgid "Next"
+msgstr "Nästa"
 
-#: lib/remote.tcl:213
-msgid "Push to"
-msgstr "Sänd till"
+#: lib/search.tcl:24
+msgid "Prev"
+msgstr "Föreg"
+
+#: lib/search.tcl:25
+msgid "Case-Sensitive"
+msgstr "Skilj på VERSALER/gemener"
 
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
@@ -1939,23 +2314,188 @@
 msgid "No Suggestions"
 msgstr "Inga förslag"
 
-#: lib/spellcheck.tcl:387
+#: lib/spellcheck.tcl:388
 msgid "Unexpected EOF from spell checker"
 msgstr "Oväntat filslut från stavningskontroll"
 
-#: lib/spellcheck.tcl:391
+#: lib/spellcheck.tcl:392
 msgid "Spell Checker Failed"
 msgstr "Stavningskontroll misslyckades"
 
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Inga nycklar hittades."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Hittade öppen nyckel i: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Skapa nyckel"
+
+#: lib/sshkey.tcl:56
+msgid "Copy To Clipboard"
+msgstr "Kopiera till Urklipp"
+
+#: lib/sshkey.tcl:70
+msgid "Your OpenSSH Public Key"
+msgstr "Din öppna OpenSSH-nyckel"
+
+#: lib/sshkey.tcl:78
+msgid "Generating..."
+msgstr "Skapar..."
+
+#: lib/sshkey.tcl:84
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Kunde inte starta ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:111
+msgid "Generation failed."
+msgstr "Misslyckades med att skapa."
+
+#: lib/sshkey.tcl:118
+msgid "Generation succeded, but no keys found."
+msgstr "Lyckades skapa nyckeln, men hittar inte någon nyckel."
+
+#: lib/sshkey.tcl:121
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Din nyckel finns i: %s"
+
 #: lib/status_bar.tcl:83
 #, tcl-format
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s... %*i av %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
+#: lib/tools.tcl:75
 #, tcl-format
-msgid "fetch %s"
-msgstr "hämta %s"
+msgid "Running %s requires a selected file."
+msgstr "För att starta %s måste du välja en fil."
+
+#: lib/tools.tcl:90
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Är du säker på att du vill starta %s?"
+
+#: lib/tools.tcl:110
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Verktyg: %s"
+
+#: lib/tools.tcl:111
+#, tcl-format
+msgid "Running: %s"
+msgstr "Exekverar: %s"
+
+#: lib/tools.tcl:149
+#, tcl-format
+msgid "Tool completed succesfully: %s"
+msgstr "Verktyget avslutades framgångsrikt: %s"
+
+#: lib/tools.tcl:151
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Verktyget misslyckades: %s"
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Lägg till verktyg"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Lägg till nytt verktygskommando"
+
+#: lib/tools_dlg.tcl:33
+msgid "Add globally"
+msgstr "Lägg till globalt"
+
+#: lib/tools_dlg.tcl:45
+msgid "Tool Details"
+msgstr "Detaljer för verktyg"
+
+#: lib/tools_dlg.tcl:48
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Använd \"/\"-avdelare för att skapa ett undermenyträd:"
+
+#: lib/tools_dlg.tcl:61
+msgid "Command:"
+msgstr "Kommando:"
+
+#: lib/tools_dlg.tcl:74
+msgid "Show a dialog before running"
+msgstr "Visa dialog innan programmet startas"
+
+#: lib/tools_dlg.tcl:80
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Be användaren välja en version (sätter $REVISION)"
+
+#: lib/tools_dlg.tcl:85
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Be användaren om ytterligare parametrar (sätter $ARGS)"
+
+#: lib/tools_dlg.tcl:92
+msgid "Don't show the command output window"
+msgstr "Visa inte kommandots utdatafönster"
+
+#: lib/tools_dlg.tcl:97
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Kör endast om en diff har markerats ($FILENAME är inte tomt)"
+
+#: lib/tools_dlg.tcl:121
+msgid "Please supply a name for the tool."
+msgstr "Ange ett namn för verktyget."
+
+#: lib/tools_dlg.tcl:129
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "Verktyget \"%s\" finns redan."
+
+#: lib/tools_dlg.tcl:151
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Kunde inte lägga till verktyget:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:190
+msgid "Remove Tool"
+msgstr "Ta bort verktyg"
+
+#: lib/tools_dlg.tcl:196
+msgid "Remove Tool Commands"
+msgstr "Ta bort verktygskommandon"
+
+#: lib/tools_dlg.tcl:200
+msgid "Remove"
+msgstr "Ta bort"
+
+#: lib/tools_dlg.tcl:236
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Blått anger verktyg lokala för arkivet)"
+
+#: lib/tools_dlg.tcl:297
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Kör kommandot: %s"
+
+#: lib/tools_dlg.tcl:311
+msgid "Arguments"
+msgstr "Argument"
+
+#: lib/tools_dlg.tcl:348
+msgid "OK"
+msgstr "OK"
 
 #: lib/transport.tcl:7
 #, tcl-format
@@ -1972,49 +2512,55 @@
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Tar bort spårande grenar som tagits bort från %s"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "sänd %s"
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
 msgstr "Sänder ändringar till %s"
 
-#: lib/transport.tcl:72
+#: lib/transport.tcl:64
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Speglar till %s"
+
+#: lib/transport.tcl:82
 #, tcl-format
 msgid "Pushing %s %s to %s"
 msgstr "Sänder %s %s till %s"
 
-#: lib/transport.tcl:89
+#: lib/transport.tcl:100
 msgid "Push Branches"
-msgstr "Sänder grenar"
+msgstr "Sänd grenar"
 
-#: lib/transport.tcl:103
+#: lib/transport.tcl:114
 msgid "Source Branches"
 msgstr "Källgrenar"
 
-#: lib/transport.tcl:120
+#: lib/transport.tcl:131
 msgid "Destination Repository"
 msgstr "Destinationsarkiv"
 
-#: lib/transport.tcl:158
+#: lib/transport.tcl:169
 msgid "Transfer Options"
 msgstr "Överföringsalternativ"
 
-#: lib/transport.tcl:160
+#: lib/transport.tcl:171
 msgid "Force overwrite existing branch (may discard changes)"
 msgstr "Tvinga överskrivning av befintlig gren (kan kasta bort ändringar)"
 
-#: lib/transport.tcl:164
+#: lib/transport.tcl:175
 msgid "Use thin pack (for slow network connections)"
 msgstr "Använd tunt paket (för långsamma nätverksanslutningar)"
 
-#: lib/transport.tcl:168
+#: lib/transport.tcl:179
 msgid "Include tags"
 msgstr "Ta med taggar"
 
+#~ msgid "URL:"
+#~ msgstr "Webbadress:"
+
+#~ msgid "Delete Remote Branch"
+#~ msgstr "Ta bort fjärrgren"
+
 #~ msgid ""
 #~ "Unable to start gitk:\n"
 #~ "\n"
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 0843372..5f4419b 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -49,7 +49,7 @@
 	esac
 
 	httpd_only="$(echo $httpd | cut -f1 -d' ')"
-	if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
+	if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null 2>&1;; esac
 	then
 		full_httpd=$httpd
 	else
@@ -179,11 +179,74 @@
 	cat > "$conf" <<EOF
 server.document-root = "$fqgitdir/gitweb"
 server.port = $port
-server.modules = ( "mod_cgi" )
+server.modules = ( "mod_setenv", "mod_cgi" )
 server.indexfiles = ( "gitweb.cgi" )
 server.pid-file = "$fqgitdir/pid"
+server.errorlog = "$fqgitdir/gitweb/error.log"
+
+# to enable, add "mod_access", "mod_accesslog" to server.modules
+# variable above and uncomment this
+#accesslog.filename = "$fqgitdir/gitweb/access.log"
+
+setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" )
+
 cgi.assign = ( ".cgi" => "" )
-mimetype.assign = ( ".css" => "text/css" )
+
+# mimetype mapping
+mimetype.assign             = (
+  ".pdf"          =>      "application/pdf",
+  ".sig"          =>      "application/pgp-signature",
+  ".spl"          =>      "application/futuresplash",
+  ".class"        =>      "application/octet-stream",
+  ".ps"           =>      "application/postscript",
+  ".torrent"      =>      "application/x-bittorrent",
+  ".dvi"          =>      "application/x-dvi",
+  ".gz"           =>      "application/x-gzip",
+  ".pac"          =>      "application/x-ns-proxy-autoconfig",
+  ".swf"          =>      "application/x-shockwave-flash",
+  ".tar.gz"       =>      "application/x-tgz",
+  ".tgz"          =>      "application/x-tgz",
+  ".tar"          =>      "application/x-tar",
+  ".zip"          =>      "application/zip",
+  ".mp3"          =>      "audio/mpeg",
+  ".m3u"          =>      "audio/x-mpegurl",
+  ".wma"          =>      "audio/x-ms-wma",
+  ".wax"          =>      "audio/x-ms-wax",
+  ".ogg"          =>      "application/ogg",
+  ".wav"          =>      "audio/x-wav",
+  ".gif"          =>      "image/gif",
+  ".jpg"          =>      "image/jpeg",
+  ".jpeg"         =>      "image/jpeg",
+  ".png"          =>      "image/png",
+  ".xbm"          =>      "image/x-xbitmap",
+  ".xpm"          =>      "image/x-xpixmap",
+  ".xwd"          =>      "image/x-xwindowdump",
+  ".css"          =>      "text/css",
+  ".html"         =>      "text/html",
+  ".htm"          =>      "text/html",
+  ".js"           =>      "text/javascript",
+  ".asc"          =>      "text/plain",
+  ".c"            =>      "text/plain",
+  ".cpp"          =>      "text/plain",
+  ".log"          =>      "text/plain",
+  ".conf"         =>      "text/plain",
+  ".text"         =>      "text/plain",
+  ".txt"          =>      "text/plain",
+  ".dtd"          =>      "text/xml",
+  ".xml"          =>      "text/xml",
+  ".mpeg"         =>      "video/mpeg",
+  ".mpg"          =>      "video/mpeg",
+  ".mov"          =>      "video/quicktime",
+  ".qt"           =>      "video/quicktime",
+  ".avi"          =>      "video/x-msvideo",
+  ".asf"          =>      "video/x-ms-asf",
+  ".asx"          =>      "video/x-ms-asf",
+  ".wmv"          =>      "video/x-ms-wmv",
+  ".bz2"          =>      "application/x-bzip",
+  ".tbz"          =>      "application/x-bzip-compressed-tar",
+  ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
+  ""              =>      "text/plain"
+ )
 EOF
 	test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
 }
diff --git a/git-lost-found.sh b/git-lost-found.sh
index 9cedaf8..0b3e8c7 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -20,7 +20,7 @@
 do
 	case "$dangling" in
 	dangling)
-		if git rev-parse --verify "$sha1^0" >/dev/null 2>/dev/null
+		if git rev-parse -q --verify "$sha1^0" >/dev/null
 		then
 			dir="$laf/commit"
 			git show-branch "$sha1"
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index 645e114..1dadbb4 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -61,7 +61,7 @@
 		exit 2
 	esac
 
-	common=$(git merge-base --all $MRC $SHA1) ||
+	common=$(git merge-base --all $SHA1 $MRC) ||
 		die "Unable to find common commit with $SHA1"
 
 	case "$LF$common$LF" in
@@ -100,14 +100,7 @@
 		next=$(git write-tree 2>/dev/null)
 	fi
 
-	# We have merged the other branch successfully.  Ideally
-	# we could implement OR'ed heads in merge-base, and keep
-	# a list of commits we have merged so far in MRC to feed
-	# them to merge-base, but we approximate it by keep using
-	# the current MRC.  We used to update it to $common, which
-	# was incorrectly doing AND'ed merge-base here, which was
-	# unneeded.
-
+	MRC="$MRC $SHA1"
 	MRT=$next
 done
 
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
new file mode 100644
index 0000000..a16a279
--- /dev/null
+++ b/git-mergetool--lib.sh
@@ -0,0 +1,385 @@
+# git-mergetool--lib is a library for common merge tool functions
+diff_mode() {
+	test "$TOOL_MODE" = diff
+}
+
+merge_mode() {
+	test "$TOOL_MODE" = merge
+}
+
+translate_merge_tool_path () {
+	case "$1" in
+	vimdiff)
+		echo vim
+		;;
+	gvimdiff)
+		echo gvim
+		;;
+	emerge)
+		echo emacs
+		;;
+	*)
+		echo "$1"
+		;;
+	esac
+}
+
+check_unchanged () {
+	if test "$MERGED" -nt "$BACKUP"; then
+		status=0
+	else
+		while true; do
+			echo "$MERGED seems unchanged."
+			printf "Was the merge successful? [y/n] "
+			read answer < /dev/tty
+			case "$answer" in
+			y*|Y*) status=0; break ;;
+			n*|N*) status=1; break ;;
+			esac
+		done
+	fi
+}
+
+valid_tool () {
+	case "$1" in
+	kdiff3 | tkdiff | xxdiff | meld | opendiff | \
+	emerge | vimdiff | gvimdiff | ecmerge | diffuse)
+		;; # happy
+	tortoisemerge)
+		if ! merge_mode; then
+			return 1
+		fi
+		;;
+	kompare)
+		if ! diff_mode; then
+			return 1
+		fi
+		;;
+	*)
+		if test -z "$(get_merge_tool_cmd "$1")"; then
+			return 1
+		fi
+		;;
+	esac
+}
+
+get_merge_tool_cmd () {
+	# Prints the custom command for a merge tool
+	if test -n "$1"; then
+		merge_tool="$1"
+	else
+		merge_tool="$(get_merge_tool)"
+	fi
+	if diff_mode; then
+		echo "$(git config difftool.$merge_tool.cmd ||
+		        git config mergetool.$merge_tool.cmd)"
+	else
+		echo "$(git config mergetool.$merge_tool.cmd)"
+	fi
+}
+
+run_merge_tool () {
+	merge_tool_path="$(get_merge_tool_path "$1")" || exit
+	base_present="$2"
+	status=0
+
+	case "$1" in
+	kdiff3)
+		if merge_mode; then
+			if $base_present; then
+				("$merge_tool_path" --auto \
+					--L1 "$MERGED (Base)" \
+					--L2 "$MERGED (Local)" \
+					--L3 "$MERGED (Remote)" \
+					-o "$MERGED" \
+					"$BASE" "$LOCAL" "$REMOTE" \
+				> /dev/null 2>&1)
+			else
+				("$merge_tool_path" --auto \
+					--L1 "$MERGED (Local)" \
+					--L2 "$MERGED (Remote)" \
+					-o "$MERGED" \
+					"$LOCAL" "$REMOTE" \
+				> /dev/null 2>&1)
+			fi
+			status=$?
+		else
+			("$merge_tool_path" --auto \
+				--L1 "$MERGED (A)" \
+				--L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
+			> /dev/null 2>&1)
+		fi
+		;;
+	kompare)
+		"$merge_tool_path" "$LOCAL" "$REMOTE"
+		;;
+	tkdiff)
+		if merge_mode; then
+			if $base_present; then
+				"$merge_tool_path" -a "$BASE" \
+					-o "$MERGED" "$LOCAL" "$REMOTE"
+			else
+				"$merge_tool_path" \
+					-o "$MERGED" "$LOCAL" "$REMOTE"
+			fi
+			status=$?
+		else
+			"$merge_tool_path" "$LOCAL" "$REMOTE"
+		fi
+		;;
+	meld)
+		if merge_mode; then
+			touch "$BACKUP"
+			"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
+			check_unchanged
+		else
+			"$merge_tool_path" "$LOCAL" "$REMOTE"
+		fi
+		;;
+	diffuse)
+		if merge_mode; then
+			touch "$BACKUP"
+			if $base_present; then
+				"$merge_tool_path" \
+					"$LOCAL" "$MERGED" "$REMOTE" \
+					"$BASE" | cat
+			else
+				"$merge_tool_path" \
+					"$LOCAL" "$MERGED" "$REMOTE" | cat
+			fi
+			check_unchanged
+		else
+			"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
+		fi
+		;;
+	vimdiff)
+		if merge_mode; then
+			touch "$BACKUP"
+			"$merge_tool_path" -d -c "wincmd l" \
+				"$LOCAL" "$MERGED" "$REMOTE"
+			check_unchanged
+		else
+			"$merge_tool_path" -d -c "wincmd l" \
+				"$LOCAL" "$REMOTE"
+		fi
+		;;
+	gvimdiff)
+		if merge_mode; then
+			touch "$BACKUP"
+			"$merge_tool_path" -d -c "wincmd l" -f \
+				"$LOCAL" "$MERGED" "$REMOTE"
+			check_unchanged
+		else
+			"$merge_tool_path" -d -c "wincmd l" -f \
+				"$LOCAL" "$REMOTE"
+		fi
+		;;
+	xxdiff)
+		if merge_mode; then
+			touch "$BACKUP"
+			if $base_present; then
+				"$merge_tool_path" -X --show-merged-pane \
+					-R 'Accel.SaveAsMerged: "Ctrl-S"' \
+					-R 'Accel.Search: "Ctrl+F"' \
+					-R 'Accel.SearchForward: "Ctrl-G"' \
+					--merged-file "$MERGED" \
+					"$LOCAL" "$BASE" "$REMOTE"
+			else
+				"$merge_tool_path" -X $extra \
+					-R 'Accel.SaveAsMerged: "Ctrl-S"' \
+					-R 'Accel.Search: "Ctrl+F"' \
+					-R 'Accel.SearchForward: "Ctrl-G"' \
+					--merged-file "$MERGED" \
+					"$LOCAL" "$REMOTE"
+			fi
+			check_unchanged
+		else
+			"$merge_tool_path" \
+				-R 'Accel.Search: "Ctrl+F"' \
+				-R 'Accel.SearchForward: "Ctrl-G"' \
+				"$LOCAL" "$REMOTE"
+		fi
+		;;
+	opendiff)
+		if merge_mode; then
+			touch "$BACKUP"
+			if $base_present; then
+				"$merge_tool_path" "$LOCAL" "$REMOTE" \
+					-ancestor "$BASE" \
+					-merge "$MERGED" | cat
+			else
+				"$merge_tool_path" "$LOCAL" "$REMOTE" \
+					-merge "$MERGED" | cat
+			fi
+			check_unchanged
+		else
+			"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
+		fi
+		;;
+	ecmerge)
+		if merge_mode; then
+			touch "$BACKUP"
+			if $base_present; then
+				"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
+					--default --mode=merge3 --to="$MERGED"
+			else
+				"$merge_tool_path" "$LOCAL" "$REMOTE" \
+					--default --mode=merge2 --to="$MERGED"
+			fi
+			check_unchanged
+		else
+			"$merge_tool_path" "$LOCAL" "$REMOTE" \
+				--default --mode=merge2 --to="$MERGED"
+		fi
+		;;
+	emerge)
+		if merge_mode; then
+			if $base_present; then
+				"$merge_tool_path" \
+					-f emerge-files-with-ancestor-command \
+					"$LOCAL" "$REMOTE" "$BASE" \
+					"$(basename "$MERGED")"
+			else
+				"$merge_tool_path" \
+					-f emerge-files-command \
+					"$LOCAL" "$REMOTE" \
+					"$(basename "$MERGED")"
+			fi
+			status=$?
+		else
+			"$merge_tool_path" -f emerge-files-command \
+				"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
+		fi
+		;;
+	tortoisemerge)
+		if $base_present; then
+			touch "$BACKUP"
+			"$merge_tool_path" \
+				-base:"$BASE" -mine:"$LOCAL" \
+				-theirs:"$REMOTE" -merged:"$MERGED"
+			check_unchanged
+		else
+			echo "TortoiseMerge cannot be used without a base" 1>&2
+			status=1
+		fi
+		;;
+	*)
+		merge_tool_cmd="$(get_merge_tool_cmd "$1")"
+		if test -z "$merge_tool_cmd"; then
+			if merge_mode; then
+				status=1
+			fi
+			break
+		fi
+		if merge_mode; then
+			trust_exit_code="$(git config --bool \
+				mergetool."$1".trustExitCode || echo false)"
+			if test "$trust_exit_code" = "false"; then
+				touch "$BACKUP"
+				( eval $merge_tool_cmd )
+				check_unchanged
+			else
+				( eval $merge_tool_cmd )
+				status=$?
+			fi
+		else
+			( eval $merge_tool_cmd )
+		fi
+		;;
+	esac
+	return $status
+}
+
+guess_merge_tool () {
+	if merge_mode; then
+		tools="tortoisemerge"
+	else
+		tools="kompare"
+	fi
+	if test -n "$DISPLAY"; then
+		if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
+			tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
+		else
+			tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
+		fi
+		tools="$tools gvimdiff diffuse ecmerge"
+	fi
+	if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
+		# $EDITOR is emacs so add emerge as a candidate
+		tools="$tools emerge vimdiff"
+	elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
+		# $EDITOR is vim so add vimdiff as a candidate
+		tools="$tools vimdiff emerge"
+	else
+		tools="$tools emerge vimdiff"
+	fi
+	echo >&2 "merge tool candidates: $tools"
+
+	# Loop over each candidate and stop when a valid merge tool is found.
+	for i in $tools
+	do
+		merge_tool_path="$(translate_merge_tool_path "$i")"
+		if type "$merge_tool_path" > /dev/null 2>&1; then
+			echo "$i"
+			return 0
+		fi
+	done
+
+	echo >&2 "No known merge resolution program available."
+	return 1
+}
+
+get_configured_merge_tool () {
+	# Diff mode first tries diff.tool and falls back to merge.tool.
+	# Merge mode only checks merge.tool
+	if diff_mode; then
+		merge_tool=$(git config diff.tool || git config merge.tool)
+	else
+		merge_tool=$(git config merge.tool)
+	fi
+	if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
+		echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
+		echo >&2 "Resetting to default..."
+		return 1
+	fi
+	echo "$merge_tool"
+}
+
+get_merge_tool_path () {
+	# A merge tool has been set, so verify that it's valid.
+	if test -n "$1"; then
+		merge_tool="$1"
+	else
+		merge_tool="$(get_merge_tool)"
+	fi
+	if ! valid_tool "$merge_tool"; then
+		echo >&2 "Unknown merge tool $merge_tool"
+		exit 1
+	fi
+	if diff_mode; then
+		merge_tool_path=$(git config difftool."$merge_tool".path ||
+		                  git config mergetool."$merge_tool".path)
+	else
+		merge_tool_path=$(git config mergetool."$merge_tool".path)
+	fi
+	if test -z "$merge_tool_path"; then
+		merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
+	fi
+	if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
+	! type "$merge_tool_path" > /dev/null 2>&1; then
+		echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
+		         "'$merge_tool_path'"
+		exit 1
+	fi
+	echo "$merge_tool_path"
+}
+
+get_merge_tool () {
+	# Check if a merge tool has been configured
+	merge_tool=$(get_configured_merge_tool)
+	# Try to guess an appropriate merge tool if no tool has been set.
+	if test -z "$merge_tool"; then
+		merge_tool="$(guess_merge_tool)" || exit
+	fi
+	echo "$merge_tool"
+}
diff --git a/git-mergetool.sh b/git-mergetool.sh
index d4078a6..b52a741 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -8,12 +8,13 @@
 # at the discretion of Junio C Hamano.
 #
 
-USAGE='[--tool=tool] [file to merge] ...'
+USAGE='[--tool=tool] [-y|--no-prompt|--prompt] [file to merge] ...'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
+TOOL_MODE=merge
 . git-sh-setup
+. git-mergetool--lib
 require_work_tree
-prefix=$(git rev-parse --show-prefix)
 
 # Returns true if the mode reflects a symlink
 is_symlink () {
@@ -70,16 +71,16 @@
 		git checkout-index -f --stage=2 -- "$MERGED"
 		git add -- "$MERGED"
 		cleanup_temp_files --save-backup
-		return
+		return 0
 		;;
 	    [rR]*)
 		git checkout-index -f --stage=3 -- "$MERGED"
 		git add -- "$MERGED"
 		cleanup_temp_files --save-backup
-		return
+		return 0
 		;;
 	    [aA]*)
-		exit 1
+		return 1
 		;;
 	    esac
 	done
@@ -97,47 +98,39 @@
 	    [mMcC]*)
 		git add -- "$MERGED"
 		cleanup_temp_files --save-backup
-		return
+		return 0
 		;;
 	    [dD]*)
 		git rm -- "$MERGED" > /dev/null
 		cleanup_temp_files
-		return
+		return 0
 		;;
 	    [aA]*)
-		exit 1
+		return 1
 		;;
 	    esac
 	done
 }
 
-check_unchanged () {
-    if test "$MERGED" -nt "$BACKUP" ; then
-	status=0;
-    else
-	while true; do
-	    echo "$MERGED seems unchanged."
-	    printf "Was the merge successful? [y/n] "
-	    read answer < /dev/tty
-	    case "$answer" in
-		y*|Y*) status=0; break ;;
-		n*|N*) status=1; break ;;
-	    esac
-	done
+checkout_staged_file () {
+    tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^	]*\)	')
+
+    if test $? -eq 0 -a -n "$tmpfile" ; then
+	mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
     fi
 }
 
 merge_file () {
     MERGED="$1"
 
-    f=`git ls-files -u -- "$MERGED"`
+    f=$(git ls-files -u -- "$MERGED")
     if test -z "$f" ; then
 	if test ! -f "$MERGED" ; then
 	    echo "$MERGED: file not found"
 	else
 	    echo "$MERGED: file does not need merging"
 	fi
-	exit 1
+	return 1
     fi
 
     ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
@@ -149,13 +142,13 @@
     mv -- "$MERGED" "$BACKUP"
     cp -- "$BACKUP" "$MERGED"
 
-    base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
-    local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
-    remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
+    base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
+    local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
+    remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
 
-    base_present   && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
-    local_present  && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
-    remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
+    base_present   && checkout_staged_file 1 "$MERGED" "$BASE"
+    local_present  && checkout_staged_file 2 "$MERGED" "$LOCAL"
+    remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE"
 
     if test -z "$local_mode" -o -z "$remote_mode"; then
 	echo "Deleted merge conflict for '$MERGED':"
@@ -176,98 +169,26 @@
     echo "Normal merge conflict for '$MERGED':"
     describe_file "$local_mode" "local" "$LOCAL"
     describe_file "$remote_mode" "remote" "$REMOTE"
-    printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
-    read ans
+    if "$prompt" = true; then
+	printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
+	read ans
+    fi
 
-    case "$merge_tool" in
-	kdiff3)
-	    if base_present ; then
-		("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
-		    -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
-	    else
-		("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
-		    -o "$MERGED" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
-	    fi
-	    status=$?
-	    ;;
-	tkdiff)
-	    if base_present ; then
-		"$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"
-	    else
-		"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
-	    fi
-	    status=$?
-	    ;;
-	meld|vimdiff)
-	    touch "$BACKUP"
-	    "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
-	    check_unchanged
-	    ;;
-	gvimdiff)
-	    touch "$BACKUP"
-	    "$merge_tool_path" -f "$LOCAL" "$MERGED" "$REMOTE"
-	    check_unchanged
-	    ;;
-	xxdiff)
-	    touch "$BACKUP"
-	    if base_present ; then
-		"$merge_tool_path" -X --show-merged-pane \
-		    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
-		    -R 'Accel.Search: "Ctrl+F"' \
-		    -R 'Accel.SearchForward: "Ctrl-G"' \
-		    --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
-	    else
-		"$merge_tool_path" -X --show-merged-pane \
-		    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
-		    -R 'Accel.Search: "Ctrl+F"' \
-		    -R 'Accel.SearchForward: "Ctrl-G"' \
-		    --merged-file "$MERGED" "$LOCAL" "$REMOTE"
-	    fi
-	    check_unchanged
-	    ;;
-	opendiff)
-	    touch "$BACKUP"
-	    if base_present; then
-		"$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
-	    else
-		"$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
-	    fi
-	    check_unchanged
-	    ;;
-	ecmerge)
-	    touch "$BACKUP"
-	    if base_present; then
-		"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"
-	    else
-		"$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"
-	    fi
-	    check_unchanged
-	    ;;
-	emerge)
-	    if base_present ; then
-		"$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
-	    else
-		"$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
-	    fi
-	    status=$?
-	    ;;
-	*)
-	    if test -n "$merge_tool_cmd"; then
-		if test "$merge_tool_trust_exit_code" = "false"; then
-		    touch "$BACKUP"
-		    ( eval $merge_tool_cmd )
-		    check_unchanged
-		else
-		    ( eval $merge_tool_cmd )
-		    status=$?
-		fi
-	    fi
-	    ;;
-    esac
-    if test "$status" -ne 0; then
+    if base_present; then
+	    present=true
+    else
+	    present=false
+    fi
+
+    if ! run_merge_tool "$merge_tool" "$present"; then
 	echo "merge of $MERGED failed" 1>&2
 	mv -- "$BACKUP" "$MERGED"
-	exit 1
+
+	if test "$merge_keep_temporaries" = "false"; then
+	    cleanup_temp_files
+	fi
+
+	return 1
     fi
 
     if test "$merge_keep_backup" = "true"; then
@@ -278,15 +199,18 @@
 
     git add -- "$MERGED"
     cleanup_temp_files
+    return 0
 }
 
+prompt=$(git config --bool mergetool.prompt || echo true)
+
 while test $# != 0
 do
     case "$1" in
 	-t|--tool*)
 	    case "$#,$1" in
 		*,*=*)
-		    merge_tool=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		    merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
 		    ;;
 		1,*)
 		    usage ;;
@@ -295,6 +219,12 @@
 		    shift ;;
 	    esac
 	    ;;
+	-y|--no-prompt)
+	    prompt=false
+	    ;;
+	--prompt)
+	    prompt=true
+	    ;;
 	--)
 	    shift
 	    break
@@ -309,118 +239,67 @@
     shift
 done
 
-valid_custom_tool()
-{
-    merge_tool_cmd="$(git config mergetool.$1.cmd)"
-    test -n "$merge_tool_cmd"
-}
+prompt_after_failed_merge() {
+    while true; do
+	printf "Continue merging other unresolved paths (y/n) ? "
+	read ans
+	case "$ans" in
 
-valid_tool() {
-	case "$1" in
-		kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
-			;; # happy
-		*)
-			if ! valid_custom_tool "$1"; then
-				return 1
-			fi
-			;;
+	    [yY]*)
+		return 0
+		;;
+
+	    [nN]*)
+		return 1
+		;;
 	esac
+    done
 }
 
-init_merge_tool_path() {
-	merge_tool_path=`git config mergetool.$1.path`
-	if test -z "$merge_tool_path" ; then
-		case "$1" in
-			emerge)
-				merge_tool_path=emacs
-				;;
-			*)
-				merge_tool_path=$1
-				;;
-		esac
-	fi
-}
-
-
 if test -z "$merge_tool"; then
-    merge_tool=`git config merge.tool`
-    if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
-	    echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
-	    echo >&2 "Resetting to default..."
-	    unset merge_tool
-    fi
+    merge_tool=$(get_merge_tool "$merge_tool") || exit
 fi
+merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
+merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
 
-if test -z "$merge_tool" ; then
-    if test -n "$DISPLAY"; then
-        merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
-        if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
-            merge_tool_candidates="meld $merge_tool_candidates"
-        fi
-        if test "$KDE_FULL_SESSION" = "true"; then
-            merge_tool_candidates="kdiff3 $merge_tool_candidates"
-        fi
-    fi
-    if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
-        merge_tool_candidates="$merge_tool_candidates emerge"
-    fi
-    if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
-        merge_tool_candidates="$merge_tool_candidates vimdiff"
-    fi
-    merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
-    echo "merge tool candidates: $merge_tool_candidates"
-    for i in $merge_tool_candidates; do
-        init_merge_tool_path $i
-        if type "$merge_tool_path" > /dev/null 2>&1; then
-            merge_tool=$i
-            break
-        fi
-    done
-    if test -z "$merge_tool" ; then
-	echo "No known merge resolution program available."
-	exit 1
-    fi
-else
-    if ! valid_tool "$merge_tool"; then
-        echo >&2 "Unknown merge_tool $merge_tool"
-        exit 1
-    fi
-
-    init_merge_tool_path "$merge_tool"
-
-    merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
-
-    if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
-        echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
-        exit 1
-    fi
-
-    if ! test -z "$merge_tool_cmd"; then
-        merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
-    fi
-fi
-
+last_status=0
+rollup_status=0
 
 if test $# -eq 0 ; then
-	files=`git ls-files -u | sed -e 's/^[^	]*	//' | sort -u`
-	if test -z "$files" ; then
-		echo "No files need merging"
-		exit 0
+    files=$(git ls-files -u | sed -e 's/^[^	]*	//' | sort -u)
+    if test -z "$files" ; then
+	echo "No files need merging"
+	exit 0
+    fi
+    echo Merging the files: "$files"
+    git ls-files -u |
+    sed -e 's/^[^	]*	//' |
+    sort -u |
+    while IFS= read i
+    do
+	if test $last_status -ne 0; then
+	    prompt_after_failed_merge < /dev/tty || exit 1
 	fi
-	echo Merging the files: "$files"
-	git ls-files -u |
-	sed -e 's/^[^	]*	//' |
-	sort -u |
-	while IFS= read i
-	do
-		printf "\n"
-		merge_file "$i" < /dev/tty > /dev/tty
-	done
+	printf "\n"
+	merge_file "$i" < /dev/tty > /dev/tty
+	last_status=$?
+	if test $last_status -ne 0; then
+	    rollup_status=1
+	fi
+    done
 else
-	while test $# -gt 0; do
-		printf "\n"
-		merge_file "$1"
-		shift
-	done
+    while test $# -gt 0; do
+	if test $last_status -ne 0; then
+	    prompt_after_failed_merge || exit 1
+	fi
+	printf "\n"
+	merge_file "$1"
+	last_status=$?
+	if test $last_status -ne 0; then
+	    rollup_status=1
+	fi
+	shift
+    done
 fi
-exit 0
+
+exit $rollup_status
diff --git a/git-pull.sh b/git-pull.sh
index 664fe34..8c75027 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -16,17 +16,21 @@
 test -z "$(git ls-files -u)" ||
 	die "You are in the middle of a conflicted merge."
 
-strategy_args= no_stat= no_commit= squash= no_ff= log_arg=
+strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
 while :
 do
 	case "$1" in
+	-q|--quiet)
+		verbosity="$verbosity -q" ;;
+	-v|--verbose)
+		verbosity="$verbosity -v" ;;
 	-n|--no-stat|--no-summary)
-		no_stat=-n ;;
+		diffstat=--no-stat ;;
 	--stat|--summary)
-		no_stat=$1 ;;
+		diffstat=--stat ;;
 	--log|--no-log)
 		log_arg=$1 ;;
 	--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
@@ -86,23 +90,31 @@
 
 	curr_branch=${curr_branch#refs/heads/}
 
-	echo "You asked me to pull without telling me which branch you"
-	echo "want to merge with, and 'branch.${curr_branch}.merge' in"
-	echo "your configuration file does not tell me either.  Please"
-	echo "name which branch you want to merge on the command line and"
-	echo "try again (e.g. 'git pull <repository> <refspec>')."
-	echo "See git-pull(1) for details on the refspec."
-	echo
-	echo "If you often merge with the same branch, you may want to"
-	echo "configure the following variables in your configuration"
-	echo "file:"
-	echo
-	echo "    branch.${curr_branch}.remote = <nickname>"
-	echo "    branch.${curr_branch}.merge = <remote-ref>"
-	echo "    remote.<nickname>.url = <url>"
-	echo "    remote.<nickname>.fetch = <refspec>"
-	echo
-	echo "See git-config(1) for details."
+	if [ -z "$curr_branch" ]; then
+		echo "You are not currently on a branch, so I cannot use any"
+		echo "'branch.<branchname>.merge' in your configuration file."
+		echo "Please specify which branch you want to merge on the command"
+		echo "line and try again (e.g. 'git pull <repository> <refspec>')."
+		echo "See git-pull(1) for details."
+	else
+		echo "You asked me to pull without telling me which branch you"
+		echo "want to merge with, and 'branch.${curr_branch}.merge' in"
+		echo "your configuration file does not tell me either.	Please"
+		echo "specify which branch you want to merge on the command line and"
+		echo "try again (e.g. 'git pull <repository> <refspec>')."
+		echo "See git-pull(1) for details."
+		echo
+		echo "If you often merge with the same branch, you may want to"
+		echo "configure the following variables in your configuration"
+		echo "file:"
+		echo
+		echo "    branch.${curr_branch}.remote = <nickname>"
+		echo "    branch.${curr_branch}.merge = <remote-ref>"
+		echo "    remote.<nickname>.url = <url>"
+		echo "    remote.<nickname>.fetch = <refspec>"
+		echo
+		echo "See git-config(1) for details."
+	fi
 	exit 1
 }
 
@@ -117,13 +129,13 @@
 	test -z "$origin" && origin=$(get_default_remote)
 	reflist="$(get_remote_refs_for_fetch "$@" 2>/dev/null |
 		sed "s|refs/heads/\(.*\):|\1|")" &&
-	oldremoteref="$(git rev-parse --verify \
-		"refs/remotes/$origin/$reflist" 2>/dev/null)"
+	oldremoteref="$(git rev-parse -q --verify \
+		"refs/remotes/$origin/$reflist")"
 }
-orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
-git fetch --update-head-ok "$@" || exit 1
+orig_head=$(git rev-parse -q --verify HEAD)
+git fetch $verbosity --update-head-ok "$@" || exit 1
 
-curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
+curr_head=$(git rev-parse -q --verify HEAD)
 if test -n "$orig_head" && test "$curr_head" != "$orig_head"
 then
 	# The fetch involved updating the current branch.
@@ -167,6 +179,11 @@
 		echo >&2 "Cannot merge multiple branches into empty head"
 		exit 1
 	fi
+	if test true = "$rebase"
+	then
+		echo >&2 "Cannot rebase onto multiple branches"
+		exit 1
+	fi
 	;;
 esac
 
@@ -179,7 +196,7 @@
 
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
-	exec git-rebase $strategy_args --onto $merge_head \
+	exec git-rebase $diffstat $strategy_args --onto $merge_head \
 	${oldremoteref:-$merge_head}
-exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
-	"$merge_name" HEAD $merge_head
+exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \
+	"$merge_name" HEAD $merge_head $verbosity
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index cebaee1..9a6ba2b 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -63,7 +63,7 @@
 commit=$(git rev-parse HEAD)
 
 mkdir $tmp_dir || exit 2
-while read patch_name level garbage
+while read patch_name level garbage <&3
 do
 	case "$patch_name" in ''|'#'*) continue;; esac
 	case "$level" in
@@ -134,5 +134,5 @@
 		commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
 		git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
 	fi
-done <"$QUILT_PATCHES/series"
+done 3<"$QUILT_PATCHES/series"
 rm -rf $tmp_dir || exit 5
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 59c148f..314cd36 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -26,6 +26,8 @@
 continue           continue rebasing process
 abort              abort rebasing process and restore original branch
 skip               skip current patch and continue rebasing process
+no-verify          override pre-rebase hook from stopping the operation
+root               rebase all reachable commmits up to the root(s)
 "
 
 . git-sh-setup
@@ -37,10 +39,13 @@
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
 REWRITTEN="$DOTEST"/rewritten
+DROPPED="$DOTEST"/dropped
 PRESERVE_MERGES=
 STRATEGY=
 ONTO=
 VERBOSE=
+OK_TO_SKIP_PRE_REBASE=
+REBASE_ROOT=
 
 GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
 mark the corrected paths with 'git add <paths>', and
@@ -66,7 +71,8 @@
 }
 
 run_pre_rebase_hook () {
-	if test -x "$GIT_DIR/hooks/pre-rebase"
+	if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+	   test -x "$GIT_DIR/hooks/pre-rebase"
 	then
 		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
 			echo >&2 "The pre-rebase hook refused to rebase."
@@ -111,9 +117,18 @@
 }
 
 make_patch () {
-	parent_sha1=$(git rev-parse --verify "$1"^) ||
-		die "Cannot get patch for $1^"
-	git diff-tree -p "$parent_sha1".."$1" > "$DOTEST"/patch
+	sha1_and_parents="$(git rev-list --parents -1 "$1")"
+	case "$sha1_and_parents" in
+	?*' '?*' '?*)
+		git diff --cc $sha1_and_parents
+		;;
+	?*' '?*)
+		git diff-tree -p "$1^!"
+		;;
+	*)
+		echo "Root commit"
+		;;
+	esac > "$DOTEST"/patch
 	test -f "$DOTEST"/message ||
 		git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
 	test -f "$DOTEST"/author-script ||
@@ -141,6 +156,11 @@
 	output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
 	test -d "$REWRITTEN" &&
 		pick_one_preserving_merges "$@" && return
+	if test ! -z "$REBASE_ROOT"
+	then
+		output git cherry-pick "$@"
+		return
+	fi
 	parent_sha1=$(git rev-parse --verify $sha1^) ||
 		die "Could not get the parent of $sha1"
 	current_sha1=$(git rev-parse --verify HEAD)
@@ -169,21 +189,43 @@
 
 	if test -f "$DOTEST"/current-commit
 	then
-		current_commit=$(cat "$DOTEST"/current-commit) &&
-		git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
-		rm "$DOTEST"/current-commit ||
-		die "Cannot write current commit's replacement sha1"
+		if test "$fast_forward" = t
+		then
+			cat "$DOTEST"/current-commit | while read current_commit
+			do
+				git rev-parse HEAD > "$REWRITTEN"/$current_commit
+			done
+			rm "$DOTEST"/current-commit ||
+			die "Cannot write current commit's replacement sha1"
+		fi
 	fi
 
-	echo $sha1 > "$DOTEST"/current-commit
+	echo $sha1 >> "$DOTEST"/current-commit
 
 	# rewrite parents; if none were rewritten, we can fast-forward.
 	new_parents=
-	for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
+	pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
+	if test "$pend" = " "
+	then
+		pend=" root"
+	fi
+	while [ "$pend" != "" ]
 	do
+		p=$(expr "$pend" : ' \([^ ]*\)')
+		pend="${pend# $p}"
+
 		if test -f "$REWRITTEN"/$p
 		then
 			new_p=$(cat "$REWRITTEN"/$p)
+
+			# If the todo reordered commits, and our parent is marked for
+			# rewriting, but hasn't been gotten to yet, assume the user meant to
+			# drop it on top of the current HEAD
+			if test -z "$new_p"
+			then
+				new_p=$(git rev-parse HEAD)
+			fi
+
 			test $p != $new_p && fast_forward=f
 			case "$new_parents" in
 			*$new_p*)
@@ -193,7 +235,15 @@
 				;;
 			esac
 		else
-			new_parents="$new_parents $p"
+			if test -f "$DROPPED"/$p
+			then
+				fast_forward=f
+				replacement="$(cat "$DROPPED"/$p)"
+				test -z "$replacement" && replacement=root
+				pend=" $replacement$pend"
+			else
+				new_parents="$new_parents $p"
+			fi
 		fi
 	done
 	case $fast_forward in
@@ -203,15 +253,19 @@
 			die "Cannot fast forward to $sha1"
 		;;
 	f)
-		test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
-
 		first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
-		# detach HEAD to current parent
-		output git checkout $first_parent 2> /dev/null ||
-			die "Cannot move HEAD to $first_parent"
+
+		if [ "$1" != "-n" ]
+		then
+			# detach HEAD to current parent
+			output git checkout $first_parent 2> /dev/null ||
+				die "Cannot move HEAD to $first_parent"
+		fi
 
 		case "$new_parents" in
 		' '*' '*)
+			test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
+
 			# redo merge
 			author_script=$(get_author_ident_from_commit $sha1)
 			eval "$author_script"
@@ -224,9 +278,8 @@
 				output git merge $STRATEGY -m "$msg" \
 					$new_parents
 			then
-				git rerere
 				printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
-				die Error redoing merge $sha1
+				die_with_patch $sha1 "Error redoing merge $sha1"
 			fi
 			;;
 		*)
@@ -320,17 +373,15 @@
 		pick_one -n $sha1 || failed=t
 		case "$(peek_next_command)" in
 		squash|s)
-			EDIT_COMMIT=
 			USE_OUTPUT=output
 			MSG_OPT=-F
-			MSG_FILE="$MSG"
+			EDIT_OR_FILE="$MSG"
 			cp "$MSG" "$SQUASH_MSG"
 			;;
 		*)
-			EDIT_COMMIT=-e
 			USE_OUTPUT=
 			MSG_OPT=
-			MSG_FILE=
+			EDIT_OR_FILE=-e
 			rm -f "$SQUASH_MSG" || exit
 			cp "$MSG" "$GIT_DIR"/SQUASH_MSG
 			rm -f "$GIT_DIR"/MERGE_MSG || exit
@@ -344,7 +395,8 @@
 			GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
 			GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
 			GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-			$USE_OUTPUT git commit --no-verify $MSG_OPT "$MSG_FILE" $EDIT_COMMIT || failed=t
+			$USE_OUTPUT git commit --no-verify \
+				$MSG_OPT "$EDIT_OR_FILE" || failed=t
 		fi
 		if test $failed = t
 		then
@@ -365,20 +417,7 @@
 	HEADNAME=$(cat "$DOTEST"/head-name) &&
 	OLDHEAD=$(cat "$DOTEST"/head) &&
 	SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
-	if test -d "$REWRITTEN"
-	then
-		test -f "$DOTEST"/current-commit &&
-			current_commit=$(cat "$DOTEST"/current-commit) &&
-			git rev-parse HEAD > "$REWRITTEN"/$current_commit
-		if test -f "$REWRITTEN"/$OLDHEAD
-		then
-			NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD)
-		else
-			NEWHEAD=$OLDHEAD
-		fi
-	else
-		NEWHEAD=$(git rev-parse HEAD)
-	fi &&
+	NEWHEAD=$(git rev-parse HEAD) &&
 	case $HEADNAME in
 	refs/*)
 		message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
@@ -403,6 +442,30 @@
 	done
 }
 
+# skip picking commits whose parents are unchanged
+skip_unnecessary_picks () {
+	fd=3
+	while read command sha1 rest
+	do
+		# fd=3 means we skip the command
+		case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
+		3,pick,"$ONTO"*|3,p,"$ONTO"*)
+			# pick a commit whose parent is current $ONTO -> skip
+			ONTO=$sha1
+			;;
+		3,#*|3,,*)
+			# copy comments
+			;;
+		*)
+			fd=1
+			;;
+		esac
+		echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+	done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
+	mv -f "$TODO".new "$TODO" ||
+	die "Could not skip unnecessary pick commands"
+}
+
 # check if no other options are set
 is_standalone () {
 	test $# -eq 2 -a "$2" = '--' &&
@@ -416,11 +479,17 @@
 	test -d "$REWRITTEN" && PRESERVE_MERGES=t
 	test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
 	test -f "$DOTEST"/verbose && VERBOSE=t
+	test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
 }
 
 while test $# != 0
 do
 	case "$1" in
+	--no-verify)
+		OK_TO_SKIP_PRE_REBASE=yes
+		;;
+	--verify)
+		;;
 	--continue)
 		is_standalone "$@" || usage
 		get_saved_options
@@ -515,6 +584,9 @@
 	-i)
 		# yeah, we know
 		;;
+	--root)
+		REBASE_ROOT=t
+		;;
 	--onto)
 		shift
 		ONTO=$(git rev-parse --verify "$1") ||
@@ -522,27 +594,38 @@
 		;;
 	--)
 		shift
-		run_pre_rebase_hook ${1+"$@"}
-		test $# -eq 1 -o $# -eq 2 || usage
+		test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
+		test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
 		test -d "$DOTEST" &&
 			die "Interactive rebase already started"
 
 		git var GIT_COMMITTER_IDENT >/dev/null ||
 			die "You need to set your committer info first"
 
+		if test -z "$REBASE_ROOT"
+		then
+			UPSTREAM_ARG="$1"
+			UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
+			test -z "$ONTO" && ONTO=$UPSTREAM
+			shift
+		else
+			UPSTREAM=
+			UPSTREAM_ARG=--root
+			test -z "$ONTO" &&
+				die "You must specify --onto when using --root"
+		fi
+		run_pre_rebase_hook "$UPSTREAM_ARG" "$@"
+
 		comment_for_reflog start
 
 		require_clean_work_tree
 
-		UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
-		test -z "$ONTO" && ONTO=$UPSTREAM
-
-		if test ! -z "$2"
+		if test ! -z "$1"
 		then
-			output git show-ref --verify --quiet "refs/heads/$2" ||
-				die "Invalid branchname: $2"
-			output git checkout "$2" ||
-				die "Could not checkout $2"
+			output git show-ref --verify --quiet "refs/heads/$1" ||
+				die "Invalid branchname: $1"
+			output git checkout "$1" ||
+				die "Could not checkout $1"
 		fi
 
 		HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
@@ -553,7 +636,12 @@
 			echo "detached HEAD" > "$DOTEST"/head-name
 
 		echo $HEAD > "$DOTEST"/head
-		echo $UPSTREAM > "$DOTEST"/upstream
+		case "$REBASE_ROOT" in
+		'')
+			rm -f "$DOTEST"/rebase-root ;;
+		*)
+			: >"$DOTEST"/rebase-root ;;
+		esac
 		echo $ONTO > "$DOTEST"/onto
 		test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
 		test t = "$VERBOSE" && : > "$DOTEST"/verbose
@@ -566,28 +654,101 @@
 			# This ensures that commits on merged, but otherwise
 			# unrelated side branches are left alone. (Think "X"
 			# in the man page's example.)
-			mkdir "$REWRITTEN" &&
-			for c in $(git merge-base --all $HEAD $UPSTREAM)
-			do
-				echo $ONTO > "$REWRITTEN"/$c ||
+			if test -z "$REBASE_ROOT"
+			then
+				mkdir "$REWRITTEN" &&
+				for c in $(git merge-base --all $HEAD $UPSTREAM)
+				do
+					echo $ONTO > "$REWRITTEN"/$c ||
+						die "Could not init rewritten commits"
+				done
+			else
+				mkdir "$REWRITTEN" &&
+				echo $ONTO > "$REWRITTEN"/root ||
 					die "Could not init rewritten commits"
-			done
+			fi
+			# No cherry-pick because our first pass is to determine
+			# parents to rewrite and skipping dropped commits would
+			# prematurely end our probe
 			MERGES_OPTION=
+			first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)"
 		else
-			MERGES_OPTION=--no-merges
+			MERGES_OPTION="--no-merges --cherry-pick"
 		fi
 
-		SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
 		SHORTHEAD=$(git rev-parse --short $HEAD)
 		SHORTONTO=$(git rev-parse --short $ONTO)
+		if test -z "$REBASE_ROOT"
+			# this is now equivalent to ! -z "$UPSTREAM"
+		then
+			SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
+			REVISIONS=$UPSTREAM...$HEAD
+			SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD
+		else
+			REVISIONS=$ONTO...$HEAD
+			SHORTREVISIONS=$SHORTHEAD
+		fi
 		git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
-			--abbrev=7 --reverse --left-right --cherry-pick \
-			$UPSTREAM...$HEAD | \
-			sed -n "s/^>/pick /p" > "$TODO"
+			--abbrev=7 --reverse --left-right --topo-order \
+			$REVISIONS | \
+			sed -n "s/^>//p" | while read shortsha1 rest
+		do
+			if test t != "$PRESERVE_MERGES"
+			then
+				echo "pick $shortsha1 $rest" >> "$TODO"
+			else
+				sha1=$(git rev-parse $shortsha1)
+				if test -z "$REBASE_ROOT"
+				then
+					preserve=t
+					for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
+					do
+						if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
+						then
+							preserve=f
+						fi
+					done
+				else
+					preserve=f
+				fi
+				if test f = "$preserve"
+				then
+					touch "$REWRITTEN"/$sha1
+					echo "pick $shortsha1 $rest" >> "$TODO"
+				fi
+			fi
+		done
+
+		# Watch for commits that been dropped by --cherry-pick
+		if test t = "$PRESERVE_MERGES"
+		then
+			mkdir "$DROPPED"
+			# Save all non-cherry-picked changes
+			git rev-list $REVISIONS --left-right --cherry-pick | \
+				sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks
+			# Now all commits and note which ones are missing in
+			# not-cherry-picks and hence being dropped
+			git rev-list $REVISIONS |
+			while read rev
+			do
+				if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
+				then
+					# Use -f2 because if rev-list is telling us this commit is
+					# not worthwhile, we don't want to track its multiple heads,
+					# just the history of its first-parent for others that will
+					# be rebasing on top of it
+					git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev
+					short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
+					grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
+					rm "$REWRITTEN"/$rev
+				fi
+			done
+		fi
+
 		test -s "$TODO" || echo noop >> "$TODO"
 		cat >> "$TODO" << EOF
 
-# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
+# Rebase $SHORTREVISIONS onto $SHORTONTO
 #
 # Commands:
 #  p, pick = use commit
@@ -609,6 +770,8 @@
 		has_action "$TODO" ||
 			die_abort "Nothing to do"
 
+		test -d "$REWRITTEN" || skip_unnecessary_picks
+
 		git update-ref ORIG_HEAD $HEAD
 		output git checkout $ONTO && do_rest
 		;;
diff --git a/git-rebase.sh b/git-rebase.sh
index a30d40c..b83fd3f 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]'
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -34,6 +34,7 @@
 require_work_tree
 cd_to_toplevel
 
+OK_TO_SKIP_PRE_REBASE=
 RESOLVEMSG="
 When you have resolved this problem run \"git rebase --continue\".
 If you would prefer to skip this patch, instead run \"git rebase --skip\".
@@ -45,7 +46,10 @@
 dotest="$GIT_DIR"/rebase-merge
 prec=4
 verbose=
+diffstat=$(git config --bool rebase.stat)
 git_am_opt=
+rebase_root=
+force_rebase=
 
 continue_merge () {
 	test -n "$prev_head" || die "prev_head must be defined"
@@ -138,14 +142,31 @@
 }
 
 is_interactive () {
-	test -f "$dotest"/interactive ||
-	while :; do case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac
+	while test $# != 0
+	do
+		case "$1" in
+			-i|--interactive)
+				interactive_rebase=explicit
+				break
+			;;
+			-p|--preserve-merges)
+				interactive_rebase=implied
+			;;
+		esac
 		shift
-	done && test -n "$1"
+	done
+
+	if [ "$interactive_rebase" = implied ]; then
+		GIT_EDITOR=:
+		export GIT_EDITOR
+	fi
+
+	test -n "$interactive_rebase" || test -f "$dotest"/interactive
 }
 
 run_pre_rebase_hook () {
-	if test -x "$GIT_DIR/hooks/pre-rebase"
+	if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+	   test -x "$GIT_DIR/hooks/pre-rebase"
 	then
 		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
 			echo >&2 "The pre-rebase hook refused to rebase."
@@ -170,6 +191,9 @@
 while test $# != 0
 do
 	case "$1" in
+	--no-verify)
+		OK_TO_SKIP_PRE_REBASE=yes
+		;;
 	--continue)
 		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
 			die "No rebase in progress?"
@@ -267,15 +291,37 @@
 		esac
 		do_merge=t
 		;;
+	-n|--no-stat)
+		diffstat=
+		;;
+	--stat)
+		diffstat=t
+		;;
 	-v|--verbose)
 		verbose=t
+		diffstat=t
 		;;
 	--whitespace=*)
 		git_am_opt="$git_am_opt $1"
+		case "$1" in
+		--whitespace=fix|--whitespace=strip)
+			force_rebase=t
+			;;
+		esac
+		;;
+	--committer-date-is-author-date|--ignore-date)
+		git_am_opt="$git_am_opt $1"
+		force_rebase=t
 		;;
 	-C*)
 		git_am_opt="$git_am_opt $1"
 		;;
+	--root)
+		rebase_root=t
+		;;
+	-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
+		force_rebase=t
+		;;
 	-*)
 		usage
 		;;
@@ -285,6 +331,7 @@
 	esac
 	shift
 done
+test $# -gt 2 && usage
 
 # Make sure we do not have $GIT_DIR/rebase-apply
 if test -z "$do_merge"
@@ -311,26 +358,41 @@
 fi
 
 # The tree must be really really clean.
-git update-index --ignore-submodules --refresh || exit
+if ! git update-index --ignore-submodules --refresh; then
+	echo >&2 "cannot rebase: you have unstaged changes"
+	exit 1
+fi
 diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
 case "$diff" in
-?*)	echo "cannot rebase: your index is not up-to-date"
-	echo "$diff"
+?*)	echo >&2 "cannot rebase: your index contains uncommitted changes"
+	echo >&2 "$diff"
 	exit 1
 	;;
 esac
 
-# The upstream head must be given.  Make sure it is valid.
-upstream_name="$1"
-upstream=`git rev-parse --verify "${upstream_name}^0"` ||
-    die "invalid upstream $upstream_name"
+if test -z "$rebase_root"
+then
+	# The upstream head must be given.  Make sure it is valid.
+	upstream_name="$1"
+	shift
+	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
+	die "invalid upstream $upstream_name"
+	unset root_flag
+	upstream_arg="$upstream_name"
+else
+	test -z "$newbase" && die "--root must be used with --onto"
+	unset upstream_name
+	unset upstream
+	root_flag="--root"
+	upstream_arg="$root_flag"
+fi
 
 # Make sure the branch to rebase onto is valid.
 onto_name=${newbase-"$upstream_name"}
 onto=$(git rev-parse --verify "${onto_name}^0") || exit
 
 # If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook ${1+"$@"}
+run_pre_rebase_hook "$upstream_arg" "$@"
 
 # If the branch to rebase is given, that is the branch we will rebase
 # $branch_name -- branch being rebased, or HEAD (already detached)
@@ -338,16 +400,16 @@
 # $head_name -- refs/heads/<that-branch> or "detached HEAD"
 switch_to=
 case "$#" in
-2)
+1)
 	# Is it "rebase other $branchname" or "rebase other $commit"?
-	branch_name="$2"
-	switch_to="$2"
+	branch_name="$1"
+	switch_to="$1"
 
-	if git show-ref --verify --quiet -- "refs/heads/$2" &&
-	   branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
+	if git show-ref --verify --quiet -- "refs/heads/$1" &&
+	   branch=$(git rev-parse -q --verify "refs/heads/$1")
 	then
-		head_name="refs/heads/$2"
-	elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
+		head_name="refs/heads/$1"
+	elif branch=$(git rev-parse -q --verify "$1")
 	then
 		head_name="detached HEAD"
 	else
@@ -369,7 +431,8 @@
 esac
 orig_head=$branch
 
-# Now we are rebasing commits $upstream..$branch on top of $onto
+# Now we are rebasing commits $upstream..$branch (or with --root,
+# everything leading up to $branch) on top of $onto
 
 # Check if we are already based on $onto with linear history,
 # but this should be done only when upstream and onto are the same.
@@ -378,17 +441,15 @@
 	# linear history?
 	! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
 then
-	# Lazily switch to the target branch if needed...
-	test -z "$switch_to" || git checkout "$switch_to"
-	echo >&2 "Current branch $branch_name is up to date."
-	exit 0
-fi
-
-if test -n "$verbose"
-then
-	echo "Changes from $mb to $onto:"
-	# We want color (if set), but no pager
-	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+	if test -z "$force_rebase"
+	then
+		# Lazily switch to the target branch if needed...
+		test -z "$switch_to" || git checkout "$switch_to"
+		echo >&2 "Current branch $branch_name is up to date."
+		exit 0
+	else
+		echo "Current branch $branch_name is up to date, rebase forced."
+	fi
 fi
 
 # Detach HEAD and reset the tree
@@ -396,6 +457,16 @@
 git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $branch
 
+if test -n "$diffstat"
+then
+	if test -n "$verbose"
+	then
+		echo "Changes from $mb to $onto:"
+	fi
+	# We want color (if set), but no pager
+	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+
 # If the $onto is a proper descendant of the tip of the branch, then
 # we just fast forwarded.
 if test "$mb" = "$branch"
@@ -405,10 +476,17 @@
 	exit 0
 fi
 
+if test -n "$rebase_root"
+then
+	revisions="$onto..$orig_head"
+else
+	revisions="$upstream..$orig_head"
+fi
+
 if test -z "$do_merge"
 then
 	git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-		"$upstream..$orig_head" |
+		$root_flag "$revisions" |
 	git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
 	move_to_original_branch
 	ret=$?
@@ -431,7 +509,7 @@
 echo "$head_name" > "$dotest/head-name"
 
 msgnum=0
-for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"`
+for cmt in `git rev-list --reverse --no-merges "$revisions"`
 do
 	msgnum=$(($msgnum + 1))
 	echo "$cmt" > "$dotest/cmt.$msgnum"
diff --git a/git-repack.sh b/git-repack.sh
index 00c597e..0868734 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -60,6 +60,7 @@
 	args='--unpacked --incremental'
 	;;
 ,t,)
+	args= existing=
 	if [ -d "$PACKDIR" ]; then
 		for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \
 			| sed -e 's/^\.\///' -e 's/\.pack$//'`
@@ -67,11 +68,10 @@
 			if [ -e "$PACKDIR/$e.keep" ]; then
 				: keep
 			else
-				args="$args --unpacked=$e.pack"
 				existing="$existing $e"
 			fi
 		done
-		if test -n "$args" -a -n "$unpack_unreachable" -a \
+		if test -n "$existing" -a -n "$unpack_unreachable" -a \
 			-n "$remove_redundant"
 		then
 			args="$args $unpack_unreachable"
diff --git a/git-send-email.perl b/git-send-email.perl
index 449d938..172b53c 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -23,8 +23,12 @@
 use Text::ParseWords;
 use Data::Dumper;
 use Term::ANSIColor;
+use File::Temp qw/ tempdir tempfile /;
+use Error qw(:try);
 use Git;
 
+Getopt::Long::Configure qw/ pass_through /;
+
 package FakeTerm;
 sub new {
 	my ($class, $reason) = @_;
@@ -39,76 +43,45 @@
 
 sub usage {
 	print <<EOT;
-git send-email [options] <file | directory>...
-Options:
-   --from         Specify the "From:" line of the email to be sent.
+git send-email [options] <file | directory | rev-list options >
 
-   --to           Specify the primary "To:" line of the email.
+  Composing:
+    --from                  <str>  * Email From:
+    --to                    <str>  * Email To:
+    --cc                    <str>  * Email Cc:
+    --bcc                   <str>  * Email Bcc:
+    --subject               <str>  * Email "Subject:"
+    --in-reply-to           <str>  * Email "In-Reply-To:"
+    --annotate                     * Review each patch that will be sent in an editor.
+    --compose                      * Open an editor for introduction.
 
-   --cc           Specify an initial "Cc:" list for the entire series
-                  of emails.
+  Sending:
+    --envelope-sender       <str>  * Email envelope sender.
+    --smtp-server       <str:int>  * Outgoing SMTP server to use. The port
+                                     is optional. Default 'localhost'.
+    --smtp-server-port      <int>  * Outgoing SMTP server port.
+    --smtp-user             <str>  * Username for SMTP-AUTH.
+    --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
+    --smtp-encryption       <str>  * tls or ssl; anything else disables.
+    --smtp-ssl                     * Deprecated. Use '--smtp-encryption ssl'.
 
-   --cc-cmd       Specify a command to execute per file which adds
-                  per file specific cc address entries
+  Automating:
+    --identity              <str>  * Use the sendemail.<id> options.
+    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
+    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
+    --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
+    --[no-]suppress-from           * Send to self. Default off.
+    --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default on.
+    --[no-]thread                  * Use In-Reply-To: field. Default on.
 
-   --bcc          Specify a list of email addresses that should be Bcc:
-		  on all the emails.
-
-   --compose      Use \$GIT_EDITOR, core.editor, \$EDITOR, or \$VISUAL to edit
-		  an introductory message for the patch series.
-
-   --subject      Specify the initial "Subject:" line.
-                  Only necessary if --compose is also set.  If --compose
-		  is not set, this will be prompted for.
-
-   --in-reply-to  Specify the first "In-Reply-To:" header line.
-                  Only used if --compose is also set.  If --compose is not
-		  set, this will be prompted for.
-
-   --chain-reply-to If set, the replies will all be to the previous
-                  email sent, rather than to the first email sent.
-                  Defaults to on.
-
-   --signed-off-cc Automatically add email addresses that appear in
-                 Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
-
-   --identity     The configuration identity, a subsection to prioritise over
-                  the default section.
-
-   --smtp-server  If set, specifies the outgoing SMTP server to use.
-                  Defaults to localhost.  Port number can be specified here with
-                  hostname:port format or by using --smtp-server-port option.
-
-   --smtp-server-port Specify a port on the outgoing SMTP server to connect to.
-
-   --smtp-user    The username for SMTP-AUTH.
-
-   --smtp-pass    The password for SMTP-AUTH.
-
-   --smtp-encryption Specify 'tls' for STARTTLS encryption, or 'ssl' for SSL.
-                  Any other value disables the feature.
-
-   --smtp-ssl     Synonym for '--smtp-encryption=ssl'.  Deprecated.
-
-   --suppress-cc  Suppress the specified category of auto-CC.  The category
-		  can be one of 'author' for the patch author, 'self' to
-		  avoid copying yourself, 'sob' for Signed-off-by lines,
-		  'cccmd' for the output of the cccmd, or 'all' to suppress
-		  all of these.
-
-   --suppress-from Suppress sending emails to yourself. Defaults to off.
-
-   --thread       Specify that the "In-Reply-To:" header should be set on all
-                  emails. Defaults to on.
-
-   --quiet	  Make git-send-email less verbose.  One line per email
-                  should be all that is output.
-
-   --dry-run	  Do everything except actually send the emails.
-
-   --envelope-sender	Specify the envelope sender used to send the emails.
-
-   --no-validate	Don't perform any sanity checks on patches.
+  Administering:
+    --confirm               <str>  * Confirm recipients before sending;
+                                     auto, cc, compose, always, or never.
+    --quiet                        * Output one line of info per email.
+    --dry-run                      * Don't actually send the emails.
+    --[no-]validate                * Perform patch sanity checks. Default on.
+    --[no-]format-patch            * understand any non optional arguments as
+                                     `git format-patch` ones.
 
 EOT
 	exit(1);
@@ -154,18 +127,17 @@
 }
 
 my $have_email_valid = eval { require Email::Valid; 1 };
+my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
 
 sub unique_email_list(@);
 sub cleanup_compose_files();
 
-# Constants (essentially)
-my $compose_filename = ".msg.$$";
-
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+	$initial_reply_to,$initial_subject,@files,
+	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
 
@@ -185,19 +157,42 @@
 
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
+my $format_patch;
+my $compose_filename;
+
+# Handle interactive edition of files.
+my $multiedit;
+my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+sub do_edit {
+	if (defined($multiedit) && !$multiedit) {
+		map {
+			system('sh', '-c', $editor.' "$@"', $editor, $_);
+			if (($? & 127) || ($? >> 8)) {
+				die("the editor exited uncleanly, aborting everything");
+			}
+		} @_;
+	} else {
+		system('sh', '-c', $editor.' "$@"', $editor, @_);
+		if (($? & 127) || ($? >> 8)) {
+			die("the editor exited uncleanly, aborting everything");
+		}
+	}
+}
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
-my ($no_validate);
+my ($validate, $confirm);
 my (@suppress_cc);
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
     "chainreplyto" => [\$chain_reply_to, 1],
     "suppressfrom" => [\$suppress_from, undef],
-    "signedoffcc" => [\$signed_off_cc, undef],
+    "signedoffbycc" => [\$signed_off_by_cc, undef],
+    "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
+    "validate" => [\$validate, 1],
 );
 
 my %config_settings = (
@@ -213,6 +208,8 @@
     "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
+    "multiedit" => \$multiedit,
+    "confirm"   => \$confirm,
 );
 
 # Handle Uncouth Termination
@@ -225,11 +222,13 @@
 	system "stty echo";
 
 	# tmp files from --compose
-	if (-e $compose_filename) {
-		print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
-	}
-	if (-e ($compose_filename . ".final")) {
-		print "'$compose_filename.final' contains the composed email.\n"
+	if (defined $compose_filename) {
+		if (-e $compose_filename) {
+			print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+		}
+		if (-e ($compose_filename . ".final")) {
+			print "'$compose_filename.final' contains the composed email.\n"
+		}
 	}
 
 	exit;
@@ -255,22 +254,28 @@
 		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 		    "smtp-encryption=s" => \$smtp_encryption,
 		    "identity=s" => \$identity,
+		    "annotate" => \$annotate,
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
 		    "suppress-from!" => \$suppress_from,
 		    "suppress-cc=s" => \@suppress_cc,
-		    "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
+		    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
+		    "confirm=s" => \$confirm,
 		    "dry-run" => \$dry_run,
 		    "envelope-sender=s" => \$envelope_sender,
 		    "thread!" => \$thread,
-		    "no-validate" => \$no_validate,
+		    "validate!" => \$validate,
+		    "format-patch!" => \$format_patch,
 	 );
 
 unless ($rc) {
     usage();
 }
 
+die "Cannot run git format-patch from outside a repository\n"
+	if $format_patch and not $repo;
+
 # Now, let's fill any that aren't set in with defaults:
 
 sub read_config {
@@ -322,13 +327,13 @@
 if (@suppress_cc) {
 	foreach my $entry (@suppress_cc) {
 		die "Unknown --suppress-cc field: '$entry'\n"
-			unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/;
+			unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/;
 		$suppress_cc{$entry} = 1;
 	}
 }
 
 if ($suppress_cc{'all'}) {
-	foreach my $entry (qw (ccmd cc author self sob)) {
+	foreach my $entry (qw (ccmd cc author self sob body bodycc)) {
 		$suppress_cc{$entry} = 1;
 	}
 	delete $suppress_cc{'all'};
@@ -336,7 +341,22 @@
 
 # If explicit old-style ones are specified, they trump --suppress-cc.
 $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
-$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
+$suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
+
+if ($suppress_cc{'body'}) {
+	foreach my $entry (qw (sob bodycc)) {
+		$suppress_cc{$entry} = 1;
+	}
+	delete $suppress_cc{'body'};
+}
+
+# Set confirm's default value
+my $confirm_unconfigured = !defined $confirm;
+if ($confirm_unconfigured) {
+	$confirm = scalar %suppress_cc ? 'compose' : 'auto';
+};
+die "Unknown --confirm setting: '$confirm'\n"
+	unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
 if (0) {
@@ -364,8 +384,16 @@
 	die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
 }
 
+sub parse_address_line {
+	if ($have_mail_address) {
+		return map { $_->format } Mail::Address->parse($_[0]);
+	} else {
+		return split_addrs($_[0]);
+	}
+}
+
 sub split_addrs {
-	return parse_line('\s*,\s*', 1, @_);
+	return quotewords('\s*,\s*', 1, @_);
 }
 
 my %aliases;
@@ -383,10 +411,13 @@
 			# spaces delimit multiple addresses
 			$aliases{$1} = [ split(/\s+/, $2) ];
 		}}},
-	pine => sub { my $fh = shift; while (<$fh>) {
-		if (/^(\S+)\t.*\t(.*)$/) {
+	pine => sub { my $fh = shift; my $f='\t[^\t]*';
+	        for (my $x = ''; defined($x); $x = $_) {
+			chomp $x;
+		        $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
+			$x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
 			$aliases{$1} = [ split_addrs($2) ];
-		}}},
+		}},
 	gnus => sub { my $fh = shift; while (<$fh>) {
 		if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
 			$aliases{$1} = [ $2 ];
@@ -403,24 +434,56 @@
 
 ($sender) = expand_aliases($sender) if defined $sender;
 
+# returns 1 if the conflict must be solved using it as a format-patch argument
+sub check_file_rev_conflict($) {
+	return unless $repo;
+	my $f = shift;
+	try {
+		$repo->command('rev-parse', '--verify', '--quiet', $f);
+		if (defined($format_patch)) {
+			print "foo\n";
+			return $format_patch;
+		}
+		die(<<EOF);
+File '$f' exists but it could also be the range of commits
+to produce patches for.  Please disambiguate by...
+
+    * Saying "./$f" if you mean a file; or
+    * Giving --format-patch option if you mean a range.
+EOF
+	} catch Git::Error::Command with {
+		return 0;
+	}
+}
+
 # Now that all the defaults are set, process the rest of the command line
 # arguments and collect up the files that need to be processed.
-for my $f (@ARGV) {
-	if (-d $f) {
+my @rev_list_opts;
+while (defined(my $f = shift @ARGV)) {
+	if ($f eq "--") {
+		push @rev_list_opts, "--", @ARGV;
+		@ARGV = ();
+	} elsif (-d $f and !check_file_rev_conflict($f)) {
 		opendir(DH,$f)
 			or die "Failed to opendir $f: $!";
 
 		push @files, grep { -f $_ } map { +$f . "/" . $_ }
 				sort readdir(DH);
 		closedir(DH);
-	} elsif (-f $f or -p $f) {
+	} elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
 		push @files, $f;
 	} else {
-		print STDERR "Skipping $f - not found.\n";
+		push @rev_list_opts, $f;
 	}
 }
 
-if (!$no_validate) {
+if (@rev_list_opts) {
+	die "Cannot run git format-patch from outside a repository\n"
+		unless $repo;
+	push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+}
+
+if ($validate) {
 	foreach my $f (@files) {
 		unless (-p $f) {
 			my $error = validate_patch($f);
@@ -438,32 +501,148 @@
 	usage();
 }
 
+sub get_patch_subject($) {
+	my $fn = shift;
+	open (my $fh, '<', $fn);
+	while (my $line = <$fh>) {
+		next unless ($line =~ /^Subject: (.*)$/);
+		close $fh;
+		return "GIT: $1\n";
+	}
+	close $fh;
+	die "No subject line in $fn ?";
+}
+
+if ($compose) {
+	# Note that this does not need to be secure, but we will make a small
+	# effort to have it be unique
+	$compose_filename = ($repo ?
+		tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
+		tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
+	open(C,">",$compose_filename)
+		or die "Failed to open for writing $compose_filename: $!";
+
+
+	my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+	my $tpl_subject = $initial_subject || '';
+	my $tpl_reply_to = $initial_reply_to || '';
+
+	print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+GIT:
+GIT: Clear the body content if you don't wish to send a summary.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
+EOT
+	for my $f (@files) {
+		print C get_patch_subject($f);
+	}
+	close(C);
+
+	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
+
+	open(C2,">",$compose_filename . ".final")
+		or die "Failed to open $compose_filename.final : " . $!;
+
+	open(C,"<",$compose_filename)
+		or die "Failed to open $compose_filename : " . $!;
+
+	my $need_8bit_cte = file_has_nonascii($compose_filename);
+	my $in_body = 0;
+	my $summary_empty = 1;
+	while(<C>) {
+		next if m/^GIT: /;
+		if ($in_body) {
+			$summary_empty = 0 unless (/^\n$/);
+		} elsif (/^\n$/) {
+			$in_body = 1;
+			if ($need_8bit_cte) {
+				print C2 "MIME-Version: 1.0\n",
+					 "Content-Type: text/plain; ",
+					   "charset=utf-8\n",
+					 "Content-Transfer-Encoding: 8bit\n";
+			}
+		} elsif (/^MIME-Version:/i) {
+			$need_8bit_cte = 0;
+		} elsif (/^Subject:\s*(.+)\s*$/i) {
+			$initial_subject = $1;
+			my $subject = $initial_subject;
+			$_ = "Subject: " .
+				($subject =~ /[^[:ascii:]]/ ?
+				 quote_rfc2047($subject) :
+				 $subject) .
+				"\n";
+		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+			$initial_reply_to = $1;
+			next;
+		} elsif (/^From:\s*(.+)\s*$/i) {
+			$sender = $1;
+			next;
+		} elsif (/^(?:To|Cc|Bcc):/i) {
+			print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+			next;
+		}
+		print C2 $_;
+	}
+	close(C);
+	close(C2);
+
+	if ($summary_empty) {
+		print "Summary email is empty, skipping it\n";
+		$compose = -1;
+	}
+} elsif ($annotate) {
+	do_edit(@files);
+}
+
+sub ask {
+	my ($prompt, %arg) = @_;
+	my $valid_re = $arg{valid_re};
+	my $default = $arg{default};
+	my $resp;
+	my $i = 0;
+	return defined $default ? $default : undef
+		unless defined $term->IN and defined fileno($term->IN) and
+		       defined $term->OUT and defined fileno($term->OUT);
+	while ($i++ < 10) {
+		$resp = $term->readline($prompt);
+		if (!defined $resp) { # EOF
+			print "\n";
+			return defined $default ? $default : undef;
+		}
+		if ($resp eq '' and defined $default) {
+			return $default;
+		}
+		if (!defined $valid_re or $resp =~ /$valid_re/) {
+			return $resp;
+		}
+	}
+	return undef;
+}
+
 my $prompting = 0;
 if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
-
-	while (1) {
-		$_ = $term->readline("Who should the emails appear to be from? [$sender] ");
-		last if defined $_;
-		print "\n";
-	}
-
-	$sender = $_ if ($_);
+	$sender = ask("Who should the emails appear to be from? [$sender] ",
+	              default => $sender);
 	print "Emails will be sent from: ", $sender, "\n";
 	$prompting++;
 }
 
 if (!@to) {
-
-
-	while (1) {
-		$_ = $term->readline("Who should the emails be sent to? ", "");
-		last if defined $_;
-		print "\n";
-	}
-
-	my $to = $_;
-	push @to, split_addrs($to);
+	my $to = ask("Who should the emails be sent to? ");
+	push @to, parse_address_line($to) if defined $to; # sanitized/validated later
 	$prompting++;
 }
 
@@ -482,25 +661,9 @@
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
-if (!defined $initial_subject && $compose) {
-	while (1) {
-		$_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
-		last if defined $_;
-		print "\n";
-	}
-
-	$initial_subject = $_;
-	$prompting++;
-}
-
 if ($thread && !defined $initial_reply_to && $prompting) {
-	while (1) {
-		$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
-		last if defined $_;
-		print "\n";
-	}
-
-	$initial_reply_to = $_;
+	$initial_reply_to = ask(
+		"Message-ID to be used as In-Reply-To for the first email? ");
 }
 if (defined $initial_reply_to) {
 	$initial_reply_to =~ s/^\s*<?//;
@@ -518,76 +681,13 @@
 	$smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug*
 }
 
-if ($compose) {
-	# Note that this does not need to be secure, but we will make a small
-	# effort to have it be unique
-	open(C,">",$compose_filename)
-		or die "Failed to open for writing $compose_filename: $!";
-	print C "From $sender # This line is ignored.\n";
-	printf C "Subject: %s\n\n", $initial_subject;
-	printf C <<EOT;
-GIT: Please enter your email below.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-
-EOT
-	close(C);
-
-	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-	system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
-
-	open(C2,">",$compose_filename . ".final")
-		or die "Failed to open $compose_filename.final : " . $!;
-
-	open(C,"<",$compose_filename)
-		or die "Failed to open $compose_filename : " . $!;
-
-	my $need_8bit_cte = file_has_nonascii($compose_filename);
-	my $in_body = 0;
-	while(<C>) {
-		next if m/^GIT: /;
-		if (!$in_body && /^\n$/) {
-			$in_body = 1;
-			if ($need_8bit_cte) {
-				print C2 "MIME-Version: 1.0\n",
-					 "Content-Type: text/plain; ",
-					   "charset=utf-8\n",
-					 "Content-Transfer-Encoding: 8bit\n";
-			}
-		}
-		if (!$in_body && /^MIME-Version:/i) {
-			$need_8bit_cte = 0;
-		}
-		if (!$in_body && /^Subject: ?(.*)/i) {
-			my $subject = $1;
-			$_ = "Subject: " .
-				($subject =~ /[^[:ascii:]]/ ?
-				 quote_rfc2047($subject) :
-				 $subject) .
-				"\n";
-		}
-		print C2 $_;
-	}
-	close(C);
-	close(C2);
-
-	while (1) {
-		$_ = $term->readline("Send this email? (y|n) ");
-		last if defined $_;
-		print "\n";
-	}
-
-	if (uc substr($_,0,1) ne 'Y') {
-		cleanup_compose_files();
-		exit(0);
-	}
-
+if ($compose && $compose > 0) {
 	@files = ($compose_filename . ".final", @files);
 }
 
 # Variables we set as part of the loop over files
-our ($message_id, %mail, $subject, $reply_to, $references, $message);
+our ($message_id, %mail, $subject, $reply_to, $references, $message,
+	$needs_confirm, $message_num, $ask_default);
 
 sub extract_valid_address {
 	my $address = shift;
@@ -676,12 +776,13 @@
 	}
 
 	# if recipient_name is already quoted, do nothing
-	if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) {
+	if ($recipient_name =~ /^("[[:ascii:]]*"|=\?utf-8\?q\?.*\?=)$/) {
 		return $recipient;
 	}
 
 	# rfc2047 is needed if a non-ascii char is included
 	if ($recipient_name =~ /[^[:ascii:]]/) {
+		$recipient_name =~ s/^"(.*)"$/$1/;
 		$recipient_name = quote_rfc2047($recipient_name);
 	}
 
@@ -727,7 +828,7 @@
 Message-Id: $message_id
 X-Mailer: git-send-email $gitversion
 ";
-	if ($thread && $reply_to) {
+	if ($reply_to) {
 
 		$header .= "In-Reply-To: $reply_to\n";
 		$header .= "References: $references\n";
@@ -743,6 +844,35 @@
 	unshift (@sendmail_parameters,
 			'-f', $raw_from) if(defined $envelope_sender);
 
+	if ($needs_confirm && !$dry_run) {
+		print "\n$header\n";
+		if ($needs_confirm eq "inform") {
+			$confirm_unconfigured = 0; # squelch this message for the rest of this run
+			$ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation
+			print "    The Cc list above has been expanded by additional\n";
+			print "    addresses found in the patch commit message. By default\n";
+			print "    send-email prompts before sending whenever this occurs.\n";
+			print "    This behavior is controlled by the sendemail.confirm\n";
+			print "    configuration setting.\n";
+			print "\n";
+			print "    For additional information, run 'git send-email --help'.\n";
+			print "    To retain the current behavior, but squelch this message,\n";
+			print "    run 'git config --global sendemail.confirm auto'.\n\n";
+		}
+		$_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+		         valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
+		         default => $ask_default);
+		die "Send this email reply required" unless defined $_;
+		if (/^n/i) {
+			return;
+		} elsif (/^q/i) {
+			cleanup_compose_files();
+			exit(0);
+		} elsif (/^a/i) {
+			$confirm = 'never';
+		}
+	}
+
 	if ($dry_run) {
 		# We don't want to send the email.
 	} elsif ($smtp_server =~ m#^/#) {
@@ -841,6 +971,7 @@
 $reply_to = $initial_reply_to;
 $references = $initial_reply_to || '';
 $subject = $initial_subject;
+$message_num = 0;
 
 foreach my $t (@files) {
 	open(F,"<",$t) or die "can't open file $t";
@@ -849,91 +980,106 @@
 	my $author_encoding;
 	my $has_content_type;
 	my $body_encoding;
-	@cc = @initial_cc;
+	@cc = ();
 	@xh = ();
 	my $input_format = undef;
-	my $header_done = 0;
+	my @header = ();
 	$message = "";
+	$message_num++;
+	# First unfold multiline header fields
 	while(<F>) {
-		if (!$header_done) {
-			if (/^From /) {
-				$input_format = 'mbox';
-				next;
-			}
-			chomp;
-			if (!defined $input_format && /^[-A-Za-z]+:\s/) {
-				$input_format = 'mbox';
-			}
+		last if /^\s*$/;
+		if (/^\s+\S/ and @header) {
+			chomp($header[$#header]);
+			s/^\s+/ /;
+			$header[$#header] .= $_;
+	    } else {
+			push(@header, $_);
+		}
+	}
+	# Now parse the header
+	foreach(@header) {
+		if (/^From /) {
+			$input_format = 'mbox';
+			next;
+		}
+		chomp;
+		if (!defined $input_format && /^[-A-Za-z]+:\s/) {
+			$input_format = 'mbox';
+		}
 
-			if (defined $input_format && $input_format eq 'mbox') {
-				if (/^Subject:\s+(.*)$/) {
-					$subject = $1;
-
-				} elsif (/^(Cc|From):\s+(.*)$/) {
-					if (unquote_rfc2047($2) eq $sender) {
+		if (defined $input_format && $input_format eq 'mbox') {
+			if (/^Subject:\s+(.*)$/) {
+				$subject = $1;
+			}
+			elsif (/^From:\s+(.*)$/) {
+				($author, $author_encoding) = unquote_rfc2047($1);
+				next if $suppress_cc{'author'};
+				next if $suppress_cc{'self'} and $author eq $sender;
+				printf("(mbox) Adding cc: %s from line '%s'\n",
+					$1, $_) unless $quiet;
+				push @cc, $1;
+			}
+			elsif (/^Cc:\s+(.*)$/) {
+				foreach my $addr (parse_address_line($1)) {
+					if (unquote_rfc2047($addr) eq $sender) {
 						next if ($suppress_cc{'self'});
-					}
-					elsif ($1 eq 'From') {
-						($author, $author_encoding)
-						  = unquote_rfc2047($2);
-						next if ($suppress_cc{'author'});
 					} else {
 						next if ($suppress_cc{'cc'});
 					}
 					printf("(mbox) Adding cc: %s from line '%s'\n",
-						$2, $_) unless $quiet;
-					push @cc, $2;
-				}
-				elsif (/^Content-type:/i) {
-					$has_content_type = 1;
-					if (/charset="?([^ "]+)/) {
-						$body_encoding = $1;
-					}
-					push @xh, $_;
-				}
-				elsif (/^Message-Id: (.*)/i) {
-					$message_id = $1;
-				}
-				elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
-					push @xh, $_;
-				}
-
-			} else {
-				# In the traditional
-				# "send lots of email" format,
-				# line 1 = cc
-				# line 2 = subject
-				# So let's support that, too.
-				$input_format = 'lots';
-				if (@cc == 0 && !$suppress_cc{'cc'}) {
-					printf("(non-mbox) Adding cc: %s from line '%s'\n",
-						$_, $_) unless $quiet;
-
-					push @cc, $_;
-
-				} elsif (!defined $subject) {
-					$subject = $_;
+						$addr, $_) unless $quiet;
+					push @cc, $addr;
 				}
 			}
-
-			# A whitespace line will terminate the headers
-			if (m/^\s*$/) {
-				$header_done = 1;
+			elsif (/^Content-type:/i) {
+				$has_content_type = 1;
+				if (/charset="?([^ "]+)/) {
+					$body_encoding = $1;
+				}
+				push @xh, $_;
 			}
+			elsif (/^Message-Id: (.*)/i) {
+				$message_id = $1;
+			}
+			elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
+				push @xh, $_;
+			}
+
 		} else {
-			$message .=  $_;
-			if (/^(Signed-off-by|Cc): (.*)$/i) {
-				next if ($suppress_cc{'sob'});
-				chomp;
-				my $c = $2;
-				chomp $c;
-				next if ($c eq $sender and $suppress_cc{'self'});
-				push @cc, $c;
-				printf("(sob) Adding cc: %s from line '%s'\n",
-					$c, $_) unless $quiet;
+			# In the traditional
+			# "send lots of email" format,
+			# line 1 = cc
+			# line 2 = subject
+			# So let's support that, too.
+			$input_format = 'lots';
+			if (@cc == 0 && !$suppress_cc{'cc'}) {
+				printf("(non-mbox) Adding cc: %s from line '%s'\n",
+					$_, $_) unless $quiet;
+				push @cc, $_;
+			} elsif (!defined $subject) {
+				$subject = $_;
 			}
 		}
 	}
+	# Now parse the message body
+	while(<F>) {
+		$message .=  $_;
+		if (/^(Signed-off-by|Cc): (.*)$/i) {
+			chomp;
+			my ($what, $c) = ($1, $2);
+			chomp $c;
+			if ($c eq $sender) {
+				next if ($suppress_cc{'self'});
+			} else {
+				next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
+				next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
+			}
+			push @cc, $c;
+			printf("(body) Adding cc: %s from line '%s'\n",
+				$c, $_) unless $quiet;
+		}
+	}
 	close F;
 
 	if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
@@ -952,7 +1098,7 @@
 			or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
 	}
 
-	if (defined $author) {
+	if (defined $author and $author ne $sender) {
 		$message = "From: $author\n\n$message";
 		if (defined $author_encoding) {
 			if ($has_content_type) {
@@ -972,6 +1118,14 @@
 		}
 	}
 
+	$needs_confirm = (
+		$confirm eq "always" or
+		($confirm =~ /^(?:auto|cc)$/ && @cc) or
+		($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
+	$needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
+
+	@cc = (@initial_cc, @cc);
+
 	send_message();
 
 	# set up for the next message
@@ -986,13 +1140,10 @@
 	$message_id = undef;
 }
 
-if ($compose) {
-	cleanup_compose_files();
-}
+cleanup_compose_files();
 
 sub cleanup_compose_files() {
-	unlink($compose_filename, $compose_filename . ".final");
-
+	unlink($compose_filename, $compose_filename . ".final") if $compose;
 }
 
 $smtp->quit if $smtp;
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index dbdf209..8382339 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -85,7 +85,13 @@
 	cdup=$(git rev-parse --show-cdup)
 	if test ! -z "$cdup"
 	then
-		cd "$cdup" || {
+		# The "-P" option says to follow "physical" directory
+		# structure instead of following symbolic links.  When cdup is
+		# "../", this means following the ".." entry in the current
+		# directory instead textually removing a symlink path element
+		# from the PWD shell variable.  The "-P" behavior is more
+		# consistent with the C-style chdir used by most of Git.
+		cd -P "$cdup" || {
 			echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
 			exit 1
 		}
diff --git a/git-submodule.sh b/git-submodule.sh
index 97e4d9a..7c2e060 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -5,16 +5,18 @@
 # Copyright (c) 2007 Lars Hjemli
 
 USAGE="[--quiet] [--cached] \
-[add <repo> [-b branch] <path>]|[status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]"
+[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit <n>] [<commit>]] \
+[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
 OPTIONS_SPEC=
 . git-sh-setup
+. git-parse-remote
 require_work_tree
 
 command=
 branch=
 quiet=
 cached=
+nofetch=
 
 #
 # print stuff on stdout unless -q was specified
@@ -30,12 +32,11 @@
 # Resolve relative url by appending to parent's url
 resolve_relative_url ()
 {
-	branch="$(git symbolic-ref HEAD 2>/dev/null)"
-	remote="$(git config branch.${branch#refs/heads/}.remote)"
-	remote="${remote:-origin}"
+	remote=$(get_default_remote)
 	remoteurl=$(git config "remote.$remote.url") ||
 		die "remote ($remote) does not have a url defined in .git/config"
 	url="$1"
+	remoteurl=${remoteurl%/}
 	while test -n "$url"
 	do
 		case "$url" in
@@ -50,7 +51,16 @@
 			break;;
 		esac
 	done
-	echo "$remoteurl/$url"
+	echo "$remoteurl/${url%/}"
+}
+
+#
+# Get submodule info for registered submodules
+# $@ = path to limit submodule list
+#
+module_list()
+{
+	git ls-files --error-unmatch --stage -- "$@" | grep '^160000 '
 }
 
 #
@@ -157,9 +167,18 @@
 	;;
 	esac
 
-	# strip trailing slashes from path
-	path=$(echo "$path" | sed -e 's|/*$||')
-
+	# normalize path:
+	# multiple //; leading ./; /./; /../; trailing /
+	path=$(printf '%s/\n' "$path" |
+		sed -e '
+			s|//*|/|g
+			s|^\(\./\)*||
+			s|/\./|/|g
+			:start
+			s|\([^/]*\)/\.\./||
+			tstart
+			s|/*$||
+		')
 	git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
 	die "'$path' already exists in the index"
 
@@ -199,6 +218,26 @@
 }
 
 #
+# Execute an arbitrary command sequence in each checked out
+# submodule
+#
+# $@ = command to execute
+#
+cmd_foreach()
+{
+	module_list |
+	while read mode sha1 stage path
+	do
+		if test -e "$path"/.git
+		then
+			say "Entering '$path'"
+			(cd "$path" && eval "$@") ||
+			die "Stopping at '$path'; script returned non-zero status."
+		fi
+	done
+}
+
+#
 # Register submodules in .git/config
 #
 # $@ = requested paths (default to all)
@@ -226,7 +265,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep '^160000 ' |
+	module_list "$@" |
 	while read mode sha1 stage path
 	do
 		# Skip already registered paths
@@ -271,6 +310,10 @@
 			shift
 			cmd_init "$@" || return
 			;;
+		-N|--no-fetch)
+			shift
+			nofetch=1
+			;;
 		--)
 			shift
 			break
@@ -284,7 +327,7 @@
 		esac
 	done
 
-	git ls-files --stage -- "$@" | grep '^160000 ' |
+	module_list "$@" |
 	while read mode sha1 stage path
 	do
 		name=$(module_name "$path") || exit
@@ -316,8 +359,16 @@
 			then
 				force="-f"
 			fi
-			(unset GIT_DIR; cd "$path" && git-fetch &&
-				git-checkout $force -q "$sha1") ||
+
+			if test -z "$nofetch"
+			then
+				(unset GIT_DIR; cd "$path" &&
+					git-fetch) ||
+				die "Unable to fetch in submodule path '$path'"
+			fi
+
+			(unset GIT_DIR; cd "$path" &&
+				  git-checkout $force -q "$sha1") ||
 			die "Unable to checkout '$sha1' in submodule path '$path'"
 
 			say "Submodule path '$path': checked out '$sha1'"
@@ -384,7 +435,7 @@
 
 	test $summary_limit = 0 && return
 
-	if rev=$(git rev-parse --verify "$1^0" 2>/dev/null)
+	if rev=$(git rev-parse -q --verify "$1^0")
 	then
 		head=$rev
 		shift
@@ -395,7 +446,7 @@
 	cd_to_toplevel
 	# Get modified modules cared by user
 	modules=$(git diff-index $cached --raw $head -- "$@" |
-		grep -e '^:160000' -e '^:[0-7]* 160000' |
+		egrep '^:([0-7]* )?160000' |
 		while read mod_src mod_dst sha1_src sha1_dst status name
 		do
 			# Always show modules deleted or type-changed (blob<->module)
@@ -409,7 +460,7 @@
 	test -z "$modules" && return
 
 	git diff-index $cached --raw $head -- $modules |
-	grep -e '^:160000' -e '^:[0-7]* 160000' |
+	egrep '^:([0-7]* )?160000' |
 	cut -c2- |
 	while read mod_src mod_dst sha1_src sha1_dst status name
 	do
@@ -435,11 +486,11 @@
 		missing_dst=
 
 		test $mod_src = 160000 &&
-		! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_src^0 >/dev/null 2>&1 &&
+		! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
 		missing_src=t
 
 		test $mod_dst = 160000 &&
-		! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_dst^0 >/dev/null 2>&1 &&
+		! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
 		missing_dst=t
 
 		total_commits=
@@ -554,7 +605,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep '^160000 ' |
+	module_list "$@" |
 	while read mode sha1 stage path
 	do
 		name=$(module_name "$path") || exit
@@ -578,6 +629,58 @@
 		fi
 	done
 }
+#
+# Sync remote urls for submodules
+# This makes the value for remote.$remote.url match the value
+# specified in .gitmodules.
+#
+cmd_sync()
+{
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			quiet=1
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+	done
+	cd_to_toplevel
+	module_list "$@" |
+	while read mode sha1 stage path
+	do
+		name=$(module_name "$path")
+		url=$(git config -f .gitmodules --get submodule."$name".url)
+
+		# Possibly a url relative to parent
+		case "$url" in
+		./*|../*)
+			url=$(resolve_relative_url "$url") || exit
+			;;
+		esac
+
+		if test -e "$path"/.git
+		then
+		(
+			unset GIT_DIR
+			cd "$path"
+			remote=$(get_default_remote)
+			say "Synchronizing submodule url for '$name'"
+			git config remote."$remote".url "$url"
+		)
+		fi
+	done
+}
 
 # This loop parses the command line arguments to find the
 # subcommand name to dispatch.  Parsing of the subcommand specific
@@ -588,7 +691,7 @@
 while test $# != 0 && test -z "$command"
 do
 	case "$1" in
-	add | init | update | status | summary)
+	add | foreach | init | update | status | summary | sync)
 		command=$1
 		;;
 	-q|--quiet)
diff --git a/git-svn.perl b/git-svn.perl
index 25ed2f4..c5965c9 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -47,7 +47,8 @@
 	# import functions from Git into our packages, en masse
 	no strict 'refs';
 	foreach (qw/command command_oneline command_noisy command_output_pipe
-	            command_input_pipe command_close_pipe/) {
+	            command_input_pipe command_close_pipe
+	            command_bidi_pipe command_close_bidi_pipe/) {
 		for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
 			Git::SVN::Migration Git::SVN::Log Git::SVN),
 			__PACKAGE__) {
@@ -63,14 +64,16 @@
 my ($_stdin, $_help, $_edit,
 	$_message, $_file,
 	$_template, $_shared,
-	$_version, $_fetch_all, $_no_rebase,
+	$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
 	$_merge, $_strategy, $_dry_run, $_local,
 	$_prefix, $_no_checkout, $_url, $_verbose,
-	$_git_format, $_commit_url);
+	$_git_format, $_commit_url, $_tag);
 $Git::SVN::_follow_parent = 1;
+$_q ||= 0;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
-                    'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache );
+                    'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
+                    'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex );
 my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
 		'authors-file|A=s' => \$_authors,
 		'repack:i' => \$Git::SVN::_repack,
@@ -79,11 +82,12 @@
 		'useSvnsyncProps' => \$Git::SVN::_use_svnsync_props,
 		'log-window-size=i' => \$Git::SVN::Ra::_log_window_size,
 		'no-checkout' => \$_no_checkout,
-		'quiet|q' => \$_q,
+		'quiet|q+' => \$_q,
 		'repack-flags|repack-args|repack-opts=s' =>
 		   \$Git::SVN::_repack_flags,
 		'use-log-author' => \$Git::SVN::_use_log_author,
 		'add-author-from' => \$Git::SVN::_add_author_from,
+		'localtime' => \$Git::SVN::_localtime,
 		%remote_opts );
 
 my ($_trunk, $_tags, $_branches, $_stdlayout);
@@ -109,6 +113,7 @@
 	fetch => [ \&cmd_fetch, "Download new revisions from SVN",
 			{ 'revision|r=s' => \$_revision,
 			  'fetch-all|all' => \$_fetch_all,
+			  'parent|p' => \$_fetch_parent,
 			   %fc_opts } ],
 	clone => [ \&cmd_clone, "Initialize and fetch revisions",
 			{ 'revision|r=s' => \$_revision,
@@ -131,6 +136,15 @@
 			  'revision|r=i' => \$_revision,
 			  'no-rebase' => \$_no_rebase,
 			%cmt_opts, %fc_opts } ],
+	branch => [ \&cmd_branch,
+	            'Create a branch in the SVN repository',
+	            { 'message|m=s' => \$_message,
+	              'dry-run|n' => \$_dry_run,
+		      'tag|t' => \$_tag } ],
+	tag => [ sub { $_tag = 1; cmd_branch(@_) },
+	         'Create a tag in the SVN repository',
+	         { 'message|m=s' => \$_message,
+	           'dry-run|n' => \$_dry_run } ],
 	'set-tree' => [ \&cmd_set_tree,
 	                "Set an SVN repository to a git tree-ish",
 			{ 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
@@ -214,11 +228,13 @@
 			    "but it is not a directory\n";
 		}
 		my $git_dir = delete $ENV{GIT_DIR};
-		chomp(my $cdup = command_oneline(qw/rev-parse --show-cdup/));
-		unless (length $cdup) {
-			die "Already at toplevel, but $git_dir ",
-			    "not found '$cdup'\n";
-		}
+		my $cdup = undef;
+		git_cmd_try {
+			$cdup = command_oneline(qw/rev-parse --show-cdup/);
+			$git_dir = '.' unless ($cdup);
+			chomp $cdup if ($cdup);
+			$cdup = "." unless ($cdup && length $cdup);
+		} "Already at toplevel, but $git_dir not found\n";
 		chdir $cdup or die "Unable to chdir up to '$cdup'\n";
 		unless (-d $git_dir) {
 			die "$git_dir still not found after going to ",
@@ -312,6 +328,7 @@
 		command_noisy(@init_db);
 		$_repository = Git->repository(Repository => ".git");
 	}
+	command_noisy('config', 'core.autocrlf', 'false');
 	my $set;
 	my $pfx = "svn-remote.$Git::SVN::default_repo_id";
 	foreach my $i (keys %icv) {
@@ -320,6 +337,9 @@
 		command_noisy('config', "$pfx.$i", $icv{$i});
 		$set = $i;
 	}
+	my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex;
+	command_noisy('config', "$pfx.ignore-paths", $$ignore_regex)
+		if defined $$ignore_regex;
 }
 
 sub init_subdir {
@@ -367,12 +387,21 @@
 	}
 	my ($remote) = @_;
 	if (@_ > 1) {
-		die "Usage: $0 fetch [--all] [svn-remote]\n";
+		die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n";
 	}
-	$remote ||= $Git::SVN::default_repo_id;
-	if ($_fetch_all) {
+	if ($_fetch_parent) {
+		my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+		unless ($gs) {
+			die "Unable to determine upstream SVN information from ",
+			    "working tree history\n";
+		}
+	        # just fetch, don't checkout.
+		$_no_checkout = 'true';
+		$_fetch_all ? $gs->fetch_all : $gs->fetch;
+	} elsif ($_fetch_all) {
 		cmd_multi_fetch();
 	} else {
+		$remote ||= $Git::SVN::default_repo_id;
 		Git::SVN::fetch_all($remote, Git::SVN::read_all_remotes());
 	}
 }
@@ -421,15 +450,25 @@
 	$head ||= 'HEAD';
 	my @refs;
 	my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
-	$url = defined $_commit_url ? $_commit_url : $gs->full_url;
-	my $last_rev = $_revision if defined $_revision;
-	if ($url) {
-		print "Committing to $url ...\n";
-	}
 	unless ($gs) {
 		die "Unable to determine upstream SVN information from ",
 		    "$head history.\nPerhaps the repository is empty.";
 	}
+
+	if (defined $_commit_url) {
+		$url = $_commit_url;
+	} else {
+		$url = eval { command_oneline('config', '--get',
+			      "svn-remote.$gs->{repo_id}.commiturl") };
+		if (!$url) {
+			$url = $gs->full_url
+		}
+	}
+
+	my $last_rev = $_revision if defined $_revision;
+	if ($url) {
+		print "Committing to $url ...\n";
+	}
 	my ($linear_refs, $parents) = linearize_history($gs, \@refs);
 	if ($_no_rebase && scalar(@$linear_refs) > 1) {
 		warn "Attempting to commit more than one change while ",
@@ -537,6 +576,42 @@
 	unlink $gs->{index};
 }
 
+sub cmd_branch {
+	my ($branch_name, $head) = @_;
+
+	unless (defined $branch_name && length $branch_name) {
+		die(($_tag ? "tag" : "branch") . " name required\n");
+	}
+	$head ||= 'HEAD';
+
+	my ($src, $rev, undef, $gs) = working_head_info($head);
+
+	my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
+	my $glob = $remote->{ $_tag ? 'tags' : 'branches' };
+	my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};
+	my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());
+
+	my $ctx = SVN::Client->new(
+		auth    => Git::SVN::Ra::_auth_providers(),
+		log_msg => sub {
+			${ $_[0] } = defined $_message
+				? $_message
+				: 'Create ' . ($_tag ? 'tag ' : 'branch ' )
+				. $branch_name;
+		},
+	);
+
+	eval {
+		$ctx->ls($dst, 'HEAD', 0);
+	} and die "branch ${branch_name} already exists\n";
+
+	print "Copying ${src} at r${rev} to ${dst}...\n";
+	$ctx->copy($src, $rev, $dst)
+		unless $_dry_run;
+
+	$gs->fetch_all;
+}
+
 sub cmd_find_rev {
 	my $revision_or_hash = shift or die "SVN or git revision required ",
 	                                    "as a command-line argument\n";
@@ -621,7 +696,11 @@
 	$gs->prop_walk($gs->{path}, $r, sub {
 		my ($gs, $path, $props) = @_;
 		# $path is of the form /path/to/dir/
-		my $ignore = '.' . $path . '.gitignore';
+		$path = '.' . $path;
+		# SVN can have attributes on empty directories,
+		# which git won't track
+		mkpath([$path]) unless -d $path;
+		my $ignore = $path . '.gitignore';
 		my $s = $props->{'svn:ignore'} or return;
 		open(GITIGNORE, '>', $ignore)
 		  or fatal("Failed to open `$ignore' for writing: $!");
@@ -803,8 +882,28 @@
 	}
 }
 
+sub escape_uri_only {
+	my ($uri) = @_;
+	my @tmp;
+	foreach (split m{/}, $uri) {
+		s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+		push @tmp, $_;
+	}
+	join('/', @tmp);
+}
+
+sub escape_url {
+	my ($url) = @_;
+	if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) {
+		my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
+		$url = "$scheme://$domain$uri";
+	}
+	$url;
+}
+
 sub cmd_info {
 	my $path = canonicalize_path(defined($_[0]) ? $_[0] : ".");
+	my $fullpath = canonicalize_path($cmd_dir_prefix . $path);
 	if (exists $_[1]) {
 		die "Too many arguments specified\n";
 	}
@@ -812,8 +911,8 @@
 	my ($file_type, $diff_status) = find_file_type_and_diff_status($path);
 
 	if (!$file_type && !$diff_status) {
-		print STDERR "$path:  (Not a versioned resource)\n\n";
-		return;
+		print STDERR "svn: '$path' is not under version control\n";
+		exit 1;
 	}
 
 	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
@@ -825,26 +924,27 @@
 	# canonicalize_path() will return "" to make libsvn 1.5.x happy,
 	$path = "." if $path eq "";
 
-	my $full_url = $url . ($path eq "." ? "" : "/$path");
+	my $full_url = $url . ($fullpath eq "" ? "" : "/$fullpath");
 
 	if ($_url) {
-		print $full_url, "\n";
+		print escape_url($full_url), "\n";
 		return;
 	}
 
 	my $result = "Path: $path\n";
 	$result .= "Name: " . basename($path) . "\n" if $file_type ne "dir";
-	$result .= "URL: " . $full_url . "\n";
+	$result .= "URL: " . escape_url($full_url) . "\n";
 
 	eval {
 		my $repos_root = $gs->repos_root;
 		Git::SVN::remove_username($repos_root);
-		$result .= "Repository Root: $repos_root\n";
+		$result .= "Repository Root: " . escape_url($repos_root) . "\n";
 	};
 	if ($@) {
 		$result .= "Repository Root: (offline)\n";
 	}
-	$result .= "Repository UUID: $uuid\n" unless $diff_status eq "A";
+	$result .= "Repository UUID: $uuid\n" unless $diff_status eq "A" &&
+		($SVN::Core::VERSION le '1.5.4' || $file_type ne "dir");
 	$result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n";
 
 	$result .= "Node Kind: " .
@@ -861,7 +961,7 @@
 	}
 
 	my ($lc_author, $lc_rev, $lc_date_utc);
-	my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path);
+	my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $fullpath);
 	my $log = command_output_pipe(@args);
 	my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
 	while (<$log>) {
@@ -1071,9 +1171,19 @@
 		system($editor, $commit_editmsg);
 	}
 	rename $commit_editmsg, $commit_msg or croak $!;
-	open $log_fh, '<', $commit_msg or croak $!;
-	{ local $/; chomp($log_entry{log} = <$log_fh>); }
-	close $log_fh or croak $!;
+	{
+		# SVN requires messages to be UTF-8 when entering the repo
+		local $/;
+		open $log_fh, '<', $commit_msg or croak $!;
+		binmode $log_fh;
+		chomp($log_entry{log} = <$log_fh>);
+
+		if (my $enc = Git::config('i18n.commitencoding')) {
+			require Encode;
+			Encode::from_to($log_entry{log}, $enc, 'UTF-8');
+		}
+		close $log_fh or croak $!;
+	}
 	unlink $commit_msg;
 	\%log_entry;
 }
@@ -1159,6 +1269,40 @@
 		command(qw/cat-file commit/, shift)))[-1]);
 }
 
+sub cmt_sha2rev_batch {
+	my %s2r;
+	my ($pid, $in, $out, $ctx) = command_bidi_pipe(qw/cat-file --batch/);
+	my $list = shift;
+
+	foreach my $sha (@{$list}) {
+		my $first = 1;
+		my $size = 0;
+		print $out $sha, "\n";
+
+		while (my $line = <$in>) {
+			if ($first && $line =~ /^[[:xdigit:]]{40}\smissing$/) {
+				last;
+			} elsif ($first &&
+			       $line =~ /^[[:xdigit:]]{40}\scommit\s(\d+)$/) {
+				$first = 0;
+				$size = $1;
+				next;
+			} elsif ($line =~ /^(git-svn-id: )/) {
+				my (undef, $rev, undef) =
+				                      extract_metadata($line);
+				$s2r{$sha} = $rev;
+			}
+
+			$size -= length($line);
+			last if ($size == 0);
+		}
+	}
+
+	command_close_bidi_pipe($pid, $in, $out, $ctx);
+
+	return \%s2r;
+}
+
 sub working_head_info {
 	my ($head, $refs) = @_;
 	my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
@@ -1287,7 +1431,7 @@
 use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags $_use_svm_props $_head
             $_use_svnsync_props $no_reuse_existing $_minimize_url
-	    $_use_log_author $_add_author_from/;
+	    $_use_log_author $_add_author_from $_localtime/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
@@ -1613,6 +1757,7 @@
 			my $prefix = '';
 			if ($rwr) {
 				$z = $rwr;
+				remove_username($z);
 			} elsif (defined $svm) {
 				$z = $svm->{source};
 				$prefix = $svm->{replace};
@@ -2208,6 +2353,14 @@
 	}
 	defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec))
 	                                                           or croak $!;
+	binmode $msg_fh;
+
+	# we always get UTF-8 from SVN, but we may want our commits in
+	# a different encoding.
+	if (my $enc = Git::config('i18n.commitencoding')) {
+		require Encode;
+		Encode::from_to($log_entry->{log}, 'UTF-8', $enc);
+	}
 	print $msg_fh $log_entry->{log} or croak $!;
 	restore_commit_header_env($old_env);
 	unless ($self->no_metadata) {
@@ -2228,13 +2381,13 @@
 
 	$self->{last_rev} = $log_entry->{revision};
 	$self->{last_commit} = $commit;
-	print "r$log_entry->{revision}";
+	print "r$log_entry->{revision}" unless $::_q > 1;
 	if (defined $log_entry->{svm_revision}) {
-		 print " (\@$log_entry->{svm_revision})";
+		 print " (\@$log_entry->{svm_revision})" unless $::_q > 1;
 		 $self->rev_map_set($log_entry->{svm_revision}, $commit,
 		                   0, $self->svm_uuid);
 	}
-	print " = $commit ($self->{ref_id})\n";
+	print " = $commit ($self->{ref_id})\n" unless $::_q > 1;
 	if (--$_gc_nr == 0) {
 		$_gc_nr = $_gc_period;
 		gc();
@@ -2248,7 +2401,10 @@
 	if (my $path = $paths->{"/$self->{path}"}) {
 		return ($path->{action} eq 'D') ? 0 : 1;
 	}
-	$self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
+	my $repos_root = $self->ra->{repos_root};
+	my $extended_path = $self->{url} . '/' . $self->{path};
+	$extended_path =~ s#^\Q$repos_root\E(/|$)##;
+	$self->{path_regex} ||= qr/^\/\Q$extended_path\E\//;
 	if (grep /$self->{path_regex}/, keys %$paths) {
 		return 1;
 	}
@@ -2301,22 +2457,8 @@
 	print STDERR  "Found possible branch point: ",
 	              "$new_url => ", $self->full_url, ", $r\n";
 	$branch_from =~ s#^/##;
-	my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
-	unless ($gs) {
-		my $ref_id = $self->{ref_id};
-		$ref_id =~ s/\@\d+$//;
-		$ref_id .= "\@$r";
-		# just grow a tail if we're not unique enough :x
-		$ref_id .= '-' while find_ref($ref_id);
-		print STDERR "Initializing parent: $ref_id\n";
-		my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
-		if ($u =~ s#^\Q$url\E(/|$)##) {
-			$p = $u;
-			$u = $url;
-			$repo_id = $self->{repo_id};
-		}
-		$gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
-	}
+	my $gs = $self->other_gs($new_url, $url, $repos_root,
+		                 $branch_from, $r, $self->{ref_id});
 	my ($r0, $parent) = $gs->find_rev_before($r, 1);
 	{
 		my ($base, $head);
@@ -2342,8 +2484,9 @@
 			# do_switch works with svn/trunk >= r22312, but that
 			# is not included with SVN 1.4.3 (the latest version
 			# at the moment), so we can't rely on it
+			$self->{last_rev} = $r0;
 			$self->{last_commit} = $parent;
-			$ed = SVN::Git::Fetcher->new($self);
+			$ed = SVN::Git::Fetcher->new($self, $gs->{path});
 			$gs->ra->gs_do_switch($r0, $rev, $gs,
 					      $self->full_url, $ed)
 			  or die "SVN connection failed somewhere...\n";
@@ -2441,12 +2584,83 @@
 	\@out;
 }
 
+# parse_svn_date(DATE)
+# --------------------
+# Given a date (in UTC) from Subversion, return a string in the format
+# "<TZ Offset> <local date/time>" that Git will use.
+#
+# By default the parsed date will be in UTC; if $Git::SVN::_localtime
+# is true we'll convert it to the local timezone instead.
 sub parse_svn_date {
 	my $date = shift || return '+0000 1970-01-01 00:00:00';
 	my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
-	                                    (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x) or
+	                                    (\d\d)\:(\d\d)\:(\d\d)\.\d*Z$/x) or
 	                                 croak "Unable to parse date: $date\n";
-	"+0000 $Y-$m-$d $H:$M:$S";
+	my $parsed_date;    # Set next.
+
+	if ($Git::SVN::_localtime) {
+		# Translate the Subversion datetime to an epoch time.
+		# Begin by switching ourselves to $date's timezone, UTC.
+		my $old_env_TZ = $ENV{TZ};
+		$ENV{TZ} = 'UTC';
+
+		my $epoch_in_UTC =
+		    POSIX::strftime('%s', $S, $M, $H, $d, $m - 1, $Y - 1900);
+
+		# Determine our local timezone (including DST) at the
+		# time of $epoch_in_UTC.  $Git::SVN::Log::TZ stored the
+		# value of TZ, if any, at the time we were run.
+		if (defined $Git::SVN::Log::TZ) {
+			$ENV{TZ} = $Git::SVN::Log::TZ;
+		} else {
+			delete $ENV{TZ};
+		}
+
+		my $our_TZ =
+		    POSIX::strftime('%Z', $S, $M, $H, $d, $m - 1, $Y - 1900);
+
+		# This converts $epoch_in_UTC into our local timezone.
+		my ($sec, $min, $hour, $mday, $mon, $year,
+		    $wday, $yday, $isdst) = localtime($epoch_in_UTC);
+
+		$parsed_date = sprintf('%s %04d-%02d-%02d %02d:%02d:%02d',
+				       $our_TZ, $year + 1900, $mon + 1,
+				       $mday, $hour, $min, $sec);
+
+		# Reset us to the timezone in effect when we entered
+		# this routine.
+		if (defined $old_env_TZ) {
+			$ENV{TZ} = $old_env_TZ;
+		} else {
+			delete $ENV{TZ};
+		}
+	} else {
+		$parsed_date = "+0000 $Y-$m-$d $H:$M:$S";
+	}
+
+	return $parsed_date;
+}
+
+sub other_gs {
+	my ($self, $new_url, $url, $repos_root,
+	    $branch_from, $r, $old_ref_id) = @_;
+	my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
+	unless ($gs) {
+		my $ref_id = $old_ref_id;
+		$ref_id =~ s/\@\d+$//;
+		$ref_id .= "\@$r";
+		# just grow a tail if we're not unique enough :x
+		$ref_id .= '-' while find_ref($ref_id);
+		print STDERR "Initializing parent: $ref_id\n";
+		my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
+		if ($u =~ s#^\Q$url\E(/|$)##) {
+			$p = $u;
+			$u = $url;
+			$repo_id = $self->{repo_id};
+		}
+		$gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+	}
+	$gs
 }
 
 sub check_author {
@@ -2614,9 +2828,9 @@
 sub rebuild {
 	my ($self) = @_;
 	my $map_path = $self->map_path;
-	return if (-e $map_path && ! -z $map_path);
+	my $partial = (-e $map_path && ! -z $map_path);
 	return unless ::verify_ref($self->refname.'^0');
-	if ($self->use_svm_props || $self->no_metadata) {
+	if (!$partial && ($self->use_svm_props || $self->no_metadata)) {
 		my $rev_db = $self->rev_db_path;
 		$self->rebuild_from_rev_db($rev_db);
 		if ($self->use_svm_props) {
@@ -2626,10 +2840,13 @@
 		$self->unlink_rev_db_symlink;
 		return;
 	}
-	print "Rebuilding $map_path ...\n";
+	print "Rebuilding $map_path ...\n" if (!$partial);
+	my ($base_rev, $head) = ($partial ? $self->rev_map_max_norebuild(1) :
+		(undef, undef));
 	my ($log, $ctx) =
 	    command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,
-	                        $self->refname, '--');
+				($head ? "$head.." : "") . $self->refname,
+				'--');
 	my $metadata_url = $self->metadata_url;
 	remove_username($metadata_url);
 	my $svn_uuid = $self->ra_uuid;
@@ -2652,12 +2869,17 @@
 		    ($metadata_url && $url && ($url ne $metadata_url))) {
 			next;
 		}
+		if ($partial && $head) {
+			print "Partial-rebuilding $map_path ...\n";
+			print "Currently at $base_rev = $head\n";
+			$head = undef;
+		}
 
 		$self->rev_map_set($rev, $c);
 		print "r$rev = $c\n";
 	}
 	command_close_pipe($log, $ctx);
-	print "Done rebuilding $map_path\n";
+	print "Done rebuilding $map_path\n" if (!$partial || !$head);
 	my $rev_db_path = $self->rev_db_path;
 	if (-f $self->rev_db_path) {
 		unlink $self->rev_db_path or croak "unlink: $!";
@@ -2797,6 +3019,12 @@
 sub rev_map_max {
 	my ($self, $want_commit) = @_;
 	$self->rebuild;
+	my ($r, $c) = $self->rev_map_max_norebuild($want_commit);
+	$want_commit ? ($r, $c) : $r;
+}
+
+sub rev_map_max_norebuild {
+	my ($self, $want_commit) = @_;
 	my $map_path = $self->map_path;
 	stat $map_path or return $want_commit ? (0, undef) : 0;
 	sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
@@ -3095,13 +3323,20 @@
 use Carp qw/croak/;
 use File::Temp qw/tempfile/;
 use IO::File qw//;
+use vars qw/$_ignore_regex/;
 
 # file baton members: path, mode_a, mode_b, pool, fh, blob, base
 sub new {
-	my ($class, $git_svn) = @_;
+	my ($class, $git_svn, $switch_path) = @_;
 	my $self = SVN::Delta::Editor->new;
 	bless $self, $class;
-	$self->{c} = $git_svn->{last_commit} if exists $git_svn->{last_commit};
+	if (exists $git_svn->{last_commit}) {
+		$self->{c} = $git_svn->{last_commit};
+		$self->{empty_symlinks} =
+		                  _mark_empty_symlinks($git_svn, $switch_path);
+	}
+	$self->{ignore_regex} = eval { command_oneline('config', '--get',
+			     "svn-remote.$git_svn->{repo_id}.ignore-paths") };
 	$self->{empty} = {};
 	$self->{dir_prop} = {};
 	$self->{file_prop} = {};
@@ -3111,6 +3346,70 @@
 	$self;
 }
 
+# this uses the Ra object, so it must be called before do_{switch,update},
+# not inside them (when the Git::SVN::Fetcher object is passed) to
+# do_{switch,update}
+sub _mark_empty_symlinks {
+	my ($git_svn, $switch_path) = @_;
+	my $bool = Git::config_bool('svn.brokenSymlinkWorkaround');
+	return {} if (!defined($bool)) || (defined($bool) && ! $bool);
+
+	my %ret;
+	my ($rev, $cmt) = $git_svn->last_rev_commit;
+	return {} unless ($rev && $cmt);
+
+	# allow the warning to be printed for each revision we fetch to
+	# ensure the user sees it.  The user can also disable the workaround
+	# on the repository even while git svn is running and the next
+	# revision fetched will skip this expensive function.
+	my $printed_warning;
+	chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
+	my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
+	local $/ = "\0";
+	my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path};
+	$pfx .= '/' if length($pfx);
+	while (<$ls>) {
+		chomp;
+		s/\A100644 blob $empty_blob\t//o or next;
+		unless ($printed_warning) {
+			print STDERR "Scanning for empty symlinks, ",
+			             "this may take a while if you have ",
+				     "many empty files\n",
+				     "You may disable this with `",
+				     "git config svn.brokenSymlinkWorkaround ",
+				     "false'.\n",
+				     "This may be done in a different ",
+				     "terminal without restarting ",
+				     "git svn\n";
+			$printed_warning = 1;
+		}
+		my $path = $_;
+		my (undef, $props) =
+		               $git_svn->ra->get_file($pfx.$path, $rev, undef);
+		if ($props->{'svn:special'}) {
+			$ret{$path} = 1;
+		}
+	}
+	command_close_pipe($ls, $ctx);
+	\%ret;
+}
+
+# returns true if a given path is inside a ".git" directory
+sub in_dot_git {
+	$_[0] =~ m{(?:^|/)\.git(?:/|$)};
+}
+
+# return value: 0 -- don't ignore, 1 -- ignore
+sub is_path_ignored {
+	my ($self, $path) = @_;
+	return 1 if in_dot_git($path);
+	return 1 if defined($self->{ignore_regex}) &&
+	            $path =~ m!$self->{ignore_regex}!;
+	return 0 unless defined($_ignore_regex);
+	return 1 if $path =~ m!$_ignore_regex!o;
+	return 0;
+}
+
 sub set_path_strip {
 	my ($self, $path) = @_;
 	$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
@@ -3136,20 +3435,24 @@
 
 sub delete_entry {
 	my ($self, $path, $rev, $pb) = @_;
+	return undef if $self->is_path_ignored($path);
 
 	my $gpath = $self->git_path($path);
 	return undef if ($gpath eq '');
 
 	# remove entire directories.
-	if (command('ls-tree', $self->{c}, '--', $gpath) =~ /^040000 tree/) {
+	my ($tree) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
+	                 =~ /\A040000 tree ([a-f\d]{40})\t\Q$gpath\E\0/);
+	if ($tree) {
 		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
 		                                     -r --name-only -z/,
-				                     $self->{c}, '--', $gpath);
+				                     $tree);
 		local $/ = "\0";
 		while (<$ls>) {
 			chomp;
-			$self->{gii}->remove($_);
-			print "\tD\t$_\n" unless $::_q;
+			my $rmpath = "$gpath/$_";
+			$self->{gii}->remove($rmpath);
+			print "\tD\t$rmpath\n" unless $::_q;
 		}
 		print "\tD\t$gpath/\n" unless $::_q;
 		command_close_pipe($ls, $ctx);
@@ -3163,26 +3466,40 @@
 
 sub open_file {
 	my ($self, $path, $pb, $rev) = @_;
+	my ($mode, $blob);
+
+	goto out if $self->is_path_ignored($path);
+
 	my $gpath = $self->git_path($path);
-	my ($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath)
-	                     =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
+	($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
+	                     =~ /\A(\d{6}) blob ([a-f\d]{40})\t\Q$gpath\E\0/);
 	unless (defined $mode && defined $blob) {
 		die "$path was not found in commit $self->{c} (r$rev)\n";
 	}
+	if ($mode eq '100644' && $self->{empty_symlinks}->{$path}) {
+		$mode = '120000';
+	}
+out:
 	{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
 	  pool => SVN::Pool->new, action => 'M' };
 }
 
 sub add_file {
 	my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
-	my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
-	delete $self->{empty}->{$dir};
-	{ path => $path, mode_a => 100644, mode_b => 100644,
+	my $mode;
+
+	if (!$self->is_path_ignored($path)) {
+		my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
+		delete $self->{empty}->{$dir};
+		$mode = '100644';
+	}
+	{ path => $path, mode_a => $mode, mode_b => $mode,
 	  pool => SVN::Pool->new, action => 'A' };
 }
 
 sub add_directory {
 	my ($self, $path, $cp_path, $cp_rev) = @_;
+	goto out if $self->is_path_ignored($path);
 	my $gpath = $self->git_path($path);
 	if ($gpath eq '') {
 		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
@@ -3200,11 +3517,13 @@
 	my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
 	delete $self->{empty}->{$dir};
 	$self->{empty}->{$path} = 1;
+out:
 	{ path => $path };
 }
 
 sub change_dir_prop {
 	my ($self, $db, $prop, $value) = @_;
+	return undef if $self->is_path_ignored($db->{path});
 	$self->{dir_prop}->{$db->{path}} ||= {};
 	$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
 	undef;
@@ -3212,6 +3531,7 @@
 
 sub absent_directory {
 	my ($self, $path, $pb) = @_;
+	return undef if $self->is_path_ignored($path);
 	$self->{absent_dir}->{$pb->{path}} ||= [];
 	push @{$self->{absent_dir}->{$pb->{path}}}, $path;
 	undef;
@@ -3219,6 +3539,7 @@
 
 sub absent_file {
 	my ($self, $path, $pb) = @_;
+	return undef if $self->is_path_ignored($path);
 	$self->{absent_file}->{$pb->{path}} ||= [];
 	push @{$self->{absent_file}->{$pb->{path}}}, $path;
 	undef;
@@ -3226,6 +3547,7 @@
 
 sub change_file_prop {
 	my ($self, $fb, $prop, $value) = @_;
+	return undef if $self->is_path_ignored($fb->{path});
 	if ($prop eq 'svn:executable') {
 		if ($fb->{mode_b} != 120000) {
 			$fb->{mode_b} = defined $value ? 100755 : 100644;
@@ -3241,22 +3563,43 @@
 
 sub apply_textdelta {
 	my ($self, $fb, $exp) = @_;
-	my $fh = Git::temp_acquire('svn_delta');
+	return undef if $self->is_path_ignored($fb->{path});
+	my $fh = $::_repository->temp_acquire('svn_delta');
 	# $fh gets auto-closed() by SVN::TxDelta::apply(),
 	# (but $base does not,) so dup() it for reading in close_file
 	open my $dup, '<&', $fh or croak $!;
-	my $base = Git::temp_acquire('git_blob');
+	my $base = $::_repository->temp_acquire('git_blob');
+
 	if ($fb->{blob}) {
-		print $base 'link ' if ($fb->{mode_a} == 120000);
-		my $size = $::_repository->cat_blob($fb->{blob}, $base);
+		my ($base_is_link, $size);
+
+		if ($fb->{mode_a} eq '120000' &&
+		    ! $self->{empty_symlinks}->{$fb->{path}}) {
+			print $base 'link ' or die "print $!\n";
+			$base_is_link = 1;
+		}
+	retry:
+		$size = $::_repository->cat_blob($fb->{blob}, $base);
 		die "Failed to read object $fb->{blob}" if ($size < 0);
 
 		if (defined $exp) {
 			seek $base, 0, 0 or croak $!;
 			my $got = ::md5sum($base);
-			die "Checksum mismatch: $fb->{path} $fb->{blob}\n",
-			    "expected: $exp\n",
-			    "     got: $got\n" if ($got ne $exp);
+			if ($got ne $exp) {
+				my $err = "Checksum mismatch: ".
+				       "$fb->{path} $fb->{blob}\n" .
+				       "expected: $exp\n" .
+				       "     got: $got\n";
+				if ($base_is_link) {
+					warn $err,
+					     "Retrying... (possibly ",
+					     "a bad symlink from SVN)\n";
+					$::_repository->temp_reset($base);
+					$base_is_link = 0;
+					goto retry;
+				}
+				die $err;
+			}
 		}
 	}
 	seek $base, 0, 0 or croak $!;
@@ -3267,6 +3610,8 @@
 
 sub close_file {
 	my ($self, $fb, $exp) = @_;
+	return undef if $self->is_path_ignored($fb->{path});
+
 	my $hash;
 	my $path = $self->git_path($fb->{path});
 	if (my $fh = $fb->{fh}) {
@@ -3280,13 +3625,22 @@
 		}
 		if ($fb->{mode_b} == 120000) {
 			sysseek($fh, 0, 0) or croak $!;
-			sysread($fh, my $buf, 5) == 5 or croak $!;
+			my $rd = sysread($fh, my $buf, 5);
 
-			unless ($buf eq 'link ') {
+			if (!defined $rd) {
+				croak "sysread: $!\n";
+			} elsif ($rd == 0) {
 				warn "$path has mode 120000",
-						" but is not a link\n";
+				     " but it points to nothing\n",
+				     "converting to an empty file with mode",
+				     " 100644\n";
+				$fb->{mode_b} = '100644';
+			} elsif ($buf ne 'link ') {
+				warn "$path has mode 120000",
+				     " but is not a link\n";
 			} else {
-				my $tmp_fh = Git::temp_acquire('svn_hash');
+				my $tmp_fh = $::_repository->temp_acquire(
+					'svn_hash');
 				my $res;
 				while ($res = sysread($fh, my $str, 1024)) {
 					my $out = syswrite($tmp_fh, $str, $res);
@@ -3388,11 +3742,12 @@
 	while (<$diff_fh>) {
 		chomp $_; # this gets rid of the trailing "\0"
 		if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
-					$::sha1\s($::sha1)\s
+					($::sha1)\s($::sha1)\s
 					([MTCRAD])\d*$/xo) {
 			push @mods, {	mode_a => $1, mode_b => $2,
-					sha1_b => $3, chg => $4 };
-			if ($4 =~ /^(?:C|R)$/) {
+					sha1_a => $3, sha1_b => $4,
+					chg => $5 };
+			if ($5 =~ /^(?:C|R)$/) {
 				$state = 'file_a';
 			} else {
 				$state = 'file_b';
@@ -3465,7 +3820,7 @@
 sub url_path {
 	my ($self, $path) = @_;
 	if ($self->{url} =~ m#^https?://#) {
-		$path =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+		$path =~ s/([^~a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
 	}
 	$self->{url} . '/' . $self->repo_path($path);
 }
@@ -3644,6 +3999,7 @@
 	my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
 				$self->url_path($m->{file_a}), $self->{r});
 	print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
+	$self->apply_autoprops($file, $fbat);
 	$self->chg_file($fbat, $m);
 	$self->close_file($fbat,undef,$self->{pool});
 
@@ -3670,6 +4026,27 @@
 	$self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool});
 }
 
+sub _chg_file_get_blob ($$$$) {
+	my ($self, $fbat, $m, $which) = @_;
+	my $fh = $::_repository->temp_acquire("git_blob_$which");
+	if ($m->{"mode_$which"} =~ /^120/) {
+		print $fh 'link ' or croak $!;
+		$self->change_file_prop($fbat,'svn:special','*');
+	} elsif ($m->{mode_a} =~ /^120/ && $m->{"mode_$which"} !~ /^120/) {
+		$self->change_file_prop($fbat,'svn:special',undef);
+	}
+	my $blob = $m->{"sha1_$which"};
+	return ($fh,) if ($blob =~ /^0{40}$/);
+	my $size = $::_repository->cat_blob($blob, $fh);
+	croak "Failed to read object $blob" if ($size < 0);
+	$fh->flush == 0 or croak $!;
+	seek $fh, 0, 0 or croak $!;
+
+	my $exp = ::md5sum($fh);
+	seek $fh, 0, 0 or croak $!;
+	return ($fh, $exp);
+}
+
 sub chg_file {
 	my ($self, $fbat, $m) = @_;
 	if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) {
@@ -3677,26 +4054,24 @@
 	} elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
 		$self->change_file_prop($fbat,'svn:executable',undef);
 	}
-	my $fh = Git::temp_acquire('git_blob');
-	if ($m->{mode_b} =~ /^120/) {
-		print $fh 'link ' or croak $!;
-		$self->change_file_prop($fbat,'svn:special','*');
-	} elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
-		$self->change_file_prop($fbat,'svn:special',undef);
-	}
-	my $size = $::_repository->cat_blob($m->{sha1_b}, $fh);
-	croak "Failed to read object $m->{sha1_b}" if ($size < 0);
-	$fh->flush == 0 or croak $!;
-	seek $fh, 0, 0 or croak $!;
-
-	my $exp = ::md5sum($fh);
-	seek $fh, 0, 0 or croak $!;
-
+	my ($fh_a, $exp_a) = _chg_file_get_blob $self, $fbat, $m, 'a';
+	my ($fh_b, $exp_b) = _chg_file_get_blob $self, $fbat, $m, 'b';
 	my $pool = SVN::Pool->new;
-	my $atd = $self->apply_textdelta($fbat, undef, $pool);
-	my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
-	die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
-	Git::temp_release($fh, 1);
+	my $atd = $self->apply_textdelta($fbat, $exp_a, $pool);
+	if (-s $fh_a) {
+		my $txstream = SVN::TxDelta::new ($fh_a, $fh_b, $pool);
+		my $res = SVN::TxDelta::send_txstream($txstream, @$atd, $pool);
+		if (defined $res) {
+			die "Unexpected result from send_txstream: $res\n",
+			    "(SVN::Core::VERSION: $SVN::Core::VERSION)\n";
+		}
+	} else {
+		my $got = SVN::TxDelta::send_stream($fh_b, @$atd, $pool);
+		die "Checksum mismatch\nexpected: $exp_b\ngot: $got\n"
+		    if ($got ne $exp_b);
+	}
+	Git::temp_release($fh_b, 1);
+	Git::temp_release($fh_a, 1);
 	$pool->clear;
 }
 
@@ -3762,7 +4137,8 @@
 BEGIN {
 	# enforce temporary pool usage for some simple functions
 	no strict 'refs';
-	for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) {
+	for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root
+	              get_file/) {
 		my $SUPER = "SUPER::$f";
 		*$f = sub {
 			my $self = shift;
@@ -3798,7 +4174,7 @@
 	my ($uri) = @_;
 	my @tmp;
 	foreach (split m{/}, $uri) {
-		s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+		s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
 		push @tmp, $_;
 	}
 	join('/', @tmp);
@@ -3898,10 +4274,23 @@
 	# do not call the real DESTROY since we store ourselves in $RA
 }
 
+# get_log(paths, start, end, limit,
+#         discover_changed_paths, strict_node_history, receiver)
 sub get_log {
 	my ($self, @args) = @_;
 	my $pool = SVN::Pool->new;
-	splice(@args, 3, 1) if ($SVN::Core::VERSION le '1.2.0');
+
+	# the limit parameter was not supported in SVN 1.1.x, so we
+	# drop it.  Therefore, the receiver callback passed to it
+	# is made aware of this limitation by being wrapped if
+	# the limit passed to is being wrapped.
+	if ($SVN::Core::VERSION le '1.2.0') {
+		my $limit = splice(@args, 3, 1);
+		if ($limit > 0) {
+			my $receiver = pop @args;
+			push(@args, sub { &$receiver(@_) if (--$limit >= 0) });
+		}
+	}
 	my $ret = $self->SUPER::get_log(@args, $pool);
 	$pool->clear;
 	$ret;
@@ -4064,6 +4453,9 @@
 		}
 		$self->get_log([$longest_path], $min, $max, 0, 1, 1,
 		               sub { $revs{$_[1]} = _cb(@_) });
+		if ($err) {
+			print "Checked through r$max\r";
+		}
 		if ($err && $max >= $head) {
 			print STDERR "Path '$longest_path' ",
 				     "was probably deleted:\n",
@@ -4298,6 +4690,7 @@
 use strict;
 use warnings;
 use POSIX qw/strftime/;
+use Time::Local;
 use constant commit_log_separator => ('-' x 72) . "\n";
 use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline
             %rusers $show_commit $incremental/;
@@ -4404,7 +4797,12 @@
 }
 
 sub format_svn_date {
-	return strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", localtime(shift));
+	# some systmes don't handle or mishandle %z, so be creative.
+	my $t = shift || time;
+	my $gm = timelocal(gmtime($t));
+	my $sign = qw( + + - )[ $t <=> $gm ];
+	my $gmoff = sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
+	return strftime("%Y-%m-%d %H:%M:%S $gmoff (%a, %d %b %Y)", localtime($t));
 }
 
 sub parse_git_date {
@@ -4646,11 +5044,22 @@
 						  '--', $path);
 		my ($sha1);
 		my %authors;
+		my @buffer;
+		my %dsha; #distinct sha keys
+
 		while (my $line = <$fh>) {
+			push @buffer, $line;
 			if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
-				$sha1 = $1;
-				(undef, $rev, undef) = ::cmt_metadata($1);
-				$rev = '0' if (!$rev);
+				$dsha{$1} = 1;
+			}
+		}
+
+		my $s2r = ::cmt_sha2rev_batch([keys %dsha]);
+
+		foreach my $line (@buffer) {
+			if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
+				$rev = $s2r->{$1};
+				$rev = '0' if (!$rev)
 			}
 			elsif ($line =~ /^author (.*)/) {
 				$authors{$rev} = $1;
@@ -4894,8 +5303,7 @@
 		}
 	}
 	if (@emptied) {
-		my $file = $ENV{GIT_CONFIG} || $ENV{GIT_CONFIG_LOCAL} ||
-		           "$ENV{GIT_DIR}/config";
+		my $file = $ENV{GIT_CONFIG} || "$ENV{GIT_DIR}/config";
 		print STDERR <<EOF;
 The following [svn-remote] sections in your config file ($file) are empty
 and can be safely removed:
diff --git a/git-web--browse.sh b/git-web--browse.sh
index 384148a..7ed0fad 100755
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -31,7 +31,7 @@
 
 valid_tool() {
 	case "$1" in
-		firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open)
+		firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open | start)
 			;; # happy
 		*)
 			valid_custom_tool "$1" || return 1
@@ -114,6 +114,10 @@
     if test -n "$SECURITYSESSIONID"; then
 	browser_candidates="open $browser_candidates"
     fi
+    # /bin/start indicates MinGW
+    if test -x /bin/start; then
+	browser_candidates="start $browser_candidates"
+    fi
 
     for i in $browser_candidates; do
 	init_browser_path $i
@@ -157,7 +161,7 @@
 		;;
 	esac
 	;;
-    w3m|links|lynx|open)
+    w3m|links|lynx|open|start)
 	eval "$browser_path" "$@"
 	;;
     dillo)
diff --git a/git.c b/git.c
index 5582c51..bfb6508 100644
--- a/git.c
+++ b/git.c
@@ -2,9 +2,10 @@
 #include "exec_cmd.h"
 #include "cache.h"
 #include "quote.h"
+#include "run-command.h"
 
 const char git_usage_string[] =
-	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
+	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
 
 const char git_more_info_string[] =
 	"See 'git help COMMAND' for more information on a specific command.";
@@ -74,6 +75,9 @@
 				puts(git_exec_path());
 				exit(0);
 			}
+		} else if (!strcmp(cmd, "--html-path")) {
+			puts(system_path(GIT_HTML_PATH));
+			exit(0);
 		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
 			use_pager = 1;
 		} else if (!strcmp(cmd, "--no-pager")) {
@@ -158,7 +162,7 @@
 			if (ret >= 0 && WIFEXITED(ret) &&
 			    WEXITSTATUS(ret) != 127)
 				exit(WEXITSTATUS(ret));
-			die("Failed to run '%s' when expanding alias '%s'\n",
+			die("Failed to run '%s' when expanding alias '%s'",
 			    alias_string + 1, alias_command);
 		}
 		count = split_cmdline(alias_string, &new_argv);
@@ -195,8 +199,8 @@
 		ret = 1;
 	}
 
-	if (subdir)
-		chdir(subdir);
+	if (subdir && chdir(subdir))
+		die("Cannot change to %s: %s", subdir, strerror(errno));
 
 	errno = saved_errno;
 
@@ -219,7 +223,7 @@
 	int option;
 };
 
-static int run_command(struct cmd_struct *p, int argc, const char **argv)
+static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 {
 	int status;
 	struct stat st;
@@ -266,9 +270,11 @@
 	const char *cmd = argv[0];
 	static struct cmd_struct commands[] = {
 		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
 		{ "annotate", cmd_annotate, RUN_SETUP },
 		{ "apply", cmd_apply },
 		{ "archive", cmd_archive },
+		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
 		{ "blame", cmd_blame, RUN_SETUP },
 		{ "branch", cmd_branch, RUN_SETUP },
 		{ "bundle", cmd_bundle },
@@ -330,6 +336,7 @@
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
 		{ "push", cmd_push, RUN_SETUP },
 		{ "read-tree", cmd_read_tree, RUN_SETUP },
+		{ "receive-pack", cmd_receive_pack },
 		{ "reflog", cmd_reflog, RUN_SETUP },
 		{ "remote", cmd_remote, RUN_SETUP },
 		{ "repo-config", cmd_config },
@@ -366,7 +373,7 @@
 	if (sizeof(ext) > 1) {
 		i = strlen(argv[0]) - strlen(ext);
 		if (i > 0 && !strcmp(argv[0] + i, ext)) {
-			char *argv0 = strdup(argv[0]);
+			char *argv0 = xstrdup(argv[0]);
 			argv[0] = cmd = argv0;
 			argv0[i] = '\0';
 		}
@@ -382,16 +389,16 @@
 		struct cmd_struct *p = commands+i;
 		if (strcmp(p->cmd, cmd))
 			continue;
-		exit(run_command(p, argc, argv));
+		exit(run_builtin(p, argc, argv));
 	}
 }
 
 static void execv_dashed_external(const char **argv)
 {
-	struct strbuf cmd;
+	struct strbuf cmd = STRBUF_INIT;
 	const char *tmp;
+	int status;
 
-	strbuf_init(&cmd, 0);
 	strbuf_addf(&cmd, "git-%s", argv[0]);
 
 	/*
@@ -405,36 +412,54 @@
 
 	trace_argv_printf(argv, "trace: exec:");
 
-	/* execvp() can only ever return if it fails */
-	execvp(cmd.buf, (char **)argv);
-
-	trace_printf("trace: exec failed: %s\n", strerror(errno));
+	/*
+	 * if we fail because the command is not found, it is
+	 * OK to return. Otherwise, we just pass along the status code.
+	 */
+	status = run_command_v_opt(argv, 0);
+	if (status != -ERR_RUN_COMMAND_EXEC) {
+		if (IS_RUN_COMMAND_ERR(status))
+			die("unable to run '%s'", argv[0]);
+		exit(-status);
+	}
+	errno = ENOENT; /* as if we called execvp */
 
 	argv[0] = tmp;
 
 	strbuf_release(&cmd);
 }
 
+static int run_argv(int *argcp, const char ***argv)
+{
+	int done_alias = 0;
+
+	while (1) {
+		/* See if it's an internal command */
+		handle_internal_command(*argcp, *argv);
+
+		/* .. then try the external ones */
+		execv_dashed_external(*argv);
+
+		/* It could be an alias -- this works around the insanity
+		 * of overriding "git log" with "git show" by having
+		 * alias.log = show
+		 */
+		if (done_alias || !handle_alias(argcp, argv))
+			break;
+		done_alias = 1;
+	}
+
+	return done_alias;
+}
+
 
 int main(int argc, const char **argv)
 {
-	const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
-	char *slash = (char *)cmd + strlen(cmd);
-	int done_alias = 0;
+	const char *cmd;
 
-	/*
-	 * Take the basename of argv[0] as the command
-	 * name, and the dirname as the default exec_path
-	 * if we don't have anything better.
-	 */
-	do
-		--slash;
-	while (cmd <= slash && !is_dir_sep(*slash));
-	if (cmd <= slash) {
-		*slash++ = 0;
-		git_set_argv0_path(cmd);
-		cmd = slash;
-	}
+	cmd = git_extract_argv0_path(argv[0]);
+	if (!cmd)
+		cmd = "git-help";
 
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
@@ -479,29 +504,22 @@
 	setup_path();
 
 	while (1) {
-		/* See if it's an internal command */
-		handle_internal_command(argc, argv);
-
-		/* .. then try the external ones */
-		execv_dashed_external(argv);
-
-		/* It could be an alias -- this works around the insanity
-		 * of overriding "git log" with "git show" by having
-		 * alias.log = show
-		 */
-		if (done_alias || !handle_alias(&argc, &argv))
+		static int done_help = 0;
+		static int was_alias = 0;
+		was_alias = run_argv(&argc, &argv);
+		if (errno != ENOENT)
 			break;
-		done_alias = 1;
-	}
-
-	if (errno == ENOENT) {
-		if (done_alias) {
+		if (was_alias) {
 			fprintf(stderr, "Expansion of alias '%s' failed; "
 				"'%s' is not a git-command\n",
 				cmd, argv[0]);
 			exit(1);
 		}
-		help_unknown_cmd(cmd);
+		if (!done_help) {
+			cmd = argv[0] = help_unknown_cmd(cmd);
+			done_help = 1;
+		} else
+			break;
 	}
 
 	fprintf(stderr, "Failed to run command '%s': %s\n",
diff --git a/git.spec.in b/git.spec.in
index 0319c82..4be0834 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -168,6 +168,7 @@
 %defattr(-,root,root)
 %{_libexecdir}/git-core/git-gui
 %{_libexecdir}/git-core/git-citool
+%{_libexecdir}/git-core/git-gui--askpass
 %{_datadir}/git-gui/
 %{!?_without_docs: %{_mandir}/man1/git-gui.1*}
 %{!?_without_docs: %doc Documentation/git-gui.html}
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 087c4ac..1773ae6 100644
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -155,18 +155,16 @@
 		set origargs [lreplace $origargs $i $i]
 		incr i -1
 	    }
-	    # These request or affect diff output, which we don't want.
-	    # Some could be used to set our defaults for diff display.
 	    "-[puabwcrRBMC]" -
 	    "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
 	    "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
 	    "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
 	    "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
 	    "--ignore-space-change" - "-U*" - "--unified=*" {
+		# These request or affect diff output, which we don't want.
+		# Some could be used to set our defaults for diff display.
 		lappend diffargs $arg
 	    }
-	    # These cause our parsing of git log's output to fail, or else
-	    # they're options we want to set ourselves, so ignore them.
 	    "--raw" - "--patch-with-raw" - "--patch-with-stat" -
 	    "--name-only" - "--name-status" - "--color" - "--color-words" -
 	    "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
@@ -174,36 +172,34 @@
 	    "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
 	    "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
 	    "--objects" - "--objects-edge" - "--reverse" {
+		# These cause our parsing of git log's output to fail, or else
+		# they're options we want to set ourselves, so ignore them.
 	    }
-	    # These are harmless, and some are even useful
 	    "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
 	    "--check" - "--exit-code" - "--quiet" - "--topo-order" -
 	    "--full-history" - "--dense" - "--sparse" -
 	    "--follow" - "--left-right" - "--encoding=*" {
+		# These are harmless, and some are even useful
 		lappend glflags $arg
 	    }
-	    # These mean that we get a subset of the commits
 	    "--diff-filter=*" - "--no-merges" - "--unpacked" -
 	    "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
 	    "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
 	    "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
 	    "--remove-empty" - "--first-parent" - "--cherry-pick" -
-	    "-S*" - "--pickaxe-all" - "--pickaxe-regex" - {
+	    "-S*" - "--pickaxe-all" - "--pickaxe-regex" {
+		# These mean that we get a subset of the commits
 		set filtered 1
 		lappend glflags $arg
 	    }
-	    # This appears to be the only one that has a value as a
-	    # separate word following it
 	    "-n" {
+		# This appears to be the only one that has a value as a
+		# separate word following it
 		set filtered 1
 		set nextisval 1
 		lappend glflags $arg
 	    }
-	    "--not" {
-		set notflag [expr {!$notflag}]
-		lappend revargs $arg
-	    }
-	    "--all" {
+	    "--not" - "--all" {
 		lappend revargs $arg
 	    }
 	    "--merge" {
@@ -211,8 +207,8 @@
 		# git rev-parse doesn't understand --merge
 		lappend revargs --gitk-symmetric-diff-marker MERGE_HEAD...HEAD
 	    }
-	    # Other flag arguments including -<n>
 	    "-*" {
+		# Other flag arguments including -<n>
 		if {[string is digit -strict [string range $arg 1 end]]} {
 		    set filtered 1
 		} else {
@@ -222,8 +218,8 @@
 		}
 		lappend glflags $arg
 	    }
-	    # Non-flag arguments specify commits or ranges of commits
 	    default {
+		# Non-flag arguments specify commits or ranges of commits
 		if {[string match "*...*" $arg]} {
 		    lappend revargs --gitk-symmetric-diff-marker
 		}
@@ -269,7 +265,7 @@
 		lappend badrev $line
 	    }
 	}		    
-	error_popup "Error parsing revisions: $err"
+	error_popup "[mc "Error parsing revisions:"] $err"
 	return {}
     }
     set ret {}
@@ -307,9 +303,9 @@
     global startmsecs commitidx viewcomplete curview
     global tclencoding
     global viewargs viewargscmd viewfiles vfilelimit
-    global showlocalchanges commitinterest
+    global showlocalchanges
     global viewactive viewinstances vmergeonly
-    global mainheadid
+    global mainheadid viewmainheadid viewmainheadid_orig
     global vcanopt vflags vrevs vorigargs
 
     set startmsecs [clock clicks -milliseconds]
@@ -324,7 +320,7 @@
 	if {[catch {
 	    set str [exec sh -c $viewargscmd($view)]
 	} err]} {
-	    error_popup "Error executing --argscmd command: $err"
+	    error_popup "[mc "Error executing --argscmd command:"] $err"
 	    return 0
 	}
 	set args [concat $args [split $str "\n"]]
@@ -367,8 +363,13 @@
     }
     set i [reg_instance $fd]
     set viewinstances($view) [list $i]
-    if {$showlocalchanges && $mainheadid ne {}} {
-	lappend commitinterest($mainheadid) {dodiffindex}
+    set viewmainheadid($view) $mainheadid
+    set viewmainheadid_orig($view) $mainheadid
+    if {$files ne {} && $mainheadid ne {}} {
+	get_viewmainhead $view
+    }
+    if {$showlocalchanges && $viewmainheadid($view) ne {}} {
+	interestedin $viewmainheadid($view) dodiffindex
     }
     fconfigure $fd -blocking 0 -translation lf -eofchar {}
     if {$tclencoding != {}} {
@@ -418,10 +419,12 @@
 }
 
 proc reset_pending_select {selid} {
-    global pending_select mainheadid
+    global pending_select mainheadid selectheadid
 
     if {$selid ne {}} {
 	set pending_select $selid
+    } elseif {$selectheadid ne {}} {
+	set pending_select $selectheadid
     } else {
 	set pending_select $mainheadid
     }
@@ -444,22 +447,26 @@
     global curview vcanopt vorigargs vfilelimit viewinstances
     global viewactive viewcomplete tclencoding
     global startmsecs showneartags showlocalchanges
-    global mainheadid pending_select
+    global mainheadid viewmainheadid viewmainheadid_orig pending_select
     global isworktree
     global varcid vposids vnegids vflags vrevs
 
     set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
-    set oldmainid $mainheadid
     rereadrefs
-    if {$showlocalchanges} {
-	if {$mainheadid ne $oldmainid} {
+    set view $curview
+    if {$mainheadid ne $viewmainheadid_orig($view)} {
+	if {$showlocalchanges} {
 	    dohidelocalchanges
 	}
-	if {[commitinview $mainheadid $curview]} {
-	    dodiffindex
+	set viewmainheadid($view) $mainheadid
+	set viewmainheadid_orig($view) $mainheadid
+	if {$vfilelimit($view) ne {}} {
+	    get_viewmainhead $view
 	}
     }
-    set view $curview
+    if {$showlocalchanges} {
+	doshowlocalchanges
+    }
     if {$vcanopt($view)} {
 	set oldpos $vposids($view)
 	set oldneg $vnegids($view)
@@ -498,7 +505,7 @@
 	set fd [open [concat | git log --no-color -z --pretty=raw --parents \
 			  --boundary $args "--" $vfilelimit($view)] r]
     } err]} {
-	error_popup "Error executing git log: $err"
+	error_popup "[mc "Error executing git log:"] $err"
 	return
     }
     if {$viewactive($view) == 0} {
@@ -694,16 +701,17 @@
 }
 
 proc splitvarc {p v} {
-    global varcid varcstart varccommits varctok
+    global varcid varcstart varccommits varctok vtokmod
     global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
 
     set oa $varcid($v,$p)
+    set otok [lindex $varctok($v) $oa]
     set ac $varccommits($v,$oa)
     set i [lsearch -exact $varccommits($v,$oa) $p]
     if {$i <= 0} return
     set na [llength $varctok($v)]
     # "%" sorts before "0"...
-    set tok "[lindex $varctok($v) $oa]%[strrep $i]"
+    set tok "$otok%[strrep $i]"
     lappend varctok($v) $tok
     lappend varcrow($v) {}
     lappend varcix($v) {}
@@ -723,6 +731,9 @@
     for {set b [lindex $vdownptr($v) $na]} {$b != 0} {set b [lindex $vleftptr($v) $b]} {
 	lset vupptr($v) $b $na
     }
+    if {[string compare $otok $vtokmod($v)] <= 0} {
+	modify_arc $v $oa
+    }
 }
 
 proc renumbervarc {a v} {
@@ -1229,7 +1240,7 @@
 
 proc closevarcs {v} {
     global varctok varccommits varcid parents children
-    global cmitlisted commitidx commitinterest vtokmod
+    global cmitlisted commitidx vtokmod
 
     set missing_parents 0
     set scripts {}
@@ -1254,12 +1265,7 @@
 	    }
 	    lappend varccommits($v,$b) $p
 	    incr commitidx($v)
-	    if {[info exists commitinterest($p)]} {
-		foreach script $commitinterest($p) {
-		    lappend scripts [string map [list "%I" $p] $script]
-		}
-		unset commitinterest($id)
-	    }
+	    set scripts [check_interest $p $scripts]
 	}
     }
     if {$missing_parents > 0} {
@@ -1295,8 +1301,41 @@
     }
 }
 
+# Mechanism for registering a command to be executed when we come
+# across a particular commit.  To handle the case when only the
+# prefix of the commit is known, the commitinterest array is now
+# indexed by the first 4 characters of the ID.  Each element is a
+# list of id, cmd pairs.
+proc interestedin {id cmd} {
+    global commitinterest
+
+    lappend commitinterest([string range $id 0 3]) $id $cmd
+}
+
+proc check_interest {id scripts} {
+    global commitinterest
+
+    set prefix [string range $id 0 3]
+    if {[info exists commitinterest($prefix)]} {
+	set newlist {}
+	foreach {i script} $commitinterest($prefix) {
+	    if {[string match "$i*" $id]} {
+		lappend scripts [string map [list "%I" $id "%P" $i] $script]
+	    } else {
+		lappend newlist $i $script
+	    }
+	}
+	if {$newlist ne {}} {
+	    set commitinterest($prefix) $newlist
+	} else {
+	    unset commitinterest($prefix)
+	}
+    }
+    return $scripts
+}
+
 proc getcommitlines {fd inst view updating}  {
-    global cmitlisted commitinterest leftover
+    global cmitlisted leftover
     global commitidx commitdata vdatemode
     global parents children curview hlview
     global idpending ordertok
@@ -1472,12 +1511,7 @@
 	    incr i
 	}
 
-	if {[info exists commitinterest($id)]} {
-	    foreach script $commitinterest($id) {
-		lappend scripts [string map [list "%I" $id] $script]
-	    }
-	    unset commitinterest($id)
-	}
+	set scripts [check_interest $id $scripts]
 	set gotsome 1
     }
     if {$gotsome} {
@@ -1530,9 +1564,27 @@
     return 0
 }
 
+proc do_readcommit {id} {
+    global tclencoding
+
+    # Invoke git-log to handle automatic encoding conversion
+    set fd [open [concat | git log --no-color --pretty=raw -1 $id] r]
+    # Read the results using i18n.logoutputencoding
+    fconfigure $fd -translation lf -eofchar {}
+    if {$tclencoding != {}} {
+	fconfigure $fd -encoding $tclencoding
+    }
+    set contents [read $fd]
+    close $fd
+    # Remove the heading line
+    regsub {^commit [0-9a-f]+\n} $contents {} contents
+
+    return $contents
+}
+
 proc readcommit {id} {
-    if {[catch {set contents [exec git cat-file commit $id]}]} return
-    parsecommit $id $contents 0
+    if {[catch {set contents [do_readcommit $id]}]} return
+    parsecommit $id $contents 1
 }
 
 proc parsecommit {id contents listed} {
@@ -1553,13 +1605,14 @@
     set header [string range $contents 0 [expr {$hdrend - 1}]]
     set comment [string range $contents [expr {$hdrend + 2}] end]
     foreach line [split $header "\n"] {
+	set line [split $line " "]
 	set tag [lindex $line 0]
 	if {$tag == "author"} {
 	    set audate [lindex $line end-1]
-	    set auname [lrange $line 1 end-2]
+	    set auname [join [lrange $line 1 end-2] " "]
 	} elseif {$tag == "committer"} {
 	    set comdate [lindex $line end-1]
-	    set comname [lrange $line 1 end-2]
+	    set comname [join [lrange $line 1 end-2] " "]
 	}
     }
     set headline {}
@@ -1606,9 +1659,23 @@
     return 1
 }
 
+# Expand an abbreviated commit ID to a list of full 40-char IDs that match
+# and are present in the current view.
+# This is fairly slow...
+proc longid {prefix} {
+    global varcid curview
+
+    set ids {}
+    foreach match [array names varcid "$curview,$prefix*"] {
+	lappend ids [lindex [split $match ","] 1]
+    }
+    return $ids
+}
+
 proc readrefs {} {
     global tagids idtags headids idheads tagobjid
     global otherrefids idotherrefs mainhead mainheadid
+    global selecthead selectheadid
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
 	catch {unset $v}
@@ -1655,6 +1722,12 @@
 	    set mainhead [string range $thehead 11 end]
 	}
     }
+    set selectheadid {}
+    if {$selecthead ne {}} {
+	catch {
+	    set selectheadid [exec git rev-parse --verify $selecthead]
+	}
+    }
 }
 
 # skip over fake commits
@@ -1694,6 +1767,24 @@
     unset headids($name)
 }
 
+proc make_transient {window origin} {
+    global have_tk85
+
+    # In MacOS Tk 8.4 transient appears to work by setting
+    # overrideredirect, which is utterly useless, since the
+    # windows get no border, and are not even kept above
+    # the parent.
+    if {!$have_tk85 && [tk windowingsystem] eq {aqua}} return
+
+    wm transient $window $origin
+
+    # Windows fails to place transient windows normally, so
+    # schedule a callback to center them on the parent.
+    if {[tk windowingsystem] eq {win32}} {
+	after idle [list tk::PlaceWindow $window widget $origin]
+    }
+}
+
 proc show_error {w top msg} {
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
@@ -1701,22 +1792,24 @@
     pack $w.ok -side bottom -fill x
     bind $top <Visibility> "grab $top; focus $top"
     bind $top <Key-Return> "destroy $top"
+    bind $top <Key-space>  "destroy $top"
+    bind $top <Key-Escape> "destroy $top"
     tkwait window $top
 }
 
-proc error_popup msg {
+proc error_popup {msg {owner .}} {
     set w .error
     toplevel $w
-    wm transient $w .
+    make_transient $w $owner
     show_error $w $w $msg
 }
 
-proc confirm_popup msg {
+proc confirm_popup {msg {owner .}} {
     global confirm_ok
     set confirm_ok 0
     set w .confirm
     toplevel $w
-    wm transient $w .
+    make_transient $w $owner
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
     button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
@@ -1724,6 +1817,9 @@
     button $w.cancel -text [mc Cancel] -command "destroy $w"
     pack $w.cancel -side right -fill x
     bind $w <Visibility> "grab $w; focus $w"
+    bind $w <Key-Return> "set confirm_ok 1; destroy $w"
+    bind $w <Key-space>  "set confirm_ok 1; destroy $w"
+    bind $w <Key-Escape> "destroy $w"
     tkwait window $w
     return $confirm_ok
 }
@@ -1741,6 +1837,60 @@
     option add *Entry.font uifont startupFile
 }
 
+# Make a menu and submenus.
+# m is the window name for the menu, items is the list of menu items to add.
+# Each item is a list {mc label type description options...}
+# mc is ignored; it's so we can put mc there to alert xgettext
+# label is the string that appears in the menu
+# type is cascade, command or radiobutton (should add checkbutton)
+# description depends on type; it's the sublist for cascade, the
+# command to invoke for command, or {variable value} for radiobutton
+proc makemenu {m items} {
+    menu $m
+    if {[tk windowingsystem] eq {aqua}} {
+	set Meta1 Cmd
+    } else {
+	set Meta1 Ctrl
+    }
+    foreach i $items {
+	set name [mc [lindex $i 1]]
+	set type [lindex $i 2]
+	set thing [lindex $i 3]
+	set params [list $type]
+	if {$name ne {}} {
+	    set u [string first "&" [string map {&& x} $name]]
+	    lappend params -label [string map {&& & & {}} $name]
+	    if {$u >= 0} {
+		lappend params -underline $u
+	    }
+	}
+	switch -- $type {
+	    "cascade" {
+		set submenu [string tolower [string map {& ""} [lindex $i 1]]]
+		lappend params -menu $m.$submenu
+	    }
+	    "command" {
+		lappend params -command $thing
+	    }
+	    "radiobutton" {
+		lappend params -variable [lindex $thing 0] \
+		    -value [lindex $thing 1]
+	    }
+	}
+	set tail [lrange $i 4 end]
+	regsub -all {\yMeta1\y} $tail $Meta1 tail
+	eval $m add $params $tail
+	if {$type eq "cascade"} {
+	    makemenu $m.$submenu $thing
+	}
+    }
+}
+
+# translate string and remove ampersands
+proc mca {str} {
+    return [string map {&& & & {}} [mc $str]]
+}
+
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist cscroll
     global tabstop
@@ -1758,33 +1908,34 @@
     global rprogitem rprogcoord rownumsel numcommits
     global have_tk85
 
-    menu .bar
-    .bar add cascade -label [mc "File"] -menu .bar.file
-    menu .bar.file
-    .bar.file add command -label [mc "Update"] -command updatecommits
-    .bar.file add command -label [mc "Reload"] -command reloadcommits
-    .bar.file add command -label [mc "Reread references"] -command rereadrefs
-    .bar.file add command -label [mc "List references"] -command showrefs
-    .bar.file add command -label [mc "Quit"] -command doquit
-    menu .bar.edit
-    .bar add cascade -label [mc "Edit"] -menu .bar.edit
-    .bar.edit add command -label [mc "Preferences"] -command doprefs
-
-    menu .bar.view
-    .bar add cascade -label [mc "View"] -menu .bar.view
-    .bar.view add command -label [mc "New view..."] -command {newview 0}
-    .bar.view add command -label [mc "Edit view..."] -command editview \
-	-state disabled
-    .bar.view add command -label [mc "Delete view"] -command delview -state disabled
-    .bar.view add separator
-    .bar.view add radiobutton -label [mc "All files"] -command {showview 0} \
-	-variable selectedview -value 0
-
-    menu .bar.help
-    .bar add cascade -label [mc "Help"] -menu .bar.help
-    .bar.help add command -label [mc "About gitk"] -command about
-    .bar.help add command -label [mc "Key bindings"] -command keys
-    .bar.help configure
+    # The "mc" arguments here are purely so that xgettext
+    # sees the following string as needing to be translated
+    makemenu .bar {
+	{mc "File" cascade {
+	    {mc "Update" command updatecommits -accelerator F5}
+	    {mc "Reload" command reloadcommits -accelerator Meta1-F5}
+	    {mc "Reread references" command rereadrefs}
+	    {mc "List references" command showrefs -accelerator F2}
+	    {xx "" separator}
+	    {mc "Start git gui" command {exec git gui &}}
+	    {xx "" separator}
+	    {mc "Quit" command doquit -accelerator Meta1-Q}
+	}}
+	{mc "Edit" cascade {
+	    {mc "Preferences" command doprefs}
+	}}
+	{mc "View" cascade {
+	    {mc "New view..." command {newview 0} -accelerator Shift-F4}
+	    {mc "Edit view..." command editview -state disabled -accelerator F4}
+	    {mc "Delete view" command delview -state disabled}
+	    {xx "" separator}
+	    {mc "All files" radiobutton {selectedview 0} -command {showview 0}}
+	}}
+	{mc "Help" cascade {
+	    {mc "About gitk" command about}
+	    {mc "Key bindings" command keys}
+	}}
+    }
     . configure -menu .bar
 
     # the gui has upper and lower half, parts of a paned window.
@@ -2008,7 +2159,7 @@
     $ctext tag conf filesep -font textfontbold -back "#aaaaaa"
     $ctext tag conf hunksep -fore [lindex $diffcolors 2]
     $ctext tag conf d0 -fore [lindex $diffcolors 0]
-    $ctext tag conf d1 -fore [lindex $diffcolors 1]
+    $ctext tag conf dresult -fore [lindex $diffcolors 1]
     $ctext tag conf m0 -fore red
     $ctext tag conf m1 -fore blue
     $ctext tag conf m2 -fore green
@@ -2133,11 +2284,16 @@
     bindkey b prevfile
     bindkey d "$ctext yview scroll 18 units"
     bindkey u "$ctext yview scroll -18 units"
-    bindkey / {dofind 1 1}
+    bindkey / {focus $fstring}
     bindkey <Key-Return> {dofind 1 1}
     bindkey ? {dofind -1 1}
     bindkey f nextfile
-    bindkey <F5> updatecommits
+    bind . <F5> updatecommits
+    bind . <$M1B-F5> reloadcommits
+    bind . <F2> showrefs
+    bind . <Shift-F4> {newview 0}
+    catch { bind . <Shift-Key-XF86_Switch_VT_4> {newview 0} }
+    bind . <F4> edit_or_newview
     bind . <$M1B-q> doquit
     bind . <$M1B-f> {dofind 1 1}
     bind . <$M1B-g> {dofind 1 0}
@@ -2152,59 +2308,64 @@
     bind . <Destroy> {stop_backends}
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> {dofind 1 1}
-    bind $sha1entry <Key-Return> gotocommit
+    bind $sha1entry <Key-Return> {gotocommit; break}
     bind $sha1entry <<PasteSelection>> clearsha1
     bind $cflist <1> {sel_flist %W %x %y; break}
     bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
     bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
-    bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
+    global ctxbut
+    bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
+    bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
     set curtextcursor $textcursor
 
     set rowctxmenu .rowctxmenu
-    menu $rowctxmenu -tearoff 0
-    $rowctxmenu add command -label [mc "Diff this -> selected"] \
-	-command {diffvssel 0}
-    $rowctxmenu add command -label [mc "Diff selected -> this"] \
-	-command {diffvssel 1}
-    $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
-    $rowctxmenu add command -label [mc "Create tag"] -command mktag
-    $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
-    $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
-    $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
-	-command cherrypick
-    $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
-	-command resethead
+    makemenu $rowctxmenu {
+	{mc "Diff this -> selected" command {diffvssel 0}}
+	{mc "Diff selected -> this" command {diffvssel 1}}
+	{mc "Make patch" command mkpatch}
+	{mc "Create tag" command mktag}
+	{mc "Write commit to file" command writecommit}
+	{mc "Create new branch" command mkbranch}
+	{mc "Cherry-pick this commit" command cherrypick}
+	{mc "Reset HEAD branch to here" command resethead}
+    }
+    $rowctxmenu configure -tearoff 0
 
     set fakerowmenu .fakerowmenu
-    menu $fakerowmenu -tearoff 0
-    $fakerowmenu add command -label [mc "Diff this -> selected"] \
-	-command {diffvssel 0}
-    $fakerowmenu add command -label [mc "Diff selected -> this"] \
-	-command {diffvssel 1}
-    $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
-#    $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
-#    $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
-#    $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
+    makemenu $fakerowmenu {
+	{mc "Diff this -> selected" command {diffvssel 0}}
+	{mc "Diff selected -> this" command {diffvssel 1}}
+	{mc "Make patch" command mkpatch}
+    }
+    $fakerowmenu configure -tearoff 0
 
     set headctxmenu .headctxmenu
-    menu $headctxmenu -tearoff 0
-    $headctxmenu add command -label [mc "Check out this branch"] \
-	-command cobranch
-    $headctxmenu add command -label [mc "Remove this branch"] \
-	-command rmbranch
+    makemenu $headctxmenu {
+	{mc "Check out this branch" command cobranch}
+	{mc "Remove this branch" command rmbranch}
+    }
+    $headctxmenu configure -tearoff 0
 
     global flist_menu
     set flist_menu .flistctxmenu
-    menu $flist_menu -tearoff 0
-    $flist_menu add command -label [mc "Highlight this too"] \
-	-command {flist_hl 0}
-    $flist_menu add command -label [mc "Highlight this only"] \
-	-command {flist_hl 1}
-    $flist_menu add command -label [mc "External diff"] \
-        -command {external_diff}
+    makemenu $flist_menu {
+	{mc "Highlight this too" command {flist_hl 0}}
+	{mc "Highlight this only" command {flist_hl 1}}
+	{mc "External diff" command {external_diff}}
+	{mc "Blame parent commit" command {external_blame 1}}
+    }
+    $flist_menu configure -tearoff 0
+
+    global diff_menu
+    set diff_menu .diffctxmenu
+    makemenu $diff_menu {
+	{mc "Show origin of this line" command show_line_source}
+	{mc "Run git gui blame on this line" command {external_blame_diff}}
+    }
+    $diff_menu configure -tearoff 0
 }
 
 # Windows sends all mouse wheel events to the current focused window, not
@@ -2320,7 +2481,7 @@
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
-    global autoselect extdifftool
+    global autoselect extdifftool perfile_attrs markbgcolor
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -2344,9 +2505,11 @@
 	puts $f [list set fgcolor $fgcolor]
 	puts $f [list set colors $colors]
 	puts $f [list set diffcolors $diffcolors]
+	puts $f [list set markbgcolor $markbgcolor]
 	puts $f [list set diffcontext $diffcontext]
 	puts $f [list set selectbgcolor $selectbgcolor]
 	puts $f [list set extdifftool $extdifftool]
+	puts $f [list set perfile_attrs $perfile_attrs]
 
 	puts $f "set geometry(main) [wm geometry .]"
 	puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -2444,6 +2607,7 @@
     }
     toplevel $w
     wm title $w [mc "About gitk"]
+    make_transient $w .
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
@@ -2472,6 +2636,7 @@
     }
     toplevel $w
     wm title $w [mc "Gitk key bindings"]
+    make_transient $w .
     message $w.m -text "
 [mc "Gitk key bindings:"]
 
@@ -2500,7 +2665,7 @@
 [mc "<%s-F>		Find" $M1T]
 [mc "<%s-G>		Move to next find hit" $M1T]
 [mc "<Return>	Move to next find hit"]
-[mc "/		Move to next find hit, or redo find"]
+[mc "/		Focus the search box"]
 [mc "?		Move to previous find hit"]
 [mc "f		Scroll diff view to next file"]
 [mc "<%s-S>		Search for next hit in diff view" $M1T]
@@ -2514,6 +2679,7 @@
 	    -justify left -bg white -border 2 -relief groove
     pack $w.m -side top -fill both -padx 2 -pady 2
     button $w.ok -text [mc "Close"] -command "destroy $w" -default active
+    bind $w <Key-Escape> [list destroy $w]
     pack $w.ok -side bottom
     bind $w <Visibility> "focus $w.ok"
     bind $w <Key-Escape> "destroy $w"
@@ -2694,7 +2860,7 @@
 	    $w insert e:$ix $e [highlight_tag $de]
 	}
     }
-    $w mark gravity e:$ix left
+    $w mark gravity e:$ix right
     $w conf -state disabled
     set treediropen($dir) 1
     set top [lindex [split [$w index @0,0] .] 0]
@@ -2735,9 +2901,15 @@
 }
 
 proc setfilelist {id} {
-    global treefilelist cflist
+    global treefilelist cflist jump_to_here
 
     treeview $cflist $treefilelist($id) 0
+    if {$jump_to_here ne {}} {
+	set f [lindex $jump_to_here 0]
+	if {[lsearch -exact $treefilelist($id) $f] >= 0} {
+	    showfile $f
+	}
+    }
 }
 
 image create bitmap tri-rt -background black -foreground blue -data {
@@ -2906,6 +3078,38 @@
     tk_popup $flist_menu $X $Y
 }
 
+proc find_ctext_fileinfo {line} {
+    global ctext_file_names ctext_file_lines
+
+    set ok [bsearch $ctext_file_lines $line]
+    set tline [lindex $ctext_file_lines $ok]
+
+    if {$ok >= [llength $ctext_file_lines] || $line < $tline} {
+        return {}
+    } else {
+        return [list [lindex $ctext_file_names $ok] $tline]
+    }
+}
+
+proc pop_diff_menu {w X Y x y} {
+    global ctext diff_menu flist_menu_file
+    global diff_menu_txtpos diff_menu_line
+    global diff_menu_filebase
+
+    set diff_menu_txtpos [split [$w index "@$x,$y"] "."]
+    set diff_menu_line [lindex $diff_menu_txtpos 0]
+    # don't pop up the menu on hunk-separator or file-separator lines
+    if {[lsearch -glob [$ctext tag names $diff_menu_line.0] "*sep"] >= 0} {
+	return
+    }
+    stopfinding
+    set f [find_ctext_fileinfo $diff_menu_line]
+    if {$f eq {}} return
+    set flist_menu_file [lindex $f 0]
+    set diff_menu_filebase [lindex $f 1]
+    tk_popup $diff_menu $X $Y
+}
+
 proc flist_hl {only} {
     global flist_menu_file findstring gdttype
 
@@ -2925,7 +3129,7 @@
 	if {[string match "fatal: bad revision *" $err]} {
 	    return $nullfile
 	}
-	error_popup "Error getting \"$filename\" from $what: $err"
+	error_popup "[mc "Error getting \"%s\" from %s:" $filename $what] $err"
 	return {}
     }
     return $output
@@ -2982,7 +3186,7 @@
 	set gitktmpdir [file join [file dirname $gitdir] \
 			    [format ".gitk-tmp.%s" [pid]]]
 	if {[catch {file mkdir $gitktmpdir} err]} {
-	    error_popup "Error creating temporary directory $gitktmpdir: $err"
+	    error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
 	    unset gitktmpdir
 	    return
 	}
@@ -2991,7 +3195,7 @@
     incr diffnum
     set diffdir [file join $gitktmpdir $diffnum]
     if {[catch {file mkdir $diffdir} err]} {
-	error_popup "Error creating temporary directory $diffdir: $err"
+	error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
 	return
     }
 
@@ -3004,7 +3208,7 @@
 		     [list $difffromfile $difftofile]]
         if {[catch {set fl [open $cmd r]} err]} {
             file delete -force $diffdir
-            error_popup [mc "$extdifftool: command failed: $err"]
+            error_popup "$extdifftool: [mc "command failed:"] $err"
         } else {
             fconfigure $fl -blocking 0
             filerun $fl [list delete_at_eof $fl $diffdir]
@@ -3012,12 +3216,300 @@
     }
 }
 
+proc find_hunk_blamespec {base line} {
+    global ctext
+
+    # Find and parse the hunk header
+    set s_lix [$ctext search -backwards -regexp ^@@ "$line.0 lineend" $base.0]
+    if {$s_lix eq {}} return
+
+    set s_line [$ctext get $s_lix "$s_lix + 1 lines"]
+    if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \
+	    s_line old_specs osz osz1 new_line nsz]} {
+	return
+    }
+
+    # base lines for the parents
+    set base_lines [list $new_line]
+    foreach old_spec [lrange [split $old_specs " "] 1 end] {
+	if {![regexp -- {-(\d+)(,\d+)?} $old_spec \
+	        old_spec old_line osz]} {
+	    return
+	}
+	lappend base_lines $old_line
+    }
+
+    # Now scan the lines to determine offset within the hunk
+    set max_parent [expr {[llength $base_lines]-2}]
+    set dline 0
+    set s_lno [lindex [split $s_lix "."] 0]
+
+    # Determine if the line is removed
+    set chunk [$ctext get $line.0 "$line.1 + $max_parent chars"]
+    if {[string match {[-+ ]*} $chunk]} {
+	set removed_idx [string first "-" $chunk]
+	# Choose a parent index
+	if {$removed_idx >= 0} {
+	    set parent $removed_idx
+	} else {
+	    set unchanged_idx [string first " " $chunk]
+	    if {$unchanged_idx >= 0} {
+		set parent $unchanged_idx
+	    } else {
+		# blame the current commit
+		set parent -1
+	    }
+	}
+	# then count other lines that belong to it
+	for {set i $line} {[incr i -1] > $s_lno} {} {
+	    set chunk [$ctext get $i.0 "$i.1 + $max_parent chars"]
+	    # Determine if the line is removed
+	    set removed_idx [string first "-" $chunk]
+	    if {$parent >= 0} {
+		set code [string index $chunk $parent]
+		if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
+		    incr dline
+		}
+	    } else {
+		if {$removed_idx < 0} {
+		    incr dline
+		}
+	    }
+	}
+	incr parent
+    } else {
+	set parent 0
+    }
+
+    incr dline [lindex $base_lines $parent]
+    return [list $parent $dline]
+}
+
+proc external_blame_diff {} {
+    global currentid cmitmode
+    global diff_menu_txtpos diff_menu_line
+    global diff_menu_filebase flist_menu_file
+
+    if {$cmitmode eq "tree"} {
+	set parent_idx 0
+	set line [expr {$diff_menu_line - $diff_menu_filebase}]
+    } else {
+	set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
+	if {$hinfo ne {}} {
+	    set parent_idx [lindex $hinfo 0]
+	    set line [lindex $hinfo 1]
+	} else {
+	    set parent_idx 0
+	    set line 0
+	}
+    }
+
+    external_blame $parent_idx $line
+}
+
+# Find the SHA1 ID of the blob for file $fname in the index
+# at stage 0 or 2
+proc index_sha1 {fname} {
+    set f [open [list | git ls-files -s $fname] r]
+    while {[gets $f line] >= 0} {
+	set info [lindex [split $line "\t"] 0]
+	set stage [lindex $info 2]
+	if {$stage eq "0" || $stage eq "2"} {
+	    close $f
+	    return [lindex $info 1]
+	}
+    }
+    close $f
+    return {}
+}
+
+# Turn an absolute path into one relative to the current directory
+proc make_relative {f} {
+    set elts [file split $f]
+    set here [file split [pwd]]
+    set ei 0
+    set hi 0
+    set res {}
+    foreach d $here {
+	if {$ei < $hi || $ei >= [llength $elts] || [lindex $elts $ei] ne $d} {
+	    lappend res ".."
+	} else {
+	    incr ei
+	}
+	incr hi
+    }
+    set elts [concat $res [lrange $elts $ei end]]
+    return [eval file join $elts]
+}
+
+proc external_blame {parent_idx {line {}}} {
+    global flist_menu_file gitdir
+    global nullid nullid2
+    global parentlist selectedline currentid
+
+    if {$parent_idx > 0} {
+	set base_commit [lindex $parentlist $selectedline [expr {$parent_idx-1}]]
+    } else {
+	set base_commit $currentid
+    }
+
+    if {$base_commit eq {} || $base_commit eq $nullid || $base_commit eq $nullid2} {
+	error_popup [mc "No such commit"]
+	return
+    }
+
+    set cmdline [list git gui blame]
+    if {$line ne {} && $line > 1} {
+	lappend cmdline "--line=$line"
+    }
+    set f [file join [file dirname $gitdir] $flist_menu_file]
+    # Unfortunately it seems git gui blame doesn't like
+    # being given an absolute path...
+    set f [make_relative $f]
+    lappend cmdline $base_commit $f
+    if {[catch {eval exec $cmdline &} err]} {
+	error_popup "[mc "git gui blame: command failed:"] $err"
+    }
+}
+
+proc show_line_source {} {
+    global cmitmode currentid parents curview blamestuff blameinst
+    global diff_menu_line diff_menu_filebase flist_menu_file
+    global nullid nullid2 gitdir
+
+    set from_index {}
+    if {$cmitmode eq "tree"} {
+	set id $currentid
+	set line [expr {$diff_menu_line - $diff_menu_filebase}]
+    } else {
+	set h [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
+	if {$h eq {}} return
+	set pi [lindex $h 0]
+	if {$pi == 0} {
+	    mark_ctext_line $diff_menu_line
+	    return
+	}
+	incr pi -1
+	if {$currentid eq $nullid} {
+	    if {$pi > 0} {
+		# must be a merge in progress...
+		if {[catch {
+		    # get the last line from .git/MERGE_HEAD
+		    set f [open [file join $gitdir MERGE_HEAD] r]
+		    set id [lindex [split [read $f] "\n"] end-1]
+		    close $f
+		} err]} {
+		    error_popup [mc "Couldn't read merge head: %s" $err]
+		    return
+		}
+	    } elseif {$parents($curview,$currentid) eq $nullid2} {
+		# need to do the blame from the index
+		if {[catch {
+		    set from_index [index_sha1 $flist_menu_file]
+		} err]} {
+		    error_popup [mc "Error reading index: %s" $err]
+		    return
+		}
+	    } else {
+		set id $parents($curview,$currentid)
+	    }
+	} else {
+	    set id [lindex $parents($curview,$currentid) $pi]
+	}
+	set line [lindex $h 1]
+    }
+    set blameargs {}
+    if {$from_index ne {}} {
+	lappend blameargs | git cat-file blob $from_index
+    }
+    lappend blameargs | git blame -p -L$line,+1
+    if {$from_index ne {}} {
+	lappend blameargs --contents -
+    } else {
+	lappend blameargs $id
+    }
+    lappend blameargs -- [file join [file dirname $gitdir] $flist_menu_file]
+    if {[catch {
+	set f [open $blameargs r]
+    } err]} {
+	error_popup [mc "Couldn't start git blame: %s" $err]
+	return
+    }
+    nowbusy blaming [mc "Searching"]
+    fconfigure $f -blocking 0
+    set i [reg_instance $f]
+    set blamestuff($i) {}
+    set blameinst $i
+    filerun $f [list read_line_source $f $i]
+}
+
+proc stopblaming {} {
+    global blameinst
+
+    if {[info exists blameinst]} {
+	stop_instance $blameinst
+	unset blameinst
+	notbusy blaming
+    }
+}
+
+proc read_line_source {fd inst} {
+    global blamestuff curview commfd blameinst nullid nullid2
+
+    while {[gets $fd line] >= 0} {
+	lappend blamestuff($inst) $line
+    }
+    if {![eof $fd]} {
+	return 1
+    }
+    unset commfd($inst)
+    unset blameinst
+    notbusy blaming
+    fconfigure $fd -blocking 1
+    if {[catch {close $fd} err]} {
+	error_popup [mc "Error running git blame: %s" $err]
+	return 0
+    }
+
+    set fname {}
+    set line [split [lindex $blamestuff($inst) 0] " "]
+    set id [lindex $line 0]
+    set lnum [lindex $line 1]
+    if {[string length $id] == 40 && [string is xdigit $id] &&
+	[string is digit -strict $lnum]} {
+	# look for "filename" line
+	foreach l $blamestuff($inst) {
+	    if {[string match "filename *" $l]} {
+		set fname [string range $l 9 end]
+		break
+	    }
+	}
+    }
+    if {$fname ne {}} {
+	# all looks good, select it
+	if {$id eq $nullid} {
+	    # blame uses all-zeroes to mean not committed,
+	    # which would mean a change in the index
+	    set id $nullid2
+	}
+	if {[commitinview $id $curview]} {
+	    selectline [rowofcommit $id] 1 [list $fname $lnum]
+	} else {
+	    error_popup [mc "That line comes from commit %s, \
+			     which is not in this view" [shortids $id]]
+	}
+    } else {
+	puts "oops couldn't parse git blame output"
+    }
+    return 0
+}
+
 # delete $dir when we see eof on $f (presumably because the child has exited)
 proc delete_at_eof {f dir} {
     while {[gets $f line] >= 0} {}
     if {[eof $f]} {
 	if {[catch {close $f} err]} {
-	    error_popup "External diff viewer failed: $err"
+	    error_popup "[mc "External diff viewer failed:"] $err"
 	}
 	file delete -force $dir
 	return 0
@@ -3122,8 +3614,8 @@
 # Code to implement multiple views
 
 proc newview {ishighlight} {
-    global nextviewnum newviewname newviewperm newishighlight
-    global newviewargs revtreeargs viewargscmd newviewargscmd curview
+    global nextviewnum newviewname newishighlight
+    global revtreeargs viewargscmd newviewopts curview
 
     set newishighlight $ishighlight
     set top .gitkview
@@ -3132,58 +3624,183 @@
 	return
     }
     set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
-    set newviewperm($nextviewnum) 0
-    set newviewargs($nextviewnum) [shellarglist $revtreeargs]
-    set newviewargscmd($nextviewnum) $viewargscmd($curview)
+    set newviewopts($nextviewnum,perm) 0
+    set newviewopts($nextviewnum,cmd)  $viewargscmd($curview)
+    decode_view_opts $nextviewnum $revtreeargs
     vieweditor $top $nextviewnum [mc "Gitk view definition"]
 }
 
+set known_view_options {
+    {perm    b    . {}               {mc "Remember this view"}}
+    {args    t50= + {}               {mc "Commits to include (arguments to git log):"}}
+    {all     b    * "--all"          {mc "Use all refs"}}
+    {dorder  b    . {"--date-order" "-d"}      {mc "Strictly sort by date"}}
+    {lright  b    . "--left-right"   {mc "Mark branch sides"}}
+    {since   t15  + {"--since=*" "--after=*"}  {mc "Since date:"}}
+    {until   t15  . {"--until=*" "--before=*"} {mc "Until date:"}}
+    {limit   t10  + "--max-count=*"  {mc "Max count:"}}
+    {skip    t10  . "--skip=*"       {mc "Skip:"}}
+    {first   b    . "--first-parent" {mc "Limit to first parent"}}
+    {cmd     t50= + {}               {mc "Command to generate more commits to include:"}}
+    }
+
+proc encode_view_opts {n} {
+    global known_view_options newviewopts
+
+    set rargs [list]
+    foreach opt $known_view_options {
+	set patterns [lindex $opt 3]
+	if {$patterns eq {}} continue
+	set pattern [lindex $patterns 0]
+
+	set val $newviewopts($n,[lindex $opt 0])
+	
+	if {[lindex $opt 1] eq "b"} {
+	    if {$val} {
+		lappend rargs $pattern
+	    }
+	} else {
+	    set val [string trim $val]
+	    if {$val ne {}} {
+		set pfix [string range $pattern 0 end-1]
+		lappend rargs $pfix$val
+	    }
+	}
+    }
+    return [concat $rargs [shellsplit $newviewopts($n,args)]]
+}
+
+proc decode_view_opts {n view_args} {
+    global known_view_options newviewopts
+
+    foreach opt $known_view_options {
+	if {[lindex $opt 1] eq "b"} {
+	    set val 0
+	} else {
+	    set val {}
+	}
+	set newviewopts($n,[lindex $opt 0]) $val
+    }
+    set oargs [list]
+    foreach arg $view_args {
+	if {[regexp -- {^-([0-9]+)$} $arg arg cnt]
+	    && ![info exists found(limit)]} {
+	    set newviewopts($n,limit) $cnt
+	    set found(limit) 1
+	    continue
+	}
+	catch { unset val }
+	foreach opt $known_view_options {
+	    set id [lindex $opt 0]
+	    if {[info exists found($id)]} continue
+	    foreach pattern [lindex $opt 3] {
+		if {![string match $pattern $arg]} continue
+		if {[lindex $opt 1] ne "b"} {
+		    set size [string length $pattern]
+		    set val [string range $arg [expr {$size-1}] end]
+		} else {
+		    set val 1
+		}
+		set newviewopts($n,$id) $val
+		set found($id) 1
+		break
+	    }
+	    if {[info exists val]} break
+	}
+	if {[info exists val]} continue
+	lappend oargs $arg
+    }
+    set newviewopts($n,args) [shellarglist $oargs]
+}
+
+proc edit_or_newview {} {
+    global curview
+
+    if {$curview > 0} {
+	editview
+    } else {
+	newview 0
+    }
+}
+
 proc editview {} {
     global curview
-    global viewname viewperm newviewname newviewperm
-    global viewargs newviewargs viewargscmd newviewargscmd
+    global viewname viewperm newviewname newviewopts
+    global viewargs viewargscmd
 
     set top .gitkvedit-$curview
     if {[winfo exists $top]} {
 	raise $top
 	return
     }
-    set newviewname($curview) $viewname($curview)
-    set newviewperm($curview) $viewperm($curview)
-    set newviewargs($curview) [shellarglist $viewargs($curview)]
-    set newviewargscmd($curview) $viewargscmd($curview)
+    set newviewname($curview)      $viewname($curview)
+    set newviewopts($curview,perm) $viewperm($curview)
+    set newviewopts($curview,cmd)  $viewargscmd($curview)
+    decode_view_opts $curview $viewargs($curview)
     vieweditor $top $curview "Gitk: edit view $viewname($curview)"
 }
 
 proc vieweditor {top n title} {
-    global newviewname newviewperm viewfiles bgcolor
+    global newviewname newviewopts viewfiles bgcolor
+    global known_view_options
 
     toplevel $top
     wm title $top $title
+    make_transient $top .
+
+    # View name
+    frame $top.nfr
     label $top.nl -text [mc "Name"]
     entry $top.name -width 20 -textvariable newviewname($n)
-    grid $top.nl $top.name -sticky w -pady 5
-    checkbutton $top.perm -text [mc "Remember this view"] \
-	-variable newviewperm($n)
-    grid $top.perm - -pady 5 -sticky w
-    message $top.al -aspect 1000 \
-	-text [mc "Commits to include (arguments to git log):"]
-    grid $top.al - -sticky w -pady 5
-    entry $top.args -width 50 -textvariable newviewargs($n) \
-	-background $bgcolor
-    grid $top.args - -sticky ew -padx 5
+    pack $top.nfr -in $top -fill x -pady 5 -padx 3
+    pack $top.nl -in $top.nfr -side left -padx {0 30}
+    pack $top.name -in $top.nfr -side left
 
-    message $top.ac -aspect 1000 \
-	-text [mc "Command to generate more commits to include:"]
-    grid $top.ac - -sticky w -pady 5
-    entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
-	-background white
-    grid $top.argscmd - -sticky ew -padx 5
+    # View options
+    set cframe $top.nfr
+    set cexpand 0
+    set cnt 0
+    foreach opt $known_view_options {
+	set id [lindex $opt 0]
+	set type [lindex $opt 1]
+	set flags [lindex $opt 2]
+	set title [eval [lindex $opt 4]]
+	set lxpad 0
 
-    message $top.l -aspect 1000 \
+	if {$flags eq "+" || $flags eq "*"} {
+	    set cframe $top.fr$cnt
+	    incr cnt
+	    frame $cframe
+	    pack $cframe -in $top -fill x -pady 3 -padx 3
+	    set cexpand [expr {$flags eq "*"}]
+	} else {
+	    set lxpad 5
+	}
+
+	if {$type eq "b"} {
+	    checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
+	    pack $cframe.c_$id -in $cframe -side left \
+		-padx [list $lxpad 0] -expand $cexpand -anchor w
+	} elseif {[regexp {^t(\d+)$} $type type sz]} {
+	    message $cframe.l_$id -aspect 1500 -text $title
+	    entry $cframe.e_$id -width $sz -background $bgcolor \
+		-textvariable newviewopts($n,$id)
+	    pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0]
+	    pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x
+	} elseif {[regexp {^t(\d+)=$} $type type sz]} {
+	    message $cframe.l_$id -aspect 1500 -text $title
+	    entry $cframe.e_$id -width $sz -background $bgcolor \
+		-textvariable newviewopts($n,$id)
+	    pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
+	    pack $cframe.e_$id -in $cframe -side top -fill x
+	}
+    }
+
+    # Path list
+    message $top.l -aspect 1500 \
 	-text [mc "Enter files and directories to include, one per line:"]
-    grid $top.l - -sticky w
-    text $top.t -width 40 -height 10 -background $bgcolor -font uifont
+    pack $top.l -in $top -side top -pady [list 7 0] -anchor w -padx 3
+    text $top.t -width 40 -height 5 -background $bgcolor -font uifont
     if {[info exists viewfiles($n)]} {
 	foreach f $viewfiles($n) {
 	    $top.t insert end $f
@@ -3192,14 +3809,19 @@
 	$top.t delete {end - 1c} end
 	$top.t mark set insert 0.0
     }
-    grid $top.t - -sticky ew -padx 5
+    pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
     frame $top.buts
     button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
+    button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
     button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
-    grid $top.buts.ok $top.buts.can
+    bind $top <Control-Return> [list newviewok $top $n]
+    bind $top <F5> [list newviewok $top $n 1]
+    bind $top <Escape> [list destroy $top]
+    grid $top.buts.ok $top.buts.apply $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
+    grid columnconfigure $top.buts 2 -weight 1 -uniform a
+    pack $top.buts -in $top -side top -fill x
     focus $top.t
 }
 
@@ -3220,17 +3842,15 @@
     # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
 }
 
-proc newviewok {top n} {
+proc newviewok {top n {apply 0}} {
     global nextviewnum newviewperm newviewname newishighlight
     global viewname viewfiles viewperm selectedview curview
-    global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
+    global viewargs viewargscmd newviewopts viewhlmenu
 
     if {[catch {
-	set newargs [shellsplit $newviewargs($n)]
+	set newargs [encode_view_opts $n]
     } err]} {
-	error_popup "[mc "Error in commit selection arguments:"] $err"
-	wm raise $top
-	focus $top
+	error_popup "[mc "Error in commit selection arguments:"] $err" $top
 	return
     }
     set files {}
@@ -3244,10 +3864,10 @@
 	# creating a new view
 	incr nextviewnum
 	set viewname($n) $newviewname($n)
-	set viewperm($n) $newviewperm($n)
+	set viewperm($n) $newviewopts($n,perm)
 	set viewfiles($n) $files
 	set viewargs($n) $newargs
-	set viewargscmd($n) $newviewargscmd($n)
+	set viewargscmd($n) $newviewopts($n,cmd)
 	addviewmenu $n
 	if {!$newishighlight} {
 	    run showview $n
@@ -3256,7 +3876,7 @@
 	}
     } else {
 	# editing an existing view
-	set viewperm($n) $newviewperm($n)
+	set viewperm($n) $newviewopts($n,perm)
 	if {$newviewname($n) ne $viewname($n)} {
 	    set viewname($n) $newviewname($n)
 	    doviewmenu .bar.view 5 [list showview $n] \
@@ -3265,15 +3885,16 @@
 		# entryconf [list -label $viewname($n) -value $viewname($n)]
 	}
 	if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
-		$newviewargscmd($n) ne $viewargscmd($n)} {
+		$newviewopts($n,cmd) ne $viewargscmd($n)} {
 	    set viewfiles($n) $files
 	    set viewargs($n) $newargs
-	    set viewargscmd($n) $newviewargscmd($n)
+	    set viewargscmd($n) $newviewopts($n,cmd)
 	    if {$curview == $n} {
 		run reloadcommits
 	    }
 	}
     }
+    if {$apply} return
     catch {destroy $top}
 }
 
@@ -3342,8 +3963,8 @@
 
     set curview $n
     set selectedview $n
-    .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
-    .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf [mca "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf [mca "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
 
     run refill_reflist
     if {![info exists viewcomplete($n)]} {
@@ -3424,28 +4045,31 @@
     return 0
 }
 
-proc bolden {row font} {
-    global canv linehtag selectedline boldrows
+proc bolden {id font} {
+    global canv linehtag currentid boldids need_redisplay
 
-    lappend boldrows $row
-    $canv itemconf $linehtag($row) -font $font
-    if {$row == $selectedline} {
+    # need_redisplay = 1 means the display is stale and about to be redrawn
+    if {$need_redisplay} return
+    lappend boldids $id
+    $canv itemconf $linehtag($id) -font $font
+    if {[info exists currentid] && $id eq $currentid} {
 	$canv delete secsel
-	set t [eval $canv create rect [$canv bbox $linehtag($row)] \
+	set t [eval $canv create rect [$canv bbox $linehtag($id)] \
 		   -outline {{}} -tags secsel \
 		   -fill [$canv cget -selectbackground]]
 	$canv lower $t
     }
 }
 
-proc bolden_name {row font} {
-    global canv2 linentag selectedline boldnamerows
+proc bolden_name {id font} {
+    global canv2 linentag currentid boldnameids need_redisplay
 
-    lappend boldnamerows $row
-    $canv2 itemconf $linentag($row) -font $font
-    if {$row == $selectedline} {
+    if {$need_redisplay} return
+    lappend boldnameids $id
+    $canv2 itemconf $linentag($id) -font $font
+    if {[info exists currentid] && $id eq $currentid} {
 	$canv2 delete secsel
-	set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
+	set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] \
 		   -outline {{}} -tags secsel \
 		   -fill [$canv2 cget -selectbackground]]
 	$canv2 lower $t
@@ -3453,17 +4077,17 @@
 }
 
 proc unbolden {} {
-    global boldrows
+    global boldids
 
     set stillbold {}
-    foreach row $boldrows {
-	if {![ishighlighted [commitonrow $row]]} {
-	    bolden $row mainfont
+    foreach id $boldids {
+	if {![ishighlighted $id]} {
+	    bolden $id mainfont
 	} else {
-	    lappend stillbold $row
+	    lappend stillbold $id
 	}
     }
-    set boldrows $stillbold
+    set boldids $stillbold
 }
 
 proc addvhighlight {n} {
@@ -3504,7 +4128,7 @@
 	    set row [rowofcommit $id]
 	    if {$r0 <= $row && $row <= $r1} {
 		if {![highlighted $row]} {
-		    bolden $row mainfontbold
+		    bolden $id mainfontbold
 		}
 		set vhighlights($id) 1
 	    }
@@ -3519,7 +4143,7 @@
 
     if {[commitinview $id $hlview]} {
 	if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
-	    bolden $row mainfontbold
+	    bolden $id mainfontbold
 	}
 	set vhighlights($id) 1
     } else {
@@ -3529,7 +4153,7 @@
 
 proc hfiles_change {} {
     global highlight_files filehighlight fhighlights fh_serial
-    global highlight_paths gdttype
+    global highlight_paths
 
     if {[info exists filehighlight]} {
 	# delete previous highlights
@@ -3587,15 +4211,15 @@
 }
 
 proc findcom_change args {
-    global nhighlights boldnamerows
+    global nhighlights boldnameids
     global findpattern findtype findstring gdttype
 
     stopfinding
     # delete previous highlights, if any
-    foreach row $boldnamerows {
-	bolden_name $row mainfont
+    foreach id $boldnameids {
+	bolden_name $id mainfont
     }
-    set boldnamerows {}
+    set boldnameids {}
     catch {unset nhighlights}
     unbolden
     unmarkmatches
@@ -3684,9 +4308,8 @@
 	set fhl_list [lrange $fhl_list [expr {$i+1}] end]
 	if {$line eq {}} continue
 	if {![commitinview $line $curview]} continue
-	set row [rowofcommit $line]
 	if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
-	    bolden $row mainfontbold
+	    bolden $line mainfontbold
 	}
 	set fhighlights($line) 1
     }
@@ -3738,9 +4361,9 @@
     }
     if {$isbold && [info exists iddrawn($id)]} {
 	if {![ishighlighted $id]} {
-	    bolden $row mainfontbold
+	    bolden $id mainfontbold
 	    if {$isbold > 1} {
-		bolden_name $row mainfontbold
+		bolden_name $id mainfontbold
 	    }
 	}
 	if {$markingmatches} {
@@ -3760,15 +4383,15 @@
     if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
 	set m [findmatches $headline]
 	if {$m ne {}} {
-	    markmatches $canv $row $headline $linehtag($row) $m \
-		[$canv itemcget $linehtag($row) -font] $row
+	    markmatches $canv $row $headline $linehtag($id) $m \
+		[$canv itemcget $linehtag($id) -font] $row
 	}
     }
     if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
 	set m [findmatches $author]
 	if {$m ne {}} {
-	    markmatches $canv2 $row $author $linentag($row) $m \
-		[$canv2 itemcget $linentag($row) -font] $row
+	    markmatches $canv2 $row $author $linentag($id) $m \
+		[$canv2 itemcget $linentag($id) -font] $row
 	}
     }
 }
@@ -3893,7 +4516,7 @@
     }
     if {[info exists iddrawn($id)]} {
 	if {$isbold && ![ishighlighted $id]} {
-	    bolden $row mainfontbold
+	    bolden $id mainfontbold
 	}
     }
     set rhighlights($id) $isbold
@@ -4047,7 +4670,7 @@
 proc layoutmore {} {
     global commitidx viewcomplete curview
     global numcommits pending_select curview
-    global lastscrollset lastscrollrows commitinterest
+    global lastscrollset lastscrollrows
 
     if {$lastscrollrows < 100 || $viewcomplete($curview) ||
 	[clock clicks -milliseconds] - $lastscrollset > 500} {
@@ -4061,14 +4684,56 @@
     drawvisible
 }
 
-proc doshowlocalchanges {} {
-    global curview mainheadid
+# With path limiting, we mightn't get the actual HEAD commit,
+# so ask git rev-list what is the first ancestor of HEAD that
+# touches a file in the path limit.
+proc get_viewmainhead {view} {
+    global viewmainheadid vfilelimit viewinstances mainheadid
 
-    if {$mainheadid eq {}} return
-    if {[commitinview $mainheadid $curview]} {
+    catch {
+	set rfd [open [concat | git rev-list -1 $mainheadid \
+			   -- $vfilelimit($view)] r]
+	set j [reg_instance $rfd]
+	lappend viewinstances($view) $j
+	fconfigure $rfd -blocking 0
+	filerun $rfd [list getviewhead $rfd $j $view]
+	set viewmainheadid($curview) {}
+    }
+}
+
+# git rev-list should give us just 1 line to use as viewmainheadid($view)
+proc getviewhead {fd inst view} {
+    global viewmainheadid commfd curview viewinstances showlocalchanges
+
+    set id {}
+    if {[gets $fd line] < 0} {
+	if {![eof $fd]} {
+	    return 1
+	}
+    } elseif {[string length $line] == 40 && [string is xdigit $line]} {
+	set id $line
+    }
+    set viewmainheadid($view) $id
+    close $fd
+    unset commfd($inst)
+    set i [lsearch -exact $viewinstances($view) $inst]
+    if {$i >= 0} {
+	set viewinstances($view) [lreplace $viewinstances($view) $i $i]
+    }
+    if {$showlocalchanges && $id ne {} && $view == $curview} {
+	doshowlocalchanges
+    }
+    return 0
+}
+
+proc doshowlocalchanges {} {
+    global curview viewmainheadid
+
+    if {$viewmainheadid($curview) eq {}} return
+    if {[commitinview $viewmainheadid($curview) $curview]} {
 	dodiffindex
     } else {
-	lappend commitinterest($mainheadid) {dodiffindex}
+	interestedin $viewmainheadid($curview) dodiffindex
     }
 }
 
@@ -4086,19 +4751,24 @@
 
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
-    global lserial showlocalchanges
+    global lserial showlocalchanges vfilelimit curview
     global isworktree
 
     if {!$showlocalchanges || !$isworktree} return
     incr lserial
-    set fd [open "|git diff-index --cached HEAD" r]
+    set cmd "|git diff-index --cached HEAD"
+    if {$vfilelimit($curview) ne {}} {
+	set cmd [concat $cmd -- $vfilelimit($curview)]
+    }
+    set fd [open $cmd r]
     fconfigure $fd -blocking 0
     set i [reg_instance $fd]
     filerun $fd [list readdiffindex $fd $lserial $i]
 }
 
 proc readdiffindex {fd serial inst} {
-    global mainheadid nullid nullid2 curview commitinfo commitdata lserial
+    global viewmainheadid nullid nullid2 curview commitinfo commitdata lserial
+    global vfilelimit
 
     set isdiff 1
     if {[gets $fd line] < 0} {
@@ -4115,7 +4785,11 @@
     }
 
     # now see if there are any local changes not checked in to the index
-    set fd [open "|git diff-files" r]
+    set cmd "|git diff-files"
+    if {$vfilelimit($curview) ne {}} {
+	set cmd [concat $cmd -- $vfilelimit($curview)]
+    }
+    set fd [open $cmd r]
     fconfigure $fd -blocking 0
     set i [reg_instance $fd]
     filerun $fd [list readdifffiles $fd $serial $i]
@@ -4128,15 +4802,18 @@
 	if {[commitinview $nullid $curview]} {
 	    removefakerow $nullid
 	}
-	insertfakerow $nullid2 $mainheadid
+	insertfakerow $nullid2 $viewmainheadid($curview)
     } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
+	if {[commitinview $nullid $curview]} {
+	    removefakerow $nullid
+	}
 	removefakerow $nullid2
     }
     return 0
 }
 
 proc readdifffiles {fd serial inst} {
-    global mainheadid nullid nullid2 curview
+    global viewmainheadid nullid nullid2 curview
     global commitinfo commitdata lserial
 
     set isdiff 1
@@ -4161,7 +4838,7 @@
 	if {[commitinview $nullid2 $curview]} {
 	    set p $nullid2
 	} else {
-	    set p $mainheadid
+	    set p $viewmainheadid($curview)
 	}
 	insertfakerow $nullid $p
     } elseif {!$isdiff && [commitinview $nullid $curview]} {
@@ -4886,8 +5563,8 @@
     global cmitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag selectedline
-    global canvxmax boldrows boldnamerows fgcolor
-    global mainheadid nullid nullid2 circleitem circlecolors
+    global canvxmax boldids boldnameids fgcolor
+    global mainheadid nullid nullid2 circleitem circlecolors ctxbut
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
     set listed $cmitlisted($curview,$id)
@@ -4951,22 +5628,22 @@
     set nfont mainfont
     set isbold [ishighlighted $id]
     if {$isbold > 0} {
-	lappend boldrows $row
+	lappend boldids $id
 	set font mainfontbold
 	if {$isbold > 1} {
-	    lappend boldnamerows $row
+	    lappend boldnameids $id
 	    set nfont mainfontbold
 	}
     }
-    set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
-			    -text $headline -font $font -tags text]
-    $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
-    set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
-			    -text $name -font $nfont -tags text]
-    set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
-			    -text $date -font mainfont -tags text]
+    set linehtag($id) [$canv create text $xt $y -anchor w -fill $fgcolor \
+			   -text $headline -font $font -tags text]
+    $canv bind $linehtag($id) $ctxbut "rowmenu %X %Y $id"
+    set linentag($id) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
+			   -text $name -font $nfont -tags text]
+    set linedtag($id) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
+			   -text $date -font mainfont -tags text]
     if {$selectedline == $row} {
-	make_secsel $row
+	make_secsel $id
     }
     set xr [expr {$xt + [font measure $font $headline]}]
     if {$xr > $canvxmax} {
@@ -5057,7 +5734,6 @@
     optimize_rows $ro1 0 $r2
     if {$need_redisplay || $nrows_drawn > 2000} {
 	clear_display
-	drawvisible
     }
 
     # make the lines join to already-drawn rows either side
@@ -5174,7 +5850,7 @@
 proc clear_display {} {
     global iddrawn linesegs need_redisplay nrows_drawn
     global vhighlights fhighlights nhighlights rhighlights
-    global linehtag linentag linedtag boldrows boldnamerows
+    global linehtag linentag linedtag boldids boldnameids
 
     allcanvs delete all
     catch {unset iddrawn}
@@ -5182,8 +5858,8 @@
     catch {unset linehtag}
     catch {unset linentag}
     catch {unset linedtag}
-    set boldrows {}
-    set boldnamerows {}
+    set boldids {}
+    set boldnameids {}
     catch {unset vhighlights}
     catch {unset fhighlights}
     catch {unset nhighlights}
@@ -5302,7 +5978,7 @@
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
-    global canv rowtextx curview fgcolor bgcolor
+    global canv rowtextx curview fgcolor bgcolor ctxbut
 
     set marks {}
     set ntags 0
@@ -5380,7 +6056,7 @@
 	if {$ntags >= 0} {
 	    $canv bind $t <1> [list showtag $tag 1]
 	} elseif {$nheads >= 0} {
-	    $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
+	    $canv bind $t $ctxbut [list headmenu %X %Y $id $tag]
 	}
     }
     return $xt
@@ -5504,6 +6180,7 @@
 	set fprogcoord 0
 	adjustprogress
     }
+    stopblaming
 }
 
 proc findmore {} {
@@ -5640,10 +6317,11 @@
 proc findselectline {l} {
     global findloc commentend ctext findcurline markingmatches gdttype
 
-    set markingmatches 1
+    set markingmatches [expr {$gdttype eq [mc "containing:"]}]
     set findcurline $l
     selectline $l 1
-    if {$findloc == [mc "All fields"] || $findloc == [mc "Comments"]} {
+    if {$markingmatches &&
+	($findloc eq [mc "All fields"] || $findloc eq [mc "Comments"])} {
 	# highlight the matches in the comments
 	set f [$ctext get 1.0 $commentend]
 	set matches [findmatches $f]
@@ -5723,11 +6401,11 @@
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
 proc appendwithlinks {text tags} {
-    global ctext linknum curview pendinglinks
+    global ctext linknum curview
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
-    set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
+    set links [regexp -indices -all -inline {\m[0-9a-f]{6,40}\M} $text]
     foreach l $links {
 	set s [lindex $l 0]
 	set e [lindex $l 1]
@@ -5741,16 +6419,27 @@
 }
 
 proc setlink {id lk} {
-    global curview ctext pendinglinks commitinterest
+    global curview ctext pendinglinks
 
-    if {[commitinview $id $curview]} {
+    set known 0
+    if {[string length $id] < 40} {
+	set matches [longid $id]
+	if {[llength $matches] > 0} {
+	    if {[llength $matches] > 1} return
+	    set known 1
+	    set id [lindex $matches 0]
+	}
+    } else {
+	set known [commitinview $id $curview]
+    }
+    if {$known} {
 	$ctext tag conf $lk -foreground blue -underline 1
-	$ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
+	$ctext tag bind $lk <1> [list selbyid $id]
 	$ctext tag bind $lk <Enter> {linkcursor %W 1}
 	$ctext tag bind $lk <Leave> {linkcursor %W -1}
     } else {
 	lappend pendinglinks($id) $lk
-	lappend commitinterest($id) {makelink %I}
+	interestedin $id {makelink %P}
     }
 }
 
@@ -5879,25 +6568,25 @@
     }
 }
 
-proc make_secsel {l} {
+proc make_secsel {id} {
     global linehtag linentag linedtag canv canv2 canv3
 
-    if {![info exists linehtag($l)]} return
+    if {![info exists linehtag($id)]} return
     $canv delete secsel
-    set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
+    set t [eval $canv create rect [$canv bbox $linehtag($id)] -outline {{}} \
 	       -tags secsel -fill [$canv cget -selectbackground]]
     $canv lower $t
     $canv2 delete secsel
-    set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
+    set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] -outline {{}} \
 	       -tags secsel -fill [$canv2 cget -selectbackground]]
     $canv2 lower $t
     $canv3 delete secsel
-    set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
+    set t [eval $canv3 create rect [$canv3 bbox $linedtag($id)] -outline {{}} \
 	       -tags secsel -fill [$canv3 cget -selectbackground]]
     $canv3 lower $t
 }
 
-proc selectline {l isnew} {
+proc selectline {l isnew {desired_loc {}}} {
     global canv ctext commitinfo selectedline
     global canvy0 linespc parents children curview
     global currentid sha1entry
@@ -5905,7 +6594,7 @@
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
     global targetrow targetid lastscrollrows
-    global autoselect
+    global autoselect jump_to_here
 
     catch {unset pending_select}
     $canv delete hover
@@ -5958,7 +6647,7 @@
 	drawvisible
     }
 
-    make_secsel $l
+    make_secsel $id
 
     if {$isnew} {
 	addtohistory [list selbyid $id]
@@ -6044,6 +6733,7 @@
     $ctext conf -state disabled
     set commentend [$ctext index "end - 1c"]
 
+    set jump_to_here $desired_loc
     init_flist [mc "Comments"]
     if {$cmitmode eq "tree"} {
 	gettree $id
@@ -6196,7 +6886,7 @@
 	    set treepending $id
 	    set treefilelist($id) {}
 	    set treeidlist($id) {}
-	    fconfigure $gtf -blocking 0
+	    fconfigure $gtf -blocking 0 -encoding binary
 	    filerun $gtf [list gettreeline $gtf $id]
 	}
     } else {
@@ -6218,11 +6908,12 @@
 	    set line [string range $line 0 [expr {$i-1}]]
 	    if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
 	    set sha1 [lindex $line 2]
-	    if {[string index $fname 0] eq "\""} {
-		set fname [lindex $fname 0]
-	    }
 	    lappend treeidlist($id) $sha1
 	}
+	if {[string index $fname 0] eq "\""} {
+	    set fname [lindex $fname 0]
+	}
+	set fname [encoding convertfrom $fname]
 	lappend treefilelist($id) $fname
     }
     if {![eof $gtf]} {
@@ -6244,6 +6935,7 @@
 
 proc showfile {f} {
     global treefilelist treeidlist diffids nullid nullid2
+    global ctext_file_names ctext_file_lines
     global ctext commentend
 
     set i [lsearch -exact $treefilelist($diffids) $f]
@@ -6263,10 +6955,12 @@
 	    return
 	}
     }
-    fconfigure $bf -blocking 0
+    fconfigure $bf -blocking 0 -encoding [get_path_encoding $f]
     filerun $bf [list getblobline $bf $diffids]
     $ctext config -state normal
     clear_ctext $commentend
+    lappend ctext_file_names $f
+    lappend ctext_file_lines [lindex [split $commentend "."] 0]
     $ctext insert end "\n"
     $ctext insert end "$f\n" filesep
     $ctext config -state disabled
@@ -6287,109 +6981,43 @@
 	$ctext insert end "$line\n"
     }
     if {[eof $bf]} {
+	global jump_to_here ctext_file_names commentend
+
 	# delete last newline
 	$ctext delete "end - 2c" "end - 1c"
 	close $bf
+	if {$jump_to_here ne {} &&
+	    [lindex $jump_to_here 0] eq [lindex $ctext_file_names 0]} {
+	    set lnum [expr {[lindex $jump_to_here 1] +
+			    [lindex [split $commentend .] 0]}]
+	    mark_ctext_line $lnum
+	}
 	return 0
     }
     $ctext config -state disabled
     return [expr {$nl >= 1000? 2: 1}]
 }
 
+proc mark_ctext_line {lnum} {
+    global ctext markbgcolor
+
+    $ctext tag delete omark
+    $ctext tag add omark $lnum.0 "$lnum.0 + 1 line"
+    $ctext tag conf omark -background $markbgcolor
+    $ctext see $lnum.0
+}
+
 proc mergediff {id} {
-    global diffmergeid mdifffd
-    global diffids
-    global parents
-    global diffcontext
-    global limitdiffs vfilelimit curview
+    global diffmergeid
+    global diffids treediffs
+    global parents curview
 
     set diffmergeid $id
     set diffids $id
-    # this doesn't seem to actually affect anything...
-    set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
-    if {$limitdiffs && $vfilelimit($curview) ne {}} {
-	set cmd [concat $cmd -- $vfilelimit($curview)]
-    }
-    if {[catch {set mdf [open $cmd r]} err]} {
-	error_popup "[mc "Error getting merge diffs:"] $err"
-	return
-    }
-    fconfigure $mdf -blocking 0
-    set mdifffd($id) $mdf
+    set treediffs($id) {}
     set np [llength $parents($curview,$id)]
     settabs $np
-    filerun $mdf [list getmergediffline $mdf $id $np]
-}
-
-proc getmergediffline {mdf id np} {
-    global diffmergeid ctext cflist mergemax
-    global difffilestart mdifffd
-
-    $ctext conf -state normal
-    set nr 0
-    while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
-	if {![info exists diffmergeid] || $id != $diffmergeid
-	    || $mdf != $mdifffd($id)} {
-	    close $mdf
-	    return 0
-	}
-	if {[regexp {^diff --cc (.*)} $line match fname]} {
-	    # start of a new file
-	    $ctext insert end "\n"
-	    set here [$ctext index "end - 1c"]
-	    lappend difffilestart $here
-	    add_flist [list $fname]
-	    set l [expr {(78 - [string length $fname]) / 2}]
-	    set pad [string range "----------------------------------------" 1 $l]
-	    $ctext insert end "$pad $fname $pad\n" filesep
-	} elseif {[regexp {^@@} $line]} {
-	    $ctext insert end "$line\n" hunksep
-	} elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
-	    # do nothing
-	} else {
-	    # parse the prefix - one ' ', '-' or '+' for each parent
-	    set spaces {}
-	    set minuses {}
-	    set pluses {}
-	    set isbad 0
-	    for {set j 0} {$j < $np} {incr j} {
-		set c [string range $line $j $j]
-		if {$c == " "} {
-		    lappend spaces $j
-		} elseif {$c == "-"} {
-		    lappend minuses $j
-		} elseif {$c == "+"} {
-		    lappend pluses $j
-		} else {
-		    set isbad 1
-		    break
-		}
-	    }
-	    set tags {}
-	    set num {}
-	    if {!$isbad && $minuses ne {} && $pluses eq {}} {
-		# line doesn't appear in result, parents in $minuses have the line
-		set num [lindex $minuses 0]
-	    } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
-		# line appears in result, parents in $pluses don't have the line
-		lappend tags mresult
-		set num [lindex $spaces 0]
-	    }
-	    if {$num ne {}} {
-		if {$num >= $mergemax} {
-		    set num "max"
-		}
-		lappend tags m$num
-	    }
-	    $ctext insert end "$line\n" $tags
-	}
-    }
-    $ctext conf -state disabled
-    if {[eof $mdf]} {
-	close $mdf
-	return 0
-    }
-    return [expr {$nr >= 1000? 2: 1}]
+    getblobdiffs $id
 }
 
 proc startdiff {ids} {
@@ -6481,27 +7109,44 @@
 
     set treepending $ids
     set treediff {}
-    fconfigure $gdtf -blocking 0
+    fconfigure $gdtf -blocking 0 -encoding binary
     filerun $gdtf [list gettreediffline $gdtf $ids]
 }
 
 proc gettreediffline {gdtf ids} {
     global treediff treediffs treepending diffids diffmergeid
-    global cmitmode vfilelimit curview limitdiffs
+    global cmitmode vfilelimit curview limitdiffs perfile_attrs
 
     set nr 0
-    while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
+    set sublist {}
+    set max 1000
+    if {$perfile_attrs} {
+	# cache_gitattr is slow, and even slower on win32 where we
+	# have to invoke it for only about 30 paths at a time
+	set max 500
+	if {[tk windowingsystem] == "win32"} {
+	    set max 120
+	}
+    }
+    while {[incr nr] <= $max && [gets $gdtf line] >= 0} {
 	set i [string first "\t" $line]
 	if {$i >= 0} {
 	    set file [string range $line [expr {$i+1}] end]
 	    if {[string index $file 0] eq "\""} {
 		set file [lindex $file 0]
 	    }
-	    lappend treediff $file
+	    set file [encoding convertfrom $file]
+	    if {$file ne [lindex $treediff end]} {
+		lappend treediff $file
+		lappend sublist $file
+	    }
 	}
     }
+    if {$perfile_attrs} {
+	cache_gitattr encoding $sublist
+    }
     if {![eof $gdtf]} {
-	return [expr {$nr >= 1000? 2: 1}]
+	return [expr {$nr >= $max? 2: 1}]
     }
     close $gdtf
     if {$limitdiffs && $vfilelimit($curview) ne {}} {
@@ -6516,7 +7161,7 @@
 	set treediffs($ids) $treediff
     }
     unset treepending
-    if {$cmitmode eq "tree"} {
+    if {$cmitmode eq "tree" && [llength $diffids] == 1} {
 	gettree $diffids
     } elseif {$ids != $diffids} {
 	if {![info exists diffmergeid]} {
@@ -6554,8 +7199,9 @@
     global diffcontext
     global ignorespace
     global limitdiffs vfilelimit curview
+    global diffencoding targetline diffnparents
 
-    set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
+    set cmd [diffcmd $ids "-p -C --cc --no-commit-id -U$diffcontext"]
     if {$ignorespace} {
 	append cmd " -w"
     }
@@ -6563,11 +7209,14 @@
 	set cmd [concat $cmd -- $vfilelimit($curview)]
     }
     if {[catch {set bdf [open $cmd r]} err]} {
-	puts "error getting diffs: $err"
+	error_popup [mc "Error getting diffs: %s" $err]
 	return
     }
+    set targetline {}
+    set diffnparents 0
     set diffinhdr 0
-    fconfigure $bdf -blocking 0
+    set diffencoding [get_path_encoding {}]
+    fconfigure $bdf -blocking 0 -encoding binary
     set blobdifffd($ids) $bdf
     filerun $bdf [list getblobdiffline $bdf $diffids]
 }
@@ -6586,21 +7235,32 @@
 }
 
 proc makediffhdr {fname ids} {
-    global ctext curdiffstart treediffs
+    global ctext curdiffstart treediffs diffencoding
+    global ctext_file_names jump_to_here targetline diffline
 
+    set fname [encoding convertfrom $fname]
+    set diffencoding [get_path_encoding $fname]
     set i [lsearch -exact $treediffs($ids) $fname]
     if {$i >= 0} {
 	setinlist difffilestart $i $curdiffstart
     }
+    lset ctext_file_names end $fname
     set l [expr {(78 - [string length $fname]) / 2}]
     set pad [string range "----------------------------------------" 1 $l]
     $ctext insert $curdiffstart "$pad $fname $pad" filesep
+    set targetline {}
+    if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
+	set targetline [lindex $jump_to_here 1]
+    }
+    set diffline 0
 }
 
 proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdiffstart
     global diffnexthead diffnextnote difffilestart
-    global diffinhdr treediffs
+    global ctext_file_names ctext_file_lines
+    global diffinhdr treediffs mergemax diffnparents
+    global diffencoding jump_to_here targetline diffline
 
     set nr 0
     $ctext conf -state normal
@@ -6609,40 +7269,74 @@
 	    close $bdf
 	    return 0
 	}
-	if {![string compare -length 11 "diff --git " $line]} {
-	    # trim off "diff --git "
-	    set line [string range $line 11 end]
-	    set diffinhdr 1
-	    # start of a new file
-	    $ctext insert end "\n"
-	    set curdiffstart [$ctext index "end - 1c"]
-	    $ctext insert end "\n" filesep
-	    # If the name hasn't changed the length will be odd,
-	    # the middle char will be a space, and the two bits either
-	    # side will be a/name and b/name, or "a/name" and "b/name".
-	    # If the name has changed we'll get "rename from" and
-	    # "rename to" or "copy from" and "copy to" lines following this,
-	    # and we'll use them to get the filenames.
-	    # This complexity is necessary because spaces in the filename(s)
-	    # don't get escaped.
-	    set l [string length $line]
-	    set i [expr {$l / 2}]
-	    if {!(($l & 1) && [string index $line $i] eq " " &&
-		  [string range $line 2 [expr {$i - 1}]] eq \
-		      [string range $line [expr {$i + 3}] end])} {
+	if {![string compare -length 5 "diff " $line]} {
+	    if {![regexp {^diff (--cc|--git) } $line m type]} {
+		set line [encoding convertfrom $line]
+		$ctext insert end "$line\n" hunksep
 		continue
 	    }
-	    # unescape if quoted and chop off the a/ from the front
-	    if {[string index $line 0] eq "\""} {
-		set fname [string range [lindex $line 0] 2 end]
+	    # start of a new file
+	    set diffinhdr 1
+	    $ctext insert end "\n"
+	    set curdiffstart [$ctext index "end - 1c"]
+	    lappend ctext_file_names ""
+	    lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+	    $ctext insert end "\n" filesep
+
+	    if {$type eq "--cc"} {
+		# start of a new file in a merge diff
+		set fname [string range $line 10 end]
+		if {[lsearch -exact $treediffs($ids) $fname] < 0} {
+		    lappend treediffs($ids) $fname
+		    add_flist [list $fname]
+		}
+
 	    } else {
-		set fname [string range $line 2 [expr {$i - 1}]]
+		set line [string range $line 11 end]
+		# If the name hasn't changed the length will be odd,
+		# the middle char will be a space, and the two bits either
+		# side will be a/name and b/name, or "a/name" and "b/name".
+		# If the name has changed we'll get "rename from" and
+		# "rename to" or "copy from" and "copy to" lines following
+		# this, and we'll use them to get the filenames.
+		# This complexity is necessary because spaces in the
+		# filename(s) don't get escaped.
+		set l [string length $line]
+		set i [expr {$l / 2}]
+		if {!(($l & 1) && [string index $line $i] eq " " &&
+		      [string range $line 2 [expr {$i - 1}]] eq \
+			  [string range $line [expr {$i + 3}] end])} {
+		    continue
+		}
+		# unescape if quoted and chop off the a/ from the front
+		if {[string index $line 0] eq "\""} {
+		    set fname [string range [lindex $line 0] 2 end]
+		} else {
+		    set fname [string range $line 2 [expr {$i - 1}]]
+		}
 	    }
 	    makediffhdr $fname $ids
 
-	} elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
-		       $line match f1l f1c f2l f2c rest]} {
+	} elseif {![string compare -length 16 "* Unmerged path " $line]} {
+	    set fname [encoding convertfrom [string range $line 16 end]]
+	    $ctext insert end "\n"
+	    set curdiffstart [$ctext index "end - 1c"]
+	    lappend ctext_file_names $fname
+	    lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+	    $ctext insert end "$line\n" filesep
+	    set i [lsearch -exact $treediffs($ids) $fname]
+	    if {$i >= 0} {
+		setinlist difffilestart $i $curdiffstart
+	    }
+
+	} elseif {![string compare -length 2 "@@" $line]} {
+	    regexp {^@@+} $line ats
+	    set line [encoding convertfrom $diffencoding $line]
 	    $ctext insert end "$line\n" hunksep
+	    if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
+		set diffline $nl
+	    }
+	    set diffnparents [expr {[string length $ats] - 1}]
 	    set diffinhdr 0
 
 	} elseif {$diffinhdr} {
@@ -6651,6 +7345,7 @@
 		if {[string index $fname 0] eq "\""} {
 		    set fname [lindex $fname 0]
 		}
+		set fname [encoding convertfrom $fname]
 		set i [lsearch -exact $treediffs($ids) $fname]
 		if {$i >= 0} {
 		    setinlist difffilestart $i $curdiffstart
@@ -6672,12 +7367,44 @@
 	    $ctext insert end "$line\n" filesep
 
 	} else {
-	    set x [string range $line 0 0]
-	    if {$x == "-" || $x == "+"} {
-		set tag [expr {$x == "+"}]
-		$ctext insert end "$line\n" d$tag
-	    } elseif {$x == " "} {
-		$ctext insert end "$line\n"
+	    set line [encoding convertfrom $diffencoding $line]
+	    # parse the prefix - one ' ', '-' or '+' for each parent
+	    set prefix [string range $line 0 [expr {$diffnparents - 1}]]
+	    set tag [expr {$diffnparents > 1? "m": "d"}]
+	    if {[string trim $prefix " -+"] eq {}} {
+		# prefix only has " ", "-" and "+" in it: normal diff line
+		set num [string first "-" $prefix]
+		if {$num >= 0} {
+		    # removed line, first parent with line is $num
+		    if {$num >= $mergemax} {
+			set num "max"
+		    }
+		    $ctext insert end "$line\n" $tag$num
+		} else {
+		    set tags {}
+		    if {[string first "+" $prefix] >= 0} {
+			# added line
+			lappend tags ${tag}result
+			if {$diffnparents > 1} {
+			    set num [string first " " $prefix]
+			    if {$num >= 0} {
+				if {$num >= $mergemax} {
+				    set num "max"
+				}
+				lappend tags m$num
+			    }
+			}
+		    }
+		    if {$targetline ne {}} {
+			if {$diffline == $targetline} {
+			    set seehere [$ctext index "end - 1 chars"]
+			    set targetline {}
+			} else {
+			    incr diffline
+			}
+		    }
+		    $ctext insert end "$line\n" $tags
+		}
 	    } else {
 		# "\ No newline at end of file",
 		# or something else we don't recognize
@@ -6685,6 +7412,9 @@
 	    }
 	}
     }
+    if {[info exists seehere]} {
+	mark_ctext_line [lindex [split $seehere .] 0]
+    }
     $ctext conf -state disabled
     if {[eof $bdf]} {
 	close $bdf
@@ -6697,7 +7427,7 @@
     global ctext diffelide
 
     $ctext tag conf d0 -elide [lindex $diffelide 0]
-    $ctext tag conf d1 -elide [lindex $diffelide 1]
+    $ctext tag conf dresult -elide [lindex $diffelide 1]
 }
 
 proc highlightfile {loc cline} {
@@ -6745,6 +7475,7 @@
 
 proc clear_ctext {{first 1.0}} {
     global ctext smarktop smarkbot
+    global ctext_file_names ctext_file_lines
     global pendinglinks
 
     set l [lindex [split $first .] 0]
@@ -6758,6 +7489,8 @@
     if {$first eq "1.0"} {
 	catch {unset pendinglinks}
     }
+    set ctext_file_names {}
+    set ctext_file_lines {}
 }
 
 proc settabs {{firstab {}}} {
@@ -7033,13 +7766,13 @@
     } else {
 	set id [string tolower $sha1string]
 	if {[regexp {^[0-9a-f]{4,39}$} $id]} {
-	    set matches [array names varcid "$curview,$id*"]
+	    set matches [longid $id]
 	    if {$matches ne {}} {
 		if {[llength $matches] > 1} {
 		    error_popup [mc "Short SHA1 id %s is ambiguous" $id]
 		    return
 		}
-		set id [lindex [split [lindex $matches 0] ","] 1]
+		set id [lindex $matches 0]
 	    }
 	}
     }
@@ -7249,16 +7982,16 @@
     if {$id ne $nullid && $id ne $nullid2} {
 	set menu $rowctxmenu
 	if {$mainhead ne {}} {
-	    $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
+	    $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead] -state normal
 	} else {
 	    $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
 	}
     } else {
 	set menu $fakerowmenu
     }
-    $menu entryconfigure [mc "Diff this -> selected"] -state $state
-    $menu entryconfigure [mc "Diff selected -> this"] -state $state
-    $menu entryconfigure [mc "Make patch"] -state $state
+    $menu entryconfigure [mca "Diff this -> selected"] -state $state
+    $menu entryconfigure [mca "Diff selected -> this"] -state $state
+    $menu entryconfigure [mca "Make patch"] -state $state
     tk_popup $menu $x $y
 }
 
@@ -7312,6 +8045,7 @@
     set patchtop $top
     catch {destroy $top}
     toplevel $top
+    make_transient $top .
     label $top.title -text [mc "Generate patch"]
     grid $top.title - -pady 10
     label $top.from -text [mc "From:"]
@@ -7342,6 +8076,8 @@
     frame $top.buts
     button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
     button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
+    bind $top <Key-Return> mkpatchgo
+    bind $top <Key-Escape> mkpatchcan
     grid $top.buts.gen $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7376,7 +8112,7 @@
     set cmd [lrange $cmd 1 end]
     lappend cmd >$fname &
     if {[catch {eval exec $cmd} err]} {
-	error_popup "[mc "Error creating patch:"] $err"
+	error_popup "[mc "Error creating patch:"] $err" $patchtop
     }
     catch {destroy $patchtop}
     unset patchtop
@@ -7396,6 +8132,7 @@
     set mktagtop $top
     catch {destroy $top}
     toplevel $top
+    make_transient $top .
     label $top.title -text [mc "Create tag"]
     grid $top.title - -pady 10
     label $top.id -text [mc "ID:"]
@@ -7413,6 +8150,8 @@
     frame $top.buts
     button $top.buts.gen -text [mc "Create"] -command mktaggo
     button $top.buts.can -text [mc "Cancel"] -command mktagcan
+    bind $top <Key-Return> mktaggo
+    bind $top <Key-Escape> mktagcan
     grid $top.buts.gen $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7426,18 +8165,18 @@
     set id [$mktagtop.sha1 get]
     set tag [$mktagtop.tag get]
     if {$tag == {}} {
-	error_popup [mc "No tag name specified"]
-	return
+	error_popup [mc "No tag name specified"] $mktagtop
+	return 0
     }
     if {[info exists tagids($tag)]} {
-	error_popup [mc "Tag \"%s\" already exists" $tag]
-	return
+	error_popup [mc "Tag \"%s\" already exists" $tag] $mktagtop
+	return 0
     }
     if {[catch {
 	exec git tag $tag $id
     } err]} {
-	error_popup "[mc "Error creating tag:"] $err"
-	return
+	error_popup "[mc "Error creating tag:"] $err" $mktagtop
+	return 0
     }
 
     set tagids($tag) $id
@@ -7446,6 +8185,7 @@
     addedtag $id
     dispneartags 0
     run refill_reflist
+    return 1
 }
 
 proc redrawtags {id} {
@@ -7463,16 +8203,16 @@
     $canv itemconf $circleitem($row) -fill $ofill
     $canv delete tag.$id
     set xt [eval drawtags $id $idpos($id)]
-    $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
-    set text [$canv itemcget $linehtag($row) -text]
-    set font [$canv itemcget $linehtag($row) -font]
+    $canv coords $linehtag($id) $xt [lindex $idpos($id) 2]
+    set text [$canv itemcget $linehtag($id) -text]
+    set font [$canv itemcget $linehtag($id) -font]
     set xr [expr {$xt + [font measure $font $text]}]
     if {$xr > $canvxmax} {
 	set canvxmax $xr
 	setcanvscroll
     }
     if {[info exists currentid] && $currentid == $id} {
-	make_secsel $row
+	make_secsel $id
     }
 }
 
@@ -7484,7 +8224,7 @@
 }
 
 proc mktaggo {} {
-    domktag
+    if {![domktag]} return
     mktagcan
 }
 
@@ -7495,6 +8235,7 @@
     set wrcomtop $top
     catch {destroy $top}
     toplevel $top
+    make_transient $top .
     label $top.title -text [mc "Write commit to file"]
     grid $top.title - -pady 10
     label $top.id -text [mc "ID:"]
@@ -7516,6 +8257,8 @@
     frame $top.buts
     button $top.buts.gen -text [mc "Write"] -command wrcomgo
     button $top.buts.can -text [mc "Cancel"] -command wrcomcan
+    bind $top <Key-Return> wrcomgo
+    bind $top <Key-Escape> wrcomcan
     grid $top.buts.gen $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7530,7 +8273,7 @@
     set cmd "echo $id | [$wrcomtop.cmd get]"
     set fname [$wrcomtop.fname get]
     if {[catch {exec sh -c $cmd >$fname &} err]} {
-	error_popup "[mc "Error writing commit:"] $err"
+	error_popup "[mc "Error writing commit:"] $err" $wrcomtop
     }
     catch {destroy $wrcomtop}
     unset wrcomtop
@@ -7549,6 +8292,7 @@
     set top .makebranch
     catch {destroy $top}
     toplevel $top
+    make_transient $top .
     label $top.title -text [mc "Create new branch"]
     grid $top.title - -pady 10
     label $top.id -text [mc "ID:"]
@@ -7562,6 +8306,8 @@
     frame $top.buts
     button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
     button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
+    bind $top <Key-Return> [list mkbrgo $top]
+    bind $top <Key-Escape> "catch {destroy $top}"
     grid $top.buts.go $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
     grid columnconfigure $top.buts 1 -weight 1 -uniform a
@@ -7574,29 +8320,73 @@
 
     set name [$top.name get]
     set id [$top.sha1 get]
+    set cmdargs {}
+    set old_id {}
     if {$name eq {}} {
-	error_popup [mc "Please specify a name for the new branch"]
+	error_popup [mc "Please specify a name for the new branch"] $top
 	return
     }
+    if {[info exists headids($name)]} {
+	if {![confirm_popup [mc \
+		"Branch '%s' already exists. Overwrite?" $name] $top]} {
+	    return
+	}
+	set old_id $headids($name)
+	lappend cmdargs -f
+    }
     catch {destroy $top}
+    lappend cmdargs $name $id
     nowbusy newbranch
     update
     if {[catch {
-	exec git branch $name $id
+	eval exec git branch $cmdargs
     } err]} {
 	notbusy newbranch
 	error_popup $err
     } else {
-	set headids($name) $id
-	lappend idheads($id) $name
-	addedhead $id $name
 	notbusy newbranch
-	redrawtags $id
+	if {$old_id ne {}} {
+	    movehead $id $name
+	    movedhead $id $name
+	    redrawtags $old_id
+	    redrawtags $id
+	} else {
+	    set headids($name) $id
+	    lappend idheads($id) $name
+	    addedhead $id $name
+	    redrawtags $id
+	}
 	dispneartags 0
 	run refill_reflist
     }
 }
 
+proc exec_citool {tool_args {baseid {}}} {
+    global commitinfo env
+
+    set save_env [array get env GIT_AUTHOR_*]
+
+    if {$baseid ne {}} {
+	if {![info exists commitinfo($baseid)]} {
+	    getcommit $baseid
+	}
+	set author [lindex $commitinfo($baseid) 1]
+	set date [lindex $commitinfo($baseid) 2]
+	if {[regexp {^\s*(\S.*\S|\S)\s*<(.*)>\s*$} \
+	            $author author name email]
+	    && $date ne {}} {
+	    set env(GIT_AUTHOR_NAME) $name
+	    set env(GIT_AUTHOR_EMAIL) $email
+	    set env(GIT_AUTHOR_DATE) $date
+	}
+    }
+
+    eval exec git citool $tool_args &
+
+    array unset env GIT_AUTHOR_*
+    array set env $save_env
+}
+
 proc cherrypick {} {
     global rowmenuid curview
     global mainhead mainheadid
@@ -7615,7 +8405,26 @@
     # no error occurs, and exec takes that as an indication of error...
     if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
 	notbusy cherrypick
-	error_popup $err
+	if {[regexp -line \
+		 {Entry '(.*)' (would be overwritten by merge|not uptodate)} \
+		 $err msg fname]} {
+	    error_popup [mc "Cherry-pick failed because of local changes\
+			to file '%s'.\nPlease commit, reset or stash\
+			your changes and try again." $fname]
+	} elseif {[regexp -line \
+		       {^(CONFLICT \(.*\):|Automatic cherry-pick failed)} \
+		       $err]} {
+	    if {[confirm_popup [mc "Cherry-pick failed because of merge\
+			conflict.\nDo you wish to run git citool to\
+			resolve it?"]]} {
+		# Force citool to read MERGE_MSG
+		file delete [file join [gitdir] "GITGUI_MSG"]
+		exec_citool {} $rowmenuid
+	    }
+	} else {
+	    error_popup $err
+	}
+	run updatecommits
 	return
     }
     set newhead [exec git rev-parse HEAD]
@@ -7626,6 +8435,7 @@
     }
     addnewchild $newhead $oldhead
     if {[commitinview $oldhead $curview]} {
+	# XXX this isn't right if we have a path limit...
 	insertrow $newhead $oldhead $curview
 	if {$mainhead ne {}} {
 	    movehead $newhead $mainhead
@@ -7645,7 +8455,7 @@
     set confirm_ok 0
     set w ".confirmreset"
     toplevel $w
-    wm transient $w .
+    make_transient $w .
     wm title $w [mc "Confirm reset"]
     message $w.m -text \
 	[mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
@@ -7668,6 +8478,7 @@
     button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
     pack $w.ok -side left -fill x -padx 20 -pady 20
     button $w.cancel -text [mc Cancel] -command "destroy $w"
+    bind $w <Key-Escape> [list destroy $w]
     pack $w.cancel -side right -fill x -padx 20 -pady 20
     bind $w <Visibility> "grab $w; focus $w"
     tkwait window $w
@@ -7732,7 +8543,7 @@
 
 proc cobranch {} {
     global headmenuid headmenuhead headids
-    global showlocalchanges mainheadid
+    global showlocalchanges
 
     # check the tree is clean first??
     nowbusy checkout [mc "Checking out"]
@@ -7753,6 +8564,7 @@
 
 proc readcheckoutstat {fd newhead newheadid} {
     global mainhead mainheadid headids showlocalchanges progresscoords
+    global viewmainheadid curview
 
     if {[gets $fd line] >= 0} {
 	if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
@@ -7770,6 +8582,7 @@
     set oldmainid $mainheadid
     set mainhead $newhead
     set mainheadid $newheadid
+    set viewmainheadid($curview) $newheadid
     redrawtags $oldmainid
     redrawtags $newheadid
     selbyid $newheadid
@@ -7824,6 +8637,7 @@
     }
     toplevel $top
     wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
+    make_transient $top .
     text $top.list -background $bgcolor -foreground $fgcolor \
 	-selectbackground $selectbgcolor -font mainfont \
 	-xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
@@ -7845,6 +8659,7 @@
     pack $top.f.l -side left
     grid $top.f - -sticky ew -pady 2
     button $top.close -command [list destroy $top] -text [mc "Close"]
+    bind $top <Key-Escape> [list destroy $top]
     grid $top.close -
     grid columnconfigure $top 0 -weight 1
     grid rowconfigure $top 0 -weight 1
@@ -7886,7 +8701,7 @@
 
 proc refill_reflist {} {
     global reflist reflistfilter showrefstop headids tagids otherrefids
-    global curview commitinterest
+    global curview
 
     if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
     set refs {}
@@ -7895,7 +8710,7 @@
 	    if {[commitinview $headids($n) $curview]} {
 		lappend refs [list $n H]
 	    } else {
-		set commitinterest($headids($n)) {run refill_reflist}
+		interestedin $headids($n) {run refill_reflist}
 	    }
 	}
     }
@@ -7904,7 +8719,7 @@
 	    if {[commitinview $tagids($n) $curview]} {
 		lappend refs [list $n T]
 	    } else {
-		set commitinterest($tagids($n)) {run refill_reflist}
+		interestedin $tagids($n) {run refill_reflist}
 	    }
 	}
     }
@@ -7913,7 +8728,7 @@
 	    if {[commitinview $otherrefids($n) $curview]} {
 		lappend refs [list $n o]
 	    } else {
-		set commitinterest($otherrefids($n)) {run refill_reflist}
+		interestedin $otherrefids($n) {run refill_reflist}
 	    }
 	}
     }
@@ -9150,6 +9965,7 @@
 
 proc choosefont {font which} {
     global fontparam fontlist fonttop fontattr
+    global prefstop
 
     set fontparam(which) $which
     set fontparam(font) $font
@@ -9163,6 +9979,7 @@
 	font create sample
 	eval font config sample [font actual $font]
 	toplevel $top
+	make_transient $top $prefstop
 	wm title $top [mc "Gitk font chooser"]
 	label $top.l -textvariable fontparam(which)
 	pack $top.l -side top
@@ -9196,6 +10013,8 @@
 	frame $top.buts
 	button $top.buts.ok -text [mc "OK"] -command fontok -default active
 	button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
+	bind $top <Key-Return> fontok
+	bind $top <Key-Escape> fontcan
 	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
@@ -9262,8 +10081,8 @@
 proc doprefs {} {
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
-    global bgcolor fgcolor ctext diffcolors selectbgcolor
-    global tabstop limitdiffs autoselect extdifftool
+    global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
+    global tabstop limitdiffs autoselect extdifftool perfile_attrs
 
     set top .gitkprefs
     set prefstop $top
@@ -9272,11 +10091,12 @@
 	return
     }
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-		   limitdiffs tabstop} {
+		   limitdiffs tabstop perfile_attrs} {
 	set oldprefs($v) [set $v]
     }
     toplevel $top
     wm title $top [mc "Gitk preferences"]
+    make_transient $top .
     label $top.ldisp -text [mc "Commit list display options"]
     grid $top.ldisp - -sticky w -pady 10
     label $top.spacer -text " "
@@ -9288,15 +10108,11 @@
 	-font optionfont
     spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
     grid x $top.maxpctl $top.maxpct -sticky w
-    frame $top.showlocal
-    label $top.showlocal.l -text [mc "Show local changes"] -font optionfont
-    checkbutton $top.showlocal.b -variable showlocalchanges
-    pack $top.showlocal.b $top.showlocal.l -side left
+    checkbutton $top.showlocal -text [mc "Show local changes"] \
+	-font optionfont -variable showlocalchanges
     grid x $top.showlocal -sticky w
-    frame $top.autoselect
-    label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
-    checkbutton $top.autoselect.b -variable autoselect
-    pack $top.autoselect.b $top.autoselect.l -side left
+    checkbutton $top.autoselect -text [mc "Auto-select SHA1"] \
+	-font optionfont -variable autoselect
     grid x $top.autoselect -sticky w
 
     label $top.ddisp -text [mc "Diff display options"]
@@ -9304,16 +10120,15 @@
     label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
     spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
     grid x $top.tabstopl $top.tabstop -sticky w
-    frame $top.ntag
-    label $top.ntag.l -text [mc "Display nearby tags"] -font optionfont
-    checkbutton $top.ntag.b -variable showneartags
-    pack $top.ntag.b $top.ntag.l -side left
+    checkbutton $top.ntag -text [mc "Display nearby tags"] \
+	-font optionfont -variable showneartags
     grid x $top.ntag -sticky w
-    frame $top.ldiff
-    label $top.ldiff.l -text [mc "Limit diffs to listed paths"] -font optionfont
-    checkbutton $top.ldiff.b -variable limitdiffs
-    pack $top.ldiff.b $top.ldiff.l -side left
+    checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
+	-font optionfont -variable limitdiffs
     grid x $top.ldiff -sticky w
+    checkbutton $top.lattr -text [mc "Support per-file encodings"] \
+	-font optionfont -variable perfile_attrs
+    grid x $top.lattr -sticky w
 
     entry $top.extdifft -textvariable extdifftool
     frame $top.extdifff
@@ -9328,31 +10143,37 @@
     grid $top.cdisp - -sticky w -pady 10
     label $top.bg -padx 40 -relief sunk -background $bgcolor
     button $top.bgbut -text [mc "Background"] -font optionfont \
-	-command [list choosecolor bgcolor {} $top.bg background setbg]
+	-command [list choosecolor bgcolor {} $top.bg [mc "background"] setbg]
     grid x $top.bgbut $top.bg -sticky w
     label $top.fg -padx 40 -relief sunk -background $fgcolor
     button $top.fgbut -text [mc "Foreground"] -font optionfont \
-	-command [list choosecolor fgcolor {} $top.fg foreground setfg]
+	-command [list choosecolor fgcolor {} $top.fg [mc "foreground"] setfg]
     grid x $top.fgbut $top.fg -sticky w
     label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
     button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
-	-command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
+	-command [list choosecolor diffcolors 0 $top.diffold [mc "diff old lines"] \
 		      [list $ctext tag conf d0 -foreground]]
     grid x $top.diffoldbut $top.diffold -sticky w
     label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
     button $top.diffnewbut -text [mc "Diff: new lines"] -font optionfont \
-	-command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
-		      [list $ctext tag conf d1 -foreground]]
+	-command [list choosecolor diffcolors 1 $top.diffnew [mc "diff new lines"] \
+		      [list $ctext tag conf dresult -foreground]]
     grid x $top.diffnewbut $top.diffnew -sticky w
     label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
     button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
 	-command [list choosecolor diffcolors 2 $top.hunksep \
-		      "diff hunk header" \
+		      [mc "diff hunk header"] \
 		      [list $ctext tag conf hunksep -foreground]]
     grid x $top.hunksepbut $top.hunksep -sticky w
+    label $top.markbgsep -padx 40 -relief sunk -background $markbgcolor
+    button $top.markbgbut -text [mc "Marked line bg"] -font optionfont \
+	-command [list choosecolor markbgcolor {} $top.markbgsep \
+		      [mc "marked line background"] \
+		      [list $ctext tag conf omark -background]]
+    grid x $top.markbgbut $top.markbgsep -sticky w
     label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
     button $top.selbgbut -text [mc "Select bg"] -font optionfont \
-	-command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
+	-command [list choosecolor selectbgcolor {} $top.selbgsep [mc "background"] setselbg]
     grid x $top.selbgbut $top.selbgsep -sticky w
 
     label $top.cfont -text [mc "Fonts: press to choose"]
@@ -9364,6 +10185,8 @@
     frame $top.buts
     button $top.buts.ok -text [mc "OK"] -command prefsok -default active
     button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
+    bind $top <Key-Return> prefsok
+    bind $top <Key-Escape> 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
@@ -9423,7 +10246,7 @@
     global oldprefs prefstop
 
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-		   limitdiffs tabstop} {
+		   limitdiffs tabstop perfile_attrs} {
 	global $v
 	set $v $oldprefs($v)
     }
@@ -9436,7 +10259,7 @@
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global fontpref mainfont textfont uifont
-    global limitdiffs treediffs
+    global limitdiffs treediffs perfile_attrs
 
     catch {destroy $prefstop}
     unset prefstop
@@ -9469,8 +10292,10 @@
 	    dohidelocalchanges
 	}
     }
-    if {$limitdiffs != $oldprefs(limitdiffs)} {
-	# treediffs elements are limited by path
+    if {$limitdiffs != $oldprefs(limitdiffs) ||
+	($perfile_attrs && !$oldprefs(perfile_attrs))} {
+	# treediffs elements are limited by path;
+	# won't have encodings cached if perfile_attrs was just turned on
 	catch {unset treediffs}
     }
     if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
@@ -9694,7 +10519,7 @@
     { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
     { GBK CP936 MS936 windows-936 }
     { JIS_Encoding csJISEncoding }
-    { Shift_JIS MS_Kanji csShiftJIS }
+    { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
     { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
       EUC-JP }
     { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
@@ -9729,14 +10554,17 @@
 }
 
 proc tcl_encoding {enc} {
-    global encoding_aliases
+    global encoding_aliases tcl_encoding_cache
+    if {[info exists tcl_encoding_cache($enc)]} {
+	return $tcl_encoding_cache($enc)
+    }
     set names [encoding names]
     set lcnames [string tolower $names]
     set enc [string tolower $enc]
     set i [lsearch -exact $lcnames $enc]
     if {$i < 0} {
 	# look for "isonnn" instead of "iso-nnn" or "iso_nnn"
-	if {[regsub {^iso[-_]} $enc iso encx]} {
+	if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
 	    set i [lsearch -exact $lcnames $encx]
 	}
     }
@@ -9748,7 +10576,7 @@
 	    foreach e $ll {
 		set i [lsearch -exact $lcnames $e]
 		if {$i < 0} {
-		    if {[regsub {^iso[-_]} $e iso ex]} {
+		    if {[regsub {^(iso|cp|ibm|jis)[-_]} $e {\1} ex]} {
 			set i [lsearch -exact $lcnames $ex]
 		    }
 		}
@@ -9757,10 +10585,70 @@
 	    break
 	}
     }
+    set tclenc {}
     if {$i >= 0} {
-	return [lindex $names $i]
+	set tclenc [lindex $names $i]
     }
-    return {}
+    set tcl_encoding_cache($enc) $tclenc
+    return $tclenc
+}
+
+proc gitattr {path attr default} {
+    global path_attr_cache
+    if {[info exists path_attr_cache($attr,$path)]} {
+	set r $path_attr_cache($attr,$path)
+    } else {
+	set r "unspecified"
+	if {![catch {set line [exec git check-attr $attr -- $path]}]} {
+	    regexp "(.*): encoding: (.*)" $line m f r
+	}
+	set path_attr_cache($attr,$path) $r
+    }
+    if {$r eq "unspecified"} {
+	return $default
+    }
+    return $r
+}
+
+proc cache_gitattr {attr pathlist} {
+    global path_attr_cache
+    set newlist {}
+    foreach path $pathlist {
+	if {![info exists path_attr_cache($attr,$path)]} {
+	    lappend newlist $path
+	}
+    }
+    set lim 1000
+    if {[tk windowingsystem] == "win32"} {
+	# windows has a 32k limit on the arguments to a command...
+	set lim 30
+    }
+    while {$newlist ne {}} {
+	set head [lrange $newlist 0 [expr {$lim - 1}]]
+	set newlist [lrange $newlist $lim end]
+	if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
+	    foreach row [split $rlist "\n"] {
+		if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+		    if {[string index $path 0] eq "\""} {
+			set path [encoding convertfrom [lindex $path 0]]
+		    }
+		    set path_attr_cache($attr,$path) $value
+		}
+	    }
+	}
+    }
+}
+
+proc get_path_encoding {path} {
+    global gui_encoding perfile_attrs
+    set tcl_enc $gui_encoding
+    if {$path ne {} && $perfile_attrs} {
+	set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
+	if {$enc2 ne {}} {
+	    set tcl_enc $enc2
+	}
+    }
+    return $tcl_enc
 }
 
 # First check that Tcl/Tk is recent enough
@@ -9777,6 +10665,9 @@
 catch {
     set gitencoding [exec git config --get i18n.commitencoding]
 }
+catch {
+    set gitencoding [exec git config --get i18n.logoutputencoding]
+}
 if {$gitencoding == ""} {
     set gitencoding "utf-8"
 }
@@ -9785,6 +10676,19 @@
     puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
 }
 
+set gui_encoding [encoding system]
+catch {
+    set enc [exec git config --get gui.encoding]
+    if {$enc ne {}} {
+	set tclenc [tcl_encoding $enc]
+	if {$tclenc ne {}} {
+	    set gui_encoding $tclenc
+	} else {
+	    puts stderr "Warning: encoding $enc is not supported by Tcl/Tk"
+	}
+    }
+}
+
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set uifont {Helvetica 9 bold}
@@ -9806,6 +10710,7 @@
 set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
+set perfile_attrs 0
 
 set extdifftool "meld"
 
@@ -9816,9 +10721,17 @@
 set diffcontext 3
 set ignorespace 0
 set selectbgcolor gray85
+set markbgcolor "#e0e0ff"
 
 set circlecolors {white blue gray blue blue}
 
+# button for popping up context menus
+if {[tk windowingsystem] eq "aqua"} {
+    set ctxbut <Button-2>
+} else {
+    set ctxbut <Button-3>
+}
+
 ## For msgcat loading, first locate the installation location.
 if { [info exists ::env(GITK_MSGSDIR)] } {
     ## Msgsdir was manually set in the environment.
@@ -9865,6 +10778,9 @@
     exit 1
 }
 
+set selecthead {}
+set selectheadid {}
+
 set revtreeargs {}
 set cmdline_files {}
 set i 0
@@ -9876,6 +10792,9 @@
 	    set cmdline_files [lrange $argv [expr {$i + 1}] end]
 	    break
 	}
+	"--select-commit=*" {
+	    set selecthead [string range $arg 16 end]
+	}
 	"--argscmd=*" {
 	    set revtreeargscmd [string range $arg 10 end]
 	}
@@ -9886,6 +10805,10 @@
     incr i
 }
 
+if {$selecthead eq "HEAD"} {
+    set selecthead {}
+}
+
 if {$i >= [llength $argv] && $revtreeargs ne {}} {
     # no -- on command line, but some arguments (other than --argscmd)
     if {[catch {
@@ -9929,8 +10852,8 @@
 set highlight_paths {}
 set findpattern {}
 set searchdirn -forwards
-set boldrows {}
-set boldnamerows {}
+set boldids {}
+set boldnameids {}
 set diffelide {0 0}
 set markingmatches 0
 set linkentercount 0
@@ -9977,8 +10900,8 @@
     set viewperm(1) 0
     set vdatemode(1) 0
     addviewmenu 1
-    .bar.view entryconf [mc "Edit view..."] -state normal
-    .bar.view entryconf [mc "Delete view"] -state normal
+    .bar.view entryconf [mca "Edit view..."] -state normal
+    .bar.view entryconf [mca "Delete view"] -state normal
 }
 
 if {[info exists permviews]} {
@@ -9993,4 +10916,9 @@
 	addviewmenu $n
     }
 }
+
+if {[tk windowingsystem] eq "win32"} {
+    focus -force .
+}
+
 getcommits {}
diff --git a/gitk-git/po/de.po b/gitk-git/po/de.po
index 04ee570..825dc98 100644
--- a/gitk-git/po/de.po
+++ b/gitk-git/po/de.po
@@ -7,25 +7,33 @@
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-24 22:32+0200\n"
-"PO-Revision-Date: 2008-05-24 22:40+0200\n"
+"POT-Creation-Date: 2008-12-06 20:40+0100\n"
+"PO-Revision-Date: 2008-12-06 20:45+0100\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:102
+#: gitk:113
 msgid "Couldn't get list of unmerged files:"
 msgstr "Liste der nicht-zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:329
+#: gitk:272
+msgid "Error parsing revisions:"
+msgstr "Fehler beim Laden der Versionen:"
+
+#: gitk:327
+msgid "Error executing --argscmd command:"
+msgstr "Fehler beim --argscmd Kommando:"
+
+#: gitk:340
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Keine Dateien ausgewählt: --merge angegeben, es existieren aber keine nicht-"
 "zusammengeführten Dateien."
 
-#: gitk:332
+#: gitk:343
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -33,257 +41,273 @@
 "Keine Dateien ausgewähle: --merge angegeben, aber keine nicht-"
 "zusammengeführten Dateien sind in der Dateiauswahl."
 
-#: gitk:354
+#: gitk:365 gitk:503
 msgid "Error executing git log:"
 msgstr "Fehler beim Ausführen von git-log:"
 
-#: gitk:369
+#: gitk:378
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:151 gitk:2191
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Versionen lesen..."
 
-#: gitk:275
-msgid "Can't parse git log output:"
-msgstr "Ausgabe von git-log kann nicht erkannt werden:"
-
-#: gitk:386 gitk:2195
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "Keine Versionen ausgewählt."
 
-#: gitk:500
+#: gitk:1399
+msgid "Can't parse git log output:"
+msgstr "Ausgabe von git-log kann nicht erkannt werden:"
+
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:599 gitk:621 gitk:1955 gitk:6424 gitk:7924 gitk:8083
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:623 gitk:1956 gitk:6108 gitk:6179 gitk:6276 gitk:6322 gitk:6426
-#: gitk:7925 gitk:8084
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:661
-msgid "File"
-msgstr "Datei"
-
-#: gitk:663
+#: gitk:1811
 msgid "Update"
 msgstr "Aktualisieren"
 
-#: gitk:1722
+#: gitk:1812
 msgid "Reload"
 msgstr "Neu laden"
 
-#: gitk:1723
+#: gitk:1813
 msgid "Reread references"
 msgstr "Zweige neu laden"
 
-#: gitk:665
+#: gitk:1814
 msgid "List references"
 msgstr "Zweige/Markierungen auflisten"
 
-#: gitk:666
+#: gitk:1915
+msgid "Start git gui"
+msgstr "»git gui« starten"
+
+#: gitk:1917
 msgid "Quit"
 msgstr "Beenden"
 
-#: gitk:668
-msgid "Edit"
-msgstr "Bearbeiten"
+#: gitk:1810
+msgid "File"
+msgstr "Datei"
 
-#: gitk:669
+#: gitk:1818
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: gitk:672 gitk:1892
-msgid "View"
-msgstr "Ansicht"
+#: gitk:1817
+msgid "Edit"
+msgstr "Bearbeiten"
 
-#: gitk:673
+#: gitk:1821
 msgid "New view..."
 msgstr "Neue Ansicht..."
 
-#: gitk:674 gitk:2133 gitk:8723
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Ansicht bearbeiten..."
 
-#: gitk:676 gitk:2134 gitk:8724
+#: gitk:1823
 msgid "Delete view"
 msgstr "Ansicht löschen"
 
-#: gitk:678
+#: gitk:1825
 msgid "All files"
 msgstr "Alle Dateien"
 
-#: gitk:682
-msgid "Help"
-msgstr "Hilfe"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Ansicht"
 
-#: gitk:683 gitk:1317
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Über gitk"
 
-#: gitk:684
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Tastenkürzel"
 
-#: gitk:741
+#: gitk:1827
+msgid "Help"
+msgstr "Hilfe"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1:"
 
-#: gitk:1831
+#: gitk:1918
 msgid "Row"
 msgstr "Zeile"
 
-#: gitk:1862
+#: gitk:1949
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:792
+#: gitk:1950
 msgid "next"
 msgstr "nächste"
 
-#: gitk:793
+#: gitk:1951
 msgid "prev"
 msgstr "vorige"
 
-#: gitk:794
+#: gitk:1952
 msgid "commit"
 msgstr "Version nach"
 
-#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "Beschreibung:"
 
-#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "Dateien:"
 
-#: gitk:801 gitk:2436
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "Änderungen:"
 
-#: gitk:810 gitk:812
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:812 gitk:2514 gitk:4274
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5957
-#: gitk:5972
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:845
+#: gitk:2003
 msgid "Search"
 msgstr "Suche"
 
-#: gitk:852
+#: gitk:2010
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:854
+#: gitk:2012
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:856
+#: gitk:2014
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:858
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:868
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Leerzeichenänderungen ignorieren"
 
-#: gitk:926
+#: gitk:2084
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:928
+#: gitk:2086
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:1053 gitk:1068 gitk:6023
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Vergleich diese -> gewählte"
 
-#: gitk:1055 gitk:1070 gitk:6024
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Vergleich gewählte -> diese"
 
-#: gitk:1057 gitk:1072 gitk:6025
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:1058 gitk:6163
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:1059 gitk:6256
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:1060 gitk:6310
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:1061
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:1063
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:1079
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:1081
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:1087
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:1089
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:2162
+#: gitk:2244
 msgid "External diff"
 msgstr "Externer Vergleich"
 
-#: gitk:2403
+#: gitk:2255
+msgid "Blame parent commit"
+msgstr "Annotieren der Elternversion"
+
+#: gitk:2360
+msgid "Show origin of this line"
+msgstr "Herkunft dieser Zeile anzeigen"
+
+#: gitk:2361
+msgid "Run git gui blame on this line"
+msgstr "Annotieren (»git gui blame«) von dieser Zeile"
+
+#: gitk:2606
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -297,431 +321,546 @@
 "\n"
 "Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
-"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public License"
+"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public "
+"License"
 
-#: gitk:1326 gitk:1387 gitk:6582
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:1345
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Gitk Tastaturbelegung"
 
-#: gitk:1347
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Gitk Tastaturbelegung:"
 
-#: gitk:1349
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tBeenden"
 
-#: gitk:1350
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Pos1>\t\tZur neuesten Version springen"
 
-#: gitk:1351
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<Ende>\t\tZur ältesten Version springen"
 
-#: gitk:1352
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Hoch>, p, i\tNächste neuere Version"
 
-#: gitk:1353
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Runter>, n, k\tNächste ältere Version"
 
-#: gitk:1354
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Links>, z, j\tEine Version zurückgehen"
 
-#: gitk:1355
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Rechts>, x, l\tEine Version weitergehen"
 
-#: gitk:1356
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<BildHoch>\tEine Seite nach oben blättern"
 
-#: gitk:1357
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<BildRunter>\tEine Seite nach unten blättern"
 
-#: gitk:1358
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
 
-#: gitk:1359
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
 
-#: gitk:1360
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
 
-#: gitk:1361
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
 
-#: gitk:1362
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-BildHoch>\tVersionsliste eine Seite hoch blättern"
 
-#: gitk:1363
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
 
-#: gitk:1364
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
 
-#: gitk:1365
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
 
-#: gitk:1366
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
 
-#: gitk:1367
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
 
-#: gitk:1368
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
 
-#: gitk:1369
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tVergleich um 18 Zeilen nach oben (»up«) blättern"
 
-#: gitk:1370
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tVergleich um 18 Zeilen nach unten (»down«) blättern"
 
-#: gitk:1371
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSuchen"
 
-#: gitk:1372
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tWeitersuchen"
 
-#: gitk:1373
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Eingabetaste>\tWeitersuchen"
 
-#: gitk:1374
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tWeitersuchen oder neue Suche beginnen"
 
-#: gitk:1375
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tRückwärts weitersuchen"
 
-#: gitk:1376
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tVergleich zur nächsten Datei (»file«) blättern"
 
-#: gitk:1377
+#: gitk:2547
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
 
-#: gitk:1378
+#: gitk:2548
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich"
 
-#: gitk:1379
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Nummerblock-Plus>\tSchriftgröße vergrößern"
 
-#: gitk:1380
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-Plus>\tSchriftgröße vergrößern"
 
-#: gitk:1381
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Nummernblock-> Schriftgröße verkleinern"
 
-#: gitk:1382
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-Minus>\tSchriftgröße verkleinern"
 
-#: gitk:1383
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAktualisieren"
 
-#: gitk:1896
-msgid "Gitk view definition"
-msgstr "Gitk Ansichten"
+#: gitk:2979
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Fehler beim Holen von »%s« von »%s«:"
 
-#: gitk:1921
-msgid "Name"
-msgstr "Name"
+#: gitk:3036 gitk:3045
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Fehler beim Erzeugen eines temporären Verzeichnisses »%s«:"
 
-#: gitk:1924
-msgid "Remember this view"
-msgstr "Diese Ansicht speichern"
+#: gitk:3058
+msgid "command failed:"
+msgstr "Kommando fehlgeschlagen:"
 
-#: gitk:3126
-msgid "Commits to include (arguments to git log):"
-msgstr "Versionen anzeigen (Argumente von git-log):"
+#: gitk:3078
+msgid "No such commit"
+msgstr "Version nicht gefunden"
 
-#: gitk:3133
-msgid "Command to generate more commits to include:"
-msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
+#: gitk:3083
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: Kommando fehlgeschlagen:"
 
-#: gitk:1942
-msgid "Enter files and directories to include, one per line:"
-msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
+#: gitk:3398
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Zusammenführungs-Spitze konnte nicht gelesen werden: %s"
 
-#: gitk:1989
-msgid "Error in commit selection arguments:"
-msgstr "Fehler in den ausgewählten Versionen:"
+#: gitk:3406
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Fehler beim Lesen der Bereitstellung (»index«): %s"
 
-#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8689 gitk:8690
-msgid "None"
-msgstr "Keine"
+#: gitk:3431
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "»git blame« konnte nicht gestartet werden: %s"
 
-#: gitk:2531 gitk:4336 gitk:5959 gitk:5974
-msgid "Date"
-msgstr "Datum"
-
-#: gitk:2531 gitk:4336
-msgid "CDate"
-msgstr "Eintragedatum"
-
-#: gitk:2680 gitk:2685
-msgid "Descendant"
-msgstr "Abkömmling"
-
-#: gitk:2681
-msgid "Not descendant"
-msgstr "Nicht Abkömmling"
-
-#: gitk:2688 gitk:2693
-msgid "Ancestor"
-msgstr "Vorgänger"
-
-#: gitk:2689
-msgid "Not ancestor"
-msgstr "Nicht Vorgänger"
-
-#: gitk:2924
-msgid "Local changes checked in to index but not committed"
-msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
-
-#: gitk:2954
-msgid "Local uncommitted changes, not checked in to index"
-msgstr "Lokale Änderungen, nicht bereitgestellt"
-
-#: gitk:4305
+#: gitk:3434 gitk:6160
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:4767
+#: gitk:3466
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Fehler beim Ausführen von »git blame«: %s"
+
+#: gitk:3494
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr ""
+"Diese Zeile stammt aus Version %s, welche nicht in dieser Ansicht gezeigt "
+"wird."
+
+#: gitk:3508
+msgid "External diff viewer failed:"
+msgstr "Externes Vergleich-(Diff-)Programm fehlgeschlagen:"
+
+#: gitk:3210
+msgid "Gitk view definition"
+msgstr "Gitk Ansichten"
+
+#: gitk:3630
+msgid "Remember this view"
+msgstr "Diese Ansicht speichern"
+
+#: gitk:3232
+msgid "Commits to include (arguments to git log):"
+msgstr "Versionen anzeigen (Argumente von git-log):"
+
+#: gitk:3632
+msgid "Use all refs"
+msgstr "Alle Zweige verwenden"
+
+#: gitk:3633
+msgid "Strictly sort by date"
+msgstr "Streng nach Datum sortieren"
+
+#: gitk:3634
+msgid "Mark branch sides"
+msgstr "Zweig-Seiten markieren"
+
+#: gitk:3635
+msgid "Since date:"
+msgstr "Von Datum:"
+
+#: gitk:3636
+msgid "Until date:"
+msgstr "Bis Datum:"
+
+#: gitk:3637
+msgid "Max count:"
+msgstr "Max. Anzahl:"
+
+#: gitk:3638
+msgid "Skip:"
+msgstr "Überspringen:"
+
+#: gitk:3639
+msgid "Limit to first parent"
+msgstr "Auf erste Elternversion beschränken"
+
+#: gitk:3640
+msgid "Command to generate more commits to include:"
+msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
+
+#: gitk:3749
+msgid "Name"
+msgstr "Name"
+
+#: gitk:3797
+msgid "Enter files and directories to include, one per line:"
+msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
+
+#: gitk:3811
+msgid "Apply (F5)"
+msgstr "Anwenden (F5)"
+
+#: gitk:3849
+msgid "Error in commit selection arguments:"
+msgstr "Fehler in den ausgewählten Versionen:"
+
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
+msgid "None"
+msgstr "Keine"
+
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
+msgid "Date"
+msgstr "Datum"
+
+#: gitk:3790 gitk:5580
+msgid "CDate"
+msgstr "Eintragedatum"
+
+#: gitk:3939 gitk:3944
+msgid "Descendant"
+msgstr "Abkömmling"
+
+#: gitk:3940
+msgid "Not descendant"
+msgstr "Nicht Abkömmling"
+
+#: gitk:3947 gitk:3952
+msgid "Ancestor"
+msgstr "Vorgänger"
+
+#: gitk:3948
+msgid "Not ancestor"
+msgstr "Nicht Vorgänger"
+
+#: gitk:4187
+msgid "Local changes checked in to index but not committed"
+msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
+
+#: gitk:4220
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Lokale Änderungen, nicht bereitgestellt"
+
+#: gitk:6673
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:4784 gitk:4790 gitk:5952
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:4795
+#: gitk:6077
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:4804
+#: gitk:6086
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:4807
+#: gitk:6089
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:4810
+#: gitk:6092
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:5094
-msgid "Error getting merge diffs:"
-msgstr "Fehler beim Laden des Vergleichs:"
+#: gitk:7209
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Fehler beim Laden des Vergleichs: %s"
 
-#: gitk:5779
+#: gitk:7748
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:5781
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1-Hashwert:"
 
-#: gitk:5806
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:5818
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA1-Hashwert »%s« unbekannt"
 
-#: gitk:5820
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Markierung/Zweig »%s« ist unbekannt"
 
-#: gitk:5962
+#: gitk:7290
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:6019
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:7204
+#: gitk:7349
 msgid "Detached head: can't reset"
 msgstr "Zweigspitze ist abgetrennt: Zurücksetzen nicht möglich"
 
-#: gitk:7236
+#: gitk:7381
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:6051
+#: gitk:7382
 msgid "From"
 msgstr "Von"
 
-#: gitk:6056
+#: gitk:7387
 msgid "To"
 msgstr "bis"
 
-#: gitk:6079
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:6081
+#: gitk:7412
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:6090
+#: gitk:7421
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:6099
+#: gitk:7430
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:6101 gitk:6270
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:6107
+#: gitk:7438
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:6143
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Fehler beim Patch erzeugen:"
 
-#: gitk:6165 gitk:6258 gitk:6312
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6174
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:6178 gitk:6321
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:6193
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:6197
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:6203
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Fehler bei Markierung erstellen:"
 
-#: gitk:6267
+#: gitk:7604
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:6275
+#: gitk:7612
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:6291
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:6317
+#: gitk:7654
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:6336
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:6365
+#: gitk:8328
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Zweig »%s« existiert bereits. Soll er überschrieben werden?"
+
+#: gitk:8394
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut "
 "eintragen?"
 
-#: gitk:6370
+#: gitk:7718
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:6382
+#: gitk:8408
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Pflücken fehlgeschlagen, da noch lokale Änderungen in Datei »%s«\n"
+"vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n"
+"zwischenspeichern (»git stash») und dann erneut versuchen."
+
+#: gitk:8414
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Pflücken fehlgeschlagen, da ein Zusammenführungs-Konflikt aufgetreten\n"
+"ist. Soll das »git citool« (Zusammenführungs-Werkzeug) aufgerufen\n"
+"werden, um diesen Konflikt aufzulösen?"
+
+#: gitk:8430
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:6405
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:6407
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:6411
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:6415
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:6418
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:6421
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -729,21 +868,21 @@
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:6437
+#: gitk:7777
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:6494
+#: gitk:7834
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:6524
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:6530
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -752,16 +891,16 @@
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:6561
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:6575
+#: gitk:7936
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:6869
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -769,125 +908,157 @@
 "Fehler beim Lesen der Strukturinformationen; Zweige und Vorgänger/Nachfolger "
 "Informationen werden unvollständig sein."
 
-#: gitk:7853
+#: gitk:9216
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:7853
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7893
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Gitk Schriften wählen"
 
-#: gitk:7910
+#: gitk:9279
 msgid "B"
 msgstr "F"
 
-#: gitk:7913
+#: gitk:9282
 msgid "I"
 msgstr "K"
 
-#: gitk:8006
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Gitk Einstellungen"
 
-#: gitk:8007
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Anzeige Versionsliste"
 
-#: gitk:8010
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:8014
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:8019
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:8024
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "SHA1-Hashwert automatisch markieren"
 
-#: gitk:8029
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Anzeige Vergleich"
 
-#: gitk:8031
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:8035
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Naheliegende Überschriften anzeigen"
 
-#: gitk:8040
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:9264
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr "Zeichenkodierung pro Datei ermitteln"
+
+#: gitk:9421
 msgid "External diff tool"
 msgstr "Externes Vergleich-(Diff-)Programm"
 
-#: gitk:9266
+#: gitk:9423
 msgid "Choose..."
 msgstr "Wählen..."
 
-#: gitk:9271
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:8048
+#: gitk:9431
 msgid "Background"
 msgstr "Hintergrund"
 
-#: gitk:8052
+#: gitk:10153 gitk:10183
+msgid "background"
+msgstr "Hintergrund"
+
+#: gitk:10156
 msgid "Foreground"
 msgstr "Vordergrund"
 
-#: gitk:8056
+#: gitk:10157
+msgid "foreground"
+msgstr "Vordergrund"
+
+#: gitk:10160
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:8061
+#: gitk:10161
+msgid "diff old lines"
+msgstr "Vergleich - Alte Zeilen"
+
+#: gitk:10165
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:8066
+#: gitk:10166
+msgid "diff new lines"
+msgstr "Vergleich - Neue Zeilen"
+
+#: gitk:10170
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:8072
+#: gitk:10172
+msgid "diff hunk header"
+msgstr "Vergleich - Änderungstitel"
+
+#: gitk:10176
+msgid "Marked line bg"
+msgstr "Markierte Zeile Hintergrund"
+
+#: gitk:10178
+msgid "marked line background"
+msgstr "markierte Zeile Hintergrund"
+
+#: gitk:10182
 msgid "Select bg"
 msgstr "Hintergrundfarbe Auswählen"
 
-#: gitk:8076
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:8078
+#: gitk:9461
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:8079
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Vergleich"
 
-#: gitk:8080
+#: gitk:9463
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:8096
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:8477
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -895,24 +1066,24 @@
 "Gitk läuft nicht mit dieser Version von Tcl/Tk.\n"
 "Gitk benötigt mindestens Tcl/Tk 8.4."
 
-#: gitk:8566
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:8570
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Git-Verzeichnis »%s« wurde nicht gefunden."
 
-#: gitk:8613
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:8625
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-#: gitk:9915
+#: gitk:10170
 msgid "Command line"
 msgstr "Kommandozeile"
diff --git a/gitk-git/po/es.po b/gitk-git/po/es.po
index 2cb1486..0e19b5e 100644
--- a/gitk-git/po/es.po
+++ b/gitk-git/po/es.po
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: gitk\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-03-25 11:20+0100\n"
 "Last-Translator: Santiago Gala <santiago.gala@gmail.com>\n"
 "Language-Team: Spanish\n"
@@ -16,676 +16,702 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:111
-msgid "Error executing git rev-list:"
-msgstr "Error al ejecutar git rev-list:"
+#: gitk:113
+msgid "Couldn't get list of unmerged files:"
+msgstr "Imposible obtener la lista de archivos pendientes de fusión:"
 
-#: gitk:124
+#: gitk:340
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"No hay archivos seleccionados: se seleccionó la opción --merge pero no hay "
+"archivos pendientes de fusión."
+
+#: gitk:343
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"No hay archivos seleccionados: se seleccionó la opción --merge pero los "
+"archivos especificados no necesitan fusión."
+
+#: gitk:378
 msgid "Reading"
 msgstr "Leyendo"
 
-#: gitk:151 gitk:2191
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Leyendo revisiones..."
 
-#: gitk:275
-msgid "Can't parse git log output:"
-msgstr "Error analizando la salida de git log:"
-
-#: gitk:386 gitk:2195
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "No se seleccionaron revisiones"
 
-#: gitk:500
+#: gitk:1399
+msgid "Can't parse git log output:"
+msgstr "Error analizando la salida de git log:"
+
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Falta información sobre las revisiones"
 
-#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "Aceptar"
 
-#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
-#: gitk:7924 gitk:8083
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Cancelar"
 
-#: gitk:661
-msgid "File"
-msgstr "Archivo"
-
-#: gitk:663
+#: gitk:1811
 msgid "Update"
 msgstr "Actualizar"
 
-#: gitk:664
+#: gitk:1813
 msgid "Reread references"
 msgstr "Releer referencias"
 
-#: gitk:665
+#: gitk:1814
 msgid "List references"
 msgstr "Lista de referencias"
 
-#: gitk:666
+#: gitk:1815
 msgid "Quit"
 msgstr "Salir"
 
-#: gitk:668
-msgid "Edit"
-msgstr "Editar"
+#: gitk:1810
+msgid "File"
+msgstr "Archivo"
 
-#: gitk:669
+#: gitk:1818
 msgid "Preferences"
 msgstr "Preferencias"
 
-#: gitk:672 gitk:1892
-msgid "View"
-msgstr "Vista"
+#: gitk:1817
+msgid "Edit"
+msgstr "Editar"
 
-#: gitk:673
+#: gitk:1821
 msgid "New view..."
 msgstr "Nueva vista..."
 
-#: gitk:674 gitk:2133 gitk:8722
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Modificar vista..."
 
-#: gitk:676 gitk:2134 gitk:8723
+#: gitk:1823
 msgid "Delete view"
 msgstr "Eliminar vista"
 
-#: gitk:678
+#: gitk:1825
 msgid "All files"
 msgstr "Todos los archivos"
 
-#: gitk:682
-msgid "Help"
-msgstr "Ayuda"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Vista"
 
-#: gitk:683 gitk:1317
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Acerca de gitk"
 
-#: gitk:684
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Combinaciones de teclas"
 
-#: gitk:741
+#: gitk:1827
+msgid "Help"
+msgstr "Ayuda"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1 ID: "
 
-#: gitk:791
+#: gitk:1918
+msgid "Row"
+msgstr ""
+
+#: gitk:1949
 msgid "Find"
 msgstr "Buscar"
 
-#: gitk:792
+#: gitk:1950
 msgid "next"
 msgstr "<<"
 
-#: gitk:793
+#: gitk:1951
 msgid "prev"
 msgstr ">>"
 
-#: gitk:794
+#: gitk:1952
 msgid "commit"
 msgstr "revisión"
 
-#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "que contiene:"
 
-#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "que modifica la ruta:"
 
-#: gitk:801 gitk:2436
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "que añade/elimina cadena:"
 
-#: gitk:810 gitk:812
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Exacto"
 
-#: gitk:812 gitk:2514 gitk:4274
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr "NoMayús"
 
-#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr "Regex"
 
-#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Todos los campos"
 
-#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Título"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Comentarios"
 
-#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
-#: gitk:5971
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr ""
 
-#: gitk:845
+#: gitk:2003
 msgid "Search"
 msgstr "Buscar"
 
-#: gitk:852
+#: gitk:2010
 msgid "Diff"
 msgstr "Diferencia"
 
-#: gitk:854
+#: gitk:2012
 msgid "Old version"
 msgstr "Versión antigua"
 
-#: gitk:856
+#: gitk:2014
 msgid "New version"
 msgstr "Versión nueva"
 
-#: gitk:858
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Líneas de contexto"
 
-#: gitk:868
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Ignora cambios de espaciado"
 
-#: gitk:926
+#: gitk:2084
 msgid "Patch"
 msgstr "Parche"
 
-#: gitk:928
+#: gitk:2086
 msgid "Tree"
 msgstr "Árbol"
 
-#: gitk:1053 gitk:1068 gitk:6022
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Diferencia de esta -> seleccionada"
 
-#: gitk:1055 gitk:1070 gitk:6023
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Diferencia de seleccionada -> esta"
 
-#: gitk:1057 gitk:1072 gitk:6024
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Crear patch"
 
-#: gitk:1058 gitk:6162
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Crear etiqueta"
 
-#: gitk:1059 gitk:6255
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Escribir revisiones a archivo"
 
-#: gitk:1060 gitk:6309
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Crear nueva rama"
 
-#: gitk:1061
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Añadir esta revisión a la rama actual (cherry-pick)"
 
-#: gitk:1063
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "Traer la rama HEAD aquí"
 
-#: gitk:1079
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Cambiar a esta rama"
 
-#: gitk:1081
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Eliminar esta rama"
 
-#: gitk:1087
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Seleccionar también"
 
-#: gitk:1089
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Seleccionar sólo"
 
-#: gitk:1318
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizador de revisiones para git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
-"Uso y redistribución permitidos según los términos de la Licencia Pública General de "
-"GNU (GNU GPL)"
+"Uso y redistribución permitidos según los términos de la Licencia Pública "
+"General de GNU (GNU GPL)"
 
-#: gitk:1326 gitk:1387 gitk:6581
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Cerrar"
 
-#: gitk:1345
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Combinaciones de tecla de Gitk"
 
-#: gitk:1347
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Combinaciones de tecla de Gitk:"
 
-#: gitk:1349
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tSalir"
 
-#: gitk:1350
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tIr a la primera revisión"
 
-#: gitk:1351
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tIr a la última revisión"
 
-#: gitk:1352
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Up>, p, i\tSubir una revisión"
 
-#: gitk:1353
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Down>, n, k\tBajar una revisión"
 
-#: gitk:1354
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Left>, z, j\tRetroceder en la historia"
 
-#: gitk:1355
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Right>, x, l\tAvanzar en la historia"
 
-#: gitk:1356
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tSubir una página en la lista de revisiones"
 
-#: gitk:1357
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tBajar una página en la lista de revisiones"
 
-#: gitk:1358
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tDesplazarse al inicio de la lista de revisiones"
 
-#: gitk:1359
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tDesplazarse al final de la lista de revisiones"
 
-#: gitk:1360
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Up>\tDesplazar una línea hacia arriba la lista de revisiones"
 
-#: gitk:1361
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Down>\tDesplazar una línea hacia abajo la lista de revisiones"
 
-#: gitk:1362
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tDesplazar una página hacia arriba la lista de revisiones"
 
-#: gitk:1363
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tDesplazar una página hacia abajo la lista de revisiones"
 
-#: gitk:1364
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Shift-Up>\tBuscar hacia atrás (arriba, revisiones siguientes)"
 
-#: gitk:1365
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Shift-Down>\tBuscar hacia adelante (abajo, revisiones anteriores)"
 
-#: gitk:1366
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tDesplaza hacia arriba una página la vista de diferencias"
 
-#: gitk:1367
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Backspace>\tDesplaza hacia arriba una página la vista de diferencias"
 
-#: gitk:1368
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Space>\t\tDesplaza hacia abajo una página la vista de diferencias"
 
-#: gitk:1369
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tDesplaza hacia arriba 18 líneas la vista de diferencias"
 
-#: gitk:1370
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tDesplaza hacia abajo 18 líneas la vista de diferencias"
 
-#: gitk:1371
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tBuscar"
 
-#: gitk:1372
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tBuscar el siguiente"
 
-#: gitk:1373
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\tBuscar el siguiente"
 
-#: gitk:1374
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tBuscar el siguiente, o reiniciar la búsqueda"
 
-#: gitk:1375
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tBuscar el anterior"
 
-#: gitk:1376
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente"
 
-#: gitk:1377
+#: gitk:2547
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tBuscar siguiente en la vista de diferencias"
 
-#: gitk:1378
+#: gitk:2548
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tBuscar anterior en la vista de diferencias"
 
-#: gitk:1379
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-KP+>\tAumentar tamaño del texto"
 
-#: gitk:1380
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tAumentar tamaño del texto"
 
-#: gitk:1381
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-KP->\tDisminuir tamaño del texto"
 
-#: gitk:1382
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tDisminuir tamaño del texto"
 
-#: gitk:1383
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tActualizar"
 
-#: gitk:1896
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Definición de vistas de Gitk"
 
-#: gitk:1921
+#: gitk:3225
 msgid "Name"
 msgstr "Nombre"
 
-#: gitk:1924
+#: gitk:3228
 msgid "Remember this view"
 msgstr "Recordar esta vista"
 
-#: gitk:1928
-msgid "Commits to include (arguments to git rev-list):"
-msgstr "Revisiones a incluir (argumentos a git rev-list):"
+#: gitk:3232
+msgid "Commits to include (arguments to git log):"
+msgstr "Revisiones a incluir (argumentos a git log):"
 
-#: gitk:1935
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Comando que genera más revisiones a incluir:"
 
-#: gitk:1942
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Introducir archivos y directorios a incluir, uno por línea:"
 
-#: gitk:1989
+#: gitk:3293
 msgid "Error in commit selection arguments:"
 msgstr "Error en los argumentos de selección de las revisiones:"
 
-#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Ninguno"
 
-#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Fecha"
 
-#: gitk:2531 gitk:4336
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr "Fecha de creación"
 
-#: gitk:2680 gitk:2685
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Descendiente"
 
-#: gitk:2681
+#: gitk:3940
 msgid "Not descendant"
 msgstr "No descendiente"
 
-#: gitk:2688 gitk:2693
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Antepasado"
 
-#: gitk:2689
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "No antepasado"
 
-#: gitk:2924
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Cambios locales añadidos al índice pero sin completar revisión"
 
-#: gitk:2954
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Cambios locales sin añadir al índice"
 
-#: gitk:4305
+#: gitk:5549
 msgid "Searching"
 msgstr "Buscando"
 
-#: gitk:4767
+#: gitk:6049
 msgid "Tags:"
 msgstr "Etiquetas:"
 
-#: gitk:4784 gitk:4790 gitk:5951
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Padre"
 
-#: gitk:4795
+#: gitk:6077
 msgid "Child"
 msgstr "Hija"
 
-#: gitk:4804
+#: gitk:6086
 msgid "Branch"
 msgstr "Rama"
 
-#: gitk:4807
+#: gitk:6089
 msgid "Follows"
 msgstr "Sigue-a"
 
-#: gitk:4810
+#: gitk:6092
 msgid "Precedes"
 msgstr "Precede-a"
 
-#: gitk:5093
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Error al leer las diferencias de fusión:"
 
-#: gitk:5778
+#: gitk:7113
 msgid "Goto:"
 msgstr "Ir a:"
 
-#: gitk:5780
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1 ID:"
 
-#: gitk:5805
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "La id SHA1 abreviada %s es ambigua"
 
-#: gitk:5817
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "La id SHA1 %s es desconocida"
 
-#: gitk:5819
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "La etiqueta/rama %s es deconocida"
 
-#: gitk:5961
+#: gitk:7290
 msgid "Children"
 msgstr "Hijas"
 
-#: gitk:6018
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Poner la rama %s en esta revisión"
 
-#: gitk:6049
+#: gitk:7349
+msgid "Detached head: can't reset"
+msgstr ""
+
+#: gitk:7381
 msgid "Top"
 msgstr "Origen"
 
-#: gitk:6050
+#: gitk:7382
 msgid "From"
 msgstr "De"
 
-#: gitk:6055
+#: gitk:7387
 msgid "To"
 msgstr "A"
 
-#: gitk:6078
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Generar parche"
 
-#: gitk:6080
+#: gitk:7412
 msgid "From:"
 msgstr "De:"
 
-#: gitk:6089
+#: gitk:7421
 msgid "To:"
 msgstr "Para:"
 
-#: gitk:6098
+#: gitk:7430
 msgid "Reverse"
 msgstr "Invertir"
 
-#: gitk:6100 gitk:6269
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Escribir a archivo:"
 
-#: gitk:6106
+#: gitk:7438
 msgid "Generate"
 msgstr "Generar"
 
-#: gitk:6142
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Error en la creación del parche:"
 
-#: gitk:6164 gitk:6257 gitk:6311
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6173
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Nombre de etiqueta:"
 
-#: gitk:6177 gitk:6320
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Crear"
 
-#: gitk:6192
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "No se ha especificado etiqueta"
 
-#: gitk:6196
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "La etiqueta \"%s\" ya existe"
 
-#: gitk:6202
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Error al crear la etiqueta:"
 
-#: gitk:6266
+#: gitk:7604
 msgid "Command:"
 msgstr "Comando:"
 
-#: gitk:6274
+#: gitk:7612
 msgid "Write"
 msgstr "Escribir"
 
-#: gitk:6290
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Error al escribir revisión:"
 
-#: gitk:6316
+#: gitk:7654
 msgid "Name:"
 msgstr "Nombre:"
 
-#: gitk:6335
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Especifique un nombre para la nueva rama"
 
-#: gitk:6364
+#: gitk:7703
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr "La revisión %s ya está incluida en la rama %s -- ¿Volver a aplicarla?"
 
-#: gitk:6369
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr "Eligiendo revisiones (cherry-picking)"
 
-#: gitk:6381
+#: gitk:7720
 msgid "No changes committed"
 msgstr "No se han guardado cambios"
 
-#: gitk:6404
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Confirmar git reset"
 
-#: gitk:6406
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "¿Reponer la rama %s a %s?"
 
-#: gitk:6410
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Tipo de reposición:"
 
-#: gitk:6414
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Suave: No altera la copia de trabajo ni el índice"
 
-#: gitk:6417
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Mixta: Actualiza el índice, no altera la copia de trabajo"
 
-#: gitk:6420
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -693,19 +719,19 @@
 "Dura: Actualiza el índice y la copia de trabajo\n"
 "(abandona TODAS las modificaciones locales)"
 
-#: gitk:6436
+#: gitk:7777
 msgid "Resetting"
 msgstr "Reponiendo"
 
-#: gitk:6493
+#: gitk:7834
 msgid "Checking out"
 msgstr "Creando copia de trabajo"
 
-#: gitk:6523
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr "No se puede borrar la rama actual"
 
-#: gitk:6529
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -714,134 +740,146 @@
 "Las revisiones de la rama %s no están presentes en otras ramas.\n"
 "¿Borrar la rama %s?"
 
-#: gitk:6560
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Etiquetas y ramas: %s"
 
-#: gitk:6574
+#: gitk:7936
 msgid "Filter"
 msgstr "Filtro"
 
-#: gitk:6868
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
 msgstr ""
-"Error al leer la topología de revisiones: la información sobre "
-"las ramas y etiquetas precedentes y siguientes será incompleta."
+"Error al leer la topología de revisiones: la información sobre las ramas y "
+"etiquetas precedentes y siguientes será incompleta."
 
-#: gitk:7852
+#: gitk:9216
 msgid "Tag"
 msgstr "Etiqueta"
 
-#: gitk:7852
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7892
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Selector de tipografías gitk"
 
-#: gitk:7909
+#: gitk:9279
 msgid "B"
 msgstr "B"
 
-#: gitk:7912
+#: gitk:9282
 msgid "I"
 msgstr "I"
 
-#: gitk:8005
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Preferencias de gitk"
 
-#: gitk:8006
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Opciones de visualización de la lista de revisiones"
 
-#: gitk:8009
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Ancho máximo del gráfico (en líneas)"
 
-#: gitk:8013
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Ancho máximo del gráfico (en % del panel)"
 
-#: gitk:8018
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Mostrar cambios locales"
 
-#: gitk:8023
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "Seleccionar automáticamente SHA1 hash"
 
-#: gitk:8028
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Opciones de visualización de diferencias"
 
-#: gitk:8030
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Espaciado de tabulador"
 
-#: gitk:8034
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Mostrar etiquetas cercanas"
 
-#: gitk:8039
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Limitar las diferencias a las rutas seleccionadas"
 
-#: gitk:8044
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
+msgid "External diff tool"
+msgstr ""
+
+#: gitk:9423
+msgid "Choose..."
+msgstr ""
+
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Colores: pulse para seleccionar"
 
-#: gitk:8047
+#: gitk:9431
 msgid "Background"
 msgstr "Fondo"
 
-#: gitk:8051
+#: gitk:9435
 msgid "Foreground"
 msgstr "Primer plano"
 
-#: gitk:8055
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Diff: líneas viejas"
 
-#: gitk:8060
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Diff: líneas nuevas"
 
-#: gitk:8065
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Diff: cabecera de fragmento"
 
-#: gitk:8071
+#: gitk:9455
 msgid "Select bg"
 msgstr "Color de fondo de la selección"
 
-#: gitk:8075
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Tipografías: pulse para elegir"
 
-#: gitk:8077
+#: gitk:9461
 msgid "Main font"
 msgstr "Tipografía principal"
 
-#: gitk:8078
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Tipografía para diferencias"
 
-#: gitk:8079
+#: gitk:9463
 msgid "User interface font"
 msgstr "Tipografía para interfaz de usuario"
 
-#: gitk:8095
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: elegir color para %s"
 
-#: gitk:8476
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -849,42 +887,25 @@
 "Esta versión de Tcl/Tk es demasiado antigua.\n"
 " Gitk requiere Tcl/Tk versión 8.4 o superior."
 
-#: gitk:8565
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "No hay un repositorio git aquí."
 
-#: gitk:8569
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "No hay directorio git \"%s\"."
 
-#: gitk:8612
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
-msgstr "Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo"
+msgstr ""
+"Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo"
 
-#: gitk:8624
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Argumentos incorrectos a Gitk:"
 
-#: gitk:8636
-msgid "Couldn't get list of unmerged files:"
-msgstr "Imposible obtener la lista de archivos pendientes de fusión:"
-
-#: gitk:8652
-msgid "No files selected: --merge specified but no files are unmerged."
-msgstr ""
-"No hay archivos seleccionados: se seleccionó la opción --merge pero no hay "
-"archivos pendientes de fusión."
-
-#: gitk:8655
-msgid ""
-"No files selected: --merge specified but no unmerged files are within file "
-"limit."
-msgstr ""
-"No hay archivos seleccionados: se seleccionó la opción --merge pero los archivos "
-"especificados no necesitan fusión."
-
-#: gitk:8716
+#: gitk:10170
 msgid "Command line"
 msgstr "Línea de comandos"
diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po
index d0f4c2e..e89c957 100644
--- a/gitk-git/po/it.po
+++ b/gitk-git/po/it.po
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: gitk\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-03-13 17:34+0100\n"
 "Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
 "Language-Team: Italian\n"
@@ -16,676 +16,706 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:111
-msgid "Error executing git rev-list:"
-msgstr "Errore nell'esecuzione di git rev-list:"
+#: gitk:113
+msgid "Couldn't get list of unmerged files:"
+msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
 
-#: gitk:124
+#: gitk:340
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
+"sono file in attesa di fusione."
+
+#: gitk:343
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
+"specificati non sono in attesa di fusione."
+
+#: gitk:365 gitk:503
+msgid "Error executing git log:"
+msgstr "Errore nell'esecuzione di git log:"
+
+#: gitk:378
 msgid "Reading"
 msgstr "Lettura in corso"
 
-#: gitk:151 gitk:2191
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Lettura delle revisioni in corso..."
 
-#: gitk:275
-msgid "Can't parse git log output:"
-msgstr "Impossibile elaborare i dati di git log:"
-
-#: gitk:386 gitk:2195
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "Nessuna revisione selezionata"
 
-#: gitk:500
+#: gitk:1399
+msgid "Can't parse git log output:"
+msgstr "Impossibile elaborare i dati di git log:"
+
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Nessuna informazione disponibile sulle revisioni"
 
-#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "OK"
 
-#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
-#: gitk:7924 gitk:8083
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Annulla"
 
-#: gitk:661
-msgid "File"
-msgstr "File"
-
-#: gitk:663
+#: gitk:1811
 msgid "Update"
 msgstr "Aggiorna"
 
-#: gitk:664
+#: gitk:1813
 msgid "Reread references"
 msgstr "Rileggi riferimenti"
 
-#: gitk:665
+#: gitk:1814
 msgid "List references"
 msgstr "Elenca riferimenti"
 
-#: gitk:666
+#: gitk:1815
 msgid "Quit"
 msgstr "Esci"
 
-#: gitk:668
-msgid "Edit"
-msgstr "Modifica"
+#: gitk:1810
+msgid "File"
+msgstr "File"
 
-#: gitk:669
+#: gitk:1818
 msgid "Preferences"
 msgstr "Preferenze"
 
-#: gitk:672 gitk:1892
-msgid "View"
-msgstr "Vista"
+#: gitk:1817
+msgid "Edit"
+msgstr "Modifica"
 
-#: gitk:673
+#: gitk:1821
 msgid "New view..."
 msgstr "Nuova vista..."
 
-#: gitk:674 gitk:2133 gitk:8722
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Modifica vista..."
 
-#: gitk:676 gitk:2134 gitk:8723
+#: gitk:1823
 msgid "Delete view"
 msgstr "Elimina vista"
 
-#: gitk:678
+#: gitk:1825
 msgid "All files"
 msgstr "Tutti i file"
 
-#: gitk:682
-msgid "Help"
-msgstr "Aiuto"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Vista"
 
-#: gitk:683 gitk:1317
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Informazioni su gitk"
 
-#: gitk:684
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Scorciatoie da tastiera"
 
-#: gitk:741
+#: gitk:1827
+msgid "Help"
+msgstr "Aiuto"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1 ID: "
 
-#: gitk:791
+#: gitk:1918
+msgid "Row"
+msgstr ""
+
+#: gitk:1949
 msgid "Find"
 msgstr "Trova"
 
-#: gitk:792
+#: gitk:1950
 msgid "next"
 msgstr "succ"
 
-#: gitk:793
+#: gitk:1951
 msgid "prev"
 msgstr "prec"
 
-#: gitk:794
+#: gitk:1952
 msgid "commit"
 msgstr "revisione"
 
-#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "contenente:"
 
-#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "che riguarda i percorsi:"
 
-#: gitk:801 gitk:2436
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "che aggiunge/rimuove la stringa:"
 
-#: gitk:810 gitk:812
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Esatto"
 
-#: gitk:812 gitk:2514 gitk:4274
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr ""
 
-#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr ""
 
-#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Tutti i campi"
 
-#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Titolo"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Commenti"
 
-#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
-#: gitk:5971
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Autore"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr "Revisione creata da"
 
-#: gitk:845
+#: gitk:2003
 msgid "Search"
 msgstr "Cerca"
 
-#: gitk:852
+#: gitk:2010
 msgid "Diff"
 msgstr ""
 
-#: gitk:854
+#: gitk:2012
 msgid "Old version"
 msgstr "Vecchia versione"
 
-#: gitk:856
+#: gitk:2014
 msgid "New version"
 msgstr "Nuova versione"
 
-#: gitk:858
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Linee di contesto"
 
-#: gitk:868
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Ignora modifiche agli spazi"
 
-#: gitk:926
+#: gitk:2084
 msgid "Patch"
 msgstr "Modifiche"
 
-#: gitk:928
+#: gitk:2086
 msgid "Tree"
 msgstr "Directory"
 
-#: gitk:1053 gitk:1068 gitk:6022
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Diff questo -> selezionato"
 
-#: gitk:1055 gitk:1070 gitk:6023
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Diff selezionato -> questo"
 
-#: gitk:1057 gitk:1072 gitk:6024
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Crea patch"
 
-#: gitk:1058 gitk:6162
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Crea etichetta"
 
-#: gitk:1059 gitk:6255
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Scrivi revisione in un file"
 
-#: gitk:1060 gitk:6309
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Crea un nuovo ramo"
 
-#: gitk:1061
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Porta questa revisione in cima al ramo attuale"
 
-#: gitk:1063
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "Aggiorna il ramo HEAD a questa revisione"
 
-#: gitk:1079
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Attiva questo ramo"
 
-#: gitk:1081
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Elimina questo ramo"
 
-#: gitk:1087
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Evidenzia anche questo"
 
-#: gitk:1089
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Evidenzia solo questo"
 
-#: gitk:1318
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizzatore di revisioni per git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
 "Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
 "License"
 
-#: gitk:1326 gitk:1387 gitk:6581
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Chiudi"
 
-#: gitk:1345
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Scorciatoie da tastiera di Gitk"
 
-#: gitk:1347
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Scorciatoie da tastiera di Gitk:"
 
-#: gitk:1349
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tEsci"
 
-#: gitk:1350
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tVai alla prima revisione"
 
-#: gitk:1351
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tVai all'ultima revisione"
 
-#: gitk:1352
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Up>, p, i\tVai più in alto di una revisione"
 
-#: gitk:1353
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Down>, n, k\tVai più in basso di una revisione"
 
-#: gitk:1354
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Left>, z, j\tTorna indietro nella cronologia"
 
-#: gitk:1355
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Right>, x, l\tVai avanti nella cronologia"
 
-#: gitk:1356
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni"
 
-#: gitk:1357
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni"
 
-#: gitk:1358
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni"
 
-#: gitk:1359
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tScorri alla fine della lista delle revisioni"
 
-#: gitk:1360
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga"
 
-#: gitk:1361
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga"
 
-#: gitk:1362
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina"
 
-#: gitk:1363
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina"
 
-#: gitk:1364
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)"
 
-#: gitk:1365
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)"
 
-#: gitk:1366
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina"
 
-#: gitk:1367
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina"
 
-#: gitk:1368
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina"
 
-#: gitk:1369
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee"
 
-#: gitk:1370
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee"
 
-#: gitk:1371
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tTrova"
 
-#: gitk:1372
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tTrova in avanti"
 
-#: gitk:1373
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\tTrova in avanti"
 
-#: gitk:1374
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tTrova in avanti, o cerca di nuovo"
 
-#: gitk:1375
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tTrova all'indietro"
 
-#: gitk:1376
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tScorri la vista delle differenze al file successivo"
 
-#: gitk:1377
+#: gitk:2547
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze"
 
-#: gitk:1378
+#: gitk:2548
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze"
 
-#: gitk:1379
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-KP+>\tAumenta grandezza carattere"
 
-#: gitk:1380
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tAumenta grandezza carattere"
 
-#: gitk:1381
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-KP->\tDiminuisci grandezza carattere"
 
-#: gitk:1382
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tDiminuisci grandezza carattere"
 
-#: gitk:1383
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAggiorna"
 
-#: gitk:1896
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Scelta vista Gitk"
 
-#: gitk:1921
+#: gitk:3225
 msgid "Name"
 msgstr "Nome"
 
-#: gitk:1924
+#: gitk:3228
 msgid "Remember this view"
 msgstr "Ricorda questa vista"
 
-#: gitk:1928
-msgid "Commits to include (arguments to git rev-list):"
-msgstr "Revisioni da includere (argomenti di git rev-list):"
+#: gitk:3232
+msgid "Commits to include (arguments to git log):"
+msgstr "Revisioni da includere (argomenti di git log):"
 
-#: gitk:1935
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Comando che genera altre revisioni da visualizzare:"
 
-#: gitk:1942
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Inserire file e directory da includere, uno per riga:"
 
-#: gitk:1989
+#: gitk:3293
 msgid "Error in commit selection arguments:"
 msgstr "Errore negli argomenti di selezione delle revisioni:"
 
-#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Nessuno"
 
-#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Data"
 
-#: gitk:2531 gitk:4336
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr ""
 
-#: gitk:2680 gitk:2685
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Discendente"
 
-#: gitk:2681
+#: gitk:3940
 msgid "Not descendant"
 msgstr "Non discendente"
 
-#: gitk:2688 gitk:2693
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Ascendente"
 
-#: gitk:2689
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "Non ascendente"
 
-#: gitk:2924
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
 
-#: gitk:2954
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
 
-#: gitk:4305
+#: gitk:5549
 msgid "Searching"
 msgstr "Ricerca in corso"
 
-#: gitk:4767
+#: gitk:6049
 msgid "Tags:"
 msgstr "Etichette:"
 
-#: gitk:4784 gitk:4790 gitk:5951
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Genitore"
 
-#: gitk:4795
+#: gitk:6077
 msgid "Child"
 msgstr "Figlio"
 
-#: gitk:4804
+#: gitk:6086
 msgid "Branch"
 msgstr "Ramo"
 
-#: gitk:4807
+#: gitk:6089
 msgid "Follows"
 msgstr "Segue"
 
-#: gitk:4810
+#: gitk:6092
 msgid "Precedes"
 msgstr "Precede"
 
-#: gitk:5093
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Errore nella lettura delle differenze di fusione:"
 
-#: gitk:5778
+#: gitk:7113
 msgid "Goto:"
 msgstr "Vai a:"
 
-#: gitk:5780
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1 ID:"
 
-#: gitk:5805
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "La SHA1 id abbreviata %s è ambigua"
 
-#: gitk:5817
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "La SHA1 id %s è sconosciuta"
 
-#: gitk:5819
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "L'etichetta/ramo %s è sconosciuto"
 
-#: gitk:5961
+#: gitk:7290
 msgid "Children"
 msgstr "Figli"
 
-#: gitk:6018
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Aggiorna il ramo %s a questa revisione"
 
-#: gitk:6049
+#: gitk:7349
+msgid "Detached head: can't reset"
+msgstr ""
+
+#: gitk:7381
 msgid "Top"
 msgstr "Inizio"
 
-#: gitk:6050
+#: gitk:7382
 msgid "From"
 msgstr "Da"
 
-#: gitk:6055
+#: gitk:7387
 msgid "To"
 msgstr "A"
 
-#: gitk:6078
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Genera patch"
 
-#: gitk:6080
+#: gitk:7412
 msgid "From:"
 msgstr "Da:"
 
-#: gitk:6089
+#: gitk:7421
 msgid "To:"
 msgstr "A:"
 
-#: gitk:6098
+#: gitk:7430
 msgid "Reverse"
 msgstr "Inverti"
 
-#: gitk:6100 gitk:6269
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Scrivi sul file:"
 
-#: gitk:6106
+#: gitk:7438
 msgid "Generate"
 msgstr "Genera"
 
-#: gitk:6142
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Errore nella creazione della patch:"
 
-#: gitk:6164 gitk:6257 gitk:6311
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6173
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Nome etichetta:"
 
-#: gitk:6177 gitk:6320
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Crea"
 
-#: gitk:6192
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "Nessuna etichetta specificata"
 
-#: gitk:6196
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "L'etichetta \"%s\" esiste già"
 
-#: gitk:6202
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Errore nella creazione dell'etichetta:"
 
-#: gitk:6266
+#: gitk:7604
 msgid "Command:"
 msgstr "Comando:"
 
-#: gitk:6274
+#: gitk:7612
 msgid "Write"
 msgstr "Scrivi"
 
-#: gitk:6290
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Errore nella scrittura della revisione:"
 
-#: gitk:6316
+#: gitk:7654
 msgid "Name:"
 msgstr "Nome:"
 
-#: gitk:6335
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Specificare un nome per il nuovo ramo"
 
-#: gitk:6364
+#: gitk:7703
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?"
 
-#: gitk:6369
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr ""
 
-#: gitk:6381
+#: gitk:7720
 msgid "No changes committed"
 msgstr "Nessuna modifica archiviata"
 
-#: gitk:6404
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Conferma git reset"
 
-#: gitk:6406
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Aggiornare il ramo %s a %s?"
 
-#: gitk:6410
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Tipo di aggiornamento:"
 
-#: gitk:6414
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono"
 
-#: gitk:6417
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice"
 
-#: gitk:6420
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -693,19 +723,19 @@
 "Hard: Aggiorna la directory di lavoro e l'indice\n"
 "(abbandona TUTTE le modifiche locali)"
 
-#: gitk:6436
+#: gitk:7777
 msgid "Resetting"
 msgstr "git reset in corso"
 
-#: gitk:6493
+#: gitk:7834
 msgid "Checking out"
 msgstr "Attivazione in corso"
 
-#: gitk:6523
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Impossibile cancellare il ramo attualmente attivo"
 
-#: gitk:6529
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -714,16 +744,16 @@
 "Le revisioni nel ramo %s non sono presenti su altri rami.\n"
 "Cancellare il ramo %s?"
 
-#: gitk:6560
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Etichette e rami: %s"
 
-#: gitk:6574
+#: gitk:7936
 msgid "Filter"
 msgstr "Filtro"
 
-#: gitk:6868
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -731,117 +761,129 @@
 "Errore nella lettura della topologia delle revisioni: le informazioni sul "
 "ramo e le etichette precedenti e seguenti saranno incomplete."
 
-#: gitk:7852
+#: gitk:9216
 msgid "Tag"
 msgstr "Etichetta"
 
-#: gitk:7852
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7892
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Scelta caratteri gitk"
 
-#: gitk:7909
+#: gitk:9279
 msgid "B"
 msgstr "B"
 
-#: gitk:7912
+#: gitk:9282
 msgid "I"
 msgstr "I"
 
-#: gitk:8005
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Preferenze gitk"
 
-#: gitk:8006
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Opzioni visualizzazione dell'elenco revisioni"
 
-#: gitk:8009
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Larghezza massima del grafico (in linee)"
 
-#: gitk:8013
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Larghezza massima del grafico (% del pannello)"
 
-#: gitk:8018
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Mostra modifiche locali"
 
-#: gitk:8023
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "Seleziona automaticamente SHA1 hash"
 
-#: gitk:8028
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Opzioni di visualizzazione delle differenze"
 
-#: gitk:8030
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Spaziatura tabulazioni"
 
-#: gitk:8034
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Mostra etichette vicine"
 
-#: gitk:8039
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Limita le differenze ai percorsi elencati"
 
-#: gitk:8044
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
+msgid "External diff tool"
+msgstr ""
+
+#: gitk:9423
+msgid "Choose..."
+msgstr ""
+
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Colori: premere per scegliere"
 
-#: gitk:8047
+#: gitk:9431
 msgid "Background"
 msgstr "Sfondo"
 
-#: gitk:8051
+#: gitk:9435
 msgid "Foreground"
 msgstr "Primo piano"
 
-#: gitk:8055
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Diff: vecchie linee"
 
-#: gitk:8060
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Diff: nuove linee"
 
-#: gitk:8065
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Diff: intestazione della sezione"
 
-#: gitk:8071
+#: gitk:9455
 msgid "Select bg"
 msgstr "Sfondo selezione"
 
-#: gitk:8075
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Carattere: premere per scegliere"
 
-#: gitk:8077
+#: gitk:9461
 msgid "Main font"
 msgstr "Carattere principale"
 
-#: gitk:8078
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Carattere per differenze"
 
-#: gitk:8079
+#: gitk:9463
 msgid "User interface font"
 msgstr "Carattere per interfaccia utente"
 
-#: gitk:8095
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: scegliere un colore per %s"
 
-#: gitk:8476
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -849,42 +891,24 @@
 "Questa versione di Tcl/Tk non può avviare gitk.\n"
 " Gitk richiede Tcl/Tk versione 8.4 o superiore."
 
-#: gitk:8565
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "Archivio git non trovato."
 
-#: gitk:8569
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Directory git \"%s\" non trovata."
 
-#: gitk:8612
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file"
 
-#: gitk:8624
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Gitk: argomenti errati:"
 
-#: gitk:8636
-msgid "Couldn't get list of unmerged files:"
-msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
-
-#: gitk:8652
-msgid "No files selected: --merge specified but no files are unmerged."
-msgstr ""
-"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
-"sono file in attesa di fusione."
-
-#: gitk:8655
-msgid ""
-"No files selected: --merge specified but no unmerged files are within file "
-"limit."
-msgstr ""
-"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
-"specificati non sono in attesa di fusione."
-
-#: gitk:8716
+#: gitk:10170
 msgid "Command line"
 msgstr "Linea di comando"
diff --git a/gitk-git/po/sv.po b/gitk-git/po/sv.po
index e1ecfb7..947b53f 100644
--- a/gitk-git/po/sv.po
+++ b/gitk-git/po/sv.po
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-03 18:58+0200\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-08-03 19:03+0200\n"
 "Last-Translator: Mikael Magnusson <mikachu@gmail.com>\n"
 "Language-Team: Swedish <sv@li.org>\n"
@@ -16,17 +16,17 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:102
+#: gitk:113
 msgid "Couldn't get list of unmerged files:"
 msgstr "Kunde inta hämta lista över ej sammanslagna filer:"
 
-#: gitk:329
+#: gitk:340
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Inga filer valdes: --merge angavs men det finns inga filer som inte har "
 "slagits samman."
 
-#: gitk:332
+#: gitk:343
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -34,257 +34,261 @@
 "Inga filer valdes: --merge angavs men det finns inga filer inom "
 "filbegränsningen."
 
-#: gitk:354
+#: gitk:365 gitk:503
 msgid "Error executing git log:"
 msgstr "Fel vid körning av git log:"
 
-#: gitk:369
+#: gitk:378
 msgid "Reading"
 msgstr "Läser"
 
-#: gitk:400 gitk:3356
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Läser incheckningar..."
 
-#: gitk:403 gitk:1480 gitk:3359
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "Inga incheckningar markerade"
 
-#: gitk:1358
+#: gitk:1399
 msgid "Can't parse git log output:"
 msgstr "Kan inte tolka utdata från git log:"
 
-#: gitk:1557
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Ingen incheckningsinformation är tillgänglig"
 
-#: gitk:1654 gitk:1676 gitk:3150 gitk:7620 gitk:9149 gitk:9317
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1678 gitk:3151 gitk:7296 gitk:7367 gitk:7470 gitk:7516 gitk:7622
-#: gitk:9150 gitk:9318
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: gitk:1716
-msgid "File"
-msgstr "Arkiv"
-
-#: gitk:1718
+#: gitk:1811
 msgid "Update"
 msgstr "Uppdatera"
 
-#: gitk:1719
+#: gitk:1812
 msgid "Reload"
 msgstr "Ladda om"
 
-#: gitk:1720
+#: gitk:1813
 msgid "Reread references"
 msgstr "Läs om referenser"
 
-#: gitk:1721
+#: gitk:1814
 msgid "List references"
 msgstr "Visa referenser"
 
-#: gitk:1722
+#: gitk:1815
 msgid "Quit"
 msgstr "Avsluta"
 
-#: gitk:1724
-msgid "Edit"
-msgstr "Redigera"
+#: gitk:1810
+msgid "File"
+msgstr "Arkiv"
 
-#: gitk:1725
+#: gitk:1818
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: gitk:1728 gitk:3087
-msgid "View"
-msgstr "Visa"
+#: gitk:1817
+msgid "Edit"
+msgstr "Redigera"
 
-#: gitk:1729
+#: gitk:1821
 msgid "New view..."
 msgstr "Ny vy..."
 
-#: gitk:1730 gitk:3298 gitk:9932
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Ändra vy..."
 
-#: gitk:1732 gitk:3299 gitk:9933
+#: gitk:1823
 msgid "Delete view"
 msgstr "Ta bort vy"
 
-#: gitk:1734
+#: gitk:1825
 msgid "All files"
 msgstr "Alla filer"
 
-#: gitk:1738
-msgid "Help"
-msgstr "Hjälp"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Visa"
 
-#: gitk:1739 gitk:2399
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Om gitk"
 
-#: gitk:1740
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Tangentbordsbindningar"
 
-#: gitk:1797
+#: gitk:1827
+msgid "Help"
+msgstr "Hjälp"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1-id: "
 
-#: gitk:1828
+#: gitk:1918
 msgid "Row"
 msgstr "Rad"
 
-#: gitk:1859
+#: gitk:1949
 msgid "Find"
 msgstr "Sök"
 
-#: gitk:1860
+#: gitk:1950
 msgid "next"
 msgstr "nästa"
 
-#: gitk:1861
+#: gitk:1951
 msgid "prev"
 msgstr "föreg"
 
-#: gitk:1862
+#: gitk:1952
 msgid "commit"
 msgstr "incheckning"
 
-#: gitk:1865 gitk:1867 gitk:3511 gitk:3534 gitk:3558 gitk:5441 gitk:5512
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "som innehåller:"
 
-#: gitk:1868 gitk:2866 gitk:2871 gitk:3586
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "som rör sökväg:"
 
-#: gitk:1869 gitk:3591
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "som lägger/till tar bort sträng:"
 
-#: gitk:1878 gitk:1880
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:1880 gitk:3667 gitk:5409
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr "IgnVersaler"
 
-#: gitk:1880 gitk:3560 gitk:3665 gitk:5405
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr "Reg.uttr."
 
-#: gitk:1882 gitk:1883 gitk:3686 gitk:3716 gitk:3723 gitk:5532 gitk:5599
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Alla fält"
 
-#: gitk:1883 gitk:3684 gitk:3716 gitk:5471
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Rubrik"
 
-#: gitk:1884 gitk:3684 gitk:5471 gitk:5599 gitk:6000
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Kommentarer"
 
-#: gitk:1884 gitk:3684 gitk:3688 gitk:3723 gitk:5471 gitk:5936 gitk:7142
-#: gitk:7157
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Författare"
 
-#: gitk:1884 gitk:3684 gitk:5471 gitk:5938
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr "Incheckare"
 
-#: gitk:1913
+#: gitk:2003
 msgid "Search"
 msgstr "Sök"
 
-#: gitk:1920
+#: gitk:2010
 msgid "Diff"
 msgstr "Diff"
 
-#: gitk:1922
+#: gitk:2012
 msgid "Old version"
 msgstr "Gammal version"
 
-#: gitk:1924
+#: gitk:2014
 msgid "New version"
 msgstr "Ny version"
 
-#: gitk:1926
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Rader sammanhang"
 
-#: gitk:1936
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: gitk:1994
+#: gitk:2084
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:1996
+#: gitk:2086
 msgid "Tree"
 msgstr "Träd"
 
-#: gitk:2121 gitk:2136 gitk:7211
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Diff denna -> markerad"
 
-#: gitk:2123 gitk:2138 gitk:7212
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Diff markerad -> denna"
 
-#: gitk:2125 gitk:2140 gitk:7213
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Skapa patch"
 
-#: gitk:2126 gitk:7351
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Skapa tagg"
 
-#: gitk:2127 gitk:7450
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Skriv incheckning till fil"
 
-#: gitk:2128 gitk:7504
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Skapa ny gren"
 
-#: gitk:2129
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Plocka denna incheckning"
 
-#: gitk:2131
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "Återställ HEAD-grenen hit"
 
-#: gitk:2147
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Checka ut denna gren"
 
-#: gitk:2149
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Ta bort denna gren"
 
-#: gitk:2155
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Markera även detta"
 
-#: gitk:2157
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Markera bara detta"
 
-#: gitk:2159
+#: gitk:2244
 msgid "External diff"
 msgstr "Extern diff"
 
-#: gitk:2400
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -300,427 +304,427 @@
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
-#: gitk:2408 gitk:2469 gitk:7799
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Stäng"
 
-#: gitk:2427
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Tangentbordsbindningar för Gitk"
 
-#: gitk:2429
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Tangentbordsbindningar för Gitk:"
 
-#: gitk:2431
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tAvsluta"
 
-#: gitk:2432
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tGå till första incheckning"
 
-#: gitk:2433
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tGå till sista incheckning"
 
-#: gitk:2434
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Upp>, p, i\tGå en incheckning upp"
 
-#: gitk:2435
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Ned>, n, k\tGå en incheckning ned"
 
-#: gitk:2436
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Vänster>, z, j\tGå bakåt i historiken"
 
-#: gitk:2437
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Höger>, x, l\tGå framåt i historiken"
 
-#: gitk:2438
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
 
-#: gitk:2439
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
 
-#: gitk:2440
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tRulla till början av incheckningslistan"
 
-#: gitk:2441
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
 
-#: gitk:2442
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
 
-#: gitk:2443
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
 
-#: gitk:2444
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
 
-#: gitk:2445
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
 
-#: gitk:2446
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
 
-#: gitk:2447
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
 
-#: gitk:2448
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
 
-#: gitk:2449
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
 
-#: gitk:2450
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
 
-#: gitk:2451
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tRulla diffvisningen upp 18 rader"
 
-#: gitk:2452
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tRulla diffvisningen ned 18 rader"
 
-#: gitk:2453
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSök"
 
-#: gitk:2454
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tGå till nästa sökträff"
 
-#: gitk:2455
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tGå till nästa sökträff"
 
-#: gitk:2456
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
 
-#: gitk:2457
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tGå till föregående sökträff"
 
-#: gitk:2458
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tRulla diffvisningen till nästa fil"
 
-#: gitk:2459
+#: gitk:2547
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen"
 
-#: gitk:2460
+#: gitk:2548
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen"
 
-#: gitk:2461
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Num+>\tÖka teckenstorlek"
 
-#: gitk:2462
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tÖka teckenstorlek"
 
-#: gitk:2463
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Num->\tMinska teckenstorlek"
 
-#: gitk:2464
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tMinska teckenstorlek"
 
-#: gitk:2465
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tUppdatera"
 
-#: gitk:3091
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Definition av Gitk-vy"
 
-#: gitk:3116
+#: gitk:3225
 msgid "Name"
 msgstr "Namn"
 
-#: gitk:3119
+#: gitk:3228
 msgid "Remember this view"
 msgstr "Spara denna vy"
 
-#: gitk:3123
+#: gitk:3232
 msgid "Commits to include (arguments to git log):"
 msgstr "Incheckningar att ta med (argument till git log):"
 
-#: gitk:3130
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Kommando för att generera fler incheckningar att ta med:"
 
-#: gitk:3137
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Ange filer och kataloger att ta med, en per rad:"
 
-#: gitk:3184
+#: gitk:3293
 msgid "Error in commit selection arguments:"
 msgstr "Fel i argument för val av incheckningar:"
 
-#: gitk:3238 gitk:3290 gitk:3736 gitk:3750 gitk:4951 gitk:9896 gitk:9897
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Inget"
 
-#: gitk:3684 gitk:5471 gitk:7144 gitk:7159
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:3684 gitk:5471
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr "Skapat datum"
 
-#: gitk:3833 gitk:3838
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Avkomling"
 
-#: gitk:3834
+#: gitk:3940
 msgid "Not descendant"
 msgstr "Inte avkomling"
 
-#: gitk:3841 gitk:3846
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Förfader"
 
-#: gitk:3842
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "Inte förfader"
 
-#: gitk:4078
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokala ändringar sparade i indexet men inte incheckade"
 
-#: gitk:4111
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokala ändringar, ej sparade i indexet"
 
-#: gitk:5440
+#: gitk:5549
 msgid "Searching"
 msgstr "Söker"
 
-#: gitk:5940
+#: gitk:6049
 msgid "Tags:"
 msgstr "Taggar:"
 
-#: gitk:5957 gitk:5963 gitk:7137
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Förälder"
 
-#: gitk:5968
+#: gitk:6077
 msgid "Child"
 msgstr "Barn"
 
-#: gitk:5977
+#: gitk:6086
 msgid "Branch"
 msgstr "Gren"
 
-#: gitk:5980
+#: gitk:6089
 msgid "Follows"
 msgstr "Följer"
 
-#: gitk:5983
+#: gitk:6092
 msgid "Precedes"
 msgstr "Föregår"
 
-#: gitk:6267
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Fel vid hämtning av sammanslagningsdiff:"
 
-#: gitk:6970
+#: gitk:7113
 msgid "Goto:"
 msgstr "Gå till:"
 
-#: gitk:6972
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1-id:"
 
-#: gitk:6991
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Förkortat SHA1-id %s är tvetydigt"
 
-#: gitk:7003
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA-id:t %s är inte känt"
 
-#: gitk:7005
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Tagg/huvud %s är okänt"
 
-#: gitk:7147
+#: gitk:7290
 msgid "Children"
 msgstr "Barn"
 
-#: gitk:7204
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Återställ grenen %s hit"
 
-#: gitk:7206
+#: gitk:7349
 msgid "Detached head: can't reset"
 msgstr "Frånkopplad head: kan inte återställa"
 
-#: gitk:7238
+#: gitk:7381
 msgid "Top"
 msgstr "Topp"
 
-#: gitk:7239
+#: gitk:7382
 msgid "From"
 msgstr "Från"
 
-#: gitk:7244
+#: gitk:7387
 msgid "To"
 msgstr "Till"
 
-#: gitk:7267
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Generera patch"
 
-#: gitk:7269
+#: gitk:7412
 msgid "From:"
 msgstr "Från:"
 
-#: gitk:7278
+#: gitk:7421
 msgid "To:"
 msgstr "Till:"
 
-#: gitk:7287
+#: gitk:7430
 msgid "Reverse"
 msgstr "Vänd"
 
-#: gitk:7289 gitk:7464
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Utdatafil:"
 
-#: gitk:7295
+#: gitk:7438
 msgid "Generate"
 msgstr "Generera"
 
-#: gitk:7331
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Fel vid generering av patch:"
 
-#: gitk:7353 gitk:7452 gitk:7506
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "Id:"
 
-#: gitk:7362
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Taggnamn:"
 
-#: gitk:7366 gitk:7515
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Skapa"
 
-#: gitk:7381
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "Inget taggnamn angavs"
 
-#: gitk:7385
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Taggen \"%s\" finns redan"
 
-#: gitk:7391
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Fel vid skapande av tagg:"
 
-#: gitk:7461
+#: gitk:7604
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:7469
+#: gitk:7612
 msgid "Write"
 msgstr "Skriv"
 
-#: gitk:7485
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Fel vid skrivning av incheckning:"
 
-#: gitk:7511
+#: gitk:7654
 msgid "Name:"
 msgstr "Namn:"
 
-#: gitk:7530
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Ange ett namn för den nya grenen"
 
-#: gitk:7559
+#: gitk:7703
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras "
 "på nytt?"
 
-#: gitk:7564
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr "Plockar"
 
-#: gitk:7576
+#: gitk:7720
 msgid "No changes committed"
 msgstr "Inga ändringar incheckade"
 
-#: gitk:7601
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Bekräfta återställning"
 
-#: gitk:7603
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Återställa grenen %s till %s?"
 
-#: gitk:7607
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Typ av återställning:"
 
-#: gitk:7611
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Mjuk: Rör inte utcheckning och index"
 
-#: gitk:7614
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Blandad: Rör inte utcheckning, återställ index"
 
-#: gitk:7617
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -728,19 +732,19 @@
 "Hård: Återställ utcheckning och index\n"
 "(förkastar ALLA lokala ändringar)"
 
-#: gitk:7633
+#: gitk:7777
 msgid "Resetting"
 msgstr "Återställer"
 
-#: gitk:7690
+#: gitk:7834
 msgid "Checking out"
 msgstr "Checkar ut"
 
-#: gitk:7741
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Kan inte ta bort den just nu utcheckade grenen"
 
-#: gitk:7747
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -749,16 +753,16 @@
 "Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
 "Vill du verkligen ta bort grenen %s?"
 
-#: gitk:7778
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Taggar och huvuden: %s"
 
-#: gitk:7792
+#: gitk:7936
 msgid "Filter"
 msgstr "Filter"
 
-#: gitk:8086
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -766,125 +770,129 @@
 "Fel vid läsning av information om incheckningstopologi; information om "
 "grenar och föregående/senare taggar kommer inte vara komplett."
 
-#: gitk:9072
+#: gitk:9216
 msgid "Tag"
 msgstr "Tagg"
 
-#: gitk:9072
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:9118
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Teckensnittsväljare för Gitk"
 
-#: gitk:9135
+#: gitk:9279
 msgid "B"
 msgstr "F"
 
-#: gitk:9138
+#: gitk:9282
 msgid "I"
 msgstr "K"
 
-#: gitk:9231
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Inställningar för Gitk"
 
-#: gitk:9232
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Alternativ för incheckningslistvy"
 
-#: gitk:9235
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Maximal grafbredd (rader)"
 
-#: gitk:9239
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximal grafbredd (% av ruta)"
 
-#: gitk:9244
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Visa lokala ändringar"
 
-#: gitk:9249
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "Välj SHA1 automatiskt"
 
-#: gitk:9254
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Alternativ för diffvy"
 
-#: gitk:9256
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Blanksteg för tabulatortecken"
 
-#: gitk:9260
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Visa närliggande taggar"
 
-#: gitk:9265
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Begränsa diff till listade sökvägar"
 
-#: gitk:9272
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
 msgid "External diff tool"
 msgstr "Externt diff-verktyg"
 
-#: gitk:9274
+#: gitk:9423
 msgid "Choose..."
 msgstr "Välj..."
 
-#: gitk:9279
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Färger: tryck för att välja"
 
-#: gitk:9282
+#: gitk:9431
 msgid "Background"
 msgstr "Bakgrund"
 
-#: gitk:9286
+#: gitk:9435
 msgid "Foreground"
 msgstr "Förgrund"
 
-#: gitk:9290
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Diff: gamla rader"
 
-#: gitk:9295
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Diff: nya rader"
 
-#: gitk:9300
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Diff: delhuvud"
 
-#: gitk:9306
+#: gitk:9455
 msgid "Select bg"
 msgstr "Markerad bakgrund"
 
-#: gitk:9310
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Teckensnitt: tryck för att välja"
 
-#: gitk:9312
+#: gitk:9461
 msgid "Main font"
 msgstr "Huvudteckensnitt"
 
-#: gitk:9313
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Teckensnitt för diffvisning"
 
-#: gitk:9314
+#: gitk:9463
 msgid "User interface font"
 msgstr "Teckensnitt för användargränssnitt"
 
-#: gitk:9339
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: välj färg för %s"
 
-#: gitk:9720
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -892,24 +900,24 @@
 "Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
 " Gitk kräver åtminstone Tcl/Tk 8.4."
 
-#: gitk:9812
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "Hittar inget gitk-arkiv här."
 
-#: gitk:9816
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Hittar inte git-katalogen \"%s\"."
 
-#: gitk:9853
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
 
-#: gitk:9865
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Felaktiga argument till gitk:"
 
-#: gitk:9925
+#: gitk:10170
 msgid "Command line"
 msgstr "Kommandorad"
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 26967e2..18c9ce3 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -166,6 +166,27 @@
   shows repositories only if this file exists in its object database
   (if directory has the magic file named $export_ok).
 
+- Finally, it is possible to specify an arbitrary perl subroutine that
+  will be called for each project to determine if it can be exported.
+  The subroutine receives an absolute path to the project as its only
+  parameter.
+
+  For example, if you use mod_perl to run the script, and have dumb
+  http protocol authentication configured for your repositories, you
+  can use the following hook to allow access only if the user is
+  authorized to read the files:
+
+    $export_auth_hook = sub {
+        use Apache2::SubRequest ();
+        use Apache2::Const -compile => qw(HTTP_OK);
+        my $path = "$_[0]/HEAD";
+        my $r    = Apache2::RequestUtil->request;
+        my $sub  = $r->lookup_file($path);
+        return $sub->filename eq $path
+            && $sub->status == Apache2::Const::HTTP_OK;
+    };
+
+
 Generating projects list using gitweb
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/gitweb/README b/gitweb/README
index 825162a..8433dd1 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -162,14 +162,12 @@
    $GITWEB_LIST during installation.  If empty, $projectroot is used
    to scan for repositories.
  * $my_url, $my_uri
-   URL and absolute URL of gitweb script; you might need to set those
-   variables if you are using 'pathinfo' feature: see also below.
+   Full URL and absolute URL of gitweb script;
+   in earlier versions of gitweb you might have need to set those
+   variables, now there should be no need to do it.
  * $home_link
    Target of the home link on top of all pages (the first part of view
-   "breadcrumbs").  By default set to absolute URI of a page; you might
-   need to set it up to [base] gitweb URI if you use 'pathinfo' feature
-   (alternative format of the URLs, with project name embedded directly
-   in the path part of URL).
+   "breadcrumbs").  By default set to absolute URI of a page ($my_uri).
  * @stylesheets
    List of URIs of stylesheets (relative to base URI of a page). You
    might specify more than one stylesheet, for example use gitweb.css
@@ -214,6 +212,11 @@
    Rename detection options for git-diff and git-diff-tree. By default
    ('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or
    set it to () if you don't want to have renames detection.
+ * $prevent_xss
+   If true, some gitweb features are disabled to prevent content in
+   repositories from launching cross-site scripting (XSS) attacks.  Set this
+   to true if you don't trust the content of your repositories. The default
+   is false.
 
 
 Projects list file format
@@ -260,7 +263,9 @@
    A .html file (HTML fragment) which is included on the gitweb project
    summary page inside <div> block element. You can use it for longer
    description of a project, to provide links (for example to project's
-   homepage), etc.
+   homepage), etc. This is recognized only if XSS prevention is off
+   ($prevent_xss is false); a way to include a readme safely when XSS
+   prevention is on may be worked out in the future.
  * description (or gitweb.description)
    Short (shortened by default to 25 characters in the projects list page)
    single line description of a project (of a repository). Plain text file;
@@ -322,6 +327,82 @@
   $home_link = "/";
 
 
+PATH_INFO usage
+-----------------------
+If you enable PATH_INFO usage in gitweb by putting
+
+   $feature{'pathinfo'}{'default'} = [1];
+
+in your gitweb.conf, it is possible to set up your server so that it
+consumes and produces URLs in the form
+
+http://git.example.com/project.git/shortlog/sometag
+
+by using a configuration such as the following, that assumes that
+/var/www/gitweb is the DocumentRoot of your webserver, and that it
+contains the gitweb.cgi script and complementary static files
+(stylesheet, favicon):
+
+<VirtualHost *:80>
+	ServerAlias git.example.com
+
+	DocumentRoot /var/www/gitweb
+
+	<Directory /var/www/gitweb>
+		Options ExecCGI
+		AddHandler cgi-script cgi
+
+		DirectoryIndex gitweb.cgi
+
+		RewriteEngine On
+		RewriteCond %{REQUEST_FILENAME} !-f
+		RewriteCond %{REQUEST_FILENAME} !-d
+		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
+	</Directory>
+</VirtualHost>
+
+The rewrite rule guarantees that existing static files will be properly
+served, whereas any other URL will be passed to gitweb as PATH_INFO
+parameter.
+
+Notice that in this case you don't need special settings for
+@stylesheets, $my_uri and $home_link, but you lose "dumb client" access
+to your project .git dirs. A possible workaround for the latter is the
+following: in your project root dir (e.g. /pub/git) have the projects
+named without a .git extension (e.g. /pub/git/project instead of
+/pub/git/project.git) and configure Apache as follows:
+
+<VirtualHost *:80>
+	ServerAlias git.example.com
+
+	DocumentRoot /var/www/gitweb
+
+	AliasMatch ^(/.*?)(\.git)(/.*)? /pub/git$1$3
+	<Directory /var/www/gitweb>
+		Options ExecCGI
+		AddHandler cgi-script cgi
+
+		DirectoryIndex gitweb.cgi
+
+		RewriteEngine On
+		RewriteCond %{REQUEST_FILENAME} !-f
+		RewriteCond %{REQUEST_FILENAME} !-d
+		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
+	</Directory>
+</VirtualHost>
+
+The additional AliasMatch makes it so that
+
+http://git.example.com/project.git
+
+will give raw access to the project's git dir (so that the project can
+be cloned), while
+
+http://git.example.com/project
+
+will provide human-friendly gitweb access.
+
+
 Originally written by:
   Kay Sievers <kay.sievers@vrfy.org>
 
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index aa0eeca..a01eac8 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -435,6 +435,10 @@
 	right: 12px
 }
 
+p.projsearch {
+	text-align: center;
+}
+
 td.linenr {
 	text-align: right;
 }
@@ -481,6 +485,19 @@
 	border-color: #ffccff #ff00ee #ff00ee #ffccff;
 }
 
+span.refs span a {
+	text-decoration: none;
+	color: inherit;
+}
+
+span.refs span a:hover {
+	text-decoration: underline;
+}
+
+span.refs span.indirect {
+	font-style: italic;
+}
+
 span.refs span.ref {
 	background-color: #aaaaff;
 	border-color: #ccccff #0033cc #0033cc #ccccff;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 804670c..33ef190 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -27,11 +27,29 @@
 our $my_url = $cgi->url();
 our $my_uri = $cgi->url(-absolute => 1);
 
-# if we're called with PATH_INFO, we have to strip that
-# from the URL to find our real URL
-if (my $path_info = $ENV{"PATH_INFO"}) {
-	$my_url =~ s,\Q$path_info\E$,,;
-	$my_uri =~ s,\Q$path_info\E$,,;
+# Base URL for relative URLs in gitweb ($logo, $favicon, ...),
+# needed and used only for URLs with nonempty PATH_INFO
+our $base_url = $my_url;
+
+# When the script is used as DirectoryIndex, the URL does not contain the name
+# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
+# have to do it ourselves. We make $path_info global because it's also used
+# later on.
+#
+# Another issue with the script being the DirectoryIndex is that the resulting
+# $my_url data is not the full script URL: this is good, because we want
+# generated links to keep implying the script name if it wasn't explicitly
+# indicated in the URL we're handling, but it means that $my_url cannot be used
+# as base URL.
+# Therefore, if we needed to strip PATH_INFO, then we know that we have
+# to build the base URL ourselves:
+our $path_info = $ENV{"PATH_INFO"};
+if ($path_info) {
+	if ($my_url =~ s,\Q$path_info\E$,, &&
+	    $my_uri =~ s,\Q$path_info\E$,, &&
+	    defined $ENV{'SCRIPT_NAME'}) {
+		$base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+	}
 }
 
 # core git executable to use
@@ -93,6 +111,11 @@
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
 
+# show repository only if this subroutine returns true
+# when given the path to the project, for example:
+#    sub { return -e "$_[0]/git-daemon-export-ok"; }
+our $export_auth_hook = undef;
+
 # only allow viewing of repositories also shown on the overview page
 our $strict_export = "++GITWEB_STRICT_EXPORT++";
 
@@ -125,6 +148,10 @@
 # - one might want to include '-B' option, e.g. '-B', '-M'
 our @diff_opts = ('-M'); # taken from git_commit
 
+# Disables features that would allow repository owners to inject script into
+# the gitweb domain.
+our $prevent_xss = 0;
+
 # information about snapshot formats that gitweb is capable of serving
 our %known_snapshot_formats = (
 	# name => {
@@ -183,7 +210,9 @@
 	# if there is no 'sub' key (no feature-sub), then feature cannot be
 	# overriden
 	#
-	# use gitweb_check_feature(<feature>) to check if <feature> is enabled
+	# use gitweb_get_feature(<feature>) to retrieve the <feature> value
+	# (an array) or gitweb_check_feature(<feature>) to check if <feature>
+	# is enabled
 
 	# Enable the 'blame' blob view, showing the last commit that modified
 	# each line in the file. This can be very CPU-intensive.
@@ -194,7 +223,7 @@
 	# $feature{'blame'}{'override'} = 1;
 	# and in project config gitweb.blame = 0|1;
 	'blame' => {
-		'sub' => \&feature_blame,
+		'sub' => sub { feature_bool('blame', @_) },
 		'override' => 0,
 		'default' => [0]},
 
@@ -232,7 +261,7 @@
 	# $feature{'grep'}{'override'} = 1;
 	# and in project config gitweb.grep = 0|1;
 	'grep' => {
-		'sub' => \&feature_grep,
+		'sub' => sub { feature_bool('grep', @_) },
 		'override' => 0,
 		'default' => [1]},
 
@@ -246,7 +275,7 @@
 	# $feature{'pickaxe'}{'override'} = 1;
 	# and in project config gitweb.pickaxe = 0|1;
 	'pickaxe' => {
-		'sub' => \&feature_pickaxe,
+		'sub' => sub { feature_bool('pickaxe', @_) },
 		'override' => 0,
 		'default' => [1]},
 
@@ -283,9 +312,62 @@
 	'forks' => {
 		'override' => 0,
 		'default' => [0]},
+
+	# Insert custom links to the action bar of all project pages.
+	# This enables you mainly to link to third-party scripts integrating
+	# into gitweb; e.g. git-browser for graphical history representation
+	# or custom web-based repository administration interface.
+
+	# The 'default' value consists of a list of triplets in the form
+	# (label, link, position) where position is the label after which
+	# to insert the link and link is a format string where %n expands
+	# to the project name, %f to the project path within the filesystem,
+	# %h to the current hash (h gitweb parameter) and %b to the current
+	# hash base (hb gitweb parameter); %% expands to %.
+
+	# To enable system wide have in $GITWEB_CONFIG e.g.
+	# $feature{'actions'}{'default'} = [('graphiclog',
+	# 	'/git-browser/by-commit.html?r=%n', 'summary')];
+	# Project specific override is not supported.
+	'actions' => {
+		'override' => 0,
+		'default' => []},
+
+	# Allow gitweb scan project content tags described in ctags/
+	# of project repository, and display the popular Web 2.0-ish
+	# "tag cloud" near the project list. Note that this is something
+	# COMPLETELY different from the normal Git tags.
+
+	# gitweb by itself can show existing tags, but it does not handle
+	# tagging itself; you need an external application for that.
+	# For an example script, check Girocco's cgi/tagproj.cgi.
+	# You may want to install the HTML::TagCloud Perl module to get
+	# a pretty tag cloud instead of just a list of tags.
+
+	# To enable system wide have in $GITWEB_CONFIG
+	# $feature{'ctags'}{'default'} = ['path_to_tag_script'];
+	# Project specific override is not supported.
+	'ctags' => {
+		'override' => 0,
+		'default' => [0]},
+
+	# The maximum number of patches in a patchset generated in patch
+	# view. Set this to 0 or undef to disable patch view, or to a
+	# negative number to remove any limit.
+
+	# To disable system wide have in $GITWEB_CONFIG
+	# $feature{'patches'}{'default'} = [0];
+	# To have project specific config enable override in $GITWEB_CONFIG
+	# $feature{'patches'}{'override'} = 1;
+	# and in project config gitweb.patches = 0|n;
+	# where n is the maximum number of patches allowed in a patchset.
+	'patches' => {
+		'sub' => \&feature_patches,
+		'override' => 0,
+		'default' => [16]},
 );
 
-sub gitweb_check_feature {
+sub gitweb_get_feature {
 	my ($name) = @_;
 	return unless exists $feature{$name};
 	my ($sub, $override, @defaults) = (
@@ -300,16 +382,33 @@
 	return $sub->(@defaults);
 }
 
-sub feature_blame {
-	my ($val) = git_get_project_config('blame', '--bool');
+# A wrapper to check if a given feature is enabled.
+# With this, you can say
+#
+#   my $bool_feat = gitweb_check_feature('bool_feat');
+#   gitweb_check_feature('bool_feat') or somecode;
+#
+# instead of
+#
+#   my ($bool_feat) = gitweb_get_feature('bool_feat');
+#   (gitweb_get_feature('bool_feat'))[0] or somecode;
+#
+sub gitweb_check_feature {
+	return (gitweb_get_feature(@_))[0];
+}
 
-	if ($val eq 'true') {
-		return 1;
+
+sub feature_bool {
+	my $key = shift;
+	my ($val) = git_get_project_config($key, '--bool');
+
+	if (!defined $val) {
+		return ($_[0]);
+	} elsif ($val eq 'true') {
+		return (1);
 	} elsif ($val eq 'false') {
-		return 0;
+		return (0);
 	}
-
-	return $_[0];
 }
 
 sub feature_snapshot {
@@ -324,25 +423,11 @@
 	return @fmts;
 }
 
-sub feature_grep {
-	my ($val) = git_get_project_config('grep', '--bool');
+sub feature_patches {
+	my @val = (git_get_project_config('patches', '--int'));
 
-	if ($val eq 'true') {
-		return (1);
-	} elsif ($val eq 'false') {
-		return (0);
-	}
-
-	return ($_[0]);
-}
-
-sub feature_pickaxe {
-	my ($val) = git_get_project_config('pickaxe', '--bool');
-
-	if ($val eq 'true') {
-		return (1);
-	} elsif ($val eq 'false') {
-		return (0);
+	if (@val) {
+		return @val;
 	}
 
 	return ($_[0]);
@@ -361,7 +446,8 @@
 sub check_export_ok {
 	my ($dir) = @_;
 	return (check_head_link($dir) &&
-		(!$export_ok || -e "$dir/$export_ok"));
+		(!$export_ok || -e "$dir/$export_ok") &&
+		(!$export_auth_hook || $export_auth_hook->($dir)));
 }
 
 # process alternate names for backward compatibility
@@ -391,162 +477,43 @@
 
 # ======================================================================
 # input validation and dispatch
-our $action = $cgi->param('a');
-if (defined $action) {
-	if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
-		die_error(400, "Invalid action parameter");
-	}
-}
 
-# parameters which are pathnames
-our $project = $cgi->param('p');
-if (defined $project) {
-	if (!validate_pathname($project) ||
-	    !(-d "$projectroot/$project") ||
-	    !check_head_link("$projectroot/$project") ||
-	    ($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
-	    ($strict_export && !project_in_list($project))) {
-		undef $project;
-		die_error(404, "No such project");
-	}
-}
+# input parameters can be collected from a variety of sources (presently, CGI
+# and PATH_INFO), so we define an %input_params hash that collects them all
+# together during validation: this allows subsequent uses (e.g. href()) to be
+# agnostic of the parameter origin
 
-our $file_name = $cgi->param('f');
-if (defined $file_name) {
-	if (!validate_pathname($file_name)) {
-		die_error(400, "Invalid file parameter");
-	}
-}
+our %input_params = ();
 
-our $file_parent = $cgi->param('fp');
-if (defined $file_parent) {
-	if (!validate_pathname($file_parent)) {
-		die_error(400, "Invalid file parent parameter");
-	}
-}
+# input parameters are stored with the long parameter name as key. This will
+# also be used in the href subroutine to convert parameters to their CGI
+# equivalent, and since the href() usage is the most frequent one, we store
+# the name -> CGI key mapping here, instead of the reverse.
+#
+# XXX: Warning: If you touch this, check the search form for updating,
+# too.
 
-# parameters which are refnames
-our $hash = $cgi->param('h');
-if (defined $hash) {
-	if (!validate_refname($hash)) {
-		die_error(400, "Invalid hash parameter");
-	}
-}
-
-our $hash_parent = $cgi->param('hp');
-if (defined $hash_parent) {
-	if (!validate_refname($hash_parent)) {
-		die_error(400, "Invalid hash parent parameter");
-	}
-}
-
-our $hash_base = $cgi->param('hb');
-if (defined $hash_base) {
-	if (!validate_refname($hash_base)) {
-		die_error(400, "Invalid hash base parameter");
-	}
-}
-
-my %allowed_options = (
-	"--no-merges" => [ qw(rss atom log shortlog history) ],
+our @cgi_param_mapping = (
+	project => "p",
+	action => "a",
+	file_name => "f",
+	file_parent => "fp",
+	hash => "h",
+	hash_parent => "hp",
+	hash_base => "hb",
+	hash_parent_base => "hpb",
+	page => "pg",
+	order => "o",
+	searchtext => "s",
+	searchtype => "st",
+	snapshot_format => "sf",
+	extra_options => "opt",
+	search_use_regexp => "sr",
 );
+our %cgi_param_mapping = @cgi_param_mapping;
 
-our @extra_options = $cgi->param('opt');
-if (defined @extra_options) {
-	foreach my $opt (@extra_options) {
-		if (not exists $allowed_options{$opt}) {
-			die_error(400, "Invalid option parameter");
-		}
-		if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
-			die_error(400, "Invalid option parameter for this action");
-		}
-	}
-}
-
-our $hash_parent_base = $cgi->param('hpb');
-if (defined $hash_parent_base) {
-	if (!validate_refname($hash_parent_base)) {
-		die_error(400, "Invalid hash parent base parameter");
-	}
-}
-
-# other parameters
-our $page = $cgi->param('pg');
-if (defined $page) {
-	if ($page =~ m/[^0-9]/) {
-		die_error(400, "Invalid page parameter");
-	}
-}
-
-our $searchtype = $cgi->param('st');
-if (defined $searchtype) {
-	if ($searchtype =~ m/[^a-z]/) {
-		die_error(400, "Invalid searchtype parameter");
-	}
-}
-
-our $search_use_regexp = $cgi->param('sr');
-
-our $searchtext = $cgi->param('s');
-our $search_regexp;
-if (defined $searchtext) {
-	if (length($searchtext) < 2) {
-		die_error(403, "At least two characters are required for search parameter");
-	}
-	$search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
-}
-
-# now read PATH_INFO and use it as alternative to parameters
-sub evaluate_path_info {
-	return if defined $project;
-	my $path_info = $ENV{"PATH_INFO"};
-	return if !$path_info;
-	$path_info =~ s,^/+,,;
-	return if !$path_info;
-	# find which part of PATH_INFO is project
-	$project = $path_info;
-	$project =~ s,/+$,,;
-	while ($project && !check_head_link("$projectroot/$project")) {
-		$project =~ s,/*[^/]*$,,;
-	}
-	# validate project
-	$project = validate_pathname($project);
-	if (!$project ||
-	    ($export_ok && !-e "$projectroot/$project/$export_ok") ||
-	    ($strict_export && !project_in_list($project))) {
-		undef $project;
-		return;
-	}
-	# do not change any parameters if an action is given using the query string
-	return if $action;
-	$path_info =~ s,^\Q$project\E/*,,;
-	my ($refname, $pathname) = split(/:/, $path_info, 2);
-	if (defined $pathname) {
-		# we got "project.git/branch:filename" or "project.git/branch:dir/"
-		# we could use git_get_type(branch:pathname), but it needs $git_dir
-		$pathname =~ s,^/+,,;
-		if (!$pathname || substr($pathname, -1) eq "/") {
-			$action  ||= "tree";
-			$pathname =~ s,/$,,;
-		} else {
-			$action  ||= "blob_plain";
-		}
-		$hash_base ||= validate_refname($refname);
-		$file_name ||= validate_pathname($pathname);
-	} elsif (defined $refname) {
-		# we got "project.git/branch"
-		$action ||= "shortlog";
-		$hash   ||= validate_refname($refname);
-	}
-}
-evaluate_path_info();
-
-# path to the current git repository
-our $git_dir;
-$git_dir = "$projectroot/$project" if $project;
-
-# dispatch
-my %actions = (
+# we will also need to know the possible actions, for validation
+our %actions = (
 	"blame" => \&git_blame,
 	"blobdiff" => \&git_blobdiff,
 	"blobdiff_plain" => \&git_blobdiff_plain,
@@ -559,6 +526,8 @@
 	"heads" => \&git_heads,
 	"history" => \&git_history,
 	"log" => \&git_log,
+	"patch" => \&git_patch,
+	"patches" => \&git_patches,
 	"rss" => \&git_rss,
 	"atom" => \&git_atom,
 	"search" => \&git_search,
@@ -576,6 +545,275 @@
 	"project_index" => \&git_project_index,
 );
 
+# finally, we have the hash of allowed extra_options for the commands that
+# allow them
+our %allowed_options = (
+	"--no-merges" => [ qw(rss atom log shortlog history) ],
+);
+
+# fill %input_params with the CGI parameters. All values except for 'opt'
+# should be single values, but opt can be an array. We should probably
+# build an array of parameters that can be multi-valued, but since for the time
+# being it's only this one, we just single it out
+while (my ($name, $symbol) = each %cgi_param_mapping) {
+	if ($symbol eq 'opt') {
+		$input_params{$name} = [ $cgi->param($symbol) ];
+	} else {
+		$input_params{$name} = $cgi->param($symbol);
+	}
+}
+
+# now read PATH_INFO and update the parameter list for missing parameters
+sub evaluate_path_info {
+	return if defined $input_params{'project'};
+	return if !$path_info;
+	$path_info =~ s,^/+,,;
+	return if !$path_info;
+
+	# find which part of PATH_INFO is project
+	my $project = $path_info;
+	$project =~ s,/+$,,;
+	while ($project && !check_head_link("$projectroot/$project")) {
+		$project =~ s,/*[^/]*$,,;
+	}
+	return unless $project;
+	$input_params{'project'} = $project;
+
+	# do not change any parameters if an action is given using the query string
+	return if $input_params{'action'};
+	$path_info =~ s,^\Q$project\E/*,,;
+
+	# next, check if we have an action
+	my $action = $path_info;
+	$action =~ s,/.*$,,;
+	if (exists $actions{$action}) {
+		$path_info =~ s,^$action/*,,;
+		$input_params{'action'} = $action;
+	}
+
+	# list of actions that want hash_base instead of hash, but can have no
+	# pathname (f) parameter
+	my @wants_base = (
+		'tree',
+		'history',
+	);
+
+	# we want to catch
+	# [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
+	my ($parentrefname, $parentpathname, $refname, $pathname) =
+		($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+
+	# first, analyze the 'current' part
+	if (defined $pathname) {
+		# we got "branch:filename" or "branch:dir/"
+		# we could use git_get_type(branch:pathname), but:
+		# - it needs $git_dir
+		# - it does a git() call
+		# - the convention of terminating directories with a slash
+		#   makes it superfluous
+		# - embedding the action in the PATH_INFO would make it even
+		#   more superfluous
+		$pathname =~ s,^/+,,;
+		if (!$pathname || substr($pathname, -1) eq "/") {
+			$input_params{'action'} ||= "tree";
+			$pathname =~ s,/$,,;
+		} else {
+			# the default action depends on whether we had parent info
+			# or not
+			if ($parentrefname) {
+				$input_params{'action'} ||= "blobdiff_plain";
+			} else {
+				$input_params{'action'} ||= "blob_plain";
+			}
+		}
+		$input_params{'hash_base'} ||= $refname;
+		$input_params{'file_name'} ||= $pathname;
+	} elsif (defined $refname) {
+		# we got "branch". In this case we have to choose if we have to
+		# set hash or hash_base.
+		#
+		# Most of the actions without a pathname only want hash to be
+		# set, except for the ones specified in @wants_base that want
+		# hash_base instead. It should also be noted that hand-crafted
+		# links having 'history' as an action and no pathname or hash
+		# set will fail, but that happens regardless of PATH_INFO.
+		$input_params{'action'} ||= "shortlog";
+		if (grep { $_ eq $input_params{'action'} } @wants_base) {
+			$input_params{'hash_base'} ||= $refname;
+		} else {
+			$input_params{'hash'} ||= $refname;
+		}
+	}
+
+	# next, handle the 'parent' part, if present
+	if (defined $parentrefname) {
+		# a missing pathspec defaults to the 'current' filename, allowing e.g.
+		# someproject/blobdiff/oldrev..newrev:/filename
+		if ($parentpathname) {
+			$parentpathname =~ s,^/+,,;
+			$parentpathname =~ s,/$,,;
+			$input_params{'file_parent'} ||= $parentpathname;
+		} else {
+			$input_params{'file_parent'} ||= $input_params{'file_name'};
+		}
+		# we assume that hash_parent_base is wanted if a path was specified,
+		# or if the action wants hash_base instead of hash
+		if (defined $input_params{'file_parent'} ||
+			grep { $_ eq $input_params{'action'} } @wants_base) {
+			$input_params{'hash_parent_base'} ||= $parentrefname;
+		} else {
+			$input_params{'hash_parent'} ||= $parentrefname;
+		}
+	}
+
+	# for the snapshot action, we allow URLs in the form
+	# $project/snapshot/$hash.ext
+	# where .ext determines the snapshot and gets removed from the
+	# passed $refname to provide the $hash.
+	#
+	# To be able to tell that $refname includes the format extension, we
+	# require the following two conditions to be satisfied:
+	# - the hash input parameter MUST have been set from the $refname part
+	#   of the URL (i.e. they must be equal)
+	# - the snapshot format MUST NOT have been defined already (e.g. from
+	#   CGI parameter sf)
+	# It's also useless to try any matching unless $refname has a dot,
+	# so we check for that too
+	if (defined $input_params{'action'} &&
+		$input_params{'action'} eq 'snapshot' &&
+		defined $refname && index($refname, '.') != -1 &&
+		$refname eq $input_params{'hash'} &&
+		!defined $input_params{'snapshot_format'}) {
+		# We loop over the known snapshot formats, checking for
+		# extensions. Allowed extensions are both the defined suffix
+		# (which includes the initial dot already) and the snapshot
+		# format key itself, with a prepended dot
+		while (my ($fmt, %opt) = each %known_snapshot_formats) {
+			my $hash = $refname;
+			my $sfx;
+			$hash =~ s/(\Q$opt{'suffix'}\E|\Q.$fmt\E)$//;
+			next unless $sfx = $1;
+			# a valid suffix was found, so set the snapshot format
+			# and reset the hash parameter
+			$input_params{'snapshot_format'} = $fmt;
+			$input_params{'hash'} = $hash;
+			# we also set the format suffix to the one requested
+			# in the URL: this way a request for e.g. .tgz returns
+			# a .tgz instead of a .tar.gz
+			$known_snapshot_formats{$fmt}{'suffix'} = $sfx;
+			last;
+		}
+	}
+}
+evaluate_path_info();
+
+our $action = $input_params{'action'};
+if (defined $action) {
+	if (!validate_action($action)) {
+		die_error(400, "Invalid action parameter");
+	}
+}
+
+# parameters which are pathnames
+our $project = $input_params{'project'};
+if (defined $project) {
+	if (!validate_project($project)) {
+		undef $project;
+		die_error(404, "No such project");
+	}
+}
+
+our $file_name = $input_params{'file_name'};
+if (defined $file_name) {
+	if (!validate_pathname($file_name)) {
+		die_error(400, "Invalid file parameter");
+	}
+}
+
+our $file_parent = $input_params{'file_parent'};
+if (defined $file_parent) {
+	if (!validate_pathname($file_parent)) {
+		die_error(400, "Invalid file parent parameter");
+	}
+}
+
+# parameters which are refnames
+our $hash = $input_params{'hash'};
+if (defined $hash) {
+	if (!validate_refname($hash)) {
+		die_error(400, "Invalid hash parameter");
+	}
+}
+
+our $hash_parent = $input_params{'hash_parent'};
+if (defined $hash_parent) {
+	if (!validate_refname($hash_parent)) {
+		die_error(400, "Invalid hash parent parameter");
+	}
+}
+
+our $hash_base = $input_params{'hash_base'};
+if (defined $hash_base) {
+	if (!validate_refname($hash_base)) {
+		die_error(400, "Invalid hash base parameter");
+	}
+}
+
+our @extra_options = @{$input_params{'extra_options'}};
+# @extra_options is always defined, since it can only be (currently) set from
+# CGI, and $cgi->param() returns the empty array in array context if the param
+# is not set
+foreach my $opt (@extra_options) {
+	if (not exists $allowed_options{$opt}) {
+		die_error(400, "Invalid option parameter");
+	}
+	if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+		die_error(400, "Invalid option parameter for this action");
+	}
+}
+
+our $hash_parent_base = $input_params{'hash_parent_base'};
+if (defined $hash_parent_base) {
+	if (!validate_refname($hash_parent_base)) {
+		die_error(400, "Invalid hash parent base parameter");
+	}
+}
+
+# other parameters
+our $page = $input_params{'page'};
+if (defined $page) {
+	if ($page =~ m/[^0-9]/) {
+		die_error(400, "Invalid page parameter");
+	}
+}
+
+our $searchtype = $input_params{'searchtype'};
+if (defined $searchtype) {
+	if ($searchtype =~ m/[^a-z]/) {
+		die_error(400, "Invalid searchtype parameter");
+	}
+}
+
+our $search_use_regexp = $input_params{'search_use_regexp'};
+
+our $searchtext = $input_params{'searchtext'};
+our $search_regexp;
+if (defined $searchtext) {
+	if (length($searchtext) < 2) {
+		die_error(403, "At least two characters are required for search parameter");
+	}
+	$search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
+}
+
+# path to the current git repository
+our $git_dir;
+$git_dir = "$projectroot/$project" if $project;
+
+# list of supported snapshot formats
+our @snapshot_fmts = gitweb_get_feature('snapshot');
+@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+
+# dispatch
 if (!defined $action) {
 	if (defined $hash) {
 		$action = git_get_type($hash);
@@ -605,55 +843,96 @@
 	# default is to use -absolute url() i.e. $my_uri
 	my $href = $params{-full} ? $my_url : $my_uri;
 
-	# XXX: Warning: If you touch this, check the search form for updating,
-	# too.
-
-	my @mapping = (
-		project => "p",
-		action => "a",
-		file_name => "f",
-		file_parent => "fp",
-		hash => "h",
-		hash_parent => "hp",
-		hash_base => "hb",
-		hash_parent_base => "hpb",
-		page => "pg",
-		order => "o",
-		searchtext => "s",
-		searchtype => "st",
-		snapshot_format => "sf",
-		extra_options => "opt",
-		search_use_regexp => "sr",
-	);
-	my %mapping = @mapping;
-
 	$params{'project'} = $project unless exists $params{'project'};
 
 	if ($params{-replay}) {
-		while (my ($name, $symbol) = each %mapping) {
+		while (my ($name, $symbol) = each %cgi_param_mapping) {
 			if (!exists $params{$name}) {
-				# to allow for multivalued params we use arrayref form
-				$params{$name} = [ $cgi->param($symbol) ];
+				$params{$name} = $input_params{$name};
 			}
 		}
 	}
 
-	my ($use_pathinfo) = gitweb_check_feature('pathinfo');
-	if ($use_pathinfo) {
-		# use PATH_INFO for project name
-		$href .= "/".esc_url($params{'project'}) if defined $params{'project'};
+	my $use_pathinfo = gitweb_check_feature('pathinfo');
+	if ($use_pathinfo and defined $params{'project'}) {
+		# try to put as many parameters as possible in PATH_INFO:
+		#   - project name
+		#   - action
+		#   - hash_parent or hash_parent_base:/file_parent
+		#   - hash or hash_base:/filename
+		#   - the snapshot_format as an appropriate suffix
+
+		# When the script is the root DirectoryIndex for the domain,
+		# $href here would be something like http://gitweb.example.com/
+		# Thus, we strip any trailing / from $href, to spare us double
+		# slashes in the final URL
+		$href =~ s,/$,,;
+
+		# Then add the project name, if present
+		$href .= "/".esc_url($params{'project'});
 		delete $params{'project'};
 
-		# Summary just uses the project path URL
-		if (defined $params{'action'} && $params{'action'} eq 'summary') {
+		# since we destructively absorb parameters, we keep this
+		# boolean that remembers if we're handling a snapshot
+		my $is_snapshot = $params{'action'} eq 'snapshot';
+
+		# Summary just uses the project path URL, any other action is
+		# added to the URL
+		if (defined $params{'action'}) {
+			$href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary';
 			delete $params{'action'};
 		}
+
+		# Next, we put hash_parent_base:/file_parent..hash_base:/file_name,
+		# stripping nonexistent or useless pieces
+		$href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'}
+			|| $params{'hash_parent'} || $params{'hash'});
+		if (defined $params{'hash_base'}) {
+			if (defined $params{'hash_parent_base'}) {
+				$href .= esc_url($params{'hash_parent_base'});
+				# skip the file_parent if it's the same as the file_name
+				delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
+				if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
+					$href .= ":/".esc_url($params{'file_parent'});
+					delete $params{'file_parent'};
+				}
+				$href .= "..";
+				delete $params{'hash_parent'};
+				delete $params{'hash_parent_base'};
+			} elsif (defined $params{'hash_parent'}) {
+				$href .= esc_url($params{'hash_parent'}). "..";
+				delete $params{'hash_parent'};
+			}
+
+			$href .= esc_url($params{'hash_base'});
+			if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
+				$href .= ":/".esc_url($params{'file_name'});
+				delete $params{'file_name'};
+			}
+			delete $params{'hash'};
+			delete $params{'hash_base'};
+		} elsif (defined $params{'hash'}) {
+			$href .= esc_url($params{'hash'});
+			delete $params{'hash'};
+		}
+
+		# If the action was a snapshot, we can absorb the
+		# snapshot_format parameter too
+		if ($is_snapshot) {
+			my $fmt = $params{'snapshot_format'};
+			# snapshot_format should always be defined when href()
+			# is called, but just in case some code forgets, we
+			# fall back to the default
+			$fmt ||= $snapshot_fmts[0];
+			$href .= $known_snapshot_formats{$fmt}{'suffix'};
+			delete $params{'snapshot_format'};
+		}
 	}
 
 	# now encode the parameters explicitly
 	my @result = ();
-	for (my $i = 0; $i < @mapping; $i += 2) {
-		my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
+	for (my $i = 0; $i < @cgi_param_mapping; $i += 2) {
+		my ($name, $symbol) = ($cgi_param_mapping[$i], $cgi_param_mapping[$i+1]);
 		if (defined $params{$name}) {
 			if (ref($params{$name}) eq "ARRAY") {
 				foreach my $par (@{$params{$name}}) {
@@ -673,6 +952,24 @@
 ## ======================================================================
 ## validation, quoting/unquoting and escaping
 
+sub validate_action {
+	my $input = shift || return undef;
+	return undef unless exists $actions{$input};
+	return $input;
+}
+
+sub validate_project {
+	my $input = shift || return undef;
+	if (!validate_pathname($input) ||
+		!(-d "$projectroot/$input") ||
+		!check_export_ok("$projectroot/$input") ||
+		($strict_export && !project_in_list($input))) {
+		return undef;
+	} else {
+		return $input;
+	}
+}
+
 sub validate_pathname {
 	my $input = shift || return undef;
 
@@ -783,7 +1080,7 @@
 	);
 	my $chr = ( (exists $es{$cntrl})
 		    ? $es{$cntrl}
-		    : sprintf('\%03o', ord($cntrl)) );
+		    : sprintf('\%2x', ord($cntrl)) );
 	if ($opts{-nohtml}) {
 		return $chr;
 	} else {
@@ -1087,24 +1384,32 @@
 	my $line = shift;
 
 	$line = esc_html($line, -nbsp=>1);
-	if ($line =~ m/([0-9a-fA-F]{8,40})/) {
-		my $hash_text = $1;
-		my $link =
-			$cgi->a({-href => href(action=>"object", hash=>$hash_text),
-			        -class => "text"}, $hash_text);
-		$line =~ s/$hash_text/$link/;
-	}
+	$line =~ s{\b([0-9a-fA-F]{8,40})\b}{
+		$cgi->a({-href => href(action=>"object", hash=>$1),
+					-class => "text"}, $1);
+	}eg;
+
 	return $line;
 }
 
 # format marker of refs pointing to given object
+
+# the destination action is chosen based on object type and current context:
+# - for annotated tags, we choose the tag view unless it's the current view
+#   already, in which case we go to shortlog view
+# - for other refs, we keep the current view if we're in history, shortlog or
+#   log view, and select shortlog otherwise
 sub format_ref_marker {
 	my ($refs, $id) = @_;
 	my $markers = '';
 
 	if (defined $refs->{$id}) {
 		foreach my $ref (@{$refs->{$id}}) {
+			# this code exploits the fact that non-lightweight tags are the
+			# only indirect objects, and that they are the only objects for which
+			# we want to use tag instead of shortlog as action
 			my ($type, $name) = qw();
+			my $indirect = ($ref =~ s/\^\{\}$//);
 			# e.g. tags/v2.6.11 or heads/next
 			if ($ref =~ m!^(.*?)s?/(.*)$!) {
 				$type = $1;
@@ -1114,8 +1419,29 @@
 				$name = $ref;
 			}
 
-			$markers .= " <span class=\"$type\" title=\"$ref\">" .
-			            esc_html($name) . "</span>";
+			my $class = $type;
+			$class .= " indirect" if $indirect;
+
+			my $dest_action = "shortlog";
+
+			if ($indirect) {
+				$dest_action = "tag" unless $action eq "tag";
+			} elsif ($action =~ /^(history|(short)?log)$/) {
+				$dest_action = $action;
+			}
+
+			my $dest = "";
+			$dest .= "refs/" unless $ref =~ m!^refs/!;
+			$dest .= $ref;
+
+			my $link = $cgi->a({
+				-href => href(
+					action=>$dest_action,
+					hash=>$dest
+				)}, $name);
+
+			$markers .= " <span class=\"$class\" title=\"$ref\">" .
+				$link . "</span>";
 		}
 	}
 
@@ -1427,8 +1753,6 @@
 # linked.  Pass the hash of the tree/commit to snapshot.
 sub format_snapshot_links {
 	my ($hash) = @_;
-	my @snapshot_fmts = gitweb_check_feature('snapshot');
-	@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
 	my $num_fmts = @snapshot_fmts;
 	if ($num_fmts > 1) {
 		# A parenthesized list of links bearing format names.
@@ -1588,18 +1912,19 @@
 	return %config;
 }
 
-# convert config value to boolean, 'true' or 'false'
+# convert config value to boolean: 'true' or 'false'
 # no value, number > 0, 'true' and 'yes' values are true
 # rest of values are treated as false (never as error)
 sub config_to_bool {
 	my $val = shift;
 
+	return 1 if !defined $val;             # section.key
+
 	# strip leading and trailing whitespace
 	$val =~ s/^\s+//;
 	$val =~ s/\s+$//;
 
-	return (!defined $val ||               # section.key
-	        ($val =~ /^\d+$/ && $val) ||   # section.key = 1
+	return (($val =~ /^\d+$/ && $val) ||   # section.key = 1
 	        ($val =~ /^(?:true|yes)$/i));  # section.key = true
 }
 
@@ -1652,6 +1977,9 @@
 		$config_file = "$git_dir/config";
 	}
 
+	# check if config variable (key) exists
+	return unless exists $config{"gitweb.$key"};
+
 	# ensure given type
 	if (!defined $type) {
 		return $config{"gitweb.$key"};
@@ -1732,6 +2060,71 @@
 	return $descr;
 }
 
+sub git_get_project_ctags {
+	my $path = shift;
+	my $ctags = {};
+
+	$git_dir = "$projectroot/$path";
+	unless (opendir D, "$git_dir/ctags") {
+		return $ctags;
+	}
+	foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) {
+		open CT, $_ or next;
+		my $val = <CT>;
+		chomp $val;
+		close CT;
+		my $ctag = $_; $ctag =~ s#.*/##;
+		$ctags->{$ctag} = $val;
+	}
+	closedir D;
+	$ctags;
+}
+
+sub git_populate_project_tagcloud {
+	my $ctags = shift;
+
+	# First, merge different-cased tags; tags vote on casing
+	my %ctags_lc;
+	foreach (keys %$ctags) {
+		$ctags_lc{lc $_}->{count} += $ctags->{$_};
+		if (not $ctags_lc{lc $_}->{topcount}
+		    or $ctags_lc{lc $_}->{topcount} < $ctags->{$_}) {
+			$ctags_lc{lc $_}->{topcount} = $ctags->{$_};
+			$ctags_lc{lc $_}->{topname} = $_;
+		}
+	}
+
+	my $cloud;
+	if (eval { require HTML::TagCloud; 1; }) {
+		$cloud = HTML::TagCloud->new;
+		foreach (sort keys %ctags_lc) {
+			# Pad the title with spaces so that the cloud looks
+			# less crammed.
+			my $title = $ctags_lc{$_}->{topname};
+			$title =~ s/ /&nbsp;/g;
+			$title =~ s/^/&nbsp;/g;
+			$title =~ s/$/&nbsp;/g;
+			$cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count});
+		}
+	} else {
+		$cloud = \%ctags_lc;
+	}
+	$cloud;
+}
+
+sub git_show_project_tagcloud {
+	my ($cloud, $count) = @_;
+	print STDERR ref($cloud)."..\n";
+	if (ref $cloud eq 'HTML::TagCloud') {
+		return $cloud->html_and_css($count);
+	} else {
+		my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
+		return '<p align="center">' . join (', ', map {
+			"<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
+		} splice(@tags, 0, $count)) . '</p>';
+	}
+}
+
 sub git_get_project_url_list {
 	my $path = shift;
 
@@ -1753,7 +2146,7 @@
 	$filter ||= '';
 	$filter =~ s/\.git$//;
 
-	my ($check_forks) = gitweb_check_feature('forks');
+	my $check_forks = gitweb_check_feature('forks');
 
 	if (-d $projects_list) {
 		# search in directory
@@ -1780,10 +2173,9 @@
 
 				my $subdir = substr($File::Find::name, $pfxlen + 1);
 				# we check related file in $projectroot
-				if ($check_forks and $subdir =~ m#/.#) {
-					$File::Find::prune = 1;
-				} elsif (check_export_ok("$projectroot/$filter/$subdir")) {
-					push @list, { path => ($filter ? "$filter/" : '') . $subdir };
+				my $path = ($filter ? "$filter/" : '') . $subdir;
+				if (check_export_ok("$projectroot/$path")) {
+					push @list, { path => $path };
 					$File::Find::prune = 1;
 				}
 			},
@@ -1926,7 +2318,7 @@
 
 	while (my $line = <$fd>) {
 		chomp $line;
-		if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type/?[^^]+)!) {
+		if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) {
 			if (defined $refs{$1}) {
 				push @{$refs{$1}}, $2;
 			} else {
@@ -2394,6 +2786,15 @@
 	return to_utf8($owner);
 }
 
+# assume that file exists
+sub insert_file {
+	my $filename = shift;
+
+	open my $fd, '<', $filename;
+	print map { to_utf8($_) } <$fd>;
+	close $fd;
+}
+
 ## ......................................................................
 ## mimetype related functions
 
@@ -2522,9 +2923,14 @@
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
 EOF
-# print out each stylesheet that exist
+	# the stylesheet, favicon etc urls won't work correctly with path_info
+	# unless we set the appropriate base URL
+	if ($ENV{'PATH_INFO'}) {
+		print "<base href=\"".esc_url($base_url)."\" />\n";
+	}
+	# print out each stylesheet that exist, providing backwards capability
+	# for those people who defined $stylesheet in a config file
 	if (defined $stylesheet) {
-#provides backwards capability for those people who define style sheet in a config file
 		print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
 	} else {
 		foreach my $stylesheet (@stylesheets) {
@@ -2582,9 +2988,7 @@
 	      "<body>\n";
 
 	if (-f $site_header) {
-		open (my $fd, $site_header);
-		print <$fd>;
-		close $fd;
+		insert_file($site_header);
 	}
 
 	print "<div class=\"page_header\">\n" .
@@ -2601,7 +3005,7 @@
 	}
 	print "</div>\n";
 
-	my ($have_search) = gitweb_check_feature('search');
+	my $have_search = gitweb_check_feature('search');
 	if (defined $project && $have_search) {
 		if (!defined $searchtext) {
 			$searchtext = "";
@@ -2615,7 +3019,7 @@
 			$search_hash = "HEAD";
 		}
 		my $action = $my_uri;
-		my ($use_pathinfo) = gitweb_check_feature('pathinfo');
+		my $use_pathinfo = gitweb_check_feature('pathinfo');
 		if ($use_pathinfo) {
 			$action .= "/".esc_url($project);
 		}
@@ -2671,9 +3075,7 @@
 	print "</div>\n"; # class="page_footer"
 
 	if (-f $site_footer) {
-		open (my $fd, $site_footer);
-		print <$fd>;
-		close $fd;
+		insert_file($site_footer);
 	}
 
 	print "</body>\n" .
@@ -2734,13 +3136,31 @@
 			}
 		}
 	}
+
 	$arg{'tree'}{'hash'} = $treehead if defined $treehead;
 	$arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
 
+	my @actions = gitweb_get_feature('actions');
+	my %repl = (
+		'%' => '%',
+		'n' => $project,         # project name
+		'f' => $git_dir,         # project path within filesystem
+		'h' => $treehead || '',  # current hash ('h' parameter)
+		'b' => $treebase || '',  # hash base ('hb' parameter)
+	);
+	while (@actions) {
+		my ($label, $link, $pos) = splice(@actions,0,3);
+		# insert
+		@navs = map { $_ eq $pos ? ($_, $label) : $_ } @navs;
+		# munch munch
+		$link =~ s/%([%nfhb])/$repl{$1}/g;
+		$arg{$label}{'_href'} = $link;
+	}
+
 	print "<div class=\"page_nav\">\n" .
 		(join " | ",
 		 map { $_ eq $current ?
-		       $_ : $cgi->a({-href => href(%{$arg{$_}})}, "$_")
+		       $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
 		 } @navs);
 	print "<br/>\n$extra<br/>\n" .
 	      "</div>\n";
@@ -3090,7 +3510,7 @@
 sub git_difftree_body {
 	my ($difftree, $hash, @parents) = @_;
 	my ($parent) = $parents[0];
-	my ($have_blame) = gitweb_check_feature('blame');
+	my $have_blame = gitweb_check_feature('blame');
 	print "<div class=\"list_head\">\n";
 	if ($#{$difftree} > 10) {
 		print(($#{$difftree} + 1) . " files changed:\n");
@@ -3550,6 +3970,7 @@
 	my ($projlist, $check_forks) = @_;
 	my @projects;
 
+	my $show_ctags = gitweb_check_feature('ctags');
  PROJECT:
 	foreach my $pr (@$projlist) {
 		my (@activity) = git_get_last_activity($pr->{'path'});
@@ -3576,25 +3997,20 @@
 				$pr->{'forks'} = 0;
 			}
 		}
+		$show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
 		push @projects, $pr;
 	}
 
 	return @projects;
 }
 
-# print 'sort by' <th> element, either sorting by $key if $name eq $order
-# (changing $list), or generating 'sort by $name' replay link otherwise
+# print 'sort by' <th> element, generating 'sort by $name' replay link
+# if that order is not selected
 sub print_sort_th {
-	my ($str_sort, $name, $order, $key, $header, $list) = @_;
-	$key    ||= $name;
+	my ($name, $order, $header) = @_;
 	$header ||= ucfirst($name);
 
 	if ($order eq $name) {
-		if ($str_sort) {
-			@$list = sort {$a->{$key} cmp $b->{$key}} @$list;
-		} else {
-			@$list = sort {$a->{$key} <=> $b->{$key}} @$list;
-		}
 		print "<th>$header</th>\n";
 	} else {
 		print "<th>" .
@@ -3604,44 +4020,71 @@
 	}
 }
 
-sub print_sort_th_str {
-	print_sort_th(1, @_);
-}
-
-sub print_sort_th_num {
-	print_sort_th(0, @_);
-}
-
 sub git_project_list_body {
+	# actually uses global variable $project
 	my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
 
-	my ($check_forks) = gitweb_check_feature('forks');
+	my $check_forks = gitweb_check_feature('forks');
 	my @projects = fill_project_list_info($projlist, $check_forks);
 
 	$order ||= $default_projects_order;
 	$from = 0 unless defined $from;
 	$to = $#projects if (!defined $to || $#projects < $to);
 
+	my %order_info = (
+		project => { key => 'path', type => 'str' },
+		descr => { key => 'descr_long', type => 'str' },
+		owner => { key => 'owner', type => 'str' },
+		age => { key => 'age', type => 'num' }
+	);
+	my $oi = $order_info{$order};
+	if ($oi->{'type'} eq 'str') {
+		@projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects;
+	} else {
+		@projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects;
+	}
+
+	my $show_ctags = gitweb_check_feature('ctags');
+	if ($show_ctags) {
+		my %ctags;
+		foreach my $p (@projects) {
+			foreach my $ct (keys %{$p->{'ctags'}}) {
+				$ctags{$ct} += $p->{'ctags'}->{$ct};
+			}
+		}
+		my $cloud = git_populate_project_tagcloud(\%ctags);
+		print git_show_project_tagcloud($cloud, 64);
+	}
+
 	print "<table class=\"project_list\">\n";
 	unless ($no_header) {
 		print "<tr>\n";
 		if ($check_forks) {
 			print "<th></th>\n";
 		}
-		print_sort_th_str('project', $order, 'path',
-		                  'Project', \@projects);
-		print_sort_th_str('descr', $order, 'descr_long',
-		                  'Description', \@projects);
-		print_sort_th_str('owner', $order, 'owner',
-		                  'Owner', \@projects);
-		print_sort_th_num('age', $order, 'age',
-		                  'Last Change', \@projects);
+		print_sort_th('project', $order, 'Project');
+		print_sort_th('descr', $order, 'Description');
+		print_sort_th('owner', $order, 'Owner');
+		print_sort_th('age', $order, 'Last Change');
 		print "<th></th>\n" . # for links
 		      "</tr>\n";
 	}
 	my $alternate = 1;
+	my $tagfilter = $cgi->param('by_tag');
 	for (my $i = $from; $i <= $to; $i++) {
 		my $pr = $projects[$i];
+
+		next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}};
+		next if $searchtext and not $pr->{'path'} =~ /$searchtext/
+			and not $pr->{'descr_long'} =~ /$searchtext/;
+		# Weed out forks or non-matching entries of search
+		if ($check_forks) {
+			my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#;
+			$forkbase="^$forkbase" if $forkbase;
+			next if not $searchtext and not $tagfilter and $show_ctags
+				and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
+		}
+
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
@@ -3958,7 +4401,7 @@
 ## actions
 
 sub git_project_list {
-	my $order = $cgi->param('o');
+	my $order = $input_params{'order'};
 	if (defined $order && $order !~ m/none|project|descr|owner|age/) {
 		die_error(400, "Unknown order parameter");
 	}
@@ -3971,17 +4414,20 @@
 	git_header_html();
 	if (-f $home_text) {
 		print "<div class=\"index_include\">\n";
-		open (my $fd, $home_text);
-		print <$fd>;
-		close $fd;
+		insert_file($home_text);
 		print "</div>\n";
 	}
+	print $cgi->startform(-method => "get") .
+	      "<p class=\"projsearch\">Search:\n" .
+	      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
+	      "</p>" .
+	      $cgi->end_form() . "\n";
 	git_project_list_body(\@list, $order);
 	git_footer_html();
 }
 
 sub git_forks {
-	my $order = $cgi->param('o');
+	my $order = $input_params{'order'};
 	if (defined $order && $order !~ m/none|project|descr|owner|age/) {
 		die_error(400, "Unknown order parameter");
 	}
@@ -4036,7 +4482,7 @@
 	my @taglist  = git_get_tags_list(16);
 	my @headlist = git_get_heads_list(16);
 	my @forklist;
-	my ($check_forks) = gitweb_check_feature('forks');
+	my $check_forks = gitweb_check_feature('forks');
 
 	if ($check_forks) {
 		@forklist = git_get_projects_list($project);
@@ -4047,10 +4493,10 @@
 
 	print "<div class=\"title\">&nbsp;</div>\n";
 	print "<table class=\"projects_list\">\n" .
-	      "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
-	      "<tr><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
+	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
 	if (defined $cd{'rfc2822'}) {
-		print "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
 	}
 
 	# use per project git URL list in $projectroot/$project/cloneurl
@@ -4060,19 +4506,32 @@
 	@url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
 	foreach my $git_url (@url_list) {
 		next unless $git_url;
-		print "<tr><td>$url_tag</td><td>$git_url</td></tr>\n";
+		print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
 		$url_tag = "";
 	}
+
+	# Tag cloud
+	my $show_ctags = gitweb_check_feature('ctags');
+	if ($show_ctags) {
+		my $ctags = git_get_project_ctags($project);
+		my $cloud = git_populate_project_tagcloud($ctags);
+		print "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
+		print "</td>\n<td>" unless %$ctags;
+		print "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
+		print "</td>\n<td>" if %$ctags;
+		print git_show_project_tagcloud($cloud, 48);
+		print "</td></tr>";
+	}
+
 	print "</table>\n";
 
-	if (-s "$projectroot/$project/README.html") {
-		if (open my $fd, "$projectroot/$project/README.html") {
-			print "<div class=\"title\">readme</div>\n" .
-			      "<div class=\"readme\">\n";
-			print $_ while (<$fd>);
-			print "\n</div>\n"; # class="readme"
-			close $fd;
-		}
+	# If XSS prevention is on, we don't include README.html.
+	# TODO: Allow a readme in some safe format.
+	if (!$prevent_xss && -s "$projectroot/$project/README.html") {
+		print "<div class=\"title\">readme</div>\n" .
+		      "<div class=\"readme\">\n";
+		insert_file("$projectroot/$project/README.html");
+		print "\n</div>\n"; # class="readme"
 	}
 
 	# we need to request one more than 16 (0..15) to check if
@@ -4101,10 +4560,10 @@
 
 	if (@forklist) {
 		git_print_header_div('forks');
-		git_project_list_body(\@forklist, undef, 0, 15,
+		git_project_list_body(\@forklist, 'age', 0, 15,
 		                      $#forklist <= 15 ? undef :
 		                      $cgi->a({-href => href(action=>"forks")}, "..."),
-		                      'noheader');
+		                      'no_header');
 	}
 
 	git_footer_html();
@@ -4150,28 +4609,33 @@
 }
 
 sub git_blame {
-	my $fd;
-	my $ftype;
-
+	# permissions
 	gitweb_check_feature('blame')
-	    or die_error(403, "Blame view not allowed");
+		or die_error(403, "Blame view not allowed");
 
+	# error checking
 	die_error(400, "No file name given") unless $file_name;
 	$hash_base ||= git_get_head_hash($project);
-	die_error(404, "Couldn't find base commit") unless ($hash_base);
+	die_error(404, "Couldn't find base commit") unless $hash_base;
 	my %co = parse_commit($hash_base)
 		or die_error(404, "Commit not found");
+	my $ftype = "blob";
 	if (!defined $hash) {
 		$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
 			or die_error(404, "Error looking up file");
+	} else {
+		$ftype = git_get_type($hash);
+		if ($ftype !~ "blob") {
+			die_error(400, "Object is not a blob");
+		}
 	}
-	$ftype = git_get_type($hash);
-	if ($ftype !~ "blob") {
-		die_error(400, "Object is not a blob");
-	}
-	open ($fd, "-|", git_cmd(), "blame", '-p', '--',
-	      $file_name, $hash_base)
+
+	# run git-blame --porcelain
+	open my $fd, "-|", git_cmd(), "blame", '-p',
+		$hash_base, '--', $file_name
 		or die_error(500, "Open git-blame failed");
+
+	# page header
 	git_header_html();
 	my $formats_nav =
 		$cgi->a({-href => href(action=>"blob", -replay=>1)},
@@ -4185,42 +4649,46 @@
 	git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 	git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 	git_print_page_path($file_name, $ftype, $hash_base);
-	my @rev_color = (qw(light2 dark2));
+
+	# page body
+	my @rev_color = qw(light2 dark2);
 	my $num_colors = scalar(@rev_color);
 	my $current_color = 0;
-	my $last_rev;
+	my %metainfo = ();
+
 	print <<HTML;
 <div class="page_body">
 <table class="blame">
 <tr><th>Commit</th><th>Line</th><th>Data</th></tr>
 HTML
-	my %metainfo = ();
-	while (1) {
-		$_ = <$fd>;
-		last unless defined $_;
+ LINE:
+	while (my $line = <$fd>) {
+		chomp $line;
+		# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
+		# no <lines in group> for subsequent lines in group of lines
 		my ($full_rev, $orig_lineno, $lineno, $group_size) =
-		    /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
+		   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
 		if (!exists $metainfo{$full_rev}) {
 			$metainfo{$full_rev} = {};
 		}
 		my $meta = $metainfo{$full_rev};
-		while (<$fd>) {
-			last if (s/^\t//);
-			if (/^(\S+) (.*)$/) {
+		my $data;
+		while ($data = <$fd>) {
+			chomp $data;
+			last if ($data =~ s/^\t//); # contents of line
+			if ($data =~ /^(\S+) (.*)$/) {
 				$meta->{$1} = $2;
 			}
 		}
-		my $data = $_;
-		chomp $data;
-		my $rev = substr($full_rev, 0, 8);
+		my $short_rev = substr($full_rev, 0, 8);
 		my $author = $meta->{'author'};
-		my %date = parse_date($meta->{'author-time'},
-		                      $meta->{'author-tz'});
+		my %date =
+			parse_date($meta->{'author-time'}, $meta->{'author-tz'});
 		my $date = $date{'iso-tz'};
 		if ($group_size) {
-			$current_color = ++$current_color % $num_colors;
+			$current_color = ($current_color + 1) % $num_colors;
 		}
-		print "<tr class=\"$rev_color[$current_color]\">\n";
+		print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
 		if ($group_size) {
 			print "<td class=\"sha1\"";
 			print " title=\"". esc_html($author) . ", $date\"";
@@ -4229,20 +4697,25 @@
 			print $cgi->a({-href => href(action=>"commit",
 			                             hash=>$full_rev,
 			                             file_name=>$file_name)},
-			              esc_html($rev));
+			              esc_html($short_rev));
 			print "</td>\n";
 		}
-		open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
-			or die_error(500, "Open git-rev-parse failed");
-		my $parent_commit = <$dd>;
-		close $dd;
-		chomp($parent_commit);
+		my $parent_commit;
+		if (!exists $meta->{'parent'}) {
+			open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
+				or die_error(500, "Open git-rev-parse failed");
+			$parent_commit = <$dd>;
+			close $dd;
+			chomp($parent_commit);
+			$meta->{'parent'} = $parent_commit;
+		} else {
+			$parent_commit = $meta->{'parent'};
+		}
 		my $blamed = href(action => 'blame',
 		                  file_name => $meta->{'filename'},
 		                  hash_base => $parent_commit);
 		print "<td class=\"linenr\">";
 		print $cgi->a({ -href => "$blamed#l$orig_lineno",
-		                -id => "l$lineno",
 		                -class => "linenr" },
 		              esc_html($lineno));
 		print "</td>";
@@ -4253,6 +4726,8 @@
 	print "</div>";
 	close $fd
 		or print "Reading blob failed\n";
+
+	# page footer
 	git_footer_html();
 }
 
@@ -4313,10 +4788,21 @@
 		$save_as .= '.txt';
 	}
 
+	# With XSS prevention on, blobs of all types except a few known safe
+	# ones are served with "Content-Disposition: attachment" to make sure
+	# they don't run in our security domain.  For certain image types,
+	# blob view writes an <img> tag referring to blob_plain view, and we
+	# want to be sure not to break that by serving the image as an
+	# attachment (though Firefox 3 doesn't seem to care).
+	my $sandbox = $prevent_xss &&
+		$type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;
+
 	print $cgi->header(
 		-type => $type,
 		-expires => $expires,
-		-content_disposition => 'inline; filename="' . $save_as . '"');
+		-content_disposition =>
+			($sandbox ? 'attachment' : 'inline')
+			. '; filename="' . $save_as . '"');
 	undef $/;
 	binmode STDOUT, ':raw';
 	print <$fd>;
@@ -4341,7 +4827,7 @@
 		$expires = "+1d";
 	}
 
-	my ($have_blame) = gitweb_check_feature('blame');
+	my $have_blame = gitweb_check_feature('blame');
 	open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
 		or die_error(500, "Couldn't cat $file_name, $hash");
 	my $mimetype = blob_mimetype($fd, $file_name);
@@ -4434,7 +4920,7 @@
 	my $ref = format_ref_marker($refs, $hash_base);
 	git_header_html();
 	my $basedir = '';
-	my ($have_blame) = gitweb_check_feature('blame');
+	my $have_blame = gitweb_check_feature('blame');
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
 		my @views_nav = ();
 		if (defined $file_name) {
@@ -4512,20 +4998,17 @@
 }
 
 sub git_snapshot {
-	my @supported_fmts = gitweb_check_feature('snapshot');
-	@supported_fmts = filter_snapshot_fmts(@supported_fmts);
-
-	my $format = $cgi->param('sf');
-	if (!@supported_fmts) {
+	my $format = $input_params{'snapshot_format'};
+	if (!@snapshot_fmts) {
 		die_error(403, "Snapshots not allowed");
 	}
 	# default to first supported snapshot format
-	$format ||= $supported_fmts[0];
+	$format ||= $snapshot_fmts[0];
 	if ($format !~ m/^[a-z0-9]+$/) {
 		die_error(400, "Invalid snapshot format parameter");
 	} elsif (!exists($known_snapshot_formats{$format})) {
 		die_error(400, "Unknown snapshot format");
-	} elsif (!grep($_ eq $format, @supported_fmts)) {
+	} elsif (!grep($_ eq $format, @snapshot_fmts)) {
 		die_error(403, "Unsupported snapshot format");
 	}
 
@@ -4575,6 +5058,15 @@
 
 	my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100);
 
+	my ($patch_max) = gitweb_get_feature('patches');
+	if ($patch_max) {
+		if ($patch_max < 0 || @commitlist <= $patch_max) {
+			$paging_nav .= " &sdot; " .
+				$cgi->a({-href => href(action=>"patches", -replay=>1)},
+					"patches");
+		}
+	}
+
 	git_header_html();
 	git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
 
@@ -4654,6 +5146,11 @@
 			} @$parents ) .
 			')';
 	}
+	if (gitweb_check_feature('patches')) {
+		$formats_nav .= " | " .
+			$cgi->a({-href => href(action=>"patch", -replay=>1)},
+				"patch");
+	}
 
 	if (!defined $parent) {
 		$parent = "--root";
@@ -4930,7 +5427,14 @@
 }
 
 sub git_commitdiff {
-	my $format = shift || 'html';
+	my %params = @_;
+	my $format = $params{-format} || 'html';
+
+	my ($patch_max) = gitweb_get_feature('patches');
+	if ($format eq 'patch') {
+		die_error(403, "Patch view not allowed") unless $patch_max;
+	}
+
 	$hash ||= $hash_base || "HEAD";
 	my %co = parse_commit($hash)
 	    or die_error(404, "Unknown commit object");
@@ -4945,6 +5449,11 @@
 		$formats_nav =
 			$cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
 			        "raw");
+		if ($patch_max) {
+			$formats_nav .= " | " .
+				$cgi->a({-href => href(action=>"patch", -replay=>1)},
+					"patch");
+		}
 
 		if (defined $hash_parent &&
 		    $hash_parent ne '-c' && $hash_parent ne '--cc') {
@@ -5028,7 +5537,31 @@
 		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
 			'-p', $hash_parent_param, $hash, "--"
 			or die_error(500, "Open git-diff-tree failed");
-
+	} elsif ($format eq 'patch') {
+		# For commit ranges, we limit the output to the number of
+		# patches specified in the 'patches' feature.
+		# For single commits, we limit the output to a single patch,
+		# diverging from the git-format-patch default.
+		my @commit_spec = ();
+		if ($hash_parent) {
+			if ($patch_max > 0) {
+				push @commit_spec, "-$patch_max";
+			}
+			push @commit_spec, '-n', "$hash_parent..$hash";
+		} else {
+			if ($params{-single}) {
+				push @commit_spec, '-1';
+			} else {
+				if ($patch_max > 0) {
+					push @commit_spec, "-$patch_max";
+				}
+				push @commit_spec, "-n";
+			}
+			push @commit_spec, '--root', $hash;
+		}
+		open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8',
+			'--stdout', @commit_spec
+			or die_error(500, "Open git-format-patch failed");
 	} else {
 		die_error(400, "Unknown commitdiff format");
 	}
@@ -5077,6 +5610,14 @@
 			print to_utf8($line) . "\n";
 		}
 		print "---\n\n";
+	} elsif ($format eq 'patch') {
+		my $filename = basename($project) . "-$hash.patch";
+
+		print $cgi->header(
+			-type => 'text/plain',
+			-charset => 'utf-8',
+			-expires => $expires,
+			-content_disposition => 'inline; filename="' . "$filename" . '"');
 	}
 
 	# write patch
@@ -5098,11 +5639,25 @@
 		print <$fd>;
 		close $fd
 			or print "Reading git-diff-tree failed\n";
+	} elsif ($format eq 'patch') {
+		local $/ = undef;
+		print <$fd>;
+		close $fd
+			or print "Reading git-format-patch failed\n";
 	}
 }
 
 sub git_commitdiff_plain {
-	git_commitdiff('plain');
+	git_commitdiff(-format => 'plain');
+}
+
+# format-patch-style patches
+sub git_patch {
+	git_commitdiff(-format => 'patch', -single=> 1);
+}
+
+sub git_patches {
+	git_commitdiff(-format => 'patch');
 }
 
 sub git_history {
@@ -5401,7 +5956,7 @@
 <dt><b>commit</b></dt>
 <dd>The commit messages and authorship information will be scanned for the given pattern.</dd>
 EOT
-	my ($have_grep) = gitweb_check_feature('grep');
+	my $have_grep = gitweb_check_feature('grep');
 	if ($have_grep) {
 		print <<EOT;
 <dt><b>grep</b></dt>
@@ -5418,7 +5973,7 @@
 <dt><b>committer</b></dt>
 <dd>Name and e-mail of the committer and date of commit will be scanned for the given pattern.</dd>
 EOT
-	my ($have_pickaxe) = gitweb_check_feature('pickaxe');
+	my $have_pickaxe = gitweb_check_feature('pickaxe');
 	if ($have_pickaxe) {
 		print <<EOT;
 <dt><b>pickaxe</b></dt>
@@ -5442,7 +5997,11 @@
 	}
 	my $refs = git_get_references();
 
-	my @commitlist = parse_commits($hash, 101, (100 * $page));
+	my $commit_hash = $hash;
+	if (defined $hash_parent) {
+		$commit_hash = "$hash_parent..$hash";
+	}
+	my @commitlist = parse_commits($commit_hash, 101, (100 * $page));
 
 	my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100);
 	my $next_link = '';
@@ -5451,6 +6010,14 @@
 			$cgi->a({-href => href(-replay=>1, page=>$page+1),
 			         -accesskey => "n", -title => "Alt-n"}, "next");
 	}
+	my $patch_max = gitweb_check_feature('patches');
+	if ($patch_max) {
+		if ($patch_max < 0 || @commitlist <= $patch_max) {
+			$paging_nav .= " &sdot; " .
+				$cgi->a({-href => href(action=>"patches", -replay=>1)},
+					"patches");
+		}
+	}
 
 	git_header_html();
 	git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
@@ -5466,7 +6033,7 @@
 
 sub git_feed {
 	my $format = shift || 'atom';
-	my ($have_blame) = gitweb_check_feature('blame');
+	my $have_blame = gitweb_check_feature('blame');
 
 	# Atom: http://www.atomenabled.org/developers/syndication/
 	# RSS:  http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
@@ -5488,7 +6055,25 @@
 	}
 	if (defined($commitlist[0])) {
 		%latest_commit = %{$commitlist[0]};
-		%latest_date   = parse_date($latest_commit{'author_epoch'});
+		my $latest_epoch = $latest_commit{'committer_epoch'};
+		%latest_date   = parse_date($latest_epoch);
+		my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
+		if (defined $if_modified) {
+			my $since;
+			if (eval { require HTTP::Date; 1; }) {
+				$since = HTTP::Date::str2time($if_modified);
+			} elsif (eval { require Time::ParseDate; 1; }) {
+				$since = Time::ParseDate::parsedate($if_modified, GMT => 1);
+			}
+			if (defined $since && $latest_epoch <= $since) {
+				print $cgi->header(
+					-type => $content_type,
+					-charset => 'utf-8',
+					-last_modified => $latest_date{'rfc2822'},
+					-status => '304 Not Modified');
+				return;
+			}
+		}
 		print $cgi->header(
 			-type => $content_type,
 			-charset => 'utf-8',
@@ -5547,7 +6132,24 @@
 		print "<title>$title</title>\n" .
 		      "<link>$alt_url</link>\n" .
 		      "<description>$descr</description>\n" .
-		      "<language>en</language>\n";
+		      "<language>en</language>\n" .
+		      # project owner is responsible for 'editorial' content
+		      "<managingEditor>$owner</managingEditor>\n";
+		if (defined $logo || defined $favicon) {
+			# prefer the logo to the favicon, since RSS
+			# doesn't allow both
+			my $img = esc_url($logo || $favicon);
+			print "<image>\n" .
+			      "<url>$img</url>\n" .
+			      "<title>$title</title>\n" .
+			      "<link>$alt_url</link>\n" .
+			      "</image>\n";
+		}
+		if (%latest_date) {
+			print "<pubDate>$latest_date{'rfc2822'}</pubDate>\n";
+			print "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n";
+		}
+		print "<generator>gitweb v.$version/$git_version</generator>\n";
 	} elsif ($format eq 'atom') {
 		print <<XML;
 <feed xmlns="http://www.w3.org/2005/Atom">
@@ -5574,6 +6176,7 @@
 		} else {
 			print "<updated>$latest_date{'iso-8601'}</updated>\n";
 		}
+		print "<generator version='$version/$git_version'>gitweb</generator>\n";
 	}
 
 	# contents
@@ -5695,7 +6298,11 @@
 sub git_opml {
 	my @list = git_get_projects_list();
 
-	print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
+	print $cgi->header(
+		-type => 'text/xml',
+		-charset => 'utf-8',
+		-content_disposition => 'inline; filename="opml.xml"');
+
 	print <<XML;
 <?xml version="1.0" encoding="utf-8"?>
 <opml version="1.0">
@@ -5719,8 +6326,8 @@
 		}
 
 		my $path = esc_html(chop_str($proj{'path'}, 25, 5));
-		my $rss  = "$my_url?p=$proj{'path'};a=rss";
-		my $html = "$my_url?p=$proj{'path'};a=summary";
+		my $rss  = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
+		my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
 		print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
 	}
 	print <<XML;
diff --git a/graph.c b/graph.c
index e2633f8..d4571cf 100644
--- a/graph.c
+++ b/graph.c
@@ -1,15 +1,49 @@
 #include "cache.h"
 #include "commit.h"
+#include "color.h"
 #include "graph.h"
 #include "diff.h"
 #include "revision.h"
 
+/* Internal API */
+
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Output a padding line in the graph.
+ * This is similar to graph_next_line().  However, it is guaranteed to
+ * never print the current commit line.  Instead, if the commit line is
+ * next, it will simply output a line of vertical padding, extending the
+ * branch lines downwards, but leaving them otherwise unchanged.
+ */
+static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
+ * first will be prefixed with the graph output.
+ *
+ * If the strbuf ends with a newline, the output will end after this
+ * newline.  A new graph line will not be printed after the final newline.
+ * If the strbuf is empty, no output will be printed.
+ *
+ * Since the first line will not include the graph ouput, the caller is
+ * responsible for printing this line's graph (perhaps via
+ * graph_show_commit() or graph_show_oneline()) before calling
+ * graph_show_strbuf().
+ */
+static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
+
 /*
  * TODO:
- * - Add colors to the graph.
- *   Pick a color for each column, and print all characters
- *   in that column with the specified color.
- *
  * - Limit the number of columns, similar to the way gitk does.
  *   If we reach more than a specified number of columns, omit
  *   sections of some columns.
@@ -35,9 +69,10 @@
 	 */
 	struct commit *commit;
 	/*
-	 * XXX: Once we add support for colors, struct column could also
-	 * contain the color of its branch line.
+	 * The color to (optionally) print this column in.  This is an
+	 * index into column_colors.
 	 */
+	unsigned short color;
 };
 
 enum graph_state {
@@ -49,6 +84,41 @@
 	GRAPH_COLLAPSING
 };
 
+/*
+ * The list of available column colors.
+ */
+static char column_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RED,
+	GIT_COLOR_GREEN,
+	GIT_COLOR_YELLOW,
+	GIT_COLOR_BLUE,
+	GIT_COLOR_MAGENTA,
+	GIT_COLOR_CYAN,
+	GIT_COLOR_BOLD GIT_COLOR_RED,
+	GIT_COLOR_BOLD GIT_COLOR_GREEN,
+	GIT_COLOR_BOLD GIT_COLOR_YELLOW,
+	GIT_COLOR_BOLD GIT_COLOR_BLUE,
+	GIT_COLOR_BOLD GIT_COLOR_MAGENTA,
+	GIT_COLOR_BOLD GIT_COLOR_CYAN,
+};
+
+#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
+
+static const char *column_get_color_code(const struct column *c)
+{
+	return column_colors[c->color];
+}
+
+static void strbuf_write_column(struct strbuf *sb, const struct column *c,
+				char col_char)
+{
+	if (c->color < COLUMN_COLORS_MAX)
+		strbuf_addstr(sb, column_get_color_code(c));
+	strbuf_addch(sb, col_char);
+	if (c->color < COLUMN_COLORS_MAX)
+		strbuf_addstr(sb, GIT_COLOR_RESET);
+}
+
 struct git_graph {
 	/*
 	 * The commit currently being processed
@@ -148,6 +218,11 @@
 	 * temporary array each time we have to output a collapsing line.
 	 */
 	int *new_mapping;
+	/*
+	 * The current default column color being used.  This is
+	 * stored as an index into the array column_colors.
+	 */
+	unsigned short default_column_color;
 };
 
 struct git_graph *graph_init(struct rev_info *opt)
@@ -164,6 +239,7 @@
 	graph->num_columns = 0;
 	graph->num_new_columns = 0;
 	graph->mapping_size = 0;
+	graph->default_column_color = 0;
 
 	/*
 	 * Allocate a reasonably large default number of columns
@@ -180,14 +256,6 @@
 	return graph;
 }
 
-void graph_release(struct git_graph *graph)
-{
-	free(graph->columns);
-	free(graph->new_columns);
-	free(graph->mapping);
-	free(graph);
-}
-
 static void graph_update_state(struct git_graph *graph, enum graph_state s)
 {
 	graph->prev_state = graph->state;
@@ -283,6 +351,33 @@
 	return next_interesting_parent(graph, parents);
 }
 
+static unsigned short graph_get_current_column_color(const struct git_graph *graph)
+{
+	if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
+		return COLUMN_COLORS_MAX;
+	return graph->default_column_color;
+}
+
+/*
+ * Update the graph's default column color.
+ */
+static void graph_increment_column_color(struct git_graph *graph)
+{
+	graph->default_column_color = (graph->default_column_color + 1) %
+		COLUMN_COLORS_MAX;
+}
+
+static unsigned short graph_find_commit_color(const struct git_graph *graph,
+					      const struct commit *commit)
+{
+	int i;
+	for (i = 0; i < graph->num_columns; i++) {
+		if (graph->columns[i].commit == commit)
+			return graph->columns[i].color;
+	}
+	return graph_get_current_column_color(graph);
+}
+
 static void graph_insert_into_new_columns(struct git_graph *graph,
 					  struct commit *commit,
 					  int *mapping_index)
@@ -305,6 +400,7 @@
 	 * This commit isn't already in new_columns.  Add it.
 	 */
 	graph->new_columns[graph->num_new_columns].commit = commit;
+	graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit);
 	graph->mapping[*mapping_index] = graph->num_new_columns;
 	*mapping_index += 2;
 	graph->num_new_columns++;
@@ -416,6 +512,12 @@
 			for (parent = first_interesting_parent(graph);
 			     parent;
 			     parent = next_interesting_parent(graph, parent)) {
+				/*
+				 * If this is a merge increment the current
+				 * color.
+				 */
+				if (graph->num_parents > 1)
+					graph_increment_column_color(graph);
 				graph_insert_into_new_columns(graph,
 							      parent->item,
 							      &mapping_idx);
@@ -531,7 +633,8 @@
 	return 1;
 }
 
-static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
+static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
+				   int chars_written)
 {
 	/*
 	 * Add additional spaces to the end of the strbuf, so that all
@@ -541,10 +644,10 @@
 	 * aligned for the entire commit.
 	 */
 	int extra;
-	if (sb->len >= graph->width)
+	if (chars_written >= graph->width)
 		return;
 
-	extra = graph->width - sb->len;
+	extra = graph->width - chars_written;
 	strbuf_addf(sb, "%*s", (int) extra, "");
 }
 
@@ -567,10 +670,11 @@
 	 * Output a padding row, that leaves all branch lines unchanged
 	 */
 	for (i = 0; i < graph->num_new_columns; i++) {
-		strbuf_addstr(sb, "| ");
+		strbuf_write_column(sb, &graph->new_columns[i], '|');
+		strbuf_addch(sb, ' ');
 	}
 
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
 }
 
 static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
@@ -580,7 +684,7 @@
 	 * of the graph is missing.
 	 */
 	strbuf_addstr(sb, "...");
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, 3);
 
 	if (graph->num_parents >= 3 &&
 	    graph->commit_index < (graph->num_columns - 1))
@@ -594,6 +698,7 @@
 {
 	int num_expansion_rows;
 	int i, seen_this;
+	int chars_written;
 
 	/*
 	 * This function formats a row that increases the space around a commit
@@ -616,11 +721,14 @@
 	 * Output the row
 	 */
 	seen_this = 0;
+	chars_written = 0;
 	for (i = 0; i < graph->num_columns; i++) {
 		struct column *col = &graph->columns[i];
 		if (col->commit == graph->commit) {
 			seen_this = 1;
-			strbuf_addf(sb, "| %*s", graph->expansion_row, "");
+			strbuf_write_column(sb, col, '|');
+			strbuf_addf(sb, " %*s", graph->expansion_row, "");
+			chars_written += 2 + graph->expansion_row;
 		} else if (seen_this && (graph->expansion_row == 0)) {
 			/*
 			 * This is the first line of the pre-commit output.
@@ -633,17 +741,22 @@
 			 */
 			if (graph->prev_state == GRAPH_POST_MERGE &&
 			    graph->prev_commit_index < i)
-				strbuf_addstr(sb, "\\ ");
+				strbuf_write_column(sb, col, '\\');
 			else
-				strbuf_addstr(sb, "| ");
+				strbuf_write_column(sb, col, '|');
+			chars_written++;
 		} else if (seen_this && (graph->expansion_row > 0)) {
-			strbuf_addstr(sb, "\\ ");
+			strbuf_write_column(sb, col, '\\');
+			chars_written++;
 		} else {
-			strbuf_addstr(sb, "| ");
+			strbuf_write_column(sb, col, '|');
+			chars_written++;
 		}
+		strbuf_addch(sb, ' ');
+		chars_written++;
 	}
 
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, chars_written);
 
 	/*
 	 * Increment graph->expansion_row,
@@ -685,10 +798,34 @@
 	strbuf_addch(sb, '*');
 }
 
-void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
+/*
+ * Draw an octopus merge and return the number of characters written.
+ */
+static int graph_draw_octopus_merge(struct git_graph *graph,
+				    struct strbuf *sb)
+{
+	/*
+	 * Here dashless_commits represents the number of parents
+	 * which don't need to have dashes (because their edges fit
+	 * neatly under the commit).
+	 */
+	const int dashless_commits = 2;
+	int col_num, i;
+	int num_dashes =
+		((graph->num_parents - dashless_commits) * 2) - 1;
+	for (i = 0; i < num_dashes; i++) {
+		col_num = (i / 2) + dashless_commits;
+		strbuf_write_column(sb, &graph->new_columns[col_num], '-');
+	}
+	col_num = (i / 2) + dashless_commits;
+	strbuf_write_column(sb, &graph->new_columns[col_num], '.');
+	return num_dashes + 1;
+}
+
+static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
 {
 	int seen_this = 0;
-	int i, j;
+	int i, chars_written;
 
 	/*
 	 * Output the row containing this commit
@@ -698,7 +835,9 @@
 	 * children that we have already processed.)
 	 */
 	seen_this = 0;
+	chars_written = 0;
 	for (i = 0; i <= graph->num_columns; i++) {
+		struct column *col = &graph->columns[i];
 		struct commit *col_commit;
 		if (i == graph->num_columns) {
 			if (seen_this)
@@ -711,18 +850,14 @@
 		if (col_commit == graph->commit) {
 			seen_this = 1;
 			graph_output_commit_char(graph, sb);
+			chars_written++;
 
-			if (graph->num_parents < 3)
-				strbuf_addch(sb, ' ');
-			else {
-				int num_dashes =
-					((graph->num_parents - 2) * 2) - 1;
-				for (j = 0; j < num_dashes; j++)
-					strbuf_addch(sb, '-');
-				strbuf_addstr(sb, ". ");
-			}
+			if (graph->num_parents > 3)
+				chars_written += graph_draw_octopus_merge(graph,
+									  sb);
 		} else if (seen_this && (graph->num_parents > 2)) {
-			strbuf_addstr(sb, "\\ ");
+			strbuf_write_column(sb, col, '\\');
+			chars_written++;
 		} else if (seen_this && (graph->num_parents == 2)) {
 			/*
 			 * This is a 2-way merge commit.
@@ -739,15 +874,19 @@
 			 */
 			if (graph->prev_state == GRAPH_POST_MERGE &&
 			    graph->prev_commit_index < i)
-				strbuf_addstr(sb, "\\ ");
+				strbuf_write_column(sb, col, '\\');
 			else
-				strbuf_addstr(sb, "| ");
+				strbuf_write_column(sb, col, '|');
+			chars_written++;
 		} else {
-			strbuf_addstr(sb, "| ");
+			strbuf_write_column(sb, col, '|');
+			chars_written++;
 		}
+		strbuf_addch(sb, ' ');
+		chars_written++;
 	}
 
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, chars_written);
 
 	/*
 	 * Update graph->state
@@ -760,37 +899,75 @@
 		graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
-void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
+static struct column *find_new_column_by_commit(struct git_graph *graph,
+						struct commit *commit)
+{
+	int i;
+	for (i = 0; i < graph->num_new_columns; i++) {
+		if (graph->new_columns[i].commit == commit)
+			return &graph->new_columns[i];
+	}
+	return 0;
+}
+
+static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
 {
 	int seen_this = 0;
-	int i, j;
+	int i, j, chars_written;
 
 	/*
 	 * Output the post-merge row
 	 */
+	chars_written = 0;
 	for (i = 0; i <= graph->num_columns; i++) {
+		struct column *col = &graph->columns[i];
 		struct commit *col_commit;
 		if (i == graph->num_columns) {
 			if (seen_this)
 				break;
 			col_commit = graph->commit;
 		} else {
-			col_commit = graph->columns[i].commit;
+			col_commit = col->commit;
 		}
 
 		if (col_commit == graph->commit) {
+			/*
+			 * Since the current commit is a merge find
+			 * the columns for the parent commits in
+			 * new_columns and use those to format the
+			 * edges.
+			 */
+			struct commit_list *parents = NULL;
+			struct column *par_column;
 			seen_this = 1;
-			strbuf_addch(sb, '|');
-			for (j = 0; j < graph->num_parents - 1; j++)
-				strbuf_addstr(sb, "\\ ");
+			parents = first_interesting_parent(graph);
+			assert(parents);
+			par_column = find_new_column_by_commit(graph, parents->item);
+			assert(par_column);
+
+			strbuf_write_column(sb, par_column, '|');
+			chars_written++;
+			for (j = 0; j < graph->num_parents - 1; j++) {
+				parents = next_interesting_parent(graph, parents);
+				assert(parents);
+				par_column = find_new_column_by_commit(graph, parents->item);
+				assert(par_column);
+				strbuf_write_column(sb, par_column, '\\');
+				strbuf_addch(sb, ' ');
+			}
+			chars_written += j * 2;
 		} else if (seen_this) {
-			strbuf_addstr(sb, "\\ ");
+			strbuf_write_column(sb, col, '\\');
+			strbuf_addch(sb, ' ');
+			chars_written += 2;
 		} else {
-			strbuf_addstr(sb, "| ");
+			strbuf_write_column(sb, col, '|');
+			strbuf_addch(sb, ' ');
+			chars_written += 2;
 		}
 	}
 
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, chars_written);
 
 	/*
 	 * Update graph->state
@@ -801,7 +978,7 @@
 		graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
-void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
 {
 	int i;
 	int *tmp_mapping;
@@ -883,12 +1060,12 @@
 		if (target < 0)
 			strbuf_addch(sb, ' ');
 		else if (target * 2 == i)
-			strbuf_addch(sb, '|');
+			strbuf_write_column(sb, &graph->new_columns[target], '|');
 		else
-			strbuf_addch(sb, '/');
+			strbuf_write_column(sb, &graph->new_columns[target], '/');
 	}
 
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, graph->mapping_size);
 
 	/*
 	 * Swap mapping and new_mapping
@@ -906,7 +1083,7 @@
 		graph_update_state(graph, GRAPH_PADDING);
 }
 
-int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
 	switch (graph->state) {
 	case GRAPH_PADDING:
@@ -933,7 +1110,7 @@
 	return 0;
 }
 
-void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
 {
 	int i, j;
 
@@ -950,9 +1127,10 @@
 	 * children that we have already processed.)
 	 */
 	for (i = 0; i < graph->num_columns; i++) {
-		struct commit *col_commit = graph->columns[i].commit;
+		struct column *col = &graph->columns[i];
+		struct commit *col_commit = col->commit;
 		if (col_commit == graph->commit) {
-			strbuf_addch(sb, '|');
+			strbuf_write_column(sb, col, '|');
 
 			if (graph->num_parents < 3)
 				strbuf_addch(sb, ' ');
@@ -962,11 +1140,12 @@
 					strbuf_addch(sb, ' ');
 			}
 		} else {
-			strbuf_addstr(sb, "| ");
+			strbuf_write_column(sb, col, '|');
+			strbuf_addch(sb, ' ');
 		}
 	}
 
-	graph_pad_horizontally(graph, sb);
+	graph_pad_horizontally(graph, sb, graph->num_columns);
 
 	/*
 	 * Update graph->prev_state since we have output a padding line
@@ -981,14 +1160,12 @@
 
 void graph_show_commit(struct git_graph *graph)
 {
-	struct strbuf msgbuf;
+	struct strbuf msgbuf = STRBUF_INIT;
 	int shown_commit_line = 0;
 
 	if (!graph)
 		return;
 
-	strbuf_init(&msgbuf, 0);
-
 	while (!shown_commit_line) {
 		shown_commit_line = graph_next_line(graph, &msgbuf);
 		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
@@ -1002,12 +1179,11 @@
 
 void graph_show_oneline(struct git_graph *graph)
 {
-	struct strbuf msgbuf;
+	struct strbuf msgbuf = STRBUF_INIT;
 
 	if (!graph)
 		return;
 
-	strbuf_init(&msgbuf, 0);
 	graph_next_line(graph, &msgbuf);
 	fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
 	strbuf_release(&msgbuf);
@@ -1015,12 +1191,11 @@
 
 void graph_show_padding(struct git_graph *graph)
 {
-	struct strbuf msgbuf;
+	struct strbuf msgbuf = STRBUF_INIT;
 
 	if (!graph)
 		return;
 
-	strbuf_init(&msgbuf, 0);
 	graph_padding_line(graph, &msgbuf);
 	fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
 	strbuf_release(&msgbuf);
@@ -1028,7 +1203,7 @@
 
 int graph_show_remainder(struct git_graph *graph)
 {
-	struct strbuf msgbuf;
+	struct strbuf msgbuf = STRBUF_INIT;
 	int shown = 0;
 
 	if (!graph)
@@ -1037,7 +1212,6 @@
 	if (graph_is_commit_finished(graph))
 		return 0;
 
-	strbuf_init(&msgbuf, 0);
 	for (;;) {
 		graph_next_line(graph, &msgbuf);
 		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
@@ -1055,7 +1229,7 @@
 }
 
 
-void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
+static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
 {
 	char *p;
 
diff --git a/graph.h b/graph.h
index eab4e3d..bc30d68 100644
--- a/graph.h
+++ b/graph.h
@@ -11,11 +11,6 @@
 struct git_graph *graph_init(struct rev_info *opt);
 
 /*
- * Destroy a struct git_graph and free associated memory.
- */
-void graph_release(struct git_graph *graph);
-
-/*
  * Update a git_graph with a new commit.
  * This will cause the graph to begin outputting lines for the new commit
  * the next time graph_next_line() is called.
@@ -27,26 +22,6 @@
 void graph_update(struct git_graph *graph, struct commit *commit);
 
 /*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf.  It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
-/*
- * Output a padding line in the graph.
- * This is similar to graph_next_line().  However, it is guaranteed to
- * never print the current commit line.  Instead, if the commit line is
- * next, it will simply output a line of vertical padding, extending the
- * branch lines downwards, but leaving them otherwise unchanged.
- */
-void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
-
-/*
  * Determine if a graph has finished outputting lines for the current
  * commit.
  *
@@ -90,21 +65,6 @@
 int graph_show_remainder(struct git_graph *graph);
 
 /*
- * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
- * first will be prefixed with the graph output.
- *
- * If the strbuf ends with a newline, the output will end after this
- * newline.  A new graph line will not be printed after the final newline.
- * If the strbuf is empty, no output will be printed.
- *
- * Since the first line will not include the graph ouput, the caller is
- * responsible for printing this line's graph (perhaps via
- * graph_show_commit() or graph_show_oneline()) before calling
- * graph_show_strbuf().
- */
-void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
-
-/*
  * Print a commit message strbuf and the remainder of the graph to stdout.
  *
  * This is similar to graph_show_strbuf(), but it always prints the
diff --git a/grep.c b/grep.c
index 13c18ff..f3a27d7 100644
--- a/grep.c
+++ b/grep.c
@@ -28,9 +28,27 @@
 	p->next = NULL;
 }
 
+static int is_fixed(const char *s)
+{
+	while (*s && !is_regex_special(*s))
+		s++;
+	return !*s;
+}
+
 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
-	int err = regcomp(&p->regexp, p->pattern, opt->regflags);
+	int err;
+
+	p->word_regexp = opt->word_regexp;
+
+	if (opt->fixed || is_fixed(p->pattern))
+		p->fixed = 1;
+	if (opt->regflags & REG_ICASE)
+		p->fixed = 0;
+	if (p->fixed)
+		return;
+
+	err = regcomp(&p->regexp, p->pattern, opt->regflags);
 	if (err) {
 		char errbuf[1024];
 		char where[1024];
@@ -159,8 +177,7 @@
 		case GREP_PATTERN: /* atom */
 		case GREP_PATTERN_HEAD:
 		case GREP_PATTERN_BODY:
-			if (!opt->fixed)
-				compile_regexp(p, opt);
+			compile_regexp(p, opt);
 			break;
 		default:
 			opt->extended = 1;
@@ -175,7 +192,8 @@
 	 * A classic recursive descent parser would do.
 	 */
 	p = opt->pattern_list;
-	opt->pattern_expression = compile_pattern_expr(&p);
+	if (p)
+		opt->pattern_expression = compile_pattern_expr(&p);
 	if (p)
 		die("incomplete pattern expression: %s", p->pattern);
 }
@@ -236,14 +254,9 @@
 	return isalnum(ch) || ch == '_';
 }
 
-static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
-		      const char *name, unsigned lno, char sign)
+static void show_name(struct grep_opt *opt, const char *name)
 {
-	if (opt->pathname)
-		printf("%s%c", name, sign);
-	if (opt->linenum)
-		printf("%d%c", lno, sign);
-	printf("%.*s\n", (int)(eol-bol), bol);
+	printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
 }
 
 static int fixmatch(const char *pattern, char *line, regmatch_t *match)
@@ -284,12 +297,12 @@
 	{ "committer ", 10 },
 };
 
-static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx)
+static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
+			     enum grep_context ctx,
+			     regmatch_t *pmatch, int eflags)
 {
 	int hit = 0;
-	int at_true_bol = 1;
 	int saved_ch = 0;
-	regmatch_t pmatch[10];
 
 	if ((p->token != GREP_PATTERN) &&
 	    ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
@@ -308,16 +321,12 @@
 	}
 
  again:
-	if (!opt->fixed) {
-		regex_t *exp = &p->regexp;
-		hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-			       pmatch, 0);
-	}
-	else {
+	if (p->fixed)
 		hit = !fixmatch(p->pattern, bol, pmatch);
-	}
+	else
+		hit = !regexec(&p->regexp, bol, 1, pmatch, eflags);
 
-	if (hit && opt->word_regexp) {
+	if (hit && p->word_regexp) {
 		if ((pmatch[0].rm_so < 0) ||
 		    (eol - bol) <= pmatch[0].rm_so ||
 		    (pmatch[0].rm_eo < 0) ||
@@ -330,7 +339,7 @@
 		 * either end of the line, or at word boundary
 		 * (i.e. the next char must not be a word char).
 		 */
-		if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
+		if ( ((pmatch[0].rm_so == 0) ||
 		      !word_char(bol[pmatch[0].rm_so-1])) &&
 		     ((pmatch[0].rm_eo == (eol-bol)) ||
 		      !word_char(bol[pmatch[0].rm_eo])) )
@@ -342,10 +351,14 @@
 			/* There could be more than one match on the
 			 * line, and the first match might not be
 			 * strict word match.  But later ones could be!
+			 * Forward to the next possible start, i.e. the
+			 * next position following a non-word char.
 			 */
 			bol = pmatch[0].rm_so + bol + 1;
-			at_true_bol = 0;
-			goto again;
+			while (word_char(bol[-1]) && bol < eol)
+				bol++;
+			if (bol < eol)
+				goto again;
 		}
 	}
 	if (p->token == GREP_PATTERN_HEAD && saved_ch)
@@ -353,42 +366,36 @@
 	return hit;
 }
 
-static int match_expr_eval(struct grep_opt *o,
-			   struct grep_expr *x,
-			   char *bol, char *eol,
-			   enum grep_context ctx,
-			   int collect_hits)
+static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
+			   enum grep_context ctx, int collect_hits)
 {
 	int h = 0;
+	regmatch_t match;
 
 	switch (x->node) {
 	case GREP_NODE_ATOM:
-		h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
+		h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
 		break;
 	case GREP_NODE_NOT:
-		h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
+		h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);
 		break;
 	case GREP_NODE_AND:
-		if (!collect_hits)
-			return (match_expr_eval(o, x->u.binary.left,
-						bol, eol, ctx, 0) &&
-				match_expr_eval(o, x->u.binary.right,
-						bol, eol, ctx, 0));
-		h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
-		h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
+		if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0))
+			return 0;
+		h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0);
 		break;
 	case GREP_NODE_OR:
 		if (!collect_hits)
-			return (match_expr_eval(o, x->u.binary.left,
+			return (match_expr_eval(x->u.binary.left,
 						bol, eol, ctx, 0) ||
-				match_expr_eval(o, x->u.binary.right,
+				match_expr_eval(x->u.binary.right,
 						bol, eol, ctx, 0));
-		h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+		h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0);
 		x->u.binary.left->hit |= h;
-		h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
+		h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1);
 		break;
 	default:
-		die("Unexpected node type (internal error) %d\n", x->node);
+		die("Unexpected node type (internal error) %d", x->node);
 	}
 	if (collect_hits)
 		x->hit |= h;
@@ -399,24 +406,104 @@
 		      enum grep_context ctx, int collect_hits)
 {
 	struct grep_expr *x = opt->pattern_expression;
-	return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
+	return match_expr_eval(x, bol, eol, ctx, collect_hits);
 }
 
 static int match_line(struct grep_opt *opt, char *bol, char *eol,
 		      enum grep_context ctx, int collect_hits)
 {
 	struct grep_pat *p;
+	regmatch_t match;
+
 	if (opt->extended)
 		return match_expr(opt, bol, eol, ctx, collect_hits);
 
 	/* we do not call with collect_hits without being extended */
 	for (p = opt->pattern_list; p; p = p->next) {
-		if (match_one_pattern(opt, p, bol, eol, ctx))
+		if (match_one_pattern(p, bol, eol, ctx, &match, 0))
 			return 1;
 	}
 	return 0;
 }
 
+static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
+			      enum grep_context ctx,
+			      regmatch_t *pmatch, int eflags)
+{
+	regmatch_t match;
+
+	if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
+		return 0;
+	if (match.rm_so < 0 || match.rm_eo < 0)
+		return 0;
+	if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) {
+		if (match.rm_so > pmatch->rm_so)
+			return 1;
+		if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo)
+			return 1;
+	}
+	pmatch->rm_so = match.rm_so;
+	pmatch->rm_eo = match.rm_eo;
+	return 1;
+}
+
+static int next_match(struct grep_opt *opt, char *bol, char *eol,
+		      enum grep_context ctx, regmatch_t *pmatch, int eflags)
+{
+	struct grep_pat *p;
+	int hit = 0;
+
+	pmatch->rm_so = pmatch->rm_eo = -1;
+	if (bol < eol) {
+		for (p = opt->pattern_list; p; p = p->next) {
+			switch (p->token) {
+			case GREP_PATTERN: /* atom */
+			case GREP_PATTERN_HEAD:
+			case GREP_PATTERN_BODY:
+				hit |= match_next_pattern(p, bol, eol, ctx,
+							  pmatch, eflags);
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	return hit;
+}
+
+static void show_line(struct grep_opt *opt, char *bol, char *eol,
+		      const char *name, unsigned lno, char sign)
+{
+	int rest = eol - bol;
+
+	if (opt->null_following_name)
+		sign = '\0';
+	if (opt->pathname)
+		printf("%s%c", name, sign);
+	if (opt->linenum)
+		printf("%d%c", lno, sign);
+	if (opt->color) {
+		regmatch_t match;
+		enum grep_context ctx = GREP_CONTEXT_BODY;
+		int ch = *eol;
+		int eflags = 0;
+
+		*eol = '\0';
+		while (next_match(opt, bol, eol, ctx, &match, eflags)) {
+			printf("%.*s%s%.*s%s",
+			       (int)match.rm_so, bol,
+			       opt->color_match,
+			       (int)(match.rm_eo - match.rm_so), bol + match.rm_so,
+			       GIT_COLOR_RESET);
+			bol += match.rm_eo;
+			rest -= match.rm_eo;
+			eflags = REG_NOTBOL;
+		}
+		*eol = ch;
+	}
+	printf("%.*s\n", rest, bol);
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
 			 char *buf, unsigned long size, int collect_hits)
 {
@@ -489,7 +576,7 @@
 				return 1;
 			}
 			if (opt->name_only) {
-				printf("%s\n", name);
+				show_name(opt, name);
 				return 1;
 			}
 			/* Hit at this line.  If we haven't shown the
@@ -555,7 +642,7 @@
 		return 0;
 	if (opt->unmatch_name_only) {
 		/* We did not see any hit, so we want to show this */
-		printf("%s\n", name);
+		show_name(opt, name);
 		return 1;
 	}
 
@@ -565,7 +652,8 @@
 	 * make it another option?  For now suppress them.
 	 */
 	if (opt->count && count)
-		printf("%s:%u\n", name, count);
+		printf("%s%c%u\n", name,
+		       opt->null_following_name ? '\0' : ':', count);
 	return !!last_hit;
 }
 
diff --git a/grep.h b/grep.h
index 59b3f87..a67005d 100644
--- a/grep.h
+++ b/grep.h
@@ -1,5 +1,6 @@
 #ifndef GREP_H
 #define GREP_H
+#include "color.h"
 
 enum grep_pat_token {
 	GREP_PATTERN,
@@ -30,6 +31,8 @@
 	const char *pattern;
 	enum grep_header_field field;
 	regex_t regexp;
+	unsigned fixed:1;
+	unsigned word_regexp:1;
 };
 
 enum grep_expr_node {
@@ -74,6 +77,10 @@
 	unsigned extended:1;
 	unsigned relative:1;
 	unsigned pathname:1;
+	unsigned null_following_name:1;
+	int color;
+	char color_match[COLOR_MAXLEN];
+	const char *color_external;
 	int regflags;
 	unsigned pre_context;
 	unsigned post_context;
diff --git a/hash-object.c b/hash-object.c
index 46c06a9..ebb3bed 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -7,16 +7,15 @@
 #include "cache.h"
 #include "blob.h"
 #include "quote.h"
+#include "parse-options.h"
+#include "exec_cmd.h"
 
-static void hash_object(const char *path, enum object_type type, int write_object)
+static void hash_fd(int fd, const char *type, int write_object, const char *path)
 {
-	int fd;
 	struct stat st;
 	unsigned char sha1[20];
-	fd = open(path, O_RDONLY);
-	if (fd < 0 ||
-	    fstat(fd, &st) < 0 ||
-	    index_fd(sha1, fd, &st, write_object, type, path))
+	if (fstat(fd, &st) < 0 ||
+	    index_fd(sha1, fd, &st, write_object, type_from_string(type), path))
 		die(write_object
 		    ? "Unable to add %s to database"
 		    : "Unable to hash %s", path);
@@ -24,20 +23,20 @@
 	maybe_flush_or_die(stdout, "hash to stdout");
 }
 
-static void hash_stdin(const char *type, int write_object)
+static void hash_object(const char *path, const char *type, int write_object,
+			const char *vpath)
 {
-	unsigned char sha1[20];
-	if (index_pipe(sha1, 0, type, write_object))
-		die("Unable to add stdin to database");
-	printf("%s\n", sha1_to_hex(sha1));
+	int fd;
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		die("Cannot open %s", path);
+	hash_fd(fd, type, write_object, vpath);
 }
 
 static void hash_stdin_paths(const char *type, int write_objects)
 {
-	struct strbuf buf, nbuf;
+	struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
 
-	strbuf_init(&buf, 0);
-	strbuf_init(&nbuf, 0);
 	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
 		if (buf.buf[0] == '"') {
 			strbuf_reset(&nbuf);
@@ -45,92 +44,93 @@
 				die("line is badly quoted");
 			strbuf_swap(&buf, &nbuf);
 		}
-		hash_object(buf.buf, type_from_string(type), write_objects);
+		hash_object(buf.buf, type, write_objects, buf.buf);
 	}
 	strbuf_release(&buf);
 	strbuf_release(&nbuf);
 }
 
-static const char hash_object_usage[] =
-"git hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]";
+static const char * const hash_object_usage[] = {
+	"git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...",
+	"git hash-object  --stdin-paths < <list-of-paths>",
+	NULL
+};
 
-int main(int argc, char **argv)
+static const char *type;
+static int write_object;
+static int hashstdin;
+static int stdin_paths;
+static int no_filters;
+static const char *vpath;
+
+static const struct option hash_object_options[] = {
+	OPT_STRING('t', NULL, &type, "type", "object type"),
+	OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"),
+	OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"),
+	OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"),
+	OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"),
+	OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"),
+	OPT_END()
+};
+
+int main(int argc, const char **argv)
 {
 	int i;
-	const char *type = blob_type;
-	int write_object = 0;
 	const char *prefix = NULL;
 	int prefix_length = -1;
-	int no_more_flags = 0;
-	int hashstdin = 0;
-	int stdin_paths = 0;
+	const char *errstr = NULL;
+
+	type = blob_type;
+
+	git_extract_argv0_path(argv[0]);
+
+	argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0);
+
+	if (write_object) {
+		prefix = setup_git_directory();
+		prefix_length = prefix ? strlen(prefix) : 0;
+		if (vpath && prefix)
+			vpath = prefix_filename(prefix, prefix_length, vpath);
+	}
 
 	git_config(git_default_config, NULL);
 
-	for (i = 1 ; i < argc; i++) {
-		if (!no_more_flags && argv[i][0] == '-') {
-			if (!strcmp(argv[i], "-t")) {
-				if (argc <= ++i)
-					usage(hash_object_usage);
-				type = argv[i];
-			}
-			else if (!strcmp(argv[i], "-w")) {
-				if (prefix_length < 0) {
-					prefix = setup_git_directory();
-					prefix_length =
-						prefix ? strlen(prefix) : 0;
-				}
-				write_object = 1;
-			}
-			else if (!strcmp(argv[i], "--")) {
-				no_more_flags = 1;
-			}
-			else if (!strcmp(argv[i], "--help"))
-				usage(hash_object_usage);
-			else if (!strcmp(argv[i], "--stdin-paths")) {
-				if (hashstdin) {
-					error("Can't use --stdin-paths with --stdin");
-					usage(hash_object_usage);
-				}
-				stdin_paths = 1;
+	if (stdin_paths) {
+		if (hashstdin)
+			errstr = "Can't use --stdin-paths with --stdin";
+		else if (argc)
+			errstr = "Can't specify files with --stdin-paths";
+		else if (vpath)
+			errstr = "Can't use --stdin-paths with --path";
+		else if (no_filters)
+			errstr = "Can't use --stdin-paths with --no-filters";
+	}
+	else {
+		if (hashstdin > 1)
+			errstr = "Multiple --stdin arguments are not supported";
+		if (vpath && no_filters)
+			errstr = "Can't use --path with --no-filters";
+	}
 
-			}
-			else if (!strcmp(argv[i], "--stdin")) {
-				if (stdin_paths) {
-					error("Can't use %s with --stdin-paths", argv[i]);
-					usage(hash_object_usage);
-				}
-				if (hashstdin)
-					die("Multiple --stdin arguments are not supported");
-				hashstdin = 1;
-			}
-			else
-				usage(hash_object_usage);
-		}
-		else {
-			const char *arg = argv[i];
+	if (errstr) {
+		error("%s", errstr);
+		usage_with_options(hash_object_usage, hash_object_options);
+	}
 
-			if (stdin_paths) {
-				error("Can't specify files (such as \"%s\") with --stdin-paths", arg);
-				usage(hash_object_usage);
-			}
+	if (hashstdin)
+		hash_fd(0, type, write_object, vpath);
 
-			if (hashstdin) {
-				hash_stdin(type, write_object);
-				hashstdin = 0;
-			}
-			if (0 <= prefix_length)
-				arg = prefix_filename(prefix, prefix_length,
-						      arg);
-			hash_object(arg, type_from_string(type), write_object);
-			no_more_flags = 1;
-		}
+	for (i = 0 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (0 <= prefix_length)
+			arg = prefix_filename(prefix, prefix_length, arg);
+		hash_object(arg, type, write_object,
+			    no_filters ? NULL : vpath ? vpath : arg);
 	}
 
 	if (stdin_paths)
 		hash_stdin_paths(type, write_object);
 
-	if (hashstdin)
-		hash_stdin(type, write_object);
 	return 0;
 }
diff --git a/help.c b/help.c
index dc0786d..fd87bb5 100644
--- a/help.c
+++ b/help.c
@@ -1,276 +1,8 @@
-/*
- * builtin-help.c
- *
- * Builtin help-related commands (help, usage, version)
- */
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
-#include "common-cmds.h"
-#include "parse-options.h"
-#include "run-command.h"
-
-static struct man_viewer_list {
-	struct man_viewer_list *next;
-	char name[FLEX_ARRAY];
-} *man_viewer_list;
-
-static struct man_viewer_info_list {
-	struct man_viewer_info_list *next;
-	const char *info;
-	char name[FLEX_ARRAY];
-} *man_viewer_info_list;
-
-enum help_format {
-	HELP_FORMAT_MAN,
-	HELP_FORMAT_INFO,
-	HELP_FORMAT_WEB,
-};
-
-static int show_all = 0;
-static enum help_format help_format = HELP_FORMAT_MAN;
-static struct option builtin_help_options[] = {
-	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
-	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
-	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
-			HELP_FORMAT_WEB),
-	OPT_SET_INT('i', "info", &help_format, "show info page",
-			HELP_FORMAT_INFO),
-	OPT_END(),
-};
-
-static const char * const builtin_help_usage[] = {
-	"git help [--all] [--man|--web|--info] [command]",
-	NULL
-};
-
-static enum help_format parse_help_format(const char *format)
-{
-	if (!strcmp(format, "man"))
-		return HELP_FORMAT_MAN;
-	if (!strcmp(format, "info"))
-		return HELP_FORMAT_INFO;
-	if (!strcmp(format, "web") || !strcmp(format, "html"))
-		return HELP_FORMAT_WEB;
-	die("unrecognized help format '%s'", format);
-}
-
-static const char *get_man_viewer_info(const char *name)
-{
-	struct man_viewer_info_list *viewer;
-
-	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
-	{
-		if (!strcasecmp(name, viewer->name))
-			return viewer->info;
-	}
-	return NULL;
-}
-
-static int check_emacsclient_version(void)
-{
-	struct strbuf buffer = STRBUF_INIT;
-	struct child_process ec_process;
-	const char *argv_ec[] = { "emacsclient", "--version", NULL };
-	int version;
-
-	/* emacsclient prints its version number on stderr */
-	memset(&ec_process, 0, sizeof(ec_process));
-	ec_process.argv = argv_ec;
-	ec_process.err = -1;
-	ec_process.stdout_to_stderr = 1;
-	if (start_command(&ec_process)) {
-		fprintf(stderr, "Failed to start emacsclient.\n");
-		return -1;
-	}
-	strbuf_read(&buffer, ec_process.err, 20);
-	close(ec_process.err);
-
-	/*
-	 * Don't bother checking return value, because "emacsclient --version"
-	 * seems to always exits with code 1.
-	 */
-	finish_command(&ec_process);
-
-	if (prefixcmp(buffer.buf, "emacsclient")) {
-		fprintf(stderr, "Failed to parse emacsclient version.\n");
-		strbuf_release(&buffer);
-		return -1;
-	}
-
-	strbuf_remove(&buffer, 0, strlen("emacsclient"));
-	version = atoi(buffer.buf);
-
-	if (version < 22) {
-		fprintf(stderr,
-			"emacsclient version '%d' too old (< 22).\n",
-			version);
-		strbuf_release(&buffer);
-		return -1;
-	}
-
-	strbuf_release(&buffer);
-	return 0;
-}
-
-static void exec_woman_emacs(const char* path, const char *page)
-{
-	if (!check_emacsclient_version()) {
-		/* This works only with emacsclient version >= 22. */
-		struct strbuf man_page = STRBUF_INIT;
-
-		if (!path)
-			path = "emacsclient";
-		strbuf_addf(&man_page, "(woman \"%s\")", page);
-		execlp(path, "emacsclient", "-e", man_page.buf, NULL);
-		warning("failed to exec '%s': %s", path, strerror(errno));
-	}
-}
-
-static void exec_man_konqueror(const char* path, const char *page)
-{
-	const char *display = getenv("DISPLAY");
-	if (display && *display) {
-		struct strbuf man_page = STRBUF_INIT;
-		const char *filename = "kfmclient";
-
-		/* It's simpler to launch konqueror using kfmclient. */
-		if (path) {
-			const char *file = strrchr(path, '/');
-			if (file && !strcmp(file + 1, "konqueror")) {
-				char *new = xstrdup(path);
-				char *dest = strrchr(new, '/');
-
-				/* strlen("konqueror") == strlen("kfmclient") */
-				strcpy(dest + 1, "kfmclient");
-				path = new;
-			}
-			if (file)
-				filename = file;
-		} else
-			path = "kfmclient";
-		strbuf_addf(&man_page, "man:%s(1)", page);
-		execlp(path, filename, "newTab", man_page.buf, NULL);
-		warning("failed to exec '%s': %s", path, strerror(errno));
-	}
-}
-
-static void exec_man_man(const char* path, const char *page)
-{
-	if (!path)
-		path = "man";
-	execlp(path, "man", page, NULL);
-	warning("failed to exec '%s': %s", path, strerror(errno));
-}
-
-static void exec_man_cmd(const char *cmd, const char *page)
-{
-	struct strbuf shell_cmd = STRBUF_INIT;
-	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
-	execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
-	warning("failed to exec '%s': %s", cmd, strerror(errno));
-}
-
-static void add_man_viewer(const char *name)
-{
-	struct man_viewer_list **p = &man_viewer_list;
-	size_t len = strlen(name);
-
-	while (*p)
-		p = &((*p)->next);
-	*p = xcalloc(1, (sizeof(**p) + len + 1));
-	strncpy((*p)->name, name, len);
-}
-
-static int supported_man_viewer(const char *name, size_t len)
-{
-	return (!strncasecmp("man", name, len) ||
-		!strncasecmp("woman", name, len) ||
-		!strncasecmp("konqueror", name, len));
-}
-
-static void do_add_man_viewer_info(const char *name,
-				   size_t len,
-				   const char *value)
-{
-	struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
-
-	strncpy(new->name, name, len);
-	new->info = xstrdup(value);
-	new->next = man_viewer_info_list;
-	man_viewer_info_list = new;
-}
-
-static int add_man_viewer_path(const char *name,
-			       size_t len,
-			       const char *value)
-{
-	if (supported_man_viewer(name, len))
-		do_add_man_viewer_info(name, len, value);
-	else
-		warning("'%s': path for unsupported man viewer.\n"
-			"Please consider using 'man.<tool>.cmd' instead.",
-			name);
-
-	return 0;
-}
-
-static int add_man_viewer_cmd(const char *name,
-			      size_t len,
-			      const char *value)
-{
-	if (supported_man_viewer(name, len))
-		warning("'%s': cmd for supported man viewer.\n"
-			"Please consider using 'man.<tool>.path' instead.",
-			name);
-	else
-		do_add_man_viewer_info(name, len, value);
-
-	return 0;
-}
-
-static int add_man_viewer_info(const char *var, const char *value)
-{
-	const char *name = var + 4;
-	const char *subkey = strrchr(name, '.');
-
-	if (!subkey)
-		return error("Config with no key for man viewer: %s", name);
-
-	if (!strcmp(subkey, ".path")) {
-		if (!value)
-			return config_error_nonbool(var);
-		return add_man_viewer_path(name, subkey - name, value);
-	}
-	if (!strcmp(subkey, ".cmd")) {
-		if (!value)
-			return config_error_nonbool(var);
-		return add_man_viewer_cmd(name, subkey - name, value);
-	}
-
-	warning("'%s': unsupported man viewer sub key.", subkey);
-	return 0;
-}
-
-static int git_help_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "help.format")) {
-		if (!value)
-			return config_error_nonbool(var);
-		help_format = parse_help_format(value);
-		return 0;
-	}
-	if (!strcmp(var, "man.viewer")) {
-		if (!value)
-			return config_error_nonbool(var);
-		add_man_viewer(value);
-		return 0;
-	}
-	if (!prefixcmp(var, "man."))
-		return add_man_viewer_info(var, value);
-
-	return git_default_config(var, value, cb);
-}
+#include "levenshtein.h"
+#include "help.h"
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
@@ -294,24 +26,9 @@
 	return 80;
 }
 
-static inline void mput_char(char c, unsigned int num)
+void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
-	while(num--)
-		putchar(c);
-}
-
-static struct cmdnames {
-	int alloc;
-	int cnt;
-	struct cmdname {
-		size_t len;
-		char name[1];
-	} **names;
-} main_cmds, other_cmds;
-
-static void add_cmdname(struct cmdnames *cmds, const char *name, int len)
-{
-	struct cmdname *ent = xmalloc(sizeof(*ent) + len);
+	struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1);
 
 	ent->len = len;
 	memcpy(ent->name, name, len);
@@ -321,6 +38,16 @@
 	cmds->names[cmds->cnt++] = ent;
 }
 
+static void clean_cmdnames(struct cmdnames *cmds)
+{
+	int i;
+	for (i = 0; i < cmds->cnt; ++i)
+		free(cmds->names[i]);
+	free(cmds->names);
+	cmds->cnt = 0;
+	cmds->alloc = 0;
+}
+
 static int cmdname_compare(const void *a_, const void *b_)
 {
 	struct cmdname *a = *(struct cmdname **)a_;
@@ -342,7 +69,7 @@
 	cmds->cnt = j;
 }
 
-static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
 {
 	int ci, cj, ei;
 	int cmp;
@@ -417,19 +144,21 @@
 	return st.st_mode & S_IXUSR;
 }
 
-static unsigned int list_commands_in_dir(struct cmdnames *cmds,
-					 const char *path)
+static void list_commands_in_dir(struct cmdnames *cmds,
+					 const char *path,
+					 const char *prefix)
 {
-	unsigned int longest = 0;
-	const char *prefix = "git-";
-	int prefix_len = strlen(prefix);
+	int prefix_len;
 	DIR *dir = opendir(path);
 	struct dirent *de;
 	struct strbuf buf = STRBUF_INIT;
 	int len;
 
 	if (!dir)
-		return 0;
+		return;
+	if (!prefix)
+		prefix = "git-";
+	prefix_len = strlen(prefix);
 
 	strbuf_addf(&buf, "%s/", path);
 	len = buf.len;
@@ -449,100 +178,81 @@
 		if (has_extension(de->d_name, ".exe"))
 			entlen -= 4;
 
-		if (longest < entlen)
-			longest = entlen;
-
 		add_cmdname(cmds, de->d_name + prefix_len, entlen);
 	}
 	closedir(dir);
 	strbuf_release(&buf);
-
-	return longest;
 }
 
-static unsigned int load_command_list(void)
+void load_command_list(const char *prefix,
+		struct cmdnames *main_cmds,
+		struct cmdnames *other_cmds)
 {
-	unsigned int longest = 0;
-	unsigned int len;
 	const char *env_path = getenv("PATH");
-	char *paths, *path, *colon;
 	const char *exec_path = git_exec_path();
 
-	if (exec_path)
-		longest = list_commands_in_dir(&main_cmds, exec_path);
-
-	if (!env_path) {
-		fprintf(stderr, "PATH not set\n");
-		exit(1);
+	if (exec_path) {
+		list_commands_in_dir(main_cmds, exec_path, prefix);
+		qsort(main_cmds->names, main_cmds->cnt,
+		      sizeof(*main_cmds->names), cmdname_compare);
+		uniq(main_cmds);
 	}
 
-	path = paths = xstrdup(env_path);
-	while (1) {
-		if ((colon = strchr(path, PATH_SEP)))
-			*colon = 0;
+	if (env_path) {
+		char *paths, *path, *colon;
+		path = paths = xstrdup(env_path);
+		while (1) {
+			if ((colon = strchr(path, PATH_SEP)))
+				*colon = 0;
+			if (!exec_path || strcmp(path, exec_path))
+				list_commands_in_dir(other_cmds, path, prefix);
 
-		len = list_commands_in_dir(&other_cmds, path);
-		if (len > longest)
-			longest = len;
+			if (!colon)
+				break;
+			path = colon + 1;
+		}
+		free(paths);
 
-		if (!colon)
-			break;
-		path = colon + 1;
+		qsort(other_cmds->names, other_cmds->cnt,
+		      sizeof(*other_cmds->names), cmdname_compare);
+		uniq(other_cmds);
 	}
-	free(paths);
-
-	qsort(main_cmds.names, main_cmds.cnt,
-	      sizeof(*main_cmds.names), cmdname_compare);
-	uniq(&main_cmds);
-
-	qsort(other_cmds.names, other_cmds.cnt,
-	      sizeof(*other_cmds.names), cmdname_compare);
-	uniq(&other_cmds);
-	exclude_cmds(&other_cmds, &main_cmds);
-
-	return longest;
+	exclude_cmds(other_cmds, main_cmds);
 }
 
-static void list_commands(void)
-{
-	unsigned int longest = load_command_list();
-	const char *exec_path = git_exec_path();
-
-	if (main_cmds.cnt) {
-		printf("available git commands in '%s'\n", exec_path);
-		printf("----------------------------");
-		mput_char('-', strlen(exec_path));
-		putchar('\n');
-		pretty_print_string_list(&main_cmds, longest);
-		putchar('\n');
-	}
-
-	if (other_cmds.cnt) {
-		printf("git commands available from elsewhere on your $PATH\n");
-		printf("---------------------------------------------------\n");
-		pretty_print_string_list(&other_cmds, longest);
-		putchar('\n');
-	}
-}
-
-void list_common_cmds_help(void)
+void list_commands(const char *title, struct cmdnames *main_cmds,
+		   struct cmdnames *other_cmds)
 {
 	int i, longest = 0;
 
-	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-		if (longest < strlen(common_cmds[i].name))
-			longest = strlen(common_cmds[i].name);
+	for (i = 0; i < main_cmds->cnt; i++)
+		if (longest < main_cmds->names[i]->len)
+			longest = main_cmds->names[i]->len;
+	for (i = 0; i < other_cmds->cnt; i++)
+		if (longest < other_cmds->names[i]->len)
+			longest = other_cmds->names[i]->len;
+
+	if (main_cmds->cnt) {
+		const char *exec_path = git_exec_path();
+		printf("available %s in '%s'\n", title, exec_path);
+		printf("----------------");
+		mput_char('-', strlen(title) + strlen(exec_path));
+		putchar('\n');
+		pretty_print_string_list(main_cmds, longest);
+		putchar('\n');
 	}
 
-	puts("The most commonly used git commands are:");
-	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-		printf("   %s   ", common_cmds[i].name);
-		mput_char(' ', longest - strlen(common_cmds[i].name));
-		puts(common_cmds[i].help);
+	if (other_cmds->cnt) {
+		printf("%s available from elsewhere on your $PATH\n", title);
+		printf("---------------------------------------");
+		mput_char('-', strlen(title));
+		putchar('\n');
+		pretty_print_string_list(other_cmds, longest);
+		putchar('\n');
 	}
 }
 
-static int is_in_cmdlist(struct cmdnames *c, const char *s)
+int is_in_cmdlist(struct cmdnames *c, const char *s)
 {
 	int i;
 	for (i = 0; i < c->cnt; i++)
@@ -551,133 +261,101 @@
 	return 0;
 }
 
-static int is_git_command(const char *s)
+static int autocorrect;
+static struct cmdnames aliases;
+
+static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
 {
-	load_command_list();
-	return is_in_cmdlist(&main_cmds, s) ||
-		is_in_cmdlist(&other_cmds, s) ||
-		!strcmp(s, "help");
+	if (!strcmp(var, "help.autocorrect"))
+		autocorrect = git_config_int(var,value);
+	/* Also use aliases for command lookup */
+	if (!prefixcmp(var, "alias."))
+		add_cmdname(&aliases, var + 6, strlen(var + 6));
+
+	return git_default_config(var, value, cb);
 }
 
-static const char *prepend(const char *prefix, const char *cmd)
+static int levenshtein_compare(const void *p1, const void *p2)
 {
-	size_t pre_len = strlen(prefix);
-	size_t cmd_len = strlen(cmd);
-	char *p = xmalloc(pre_len + cmd_len + 1);
-	memcpy(p, prefix, pre_len);
-	strcpy(p + pre_len, cmd);
-	return p;
+	const struct cmdname *const *c1 = p1, *const *c2 = p2;
+	const char *s1 = (*c1)->name, *s2 = (*c2)->name;
+	int l1 = (*c1)->len;
+	int l2 = (*c2)->len;
+	return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
 }
 
-static const char *cmd_to_page(const char *git_cmd)
+static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
 {
-	if (!git_cmd)
-		return "git";
-	else if (!prefixcmp(git_cmd, "git"))
-		return git_cmd;
-	else if (is_git_command(git_cmd))
-		return prepend("git-", git_cmd);
-	else
-		return prepend("git", git_cmd);
+	int i;
+	ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
+
+	for (i = 0; i < old->cnt; i++)
+		cmds->names[cmds->cnt++] = old->names[i];
+	free(old->names);
+	old->cnt = 0;
+	old->names = NULL;
 }
 
-static void setup_man_path(void)
+const char *help_unknown_cmd(const char *cmd)
 {
-	struct strbuf new_path;
-	const char *old_path = getenv("MANPATH");
+	int i, n, best_similarity = 0;
+	struct cmdnames main_cmds, other_cmds;
 
-	strbuf_init(&new_path, 0);
+	memset(&main_cmds, 0, sizeof(main_cmds));
+	memset(&other_cmds, 0, sizeof(main_cmds));
+	memset(&aliases, 0, sizeof(aliases));
 
-	/* We should always put ':' after our path. If there is no
-	 * old_path, the ':' at the end will let 'man' to try
-	 * system-wide paths after ours to find the manual page. If
-	 * there is old_path, we need ':' as delimiter. */
-	strbuf_addstr(&new_path, GIT_MAN_PATH);
-	strbuf_addch(&new_path, ':');
-	if (old_path)
-		strbuf_addstr(&new_path, old_path);
+	git_config(git_unknown_cmd_config, NULL);
 
-	setenv("MANPATH", new_path.buf, 1);
+	load_command_list("git-", &main_cmds, &other_cmds);
 
-	strbuf_release(&new_path);
-}
+	add_cmd_list(&main_cmds, &aliases);
+	add_cmd_list(&main_cmds, &other_cmds);
+	qsort(main_cmds.names, main_cmds.cnt,
+	      sizeof(main_cmds.names), cmdname_compare);
+	uniq(&main_cmds);
 
-static void exec_viewer(const char *name, const char *page)
-{
-	const char *info = get_man_viewer_info(name);
+	/* This reuses cmdname->len for similarity index */
+	for (i = 0; i < main_cmds.cnt; ++i)
+		main_cmds.names[i]->len =
+			levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
 
-	if (!strcasecmp(name, "man"))
-		exec_man_man(info, page);
-	else if (!strcasecmp(name, "woman"))
-		exec_woman_emacs(info, page);
-	else if (!strcasecmp(name, "konqueror"))
-		exec_man_konqueror(info, page);
-	else if (info)
-		exec_man_cmd(info, page);
-	else
-		warning("'%s': unknown man viewer.", name);
-}
+	qsort(main_cmds.names, main_cmds.cnt,
+	      sizeof(*main_cmds.names), levenshtein_compare);
 
-static void show_man_page(const char *git_cmd)
-{
-	struct man_viewer_list *viewer;
-	const char *page = cmd_to_page(git_cmd);
+	if (!main_cmds.cnt)
+		die ("Uh oh. Your system reports no Git commands at all.");
 
-	setup_man_path();
-	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
-	{
-		exec_viewer(viewer->name, page); /* will return when unable */
+	best_similarity = main_cmds.names[0]->len;
+	n = 1;
+	while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
+		++n;
+	if (autocorrect && n == 1) {
+		const char *assumed = main_cmds.names[0]->name;
+		main_cmds.names[0] = NULL;
+		clean_cmdnames(&main_cmds);
+		fprintf(stderr, "WARNING: You called a Git program named '%s', "
+			"which does not exist.\n"
+			"Continuing under the assumption that you meant '%s'\n",
+			cmd, assumed);
+		if (autocorrect > 0) {
+			fprintf(stderr, "in %0.1f seconds automatically...\n",
+				(float)autocorrect/10.0);
+			poll(NULL, 0, autocorrect * 100);
+		}
+		return assumed;
 	}
-	exec_viewer("man", page);
-	die("no man viewer handled the request");
-}
 
-static void show_info_page(const char *git_cmd)
-{
-	const char *page = cmd_to_page(git_cmd);
-	setenv("INFOPATH", GIT_INFO_PATH, 1);
-	execlp("info", "info", "gitman", page, NULL);
-}
-
-static void get_html_page_path(struct strbuf *page_path, const char *page)
-{
-	struct stat st;
-	const char *html_path = system_path(GIT_HTML_PATH);
-
-	/* Check that we have a git documentation directory. */
-	if (stat(mkpath("%s/git.html", html_path), &st)
-	    || !S_ISREG(st.st_mode))
-		die("'%s': not a documentation directory.", html_path);
-
-	strbuf_init(page_path, 0);
-	strbuf_addf(page_path, "%s/%s.html", html_path, page);
-}
-
-/*
- * If open_html is not defined in a platform-specific way (see for
- * example compat/mingw.h), we use the script web--browse to display
- * HTML.
- */
-#ifndef open_html
-void open_html(const char *path)
-{
-	execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
-}
-#endif
-
-static void show_html_page(const char *git_cmd)
-{
-	const char *page = cmd_to_page(git_cmd);
-	struct strbuf page_path; /* it leaks but we exec bellow */
-
-	get_html_page_path(&page_path, page);
-
-	open_html(page_path.buf);
-}
-
-void help_unknown_cmd(const char *cmd)
-{
 	fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
+
+	if (best_similarity < 6) {
+		fprintf(stderr, "\nDid you mean %s?\n",
+			n < 2 ? "this": "one of these");
+
+		for (i = 0; i < n; i++)
+			fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+	}
+
 	exit(1);
 }
 
@@ -686,49 +364,3 @@
 	printf("git version %s\n", git_version_string);
 	return 0;
 }
-
-int cmd_help(int argc, const char **argv, const char *prefix)
-{
-	int nongit;
-	const char *alias;
-
-	setup_git_directory_gently(&nongit);
-	git_config(git_help_config, NULL);
-
-	argc = parse_options(argc, argv, builtin_help_options,
-			builtin_help_usage, 0);
-
-	if (show_all) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_commands();
-		printf("%s\n", git_more_info_string);
-		return 0;
-	}
-
-	if (!argv[0]) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_common_cmds_help();
-		printf("\n%s\n", git_more_info_string);
-		return 0;
-	}
-
-	alias = alias_lookup(argv[0]);
-	if (alias && !is_git_command(argv[0])) {
-		printf("`git %s' is aliased to `%s'\n", argv[0], alias);
-		return 0;
-	}
-
-	switch (help_format) {
-	case HELP_FORMAT_MAN:
-		show_man_page(argv[0]);
-		break;
-	case HELP_FORMAT_INFO:
-		show_info_page(argv[0]);
-		break;
-	case HELP_FORMAT_WEB:
-		show_html_page(argv[0]);
-		break;
-	}
-
-	return 0;
-}
diff --git a/help.h b/help.h
new file mode 100644
index 0000000..56bc154
--- /dev/null
+++ b/help.h
@@ -0,0 +1,29 @@
+#ifndef HELP_H
+#define HELP_H
+
+struct cmdnames {
+	int alloc;
+	int cnt;
+	struct cmdname {
+		size_t len; /* also used for similarity index in help.c */
+		char name[FLEX_ARRAY];
+	} **names;
+};
+
+static inline void mput_char(char c, unsigned int num)
+{
+	while(num--)
+		putchar(c);
+}
+
+void load_command_list(const char *prefix,
+		struct cmdnames *main_cmds,
+		struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+/* Here we require that excludes is a sorted list. */
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *c, const char *s);
+void list_commands(const char *title, struct cmdnames *main_cmds,
+		   struct cmdnames *other_cmds);
+
+#endif /* HELP_H */
diff --git a/http-push.c b/http-push.c
index 6805288..5138224 100644
--- a/http-push.c
+++ b/http-push.c
@@ -10,6 +10,7 @@
 #include "exec_cmd.h"
 #include "remote.h"
 #include "list-objects.h"
+#include "sigchain.h"
 
 #include <expat.h>
 
@@ -87,6 +88,7 @@
 struct repo
 {
 	char *url;
+	char *path;
 	int path_len;
 	int has_info_refs;
 	int can_update_info_refs;
@@ -95,7 +97,7 @@
 	struct remote_lock *locks;
 };
 
-static struct repo *remote;
+static struct repo *repo;
 
 enum transfer_state {
 	NEED_FETCH,
@@ -126,7 +128,7 @@
 	char errorstr[CURL_ERROR_SIZE];
 	long http_code;
 	unsigned char real_sha1[20];
-	SHA_CTX c;
+	git_SHA_CTX c;
 	z_stream stream;
 	int zret;
 	int rename;
@@ -151,6 +153,7 @@
 	char *url;
 	char *owner;
 	char *token;
+	char tmpfile_suffix[41];
 	time_t start_time;
 	long timeout;
 	int refreshing;
@@ -176,6 +179,73 @@
 	struct remote_ls_ctx *parent;
 };
 
+/* get_dav_token_headers options */
+enum dav_header_flag {
+	DAV_HEADER_IF = (1u << 0),
+	DAV_HEADER_LOCK = (1u << 1),
+	DAV_HEADER_TIMEOUT = (1u << 2)
+};
+
+static char *xml_entities(char *s)
+{
+	struct strbuf buf = STRBUF_INIT;
+	while (*s) {
+		size_t len = strcspn(s, "\"<>&");
+		strbuf_add(&buf, s, len);
+		s += len;
+		switch (*s) {
+		case '"':
+			strbuf_addstr(&buf, "&quot;");
+			break;
+		case '<':
+			strbuf_addstr(&buf, "&lt;");
+			break;
+		case '>':
+			strbuf_addstr(&buf, "&gt;");
+			break;
+		case '&':
+			strbuf_addstr(&buf, "&amp;");
+			break;
+		}
+		s++;
+	}
+	return strbuf_detach(&buf, NULL);
+}
+
+static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct curl_slist *dav_headers = NULL;
+
+	if (options & DAV_HEADER_IF) {
+		strbuf_addf(&buf, "If: (<%s>)", lock->token);
+		dav_headers = curl_slist_append(dav_headers, buf.buf);
+		strbuf_reset(&buf);
+	}
+	if (options & DAV_HEADER_LOCK) {
+		strbuf_addf(&buf, "Lock-Token: <%s>", lock->token);
+		dav_headers = curl_slist_append(dav_headers, buf.buf);
+		strbuf_reset(&buf);
+	}
+	if (options & DAV_HEADER_TIMEOUT) {
+		strbuf_addf(&buf, "Timeout: Second-%ld", lock->timeout);
+		dav_headers = curl_slist_append(dav_headers, buf.buf);
+		strbuf_reset(&buf);
+	}
+	strbuf_release(&buf);
+
+	return dav_headers;
+}
+
+static void append_remote_object_url(struct strbuf *buf, const char *url,
+				     const char *hex,
+				     int only_two_digit_prefix)
+{
+	strbuf_addf(buf, "%sobjects/%.*s/", url, 2, hex);
+	if (!only_two_digit_prefix)
+		strbuf_addf(buf, "%s", hex+2);
+}
+
 static void finish_request(struct transfer_request *request);
 static void release_request(struct transfer_request *request);
 
@@ -188,6 +258,15 @@
 }
 
 #ifdef USE_CURL_MULTI
+
+static char *get_remote_object_url(const char *url, const char *hex,
+				   int only_two_digit_prefix)
+{
+	struct strbuf buf = STRBUF_INIT;
+	append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
+	return strbuf_detach(&buf, NULL);
+}
+
 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
 			       void *data)
 {
@@ -208,8 +287,8 @@
 	do {
 		request->stream.next_out = expn;
 		request->stream.avail_out = sizeof(expn);
-		request->zret = inflate(&request->stream, Z_SYNC_FLUSH);
-		SHA1_Update(&request->c, expn,
+		request->zret = git_inflate(&request->stream, Z_SYNC_FLUSH);
+		git_SHA1_Update(&request->c, expn,
 			    sizeof(expn) - request->stream.avail_out);
 	} while (request->stream.avail_in && request->zret == Z_OK);
 	data_received++;
@@ -222,7 +301,6 @@
 	char *filename;
 	char prevfile[PATH_MAX];
 	char *url;
-	char *posn;
 	int prevlocal;
 	unsigned char prev_buf[PREV_BUF_SIZE];
 	ssize_t prev_read = 0;
@@ -268,21 +346,12 @@
 
 	memset(&request->stream, 0, sizeof(request->stream));
 
-	inflateInit(&request->stream);
+	git_inflate_init(&request->stream);
 
-	SHA1_Init(&request->c);
+	git_SHA1_Init(&request->c);
 
-	url = xmalloc(strlen(remote->url) + 50);
-	request->url = xmalloc(strlen(remote->url) + 50);
-	strcpy(url, remote->url);
-	posn = url + strlen(remote->url);
-	strcpy(posn, "objects/");
-	posn += 8;
-	memcpy(posn, hex, 2);
-	posn += 2;
-	*(posn++) = '/';
-	strcpy(posn, hex + 2);
-	strcpy(request->url, url);
+	url = get_remote_object_url(repo->url, hex, 0);
+	request->url = xstrdup(url);
 
 	/* If a previous temp file is present, process what was already
 	   fetched. */
@@ -309,8 +378,8 @@
 	   file; also rewind to the beginning of the local file. */
 	if (prev_read == -1) {
 		memset(&request->stream, 0, sizeof(request->stream));
-		inflateInit(&request->stream);
-		SHA1_Init(&request->c);
+		git_inflate_init(&request->stream);
+		git_SHA1_Init(&request->c);
 		if (prev_posn>0) {
 			prev_posn = 0;
 			lseek(request->local_fileno, 0, SEEK_SET);
@@ -346,7 +415,7 @@
 	request->state = RUN_FETCH_LOOSE;
 	if (!start_active_slot(slot)) {
 		fprintf(stderr, "Unable to start GET request\n");
-		remote->can_update_info_refs = 0;
+		repo->can_update_info_refs = 0;
 		release_request(request);
 	}
 }
@@ -355,16 +424,8 @@
 {
 	char *hex = sha1_to_hex(request->obj->sha1);
 	struct active_request_slot *slot;
-	char *posn;
 
-	request->url = xmalloc(strlen(remote->url) + 13);
-	strcpy(request->url, remote->url);
-	posn = request->url + strlen(remote->url);
-	strcpy(posn, "objects/");
-	posn += 8;
-	memcpy(posn, hex, 2);
-	posn += 2;
-	strcpy(posn, "/");
+	request->url = get_remote_object_url(repo->url, hex, 1);
 
 	slot = get_active_slot();
 	slot->callback_func = process_response;
@@ -399,10 +460,10 @@
 	struct transfer_request *check_request = request_queue_head;
 	struct active_request_slot *slot;
 
-	target = find_sha1_pack(request->obj->sha1, remote->packs);
+	target = find_sha1_pack(request->obj->sha1, repo->packs);
 	if (!target) {
 		fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", sha1_to_hex(request->obj->sha1));
-		remote->can_update_info_refs = 0;
+		repo->can_update_info_refs = 0;
 		release_request(request);
 		return;
 	}
@@ -415,9 +476,9 @@
 	snprintf(request->tmpfile, sizeof(request->tmpfile),
 		 "%s.temp", filename);
 
-	url = xmalloc(strlen(remote->url) + 64);
+	url = xmalloc(strlen(repo->url) + 64);
 	sprintf(url, "%sobjects/pack/pack-%s.pack",
-		remote->url, sha1_to_hex(target->sha1));
+		repo->url, sha1_to_hex(target->sha1));
 
 	/* Make sure there isn't another open request for this pack */
 	while (check_request) {
@@ -434,7 +495,7 @@
 	if (!packfile) {
 		fprintf(stderr, "Unable to open local file %s for pack",
 			request->tmpfile);
-		remote->can_update_info_refs = 0;
+		repo->can_update_info_refs = 0;
 		free(url);
 		return;
 	}
@@ -470,7 +531,7 @@
 	request->state = RUN_FETCH_PACKED;
 	if (!start_active_slot(slot)) {
 		fprintf(stderr, "Unable to start GET request\n");
-		remote->can_update_info_refs = 0;
+		repo->can_update_info_refs = 0;
 		release_request(request);
 	}
 }
@@ -479,7 +540,7 @@
 {
 	char *hex = sha1_to_hex(request->obj->sha1);
 	struct active_request_slot *slot;
-	char *posn;
+	struct strbuf buf = STRBUF_INIT;
 	enum object_type type;
 	char hdr[50];
 	void *unpacked;
@@ -518,21 +579,13 @@
 
 	request->buffer.buf.len = stream.total_out;
 
-	request->url = xmalloc(strlen(remote->url) +
-			       strlen(request->lock->token) + 51);
-	strcpy(request->url, remote->url);
-	posn = request->url + strlen(remote->url);
-	strcpy(posn, "objects/");
-	posn += 8;
-	memcpy(posn, hex, 2);
-	posn += 2;
-	*(posn++) = '/';
-	strcpy(posn, hex + 2);
-	request->dest = xmalloc(strlen(request->url) + 14);
-	sprintf(request->dest, "Destination: %s", request->url);
-	posn += 38;
-	*(posn++) = '_';
-	strcpy(posn, request->lock->token);
+	strbuf_addstr(&buf, "Destination: ");
+	append_remote_object_url(&buf, repo->url, hex, 0);
+	request->dest = strbuf_detach(&buf, NULL);
+
+	append_remote_object_url(&buf, repo->url, hex, 0);
+	strbuf_add(&buf, request->lock->tmpfile_suffix, 41);
+	request->url = strbuf_detach(&buf, NULL);
 
 	slot = get_active_slot();
 	slot->callback_func = process_response;
@@ -540,6 +593,10 @@
 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.buf.len);
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer);
+#endif
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
@@ -587,18 +644,12 @@
 {
 	struct active_request_slot *slot;
 	struct slot_results results;
-	char *if_header;
-	char timeout_header[25];
-	struct curl_slist *dav_headers = NULL;
+	struct curl_slist *dav_headers;
 	int rc = 0;
 
 	lock->refreshing = 1;
 
-	if_header = xmalloc(strlen(lock->token) + 25);
-	sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
-	sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
-	dav_headers = curl_slist_append(dav_headers, if_header);
-	dav_headers = curl_slist_append(dav_headers, timeout_header);
+	dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF | DAV_HEADER_TIMEOUT);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -621,14 +672,13 @@
 
 	lock->refreshing = 0;
 	curl_slist_free_all(dav_headers);
-	free(if_header);
 
 	return rc;
 }
 
 static void check_locks(void)
 {
-	struct remote_lock *lock = remote->locks;
+	struct remote_lock *lock = repo->locks;
 	time_t current_time = time(NULL);
 	int time_remaining;
 
@@ -728,7 +778,6 @@
 			aborted = 1;
 		}
 	} else if (request->state == RUN_FETCH_LOOSE) {
-		fchmod(request->local_fileno, 0444);
 		close(request->local_fileno); request->local_fileno = -1;
 
 		if (request->curl_result != CURLE_OK &&
@@ -739,10 +788,10 @@
 			}
 		} else {
 			if (request->http_code == 416)
-				fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+				warning("requested range invalid; we may already have all the data.");
 
-			inflateEnd(&request->stream);
-			SHA1_Final(request->real_sha1, &request->c);
+			git_inflate_end(&request->stream);
+			git_SHA1_Final(request->real_sha1, &request->c);
 			if (request->zret != Z_STREAM_END) {
 				unlink(request->tmpfile);
 			} else if (hashcmp(request->obj->sha1, request->real_sha1)) {
@@ -768,7 +817,7 @@
 		if (request->curl_result != CURLE_OK) {
 			fprintf(stderr, "Unable to get pack file %s\n%s",
 				request->url, curl_errorstr);
-			remote->can_update_info_refs = 0;
+			repo->can_update_info_refs = 0;
 		} else {
 			off_t pack_size = ftell(request->local_stream);
 
@@ -778,7 +827,7 @@
 					       request->filename)) {
 				target = (struct packed_git *)request->userData;
 				target->pack_size = pack_size;
-				lst = &remote->packs;
+				lst = &repo->packs;
 				while (*lst != target)
 					lst = &((*lst)->next);
 				*lst = (*lst)->next;
@@ -786,7 +835,7 @@
 				if (!verify_pack(target))
 					install_packed_git(target);
 				else
-					remote->can_update_info_refs = 0;
+					repo->can_update_info_refs = 0;
 			}
 		}
 		release_request(request);
@@ -796,7 +845,7 @@
 #ifdef USE_CURL_MULTI
 static int fill_active_slot(void *unused)
 {
-	struct transfer_request *request = request_queue_head;
+	struct transfer_request *request;
 
 	if (aborted)
 		return 0;
@@ -869,7 +918,7 @@
 		get_remote_object_list(obj->sha1[0]);
 	if (obj->flags & (REMOTE | PUSHING))
 		return 0;
-	target = find_sha1_pack(obj->sha1, remote->packs);
+	target = find_sha1_pack(obj->sha1, repo->packs);
 	if (target) {
 		obj->flags |= REMOTE;
 		return 0;
@@ -910,8 +959,8 @@
 	struct slot_results results;
 
 	/* Don't use the index if the pack isn't there */
-	url = xmalloc(strlen(remote->url) + 64);
-	sprintf(url, "%sobjects/pack/pack-%s.pack", remote->url, hex);
+	url = xmalloc(strlen(repo->url) + 64);
+	sprintf(url, "%sobjects/pack/pack-%s.pack", repo->url, hex);
 	slot = get_active_slot();
 	slot->results = &results;
 	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
@@ -936,7 +985,7 @@
 	if (push_verbosely)
 		fprintf(stderr, "Getting index for pack %s\n", hex);
 
-	sprintf(url, "%sobjects/pack/pack-%s.idx", remote->url, hex);
+	sprintf(url, "%sobjects/pack/pack-%s.idx", repo->url, hex);
 
 	filename = sha1_pack_index_name(sha1);
 	snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
@@ -998,8 +1047,8 @@
 		return -1;
 
 	new_pack = parse_pack_index(sha1);
-	new_pack->next = remote->packs;
-	remote->packs = new_pack;
+	new_pack->next = repo->packs;
+	repo->packs = new_pack;
 	return 0;
 }
 
@@ -1017,8 +1066,8 @@
 	if (push_verbosely)
 		fprintf(stderr, "Getting pack list\n");
 
-	url = xmalloc(strlen(remote->url) + 20);
-	sprintf(url, "%sobjects/info/packs", remote->url);
+	url = xmalloc(strlen(repo->url) + 20);
+	sprintf(url, "%sobjects/info/packs", repo->url);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -1110,6 +1159,8 @@
 static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 {
 	struct remote_lock *lock = (struct remote_lock *)ctx->userData;
+	git_SHA_CTX sha_ctx;
+	unsigned char lock_token_sha1[20];
 
 	if (tag_closed && ctx->cdata) {
 		if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
@@ -1120,10 +1171,15 @@
 				lock->timeout =
 					strtol(ctx->cdata + 7, NULL, 10);
 		} else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
-			if (!prefixcmp(ctx->cdata, "opaquelocktoken:")) {
-				lock->token = xmalloc(strlen(ctx->cdata) - 15);
-				strcpy(lock->token, ctx->cdata + 16);
-			}
+			lock->token = xmalloc(strlen(ctx->cdata) + 1);
+			strcpy(lock->token, ctx->cdata);
+
+			git_SHA1_Init(&sha_ctx);
+			git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
+			git_SHA1_Final(lock_token_sha1, &sha_ctx);
+
+			lock->tmpfile_suffix[0] = '_';
+			memcpy(lock->tmpfile_suffix + 1, sha1_to_hex(lock_token_sha1), 40);
 		}
 	}
 }
@@ -1195,14 +1251,16 @@
 	struct remote_lock *lock = NULL;
 	struct curl_slist *dav_headers = NULL;
 	struct xml_ctx ctx;
+	char *escaped;
 
-	url = xmalloc(strlen(remote->url) + strlen(path) + 1);
-	sprintf(url, "%s%s", remote->url, path);
+	url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+	sprintf(url, "%s%s", repo->url, path);
 
 	/* Make sure leading directories exist for the remote ref */
-	ep = strchr(url + strlen(remote->url) + 1, '/');
+	ep = strchr(url + strlen(repo->url) + 1, '/');
 	while (ep) {
-		*ep = 0;
+		char saved_character = ep[1];
+		ep[1] = '\0';
 		slot = get_active_slot();
 		slot->results = &results;
 		curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
@@ -1224,11 +1282,13 @@
 			free(url);
 			return NULL;
 		}
-		*ep = '/';
+		ep[1] = saved_character;
 		ep = strchr(ep + 1, '/');
 	}
 
-	strbuf_addf(&out_buffer.buf, LOCK_REQUEST, git_default_email);
+	escaped = xml_entities(git_default_email);
+	strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
+	free(escaped);
 
 	sprintf(timeout_header, "Timeout: Second-%ld", timeout);
 	dav_headers = curl_slist_append(dav_headers, timeout_header);
@@ -1239,6 +1299,10 @@
 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
@@ -1291,8 +1355,8 @@
 	} else {
 		lock->url = url;
 		lock->start_time = time(NULL);
-		lock->next = remote->locks;
-		remote->locks = lock;
+		lock->next = repo->locks;
+		repo->locks = lock;
 	}
 
 	return lock;
@@ -1302,15 +1366,11 @@
 {
 	struct active_request_slot *slot;
 	struct slot_results results;
-	struct remote_lock *prev = remote->locks;
-	char *lock_token_header;
-	struct curl_slist *dav_headers = NULL;
+	struct remote_lock *prev = repo->locks;
+	struct curl_slist *dav_headers;
 	int rc = 0;
 
-	lock_token_header = xmalloc(strlen(lock->token) + 31);
-	sprintf(lock_token_header, "Lock-Token: <opaquelocktoken:%s>",
-		lock->token);
-	dav_headers = curl_slist_append(dav_headers, lock_token_header);
+	dav_headers = get_dav_token_headers(lock, DAV_HEADER_LOCK);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -1331,10 +1391,9 @@
 	}
 
 	curl_slist_free_all(dav_headers);
-	free(lock_token_header);
 
-	if (remote->locks == lock) {
-		remote->locks = lock->next;
+	if (repo->locks == lock) {
+		repo->locks = lock->next;
 	} else {
 		while (prev && prev->next != lock)
 			prev = prev->next;
@@ -1352,7 +1411,7 @@
 
 static void remove_locks(void)
 {
-	struct remote_lock *lock = remote->locks;
+	struct remote_lock *lock = repo->locks;
 
 	fprintf(stderr, "Removing remote locks...\n");
 	while (lock) {
@@ -1364,7 +1423,7 @@
 static void remove_locks_on_signal(int signo)
 {
 	remove_locks();
-	signal(signo, SIG_DFL);
+	sigchain_pop(signo);
 	raise(signo);
 }
 
@@ -1426,9 +1485,17 @@
 				ls->userFunc(ls);
 			}
 		} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
-			ls->dentry_name = xmalloc(strlen(ctx->cdata) -
-						  remote->path_len + 1);
-			strcpy(ls->dentry_name, ctx->cdata + remote->path_len);
+			char *path = ctx->cdata;
+			if (*ctx->cdata == 'h') {
+				path = strstr(path, "//");
+				if (path) {
+					path = strchr(path+2, '/');
+				}
+			}
+			if (path) {
+				path += repo->path_len;
+				ls->dentry_name = xstrdup(path);
+			}
 		} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
 			ls->dentry_flags |= IS_DIR;
 		}
@@ -1439,11 +1506,17 @@
 	}
 }
 
+/*
+ * NEEDSWORK: remote_ls() ignores info/refs on the remote side.  But it
+ * should _only_ heed the information from that file, instead of trying to
+ * determine the refs from the remote file system (badly: it does not even
+ * know about packed-refs).
+ */
 static void remote_ls(const char *path, int flags,
 		      void (*userFunc)(struct remote_ls_ctx *ls),
 		      void *userData)
 {
-	char *url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+	char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
 	struct active_request_slot *slot;
 	struct slot_results results;
 	struct strbuf in_buffer = STRBUF_INIT;
@@ -1459,7 +1532,7 @@
 	ls.userData = userData;
 	ls.userFunc = userFunc;
 
-	sprintf(url, "%s%s", remote->url, path);
+	sprintf(url, "%s%s", repo->url, path);
 
 	strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
 
@@ -1471,6 +1544,10 @@
 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
@@ -1536,8 +1613,11 @@
 	struct curl_slist *dav_headers = NULL;
 	struct xml_ctx ctx;
 	int lock_flags = 0;
+	char *escaped;
 
-	strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url);
+	escaped = xml_entities(repo->url);
+	strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, escaped);
+	free(escaped);
 
 	dav_headers = curl_slist_append(dav_headers, "Depth: 0");
 	dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
@@ -1547,9 +1627,13 @@
 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
 	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, repo->url);
 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
@@ -1579,16 +1663,16 @@
 			}
 			XML_ParserFree(parser);
 			if (!lock_flags)
-				error("Error: no DAV locking support on %s",
-				      remote->url);
+				error("no DAV locking support on %s",
+				      repo->url);
 
 		} else {
 			error("Cannot access URL %s, return code %d",
-			      remote->url, results.curl_result);
+			      repo->url, results.curl_result);
 			lock_flags = 0;
 		}
 	} else {
-		error("Unable to start PROPFIND request on %s", remote->url);
+		error("Unable to start PROPFIND request on %s", repo->url);
 	}
 
 	strbuf_release(&out_buffer.buf);
@@ -1717,13 +1801,10 @@
 {
 	struct active_request_slot *slot;
 	struct slot_results results;
-	char *if_header;
 	struct buffer out_buffer = { STRBUF_INIT, 0 };
-	struct curl_slist *dav_headers = NULL;
+	struct curl_slist *dav_headers;
 
-	if_header = xmalloc(strlen(lock->token) + 25);
-	sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
-	dav_headers = curl_slist_append(dav_headers, if_header);
+	dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF);
 
 	strbuf_addf(&out_buffer.buf, "%s\n", sha1_to_hex(sha1));
 
@@ -1732,6 +1813,10 @@
 	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
 	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.buf.len);
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &out_buffer);
+#endif
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
@@ -1742,7 +1827,6 @@
 	if (start_active_slot(slot)) {
 		run_active_slot(slot);
 		strbuf_release(&out_buffer.buf);
-		free(if_header);
 		if (results.curl_result != CURLE_OK) {
 			fprintf(stderr,
 				"PUT error: curl result=%d, HTTP code=%ld\n",
@@ -1752,7 +1836,6 @@
 		}
 	} else {
 		strbuf_release(&out_buffer.buf);
-		free(if_header);
 		fprintf(stderr, "Unable to start PUT request\n");
 		return 0;
 	}
@@ -1760,32 +1843,19 @@
 	return 1;
 }
 
-static struct ref *local_refs, **local_tail;
 static struct ref *remote_refs, **remote_tail;
 
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct ref *ref;
-	int len = strlen(refname) + 1;
-	ref = xcalloc(1, sizeof(*ref) + len);
-	hashcpy(ref->new_sha1, sha1);
-	memcpy(ref->name, refname, len);
-	*local_tail = ref;
-	local_tail = &ref->next;
-	return 0;
-}
-
 static void one_remote_ref(char *refname)
 {
 	struct ref *ref;
 	struct object *obj;
 
-	ref = alloc_ref_from_str(refname);
+	ref = alloc_ref(refname);
 
-	if (http_fetch_ref(remote->url, ref) != 0) {
+	if (http_fetch_ref(repo->url, ref) != 0) {
 		fprintf(stderr,
 			"Unable to fetch ref %s from %s\n",
-			refname, remote->url);
+			refname, repo->url);
 		free(ref);
 		return;
 	}
@@ -1794,7 +1864,7 @@
 	 * Fetch a copy of the object if it doesn't exist locally - it
 	 * may be required for updating server info later.
 	 */
-	if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
+	if (repo->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
 		obj = lookup_unknown_object(ref->old_sha1);
 		if (obj) {
 			fprintf(stderr,	"  fetch %s for %s\n",
@@ -1807,12 +1877,6 @@
 	remote_tail = &ref->next;
 }
 
-static void get_local_heads(void)
-{
-	local_tail = &local_refs;
-	for_each_ref(one_local_ref, NULL);
-}
-
 static void get_dav_remote_heads(void)
 {
 	remote_tail = &remote_refs;
@@ -1830,55 +1894,6 @@
 	return 1;
 }
 
-static void unmark_and_free(struct commit_list *list, unsigned int mark)
-{
-	while (list) {
-		struct commit_list *temp = list;
-		temp->item->object.flags &= ~mark;
-		list = temp->next;
-		free(temp);
-	}
-}
-
-static int ref_newer(const unsigned char *new_sha1,
-		     const unsigned char *old_sha1)
-{
-	struct object *o;
-	struct commit *old, *new;
-	struct commit_list *list, *used;
-	int found = 0;
-
-	/* Both new and old must be commit-ish and new is descendant of
-	 * old.  Otherwise we require --force.
-	 */
-	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != OBJ_COMMIT)
-		return 0;
-	old = (struct commit *) o;
-
-	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != OBJ_COMMIT)
-		return 0;
-	new = (struct commit *) o;
-
-	if (parse_commit(new) < 0)
-		return 0;
-
-	used = list = NULL;
-	commit_list_insert(new, &list);
-	while (list) {
-		new = pop_most_recent_commit(&list, TMP_MARK);
-		commit_list_insert(new, &used);
-		if (new == old) {
-			found = 1;
-			break;
-		}
-	}
-	unmark_and_free(list, TMP_MARK);
-	unmark_and_free(used, TMP_MARK);
-	return found;
-}
-
 static void add_remote_info_ref(struct remote_ls_ctx *ls)
 {
 	struct strbuf *buf = (struct strbuf *)ls->userData;
@@ -1887,12 +1902,12 @@
 	char *ref_info;
 	struct ref *ref;
 
-	ref = alloc_ref_from_str(ls->dentry_name);
+	ref = alloc_ref(ls->dentry_name);
 
-	if (http_fetch_ref(remote->url, ref) != 0) {
+	if (http_fetch_ref(repo->url, ref) != 0) {
 		fprintf(stderr,
 			"Unable to fetch ref %s from %s\n",
-			ls->dentry_name, remote->url);
+			ls->dentry_name, repo->url);
 		aborted = 1;
 		free(ref);
 		return;
@@ -1934,21 +1949,22 @@
 	struct buffer buffer = { STRBUF_INIT, 0 };
 	struct active_request_slot *slot;
 	struct slot_results results;
-	char *if_header;
-	struct curl_slist *dav_headers = NULL;
+	struct curl_slist *dav_headers;
 
 	remote_ls("refs/", (PROCESS_FILES | RECURSIVE),
 		  add_remote_info_ref, &buffer.buf);
 	if (!aborted) {
-		if_header = xmalloc(strlen(lock->token) + 25);
-		sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
-		dav_headers = curl_slist_append(dav_headers, if_header);
+		dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF);
 
 		slot = get_active_slot();
 		slot->results = &results;
 		curl_easy_setopt(slot->curl, CURLOPT_INFILE, &buffer);
 		curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, buffer.buf.len);
 		curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+#ifndef NO_CURL_IOCTL
+		curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
+		curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &buffer);
+#endif
 		curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
 		curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
 		curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
@@ -1964,19 +1980,18 @@
 					results.curl_result, results.http_code);
 			}
 		}
-		free(if_header);
 	}
 	strbuf_release(&buffer.buf);
 }
 
 static int remote_exists(const char *path)
 {
-	char *url = xmalloc(strlen(remote->url) + strlen(path) + 1);
+	char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
 	struct active_request_slot *slot;
 	struct slot_results results;
 	int ret = -1;
 
-	sprintf(url, "%s%s", remote->url, path);
+	sprintf(url, "%s%s", repo->url, path);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -2006,8 +2021,8 @@
 	struct active_request_slot *slot;
 	struct slot_results results;
 
-	url = xmalloc(strlen(remote->url) + strlen(path) + 1);
-	sprintf(url, "%s%s", remote->url, path);
+	url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+	sprintf(url, "%s%s", repo->url, path);
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -2122,7 +2137,7 @@
 				     "of your current HEAD.\n"
 				     "If you are sure you want to delete it,"
 				     " run:\n\t'git http-push -D %s %s'",
-				     remote_ref->name, remote->url, pattern);
+				     remote_ref->name, repo->url, pattern);
 		}
 	}
 
@@ -2130,8 +2145,8 @@
 	fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
 	if (dry_run)
 		return 0;
-	url = xmalloc(strlen(remote->url) + strlen(remote_ref->name) + 1);
-	sprintf(url, "%s%s", remote->url, remote_ref->name);
+	url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1);
+	sprintf(url, "%s%s", repo->url, remote_ref->name);
 	slot = get_active_slot();
 	slot->results = &results;
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
@@ -2167,12 +2182,15 @@
 	int rc = 0;
 	int i;
 	int new_refs;
-	struct ref *ref;
+	struct ref *ref, *local_refs;
+	struct remote *remote;
 	char *rewritten_url = NULL;
 
+	git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
-	remote = xcalloc(sizeof(*remote), 1);
+	repo = xcalloc(sizeof(*repo), 1);
 
 	argv++;
 	for (i = 1; i < argc; i++, argv++) {
@@ -2205,13 +2223,14 @@
 				continue;
 			}
 		}
-		if (!remote->url) {
+		if (!repo->url) {
 			char *path = strstr(arg, "//");
-			remote->url = arg;
+			repo->url = arg;
+			repo->path_len = strlen(arg);
 			if (path) {
-				path = strchr(path+2, '/');
-				if (path)
-					remote->path_len = strlen(path);
+				repo->path = strchr(path+2, '/');
+				if (repo->path)
+					repo->path_len = strlen(repo->path);
 			}
 			continue;
 		}
@@ -2224,7 +2243,7 @@
 	die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI");
 #endif
 
-	if (!remote->url)
+	if (!repo->url)
 		usage(http_push_usage);
 
 	if (delete_branch && nr_refspec != 1)
@@ -2232,16 +2251,24 @@
 
 	memset(remote_dir_exists, -1, 256);
 
-	http_init(NULL);
+	/*
+	 * Create a minimum remote by hand to give to http_init(),
+	 * primarily to allow it to look at the URL.
+	 */
+	remote = xcalloc(sizeof(*remote), 1);
+	ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
+	remote->url[remote->url_nr++] = repo->url;
+	http_init(remote);
 
 	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 
-	if (remote->url && remote->url[strlen(remote->url)-1] != '/') {
-		rewritten_url = malloc(strlen(remote->url)+2);
-		strcpy(rewritten_url, remote->url);
+	if (repo->url && repo->url[strlen(repo->url)-1] != '/') {
+		rewritten_url = xmalloc(strlen(repo->url)+2);
+		strcpy(rewritten_url, repo->url);
 		strcat(rewritten_url, "/");
-		remote->url = rewritten_url;
-		++remote->path_len;
+		repo->path = rewritten_url + (repo->path - repo->url);
+		repo->path_len++;
+		repo->url = rewritten_url;
 	}
 
 	/* Verify DAV compliance/lock support */
@@ -2250,30 +2277,27 @@
 		goto cleanup;
 	}
 
-	signal(SIGINT, remove_locks_on_signal);
-	signal(SIGHUP, remove_locks_on_signal);
-	signal(SIGQUIT, remove_locks_on_signal);
-	signal(SIGTERM, remove_locks_on_signal);
+	sigchain_push_common(remove_locks_on_signal);
 
 	/* Check whether the remote has server info files */
-	remote->can_update_info_refs = 0;
-	remote->has_info_refs = remote_exists("info/refs");
-	remote->has_info_packs = remote_exists("objects/info/packs");
-	if (remote->has_info_refs) {
+	repo->can_update_info_refs = 0;
+	repo->has_info_refs = remote_exists("info/refs");
+	repo->has_info_packs = remote_exists("objects/info/packs");
+	if (repo->has_info_refs) {
 		info_ref_lock = lock_remote("info/refs", LOCK_TIME);
 		if (info_ref_lock)
-			remote->can_update_info_refs = 1;
+			repo->can_update_info_refs = 1;
 		else {
-			fprintf(stderr, "Error: cannot lock existing info/refs\n");
+			error("cannot lock existing info/refs");
 			rc = 1;
 			goto cleanup;
 		}
 	}
-	if (remote->has_info_packs)
+	if (repo->has_info_packs)
 		fetch_indices();
 
 	/* Get a list of all local and remote heads to validate refspecs */
-	get_local_heads();
+	local_refs = get_local_heads();
 	fprintf(stderr, "Fetching remote heads...\n");
 	get_dav_remote_heads();
 
@@ -2427,8 +2451,8 @@
 	}
 
 	/* Update remote server info if appropriate */
-	if (remote->has_info_refs && new_refs) {
-		if (info_ref_lock && remote->can_update_info_refs) {
+	if (repo->has_info_refs && new_refs) {
+		if (info_ref_lock && repo->can_update_info_refs) {
 			fprintf(stderr, "Updating remote server info\n");
 			if (!dry_run)
 				update_remote_info_refs(info_ref_lock);
@@ -2441,7 +2465,7 @@
 	free(rewritten_url);
 	if (info_ref_lock)
 		unlock_remote(info_ref_lock);
-	free(remote);
+	free(repo);
 
 	curl_slist_free_all(no_pragma_header);
 
diff --git a/http-walker.c b/http-walker.c
index 9dc6b27..c5a3ea3 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -36,7 +36,7 @@
 	char errorstr[CURL_ERROR_SIZE];
 	long http_code;
 	unsigned char real_sha1[20];
-	SHA_CTX c;
+	git_SHA_CTX c;
 	z_stream stream;
 	int zret;
 	int rename;
@@ -82,8 +82,8 @@
 	do {
 		obj_req->stream.next_out = expn;
 		obj_req->stream.avail_out = sizeof(expn);
-		obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
-		SHA1_Update(&obj_req->c, expn,
+		obj_req->zret = git_inflate(&obj_req->stream, Z_SYNC_FLUSH);
+		git_SHA1_Update(&obj_req->c, expn,
 			    sizeof(expn) - obj_req->stream.avail_out);
 	} while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
 	data_received++;
@@ -142,9 +142,9 @@
 
 	memset(&obj_req->stream, 0, sizeof(obj_req->stream));
 
-	inflateInit(&obj_req->stream);
+	git_inflate_init(&obj_req->stream);
 
-	SHA1_Init(&obj_req->c);
+	git_SHA1_Init(&obj_req->c);
 
 	url = xmalloc(strlen(obj_req->repo->base) + 51);
 	obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
@@ -183,8 +183,8 @@
 	   file; also rewind to the beginning of the local file. */
 	if (prev_read == -1) {
 		memset(&obj_req->stream, 0, sizeof(obj_req->stream));
-		inflateInit(&obj_req->stream);
-		SHA1_Init(&obj_req->c);
+		git_inflate_init(&obj_req->stream);
+		git_SHA1_Init(&obj_req->c);
 		if (prev_posn>0) {
 			prev_posn = 0;
 			lseek(obj_req->local, 0, SEEK_SET);
@@ -231,7 +231,6 @@
 {
 	struct stat st;
 
-	fchmod(obj_req->local, 0444);
 	close(obj_req->local); obj_req->local = -1;
 
 	if (obj_req->http_code == 416) {
@@ -243,8 +242,8 @@
 		return;
 	}
 
-	inflateEnd(&obj_req->stream);
-	SHA1_Final(obj_req->real_sha1, &obj_req->c);
+	git_inflate_end(&obj_req->stream);
+	git_SHA1_Final(obj_req->real_sha1, &obj_req->c);
 	if (obj_req->zret != Z_STREAM_END) {
 		unlink(obj_req->tmpfile);
 		return;
diff --git a/http.c b/http.c
index c18e30a..2e3d649 100644
--- a/http.c
+++ b/http.c
@@ -1,7 +1,7 @@
 #include "http.h"
 
 int data_received;
-int active_requests = 0;
+int active_requests;
 
 #ifdef USE_CURL_MULTI
 static int max_requests = -1;
@@ -13,22 +13,23 @@
 char curl_errorstr[CURL_ERROR_SIZE];
 
 static int curl_ssl_verify = -1;
-static const char *ssl_cert = NULL;
+static const char *ssl_cert;
 #if LIBCURL_VERSION_NUM >= 0x070902
-static const char *ssl_key = NULL;
+static const char *ssl_key;
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-static const char *ssl_capath = NULL;
+static const char *ssl_capath;
 #endif
-static const char *ssl_cainfo = NULL;
+static const char *ssl_cainfo;
 static long curl_low_speed_limit = -1;
 static long curl_low_speed_time = -1;
-static int curl_ftp_no_epsv = 0;
-static const char *curl_http_proxy = NULL;
+static int curl_ftp_no_epsv;
+static const char *curl_http_proxy;
+static char *user_name, *user_pass;
 
 static struct curl_slist *pragma_header;
 
-static struct active_request_slot *active_queue_head = NULL;
+static struct active_request_slot *active_queue_head;
 
 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
@@ -43,6 +44,25 @@
 	return size;
 }
 
+#ifndef NO_CURL_IOCTL
+curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
+{
+	struct buffer *buffer = clientp;
+
+	switch (cmd) {
+	case CURLIOCMD_NOP:
+		return CURLIOE_OK;
+
+	case CURLIOCMD_RESTARTREAD:
+		buffer->posn = 0;
+		return CURLIOE_OK;
+
+	default:
+		return CURLIOE_UNKNOWNCMD;
+	}
+}
+#endif
+
 size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
 	size_t size = eltsize * nmemb;
@@ -94,53 +114,33 @@
 static int http_options(const char *var, const char *value, void *cb)
 {
 	if (!strcmp("http.sslverify", var)) {
-		if (curl_ssl_verify == -1) {
-			curl_ssl_verify = git_config_bool(var, value);
-		}
+		curl_ssl_verify = git_config_bool(var, value);
 		return 0;
 	}
-
-	if (!strcmp("http.sslcert", var)) {
-		if (ssl_cert == NULL)
-			return git_config_string(&ssl_cert, var, value);
-		return 0;
-	}
+	if (!strcmp("http.sslcert", var))
+		return git_config_string(&ssl_cert, var, value);
 #if LIBCURL_VERSION_NUM >= 0x070902
-	if (!strcmp("http.sslkey", var)) {
-		if (ssl_key == NULL)
-			return git_config_string(&ssl_key, var, value);
-		return 0;
-	}
+	if (!strcmp("http.sslkey", var))
+		return git_config_string(&ssl_key, var, value);
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-	if (!strcmp("http.sslcapath", var)) {
-		if (ssl_capath == NULL)
-			return git_config_string(&ssl_capath, var, value);
-		return 0;
-	}
+	if (!strcmp("http.sslcapath", var))
+		return git_config_string(&ssl_capath, var, value);
 #endif
-	if (!strcmp("http.sslcainfo", var)) {
-		if (ssl_cainfo == NULL)
-			return git_config_string(&ssl_cainfo, var, value);
-		return 0;
-	}
-
+	if (!strcmp("http.sslcainfo", var))
+		return git_config_string(&ssl_cainfo, var, value);
 #ifdef USE_CURL_MULTI
 	if (!strcmp("http.maxrequests", var)) {
-		if (max_requests == -1)
-			max_requests = git_config_int(var, value);
+		max_requests = git_config_int(var, value);
 		return 0;
 	}
 #endif
-
 	if (!strcmp("http.lowspeedlimit", var)) {
-		if (curl_low_speed_limit == -1)
-			curl_low_speed_limit = (long)git_config_int(var, value);
+		curl_low_speed_limit = (long)git_config_int(var, value);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedtime", var)) {
-		if (curl_low_speed_time == -1)
-			curl_low_speed_time = (long)git_config_int(var, value);
+		curl_low_speed_time = (long)git_config_int(var, value);
 		return 0;
 	}
 
@@ -148,19 +148,28 @@
 		curl_ftp_no_epsv = git_config_bool(var, value);
 		return 0;
 	}
-	if (!strcmp("http.proxy", var)) {
-		if (curl_http_proxy == NULL)
-			return git_config_string(&curl_http_proxy, var, value);
-		return 0;
-	}
+	if (!strcmp("http.proxy", var))
+		return git_config_string(&curl_http_proxy, var, value);
 
 	/* Fall back on the default ones */
 	return git_default_config(var, value, cb);
 }
 
-static CURL* get_curl_handle(void)
+static void init_curl_http_auth(CURL *result)
 {
-	CURL* result = curl_easy_init();
+	if (user_name) {
+		struct strbuf up = STRBUF_INIT;
+		if (!user_pass)
+			user_pass = xstrdup(getpass("Password: "));
+		strbuf_addf(&up, "%s:%s", user_name, user_pass);
+		curl_easy_setopt(result, CURLOPT_USERPWD,
+				 strbuf_detach(&up, NULL));
+	}
+}
+
+static CURL *get_curl_handle(void)
+{
+	CURL *result = curl_easy_init();
 
 	if (!curl_ssl_verify) {
 		curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
@@ -176,6 +185,8 @@
 	curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
 
+	init_curl_http_auth(result);
+
 	if (ssl_cert != NULL)
 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
 #if LIBCURL_VERSION_NUM >= 0x070902
@@ -213,11 +224,60 @@
 	return result;
 }
 
+static void http_auth_init(const char *url)
+{
+	char *at, *colon, *cp, *slash;
+	int len;
+
+	cp = strstr(url, "://");
+	if (!cp)
+		return;
+
+	/*
+	 * Ok, the URL looks like "proto://something".  Which one?
+	 * "proto://<user>:<pass>@<host>/...",
+	 * "proto://<user>@<host>/...", or just
+	 * "proto://<host>/..."?
+	 */
+	cp += 3;
+	at = strchr(cp, '@');
+	colon = strchr(cp, ':');
+	slash = strchrnul(cp, '/');
+	if (!at || slash <= at)
+		return; /* No credentials */
+	if (!colon || at <= colon) {
+		/* Only username */
+		len = at - cp;
+		user_name = xmalloc(len + 1);
+		memcpy(user_name, cp, len);
+		user_name[len] = '\0';
+		user_pass = NULL;
+	} else {
+		len = colon - cp;
+		user_name = xmalloc(len + 1);
+		memcpy(user_name, cp, len);
+		user_name[len] = '\0';
+		len = at - (colon + 1);
+		user_pass = xmalloc(len + 1);
+		memcpy(user_pass, colon + 1, len);
+		user_pass[len] = '\0';
+	}
+}
+
+static void set_from_env(const char **var, const char *envname)
+{
+	const char *val = getenv(envname);
+	if (val)
+		*var = val;
+}
+
 void http_init(struct remote *remote)
 {
 	char *low_speed_limit;
 	char *low_speed_time;
 
+	git_config(http_options, NULL);
+
 	curl_global_init(CURL_GLOBAL_ALL);
 
 	if (remote && remote->http_proxy)
@@ -242,14 +302,14 @@
 	if (getenv("GIT_SSL_NO_VERIFY"))
 		curl_ssl_verify = 0;
 
-	ssl_cert = getenv("GIT_SSL_CERT");
+	set_from_env(&ssl_cert, "GIT_SSL_CERT");
 #if LIBCURL_VERSION_NUM >= 0x070902
-	ssl_key = getenv("GIT_SSL_KEY");
+	set_from_env(&ssl_key, "GIT_SSL_KEY");
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-	ssl_capath = getenv("GIT_SSL_CAPATH");
+	set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
 #endif
-	ssl_cainfo = getenv("GIT_SSL_CAINFO");
+	set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
 	low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
 	if (low_speed_limit != NULL)
@@ -258,8 +318,6 @@
 	if (low_speed_time != NULL)
 		curl_low_speed_time = strtol(low_speed_time, NULL, 10);
 
-	git_config(http_options, NULL);
-
 	if (curl_ssl_verify == -1)
 		curl_ssl_verify = 1;
 
@@ -271,6 +329,9 @@
 	if (getenv("GIT_CURL_FTP_NO_EPSV"))
 		curl_ftp_no_epsv = 1;
 
+	if (remote && remote->url && remote->url[0])
+		http_auth_init(remote->url[0]);
+
 #ifndef NO_CURL_EASY_DUPHANDLE
 	curl_default = get_curl_handle();
 #endif
@@ -322,15 +383,14 @@
 	/* Wait for a slot to open up if the queue is full */
 	while (active_requests >= max_requests) {
 		curl_multi_perform(curlm, &num_transfers);
-		if (num_transfers < active_requests) {
+		if (num_transfers < active_requests)
 			process_curl_messages();
-		}
 	}
 #endif
 
-	while (slot != NULL && slot->in_use) {
+	while (slot != NULL && slot->in_use)
 		slot = slot->next;
-	}
+
 	if (slot == NULL) {
 		newslot = xmalloc(sizeof(*newslot));
 		newslot->curl = NULL;
@@ -341,9 +401,8 @@
 		if (slot == NULL) {
 			active_queue_head = newslot;
 		} else {
-			while (slot->next != NULL) {
+			while (slot->next != NULL)
 				slot = slot->next;
-			}
 			slot->next = newslot;
 		}
 		slot = newslot;
@@ -404,11 +463,11 @@
 	struct fill_chain *next;
 };
 
-static struct fill_chain *fill_cfg = NULL;
+static struct fill_chain *fill_cfg;
 
 void add_fill_function(void *data, int (*fill)(void *))
 {
-	struct fill_chain *new = malloc(sizeof(*new));
+	struct fill_chain *new = xmalloc(sizeof(*new));
 	struct fill_chain **linkp = &fill_cfg;
 	new->data = data;
 	new->fill = fill;
@@ -535,9 +594,8 @@
 	}
 
 	/* Run callback if appropriate */
-	if (slot->callback_func != NULL) {
+	if (slot->callback_func != NULL)
 		slot->callback_func(slot->callback_data);
-	}
 }
 
 void finish_all_active_slots(void)
@@ -567,37 +625,29 @@
 
 static inline int hex(int v)
 {
-	if (v < 10) return '0' + v;
-	else return 'A' + v - 10;
+	if (v < 10)
+		return '0' + v;
+	else
+		return 'A' + v - 10;
 }
 
 static char *quote_ref_url(const char *base, const char *ref)
 {
+	struct strbuf buf = STRBUF_INIT;
 	const char *cp;
-	char *dp, *qref;
-	int len, baselen, ch;
+	int ch;
 
-	baselen = strlen(base);
-	len = baselen + 2; /* '/' after base and terminating NUL */
-	for (cp = ref; (ch = *cp) != 0; cp++, len++)
+	strbuf_addstr(&buf, base);
+	if (buf.len && buf.buf[buf.len - 1] != '/' && *ref != '/')
+		strbuf_addstr(&buf, "/");
+
+	for (cp = ref; (ch = *cp) != 0; cp++)
 		if (needs_quote(ch))
-			len += 2; /* extra two hex plus replacement % */
-	qref = xmalloc(len);
-	memcpy(qref, base, baselen);
-	dp = qref + baselen;
-	*(dp++) = '/';
-	for (cp = ref; (ch = *cp) != 0; cp++) {
-		if (needs_quote(ch)) {
-			*dp++ = '%';
-			*dp++ = hex((ch >> 4) & 0xF);
-			*dp++ = hex(ch & 0xF);
-		}
+			strbuf_addf(&buf, "%%%02x", ch);
 		else
-			*dp++ = ch;
-	}
-	*dp = 0;
+			strbuf_addch(&buf, *cp);
 
-	return qref;
+	return strbuf_detach(&buf, NULL);
 }
 
 int http_fetch_ref(const char *base, struct ref *ref)
diff --git a/http.h b/http.h
index 905b462..26abebe 100644
--- a/http.h
+++ b/http.h
@@ -37,6 +37,10 @@
 #define CURLE_HTTP_RETURNED_ERROR CURLE_HTTP_NOT_FOUND
 #endif
 
+#if LIBCURL_VERSION_NUM < 0x070c03
+#define NO_CURL_IOCTL
+#endif
+
 struct slot_results
 {
 	CURLcode curl_result;
@@ -67,6 +71,9 @@
 extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 extern size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 extern size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+#ifndef NO_CURL_IOCTL
+extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
+#endif
 
 /* Slot lifecycle functions */
 extern struct active_request_slot *get_active_slot(void);
diff --git a/ident.c b/ident.c
index 09cf0c9..99f1c85 100644
--- a/ident.c
+++ b/ident.c
@@ -121,6 +121,7 @@
 		c == '<' ||
 		c == '>' ||
 		c == '"' ||
+		c == '\\' ||
 		c == '\'';
 }
 
diff --git a/imap-send.c b/imap-send.c
index 1ec1310..8154cb2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -23,71 +23,75 @@
  */
 
 #include "cache.h"
+#include "exec_cmd.h"
+#ifdef NO_OPENSSL
+typedef void *SSL;
+#endif
 
-typedef struct store_conf {
+struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
 	char *map_inbox;
 	char *trash;
 	unsigned max_size; /* off_t is overkill */
 	unsigned trash_remote_new:1, trash_only_new:1;
-} store_conf_t;
+};
 
-typedef struct string_list {
+struct string_list {
 	struct string_list *next;
 	char string[1];
-} string_list_t;
+};
 
-typedef struct channel_conf {
+struct channel_conf {
 	struct channel_conf *next;
 	char *name;
-	store_conf_t *master, *slave;
+	struct store_conf *master, *slave;
 	char *master_name, *slave_name;
 	char *sync_state;
-	string_list_t *patterns;
+	struct string_list *patterns;
 	int mops, sops;
 	unsigned max_messages; /* for slave only */
-} channel_conf_t;
+};
 
-typedef struct group_conf {
+struct group_conf {
 	struct group_conf *next;
 	char *name;
-	string_list_t *channels;
-} group_conf_t;
+	struct string_list *channels;
+};
 
 /* For message->status */
 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
 #define M_DEAD         (1<<1) /* expunged */
 #define M_FLAGS        (1<<2) /* flags fetched */
 
-typedef struct message {
+struct message {
 	struct message *next;
-	/* string_list_t *keywords; */
+	/* struct string_list *keywords; */
 	size_t size; /* zero implies "not fetched" */
 	int uid;
 	unsigned char flags, status;
-} message_t;
+};
 
-typedef struct store {
-	store_conf_t *conf; /* foreign */
+struct store {
+	struct store_conf *conf; /* foreign */
 
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	char *path; /* own */
-	message_t *msgs; /* own */
+	struct message *msgs; /* own */
 	int uidvalidity;
 	unsigned char opts; /* maybe preset? */
 	/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
 	int count; /* # of messages */
 	int recent; /* # of recent messages - don't trust this beyond the initial read */
-} store_t;
+};
 
-typedef struct {
+struct msg_data {
 	char *data;
 	int len;
 	unsigned char flags;
 	unsigned int crlf:1;
-} msg_data_t;
+};
 
 #define DRV_OK          0
 #define DRV_MSG_BAD     -1
@@ -96,14 +100,14 @@
 
 static int Verbose, Quiet;
 
-static void imap_info( const char *, ... );
-static void imap_warn( const char *, ... );
+static void imap_info(const char *, ...);
+static void imap_warn(const char *, ...);
 
-static char *next_arg( char ** );
+static char *next_arg(char **);
 
-static void free_generic_messages( message_t * );
+static void free_generic_messages(struct message *);
 
-static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
+static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
 {
@@ -112,74 +116,78 @@
 
 	len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
 	if (len < 0)
-		die("Fatal: Out of memory\n");
+		die("Fatal: Out of memory");
 	if (len >= sizeof(tmp))
-		die("imap command overflow !\n");
+		die("imap command overflow!");
 	*strp = xmemdupz(tmp, len);
 	return len;
 }
 
-static void arc4_init( void );
-static unsigned char arc4_getbyte( void );
+static void arc4_init(void);
+static unsigned char arc4_getbyte(void);
 
-typedef struct imap_server_conf {
+struct imap_server_conf {
 	char *name;
 	char *tunnel;
 	char *host;
 	int port;
 	char *user;
 	char *pass;
-} imap_server_conf_t;
+	int use_ssl;
+	int ssl_verify;
+	int use_html;
+};
 
-typedef struct imap_store_conf {
-	store_conf_t gen;
-	imap_server_conf_t *server;
+struct imap_store_conf {
+	struct store_conf gen;
+	struct imap_server_conf *server;
 	unsigned use_namespace:1;
-} imap_store_conf_t;
+};
 
-#define NIL	(void*)0x1
-#define LIST	(void*)0x2
+#define NIL	(void *)0x1
+#define LIST	(void *)0x2
 
-typedef struct _list {
-	struct _list *next, *child;
+struct imap_list {
+	struct imap_list *next, *child;
 	char *val;
 	int len;
-} list_t;
+};
 
-typedef struct {
+struct imap_socket {
 	int fd;
-} Socket_t;
+	SSL *ssl;
+};
 
-typedef struct {
-	Socket_t sock;
+struct imap_buffer {
+	struct imap_socket sock;
 	int bytes;
 	int offset;
 	char buf[1024];
-} buffer_t;
+};
 
 struct imap_cmd;
 
-typedef struct imap {
+struct imap {
 	int uidnext; /* from SELECT responses */
-	list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
+	struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
 	unsigned caps, rcaps; /* CAPABILITY results */
 	/* command queue */
 	int nexttag, num_in_progress, literal_pending;
 	struct imap_cmd *in_progress, **in_progress_append;
-	buffer_t buf; /* this is BIG, so put it last */
-} imap_t;
+	struct imap_buffer buf; /* this is BIG, so put it last */
+};
 
-typedef struct imap_store {
-	store_t gen;
+struct imap_store {
+	struct store gen;
 	int uidvalidity;
-	imap_t *imap;
+	struct imap *imap;
 	const char *prefix;
 	unsigned /*currentnc:1,*/ trashnc:1;
-} imap_store_t;
+};
 
 struct imap_cmd_cb {
-	int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt );
-	void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response);
+	int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
+	void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
 	void *ctx;
 	char *data;
 	int dlen;
@@ -201,6 +209,7 @@
 	UIDPLUS,
 	LITERALPLUS,
 	NAMESPACE,
+	STARTTLS,
 };
 
 static const char *cap_list[] = {
@@ -208,13 +217,14 @@
 	"UIDPLUS",
 	"LITERAL+",
 	"NAMESPACE",
+	"STARTTLS",
 };
 
 #define RESP_OK    0
 #define RESP_NO    1
 #define RESP_BAD   2
 
-static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
 
 
 static const char *Flags[] = {
@@ -225,42 +235,137 @@
 	"Deleted",
 };
 
-static void
-socket_perror( const char *func, Socket_t *sock, int ret )
+#ifndef NO_OPENSSL
+static void ssl_socket_perror(const char *func)
 {
-	if (ret < 0)
-		perror( func );
+	fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
+}
+#endif
+
+static void socket_perror(const char *func, struct imap_socket *sock, int ret)
+{
+#ifndef NO_OPENSSL
+	if (sock->ssl) {
+		int sslerr = SSL_get_error(sock->ssl, ret);
+		switch (sslerr) {
+		case SSL_ERROR_NONE:
+			break;
+		case SSL_ERROR_SYSCALL:
+			perror("SSL_connect");
+			break;
+		default:
+			ssl_socket_perror("SSL_connect");
+			break;
+		}
+	} else
+#endif
+	{
+		if (ret < 0)
+			perror(func);
+		else
+			fprintf(stderr, "%s: unexpected EOF\n", func);
+	}
+}
+
+static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+{
+#ifdef NO_OPENSSL
+	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+	return -1;
+#else
+	SSL_METHOD *meth;
+	SSL_CTX *ctx;
+	int ret;
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	if (use_tls_only)
+		meth = TLSv1_method();
 	else
-		fprintf( stderr, "%s: unexpected EOF\n", func );
+		meth = SSLv23_method();
+
+	if (!meth) {
+		ssl_socket_perror("SSLv23_method");
+		return -1;
+	}
+
+	ctx = SSL_CTX_new(meth);
+
+	if (verify)
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+
+	if (!SSL_CTX_set_default_verify_paths(ctx)) {
+		ssl_socket_perror("SSL_CTX_set_default_verify_paths");
+		return -1;
+	}
+	sock->ssl = SSL_new(ctx);
+	if (!sock->ssl) {
+		ssl_socket_perror("SSL_new");
+		return -1;
+	}
+	if (!SSL_set_fd(sock->ssl, sock->fd)) {
+		ssl_socket_perror("SSL_set_fd");
+		return -1;
+	}
+
+	ret = SSL_connect(sock->ssl);
+	if (ret <= 0) {
+		socket_perror("SSL_connect", sock, ret);
+		return -1;
+	}
+
+	return 0;
+#endif
 }
 
-static int
-socket_read( Socket_t *sock, char *buf, int len )
+static int socket_read(struct imap_socket *sock, char *buf, int len)
 {
-	ssize_t n = xread( sock->fd, buf, len );
+	ssize_t n;
+#ifndef NO_OPENSSL
+	if (sock->ssl)
+		n = SSL_read(sock->ssl, buf, len);
+	else
+#endif
+		n = xread(sock->fd, buf, len);
 	if (n <= 0) {
-		socket_perror( "read", sock, n );
-		close( sock->fd );
+		socket_perror("read", sock, n);
+		close(sock->fd);
 		sock->fd = -1;
 	}
 	return n;
 }
 
-static int
-socket_write( Socket_t *sock, const char *buf, int len )
+static int socket_write(struct imap_socket *sock, const char *buf, int len)
 {
-	int n = write_in_full( sock->fd, buf, len );
+	int n;
+#ifndef NO_OPENSSL
+	if (sock->ssl)
+		n = SSL_write(sock->ssl, buf, len);
+	else
+#endif
+		n = write_in_full(sock->fd, buf, len);
 	if (n != len) {
-		socket_perror( "write", sock, n );
-		close( sock->fd );
+		socket_perror("write", sock, n);
+		close(sock->fd);
 		sock->fd = -1;
 	}
 	return n;
 }
 
+static void socket_shutdown(struct imap_socket *sock)
+{
+#ifndef NO_OPENSSL
+	if (sock->ssl) {
+		SSL_shutdown(sock->ssl);
+		SSL_free(sock->ssl);
+	}
+#endif
+	close(sock->fd);
+}
+
 /* simple line buffering */
-static int
-buffer_gets( buffer_t * b, char **s )
+static int buffer_gets(struct imap_buffer *b, char **s)
 {
 	int n;
 	int start = b->offset;
@@ -274,7 +379,7 @@
 				/* shift down used bytes */
 				*s = b->buf;
 
-				assert( start <= b->bytes );
+				assert(start <= b->bytes);
 				n = b->bytes - start;
 
 				if (n)
@@ -284,8 +389,8 @@
 				start = 0;
 			}
 
-			n = socket_read( &b->sock, b->buf + b->bytes,
-					 sizeof(b->buf) - b->bytes );
+			n = socket_read(&b->sock, b->buf + b->bytes,
+					 sizeof(b->buf) - b->bytes);
 
 			if (n <= 0)
 				return -1;
@@ -294,12 +399,12 @@
 		}
 
 		if (b->buf[b->offset] == '\r') {
-			assert( b->offset + 1 < b->bytes );
+			assert(b->offset + 1 < b->bytes);
 			if (b->buf[b->offset + 1] == '\n') {
 				b->buf[b->offset] = 0;  /* terminate the string */
 				b->offset += 2; /* next line */
 				if (Verbose)
-					puts( *s );
+					puts(*s);
 				return 0;
 			}
 		}
@@ -309,39 +414,36 @@
 	/* not reached */
 }
 
-static void
-imap_info( const char *msg, ... )
+static void imap_info(const char *msg, ...)
 {
 	va_list va;
 
 	if (!Quiet) {
-		va_start( va, msg );
-		vprintf( msg, va );
-		va_end( va );
-		fflush( stdout );
+		va_start(va, msg);
+		vprintf(msg, va);
+		va_end(va);
+		fflush(stdout);
 	}
 }
 
-static void
-imap_warn( const char *msg, ... )
+static void imap_warn(const char *msg, ...)
 {
 	va_list va;
 
 	if (Quiet < 2) {
-		va_start( va, msg );
-		vfprintf( stderr, msg, va );
-		va_end( va );
+		va_start(va, msg);
+		vfprintf(stderr, msg, va);
+		va_end(va);
 	}
 }
 
-static char *
-next_arg( char **s )
+static char *next_arg(char **s)
 {
 	char *ret;
 
 	if (!s || !*s)
 		return NULL;
-	while (isspace( (unsigned char) **s ))
+	while (isspace((unsigned char) **s))
 		(*s)++;
 	if (!**s) {
 		*s = NULL;
@@ -350,10 +452,10 @@
 	if (**s == '"') {
 		++*s;
 		ret = *s;
-		*s = strchr( *s, '"' );
+		*s = strchr(*s, '"');
 	} else {
 		ret = *s;
-		while (**s && !isspace( (unsigned char) **s ))
+		while (**s && !isspace((unsigned char) **s))
 			(*s)++;
 	}
 	if (*s) {
@@ -365,27 +467,25 @@
 	return ret;
 }
 
-static void
-free_generic_messages( message_t *msgs )
+static void free_generic_messages(struct message *msgs)
 {
-	message_t *tmsg;
+	struct message *tmsg;
 
 	for (; msgs; msgs = tmsg) {
 		tmsg = msgs->next;
-		free( msgs );
+		free(msgs);
 	}
 }
 
-static int
-nfsnprintf( char *buf, int blen, const char *fmt, ... )
+static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 {
 	int ret;
 	va_list va;
 
-	va_start( va, fmt );
-	if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
-		die( "Fatal: buffer too small. Please report a bug.\n");
-	va_end( va );
+	va_start(va, fmt);
+	if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
+		die("Fatal: buffer too small. Please report a bug.");
+	va_end(va);
 	return ret;
 }
 
@@ -393,21 +493,20 @@
 	unsigned char i, j, s[256];
 } rs;
 
-static void
-arc4_init( void )
+static void arc4_init(void)
 {
 	int i, fd;
 	unsigned char j, si, dat[128];
 
-	if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
-		fprintf( stderr, "Fatal: no random number source available.\n" );
-		exit( 3 );
+	if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
+		fprintf(stderr, "Fatal: no random number source available.\n");
+		exit(3);
 	}
-	if (read_in_full( fd, dat, 128 ) != 128) {
-		fprintf( stderr, "Fatal: cannot read random number source.\n" );
-		exit( 3 );
+	if (read_in_full(fd, dat, 128) != 128) {
+		fprintf(stderr, "Fatal: cannot read random number source.\n");
+		exit(3);
 	}
-	close( fd );
+	close(fd);
 
 	for (i = 0; i < 256; i++)
 		rs.s[i] = i;
@@ -423,8 +522,7 @@
 		arc4_getbyte();
 }
 
-static unsigned char
-arc4_getbyte( void )
+static unsigned char arc4_getbyte(void)
 {
 	unsigned char si, sj;
 
@@ -437,54 +535,53 @@
 	return rs.s[(si + sj) & 0xff];
 }
 
-static struct imap_cmd *
-v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
-		  const char *fmt, va_list ap )
+static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
+					 struct imap_cmd_cb *cb,
+					 const char *fmt, va_list ap)
 {
-	imap_t *imap = ctx->imap;
+	struct imap *imap = ctx->imap;
 	struct imap_cmd *cmd;
 	int n, bufl;
 	char buf[1024];
 
-	cmd = xmalloc( sizeof(struct imap_cmd) );
-	nfvasprintf( &cmd->cmd, fmt, ap );
+	cmd = xmalloc(sizeof(struct imap_cmd));
+	nfvasprintf(&cmd->cmd, fmt, ap);
 	cmd->tag = ++imap->nexttag;
 
 	if (cb)
 		cmd->cb = *cb;
 	else
-		memset( &cmd->cb, 0, sizeof(cmd->cb) );
+		memset(&cmd->cb, 0, sizeof(cmd->cb));
 
 	while (imap->literal_pending)
-		get_cmd_result( ctx, NULL );
+		get_cmd_result(ctx, NULL);
 
-	bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
+	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
 			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen );
+			   cmd->tag, cmd->cmd, cmd->cb.dlen);
 	if (Verbose) {
 		if (imap->num_in_progress)
-			printf( "(%d in progress) ", imap->num_in_progress );
-		if (memcmp( cmd->cmd, "LOGIN", 5 ))
-			printf( ">>> %s", buf );
+			printf("(%d in progress) ", imap->num_in_progress);
+		if (memcmp(cmd->cmd, "LOGIN", 5))
+			printf(">>> %s", buf);
 		else
-			printf( ">>> %d LOGIN <user> <pass>\n", cmd->tag );
+			printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
 	}
-	if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
-		free( cmd->cmd );
-		free( cmd );
+	if (socket_write(&imap->buf.sock, buf, bufl) != bufl) {
+		free(cmd->cmd);
+		free(cmd);
 		if (cb)
-			free( cb->data );
+			free(cb->data);
 		return NULL;
 	}
 	if (cmd->cb.data) {
 		if (CAP(LITERALPLUS)) {
-			n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen );
-			free( cmd->cb.data );
+			n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
+			free(cmd->cb.data);
 			if (n != cmd->cb.dlen ||
-			    (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2)
-			{
-				free( cmd->cmd );
-				free( cmd );
+			    socket_write(&imap->buf.sock, "\r\n", 2) != 2) {
+				free(cmd->cmd);
+				free(cmd);
 				return NULL;
 			}
 			cmd->cb.data = NULL;
@@ -499,109 +596,106 @@
 	return cmd;
 }
 
-static struct imap_cmd *
-issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
+				       struct imap_cmd_cb *cb,
+				       const char *fmt, ...)
 {
 	struct imap_cmd *ret;
 	va_list ap;
 
-	va_start( ap, fmt );
-	ret = v_issue_imap_cmd( ctx, cb, fmt, ap );
-	va_end( ap );
+	va_start(ap, fmt);
+	ret = v_issue_imap_cmd(ctx, cb, fmt, ap);
+	va_end(ap);
 	return ret;
 }
 
-static int
-imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
+		     const char *fmt, ...)
 {
 	va_list ap;
 	struct imap_cmd *cmdp;
 
-	va_start( ap, fmt );
-	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
-	va_end( ap );
+	va_start(ap, fmt);
+	cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+	va_end(ap);
 	if (!cmdp)
 		return RESP_BAD;
 
-	return get_cmd_result( ctx, cmdp );
+	return get_cmd_result(ctx, cmdp);
 }
 
-static int
-imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
+		       const char *fmt, ...)
 {
 	va_list ap;
 	struct imap_cmd *cmdp;
 
-	va_start( ap, fmt );
-	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
-	va_end( ap );
+	va_start(ap, fmt);
+	cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+	va_end(ap);
 	if (!cmdp)
 		return DRV_STORE_BAD;
 
-	switch (get_cmd_result( ctx, cmdp )) {
+	switch (get_cmd_result(ctx, cmdp)) {
 	case RESP_BAD: return DRV_STORE_BAD;
 	case RESP_NO: return DRV_MSG_BAD;
 	default: return DRV_OK;
 	}
 }
 
-static int
-is_atom( list_t *list )
+static int is_atom(struct imap_list *list)
 {
 	return list && list->val && list->val != NIL && list->val != LIST;
 }
 
-static int
-is_list( list_t *list )
+static int is_list(struct imap_list *list)
 {
 	return list && list->val == LIST;
 }
 
-static void
-free_list( list_t *list )
+static void free_list(struct imap_list *list)
 {
-	list_t *tmp;
+	struct imap_list *tmp;
 
 	for (; list; list = tmp) {
 		tmp = list->next;
-		if (is_list( list ))
-			free_list( list->child );
-		else if (is_atom( list ))
-			free( list->val );
-		free( list );
+		if (is_list(list))
+			free_list(list->child);
+		else if (is_atom(list))
+			free(list->val);
+		free(list);
 	}
 }
 
-static int
-parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
+static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level)
 {
-	list_t *cur;
+	struct imap_list *cur;
 	char *s = *sp, *p;
 	int n, bytes;
 
 	for (;;) {
-		while (isspace( (unsigned char)*s ))
+		while (isspace((unsigned char)*s))
 			s++;
 		if (level && *s == ')') {
 			s++;
 			break;
 		}
-		*curp = cur = xmalloc( sizeof(*cur) );
+		*curp = cur = xmalloc(sizeof(*cur));
 		curp = &cur->next;
 		cur->val = NULL; /* for clean bail */
 		if (*s == '(') {
 			/* sublist */
 			s++;
 			cur->val = LIST;
-			if (parse_imap_list_l( imap, &s, &cur->child, level + 1 ))
+			if (parse_imap_list_l(imap, &s, &cur->child, level + 1))
 				goto bail;
 		} else if (imap && *s == '{') {
 			/* literal */
-			bytes = cur->len = strtol( s + 1, &s, 10 );
+			bytes = cur->len = strtol(s + 1, &s, 10);
 			if (*s != '}')
 				goto bail;
 
-			s = cur->val = xmalloc( cur->len );
+			s = cur->val = xmalloc(cur->len);
 
 			/* dump whats left over in the input buffer */
 			n = imap->buf.bytes - imap->buf.offset;
@@ -610,7 +704,7 @@
 				/* the entire message fit in the buffer */
 				n = bytes;
 
-			memcpy( s, imap->buf.buf + imap->buf.offset, n );
+			memcpy(s, imap->buf.buf + imap->buf.offset, n);
 			s += n;
 			bytes -= n;
 
@@ -619,13 +713,13 @@
 
 			/* now read the rest of the message */
 			while (bytes > 0) {
-				if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0)
+				if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0)
 					goto bail;
 				s += n;
 				bytes -= n;
 			}
 
-			if (buffer_gets( &imap->buf, &s ))
+			if (buffer_gets(&imap->buf, &s))
 				goto bail;
 		} else if (*s == '"') {
 			/* quoted string */
@@ -640,15 +734,14 @@
 		} else {
 			/* atom */
 			p = s;
-			for (; *s && !isspace( (unsigned char)*s ); s++)
+			for (; *s && !isspace((unsigned char)*s); s++)
 				if (level && *s == ')')
 					break;
 			cur->len = s - p;
-			if (cur->len == 3 && !memcmp ("NIL", p, 3)) {
+			if (cur->len == 3 && !memcmp("NIL", p, 3))
 				cur->val = NIL;
-			} else {
+			else
 				cur->val = xmemdupz(p, cur->len);
-			}
 		}
 
 		if (!level)
@@ -660,127 +753,122 @@
 	*curp = NULL;
 	return 0;
 
-  bail:
+bail:
 	*curp = NULL;
 	return -1;
 }
 
-static list_t *
-parse_imap_list( imap_t *imap, char **sp )
+static struct imap_list *parse_imap_list(struct imap *imap, char **sp)
 {
-	list_t *head;
+	struct imap_list *head;
 
-	if (!parse_imap_list_l( imap, sp, &head, 0 ))
+	if (!parse_imap_list_l(imap, sp, &head, 0))
 		return head;
-	free_list( head );
+	free_list(head);
 	return NULL;
 }
 
-static list_t *
-parse_list( char **sp )
+static struct imap_list *parse_list(char **sp)
 {
-	return parse_imap_list( NULL, sp );
+	return parse_imap_list(NULL, sp);
 }
 
-static void
-parse_capability( imap_t *imap, char *cmd )
+static void parse_capability(struct imap *imap, char *cmd)
 {
 	char *arg;
 	unsigned i;
 
 	imap->caps = 0x80000000;
-	while ((arg = next_arg( &cmd )))
+	while ((arg = next_arg(&cmd)))
 		for (i = 0; i < ARRAY_SIZE(cap_list); i++)
-			if (!strcmp( cap_list[i], arg ))
+			if (!strcmp(cap_list[i], arg))
 				imap->caps |= 1 << i;
 	imap->rcaps = imap->caps;
 }
 
-static int
-parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s )
+static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
+			       char *s)
 {
-	imap_t *imap = ctx->imap;
+	struct imap *imap = ctx->imap;
 	char *arg, *p;
 
 	if (*s != '[')
 		return RESP_OK;		/* no response code */
 	s++;
-	if (!(p = strchr( s, ']' ))) {
-		fprintf( stderr, "IMAP error: malformed response code\n" );
+	if (!(p = strchr(s, ']'))) {
+		fprintf(stderr, "IMAP error: malformed response code\n");
 		return RESP_BAD;
 	}
 	*p++ = 0;
-	arg = next_arg( &s );
-	if (!strcmp( "UIDVALIDITY", arg )) {
-		if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) {
-			fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" );
+	arg = next_arg(&s);
+	if (!strcmp("UIDVALIDITY", arg)) {
+		if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg))) {
+			fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
 			return RESP_BAD;
 		}
-	} else if (!strcmp( "UIDNEXT", arg )) {
-		if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) {
-			fprintf( stderr, "IMAP error: malformed NEXTUID status\n" );
+	} else if (!strcmp("UIDNEXT", arg)) {
+		if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) {
+			fprintf(stderr, "IMAP error: malformed NEXTUID status\n");
 			return RESP_BAD;
 		}
-	} else if (!strcmp( "CAPABILITY", arg )) {
-		parse_capability( imap, s );
-	} else if (!strcmp( "ALERT", arg )) {
+	} else if (!strcmp("CAPABILITY", arg)) {
+		parse_capability(imap, s);
+	} else if (!strcmp("ALERT", arg)) {
 		/* RFC2060 says that these messages MUST be displayed
 		 * to the user
 		 */
-		for (; isspace( (unsigned char)*p ); p++);
-		fprintf( stderr, "*** IMAP ALERT *** %s\n", p );
-	} else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) {
-		if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) ||
-		    !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg )))
-		{
-			fprintf( stderr, "IMAP error: malformed APPENDUID status\n" );
+		for (; isspace((unsigned char)*p); p++);
+		fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
+	} else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
+		if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) ||
+		    !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
+			fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
 			return RESP_BAD;
 		}
 	}
 	return RESP_OK;
 }
 
-static int
-get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
 {
-	imap_t *imap = ctx->imap;
+	struct imap *imap = ctx->imap;
 	struct imap_cmd *cmdp, **pcmdp, *ncmdp;
 	char *cmd, *arg, *arg1, *p;
 	int n, resp, resp2, tag;
 
 	for (;;) {
-		if (buffer_gets( &imap->buf, &cmd ))
+		if (buffer_gets(&imap->buf, &cmd))
 			return RESP_BAD;
 
-		arg = next_arg( &cmd );
+		arg = next_arg(&cmd);
 		if (*arg == '*') {
-			arg = next_arg( &cmd );
+			arg = next_arg(&cmd);
 			if (!arg) {
-				fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
 				return RESP_BAD;
 			}
 
-			if (!strcmp( "NAMESPACE", arg )) {
-				imap->ns_personal = parse_list( &cmd );
-				imap->ns_other = parse_list( &cmd );
-				imap->ns_shared = parse_list( &cmd );
-			} else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) ||
-				   !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) {
-				if ((resp = parse_response_code( ctx, NULL, cmd )) != RESP_OK)
+			if (!strcmp("NAMESPACE", arg)) {
+				imap->ns_personal = parse_list(&cmd);
+				imap->ns_other = parse_list(&cmd);
+				imap->ns_shared = parse_list(&cmd);
+			} else if (!strcmp("OK", arg) || !strcmp("BAD", arg) ||
+				   !strcmp("NO", arg) || !strcmp("BYE", arg)) {
+				if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
 					return resp;
-			} else if (!strcmp( "CAPABILITY", arg ))
-				parse_capability( imap, cmd );
-			else if ((arg1 = next_arg( &cmd ))) {
-				if (!strcmp( "EXISTS", arg1 ))
-					ctx->gen.count = atoi( arg );
-				else if (!strcmp( "RECENT", arg1 ))
-					ctx->gen.recent = atoi( arg );
+			} else if (!strcmp("CAPABILITY", arg))
+				parse_capability(imap, cmd);
+			else if ((arg1 = next_arg(&cmd))) {
+				if (!strcmp("EXISTS", arg1))
+					ctx->gen.count = atoi(arg);
+				else if (!strcmp("RECENT", arg1))
+					ctx->gen.recent = atoi(arg);
 			} else {
-				fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
 				return RESP_BAD;
 			}
 		} else if (!imap->in_progress) {
-			fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
+			fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "");
 			return RESP_BAD;
 		} else if (*arg == '+') {
 			/* This can happen only with the last command underway, as
@@ -788,57 +876,57 @@
 			cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
 			       offsetof(struct imap_cmd, next));
 			if (cmdp->cb.data) {
-				n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen );
-				free( cmdp->cb.data );
+				n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen);
+				free(cmdp->cb.data);
 				cmdp->cb.data = NULL;
 				if (n != (int)cmdp->cb.dlen)
 					return RESP_BAD;
 			} else if (cmdp->cb.cont) {
-				if (cmdp->cb.cont( ctx, cmdp, cmd ))
+				if (cmdp->cb.cont(ctx, cmdp, cmd))
 					return RESP_BAD;
 			} else {
-				fprintf( stderr, "IMAP error: unexpected command continuation request\n" );
+				fprintf(stderr, "IMAP error: unexpected command continuation request\n");
 				return RESP_BAD;
 			}
-			if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2)
+			if (socket_write(&imap->buf.sock, "\r\n", 2) != 2)
 				return RESP_BAD;
 			if (!cmdp->cb.cont)
 				imap->literal_pending = 0;
 			if (!tcmd)
 				return DRV_OK;
 		} else {
-			tag = atoi( arg );
+			tag = atoi(arg);
 			for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
 				if (cmdp->tag == tag)
 					goto gottag;
-			fprintf( stderr, "IMAP error: unexpected tag %s\n", arg );
+			fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
 			return RESP_BAD;
-		  gottag:
+		gottag:
 			if (!(*pcmdp = cmdp->next))
 				imap->in_progress_append = pcmdp;
 			imap->num_in_progress--;
 			if (cmdp->cb.cont || cmdp->cb.data)
 				imap->literal_pending = 0;
-			arg = next_arg( &cmd );
-			if (!strcmp( "OK", arg ))
+			arg = next_arg(&cmd);
+			if (!strcmp("OK", arg))
 				resp = DRV_OK;
 			else {
-				if (!strcmp( "NO", arg )) {
-					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */
-						p = strchr( cmdp->cmd, '"' );
-						if (!issue_imap_cmd( ctx, NULL, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) {
+				if (!strcmp("NO", arg)) {
+					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
+						p = strchr(cmdp->cmd, '"');
+						if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
 							resp = RESP_BAD;
 							goto normal;
 						}
 						/* not waiting here violates the spec, but a server that does not
 						   grok this nonetheless violates it too. */
 						cmdp->cb.create = 0;
-						if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) {
+						if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) {
 							resp = RESP_BAD;
 							goto normal;
 						}
-						free( cmdp->cmd );
-						free( cmdp );
+						free(cmdp->cmd);
+						free(cmdp);
 						if (!tcmd)
 							return 0;	/* ignored */
 						if (cmdp == tcmd)
@@ -846,21 +934,21 @@
 						continue;
 					}
 					resp = RESP_NO;
-				} else /*if (!strcmp( "BAD", arg ))*/
+				} else /*if (!strcmp("BAD", arg))*/
 					resp = RESP_BAD;
-				fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n",
-					 memcmp (cmdp->cmd, "LOGIN", 5) ?
+				fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
+					 memcmp(cmdp->cmd, "LOGIN", 5) ?
 							cmdp->cmd : "LOGIN <user> <pass>",
 							arg, cmd ? cmd : "");
 			}
-			if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp)
+			if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
 				resp = resp2;
-		  normal:
+		normal:
 			if (cmdp->cb.done)
-				cmdp->cb.done( ctx, cmdp, resp );
-			free( cmdp->cb.data );
-			free( cmdp->cmd );
-			free( cmdp );
+				cmdp->cb.done(ctx, cmdp, resp);
+			free(cmdp->cb.data);
+			free(cmdp->cmd);
+			free(cmdp);
 			if (!tcmd || tcmd == cmdp)
 				return resp;
 		}
@@ -868,170 +956,184 @@
 	/* not reached */
 }
 
-static void
-imap_close_server( imap_store_t *ictx )
+static void imap_close_server(struct imap_store *ictx)
 {
-	imap_t *imap = ictx->imap;
+	struct imap *imap = ictx->imap;
 
 	if (imap->buf.sock.fd != -1) {
-		imap_exec( ictx, NULL, "LOGOUT" );
-		close( imap->buf.sock.fd );
+		imap_exec(ictx, NULL, "LOGOUT");
+		socket_shutdown(&imap->buf.sock);
 	}
-	free_list( imap->ns_personal );
-	free_list( imap->ns_other );
-	free_list( imap->ns_shared );
-	free( imap );
+	free_list(imap->ns_personal);
+	free_list(imap->ns_other);
+	free_list(imap->ns_shared);
+	free(imap);
 }
 
-static void
-imap_close_store( store_t *ctx )
+static void imap_close_store(struct store *ctx)
 {
-	imap_close_server( (imap_store_t *)ctx );
-	free_generic_messages( ctx->msgs );
-	free( ctx );
+	imap_close_server((struct imap_store *)ctx);
+	free_generic_messages(ctx->msgs);
+	free(ctx);
 }
 
-static store_t *
-imap_open_store( imap_server_conf_t *srvc )
+static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
-	imap_store_t *ctx;
-	imap_t *imap;
+	struct imap_store *ctx;
+	struct imap *imap;
 	char *arg, *rsp;
 	struct hostent *he;
 	struct sockaddr_in addr;
 	int s, a[2], preauth;
 	pid_t pid;
 
-	ctx = xcalloc( sizeof(*ctx), 1 );
+	ctx = xcalloc(sizeof(*ctx), 1);
 
-	ctx->imap = imap = xcalloc( sizeof(*imap), 1 );
+	ctx->imap = imap = xcalloc(sizeof(*imap), 1);
 	imap->buf.sock.fd = -1;
 	imap->in_progress_append = &imap->in_progress;
 
 	/* open connection to IMAP server */
 
 	if (srvc->tunnel) {
-		imap_info( "Starting tunnel '%s'... ", srvc->tunnel );
+		imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-		if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
-			perror( "socketpair" );
-			exit( 1 );
+		if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
+			perror("socketpair");
+			exit(1);
 		}
 
 		pid = fork();
 		if (pid < 0)
-			_exit( 127 );
+			_exit(127);
 		if (!pid) {
-			if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)
-				_exit( 127 );
-			close( a[0] );
-			close( a[1] );
-			execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL );
-			_exit( 127 );
+			if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
+				_exit(127);
+			close(a[0]);
+			close(a[1]);
+			execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
+			_exit(127);
 		}
 
-		close (a[0]);
+		close(a[0]);
 
 		imap->buf.sock.fd = a[1];
 
-		imap_info( "ok\n" );
+		imap_info("ok\n");
 	} else {
-		memset( &addr, 0, sizeof(addr) );
-		addr.sin_port = htons( srvc->port );
+		memset(&addr, 0, sizeof(addr));
+		addr.sin_port = htons(srvc->port);
 		addr.sin_family = AF_INET;
 
-		imap_info( "Resolving %s... ", srvc->host );
-		he = gethostbyname( srvc->host );
+		imap_info("Resolving %s... ", srvc->host);
+		he = gethostbyname(srvc->host);
 		if (!he) {
-			perror( "gethostbyname" );
+			perror("gethostbyname");
 			goto bail;
 		}
-		imap_info( "ok\n" );
+		imap_info("ok\n");
 
 		addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
 
-		s = socket( PF_INET, SOCK_STREAM, 0 );
+		s = socket(PF_INET, SOCK_STREAM, 0);
 
-		imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
-		if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
-			close( s );
-			perror( "connect" );
+		imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+		if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
+			close(s);
+			perror("connect");
 			goto bail;
 		}
-		imap_info( "ok\n" );
 
 		imap->buf.sock.fd = s;
 
+		if (srvc->use_ssl &&
+		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+			close(s);
+			goto bail;
+		}
+		imap_info("ok\n");
 	}
 
 	/* read the greeting string */
-	if (buffer_gets( &imap->buf, &rsp )) {
-		fprintf( stderr, "IMAP error: no greeting response\n" );
+	if (buffer_gets(&imap->buf, &rsp)) {
+		fprintf(stderr, "IMAP error: no greeting response\n");
 		goto bail;
 	}
-	arg = next_arg( &rsp );
-	if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {
-		fprintf( stderr, "IMAP error: invalid greeting response\n" );
+	arg = next_arg(&rsp);
+	if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) {
+		fprintf(stderr, "IMAP error: invalid greeting response\n");
 		goto bail;
 	}
 	preauth = 0;
-	if (!strcmp( "PREAUTH", arg ))
+	if (!strcmp("PREAUTH", arg))
 		preauth = 1;
-	else if (strcmp( "OK", arg ) != 0) {
-		fprintf( stderr, "IMAP error: unknown greeting response\n" );
+	else if (strcmp("OK", arg) != 0) {
+		fprintf(stderr, "IMAP error: unknown greeting response\n");
 		goto bail;
 	}
-	parse_response_code( ctx, NULL, rsp );
-	if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK)
+	parse_response_code(ctx, NULL, rsp);
+	if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
 		goto bail;
 
 	if (!preauth) {
-
-		imap_info ("Logging in...\n");
+#ifndef NO_OPENSSL
+		if (!srvc->use_ssl && CAP(STARTTLS)) {
+			if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
+				goto bail;
+			if (ssl_socket_connect(&imap->buf.sock, 1,
+					       srvc->ssl_verify))
+				goto bail;
+			/* capabilities may have changed, so get the new capabilities */
+			if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
+				goto bail;
+		}
+#endif
+		imap_info("Logging in...\n");
 		if (!srvc->user) {
-			fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
+			fprintf(stderr, "Skipping server %s, no user\n", srvc->host);
 			goto bail;
 		}
 		if (!srvc->pass) {
 			char prompt[80];
-			sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host );
-			arg = getpass( prompt );
+			sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
+			arg = getpass(prompt);
 			if (!arg) {
-				perror( "getpass" );
-				exit( 1 );
+				perror("getpass");
+				exit(1);
 			}
 			if (!*arg) {
-				fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host );
+				fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
 				goto bail;
 			}
 			/*
 			 * getpass() returns a pointer to a static buffer.  make a copy
 			 * for long term storage.
 			 */
-			srvc->pass = xstrdup( arg );
+			srvc->pass = xstrdup(arg);
 		}
 		if (CAP(NOLOGIN)) {
-			fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
+			fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
 			goto bail;
 		}
-		imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
-		if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
-			fprintf( stderr, "IMAP error: LOGIN failed\n" );
+		if (!imap->buf.sock.ssl)
+			imap_warn("*** IMAP Warning *** Password is being "
+				  "sent in the clear\n");
+		if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+			fprintf(stderr, "IMAP error: LOGIN failed\n");
 			goto bail;
 		}
 	} /* !preauth */
 
 	ctx->prefix = "";
 	ctx->trashnc = 1;
-	return (store_t *)ctx;
+	return (struct store *)ctx;
 
-  bail:
-	imap_close_store( &ctx->gen );
+bail:
+	imap_close_store(&ctx->gen);
 	return NULL;
 }
 
-static int
-imap_make_flags( int flags, char *buf )
+static int imap_make_flags(int flags, char *buf)
 {
 	const char *s;
 	unsigned i, d;
@@ -1050,11 +1152,10 @@
 
 #define TUIDL 8
 
-static int
-imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
+static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
 {
-	imap_store_t *ctx = (imap_store_t *)gctx;
-	imap_t *imap = ctx->imap;
+	struct imap_store *ctx = (struct imap_store *)gctx;
+	struct imap *imap = ctx->imap;
 	struct imap_cmd_cb cb;
 	char *fmap, *buf;
 	const char *prefix, *box;
@@ -1062,14 +1163,14 @@
 	int start, sbreak = 0, ebreak = 0;
 	char flagstr[128], tuid[TUIDL * 2 + 1];
 
-	memset( &cb, 0, sizeof(cb) );
+	memset(&cb, 0, sizeof(cb));
 
 	fmap = data->data;
 	len = data->len;
 	nocr = !data->crlf;
 	extra = 0, i = 0;
 	if (!CAP(UIDPLUS) && uid) {
-	  nloop:
+	nloop:
 		start = i;
 		while (i < len)
 			if (fmap[i++] == '\n') {
@@ -1078,18 +1179,18 @@
 					sbreak = ebreak = i - 2 + nocr;
 					goto mktid;
 				}
-				if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
+				if (!memcmp(fmap + start, "X-TUID: ", 8)) {
 					extra -= (ebreak = i) - (sbreak = start) + nocr;
 					goto mktid;
 				}
 				goto nloop;
 			}
 		/* invalid message */
-		free( fmap );
+		free(fmap);
 		return DRV_MSG_BAD;
-	 mktid:
+	mktid:
 		for (j = 0; j < TUIDL; j++)
-			sprintf( tuid + j * 2, "%02x", arc4_getbyte() );
+			sprintf(tuid + j * 2, "%02x", arc4_getbyte());
 		extra += 8 + TUIDL * 2 + 2;
 	}
 	if (nocr)
@@ -1098,7 +1199,7 @@
 				extra++;
 
 	cb.dlen = len + extra;
-	buf = cb.data = xmalloc( cb.dlen );
+	buf = cb.data = xmalloc(cb.dlen);
 	i = 0;
 	if (!CAP(UIDPLUS) && uid) {
 		if (nocr) {
@@ -1109,12 +1210,12 @@
 				} else
 					*buf++ = fmap[i];
 		} else {
-			memcpy( buf, fmap, sbreak );
+			memcpy(buf, fmap, sbreak);
 			buf += sbreak;
 		}
-		memcpy( buf, "X-TUID: ", 8 );
+		memcpy(buf, "X-TUID: ", 8);
 		buf += 8;
-		memcpy( buf, tuid, TUIDL * 2 );
+		memcpy(buf, tuid, TUIDL * 2);
 		buf += TUIDL * 2;
 		*buf++ = '\r';
 		*buf++ = '\n';
@@ -1128,13 +1229,13 @@
 			} else
 				*buf++ = fmap[i];
 	} else
-		memcpy( buf, fmap + i, len - i );
+		memcpy(buf, fmap + i, len - i);
 
-	free( fmap );
+	free(fmap);
 
 	d = 0;
 	if (data->flags) {
-		d = imap_make_flags( data->flags, flagstr );
+		d = imap_make_flags(data->flags, flagstr);
 		flagstr[d++] = ' ';
 	}
 	flagstr[d] = 0;
@@ -1147,11 +1248,11 @@
 			imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
 	} else {
 		box = gctx->name;
-		prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
+		prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
 		cb.create = 0;
 	}
 	cb.ctx = uid;
-	ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
+	ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
 	imap->caps = imap->rcaps;
 	if (ret != DRV_OK)
 		return ret;
@@ -1163,15 +1264,60 @@
 	return DRV_OK;
 }
 
+static void encode_html_chars(struct strbuf *p)
+{
+	int i;
+	for (i = 0; i < p->len; i++) {
+		if (p->buf[i] == '&')
+			strbuf_splice(p, i, 1, "&amp;", 5);
+		if (p->buf[i] == '<')
+			strbuf_splice(p, i, 1, "&lt;", 4);
+		if (p->buf[i] == '>')
+			strbuf_splice(p, i, 1, "&gt;", 4);
+		if (p->buf[i] == '"')
+			strbuf_splice(p, i, 1, "&quot;", 6);
+	}
+}
+static void wrap_in_html(struct msg_data *msg)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf **lines;
+	struct strbuf **p;
+	static char *content_type = "Content-Type: text/html;\n";
+	static char *pre_open = "<pre>\n";
+	static char *pre_close = "</pre>\n";
+	int added_header = 0;
+
+	strbuf_attach(&buf, msg->data, msg->len, msg->len);
+	lines = strbuf_split(&buf, '\n');
+	strbuf_release(&buf);
+	for (p = lines; *p; p++) {
+		if (! added_header) {
+			if ((*p)->len == 1 && *((*p)->buf) == '\n') {
+				strbuf_addstr(&buf, content_type);
+				strbuf_addbuf(&buf, *p);
+				strbuf_addstr(&buf, pre_open);
+				added_header = 1;
+				continue;
+			}
+		}
+		else
+			encode_html_chars(*p);
+		strbuf_addbuf(&buf, *p);
+	}
+	strbuf_addstr(&buf, pre_close);
+	strbuf_list_free(lines);
+	msg->len  = buf.len;
+	msg->data = strbuf_detach(&buf, NULL);
+}
+
 #define CHUNKSIZE 0x1000
 
-static int
-read_message( FILE *f, msg_data_t *msg )
+static int read_message(FILE *f, struct msg_data *msg)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 
 	memset(msg, 0, sizeof(*msg));
-	strbuf_init(&buf, 0);
 
 	do {
 		if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
@@ -1183,8 +1329,7 @@
 	return msg->len;
 }
 
-static int
-count_messages( msg_data_t *msg )
+static int count_messages(struct msg_data *msg)
 {
 	int count = 0;
 	char *p = msg->data;
@@ -1194,7 +1339,7 @@
 			count++;
 			p += 5;
 		}
-		p = strstr( p+5, "\nFrom ");
+		p = strstr(p+5, "\nFrom ");
 		if (!p)
 			break;
 		p++;
@@ -1202,22 +1347,21 @@
 	return count;
 }
 
-static int
-split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
+static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 {
 	char *p, *data;
 
-	memset( msg, 0, sizeof *msg );
+	memset(msg, 0, sizeof *msg);
 	if (*ofs >= all_msgs->len)
 		return 0;
 
-	data = &all_msgs->data[ *ofs ];
+	data = &all_msgs->data[*ofs];
 	msg->len = all_msgs->len - *ofs;
 
 	if (msg->len < 5 || prefixcmp(data, "From "))
 		return 0;
 
-	p = strchr( data, '\n' );
+	p = strchr(data, '\n');
 	if (p) {
 		p = &p[1];
 		msg->len -= p-data;
@@ -1225,7 +1369,7 @@
 		data = p;
 	}
 
-	p = strstr( data, "\nFrom " );
+	p = strstr(data, "\nFrom ");
 	if (p)
 		msg->len = &p[1] - data;
 
@@ -1234,24 +1378,25 @@
 	return 1;
 }
 
-static imap_server_conf_t server =
-{
+static struct imap_server_conf server = {
 	NULL,	/* name */
 	NULL,	/* tunnel */
 	NULL,	/* host */
 	0,	/* port */
 	NULL,	/* user */
 	NULL,	/* pass */
+	0,   	/* use_ssl */
+	1,   	/* ssl_verify */
+	0,   	/* use_html */
 };
 
 static char *imap_folder;
 
-static int
-git_imap_config(const char *key, const char *val, void *cb)
+static int git_imap_config(const char *key, const char *val, void *cb)
 {
 	char imap_key[] = "imap.";
 
-	if (strncmp( key, imap_key, sizeof imap_key - 1 ))
+	if (strncmp(key, imap_key, sizeof imap_key - 1))
 		return 0;
 
 	if (!val)
@@ -1259,90 +1404,102 @@
 
 	key += sizeof imap_key - 1;
 
-	if (!strcmp( "folder", key )) {
-		imap_folder = xstrdup( val );
-	} else if (!strcmp( "host", key )) {
-		{
-			if (!prefixcmp(val, "imap:"))
-				val += 5;
-			if (!server.port)
-				server.port = 143;
+	if (!strcmp("folder", key)) {
+		imap_folder = xstrdup(val);
+	} else if (!strcmp("host", key)) {
+		if (!prefixcmp(val, "imap:"))
+			val += 5;
+		else if (!prefixcmp(val, "imaps:")) {
+			val += 6;
+			server.use_ssl = 1;
 		}
 		if (!prefixcmp(val, "//"))
 			val += 2;
-		server.host = xstrdup( val );
-	}
-	else if (!strcmp( "user", key ))
-		server.user = xstrdup( val );
-	else if (!strcmp( "pass", key ))
-		server.pass = xstrdup( val );
-	else if (!strcmp( "port", key ))
-		server.port = git_config_int( key, val );
-	else if (!strcmp( "tunnel", key ))
-		server.tunnel = xstrdup( val );
+		server.host = xstrdup(val);
+	} else if (!strcmp("user", key))
+		server.user = xstrdup(val);
+	else if (!strcmp("pass", key))
+		server.pass = xstrdup(val);
+	else if (!strcmp("port", key))
+		server.port = git_config_int(key, val);
+	else if (!strcmp("tunnel", key))
+		server.tunnel = xstrdup(val);
+	else if (!strcmp("sslverify", key))
+		server.ssl_verify = git_config_bool(key, val);
+	else if (!strcmp("preformattedHTML", key))
+		server.use_html = git_config_bool(key, val);
 	return 0;
 }
 
-int
-main(int argc, char **argv)
+int main(int argc, char **argv)
 {
-	msg_data_t all_msgs, msg;
-	store_t *ctx = NULL;
+	struct msg_data all_msgs, msg;
+	struct store *ctx = NULL;
 	int uid = 0;
 	int ofs = 0;
 	int r;
 	int total, n = 0;
+	int nongit_ok;
+
+	git_extract_argv0_path(argv[0]);
 
 	/* init the random number generator */
 	arc4_init();
 
+	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, NULL);
 
+	if (!server.port)
+		server.port = server.use_ssl ? 993 : 143;
+
 	if (!imap_folder) {
-		fprintf( stderr, "no imap store specified\n" );
+		fprintf(stderr, "no imap store specified\n");
 		return 1;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
-			fprintf( stderr, "no imap host specified\n" );
+			fprintf(stderr, "no imap host specified\n");
 			return 1;
 		}
 		server.host = "tunnel";
 	}
 
 	/* read the messages */
-	if (!read_message( stdin, &all_msgs )) {
-		fprintf(stderr,"nothing to send\n");
+	if (!read_message(stdin, &all_msgs)) {
+		fprintf(stderr, "nothing to send\n");
 		return 1;
 	}
 
-	total = count_messages( &all_msgs );
+	total = count_messages(&all_msgs);
 	if (!total) {
-		fprintf(stderr,"no messages to send\n");
+		fprintf(stderr, "no messages to send\n");
 		return 1;
 	}
 
 	/* write it to the imap server */
-	ctx = imap_open_store( &server );
+	ctx = imap_open_store(&server);
 	if (!ctx) {
-		fprintf( stderr,"failed to open store\n");
+		fprintf(stderr, "failed to open store\n");
 		return 1;
 	}
 
-	fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" );
+	fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
 	ctx->name = imap_folder;
 	while (1) {
 		unsigned percent = n * 100 / total;
-		fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total );
-		if (!split_msg( &all_msgs, &msg, &ofs ))
+		fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
+		if (!split_msg(&all_msgs, &msg, &ofs))
 			break;
-		r = imap_store_msg( ctx, &msg, &uid );
-		if (r != DRV_OK) break;
+		if (server.use_html)
+			wrap_in_html(&msg);
+		r = imap_store_msg(ctx, &msg, &uid);
+		if (r != DRV_OK)
+			break;
 		n++;
 	}
-	fprintf( stderr,"\n" );
+	fprintf(stderr, "\n");
 
-	imap_close_store( ctx );
+	imap_close_store(ctx);
 
 	return 0;
 }
diff --git a/index-pack.c b/index-pack.c
index c99a1a1..6e93ee6 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -8,6 +8,7 @@
 #include "tree.h"
 #include "progress.h"
 #include "fsck.h"
+#include "exec_cmd.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
@@ -67,7 +68,7 @@
 static unsigned char input_buffer[4096];
 static unsigned int input_offset, input_len;
 static off_t consumed_bytes;
-static SHA_CTX input_ctx;
+static git_SHA_CTX input_ctx;
 static uint32_t input_crc32;
 static int input_fd, output_fd, pack_fd;
 
@@ -119,7 +120,7 @@
 	if (input_offset) {
 		if (output_fd >= 0)
 			write_or_die(output_fd, input_buffer, input_offset);
-		SHA1_Update(&input_ctx, input_buffer, input_offset);
+		git_SHA1_Update(&input_ctx, input_buffer, input_offset);
 		memmove(input_buffer, input_buffer + input_offset, input_len);
 		input_offset = 0;
 	}
@@ -171,14 +172,13 @@
 		input_fd = 0;
 		if (!pack_name) {
 			static char tmpfile[PATH_MAX];
-			snprintf(tmpfile, sizeof(tmpfile),
-				 "%s/pack/tmp_pack_XXXXXX", get_object_directory());
-			output_fd = xmkstemp(tmpfile);
+			output_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
+						"pack/tmp_pack_XXXXXX");
 			pack_name = xstrdup(tmpfile);
 		} else
 			output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
 		if (output_fd < 0)
-			die("unable to create %s: %s\n", pack_name, strerror(errno));
+			die("unable to create %s: %s", pack_name, strerror(errno));
 		pack_fd = output_fd;
 	} else {
 		input_fd = open(pack_name, O_RDONLY);
@@ -188,7 +188,7 @@
 		output_fd = -1;
 		pack_fd = input_fd;
 	}
-	SHA1_Init(&input_ctx);
+	git_SHA1_Init(&input_ctx);
 	return pack_name;
 }
 
@@ -221,17 +221,23 @@
 	die("pack has bad object at offset %lu: %s", offset, buf);
 }
 
+static void free_base_data(struct base_data *c)
+{
+	if (c->data) {
+		free(c->data);
+		c->data = NULL;
+		base_cache_used -= c->size;
+	}
+}
+
 static void prune_base_data(struct base_data *retain)
 {
-	struct base_data *b = base_cache;
+	struct base_data *b;
 	for (b = base_cache;
 	     base_cache_used > delta_base_cache_limit && b;
 	     b = b->child) {
-		if (b->data && b != retain) {
-			free(b->data);
-			b->data = NULL;
-			base_cache_used -= b->size;
-		}
+		if (b->data && b != retain)
+			free_base_data(b);
 	}
 }
 
@@ -244,7 +250,8 @@
 
 	c->base = base;
 	c->child = NULL;
-	base_cache_used += c->size;
+	if (c->data)
+		base_cache_used += c->size;
 	prune_base_data(c);
 }
 
@@ -255,10 +262,7 @@
 		base->child = NULL;
 	else
 		base_cache = NULL;
-	if (c->data) {
-		free(c->data);
-		base_cache_used -= c->size;
-	}
+	free_base_data(c);
 }
 
 static void *unpack_entry_data(unsigned long offset, unsigned long size)
@@ -271,10 +275,10 @@
 	stream.avail_out = size;
 	stream.next_in = fill(1);
 	stream.avail_in = input_len;
-	inflateInit(&stream);
+	git_inflate_init(&stream);
 
 	for (;;) {
-		int ret = inflate(&stream, 0);
+		int ret = git_inflate(&stream, 0);
 		use(input_len - stream.avail_in);
 		if (stream.total_out == size && ret == Z_STREAM_END)
 			break;
@@ -283,7 +287,7 @@
 		stream.next_in = fill(1);
 		stream.avail_in = input_len;
 	}
-	inflateEnd(&stream);
+	git_inflate_end(&stream);
 	return buf;
 }
 
@@ -334,7 +338,7 @@
 			base_offset = (base_offset << 7) + (c & 127);
 		}
 		delta_base->offset = obj->idx.offset - base_offset;
-		if (delta_base->offset >= obj->idx.offset)
+		if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
 			bad_object(obj->idx.offset, "delta base offset is out of bound");
 		break;
 	case OBJ_COMMIT:
@@ -378,9 +382,9 @@
 	stream.avail_out = obj->size;
 	stream.next_in = src;
 	stream.avail_in = len;
-	inflateInit(&stream);
-	while ((st = inflate(&stream, Z_FINISH)) == Z_OK);
-	inflateEnd(&stream);
+	git_inflate_init(&stream);
+	while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
+	git_inflate_end(&stream);
 	if (st != Z_STREAM_END || stream.total_out != obj->size)
 		die("serious inflate inconsistency");
 	free(src);
@@ -408,22 +412,24 @@
         return -first-1;
 }
 
-static int find_delta_children(const union delta_base *base,
-			       int *first_index, int *last_index)
+static void find_delta_children(const union delta_base *base,
+				int *first_index, int *last_index)
 {
 	int first = find_delta(base);
 	int last = first;
 	int end = nr_deltas - 1;
 
-	if (first < 0)
-		return -1;
+	if (first < 0) {
+		*first_index = 0;
+		*last_index = -1;
+		return;
+	}
 	while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
 		--first;
 	while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
 		++last;
 	*first_index = first;
 	*last_index = last;
-	return 0;
 }
 
 static void sha1_object(const void *data, unsigned long size,
@@ -494,8 +500,10 @@
 			free(raw);
 			if (!c->data)
 				bad_object(obj->idx.offset, "failed to apply delta");
-		} else
+		} else {
 			c->data = get_data_from_pack(obj);
+			c->size = obj->size;
+		}
 
 		base_cache_used += c->size;
 		prune_base_data(c);
@@ -504,49 +512,74 @@
 }
 
 static void resolve_delta(struct object_entry *delta_obj,
-			  struct base_data *base_obj, enum object_type type)
+			  struct base_data *base, struct base_data *result)
 {
-	void *delta_data;
-	unsigned long delta_size;
-	union delta_base delta_base;
-	int j, first, last;
-	struct base_data result;
+	void *base_data, *delta_data;
 
-	delta_obj->real_type = type;
+	delta_obj->real_type = base->obj->real_type;
 	delta_data = get_data_from_pack(delta_obj);
-	delta_size = delta_obj->size;
-	result.data = patch_delta(get_base_data(base_obj), base_obj->size,
-			     delta_data, delta_size,
-			     &result.size);
+	base_data = get_base_data(base);
+	result->obj = delta_obj;
+	result->data = patch_delta(base_data, base->size,
+				   delta_data, delta_obj->size, &result->size);
 	free(delta_data);
-	if (!result.data)
+	if (!result->data)
 		bad_object(delta_obj->idx.offset, "failed to apply delta");
-	sha1_object(result.data, result.size, type, delta_obj->idx.sha1);
+	sha1_object(result->data, result->size, delta_obj->real_type,
+		    delta_obj->idx.sha1);
 	nr_resolved_deltas++;
+}
 
-	result.obj = delta_obj;
-	link_base_data(base_obj, &result);
+static void find_unresolved_deltas(struct base_data *base,
+				   struct base_data *prev_base)
+{
+	int i, ref_first, ref_last, ofs_first, ofs_last;
 
-	hashcpy(delta_base.sha1, delta_obj->idx.sha1);
-	if (!find_delta_children(&delta_base, &first, &last)) {
-		for (j = first; j <= last; j++) {
-			struct object_entry *child = objects + deltas[j].obj_no;
-			if (child->real_type == OBJ_REF_DELTA)
-				resolve_delta(child, &result, type);
+	/*
+	 * This is a recursive function. Those brackets should help reducing
+	 * stack usage by limiting the scope of the delta_base union.
+	 */
+	{
+		union delta_base base_spec;
+
+		hashcpy(base_spec.sha1, base->obj->idx.sha1);
+		find_delta_children(&base_spec, &ref_first, &ref_last);
+
+		memset(&base_spec, 0, sizeof(base_spec));
+		base_spec.offset = base->obj->idx.offset;
+		find_delta_children(&base_spec, &ofs_first, &ofs_last);
+	}
+
+	if (ref_last == -1 && ofs_last == -1) {
+		free(base->data);
+		return;
+	}
+
+	link_base_data(prev_base, base);
+
+	for (i = ref_first; i <= ref_last; i++) {
+		struct object_entry *child = objects + deltas[i].obj_no;
+		if (child->real_type == OBJ_REF_DELTA) {
+			struct base_data result;
+			resolve_delta(child, base, &result);
+			if (i == ref_last && ofs_last == -1)
+				free_base_data(base);
+			find_unresolved_deltas(&result, base);
 		}
 	}
 
-	memset(&delta_base, 0, sizeof(delta_base));
-	delta_base.offset = delta_obj->idx.offset;
-	if (!find_delta_children(&delta_base, &first, &last)) {
-		for (j = first; j <= last; j++) {
-			struct object_entry *child = objects + deltas[j].obj_no;
-			if (child->real_type == OBJ_OFS_DELTA)
-				resolve_delta(child, &result, type);
+	for (i = ofs_first; i <= ofs_last; i++) {
+		struct object_entry *child = objects + deltas[i].obj_no;
+		if (child->real_type == OBJ_OFS_DELTA) {
+			struct base_data result;
+			resolve_delta(child, base, &result);
+			if (i == ofs_last)
+				free_base_data(base);
+			find_unresolved_deltas(&result, base);
 		}
 	}
 
-	unlink_base_data(&result);
+	unlink_base_data(base);
 }
 
 static int compare_delta_entry(const void *a, const void *b)
@@ -591,7 +624,7 @@
 
 	/* Check pack integrity */
 	flush();
-	SHA1_Final(sha1, &input_ctx);
+	git_SHA1_Final(sha1, &input_ctx);
 	if (hashcmp(fill(20), sha1))
 		die("pack is corrupted (SHA1 mismatch)");
 	use(20);
@@ -622,37 +655,13 @@
 		progress = start_progress("Resolving deltas", nr_deltas);
 	for (i = 0; i < nr_objects; i++) {
 		struct object_entry *obj = &objects[i];
-		union delta_base base;
-		int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
 		struct base_data base_obj;
 
 		if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
 			continue;
-		hashcpy(base.sha1, obj->idx.sha1);
-		ref = !find_delta_children(&base, &ref_first, &ref_last);
-		memset(&base, 0, sizeof(base));
-		base.offset = obj->idx.offset;
-		ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
-		if (!ref && !ofs)
-			continue;
-		base_obj.data = get_data_from_pack(obj);
-		base_obj.size = obj->size;
 		base_obj.obj = obj;
-		link_base_data(NULL, &base_obj);
-
-		if (ref)
-			for (j = ref_first; j <= ref_last; j++) {
-				struct object_entry *child = objects + deltas[j].obj_no;
-				if (child->real_type == OBJ_REF_DELTA)
-					resolve_delta(child, &base_obj, obj->type);
-			}
-		if (ofs)
-			for (j = ofs_first; j <= ofs_last; j++) {
-				struct object_entry *child = objects + deltas[j].obj_no;
-				if (child->real_type == OBJ_OFS_DELTA)
-					resolve_delta(child, &base_obj, obj->type);
-			}
-		unlink_base_data(&base_obj);
+		base_obj.data = NULL;
+		find_unresolved_deltas(&base_obj, NULL);
 		display_progress(progress, nr_resolved_deltas);
 	}
 }
@@ -745,7 +754,6 @@
 	for (i = 0; i < n; i++) {
 		struct delta_entry *d = sorted_by_pos[i];
 		enum object_type type;
-		int j, first, last;
 		struct base_data base_obj;
 
 		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
@@ -759,16 +767,7 @@
 			die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
 		base_obj.obj = append_obj_to_pack(f, d->base.sha1,
 					base_obj.data, base_obj.size, type);
-		link_base_data(NULL, &base_obj);
-
-		find_delta_children(&d->base, &first, &last);
-		for (j = first; j <= last; j++) {
-			struct object_entry *child = objects + deltas[j].obj_no;
-			if (child->real_type == OBJ_REF_DELTA)
-				resolve_delta(child, &base_obj, type);
-		}
-
-		unlink_base_data(&base_obj);
+		find_unresolved_deltas(&base_obj, NULL);
 		display_progress(progress, nr_resolved_deltas);
 	}
 	free(sorted_by_pos);
@@ -790,27 +789,28 @@
 		err = close(output_fd);
 		if (err)
 			die("error while closing pack file: %s", strerror(errno));
-		chmod(curr_pack_name, 0444);
 	}
 
 	if (keep_msg) {
 		int keep_fd, keep_msg_len = strlen(keep_msg);
-		if (!keep_name) {
-			snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
-				 get_object_directory(), sha1_to_hex(sha1));
-			keep_name = name;
-		}
-		keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+		if (!keep_name)
+			keep_fd = odb_pack_keep(name, sizeof(name), sha1);
+		else
+			keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+
 		if (keep_fd < 0) {
 			if (errno != EEXIST)
-				die("cannot write keep file");
+				die("cannot write keep file '%s' (%s)",
+				    keep_name, strerror(errno));
 		} else {
 			if (keep_msg_len > 0) {
 				write_or_die(keep_fd, keep_msg, keep_msg_len);
 				write_or_die(keep_fd, "\n", 1);
 			}
 			if (close(keep_fd) != 0)
-				die("cannot write keep file");
+				die("cannot close written keep file '%s' (%s)",
+				    keep_name, strerror(errno));
 			report = "keep";
 		}
 	}
@@ -823,9 +823,9 @@
 		}
 		if (move_temp_to_file(curr_pack_name, final_pack_name))
 			die("cannot store pack file");
-	}
+	} else if (from_stdin)
+		chmod(final_pack_name, 0444);
 
-	chmod(curr_index_name, 0444);
 	if (final_index_name != curr_index_name) {
 		if (!final_index_name) {
 			snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
@@ -834,7 +834,8 @@
 		}
 		if (move_temp_to_file(curr_index_name, final_index_name))
 			die("cannot store index file");
-	}
+	} else
+		chmod(final_index_name, 0444);
 
 	if (!from_stdin) {
 		printf("%s\n", sha1_to_hex(sha1));
@@ -880,6 +881,8 @@
 	struct pack_idx_entry **idx_objects;
 	unsigned char pack_sha1[20];
 
+	git_extract_argv0_path(argv[0]);
+
 	/*
 	 * We wish to read the repository's config file if any, and
 	 * for that it is necessary to call setup_git_directory_gently().
diff --git a/interpolate.c b/interpolate.c
deleted file mode 100644
index 7f03bd9..0000000
--- a/interpolate.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2006 Jon Loeliger
- */
-
-#include "git-compat-util.h"
-#include "interpolate.h"
-
-
-void interp_set_entry(struct interp *table, int slot, const char *value)
-{
-	char *oldval = table[slot].value;
-	char *newval = NULL;
-
-	free(oldval);
-
-	if (value)
-		newval = xstrdup(value);
-
-	table[slot].value = newval;
-}
-
-
-void interp_clear_table(struct interp *table, int ninterps)
-{
-	int i;
-
-	for (i = 0; i < ninterps; i++) {
-		interp_set_entry(table, i, NULL);
-	}
-}
-
-
-/*
- * Convert a NUL-terminated string in buffer orig
- * into the supplied buffer, result, whose length is reslen,
- * performing substitutions on %-named sub-strings from
- * the table, interps, with ninterps entries.
- *
- * Example interps:
- *    {
- *        { "%H", "example.org"},
- *        { "%port", "123"},
- *        { "%%", "%"},
- *    }
- *
- * Returns the length of the substituted string (not including the final \0).
- * Like with snprintf, if the result is >= reslen, then it overflowed.
- */
-
-unsigned long interpolate(char *result, unsigned long reslen,
-		const char *orig,
-		const struct interp *interps, int ninterps)
-{
-	const char *src = orig;
-	char *dest = result;
-	unsigned long newlen = 0;
-	const char *name, *value;
-	unsigned long namelen, valuelen;
-	int i;
-	char c;
-
-	while ((c = *src)) {
-		if (c == '%') {
-			/* Try to match an interpolation string. */
-			for (i = 0; i < ninterps; i++) {
-				name = interps[i].name;
-				namelen = strlen(name);
-				if (strncmp(src, name, namelen) == 0)
-					break;
-			}
-
-			/* Check for valid interpolation. */
-			if (i < ninterps) {
-				value = interps[i].value;
-				if (!value) {
-					src += namelen;
-					continue;
-				}
-
-				valuelen = strlen(value);
-				if (newlen + valuelen < reslen) {
-					/* Substitute. */
-					memcpy(dest, value, valuelen);
-					dest += valuelen;
-				}
-				newlen += valuelen;
-				src += namelen;
-				continue;
-			}
-		}
-		/* Straight copy one non-interpolation character. */
-		if (newlen + 1 < reslen)
-			*dest++ = *src;
-		src++;
-		newlen++;
-	}
-
-	/* XXX: the previous loop always keep room for the ending NUL,
-	   we just need to check if there was room for a NUL in the first place */
-	if (reslen > 0)
-		*dest = '\0';
-	return newlen;
-}
diff --git a/interpolate.h b/interpolate.h
deleted file mode 100644
index 77407e6..0000000
--- a/interpolate.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2006 Jon Loeliger
- */
-
-#ifndef INTERPOLATE_H
-#define INTERPOLATE_H
-
-/*
- * Convert a NUL-terminated string in buffer orig,
- * performing substitutions on %-named sub-strings from
- * the interpretation table.
- */
-
-struct interp {
-	const char *name;
-	char *value;
-};
-
-extern void interp_set_entry(struct interp *table, int slot, const char *value);
-extern void interp_clear_table(struct interp *table, int ninterps);
-
-extern unsigned long interpolate(char *result, unsigned long reslen,
-				 const char *orig,
-				 const struct interp *interps, int ninterps);
-
-#endif /* INTERPOLATE_H */
diff --git a/levenshtein.c b/levenshtein.c
new file mode 100644
index 0000000..a32f4cd
--- /dev/null
+++ b/levenshtein.c
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "levenshtein.h"
+
+/*
+ * This function implements the Damerau-Levenshtein algorithm to
+ * calculate a distance between strings.
+ *
+ * Basically, it says how many letters need to be swapped, substituted,
+ * deleted from, or added to string1, at least, to get string2.
+ *
+ * The idea is to build a distance matrix for the substrings of both
+ * strings.  To avoid a large space complexity, only the last three rows
+ * are kept in memory (if swaps had the same or higher cost as one deletion
+ * plus one insertion, only two rows would be needed).
+ *
+ * At any stage, "i + 1" denotes the length of the current substring of
+ * string1 that the distance is calculated for.
+ *
+ * row2 holds the current row, row1 the previous row (i.e. for the substring
+ * of string1 of length "i"), and row0 the row before that.
+ *
+ * In other words, at the start of the big loop, row2[j + 1] contains the
+ * Damerau-Levenshtein distance between the substring of string1 of length
+ * "i" and the substring of string2 of length "j + 1".
+ *
+ * All the big loop does is determine the partial minimum-cost paths.
+ *
+ * It does so by calculating the costs of the path ending in characters
+ * i (in string1) and j (in string2), respectively, given that the last
+ * operation is a substition, a swap, a deletion, or an insertion.
+ *
+ * This implementation allows the costs to be weighted:
+ *
+ * - w (as in "sWap")
+ * - s (as in "Substitution")
+ * - a (for insertion, AKA "Add")
+ * - d (as in "Deletion")
+ *
+ * Note that this algorithm calculates a distance _iff_ d == a.
+ */
+int levenshtein(const char *string1, const char *string2,
+		int w, int s, int a, int d)
+{
+	int len1 = strlen(string1), len2 = strlen(string2);
+	int *row0 = xmalloc(sizeof(int) * (len2 + 1));
+	int *row1 = xmalloc(sizeof(int) * (len2 + 1));
+	int *row2 = xmalloc(sizeof(int) * (len2 + 1));
+	int i, j;
+
+	for (j = 0; j <= len2; j++)
+		row1[j] = j * a;
+	for (i = 0; i < len1; i++) {
+		int *dummy;
+
+		row2[0] = (i + 1) * d;
+		for (j = 0; j < len2; j++) {
+			/* substitution */
+			row2[j + 1] = row1[j] + s * (string1[i] != string2[j]);
+			/* swap */
+			if (i > 0 && j > 0 && string1[i - 1] == string2[j] &&
+					string1[i] == string2[j - 1] &&
+					row2[j + 1] > row0[j - 1] + w)
+				row2[j + 1] = row0[j - 1] + w;
+			/* deletion */
+			if (row2[j + 1] > row1[j + 1] + d)
+				row2[j + 1] = row1[j + 1] + d;
+			/* insertion */
+			if (row2[j + 1] > row2[j] + a)
+				row2[j + 1] = row2[j] + a;
+		}
+
+		dummy = row0;
+		row0 = row1;
+		row1 = row2;
+		row2 = dummy;
+	}
+
+	i = row1[len2];
+	free(row0);
+	free(row1);
+	free(row2);
+
+	return i;
+}
diff --git a/levenshtein.h b/levenshtein.h
new file mode 100644
index 0000000..0173abe
--- /dev/null
+++ b/levenshtein.h
@@ -0,0 +1,8 @@
+#ifndef LEVENSHTEIN_H
+#define LEVENSHTEIN_H
+
+int levenshtein(const char *string1, const char *string2,
+	int swap_penalty, int substition_penalty,
+	int insertion_penalty, int deletion_penalty);
+
+#endif
diff --git a/list-objects.c b/list-objects.c
index 30ded3d..8953548 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -141,14 +141,15 @@
 
 void traverse_commit_list(struct rev_info *revs,
 			  show_commit_fn show_commit,
-			  show_object_fn show_object)
+			  show_object_fn show_object,
+			  void *data)
 {
 	int i;
 	struct commit *commit;
 
 	while ((commit = get_revision(revs)) != NULL) {
 		add_pending_tree(revs, commit->tree);
-		show_commit(commit);
+		show_commit(commit, data);
 	}
 	for (i = 0; i < revs->pending.nr; i++) {
 		struct object_array_entry *pending = revs->pending.objects + i;
diff --git a/list-objects.h b/list-objects.h
index 0b2de64..d65dbf0 100644
--- a/list-objects.h
+++ b/list-objects.h
@@ -1,11 +1,11 @@
 #ifndef LIST_OBJECTS_H
 #define LIST_OBJECTS_H
 
-typedef void (*show_commit_fn)(struct commit *);
+typedef void (*show_commit_fn)(struct commit *, void *);
 typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *);
 typedef void (*show_edge_fn)(struct commit *);
 
-void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
+void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
 
 void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
 
diff --git a/ll-merge.c b/ll-merge.c
index 9837c84..fa2ca52 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -8,7 +8,6 @@
 #include "attr.h"
 #include "xdiff-interface.h"
 #include "run-command.h"
-#include "interpolate.h"
 #include "ll-merge.h"
 
 struct ll_merge_driver;
@@ -63,6 +62,7 @@
 			int virtual_ancestor)
 {
 	xpparam_t xpp;
+	int style = 0;
 
 	if (buffer_is_binary(orig->ptr, orig->size) ||
 	    buffer_is_binary(src1->ptr, src1->size) ||
@@ -77,10 +77,12 @@
 	}
 
 	memset(&xpp, 0, sizeof(xpp));
+	if (git_xmerge_style >= 0)
+		style = git_xmerge_style;
 	return xdl_merge(orig,
 			 src1, name1,
 			 src2, name2,
-			 &xpp, XDL_MERGE_ZEALOUS,
+			 &xpp, XDL_MERGE_ZEALOUS | style,
 			 result);
 }
 
@@ -95,10 +97,15 @@
 	char *src, *dst;
 	long size;
 	const int marker_size = 7;
+	int status, saved_style;
 
-	int status = ll_xdl_merge(drv_unused, result, path_unused,
-				  orig, src1, NULL, src2, NULL,
-				  virtual_ancestor);
+	/* We have to force the RCS "merge" style */
+	saved_style = git_xmerge_style;
+	git_xmerge_style = 0;
+	status = ll_xdl_merge(drv_unused, result, path_unused,
+			      orig, src1, NULL, src2, NULL,
+			      virtual_ancestor);
+	git_xmerge_style = saved_style;
 	if (status <= 0)
 		return status;
 	size = result->size;
@@ -161,11 +168,12 @@
 			int virtual_ancestor)
 {
 	char temp[3][50];
-	char cmdbuf[2048];
-	struct interp table[] = {
-		{ "%O" },
-		{ "%A" },
-		{ "%B" },
+	struct strbuf cmd = STRBUF_INIT;
+	struct strbuf_expand_dict_entry dict[] = {
+		{ "O", temp[0] },
+		{ "A", temp[1] },
+		{ "B", temp[2] },
+		{ NULL }
 	};
 	struct child_process child;
 	const char *args[20];
@@ -181,17 +189,13 @@
 	create_temp(src1, temp[1]);
 	create_temp(src2, temp[2]);
 
-	interp_set_entry(table, 0, temp[0]);
-	interp_set_entry(table, 1, temp[1]);
-	interp_set_entry(table, 2, temp[2]);
-
-	interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+	strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
 
 	memset(&child, 0, sizeof(child));
 	child.argv = args;
 	args[0] = "sh";
 	args[1] = "-c";
-	args[2] = cmdbuf;
+	args[2] = cmd.buf;
 	args[3] = NULL;
 
 	status = run_command(&child);
@@ -216,6 +220,7 @@
  bad:
 	for (i = 0; i < 3; i++)
 		unlink(temp[i]);
+	strbuf_release(&cmd);
 	return status;
 }
 
diff --git a/lockfile.c b/lockfile.c
index 6d75608..3dbb2d1 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -2,6 +2,7 @@
  * Copyright (c) 2005, Junio C Hamano
  */
 #include "cache.h"
+#include "sigchain.h"
 
 static struct lock_file *lock_file_list;
 static const char *alternate_index_output;
@@ -24,7 +25,7 @@
 static void remove_lock_file_on_signal(int signo)
 {
 	remove_lock_file();
-	signal(signo, SIG_DFL);
+	sigchain_pop(signo);
 	raise(signo);
 }
 
@@ -136,10 +137,7 @@
 	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= lk->fd) {
 		if (!lock_file_list) {
-			signal(SIGINT, remove_lock_file_on_signal);
-			signal(SIGHUP, remove_lock_file_on_signal);
-			signal(SIGTERM, remove_lock_file_on_signal);
-			signal(SIGQUIT, remove_lock_file_on_signal);
+			sigchain_push_common(remove_lock_file_on_signal);
 			atexit(remove_lock_file);
 		}
 		lk->owner = getpid();
@@ -157,11 +155,25 @@
 	return lk->fd;
 }
 
+
+NORETURN void unable_to_lock_index_die(const char *path, int err)
+{
+	if (err == EEXIST) {
+		die("Unable to create '%s.lock': %s.\n\n"
+		    "If no other git process is currently running, this probably means a\n"
+		    "git process crashed in this repository earlier. Make sure no other git\n"
+		    "process is running and remove the file manually to continue.",
+		    path, strerror(err));
+	} else {
+		die("Unable to create '%s.lock': %s", path, strerror(err));
+	}
+}
+
 int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
 {
 	int fd = lock_file(lk, path, flags);
 	if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
-		die("unable to create '%s.lock': %s", path, strerror(errno));
+		unable_to_lock_index_die(path, errno);
 	return fd;
 }
 
@@ -172,7 +184,7 @@
 	fd = lock_file(lk, path, flags);
 	if (fd < 0) {
 		if (flags & LOCK_DIE_ON_ERROR)
-			die("unable to create '%s.lock': %s", path, strerror(errno));
+			unable_to_lock_index_die(path, errno);
 		return fd;
 	}
 
diff --git a/log-tree.c b/log-tree.c
index bd8b9e4..5bd29e6 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1,26 +1,67 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
+#include "tag.h"
 #include "graph.h"
 #include "log-tree.h"
 #include "reflog-walk.h"
+#include "refs.h"
+#include "string-list.h"
 
 struct decoration name_decoration = { "object names" };
 
+static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+{
+	int plen = strlen(prefix);
+	int nlen = strlen(name);
+	struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
+	memcpy(res->name, prefix, plen);
+	memcpy(res->name + plen, name, nlen + 1);
+	res->next = add_decoration(&name_decoration, obj, res);
+}
+
+static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct object *obj = parse_object(sha1);
+	if (!obj)
+		return 0;
+	add_name_decoration("", refname, obj);
+	while (obj->type == OBJ_TAG) {
+		obj = ((struct tag *)obj)->tagged;
+		if (!obj)
+			break;
+		add_name_decoration("tag: ", refname, obj);
+	}
+	return 0;
+}
+
+void load_ref_decorations(void)
+{
+	static int loaded;
+	if (!loaded) {
+		loaded = 1;
+		for_each_ref(add_ref_decoration, NULL);
+	}
+}
+
 static void show_parents(struct commit *commit, int abbrev)
 {
 	struct commit_list *p;
 	for (p = commit->parents; p ; p = p->next) {
 		struct commit *parent = p->item;
-		printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev));
+		printf(" %s", find_unique_abbrev(parent->object.sha1, abbrev));
 	}
 }
 
-void show_decorations(struct commit *commit)
+void show_decorations(struct rev_info *opt, struct commit *commit)
 {
 	const char *prefix;
 	struct name_decoration *decoration;
 
+	if (opt->show_source && commit->util)
+		printf("\t%s", (char *) commit->util);
+	if (!opt->show_decorations)
+		return;
 	decoration = lookup_decoration(&name_decoration, &commit->object);
 	if (!decoration)
 		return;
@@ -39,18 +80,18 @@
  */
 static int detect_any_signoff(char *letter, int size)
 {
-	char ch, *cp;
+	char *cp;
 	int seen_colon = 0;
 	int seen_at = 0;
 	int seen_name = 0;
 	int seen_head = 0;
 
 	cp = letter + size;
-	while (letter <= --cp && (ch = *cp) == '\n')
+	while (letter <= --cp && *cp == '\n')
 		continue;
 
 	while (letter <= cp) {
-		ch = *cp--;
+		char ch = *cp--;
 		if (ch == '\n')
 			break;
 
@@ -138,13 +179,31 @@
 	return 0;
 }
 
-void log_write_email_headers(struct rev_info *opt, const char *name,
+void get_patch_filename(struct commit *commit, int nr, const char *suffix,
+			struct strbuf *buf)
+{
+	int suffix_len = strlen(suffix) + 1;
+	int start_len = buf->len;
+
+	strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
+	if (commit) {
+		int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
+
+		format_commit_message(commit, "%f", buf, DATE_NORMAL);
+		if (max_len < buf->len)
+			strbuf_setlen(buf, max_len);
+		strbuf_addstr(buf, suffix);
+	}
+}
+
+void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 			     const char **subject_p,
 			     const char **extra_headers_p,
 			     int *need_8bit_cte_p)
 {
 	const char *subject = NULL;
 	const char *extra_headers = opt->extra_headers;
+	const char *name = sha1_to_hex(commit->object.sha1);
 
 	*need_8bit_cte_p = 0; /* unknown */
 	if (opt->total > 0) {
@@ -171,14 +230,19 @@
 		printf("Message-Id: <%s>\n", opt->message_id);
 		graph_show_oneline(opt->graph);
 	}
-	if (opt->ref_message_id) {
-		printf("In-Reply-To: <%s>\nReferences: <%s>\n",
-		       opt->ref_message_id, opt->ref_message_id);
+	if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
+		int i, n;
+		n = opt->ref_message_ids->nr;
+		printf("In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
+		for (i = 0; i < n; i++)
+			printf("%s<%s>\n", (i > 0 ? "\t" : "References: "),
+			       opt->ref_message_ids->items[i].string);
 		graph_show_oneline(opt->graph);
 	}
 	if (opt->mime_boundary) {
 		static char subject_buffer[1024];
 		static char buffer[1024];
+		struct strbuf filename =  STRBUF_INIT;
 		*need_8bit_cte_p = -1; /* NEVER */
 		snprintf(subject_buffer, sizeof(subject_buffer) - 1,
 			 "%s"
@@ -197,18 +261,21 @@
 			 mime_boundary_leader, opt->mime_boundary);
 		extra_headers = subject_buffer;
 
+		get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
+				    opt->patch_suffix, &filename);
 		snprintf(buffer, sizeof(buffer) - 1,
 			 "\n--%s%s\n"
 			 "Content-Type: text/x-patch;"
-			 " name=\"%s.diff\"\n"
+			 " name=\"%s\"\n"
 			 "Content-Transfer-Encoding: 8bit\n"
 			 "Content-Disposition: %s;"
-			 " filename=\"%s.diff\"\n\n",
+			 " filename=\"%s\"\n\n",
 			 mime_boundary_leader, opt->mime_boundary,
-			 name,
+			 filename.buf,
 			 opt->no_inline ? "attachment" : "inline",
-			 name);
+			 filename.buf);
 		opt->diffopt.stat_sep = buffer;
+		strbuf_release(&filename);
 	}
 	*subject_p = subject;
 	*extra_headers_p = extra_headers;
@@ -216,7 +283,7 @@
 
 void show_log(struct rev_info *opt)
 {
-	struct strbuf msgbuf;
+	struct strbuf msgbuf = STRBUF_INIT;
 	struct log_info *log = opt->loginfo;
 	struct commit *commit = log->commit, *parent = log->parent;
 	int abbrev = opt->diffopt.abbrev;
@@ -240,10 +307,10 @@
 					putchar('>');
 			}
 		}
-		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
+		fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
-		show_decorations(commit);
+		show_decorations(opt, commit);
 		if (opt->graph && !graph_is_commit_finished(opt->graph)) {
 			putchar('\n');
 			graph_show_remainder(opt->graph);
@@ -288,8 +355,7 @@
 	 */
 
 	if (opt->commit_format == CMIT_FMT_EMAIL) {
-		log_write_email_headers(opt, sha1_to_hex(commit->object.sha1),
-					&subject, &extra_headers,
+		log_write_email_headers(opt, commit, &subject, &extra_headers,
 					&need_8bit_cte);
 	} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
@@ -308,15 +374,15 @@
 					putchar('>');
 			}
 		}
-		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit),
+		fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
 		      stdout);
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit);
 		if (parent)
 			printf(" (from %s)",
-			       diff_unique_abbrev(parent->object.sha1,
+			       find_unique_abbrev(parent->object.sha1,
 						  abbrev_commit));
-		show_decorations(commit);
+		show_decorations(opt, commit);
 		printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
 		if (opt->commit_format == CMIT_FMT_ONELINE) {
 			putchar(' ');
@@ -345,7 +411,6 @@
 	/*
 	 * And then the pretty-printed message itself
 	 */
-	strbuf_init(&msgbuf, 0);
 	if (need_8bit_cte >= 0)
 		need_8bit_cte = has_non_ascii(opt->add_signoff);
 	pretty_print_commit(opt->commit_format, commit, &msgbuf,
@@ -432,7 +497,7 @@
 	struct commit_list *parents;
 	unsigned const char *sha1 = commit->object.sha1;
 
-	if (!opt->diff)
+	if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
 		return 0;
 
 	/* Root commit? */
diff --git a/log-tree.h b/log-tree.h
index 59ba4c4..20b5caf 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -12,10 +12,15 @@
 int log_tree_commit(struct rev_info *, struct commit *);
 int log_tree_opt_parse(struct rev_info *, const char **, int);
 void show_log(struct rev_info *opt);
-void show_decorations(struct commit *commit);
-void log_write_email_headers(struct rev_info *opt, const char *name,
+void show_decorations(struct rev_info *opt, struct commit *commit);
+void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 			     const char **subject_p,
 			     const char **extra_headers_p,
 			     int *need_8bit_cte_p);
+void load_ref_decorations(void);
+
+#define FORMAT_PATCH_NAME_MAX 64
+void get_patch_filename(struct commit *commit, int nr, const char *suffix,
+			struct strbuf *buf);
 
 #endif
diff --git a/mailmap.c b/mailmap.c
index 88fc6f3..bb1f2fb 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -2,17 +2,141 @@
 #include "string-list.h"
 #include "mailmap.h"
 
-int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
+#define DEBUG_MAILMAP 0
+#if DEBUG_MAILMAP
+#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#else
+static inline void debug_mm(const char *format, ...) {}
+#endif
+
+const char *git_mailmap_file;
+
+struct mailmap_info {
+	char *name;
+	char *email;
+};
+
+struct mailmap_entry {
+	/* name and email for the simple mail-only case */
+	char *name;
+	char *email;
+
+	/* name and email for the complex mail and name matching case */
+	struct string_list namemap;
+};
+
+static void free_mailmap_info(void *p, const char *s)
+{
+	struct mailmap_info *mi = (struct mailmap_info *)p;
+	debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email);
+	free(mi->name);
+	free(mi->email);
+}
+
+static void free_mailmap_entry(void *p, const char *s)
+{
+	struct mailmap_entry *me = (struct mailmap_entry *)p;
+	debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr);
+	debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email);
+	free(me->name);
+	free(me->email);
+
+	me->namemap.strdup_strings = 1;
+	string_list_clear_func(&me->namemap, free_mailmap_info);
+}
+
+static void add_mapping(struct string_list *map,
+			char *new_name, char *new_email, char *old_name, char *old_email)
+{
+	struct mailmap_entry *me;
+	int index;
+	char *p;
+
+	if (old_email)
+		for (p = old_email; *p; p++)
+			*p = tolower(*p);
+	if (new_email)
+		for (p = new_email; *p; p++)
+			*p = tolower(*p);
+
+	if (old_email == NULL) {
+		old_email = new_email;
+		new_email = NULL;
+	}
+
+	if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
+		/* mailmap entry exists, invert index value */
+		index = -1 - index;
+	} else {
+		/* create mailmap entry */
+		struct string_list_item *item = string_list_insert_at_index(index, old_email, map);
+		item->util = xmalloc(sizeof(struct mailmap_entry));
+		memset(item->util, 0, sizeof(struct mailmap_entry));
+		((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
+	}
+	me = (struct mailmap_entry *)map->items[index].util;
+
+	if (old_name == NULL) {
+		debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
+		/* Replace current name and new email for simple entry */
+		free(me->name);
+		free(me->email);
+		if (new_name)
+			me->name = xstrdup(new_name);
+		if (new_email)
+			me->email = xstrdup(new_email);
+	} else {
+		struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info));
+		debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
+		if (new_name)
+			mi->name = xstrdup(new_name);
+		if (new_email)
+			mi->email = xstrdup(new_email);
+		string_list_insert(old_name, &me->namemap)->util = mi;
+	}
+
+	debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
+		 old_name, old_email, new_name, new_email);
+}
+
+static char *parse_name_and_email(char *buffer, char **name,
+		char **email, int allow_empty_email)
+{
+	char *left, *right, *nstart, *nend;
+	*name = *email = 0;
+
+	if ((left = strchr(buffer, '<')) == NULL)
+		return NULL;
+	if ((right = strchr(left+1, '>')) == NULL)
+		return NULL;
+	if (!allow_empty_email && (left+1 == right))
+		return NULL;
+
+	/* remove whitespace from beginning and end of name */
+	nstart = buffer;
+	while (isspace(*nstart) && nstart < left)
+		++nstart;
+	nend = left-1;
+	while (isspace(*nend) && nend > nstart)
+		--nend;
+
+	*name = (nstart < nend ? nstart : NULL);
+	*email = left+1;
+	*(nend+1) = '\0';
+	*right++ = '\0';
+
+	return (*right == '\0' ? NULL : right);
+}
+
+static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
 {
 	char buffer[1024];
-	FILE *f = fopen(filename, "r");
+	FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
 
 	if (f == NULL)
 		return 1;
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
-		char *end_of_name, *left_bracket, *right_bracket;
-		char *name, *email;
-		int i;
+		char *name1 = 0, *email1 = 0, *name2 = 0, *email2 = 0;
 		if (buffer[0] == '#') {
 			static const char abbrev[] = "# repo-abbrev:";
 			int abblen = sizeof(abbrev) - 1;
@@ -36,41 +160,49 @@
 			}
 			continue;
 		}
-		if ((left_bracket = strchr(buffer, '<')) == NULL)
-			continue;
-		if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
-			continue;
-		if (right_bracket == left_bracket + 1)
-			continue;
-		for (end_of_name = left_bracket;
-		     end_of_name != buffer && isspace(end_of_name[-1]);
-		     end_of_name--)
-			; /* keep on looking */
-		if (end_of_name == buffer)
-			continue;
-		name = xmalloc(end_of_name - buffer + 1);
-		strlcpy(name, buffer, end_of_name - buffer + 1);
-		email = xmalloc(right_bracket - left_bracket);
-		for (i = 0; i < right_bracket - left_bracket - 1; i++)
-			email[i] = tolower(left_bracket[i + 1]);
-		email[right_bracket - left_bracket - 1] = '\0';
-		string_list_insert(email, map)->util = name;
+		if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
+			parse_name_and_email(name2, &name2, &email2, 1);
+
+		if (email1)
+			add_mapping(map, name1, email1, name2, email2);
 	}
 	fclose(f);
 	return 0;
 }
 
-int map_email(struct string_list *map, const char *email, char *name, int maxlen)
+int read_mailmap(struct string_list *map, char **repo_abbrev)
+{
+	map->strdup_strings = 1;
+	/* each failure returns 1, so >1 means both calls failed */
+	return read_single_mailmap(map, ".mailmap", repo_abbrev) +
+	       read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;
+}
+
+void clear_mailmap(struct string_list *map)
+{
+	debug_mm("mailmap: clearing %d entries...\n", map->nr);
+	map->strdup_strings = 1;
+	string_list_clear_func(map, free_mailmap_entry);
+	debug_mm("mailmap: cleared\n");
+}
+
+int map_user(struct string_list *map,
+	     char *email, int maxlen_email, char *name, int maxlen_name)
 {
 	char *p;
 	struct string_list_item *item;
+	struct mailmap_entry *me;
 	char buf[1024], *mailbuf;
 	int i;
 
-	/* autocomplete common developers */
+	/* figure out space requirement for email */
 	p = strchr(email, '>');
-	if (!p)
-		return 0;
+	if (!p) {
+		/* email passed in might not be wrapped in <>, but end with a \0 */
+		p = memchr(email, '\0', maxlen_email);
+		if (p == 0)
+			return 0;
+	}
 	if (p - email + 1 < sizeof(buf))
 		mailbuf = buf;
 	else
@@ -80,13 +212,39 @@
 	for (i = 0; i < p - email; i++)
 		mailbuf[i] = tolower(email[i]);
 	mailbuf[i] = 0;
+
+	debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
 	item = string_list_lookup(mailbuf, map);
+	if (item != NULL) {
+		me = (struct mailmap_entry *)item->util;
+		if (me->namemap.nr) {
+			/* The item has multiple items, so we'll look up on name too */
+			/* If the name is not found, we choose the simple entry      */
+			struct string_list_item *subitem = string_list_lookup(name, &me->namemap);
+			if (subitem)
+				item = subitem;
+		}
+	}
 	if (mailbuf != buf)
 		free(mailbuf);
 	if (item != NULL) {
-		const char *realname = (const char *)item->util;
-		strlcpy(name, realname, maxlen);
+		struct mailmap_info *mi = (struct mailmap_info *)item->util;
+		if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {
+			debug_mm("map_user:  -- (no simple mapping)\n");
+			return 0;
+		}
+		if (maxlen_email && mi->email)
+			strlcpy(email, mi->email, maxlen_email);
+		if (maxlen_name && mi->name)
+			strlcpy(name, mi->name, maxlen_name);
+		debug_mm("map_user:  to '%s' <%s>\n", name, mi->email ? mi->email : "");
 		return 1;
 	}
+	debug_mm("map_user:  --\n");
 	return 0;
 }
+
+int map_email(struct string_list *map, const char *email, char *name, int maxlen)
+{
+	return map_user(map, (char *)email, 0, name, maxlen);
+}
diff --git a/mailmap.h b/mailmap.h
index 6e48f83..4b2ca3a 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -1,7 +1,11 @@
 #ifndef MAILMAP_H
 #define MAILMAP_H
 
-int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev);
+int read_mailmap(struct string_list *map, char **repo_abbrev);
+void clear_mailmap(struct string_list *map);
+
 int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen);
+int map_user(struct string_list *mailmap,
+	     char *email, int maxlen_email, char *name, int maxlen_name);
 
 #endif
diff --git a/merge-file.c b/merge-file.c
index 2a939c9..3120a95 100644
--- a/merge-file.c
+++ b/merge-file.c
@@ -61,6 +61,7 @@
 	xdemitconf_t xecfg;
 	xdemitcb_t ecb;
 
+	memset(&xpp, 0, sizeof(xpp));
 	xpp.flags = XDF_NEED_MINIMAL;
 	memset(&xecfg, 0, sizeof(xecfg));
 	xecfg.ctxlen = 3;
diff --git a/merge-index.c b/merge-index.c
index 7827e87..aa9cf23 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "run-command.h"
+#include "exec_cmd.h"
 
 static const char *pgm;
 static const char *arguments[9];
@@ -91,7 +92,9 @@
 	signal(SIGCHLD, SIG_DFL);
 
 	if (argc < 3)
-		usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
+		usage("git merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
+
+	git_extract_argv0_path(argv[0]);
 
 	setup_git_directory();
 	read_cache();
diff --git a/merge-recursive.c b/merge-recursive.c
new file mode 100644
index 0000000..d6f0582
--- /dev/null
+++ b/merge-recursive.c
@@ -0,0 +1,1380 @@
+/*
+ * Recursive Merge algorithm stolen from git-merge-recursive.py by
+ * Fredrik Kuivinen.
+ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
+ */
+#include "cache.h"
+#include "cache-tree.h"
+#include "commit.h"
+#include "blob.h"
+#include "builtin.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "tag.h"
+#include "unpack-trees.h"
+#include "string-list.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
+#include "attr.h"
+#include "merge-recursive.h"
+#include "dir.h"
+
+static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+{
+	unsigned char shifted[20];
+
+	/*
+	 * NEEDSWORK: this limits the recursion depth to hardcoded
+	 * value '2' to avoid excessive overhead.
+	 */
+	shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+	if (!hashcmp(two->object.sha1, shifted))
+		return two;
+	return lookup_tree(shifted);
+}
+
+/*
+ * A virtual commit has (const char *)commit->util set to the name.
+ */
+
+struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+{
+	struct commit *commit = xcalloc(1, sizeof(struct commit));
+	commit->tree = tree;
+	commit->util = (void*)comment;
+	/* avoid warnings */
+	commit->object.parsed = 1;
+	return commit;
+}
+
+/*
+ * Since we use get_tree_entry(), which does not put the read object into
+ * the object pool, we cannot rely on a == b.
+ */
+static int sha_eq(const unsigned char *a, const unsigned char *b)
+{
+	if (!a && !b)
+		return 2;
+	return a && b && hashcmp(a, b) == 0;
+}
+
+/*
+ * Since we want to write the index eventually, we cannot reuse the index
+ * for these (temporary) data.
+ */
+struct stage_data
+{
+	struct
+	{
+		unsigned mode;
+		unsigned char sha[20];
+	} stages[4];
+	unsigned processed:1;
+};
+
+static int show(struct merge_options *o, int v)
+{
+	return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
+}
+
+static void flush_output(struct merge_options *o)
+{
+	if (o->obuf.len) {
+		fputs(o->obuf.buf, stdout);
+		strbuf_reset(&o->obuf);
+	}
+}
+
+static void output(struct merge_options *o, int v, const char *fmt, ...)
+{
+	int len;
+	va_list ap;
+
+	if (!show(o, v))
+		return;
+
+	strbuf_grow(&o->obuf, o->call_depth * 2 + 2);
+	memset(o->obuf.buf + o->obuf.len, ' ', o->call_depth * 2);
+	strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
+
+	va_start(ap, fmt);
+	len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
+	va_end(ap);
+
+	if (len < 0)
+		len = 0;
+	if (len >= strbuf_avail(&o->obuf)) {
+		strbuf_grow(&o->obuf, len + 2);
+		va_start(ap, fmt);
+		len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
+		va_end(ap);
+		if (len >= strbuf_avail(&o->obuf)) {
+			die("this should not happen, your snprintf is broken");
+		}
+	}
+	strbuf_setlen(&o->obuf, o->obuf.len + len);
+	strbuf_add(&o->obuf, "\n", 1);
+	if (!o->buffer_output)
+		flush_output(o);
+}
+
+static void output_commit_title(struct merge_options *o, struct commit *commit)
+{
+	int i;
+	flush_output(o);
+	for (i = o->call_depth; i--;)
+		fputs("  ", stdout);
+	if (commit->util)
+		printf("virtual %s\n", (char *)commit->util);
+	else {
+		printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+		if (parse_commit(commit) != 0)
+			printf("(bad commit)\n");
+		else {
+			const char *s;
+			int len;
+			for (s = commit->buffer; *s; s++)
+				if (*s == '\n' && s[1] == '\n') {
+					s += 2;
+					break;
+				}
+			for (len = 0; s[len] && '\n' != s[len]; len++)
+				; /* do nothing */
+			printf("%.*s\n", len, s);
+		}
+	}
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+		const char *path, int stage, int refresh, int options)
+{
+	struct cache_entry *ce;
+	ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+	if (!ce)
+		return error("addinfo_cache failed for path '%s'", path);
+	return add_cache_entry(ce, options);
+}
+
+static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
+{
+	parse_tree(tree);
+	init_tree_desc(desc, tree->buffer, tree->size);
+}
+
+static int git_merge_trees(int index_only,
+			   struct tree *common,
+			   struct tree *head,
+			   struct tree *merge)
+{
+	int rc;
+	struct tree_desc t[3];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	if (index_only)
+		opts.index_only = 1;
+	else
+		opts.update = 1;
+	opts.merge = 1;
+	opts.head_idx = 2;
+	opts.fn = threeway_merge;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	init_tree_desc_from_tree(t+0, common);
+	init_tree_desc_from_tree(t+1, head);
+	init_tree_desc_from_tree(t+2, merge);
+
+	rc = unpack_trees(3, t, &opts);
+	cache_tree_free(&active_cache_tree);
+	return rc;
+}
+
+struct tree *write_tree_from_memory(struct merge_options *o)
+{
+	struct tree *result = NULL;
+
+	if (unmerged_cache()) {
+		int i;
+		output(o, 0, "There are unmerged index entries:");
+		for (i = 0; i < active_nr; i++) {
+			struct cache_entry *ce = active_cache[i];
+			if (ce_stage(ce))
+				output(o, 0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
+		}
+		return NULL;
+	}
+
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
+
+	if (!cache_tree_fully_valid(active_cache_tree) &&
+	    cache_tree_update(active_cache_tree,
+			      active_cache, active_nr, 0, 0) < 0)
+		die("error building trees");
+
+	result = lookup_tree(active_cache_tree->sha1);
+
+	return result;
+}
+
+static int save_files_dirs(const unsigned char *sha1,
+		const char *base, int baselen, const char *path,
+		unsigned int mode, int stage, void *context)
+{
+	int len = strlen(path);
+	char *newpath = xmalloc(baselen + len + 1);
+	struct merge_options *o = context;
+
+	memcpy(newpath, base, baselen);
+	memcpy(newpath + baselen, path, len);
+	newpath[baselen + len] = '\0';
+
+	if (S_ISDIR(mode))
+		string_list_insert(newpath, &o->current_directory_set);
+	else
+		string_list_insert(newpath, &o->current_file_set);
+	free(newpath);
+
+	return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
+}
+
+static int get_files_dirs(struct merge_options *o, struct tree *tree)
+{
+	int n;
+	if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs, o))
+		return 0;
+	n = o->current_file_set.nr + o->current_directory_set.nr;
+	return n;
+}
+
+/*
+ * Returns an index_entry instance which doesn't have to correspond to
+ * a real cache entry in Git's index.
+ */
+static struct stage_data *insert_stage_data(const char *path,
+		struct tree *o, struct tree *a, struct tree *b,
+		struct string_list *entries)
+{
+	struct string_list_item *item;
+	struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
+	get_tree_entry(o->object.sha1, path,
+			e->stages[1].sha, &e->stages[1].mode);
+	get_tree_entry(a->object.sha1, path,
+			e->stages[2].sha, &e->stages[2].mode);
+	get_tree_entry(b->object.sha1, path,
+			e->stages[3].sha, &e->stages[3].mode);
+	item = string_list_insert(path, entries);
+	item->util = e;
+	return e;
+}
+
+/*
+ * Create a dictionary mapping file names to stage_data objects. The
+ * dictionary contains one entry for every path with a non-zero stage entry.
+ */
+static struct string_list *get_unmerged(void)
+{
+	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+	int i;
+
+	unmerged->strdup_strings = 1;
+
+	for (i = 0; i < active_nr; i++) {
+		struct string_list_item *item;
+		struct stage_data *e;
+		struct cache_entry *ce = active_cache[i];
+		if (!ce_stage(ce))
+			continue;
+
+		item = string_list_lookup(ce->name, unmerged);
+		if (!item) {
+			item = string_list_insert(ce->name, unmerged);
+			item->util = xcalloc(1, sizeof(struct stage_data));
+		}
+		e = item->util;
+		e->stages[ce_stage(ce)].mode = ce->ce_mode;
+		hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
+	}
+
+	return unmerged;
+}
+
+struct rename
+{
+	struct diff_filepair *pair;
+	struct stage_data *src_entry;
+	struct stage_data *dst_entry;
+	unsigned processed:1;
+};
+
+/*
+ * Get information of all renames which occurred between 'o_tree' and
+ * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and
+ * 'b_tree') to be able to associate the correct cache entries with
+ * the rename information. 'tree' is always equal to either a_tree or b_tree.
+ */
+static struct string_list *get_renames(struct merge_options *o,
+				       struct tree *tree,
+				       struct tree *o_tree,
+				       struct tree *a_tree,
+				       struct tree *b_tree,
+				       struct string_list *entries)
+{
+	int i;
+	struct string_list *renames;
+	struct diff_options opts;
+
+	renames = xcalloc(1, sizeof(struct string_list));
+	diff_setup(&opts);
+	DIFF_OPT_SET(&opts, RECURSIVE);
+	opts.detect_rename = DIFF_DETECT_RENAME;
+	opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
+			    o->diff_rename_limit >= 0 ? o->diff_rename_limit :
+			    500;
+	opts.warn_on_too_large_rename = 1;
+	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	if (diff_setup_done(&opts) < 0)
+		die("diff setup failed");
+	diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
+	diffcore_std(&opts);
+	for (i = 0; i < diff_queued_diff.nr; ++i) {
+		struct string_list_item *item;
+		struct rename *re;
+		struct diff_filepair *pair = diff_queued_diff.queue[i];
+		if (pair->status != 'R') {
+			diff_free_filepair(pair);
+			continue;
+		}
+		re = xmalloc(sizeof(*re));
+		re->processed = 0;
+		re->pair = pair;
+		item = string_list_lookup(re->pair->one->path, entries);
+		if (!item)
+			re->src_entry = insert_stage_data(re->pair->one->path,
+					o_tree, a_tree, b_tree, entries);
+		else
+			re->src_entry = item->util;
+
+		item = string_list_lookup(re->pair->two->path, entries);
+		if (!item)
+			re->dst_entry = insert_stage_data(re->pair->two->path,
+					o_tree, a_tree, b_tree, entries);
+		else
+			re->dst_entry = item->util;
+		item = string_list_insert(pair->one->path, renames);
+		item->util = re;
+	}
+	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	diff_queued_diff.nr = 0;
+	diff_flush(&opts);
+	return renames;
+}
+
+static int update_stages(const char *path, struct diff_filespec *o,
+			 struct diff_filespec *a, struct diff_filespec *b,
+			 int clear)
+{
+	int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+	if (clear)
+		if (remove_file_from_cache(path))
+			return -1;
+	if (o)
+		if (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))
+			return -1;
+	if (a)
+		if (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))
+			return -1;
+	if (b)
+		if (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))
+			return -1;
+	return 0;
+}
+
+static int remove_file(struct merge_options *o, int clean,
+		       const char *path, int no_wd)
+{
+	int update_cache = o->call_depth || clean;
+	int update_working_directory = !o->call_depth && !no_wd;
+
+	if (update_cache) {
+		if (remove_file_from_cache(path))
+			return -1;
+	}
+	if (update_working_directory) {
+		if (remove_path(path) && errno != ENOENT)
+			return -1;
+	}
+	return 0;
+}
+
+static char *unique_path(struct merge_options *o, const char *path, const char *branch)
+{
+	char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+	int suffix = 0;
+	struct stat st;
+	char *p = newpath + strlen(path);
+	strcpy(newpath, path);
+	*(p++) = '~';
+	strcpy(p, branch);
+	for (; *p; ++p)
+		if ('/' == *p)
+			*p = '_';
+	while (string_list_has_string(&o->current_file_set, newpath) ||
+	       string_list_has_string(&o->current_directory_set, newpath) ||
+	       lstat(newpath, &st) == 0)
+		sprintf(p, "_%d", suffix++);
+
+	string_list_insert(newpath, &o->current_file_set);
+	return newpath;
+}
+
+static void flush_buffer(int fd, const char *buf, unsigned long size)
+{
+	while (size > 0) {
+		long ret = write_in_full(fd, buf, size);
+		if (ret < 0) {
+			/* Ignore epipe */
+			if (errno == EPIPE)
+				break;
+			die("merge-recursive: %s", strerror(errno));
+		} else if (!ret) {
+			die("merge-recursive: disk full?");
+		}
+		size -= ret;
+		buf += ret;
+	}
+}
+
+static int would_lose_untracked(const char *path)
+{
+	int pos = cache_name_pos(path, strlen(path));
+
+	if (pos < 0)
+		pos = -1 - pos;
+	while (pos < active_nr &&
+	       !strcmp(path, active_cache[pos]->name)) {
+		/*
+		 * If stage #0, it is definitely tracked.
+		 * If it has stage #2 then it was tracked
+		 * before this merge started.  All other
+		 * cases the path was not tracked.
+		 */
+		switch (ce_stage(active_cache[pos])) {
+		case 0:
+		case 2:
+			return 0;
+		}
+		pos++;
+	}
+	return file_exists(path);
+}
+
+static int make_room_for_path(const char *path)
+{
+	int status;
+	const char *msg = "failed to create path '%s'%s";
+
+	status = safe_create_leading_directories_const(path);
+	if (status) {
+		if (status == -3) {
+			/* something else exists */
+			error(msg, path, ": perhaps a D/F conflict?");
+			return -1;
+		}
+		die(msg, path, "");
+	}
+
+	/*
+	 * Do not unlink a file in the work tree if we are not
+	 * tracking it.
+	 */
+	if (would_lose_untracked(path))
+		return error("refusing to lose untracked file at '%s'",
+			     path);
+
+	/* Successful unlink is good.. */
+	if (!unlink(path))
+		return 0;
+	/* .. and so is no existing file */
+	if (errno == ENOENT)
+		return 0;
+	/* .. but not some other error (who really cares what?) */
+	return error(msg, path, ": perhaps a D/F conflict?");
+}
+
+static void update_file_flags(struct merge_options *o,
+			      const unsigned char *sha,
+			      unsigned mode,
+			      const char *path,
+			      int update_cache,
+			      int update_wd)
+{
+	if (o->call_depth)
+		update_wd = 0;
+
+	if (update_wd) {
+		enum object_type type;
+		void *buf;
+		unsigned long size;
+
+		if (S_ISGITLINK(mode))
+			die("cannot read object %s '%s': It is a submodule!",
+			    sha1_to_hex(sha), path);
+
+		buf = read_sha1_file(sha, &type, &size);
+		if (!buf)
+			die("cannot read object %s '%s'", sha1_to_hex(sha), path);
+		if (type != OBJ_BLOB)
+			die("blob expected for %s '%s'", sha1_to_hex(sha), path);
+		if (S_ISREG(mode)) {
+			struct strbuf strbuf = STRBUF_INIT;
+			if (convert_to_working_tree(path, buf, size, &strbuf)) {
+				free(buf);
+				size = strbuf.len;
+				buf = strbuf_detach(&strbuf, NULL);
+			}
+		}
+
+		if (make_room_for_path(path) < 0) {
+			update_wd = 0;
+			free(buf);
+			goto update_index;
+		}
+		if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
+			int fd;
+			if (mode & 0100)
+				mode = 0777;
+			else
+				mode = 0666;
+			fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
+			if (fd < 0)
+				die("failed to open %s: %s", path, strerror(errno));
+			flush_buffer(fd, buf, size);
+			close(fd);
+		} else if (S_ISLNK(mode)) {
+			char *lnk = xmemdupz(buf, size);
+			safe_create_leading_directories_const(path);
+			unlink(path);
+			if (symlink(lnk, path))
+				die("failed to symlink %s: %s", path, strerror(errno));
+			free(lnk);
+		} else
+			die("do not know what to do with %06o %s '%s'",
+			    mode, sha1_to_hex(sha), path);
+		free(buf);
+	}
+ update_index:
+	if (update_cache)
+		add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
+}
+
+static void update_file(struct merge_options *o,
+			int clean,
+			const unsigned char *sha,
+			unsigned mode,
+			const char *path)
+{
+	update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);
+}
+
+/* Low level file merging, update and removal */
+
+struct merge_file_info
+{
+	unsigned char sha[20];
+	unsigned mode;
+	unsigned clean:1,
+		 merge:1;
+};
+
+static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
+{
+	unsigned long size;
+	enum object_type type;
+
+	if (!hashcmp(sha1, null_sha1)) {
+		mm->ptr = xstrdup("");
+		mm->size = 0;
+		return;
+	}
+
+	mm->ptr = read_sha1_file(sha1, &type, &size);
+	if (!mm->ptr || type != OBJ_BLOB)
+		die("unable to read blob object %s", sha1_to_hex(sha1));
+	mm->size = size;
+}
+
+static int merge_3way(struct merge_options *o,
+		      mmbuffer_t *result_buf,
+		      struct diff_filespec *one,
+		      struct diff_filespec *a,
+		      struct diff_filespec *b,
+		      const char *branch1,
+		      const char *branch2)
+{
+	mmfile_t orig, src1, src2;
+	char *name1, *name2;
+	int merge_status;
+
+	name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
+	name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
+
+	fill_mm(one->sha1, &orig);
+	fill_mm(a->sha1, &src1);
+	fill_mm(b->sha1, &src2);
+
+	merge_status = ll_merge(result_buf, a->path, &orig,
+				&src1, name1, &src2, name2,
+				o->call_depth);
+
+	free(name1);
+	free(name2);
+	free(orig.ptr);
+	free(src1.ptr);
+	free(src2.ptr);
+	return merge_status;
+}
+
+static struct merge_file_info merge_file(struct merge_options *o,
+				         struct diff_filespec *one,
+					 struct diff_filespec *a,
+					 struct diff_filespec *b,
+					 const char *branch1,
+					 const char *branch2)
+{
+	struct merge_file_info result;
+	result.merge = 0;
+	result.clean = 1;
+
+	if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
+		result.clean = 0;
+		if (S_ISREG(a->mode)) {
+			result.mode = a->mode;
+			hashcpy(result.sha, a->sha1);
+		} else {
+			result.mode = b->mode;
+			hashcpy(result.sha, b->sha1);
+		}
+	} else {
+		if (!sha_eq(a->sha1, one->sha1) && !sha_eq(b->sha1, one->sha1))
+			result.merge = 1;
+
+		/*
+		 * Merge modes
+		 */
+		if (a->mode == b->mode || a->mode == one->mode)
+			result.mode = b->mode;
+		else {
+			result.mode = a->mode;
+			if (b->mode != one->mode) {
+				result.clean = 0;
+				result.merge = 1;
+			}
+		}
+
+		if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, one->sha1))
+			hashcpy(result.sha, b->sha1);
+		else if (sha_eq(b->sha1, one->sha1))
+			hashcpy(result.sha, a->sha1);
+		else if (S_ISREG(a->mode)) {
+			mmbuffer_t result_buf;
+			int merge_status;
+
+			merge_status = merge_3way(o, &result_buf, one, a, b,
+						  branch1, branch2);
+
+			if ((merge_status < 0) || !result_buf.ptr)
+				die("Failed to execute internal merge");
+
+			if (write_sha1_file(result_buf.ptr, result_buf.size,
+					    blob_type, result.sha))
+				die("Unable to add %s to database",
+				    a->path);
+
+			free(result_buf.ptr);
+			result.clean = (merge_status == 0);
+		} else if (S_ISGITLINK(a->mode)) {
+			result.clean = 0;
+			hashcpy(result.sha, a->sha1);
+		} else if (S_ISLNK(a->mode)) {
+			hashcpy(result.sha, a->sha1);
+
+			if (!sha_eq(a->sha1, b->sha1))
+				result.clean = 0;
+		} else {
+			die("unsupported object type in the tree");
+		}
+	}
+
+	return result;
+}
+
+static void conflict_rename_rename(struct merge_options *o,
+				   struct rename *ren1,
+				   const char *branch1,
+				   struct rename *ren2,
+				   const char *branch2)
+{
+	char *del[2];
+	int delp = 0;
+	const char *ren1_dst = ren1->pair->two->path;
+	const char *ren2_dst = ren2->pair->two->path;
+	const char *dst_name1 = ren1_dst;
+	const char *dst_name2 = ren2_dst;
+	if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
+		dst_name1 = del[delp++] = unique_path(o, ren1_dst, branch1);
+		output(o, 1, "%s is a directory in %s adding as %s instead",
+		       ren1_dst, branch2, dst_name1);
+		remove_file(o, 0, ren1_dst, 0);
+	}
+	if (string_list_has_string(&o->current_directory_set, ren2_dst)) {
+		dst_name2 = del[delp++] = unique_path(o, ren2_dst, branch2);
+		output(o, 1, "%s is a directory in %s adding as %s instead",
+		       ren2_dst, branch1, dst_name2);
+		remove_file(o, 0, ren2_dst, 0);
+	}
+	if (o->call_depth) {
+		remove_file_from_cache(dst_name1);
+		remove_file_from_cache(dst_name2);
+		/*
+		 * Uncomment to leave the conflicting names in the resulting tree
+		 *
+		 * update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1);
+		 * update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2);
+		 */
+	} else {
+		update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
+		update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+	}
+	while (delp--)
+		free(del[delp]);
+}
+
+static void conflict_rename_dir(struct merge_options *o,
+				struct rename *ren1,
+				const char *branch1)
+{
+	char *new_path = unique_path(o, ren1->pair->two->path, branch1);
+	output(o, 1, "Renaming %s to %s instead", ren1->pair->one->path, new_path);
+	remove_file(o, 0, ren1->pair->two->path, 0);
+	update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
+	free(new_path);
+}
+
+static void conflict_rename_rename_2(struct merge_options *o,
+				     struct rename *ren1,
+				     const char *branch1,
+				     struct rename *ren2,
+				     const char *branch2)
+{
+	char *new_path1 = unique_path(o, ren1->pair->two->path, branch1);
+	char *new_path2 = unique_path(o, ren2->pair->two->path, branch2);
+	output(o, 1, "Renaming %s to %s and %s to %s instead",
+	       ren1->pair->one->path, new_path1,
+	       ren2->pair->one->path, new_path2);
+	remove_file(o, 0, ren1->pair->two->path, 0);
+	update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
+	update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
+	free(new_path2);
+	free(new_path1);
+}
+
+static int process_renames(struct merge_options *o,
+			   struct string_list *a_renames,
+			   struct string_list *b_renames)
+{
+	int clean_merge = 1, i, j;
+	struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+	const struct rename *sre;
+
+	for (i = 0; i < a_renames->nr; i++) {
+		sre = a_renames->items[i].util;
+		string_list_insert(sre->pair->two->path, &a_by_dst)->util
+			= sre->dst_entry;
+	}
+	for (i = 0; i < b_renames->nr; i++) {
+		sre = b_renames->items[i].util;
+		string_list_insert(sre->pair->two->path, &b_by_dst)->util
+			= sre->dst_entry;
+	}
+
+	for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
+		char *src;
+		struct string_list *renames1, *renames2Dst;
+		struct rename *ren1 = NULL, *ren2 = NULL;
+		const char *branch1, *branch2;
+		const char *ren1_src, *ren1_dst;
+
+		if (i >= a_renames->nr) {
+			ren2 = b_renames->items[j++].util;
+		} else if (j >= b_renames->nr) {
+			ren1 = a_renames->items[i++].util;
+		} else {
+			int compare = strcmp(a_renames->items[i].string,
+					     b_renames->items[j].string);
+			if (compare <= 0)
+				ren1 = a_renames->items[i++].util;
+			if (compare >= 0)
+				ren2 = b_renames->items[j++].util;
+		}
+
+		/* TODO: refactor, so that 1/2 are not needed */
+		if (ren1) {
+			renames1 = a_renames;
+			renames2Dst = &b_by_dst;
+			branch1 = o->branch1;
+			branch2 = o->branch2;
+		} else {
+			struct rename *tmp;
+			renames1 = b_renames;
+			renames2Dst = &a_by_dst;
+			branch1 = o->branch2;
+			branch2 = o->branch1;
+			tmp = ren2;
+			ren2 = ren1;
+			ren1 = tmp;
+		}
+		src = ren1->pair->one->path;
+
+		ren1->dst_entry->processed = 1;
+		ren1->src_entry->processed = 1;
+
+		if (ren1->processed)
+			continue;
+		ren1->processed = 1;
+
+		ren1_src = ren1->pair->one->path;
+		ren1_dst = ren1->pair->two->path;
+
+		if (ren2) {
+			const char *ren2_src = ren2->pair->one->path;
+			const char *ren2_dst = ren2->pair->two->path;
+			/* Renamed in 1 and renamed in 2 */
+			if (strcmp(ren1_src, ren2_src) != 0)
+				die("ren1.src != ren2.src");
+			ren2->dst_entry->processed = 1;
+			ren2->processed = 1;
+			if (strcmp(ren1_dst, ren2_dst) != 0) {
+				clean_merge = 0;
+				output(o, 1, "CONFLICT (rename/rename): "
+				       "Rename \"%s\"->\"%s\" in branch \"%s\" "
+				       "rename \"%s\"->\"%s\" in \"%s\"%s",
+				       src, ren1_dst, branch1,
+				       src, ren2_dst, branch2,
+				       o->call_depth ? " (left unresolved)": "");
+				if (o->call_depth) {
+					remove_file_from_cache(src);
+					update_file(o, 0, ren1->pair->one->sha1,
+						    ren1->pair->one->mode, src);
+				}
+				conflict_rename_rename(o, ren1, branch1, ren2, branch2);
+			} else {
+				struct merge_file_info mfi;
+				remove_file(o, 1, ren1_src, 1);
+				mfi = merge_file(o,
+						 ren1->pair->one,
+						 ren1->pair->two,
+						 ren2->pair->two,
+						 branch1,
+						 branch2);
+				if (mfi.merge || !mfi.clean)
+					output(o, 1, "Renaming %s->%s", src, ren1_dst);
+
+				if (mfi.merge)
+					output(o, 2, "Auto-merging %s", ren1_dst);
+
+				if (!mfi.clean) {
+					output(o, 1, "CONFLICT (content): merge conflict in %s",
+					       ren1_dst);
+					clean_merge = 0;
+
+					if (!o->call_depth)
+						update_stages(ren1_dst,
+							      ren1->pair->one,
+							      ren1->pair->two,
+							      ren2->pair->two,
+							      1 /* clear */);
+				}
+				update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+			}
+		} else {
+			/* Renamed in 1, maybe changed in 2 */
+			struct string_list_item *item;
+			/* we only use sha1 and mode of these */
+			struct diff_filespec src_other, dst_other;
+			int try_merge, stage = a_renames == renames1 ? 3: 2;
+
+			remove_file(o, 1, ren1_src, o->call_depth || stage == 3);
+
+			hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
+			src_other.mode = ren1->src_entry->stages[stage].mode;
+			hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha);
+			dst_other.mode = ren1->dst_entry->stages[stage].mode;
+
+			try_merge = 0;
+
+			if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
+				clean_merge = 0;
+				output(o, 1, "CONFLICT (rename/directory): Rename %s->%s in %s "
+				       " directory %s added in %s",
+				       ren1_src, ren1_dst, branch1,
+				       ren1_dst, branch2);
+				conflict_rename_dir(o, ren1, branch1);
+			} else if (sha_eq(src_other.sha1, null_sha1)) {
+				clean_merge = 0;
+				output(o, 1, "CONFLICT (rename/delete): Rename %s->%s in %s "
+				       "and deleted in %s",
+				       ren1_src, ren1_dst, branch1,
+				       branch2);
+				update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+				update_stages(ren1_dst, NULL,
+						branch1 == o->branch1 ?
+						ren1->pair->two : NULL,
+						branch1 == o->branch1 ?
+						NULL : ren1->pair->two, 1);
+			} else if (!sha_eq(dst_other.sha1, null_sha1)) {
+				const char *new_path;
+				clean_merge = 0;
+				try_merge = 1;
+				output(o, 1, "CONFLICT (rename/add): Rename %s->%s in %s. "
+				       "%s added in %s",
+				       ren1_src, ren1_dst, branch1,
+				       ren1_dst, branch2);
+				new_path = unique_path(o, ren1_dst, branch2);
+				output(o, 1, "Adding as %s instead", new_path);
+				update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+			} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
+				ren2 = item->util;
+				clean_merge = 0;
+				ren2->processed = 1;
+				output(o, 1, "CONFLICT (rename/rename): "
+				       "Rename %s->%s in %s. "
+				       "Rename %s->%s in %s",
+				       ren1_src, ren1_dst, branch1,
+				       ren2->pair->one->path, ren2->pair->two->path, branch2);
+				conflict_rename_rename_2(o, ren1, branch1, ren2, branch2);
+			} else
+				try_merge = 1;
+
+			if (try_merge) {
+				struct diff_filespec *one, *a, *b;
+				struct merge_file_info mfi;
+				src_other.path = (char *)ren1_src;
+
+				one = ren1->pair->one;
+				if (a_renames == renames1) {
+					a = ren1->pair->two;
+					b = &src_other;
+				} else {
+					b = ren1->pair->two;
+					a = &src_other;
+				}
+				mfi = merge_file(o, one, a, b,
+						o->branch1, o->branch2);
+
+				if (mfi.clean &&
+				    sha_eq(mfi.sha, ren1->pair->two->sha1) &&
+				    mfi.mode == ren1->pair->two->mode)
+					/*
+					 * This messaged is part of
+					 * t6022 test. If you change
+					 * it update the test too.
+					 */
+					output(o, 3, "Skipped %s (merged same as existing)", ren1_dst);
+				else {
+					if (mfi.merge || !mfi.clean)
+						output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
+					if (mfi.merge)
+						output(o, 2, "Auto-merging %s", ren1_dst);
+					if (!mfi.clean) {
+						output(o, 1, "CONFLICT (rename/modify): Merge conflict in %s",
+						       ren1_dst);
+						clean_merge = 0;
+
+						if (!o->call_depth)
+							update_stages(ren1_dst,
+								      one, a, b, 1);
+					}
+					update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+				}
+			}
+		}
+	}
+	string_list_clear(&a_by_dst, 0);
+	string_list_clear(&b_by_dst, 0);
+
+	return clean_merge;
+}
+
+static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
+{
+	return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
+}
+
+/* Per entry merge function */
+static int process_entry(struct merge_options *o,
+			 const char *path, struct stage_data *entry)
+{
+	/*
+	printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
+	print_index_entry("\tpath: ", entry);
+	*/
+	int clean_merge = 1;
+	unsigned o_mode = entry->stages[1].mode;
+	unsigned a_mode = entry->stages[2].mode;
+	unsigned b_mode = entry->stages[3].mode;
+	unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
+	unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
+	unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
+
+	if (o_sha && (!a_sha || !b_sha)) {
+		/* Case A: Deleted in one */
+		if ((!a_sha && !b_sha) ||
+		    (sha_eq(a_sha, o_sha) && !b_sha) ||
+		    (!a_sha && sha_eq(b_sha, o_sha))) {
+			/* Deleted in both or deleted in one and
+			 * unchanged in the other */
+			if (a_sha)
+				output(o, 2, "Removing %s", path);
+			/* do not touch working file if it did not exist */
+			remove_file(o, 1, path, !a_sha);
+		} else {
+			/* Deleted in one and changed in the other */
+			clean_merge = 0;
+			if (!a_sha) {
+				output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
+				       "and modified in %s. Version %s of %s left in tree.",
+				       path, o->branch1,
+				       o->branch2, o->branch2, path);
+				update_file(o, 0, b_sha, b_mode, path);
+			} else {
+				output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
+				       "and modified in %s. Version %s of %s left in tree.",
+				       path, o->branch2,
+				       o->branch1, o->branch1, path);
+				update_file(o, 0, a_sha, a_mode, path);
+			}
+		}
+
+	} else if ((!o_sha && a_sha && !b_sha) ||
+		   (!o_sha && !a_sha && b_sha)) {
+		/* Case B: Added in one. */
+		const char *add_branch;
+		const char *other_branch;
+		unsigned mode;
+		const unsigned char *sha;
+		const char *conf;
+
+		if (a_sha) {
+			add_branch = o->branch1;
+			other_branch = o->branch2;
+			mode = a_mode;
+			sha = a_sha;
+			conf = "file/directory";
+		} else {
+			add_branch = o->branch2;
+			other_branch = o->branch1;
+			mode = b_mode;
+			sha = b_sha;
+			conf = "directory/file";
+		}
+		if (string_list_has_string(&o->current_directory_set, path)) {
+			const char *new_path = unique_path(o, path, add_branch);
+			clean_merge = 0;
+			output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
+			       "Adding %s as %s",
+			       conf, path, other_branch, path, new_path);
+			remove_file(o, 0, path, 0);
+			update_file(o, 0, sha, mode, new_path);
+		} else {
+			output(o, 2, "Adding %s", path);
+			update_file(o, 1, sha, mode, path);
+		}
+	} else if (a_sha && b_sha) {
+		/* Case C: Added in both (check for same permissions) and */
+		/* case D: Modified in both, but differently. */
+		const char *reason = "content";
+		struct merge_file_info mfi;
+		struct diff_filespec one, a, b;
+
+		if (!o_sha) {
+			reason = "add/add";
+			o_sha = (unsigned char *)null_sha1;
+		}
+		output(o, 2, "Auto-merging %s", path);
+		one.path = a.path = b.path = (char *)path;
+		hashcpy(one.sha1, o_sha);
+		one.mode = o_mode;
+		hashcpy(a.sha1, a_sha);
+		a.mode = a_mode;
+		hashcpy(b.sha1, b_sha);
+		b.mode = b_mode;
+
+		mfi = merge_file(o, &one, &a, &b,
+				 o->branch1, o->branch2);
+
+		clean_merge = mfi.clean;
+		if (!mfi.clean) {
+			if (S_ISGITLINK(mfi.mode))
+				reason = "submodule";
+			output(o, 1, "CONFLICT (%s): Merge conflict in %s",
+					reason, path);
+		}
+		update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
+	} else if (!o_sha && !a_sha && !b_sha) {
+		/*
+		 * this entry was deleted altogether. a_mode == 0 means
+		 * we had that path and want to actively remove it.
+		 */
+		remove_file(o, 1, path, !a_mode);
+	} else
+		die("Fatal merge failure, shouldn't happen.");
+
+	return clean_merge;
+}
+
+int merge_trees(struct merge_options *o,
+		struct tree *head,
+		struct tree *merge,
+		struct tree *common,
+		struct tree **result)
+{
+	int code, clean;
+
+	if (o->subtree_merge) {
+		merge = shift_tree_object(head, merge);
+		common = shift_tree_object(head, common);
+	}
+
+	if (sha_eq(common->object.sha1, merge->object.sha1)) {
+		output(o, 0, "Already uptodate!");
+		*result = head;
+		return 1;
+	}
+
+	code = git_merge_trees(o->call_depth, common, head, merge);
+
+	if (code != 0)
+		die("merging of trees %s and %s failed",
+		    sha1_to_hex(head->object.sha1),
+		    sha1_to_hex(merge->object.sha1));
+
+	if (unmerged_cache()) {
+		struct string_list *entries, *re_head, *re_merge;
+		int i;
+		string_list_clear(&o->current_file_set, 1);
+		string_list_clear(&o->current_directory_set, 1);
+		get_files_dirs(o, head);
+		get_files_dirs(o, merge);
+
+		entries = get_unmerged();
+		re_head  = get_renames(o, head, common, head, merge, entries);
+		re_merge = get_renames(o, merge, common, head, merge, entries);
+		clean = process_renames(o, re_head, re_merge);
+		for (i = 0; i < entries->nr; i++) {
+			const char *path = entries->items[i].string;
+			struct stage_data *e = entries->items[i].util;
+			if (!e->processed
+				&& !process_entry(o, path, e))
+				clean = 0;
+		}
+
+		string_list_clear(re_merge, 0);
+		string_list_clear(re_head, 0);
+		string_list_clear(entries, 1);
+
+	}
+	else
+		clean = 1;
+
+	if (o->call_depth)
+		*result = write_tree_from_memory(o);
+
+	return clean;
+}
+
+static struct commit_list *reverse_commit_list(struct commit_list *list)
+{
+	struct commit_list *next = NULL, *current, *backup;
+	for (current = list; current; current = backup) {
+		backup = current->next;
+		current->next = next;
+		next = current;
+	}
+	return next;
+}
+
+/*
+ * Merge the commits h1 and h2, return the resulting virtual
+ * commit object and a flag indicating the cleanness of the merge.
+ */
+int merge_recursive(struct merge_options *o,
+		    struct commit *h1,
+		    struct commit *h2,
+		    struct commit_list *ca,
+		    struct commit **result)
+{
+	struct commit_list *iter;
+	struct commit *merged_common_ancestors;
+	struct tree *mrtree = mrtree;
+	int clean;
+
+	if (show(o, 4)) {
+		output(o, 4, "Merging:");
+		output_commit_title(o, h1);
+		output_commit_title(o, h2);
+	}
+
+	if (!ca) {
+		ca = get_merge_bases(h1, h2, 1);
+		ca = reverse_commit_list(ca);
+	}
+
+	if (show(o, 5)) {
+		output(o, 5, "found %u common ancestor(s):", commit_list_count(ca));
+		for (iter = ca; iter; iter = iter->next)
+			output_commit_title(o, iter->item);
+	}
+
+	merged_common_ancestors = pop_commit(&ca);
+	if (merged_common_ancestors == NULL) {
+		/* if there is no common ancestor, make an empty tree */
+		struct tree *tree = xcalloc(1, sizeof(struct tree));
+
+		tree->object.parsed = 1;
+		tree->object.type = OBJ_TREE;
+		pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
+		merged_common_ancestors = make_virtual_commit(tree, "ancestor");
+	}
+
+	for (iter = ca; iter; iter = iter->next) {
+		const char *saved_b1, *saved_b2;
+		o->call_depth++;
+		/*
+		 * When the merge fails, the result contains files
+		 * with conflict markers. The cleanness flag is
+		 * ignored, it was never actually used, as result of
+		 * merge_trees has always overwritten it: the committed
+		 * "conflicts" were already resolved.
+		 */
+		discard_cache();
+		saved_b1 = o->branch1;
+		saved_b2 = o->branch2;
+		o->branch1 = "Temporary merge branch 1";
+		o->branch2 = "Temporary merge branch 2";
+		merge_recursive(o, merged_common_ancestors, iter->item,
+				NULL, &merged_common_ancestors);
+		o->branch1 = saved_b1;
+		o->branch2 = saved_b2;
+		o->call_depth--;
+
+		if (!merged_common_ancestors)
+			die("merge returned no commit");
+	}
+
+	discard_cache();
+	if (!o->call_depth)
+		read_cache();
+
+	clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,
+			    &mrtree);
+
+	if (o->call_depth) {
+		*result = make_virtual_commit(mrtree, "merged tree");
+		commit_list_insert(h1, &(*result)->parents);
+		commit_list_insert(h2, &(*result)->parents->next);
+	}
+	flush_output(o);
+	return clean;
+}
+
+static struct commit *get_ref(const unsigned char *sha1, const char *name)
+{
+	struct object *object;
+
+	object = deref_tag(parse_object(sha1), name, strlen(name));
+	if (!object)
+		return NULL;
+	if (object->type == OBJ_TREE)
+		return make_virtual_commit((struct tree*)object, name);
+	if (object->type != OBJ_COMMIT)
+		return NULL;
+	if (parse_commit((struct commit *)object))
+		return NULL;
+	return (struct commit *)object;
+}
+
+int merge_recursive_generic(struct merge_options *o,
+			    const unsigned char *head,
+			    const unsigned char *merge,
+			    int num_base_list,
+			    const unsigned char **base_list,
+			    struct commit **result)
+{
+	int clean, index_fd;
+	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+	struct commit *head_commit = get_ref(head, o->branch1);
+	struct commit *next_commit = get_ref(merge, o->branch2);
+	struct commit_list *ca = NULL;
+
+	if (base_list) {
+		int i;
+		for (i = 0; i < num_base_list; ++i) {
+			struct commit *base;
+			if (!(base = get_ref(base_list[i], sha1_to_hex(base_list[i]))))
+				return error("Could not parse object '%s'",
+					sha1_to_hex(base_list[i]));
+			commit_list_insert(base, &ca);
+		}
+	}
+
+	index_fd = hold_locked_index(lock, 1);
+	clean = merge_recursive(o, head_commit, next_commit, ca,
+			result);
+	if (active_cache_changed &&
+			(write_cache(index_fd, active_cache, active_nr) ||
+			 commit_locked_index(lock)))
+		return error("Unable to write index.");
+
+	return clean ? 0 : 1;
+}
+
+static int merge_recursive_config(const char *var, const char *value, void *cb)
+{
+	struct merge_options *o = cb;
+	if (!strcasecmp(var, "merge.verbosity")) {
+		o->verbosity = git_config_int(var, value);
+		return 0;
+	}
+	if (!strcasecmp(var, "diff.renamelimit")) {
+		o->diff_rename_limit = git_config_int(var, value);
+		return 0;
+	}
+	if (!strcasecmp(var, "merge.renamelimit")) {
+		o->merge_rename_limit = git_config_int(var, value);
+		return 0;
+	}
+	return git_xmerge_config(var, value, cb);
+}
+
+void init_merge_options(struct merge_options *o)
+{
+	memset(o, 0, sizeof(struct merge_options));
+	o->verbosity = 2;
+	o->buffer_output = 1;
+	o->diff_rename_limit = -1;
+	o->merge_rename_limit = -1;
+	git_config(merge_recursive_config, o);
+	if (getenv("GIT_MERGE_VERBOSITY"))
+		o->verbosity =
+			strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
+	if (o->verbosity >= 5)
+		o->buffer_output = 0;
+	strbuf_init(&o->obuf, 0);
+	memset(&o->current_file_set, 0, sizeof(struct string_list));
+	o->current_file_set.strdup_strings = 1;
+	memset(&o->current_directory_set, 0, sizeof(struct string_list));
+	o->current_directory_set.strdup_strings = 1;
+}
diff --git a/merge-recursive.h b/merge-recursive.h
index f37630a..fd138ca 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -1,20 +1,48 @@
 #ifndef MERGE_RECURSIVE_H
 #define MERGE_RECURSIVE_H
 
-int merge_recursive(struct commit *h1,
+#include "string-list.h"
+
+struct merge_options {
+	const char *branch1;
+	const char *branch2;
+	unsigned subtree_merge : 1;
+	unsigned buffer_output : 1;
+	int verbosity;
+	int diff_rename_limit;
+	int merge_rename_limit;
+	int call_depth;
+	struct strbuf obuf;
+	struct string_list current_file_set;
+	struct string_list current_directory_set;
+};
+
+/* merge_trees() but with recursive ancestor consolidation */
+int merge_recursive(struct merge_options *o,
+		    struct commit *h1,
 		    struct commit *h2,
-		    const char *branch1,
-		    const char *branch2,
 		    struct commit_list *ancestors,
 		    struct commit **result);
 
-int merge_trees(struct tree *head,
+/* rename-detecting three-way merge, no recursion */
+int merge_trees(struct merge_options *o,
+		struct tree *head,
 		struct tree *merge,
 		struct tree *common,
-		const char *branch1,
-		const char *branch2,
 		struct tree **result);
 
-struct tree *write_tree_from_memory(void);
+/*
+ * "git-merge-recursive" can be fed trees; wrap them into
+ * virtual commits and call merge_recursive() proper.
+ */
+int merge_recursive_generic(struct merge_options *o,
+			    const unsigned char *head,
+			    const unsigned char *merge,
+			    int num_ca,
+			    const unsigned char **ca,
+			    struct commit **result);
+
+void init_merge_options(struct merge_options *o);
+struct tree *write_tree_from_memory(struct merge_options *o);
 
 #endif
diff --git a/merge-tree.c b/merge-tree.c
index 02fc10f..f01e7c8 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -2,8 +2,9 @@
 #include "tree-walk.h"
 #include "xdiff-interface.h"
 #include "blob.h"
+#include "exec_cmd.h"
 
-static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
+static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
 static int resolve_directories = 1;
 
 struct merge_list {
@@ -158,9 +159,8 @@
 
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
 {
-	struct merge_list *res = xmalloc(sizeof(*res));
+	struct merge_list *res = xcalloc(1, sizeof(*res));
 
-	memset(res, 0, sizeof(*res));
 	res->stage = stage;
 	res->path = path;
 	res->mode = mode;
@@ -345,6 +345,8 @@
 	if (argc != 4)
 		usage(merge_tree_usage);
 
+	git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
 	buf1 = get_tree_descriptor(t+0, argv[1]);
diff --git a/mktag.c b/mktag.c
index 0b34341..99a356e 100644
--- a/mktag.c
+++ b/mktag.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "tag.h"
+#include "exec_cmd.h"
 
 /*
  * A signature file has a very simple fixed format: four lines
@@ -153,15 +154,16 @@
 
 int main(int argc, char **argv)
 {
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	unsigned char result_sha1[20];
 
 	if (argc != 1)
-		usage("git-mktag < signaturefile");
+		usage("git mktag < signaturefile");
+
+	git_extract_argv0_path(argv[0]);
 
 	setup_git_directory();
 
-	strbuf_init(&buf, 0);
 	if (strbuf_read(&buf, 0, 4096) < 0) {
 		die("could not read from stdin");
 	}
diff --git a/mktree.c b/mktree.c
index e0da110..137a095 100644
--- a/mktree.c
+++ b/mktree.c
@@ -6,6 +6,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "tree.h"
+#include "exec_cmd.h"
 
 static struct treeent {
 	unsigned mode;
@@ -61,15 +62,17 @@
 	write_sha1_file(buf.buf, buf.len, tree_type, sha1);
 }
 
-static const char mktree_usage[] = "git-mktree [-z]";
+static const char mktree_usage[] = "git mktree [-z]";
 
 int main(int ac, char **av)
 {
-	struct strbuf sb;
-	struct strbuf p_uq;
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf p_uq = STRBUF_INIT;
 	unsigned char sha1[20];
 	int line_termination = '\n';
 
+	git_extract_argv0_path(av[0]);
+
 	setup_git_directory();
 
 	while ((1 < ac) && av[1][0] == '-') {
@@ -82,8 +85,6 @@
 		av++;
 	}
 
-	strbuf_init(&sb, 0);
-	strbuf_init(&p_uq, 0);
 	while (strbuf_getline(&sb, stdin, line_termination) != EOF) {
 		char *ptr, *ntr;
 		unsigned mode;
diff --git a/mozilla-sha1/sha1.c b/mozilla-sha1/sha1.c
index 3f06b83..95a4ebf 100644
--- a/mozilla-sha1/sha1.c
+++ b/mozilla-sha1/sha1.c
@@ -35,9 +35,9 @@
 
 #include "sha1.h"
 
-static void shaHashBlock(SHA_CTX *ctx);
+static void shaHashBlock(moz_SHA_CTX *ctx);
 
-void SHA1_Init(SHA_CTX *ctx) {
+void moz_SHA1_Init(moz_SHA_CTX *ctx) {
   int i;
 
   ctx->lenW = 0;
@@ -56,7 +56,7 @@
 }
 
 
-void SHA1_Update(SHA_CTX *ctx, const void *_dataIn, int len) {
+void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *_dataIn, int len) {
   const unsigned char *dataIn = _dataIn;
   int i;
 
@@ -75,7 +75,7 @@
 }
 
 
-void SHA1_Final(unsigned char hashout[20], SHA_CTX *ctx) {
+void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx) {
   unsigned char pad0x80 = 0x80;
   unsigned char pad0x00 = 0x00;
   unsigned char padlen[8];
@@ -91,10 +91,10 @@
   padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
   padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
   padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
-  SHA1_Update(ctx, &pad0x80, 1);
+  moz_SHA1_Update(ctx, &pad0x80, 1);
   while (ctx->lenW != 56)
-    SHA1_Update(ctx, &pad0x00, 1);
-  SHA1_Update(ctx, padlen, 8);
+    moz_SHA1_Update(ctx, &pad0x00, 1);
+  moz_SHA1_Update(ctx, padlen, 8);
 
   /* Output hash
    */
@@ -106,13 +106,13 @@
   /*
    *  Re-initialize the context (also zeroizes contents)
    */
-  SHA1_Init(ctx);
+  moz_SHA1_Init(ctx);
 }
 
 
 #define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n))))
 
-static void shaHashBlock(SHA_CTX *ctx) {
+static void shaHashBlock(moz_SHA_CTX *ctx) {
   int t;
   unsigned int A,B,C,D,E,TEMP;
 
diff --git a/mozilla-sha1/sha1.h b/mozilla-sha1/sha1.h
index 16f2d3d..aa48a46 100644
--- a/mozilla-sha1/sha1.h
+++ b/mozilla-sha1/sha1.h
@@ -38,8 +38,13 @@
   unsigned int W[80];
   int lenW;
   unsigned int sizeHi,sizeLo;
-} SHA_CTX;
+} moz_SHA_CTX;
 
-void SHA1_Init(SHA_CTX *ctx);
-void SHA1_Update(SHA_CTX *ctx, const void *dataIn, int len);
-void SHA1_Final(unsigned char hashout[20], SHA_CTX *ctx);
+void moz_SHA1_Init(moz_SHA_CTX *ctx);
+void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *dataIn, int len);
+void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx);
+
+#define git_SHA_CTX	moz_SHA_CTX
+#define git_SHA1_Init	moz_SHA1_Init
+#define git_SHA1_Update	moz_SHA1_Update
+#define git_SHA1_Final	moz_SHA1_Final
diff --git a/object.c b/object.c
index 50b6528..7e6a92c 100644
--- a/object.c
+++ b/object.c
@@ -268,3 +268,22 @@
 	objects[nr].mode = mode;
 	array->nr = ++nr;
 }
+
+void object_array_remove_duplicates(struct object_array *array)
+{
+	int ref, src, dst;
+	struct object_array_entry *objects = array->objects;
+
+	for (ref = 0; ref < array->nr - 1; ref++) {
+		for (src = ref + 1, dst = src;
+		     src < array->nr;
+		     src++) {
+			if (!strcmp(objects[ref].name, objects[src].name))
+				continue;
+			if (src != dst)
+				objects[dst] = objects[src];
+			dst++;
+		}
+		array->nr = dst;
+	}
+}
diff --git a/object.h b/object.h
index 036bd66..89dd0c4 100644
--- a/object.h
+++ b/object.h
@@ -41,7 +41,18 @@
 extern unsigned int get_max_object_index(void);
 extern struct object *get_indexed_object(unsigned int);
 
-/** Internal only **/
+/*
+ * This can be used to see if we have heard of the object before, but
+ * it can return "yes we have, and here is a half-initialised object"
+ * for an object that we haven't loaded/parsed yet.
+ *
+ * When parsing a commit to create an in-core commit object, its
+ * parents list holds commit objects that represent its parents, but
+ * they are expected to be lazily initialized and do not know what
+ * their trees or parents are yet.  When this function returns such a
+ * half-initialised objects, the caller is expected to initialize them
+ * by calling parse_object() on them.
+ */
 struct object *lookup_object(const unsigned char *sha1);
 
 extern void *create_object(const unsigned char *sha1, int type, void *obj);
@@ -71,5 +82,6 @@
 /* Object array handling .. */
 void add_object_array(struct object *obj, const char *name, struct object_array *array);
 void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
+void object_array_remove_duplicates(struct object_array *);
 
 #endif /* OBJECT_H */
diff --git a/pack-check.c b/pack-check.c
index f596bf2..90c33b1 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -47,7 +47,7 @@
 {
 	off_t index_size = p->index_size;
 	const unsigned char *index_base = p->index_data;
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	unsigned char sha1[20], *pack_sig;
 	off_t offset = 0, pack_sig_ofs = p->pack_size - 20;
 	uint32_t nr_objects, i;
@@ -60,16 +60,16 @@
 	 * immediately.
 	 */
 
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 	while (offset < pack_sig_ofs) {
 		unsigned int remaining;
 		unsigned char *in = use_pack(p, w_curs, offset, &remaining);
 		offset += remaining;
 		if (offset > pack_sig_ofs)
 			remaining -= (unsigned int)(offset - pack_sig_ofs);
-		SHA1_Update(&ctx, in, remaining);
+		git_SHA1_Update(&ctx, in, remaining);
 	}
-	SHA1_Final(sha1, &ctx);
+	git_SHA1_Final(sha1, &ctx);
 	pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
 	if (hashcmp(sha1, pack_sig))
 		err = error("%s SHA1 checksum mismatch",
@@ -135,7 +135,7 @@
 {
 	off_t index_size;
 	const unsigned char *index_base;
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	unsigned char sha1[20];
 	int err = 0;
 	struct pack_window *w_curs = NULL;
@@ -146,9 +146,9 @@
 	index_base = p->index_data;
 
 	/* Verify SHA1 sum of the index file */
-	SHA1_Init(&ctx);
-	SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
-	SHA1_Final(sha1, &ctx);
+	git_SHA1_Init(&ctx);
+	git_SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
+	git_SHA1_Final(sha1, &ctx);
 	if (hashcmp(sha1, index_base + index_size - 20))
 		err = error("Packfile index for %s SHA1 mismatch",
 			    p->pack_name);
diff --git a/pack-redundant.c b/pack-redundant.c
index 25b81a4..48a12bc 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -7,6 +7,7 @@
 */
 
 #include "cache.h"
+#include "exec_cmd.h"
 
 #define BLKSIZE 512
 
@@ -463,7 +464,7 @@
 		pll_free(perm_all);
 	}
 	if (perm_ok == NULL)
-		die("Internal error: No complete sets found!\n");
+		die("Internal error: No complete sets found!");
 
 	/* find the permutation with the smallest size */
 	perm = perm_ok;
@@ -573,14 +574,14 @@
 	struct packed_git *p = packed_git;
 
 	if (strlen(filename) < 40)
-		die("Bad pack filename: %s\n", filename);
+		die("Bad pack filename: %s", filename);
 
 	while (p) {
 		if (strstr(p->pack_name, filename))
 			return add_pack(p);
 		p = p->next;
 	}
-	die("Filename %s not found in packed_git\n", filename);
+	die("Filename %s not found in packed_git", filename);
 }
 
 static void load_all(void)
@@ -601,6 +602,8 @@
 	unsigned char *sha1;
 	char buf[42]; /* 40 byte sha1 + \n + \0 */
 
+	git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
 	for (i = 1; i < argc; i++) {
@@ -636,7 +639,7 @@
 			add_pack_file(*(argv + i++));
 
 	if (local_packs == NULL)
-		die("Zero packs found!\n");
+		die("Zero packs found!");
 
 	load_all_objects();
 
diff --git a/pack-revindex.c b/pack-revindex.c
index 6096b62..1de53c8 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -140,7 +140,8 @@
 		else
 			lo = mi + 1;
 	} while (lo < hi);
-	die("internal error: pack revindex corrupt");
+	error("bad offset for revindex");
+	return NULL;
 }
 
 void discard_revindex(void)
diff --git a/pack-write.c b/pack-write.c
index 3621f1d..7053538 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -25,7 +25,7 @@
 	off_t last_obj_offset = 0;
 	uint32_t array[256];
 	int i, fd;
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	uint32_t index_version;
 
 	if (nr_objects) {
@@ -44,9 +44,7 @@
 
 	if (!index_name) {
 		static char tmpfile[PATH_MAX];
-		snprintf(tmpfile, sizeof(tmpfile),
-			 "%s/pack/tmp_idx_XXXXXX", get_object_directory());
-		fd = xmkstemp(tmpfile);
+		fd = odb_mkstemp(tmpfile, sizeof(tmpfile), "pack/tmp_idx_XXXXXX");
 		index_name = xstrdup(tmpfile);
 	} else {
 		unlink(index_name);
@@ -86,7 +84,7 @@
 	sha1write(f, array, 256 * 4);
 
 	/* compute the SHA1 hash of sorted object names. */
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 
 	/*
 	 * Write the actual SHA1 entries..
@@ -99,7 +97,7 @@
 			sha1write(f, &offset, 4);
 		}
 		sha1write(f, obj->sha1, 20);
-		SHA1_Update(&ctx, obj->sha1, 20);
+		git_SHA1_Update(&ctx, obj->sha1, 20);
 	}
 
 	if (index_version >= 2) {
@@ -140,7 +138,7 @@
 
 	sha1write(f, sha1, 20);
 	sha1close(f, NULL, CSUM_FSYNC);
-	SHA1_Final(sha1, &ctx);
+	git_SHA1_Final(sha1, &ctx);
 	return index_name;
 }
 
@@ -168,12 +166,12 @@
 			 off_t partial_pack_offset)
 {
 	int aligned_sz, buf_sz = 8 * 1024;
-	SHA_CTX old_sha1_ctx, new_sha1_ctx;
+	git_SHA_CTX old_sha1_ctx, new_sha1_ctx;
 	struct pack_header hdr;
 	char *buf;
 
-	SHA1_Init(&old_sha1_ctx);
-	SHA1_Init(&new_sha1_ctx);
+	git_SHA1_Init(&old_sha1_ctx);
+	git_SHA1_Init(&new_sha1_ctx);
 
 	if (lseek(pack_fd, 0, SEEK_SET) != 0)
 		die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
@@ -181,9 +179,9 @@
 		die("Unable to reread header of %s: %s", pack_name, strerror(errno));
 	if (lseek(pack_fd, 0, SEEK_SET) != 0)
 		die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
-	SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr));
+	git_SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr));
 	hdr.hdr_entries = htonl(object_count);
-	SHA1_Update(&new_sha1_ctx, &hdr, sizeof(hdr));
+	git_SHA1_Update(&new_sha1_ctx, &hdr, sizeof(hdr));
 	write_or_die(pack_fd, &hdr, sizeof(hdr));
 	partial_pack_offset -= sizeof(hdr);
 
@@ -198,7 +196,7 @@
 			break;
 		if (n < 0)
 			die("Failed to checksum %s: %s", pack_name, strerror(errno));
-		SHA1_Update(&new_sha1_ctx, buf, n);
+		git_SHA1_Update(&new_sha1_ctx, buf, n);
 
 		aligned_sz -= n;
 		if (!aligned_sz)
@@ -207,11 +205,11 @@
 		if (!partial_pack_sha1)
 			continue;
 
-		SHA1_Update(&old_sha1_ctx, buf, n);
+		git_SHA1_Update(&old_sha1_ctx, buf, n);
 		partial_pack_offset -= n;
 		if (partial_pack_offset == 0) {
 			unsigned char sha1[20];
-			SHA1_Final(sha1, &old_sha1_ctx);
+			git_SHA1_Final(sha1, &old_sha1_ctx);
 			if (hashcmp(sha1, partial_pack_sha1) != 0)
 				die("Unexpected checksum for %s "
 				    "(disk corruption?)", pack_name);
@@ -220,7 +218,7 @@
 			 * pack, which also means making partial_pack_offset
 			 * big enough not to matter anymore.
 			 */
-			SHA1_Init(&old_sha1_ctx);
+			git_SHA1_Init(&old_sha1_ctx);
 			partial_pack_offset = ~partial_pack_offset;
 			partial_pack_offset -= MSB(partial_pack_offset, 1);
 		}
@@ -228,8 +226,8 @@
 	free(buf);
 
 	if (partial_pack_sha1)
-		SHA1_Final(partial_pack_sha1, &old_sha1_ctx);
-	SHA1_Final(new_pack_sha1, &new_sha1_ctx);
+		git_SHA1_Final(partial_pack_sha1, &old_sha1_ctx);
+	git_SHA1_Final(new_pack_sha1, &new_sha1_ctx);
 	write_or_die(pack_fd, new_pack_sha1, 20);
 	fsync_or_die(pack_fd, pack_name);
 }
@@ -239,7 +237,7 @@
 	char packname[46];
 
 	/*
-	 * The first thing we expects from index-pack's output
+	 * The first thing we expect from index-pack's output
 	 * is "pack\t%40s\n" or "keep\t%40s\n" (46 bytes) where
 	 * %40s is the newly created pack SHA1 name.  In the "keep"
 	 * case, we need it to remove the corresponding .keep file
diff --git a/pager.c b/pager.c
index 0b7e55f..4921843 100644
--- a/pager.c
+++ b/pager.c
@@ -1,4 +1,6 @@
 #include "cache.h"
+#include "run-command.h"
+#include "sigchain.h"
 
 /*
  * This is split up from the rest of git so that we can do
@@ -8,7 +10,7 @@
 static int spawned_pager;
 
 #ifndef __MINGW32__
-static void run_pager(const char *pager)
+static void pager_preexec(void)
 {
 	/*
 	 * Work around bug in "less" by not starting it until we
@@ -20,17 +22,13 @@
 	FD_SET(0, &in);
 	select(1, &in, NULL, &in, NULL);
 
-	execlp(pager, pager, NULL);
-	execl("/bin/sh", "sh", "-c", pager, NULL);
+	setenv("LESS", "FRSX", 0);
 }
-#else
-#include "run-command.h"
+#endif
 
 static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
-static struct child_process pager_process = {
-	.argv = pager_argv,
-	.in = -1
-};
+static struct child_process pager_process;
+
 static void wait_for_pager(void)
 {
 	fflush(stdout);
@@ -40,14 +38,16 @@
 	close(2);
 	finish_command(&pager_process);
 }
-#endif
+
+static void wait_for_pager_signal(int signo)
+{
+	wait_for_pager();
+	sigchain_pop(signo);
+	raise(signo);
+}
 
 void setup_pager(void)
 {
-#ifndef __MINGW32__
-	pid_t pid;
-	int fd[2];
-#endif
 	const char *pager = getenv("GIT_PAGER");
 
 	if (!isatty(1))
@@ -66,37 +66,13 @@
 
 	spawned_pager = 1; /* means we are emitting to terminal */
 
-#ifndef __MINGW32__
-	if (pipe(fd) < 0)
-		return;
-	pid = fork();
-	if (pid < 0) {
-		close(fd[0]);
-		close(fd[1]);
-		return;
-	}
-
-	/* return in the child */
-	if (!pid) {
-		dup2(fd[1], 1);
-		dup2(fd[1], 2);
-		close(fd[0]);
-		close(fd[1]);
-		return;
-	}
-
-	/* The original process turns into the PAGER */
-	dup2(fd[0], 0);
-	close(fd[0]);
-	close(fd[1]);
-
-	setenv("LESS", "FRSX", 0);
-	run_pager(pager);
-	die("unable to execute pager '%s'", pager);
-	exit(255);
-#else
 	/* spawn the pager */
 	pager_argv[2] = pager;
+	pager_process.argv = pager_argv;
+	pager_process.in = -1;
+#ifndef __MINGW32__
+	pager_process.preexec_cb = pager_preexec;
+#endif
 	if (start_command(&pager_process))
 		return;
 
@@ -107,8 +83,8 @@
 	close(pager_process.in);
 
 	/* this makes sure that the parent terminates after the pager */
+	sigchain_push_common(wait_for_pager_signal);
 	atexit(wait_for_pager);
-#endif
 }
 
 int pager_in_use(void)
diff --git a/parse-options.c b/parse-options.c
index fd08bb4..cf71bcf 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1,6 +1,7 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
 #include "cache.h"
+#include "commit.h"
 
 #define OPT_SHORT 1
 #define OPT_UNSET 2
@@ -243,6 +244,9 @@
 	ctx->out  = argv;
 	ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
 	ctx->flags = flags;
+	if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
+	    (flags & PARSE_OPT_STOP_AT_NON_OPTION))
+		die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
 }
 
 static int usage_with_options_internal(const char * const *,
@@ -252,6 +256,8 @@
 		       const struct option *options,
 		       const char * const usagestr[])
 {
+	int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+
 	/* we must reset ->opt, unknown short option leave it dangling */
 	ctx->opt = NULL;
 
@@ -267,18 +273,18 @@
 
 		if (arg[1] != '-') {
 			ctx->opt = arg + 1;
-			if (*ctx->opt == 'h')
+			if (internal_help && *ctx->opt == 'h')
 				return parse_options_usage(usagestr, options);
 			switch (parse_short_opt(ctx, options)) {
 			case -1:
 				return parse_options_usage(usagestr, options);
 			case -2:
-				return PARSE_OPT_UNKNOWN;
+				goto unknown;
 			}
 			if (ctx->opt)
 				check_typos(arg + 1, options);
 			while (ctx->opt) {
-				if (*ctx->opt == 'h')
+				if (internal_help && *ctx->opt == 'h')
 					return parse_options_usage(usagestr, options);
 				switch (parse_short_opt(ctx, options)) {
 				case -1:
@@ -291,7 +297,7 @@
 					 */
 					ctx->argv[0] = xstrdup(ctx->opt - 1);
 					*(char *)ctx->argv[0] = '-';
-					return PARSE_OPT_UNKNOWN;
+					goto unknown;
 				}
 			}
 			continue;
@@ -305,16 +311,22 @@
 			break;
 		}
 
-		if (!strcmp(arg + 2, "help-all"))
+		if (internal_help && !strcmp(arg + 2, "help-all"))
 			return usage_with_options_internal(usagestr, options, 1);
-		if (!strcmp(arg + 2, "help"))
+		if (internal_help && !strcmp(arg + 2, "help"))
 			return parse_options_usage(usagestr, options);
 		switch (parse_long_opt(ctx, arg + 2, options)) {
 		case -1:
 			return parse_options_usage(usagestr, options);
 		case -2:
-			return PARSE_OPT_UNKNOWN;
+			goto unknown;
 		}
+		continue;
+unknown:
+		if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
+			return PARSE_OPT_UNKNOWN;
+		ctx->out[ctx->cpidx++] = ctx->argv[0];
+		ctx->opt = NULL;
 	}
 	return PARSE_OPT_DONE;
 }
@@ -355,6 +367,9 @@
 int usage_with_options_internal(const char * const *usagestr,
 				const struct option *opts, int full)
 {
+	if (!usagestr)
+		return PARSE_OPT_HELP;
+
 	fprintf(stderr, "usage: %s\n", *usagestr++);
 	while (*usagestr && **usagestr)
 		fprintf(stderr, "   or: %s\n", *usagestr++);
@@ -484,6 +499,44 @@
 	return 0;
 }
 
+int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
+			   int unset)
+{
+	int *target = opt->value;
+
+	if (unset)
+		/* --no-quiet, --no-verbose */
+		*target = 0;
+	else if (opt->short_name == 'v') {
+		if (*target >= 0)
+			(*target)++;
+		else
+			*target = 1;
+	} else {
+		if (*target <= 0)
+			(*target)--;
+		else
+			*target = -1;
+	}
+	return 0;
+}
+
+int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
+{
+	unsigned char sha1[20];
+	struct commit *commit;
+
+	if (!arg)
+		return -1;
+	if (get_sha1(arg, sha1))
+		return error("malformed object name %s", arg);
+	commit = lookup_commit_reference(sha1);
+	if (!commit)
+		return error("no such commit %s", arg);
+	commit_list_insert(commit, opt->value);
+	return 0;
+}
+
 /*
  * This should really be OPTION_FILENAME type as a part of
  * parse_options that take prefix to do this while parsing.
diff --git a/parse-options.h b/parse-options.h
index 5199950..f8ef1db 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -21,6 +21,8 @@
 	PARSE_OPT_KEEP_DASHDASH = 1,
 	PARSE_OPT_STOP_AT_NON_OPTION = 2,
 	PARSE_OPT_KEEP_ARGV0 = 4,
+	PARSE_OPT_KEEP_UNKNOWN = 8,
+	PARSE_OPT_NO_INTERNAL_HELP = 16,
 };
 
 enum parse_opt_option_flags {
@@ -150,9 +152,16 @@
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
+extern int parse_opt_with_commit(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
 #define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
+#define OPT__VERBOSITY(var) \
+	{ OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
+	  PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
+	{ OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
+	  PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
 #define OPT__DRY_RUN(var)  OPT_BOOLEAN('n', "dry-run", (var), "dry run")
 #define OPT__ABBREV(var)  \
 	{ OPTION_CALLBACK, 0, "abbrev", (var), "n", \
diff --git a/patch-id.c b/patch-id.c
index 9349bc5..0df4cb0 100644
--- a/patch-id.c
+++ b/patch-id.c
@@ -1,6 +1,7 @@
 #include "cache.h"
+#include "exec_cmd.h"
 
-static void flush_current_id(int patchlen, unsigned char *id, SHA_CTX *c)
+static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c)
 {
 	unsigned char result[20];
 	char name[50];
@@ -8,10 +9,10 @@
 	if (!patchlen)
 		return;
 
-	SHA1_Final(result, c);
+	git_SHA1_Final(result, c);
 	memcpy(name, sha1_to_hex(id), 41);
 	printf("%s %s\n", sha1_to_hex(result), name);
-	SHA1_Init(c);
+	git_SHA1_Init(c);
 }
 
 static int remove_space(char *line)
@@ -31,10 +32,10 @@
 {
 	static unsigned char sha1[20];
 	static char line[1000];
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	int patchlen = 0;
 
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 	while (fgets(line, sizeof(line), stdin) != NULL) {
 		unsigned char n[20];
 		char *p = line;
@@ -67,18 +68,20 @@
 		/* Compute the sha without whitespace */
 		len = remove_space(line);
 		patchlen += len;
-		SHA1_Update(&ctx, line, len);
+		git_SHA1_Update(&ctx, line, len);
 	}
 	flush_current_id(patchlen, sha1, &ctx);
 }
 
-static const char patch_id_usage[] = "git-patch-id < patch";
+static const char patch_id_usage[] = "git patch-id < patch";
 
 int main(int argc, char **argv)
 {
 	if (argc != 1)
 		usage(patch_id_usage);
 
+	git_extract_argv0_path(argv[0]);
+
 	generate_id_list();
 	return 0;
 }
diff --git a/patch-ids.c b/patch-ids.c
index 3be5d31..5717257 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
+#include "sha1-lookup.h"
 #include "patch-ids.h"
 
 static int commit_patch_id(struct commit *commit, struct diff_options *options,
@@ -15,99 +16,15 @@
 	return diff_flush_patch_id(options, sha1);
 }
 
-static uint32_t take2(const unsigned char *id)
+static const unsigned char *patch_id_access(size_t index, void *table)
 {
-	return ((id[0] << 8) | id[1]);
+	struct patch_id **id_table = table;
+	return id_table[index]->patch_id;
 }
 
-/*
- * Conventional binary search loop looks like this:
- *
- *      do {
- *              int mi = (lo + hi) / 2;
- *              int cmp = "entry pointed at by mi" minus "target";
- *              if (!cmp)
- *                      return (mi is the wanted one)
- *              if (cmp > 0)
- *                      hi = mi; "mi is larger than target"
- *              else
- *                      lo = mi+1; "mi is smaller than target"
- *      } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- *   above the target (it could be at the target), hi points at a
- *   slot that is guaranteed to be above the target (it can never
- *   be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- *   as lo, but never can be the same as hi), and check if it hits
- *   the target.  There are three cases:
- *
- *    - if it is a hit, we are happy.
- *
- *    - if it is strictly higher than the target, we update hi with
- *      it.
- *
- *    - if it is strictly lower than the target, we update lo to be
- *      one slot after it, because we allow lo to be at the target.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied.  When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- */
 static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
 {
-	int hi = nr;
-	int lo = 0;
-	int mi = 0;
-
-	if (!nr)
-		return -1;
-
-	if (nr != 1) {
-		unsigned lov, hiv, miv, ofs;
-
-		for (ofs = 0; ofs < 18; ofs += 2) {
-			lov = take2(table[0]->patch_id + ofs);
-			hiv = take2(table[nr-1]->patch_id + ofs);
-			miv = take2(id + ofs);
-			if (miv < lov)
-				return -1;
-			if (hiv < miv)
-				return -1 - nr;
-			if (lov != hiv) {
-				/*
-				 * At this point miv could be equal
-				 * to hiv (but id could still be higher);
-				 * the invariant of (mi < hi) should be
-				 * kept.
-				 */
-				mi = (nr-1) * (miv - lov) / (hiv - lov);
-				if (lo <= mi && mi < hi)
-					break;
-				die("oops");
-			}
-		}
-		if (18 <= ofs)
-			die("cannot happen -- lo and hi are identical");
-	}
-
-	do {
-		int cmp;
-		cmp = hashcmp(table[mi]->patch_id, id);
-		if (!cmp)
-			return mi;
-		if (cmp > 0)
-			hi = mi;
-		else
-			lo = mi + 1;
-		mi = (hi + lo) / 2;
-	} while (lo < hi);
-	return -lo-1;
+	return sha1_pos(id, table, nr, patch_id_access);
 }
 
 #define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
diff --git a/path.c b/path.c
index a074aea..8a0a674 100644
--- a/path.c
+++ b/path.c
@@ -311,36 +311,49 @@
 	return NULL;
 }
 
-int adjust_shared_perm(const char *path)
+int set_shared_perm(const char *path, int mode)
 {
 	struct stat st;
-	int mode;
+	int tweak, shared, orig_mode;
 
-	if (!shared_repository)
+	if (!shared_repository) {
+		if (mode)
+			return chmod(path, mode & ~S_IFMT);
 		return 0;
-	if (lstat(path, &st) < 0)
-		return -1;
-	mode = st.st_mode;
-
-	if (shared_repository) {
-		int tweak = shared_repository;
-		if (!(mode & S_IWUSR))
-			tweak &= ~0222;
-		mode |= tweak;
-	} else {
-		/* Preserve old PERM_UMASK behaviour */
-		if (mode & S_IWUSR)
-			mode |= S_IWGRP;
 	}
+	if (!mode) {
+		if (lstat(path, &st) < 0)
+			return -1;
+		mode = st.st_mode;
+		orig_mode = mode;
+	} else
+		orig_mode = 0;
+	if (shared_repository < 0)
+		shared = -shared_repository;
+	else
+		shared = shared_repository;
+	tweak = shared;
+
+	if (!(mode & S_IWUSR))
+		tweak &= ~0222;
+	if (mode & S_IXUSR)
+		/* Copy read bits to execute bits */
+		tweak |= (tweak & 0444) >> 2;
+	if (shared_repository < 0)
+		mode = (mode & ~0777) | tweak;
+	else
+		mode |= tweak;
 
 	if (S_ISDIR(mode)) {
-		mode |= FORCE_DIR_SET_GID;
-
 		/* Copy read bits to execute bits */
-		mode |= (shared_repository & 0444) >> 2;
+		mode |= (shared & 0444) >> 2;
+		mode |= FORCE_DIR_SET_GID;
 	}
 
-	if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
+	if (((shared_repository < 0
+	      ? (orig_mode & (FORCE_DIR_SET_GID | 0777))
+	      : (orig_mode & mode)) != mode) &&
+	    chmod(path, (mode & ~S_IFMT)) < 0)
 		return -2;
 	return 0;
 }
@@ -363,56 +376,97 @@
 }
 
 /*
- * path = absolute path
- * buf = buffer of at least max(2, strlen(path)+1) bytes
- * It is okay if buf == path, but they should not overlap otherwise.
+ * It is okay if dst == src, but they should not overlap otherwise.
  *
- * Performs the following normalizations on path, storing the result in buf:
- * - Removes trailing slashes.
- * - Removes empty components.
+ * Performs the following normalizations on src, storing the result in dst:
+ * - Ensures that components are separated by '/' (Windows only)
+ * - Squashes sequences of '/'.
  * - Removes "." components.
  * - Removes ".." components, and the components the precede them.
- * "" and paths that contain only slashes are normalized to "/".
- * Returns the length of the output.
+ * Returns failure (non-zero) if a ".." component appears as first path
+ * component anytime during the normalization. Otherwise, returns success (0).
  *
  * Note that this function is purely textual.  It does not follow symlinks,
  * verify the existence of the path, or make any system calls.
  */
-int normalize_absolute_path(char *buf, const char *path)
+int normalize_path_copy(char *dst, const char *src)
 {
-	const char *comp_start = path, *comp_end = path;
-	char *dst = buf;
-	int comp_len;
-	assert(buf);
-	assert(path);
+	char *dst0;
 
-	while (*comp_start) {
-		assert(*comp_start == '/');
-		while (*++comp_end && *comp_end != '/')
-			; /* nothing */
-		comp_len = comp_end - comp_start;
+	if (has_dos_drive_prefix(src)) {
+		*dst++ = *src++;
+		*dst++ = *src++;
+	}
+	dst0 = dst;
 
-		if (!strncmp("/",  comp_start, comp_len) ||
-		    !strncmp("/.", comp_start, comp_len))
-			goto next;
-
-		if (!strncmp("/..", comp_start, comp_len)) {
-			while (dst > buf && *--dst != '/')
-				; /* nothing */
-			goto next;
-		}
-
-		memmove(dst, comp_start, comp_len);
-		dst += comp_len;
-	next:
-		comp_start = comp_end;
+	if (is_dir_sep(*src)) {
+		*dst++ = '/';
+		while (is_dir_sep(*src))
+			src++;
 	}
 
-	if (dst == buf)
-		*dst++ = '/';
+	for (;;) {
+		char c = *src;
 
+		/*
+		 * A path component that begins with . could be
+		 * special:
+		 * (1) "." and ends   -- ignore and terminate.
+		 * (2) "./"           -- ignore them, eat slash and continue.
+		 * (3) ".." and ends  -- strip one and terminate.
+		 * (4) "../"          -- strip one, eat slash and continue.
+		 */
+		if (c == '.') {
+			if (!src[1]) {
+				/* (1) */
+				src++;
+			} else if (is_dir_sep(src[1])) {
+				/* (2) */
+				src += 2;
+				while (is_dir_sep(*src))
+					src++;
+				continue;
+			} else if (src[1] == '.') {
+				if (!src[2]) {
+					/* (3) */
+					src += 2;
+					goto up_one;
+				} else if (is_dir_sep(src[2])) {
+					/* (4) */
+					src += 3;
+					while (is_dir_sep(*src))
+						src++;
+					goto up_one;
+				}
+			}
+		}
+
+		/* copy up to the next '/', and eat all '/' */
+		while ((c = *src++) != '\0' && !is_dir_sep(c))
+			*dst++ = c;
+		if (is_dir_sep(c)) {
+			*dst++ = '/';
+			while (is_dir_sep(c))
+				c = *src++;
+			src--;
+		} else if (!c)
+			break;
+		continue;
+
+	up_one:
+		/*
+		 * dst0..dst is prefix portion, and dst[-1] is '/';
+		 * go up one level.
+		 */
+		dst--;	/* go to trailing '/' */
+		if (dst <= dst0)
+			return -1;
+		/* Windows: dst[-1] cannot be backslash anymore */
+		while (dst0 < dst && dst[-1] != '/')
+			dst--;
+	}
 	*dst = '\0';
-	return dst - buf;
+	return 0;
 }
 
 /*
@@ -438,15 +492,16 @@
 		return -1;
 
 	for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
-		for (colon = ceil; *colon && *colon != ':'; colon++);
+		for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
 		len = colon - ceil;
 		if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
 			continue;
 		strlcpy(buf, ceil, len+1);
-		len = normalize_absolute_path(buf, buf);
-		/* Strip "trailing slashes" from "/". */
-		if (len == 1)
-			len = 0;
+		if (normalize_path_copy(buf, buf) < 0)
+			continue;
+		len = strlen(buf);
+		if (len > 0 && buf[len-1] == '/')
+			buf[--len] = '\0';
 
 		if (!strncmp(path, buf, len) &&
 		    path[len] == '/' &&
@@ -457,3 +512,39 @@
 
 	return max_len;
 }
+
+/* strip arbitrary amount of directory separators at end of path */
+static inline int chomp_trailing_dir_sep(const char *path, int len)
+{
+	while (len && is_dir_sep(path[len - 1]))
+		len--;
+	return len;
+}
+
+/*
+ * If path ends with suffix (complete path components), returns the
+ * part before suffix (sans trailing directory separators).
+ * Otherwise returns NULL.
+ */
+char *strip_path_suffix(const char *path, const char *suffix)
+{
+	int path_len = strlen(path), suffix_len = strlen(suffix);
+
+	while (suffix_len) {
+		if (!path_len)
+			return NULL;
+
+		if (is_dir_sep(path[path_len - 1])) {
+			if (!is_dir_sep(suffix[suffix_len - 1]))
+				return NULL;
+			path_len = chomp_trailing_dir_sep(path, path_len);
+			suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
+		}
+		else if (path[--path_len] != suffix[--suffix_len])
+			return NULL;
+	}
+
+	if (path_len && !is_dir_sep(path[path_len - 1]))
+		return NULL;
+	return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
+}
diff --git a/perl/Git.pm b/perl/Git.pm
index 29a1783..291ff5b 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -56,7 +56,7 @@
 @EXPORT_OK = qw(command command_oneline command_noisy
                 command_output_pipe command_input_pipe command_close_pipe
                 command_bidi_pipe command_close_bidi_pipe
-                version exec_path hash_object git_cmd_try
+                version exec_path html_path hash_object git_cmd_try
                 remote_refs
                 temp_acquire temp_release temp_reset temp_path);
 
@@ -166,11 +166,12 @@
 		}
 	}
 
-	if (not defined $opts{Repository} and not defined $opts{WorkingCopy}) {
-		$opts{Directory} ||= '.';
+	if (not defined $opts{Repository} and not defined $opts{WorkingCopy}
+		and not defined $opts{Directory}) {
+		$opts{Directory} = '.';
 	}
 
-	if ($opts{Directory}) {
+	if (defined $opts{Directory}) {
 		-d $opts{Directory} or throw Error::Simple("Directory not found: $!");
 
 		my $search = Git->repository(WorkingCopy => $opts{Directory});
@@ -204,14 +205,14 @@
 
 			unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
 				# Mimick git-rev-parse --git-dir error message:
-				throw Error::Simple('fatal: Not a git repository');
+				throw Error::Simple("fatal: Not a git repository: $dir");
 			}
 			my $search = Git->repository(Repository => $dir);
 			try {
 				$search->command('symbolic-ref', 'HEAD');
 			} catch Git::Error::Command with {
 				# Mimick git-rev-parse --git-dir error message:
-				throw Error::Simple('fatal: Not a git repository');
+				throw Error::Simple("fatal: Not a git repository: $dir");
 			}
 
 			$opts{Repository} = abs_path($dir);
@@ -491,6 +492,16 @@
 sub exec_path { command_oneline('--exec-path') }
 
 
+=item html_path ()
+
+Return path to the Git html documentation (the same as
+C<git --html-path>). Useful mostly only internally.
+
+=cut
+
+sub html_path { command_oneline('--html-path') }
+
+
 =item repo_path ()
 
 Return path to the git repository. Must be called on a repository instance.
@@ -961,9 +972,7 @@
 =cut
 
 sub temp_acquire {
-	my ($self, $name) = _maybe_self(@_);
-
-	my $temp_fd = _temp_cache($name);
+	my $temp_fd = _temp_cache(@_);
 
 	$TEMP_FILES{$temp_fd}{locked} = 1;
 	$temp_fd;
@@ -1005,7 +1014,7 @@
 }
 
 sub _temp_cache {
-	my ($name) = @_;
+	my ($self, $name) = _maybe_self(@_);
 
 	_verify_require();
 
@@ -1022,9 +1031,16 @@
 				"' was closed. Opening replacement.";
 		}
 		my $fname;
+
+		my $tmpdir;
+		if (defined $self) {
+			$tmpdir = $self->repo_path();
+		}
+
 		($$temp_fd, $fname) = File::Temp->tempfile(
-			'Git_XXXXXX', UNLINK => 1
+			'Git_XXXXXX', UNLINK => 1, DIR => $tmpdir,
 			) or throw Error::Simple("couldn't open new temp file");
+
 		$$temp_fd->autoflush;
 		binmode $$temp_fd;
 		$TEMP_FILES{$$temp_fd}{fname} = $fname;
diff --git a/ppc/sha1.c b/ppc/sha1.c
index 738e36c..ec6a192 100644
--- a/ppc/sha1.c
+++ b/ppc/sha1.c
@@ -10,10 +10,10 @@
 #include <string.h>
 #include "sha1.h"
 
-extern void sha1_core(uint32_t *hash, const unsigned char *p,
-		      unsigned int nblocks);
+extern void ppc_sha1_core(uint32_t *hash, const unsigned char *p,
+			  unsigned int nblocks);
 
-int SHA1_Init(SHA_CTX *c)
+int ppc_SHA1_Init(ppc_SHA_CTX *c)
 {
 	c->hash[0] = 0x67452301;
 	c->hash[1] = 0xEFCDAB89;
@@ -25,7 +25,7 @@
 	return 0;
 }
 
-int SHA1_Update(SHA_CTX *c, const void *ptr, unsigned long n)
+int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *ptr, unsigned long n)
 {
 	unsigned long nb;
 	const unsigned char *p = ptr;
@@ -38,12 +38,12 @@
 				nb = n;
 			memcpy(&c->buf.b[c->cnt], p, nb);
 			if ((c->cnt += nb) == 64) {
-				sha1_core(c->hash, c->buf.b, 1);
+				ppc_sha1_core(c->hash, c->buf.b, 1);
 				c->cnt = 0;
 			}
 		} else {
 			nb = n >> 6;
-			sha1_core(c->hash, p, nb);
+			ppc_sha1_core(c->hash, p, nb);
 			nb <<= 6;
 		}
 		n -= nb;
@@ -52,7 +52,7 @@
 	return 0;
 }
 
-int SHA1_Final(unsigned char *hash, SHA_CTX *c)
+int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c)
 {
 	unsigned int cnt = c->cnt;
 
@@ -60,13 +60,13 @@
 	if (cnt > 56) {
 		if (cnt < 64)
 			memset(&c->buf.b[cnt], 0, 64 - cnt);
-		sha1_core(c->hash, c->buf.b, 1);
+		ppc_sha1_core(c->hash, c->buf.b, 1);
 		cnt = 0;
 	}
 	if (cnt < 56)
 		memset(&c->buf.b[cnt], 0, 56 - cnt);
 	c->buf.l[7] = c->len;
-	sha1_core(c->hash, c->buf.b, 1);
+	ppc_sha1_core(c->hash, c->buf.b, 1);
 	memcpy(hash, c->hash, 20);
 	return 0;
 }
diff --git a/ppc/sha1.h b/ppc/sha1.h
index c3c51aa..c405f73 100644
--- a/ppc/sha1.h
+++ b/ppc/sha1.h
@@ -5,7 +5,7 @@
  */
 #include <stdint.h>
 
-typedef struct sha_context {
+typedef struct {
 	uint32_t hash[5];
 	uint32_t cnt;
 	uint64_t len;
@@ -13,8 +13,13 @@
 		unsigned char b[64];
 		uint64_t l[8];
 	} buf;
-} SHA_CTX;
+} ppc_SHA_CTX;
 
-int SHA1_Init(SHA_CTX *c);
-int SHA1_Update(SHA_CTX *c, const void *p, unsigned long n);
-int SHA1_Final(unsigned char *hash, SHA_CTX *c);
+int ppc_SHA1_Init(ppc_SHA_CTX *c);
+int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *p, unsigned long n);
+int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c);
+
+#define git_SHA_CTX	ppc_SHA_CTX
+#define git_SHA1_Init	ppc_SHA1_Init
+#define git_SHA1_Update	ppc_SHA1_Update
+#define git_SHA1_Final	ppc_SHA1_Final
diff --git a/ppc/sha1ppc.S b/ppc/sha1ppc.S
index f132696..1711eef 100644
--- a/ppc/sha1ppc.S
+++ b/ppc/sha1ppc.S
@@ -162,8 +162,8 @@
 	STEPUP4(fn, (t)+12, (s)+12,);	\
 	STEPUP4(fn, (t)+16, (s)+16, loadk)
 
-	.globl	sha1_core
-sha1_core:
+	.globl	ppc_sha1_core
+ppc_sha1_core:
 	stwu	%r1,-80(%r1)
 	stmw	%r13,4(%r1)
 
diff --git a/preload-index.c b/preload-index.c
new file mode 100644
index 0000000..88edc5f
--- /dev/null
+++ b/preload-index.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 Linus Torvalds
+ */
+#include "cache.h"
+
+#ifdef NO_PTHREADS
+static void preload_index(struct index_state *index, const char **pathspec)
+{
+	; /* nothing */
+}
+#else
+
+#include <pthread.h>
+
+/*
+ * Mostly randomly chosen maximum thread counts: we
+ * cap the parallelism to 20 threads, and we want
+ * to have at least 500 lstat's per thread for it to
+ * be worth starting a thread.
+ */
+#define MAX_PARALLEL (20)
+#define THREAD_COST (500)
+
+struct thread_data {
+	pthread_t pthread;
+	struct index_state *index;
+	const char **pathspec;
+	int offset, nr;
+};
+
+static void *preload_thread(void *_data)
+{
+	int nr;
+	struct thread_data *p = _data;
+	struct index_state *index = p->index;
+	struct cache_entry **cep = index->cache + p->offset;
+
+	nr = p->nr;
+	if (nr + p->offset > index->cache_nr)
+		nr = index->cache_nr - p->offset;
+
+	do {
+		struct cache_entry *ce = *cep++;
+		struct stat st;
+
+		if (ce_stage(ce))
+			continue;
+		if (ce_uptodate(ce))
+			continue;
+		if (!ce_path_match(ce, p->pathspec))
+			continue;
+		if (lstat(ce->name, &st))
+			continue;
+		if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
+			continue;
+		ce_mark_uptodate(ce);
+	} while (--nr > 0);
+	return NULL;
+}
+
+static void preload_index(struct index_state *index, const char **pathspec)
+{
+	int threads, i, work, offset;
+	struct thread_data data[MAX_PARALLEL];
+
+	if (!core_preload_index)
+		return;
+
+	threads = index->cache_nr / THREAD_COST;
+	if (threads < 2)
+		return;
+	if (threads > MAX_PARALLEL)
+		threads = MAX_PARALLEL;
+	offset = 0;
+	work = (index->cache_nr + threads - 1) / threads;
+	for (i = 0; i < threads; i++) {
+		struct thread_data *p = data+i;
+		p->index = index;
+		p->pathspec = pathspec;
+		p->offset = offset;
+		p->nr = work;
+		offset += work;
+		if (pthread_create(&p->pthread, NULL, preload_thread, p))
+			die("unable to create threaded lstat");
+	}
+	for (i = 0; i < threads; i++) {
+		struct thread_data *p = data+i;
+		if (pthread_join(p->pthread, NULL))
+			die("unable to join threaded lstat");
+	}
+}
+#endif
+
+int read_index_preload(struct index_state *index, const char **pathspec)
+{
+	int retval = read_index(index);
+
+	preload_index(index, pathspec);
+	return retval;
+}
diff --git a/pretty.c b/pretty.c
index a29c290..a0ef356 100644
--- a/pretty.c
+++ b/pretty.c
@@ -5,9 +5,20 @@
 #include "revision.h"
 #include "string-list.h"
 #include "mailmap.h"
+#include "log-tree.h"
+#include "color.h"
 
 static char *user_format;
 
+static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
+{
+	free(user_format);
+	user_format = xstrdup(cp);
+	if (is_tformat)
+		rev->use_terminator = 1;
+	rev->commit_format = CMIT_FMT_USERFORMAT;
+}
+
 void get_commit_format(const char *arg, struct rev_info *rev)
 {
 	int i;
@@ -31,12 +42,7 @@
 		return;
 	}
 	if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
-		const char *cp = strchr(arg, ':') + 1;
-		free(user_format);
-		user_format = xstrdup(cp);
-		if (arg[0] == 't')
-			rev->use_terminator = 1;
-		rev->commit_format = CMIT_FMT_USERFORMAT;
+		save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
 		return;
 	}
 	for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
@@ -48,6 +54,10 @@
 			return;
 		}
 	}
+	if (strchr(arg, '%')) {
+		save_user_format(rev, arg, 1);
+		return;
+	}
 
 	die("invalid --pretty format: %s", arg);
 }
@@ -73,8 +83,7 @@
 /* High bit set, or ISO-2022-INT */
 int non_ascii(int ch)
 {
-	ch = (ch & 0xff);
-	return ((ch & 0x80) || (ch == 0x1b));
+	return !isascii(ch) || ch == '\033';
 }
 
 static int is_rfc2047_special(char ch)
@@ -126,7 +135,6 @@
 	int namelen;
 	unsigned long time;
 	int tz;
-	const char *filler = "    ";
 
 	if (fmt == CMIT_FMT_ONELINE)
 		return;
@@ -145,7 +153,6 @@
 		while (line < name_tail && isspace(name_tail[-1]))
 			name_tail--;
 		display_name_length = name_tail - line;
-		filler = "";
 		strbuf_addstr(sb, "From: ");
 		add_rfc2047(sb, line, display_name_length, encoding);
 		strbuf_add(sb, name_tail, namelen - display_name_length);
@@ -153,7 +160,7 @@
 	} else {
 		strbuf_addf(sb, "%s: %.*s%.*s\n", what,
 			      (fmt == CMIT_FMT_FULLER) ? 4 : 0,
-			      filler, namelen, line);
+			      "    ", namelen, line);
 	}
 	switch (fmt) {
 	case CMIT_FMT_MEDIUM:
@@ -180,6 +187,20 @@
 	return !len;
 }
 
+static const char *skip_empty_lines(const char *msg)
+{
+	for (;;) {
+		int linelen = get_one_line(msg);
+		int ll = linelen;
+		if (!linelen)
+			break;
+		if (!is_empty_line(msg, &ll))
+			break;
+		msg += linelen;
+	}
+	return msg;
+}
+
 static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
 			const struct commit *commit, int abbrev)
 {
@@ -194,15 +215,13 @@
 	while (parent) {
 		struct commit *p = parent->item;
 		const char *hex = NULL;
-		const char *dots;
 		if (abbrev)
 			hex = find_unique_abbrev(p->object.sha1, abbrev);
 		if (!hex)
 			hex = sha1_to_hex(p->object.sha1);
-		dots = (abbrev && strlen(hex) != 40) ?  "..." : "";
 		parent = parent->next;
 
-		strbuf_addf(sb, " %s%s", hex, dots);
+		strbuf_addf(sb, " %s", hex);
 	}
 	strbuf_addch(sb, '\n');
 }
@@ -233,7 +252,7 @@
 
 static char *replace_encoding_header(char *buf, const char *encoding)
 {
-	struct strbuf tmp;
+	struct strbuf tmp = STRBUF_INIT;
 	size_t start, len;
 	char *cp = buf;
 
@@ -249,7 +268,6 @@
 		return buf; /* should not happen but be defensive */
 	len = cp + 1 - (buf + start);
 
-	strbuf_init(&tmp, 0);
 	strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
 	if (is_encoding_utf8(encoding)) {
 		/* we have re-coded to UTF-8; drop the header */
@@ -290,23 +308,14 @@
 	return out;
 }
 
-static int mailmap_name(struct strbuf *sb, const char *email)
+static int mailmap_name(char *email, int email_len, char *name, int name_len)
 {
 	static struct string_list *mail_map;
-	char buffer[1024];
-
 	if (!mail_map) {
 		mail_map = xcalloc(1, sizeof(*mail_map));
-		read_mailmap(mail_map, ".mailmap", NULL);
+		read_mailmap(mail_map, NULL);
 	}
-
-	if (!mail_map->nr)
-		return -1;
-
-	if (!map_email(mail_map, email, buffer, sizeof(buffer)))
-		return -1;
-	strbuf_addstr(sb, buffer);
-	return 0;
+	return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
 }
 
 static size_t format_person_part(struct strbuf *sb, char part,
@@ -317,6 +326,9 @@
 	int start, end, tz = 0;
 	unsigned long date = 0;
 	char *ep;
+	const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
+	char person_name[1024];
+	char person_mail[1024];
 
 	/* advance 'end' to point to email start delimiter */
 	for (end = 0; end < len && msg[end] != '<'; end++)
@@ -330,25 +342,34 @@
 	if (end >= len - 2)
 		goto skip;
 
+	/* Seek for both name and email part */
+	name_start = msg;
+	name_end = msg+end;
+	while (name_end > name_start && isspace(*(name_end-1)))
+		name_end--;
+	mail_start = msg+end+1;
+	mail_end = mail_start;
+	while (mail_end < msg_end && *mail_end != '>')
+		mail_end++;
+	if (mail_end == msg_end)
+		goto skip;
+	end = mail_end-msg;
+
+	if (part == 'N' || part == 'E') { /* mailmap lookup */
+		strlcpy(person_name, name_start, name_end-name_start+1);
+		strlcpy(person_mail, mail_start, mail_end-mail_start+1);
+		mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
+		name_start = person_name;
+		name_end = name_start + strlen(person_name);
+		mail_start = person_mail;
+		mail_end = mail_start +  strlen(person_mail);
+	}
 	if (part == 'n' || part == 'N') {	/* name */
-		while (end > 0 && isspace(msg[end - 1]))
-			end--;
-		if (part != 'N' || !msg[end] || !msg[end + 1] ||
-		    mailmap_name(sb, msg + end + 2) < 0)
-			strbuf_add(sb, msg, end);
+		strbuf_add(sb, name_start, name_end-name_start);
 		return placeholder_len;
 	}
-	start = ++end; /* save email start position */
-
-	/* advance 'end' to point to email end delimiter */
-	for ( ; end < len && msg[end] != '>'; end++)
-		; /* do nothing */
-
-	if (end >= len)
-		goto skip;
-
-	if (part == 'e') {	/* email */
-		strbuf_add(sb, msg + start, end - start);
+	if (part == 'e' || part == 'E') {	/* email */
+		strbuf_add(sb, mail_start, mail_end-mail_start);
 		return placeholder_len;
 	}
 
@@ -410,13 +431,15 @@
 struct format_commit_context {
 	const struct commit *commit;
 	enum date_mode dmode;
+	unsigned commit_header_parsed:1;
+	unsigned commit_message_parsed:1;
 
 	/* These offsets are relative to the start of the commit message. */
-	int commit_header_parsed;
-	struct chunk subject;
 	struct chunk author;
 	struct chunk committer;
 	struct chunk encoding;
+	size_t message_off;
+	size_t subject_off;
 	size_t body_off;
 
 	/* The following ones are relative to the result struct strbuf. */
@@ -446,23 +469,14 @@
 {
 	const char *msg = context->commit->buffer;
 	int i;
-	enum { HEADER, SUBJECT, BODY } state;
 
-	for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
+	for (i = 0; msg[i]; i++) {
 		int eol;
 		for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
 			; /* do nothing */
 
-		if (state == SUBJECT) {
-			context->subject.off = i;
-			context->subject.len = eol - i;
-			i = eol;
-		}
 		if (i == eol) {
-			state++;
-			/* strip empty lines */
-			while (msg[eol] == '\n' && msg[eol + 1] == '\n')
-				eol++;
+			break;
 		} else if (!prefixcmp(msg + i, "author ")) {
 			context->author.off = i + 7;
 			context->author.len = eol - i - 7;
@@ -474,13 +488,101 @@
 			context->encoding.len = eol - i - 9;
 		}
 		i = eol;
-		if (!msg[i])
-			break;
 	}
-	context->body_off = i;
+	context->message_off = i;
 	context->commit_header_parsed = 1;
 }
 
+static int istitlechar(char c)
+{
+	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+		(c >= '0' && c <= '9') || c == '.' || c == '_';
+}
+
+static void format_sanitized_subject(struct strbuf *sb, const char *msg)
+{
+	size_t trimlen;
+	size_t start_len = sb->len;
+	int space = 2;
+
+	for (; *msg && *msg != '\n'; msg++) {
+		if (istitlechar(*msg)) {
+			if (space == 1)
+				strbuf_addch(sb, '-');
+			space = 0;
+			strbuf_addch(sb, *msg);
+			if (*msg == '.')
+				while (*(msg+1) == '.')
+					msg++;
+		} else
+			space |= 1;
+	}
+
+	/* trim any trailing '.' or '-' characters */
+	trimlen = 0;
+	while (sb->len - trimlen > start_len &&
+		(sb->buf[sb->len - 1 - trimlen] == '.'
+		|| sb->buf[sb->len - 1 - trimlen] == '-'))
+		trimlen++;
+	strbuf_remove(sb, sb->len - trimlen, trimlen);
+}
+
+const char *format_subject(struct strbuf *sb, const char *msg,
+			   const char *line_separator)
+{
+	int first = 1;
+
+	for (;;) {
+		const char *line = msg;
+		int linelen = get_one_line(line);
+
+		msg += linelen;
+		if (!linelen || is_empty_line(line, &linelen))
+			break;
+
+		if (!sb)
+			continue;
+		strbuf_grow(sb, linelen + 2);
+		if (!first)
+			strbuf_addstr(sb, line_separator);
+		strbuf_add(sb, line, linelen);
+		first = 0;
+	}
+	return msg;
+}
+
+static void parse_commit_message(struct format_commit_context *c)
+{
+	const char *msg = c->commit->buffer + c->message_off;
+	const char *start = c->commit->buffer;
+
+	msg = skip_empty_lines(msg);
+	c->subject_off = msg - start;
+
+	msg = format_subject(NULL, msg, NULL);
+	msg = skip_empty_lines(msg);
+	c->body_off = msg - start;
+
+	c->commit_message_parsed = 1;
+}
+
+static void format_decoration(struct strbuf *sb, const struct commit *commit)
+{
+	struct name_decoration *d;
+	const char *prefix = " (";
+
+	load_ref_decorations();
+	d = lookup_decoration(&name_decoration, &commit->object);
+	while (d) {
+		strbuf_addstr(sb, prefix);
+		prefix = ", ";
+		strbuf_addstr(sb, d->name);
+		d = d->next;
+	}
+	if (prefix[0] == ',')
+		strbuf_addch(sb, ')');
+}
+
 static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
@@ -493,17 +595,28 @@
 	/* these are independent of the commit */
 	switch (placeholder[0]) {
 	case 'C':
+		if (placeholder[1] == '(') {
+			const char *end = strchr(placeholder + 2, ')');
+			char color[COLOR_MAXLEN];
+			if (!end)
+				return 0;
+			color_parse_mem(placeholder + 2,
+					end - (placeholder + 2),
+					"--pretty format", color);
+			strbuf_addstr(sb, color);
+			return end - placeholder + 1;
+		}
 		if (!prefixcmp(placeholder + 1, "red")) {
-			strbuf_addstr(sb, "\033[31m");
+			strbuf_addstr(sb, GIT_COLOR_RED);
 			return 4;
 		} else if (!prefixcmp(placeholder + 1, "green")) {
-			strbuf_addstr(sb, "\033[32m");
+			strbuf_addstr(sb, GIT_COLOR_GREEN);
 			return 6;
 		} else if (!prefixcmp(placeholder + 1, "blue")) {
-			strbuf_addstr(sb, "\033[34m");
+			strbuf_addstr(sb, GIT_COLOR_BLUE);
 			return 5;
 		} else if (!prefixcmp(placeholder + 1, "reset")) {
-			strbuf_addstr(sb, "\033[m");
+			strbuf_addstr(sb, GIT_COLOR_RESET);
 			return 6;
 		} else
 			return 0;
@@ -573,6 +686,9 @@
 		                 ? '<'
 		                 : '>');
 		return 1;
+	case 'd':
+		format_decoration(sb, commit);
+		return 1;
 	}
 
 	/* For the rest we have to parse the commit header. */
@@ -580,9 +696,6 @@
 		parse_commit_header(c);
 
 	switch (placeholder[0]) {
-	case 's':	/* subject */
-		strbuf_add(sb, msg + c->subject.off, c->subject.len);
-		return 1;
 	case 'a':	/* author ... */
 		return format_person_part(sb, placeholder[1],
 				   msg + c->author.off, c->author.len,
@@ -594,6 +707,19 @@
 	case 'e':	/* encoding */
 		strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
 		return 1;
+	}
+
+	/* Now we need to parse the commit message. */
+	if (!c->commit_message_parsed)
+		parse_commit_message(c);
+
+	switch (placeholder[0]) {
+	case 's':	/* subject */
+		format_subject(sb, msg + c->subject_off, " ");
+		return 1;
+	case 'f':	/* sanitized subject */
+		format_sanitized_subject(sb, msg + c->subject_off);
+		return 1;
 	case 'b':	/* body */
 		strbuf_addstr(sb, msg + c->body_off);
 		return 1;
@@ -684,27 +810,11 @@
 		   const char *encoding,
 		   int need_8bit_cte)
 {
+	const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " ";
 	struct strbuf title;
 
 	strbuf_init(&title, 80);
-
-	for (;;) {
-		const char *line = *msg_p;
-		int linelen = get_one_line(line);
-
-		*msg_p += linelen;
-		if (!linelen || is_empty_line(line, &linelen))
-			break;
-
-		strbuf_grow(&title, linelen + 2);
-		if (title.len) {
-			if (fmt == CMIT_FMT_EMAIL) {
-				strbuf_addch(&title, '\n');
-			}
-			strbuf_addch(&title, ' ');
-		}
-		strbuf_add(&title, line, linelen);
-	}
+	*msg_p = format_subject(&title, *msg_p, line_separator);
 
 	strbuf_grow(sb, title.len + 1024);
 	if (subject) {
@@ -763,6 +873,20 @@
 	}
 }
 
+char *reencode_commit_message(const struct commit *commit, const char **encoding_p)
+{
+	const char *encoding;
+
+	encoding = (git_log_output_encoding
+		    ? git_log_output_encoding
+		    : git_commit_encoding);
+	if (!encoding)
+		encoding = "utf-8";
+	if (encoding_p)
+		*encoding_p = encoding;
+	return logmsg_reencode(commit, encoding);
+}
+
 void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
 			 struct strbuf *sb, int abbrev,
 			 const char *subject, const char *after_subject,
@@ -779,12 +903,7 @@
 		return;
 	}
 
-	encoding = (git_log_output_encoding
-		    ? git_log_output_encoding
-		    : git_commit_encoding);
-	if (!encoding)
-		encoding = "utf-8";
-	reencoded = logmsg_reencode(commit, encoding);
+	reencoded = reencode_commit_message(commit, &encoding);
 	if (reencoded) {
 		msg = reencoded;
 	}
@@ -821,15 +940,7 @@
 	}
 
 	/* Skip excess blank lines at the beginning of body, if any... */
-	for (;;) {
-		int linelen = get_one_line(msg);
-		int ll = linelen;
-		if (!linelen)
-			break;
-		if (!is_empty_line(msg, &ll))
-			break;
-		msg += linelen;
-	}
+	msg = skip_empty_lines(msg);
 
 	/* These formats treat the title line specially. */
 	if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
diff --git a/quote.c b/quote.c
index 6a52085..7a49fcf 100644
--- a/quote.c
+++ b/quote.c
@@ -72,7 +72,7 @@
 	}
 }
 
-char *sq_dequote(char *arg)
+char *sq_dequote_step(char *arg, char **next)
 {
 	char *dst = arg;
 	char *src = arg;
@@ -92,6 +92,8 @@
 		switch (*++src) {
 		case '\0':
 			*dst = 0;
+			if (next)
+				*next = NULL;
 			return arg;
 		case '\\':
 			c = *++src;
@@ -101,11 +103,40 @@
 			}
 		/* Fallthrough */
 		default:
-			return NULL;
+			if (!next || !isspace(*src))
+				return NULL;
+			do {
+				c = *++src;
+			} while (isspace(c));
+			*dst = 0;
+			*next = src;
+			return arg;
 		}
 	}
 }
 
+char *sq_dequote(char *arg)
+{
+	return sq_dequote_step(arg, NULL);
+}
+
+int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
+{
+	char *next = arg;
+
+	if (!*arg)
+		return 0;
+	do {
+		char *dequoted = sq_dequote_step(next, &next);
+		if (!dequoted)
+			return -1;
+		ALLOC_GROW(*argv, *nr + 1, *alloc);
+		(*argv)[(*nr)++] = dequoted;
+	} while (next);
+
+	return 0;
+}
+
 /* 1 means: quote as octal
  * 0 means: quote as octal if (quote_path_fully)
  * -1 means: never quote
diff --git a/quote.h b/quote.h
index c5eea6f..66730f2 100644
--- a/quote.h
+++ b/quote.h
@@ -39,6 +39,15 @@
  */
 extern char *sq_dequote(char *);
 
+/*
+ * Same as the above, but can be used to unwrap many arguments in the
+ * same string separated by space. "next" is changed to point to the
+ * next argument that should be passed as first parameter. When there
+ * is no more argument to be dequoted, "next" is updated to point to NULL.
+ */
+extern char *sq_dequote_step(char *arg, char **next);
+extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
+
 extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
 extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
 extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
diff --git a/read-cache.c b/read-cache.c
index 525d138..3f58711 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -8,6 +8,12 @@
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
+#include "tree.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "blob.h"
 
 /* Index extensions.
  *
@@ -61,8 +67,10 @@
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-	ce->ce_ctime = st->st_ctime;
-	ce->ce_mtime = st->st_mtime;
+	ce->ce_ctime.sec = (unsigned int)st->st_ctime;
+	ce->ce_mtime.sec = (unsigned int)st->st_mtime;
+	ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
+	ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
 	ce->ce_dev = st->st_dev;
 	ce->ce_ino = st->st_ino;
 	ce->ce_uid = st->st_uid;
@@ -93,27 +101,21 @@
 static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
 {
 	int match = -1;
-	char *target;
 	void *buffer;
 	unsigned long size;
 	enum object_type type;
-	int len;
+	struct strbuf sb = STRBUF_INIT;
 
-	target = xmalloc(expected_size);
-	len = readlink(ce->name, target, expected_size);
-	if (len != expected_size) {
-		free(target);
+	if (strbuf_readlink(&sb, ce->name, expected_size))
 		return -1;
-	}
+
 	buffer = read_sha1_file(ce->sha1, &type, &size);
-	if (!buffer) {
-		free(target);
-		return -1;
+	if (buffer) {
+		if (size == sb.len)
+			match = memcmp(buffer, sb.buf, size);
+		free(buffer);
 	}
-	if (size == expected_size)
-		match = memcmp(buffer, target, size);
-	free(buffer);
-	free(target);
+	strbuf_release(&sb);
 	return match;
 }
 
@@ -154,7 +156,7 @@
 	return 0;
 }
 
-static int is_empty_blob_sha1(const unsigned char *sha1)
+int is_empty_blob_sha1(const unsigned char *sha1)
 {
 	static const unsigned char empty_blob_sha1[20] = {
 		0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
@@ -196,11 +198,18 @@
 	default:
 		die("internal error: ce_mode is %o", ce->ce_mode);
 	}
-	if (ce->ce_mtime != (unsigned int) st->st_mtime)
+	if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
 		changed |= MTIME_CHANGED;
-	if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime)
+	if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime)
 		changed |= CTIME_CHANGED;
 
+#ifdef USE_NSEC
+	if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
+		changed |= MTIME_CHANGED;
+	if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
+		changed |= CTIME_CHANGED;
+#endif
+
 	if (ce->ce_uid != (unsigned int) st->st_uid ||
 	    ce->ce_gid != (unsigned int) st->st_gid)
 		changed |= OWNER_CHANGED;
@@ -232,8 +241,16 @@
 static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
 {
 	return (!S_ISGITLINK(ce->ce_mode) &&
-		istate->timestamp &&
-		((unsigned int)istate->timestamp) <= ce->ce_mtime);
+		istate->timestamp.sec &&
+#ifdef USE_NSEC
+		 /* nanosecond timestamped files can also be racy! */
+		(istate->timestamp.sec < ce->ce_mtime.sec ||
+		 (istate->timestamp.sec == ce->ce_mtime.sec &&
+		  istate->timestamp.nsec <= ce->ce_mtime.nsec))
+#else
+		istate->timestamp.sec <= ce->ce_mtime.sec
+#endif
+		 );
 }
 
 int ie_match_stat(const struct index_state *istate,
@@ -251,6 +268,14 @@
 	if (!ignore_valid && (ce->ce_flags & CE_VALID))
 		return 0;
 
+	/*
+	 * Intent-to-add entries have not been added, so the index entry
+	 * by definition never matches what is in the work tree until it
+	 * actually gets added.
+	 */
+	if (ce->ce_flags & CE_INTENT_TO_ADD)
+		return DATA_CHANGED | TYPE_CHANGED | MODE_CHANGED;
+
 	changed = ce_match_stat_basic(ce, st);
 
 	/*
@@ -435,6 +460,26 @@
 	return 1;
 }
 
+/*
+ * Remove all cache ententries marked for removal, that is where
+ * CE_REMOVE is set in ce_flags.  This is much more effective than
+ * calling remove_index_entry_at() for each entry to be removed.
+ */
+void remove_marked_cache_entries(struct index_state *istate)
+{
+	struct cache_entry **ce_array = istate->cache;
+	unsigned int i, j;
+
+	for (i = j = 0; i < istate->cache_nr; i++) {
+		if (ce_array[i]->ce_flags & CE_REMOVE)
+			remove_name_hash(ce_array[i]);
+		else
+			ce_array[j++] = ce_array[i];
+	}
+	istate->cache_changed = 1;
+	istate->cache_nr = j;
+}
+
 int remove_file_from_index(struct index_state *istate, const char *path)
 {
 	int pos = index_name_pos(istate, path, strlen(path));
@@ -506,6 +551,14 @@
 	return new;
 }
 
+static void record_intent_to_add(struct cache_entry *ce)
+{
+	unsigned char sha1[20];
+	if (write_sha1_file("", 0, blob_type, sha1))
+		die("cannot create an empty blob in the object database");
+	hashcpy(ce->sha1, sha1);
+}
+
 int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
 {
 	int size, namelen, was_same;
@@ -514,6 +567,9 @@
 	unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
 	int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
 	int pretend = flags & ADD_CACHE_PRETEND;
+	int intent_only = flags & ADD_CACHE_INTENT;
+	int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
+			  (intent_only ? ADD_CACHE_NEW_ONLY : 0));
 
 	if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
 		return error("%s: can only add regular files, symbolic links or git-directories", path);
@@ -527,7 +583,10 @@
 	ce = xcalloc(1, size);
 	memcpy(ce->name, path, namelen);
 	ce->ce_flags = namelen;
-	fill_stat_cache_info(ce, st);
+	if (!intent_only)
+		fill_stat_cache_info(ce, st);
+	else
+		ce->ce_flags |= CE_INTENT_TO_ADD;
 
 	if (trust_executable_bit && has_symlinks)
 		ce->ce_mode = create_ce_mode(st_mode);
@@ -550,8 +609,12 @@
 		alias->ce_flags |= CE_ADDED;
 		return 0;
 	}
-	if (index_path(ce->sha1, path, st, 1))
-		return error("unable to index file %s", path);
+	if (!intent_only) {
+		if (index_path(ce->sha1, path, st, 1))
+			return error("unable to index file %s", path);
+	} else
+		record_intent_to_add(ce);
+
 	if (ignore_case && alias && different_name(ce, alias))
 		ce = create_alias_ce(ce, alias);
 	ce->ce_flags |= CE_ADDED;
@@ -564,7 +627,7 @@
 
 	if (pretend)
 		;
-	else if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
+	else if (add_index_entry(istate, ce, add_option))
 		return error("unable to add %s to index",path);
 	if (verbose && !was_same)
 		printf("add '%s'\n", path);
@@ -586,8 +649,10 @@
 	int size, len;
 	struct cache_entry *ce;
 
-	if (!verify_path(path))
+	if (!verify_path(path)) {
+		error("Invalid path '%s'", path);
 		return NULL;
+	}
 
 	len = strlen(path);
 	size = cache_entry_size(len);
@@ -843,13 +908,15 @@
 	int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
 	int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
 	int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
+	int new_only = option & ADD_CACHE_NEW_ONLY;
 
 	cache_tree_invalidate_path(istate->cache_tree, ce->name);
 	pos = index_name_pos(istate, ce->name, ce->ce_flags);
 
 	/* existing match? Just replace it. */
 	if (pos >= 0) {
-		replace_index_entry(istate, pos, ce);
+		if (!new_only)
+			replace_index_entry(istate, pos, ce);
 		return 0;
 	}
 	pos = -pos-1;
@@ -869,7 +936,7 @@
 	if (!ok_to_add)
 		return -1;
 	if (!verify_path(ce->name))
-		return -1;
+		return error("Invalid path '%s'", ce->name);
 
 	if (!skip_df_check &&
 	    check_file_directory_conflict(istate, ce, pos, ok_to_replace)) {
@@ -1067,16 +1134,16 @@
 
 static int verify_hdr(struct cache_header *hdr, unsigned long size)
 {
-	SHA_CTX c;
+	git_SHA_CTX c;
 	unsigned char sha1[20];
 
 	if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
 		return error("bad signature");
-	if (hdr->hdr_version != htonl(2))
+	if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
 		return error("bad index version");
-	SHA1_Init(&c);
-	SHA1_Update(&c, hdr, size - 20);
-	SHA1_Final(sha1, &c);
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, hdr, size - 20);
+	git_SHA1_Final(sha1, &c);
 	if (hashcmp(sha1, (unsigned char *)hdr + size - 20))
 		return error("bad index file sha1 signature");
 	return 0;
@@ -1107,9 +1174,12 @@
 static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
 {
 	size_t len;
+	const char *name;
 
-	ce->ce_ctime = ntohl(ondisk->ctime.sec);
-	ce->ce_mtime = ntohl(ondisk->mtime.sec);
+	ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
+	ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
+	ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
+	ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
 	ce->ce_dev   = ntohl(ondisk->dev);
 	ce->ce_ino   = ntohl(ondisk->ino);
 	ce->ce_mode  = ntohl(ondisk->mode);
@@ -1118,16 +1188,32 @@
 	ce->ce_size  = ntohl(ondisk->size);
 	/* On-disk flags are just 16 bits */
 	ce->ce_flags = ntohs(ondisk->flags);
+
 	hashcpy(ce->sha1, ondisk->sha1);
 
 	len = ce->ce_flags & CE_NAMEMASK;
+
+	if (ce->ce_flags & CE_EXTENDED) {
+		struct ondisk_cache_entry_extended *ondisk2;
+		int extended_flags;
+		ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
+		extended_flags = ntohs(ondisk2->flags2) << 16;
+		/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
+		if (extended_flags & ~CE_EXTENDED_FLAGS)
+			die("Unknown index entry format %08x", extended_flags);
+		ce->ce_flags |= extended_flags;
+		name = ondisk2->name;
+	}
+	else
+		name = ondisk->name;
+
 	if (len == CE_NAMEMASK)
-		len = strlen(ondisk->name);
+		len = strlen(name);
 	/*
 	 * NEEDSWORK: If the original index is crafted, this copy could
 	 * go unchecked.
 	 */
-	memcpy(ce->name, ondisk->name, len + 1);
+	memcpy(ce->name, name, len + 1);
 }
 
 static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
@@ -1159,7 +1245,8 @@
 		return istate->cache_nr;
 
 	errno = ENOENT;
-	istate->timestamp = 0;
+	istate->timestamp.sec = 0;
+	istate->timestamp.nsec = 0;
 	fd = open(path, O_RDONLY);
 	if (fd < 0) {
 		if (errno == ENOENT)
@@ -1211,7 +1298,9 @@
 		src_offset += ondisk_ce_size(ce);
 		dst_offset += ce_size(ce);
 	}
-	istate->timestamp = st.st_mtime;
+	istate->timestamp.sec = st.st_mtime;
+	istate->timestamp.nsec = ST_MTIME_NSEC(st);
+
 	while (src_offset <= mmap_size - 20 - 8) {
 		/* After an array of active_nr index entries,
 		 * there can be arbitrary number of extended
@@ -1241,14 +1330,15 @@
 
 int is_index_unborn(struct index_state *istate)
 {
-	return (!istate->cache_nr && !istate->alloc && !istate->timestamp);
+	return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
 }
 
 int discard_index(struct index_state *istate)
 {
 	istate->cache_nr = 0;
 	istate->cache_changed = 0;
-	istate->timestamp = 0;
+	istate->timestamp.sec = 0;
+	istate->timestamp.nsec = 0;
 	istate->name_hash_initialized = 0;
 	free_hash(&istate->name_hash);
 	cache_tree_free(&(istate->cache_tree));
@@ -1274,11 +1364,11 @@
 static unsigned char write_buffer[WRITE_BUFFER_SIZE];
 static unsigned long write_buffer_len;
 
-static int ce_write_flush(SHA_CTX *context, int fd)
+static int ce_write_flush(git_SHA_CTX *context, int fd)
 {
 	unsigned int buffered = write_buffer_len;
 	if (buffered) {
-		SHA1_Update(context, write_buffer, buffered);
+		git_SHA1_Update(context, write_buffer, buffered);
 		if (write_in_full(fd, write_buffer, buffered) != buffered)
 			return -1;
 		write_buffer_len = 0;
@@ -1286,7 +1376,7 @@
 	return 0;
 }
 
-static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
+static int ce_write(git_SHA_CTX *context, int fd, void *data, unsigned int len)
 {
 	while (len) {
 		unsigned int buffered = write_buffer_len;
@@ -1308,7 +1398,7 @@
 	return 0;
 }
 
-static int write_index_ext_header(SHA_CTX *context, int fd,
+static int write_index_ext_header(git_SHA_CTX *context, int fd,
 				  unsigned int ext, unsigned int sz)
 {
 	ext = htonl(ext);
@@ -1317,13 +1407,13 @@
 		(ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
 }
 
-static int ce_flush(SHA_CTX *context, int fd)
+static int ce_flush(git_SHA_CTX *context, int fd)
 {
 	unsigned int left = write_buffer_len;
 
 	if (left) {
 		write_buffer_len = 0;
-		SHA1_Update(context, write_buffer, left);
+		git_SHA1_Update(context, write_buffer, left);
 	}
 
 	/* Flush first if not enough space for SHA1 signature */
@@ -1334,7 +1424,7 @@
 	}
 
 	/* Append the SHA1 signature at the end */
-	SHA1_Final(write_buffer + left, context);
+	git_SHA1_Final(write_buffer + left, context);
 	left += 20;
 	return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
 }
@@ -1388,15 +1478,16 @@
 	}
 }
 
-static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)
+static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
 {
 	int size = ondisk_ce_size(ce);
 	struct ondisk_cache_entry *ondisk = xcalloc(1, size);
+	char *name;
 
-	ondisk->ctime.sec = htonl(ce->ce_ctime);
-	ondisk->ctime.nsec = 0;
-	ondisk->mtime.sec = htonl(ce->ce_mtime);
-	ondisk->mtime.nsec = 0;
+	ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
+	ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
+	ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
+	ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
 	ondisk->dev  = htonl(ce->ce_dev);
 	ondisk->ino  = htonl(ce->ce_ino);
 	ondisk->mode = htonl(ce->ce_mode);
@@ -1405,28 +1496,46 @@
 	ondisk->size = htonl(ce->ce_size);
 	hashcpy(ondisk->sha1, ce->sha1);
 	ondisk->flags = htons(ce->ce_flags);
-	memcpy(ondisk->name, ce->name, ce_namelen(ce));
+	if (ce->ce_flags & CE_EXTENDED) {
+		struct ondisk_cache_entry_extended *ondisk2;
+		ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
+		ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
+		name = ondisk2->name;
+	}
+	else
+		name = ondisk->name;
+	memcpy(name, ce->name, ce_namelen(ce));
 
 	return ce_write(c, fd, ondisk, size);
 }
 
-int write_index(const struct index_state *istate, int newfd)
+int write_index(struct index_state *istate, int newfd)
 {
-	SHA_CTX c;
+	git_SHA_CTX c;
 	struct cache_header hdr;
-	int i, err, removed;
+	int i, err, removed, extended;
 	struct cache_entry **cache = istate->cache;
 	int entries = istate->cache_nr;
+	struct stat st;
 
-	for (i = removed = 0; i < entries; i++)
+	for (i = removed = extended = 0; i < entries; i++) {
 		if (cache[i]->ce_flags & CE_REMOVE)
 			removed++;
 
+		/* reduce extended entries if possible */
+		cache[i]->ce_flags &= ~CE_EXTENDED;
+		if (cache[i]->ce_flags & CE_EXTENDED_FLAGS) {
+			extended++;
+			cache[i]->ce_flags |= CE_EXTENDED;
+		}
+	}
+
 	hdr.hdr_signature = htonl(CACHE_SIGNATURE);
-	hdr.hdr_version = htonl(2);
+	/* for extended format, increase version so older git won't try to read it */
+	hdr.hdr_version = htonl(extended ? 3 : 2);
 	hdr.hdr_entries = htonl(entries - removed);
 
-	SHA1_Init(&c);
+	git_SHA1_Init(&c);
 	if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
 		return -1;
 
@@ -1442,9 +1551,8 @@
 
 	/* Write extension data here */
 	if (istate->cache_tree) {
-		struct strbuf sb;
+		struct strbuf sb = STRBUF_INIT;
 
-		strbuf_init(&sb, 0);
 		cache_tree_write(&sb, istate->cache_tree);
 		err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
 			|| ce_write(&c, newfd, sb.buf, sb.len) < 0;
@@ -1452,12 +1560,17 @@
 		if (err)
 			return -1;
 	}
-	return ce_flush(&c, newfd);
+
+	if (ce_flush(&c, newfd) || fstat(newfd, &st))
+		return -1;
+	istate->timestamp.sec = (unsigned int)st.st_mtime;
+	istate->timestamp.nsec = ST_MTIME_NSEC(st);
+	return 0;
 }
 
 /*
  * Read the index file that is potentially unmerged into given
- * index_state, dropping any unmerged entries.  Returns true is
+ * index_state, dropping any unmerged entries.  Returns true if
  * the index is unmerged.  Callers who want to refuse to work
  * from an unmerged state can call this and check its return value,
  * instead of calling read_cache().
@@ -1491,6 +1604,81 @@
 	return unmerged;
 }
 
+struct update_callback_data
+{
+	int flags;
+	int add_errors;
+};
+
+static void update_callback(struct diff_queue_struct *q,
+			    struct diff_options *opt, void *cbdata)
+{
+	int i;
+	struct update_callback_data *data = cbdata;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		const char *path = p->one->path;
+		switch (p->status) {
+		default:
+			die("unexpected diff status %c", p->status);
+		case DIFF_STATUS_UNMERGED:
+			/*
+			 * ADD_CACHE_IGNORE_REMOVAL is unset if "git
+			 * add -u" is calling us, In such a case, a
+			 * missing work tree file needs to be removed
+			 * if there is an unmerged entry at stage #2,
+			 * but such a diff record is followed by
+			 * another with DIFF_STATUS_DELETED (and if
+			 * there is no stage #2, we won't see DELETED
+			 * nor MODIFIED).  We can simply continue
+			 * either way.
+			 */
+			if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
+				continue;
+			/*
+			 * Otherwise, it is "git add path" is asking
+			 * to explicitly add it; we fall through.  A
+			 * missing work tree file is an error and is
+			 * caught by add_file_to_index() in such a
+			 * case.
+			 */
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_TYPE_CHANGED:
+			if (add_file_to_index(&the_index, path, data->flags)) {
+				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+					die("updating files failed");
+				data->add_errors++;
+			}
+			break;
+		case DIFF_STATUS_DELETED:
+			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+				break;
+			if (!(data->flags & ADD_CACHE_PRETEND))
+				remove_file_from_index(&the_index, path);
+			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+				printf("remove '%s'\n", path);
+			break;
+		}
+	}
+}
+
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+{
+	struct update_callback_data data;
+	struct rev_info rev;
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.prune_data = pathspec;
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = update_callback;
+	data.flags = flags;
+	data.add_errors = 0;
+	rev.diffopt.format_callback_data = &data;
+	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+	return !!data.add_errors;
+}
+
 /*
  * Returns 1 if the path is an "other" path with respect to
  * the index; that is, the path is not mentioned in the index at all,
diff --git a/reflog-walk.c b/reflog-walk.c
index f751fdc..fd065f4 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -242,7 +242,7 @@
 }
 
 void show_reflog_message(struct reflog_walk_info* info, int oneline,
-	int relative_date)
+	enum date_mode dmode)
 {
 	if (info && info->last_commit_reflog) {
 		struct commit_reflog *commit_reflog = info->last_commit_reflog;
@@ -251,8 +251,10 @@
 		info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
 		if (oneline) {
 			printf("%s@{", commit_reflog->reflogs->ref);
-			if (commit_reflog->flag || relative_date)
-				printf("%s", show_date(info->timestamp, 0, 1));
+			if (commit_reflog->flag || dmode)
+				printf("%s", show_date(info->timestamp,
+						       info->tz,
+						       dmode));
 			else
 				printf("%d", commit_reflog->reflogs->nr
 				       - 2 - commit_reflog->recno);
@@ -260,10 +262,10 @@
 		}
 		else {
 			printf("Reflog: %s@{", commit_reflog->reflogs->ref);
-			if (commit_reflog->flag || relative_date)
+			if (commit_reflog->flag || dmode)
 				printf("%s", show_date(info->timestamp,
 							info->tz,
-							relative_date));
+							dmode));
 			else
 				printf("%d", commit_reflog->reflogs->nr
 				       - 2 - commit_reflog->recno);
diff --git a/reflog-walk.h b/reflog-walk.h
index 7ca1438..74c9096 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -1,11 +1,14 @@
 #ifndef REFLOG_WALK_H
 #define REFLOG_WALK_H
 
+#include "cache.h"
+
 extern void init_reflog_walk(struct reflog_walk_info** info);
 extern int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name);
 extern void fake_reflog_parent(struct reflog_walk_info *info,
 		struct commit *commit);
-extern void show_reflog_message(struct reflog_walk_info *info, int, int);
+extern void show_reflog_message(struct reflog_walk_info *info, int,
+		enum date_mode);
 
 #endif
diff --git a/refs.c b/refs.c
index be095cb..e65a3b4 100644
--- a/refs.c
+++ b/refs.c
@@ -275,10 +275,8 @@
 				list = get_ref_dir(ref, list);
 				continue;
 			}
-			if (!resolve_ref(ref, sha1, 1, &flag)) {
-				error("%s points nowhere!", ref);
-				continue;
-			}
+			if (!resolve_ref(ref, sha1, 1, &flag))
+				hashclr(sha1);
 			list = add_ref(ref, sha1, flag, list, NULL);
 		}
 		free(ref);
@@ -287,6 +285,35 @@
 	return sort_ref_list(list);
 }
 
+struct warn_if_dangling_data {
+	const char *refname;
+	const char *msg_fmt;
+};
+
+static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
+				   int flags, void *cb_data)
+{
+	struct warn_if_dangling_data *d = cb_data;
+	const char *resolves_to;
+	unsigned char junk[20];
+
+	if (!(flags & REF_ISSYMREF))
+		return 0;
+
+	resolves_to = resolve_ref(refname, junk, 0, NULL);
+	if (!resolves_to || strcmp(resolves_to, d->refname))
+		return 0;
+
+	printf(d->msg_fmt, refname);
+	return 0;
+}
+
+void warn_dangling_symref(const char *msg_fmt, const char *refname)
+{
+	struct warn_if_dangling_data data = { refname, msg_fmt };
+	for_each_rawref(warn_if_dangling_symref, &data);
+}
+
 static struct ref_list *get_loose_refs(void)
 {
 	if (!cached_refs.did_loose) {
@@ -390,6 +417,18 @@
 	return retval;
 }
 
+/*
+ * If the "reading" argument is set, this function finds out what _object_
+ * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
+ * has to exist, and if it is symbolic, it has to point at an existing ref,
+ * because the "read" goes through the symref to the ref it points at.
+ *
+ * The access that is not "reading" may often be "writing", but does not
+ * have to; it can be merely checking _where it leads to_. If it is a
+ * prelude to "writing" to the ref, a write to a symref that points at
+ * yet-to-be-born ref will create the real ref pointed by the symref.
+ * reading=0 allows the caller to check where such a symref leads to.
+ */
 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
 	int depth = MAXDEPTH;
@@ -410,13 +449,7 @@
 			return NULL;
 
 		git_snpath(path, sizeof(path), "%s", ref);
-		/* Special case: non-existing file.
-		 * Not having the refs/heads/new-branch is OK
-		 * if we are writing into it, so is .git/HEAD
-		 * that points at refs/heads/master still to be
-		 * born.  It is NOT OK if we are resolving for
-		 * reading.
-		 */
+		/* Special case: non-existing file. */
 		if (lstat(path, &st) < 0) {
 			struct ref_list *list = get_packed_refs();
 			while (list) {
@@ -492,16 +525,19 @@
 	return -1;
 }
 
+#define DO_FOR_EACH_INCLUDE_BROKEN 01
 static int do_one_ref(const char *base, each_ref_fn fn, int trim,
-		      void *cb_data, struct ref_list *entry)
+		      int flags, void *cb_data, struct ref_list *entry)
 {
 	if (strncmp(base, entry->name, trim))
 		return 0;
-	if (is_null_sha1(entry->sha1))
-		return 0;
-	if (!has_sha1_file(entry->sha1)) {
-		error("%s does not point to a valid object!", entry->name);
-		return 0;
+	if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+		if (is_null_sha1(entry->sha1))
+			return 0;
+		if (!has_sha1_file(entry->sha1)) {
+			error("%s does not point to a valid object!", entry->name);
+			return 0;
+		}
 	}
 	current_ref = entry;
 	return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
@@ -555,7 +591,7 @@
 }
 
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
-			   void *cb_data)
+			   int flags, void *cb_data)
 {
 	int retval = 0;
 	struct ref_list *packed = get_packed_refs();
@@ -564,7 +600,7 @@
 	struct ref_list *extra;
 
 	for (extra = extra_refs; extra; extra = extra->next)
-		retval = do_one_ref(base, fn, trim, cb_data, extra);
+		retval = do_one_ref(base, fn, trim, flags, cb_data, extra);
 
 	while (packed && loose) {
 		struct ref_list *entry;
@@ -580,13 +616,13 @@
 			entry = packed;
 			packed = packed->next;
 		}
-		retval = do_one_ref(base, fn, trim, cb_data, entry);
+		retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
 		if (retval)
 			goto end_each;
 	}
 
 	for (packed = packed ? packed : loose; packed; packed = packed->next) {
-		retval = do_one_ref(base, fn, trim, cb_data, packed);
+		retval = do_one_ref(base, fn, trim, flags, cb_data, packed);
 		if (retval)
 			goto end_each;
 	}
@@ -608,22 +644,33 @@
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/", fn, 0, cb_data);
+	return do_for_each_ref("refs/", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/tags/", fn, 10, cb_data);
+	return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/heads/", fn, 11, cb_data);
+	return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
+	return for_each_ref_in("refs/remotes/", fn, cb_data);
+}
+
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref("refs/", fn, 0,
+			       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
 /*
@@ -634,6 +681,7 @@
  * - it has double dots "..", or
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
  * - it ends with a "/".
+ * - it ends with ".lock"
  */
 
 static inline int bad_ref_char(int ch)
@@ -651,7 +699,8 @@
 
 int check_ref_format(const char *ref)
 {
-	int ch, level, bad_type;
+	int ch, level, bad_type, last;
+	int ret = CHECK_REF_FORMAT_OK;
 	const char *cp = ref;
 
 	level = 0;
@@ -667,33 +716,50 @@
 			return CHECK_REF_FORMAT_ERROR;
 		bad_type = bad_ref_char(ch);
 		if (bad_type) {
-			return (bad_type == 2 && !*cp)
-				? CHECK_REF_FORMAT_WILDCARD
-				: CHECK_REF_FORMAT_ERROR;
+			if (bad_type == 2 && (!*cp || *cp == '/') &&
+			    ret == CHECK_REF_FORMAT_OK)
+				ret = CHECK_REF_FORMAT_WILDCARD;
+			else
+				return CHECK_REF_FORMAT_ERROR;
 		}
 
+		last = ch;
 		/* scan the rest of the path component */
 		while ((ch = *cp++) != 0) {
 			bad_type = bad_ref_char(ch);
-			if (bad_type) {
-				return (bad_type == 2 && !*cp)
-					? CHECK_REF_FORMAT_WILDCARD
-					: CHECK_REF_FORMAT_ERROR;
-			}
+			if (bad_type)
+				return CHECK_REF_FORMAT_ERROR;
 			if (ch == '/')
 				break;
-			if (ch == '.' && *cp == '.')
+			if (last == '.' && ch == '.')
 				return CHECK_REF_FORMAT_ERROR;
+			if (last == '@' && ch == '{')
+				return CHECK_REF_FORMAT_ERROR;
+			last = ch;
 		}
 		level++;
 		if (!ch) {
+			if (ref <= cp - 2 && cp[-2] == '.')
+				return CHECK_REF_FORMAT_ERROR;
 			if (level < 2)
 				return CHECK_REF_FORMAT_ONELEVEL;
-			return CHECK_REF_FORMAT_OK;
+			if (has_extension(ref, ".lock"))
+				return CHECK_REF_FORMAT_ERROR;
+			return ret;
 		}
 	}
 }
 
+const char *prettify_ref(const struct ref *ref)
+{
+	const char *name = ref->name;
+	return name + (
+		!prefixcmp(name, "refs/heads/") ? 11 :
+		!prefixcmp(name, "refs/tags/") ? 10 :
+		!prefixcmp(name, "refs/remotes/") ? 13 :
+		0);
+}
+
 const char *ref_rev_parse_rules[] = {
 	"%.*s",
 	"refs/%.*s",
@@ -953,7 +1019,7 @@
 
 	err = unlink(git_path("logs/%s", lock->ref_name));
 	if (err && errno != ENOENT)
-		fprintf(stderr, "warning: unlink(%s) failed: %s",
+		warning("unlink(%s) failed: %s",
 			git_path("logs/%s", lock->ref_name), strerror(errno));
 	invalidate_cached_refs();
 	unlock_ref(lock);
@@ -1395,8 +1461,7 @@
 				if (get_sha1_hex(rec + 41, sha1))
 					die("Log %s is corrupt.", logfile);
 				if (hashcmp(logged_sha1, sha1)) {
-					fprintf(stderr,
-						"warning: Log %s has gap after %s.\n",
+					warning("Log %s has gap after %s.",
 						logfile, show_date(date, tz, DATE_RFC2822));
 				}
 			}
@@ -1408,8 +1473,7 @@
 				if (get_sha1_hex(rec + 41, logged_sha1))
 					die("Log %s is corrupt.", logfile);
 				if (hashcmp(logged_sha1, sha1)) {
-					fprintf(stderr,
-						"warning: Log %s unexpectedly ended on %s.\n",
+					warning("Log %s unexpectedly ended on %s.",
 						logfile, show_date(date, tz, DATE_RFC2822));
 				}
 			}
@@ -1447,7 +1511,7 @@
 	return 1;
 }
 
-int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data)
 {
 	const char *logfile;
 	FILE *logfp;
@@ -1458,6 +1522,16 @@
 	logfp = fopen(logfile, "r");
 	if (!logfp)
 		return -1;
+
+	if (ofs) {
+		struct stat statbuf;
+		if (fstat(fileno(logfp), &statbuf) ||
+		    statbuf.st_size < ofs ||
+		    fseek(logfp, -ofs, SEEK_END) ||
+		    fgets(buf, sizeof(buf), logfp))
+			return -1;
+	}
+
 	while (fgets(buf, sizeof(buf), logfp)) {
 		unsigned char osha1[20], nsha1[20];
 		char *email_end, *message;
@@ -1491,6 +1565,11 @@
 	return ret;
 }
 
+int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+{
+	return for_each_recent_reflog_ent(ref, fn, 0, cb_data);
+}
+
 static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
 {
 	DIR *dir = opendir(git_path("logs/%s", base));
@@ -1571,10 +1650,121 @@
 	return 0;
 }
 
-struct ref *find_ref_by_name(struct ref *list, const char *name)
+struct ref *find_ref_by_name(const struct ref *list, const char *name)
 {
 	for ( ; list; list = list->next)
 		if (!strcmp(list->name, name))
-			return list;
+			return (struct ref *)list;
 	return NULL;
 }
+
+/*
+ * generate a format suitable for scanf from a ref_rev_parse_rules
+ * rule, that is replace the "%.*s" spec with a "%s" spec
+ */
+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
+{
+	char *spec;
+
+	spec = strstr(rule, "%.*s");
+	if (!spec || strstr(spec + 4, "%.*s"))
+		die("invalid rule in ref_rev_parse_rules: %s", rule);
+
+	/* copy all until spec */
+	strncpy(scanf_fmt, rule, spec - rule);
+	scanf_fmt[spec - rule] = '\0';
+	/* copy new spec */
+	strcat(scanf_fmt, "%s");
+	/* copy remaining rule */
+	strcat(scanf_fmt, spec + 4);
+
+	return;
+}
+
+char *shorten_unambiguous_ref(const char *ref, int strict)
+{
+	int i;
+	static char **scanf_fmts;
+	static int nr_rules;
+	char *short_name;
+
+	/* pre generate scanf formats from ref_rev_parse_rules[] */
+	if (!nr_rules) {
+		size_t total_len = 0;
+
+		/* the rule list is NULL terminated, count them first */
+		for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
+			/* no +1 because strlen("%s") < strlen("%.*s") */
+			total_len += strlen(ref_rev_parse_rules[nr_rules]);
+
+		scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
+
+		total_len = 0;
+		for (i = 0; i < nr_rules; i++) {
+			scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
+					+ total_len;
+			gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
+			total_len += strlen(ref_rev_parse_rules[i]);
+		}
+	}
+
+	/* bail out if there are no rules */
+	if (!nr_rules)
+		return xstrdup(ref);
+
+	/* buffer for scanf result, at most ref must fit */
+	short_name = xstrdup(ref);
+
+	/* skip first rule, it will always match */
+	for (i = nr_rules - 1; i > 0 ; --i) {
+		int j;
+		int rules_to_fail = i;
+		int short_name_len;
+
+		if (1 != sscanf(ref, scanf_fmts[i], short_name))
+			continue;
+
+		short_name_len = strlen(short_name);
+
+		/*
+		 * in strict mode, all (except the matched one) rules
+		 * must fail to resolve to a valid non-ambiguous ref
+		 */
+		if (strict)
+			rules_to_fail = nr_rules;
+
+		/*
+		 * check if the short name resolves to a valid ref,
+		 * but use only rules prior to the matched one
+		 */
+		for (j = 0; j < rules_to_fail; j++) {
+			const char *rule = ref_rev_parse_rules[j];
+			unsigned char short_objectname[20];
+			char refname[PATH_MAX];
+
+			/* skip matched rule */
+			if (i == j)
+				continue;
+
+			/*
+			 * the short name is ambiguous, if it resolves
+			 * (with this previous rule) to a valid ref
+			 * read_ref() returns 0 on success
+			 */
+			mksnpath(refname, sizeof(refname),
+				 rule, short_name_len, short_name);
+			if (!read_ref(refname, short_objectname))
+				break;
+		}
+
+		/*
+		 * short name is non-ambiguous if all previous rules
+		 * haven't resolved to a valid ref
+		 */
+		if (j == rules_to_fail)
+			return short_name;
+	}
+
+	free(short_name);
+	return xstrdup(ref);
+}
diff --git a/refs.h b/refs.h
index 06ad260..29d17a4 100644
--- a/refs.h
+++ b/refs.h
@@ -20,10 +20,16 @@
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);
+extern int for_each_ref_in(const char *, each_ref_fn, void *);
 extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 
+/* can be used to learn about broken ref and symref */
+extern int for_each_rawref(each_ref_fn, void *);
+
+extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
+
 /*
  * Extra refs will be listed by for_each_ref() before any actual refs
  * for the duration of this process or until clear_extra_refs() is
@@ -60,6 +66,7 @@
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
 int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
+int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data);
 
 /*
  * Calls the specified function for each reflog file until it returns nonzero,
@@ -73,6 +80,9 @@
 #define CHECK_REF_FORMAT_WILDCARD (-3)
 extern int check_ref_format(const char *target);
 
+extern const char *prettify_ref(const struct ref *ref);
+extern char *shorten_unambiguous_ref(const char *ref, int strict);
+
 /** rename ref, return 0 on success **/
 extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
 
diff --git a/remote.c b/remote.c
index 91f1b7c..41c5b59 100644
--- a/remote.c
+++ b/remote.c
@@ -4,13 +4,15 @@
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
+#include "dir.h"
+#include "tag.h"
 
 static struct refspec s_tag_refspec = {
 	0,
 	1,
 	0,
-	"refs/tags/",
-	"refs/tags/"
+	"refs/tags/*",
+	"refs/tags/*"
 };
 
 const struct refspec *tag_refspec = &s_tag_refspec;
@@ -37,6 +39,7 @@
 
 static struct branch *current_branch;
 static const char *default_remote_name;
+static int explicit_default_remote_name;
 
 static struct rewrite **rewrite;
 static int rewrite_alloc;
@@ -69,7 +72,7 @@
 	if (!longest)
 		return url;
 
-	ret = malloc(rewrite[longest_i]->baselen +
+	ret = xmalloc(rewrite[longest_i]->baselen +
 		     (strlen(url) - longest->len) + 1);
 	strcpy(ret, rewrite[longest_i]->base);
 	strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
@@ -152,7 +155,7 @@
 		ret->name = xstrndup(name, len);
 	else
 		ret->name = xstrdup(name);
-	refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
+	refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
 	strcpy(refname, "refs/heads/");
 	strcpy(refname + strlen("refs/heads/"), ret->name);
 	ret->refname = refname;
@@ -201,6 +204,7 @@
 
 	if (!f)
 		return;
+	remote->origin = REMOTE_REMOTES;
 	while (fgets(buffer, BUF_SIZE, f)) {
 		int value_list;
 		char *s, *p;
@@ -245,7 +249,7 @@
 {
 	const char *slash = strchr(remote->name, '/');
 	char *frag;
-	struct strbuf branch;
+	struct strbuf branch = STRBUF_INIT;
 	int n = slash ? slash - remote->name : 1000;
 	FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
 	char *s, *p;
@@ -261,6 +265,7 @@
 		s++;
 	if (!*s)
 		return;
+	remote->origin = REMOTE_BRANCHES;
 	p = s + strlen(s);
 	while (isspace(p[-1]))
 		*--p = 0;
@@ -283,7 +288,6 @@
 	 * #branch specified.  The "master" (or specified) branch is
 	 * fetched and stored in the local branch of the same name.
 	 */
-	strbuf_init(&branch, 0);
 	frag = strchr(p, '#');
 	if (frag) {
 		*(frag++) = '\0';
@@ -328,8 +332,10 @@
 			if (!value)
 				return config_error_nonbool(key);
 			branch->remote_name = xstrdup(value);
-			if (branch == current_branch)
+			if (branch == current_branch) {
 				default_remote_name = branch->remote_name;
+				explicit_default_remote_name = 1;
+			}
 		} else if (!strcmp(subkey, ".merge")) {
 			if (!value)
 				return config_error_nonbool(key);
@@ -362,6 +368,7 @@
 	if (!subkey)
 		return error("Config with no key for remote %s", name);
 	remote = make_remote(name, subkey - name);
+	remote->origin = REMOTE_CONFIG;
 	if (!strcmp(subkey, ".mirror"))
 		remote->mirror = git_config_bool(key, value);
 	else if (!strcmp(subkey, ".skipdefaultupdate"))
@@ -448,19 +455,34 @@
  */
 static int verify_refname(char *name, int is_glob)
 {
-	int result, len = -1;
+	int result;
 
-	if (is_glob) {
-		len = strlen(name);
-		assert(name[len - 1] == '/');
-		name[len - 1] = '\0';
-	}
 	result = check_ref_format(name);
-	if (is_glob)
-		name[len - 1] = '/';
+	if (is_glob && result == CHECK_REF_FORMAT_WILDCARD)
+		result = CHECK_REF_FORMAT_OK;
 	return result;
 }
 
+/*
+ * This function frees a refspec array.
+ * Warning: code paths should be checked to ensure that the src
+ *          and dst pointers are always freeable pointers as well
+ *          as the refspec pointer itself.
+ */
+static void free_refspecs(struct refspec *refspec, int nr_refspec)
+{
+	int i;
+
+	if (!refspec)
+		return;
+
+	for (i = 0; i < nr_refspec; i++) {
+		free(refspec[i].src);
+		free(refspec[i].dst);
+	}
+	free(refspec);
+}
+
 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
 	int i;
@@ -472,7 +494,7 @@
 		int is_glob;
 		const char *lhs, *rhs;
 
-		llen = is_glob = 0;
+		is_glob = 0;
 
 		lhs = refspec[i];
 		if (*lhs == '+') {
@@ -493,16 +515,15 @@
 
 		if (rhs) {
 			size_t rlen = strlen(++rhs);
-			is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
-			rs[i].dst = xstrndup(rhs, rlen - is_glob);
+			is_glob = (1 <= rlen && strchr(rhs, '*'));
+			rs[i].dst = xstrndup(rhs, rlen);
 		}
 
 		llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
-		if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+		if (1 <= llen && memchr(lhs, '*', llen)) {
 			if ((rhs && !is_glob) || (!rhs && fetch))
 				goto invalid;
 			is_glob = 1;
-			llen--;
 		} else if (rhs && is_glob) {
 			goto invalid;
 		}
@@ -579,7 +600,12 @@
 
  invalid:
 	if (verify) {
-		free(rs);
+		/*
+		 * nr_refspec must be greater than zero and i must be valid
+		 * since it is only possible to reach this point from within
+		 * the for loop above.
+		 */
+		free_refspecs(rs, i+1);
 		return NULL;
 	}
 	die("Invalid refspec '%s'", refspec[i]);
@@ -591,7 +617,7 @@
 	struct refspec *refspec;
 
 	refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
-	free(refspec);
+	free_refspecs(refspec, 1);
 	return !!refspec;
 }
 
@@ -600,17 +626,14 @@
 	return parse_refspec_internal(nr_refspec, refspec, 1, 0);
 }
 
-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 {
 	return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
 
 static int valid_remote_nick(const char *name)
 {
-	if (!name[0] || /* not empty */
-	    (name[0] == '.' && /* not "." */
-	     (!name[1] || /* not ".." */
-	      (name[1] == '.' && !name[2]))))
+	if (!name[0] || is_dot_or_dotdot(name))
 		return 0;
 	return !strchr(name, '/'); /* no slash */
 }
@@ -618,10 +641,16 @@
 struct remote *remote_get(const char *name)
 {
 	struct remote *ret;
+	int name_given = 0;
 
 	read_config();
-	if (!name)
+	if (name)
+		name_given = 1;
+	else {
 		name = default_remote_name;
+		name_given = explicit_default_remote_name;
+	}
+
 	ret = make_remote(name, 0);
 	if (valid_remote_nick(name)) {
 		if (!ret->url)
@@ -629,7 +658,7 @@
 		if (!ret->url)
 			read_branches_file(ret);
 	}
-	if (!ret->url)
+	if (name_given && !ret->url)
 		add_url_alias(ret, name);
 	if (!ret->url)
 		return NULL;
@@ -638,6 +667,17 @@
 	return ret;
 }
 
+int remote_is_configured(const char *name)
+{
+	int i;
+	read_config();
+
+	for (i = 0; i < remotes_nr; i++)
+		if (!strcmp(name, remotes[i]->name))
+			return 1;
+	return 0;
+}
+
 int for_each_remote(each_remote_fn fn, void *priv)
 {
 	int i, result = 0;
@@ -694,6 +734,41 @@
 	return 0;
 }
 
+static int match_name_with_pattern(const char *key, const char *name,
+				   const char *value, char **result)
+{
+	const char *kstar = strchr(key, '*');
+	size_t klen;
+	size_t ksuffixlen;
+	size_t namelen;
+	int ret;
+	if (!kstar)
+		die("Key '%s' of pattern had no '*'", key);
+	klen = kstar - key;
+	ksuffixlen = strlen(kstar + 1);
+	namelen = strlen(name);
+	ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
+		!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
+	if (ret && value) {
+		const char *vstar = strchr(value, '*');
+		size_t vlen;
+		size_t vsuffixlen;
+		if (!vstar)
+			die("Value '%s' of pattern has no '*'", value);
+		vlen = vstar - value;
+		vsuffixlen = strlen(vstar + 1);
+		*result = xmalloc(vlen + vsuffixlen +
+				  strlen(name) -
+				  klen - ksuffixlen + 1);
+		strncpy(*result, value, vlen);
+		strncpy(*result + vlen,
+			name + klen, namelen - klen - ksuffixlen);
+		strcpy(*result + vlen + namelen - klen - ksuffixlen,
+		       vstar + 1);
+	}
+	return ret;
+}
+
 int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 {
 	int find_src = refspec->src == NULL;
@@ -717,13 +792,7 @@
 		if (!fetch->dst)
 			continue;
 		if (fetch->pattern) {
-			if (!prefixcmp(needle, key)) {
-				*result = xmalloc(strlen(value) +
-						  strlen(needle) -
-						  strlen(key) + 1);
-				strcpy(*result, value);
-				strcpy(*result + strlen(value),
-				       needle + strlen(key));
+			if (match_name_with_pattern(key, needle, value, result)) {
 				refspec->force = fetch->force;
 				return 0;
 			}
@@ -736,26 +805,35 @@
 	return -1;
 }
 
-struct ref *alloc_ref(unsigned namelen)
+static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
+		const char *name)
 {
-	struct ref *ret = xmalloc(sizeof(struct ref) + namelen);
-	memset(ret, 0, sizeof(struct ref) + namelen);
-	return ret;
+	size_t len = strlen(name);
+	struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
+	memcpy(ref->name, prefix, prefixlen);
+	memcpy(ref->name + prefixlen, name, len);
+	return ref;
 }
 
-struct ref *alloc_ref_from_str(const char* str)
+struct ref *alloc_ref(const char *name)
 {
-	struct ref *ret = alloc_ref(strlen(str) + 1);
-	strcpy(ret->name, str);
-	return ret;
+	return alloc_ref_with_prefix("", 0, name);
 }
 
 static struct ref *copy_ref(const struct ref *ref)
 {
-	struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
-	memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
-	ret->next = NULL;
-	return ret;
+	struct ref *cpy;
+	size_t len;
+	if (!ref)
+		return NULL;
+	len = strlen(ref->name);
+	cpy = xmalloc(sizeof(struct ref) + len + 1);
+	memcpy(cpy, ref, sizeof(struct ref) + len + 1);
+	cpy->next = NULL;
+	cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
+	cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
+	cpy->peer_ref = copy_ref(ref->peer_ref);
+	return cpy;
 }
 
 struct ref *copy_ref_list(const struct ref *ref)
@@ -770,10 +848,11 @@
 	return ret;
 }
 
-void free_ref(struct ref *ref)
+static void free_ref(struct ref *ref)
 {
 	if (!ref)
 		return;
+	free_ref(ref->peer_ref);
 	free(ref->remote_status);
 	free(ref->symref);
 	free(ref);
@@ -784,7 +863,6 @@
 	struct ref *next;
 	while (ref) {
 		next = ref->next;
-		free(ref->peer_ref);
 		free_ref(ref);
 		ref = next;
 	}
@@ -858,21 +936,20 @@
 	struct ref *ref;
 
 	if (!*name) {
-		ref = alloc_ref(20);
-		strcpy(ref->name, "(delete)");
+		ref = alloc_ref("(delete)");
 		hashclr(ref->new_sha1);
 		return ref;
 	}
 	if (get_sha1(name, sha1))
 		return NULL;
-	ref = alloc_ref_from_str(name);
+	ref = alloc_ref(name);
 	hashcpy(ref->new_sha1, sha1);
 	return ref;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-	struct ref *ret = alloc_ref_from_str(name);
+	struct ref *ret = alloc_ref(name);
 	tail_link_ref(ret, tail);
 	return ret;
 }
@@ -902,6 +979,7 @@
 			  struct refspec *rs)
 {
 	struct ref *matched_src, *matched_dst;
+	int copy_src;
 
 	const char *dst_value = rs->dst;
 	char *dst_guess;
@@ -912,6 +990,7 @@
 	matched_src = matched_dst = NULL;
 	switch (count_refspec_match(rs->src, src, &matched_src)) {
 	case 1:
+		copy_src = 1;
 		break;
 	case 0:
 		/* The source could be in the get_sha1() format
@@ -921,6 +1000,7 @@
 		matched_src = try_explicit_object_name(rs->src);
 		if (!matched_src)
 			return error("src refspec %s does not match any.", rs->src);
+		copy_src = 0;
 		break;
 	default:
 		return error("src refspec %s matches more than one.", rs->src);
@@ -966,7 +1046,7 @@
 		return error("dst ref %s receives from more than one src.",
 		      matched_dst->name);
 	else {
-		matched_dst->peer_ref = matched_src;
+		matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
 		matched_dst->force = rs->force;
 	}
 	return 0;
@@ -995,7 +1075,8 @@
 			continue;
 		}
 
-		if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
+		if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name,
+							     NULL, NULL))
 			return rs + i;
 	}
 	if (matching_refs != -1)
@@ -1015,6 +1096,7 @@
 	struct refspec *rs;
 	int send_all = flags & MATCH_REFS_ALL;
 	int send_mirror = flags & MATCH_REFS_MIRROR;
+	int errs;
 	static const char *default_refspec[] = { ":", 0 };
 
 	if (!nr_refspec) {
@@ -1022,8 +1104,7 @@
 		refspec = default_refspec;
 	}
 	rs = parse_push_refspec(nr_refspec, (const char **) refspec);
-	if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
-		return -1;
+	errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
 
 	/* pick the remainder */
 	for ( ; src; src = src->next) {
@@ -1049,11 +1130,9 @@
 
 		} else {
 			const char *dst_side = pat->dst ? pat->dst : pat->src;
-			dst_name = xmalloc(strlen(dst_side) +
-					   strlen(src->name) -
-					   strlen(pat->src) + 2);
-			strcpy(dst_name, dst_side);
-			strcat(dst_name, src->name + strlen(pat->src));
+			if (!match_name_with_pattern(pat->src, src->name,
+						     dst_side, &dst_name))
+				die("Didn't think it matches any more");
 		}
 		dst_peer = find_ref_by_name(dst, dst_name);
 		if (dst_peer) {
@@ -1074,11 +1153,13 @@
 			dst_peer = make_linked_ref(dst_name, dst_tail);
 			hashcpy(dst_peer->new_sha1, src->new_sha1);
 		}
-		dst_peer->peer_ref = src;
+		dst_peer->peer_ref = copy_ref(src);
 		dst_peer->force = pat->force;
 	free_name:
 		free(dst_name);
 	}
+	if (errs)
+		return -1;
 	return 0;
 }
 
@@ -1100,8 +1181,9 @@
 			for (i = 0; i < ret->merge_nr; i++) {
 				ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
 				ret->merge[i]->src = xstrdup(ret->merge_name[i]);
-				remote_find_tracking(ret->remote,
-						     ret->merge[i]);
+				if (remote_find_tracking(ret->remote, ret->merge[i])
+				    && !strcmp(ret->remote_name, "."))
+					ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
 			}
 		}
 	}
@@ -1129,21 +1211,17 @@
 	struct ref *ret = NULL;
 	struct ref **tail = &ret;
 
-	int remote_prefix_len = strlen(refspec->src);
-	int local_prefix_len = strlen(refspec->dst);
+	char *expn_name;
 
 	for (ref = remote_refs; ref; ref = ref->next) {
 		if (strchr(ref->name, '^'))
 			continue; /* a dereference item */
-		if (!prefixcmp(ref->name, refspec->src)) {
-			const char *match;
+		if (match_name_with_pattern(refspec->src, ref->name,
+					    refspec->dst, &expn_name)) {
 			struct ref *cpy = copy_ref(ref);
-			match = ref->name + remote_prefix_len;
 
-			cpy->peer_ref = alloc_ref(local_prefix_len +
-						  strlen(match) + 1);
-			sprintf(cpy->peer_ref->name, "%s%s",
-				refspec->dst, match);
+			cpy->peer_ref = alloc_ref(expn_name);
+			free(expn_name);
 			if (refspec->force)
 				cpy->peer_ref->force = 1;
 			*tail = cpy;
@@ -1176,25 +1254,18 @@
 
 static struct ref *get_local_ref(const char *name)
 {
-	struct ref *ret;
 	if (!name)
 		return NULL;
 
-	if (!prefixcmp(name, "refs/")) {
-		return alloc_ref_from_str(name);
-	}
+	if (!prefixcmp(name, "refs/"))
+		return alloc_ref(name);
 
 	if (!prefixcmp(name, "heads/") ||
 	    !prefixcmp(name, "tags/") ||
-	    !prefixcmp(name, "remotes/")) {
-		ret = alloc_ref(strlen(name) + 6);
-		sprintf(ret->name, "refs/%s", name);
-		return ret;
-	}
+	    !prefixcmp(name, "remotes/"))
+		return alloc_ref_with_prefix("refs/", 5, name);
 
-	ret = alloc_ref(strlen(name) + 12);
-	sprintf(ret->name, "refs/heads/%s", name);
-	return ret;
+	return alloc_ref_with_prefix("refs/heads/", 11, name);
 }
 
 int get_fetch_map(const struct ref *remote_refs,
@@ -1253,6 +1324,54 @@
 	return 1;
 }
 
+static void unmark_and_free(struct commit_list *list, unsigned int mark)
+{
+	while (list) {
+		struct commit_list *temp = list;
+		temp->item->object.flags &= ~mark;
+		list = temp->next;
+		free(temp);
+	}
+}
+
+int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
+{
+	struct object *o;
+	struct commit *old, *new;
+	struct commit_list *list, *used;
+	int found = 0;
+
+	/* Both new and old must be commit-ish and new is descendant of
+	 * old.  Otherwise we require --force.
+	 */
+	o = deref_tag(parse_object(old_sha1), NULL, 0);
+	if (!o || o->type != OBJ_COMMIT)
+		return 0;
+	old = (struct commit *) o;
+
+	o = deref_tag(parse_object(new_sha1), NULL, 0);
+	if (!o || o->type != OBJ_COMMIT)
+		return 0;
+	new = (struct commit *) o;
+
+	if (parse_commit(new) < 0)
+		return 0;
+
+	used = list = NULL;
+	commit_list_insert(new, &list);
+	while (list) {
+		new = pop_most_recent_commit(&list, TMP_MARK);
+		commit_list_insert(new, &used);
+		if (new == old) {
+			found = 1;
+			break;
+		}
+	}
+	unmark_and_free(list, TMP_MARK);
+	unmark_and_free(used, TMP_MARK);
+	return found;
+}
+
 /*
  * Return true if there is anything to report, otherwise false.
  */
@@ -1294,9 +1413,10 @@
 	if (theirs == ours)
 		return 0;
 
-	/* Run "rev-list --left-right ours...theirs" internally... */
+	/* Run "rev-list --no-merges --left-right ours...theirs" internally... */
 	rev_argc = 0;
 	rev_argv[rev_argc++] = NULL;
+	rev_argv[rev_argc++] = "--no-merges";
 	rev_argv[rev_argc++] = "--left-right";
 	rev_argv[rev_argc++] = symmetric;
 	rev_argv[rev_argc++] = "--";
@@ -1341,9 +1461,7 @@
 		return 0;
 
 	base = branch->merge[0]->dst;
-	if (!prefixcmp(base, "refs/remotes/")) {
-		base += strlen("refs/remotes/");
-	}
+	base = shorten_unambiguous_ref(base, 0);
 	if (!num_theirs)
 		strbuf_addf(sb, "Your branch is ahead of '%s' "
 			    "by %d commit%s.\n",
@@ -1360,3 +1478,68 @@
 			    base, num_ours, num_theirs);
 	return 1;
 }
+
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct ref ***local_tail = cb_data;
+	struct ref *ref;
+	int len;
+
+	/* we already know it starts with refs/ to get here */
+	if (check_ref_format(refname + 5))
+		return 0;
+
+	len = strlen(refname) + 1;
+	ref = xcalloc(1, sizeof(*ref) + len);
+	hashcpy(ref->new_sha1, sha1);
+	memcpy(ref->name, refname, len);
+	**local_tail = ref;
+	*local_tail = &ref->next;
+	return 0;
+}
+
+struct ref *get_local_heads(void)
+{
+	struct ref *local_refs = NULL, **local_tail = &local_refs;
+	for_each_ref(one_local_ref, &local_tail);
+	return local_refs;
+}
+
+struct ref *guess_remote_head(const struct ref *head,
+			      const struct ref *refs,
+			      int all)
+{
+	const struct ref *r;
+	struct ref *list = NULL;
+	struct ref **tail = &list;
+
+	if (!head)
+		return NULL;
+
+	/*
+	 * Some transports support directly peeking at
+	 * where HEAD points; if that is the case, then
+	 * we don't have to guess.
+	 */
+	if (head->symref)
+		return copy_ref(find_ref_by_name(refs, head->symref));
+
+	/* If refs/heads/master could be right, it is. */
+	if (!all) {
+		r = find_ref_by_name(refs, "refs/heads/master");
+		if (r && !hashcmp(r->old_sha1, head->old_sha1))
+			return copy_ref(r);
+	}
+
+	/* Look for another ref that points there */
+	for (r = refs; r; r = r->next) {
+		if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
+			*tail = copy_ref(r);
+			tail = &((*tail)->next);
+			if (!all)
+				break;
+		}
+	}
+
+	return list;
+}
diff --git a/remote.h b/remote.h
index 091b1d0..99706a8 100644
--- a/remote.h
+++ b/remote.h
@@ -1,8 +1,15 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
+enum {
+	REMOTE_CONFIG,
+	REMOTE_REMOTES,
+	REMOTE_BRANCHES
+};
+
 struct remote {
 	const char *name;
+	int origin;
 
 	const char **url;
 	int url_nr;
@@ -38,6 +45,7 @@
 };
 
 struct remote *remote_get(const char *name);
+int remote_is_configured(const char *name);
 
 typedef int each_remote_fn(struct remote *remote, void *priv);
 int for_each_remote(each_remote_fn fn, void *priv);
@@ -55,9 +63,7 @@
 
 extern const struct refspec *tag_refspec;
 
-struct ref *alloc_ref(unsigned namelen);
-
-struct ref *alloc_ref_from_str(const char* str);
+struct ref *alloc_ref(const char *name);
 
 struct ref *copy_ref_list(const struct ref *ref);
 
@@ -69,6 +75,7 @@
 void free_refs(struct ref *ref);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
+int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
 
 /*
  * Removes and frees any duplicate refs in the map.
@@ -77,7 +84,6 @@
 
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
 
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 	       int nr_refspec, const char **refspec, int all);
@@ -133,4 +139,15 @@
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
 int format_tracking_info(struct branch *branch, struct strbuf *sb);
 
+struct ref *get_local_heads(void);
+/*
+ * Find refs from a list which are likely to be pointed to by the given HEAD
+ * ref. If 'all' is false, returns the most likely ref; otherwise, returns a
+ * list of all candidate refs. If no match is found (or 'head' is NULL),
+ * returns NULL. All returns are newly allocated and should be freed.
+ */
+struct ref *guess_remote_head(const struct ref *head,
+			      const struct ref *refs,
+			      int all);
+
 #endif
diff --git a/rerere.c b/rerere.c
index a0d477a..713c6e1 100644
--- a/rerere.c
+++ b/rerere.c
@@ -12,15 +12,15 @@
 
 static char *merge_rr_path;
 
-static const char *rr_path(const char *name, const char *file)
+const char *rerere_path(const char *hex, const char *file)
 {
-	return git_path("rr-cache/%s/%s", name, file);
+	return git_path("rr-cache/%s/%s", hex, file);
 }
 
-static int has_resolution(const char *name)
+int has_rerere_resolution(const char *hex)
 {
 	struct stat st;
-	return !stat(rr_path(name, "postimage"), &st);
+	return !stat(rerere_path(hex, "postimage"), &st);
 }
 
 static void read_rr(struct string_list *rr)
@@ -70,15 +70,32 @@
 	return 0;
 }
 
+static void ferr_write(const void *p, size_t count, FILE *fp, int *err)
+{
+	if (!count || *err)
+		return;
+	if (fwrite(p, count, 1, fp) != 1)
+		*err = errno;
+}
+
+static inline void ferr_puts(const char *s, FILE *fp, int *err)
+{
+	ferr_write(s, strlen(s), fp, err);
+}
+
 static int handle_file(const char *path,
 	 unsigned char *sha1, const char *output)
 {
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	char buf[1024];
-	int hunk = 0, hunk_no = 0;
-	struct strbuf one, two;
+	int hunk_no = 0;
+	enum {
+		RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
+	} hunk = RR_CONTEXT;
+	struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
 	FILE *f = fopen(path, "r");
 	FILE *out = NULL;
+	int wrerror = 0;
 
 	if (!f)
 		return error("Could not open %s", path);
@@ -92,47 +109,51 @@
 	}
 
 	if (sha1)
-		SHA1_Init(&ctx);
+		git_SHA1_Init(&ctx);
 
-	strbuf_init(&one, 0);
-	strbuf_init(&two,  0);
 	while (fgets(buf, sizeof(buf), f)) {
 		if (!prefixcmp(buf, "<<<<<<< ")) {
-			if (hunk)
+			if (hunk != RR_CONTEXT)
 				goto bad;
-			hunk = 1;
+			hunk = RR_SIDE_1;
+		} else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) {
+			if (hunk != RR_SIDE_1)
+				goto bad;
+			hunk = RR_ORIGINAL;
 		} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
-			if (hunk != 1)
+			if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
 				goto bad;
-			hunk = 2;
+			hunk = RR_SIDE_2;
 		} else if (!prefixcmp(buf, ">>>>>>> ")) {
-			if (hunk != 2)
+			if (hunk != RR_SIDE_2)
 				goto bad;
 			if (strbuf_cmp(&one, &two) > 0)
 				strbuf_swap(&one, &two);
 			hunk_no++;
-			hunk = 0;
+			hunk = RR_CONTEXT;
 			if (out) {
-				fputs("<<<<<<<\n", out);
-				fwrite(one.buf, one.len, 1, out);
-				fputs("=======\n", out);
-				fwrite(two.buf, two.len, 1, out);
-				fputs(">>>>>>>\n", out);
+				ferr_puts("<<<<<<<\n", out, &wrerror);
+				ferr_write(one.buf, one.len, out, &wrerror);
+				ferr_puts("=======\n", out, &wrerror);
+				ferr_write(two.buf, two.len, out, &wrerror);
+				ferr_puts(">>>>>>>\n", out, &wrerror);
 			}
 			if (sha1) {
-				SHA1_Update(&ctx, one.buf ? one.buf : "",
+				git_SHA1_Update(&ctx, one.buf ? one.buf : "",
 					    one.len + 1);
-				SHA1_Update(&ctx, two.buf ? two.buf : "",
+				git_SHA1_Update(&ctx, two.buf ? two.buf : "",
 					    two.len + 1);
 			}
 			strbuf_reset(&one);
 			strbuf_reset(&two);
-		} else if (hunk == 1)
+		} else if (hunk == RR_SIDE_1)
 			strbuf_addstr(&one, buf);
-		else if (hunk == 2)
+		else if (hunk == RR_ORIGINAL)
+			; /* discard */
+		else if (hunk == RR_SIDE_2)
 			strbuf_addstr(&two, buf);
 		else if (out)
-			fputs(buf, out);
+			ferr_puts(buf, out, &wrerror);
 		continue;
 	bad:
 		hunk = 99; /* force error exit */
@@ -142,15 +163,21 @@
 	strbuf_release(&two);
 
 	fclose(f);
-	if (out)
-		fclose(out);
+	if (wrerror)
+		error("There were errors while writing %s (%s)",
+		      path, strerror(wrerror));
+	if (out && fclose(out))
+		wrerror = error("Failed to flush %s: %s",
+				path, strerror(errno));
 	if (sha1)
-		SHA1_Final(sha1, &ctx);
-	if (hunk) {
+		git_SHA1_Final(sha1, &ctx);
+	if (hunk != RR_CONTEXT) {
 		if (output)
 			unlink(output);
 		return error("Could not parse conflict hunks in %s", path);
 	}
+	if (wrerror)
+		return -1;
 	return hunk_no;
 }
 
@@ -181,21 +208,25 @@
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
 
-	if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
+	if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
 		return 1;
 
-	if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
-			read_mmfile(&base, rr_path(name, "preimage")) ||
-			read_mmfile(&other, rr_path(name, "postimage")))
+	if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
+			read_mmfile(&base, rerere_path(name, "preimage")) ||
+			read_mmfile(&other, rerere_path(name, "postimage")))
 		return 1;
 	ret = xdl_merge(&base, &cur, "", &other, "",
 			&xpp, XDL_MERGE_ZEALOUS, &result);
 	if (!ret) {
 		FILE *f = fopen(path, "w");
 		if (!f)
-			return error("Could not write to %s", path);
-		fwrite(result.ptr, result.size, 1, f);
-		fclose(f);
+			return error("Could not open %s: %s", path,
+				     strerror(errno));
+		if (fwrite(result.ptr, result.size, 1, f) != 1)
+			error("Could not write %s: %s", path, strerror(errno));
+		if (fclose(f))
+			return error("Writing %s failed: %s", path,
+				     strerror(errno));
 	}
 
 	free(cur.ptr);
@@ -259,8 +290,8 @@
 			hex = xstrdup(sha1_to_hex(sha1));
 			string_list_insert(path, rr)->util = hex;
 			if (mkdir(git_path("rr-cache/%s", hex), 0755))
-				continue;;
-			handle_file(path, NULL, rr_path(hex, "preimage"));
+				continue;
+			handle_file(path, NULL, rerere_path(hex, "preimage"));
 			fprintf(stderr, "Recorded preimage for '%s'\n", path);
 		}
 	}
@@ -276,7 +307,7 @@
 		const char *path = rr->items[i].string;
 		const char *name = (const char *)rr->items[i].util;
 
-		if (has_resolution(name)) {
+		if (has_rerere_resolution(name)) {
 			if (!merge(name, path)) {
 				if (rerere_autoupdate)
 					string_list_insert(path, &update);
@@ -295,7 +326,7 @@
 			continue;
 
 		fprintf(stderr, "Recorded resolution for '%s'.\n", path);
-		copy_file(rr_path(name, "postimage"), path, 0666);
+		copy_file(rerere_path(name, "postimage"), path, 0666);
 	mark_resolved:
 		rr->items[i].util = NULL;
 	}
@@ -319,7 +350,6 @@
 
 static int is_rerere_enabled(void)
 {
-	struct stat st;
 	const char *rr_cache;
 	int rr_cache_exists;
 
@@ -327,7 +357,7 @@
 		return 0;
 
 	rr_cache = git_path("rr-cache");
-	rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
+	rr_cache_exists = is_directory(rr_cache);
 	if (rerere_enabled < 0)
 		return rr_cache_exists;
 
diff --git a/rerere.h b/rerere.h
index f9b0386..13313f3 100644
--- a/rerere.h
+++ b/rerere.h
@@ -5,5 +5,7 @@
 
 extern int setup_rerere(struct string_list *);
 extern int rerere(void);
+extern const char *rerere_path(const char *hex, const char *file);
+extern int has_rerere_resolution(const char *hex);
 
 #endif
diff --git a/revision.c b/revision.c
index 69d5fd4..bd0ea34 100644
--- a/revision.c
+++ b/revision.c
@@ -11,6 +11,7 @@
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "decorate.h"
+#include "log-tree.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -182,8 +183,11 @@
 		if (!tag->tagged)
 			die("bad tag");
 		object = parse_object(tag->tagged->sha1);
-		if (!object)
+		if (!object) {
+			if (flags & UNINTERESTING)
+				return NULL;
 			die("bad object %s", sha1_to_hex(tag->tagged->sha1));
+		}
 	}
 
 	/*
@@ -199,6 +203,8 @@
 			mark_parents_uninteresting(commit);
 			revs->limited = 1;
 		}
+		if (revs->show_source && !commit->util)
+			commit->util = (void *) name;
 		return commit;
 	}
 
@@ -292,10 +298,31 @@
 	DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
-static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
+static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
 {
+	struct tree *t1 = parent->tree;
+	struct tree *t2 = commit->tree;
+
 	if (!t1)
 		return REV_TREE_NEW;
+
+	if (revs->simplify_by_decoration) {
+		/*
+		 * If we are simplifying by decoration, then the commit
+		 * is worth showing if it has a tag pointing at it.
+		 */
+		if (lookup_decoration(&name_decoration, &commit->object))
+			return REV_TREE_DIFFERENT;
+		/*
+		 * A commit that is not pointed by a tag is uninteresting
+		 * if we are not limited by path.  This means that you will
+		 * see the usual "commits that touch the paths" plus any
+		 * tagged commit by specifying both --simplify-by-decoration
+		 * and pathspec.
+		 */
+		if (!revs->prune_data)
+			return REV_TREE_SAME;
+	}
 	if (!t2)
 		return REV_TREE_DIFFERENT;
 	tree_difference = REV_TREE_SAME;
@@ -306,12 +333,13 @@
 	return tree_difference;
 }
 
-static int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
+static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 {
 	int retval;
 	void *tree;
 	unsigned long size;
 	struct tree_desc empty, real;
+	struct tree *t1 = commit->tree;
 
 	if (!t1)
 		return 0;
@@ -345,7 +373,7 @@
 		return;
 
 	if (!commit->parents) {
-		if (rev_same_tree_as_empty(revs, commit->tree))
+		if (rev_same_tree_as_empty(revs, commit))
 			commit->object.flags |= TREESAME;
 		return;
 	}
@@ -365,7 +393,7 @@
 			die("cannot simplify commit %s (because of %s)",
 			    sha1_to_hex(commit->object.sha1),
 			    sha1_to_hex(p->object.sha1));
-		switch (rev_compare_tree(revs, p->tree, commit->tree)) {
+		switch (rev_compare_tree(revs, p, commit)) {
 		case REV_TREE_SAME:
 			tree_same = 1;
 			if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
@@ -385,7 +413,7 @@
 
 		case REV_TREE_NEW:
 			if (revs->remove_empty_trees &&
-			    rev_same_tree_as_empty(revs, p->tree)) {
+			    rev_same_tree_as_empty(revs, p)) {
 				/* We are adding all the specified
 				 * paths from this parent, so the
 				 * history beyond this parent is not
@@ -454,9 +482,10 @@
 		while (parent) {
 			struct commit *p = parent->item;
 			parent = parent->next;
+			if (p)
+				p->object.flags |= UNINTERESTING;
 			if (parse_commit(p) < 0)
-				return -1;
-			p->object.flags |= UNINTERESTING;
+				continue;
 			if (p->parents)
 				mark_parents_uninteresting(p);
 			if (p->object.flags & SEEN)
@@ -484,12 +513,14 @@
 
 		if (parse_commit(p) < 0)
 			return -1;
+		if (revs->show_source && !p->util)
+			p->util = commit->util;
 		p->object.flags |= left_flag;
 		if (!(p->object.flags & SEEN)) {
 			p->object.flags |= SEEN;
 			insert_by_date_cached(p, list, cached_base, cache_ptr);
 		}
-		if(revs->first_parent_only)
+		if (revs->first_parent_only)
 			break;
 	}
 	return 0;
@@ -963,16 +994,6 @@
 	add_grep(revs, pattern, GREP_PATTERN_BODY);
 }
 
-static void add_ignore_packed(struct rev_info *revs, const char *name)
-{
-	int num = ++revs->num_ignore_packed;
-
-	revs->ignore_packed = xrealloc(revs->ignore_packed,
-				       sizeof(const char *) * (num + 1));
-	revs->ignore_packed[num-1] = name;
-	revs->ignore_packed[num] = NULL;
-}
-
 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
 			       int *unkc, const char **unkv)
 {
@@ -1028,6 +1049,19 @@
 	} else if (!strcmp(arg, "--topo-order")) {
 		revs->lifo = 1;
 		revs->topo_order = 1;
+	} else if (!strcmp(arg, "--simplify-merges")) {
+		revs->simplify_merges = 1;
+		revs->rewrite_parents = 1;
+		revs->simplify_history = 0;
+		revs->limited = 1;
+	} else if (!strcmp(arg, "--simplify-by-decoration")) {
+		revs->simplify_merges = 1;
+		revs->rewrite_parents = 1;
+		revs->simplify_history = 0;
+		revs->simplify_by_decoration = 1;
+		revs->limited = 1;
+		revs->prune = 1;
+		load_ref_decorations();
 	} else if (!strcmp(arg, "--date-order")) {
 		revs->lifo = 0;
 		revs->topo_order = 1;
@@ -1072,12 +1106,8 @@
 		revs->edge_hint = 1;
 	} else if (!strcmp(arg, "--unpacked")) {
 		revs->unpacked = 1;
-		free(revs->ignore_packed);
-		revs->ignore_packed = NULL;
-		revs->num_ignore_packed = 0;
 	} else if (!prefixcmp(arg, "--unpacked=")) {
-		revs->unpacked = 1;
-		add_ignore_packed(revs, arg+11);
+		die("--unpacked=<packfile> no longer supported.");
 	} else if (!strcmp(arg, "-r")) {
 		revs->diff = 1;
 		DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
@@ -1100,9 +1130,13 @@
 	} else if (!strcmp(arg, "--pretty")) {
 		revs->verbose_header = 1;
 		get_commit_format(arg+8, revs);
-	} else if (!prefixcmp(arg, "--pretty=")) {
+	} else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
 		revs->verbose_header = 1;
 		get_commit_format(arg+9, revs);
+	} else if (!strcmp(arg, "--oneline")) {
+		revs->verbose_header = 1;
+		get_commit_format("oneline", revs);
+		revs->abbrev_commit = 1;
 	} else if (!strcmp(arg, "--graph")) {
 		revs->topo_order = 1;
 		revs->rewrite_parents = 1;
@@ -1223,6 +1257,7 @@
 
 			if (!strcmp(arg, "--all")) {
 				handle_refs(revs, flags, for_each_ref);
+				handle_refs(revs, flags, head_ref);
 				continue;
 			}
 			if (!strcmp(arg, "--branches")) {
@@ -1355,6 +1390,179 @@
 	l->next = add_decoration(&revs->children, &parent->object, l);
 }
 
+static int remove_duplicate_parents(struct commit *commit)
+{
+	struct commit_list **pp, *p;
+	int surviving_parents;
+
+	/* Examine existing parents while marking ones we have seen... */
+	pp = &commit->parents;
+	while ((p = *pp) != NULL) {
+		struct commit *parent = p->item;
+		if (parent->object.flags & TMP_MARK) {
+			*pp = p->next;
+			continue;
+		}
+		parent->object.flags |= TMP_MARK;
+		pp = &p->next;
+	}
+	/* count them while clearing the temporary mark */
+	surviving_parents = 0;
+	for (p = commit->parents; p; p = p->next) {
+		p->item->object.flags &= ~TMP_MARK;
+		surviving_parents++;
+	}
+	return surviving_parents;
+}
+
+struct merge_simplify_state {
+	struct commit *simplified;
+};
+
+static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs, struct commit *commit)
+{
+	struct merge_simplify_state *st;
+
+	st = lookup_decoration(&revs->merge_simplification, &commit->object);
+	if (!st) {
+		st = xcalloc(1, sizeof(*st));
+		add_decoration(&revs->merge_simplification, &commit->object, st);
+	}
+	return st;
+}
+
+static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
+{
+	struct commit_list *p;
+	struct merge_simplify_state *st, *pst;
+	int cnt;
+
+	st = locate_simplify_state(revs, commit);
+
+	/*
+	 * Have we handled this one?
+	 */
+	if (st->simplified)
+		return tail;
+
+	/*
+	 * An UNINTERESTING commit simplifies to itself, so does a
+	 * root commit.  We do not rewrite parents of such commit
+	 * anyway.
+	 */
+	if ((commit->object.flags & UNINTERESTING) || !commit->parents) {
+		st->simplified = commit;
+		return tail;
+	}
+
+	/*
+	 * Do we know what commit all of our parents should be rewritten to?
+	 * Otherwise we are not ready to rewrite this one yet.
+	 */
+	for (cnt = 0, p = commit->parents; p; p = p->next) {
+		pst = locate_simplify_state(revs, p->item);
+		if (!pst->simplified) {
+			tail = &commit_list_insert(p->item, tail)->next;
+			cnt++;
+		}
+	}
+	if (cnt) {
+		tail = &commit_list_insert(commit, tail)->next;
+		return tail;
+	}
+
+	/*
+	 * Rewrite our list of parents.
+	 */
+	for (p = commit->parents; p; p = p->next) {
+		pst = locate_simplify_state(revs, p->item);
+		p->item = pst->simplified;
+	}
+	cnt = remove_duplicate_parents(commit);
+
+	/*
+	 * It is possible that we are a merge and one side branch
+	 * does not have any commit that touches the given paths;
+	 * in such a case, the immediate parents will be rewritten
+	 * to different commits.
+	 *
+	 *      o----X		X: the commit we are looking at;
+	 *     /    /		o: a commit that touches the paths;
+	 * ---o----'
+	 *
+	 * Further reduce the parents by removing redundant parents.
+	 */
+	if (1 < cnt) {
+		struct commit_list *h = reduce_heads(commit->parents);
+		cnt = commit_list_count(h);
+		free_commit_list(commit->parents);
+		commit->parents = h;
+	}
+
+	/*
+	 * A commit simplifies to itself if it is a root, if it is
+	 * UNINTERESTING, if it touches the given paths, or if it is a
+	 * merge and its parents simplifies to more than one commits
+	 * (the first two cases are already handled at the beginning of
+	 * this function).
+	 *
+	 * Otherwise, it simplifies to what its sole parent simplifies to.
+	 */
+	if (!cnt ||
+	    (commit->object.flags & UNINTERESTING) ||
+	    !(commit->object.flags & TREESAME) ||
+	    (1 < cnt))
+		st->simplified = commit;
+	else {
+		pst = locate_simplify_state(revs, commit->parents->item);
+		st->simplified = pst->simplified;
+	}
+	return tail;
+}
+
+static void simplify_merges(struct rev_info *revs)
+{
+	struct commit_list *list;
+	struct commit_list *yet_to_do, **tail;
+
+	if (!revs->topo_order)
+		sort_in_topological_order(&revs->commits, revs->lifo);
+	if (!revs->prune)
+		return;
+
+	/* feed the list reversed */
+	yet_to_do = NULL;
+	for (list = revs->commits; list; list = list->next)
+		commit_list_insert(list->item, &yet_to_do);
+	while (yet_to_do) {
+		list = yet_to_do;
+		yet_to_do = NULL;
+		tail = &yet_to_do;
+		while (list) {
+			struct commit *commit = list->item;
+			struct commit_list *next = list->next;
+			free(list);
+			list = next;
+			tail = simplify_one(revs, commit, tail);
+		}
+	}
+
+	/* clean up the result, removing the simplified ones */
+	list = revs->commits;
+	revs->commits = NULL;
+	tail = &revs->commits;
+	while (list) {
+		struct commit *commit = list->item;
+		struct commit_list *next = list->next;
+		struct merge_simplify_state *st;
+		free(list);
+		list = next;
+		st = locate_simplify_state(revs, commit);
+		if (st->simplified == commit)
+			tail = &commit_list_insert(commit, tail)->next;
+	}
+}
+
 static void set_children(struct rev_info *revs)
 {
 	struct commit_list *l;
@@ -1395,6 +1603,8 @@
 			return -1;
 	if (revs->topo_order)
 		sort_in_topological_order(&revs->commits, revs->lifo);
+	if (revs->simplify_merges)
+		simplify_merges(revs);
 	if (revs->children.name)
 		set_children(revs);
 	return 0;
@@ -1427,26 +1637,6 @@
 	}
 }
 
-static void remove_duplicate_parents(struct commit *commit)
-{
-	struct commit_list **pp, *p;
-
-	/* Examine existing parents while marking ones we have seen... */
-	pp = &commit->parents;
-	while ((p = *pp) != NULL) {
-		struct commit *parent = p->item;
-		if (parent->object.flags & TMP_MARK) {
-			*pp = p->next;
-			continue;
-		}
-		parent->object.flags |= TMP_MARK;
-		pp = &p->next;
-	}
-	/* ... and clear the temporary mark */
-	for (p = commit->parents; p; p = p->next)
-		p->item->object.flags &= ~TMP_MARK;
-}
-
 static int rewrite_parents(struct rev_info *revs, struct commit *commit)
 {
 	struct commit_list **pp = &commit->parents;
@@ -1485,7 +1675,7 @@
 {
 	if (commit->object.flags & SHOWN)
 		return commit_ignore;
-	if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
+	if (revs->unpacked && has_sha1_pack(commit->object.sha1))
 		return commit_ignore;
 	if (revs->show_all)
 		return commit_show;
@@ -1538,14 +1728,16 @@
 			    (commit->date < revs->max_age))
 				continue;
 			if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
-				return NULL;
+				die("Failed to traverse parents of commit %s",
+				    sha1_to_hex(commit->object.sha1));
 		}
 
 		switch (simplify_commit(revs, commit)) {
 		case commit_ignore:
 			continue;
 		case commit_error:
-			return NULL;
+			die("Failed to simplify parents of commit %s",
+			    sha1_to_hex(commit->object.sha1));
 		default:
 			return commit;
 		}
@@ -1633,26 +1825,6 @@
 		return c;
 	}
 
-	if (revs->reverse) {
-		int limit = -1;
-
-		if (0 <= revs->max_count) {
-			limit = revs->max_count;
-			if (0 < revs->skip_count)
-				limit += revs->skip_count;
-		}
-		l = NULL;
-		while ((c = get_revision_1(revs))) {
-			commit_list_insert(c, &l);
-			if ((0 < limit) && !--limit)
-				break;
-		}
-		revs->commits = l;
-		revs->reverse = 0;
-		revs->max_count = -1;
-		c = NULL;
-	}
-
 	/*
 	 * Now pick up what they want to give us
 	 */
@@ -1725,7 +1897,23 @@
 
 struct commit *get_revision(struct rev_info *revs)
 {
-	struct commit *c = get_revision_internal(revs);
+	struct commit *c;
+	struct commit_list *reversed;
+
+	if (revs->reverse) {
+		reversed = NULL;
+		while ((c = get_revision_internal(revs))) {
+			commit_list_insert(c, &reversed);
+		}
+		revs->commits = reversed;
+		revs->reverse = 0;
+		revs->reverse_output_stage = 1;
+	}
+
+	if (revs->reverse_output_stage)
+		return pop_commit(&revs->commits);
+
+	c = get_revision_internal(revs);
 	if (c && revs->graph)
 		graph_update(revs->graph, c);
 	return c;
diff --git a/revision.h b/revision.h
index e5b8908..be39e7d 100644
--- a/revision.h
+++ b/revision.h
@@ -42,17 +42,22 @@
 			simplify_history:1,
 			lifo:1,
 			topo_order:1,
+			simplify_merges:1,
+			simplify_by_decoration:1,
 			tag_objects:1,
 			tree_objects:1,
 			blob_objects:1,
 			edge_hint:1,
 			limited:1,
-			unpacked:1, /* see also ignore_packed below */
+			unpacked:1,
 			boundary:2,
 			left_right:1,
 			rewrite_parents:1,
 			print_parents:1,
+			show_source:1,
+			show_decorations:1,
 			reverse:1,
+			reverse_output_stage:1,
 			cherry_pick:1,
 			first_parent_only:1;
 
@@ -75,16 +80,15 @@
 			missing_newline:1;
 	enum date_mode date_mode;
 
-	const char **ignore_packed; /* pretend objects in these are unpacked */
-	int num_ignore_packed;
-
 	unsigned int	abbrev;
 	enum cmit_fmt	commit_format;
 	struct log_info *loginfo;
 	int		nr, total;
 	const char	*mime_boundary;
+	const char	*patch_suffix;
+	int		numbered_files;
 	char		*message_id;
-	const char	*ref_message_id;
+	struct string_list *ref_message_ids;
 	const char	*add_signoff;
 	const char	*extra_headers;
 	const char	*log_reencode;
@@ -110,6 +114,7 @@
 
 	struct reflog_walk_info *reflog_info;
 	struct decoration children;
+	struct decoration merge_simplification;
 };
 
 #define REV_TREE_SAME		0
diff --git a/run-command.c b/run-command.c
index bbb9c77..b05c734 100644
--- a/run-command.c
+++ b/run-command.c
@@ -111,12 +111,16 @@
 					unsetenv(*cmd->env);
 			}
 		}
+		if (cmd->preexec_cb)
+			cmd->preexec_cb();
 		if (cmd->git_cmd) {
 			execv_git_cmd(cmd->argv);
 		} else {
 			execvp(cmd->argv[0], (char *const*) cmd->argv);
 		}
-		die("exec %s failed.", cmd->argv[0]);
+		trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
+				strerror(errno));
+		exit(127);
 	}
 #else
 	int s0 = -1, s1 = -1, s2 = -1;	/* backups of stdin, stdout, stderr */
@@ -185,6 +189,7 @@
 #endif
 
 	if (cmd->pid < 0) {
+		int err = errno;
 		if (need_in)
 			close_pair(fdin);
 		else if (cmd->in)
@@ -195,7 +200,9 @@
 			close(cmd->out);
 		if (need_err)
 			close_pair(fderr);
-		return -ERR_RUN_COMMAND_FORK;
+		return err == ENOENT ?
+			-ERR_RUN_COMMAND_EXEC :
+			-ERR_RUN_COMMAND_FORK;
 	}
 
 	if (need_in)
@@ -234,9 +241,14 @@
 		if (!WIFEXITED(status))
 			return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
 		code = WEXITSTATUS(status);
-		if (code)
+		switch (code) {
+		case 127:
+			return -ERR_RUN_COMMAND_EXEC;
+		case 0:
+			return 0;
+		default:
 			return -code;
-		return 0;
+		}
 	}
 }
 
@@ -271,14 +283,6 @@
 	return run_command(&cmd);
 }
 
-int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
-{
-	struct child_process cmd;
-	prepare_run_command_v_opt(&cmd, argv, opt);
-	cmd.dir = dir;
-	return run_command(&cmd);
-}
-
 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
 {
 	struct child_process cmd;
@@ -348,3 +352,48 @@
 #endif
 	return ret;
 }
+
+int run_hook(const char *index_file, const char *name, ...)
+{
+	struct child_process hook;
+	const char **argv = NULL, *env[2];
+	char index[PATH_MAX];
+	va_list args;
+	int ret;
+	size_t i = 0, alloc = 0;
+
+	if (access(git_path("hooks/%s", name), X_OK) < 0)
+		return 0;
+
+	va_start(args, name);
+	ALLOC_GROW(argv, i + 1, alloc);
+	argv[i++] = git_path("hooks/%s", name);
+	while (argv[i-1]) {
+		ALLOC_GROW(argv, i + 1, alloc);
+		argv[i++] = va_arg(args, const char *);
+	}
+	va_end(args);
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	if (index_file) {
+		snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+		env[0] = index;
+		env[1] = NULL;
+		hook.env = env;
+	}
+
+	ret = start_command(&hook);
+	free(argv);
+	if (ret) {
+		warning("Could not spawn %s", argv[0]);
+		return ret;
+	}
+	ret = finish_command(&hook);
+	if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
+		warning("%s exited due to uncaught signal", argv[0]);
+
+	return ret;
+}
diff --git a/run-command.h b/run-command.h
index 5203a9e..e345502 100644
--- a/run-command.h
+++ b/run-command.h
@@ -10,6 +10,7 @@
 	ERR_RUN_COMMAND_WAITPID_SIGNAL,
 	ERR_RUN_COMMAND_WAITPID_NOEXIT,
 };
+#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
 
 struct child_process {
 	const char **argv;
@@ -42,17 +43,19 @@
 	unsigned no_stderr:1;
 	unsigned git_cmd:1; /* if this is to be git sub-command */
 	unsigned stdout_to_stderr:1;
+	void (*preexec_cb)(void);
 };
 
 int start_command(struct child_process *);
 int finish_command(struct child_process *);
 int run_command(struct child_process *);
 
+extern int run_hook(const char *index_file, const char *name, ...);
+
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_GIT_CMD	     2	/*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
 int run_command_v_opt(const char **argv, int opt);
-int run_command_v_opt_cd(const char **argv, int opt, const char *dir);
 
 /*
  * env (the environment) is to be formatted like environ: "VAR=VALUE".
diff --git a/send-pack.h b/send-pack.h
index 8ff1dc3..83d76c7 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -2,9 +2,7 @@
 #define SEND_PACK_H
 
 struct send_pack_args {
-	const char *receivepack;
 	unsigned verbose:1,
-		send_all:1,
 		send_mirror:1,
 		force_update:1,
 		use_thin_pack:1,
@@ -12,7 +10,7 @@
 };
 
 int send_pack(struct send_pack_args *args,
-	      const char *dest, struct remote *remote,
-	      int nr_heads, const char **heads);
+	      int fd[], struct child_process *conn,
+	      struct ref *remote_refs, struct extra_have_objects *extra_have);
 
 #endif
diff --git a/setup.c b/setup.c
index 78a8041..ebd60de 100644
--- a/setup.c
+++ b/setup.c
@@ -4,92 +4,6 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-static int sanitary_path_copy(char *dst, const char *src)
-{
-	char *dst0;
-
-	if (has_dos_drive_prefix(src)) {
-		*dst++ = *src++;
-		*dst++ = *src++;
-	}
-	dst0 = dst;
-
-	if (is_dir_sep(*src)) {
-		*dst++ = '/';
-		while (is_dir_sep(*src))
-			src++;
-	}
-
-	for (;;) {
-		char c = *src;
-
-		/*
-		 * A path component that begins with . could be
-		 * special:
-		 * (1) "." and ends   -- ignore and terminate.
-		 * (2) "./"           -- ignore them, eat slash and continue.
-		 * (3) ".." and ends  -- strip one and terminate.
-		 * (4) "../"          -- strip one, eat slash and continue.
-		 */
-		if (c == '.') {
-			if (!src[1]) {
-				/* (1) */
-				src++;
-			} else if (is_dir_sep(src[1])) {
-				/* (2) */
-				src += 2;
-				while (is_dir_sep(*src))
-					src++;
-				continue;
-			} else if (src[1] == '.') {
-				if (!src[2]) {
-					/* (3) */
-					src += 2;
-					goto up_one;
-				} else if (is_dir_sep(src[2])) {
-					/* (4) */
-					src += 3;
-					while (is_dir_sep(*src))
-						src++;
-					goto up_one;
-				}
-			}
-		}
-
-		/* copy up to the next '/', and eat all '/' */
-		while ((c = *src++) != '\0' && !is_dir_sep(c))
-			*dst++ = c;
-		if (is_dir_sep(c)) {
-			*dst++ = '/';
-			while (is_dir_sep(c))
-				c = *src++;
-			src--;
-		} else if (!c)
-			break;
-		continue;
-
-	up_one:
-		/*
-		 * dst0..dst is prefix portion, and dst[-1] is '/';
-		 * go up one level.
-		 */
-		dst -= 2; /* go past trailing '/' if any */
-		if (dst < dst0)
-			return -1;
-		while (1) {
-			if (dst <= dst0)
-				break;
-			c = *dst--;
-			if (c == '/') {	/* MinGW: cannot be '\\' anymore */
-				dst += 2;
-				break;
-			}
-		}
-	}
-	*dst = '\0';
-	return 0;
-}
-
 const char *prefix_path(const char *prefix, int len, const char *path)
 {
 	const char *orig = path;
@@ -101,7 +15,7 @@
 			memcpy(sanitized, prefix, len);
 		strcpy(sanitized + len, path);
 	}
-	if (sanitary_path_copy(sanitized, sanitized))
+	if (normalize_path_copy(sanitized, sanitized))
 		goto error_out;
 	if (is_absolute_path(orig)) {
 		const char *work_tree = get_git_work_tree();
@@ -456,7 +370,11 @@
 			inside_git_dir = 1;
 			if (!work_tree_env)
 				inside_work_tree = 0;
-			setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+			if (offset != len) {
+				cwd[offset] = '\0';
+				setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+			} else
+				setenv(GIT_DIR_ENVIRONMENT, ".", 1);
 			check_repository_format_gently(nongit_ok);
 			return NULL;
 		}
@@ -468,9 +386,10 @@
 				*nongit_ok = 1;
 				return NULL;
 			}
-			die("Not a git repository");
+			die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
 		}
-		chdir("..");
+		if (chdir(".."))
+			die("Cannot change to %s/..: %s", cwd, strerror(errno));
 	}
 
 	inside_git_dir = 0;
@@ -515,7 +434,7 @@
 
 	/*
 	 * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
-	 * a chmod value.
+	 * a chmod value to restrict to.
 	 */
 	switch (i) {
 	case PERM_UMASK:               /* 0 */
@@ -537,7 +456,7 @@
 	 * Mask filemode value. Others can not get write permission.
 	 * x flags for directories are handled separately.
 	 */
-	return i & 0666;
+	return -(i & 0666);
 }
 
 int check_repository_format_version(const char *var, const char *value, void *cb)
diff --git a/sha1-lookup.c b/sha1-lookup.c
index da35747..c4dc55d 100644
--- a/sha1-lookup.c
+++ b/sha1-lookup.c
@@ -1,6 +1,107 @@
 #include "cache.h"
 #include "sha1-lookup.h"
 
+static uint32_t take2(const unsigned char *sha1)
+{
+	return ((sha1[0] << 8) | sha1[1]);
+}
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ *      do {
+ *              int mi = (lo + hi) / 2;
+ *              int cmp = "entry pointed at by mi" minus "target";
+ *              if (!cmp)
+ *                      return (mi is the wanted one)
+ *              if (cmp > 0)
+ *                      hi = mi; "mi is larger than target"
+ *              else
+ *                      lo = mi+1; "mi is smaller than target"
+ *      } while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ *   above the target (it could be at the target), hi points at a
+ *   slot that is guaranteed to be above the target (it can never
+ *   be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ *   as lo, but never can be the same as hi), and check if it hits
+ *   the target.  There are three cases:
+ *
+ *    - if it is a hit, we are happy.
+ *
+ *    - if it is strictly higher than the target, we update hi with
+ *      it.
+ *
+ *    - if it is strictly lower than the target, we update lo to be
+ *      one slot after it, because we allow lo to be at the target.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied.  When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ */
+/*
+ * The table should contain "nr" elements.
+ * The sha1 of element i (between 0 and nr - 1) should be returned
+ * by "fn(i, table)".
+ */
+int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
+	     sha1_access_fn fn)
+{
+	size_t hi = nr;
+	size_t lo = 0;
+	size_t mi = 0;
+
+	if (!nr)
+		return -1;
+
+	if (nr != 1) {
+		size_t lov, hiv, miv, ofs;
+
+		for (ofs = 0; ofs < 18; ofs += 2) {
+			lov = take2(fn(0, table) + ofs);
+			hiv = take2(fn(nr - 1, table) + ofs);
+			miv = take2(sha1 + ofs);
+			if (miv < lov)
+				return -1;
+			if (hiv < miv)
+				return -1 - nr;
+			if (lov != hiv) {
+				/*
+				 * At this point miv could be equal
+				 * to hiv (but sha1 could still be higher);
+				 * the invariant of (mi < hi) should be
+				 * kept.
+				 */
+				mi = (nr - 1) * (miv - lov) / (hiv - lov);
+				if (lo <= mi && mi < hi)
+					break;
+				die("BUG: assertion failed in binary search");
+			}
+		}
+		if (18 <= ofs)
+			die("cannot happen -- lo and hi are identical");
+	}
+
+	do {
+		int cmp;
+		cmp = hashcmp(fn(mi, table), sha1);
+		if (!cmp)
+			return mi;
+		if (cmp > 0)
+			hi = mi;
+		else
+			lo = mi + 1;
+		mi = (hi + lo) / 2;
+	} while (lo < hi);
+	return -lo-1;
+}
+
 /*
  * Conventional binary search loop looks like this:
  *
diff --git a/sha1-lookup.h b/sha1-lookup.h
index 3249a81..20af285 100644
--- a/sha1-lookup.h
+++ b/sha1-lookup.h
@@ -1,6 +1,13 @@
 #ifndef SHA1_LOOKUP_H
 #define SHA1_LOOKUP_H
 
+typedef const unsigned char *sha1_access_fn(size_t index, void *table);
+
+extern int sha1_pos(const unsigned char *sha1,
+		    void *table,
+		    size_t nr,
+		    sha1_access_fn fn);
+
 extern int sha1_entry_pos(const void *table,
 			  size_t elem_size,
 			  size_t key_offset,
diff --git a/sha1_file.c b/sha1_file.c
index bcfcab3..8fe135d 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -99,7 +99,11 @@
 		pos = strchr(pos, '/');
 		if (!pos)
 			break;
-		*pos = 0;
+		while (*++pos == '/')
+			;
+		if (!*pos)
+			break;
+		*--pos = '\0';
 		if (!stat(path, &st)) {
 			/* path exists */
 			if (!S_ISDIR(st.st_mode)) {
@@ -250,7 +254,6 @@
  */
 static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
 {
-	struct stat st;
 	const char *objdir = get_object_directory();
 	struct alternate_object_database *ent;
 	struct alternate_object_database *alt;
@@ -281,7 +284,7 @@
 	ent->base[pfxlen] = ent->base[entlen-1] = 0;
 
 	/* Detect cases where alternate disappeared */
-	if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
+	if (!is_directory(ent->base)) {
 		error("object directory %s does not exist; "
 		      "check .git/objects/info/alternates.",
 		      ent->base);
@@ -394,6 +397,16 @@
 		link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
 }
 
+void foreach_alt_odb(alt_odb_fn fn, void *cb)
+{
+	struct alternate_object_database *ent;
+
+	prepare_alt_odb();
+	for (ent = alt_odb_list; ent; ent = ent->next)
+		if (fn(ent, cb))
+			return;
+}
+
 void prepare_alt_odb(void)
 {
 	const char *alt;
@@ -788,7 +801,7 @@
 	if (p->pack_fd == -1 && open_packed_git(p))
 		die("packfile %s cannot be accessed", p->pack_name);
 
-	/* Since packfiles end in a hash of their content and its
+	/* Since packfiles end in a hash of their content and it's
 	 * pointless to ask for an offset into the middle of that
 	 * hash, and the in_window function above wouldn't match
 	 * don't allow an offset too close to the end of the file.
@@ -1141,7 +1154,8 @@
 		return 0;
 }
 
-unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
+unsigned long unpack_object_header_buffer(const unsigned char *buf,
+		unsigned long len, enum object_type *type, unsigned long *sizep)
 {
 	unsigned shift;
 	unsigned char c;
@@ -1153,10 +1167,10 @@
 	size = c & 15;
 	shift = 4;
 	while (c & 0x80) {
-		if (len <= used)
+		if (len <= used || sizeof(long) * 8 <= shift) {
+			error("bad object header");
 			return 0;
-		if (sizeof(long) * 8 <= shift)
-			return 0;
+		}
 		c = buf[used++];
 		size += (c & 0x7f) << shift;
 		shift += 7;
@@ -1183,8 +1197,8 @@
 	stream->avail_out = bufsiz;
 
 	if (legacy_loose_object(map)) {
-		inflateInit(stream);
-		return inflate(stream, 0);
+		git_inflate_init(stream);
+		return git_inflate(stream, 0);
 	}
 
 
@@ -1195,7 +1209,7 @@
 	 * really worth it and we don't write it any longer.  But we
 	 * can still read it.
 	 */
-	used = unpack_object_header_gently(map, mapsize, &type, &size);
+	used = unpack_object_header_buffer(map, mapsize, &type, &size);
 	if (!used || !valid_loose_object_type[type])
 		return -1;
 	map += used;
@@ -1204,7 +1218,7 @@
 	/* Set up the stream for the rest.. */
 	stream->next_in = map;
 	stream->avail_in = mapsize;
-	inflateInit(stream);
+	git_inflate_init(stream);
 
 	/* And generate the fake traditional header */
 	stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
@@ -1241,11 +1255,11 @@
 		stream->next_out = buf + bytes;
 		stream->avail_out = size - bytes;
 		while (status == Z_OK)
-			status = inflate(stream, Z_FINISH);
+			status = git_inflate(stream, Z_FINISH);
 	}
 	buf[size] = 0;
 	if (status == Z_STREAM_END && !stream->avail_in) {
-		inflateEnd(stream);
+		git_inflate_end(stream);
 		return buf;
 	}
 
@@ -1335,17 +1349,19 @@
 	stream.next_out = delta_head;
 	stream.avail_out = sizeof(delta_head);
 
-	inflateInit(&stream);
+	git_inflate_init(&stream);
 	do {
 		in = use_pack(p, w_curs, curpos, &stream.avail_in);
 		stream.next_in = in;
-		st = inflate(&stream, Z_FINISH);
+		st = git_inflate(&stream, Z_FINISH);
 		curpos += stream.next_in - in;
 	} while ((st == Z_OK || st == Z_BUF_ERROR) &&
 		 stream.total_out < sizeof(delta_head));
-	inflateEnd(&stream);
-	if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
-		die("delta data unpack-initial failed");
+	git_inflate_end(&stream);
+	if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
+		error("delta data unpack-initial failed");
+		return 0;
+	}
 
 	/* Examine the initial part of the delta to figure out
 	 * the result size.
@@ -1386,7 +1402,7 @@
 			base_offset = (base_offset << 7) + (c & 127);
 		}
 		base_offset = delta_obj_offset - base_offset;
-		if (base_offset >= delta_obj_offset)
+		if (base_offset <= 0 || base_offset >= delta_obj_offset)
 			return 0;  /* out of bound */
 		*curpos += used;
 	} else if (type == OBJ_REF_DELTA) {
@@ -1412,15 +1428,32 @@
 	off_t base_offset;
 
 	base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
+	if (!base_offset)
+		return OBJ_BAD;
 	type = packed_object_info(p, base_offset, NULL);
+	if (type <= OBJ_NONE) {
+		struct revindex_entry *revidx;
+		const unsigned char *base_sha1;
+		revidx = find_pack_revindex(p, base_offset);
+		if (!revidx)
+			return OBJ_BAD;
+		base_sha1 = nth_packed_object_sha1(p, revidx->nr);
+		mark_bad_packed_object(p, base_sha1);
+		type = sha1_object_info(base_sha1, NULL);
+		if (type <= OBJ_NONE)
+			return OBJ_BAD;
+	}
 
 	/* We choose to only get the type of the base object and
 	 * ignore potentially corrupt pack file that expects the delta
 	 * based on a base with a wrong size.  This saves tons of
 	 * inflate() calls.
 	 */
-	if (sizep)
+	if (sizep) {
 		*sizep = get_size_from_delta(p, w_curs, curpos);
+		if (*sizep == 0)
+			type = OBJ_BAD;
+	}
 
 	return type;
 }
@@ -1442,10 +1475,11 @@
 	 * insane, so we know won't exceed what we have been given.
 	 */
 	base = use_pack(p, w_curs, *curpos, &left);
-	used = unpack_object_header_gently(base, left, &type, sizep);
-	if (!used)
-		die("object offset outside of pack file");
-	*curpos += used;
+	used = unpack_object_header_buffer(base, left, &type, sizep);
+	if (!used) {
+		type = OBJ_BAD;
+	} else
+		*curpos += used;
 
 	return type;
 }
@@ -1529,8 +1563,9 @@
 			*sizep = size;
 		break;
 	default:
-		die("pack %s contains unknown object type %d",
-		    p->pack_name, type);
+		error("unknown object type %i at offset %"PRIuMAX" in %s",
+		      type, (uintmax_t)obj_offset, p->pack_name);
+		type = OBJ_BAD;
 	}
 	unuse_pack(&w_curs);
 	return type;
@@ -1551,14 +1586,14 @@
 	stream.next_out = buffer;
 	stream.avail_out = size;
 
-	inflateInit(&stream);
+	git_inflate_init(&stream);
 	do {
 		in = use_pack(p, w_curs, curpos, &stream.avail_in);
 		stream.next_in = in;
-		st = inflate(&stream, Z_FINISH);
+		st = git_inflate(&stream, Z_FINISH);
 		curpos += stream.next_in - in;
 	} while (st == Z_OK || st == Z_BUF_ERROR);
-	inflateEnd(&stream);
+	git_inflate_end(&stream);
 	if ((st != Z_STREAM_END) || stream.total_out != size) {
 		free(buffer);
 		return NULL;
@@ -1602,11 +1637,9 @@
 	struct delta_base_cache_entry *ent = delta_base_cache + hash;
 
 	ret = ent->data;
-	if (ret && ent->p == p && ent->base_offset == base_offset)
-		goto found_cache_entry;
-	return unpack_entry(p, base_offset, type, base_size);
+	if (!ret || ent->p != p || ent->base_offset != base_offset)
+		return unpack_entry(p, base_offset, type, base_size);
 
-found_cache_entry:
 	if (!keep_cache) {
 		ent->data = NULL;
 		ent->lru.next->prev = ent->lru.prev;
@@ -1675,6 +1708,9 @@
 	delta_base_cache_lru.prev = &ent->lru;
 }
 
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+			 unsigned long *size);
+
 static void *unpack_delta_entry(struct packed_git *p,
 				struct pack_window **w_curs,
 				off_t curpos,
@@ -1703,9 +1739,12 @@
 		 * This is costly but should happen only in the presence
 		 * of a corrupted pack, and is better than failing outright.
 		 */
-		struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
-		const unsigned char *base_sha1 =
-					nth_packed_object_sha1(p, revidx->nr);
+		struct revindex_entry *revidx;
+		const unsigned char *base_sha1;
+		revidx = find_pack_revindex(p, base_offset);
+		if (!revidx)
+			return NULL;
+		base_sha1 = nth_packed_object_sha1(p, revidx->nr);
 		error("failed to read delta base object %s"
 		      " at offset %"PRIuMAX" from %s",
 		      sha1_to_hex(base_sha1), (uintmax_t)base_offset,
@@ -1734,6 +1773,8 @@
 	return result;
 }
 
+int do_check_packed_object_crc;
+
 void *unpack_entry(struct packed_git *p, off_t obj_offset,
 		   enum object_type *type, unsigned long *sizep)
 {
@@ -1741,6 +1782,20 @@
 	off_t curpos = obj_offset;
 	void *data;
 
+	if (do_check_packed_object_crc && p->index_version > 1) {
+		struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+		unsigned long len = revidx[1].offset - obj_offset;
+		if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
+			const unsigned char *sha1 =
+				nth_packed_object_sha1(p, revidx->nr);
+			error("bad packed object CRC for %s",
+			      sha1_to_hex(sha1));
+			mark_bad_packed_object(p, sha1);
+			unuse_pack(&w_curs);
+			return NULL;
+		}
+	}
+
 	*type = unpack_object_header(p, &w_curs, &curpos, sizep);
 	switch (*type) {
 	case OBJ_OFS_DELTA:
@@ -1864,25 +1919,7 @@
 	return 0;
 }
 
-int matches_pack_name(struct packed_git *p, const char *name)
-{
-	const char *last_c, *c;
-
-	if (!strcmp(p->pack_name, name))
-		return 1;
-
-	for (c = p->pack_name, last_c = c; *c;)
-		if (*c == '/')
-			last_c = ++c;
-		else
-			++c;
-	if (!strcmp(last_c, name))
-		return 1;
-
-	return 0;
-}
-
-static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
+static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
 	static struct packed_git *last_found = (void *)1;
 	struct packed_git *p;
@@ -1894,15 +1931,6 @@
 	p = (last_found == (void *)1) ? packed_git : last_found;
 
 	do {
-		if (ignore_packed) {
-			const char **ig;
-			for (ig = ignore_packed; *ig; ig++)
-				if (matches_pack_name(p, *ig))
-					break;
-			if (*ig)
-				goto next;
-		}
-
 		if (p->num_bad_objects) {
 			unsigned i;
 			for (i = 0; i < p->num_bad_objects; i++)
@@ -1973,7 +2001,7 @@
 		status = error("unable to parse %s header", sha1_to_hex(sha1));
 	else if (sizep)
 		*sizep = size;
-	inflateEnd(&stream);
+	git_inflate_end(&stream);
 	munmap(map, mapsize);
 	return status;
 }
@@ -1983,7 +2011,7 @@
 	struct pack_entry e;
 	int status;
 
-	if (!find_pack_entry(sha1, &e, NULL)) {
+	if (!find_pack_entry(sha1, &e)) {
 		/* Most likely it's a loose object. */
 		status = sha1_loose_object_info(sha1, sizep);
 		if (status >= 0)
@@ -1991,10 +2019,17 @@
 
 		/* Not a loose object; someone else may have just packed it. */
 		reprepare_packed_git();
-		if (!find_pack_entry(sha1, &e, NULL))
+		if (!find_pack_entry(sha1, &e))
 			return status;
 	}
-	return packed_object_info(e.p, e.offset, sizep);
+
+	status = packed_object_info(e.p, e.offset, sizep);
+	if (status < 0) {
+		mark_bad_packed_object(e.p, sha1);
+		status = sha1_object_info(sha1, sizep);
+	}
+
+	return status;
 }
 
 static void *read_packed_sha1(const unsigned char *sha1,
@@ -2003,7 +2038,7 @@
 	struct pack_entry e;
 	void *data;
 
-	if (!find_pack_entry(sha1, &e, NULL))
+	if (!find_pack_entry(sha1, &e))
 		return NULL;
 	data = cache_or_unpack_entry(e.p, e.offset, size, type, 1);
 	if (!data) {
@@ -2036,9 +2071,7 @@
 static int cached_object_nr, cached_object_alloc;
 
 static struct cached_object empty_tree = {
-	/* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */
-	"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60"
-	"\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04",
+	EMPTY_TREE_SHA1_BIN,
 	OBJ_TREE,
 	"",
 	0
@@ -2081,8 +2114,8 @@
 	return 0;
 }
 
-void *read_object(const unsigned char *sha1, enum object_type *type,
-		  unsigned long *size)
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+			 unsigned long *size)
 {
 	unsigned long mapsize;
 	void *map, *buf;
@@ -2170,24 +2203,28 @@
                                     const char *type, unsigned char *sha1,
                                     char *hdr, int *hdrlen)
 {
-	SHA_CTX c;
+	git_SHA_CTX c;
 
 	/* Generate the header */
 	*hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
 
 	/* Sha1.. */
-	SHA1_Init(&c);
-	SHA1_Update(&c, hdr, *hdrlen);
-	SHA1_Update(&c, buf, len);
-	SHA1_Final(sha1, &c);
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, hdr, *hdrlen);
+	git_SHA1_Update(&c, buf, len);
+	git_SHA1_Final(sha1, &c);
 }
 
 /*
- * Move the just written object into its final resting place
+ * Move the just written object into its final resting place.
+ * NEEDSWORK: this should be renamed to finalize_temp_file() as
+ * "moving" is only a part of what it does, when no patch between
+ * master to pu changes the call sites of this function.
  */
 int move_temp_to_file(const char *tmpfile, const char *filename)
 {
 	int ret = 0;
+
 	if (link(tmpfile, filename))
 		ret = errno;
 
@@ -2199,12 +2236,12 @@
 	 *
 	 * The same holds for FAT formatted media.
 	 *
-	 * When this succeeds, we just return 0. We have nothing
+	 * When this succeeds, we just return.  We have nothing
 	 * left to unlink.
 	 */
 	if (ret && ret != EEXIST) {
 		if (!rename(tmpfile, filename))
-			return 0;
+			goto out;
 		ret = errno;
 	}
 	unlink(tmpfile);
@@ -2215,6 +2252,9 @@
 		/* FIXME!!! Collision check here ? */
 	}
 
+out:
+	if (set_shared_perm(filename, (S_IFREG|0444)))
+		return error("unable to set permission to '%s'", filename);
 	return 0;
 }
 
@@ -2239,7 +2279,6 @@
 {
 	if (fsync_object_files)
 		fsync_or_die(fd, "sha1 file");
-	fchmod(fd, 0444);
 	if (close(fd) != 0)
 		die("error when closing sha1 file (%s)", strerror(errno));
 }
@@ -2404,66 +2443,36 @@
 	return 1;
 }
 
-int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed)
+int has_sha1_pack(const unsigned char *sha1)
 {
 	struct pack_entry e;
-	return find_pack_entry(sha1, &e, ignore_packed);
+	return find_pack_entry(sha1, &e);
 }
 
 int has_sha1_file(const unsigned char *sha1)
 {
 	struct pack_entry e;
 
-	if (find_pack_entry(sha1, &e, NULL))
+	if (find_pack_entry(sha1, &e))
 		return 1;
 	return has_loose_object(sha1);
 }
 
-int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+static int index_mem(unsigned char *sha1, void *buf, size_t size,
+		     int write_object, enum object_type type, const char *path)
 {
-	struct strbuf buf;
-	int ret;
-
-	strbuf_init(&buf, 0);
-	if (strbuf_read(&buf, fd, 4096) < 0) {
-		strbuf_release(&buf);
-		return -1;
-	}
-
-	if (!type)
-		type = blob_type;
-	if (write_object)
-		ret = write_sha1_file(buf.buf, buf.len, type, sha1);
-	else
-		ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
-	strbuf_release(&buf);
-
-	return ret;
-}
-
-int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-	     enum object_type type, const char *path)
-{
-	size_t size = xsize_t(st->st_size);
-	void *buf = NULL;
 	int ret, re_allocated = 0;
 
-	if (size)
-		buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-	close(fd);
-
 	if (!type)
 		type = OBJ_BLOB;
 
 	/*
 	 * Convert blobs to git internal format
 	 */
-	if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
-		struct strbuf nbuf;
-		strbuf_init(&nbuf, 0);
+	if ((type == OBJ_BLOB) && path) {
+		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(path, buf, size, &nbuf,
 		                   write_object ? safe_crlf : 0)) {
-			munmap(buf, size);
 			buf = strbuf_detach(&nbuf, &size);
 			re_allocated = 1;
 		}
@@ -2473,20 +2482,39 @@
 		ret = write_sha1_file(buf, size, typename(type), sha1);
 	else
 		ret = hash_sha1_file(buf, size, typename(type), sha1);
-	if (re_allocated) {
+	if (re_allocated)
 		free(buf);
-		return ret;
-	}
-	if (size)
+	return ret;
+}
+
+int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
+	     enum object_type type, const char *path)
+{
+	int ret;
+	size_t size = xsize_t(st->st_size);
+
+	if (!S_ISREG(st->st_mode)) {
+		struct strbuf sbuf = STRBUF_INIT;
+		if (strbuf_read(&sbuf, fd, 4096) >= 0)
+			ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
+					type, path);
+		else
+			ret = -1;
+		strbuf_release(&sbuf);
+	} else if (size) {
+		void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+		ret = index_mem(sha1, buf, size, write_object, type, path);
 		munmap(buf, size);
+	} else
+		ret = index_mem(sha1, NULL, size, write_object, type, path);
+	close(fd);
 	return ret;
 }
 
 int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
 {
 	int fd;
-	char *target;
-	size_t len;
+	struct strbuf sb = STRBUF_INIT;
 
 	switch (st->st_mode & S_IFMT) {
 	case S_IFREG:
@@ -2499,20 +2527,17 @@
 				     path);
 		break;
 	case S_IFLNK:
-		len = xsize_t(st->st_size);
-		target = xmalloc(len + 1);
-		if (readlink(path, target, len + 1) != st->st_size) {
+		if (strbuf_readlink(&sb, path, st->st_size)) {
 			char *errstr = strerror(errno);
-			free(target);
 			return error("readlink(\"%s\"): %s", path,
 			             errstr);
 		}
 		if (!write_object)
-			hash_sha1_file(target, len, blob_type, sha1);
-		else if (write_sha1_file(target, len, blob_type, sha1))
+			hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
+		else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
 			return error("%s: failed to insert into database",
 				     path);
-		free(target);
+		strbuf_release(&sb);
 		break;
 	case S_IFDIR:
 		return resolve_gitlink_ref(path, "HEAD", sha1);
diff --git a/sha1_name.c b/sha1_name.c
index c4fdade..904bcd9 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -238,8 +238,28 @@
 	return slash;
 }
 
+/*
+ * *string and *len will only be substituted, and *string returned (for
+ * later free()ing) if the string passed in is of the form @{-<n>}.
+ */
+static char *substitute_branch_name(const char **string, int *len)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int ret = interpret_branch_name(*string, &buf);
+
+	if (ret == *len) {
+		size_t size;
+		*string = strbuf_detach(&buf, &size);
+		*len = size;
+		return (char *)*string;
+	}
+
+	return NULL;
+}
+
 int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
+	char *last_branch = substitute_branch_name(&str, &len);
 	const char **p, *r;
 	int refs_found = 0;
 
@@ -248,22 +268,27 @@
 		char fullref[PATH_MAX];
 		unsigned char sha1_from_ref[20];
 		unsigned char *this_result;
+		int flag;
 
 		this_result = refs_found ? sha1_from_ref : sha1;
 		mksnpath(fullref, sizeof(fullref), *p, len, str);
-		r = resolve_ref(fullref, this_result, 1, NULL);
+		r = resolve_ref(fullref, this_result, 1, &flag);
 		if (r) {
 			if (!refs_found++)
 				*ref = xstrdup(r);
 			if (!warn_ambiguous_refs)
 				break;
-		}
+		} else if ((flag & REF_ISSYMREF) &&
+			   (len != 4 || strcmp(str, "HEAD")))
+			warning("ignoring dangling symref %s.", fullref);
 	}
+	free(last_branch);
 	return refs_found;
 }
 
 int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 {
+	char *last_branch = substitute_branch_name(&str, &len);
 	const char **p;
 	int logs_found = 0;
 
@@ -294,9 +319,12 @@
 		if (!warn_ambiguous_refs)
 			break;
 	}
+	free(last_branch);
 	return logs_found;
 }
 
+static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
 	static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
@@ -307,10 +335,10 @@
 	if (len == 40 && !get_sha1_hex(str, sha1))
 		return 0;
 
-	/* basic@{time or number} format to query ref-log */
+	/* basic@{time or number or -number} format to query ref-log */
 	reflog_len = at = 0;
 	if (len && str[len-1] == '}') {
-		for (at = 0; at < len - 1; at++) {
+		for (at = len-2; at >= 0; at--) {
 			if (str[at] == '@' && str[at+1] == '{') {
 				reflog_len = (len-1) - (at+2);
 				len = at;
@@ -324,6 +352,16 @@
 		return -1;
 
 	if (!len && reflog_len) {
+		struct strbuf buf = STRBUF_INIT;
+		int ret;
+		/* try the @{-N} syntax for n-th checkout */
+		ret = interpret_branch_name(str+at, &buf);
+		if (ret > 0) {
+			/* substitute this branch name and restart */
+			return get_sha1_1(buf.buf, buf.len, sha1);
+		} else if (ret == 0) {
+			return -1;
+		}
 		/* allow "@{...}" to mean the current branch reflog */
 		refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
 	} else if (reflog_len)
@@ -351,7 +389,10 @@
 			else
 				nth = -1;
 		}
-		if (0 <= nth)
+		if (100000000 <= nth) {
+			at_time = nth;
+			nth = -1;
+		} else if (0 <= nth)
 			at_time = 0;
 		else {
 			char *tmp = xstrndup(str + at + 2, reflog_len);
@@ -376,8 +417,6 @@
 	return 0;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
-
 static int get_parent(const char *name, int len,
 		      unsigned char *result, int idx)
 {
@@ -671,6 +710,92 @@
 	return retval;
 }
 
+struct grab_nth_branch_switch_cbdata {
+	long cnt, alloc;
+	struct strbuf *buf;
+};
+
+static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+				  const char *email, unsigned long timestamp, int tz,
+				  const char *message, void *cb_data)
+{
+	struct grab_nth_branch_switch_cbdata *cb = cb_data;
+	const char *match = NULL, *target = NULL;
+	size_t len;
+	int nth;
+
+	if (!prefixcmp(message, "checkout: moving from ")) {
+		match = message + strlen("checkout: moving from ");
+		target = strstr(match, " to ");
+	}
+
+	if (!match || !target)
+		return 0;
+
+	len = target - match;
+	nth = cb->cnt++ % cb->alloc;
+	strbuf_reset(&cb->buf[nth]);
+	strbuf_add(&cb->buf[nth], match, len);
+	return 0;
+}
+
+/*
+ * This reads "@{-N}" syntax, finds the name of the Nth previous
+ * branch we were on, and places the name of the branch in the given
+ * buf and returns the number of characters parsed if successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ */
+int interpret_branch_name(const char *name, struct strbuf *buf)
+{
+	long nth;
+	int i, retval;
+	struct grab_nth_branch_switch_cbdata cb;
+	const char *brace;
+	char *num_end;
+
+	if (name[0] != '@' || name[1] != '{' || name[2] != '-')
+		return -1;
+	brace = strchr(name, '}');
+	if (!brace)
+		return -1;
+	nth = strtol(name+3, &num_end, 10);
+	if (num_end != brace)
+		return -1;
+	if (nth <= 0)
+		return -1;
+	cb.alloc = nth;
+	cb.buf = xmalloc(nth * sizeof(struct strbuf));
+	for (i = 0; i < nth; i++)
+		strbuf_init(&cb.buf[i], 20);
+	cb.cnt = 0;
+	retval = 0;
+	for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
+	if (cb.cnt < nth) {
+		cb.cnt = 0;
+		for (i = 0; i < nth; i++)
+			strbuf_release(&cb.buf[i]);
+		for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+	}
+	if (cb.cnt < nth)
+		goto release_return;
+	i = cb.cnt % nth;
+	strbuf_reset(buf);
+	strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
+	retval = brace-name+1;
+
+release_return:
+	for (i = 0; i < nth; i++)
+		strbuf_release(&cb.buf[i]);
+	free(cb.buf);
+
+	return retval;
+}
+
 /*
  * This is like "get_sha1_basic()", except it allows "sha1 expressions",
  * notably "xyz^" for "parent of xyz"
diff --git a/shell.c b/shell.c
index e339369..b968be7 100644
--- a/shell.c
+++ b/shell.c
@@ -40,6 +40,7 @@
 } cmd_list[] = {
 	{ "git-receive-pack", do_generic_cmd },
 	{ "git-upload-pack", do_generic_cmd },
+	{ "git-upload-archive", do_generic_cmd },
 	{ "cvs", do_cvs_cmd },
 	{ NULL },
 };
diff --git a/sideband.c b/sideband.c
index cca3360..899b1ff 100644
--- a/sideband.c
+++ b/sideband.c
@@ -19,7 +19,7 @@
 
 #define FIX_SIZE 10  /* large enough for any of the above */
 
-int recv_sideband(const char *me, int in_stream, int out, int err)
+int recv_sideband(const char *me, int in_stream, int out)
 {
 	unsigned pf = strlen(PREFIX);
 	unsigned sf;
@@ -41,8 +41,7 @@
 		if (len == 0)
 			break;
 		if (len < 1) {
-			len = sprintf(buf, "%s: protocol error: no band designator\n", me);
-			safe_write(err, buf, len);
+			fprintf(stderr, "%s: protocol error: no band designator\n", me);
 			return SIDEBAND_PROTOCOL_ERROR;
 		}
 		band = buf[pf] & 0xff;
@@ -50,8 +49,8 @@
 		switch (band) {
 		case 3:
 			buf[pf] = ' ';
-			buf[pf+1+len] = '\n';
-			safe_write(err, buf, pf+1+len+1);
+			buf[pf+1+len] = '\0';
+			fprintf(stderr, "%s\n", buf);
 			return SIDEBAND_REMOTE_ERROR;
 		case 2:
 			buf[pf] = ' ';
@@ -95,12 +94,12 @@
 					memcpy(save, b + brk, sf);
 					b[brk + sf - 1] = b[brk - 1];
 					memcpy(b + brk - 1, suffix, sf);
-					safe_write(err, b, brk + sf);
+					fprintf(stderr, "%.*s", brk + sf, b);
 					memcpy(b + brk, save, sf);
 					len -= brk;
 				} else {
 					int l = brk ? brk : len;
-					safe_write(err, b, l);
+					fprintf(stderr, "%.*s", l, b);
 					len -= l;
 				}
 
@@ -112,10 +111,8 @@
 			safe_write(out, buf + pf+1, len);
 			continue;
 		default:
-			len = sprintf(buf,
-				      "%s: protocol error: bad band #%d\n",
-				      me, band);
-			safe_write(err, buf, len);
+			fprintf(stderr, "%s: protocol error: bad band #%d\n",
+				me, band);
 			return SIDEBAND_PROTOCOL_ERROR;
 		}
 	}
diff --git a/sideband.h b/sideband.h
index a84b691..d72db35 100644
--- a/sideband.h
+++ b/sideband.h
@@ -7,7 +7,7 @@
 #define DEFAULT_PACKET_MAX 1000
 #define LARGE_PACKET_MAX 65520
 
-int recv_sideband(const char *me, int in_stream, int out, int err);
+int recv_sideband(const char *me, int in_stream, int out);
 ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
 
 #endif
diff --git a/sigchain.c b/sigchain.c
new file mode 100644
index 0000000..1118b99
--- /dev/null
+++ b/sigchain.c
@@ -0,0 +1,52 @@
+#include "sigchain.h"
+#include "cache.h"
+
+#define SIGCHAIN_MAX_SIGNALS 32
+
+struct sigchain_signal {
+	sigchain_fun *old;
+	int n;
+	int alloc;
+};
+static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];
+
+static void check_signum(int sig)
+{
+	if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
+		die("BUG: signal out of range: %d", sig);
+}
+
+int sigchain_push(int sig, sigchain_fun f)
+{
+	struct sigchain_signal *s = signals + sig;
+	check_signum(sig);
+
+	ALLOC_GROW(s->old, s->n + 1, s->alloc);
+	s->old[s->n] = signal(sig, f);
+	if (s->old[s->n] == SIG_ERR)
+		return -1;
+	s->n++;
+	return 0;
+}
+
+int sigchain_pop(int sig)
+{
+	struct sigchain_signal *s = signals + sig;
+	check_signum(sig);
+	if (s->n < 1)
+		return 0;
+
+	if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
+		return -1;
+	s->n--;
+	return 0;
+}
+
+void sigchain_push_common(sigchain_fun f)
+{
+	sigchain_push(SIGINT, f);
+	sigchain_push(SIGHUP, f);
+	sigchain_push(SIGTERM, f);
+	sigchain_push(SIGQUIT, f);
+	sigchain_push(SIGPIPE, f);
+}
diff --git a/sigchain.h b/sigchain.h
new file mode 100644
index 0000000..618083b
--- /dev/null
+++ b/sigchain.h
@@ -0,0 +1,11 @@
+#ifndef SIGCHAIN_H
+#define SIGCHAIN_H
+
+typedef void (*sigchain_fun)(int);
+
+int sigchain_push(int sig, sigchain_fun f);
+int sigchain_pop(int sig);
+
+void sigchain_push_common(sigchain_fun f);
+
+#endif /* SIGCHAIN_H */
diff --git a/strbuf.c b/strbuf.c
index 720737d..a884960 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "refs.h"
 
 int prefixcmp(const char *str, const char *prefix)
 {
@@ -139,14 +140,11 @@
 
 int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
 {
-	int cmp;
-	if (a->len < b->len) {
-		cmp = memcmp(a->buf, b->buf, a->len);
-		return cmp ? cmp : -1;
-	} else {
-		cmp = memcmp(a->buf, b->buf, b->len);
-		return cmp ? cmp : a->len != b->len;
-	}
+	int len = a->len < b->len ? a->len: b->len;
+	int cmp = memcmp(a->buf, b->buf, len);
+	if (cmp)
+		return cmp;
+	return a->len < b->len ? -1: a->len != b->len;
 }
 
 void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
@@ -237,21 +235,40 @@
 	}
 }
 
+size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
+		void *context)
+{
+	struct strbuf_expand_dict_entry *e = context;
+	size_t len;
+
+	for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
+		if (!strncmp(placeholder, e->placeholder, len)) {
+			if (e->value)
+				strbuf_addstr(sb, e->value);
+			return len;
+		}
+	}
+	return 0;
+}
+
 size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
 {
 	size_t res;
+	size_t oldalloc = sb->alloc;
 
 	strbuf_grow(sb, size);
 	res = fread(sb->buf + sb->len, 1, size, f);
-	if (res > 0) {
+	if (res > 0)
 		strbuf_setlen(sb, sb->len + res);
-	}
+	else if (res < 0 && oldalloc == 0)
+		strbuf_release(sb);
 	return res;
 }
 
 ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
 {
 	size_t oldlen = sb->len;
+	size_t oldalloc = sb->alloc;
 
 	strbuf_grow(sb, hint ? hint : 8192);
 	for (;;) {
@@ -259,7 +276,10 @@
 
 		cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
 		if (cnt < 0) {
-			strbuf_setlen(sb, oldlen);
+			if (oldalloc == 0)
+				strbuf_release(sb);
+			else
+				strbuf_setlen(sb, oldlen);
 			return -1;
 		}
 		if (!cnt)
@@ -272,6 +292,36 @@
 	return sb->len - oldlen;
 }
 
+#define STRBUF_MAXLINK (2*PATH_MAX)
+
+int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
+{
+	size_t oldalloc = sb->alloc;
+
+	if (hint < 32)
+		hint = 32;
+
+	while (hint < STRBUF_MAXLINK) {
+		int len;
+
+		strbuf_grow(sb, hint);
+		len = readlink(path, sb->buf, hint);
+		if (len < 0) {
+			if (errno != ERANGE)
+				break;
+		} else if (len < hint) {
+			strbuf_setlen(sb, len);
+			return 0;
+		}
+
+		/* .. the buffer was too small - try again */
+		hint *= 2;
+	}
+	if (oldalloc == 0)
+		strbuf_release(sb);
+	return -1;
+}
+
 int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
 {
 	int ch;
@@ -308,3 +358,19 @@
 
 	return len;
 }
+
+int strbuf_branchname(struct strbuf *sb, const char *name)
+{
+	int len = strlen(name);
+	if (interpret_branch_name(name, sb) == len)
+		return 0;
+	strbuf_add(sb, name, len);
+	return len;
+}
+
+int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
+{
+	strbuf_branchname(sb, name);
+	strbuf_splice(sb, 0, 0, "refs/heads/", 11);
+	return check_ref_format(sb->buf);
+}
diff --git a/strbuf.h b/strbuf.h
index eba7ba4..9ee908a 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -111,6 +111,11 @@
 
 typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
 extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
+struct strbuf_expand_dict_entry {
+	const char *placeholder;
+	const char *value;
+};
+extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
 
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
@@ -119,10 +124,14 @@
 /* XXX: if read fails, any partial read is undone */
 extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
 extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 
 extern int strbuf_getline(struct strbuf *, FILE *, int);
 
 extern void stripspace(struct strbuf *buf, int skip_comments);
 extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
 
+extern int strbuf_branchname(struct strbuf *sb, const char *name);
+extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
+
 #endif /* STRBUF_H */
diff --git a/string-list.c b/string-list.c
index ddd83c8..1ac536e 100644
--- a/string-list.c
+++ b/string-list.c
@@ -26,10 +26,10 @@
 }
 
 /* returns -1-index if already exists */
-static int add_entry(struct string_list *list, const char *string)
+static int add_entry(int insert_at, struct string_list *list, const char *string)
 {
-	int exact_match;
-	int index = get_entry_index(list, string, &exact_match);
+	int exact_match = 0;
+	int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match);
 
 	if (exact_match)
 		return -1 - index;
@@ -53,7 +53,13 @@
 
 struct string_list_item *string_list_insert(const char *string, struct string_list *list)
 {
-	int index = add_entry(list, string);
+	return string_list_insert_at_index(-1, string, list);
+}
+
+struct string_list_item *string_list_insert_at_index(int insert_at,
+						     const char *string, struct string_list *list)
+{
+	int index = add_entry(insert_at, list, string);
 
 	if (index < 0)
 		index = -1 - index;
@@ -68,6 +74,16 @@
 	return exact_match;
 }
 
+int string_list_find_insert_index(const struct string_list *list, const char *string,
+				  int negative_existing_index)
+{
+	int exact_match;
+	int index = get_entry_index(list, string, &exact_match);
+	if (exact_match)
+		index = -1 - (negative_existing_index ? index : 0);
+	return index;
+}
+
 struct string_list_item *string_list_lookup(const char *string, struct string_list *list)
 {
 	int exact_match, i = get_entry_index(list, string, &exact_match);
@@ -76,6 +92,16 @@
 	return list->items + i;
 }
 
+int for_each_string_list(string_list_each_func_t fn,
+			 struct string_list *list, void *cb_data)
+{
+	int i, ret = 0;
+	for (i = 0; i < list->nr; i++)
+		if ((ret = fn(&list->items[i], cb_data)))
+			break;
+	return ret;
+}
+
 void string_list_clear(struct string_list *list, int free_util)
 {
 	if (list->items) {
@@ -94,6 +120,25 @@
 	list->nr = list->alloc = 0;
 }
 
+void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc)
+{
+	if (list->items) {
+		int i;
+		if (clearfunc) {
+			for (i = 0; i < list->nr; i++)
+				clearfunc(list->items[i].util, list->items[i].string);
+		}
+		if (list->strdup_strings) {
+			for (i = 0; i < list->nr; i++)
+				free(list->items[i].string);
+		}
+		free(list->items);
+	}
+	list->items = NULL;
+	list->nr = list->alloc = 0;
+}
+
+
 void print_string_list(const char *text, const struct string_list *p)
 {
 	int i;
diff --git a/string-list.h b/string-list.h
index 4d6a705..14bbc47 100644
--- a/string-list.h
+++ b/string-list.h
@@ -15,9 +15,23 @@
 void print_string_list(const char *text, const struct string_list *p);
 void string_list_clear(struct string_list *list, int free_util);
 
+/* Use this function to call a custom clear function on each util pointer */
+/* The string associated with the util pointer is passed as the second argument */
+typedef void (*string_list_clear_func_t)(void *p, const char *str);
+void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
+
+/* Use this function to iterate over each item */
+typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
+int for_each_string_list(string_list_each_func_t,
+			 struct string_list *list, void *cb_data);
+
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
+int string_list_find_insert_index(const struct string_list *list, const char *string,
+				  int negative_existing_index);
 struct string_list_item *string_list_insert(const char *string, struct string_list *list);
+struct string_list_item *string_list_insert_at_index(int insert_at,
+						     const char *string, struct string_list *list);
 struct string_list_item *string_list_lookup(const char *string, struct string_list *list);
 
 /* Use these functions only on unsorted lists: */
diff --git a/symlinks.c b/symlinks.c
index 5a5e781..1d6b35b 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -1,64 +1,306 @@
 #include "cache.h"
 
-struct pathname {
-	int len;
-	char path[PATH_MAX];
-};
-
-/* Return matching pathname prefix length, or zero if not matching */
-static inline int match_pathname(int len, const char *name, struct pathname *match)
+/*
+ * Returns the length (on a path component basis) of the longest
+ * common prefix match of 'name_a' and 'name_b'.
+ */
+static int longest_path_match(const char *name_a, int len_a,
+			      const char *name_b, int len_b,
+			      int *previous_slash)
 {
-	int match_len = match->len;
-	return (len > match_len &&
-		name[match_len] == '/' &&
-		!memcmp(name, match->path, match_len)) ? match_len : 0;
-}
+	int max_len, match_len = 0, match_len_prev = 0, i = 0;
 
-static inline void set_pathname(int len, const char *name, struct pathname *match)
-{
-	if (len < PATH_MAX) {
-		match->len = len;
-		memcpy(match->path, name, len);
-		match->path[len] = 0;
-	}
-}
-
-int has_symlink_leading_path(int len, const char *name)
-{
-	static struct pathname link, nonlink;
-	char path[PATH_MAX];
-	struct stat st;
-	char *sp;
-	int known_dir;
-
-	/*
-	 * See if the last known symlink cache matches.
-	 */
-	if (match_pathname(len, name, &link))
-		return 1;
-
-	/*
-	 * Get rid of the last known directory part
-	 */
-	known_dir = match_pathname(len, name, &nonlink);
-
-	while ((sp = strchr(name + known_dir + 1, '/')) != NULL) {
-		int thislen = sp - name ;
-		memcpy(path, name, thislen);
-		path[thislen] = 0;
-
-		if (lstat(path, &st))
-			return 0;
-		if (S_ISDIR(st.st_mode)) {
-			set_pathname(thislen, path, &nonlink);
-			known_dir = thislen;
-			continue;
+	max_len = len_a < len_b ? len_a : len_b;
+	while (i < max_len && name_a[i] == name_b[i]) {
+		if (name_a[i] == '/') {
+			match_len_prev = match_len;
+			match_len = i;
 		}
-		if (S_ISLNK(st.st_mode)) {
-			set_pathname(thislen, path, &link);
-			return 1;
+		i++;
+	}
+	/*
+	 * Is 'name_b' a substring of 'name_a', the other way around,
+	 * or is 'name_a' and 'name_b' the exact same string?
+	 */
+	if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') ||
+			     (len_a < len_b && name_b[len_a] == '/') ||
+			     (len_a == len_b))) {
+		match_len_prev = match_len;
+		match_len = i;
+	}
+	*previous_slash = match_len_prev;
+	return match_len;
+}
+
+static struct cache_def {
+	char path[PATH_MAX + 1];
+	int len;
+	int flags;
+	int track_flags;
+	int prefix_len_stat_func;
+} cache;
+
+static inline void reset_lstat_cache(void)
+{
+	cache.path[0] = '\0';
+	cache.len = 0;
+	cache.flags = 0;
+	/*
+	 * The track_flags and prefix_len_stat_func members is only
+	 * set by the safeguard rule inside lstat_cache()
+	 */
+}
+
+#define FL_DIR      (1 << 0)
+#define FL_NOENT    (1 << 1)
+#define FL_SYMLINK  (1 << 2)
+#define FL_LSTATERR (1 << 3)
+#define FL_ERR      (1 << 4)
+#define FL_FULLPATH (1 << 5)
+
+/*
+ * Check if name 'name' of length 'len' has a symlink leading
+ * component, or if the directory exists and is real, or not.
+ *
+ * To speed up the check, some information is allowed to be cached.
+ * This can be indicated by the 'track_flags' argument, which also can
+ * be used to indicate that we should check the full path.
+ *
+ * The 'prefix_len_stat_func' parameter can be used to set the length
+ * of the prefix, where the cache should use the stat() function
+ * instead of the lstat() function to test each path component.
+ */
+static int lstat_cache(const char *name, int len,
+		       int track_flags, int prefix_len_stat_func)
+{
+	int match_len, last_slash, last_slash_dir, previous_slash;
+	int match_flags, ret_flags, save_flags, max_len, ret;
+	struct stat st;
+
+	if (cache.track_flags != track_flags ||
+	    cache.prefix_len_stat_func != prefix_len_stat_func) {
+		/*
+		 * As a safeguard rule we clear the cache if the
+		 * values of track_flags and/or prefix_len_stat_func
+		 * does not match with the last supplied values.
+		 */
+		reset_lstat_cache();
+		cache.track_flags = track_flags;
+		cache.prefix_len_stat_func = prefix_len_stat_func;
+		match_len = last_slash = 0;
+	} else {
+		/*
+		 * Check to see if we have a match from the cache for
+		 * the 2 "excluding" path types.
+		 */
+		match_len = last_slash =
+			longest_path_match(name, len, cache.path, cache.len,
+					   &previous_slash);
+		match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
+		if (match_flags && match_len == cache.len)
+			return match_flags;
+		/*
+		 * If we now have match_len > 0, we would know that
+		 * the matched part will always be a directory.
+		 *
+		 * Also, if we are tracking directories and 'name' is
+		 * a substring of the cache on a path component basis,
+		 * we can return immediately.
+		 */
+		match_flags = track_flags & FL_DIR;
+		if (match_flags && len == match_len)
+			return match_flags;
+	}
+
+	/*
+	 * Okay, no match from the cache so far, so now we have to
+	 * check the rest of the path components.
+	 */
+	ret_flags = FL_DIR;
+	last_slash_dir = last_slash;
+	max_len = len < PATH_MAX ? len : PATH_MAX;
+	while (match_len < max_len) {
+		do {
+			cache.path[match_len] = name[match_len];
+			match_len++;
+		} while (match_len < max_len && name[match_len] != '/');
+		if (match_len >= max_len && !(track_flags & FL_FULLPATH))
+			break;
+		last_slash = match_len;
+		cache.path[last_slash] = '\0';
+
+		if (last_slash <= prefix_len_stat_func)
+			ret = stat(cache.path, &st);
+		else
+			ret = lstat(cache.path, &st);
+
+		if (ret) {
+			ret_flags = FL_LSTATERR;
+			if (errno == ENOENT)
+				ret_flags |= FL_NOENT;
+		} else if (S_ISDIR(st.st_mode)) {
+			last_slash_dir = last_slash;
+			continue;
+		} else if (S_ISLNK(st.st_mode)) {
+			ret_flags = FL_SYMLINK;
+		} else {
+			ret_flags = FL_ERR;
 		}
 		break;
 	}
-	return 0;
+
+	/*
+	 * At the end update the cache.  Note that max 3 different
+	 * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
+	 * for the moment!
+	 */
+	save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
+	if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
+		cache.path[last_slash] = '\0';
+		cache.len = last_slash;
+		cache.flags = save_flags;
+	} else if ((track_flags & FL_DIR) &&
+		   last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
+		/*
+		 * We have a separate test for the directory case,
+		 * since it could be that we have found a symlink or a
+		 * non-existing directory and the track_flags says
+		 * that we cannot cache this fact, so the cache would
+		 * then have been left empty in this case.
+		 *
+		 * But if we are allowed to track real directories, we
+		 * can still cache the path components before the last
+		 * one (the found symlink or non-existing component).
+		 */
+		cache.path[last_slash_dir] = '\0';
+		cache.len = last_slash_dir;
+		cache.flags = FL_DIR;
+	} else {
+		reset_lstat_cache();
+	}
+	return ret_flags;
+}
+
+/*
+ * Invalidate the given 'name' from the cache, if 'name' matches
+ * completely with the cache.
+ */
+void invalidate_lstat_cache(const char *name, int len)
+{
+	int match_len, previous_slash;
+
+	match_len = longest_path_match(name, len, cache.path, cache.len,
+				       &previous_slash);
+	if (len == match_len) {
+		if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
+			cache.path[previous_slash] = '\0';
+			cache.len = previous_slash;
+			cache.flags = FL_DIR;
+		} else
+			reset_lstat_cache();
+	}
+}
+
+/*
+ * Completely clear the contents of the cache
+ */
+void clear_lstat_cache(void)
+{
+	reset_lstat_cache();
+}
+
+#define USE_ONLY_LSTAT  0
+
+/*
+ * Return non-zero if path 'name' has a leading symlink component
+ */
+int has_symlink_leading_path(const char *name, int len)
+{
+	return lstat_cache(name, len,
+			   FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
+		FL_SYMLINK;
+}
+
+/*
+ * Return non-zero if path 'name' has a leading symlink component or
+ * if some leading path component does not exists.
+ */
+int has_symlink_or_noent_leading_path(const char *name, int len)
+{
+	return lstat_cache(name, len,
+			   FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
+		(FL_SYMLINK|FL_NOENT);
+}
+
+/*
+ * Return non-zero if all path components of 'name' exists as a
+ * directory.  If prefix_len > 0, we will test with the stat()
+ * function instead of the lstat() function for a prefix length of
+ * 'prefix_len', thus we then allow for symlinks in the prefix part as
+ * long as those points to real existing directories.
+ */
+int has_dirs_only_path(const char *name, int len, int prefix_len)
+{
+	return lstat_cache(name, len,
+			   FL_DIR|FL_FULLPATH, prefix_len) &
+		FL_DIR;
+}
+
+static struct removal_def {
+	char path[PATH_MAX];
+	int len;
+} removal;
+
+static void do_remove_scheduled_dirs(int new_len)
+{
+	while (removal.len > new_len) {
+		removal.path[removal.len] = '\0';
+		if (rmdir(removal.path))
+			break;
+		do {
+			removal.len--;
+		} while (removal.len > new_len &&
+			 removal.path[removal.len] != '/');
+	}
+	removal.len = new_len;
+	return;
+}
+
+void schedule_dir_for_removal(const char *name, int len)
+{
+	int match_len, last_slash, i, previous_slash;
+
+	match_len = last_slash = i =
+		longest_path_match(name, len, removal.path, removal.len,
+				   &previous_slash);
+	/* Find last slash inside 'name' */
+	while (i < len) {
+		if (name[i] == '/')
+			last_slash = i;
+		i++;
+	}
+
+	/*
+	 * If we are about to go down the directory tree, we check if
+	 * we must first go upwards the tree, such that we then can
+	 * remove possible empty directories as we go upwards.
+	 */
+	if (match_len < last_slash && match_len < removal.len)
+		do_remove_scheduled_dirs(match_len);
+	/*
+	 * If we go deeper down the directory tree, we only need to
+	 * save the new path components as we go down.
+	 */
+	if (match_len < last_slash) {
+		memcpy(&removal.path[match_len], &name[match_len],
+		       last_slash - match_len);
+		removal.len = last_slash;
+	}
+	return;
+}
+
+void remove_scheduled_dirs(void)
+{
+	do_remove_scheduled_dirs(0);
+	return;
 }
diff --git a/t/.gitignore b/t/.gitignore
index b27e280..7dcbb23 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1,2 +1,2 @@
-/trash directory
+/trash directory*
 /test-results
diff --git a/t/Makefile b/t/Makefile
index 0d65ced..bf816fc 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -14,7 +14,8 @@
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 TSVN = $(wildcard t91[0-9][0-9]-*.sh)
 
-all: pre-clean $(T) aggregate-results clean
+all: pre-clean
+	$(MAKE) aggregate-results-and-cleanup
 
 $(T):
 	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
@@ -23,7 +24,11 @@
 	$(RM) -r test-results
 
 clean:
-	$(RM) -r 'trash directory' test-results
+	$(RM) -r 'trash directory'.* test-results
+
+aggregate-results-and-cleanup: $(T)
+	$(MAKE) aggregate-results
+	$(MAKE) clean
 
 aggregate-results:
 	'$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
@@ -33,5 +38,7 @@
 	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
 	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
 
-.PHONY: pre-clean $(T) aggregate-results clean
-.NOTPARALLEL:
+valgrind:
+	GIT_TEST_OPTS=--valgrind $(MAKE)
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
diff --git a/t/README b/t/README
index 8f12d48..d8f6c7d 100644
--- a/t/README
+++ b/t/README
@@ -39,7 +39,8 @@
     * passed all 3 test(s)
 
 You can pass --verbose (or -v), --debug (or -d), and --immediate
-(or -i) command line argument to the test.
+(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
+appropriately before running "make".
 
 --verbose::
 	This makes the test more verbose.  Specifically, the
@@ -58,6 +59,21 @@
 	This causes additional long-running tests to be run (where
 	available), for more exhaustive testing.
 
+--valgrind::
+	Execute all Git binaries with valgrind and exit with status
+	126 on errors (just like regular tests, this will only stop
+	the test script when running under -i).  Valgrind errors
+	go to stderr, so you might want to pass the -v option, too.
+
+	Since it makes no sense to run the tests with --valgrind and
+	not see any output, this option implies --verbose.  For
+	convenience, it also implies --tee.
+
+--tee::
+	In addition to printing the test output to the terminal,
+	write it to files named 't/test-results/$TEST_NAME.out'.
+	As the names depend on the tests' file names, it is safe to
+	run the tests with this option in parallel.
 
 Skipping Tests
 --------------
@@ -212,6 +228,24 @@
    is to summarize successes and failures in the test script and
    exit with an appropriate error code.
 
+ - test_tick
+
+   Make commit and tag names consistent by setting the author and
+   committer times to defined stated.  Subsequent calls will
+   advance the times by a fixed amount.
+
+ - test_commit <message> [<filename> [<contents>]]
+
+   Creates a commit with the given message, committing the given
+   file with the given contents (default for both is to reuse the
+   message string), and adds a tag (again reusing the message
+   string as name).  Calls test_tick to make the SHA-1s
+   reproducible.
+
+ - test_merge <message> <commit-or-tag>
+
+   Merges the given rev using the given message.  Like test_commit,
+   creates a tag and calls test_tick before committing.
 
 Tips for Writing Tests
 ----------------------
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 5b5f288..773d47c 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,10 +1,16 @@
 . ./test-lib.sh
 
+remotes_git_svn=remotes/git""-svn
+git_svn_id=git""-svn-id
+
 if test -n "$NO_SVN_TESTS"
 then
-	test_expect_success 'skipping git-svn tests, NO_SVN_TESTS defined' :
+	say 'skipping git svn tests, NO_SVN_TESTS defined'
 	test_done
-	exit
+fi
+if ! test_have_prereq PERL; then
+	say 'skipping git svn tests, perl not available'
+	test_done
 fi
 
 GIT_DIR=$PWD/.git
@@ -14,9 +20,8 @@
 svn >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-svn tests, svn not found' :
+    say 'skipping git svn tests, svn not found'
     test_done
-    exit
 fi
 
 svnrepo=$PWD/svnrepo
@@ -38,9 +43,8 @@
 	else
 		err='Perl SVN libraries not found or unusable, skipping test'
 	fi
-	test_expect_success "$err" :
+	say "$err"
 	test_done
-	exit
 fi
 
 rawsvnrepo="$svnrepo"
@@ -88,7 +92,7 @@
 	mkdir "$GIT_DIR"/logs
 
 	cat > "$GIT_DIR/httpd.conf" <<EOF
-ServerName "git-svn test"
+ServerName "git svn test"
 ServerRoot "$GIT_DIR"
 DocumentRoot "$GIT_DIR"
 PidFile "$GIT_DIR/httpd.pid"
@@ -141,7 +145,6 @@
     then
         say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
         test_done
-        exit
     fi
 }
 
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index dc473df..cde659d 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -8,21 +8,33 @@
 	say "skipping test, network testing disabled by default"
 	say "(define GIT_TEST_HTTPD to enable)"
 	test_done
-	exit
 fi
 
-LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'}
+HTTPD_PARA=""
+
+case $(uname) in
+	Darwin)
+		DEFAULT_HTTPD_PATH='/usr/sbin/httpd'
+		DEFAULT_HTTPD_MODULE_PATH='/usr/libexec/apache2'
+		HTTPD_PARA="$HTTPD_PARA -DDarwin"
+	;;
+	*)
+		DEFAULT_HTTPD_PATH='/usr/sbin/apache2'
+		DEFAULT_HTTPD_MODULE_PATH='/usr/lib/apache2/modules'
+	;;
+esac
+
+LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
 LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
 
-TEST_PATH="$PWD"/../lib-httpd
+TEST_PATH="$TEST_DIRECTORY"/lib-httpd
 HTTPD_ROOT_PATH="$PWD"/httpd
 HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
 
 if ! test -x "$LIB_HTTPD_PATH"
 then
-        say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
-        test_done
-        exit
+	say "skipping test, no web server found at '$LIB_HTTPD_PATH'"
+	test_done
 fi
 
 HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
@@ -36,17 +48,14 @@
 		then
 			say "skipping test, at least Apache version 2 is required"
 			test_done
-			exit
 		fi
 
-		LIB_HTTPD_MODULE_PATH='/usr/lib/apache2/modules'
+		LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
 	fi
 else
 	error "Could not identify web server at '$LIB_HTTPD_PATH'"
 fi
 
-HTTPD_PARA=""
-
 prepare_httpd() {
 	mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
 
@@ -82,18 +91,23 @@
 }
 
 start_httpd() {
-	prepare_httpd
+	prepare_httpd >&3 2>&4
 
-	trap 'stop_httpd; die' exit
+	trap 'stop_httpd; die' EXIT
 
 	"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
 		-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-		-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start
+		-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
+		>&3 2>&4
+	if ! test $? = 0; then
+		say "skipping test, web server setup failed"
+		test_done
+	fi
 }
 
 stop_httpd() {
-	trap 'die' exit
+	trap 'die' EXIT
 
 	"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-		-f "$TEST_PATH/apache.conf" -k stop
+		-f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
 }
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 4717c2d..21aa42f 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -1,7 +1,13 @@
 ServerName dummy
+LockFile accept.lock
 PidFile httpd.pid
 DocumentRoot www
+LogFormat "%h %l %u %t \"%r\" %>s %b" common
+CustomLog access.log common
 ErrorLog error.log
+<IfModule !mod_log_config.c>
+	LoadModule log_config_module modules/mod_log_config.so
+</IfModule>
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
new file mode 100644
index 0000000..260a231
--- /dev/null
+++ b/t/lib-rebase.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# After setting the fake editor with this function, you can
+#
+# - override the commit message with $FAKE_COMMIT_MESSAGE,
+# - amend the commit message with $FAKE_COMMIT_AMEND
+# - check that non-commit messages have a certain line count with $EXPECT_COUNT
+# - rewrite a rebase -i script with $FAKE_LINES in the form
+#
+#	"[<lineno1>] [<lineno2>]..."
+#
+#   If a line number is prefixed with "squash" or "edit", the respective line's
+#   command will be replaced with the specified one.
+
+set_fake_editor () {
+	echo "#!$SHELL_PATH" >fake-editor.sh
+	cat >> fake-editor.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+	test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+	test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
+	exit
+	;;
+esac
+test -z "$EXPECT_COUNT" ||
+	test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+	exit
+test -z "$FAKE_LINES" && exit
+grep -v '^#' < "$1" > "$1".tmp
+rm -f "$1"
+cat "$1".tmp
+action=pick
+for line in $FAKE_LINES; do
+	case $line in
+	squash|edit)
+		action="$line";;
+	*)
+		echo sed -n "${line}s/^pick/$action/p"
+		sed -n "${line}p" < "$1".tmp
+		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+		action=pick;;
+	esac
+done
+EOF
+
+	test_set_editor "$(pwd)/fake-editor.sh"
+	chmod a+x fake-editor.sh
+}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 70df15c..f4ca4fc 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -57,6 +57,21 @@
 test_expect_failure 'pretend we have fixed a known breakage' '
     :
 '
+test_set_prereq HAVEIT
+haveit=no
+test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
+    test_have_prereq HAVEIT &&
+    haveit=yes
+'
+donthaveit=yes
+test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
+    donthaveit=no
+'
+if test $haveit$donthaveit != yesyes
+then
+	say "bug in test framework: prerequisite tags do not work reliably"
+	exit 1
+fi
 
 ################################################################
 # Basics of the basics
@@ -100,12 +115,31 @@
     'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
 
 # Various types of objects
+# Some filesystems do not support symblic links; on such systems
+# some expected values are different
 mkdir path2 path3 path3/subp3
-for p in path0 path2/file2 path3/file3 path3/subp3/file3
+paths='path0 path2/file2 path3/file3 path3/subp3/file3'
+for p in $paths
 do
     echo "hello $p" >$p
-    ln -s "hello $p" ${p}sym
 done
+if test_have_prereq SYMLINKS
+then
+	for p in $paths
+	do
+		ln -s "hello $p" ${p}sym
+	done
+	expectfilter=cat
+	expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
+	expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
+	expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
+else
+	expectfilter='grep -v sym'
+	expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
+	expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
+	expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
+fi
+
 test_expect_success \
     'adding various types of objects with git update-index --add.' \
     'find path* ! -type d -print | xargs git update-index --add'
@@ -115,7 +149,7 @@
     'showing stage with git ls-files --stage' \
     'git ls-files --stage >current'
 
-cat >expected <<\EOF
+$expectfilter >expected <<\EOF
 100644 f87290f8eb2cbbea7857214459a0739927eab154 0	path0
 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0	path0sym
 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0	path2/file2
@@ -127,14 +161,14 @@
 EOF
 test_expect_success \
     'validate git ls-files output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 test_expect_success \
     'writing tree out with git write-tree.' \
     'tree=$(git write-tree)'
 test_expect_success \
     'validate object ID for a known tree.' \
-    'test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b'
+    'test "$tree" = "$expectedtree"'
 
 test_expect_success \
     'showing tree with git ls-tree' \
@@ -145,16 +179,16 @@
 040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
 040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
 EOF
-test_expect_success \
+test_expect_success SYMLINKS \
     'git ls-tree output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 # 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
+$expectfilter >expected <<\EOF
 100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
@@ -166,7 +200,7 @@
 EOF
 test_expect_success \
     'git ls-tree -r output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 # But with -r -t we can have both.
 test_expect_success \
@@ -185,23 +219,23 @@
 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
 EOF
-test_expect_success \
+test_expect_success SYMLINKS \
     'git ls-tree -r output for a known tree.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 test_expect_success \
     'writing partial tree out with git write-tree --prefix.' \
     'ptree=$(git write-tree --prefix=path3)'
 test_expect_success \
     'validate object ID for a known tree.' \
-    'test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3'
+    'test "$ptree" = "$expectedptree1"'
 
 test_expect_success \
     'writing partial tree out with git write-tree --prefix.' \
     'ptree=$(git write-tree --prefix=path3/subp3)'
 test_expect_success \
     'validate object ID for a known tree.' \
-    'test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2'
+    'test "$ptree" = "$expectedptree2"'
 
 cat >badobjects <<EOF
 100644 blob 1000000000000000000000000000000000000000	dir/file1
@@ -234,7 +268,7 @@
      newtree=$(git write-tree) &&
      test "$newtree" = "$tree"'
 
-cat >expected <<\EOF
+$expectfilter >expected <<\EOF
 :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M	path0
 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M	path0sym
 :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M	path2/file2
@@ -257,7 +291,7 @@
     'git diff-files >current && cmp -s current /dev/null'
 
 ################################################################
-P=087704a96baf1c2d1c869a8b084481e121c88b5b
+P=$expectedtree
 test_expect_success \
     'git commit-tree records the correct tree in a commit.' \
     'commit0=$(echo NO | git commit-tree $P) &&
@@ -293,7 +327,7 @@
 	test $numpath0 = 1
 '
 
-test_expect_success 'absolute path works as expected' '
+test_expect_success SYMLINKS 'absolute path works as expected' '
 	mkdir first &&
 	ln -s ../.git first/.git &&
 	mkdir second &&
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 5ac0a27..e3d8464 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -199,4 +199,13 @@
 	x`git config -f shared-honor-global/.git/config core.sharedRepository`
 '
 
+test_expect_success 'init rejects insanely long --template' '
+	(
+		insane=$(printf "x%09999dx" 1) &&
+		mkdir test &&
+		cd test &&
+		test_must_fail git init --template=$insane
+	)
+'
+
 test_done
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 4db4ac4..cb14425 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -32,7 +32,7 @@
 		echo "git rev-parse accepted an invalid .git file"
 		false
 	fi &&
-	if ! grep -qe "Invalid gitfile format" .err
+	if ! grep "Invalid gitfile format" .err
 	then
 		echo "git rev-parse returned wrong error"
 		false
@@ -46,7 +46,7 @@
 		echo "git rev-parse accepted an invalid .git file path"
 		false
 	fi &&
-	if ! grep -qe "Not a git repository" .err
+	if ! grep "Not a git repository" .err
 	then
 		echo "git rev-parse returned wrong error"
 		false
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 3d8e06a..1c77192 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -47,6 +47,23 @@
 
 '
 
+test_expect_success 'attribute test: read paths from stdin' '
+
+	cat <<EOF > expect
+f: test: f
+a/f: test: f
+a/c/f: test: f
+a/g: test: a/g
+a/b/g: test: a/b/g
+b/g: test: unspecified
+a/b/h: test: a/b/h
+a/b/d/g: test: a/b/d/*
+EOF
+
+	sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'root subdir attribute test' '
 
 	attr_check a/i a/i &&
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
index 63e1217..2342ac5 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -15,7 +15,7 @@
 
 '
 
-test_expect_success 'write-tree should notice unwritable repository' '
+test_expect_success POSIXPERM 'write-tree should notice unwritable repository' '
 
 	(
 		chmod a-w .git/objects .git/objects/?? &&
@@ -27,7 +27,7 @@
 
 '
 
-test_expect_success 'commit should notice unwritable repository' '
+test_expect_success POSIXPERM 'commit should notice unwritable repository' '
 
 	(
 		chmod a-w .git/objects .git/objects/?? &&
@@ -39,7 +39,7 @@
 
 '
 
-test_expect_success 'update-index should notice unwritable repository' '
+test_expect_success POSIXPERM 'update-index should notice unwritable repository' '
 
 	(
 		echo 6O >file &&
@@ -52,7 +52,7 @@
 
 '
 
-test_expect_success 'add should notice unwritable repository' '
+test_expect_success POSIXPERM 'add should notice unwritable repository' '
 
 	(
 		echo b >file &&
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
new file mode 100755
index 0000000..09f855a
--- /dev/null
+++ b/t/t0005-signals.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description='signals work as we expect'
+. ./test-lib.sh
+
+cat >expect <<EOF
+three
+two
+one
+EOF
+
+test_expect_success 'sigchain works' '
+	test-sigchain >actual
+	case "$?" in
+	143) true ;; # POSIX w/ SIGTERM=15
+	  3) true ;; # Windows
+	  *) false ;;
+	esac &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 1be7446..4e72b53 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -429,6 +429,37 @@
 	}
 '
 
+test_expect_success 'checkout with existing .gitattributes' '
+
+	git config core.autocrlf true &&
+	git config --unset core.safecrlf &&
+	echo ".file2 -crlfQ" | q_to_cr >> .gitattributes &&
+	git add .gitattributes &&
+	git commit -m initial &&
+	echo ".file -crlfQ" | q_to_cr >> .gitattributes &&
+	echo "contents" > .file &&
+	git add .gitattributes .file &&
+	git commit -m second &&
+
+	git checkout master~1 &&
+	git checkout master &&
+	test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'checkout when deleting .gitattributes' '
+
+	git rm .gitattributes &&
+	echo "contentsQ" | q_to_cr > .file2 &&
+	git add .file2 &&
+	git commit -m third
+
+	git checkout master~1 &&
+	git checkout master &&
+	remove_cr .file2 >/dev/null
+
+'
+
 test_expect_success 'invalid .gitattributes (must not crash)' '
 
 	echo "three +crlf" >>.gitattributes &&
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
index 7d1ce2d..f1e1d48 100755
--- a/t/t0022-crlf-rename.sh
+++ b/t/t0022-crlf-rename.sh
Binary files differ
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index e533039..c7d0324 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -28,12 +28,12 @@
 
 "$UNZIP" -v >/dev/null 2>&1
 if [ $? -eq 127 ]; then
-	echo "Skipping ZIP test, because unzip was not found"
-	test_done
-	exit
+	say "Skipping ZIP test, because unzip was not found"
+else
+	test_set_prereq UNZIP
 fi
 
-test_expect_success 'zip archive' '
+test_expect_success UNZIP 'zip archive' '
 
 	git archive --format=zip HEAD >test.zip &&
 
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 7edf49d..89282cc 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -8,7 +8,9 @@
 aumlcdiar=`printf '\x61\xcc\x88'`
 
 case_insensitive=
-test_expect_success 'see if we expect ' '
+unibad=
+no_symlinks=
+test_expect_success 'see what we expect' '
 
 	test_case=test_expect_success
 	test_unicode=test_expect_success
@@ -19,7 +21,6 @@
 	then
 		test_case=test_expect_failure
 		case_insensitive=t
-		say "will test on a case insensitive filesystem"
 	fi &&
 	rm -fr junk &&
 	mkdir junk &&
@@ -27,13 +28,26 @@
 	case "$(cd junk && echo *)" in
 	"$aumlcdiar")
 		test_unicode=test_expect_failure
-		say "will test on a unicode corrupting filesystem"
+		unibad=t
 		;;
 	*)	;;
 	esac &&
-	rm -fr junk
+	rm -fr junk &&
+	{
+		ln -s x y 2> /dev/null &&
+		test -h y 2> /dev/null ||
+		no_symlinks=1
+		rm -f y
+	}
 '
 
+test "$case_insensitive" &&
+	say "will test on a case insensitive filesystem"
+test "$unibad" &&
+	say "will test on a unicode corrupting filesystem"
+test "$no_symlinks" &&
+	say "will test on a filesystem lacking symbolic links"
+
 if test "$case_insensitive"
 then
 test_expect_success "detection of case insensitive filesystem during repo init" '
@@ -48,6 +62,21 @@
 '
 fi
 
+if test "$no_symlinks"
+then
+test_expect_success "detection of filesystem w/o symlink support during repo init" '
+
+	v=$(git config --bool core.symlinks) &&
+	test "$v" = false
+'
+else
+test_expect_success "detection of filesystem w/o symlink support during repo init" '
+
+	test_must_fail git config --bool core.symlinks ||
+	test "$(git config --bool core.symlinks)" = true
+'
+fi
+
 test_expect_success "setup case tests" '
 
 	git config core.ignorecase true &&
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
new file mode 100755
index 0000000..0c6ff56
--- /dev/null
+++ b/t/t0055-beyond-symlinks.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='update-index and add refuse to add beyond symlinks'
+
+. ./test-lib.sh
+
+test_expect_success SYMLINKS setup '
+	>a &&
+	mkdir b &&
+	ln -s b c &&
+	>c/d &&
+	git update-index --add a b/d
+'
+
+test_expect_success SYMLINKS 'update-index --add beyond symlinks' '
+	test_must_fail git update-index --add c/d &&
+	! ( git ls-files | grep c/d )
+'
+
+test_expect_success SYMLINKS 'add beyond symlinks' '
+	test_must_fail git add c/d &&
+	! ( git ls-files | grep c/d )
+'
+
+test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 6e7501f..53cf1f8 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -7,40 +7,91 @@
 
 . ./test-lib.sh
 
-norm_abs() {
-	test_expect_success "normalize absolute" \
-	"test \$(test-path-utils normalize_absolute_path '$1') = '$2'"
+norm_path() {
+	test_expect_success $3 "normalize path: $1 => $2" \
+	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
 }
 
+# On Windows, we are using MSYS's bash, which mangles the paths.
+# Absolute paths are anchored at the MSYS installation directory,
+# which means that the path / accounts for this many characters:
+rootoff=$(test-path-utils normalize_path_copy / | wc -c)
+# Account for the trailing LF:
+if test $rootoff = 2; then
+	rootoff=	# we are on Unix
+else
+	rootoff=$(($rootoff-1))
+fi
+
 ancestor() {
-	test_expect_success "longest ancestor" \
-	"test \$(test-path-utils longest_ancestor_length '$1' '$2') = '$3'"
+	# We do some math with the expected ancestor length.
+	expected=$3
+	if test -n "$rootoff" && test "x$expected" != x-1; then
+		expected=$(($expected+$rootoff))
+	fi
+	test_expect_success "longest ancestor: $1 $2 => $expected" \
+	"actual=\$(test-path-utils longest_ancestor_length '$1' '$2') &&
+	 test \"\$actual\" = '$expected'"
 }
 
-norm_abs "" /
-norm_abs / /
-norm_abs // /
-norm_abs /// /
-norm_abs /. /
-norm_abs /./ /
-norm_abs /./.. /
-norm_abs /../. /
-norm_abs /./../.// /
-norm_abs /dir/.. /
-norm_abs /dir/sub/../.. /
-norm_abs /dir /dir
-norm_abs /dir// /dir
-norm_abs /./dir /dir
-norm_abs /dir/. /dir
-norm_abs /dir///./ /dir
-norm_abs /dir//sub/.. /dir
-norm_abs /dir/sub/../ /dir
-norm_abs //dir/sub/../. /dir
-norm_abs /dir/s1/../s2/ /dir/s2
-norm_abs /d1/s1///s2/..//../s3/ /d1/s3
-norm_abs /d1/s1//../s2/../../d2 /d2
-norm_abs /d1/.../d2 /d1/.../d2
-norm_abs /d1/..././../d2 /d1/d2
+# Absolute path tests must be skipped on Windows because due to path mangling
+# the test program never sees a POSIX-style absolute path
+case $(uname -s) in
+*MINGW*)
+	;;
+*)
+	test_set_prereq POSIX
+	;;
+esac
+
+norm_path "" ""
+norm_path . ""
+norm_path ./ ""
+norm_path ./. ""
+norm_path ./.. ++failed++
+norm_path ../. ++failed++
+norm_path ./../.// ++failed++
+norm_path dir/.. ""
+norm_path dir/sub/../.. ""
+norm_path dir/sub/../../.. ++failed++
+norm_path dir dir
+norm_path dir// dir/
+norm_path ./dir dir
+norm_path dir/. dir/
+norm_path dir///./ dir/
+norm_path dir//sub/.. dir/
+norm_path dir/sub/../ dir/
+norm_path dir/sub/../. dir/
+norm_path dir/s1/../s2/ dir/s2/
+norm_path d1/s1///s2/..//../s3/ d1/s3/
+norm_path d1/s1//../s2/../../d2 d2
+norm_path d1/.../d2 d1/.../d2
+norm_path d1/..././../d2 d1/d2
+
+norm_path / / POSIX
+norm_path // / POSIX
+norm_path /// / POSIX
+norm_path /. / POSIX
+norm_path /./ / POSIX
+norm_path /./.. ++failed++ POSIX
+norm_path /../. ++failed++ POSIX
+norm_path /./../.// ++failed++ POSIX
+norm_path /dir/.. / POSIX
+norm_path /dir/sub/../.. / POSIX
+norm_path /dir/sub/../../.. ++failed++ POSIX
+norm_path /dir /dir POSIX
+norm_path /dir// /dir/ POSIX
+norm_path /./dir /dir POSIX
+norm_path /dir/. /dir/ POSIX
+norm_path /dir///./ /dir/ POSIX
+norm_path /dir//sub/.. /dir/ POSIX
+norm_path /dir/sub/../ /dir/ POSIX
+norm_path //dir/sub/../. /dir/ POSIX
+norm_path /dir/s1/../s2/ /dir/s2/ POSIX
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
+norm_path /d1/s1//../s2/../../d2 /d2 POSIX
+norm_path /d1/.../d2 /d1/.../d2 POSIX
+norm_path /d1/..././../d2 /d1/d2 POSIX
 
 ancestor / "" -1
 ancestor / / -1
@@ -79,9 +130,13 @@
 ancestor /foo/bar /foo:/:/bar/ 4
 ancestor /foo/bar /:/bar/:/fo 0
 ancestor /foo/bar /:/bar/ 0
-ancestor /foo/bar :://foo/. 4
-ancestor /foo/bar :://foo/.:: 4
-ancestor /foo/bar //foo/./::/bar 4
-ancestor /foo/bar ::/bar -1
+ancestor /foo/bar .:/foo/. 4
+ancestor /foo/bar .:/foo/.:.: 4
+ancestor /foo/bar /foo/./:.:/bar 4
+ancestor /foo/bar .:/bar -1
 
+test_expect_success 'strip_path_suffix' '
+	test c:/msysgit = $(test-path-utils strip_path_suffix \
+		c:/msysgit/libexec//git-core libexec/git-core)
+'
 test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
new file mode 100755
index 0000000..680d7d6
--- /dev/null
+++ b/t/t0070-fundamental.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_description='check that the most basic functions work
+
+
+Verify wrappers and compatibility functions.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'character classes (isspace, isalpha etc.)' '
+	test-ctype
+'
+
+test_done
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
new file mode 100755
index 0000000..315b9b3
--- /dev/null
+++ b/t/t0100-previous.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='previous branch syntax @{-n}'
+
+. ./test-lib.sh
+
+test_expect_success 'branch -d @{-1}' '
+	test_commit A &&
+	git checkout -b junk &&
+	git checkout - &&
+	test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+	git branch -d @{-1} &&
+	test_must_fail git rev-parse --verify refs/heads/junk
+'
+
+test_expect_success 'branch -d @{-12} when there is not enough switches yet' '
+	git reflog expire --expire=now &&
+	git checkout -b junk2 &&
+	git checkout - &&
+	test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+	test_must_fail git branch -d @{-12} &&
+	git rev-parse --verify refs/heads/master
+'
+
+test_expect_success 'merge @{-1}' '
+	git checkout A &&
+	test_commit B &&
+	git checkout A &&
+	test_commit C &&
+	git branch -f master B &&
+	git branch -f other &&
+	git checkout other &&
+	git checkout master &&
+	git merge @{-1} &&
+	git cat-file commit HEAD | grep "Merge branch '\''other'\''"
+'
+
+test_expect_success 'merge @{-1} when there is not enough switches yet' '
+	git reflog expire --expire=now &&
+	git checkout -f master &&
+	git reset --hard B &&
+	git branch -f other C &&
+	git checkout other &&
+	git checkout master &&
+	test_must_fail git merge @{-12}
+'
+
+test_done
+
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 807fb83..22ba7a5 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -72,7 +72,7 @@
 
 '
 . ./test-lib.sh
-. ../lib-read-tree-m-3way.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 ################################################################
 # Trivial "majority when 3 stages exist" merge plus #2ALT, #3ALT
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 4b44e13..271bc4e 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -341,4 +341,55 @@
      check_cache_at DF/DF dirty &&
      :'
 
+test_expect_success \
+    'a/b (untracked) vs a case setup.' \
+    'rm -f .git/index &&
+     : >a &&
+     git update-index --add a &&
+     treeM=`git write-tree` &&
+     echo treeM $treeM &&
+     git ls-tree $treeM &&
+     git ls-files --stage >treeM.out &&
+
+     rm -f a &&
+     git update-index --remove a &&
+     mkdir a &&
+     : >a/b &&
+     treeH=`git write-tree` &&
+     echo treeH $treeH &&
+     git ls-tree $treeH'
+
+test_expect_success \
+    'a/b (untracked) vs a, plus c/d case test.' \
+    '! git read-tree -u -m "$treeH" "$treeM" &&
+     git ls-files --stage &&
+     test -f a/b'
+
+test_expect_success \
+    'a/b vs a, plus c/d case setup.' \
+    'rm -f .git/index &&
+     rm -fr a &&
+     : >a &&
+     mkdir c &&
+     : >c/d &&
+     git update-index --add a c/d &&
+     treeM=`git write-tree` &&
+     echo treeM $treeM &&
+     git ls-tree $treeM &&
+     git ls-files --stage >treeM.out &&
+
+     rm -f a &&
+     mkdir a
+     : >a/b &&
+     git update-index --add --remove a a/b &&
+     treeH=`git write-tree` &&
+     echo treeH $treeH &&
+     git ls-tree $treeH'
+
+test_expect_success \
+    'a/b vs a, plus c/d case test.' \
+    'git read-tree -u -m "$treeH" "$treeM" &&
+     git ls-files --stage | tee >treeMcheck.out &&
+     test_cmp treeM.out treeMcheck.out'
+
 test_done
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index 570d372..f19b4a2 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -157,7 +157,7 @@
 
 '
 
-test_expect_success 'funny symlink in work tree' '
+test_expect_success SYMLINKS 'funny symlink in work tree' '
 
 	git reset --hard &&
 	git checkout -b sym-b side-b &&
@@ -177,7 +177,7 @@
 
 '
 
-test_expect_success 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' '
 
 	rm -fr a b &&
 	git reset --hard &&
@@ -189,7 +189,7 @@
 '
 
 # clean-up from the above test
-chmod a+w a
+chmod a+w a 2>/dev/null
 rm -fr a b
 
 test_expect_success 'D/F setup' '
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index 076b082..fd98e44 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -49,16 +49,28 @@
 # Argument checking
 
 test_expect_success "multiple '--stdin's are rejected" '
-	test_must_fail git hash-object --stdin --stdin < example
+	echo example | test_must_fail git hash-object --stdin --stdin
 '
 
 test_expect_success "Can't use --stdin and --stdin-paths together" '
-	test_must_fail git hash-object --stdin --stdin-paths &&
-	test_must_fail git hash-object --stdin-paths --stdin
+	echo example | test_must_fail git hash-object --stdin --stdin-paths &&
+	echo example | test_must_fail git hash-object --stdin-paths --stdin
 '
 
 test_expect_success "Can't pass filenames as arguments with --stdin-paths" '
-	test_must_fail git hash-object --stdin-paths hello < example
+	echo example | test_must_fail git hash-object --stdin-paths hello
+'
+
+test_expect_success "Can't use --path with --stdin-paths" '
+	echo example | test_must_fail git hash-object --stdin-paths --path=foo
+'
+
+test_expect_success "Can't use --stdin-paths with --no-filters" '
+	echo example | test_must_fail git hash-object --stdin-paths --no-filters
+'
+
+test_expect_success "Can't use --path with --no-filters" '
+	test_must_fail git hash-object --no-filters --path=foo
 '
 
 # Behavior
@@ -93,6 +105,42 @@
 	test "$obname1" = "$obname1new"
 '
 
+test_expect_success 'check that appropriate filter is invoke when --path is used' '
+	echo fooQ | tr Q "\\015" >file0 &&
+	cp file0 file1 &&
+	echo "file0 -crlf" >.gitattributes &&
+	echo "file1 crlf" >>.gitattributes &&
+	git config core.autocrlf true &&
+	file0_sha=$(git hash-object file0) &&
+	file1_sha=$(git hash-object file1) &&
+	test "$file0_sha" != "$file1_sha" &&
+	path1_sha=$(git hash-object --path=file1 file0) &&
+	path0_sha=$(git hash-object --path=file0 file1) &&
+	test "$file0_sha" = "$path0_sha" &&
+	test "$file1_sha" = "$path1_sha" &&
+	path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
+	path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+	test "$file0_sha" = "$path0_sha" &&
+	test "$file1_sha" = "$path1_sha" &&
+	git config --unset core.autocrlf
+'
+
+test_expect_success 'check that --no-filters option works' '
+	echo fooQ | tr Q "\\015" >file0 &&
+	cp file0 file1 &&
+	echo "file0 -crlf" >.gitattributes &&
+	echo "file1 crlf" >>.gitattributes &&
+	git config core.autocrlf true &&
+	file0_sha=$(git hash-object file0) &&
+	file1_sha=$(git hash-object file1) &&
+	test "$file0_sha" != "$file1_sha" &&
+	nofilters_file1=$(git hash-object --no-filters file1) &&
+	test "$file0_sha" = "$nofilters_file1" &&
+	nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+	test "$file0_sha" = "$nofilters_file1" &&
+	git config --unset core.autocrlf
+'
+
 pop_repo
 
 for args in "-w --stdin" "--stdin -w"; do
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
new file mode 100755
index 0000000..f9e0028
--- /dev/null
+++ b/t/t1008-read-tree-overlay.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='test multi-tree read-tree without merging'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo one >a &&
+	git add a &&
+	git commit -m initial &&
+	git tag initial &&
+	echo two >b &&
+	git add b &&
+	git commit -m second &&
+	git checkout -b side initial &&
+	echo three >a &&
+	mkdir b &&
+	echo four >b/c &&
+	git add b/c &&
+	git commit -m third
+'
+
+test_expect_success 'multi-read' '
+	git read-tree initial master side &&
+	(echo a; echo b/c) >expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
+'
+
+test_done
+
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index fc386ba..210e594 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -126,7 +126,7 @@
 	cd foo.git && git show -s HEAD
 '
 
-test_expect_success 'detection should not be fooled by a symlink' '
+test_expect_success SYMLINKS 'detection should not be fooled by a symlink' '
 	cd "$HERE" &&
 	rm -fr foo.git &&
 	git clone -s .git another &&
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index 7f7fc36..c4414ff 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -40,6 +40,6 @@
 
 test_expect_success \
     'compare commit' \
-    'diff expected commit'
+    'test_cmp expected commit'
 
 test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 11b82f4..43ea283 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -118,7 +118,14 @@
 
 test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
 
-mv .git/config2 .git/config
+cp .git/config2 .git/config
+
+test_expect_success '--replace-all missing value' '
+	test_must_fail git config --replace-all beta.haha &&
+	test_cmp .git/config2 .git/config
+'
+
+rm .git/config2
 
 test_expect_success '--replace-all' \
 	'git config --replace-all beta.haha gamma'
@@ -336,10 +343,10 @@
 	'git config --bool emptyvalue.variable > output &&
 	 cmp output expect'
 
-git config > output 2>&1
-
-test_expect_success 'no arguments, but no crash' \
-	"test $? = 129 && grep usage output"
+test_expect_success 'no arguments, but no crash' '
+	test_must_fail git config >output 2>&1 &&
+	grep usage output
+'
 
 cat > .git/config << EOF
 [a.b]
@@ -373,7 +380,7 @@
 test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
 
 test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \
-	'git config --file non-existing-config -l; test $? != 0'
+	'test_must_fail git config --file non-existing-config -l'
 
 cat > other-config << EOF
 [ein]
@@ -726,7 +733,7 @@
 
 test_expect_success '--null --get-regexp' 'cmp result expect'
 
-test_expect_success 'symlinked configuration' '
+test_expect_success SYMLINKS 'symlinked configuration' '
 
 	ln -s notyet myconfig &&
 	GIT_CONFIG=myconfig git config test.frotz nitfol &&
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 653362b..de42d21 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -26,7 +26,7 @@
 
 for u in 002 022
 do
-	test_expect_success "shared=1 does not clear bits preset by umask $u" '
+	test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
 		mkdir sub && (
 			cd sub &&
 			umask $u &&
@@ -54,7 +54,7 @@
 	test 2 = $(git config core.sharedrepository)
 '
 
-test_expect_success 'update-server-info honors core.sharedRepository' '
+test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
 	: > a1 &&
 	git add a1 &&
 	test_tick &&
@@ -85,7 +85,7 @@
 	git config core.sharedrepository "$u" &&
 	umask 0277 &&
 
-	test_expect_success "shared = $u ($y) ro" '
+	test_expect_success POSIXPERM "shared = $u ($y) ro" '
 
 		rm -f .git/info/refs &&
 		git update-server-info &&
@@ -97,7 +97,7 @@
 	'
 
 	umask 077 &&
-	test_expect_success "shared = $u ($x) rw" '
+	test_expect_success POSIXPERM "shared = $u ($x) rw" '
 
 		rm -f .git/info/refs &&
 		git update-server-info &&
@@ -111,7 +111,7 @@
 
 done
 
-test_expect_success 'git reflog expire honors core.sharedRepository' '
+test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
 	git config core.sharedRepository group &&
 	git reflog expire --all &&
 	actual="$(ls -l .git/logs/refs/heads/master)" &&
@@ -126,4 +126,45 @@
 	esac
 '
 
+test_expect_success POSIXPERM 'forced modes' '
+	mkdir -p templates/hooks &&
+	echo update-server-info >templates/hooks/post-update &&
+	chmod +x templates/hooks/post-update &&
+	echo : >random-file &&
+	mkdir new &&
+	(
+		cd new &&
+		umask 002 &&
+		git init --shared=0660 --template=../templates &&
+		>frotz &&
+		git add frotz &&
+		git commit -a -m initial &&
+		git repack
+	) &&
+	# List repository files meant to be protected; note that
+	# COMMIT_EDITMSG does not matter---0mode is not about a
+	# repository with a work tree.
+	find new/.git -type f -name COMMIT_EDITMSG -prune -o -print |
+	xargs ls -ld >actual &&
+
+	# Everything must be unaccessible to others
+	test -z "$(sed -e "/^.......---/d" actual)" &&
+
+	# All directories must have either 2770 or 770
+	test -z "$(sed -n -e "/^drwxrw[sx]---/d" -e "/^d/p" actual)" &&
+
+	# post-update hook must be 0770
+	test -z "$(sed -n -e "/post-update/{
+		/^-rwxrwx---/d
+		p
+	}" actual)" &&
+
+	# All files inside objects must be accessible by us
+	test -z "$(sed -n -e "/objects\//{
+		/^d/d
+		/^-r.-r.----/d
+		p
+	}" actual)"
+'
+
 test_done
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
index 1983076..080117c 100755
--- a/t/t1303-wacky-config.sh
+++ b/t/t1303-wacky-config.sh
@@ -10,7 +10,7 @@
 
 check() {
 	echo "$2" >expected
-	git config --get "$1" >actual
+	git config --get "$1" >actual 2>&1
 	test_cmp actual expected
 }
 
@@ -40,4 +40,11 @@
 	check "$SECTION" bar
 '
 
+LONG_VALUE=$(printf "x%01021dx a" 7)
+test_expect_success 'do not crash on special long config line' '
+	setup &&
+	git config section.key "$LONG_VALUE" &&
+	check section.key "fatal: bad config file line 2 in .git/config"
+'
+
 test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index bd58926..54ba3df 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -137,7 +137,7 @@
 EOF
 test_expect_success \
 	"verifying $m's log" \
-	"diff expect .git/logs/$m"
+	"test_cmp expect .git/logs/$m"
 rm -rf .git/$m .git/logs expect
 
 test_expect_success \
@@ -168,7 +168,7 @@
 EOF
 test_expect_success \
 	"verifying $m's log" \
-	'diff expect .git/logs/$m'
+	'test_cmp expect .git/logs/$m'
 rm -f .git/$m .git/logs/$m expect
 
 git update-ref $m $D
@@ -272,7 +272,7 @@
 EOF
 test_expect_success \
 	'git commit logged updates' \
-	"diff expect .git/logs/$m"
+	"test_cmp expect .git/logs/$m"
 unset h_TEST h_OTHER h_FIXED h_MERGED
 
 test_expect_success \
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
new file mode 100755
index 0000000..7fa5f5b
--- /dev/null
+++ b/t/t1401-symbolic-ref.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='basic symbolic-ref tests'
+. ./test-lib.sh
+
+# If the tests munging HEAD fail, they can break detection of
+# the git repo, meaning that further tests will operate on
+# the surrounding git repo instead of the trash directory.
+reset_to_sane() {
+	echo ref: refs/heads/foo >.git/HEAD
+}
+
+test_expect_success 'symbolic-ref writes HEAD' '
+	git symbolic-ref HEAD refs/heads/foo &&
+	echo ref: refs/heads/foo >expect &&
+	test_cmp expect .git/HEAD
+'
+
+test_expect_success 'symbolic-ref reads HEAD' '
+	echo refs/heads/foo >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
+	test_must_fail git symbolic-ref HEAD foo
+'
+reset_to_sane
+
+test_expect_success 'symbolic-ref refuses bare sha1' '
+	echo content >file && git add file && git commit -m one
+	test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
+'
+reset_to_sane
+
+test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 5b24f05..80af6b9 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -70,9 +70,7 @@
 	E=`git rev-parse --verify HEAD:A/B/E` &&
 	check_fsck &&
 
-	chmod +x C &&
-	( test "`git config --bool core.filemode`" != false ||
-	  echo executable >>C ) &&
+	test_chmod +x C &&
 	git add C &&
 	test_tick && git commit -m dragon &&
 	L=`git rev-parse --verify HEAD` &&
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
new file mode 100755
index 0000000..c18ed8e
--- /dev/null
+++ b/t/t1411-reflog-show.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='Test reflog display routines'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo content >file &&
+	git add file &&
+	test_tick &&
+	git commit -m one
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log -g shows reflog headers' '
+	git log -g -1 >tmp &&
+	grep ^Reflog <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'oneline reflog format' '
+	git log -g -1 --oneline >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (multiline)' '
+	git log -g -1 HEAD@{now} >tmp &&
+	grep ^Reflog <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (oneline)' '
+	git log -g -1 --oneline HEAD@{now} >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'using --date= shows reflog date (multiline)' '
+	git log -g -1 --date=raw >tmp &&
+	grep ^Reflog <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{1112911993 -0700}: commit (initial): one
+EOF
+test_expect_success 'using --date= shows reflog date (oneline)' '
+	git log -g -1 --oneline --date=raw >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
new file mode 100755
index 0000000..a22632f
--- /dev/null
+++ b/t/t1450-fsck.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='git fsck random collection of tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_commit A fileA one &&
+	git checkout HEAD^0 &&
+	test_commit B fileB two &&
+	git tag -d A B &&
+	git reflog expire --expire=now --all
+'
+
+test_expect_success 'HEAD is part of refs' '
+	test 0 = $(git fsck | wc -l)
+'
+
+test_expect_success 'loose objects borrowed from alternate are not missing' '
+	mkdir another &&
+	(
+		cd another &&
+		git init &&
+		echo ../../../.git/objects >.git/objects/info/alternates &&
+		test_commit C fileC one &&
+		git fsck >out &&
+		! grep "missing blob" out
+	)
+'
+
+# Corruption tests follow.  Make sure to remove all traces of the
+# specific corruption you test afterwards, lest a later test trip over
+# it.
+
+test_expect_success 'object with bad sha1' '
+	sha=$(echo blob | git hash-object -w --stdin) &&
+	echo $sha &&
+	old=$(echo $sha | sed "s+^..+&/+") &&
+	new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+	sha="$(dirname $new)$(basename $new)"
+	mv .git/objects/$old .git/objects/$new &&
+	git update-index --add --cacheinfo 100644 $sha foo &&
+	tree=$(git write-tree) &&
+	cmt=$(echo bogus | git commit-tree $tree) &&
+	git update-ref refs/heads/bogus $cmt &&
+	(git fsck 2>out; true) &&
+	grep "$sha.*corrupt" out &&
+	rm -f .git/objects/$new &&
+	git update-ref -d refs/heads/bogus &&
+	git read-tree -u --reset HEAD
+'
+
+test_expect_success 'branch pointing to non-commit' '
+	git rev-parse HEAD^{tree} > .git/refs/heads/invalid &&
+	git fsck 2>out &&
+	grep "not a commit" out &&
+	git update-ref -d refs/heads/invalid
+'
+
+cat > invalid-tag <<EOF
+object ffffffffffffffffffffffffffffffffffffffff
+type commit
+tag invalid
+tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+This is an invalid tag.
+EOF
+
+test_expect_failure 'tag pointing to nonexistent' '
+	tag=$(git hash-object -w --stdin < invalid-tag) &&
+	echo $tag > .git/refs/tags/invalid &&
+	git fsck --tags 2>out &&
+	cat out &&
+	grep "could not load tagged object" out &&
+	rm .git/refs/tags/invalid
+'
+
+cat > wrong-tag <<EOF
+object $(echo blob | git hash-object -w --stdin)
+type commit
+tag wrong
+tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+This is an invalid tag.
+EOF
+
+test_expect_failure 'tag pointing to something else than its type' '
+	tag=$(git hash-object -w --stdin < wrong-tag) &&
+	echo $tag > .git/refs/tags/wrong &&
+	git fsck --tags 2>out &&
+	cat out &&
+	grep "some sane error message" out &&
+	rm .git/refs/tags/wrong
+'
+
+
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 85da4ca..48ee077 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -26,21 +26,28 @@
 	"test '$1' = \"\$(git rev-parse --show-prefix)\""
 	shift
 	[ $# -eq 0 ] && return
+
+	test_expect_success "$name: git-dir" \
+	"test '$1' = \"\$(git rev-parse --git-dir)\""
+	shift
+	[ $# -eq 0 ] && return
 }
 
-# label is-bare is-inside-git is-inside-work prefix
+# label is-bare is-inside-git is-inside-work prefix git-dir
 
-test_rev_parse toplevel false false true ''
+ROOT=$(pwd)
+
+test_rev_parse toplevel false false true '' .git
 
 cd .git || exit 1
-test_rev_parse .git/ false true false ''
+test_rev_parse .git/ false true false '' .
 cd objects || exit 1
-test_rev_parse .git/objects/ false true false ''
+test_rev_parse .git/objects/ false true false '' "$ROOT/.git"
 cd ../.. || exit 1
 
 mkdir -p sub/dir || exit 1
 cd sub/dir || exit 1
-test_rev_parse subdirectory false false true sub/dir/
+test_rev_parse subdirectory false false true sub/dir/ "$ROOT/.git"
 cd ../.. || exit 1
 
 git config core.bare true
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index c039ee3..f6a6f83 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -171,7 +171,7 @@
 
 test_expect_success 'git grep' '
 	(cd repo.git/work/sub &&
-	GIT_DIR=../.. GIT_WORK_TREE=.. git grep -l changed | grep -q dir/tracked)
+	GIT_DIR=../.. GIT_WORK_TREE=.. git grep -l changed | grep dir/tracked)
 '
 
 test_done
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index 91b704a..df5ad8c 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -13,7 +13,7 @@
 	"git rev-parse --show-prefix"
 }
 
-TRASH_ROOT="$(pwd)"
+TRASH_ROOT="$PWD"
 ROOT_PARENT=$(dirname "$TRASH_ROOT")
 
 
@@ -93,13 +93,13 @@
 test_prefix subdir_ceil_at_subdi_slash "sub/dir/"
 
 
-GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub"
+GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub"
 test_fail second_of_two
 
-GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:bar"
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:/bar"
 test_fail first_of_two
 
-GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub:bar"
+GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub:/bar"
 test_fail second_of_three
 
 
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
new file mode 100755
index 0000000..d709ecf
--- /dev/null
+++ b/t/t1505-rev-parse-last.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='test @{-N} syntax'
+
+. ./test-lib.sh
+
+
+make_commit () {
+	echo "$1" > "$1" &&
+	git add "$1" &&
+	git commit -m "$1"
+}
+
+
+test_expect_success 'setup' '
+
+	make_commit 1 &&
+	git branch side &&
+	make_commit 2 &&
+	make_commit 3 &&
+	git checkout side &&
+	make_commit 4 &&
+	git merge master &&
+	git checkout master
+
+'
+
+# 1 -- 2 -- 3 master
+#  \         \
+#   \         \
+#    --- 4 --- 5 side
+#
+# and 'side' should be the last branch
+
+test_rev_equivalent () {
+
+	git rev-parse "$1" > expect &&
+	git rev-parse "$2" > output &&
+	test_cmp expect output
+
+}
+
+test_expect_success '@{-1} works' '
+	test_rev_equivalent side @{-1}
+'
+
+test_expect_success '@{-1}~2 works' '
+	test_rev_equivalent side~2 @{-1}~2
+'
+
+test_expect_success '@{-1}^2 works' '
+	test_rev_equivalent side^2 @{-1}^2
+'
+
+test_expect_success '@{-1}@{1} works' '
+	test_rev_equivalent side@{1} @{-1}@{1}
+'
+
+test_expect_success '@{-2} works' '
+	test_rev_equivalent master @{-2}
+'
+
+test_expect_success '@{-3} fails' '
+	test_must_fail git rev-parse @{-3}
+'
+
+test_done
+
+
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
index ef00753..98aa73e 100755
--- a/t/t2001-checkout-cache-clash.sh
+++ b/t/t2001-checkout-cache-clash.sh
@@ -59,10 +59,10 @@
     'git read-tree -m $tree1 && git checkout-index -f -a'
 test_debug 'show_files $tree1'
 
-ln -s path0 path1
-test_expect_success \
+test_expect_success SYMLINKS \
     'git update-index --add a symlink.' \
-    'git update-index --add path1'
+    'ln -s path0 path1 &&
+     git update-index --add path1'
 test_expect_success \
     'writing tree out with git write-tree' \
     'tree3=$(git write-tree)'
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
index 71894b3..02a4fc5 100755
--- a/t/t2003-checkout-cache-mkdir.sh
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -19,7 +19,7 @@
     echo rezrov >path1/file1 &&
     git update-index --add path0 path1/file1'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'have symlink in place where dir is expected.' \
     'rm -fr path0 path1 &&
      mkdir path2 &&
@@ -59,7 +59,7 @@
      test ! -f path1/file1'
 
 # Linus fix #1
-test_expect_success \
+test_expect_success SYMLINKS \
     'use --prefix=tmp/orary/ where tmp is a symlink' \
     'rm -fr path0 path1 path2 tmp* &&
      mkdir tmp1 tmp1/orary &&
@@ -71,7 +71,7 @@
      test -h tmp'
 
 # Linus fix #2
-test_expect_success \
+test_expect_success SYMLINKS \
     'use --prefix=tmp/orary- where tmp is a symlink' \
     'rm -fr path0 path1 path2 tmp* &&
      mkdir tmp1 &&
@@ -82,7 +82,7 @@
      test -h tmp'
 
 # Linus fix #3
-test_expect_success \
+test_expect_success SYMLINKS \
     'use --prefix=tmp- where tmp-path1 is a symlink' \
     'rm -fr path0 path1 path2 tmp* &&
      mkdir tmp1 &&
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index 39133b8..36cca14 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -194,7 +194,7 @@
  test $(cat ../$s1) = tree1asubdir/path5)
 )'
 
-test_expect_success \
+test_expect_success SYMLINKS \
 'checkout --temp symlink' '
 rm -f path* .merge_* out .git/index &&
 ln -s b a &&
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
index ed12c4d..9fa5610 100755
--- a/t/t2005-checkout-index-symlinks.sh
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -13,7 +13,7 @@
 test_expect_success \
 'preparation' '
 git config core.symlinks false &&
-l=$(echo -n file | git hash-object -t blob -w --stdin) &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
 echo "120000 $l	symlink" | git update-index --index-info'
 
 test_expect_success \
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index 0526fce..20f3343 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -6,6 +6,12 @@
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+	say "symbolic links not supported - skipping tests"
+	test_done
+fi
+
 test_expect_success setup '
 
 	mkdir frotz &&
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index 764bb0a..15ebdc2 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -10,6 +10,10 @@
 	git commit -m initial
 '
 
+test_expect_success 'checkout should not start branch from a tree' '
+	test_must_fail git checkout -b newbranch master^{tree}
+'
+
 test_expect_success 'checkout master from invalid HEAD' '
 	echo 0000000000000000000000000000000000000000 >.git/HEAD &&
 	git checkout master --
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
new file mode 100755
index 0000000..87b30a2
--- /dev/null
+++ b/t/t2012-checkout-last.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='checkout can switch to last branch'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo hello >world &&
+	git add world &&
+	git commit -m initial &&
+	git branch other &&
+	echo "hello again" >>world &&
+	git add world &&
+	git commit -m second
+'
+
+test_expect_success '"checkout -" does not work initially' '
+	test_must_fail git checkout -
+'
+
+test_expect_success 'first branch switch' '
+	git checkout other
+'
+
+test_expect_success '"checkout -" switches back' '
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" switches forth' '
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success 'detach HEAD' '
+	git checkout $(git rev-parse HEAD)
+'
+
+test_expect_success '"checkout -" attaches again' '
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success '"checkout -" detaches again' '
+	git checkout - &&
+	test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'more switches' '
+	for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
+	do
+		git checkout -b branch$i
+	done
+'
+
+more_switches () {
+	for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
+	do
+		git checkout branch$i
+	done
+}
+
+test_expect_success 'switch to the last' '
+	more_switches &&
+	git checkout @{-1} &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2"
+'
+
+test_expect_success 'switch to second from the last' '
+	more_switches &&
+	git checkout @{-2} &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3"
+'
+
+test_expect_success 'switch to third from the last' '
+	more_switches &&
+	git checkout @{-3} &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4"
+'
+
+test_expect_success 'switch to fourth from the last' '
+	more_switches &&
+	git checkout @{-4} &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5"
+'
+
+test_expect_success 'switch to twelfth from the last' '
+	more_switches &&
+	git checkout @{-12} &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
+'
+
+test_done
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
new file mode 100755
index 0000000..fda3f0a
--- /dev/null
+++ b/t/t2013-checkout-submodule.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='checkout can handle submodules'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir submodule &&
+	(cd submodule &&
+	 git init &&
+	 test_commit first) &&
+	git add submodule &&
+	test_tick &&
+	git commit -m superproject &&
+	(cd submodule &&
+	 test_commit second) &&
+	git add submodule &&
+	test_tick &&
+	git commit -m updated.superproject
+'
+
+test_expect_success '"reset <submodule>" updates the index' '
+	git update-index --refresh &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD &&
+	test_must_fail git reset HEAD^ submodule &&
+	test_must_fail git diff-files --quiet &&
+	git reset submodule &&
+	git diff-files --quiet
+'
+
+test_expect_success '"checkout <submodule>" updates the index only' '
+	git update-index --refresh &&
+	git diff-files --quiet &&
+	git diff-index --quiet --cached HEAD &&
+	git checkout HEAD^ submodule &&
+	test_must_fail git diff-files --quiet &&
+	git checkout HEAD submodule &&
+	git diff-files --quiet
+'
+
+test_done
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
index 6ef2dcf..2df3fdd 100755
--- a/t/t2100-update-cache-badpath.sh
+++ b/t/t2100-update-cache-badpath.sh
@@ -26,7 +26,12 @@
 
 mkdir path2 path3
 date >path0
-ln -s xyzzy path1
+if test_have_prereq SYMLINKS
+then
+	ln -s xyzzy path1
+else
+	date > path1
+fi
 date >path2/file2
 date >path3/file3
 
@@ -38,7 +43,12 @@
 
 mkdir path0 path1
 date >path2
-ln -s frotz path3
+if test_have_prereq SYMLINKS
+then
+	ln -s frotz path3
+else
+	date > path3
+fi
 date >path0/file0
 date >path1/file1
 
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
index f195aef..1ed44ee 100755
--- a/t/t2102-update-index-symlinks.sh
+++ b/t/t2102-update-index-symlinks.sh
@@ -13,12 +13,12 @@
 test_expect_success \
 'preparation' '
 git config core.symlinks false &&
-l=$(echo -n file | git hash-object -t blob -w --stdin) &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
 echo "120000 $l	symlink" | git update-index --index-info'
 
 test_expect_success \
 'modify the symbolic link' '
-echo -n new-file > symlink &&
+printf new-file > symlink &&
 git update-index symlink'
 
 test_expect_success \
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index cd9231c..9120750 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -12,7 +12,7 @@
 only the updates to dir/sub.
 
 Also tested are "git add -u" without limiting, and "git add -u"
-without contents changes.'
+without contents changes, and other conditions'
 
 . ./test-lib.sh
 
@@ -80,7 +80,7 @@
 
 '
 
-test_expect_success 'replace a file with a symlink' '
+test_expect_success SYMLINKS 'replace a file with a symlink' '
 
 	rm foo &&
 	ln -s top foo &&
@@ -128,4 +128,52 @@
 
 '
 
+test_expect_success 'add -u resolves unmerged paths' '
+	git reset --hard &&
+	one=$(echo 1 | git hash-object -w --stdin) &&
+	two=$(echo 2 | git hash-object -w --stdin) &&
+	three=$(echo 3 | git hash-object -w --stdin) &&
+	{
+		for path in path1 path2
+		do
+			echo "100644 $one 1	$path"
+			echo "100644 $two 2	$path"
+			echo "100644 $three 3	$path"
+		done
+		echo "100644 $one 1	path3"
+		echo "100644 $one 1	path4"
+		echo "100644 $one 3	path5"
+		echo "100644 $one 3	path6"
+	} |
+	git update-index --index-info &&
+	echo 3 >path1 &&
+	echo 2 >path3 &&
+	echo 2 >path5 &&
+	git add -u &&
+	git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
+	{
+		echo "100644 $three 0	path1"
+		echo "100644 $one 1	path3"
+		echo "100644 $one 1	path4"
+		echo "100644 $one 3	path5"
+		echo "100644 $one 3	path6"
+	} >expect &&
+	test_cmp expect actual &&
+
+	# Bonus tests.  Explicit resolving
+	git add path3 path5 &&
+	test_must_fail git add path4 &&
+	test_must_fail git add path6 &&
+	git rm path4 &&
+	git rm path6 &&
+
+	git ls-files -s "path?" >actual &&
+	{
+		echo "100644 $three 0	path1"
+		echo "100644 $two 0	path3"
+		echo "100644 $two 0	path5"
+	} >expect
+
+'
+
 test_done
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
index d24c7d9..2e8f702 100755
--- a/t/t2201-add-update-typechange.sh
+++ b/t/t2201-add-update-typechange.sh
@@ -11,7 +11,13 @@
 	_empty=$(git hash-object --stdin <xyzzy) &&
 	>yomin &&
 	>caskly &&
-	ln -s frotz nitfol &&
+	if test_have_prereq SYMLINKS; then
+		ln -s frotz nitfol &&
+		T_letter=T
+	else
+		printf %s frotz > nitfol &&
+		T_letter=M
+	fi &&
 	mkdir rezrov &&
 	>rezrov/bozbar &&
 	git add caskly xyzzy yomin nitfol rezrov/bozbar &&
@@ -29,7 +35,11 @@
 	>nitfol &&
 	# rezrov/bozbar disappears
 	rm -fr rezrov &&
-	ln -s xyzzy rezrov &&
+	if test_have_prereq SYMLINKS; then
+		ln -s xyzzy rezrov
+	else
+		printf %s xyzzy > rezrov
+	fi &&
 	# xyzzy disappears (not a submodule)
 	mkdir xyzzy &&
 	echo gnusto >xyzzy/bozbar &&
@@ -71,7 +81,7 @@
 				s/blob/000000/
 			}
 			/	nitfol/{
-				s/	nitfol/ $_z40 T&/
+				s/	nitfol/ $_z40 $T_letter&/
 				s/blob/100644/
 			}
 			/	rezrov.bozbar/{
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
new file mode 100755
index 0000000..58a3299
--- /dev/null
+++ b/t/t2203-add-intent.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='Intent to add'
+
+. ./test-lib.sh
+
+test_expect_success 'intent to add' '
+	echo hello >file &&
+	echo hello >elif &&
+	git add -N file &&
+	git add elif
+'
+
+test_expect_success 'check result of "add -N"' '
+	git ls-files -s file >actual &&
+	empty=$(git hash-object --stdin </dev/null) &&
+	echo "100644 $empty 0	file" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'intent to add is just an ordinary empty blob' '
+	git add -u &&
+	git ls-files -s file >actual &&
+	git ls-files -s elif | sed -e "s/elif/file/" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'intent to add does not clobber existing paths' '
+	git add -N file elif &&
+	empty=$(git hash-object --stdin </dev/null) &&
+	git ls-files -s >actual &&
+	! grep "$empty" actual
+'
+
+test_expect_success 'cannot commit with i-t-a entry' '
+	test_tick &&
+	git commit -a -m initial &&
+	git reset --hard &&
+
+	echo xyzzy >rezrov &&
+	echo frotz >nitfol &&
+	git add rezrov &&
+	git add -N nitfol &&
+	test_must_fail git commit
+'
+
+test_expect_success 'can commit with an unrelated i-t-a entry in index' '
+	git reset --hard &&
+	echo xyzzy >rezrov &&
+	echo frotz >nitfol &&
+	git add rezrov &&
+	git add -N nitfol &&
+	git commit -m partial rezrov
+'
+
+test_expect_success 'can "commit -a" with an i-t-a entry' '
+	git reset --hard &&
+	: >nitfol &&
+	git add -N nitfol &&
+	git commit -a -m all
+'
+
+test_done
+
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
new file mode 100755
index 0000000..3b01ad2
--- /dev/null
+++ b/t/t2300-cd-to-toplevel.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='cd_to_toplevel'
+
+. ./test-lib.sh
+
+test_cd_to_toplevel () {
+	test_expect_success $3 "$2" '
+		(
+			cd '"'$1'"' &&
+			. git-sh-setup &&
+			cd_to_toplevel &&
+			[ "$(pwd -P)" = "$TOPLEVEL" ]
+		)
+	'
+}
+
+TOPLEVEL="$(pwd -P)/repo"
+mkdir -p repo/sub/dir
+mv .git repo/
+SUBDIRECTORY_OK=1
+
+test_cd_to_toplevel repo 'at physical root'
+
+test_cd_to_toplevel repo/sub/dir 'at physical subdir'
+
+ln -s repo symrepo 2>/dev/null
+test_cd_to_toplevel symrepo 'at symbolic root' SYMLINKS
+
+ln -s repo/sub/dir subdir-link 2>/dev/null
+test_cd_to_toplevel subdir-link 'at symbolic subdir' SYMLINKS
+
+cd repo
+ln -s sub/dir internal-link 2>/dev/null
+test_cd_to_toplevel internal-link 'at internal symbolic subdir' SYMLINKS
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index bc0a351..86291e8 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -13,12 +13,18 @@
     path2/file2 - a file in a directory
     path3-junk  - a file to confuse things
     path3/file3 - a file in a directory
+    path4       - an empty directory
 '
 . ./test-lib.sh
 
 date >path0
-ln -s xyzzy path1
-mkdir path2 path3
+if test_have_prereq SYMLINKS
+then
+	ln -s xyzzy path1
+else
+	date > path1
+fi
+mkdir path2 path3 path4
 date >path2/file2
 date >path2-junk
 date >path3/file3
@@ -28,6 +34,7 @@
 cat >expected1 <<EOF
 expected1
 expected2
+expected3
 output
 path0
 path1
@@ -35,6 +42,8 @@
 path2/file2
 EOF
 sed -e 's|path2/file2|path2/|' <expected1 >expected2
+cat <expected2 >expected3
+echo path4/ >>expected2
 
 test_expect_success \
     'git ls-files --others to show output.' \
@@ -42,7 +51,7 @@
 
 test_expect_success \
     'git ls-files --others should pick up symlinks.' \
-    'diff output expected1'
+    'test_cmp expected1 output'
 
 test_expect_success \
     'git ls-files --others --directory to show output.' \
@@ -51,6 +60,14 @@
 
 test_expect_success \
     'git ls-files --others --directory should not get confused.' \
-    'diff output expected2'
+    'test_cmp expected2 output'
+
+test_expect_success \
+    'git ls-files --others --directory --no-empty-directory to show output.' \
+    'git ls-files --others --directory --no-empty-directory >output'
+
+test_expect_success \
+    '--no-empty-directory hides empty directory' \
+    'test_cmp expected3 output'
 
 test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 6a17113..c65bca8 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -147,4 +147,10 @@
 
 '
 
+test_expect_success 'negated exclude matches can override previous ones' '
+
+	git ls-files --others --exclude="a.*" --exclude="!a.1" >output &&
+	grep "^a.1" output
+'
+
 test_done
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index ec14040..95671c2 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -38,7 +38,12 @@
 . ./test-lib.sh
 
 date >path0
-ln -s xyzzy path1
+if test_have_prereq SYMLINKS
+then
+	ln -s xyzzy path1
+else
+	date > path1
+fi
 mkdir path2 path3
 date >path2/file2
 date >path3/file3
@@ -52,8 +57,14 @@
 
 rm -fr path? ;# leave path10 alone
 date >path2
-ln -s frotz path3
-ln -s nitfol path5
+if test_have_prereq SYMLINKS
+then
+	ln -s frotz path3
+	ln -s nitfol path5
+else
+	date > path3
+	date > path5
+fi
 mkdir path0 path1 path6
 date >path0/file0
 date >path1/file1
@@ -75,7 +86,7 @@
 
 test_expect_success \
     'validate git ls-files -k output.' \
-    'diff .output .expected'
+    'test_cmp .expected .output'
 
 test_expect_success \
     'git ls-files -m to show modified files.' \
@@ -91,6 +102,6 @@
 
 test_expect_success \
     'validate git ls-files -m output.' \
-    'diff .output .expected'
+    'test_cmp .expected .output'
 
 test_done
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index 6e6a254..ee60d03 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -22,9 +22,21 @@
     'setup' \
     'mkdir path2 path2/baz &&
      echo Hi >path0 &&
-     ln -s path0 path1 &&
+     if test_have_prereq SYMLINKS
+     then
+	ln -s path0 path1 &&
+	ln -s ../path1 path2/bazbo
+	make_expected () {
+		cat >expected
+	}
+     else
+	printf path0 > path1 &&
+	printf ../path1 > path2/bazbo
+	make_expected () {
+		sed -e "s/120000 /100644 /" >expected
+	}
+     fi &&
      echo Lo >path2/foo &&
-     ln -s ../path1 path2/bazbo &&
      echo Mi >path2/baz/b &&
      find path? \( -type f -o -type l \) -print |
      xargs git update-index --add &&
@@ -41,7 +53,7 @@
 test_expect_success \
     'ls-tree plain' \
     'git ls-tree $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 040000 tree X	path2
@@ -51,7 +63,7 @@
 test_expect_success \
     'ls-tree recursive' \
     'git ls-tree -r $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 100644 blob X	path2/baz/b
@@ -63,7 +75,7 @@
 test_expect_success \
     'ls-tree recursive with -t' \
     'git ls-tree -r -t $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 040000 tree X	path2
@@ -77,7 +89,7 @@
 test_expect_success \
     'ls-tree recursive with -d' \
     'git ls-tree -r -d $tree >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X	path2
 040000 tree X	path2/baz
 EOF
@@ -86,7 +98,7 @@
 test_expect_success \
     'ls-tree filtered with path' \
     'git ls-tree $tree path >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 EOF
      test_output'
 
@@ -96,7 +108,7 @@
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git ls-tree $tree path1 path0 >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 EOF
@@ -105,7 +117,7 @@
 test_expect_success \
     'ls-tree filtered with path0/' \
     'git ls-tree $tree path0/ >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 EOF
      test_output'
 
@@ -114,7 +126,7 @@
 test_expect_success \
     'ls-tree filtered with path2' \
     'git ls-tree $tree path2 >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X	path2
 EOF
      test_output'
@@ -123,7 +135,7 @@
 test_expect_success \
     'ls-tree filtered with path2/' \
     'git ls-tree $tree path2/ >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X	path2/baz
 120000 blob X	path2/bazbo
 100644 blob X	path2/foo
@@ -135,7 +147,7 @@
 test_expect_success \
     'ls-tree filtered with path2/baz' \
     'git ls-tree $tree path2/baz >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X	path2/baz
 EOF
      test_output'
@@ -143,14 +155,14 @@
 test_expect_success \
     'ls-tree filtered with path2/bak' \
     'git ls-tree $tree path2/bak >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 EOF
      test_output'
 
 test_expect_success \
     'ls-tree -t filtered with path2/bak' \
     'git ls-tree -t $tree path2/bak >current &&
-     cat >expected <<\EOF &&
+     make_expected <<\EOF &&
 040000 tree X	path2
 EOF
      test_output'
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 4dd7d12..51cb4a3 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -135,4 +135,10 @@
 EOF
      test_output'
 
+test_expect_success 'ls-tree filter is leading path match' '
+	git ls-tree $tree pa path3/a >current &&
+	>expected &&
+	test_output
+'
+
 test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 25e9971..d59a9b4 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -121,7 +121,7 @@
 	! test -f .git/refs/heads/master3
 '
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'git branch -m u v should fail when the reflog for u is a symlink' '
      git branch -l u &&
      mv .git/logs/refs/heads/u real-u &&
@@ -194,7 +194,8 @@
 
 test_expect_success 'test deleting branch without config' \
     'git branch my7 s &&
-     test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
+     sha1=$(git rev-parse my7 | cut -c 1-7) &&
+     test "$(git branch -d my7 2>&1)" = "Deleted branch my7 (was $sha1)."'
 
 test_expect_success 'test --track without .fetch entries' \
     'git branch --track my8 &&
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
new file mode 100755
index 0000000..809d1c4
--- /dev/null
+++ b/t/t3203-branch-output.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='git branch display tests'
+. ./test-lib.sh
+
+test_expect_success 'make commits' '
+	echo content >file &&
+	git add file &&
+	git commit -m one &&
+	echo content >>file &&
+	git commit -a -m two
+'
+
+test_expect_success 'make branches' '
+	git branch branch-one
+	git branch branch-two HEAD^
+'
+
+test_expect_success 'make remote branches' '
+	git update-ref refs/remotes/origin/branch-one branch-one
+	git update-ref refs/remotes/origin/branch-two branch-two
+	git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+* master
+EOF
+test_expect_success 'git branch shows local branches' '
+	git branch >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  origin/HEAD -> origin/branch-one
+  origin/branch-one
+  origin/branch-two
+EOF
+test_expect_success 'git branch -r shows remote branches' '
+	git branch -r >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+  branch-one
+  branch-two
+* master
+  remotes/origin/HEAD -> origin/branch-one
+  remotes/origin/branch-one
+  remotes/origin/branch-two
+EOF
+test_expect_success 'git branch -a shows local and remote branches' '
+	git branch -a >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+two
+one
+two
+EOF
+test_expect_success 'git branch -v shows branch summaries' '
+	git branch -v >tmp &&
+	awk "{print \$NF}" <tmp >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+* (no branch)
+  branch-one
+  branch-two
+  master
+EOF
+test_expect_success 'git branch shows detached HEAD properly' '
+	git checkout HEAD^0 &&
+	git branch >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index b7a670e..6e391a3 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -14,7 +14,8 @@
 
 test_expect_success \
     'prepare repository with topic branches' \
-    'echo First > A &&
+    'git config core.logAllRefUpdates true &&
+     echo First > A &&
      git update-index --add A &&
      git commit -m "Add A." &&
      git checkout -b my-topic-branch &&
@@ -47,6 +48,10 @@
     'the rebase operation should not have destroyed author information' \
     '! (git log | grep "Author:" | grep "<>")'
 
+test_expect_success 'HEAD was detached during rebase' '
+     test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+'
+
 test_expect_success 'rebase after merge master' '
      git reset --hard topic &&
      git merge master &&
@@ -78,10 +83,16 @@
      git checkout -b modechange HEAD^ &&
      echo 1 > X &&
      git add X &&
-     chmod a+x A &&
+     test_chmod +x A &&
      test_tick &&
-     git commit -m modechange A X &&
+     git commit -m modechange &&
      GIT_TRACE=1 git rebase master
 '
 
+test_expect_success 'Show verbose error when HEAD could not be detached' '
+     : > B &&
+     test_must_fail git rebase topic 2> output.err > output.out &&
+     grep "Untracked working tree file .B. would be overwritten" output.err
+'
+
 test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 2cc8e7a..c32ff66 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -10,6 +10,10 @@
 '
 . ./test-lib.sh
 
+. ../lib-rebase.sh
+
+set_fake_editor
+
 # set up two branches like this:
 #
 # A - B - C - D - E
@@ -61,39 +65,6 @@
 	git tag I
 '
 
-echo "#!$SHELL_PATH" >fake-editor.sh
-cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
-	test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
-	test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
-	exit
-	;;
-esac
-test -z "$EXPECT_COUNT" ||
-	test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
-	exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
-	case $line in
-	squash|edit)
-		action="$line";;
-	*)
-		echo sed -n "${line}s/^pick/$action/p"
-		sed -n "${line}p" < "$1".tmp
-		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
-		action=pick;;
-	esac
-done
-EOF
-
-test_set_editor "$(pwd)/fake-editor.sh"
-chmod a+x fake-editor.sh
-
 test_expect_success 'no changes are a nop' '
 	git rebase -i F &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
@@ -462,4 +433,41 @@
 
 '
 
+test_expect_success 'submodule rebase setup' '
+	git checkout A &&
+	mkdir sub &&
+	(
+		cd sub && git init && >elif &&
+		git add elif && git commit -m "submodule initial"
+	) &&
+	echo 1 >file1 &&
+	git add file1 sub
+	test_tick &&
+	git commit -m "One" &&
+	echo 2 >file1 &&
+	test_tick &&
+	git commit -a -m "Two" &&
+	(
+		cd sub && echo 3 >elif &&
+		git commit -a -m "submodule second"
+	) &&
+	test_tick &&
+	git commit -a -m "Three changes submodule"
+'
+
+test_expect_success 'submodule rebase -i' '
+	FAKE_LINES="1 squash 2 3" git rebase -i A
+'
+
+test_expect_success 'avoid unnecessary reset' '
+	git checkout master &&
+	test-chmtime =123456789 file3 &&
+	git update-index --refresh &&
+	HEAD=$(git rev-parse HEAD) &&
+	git rebase -i HEAD~4 &&
+	test $HEAD = $(git rev-parse HEAD) &&
+	MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
+	test 123456789 = $MTIME
+'
+
 test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 5391080..85fc7c4 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -22,7 +22,8 @@
 	git checkout topic &&
 	quick_one A &&
 	quick_one B &&
-	quick_one Z
+	quick_one Z &&
+	git tag start
 
 '
 
@@ -41,4 +42,24 @@
 
 '
 
+test_expect_success 'rebase --stat' '
+        git reset --hard start
+        git rebase --stat master >diffstat.txt &&
+        grep "^ fileX |  *1 +$" diffstat.txt
+'
+
+test_expect_success 'rebase w/config rebase.stat' '
+        git reset --hard start
+        git config rebase.stat true &&
+        git rebase master >diffstat.txt &&
+        grep "^ fileX |  *1 +$" diffstat.txt
+'
+
+test_expect_success 'rebase -n overrides config rebase.stat config' '
+        git reset --hard start
+        git config rebase.stat true &&
+        git rebase -n master >diffstat.txt &&
+        ! grep "^ fileX |  *1 +$" diffstat.txt
+'
+
 test_done
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
new file mode 100755
index 0000000..e6c8327
--- /dev/null
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright(C) 2008 Stephen Habermann & Andreas Ericsson
+#
+test_description='git rebase -p should preserve merges
+
+Run "git rebase -p" and check that merges are properly carried along
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
+
+# Clone 1 (trivial merge):
+#
+# A1--A2  <-- origin/master
+#  \   \
+#   B1--M  <-- topic
+#    \
+#     B2  <-- origin/topic
+#
+# Clone 2 (conflicting merge):
+#
+# A1--A2--B3   <-- origin/master
+#  \       \
+#   B1------M  <-- topic
+#    \
+#     B2       <-- origin/topic
+#
+# In both cases, 'topic' is rebased onto 'origin/topic'.
+
+test_expect_success 'setup for merge-preserving rebase' \
+	'echo First > A &&
+	git add A &&
+	git-commit -m "Add A1" &&
+	git checkout -b topic &&
+	echo Second > B &&
+	git add B &&
+	git-commit -m "Add B1" &&
+	git checkout -f master &&
+	echo Third >> A &&
+	git-commit -a -m "Modify A2" &&
+
+	git clone ./. clone1 &&
+	cd clone1 &&
+	git checkout -b topic origin/topic &&
+	git merge origin/master &&
+	cd .. &&
+
+	echo Fifth > B &&
+	git add B &&
+	git commit -m "Add different B" &&
+
+	git clone ./. clone2 &&
+	cd clone2 &&
+	git checkout -b topic origin/topic &&
+	test_must_fail git merge origin/master &&
+	echo Resolved > B &&
+	git add B &&
+	git commit -m "Merge origin/master into topic" &&
+	cd .. &&
+
+	git checkout topic &&
+	echo Fourth >> B &&
+	git commit -a -m "Modify B2"
+'
+
+test_expect_success 'rebase -p fakes interactive rebase' '
+	(
+	cd clone1 &&
+	git fetch &&
+	git rebase -p origin/topic &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge commit" | wc -l)
+	)
+'
+
+test_expect_success '--continue works after a conflict' '
+	(
+	cd clone2 &&
+	git fetch &&
+	test_must_fail git rebase -p origin/topic &&
+	test 2 = $(git ls-files B | wc -l) &&
+	echo Resolved again > B &&
+	test_must_fail git rebase --continue &&
+	grep "^@@@ " .git/rebase-merge/patch &&
+	git add B &&
+	git rebase --continue &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Add different" | wc -l) &&
+	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge origin" | wc -l)
+	)
+'
+
+test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
new file mode 100755
index 0000000..c49143a
--- /dev/null
+++ b/t/t3410-rebase-preserve-dropped-merges.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with preserve merges and ensures commits
+dropped by the --cherry-pick flag have their childrens parents
+rewritten.
+'
+. ./test-lib.sh
+
+# set up two branches like this:
+#
+# A - B - C - D - E
+#   \
+#     F - G - H
+#       \
+#         I
+#
+# where B, D and G touch the same file.
+
+test_expect_success 'setup' '
+	test_commit A file1 &&
+	test_commit B file1 1 &&
+	test_commit C file2 &&
+	test_commit D file1 2 &&
+	test_commit E file3 &&
+	git checkout A &&
+	test_commit F file4 &&
+	test_commit G file1 3 &&
+	test_commit H file5 &&
+	git checkout F &&
+	test_commit I file6
+'
+
+# A - B - C - D - E
+#   \             \ \
+#     F - G - H -- L \        -->   L
+#       \            |               \
+#         I -- G2 -- J -- K           I -- K
+# G2 = same changes as G
+test_expect_success 'skip same-resolution merges with -p' '
+	git checkout H &&
+	! git merge E &&
+	test_commit L file1 23 &&
+	git checkout I &&
+	test_commit G2 file1 3 &&
+	! git merge E &&
+	test_commit J file1 23 &&
+	test_commit K file7 file7 &&
+	git rebase -i -p L &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse L) &&
+	test "23" = "$(cat file1)" &&
+	test "I" = "$(cat file6)" &&
+	test "file7" = "$(cat file7)"
+'
+
+# A - B - C - D - E
+#   \             \ \
+#     F - G - H -- L2 \        -->   L2
+#       \             |                \
+#         I -- G3 --- J2 -- K2           I -- G3 -- K2
+# G2 = different changes as G
+test_expect_success 'keep different-resolution merges with -p' '
+	git checkout H &&
+	! git merge E &&
+	test_commit L2 file1 23 &&
+	git checkout I &&
+	test_commit G3 file1 4 &&
+	! git merge E &&
+	test_commit J2 file1 24 &&
+	test_commit K2 file7 file7 &&
+	test_must_fail git rebase -i -p L2 &&
+	echo 234 > file1 &&
+	git add file1 &&
+	git rebase --continue &&
+	test $(git rev-parse HEAD^^^) = $(git rev-parse L2) &&
+	test "234" = "$(cat file1)" &&
+	test "I" = "$(cat file6)" &&
+	test "file7" = "$(cat file7)"
+'
+
+test_done
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
new file mode 100755
index 0000000..6533505
--- /dev/null
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with -p and tries to squash a commit from after
+a merge to before the merge.
+'
+. ./test-lib.sh
+
+. ../lib-rebase.sh
+
+set_fake_editor
+
+# set up two branches like this:
+#
+# A1 - B1 - D1 - E1 - F1
+#       \        /
+#        -- C1 --
+
+test_expect_success 'setup' '
+	test_commit A1 &&
+	test_commit B1 &&
+	test_commit C1 &&
+	git reset --hard B1 &&
+	test_commit D1 &&
+	test_merge E1 C1 &&
+	test_commit F1
+'
+
+# Should result in:
+#
+# A1 - B1 - D2 - E2
+#       \        /
+#        -- C1 --
+#
+test_expect_success 'squash F1 into D1' '
+	FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
+	test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
+	test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
+	git tag E2
+'
+
+# Start with:
+#
+# A1 - B1 - D2 - E2
+#  \
+#   G1 ---- L1 ---- M1
+#    \             /
+#     H1 -- J1 -- K1
+#      \         /
+#        -- I1 --
+#
+# And rebase G1..M1 onto E2
+
+test_expect_success 'rebase two levels of merge' '
+	test_commit G1 &&
+	test_commit H1 &&
+	test_commit I1 &&
+	git checkout -b branch3 H1 &&
+	test_commit J1 &&
+	test_merge K1 I1 &&
+	git checkout -b branch2 G1 &&
+	test_commit L1 &&
+	test_merge M1 K1 &&
+	GIT_EDITOR=: git rebase -i -p E2 &&
+	test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
+	test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
+	test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse HEAD^2^2^1)"
+'
+
+test_done
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
new file mode 100755
index 0000000..5869061
--- /dev/null
+++ b/t/t3412-rebase-root.sh
@@ -0,0 +1,280 @@
+#!/bin/sh
+
+test_description='git rebase --root
+
+Tests if git rebase --root --onto <newparent> can rebase the root commit.
+'
+. ./test-lib.sh
+
+log_with_names () {
+	git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
+	git name-rev --stdin --name-only --refs=refs/heads/$1
+}
+
+
+test_expect_success 'prepare repository' '
+	test_commit 1 A &&
+	test_commit 2 A &&
+	git symbolic-ref HEAD refs/heads/other &&
+	rm .git/index &&
+	test_commit 3 B &&
+	test_commit 1b A 1 &&
+	test_commit 4 B
+'
+
+test_expect_success 'rebase --root expects --onto' '
+	test_must_fail git rebase --root
+'
+
+test_expect_success 'setup pre-rebase hook' '
+	mkdir -p .git/hooks &&
+	cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+echo "\$1,\$2" >.git/PRE-REBASE-INPUT
+EOF
+	chmod +x .git/hooks/pre-rebase
+'
+cat > expect <<EOF
+4
+3
+2
+1
+EOF
+
+test_expect_success 'rebase --root --onto <newbase>' '
+	git checkout -b work &&
+	git rebase --root --onto master &&
+	git log --pretty=tformat:"%s" > rebased &&
+	test_cmp expect rebased
+'
+
+test_expect_success 'pre-rebase got correct input (1)' '
+	test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
+'
+
+test_expect_success 'rebase --root --onto <newbase> <branch>' '
+	git branch work2 other &&
+	git rebase --root --onto master work2 &&
+	git log --pretty=tformat:"%s" > rebased2 &&
+	test_cmp expect rebased2
+'
+
+test_expect_success 'pre-rebase got correct input (2)' '
+	test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2
+'
+
+test_expect_success 'rebase -i --root --onto <newbase>' '
+	git checkout -b work3 other &&
+	git rebase -i --root --onto master &&
+	git log --pretty=tformat:"%s" > rebased3 &&
+	test_cmp expect rebased3
+'
+
+test_expect_success 'pre-rebase got correct input (3)' '
+	test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
+'
+
+test_expect_success 'rebase -i --root --onto <newbase> <branch>' '
+	git branch work4 other &&
+	git rebase -i --root --onto master work4 &&
+	git log --pretty=tformat:"%s" > rebased4 &&
+	test_cmp expect rebased4
+'
+
+test_expect_success 'pre-rebase got correct input (4)' '
+	test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work4
+'
+
+test_expect_success 'rebase -i -p with linear history' '
+	git checkout -b work5 other &&
+	git rebase -i -p --root --onto master &&
+	git log --pretty=tformat:"%s" > rebased5 &&
+	test_cmp expect rebased5
+'
+
+test_expect_success 'pre-rebase got correct input (5)' '
+	test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
+'
+
+test_expect_success 'set up merge history' '
+	git checkout other^ &&
+	git checkout -b side &&
+	test_commit 5 C &&
+	git checkout other &&
+	git merge side
+'
+
+cat > expect-side <<'EOF'
+commit work6 work6~1 work6^2
+Merge branch 'side' into other
+commit work6^2 work6~2
+5
+commit work6~1 work6~2
+4
+commit work6~2 work6~3
+3
+commit work6~3 work6~4
+2
+commit work6~4
+1
+EOF
+
+test_expect_success 'rebase -i -p with merge' '
+	git checkout -b work6 other &&
+	git rebase -i -p --root --onto master &&
+	log_with_names work6 > rebased6 &&
+	test_cmp expect-side rebased6
+'
+
+test_expect_success 'set up second root and merge' '
+	git symbolic-ref HEAD refs/heads/third &&
+	rm .git/index &&
+	rm A B C &&
+	test_commit 6 D &&
+	git checkout other &&
+	git merge third
+'
+
+cat > expect-third <<'EOF'
+commit work7 work7~1 work7^2
+Merge branch 'third' into other
+commit work7^2 work7~4
+6
+commit work7~1 work7~2 work7~1^2
+Merge branch 'side' into other
+commit work7~1^2 work7~3
+5
+commit work7~2 work7~3
+4
+commit work7~3 work7~4
+3
+commit work7~4 work7~5
+2
+commit work7~5
+1
+EOF
+
+test_expect_success 'rebase -i -p with two roots' '
+	git checkout -b work7 other &&
+	git rebase -i -p --root --onto master &&
+	log_with_names work7 > rebased7 &&
+	test_cmp expect-third rebased7
+'
+
+test_expect_success 'setup pre-rebase hook that fails' '
+	mkdir -p .git/hooks &&
+	cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+false
+EOF
+	chmod +x .git/hooks/pre-rebase
+'
+
+test_expect_success 'pre-rebase hook stops rebase' '
+	git checkout -b stops1 other &&
+	test_must_fail git rebase --root --onto master &&
+	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1
+	test 0 = $(git rev-list other...stops1 | wc -l)
+'
+
+test_expect_success 'pre-rebase hook stops rebase -i' '
+	git checkout -b stops2 other &&
+	test_must_fail git rebase --root --onto master &&
+	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2
+	test 0 = $(git rev-list other...stops2 | wc -l)
+'
+
+test_expect_success 'remove pre-rebase hook' '
+	rm -f .git/hooks/pre-rebase
+'
+
+test_expect_success 'set up a conflict' '
+	git checkout master &&
+	echo conflict > B &&
+	git add B &&
+	git commit -m conflict
+'
+
+test_expect_success 'rebase --root with conflict (first part)' '
+	git checkout -b conflict1 other &&
+	test_must_fail git rebase --root --onto master &&
+	git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+	echo 3 > B &&
+	git add B
+'
+
+cat > expect-conflict <<EOF
+6
+5
+4
+3
+conflict
+2
+1
+EOF
+
+test_expect_success 'rebase --root with conflict (second part)' '
+	git rebase --continue &&
+	git log --pretty=tformat:"%s" > conflict1 &&
+	test_cmp expect-conflict conflict1
+'
+
+test_expect_success 'rebase -i --root with conflict (first part)' '
+	git checkout -b conflict2 other &&
+	test_must_fail git rebase -i --root --onto master &&
+	git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+	echo 3 > B &&
+	git add B
+'
+
+test_expect_success 'rebase -i --root with conflict (second part)' '
+	git rebase --continue &&
+	git log --pretty=tformat:"%s" > conflict2 &&
+	test_cmp expect-conflict conflict2
+'
+
+cat >expect-conflict-p <<\EOF
+commit conflict3 conflict3~1 conflict3^2
+Merge branch 'third' into other
+commit conflict3^2 conflict3~4
+6
+commit conflict3~1 conflict3~2 conflict3~1^2
+Merge branch 'side' into other
+commit conflict3~1^2 conflict3~3
+5
+commit conflict3~2 conflict3~3
+4
+commit conflict3~3 conflict3~4
+3
+commit conflict3~4 conflict3~5
+conflict
+commit conflict3~5 conflict3~6
+2
+commit conflict3~6
+1
+EOF
+
+test_expect_success 'rebase -i -p --root with conflict (first part)' '
+	git checkout -b conflict3 other &&
+	test_must_fail git rebase -i -p --root --onto master &&
+	git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+	echo 3 > B &&
+	git add B
+'
+
+test_expect_success 'rebase -i -p --root with conflict (second part)' '
+	git rebase --continue &&
+	log_with_names conflict3 >out &&
+	test_cmp expect-conflict-p out
+'
+
+test_done
diff --git a/t/t3409-rebase-hook.sh b/t/t3413-rebase-hook.sh
similarity index 84%
rename from t/t3409-rebase-hook.sh
rename to t/t3413-rebase-hook.sh
index bc93dda..098b755 100755
--- a/t/t3409-rebase-hook.sh
+++ b/t/t3413-rebase-hook.sh
@@ -118,9 +118,29 @@
 test_expect_success 'pre-rebase hook stops rebase (2)' '
 	git checkout test &&
 	git reset --hard side &&
-	EDITOR=true test_must_fail git rebase -i master &&
+	(
+		EDITOR=:
+		export EDITOR
+		test_must_fail git rebase -i master
+	) &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
 	test 0 = $(git rev-list HEAD...side | wc -l)
 '
 
+test_expect_success 'rebase --no-verify overrides pre-rebase (1)' '
+	git checkout test &&
+	git reset --hard side &&
+	git rebase --no-verify master &&
+	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+	test "z$(cat git)" = zworld
+'
+
+test_expect_success 'rebase --no-verify overrides pre-rebase (2)' '
+	git checkout test &&
+	git reset --hard side &&
+	EDITOR=true git rebase --no-verify -i master &&
+	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+	test "z$(cat git)" = zworld
+'
+
 test_done
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
new file mode 100755
index 0000000..f7b3518
--- /dev/null
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='cherry-pick should rerere for conflicts'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo foo >foo &&
+	git add foo && test_tick && git commit -q -m 1 &&
+	echo foo-master >foo &&
+	git add foo && test_tick && git commit -q -m 2 &&
+
+	git checkout -b dev HEAD^ &&
+	echo foo-dev >foo &&
+	git add foo && test_tick && git commit -q -m 3 &&
+	git config rerere.enabled true
+'
+
+test_expect_success 'conflicting merge' '
+	test_must_fail git merge master
+'
+
+test_expect_success 'fixup' '
+	echo foo-dev >foo &&
+	git add foo && test_tick && git commit -q -m 4 &&
+	git reset --hard HEAD^
+	echo foo-dev >expect
+'
+
+test_expect_success 'cherry-pick conflict' '
+	test_must_fail git cherry-pick master &&
+	test_cmp expect foo
+'
+
+test_expect_success 'reconfigure' '
+	git config rerere.enabled false
+	git reset --hard
+'
+
+test_expect_success 'cherry-pick conflict without rerere' '
+	test_must_fail git cherry-pick master &&
+	test_must_fail test_cmp expect foo
+'
+
+test_done
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
new file mode 100755
index 0000000..9aaeabd
--- /dev/null
+++ b/t/t3505-cherry-pick-empty.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='test cherry-picking an empty commit'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	echo first > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "first" &&
+
+	git checkout -b empty-branch &&
+	test_tick &&
+	git commit --allow-empty -m "empty"
+
+'
+
+test_expect_code 1 'cherry-pick an empty commit' '
+
+	git checkout master &&
+	git cherry-pick empty-branch
+
+'
+
+test_expect_success 'index lockfile was removed' '
+
+	test ! -f .git/index.lock
+
+'
+
+test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 66aca99..76b1bb4 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -12,30 +12,37 @@
     'Initialize test directory' \
     "touch -- foo bar baz 'space embedded' -q &&
      git add -- foo bar baz 'space embedded' -q &&
-     git commit -m 'add normal files' &&
-     test_tabs=y &&
-     if touch -- 'tab	embedded' 'newline
-embedded'
-     then
+     git commit -m 'add normal files'"
+
+if touch -- 'tab	embedded' 'newline
+embedded' 2>/dev/null
+then
+	test_set_prereq FUNNYNAMES
+else
+	say 'Your filesystem does not allow tabs in filenames.'
+fi
+
+test_expect_success FUNNYNAMES 'add files with funny names' "
      git add -- 'tab	embedded' 'newline
 embedded' &&
      git commit -m 'add files with tabs and newlines'
-     else
-         say 'Your filesystem does not allow tabs in filenames.'
-         test_tabs=n
-     fi"
+"
 
+# Determine rm behavior
 # Later we will try removing an unremovable path to make sure
 # git rm barfs, but if the test is run as root that cannot be
 # arranged.
-test_expect_success \
-    'Determine rm behavior' \
-    ': >test-file
-     chmod a-w .
-     rm -f test-file
-     test -f test-file && test_failed_remove=y
-     chmod 775 .
-     rm -f test-file'
+: >test-file
+chmod a-w .
+rm -f test-file 2>/dev/null
+if test -f test-file
+then
+	test_set_prereq RO_DIR
+else
+	say 'skipping removal failure test (perhaps running as root?)'
+fi
+chmod 775 .
+rm -f test-file
 
 test_expect_success \
     'Pre-check that foo exists and is in index before git rm foo' \
@@ -100,20 +107,16 @@
     'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \
     'git rm -- -q'
 
-test "$test_tabs" = y && test_expect_success \
+test_expect_success FUNNYNAMES \
     "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \
     "git rm -f 'space embedded' 'tab	embedded' 'newline
 embedded'"
 
-if test "$test_failed_remove" = y; then
-chmod a-w .
-test_expect_success \
-    'Test that "git rm -f" fails if its rm fails' \
-    'test_must_fail git rm -f baz'
-chmod 775 .
-else
-    test_expect_success 'skipping removal failure (perhaps running as root?)' :
-fi
+test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
+	chmod a-w . &&
+	test_must_fail git rm -f baz &&
+	chmod 775 .
+'
 
 test_expect_success \
     'When the rm in "git rm -f" fails, it should not remove the file from the index' \
@@ -187,6 +190,19 @@
 	test_must_fail git ls-files --error-unmatch baz
 '
 
+test_expect_success 'refuse to remove cached empty file with modifications' '
+	>empty &&
+	git add empty &&
+	echo content >empty &&
+	test_must_fail git rm --cached empty
+'
+
+test_expect_success 'remove intent-to-add file without --force' '
+	echo content >intent-to-add &&
+	git add -N intent-to-add
+	git rm --cached intent-to-add
+'
+
 test_expect_success 'Recursive test setup' '
 	mkdir -p frotz &&
 	echo qfwfq >frotz/nitfol &&
@@ -238,4 +254,21 @@
 
 '
 
+test_expect_success 'choking "git rm" should not let it die with cruft' '
+	git reset -q --hard &&
+	H=0000000000000000000000000000000000000000 &&
+	i=0 &&
+	while test $i -lt 12000
+	do
+	    echo "100644 $H 0	some-file-$i"
+	    i=$(( $i + 1 ))
+	done | git update-index --index-info &&
+	git rm -n "some-file-*" | :;
+	test -f .git/index.lock
+	status=$?
+	rm -f .git/index.lock
+	git reset -q --hard
+	test "$status" != 0
+'
+
 test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 2ac93a3..050de42 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -30,7 +30,7 @@
 	 *) echo fail; git ls-files --stage xfoo1; (exit 1);;
 	 esac'
 
-test_expect_success 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
 	rm -f xfoo1 &&
 	ln -s foo xfoo1 &&
 	git add xfoo1 &&
@@ -51,7 +51,7 @@
 	 *) echo fail; git ls-files --stage xfoo2; (exit 1);;
 	 esac'
 
-test_expect_success 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
 	rm -f xfoo2 &&
 	ln -s foo xfoo2 &&
 	git update-index --add xfoo2 &&
@@ -61,7 +61,7 @@
 	esac
 '
 
-test_expect_success \
+test_expect_success SYMLINKS \
 	'git update-index --add: Test that executable bit is not used...' \
 	'git config core.filemode 0 &&
 	 ln -s xfoo2 xfoo3 &&
@@ -179,7 +179,7 @@
 	test -z "`git diff-index HEAD -- foo`"
 '
 
-test_expect_success 'git add should fail atomically upon an unreadable file' '
+test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
 	git reset --hard &&
 	date >foo1 &&
 	date >foo2 &&
@@ -190,7 +190,7 @@
 
 rm -f foo2
 
-test_expect_success 'git add --ignore-errors' '
+test_expect_success POSIXPERM 'git add --ignore-errors' '
 	git reset --hard &&
 	date >foo1 &&
 	date >foo2 &&
@@ -201,7 +201,7 @@
 
 rm -f foo2
 
-test_expect_success 'git add (add.ignore-errors)' '
+test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
 	git config add.ignore-errors 1 &&
 	git reset --hard &&
 	date >foo1 &&
@@ -212,7 +212,7 @@
 '
 rm -f foo2
 
-test_expect_success 'git add (add.ignore-errors = false)' '
+test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
 	git config add.ignore-errors 0 &&
 	git reset --hard &&
 	date >foo1 &&
@@ -222,11 +222,11 @@
 	! ( git ls-files foo1 | grep foo1 )
 '
 
-test_expect_success 'git add '\''fo\[ou\]bar'\'' ignores foobar' '
+test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
 	git reset --hard &&
 	touch fo\[ou\]bar foobar &&
 	git add '\''fo\[ou\]bar'\'' &&
-	git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
+	git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
 	! ( git ls-files foobar | grep foobar )
 '
 
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index e95663d..dfc6560 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -3,6 +3,11 @@
 test_description='add -i basic tests'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping git add -i tests, perl not available'
+	test_done
+fi
+
 test_expect_success 'setup (initial)' '
 	echo content >file &&
 	git add file &&
@@ -135,10 +140,12 @@
 
 if test "$(git config --bool core.filemode)" = false
 then
-    say 'skipping filemode tests (filesystem does not properly support modes)'
+	say 'skipping filemode tests (filesystem does not properly support modes)'
 else
+	test_set_prereq FILEMODE
+fi
 
-test_expect_success 'patch does not affect mode' '
+test_expect_success FILEMODE 'patch does not affect mode' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -147,7 +154,7 @@
 	git diff file | grep "new mode"
 '
 
-test_expect_success 'stage mode but not hunk' '
+test_expect_success FILEMODE 'stage mode but not hunk' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -156,7 +163,6 @@
 	git diff          file | grep "+content"
 '
 
-fi
 # end of tests disabled when filemode is not usable
 
 test_done
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f313533..784c31a 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -16,7 +16,7 @@
 	: >F &&
 	git add F &&
 	T=$(git write-tree) &&
-	C=$(git commit-tree $T <../t3900/1-UTF-8.txt) &&
+	C=$(git commit-tree $T <"$TEST_DIRECTORY"/t3900/1-UTF-8.txt) &&
 	git update-ref HEAD $C &&
 	git tag C0
 '
@@ -32,7 +32,7 @@
 		git config i18n.commitencoding $H &&
 		git checkout -b $H C0 &&
 		echo $H >F &&
-		git commit -a -F ../t3900/$H.txt
+		git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt
 	'
 done
 
@@ -57,13 +57,13 @@
 '
 
 test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-	compare_with ISO-8859-1 ../t3900/1-UTF-8.txt
+	compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
 for H in EUCJP ISO-2022-JP
 do
 	test_expect_success "$H should be shown in UTF-8 now" '
-		compare_with '$H' ../t3900/2-UTF-8.txt
+		compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
 	'
 done
 
@@ -82,7 +82,7 @@
 do
 	test_expect_success "$H should be shown in itself now" '
 		git config i18n.commitencoding '$H' &&
-		compare_with '$H' ../t3900/'$H'.txt
+		compare_with '$H' "$TEST_DIRECTORY"/t3900/'$H'.txt
 	'
 done
 
@@ -91,13 +91,13 @@
 '
 
 test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-	compare_with ISO-8859-1 ../t3900/1-UTF-8.txt
+	compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
 for H in EUCJP ISO-2022-JP
 do
 	test_expect_success "$H should be shown in UTF-8 now" '
-		compare_with '$H' ../t3900/2-UTF-8.txt
+		compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
 	'
 done
 
@@ -107,7 +107,7 @@
 	for H in EUCJP ISO-2022-JP
 	do
 		test_expect_success "$H should be shown in $J now" '
-			compare_with '$H' ../t3900/'$J'.txt
+			compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt
 		'
 	done
 done
@@ -115,7 +115,7 @@
 for H in ISO-8859-1 EUCJP ISO-2022-JP
 do
 	test_expect_success "No conversion with $H" '
-		compare_with "--encoding=none '$H'" ../t3900/'$H'.txt
+		compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt
 	'
 done
 
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 567760e..7655da3 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -35,7 +35,7 @@
 
 	# use UTF-8 in author and committer name to match the
 	# i18n.commitencoding settings
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	test_tick &&
 	echo "$GIT_AUTHOR_NAME" >mine &&
@@ -57,7 +57,7 @@
 	# the second one on the side branch is ISO-8859-1
 	git config i18n.commitencoding ISO-8859-1 &&
 	# use author and committer name in ISO-8859-1 to match it.
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 	test_tick &&
 	echo Yet another >theirs &&
 	git add theirs &&
@@ -101,7 +101,7 @@
 
 	# The result will be committed by GIT_COMMITTER_NAME --
 	# we want UTF-8 encoded name.
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 	git checkout -b test &&
 	git rebase master &&
 
@@ -111,7 +111,7 @@
 test_expect_success 'rebase (U/L)' '
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard side &&
 	git rebase master &&
@@ -123,7 +123,7 @@
 	# In this test we want ISO-8859-1 encoded commits as the result
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git rebase master &&
@@ -136,7 +136,7 @@
 	# to get ISO-8859-1 results.
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git rebase master &&
@@ -149,7 +149,7 @@
 
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -164,7 +164,7 @@
 
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -179,7 +179,7 @@
 
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -195,7 +195,7 @@
 
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -208,7 +208,7 @@
 test_expect_success 'rebase --merge (U/U)' '
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard side &&
 	git rebase --merge master &&
@@ -219,7 +219,7 @@
 test_expect_success 'rebase --merge (U/L)' '
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard side &&
 	git rebase --merge master &&
@@ -231,7 +231,7 @@
 	# In this test we want ISO-8859-1 encoded commits as the result
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git rebase --merge master &&
@@ -244,7 +244,7 @@
 	# to get ISO-8859-1 results.
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git rebase --merge master &&
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index c44b27a..6ddd469 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index a326924..71bac83 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index a4cfde6..18695ce 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../lib-read-tree-m-3way.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 cat >.test-plain-OA <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A	AA
@@ -169,6 +169,20 @@
      cmp -s .test-a .test-recursive-AB'
 
 test_expect_success \
+    'diff-tree --stdin of known trees.' \
+    'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+     echo $tree_A $tree_B > .test-plain-ABx &&
+     cat .test-plain-AB >> .test-plain-ABx &&
+     cmp -s .test-a .test-plain-ABx'
+
+test_expect_success \
+    'diff-tree --stdin of known trees.' \
+    'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+     echo $tree_A $tree_B > .test-recursive-ABx &&
+     cat .test-recursive-AB >> .test-recursive-ABx &&
+     cmp -s .test-a .test-recursive-ABx'
+
+test_expect_success \
     'diff-cache O with A in cache' \
     'git read-tree $tree_A &&
      git diff-index --cached $tree_O >.test-a &&
@@ -244,4 +258,12 @@
     git diff-tree -r -R $tree_A $tree_B >.test-b &&
     cmp -s .test-a .test-b'
 
+test_expect_success \
+    'diff can read from stdin' \
+    'test_must_fail git diff --no-index -- MN - < NN |
+        grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+    test_must_fail git diff --no-index -- MN NN |
+        grep -v "^index" >.test-b &&
+    test_cmp .test-a .test-b'
+
 test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index 8b1f875..c6130c4 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -99,7 +99,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index 3d25be7..a4da119 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -10,7 +10,13 @@
 by an edit for them.
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
 
 test_expect_success \
     'prepare reference tree' \
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 6630017..1ba359d 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -71,7 +71,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -C --find-copies-harder $tree >current
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index 4e92fce..8c1b81e 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -15,21 +15,10 @@
      tree=`git write-tree` &&
      echo $tree'
 
-if [ "$(git config --get core.filemode)" = false ]
-then
-	say 'filemode disabled on the filesystem, using update-index --chmod=+x'
-	test_expect_success \
-	    'git update-index --chmod=+x' \
-	    'git update-index rezrov &&
-	     git update-index --chmod=+x rezrov &&
-	     git diff-index $tree >current'
-else
-	test_expect_success \
-	    'chmod' \
-	    'chmod +x rezrov &&
-	     git update-index rezrov &&
-	     git diff-index $tree >current'
-fi
+test_expect_success \
+    'chmod' \
+    'test_chmod +x rezrov &&
+     git diff-index $tree >current'
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index 104a4e1..42072d7 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -7,12 +7,12 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
     'mkdir path0 path1 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git update-index --add path0/COPYING &&
     tree=$(git write-tree) &&
     echo $tree'
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 26c2e4a..e19ca65 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -22,12 +22,12 @@
 Further, with -B and -M together, these should turn into two renames.
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
-    'cat ../../README >file0 &&
-     cat ../../COPYING >file1 &&
+    'cat "$TEST_DIRECTORY"/../README >file0 &&
+     cat "$TEST_DIRECTORY"/../COPYING >file1 &&
     git update-index --add file0 file1 &&
     tree=$(git write-tree) &&
     echo "$tree"'
@@ -99,7 +99,7 @@
     'validate result of -B -M (#4)' \
     'compare_diff_raw expected current'
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'make file0 into something completely different' \
     'rm -f file0 &&
      ln -s frotz file0 &&
@@ -114,7 +114,7 @@
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100	file1
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate result of -B (#5)' \
     'compare_diff_raw expected current'
 
@@ -129,7 +129,7 @@
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R	file0	file1
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate result of -B -M (#6)' \
     'compare_diff_raw expected current'
 
@@ -144,7 +144,7 @@
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M	file1
 EOF
 
-test_expect_success \
+test_expect_success SYMLINKS \
     'validate result of -M (#7)' \
     'compare_diff_raw expected current'
 
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index d2b45e7..de3f174 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -78,7 +78,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index ad3d9e4..94df7ae 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -10,7 +10,7 @@
         path1/file1
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
@@ -62,4 +62,12 @@
     'git diff-index --cached $tree -- file0/ >current &&
      compare_diff_raw current expected'
 
+test_expect_success 'diff-tree pathspec' '
+	tree2=$(git write-tree) &&
+	echo "$tree2" &&
+	git diff-tree -r --name-only $tree $tree2 -- pa path1/a >current &&
+	>expected &&
+	test_cmp expected current
+'
+
 test_done
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index c6d1369..d7e327c 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -7,7 +7,13 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
 
 cat > expected << EOF
 diff --git a/frotz b/frotz
@@ -82,4 +88,11 @@
     git diff-index -M -p $tree > current &&
     compare_diff_patch current expected'
 
+test_expect_success \
+    'diff symlinks with non-existing targets' \
+    'ln -s narf pinky &&
+    ln -s take\ over brain &&
+    test_must_fail git diff --no-index pinky brain > output 2> output.err &&
+    grep narf output &&
+    ! grep error output.err'
 test_done
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index eac1271..f64aa48 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -12,7 +12,7 @@
 	'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
 	 git update-index --add a b c d &&
 	 echo git >a &&
-	 cat ../test4012.png >b &&
+	 cat "$TEST_DIRECTORY"/test4012.png >b &&
 	 echo git >c &&
 	 cat b b >d'
 
@@ -25,11 +25,11 @@
 EOF
 test_expect_success 'diff without --binary' \
 	'git diff | git apply --stat --summary >current &&
-	 cmp current expected'
+	 test_cmp expected current'
 
 test_expect_success 'diff with --binary' \
 	'git diff --binary | git apply --stat --summary >current &&
-	 cmp current expected'
+	 test_cmp expected current'
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
@@ -87,7 +87,7 @@
 
 test_expect_success 'diff --no-index with binary creation' '
 	echo Q | q_to_nul >binary &&
-	(:# hide error code from diff, which just indicates differences
+	(: hide error code from diff, which just indicates differences
 	 git diff --binary --no-index /dev/null binary >current ||
 	 true
 	) &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 99d9e0b..8b33321 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -74,6 +74,10 @@
 	for i in 1 2; do echo $i; done >>dir/sub &&
 	git update-index file0 dir/sub &&
 
+	mkdir dir3 &&
+	cp dir/sub dir3/sub &&
+	test-chmtime +1 dir3/sub &&
+
 	git config log.showroot false &&
 	git commit --amend &&
 	git show-branch
@@ -97,9 +101,8 @@
 	'' | '#'*) continue ;;
 	esac
 	test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
-	cnt=`expr $test_count + 1`
-	pfx=`printf "%04d" $cnt`
-	expect="../t4013/diff.$test"
+	pfx=`printf "%04d" $test_count`
+	expect="$TEST_DIRECTORY/t4013/diff.$test"
 	actual="$pfx-diff.$test"
 
 	test_expect_success "git $cmd" '
@@ -203,6 +206,10 @@
 log --root --cc --patch-with-stat --summary master
 log -SF master
 log -SF -p master
+log --decorate --all
+
+rev-list --parents HEAD
+rev-list --children HEAD
 
 whatchanged master
 whatchanged -p master
@@ -236,11 +243,15 @@
 format-patch --stdout initial..side
 format-patch --stdout initial..master^
 format-patch --stdout initial..master
+format-patch --stdout --no-numbered initial..master
+format-patch --stdout --numbered initial..master
 format-patch --attach --stdout initial..side
+format-patch --attach --stdout --suffix=.diff initial..side
 format-patch --attach --stdout initial..master^
 format-patch --attach --stdout initial..master
 format-patch --inline --stdout initial..side
 format-patch --inline --stdout initial..master^
+format-patch --inline --stdout --numbered-files initial..master
 format-patch --inline --stdout initial..master
 format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
 config format.subjectprefix DIFFERENT_PREFIX
@@ -258,7 +269,10 @@
 diff --patch-with-raw -r initial..side
 diff --name-status dir2 dir
 diff --no-index --name-status dir2 dir
+diff --no-index --name-status -- dir2 dir
+diff --no-index dir dir3
 diff master master^ side
+diff --dirstat master~1 master~2
 EOF
 
 test_done
diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2
new file mode 100644
index 0000000..b672e1c
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_master~1_master~2
@@ -0,0 +1,3 @@
+$ git diff --dirstat master~1 master~2
+  40.0% dir/
+$
diff --git a/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir
new file mode 100644
index 0000000..6756f8d
--- /dev/null
+++ b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir
@@ -0,0 +1,3 @@
+$ git diff --no-index --name-status -- dir2 dir
+A	dir/sub
+$
diff --git a/t/t4013/diff.diff_--no-index_dir_dir3 b/t/t4013/diff.diff_--no-index_dir_dir3
new file mode 100644
index 0000000..2142c2b
--- /dev/null
+++ b/t/t4013/diff.diff_--no-index_dir_dir3
@@ -0,0 +1,2 @@
+$ git diff --no-index dir dir3
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
new file mode 100644
index 0000000..52116d3
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
@@ -0,0 +1,61 @@
+$ git format-patch --attach --stdout --suffix=.diff initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Side.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0001-Side.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
index 43346b9..ce49bd6 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/3] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -22,9 +22,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: attachment; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -63,7 +63,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/3] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -80,9 +80,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: attachment; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -111,7 +111,7 @@
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [PATCH] Side
+Subject: [PATCH 3/3] Side
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -129,9 +129,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0003-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: attachment; filename="0003-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
index d7490a9..5f1b238 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/2] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -22,9 +22,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: attachment; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -63,7 +63,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/2] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -80,9 +80,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: attachment; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
index 38f7902..4a2364a 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -20,9 +20,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0001-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: attachment; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: attachment; filename="0001-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
new file mode 100644
index 0000000..43b81eb
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
@@ -0,0 +1,170 @@
+$ git format-patch --inline --stdout --numbered-files initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="2"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="2"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="3"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="3"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
index fca5cce..ca3f60b 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [TESTCASE] Second
+Subject: [TESTCASE 1/3] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -22,9 +22,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -63,7 +63,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [TESTCASE] Third
+Subject: [TESTCASE 2/3] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -80,9 +80,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: inline; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -111,7 +111,7 @@
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [TESTCASE] Side
+Subject: [TESTCASE 3/3] Side
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -129,9 +129,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0003-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: inline; filename="0003-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
index 6d6fac3..08f2301 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/3] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -22,9 +22,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -63,7 +63,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/3] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -80,9 +80,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: inline; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -111,7 +111,7 @@
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [PATCH] Side
+Subject: [PATCH 3/3] Side
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -129,9 +129,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0003-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: inline; filename="0003-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
index 18a1110..07f1230 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/2] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -22,9 +22,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -63,7 +63,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/2] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -80,9 +80,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Type: text/x-patch; name="0002-Third.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Disposition: inline; filename="0002-Third.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
index 4f258b8..29e00ab 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
@@ -22,9 +22,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Type: text/x-patch; name="0001-Second.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Disposition: inline; filename="0001-Second.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
index e86dce6..67633d4 100644
--- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
@@ -20,9 +20,9 @@
 
 
 --------------g-i-t--v-e-r-s-i-o-n
-Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Type: text/x-patch; name="0001-Side.patch"
 Content-Transfer-Encoding: 8bit
-Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Disposition: inline; filename="0001-Side.patch"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..7289e35 100644
diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
new file mode 100644
index 0000000..f7752eb
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
@@ -0,0 +1,127 @@
+$ git format-patch --stdout --no-numbered initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
new file mode 100644
index 0000000..8e67dbf
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
@@ -0,0 +1,127 @@
+$ git format-patch --stdout --numbered initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master
index 8b88ca4..7b89978 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_initial..master
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/3] Second
 
 This is the second commit.
 ---
@@ -48,7 +48,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/3] Third
 
 ---
  dir/sub |    2 ++
@@ -82,7 +82,7 @@
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [PATCH] Side
+Subject: [PATCH 3/3] Side
 
 ---
  dir/sub |    2 ++
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^
index 47a4b88..b7f9725 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_initial..master^
@@ -2,7 +2,7 @@
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/2] Second
 
 This is the second commit.
 ---
@@ -48,7 +48,7 @@
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/2] Third
 
 ---
  dir/sub |    2 ++
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
new file mode 100644
index 0000000..12da8ac
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--all
@@ -0,0 +1,34 @@
+$ git log --decorate --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
index 3ceb8e7..bd7f5c0 100644
--- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat --summary master -- dir/
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
index 43d7776..14595a6 100644
--- a/t/t4013/diff.log_--patch-with-stat_master
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
index 5187a26..5a4e727 100644
--- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat master -- dir/
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
index c964097..df0aaa9 100644
--- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git log --root --cc --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
index ad050af..c11b5f2c 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git log --root --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
index 628c6c0..5f0c98f 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -1,6 +1,6 @@
 $ git log --root --patch-with-stat master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
index 5d4e0f1..e62c368 100644
--- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git log --root -c --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master
index 217a2eb..b42c334 100644
--- a/t/t4013/diff.log_--root_-p_master
+++ b/t/t4013/diff.log_--root_-p_master
@@ -1,6 +1,6 @@
 $ git log --root -p master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master
index e17ccfc..e8f4615 100644
--- a/t/t4013/diff.log_--root_master
+++ b/t/t4013/diff.log_--root_master
@@ -1,6 +1,6 @@
 $ git log --root master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master
index f8fefef..bf1326d 100644
--- a/t/t4013/diff.log_-p_master
+++ b/t/t4013/diff.log_-p_master
@@ -1,6 +1,6 @@
 $ git log -p master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master
index e9d9e7b..a8f6ce5 100644
--- a/t/t4013/diff.log_master
+++ b/t/t4013/diff.log_master
@@ -1,6 +1,6 @@
 $ git log master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.rev-list_--children_HEAD b/t/t4013/diff.rev-list_--children_HEAD
new file mode 100644
index 0000000..e7f17d5
--- /dev/null
+++ b/t/t4013/diff.rev-list_--children_HEAD
@@ -0,0 +1,7 @@
+$ git rev-list --children HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 59d314ad6f356dd08601a4cd5e530381da3e3c64
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 59d314ad6f356dd08601a4cd5e530381da3e3c64
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+444ac553ac7612cc88969031b02b3767fb8a353a 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+$
diff --git a/t/t4013/diff.rev-list_--parents_HEAD b/t/t4013/diff.rev-list_--parents_HEAD
new file mode 100644
index 0000000..65d2a80
--- /dev/null
+++ b/t/t4013/diff.rev-list_--parents_HEAD
@@ -0,0 +1,7 @@
+$ git rev-list --parents HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 444ac553ac7612cc88969031b02b3767fb8a353a
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 444ac553ac7612cc88969031b02b3767fb8a353a
+444ac553ac7612cc88969031b02b3767fb8a353a
+$
diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master
index 9e6e1f2..fb08ce0 100644
--- a/t/t4013/diff.show_master
+++ b/t/t4013/diff.show_master
@@ -1,6 +1,6 @@
 $ git show master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
index 5facf25..e96ff1f 100644
--- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git whatchanged --root --cc --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
index 10f6767..c0aff68 100644
--- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git whatchanged --root -c --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 7fe853c..11061dd 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Junio C Hamano
 #
 
-test_description='Format-patch skipping already incorporated patches'
+test_description='various format-patch tests'
 
 . ./test-lib.sh
 
@@ -16,9 +16,7 @@
 	git checkout -b side &&
 
 	for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
-	chmod +x elif &&
-	git update-index file elif &&
-	git update-index --chmod=+x elif &&
+	test_chmod +x elif &&
 	git commit -m "Side changes #1" &&
 
 	for i in D E F; do echo "$i"; done >>file &&
@@ -130,6 +128,21 @@
 	grep "^ *S. E. Cipient <scipient@example.com>$" patch5
 '
 
+test_expect_success 'command line headers' '
+
+	git config --unset-all format.headers &&
+	git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch6 &&
+	grep "^Cc: R. E. Cipient <rcipient@example.com>$" patch6
+'
+
+test_expect_success 'configuration headers and command line headers' '
+
+	git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+	git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch7 &&
+	grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch7 &&
+	grep "^ *S. E. Cipient <scipient@example.com>$" patch7
+'
+
 test_expect_success 'multiple files' '
 
 	rm -rf patches/ &&
@@ -138,56 +151,243 @@
 	ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
 '
 
-test_expect_success 'thread' '
+check_threading () {
+	expect="$1" &&
+	shift &&
+	(git format-patch --stdout "$@"; echo $? > status.out) |
+	# Prints everything between the Message-ID and In-Reply-To,
+	# and replaces all Message-ID-lookalikes by a sequence number
+	perl -ne '
+		if (/^(message-id|references|in-reply-to)/i) {
+			$printing = 1;
+		} elsif (/^\S/) {
+			$printing = 0;
+		}
+		if ($printing) {
+			$h{$1}=$i++ if (/<([^>]+)>/ and !exists $h{$1});
+			for $k (keys %h) {s/$k/$h{$k}/};
+			print;
+		}
+		print "---\n" if /^From /i;
+	' > actual &&
+	test 0 = "$(cat status.out)" &&
+	test_cmp "$expect" actual
+}
 
-	rm -rf patches/ &&
+cat >> expect.no-threading <<EOF
+---
+---
+---
+EOF
+
+test_expect_success 'no threading' '
 	git checkout side &&
-	git format-patch --thread -o patches/ master &&
-	FIRST_MID=$(grep "Message-Id:" patches/0001-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
-	for i in patches/0002-* patches/0003-*
-	do
-	  grep "References: $FIRST_MID" $i &&
-	  grep "In-Reply-To: $FIRST_MID" $i || break
-	done
+	check_threading expect.no-threading master
 '
 
+cat > expect.thread <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <0>
+EOF
+
+test_expect_success 'thread' '
+	check_threading expect.thread --thread master
+'
+
+cat > expect.in-reply-to <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <3>
+In-Reply-To: <1>
+References: <1>
+EOF
+
 test_expect_success 'thread in-reply-to' '
-
-	rm -rf patches/ &&
-	git checkout side &&
-	git format-patch --in-reply-to="<test.message>" --thread -o patches/ master &&
-	FIRST_MID="<test.message>" &&
-	for i in patches/*
-	do
-	  grep "References: $FIRST_MID" $i &&
-	  grep "In-Reply-To: $FIRST_MID" $i || break
-	done
+	check_threading expect.in-reply-to --in-reply-to="<test.message>" \
+		--thread master
 '
 
+cat > expect.cover-letter <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <3>
+In-Reply-To: <0>
+References: <0>
+EOF
+
 test_expect_success 'thread cover-letter' '
-
-	rm -rf patches/ &&
-	git checkout side &&
-	git format-patch --cover-letter --thread -o patches/ master &&
-	FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") &&
-	for i in patches/0001-* patches/0002-* patches/0003-*
-	do
-	  grep "References: $FIRST_MID" $i &&
-	  grep "In-Reply-To: $FIRST_MID" $i || break
-	done
+	check_threading expect.cover-letter --cover-letter --thread master
 '
 
-test_expect_success 'thread cover-letter in-reply-to' '
+cat > expect.cl-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+	<0>
+---
+Message-Id: <3>
+In-Reply-To: <0>
+References: <1>
+	<0>
+---
+Message-Id: <4>
+In-Reply-To: <0>
+References: <1>
+	<0>
+EOF
 
-	rm -rf patches/ &&
-	git checkout side &&
-	git format-patch --cover-letter --in-reply-to="<test.message>" --thread -o patches/ master &&
-	FIRST_MID="<test.message>" &&
-	for i in patches/*
-	do
-	  grep "References: $FIRST_MID" $i &&
-	  grep "In-Reply-To: $FIRST_MID" $i || break
-	done
+test_expect_success 'thread cover-letter in-reply-to' '
+	check_threading expect.cl-irt --cover-letter \
+		--in-reply-to="<test.message>" --thread master
+'
+
+test_expect_success 'thread explicit shallow' '
+	check_threading expect.cl-irt --cover-letter \
+		--in-reply-to="<test.message>" --thread=shallow master
+'
+
+cat > expect.deep <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <0>
+	<1>
+EOF
+
+test_expect_success 'thread deep' '
+	check_threading expect.deep --thread=deep master
+'
+
+cat > expect.deep-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+	<0>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <1>
+	<0>
+	<2>
+EOF
+
+test_expect_success 'thread deep in-reply-to' '
+	check_threading expect.deep-irt  --thread=deep \
+		--in-reply-to="<test.message>" master
+'
+
+cat > expect.deep-cl <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <0>
+	<1>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <0>
+	<1>
+	<2>
+EOF
+
+test_expect_success 'thread deep cover-letter' '
+	check_threading expect.deep-cl --cover-letter --thread=deep master
+'
+
+cat > expect.deep-cl-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+	<0>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <1>
+	<0>
+	<2>
+---
+Message-Id: <4>
+In-Reply-To: <3>
+References: <1>
+	<0>
+	<2>
+	<3>
+EOF
+
+test_expect_success 'thread deep cover-letter in-reply-to' '
+	check_threading expect.deep-cl-irt --cover-letter \
+		--in-reply-to="<test.message>" --thread=deep master
+'
+
+test_expect_success 'thread via config' '
+	git config format.thread true &&
+	check_threading expect.thread master
+'
+
+test_expect_success 'thread deep via config' '
+	git config format.thread deep &&
+	check_threading expect.deep master
+'
+
+test_expect_success 'thread config + override' '
+	git config format.thread deep &&
+	check_threading expect.thread --thread master
+'
+
+test_expect_success 'thread config + --no-thread' '
+	git config format.thread deep &&
+	check_threading expect.no-threading --no-thread master
 '
 
 test_expect_success 'excessive subject' '
@@ -230,4 +430,79 @@
 
 '
 
+cat > expect << EOF
+---
+ file |   16 ++++++++++++++++
+ 1 files changed, 16 insertions(+), 0 deletions(-)
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -13,4 +13,20 @@ C
+ 10
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch respects -U' '
+
+	git format-patch -U4 -2 &&
+	sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+	test_cmp expect output
+
+'
+
+test_expect_success 'format-patch from a subdirectory (1)' '
+	filename=$(
+		rm -rf sub &&
+		mkdir -p sub/dir &&
+		cd sub/dir &&
+		git format-patch -1
+	) &&
+	case "$filename" in
+	0*)
+		;; # ok
+	*)
+		echo "Oops? $filename"
+		false
+		;;
+	esac &&
+	test -f "$filename"
+'
+
+test_expect_success 'format-patch from a subdirectory (2)' '
+	filename=$(
+		rm -rf sub &&
+		mkdir -p sub/dir &&
+		cd sub/dir &&
+		git format-patch -1 -o ..
+	) &&
+	case "$filename" in
+	../0*)
+		;; # ok
+	*)
+		echo "Oops? $filename"
+		false
+		;;
+	esac &&
+	basename=$(expr "$filename" : ".*/\(.*\)") &&
+	test -f "sub/$basename"
+'
+
+test_expect_success 'format-patch from a subdirectory (3)' '
+	here="$TEST_DIRECTORY/$test" &&
+	rm -f 0* &&
+	filename=$(
+		rm -rf sub &&
+		mkdir -p sub/dir &&
+		cd sub/dir &&
+		git format-patch -1 -o "$here"
+	) &&
+	basename=$(expr "$filename" : ".*/\(.*\)") &&
+	test -f "$basename"
+'
+
 test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index b1cbd36..6d13da3 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 # Ray Lehtiniemi's example
 
@@ -98,6 +98,12 @@
 EOF
 git diff -w > out
 test_expect_success 'another test, with -w' 'test_cmp expect out'
+git diff -w -b > out
+test_expect_success 'another test, with -w -b' 'test_cmp expect out'
+git diff -w --ignore-space-at-eol > out
+test_expect_success 'another test, with -w --ignore-space-at-eol' 'test_cmp expect out'
+git diff -w -b --ignore-space-at-eol > out
+test_expect_success 'another test, with -w -b --ignore-space-at-eol' 'test_cmp expect out'
 
 tr 'Q' '\015' << EOF > expect
 diff --git a/x b/x
@@ -116,6 +122,27 @@
 EOF
 git diff -b > out
 test_expect_success 'another test, with -b' 'test_cmp expect out'
+git diff -b --ignore-space-at-eol > out
+test_expect_success 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
+-whitespace change
+-whitespace in the middle
++	whitespace at beginning
++whitespace 	 change
++white space in the middle
+ whitespace at end
+ unchanged line
+ CR at endQ
+EOF
+git diff --ignore-space-at-eol > out
+test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
 
 test_expect_success 'check mixed spaces and tabs in indent' '
 
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 398bf4b..be54134 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -32,7 +32,7 @@
 
 sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
 
-builtin_patterns="bibtex java pascal ruby tex"
+builtin_patterns="bibtex html java objc pascal php python ruby tex"
 for p in $builtin_patterns
 do
 	test_expect_success "builtin $p pattern compiles" '
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 22ef7d4..0720001 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -125,6 +125,38 @@
 test_expect_success 'force diff with "diff"' '
 	echo >.gitattributes "file diff" &&
 	git diff >actual &&
+	test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
+	echo anotherfile > file2 &&
+	git add file2 &&
+	git commit -m "added 2nd file" &&
+	echo modified >file2 &&
+	GIT_EXTERNAL_DIFF=echo git diff
+'
+
+echo "#!$SHELL_PATH" >fake-diff.sh
+cat >> fake-diff.sh <<\EOF
+cat $2 >> crlfed.txt
+EOF
+chmod a+x fake-diff.sh
+
+keep_only_cr () {
+	tr -dc '\015'
+}
+
+test_expect_success 'external diff with autocrlf = true' '
+	git config core.autocrlf true &&
+	GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
+	test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+'
+
+test_expect_success 'diff --cached' '
+	git add file &&
+	git update-index --assume-unchanged file &&
+	echo second >file &&
+	git diff --cached >actual &&
 	test_cmp ../t4020/diff.NUL actual
 '
 
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 43d64bb..390af23 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -45,17 +45,22 @@
 	grep "^Subject: \[PATCH 2/2\]" $1
 }
 
-test_expect_success 'Default: no numbered' '
+test_expect_success 'single patch defaults to no numbers' '
+	git format-patch --stdout HEAD~1 >patch0.single &&
+	test_single_no_numbered patch0.single
+'
 
-	git format-patch --stdout HEAD~2 >patch0 &&
-	test_no_numbered patch0
+test_expect_success 'multiple patch defaults to numbered' '
+
+	git format-patch --stdout HEAD~2 >patch0.multiple &&
+	test_numbered patch0.multiple
 
 '
 
 test_expect_success 'Use --numbered' '
 
-	git format-patch --numbered --stdout HEAD~2 >patch1 &&
-	test_numbered patch1
+	git format-patch --numbered --stdout HEAD~1 >patch1 &&
+	test_single_numbered patch1
 
 '
 
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index bf996fc..2a537a2 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -6,12 +6,12 @@
 
 test_expect_success setup '
 
-	cat ../../COPYING >test &&
+	cat "$TEST_DIRECTORY"/../COPYING >test &&
 	git add test &&
 	tr \
 	  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
 	  "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
-	  <../../COPYING >test
+	  <"$TEST_DIRECTORY"/../COPYING >test
 
 '
 
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 4dbfc6e..9bdf659 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -4,24 +4,30 @@
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
+
 test_expect_success setup '
 
 	rm -f foo bar &&
-	cat ../../COPYING >foo &&
+	cat "$TEST_DIRECTORY"/../COPYING >foo &&
 	ln -s linklink bar &&
 	git add foo bar &&
 	git commit -a -m Initial &&
 	git tag one &&
 
 	rm -f foo bar &&
-	cat ../../COPYING >bar &&
+	cat "$TEST_DIRECTORY"/../COPYING >bar &&
 	ln -s linklink foo &&
 	git add foo bar &&
 	git commit -a -m Second &&
 	git tag two &&
 
 	rm -f foo bar &&
-	cat ../../COPYING >foo &&
+	cat "$TEST_DIRECTORY"/../COPYING >foo &&
 	git add foo &&
 	git commit -a -m Third &&
 	git tag three &&
@@ -35,15 +41,15 @@
 	# This is purely for sanity check
 
 	rm -f foo bar &&
-	cat ../../COPYING >foo &&
-	cat ../../Makefile >bar &&
+	cat "$TEST_DIRECTORY"/../COPYING >foo &&
+	cat "$TEST_DIRECTORY"/../Makefile >bar &&
 	git add foo bar &&
 	git commit -a -m Fifth &&
 	git tag five &&
 
 	rm -f foo bar &&
-	cat ../../Makefile >foo &&
-	cat ../../COPYING >bar &&
+	cat "$TEST_DIRECTORY"/../Makefile >foo &&
+	cat "$TEST_DIRECTORY"/../COPYING >bar &&
 	git add foo bar &&
 	git commit -a -m Sixth &&
 	git tag six
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index ba6679c..1c2edeb 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -3,7 +3,7 @@
 test_description='difference in submodules'
 
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 _z40=0000000000000000000000000000000000000000
 test_expect_success setup '
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
new file mode 100755
index 0000000..9ddbbcd
--- /dev/null
+++ b/t/t4029-diff-trailing-space.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (c) Jim Meyering
+#
+test_description='diff honors config option, diff.suppressBlankEmpty'
+
+. ./test-lib.sh
+
+cat <<\EOF > exp ||
+diff --git a/f b/f
+index 5f6a263..8cb8bae 100644
+--- a/f
++++ b/f
+@@ -1,2 +1,2 @@
+
+-x
++y
+EOF
+exit 1
+
+test_expect_success \
+    "$test_description" \
+    'printf "\nx\n" > f &&
+     git add f &&
+     git commit -q -m. f &&
+     printf "\ny\n" > f &&
+     git config --bool diff.suppressBlankEmpty true &&
+     git diff f > actual &&
+     test_cmp exp actual &&
+     perl -i.bak -p -e "s/^\$/ /" exp &&
+     git config --bool diff.suppressBlankEmpty false &&
+     git diff f > actual &&
+     test_cmp exp actual &&
+     git config --bool --unset diff.suppressBlankEmpty &&
+     git diff f > actual &&
+     test_cmp exp actual
+     '
+
+test_done
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
new file mode 100755
index 0000000..a3f0897
--- /dev/null
+++ b/t/t4030-diff-textconv.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='diff.*.textconv tests'
+. ./test-lib.sh
+
+find_diff() {
+	sed '1,/^index /d' | sed '/^-- $/,$d'
+}
+
+cat >expect.binary <<'EOF'
+Binary files a/file and b/file differ
+EOF
+
+cat >expect.text <<'EOF'
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ 0
++1
+EOF
+
+cat >hexdump <<'EOF'
+#!/bin/sh
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+EOF
+chmod +x hexdump
+
+test_expect_success 'setup binary file with history' '
+	printf "\\0\\n" >file &&
+	git add file &&
+	git commit -m one &&
+	printf "\\01\\n" >>file &&
+	git add file &&
+	git commit -m two
+'
+
+test_expect_success 'file is considered binary by porcelain' '
+	git diff HEAD^ HEAD >diff &&
+	find_diff <diff >actual &&
+	test_cmp expect.binary actual
+'
+
+test_expect_success 'file is considered binary by plumbing' '
+	git diff-tree -p HEAD^ HEAD >diff &&
+	find_diff <diff >actual &&
+	test_cmp expect.binary actual
+'
+
+test_expect_success 'setup textconv filters' '
+	echo file diff=foo >.gitattributes &&
+	git config diff.foo.textconv "$PWD"/hexdump &&
+	git config diff.fail.textconv false
+'
+
+test_expect_success 'diff produces text' '
+	git diff HEAD^ HEAD >diff &&
+	find_diff <diff >actual &&
+	test_cmp expect.text actual
+'
+
+test_expect_success 'diff-tree produces binary' '
+	git diff-tree -p HEAD^ HEAD >diff &&
+	find_diff <diff >actual &&
+	test_cmp expect.binary actual
+'
+
+test_expect_success 'log produces text' '
+	git log -1 -p >log &&
+	find_diff <log >actual &&
+	test_cmp expect.text actual
+'
+
+test_expect_success 'format-patch produces binary' '
+	git format-patch --no-binary --stdout HEAD^ >patch &&
+	find_diff <patch >actual &&
+	test_cmp expect.binary actual
+'
+
+test_expect_success 'status -v produces text' '
+	git reset --soft HEAD^ &&
+	git status -v >diff &&
+	find_diff <diff >actual &&
+	test_cmp expect.text actual &&
+	git reset --soft HEAD@{1}
+'
+
+cat >expect.stat <<'EOF'
+ file |  Bin 2 -> 4 bytes
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+EOF
+test_expect_success 'diffstat does not run textconv' '
+	echo file diff=fail >.gitattributes &&
+	git diff --stat HEAD^ HEAD >actual &&
+	test_cmp expect.stat actual
+'
+# restore working setup
+echo file diff=foo >.gitattributes
+
+cat >expect.typechange <<'EOF'
+--- a/file
++++ /dev/null
+@@ -1,2 +0,0 @@
+-0
+-1
+diff --git a/file b/file
+new file mode 120000
+index 0000000..67be421
+--- /dev/null
++++ b/file
+@@ -0,0 +1 @@
++frotz
+\ No newline at end of file
+EOF
+# make a symlink the hard way that works on symlink-challenged file systems
+test_expect_success 'textconv does not act on symlinks' '
+	printf frotz > file &&
+	git add file &&
+	git ls-files -s | sed -e s/100644/120000/ |
+		git update-index --index-info &&
+	git commit -m typechange &&
+	git show >diff &&
+	find_diff <diff >actual &&
+	test_cmp expect.typechange actual
+'
+
+test_done
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
new file mode 100755
index 0000000..a894c60
--- /dev/null
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='rewrite diff on binary file'
+
+. ./test-lib.sh
+
+# We must be large enough to meet the MINIMUM_BREAK_SIZE
+# requirement.
+make_file() {
+	# common first line to help identify rewrite versus regular diff
+	printf "=\n" >file
+	for i in 1 2 3 4 5 6 7 8 9 10
+	do
+		for j in 1 2 3 4 5 6 7 8 9
+		do
+			for k in 1 2 3 4 5
+			do
+				printf "$1\n"
+			done
+		done
+	done >>file
+}
+
+test_expect_success 'create binary file with changes' '
+	make_file "\\0" &&
+	git add file &&
+	make_file "\\01"
+'
+
+test_expect_success 'vanilla diff is binary' '
+	git diff >diff &&
+	grep "Binary files a/file and b/file differ" diff
+'
+
+test_expect_success 'rewrite diff is binary' '
+	git diff -B >diff &&
+	grep "dissimilarity index" diff &&
+	grep "Binary files a/file and b/file differ" diff
+'
+
+test_expect_success 'rewrite diff can show binary patch' '
+	git diff -B --binary >diff &&
+	grep "dissimilarity index" diff &&
+	grep "GIT binary patch" diff
+'
+
+{
+	echo "#!$SHELL_PATH"
+	cat <<'EOF'
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+EOF
+} >dump
+chmod +x dump
+
+test_expect_success 'setup textconv' '
+	echo file diff=foo >.gitattributes &&
+	git config diff.foo.textconv "$PWD"/dump
+'
+
+test_expect_success 'rewrite diff respects textconv' '
+	git diff -B >diff &&
+	grep "dissimilarity index" diff &&
+	grep "^-61" diff &&
+	grep "^-0" diff
+'
+
+test_done
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
new file mode 100755
index 0000000..e4e3e28
--- /dev/null
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='diff hunk fusing'
+
+. ./test-lib.sh
+
+f() {
+	echo $1
+	i=1
+	while test $i -le $2
+	do
+		echo $i
+		i=$(expr $i + 1)
+	done
+	echo $3
+}
+
+t() {
+	case $# in
+	4) hunks=$4; cmd="diff -U$3";;
+	5) hunks=$5; cmd="diff -U$3 --inter-hunk-context=$4";;
+	esac
+	label="$cmd, $1 common $2"
+	file=f$1
+	expected=expected.$file.$3.$hunks
+
+	if ! test -f $file
+	then
+		f A $1 B >$file
+		git add $file
+		git commit -q -m. $file
+		f X $1 Y >$file
+	fi
+
+	test_expect_success "$label: count hunks ($hunks)" "
+		test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
+	"
+
+	test -f $expected &&
+	test_expect_success "$label: check output" "
+		git $cmd $file | grep -v '^index ' >actual &&
+		test_cmp $expected actual
+	"
+}
+
+cat <<EOF >expected.f1.0.1 || exit 1
+diff --git a/f1 b/f1
+--- a/f1
++++ b/f1
+@@ -1,3 +1,3 @@
+-A
++X
+ 1
+-B
++Y
+EOF
+
+cat <<EOF >expected.f1.0.2 || exit 1
+diff --git a/f1 b/f1
+--- a/f1
++++ b/f1
+@@ -1 +1 @@
+-A
++X
+@@ -3 +3 @@ A
+-B
++Y
+EOF
+
+# common lines	ctx	intrctx	hunks
+t 1 line	0		2
+t 1 line	0	0	2
+t 1 line	0	1	1
+t 1 line	0	2	1
+t 1 line	1		1
+
+t 2 lines	0		2
+t 2 lines	0	0	2
+t 2 lines	0	1	2
+t 2 lines	0	2	1
+t 2 lines	1		1
+
+t 3 lines	1		2
+t 3 lines	1	0	2
+t 3 lines	1	1	1
+t 3 lines	1	2	1
+
+t 9 lines	3		2
+t 9 lines	3	2	2
+t 9 lines	3	3	1
+
+test_done
diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh
new file mode 100755
index 0000000..1eb1498
--- /dev/null
+++ b/t/t4033-diff-patience.sh
@@ -0,0 +1,168 @@
+#!/bin/sh
+
+test_description='patience diff algorithm'
+
+. ./test-lib.sh
+
+cat >file1 <<\EOF
+#include <stdio.h>
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+    int i;
+    for(i = 0; i < 10; i++)
+    {
+        printf("Your answer is: ");
+        printf("%d\n", foo);
+    }
+}
+
+int fact(int n)
+{
+    if(n > 1)
+    {
+        return fact(n-1) * n;
+    }
+    return 1;
+}
+
+int main(int argc, char **argv)
+{
+    frobnitz(fact(10));
+}
+EOF
+
+cat >file2 <<\EOF
+#include <stdio.h>
+
+int fib(int n)
+{
+    if(n > 2)
+    {
+        return fib(n-1) + fib(n-2);
+    }
+    return 1;
+}
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+    int i;
+    for(i = 0; i < 10; i++)
+    {
+        printf("%d\n", foo);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    frobnitz(fib(10));
+}
+EOF
+
+cat >expect <<\EOF
+diff --git a/file1 b/file2
+index 6faa5a3..e3af329 100644
+--- a/file1
++++ b/file2
+@@ -1,26 +1,25 @@
+ #include <stdio.h>
+ 
++int fib(int n)
++{
++    if(n > 2)
++    {
++        return fib(n-1) + fib(n-2);
++    }
++    return 1;
++}
++
+ // Frobs foo heartily
+ int frobnitz(int foo)
+ {
+     int i;
+     for(i = 0; i < 10; i++)
+     {
+-        printf("Your answer is: ");
+         printf("%d\n", foo);
+     }
+ }
+ 
+-int fact(int n)
+-{
+-    if(n > 1)
+-    {
+-        return fact(n-1) * n;
+-    }
+-    return 1;
+-}
+-
+ int main(int argc, char **argv)
+ {
+-    frobnitz(fact(10));
++    frobnitz(fib(10));
+ }
+EOF
+
+test_expect_success 'patience diff' '
+
+	test_must_fail git diff --no-index --patience file1 file2 > output &&
+	test_cmp expect output
+
+'
+
+test_expect_success 'patience diff output is valid' '
+
+	mv file2 expect &&
+	git apply < output &&
+	test_cmp expect file2
+
+'
+
+cat >uniq1 <<\EOF
+1
+2
+3
+4
+5
+6
+EOF
+
+cat >uniq2 <<\EOF
+a
+b
+c
+d
+e
+f
+EOF
+
+cat >expect <<\EOF
+diff --git a/uniq1 b/uniq2
+index b414108..0fdf397 100644
+--- a/uniq1
++++ b/uniq2
+@@ -1,6 +1,6 @@
+-1
+-2
+-3
+-4
+-5
+-6
++a
++b
++c
++d
++e
++f
+EOF
+
+test_expect_success 'completely different files' '
+
+	test_must_fail git diff --no-index --patience uniq1 uniq2 > output &&
+	test_cmp expect output
+
+'
+
+test_done
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
new file mode 100755
index 0000000..4508eff
--- /dev/null
+++ b/t/t4034-diff-words.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+test_description='word diff colors'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	git config diff.color.old red
+	git config diff.color.new green
+
+'
+
+decrypt_color () {
+	sed \
+		-e 's/.\[1m/<WHITE>/g' \
+		-e 's/.\[31m/<RED>/g' \
+		-e 's/.\[32m/<GREEN>/g' \
+		-e 's/.\[36m/<BROWN>/g' \
+		-e 's/.\[m/<RESET>/g'
+}
+
+word_diff () {
+	test_must_fail git diff --no-index "$@" pre post > output &&
+	decrypt_color < output > output.decrypted &&
+	test_cmp expect output.decrypted
+}
+
+cat > pre <<\EOF
+h(4)
+
+a = b + c
+EOF
+
+cat > post <<\EOF
+h(4),hh[44]
+
+a = b + c
+
+aa = a
+
+aeff = aeff * ( aaa )
+EOF
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'word diff with runs of whitespace' '
+
+	word_diff --color-words
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh<RESET>[44]
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+cp expect expect.letter-runs-are-words
+
+test_expect_success 'word diff with a regular expression' '
+
+	word_diff --color-words="[a-z]+"
+
+'
+
+test_expect_success 'set a diff driver' '
+	git config diff.testdriver.wordRegex "[^[:space:]]" &&
+	cat <<EOF > .gitattributes
+pre diff=testdriver
+post diff=testdriver
+EOF
+'
+
+test_expect_success 'option overrides .gitattributes' '
+
+	word_diff --color-words="[a-z]+"
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4)<GREEN>,hh[44]<RESET>
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+cp expect expect.non-whitespace-is-word
+
+test_expect_success 'use regex supplied by driver' '
+
+	word_diff --color-words
+
+'
+
+test_expect_success 'set diff.wordRegex option' '
+	git config diff.wordRegex "[[:alnum:]]+"
+'
+
+cp expect.letter-runs-are-words expect
+
+test_expect_success 'command-line overrides config' '
+	word_diff --color-words="[a-z]+"
+'
+
+cp expect.non-whitespace-is-word expect
+
+test_expect_success '.gitattributes override config' '
+	word_diff --color-words
+'
+
+test_expect_success 'remove diff driver regex' '
+	git config --unset diff.testdriver.wordRegex
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh[44<RESET>]
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+
+test_expect_success 'use configured regex' '
+	word_diff --color-words
+'
+
+echo 'aaa (aaa)' > pre
+echo 'aaa (aaa) aaa' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index c29453b..be22f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+aaa (aaa) <GREEN>aaa<RESET>
+EOF
+
+test_expect_success 'test parsing words for newline' '
+
+	word_diff --color-words="a+"
+
+
+'
+
+echo '(:' > pre
+echo '(' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 289cb9d..2d06f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+(<RED>:<RESET>
+EOF
+
+test_expect_success 'test when words are only removed at the end' '
+
+	word_diff --color-words=.
+
+'
+
+test_done
diff --git a/t/t4017-quiet.sh b/t/t4035-diff-quiet.sh
similarity index 100%
rename from t/t4017-quiet.sh
rename to t/t4035-diff-quiet.sh
diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
similarity index 100%
rename from t/t4021-format-patch-signer-mime.sh
rename to t/t4036-format-patch-signer-mime.sh
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index e0c6774..9b433de 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -17,13 +17,13 @@
 	test_expect_success "$title" '
 		git apply --stat --summary \
 			<"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-		test_cmp ../t4100/t-apply-$num.expect current
+		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 
 	test_expect_success "$title with recount" '
 		sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
 		git apply --recount --stat --summary >current &&
-		test_cmp ../t4100/t-apply-$num.expect current
+		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 done <<\EOF
 rename
diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh
index da8abcf..e3443d0 100755
--- a/t/t4101-apply-nonl.sh
+++ b/t/t4101-apply-nonl.sh
@@ -21,9 +21,10 @@
   do
     test $i -eq $j && continue
     cat frotz.$i >frotz
-    test_expect_success \
-        "apply diff between $i and $j" \
-	"git apply <../t4101/diff.$i-$j && diff frotz.$j frotz"
+    test_expect_success "apply diff between $i and $j" '
+	git apply <"$TEST_DIRECTORY"/t4101/diff.$i-$j &&
+	test_cmp frotz.$j frotz
+    '
   done
 done
 
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index d42abff..1597965 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -31,14 +31,16 @@
 test_expect_success apply \
     'git apply --index --stat --summary --apply test-patch'
 
-if [ "$(git config --get core.filemode)" = false ]
+if test "$(git config --bool core.filemode)" = false
 then
 	say 'filemode disabled on the filesystem'
 else
-	test_expect_success validate \
-	    'test -f bar && ls -l bar | grep "^-..x......"'
+	test_set_prereq FILEMODE
 fi
 
+test_expect_success FILEMODE validate \
+	    'test -f bar && ls -l bar | grep "^-..x......"'
+
 test_expect_success 'apply reverse' \
     'git apply -R --index --stat --summary --apply test-patch &&
      test "$(cat foo)" = "This is foo"'
diff --git a/t/t4106-apply-stdin.sh b/t/t4106-apply-stdin.sh
new file mode 100755
index 0000000..72467a1
--- /dev/null
+++ b/t/t4106-apply-stdin.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='git apply --numstat - <patch'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo hello >text &&
+	git add text &&
+	echo goodbye >text &&
+	git diff >patch
+'
+
+test_expect_success 'git apply --numstat - < patch' '
+	echo "1	1	text" >expect &&
+	git apply --numstat - <patch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git apply --numstat - < patch patch' '
+	for i in 1 2; do echo "1	1	text"; done >expect &&
+	git apply --numstat - < patch patch >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index 0f185ca..99ec13d 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -9,6 +9,12 @@
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
+
 test_expect_success 'setup repository and commits' '
 	echo "hello world" > foo &&
 	echo "hi planet" > bar &&
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 9ace578..b852e58 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -9,6 +9,12 @@
 
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
+
 test_expect_success setup '
 
 	ln -s path1/path2/path3/path4/path5 link1 &&
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 841773f..0d3c1d5 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -3,6 +3,12 @@
 test_description='apply to deeper directory without getting fooled with symlink'
 . ./test-lib.sh
 
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
+
 lecho () {
 	for l_
 	do
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
new file mode 100755
index 0000000..fc7af04
--- /dev/null
+++ b/t/t4129-apply-samemode.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='applying patch with mode bits'
+
+. ./test-lib.sh
+
+if test "$(git config --bool core.filemode)" = false
+then
+	say 'filemode disabled on the filesystem'
+else
+	test_set_prereq FILEMODE
+fi
+
+test_expect_success setup '
+	echo original >file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git tag initial &&
+	echo modified >file &&
+	git diff --stat -p >patch-0.txt &&
+	chmod +x file &&
+	git diff --stat -p >patch-1.txt
+'
+
+test_expect_success FILEMODE 'same mode (no index)' '
+	git reset --hard &&
+	chmod +x file &&
+	git apply patch-0.txt &&
+	test -x file
+'
+
+test_expect_success FILEMODE 'same mode (with index)' '
+	git reset --hard &&
+	chmod +x file &&
+	git add file &&
+	git apply --index patch-0.txt &&
+	test -x file &&
+	git diff --exit-code
+'
+
+test_expect_success FILEMODE 'same mode (index only)' '
+	git reset --hard &&
+	chmod +x file &&
+	git add file &&
+	git apply --cached patch-0.txt &&
+	git ls-files -s file | grep "^100755"
+'
+
+test_expect_success FILEMODE 'mode update (no index)' '
+	git reset --hard &&
+	git apply patch-1.txt &&
+	test -x file
+'
+
+test_expect_success FILEMODE 'mode update (with index)' '
+	git reset --hard &&
+	git apply --index patch-1.txt &&
+	test -x file &&
+	git diff --exit-code
+'
+
+test_expect_success FILEMODE 'mode update (index only)' '
+	git reset --hard &&
+	git apply --cached patch-1.txt &&
+	git ls-files -s file | grep "^100755"
+'
+
+test_done
diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
new file mode 100755
index 0000000..8623dbe
--- /dev/null
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git apply handling criss-cross rename patch.'
+. ./test-lib.sh
+
+create_file() {
+	cnt=0
+	while test $cnt -le 100
+	do
+		cnt=$(($cnt + 1))
+		echo "$2" >> "$1"
+	done
+}
+
+test_expect_success 'setup' '
+	create_file file1 "File1 contents" &&
+	create_file file2 "File2 contents" &&
+	git add file1 file2 &&
+	git commit -m 1
+'
+
+test_expect_success 'criss-cross rename' '
+	mv file1 tmp &&
+	mv file2 file1 &&
+	mv tmp file2
+'
+
+test_expect_success 'diff -M -B' '
+	git diff -M -B > diff &&
+	git reset --hard
+
+'
+
+test_expect_success 'apply' '
+	git apply diff
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 1be5fb3..d6ebbae 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -165,7 +165,7 @@
 	git am --keep patch4 &&
 	! test -d .git/rebase-apply &&
 	git cat-file commit HEAD |
-		grep -q -F "Re: Re: Re: [PATCH 1/5 v2] third"
+		fgrep "Re: Re: Re: [PATCH 1/5 v2] third"
 '
 
 test_expect_success 'am -3 falls back to 3-way merge' '
@@ -257,4 +257,52 @@
 	test -z "$(git diff second)"
 '
 
+test_expect_success 'am --committer-date-is-author-date' '
+	git checkout first &&
+	test_tick &&
+	git am --committer-date-is-author-date patch1 &&
+	git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+	at=$(sed -ne "/^author /s/.*> //p" head1) &&
+	ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
+	test "$at" = "$ct"
+'
+
+test_expect_success 'am without --committer-date-is-author-date' '
+	git checkout first &&
+	test_tick &&
+	git am patch1 &&
+	git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+	at=$(sed -ne "/^author /s/.*> //p" head1) &&
+	ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
+	test "$at" != "$ct"
+'
+
+# This checks for +0000 because TZ is set to UTC and that should
+# show up when the current time is used. The date in message is set
+# by test_tick that uses -0700 timezone; if this feature does not
+# work, we will see that instead of +0000.
+test_expect_success 'am --ignore-date' '
+	git checkout first &&
+	test_tick &&
+	git am --ignore-date patch1 &&
+	git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+	at=$(sed -ne "/^author /s/.*> //p" head1) &&
+	echo "$at" | grep "+0000"
+'
+
+test_expect_success 'am into an unborn branch' '
+	rm -fr subdir &&
+	mkdir -p subdir &&
+	git format-patch --numbered-files -o subdir -1 first &&
+	(
+		cd subdir &&
+		git init &&
+		git am 1
+	) &&
+	result=$(
+		cd subdir && git rev-parse HEAD^{tree}
+	) &&
+	test "z$result" = "z$(git rev-parse first^{tree})"
+'
+
 test_done
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 4448aba..2b912d7 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -22,7 +22,7 @@
 		test_tick &&
 		git commit -a -m $i || break
 	done &&
-	git format-patch initial &&
+	git format-patch --no-numbered initial &&
 	git checkout -b side initial &&
 	echo local change >file-2-expect
 '
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 0ab925c..b986190 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -16,27 +16,71 @@
 	test_tick &&
 	git commit -m second &&
 
+	git mv one ichi &&
+	test_tick &&
+	git commit -m third &&
+
+	cp ichi ein &&
+	git add ein &&
+	test_tick &&
+	git commit -m fourth &&
+
 	mkdir a &&
 	echo ni >a/two &&
 	git add a/two &&
 	test_tick &&
-	git commit -m third &&
+	git commit -m fifth  &&
 
-	echo san >a/three &&
-	git add a/three &&
+	git rm a/two &&
 	test_tick &&
-	git commit -m fourth &&
+	git commit -m sixth
 
-	git rm a/three &&
-	test_tick &&
-	git commit -m fifth
+'
 
+printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial" > expect
+test_expect_success 'pretty' '
+
+	git log --pretty="format:%s" > actual &&
+	test_cmp expect actual
+'
+
+printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial\n" > expect
+test_expect_success 'pretty (tformat)' '
+
+	git log --pretty="tformat:%s" > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty (shortcut)' '
+
+	git log --pretty="%s" > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'format' '
+
+	git log --format="%s" > actual &&
+	test_cmp expect actual
+'
+
+cat > expect << EOF
+804a787 sixth
+394ef78 fifth
+5d31159 fourth
+2fbe8c0 third
+f7dab8e second
+3a2fdcb initial
+EOF
+test_expect_success 'oneline' '
+
+	git log --oneline > actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'diff-filter=A' '
 
 	actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
-	expect=$(echo fourth ; echo third ; echo initial) &&
+	expect=$(echo fifth ; echo fourth ; echo third ; echo initial) &&
 	test "$actual" = "$expect" || {
 		echo Oops
 		echo "Actual: $actual"
@@ -60,7 +104,43 @@
 test_expect_success 'diff-filter=D' '
 
 	actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) &&
-	expect=$(echo fifth) &&
+	expect=$(echo sixth ; echo third) &&
+	test "$actual" = "$expect" || {
+		echo Oops
+		echo "Actual: $actual"
+		false
+	}
+
+'
+
+test_expect_success 'diff-filter=R' '
+
+	actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
+	expect=$(echo third) &&
+	test "$actual" = "$expect" || {
+		echo Oops
+		echo "Actual: $actual"
+		false
+	}
+
+'
+
+test_expect_success 'diff-filter=C' '
+
+	actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
+	expect=$(echo fourth) &&
+	test "$actual" = "$expect" || {
+		echo Oops
+		echo "Actual: $actual"
+		false
+	}
+
+'
+
+test_expect_success 'git log --follow' '
+
+	actual=$(git log --follow --pretty="format:%s" ichi) &&
+	expect=$(echo third ; echo second ; echo initial) &&
 	test "$actual" = "$expect" || {
 		echo Oops
 		echo "Actual: $actual"
@@ -72,6 +152,7 @@
 test_expect_success 'setup case sensitivity tests' '
 	echo case >one &&
 	test_tick &&
+	git add one
 	git commit -a -m Second
 '
 
@@ -93,5 +174,153 @@
 	test_cmp expect actual
 '
 
+cat > expect <<EOF
+* Second
+* sixth
+* fifth
+* fourth
+* third
+* second
+* initial
+EOF
+
+test_expect_success 'simple log --graph' '
+	git log --graph --pretty=tformat:%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'set up merge history' '
+	git checkout -b side HEAD~4 &&
+	test_commit side-1 1 1 &&
+	test_commit side-2 2 2 &&
+	git checkout master &&
+	git merge side
+'
+
+cat > expect <<\EOF
+*   Merge branch 'side'
+|\
+| * side-2
+| * side-1
+* | Second
+* | sixth
+* | fifth
+* | fourth
+|/
+* third
+* second
+* initial
+EOF
+
+test_expect_success 'log --graph with merge' '
+	git log --graph --date-order --pretty=tformat:%s |
+		sed "s/ *$//" >actual &&
+	test_cmp expect actual
+'
+
+cat > expect <<\EOF
+*   commit master
+|\  Merge: A B
+| | Author: A U Thor <author@example.com>
+| |
+| |     Merge branch 'side'
+| |
+| * commit side
+| | Author: A U Thor <author@example.com>
+| |
+| |     side-2
+| |
+| * commit tags/side-1
+| | Author: A U Thor <author@example.com>
+| |
+| |     side-1
+| |
+* | commit master~1
+| | Author: A U Thor <author@example.com>
+| |
+| |     Second
+| |
+* | commit master~2
+| | Author: A U Thor <author@example.com>
+| |
+| |     sixth
+| |
+* | commit master~3
+| | Author: A U Thor <author@example.com>
+| |
+| |     fifth
+| |
+* | commit master~4
+|/  Author: A U Thor <author@example.com>
+|
+|       fourth
+|
+* commit tags/side-1~1
+| Author: A U Thor <author@example.com>
+|
+|     third
+|
+* commit tags/side-1~2
+| Author: A U Thor <author@example.com>
+|
+|     second
+|
+* commit tags/side-1~3
+  Author: A U Thor <author@example.com>
+
+      initial
+EOF
+
+test_expect_success 'log --graph with full output' '
+	git log --graph --date-order --pretty=short |
+		git name-rev --name-only --stdin |
+		sed "s/Merge:.*/Merge: A B/;s/ *$//" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'set up more tangled history' '
+	git checkout -b tangle HEAD~6 &&
+	test_commit tangle-a tangle-a a &&
+	git merge master~3 &&
+	git merge side~1 &&
+	git checkout master &&
+	git merge tangle
+'
+
+cat > expect <<\EOF
+*   Merge branch 'tangle'
+|\
+| *   Merge branch 'side' (early part) into tangle
+| |\
+| * \   Merge branch 'master' (early part) into tangle
+| |\ \
+| * | | tangle-a
+* | | |   Merge branch 'side'
+|\ \ \ \
+| * | | | side-2
+| | | |/
+| | |/|
+| |/| |
+| * | | side-1
+* | | | Second
+* | | | sixth
+| | |/
+| |/|
+|/| |
+* | | fifth
+* | | fourth
+|/ /
+* | third
+|/
+* second
+* initial
+EOF
+
+test_expect_success 'log --graph with merge' '
+	git log --graph --date-order --pretty=tformat:%s |
+		sed "s/ *$//" >actual &&
+	test_cmp expect actual
+'
+
 test_done
 
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
new file mode 100755
index 0000000..9a7d1b4
--- /dev/null
+++ b/t/t4203-mailmap.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+
+test_description='.mailmap configurations'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo one >one &&
+	git add one &&
+	test_tick &&
+	git commit -m initial &&
+	echo two >>one &&
+	git add one &&
+	git commit --author "nick1 <bugs@company.xx>" -m second
+'
+
+cat >expect <<\EOF
+A U Thor (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+
+test_expect_success 'No mailmap' '
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Repo Guy (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+
+test_expect_success 'default .mailmap' '
+	echo "Repo Guy <author@example.com>" > .mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+# Using a mailmap file in a subdirectory of the repo here, but
+# could just as well have been a file outside of the repository
+cat >expect <<\EOF
+Internal Guy (1):
+      second
+
+Repo Guy (1):
+      initial
+
+EOF
+test_expect_success 'mailmap.file set' '
+	mkdir internal_mailmap &&
+	echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
+	git config mailmap.file internal_mailmap/.mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<\EOF
+External Guy (1):
+      initial
+
+Internal Guy (1):
+      second
+
+EOF
+test_expect_success 'mailmap.file override' '
+	echo "External Guy <author@example.com>" >> internal_mailmap/.mailmap &&
+	git config mailmap.file internal_mailmap/.mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Repo Guy (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+
+test_expect_success 'mailmap.file non-existant' '
+	rm internal_mailmap/.mailmap &&
+	rmdir internal_mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<\EOF
+A U Thor (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+test_expect_success 'No mailmap files, but configured' '
+	rm .mailmap &&
+	git shortlog HEAD >actual &&
+	test_cmp expect actual
+'
+
+# Extended mailmap configurations should give us the following output for shortlog
+cat >expect <<\EOF
+A U Thor <author@example.com> (1):
+      initial
+
+CTO <cto@company.xx> (1):
+      seventh
+
+Other Author <other@author.xx> (2):
+      third
+      fourth
+
+Santa Claus <santa.claus@northpole.xx> (2):
+      fifth
+      sixth
+
+Some Dude <some@dude.xx> (1):
+      second
+
+EOF
+
+test_expect_success 'Shortlog output (complex mapping)' '
+	echo three >>one &&
+	git add one &&
+	test_tick &&
+	git commit --author "nick2 <bugs@company.xx>" -m third &&
+
+	echo four >>one &&
+	git add one &&
+	test_tick &&
+	git commit --author "nick2 <nick2@company.xx>" -m fourth &&
+
+	echo five >>one &&
+	git add one &&
+	test_tick &&
+	git commit --author "santa <me@company.xx>" -m fifth &&
+
+	echo six >>one &&
+	git add one &&
+	test_tick &&
+	git commit --author "claus <me@company.xx>" -m sixth &&
+
+	echo seven >>one &&
+	git add one &&
+	test_tick &&
+	git commit --author "CTO <cto@coompany.xx>" -m seventh &&
+
+	mkdir internal_mailmap &&
+	echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
+	echo "<cto@company.xx>                       <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
+	echo "Some Dude <some@dude.xx>         nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+	echo "Other Author <other@author.xx>   nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+	echo "Other Author <other@author.xx>         <nick2@company.xx>" >> internal_mailmap/.mailmap &&
+	echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+	echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+
+	git shortlog -e HEAD >actual &&
+	test_cmp expect actual
+
+'
+
+# git log with --pretty format which uses the name and email mailmap placemarkers
+cat >expect <<\EOF
+Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author A U Thor <author@example.com> maps to A U Thor <author@example.com>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+EOF
+
+test_expect_success 'Log output (complex mapping)' '
+	git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
+# git blame
+cat >expect <<\EOF
+^3a2fdcb (A U Thor     2005-04-07 15:13:13 -0700 1) one
+7de6f99b (Some Dude    2005-04-07 15:13:13 -0700 2) two
+5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three
+ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four
+5ab6d4fa (Santa Claus  2005-04-07 15:16:13 -0700 5) five
+38a42d8b (Santa Claus  2005-04-07 15:17:13 -0700 6) six
+8ddc0386 (CTO          2005-04-07 15:18:13 -0700 7) seven
+EOF
+
+test_expect_success 'Blame output (complex mapping)' '
+	git blame one >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
new file mode 100755
index 0000000..04f7bae
--- /dev/null
+++ b/t/t4204-patch-id.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git patch-id'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit initial foo a &&
+	test_commit first foo b &&
+	git checkout -b same HEAD^ &&
+	test_commit same-msg foo b &&
+	git checkout -b notsame HEAD^ &&
+	test_commit notsame-msg foo c
+'
+
+test_expect_success 'patch-id output is well-formed' '
+	git log -p -1 | git patch-id > output &&
+	grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
+'
+
+get_patch_id () {
+	git log -p -1 "$1" | git patch-id |
+		sed "s# .*##" > patch-id_"$1"
+}
+
+test_expect_success 'patch-id detects equality' '
+	get_patch_id master &&
+	get_patch_id same &&
+	test_cmp patch-id_master patch-id_same
+'
+
+test_expect_success 'patch-id detects inequality' '
+	get_patch_id master &&
+	get_patch_id notsame &&
+	! test_cmp patch-id_master patch-id_notsame
+'
+
+test_done
diff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh
new file mode 100755
index 0000000..f603c1b
--- /dev/null
+++ b/t/t4252-am-options.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='git am with options and not losing them'
+. ./test-lib.sh
+
+tm="$TEST_DIRECTORY/t4252"
+
+test_expect_success setup '
+	cp "$tm/file-1-0" file-1 &&
+	cp "$tm/file-2-0" file-2 &&
+	git add file-1 file-2 &&
+	test_tick &&
+	git commit -m initial &&
+	git tag initial
+'
+
+test_expect_success 'interrupted am --whitespace=fix' '
+	rm -rf .git/rebase-apply &&
+	git reset --hard initial &&
+	test_must_fail git am --whitespace=fix "$tm"/am-test-1-? &&
+	git am --skip &&
+	grep 3 file-1 &&
+	grep "^Six$" file-2
+'
+
+test_expect_success 'interrupted am -C1' '
+	rm -rf .git/rebase-apply &&
+	git reset --hard initial &&
+	test_must_fail git am -C1 "$tm"/am-test-2-? &&
+	git am --skip &&
+	grep 3 file-1 &&
+	grep "^Three$" file-2
+'
+
+test_expect_success 'interrupted am -p2' '
+	rm -rf .git/rebase-apply &&
+	git reset --hard initial &&
+	test_must_fail git am -p2 "$tm"/am-test-3-? &&
+	git am --skip &&
+	grep 3 file-1 &&
+	grep "^Three$" file-2
+'
+
+test_expect_success 'interrupted am -C1 -p2' '
+	rm -rf .git/rebase-apply &&
+	git reset --hard initial &&
+	test_must_fail git am -p2 -C1 "$tm"/am-test-4-? &&
+	git am --skip &&
+	grep 3 file-1 &&
+	grep "^Three$" file-2
+'
+
+test_expect_success 'interrupted am --directory="frotz nitfol"' '
+	rm -rf .git/rebase-apply &&
+	git reset --hard initial &&
+	test_must_fail git am --directory="frotz nitfol" "$tm"/am-test-5-? &&
+	git am --skip &&
+	grep One "frotz nitfol/file-5"
+'
+
+test_expect_success 'apply to a funny path' '
+	with_sq="with'\''sq"
+	rm -fr .git/rebase-apply &&
+	git reset --hard initial &&
+	git am --directory="$with_sq" "$tm"/am-test-5-2 &&
+	test -f "$with_sq/file-5"
+'
+
+test_expect_success 'am --reject' '
+	rm -rf .git/rebase-apply &&
+	git reset --hard initial &&
+	test_must_fail git am --reject "$tm"/am-test-6-1 &&
+	grep "@@ -1,3 +1,3 @@" file-2.rej &&
+	test_must_fail git diff-files --exit-code --quiet file-2 &&
+	grep "[-]-reject" .git/rebase-apply/apply-opt
+'
+
+test_done
diff --git a/t/t4252/am-test-1-1 b/t/t4252/am-test-1-1
new file mode 100644
index 0000000..b0c09dc
--- /dev/null
+++ b/t/t4252/am-test-1-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected because the first line in the
+context does not match.
+
+diff --git i/file-1 w/file-1
+index 06e567b..10f8342 100644
+--- i/file-1
++++ w/file-1
+@@ -1,6 +1,6 @@
+ One
+ 2
+-3
++Three 
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-1-2 b/t/t4252/am-test-1-2
new file mode 100644
index 0000000..1b874ae
--- /dev/null
+++ b/t/t4252/am-test-1-2
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with --whitespace=fix should lose 
+the trailing whitespace after "Six".
+
+diff --git i/file-2 w/file-2
+index 06e567b..b6f3a16 100644
+--- i/file-2
++++ w/file-2
+@@ -1,7 +1,7 @@
+ 1
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six 
+ 7
diff --git a/t/t4252/am-test-2-1 b/t/t4252/am-test-2-1
new file mode 100644
index 0000000..feda94a
--- /dev/null
+++ b/t/t4252/am-test-2-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected even with -C1 because the
+preimage line in the context does not match.
+
+diff --git i/file-1 w/file-1
+index 06e567b..10f8342 100644
+--- i/file-1
++++ w/file-1
+@@ -1,6 +1,6 @@
+ 1
+ 2
+-Tres
++Three 
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-2-2 b/t/t4252/am-test-2-2
new file mode 100644
index 0000000..2ac6600
--- /dev/null
+++ b/t/t4252/am-test-2-2
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with -C1 should be successful even though 
+the first line in the context does not match.
+
+diff --git i/file-2 w/file-2
+index 06e567b..b6f3a16 100644
+--- i/file-2
++++ w/file-2
+@@ -1,7 +1,7 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-3-1 b/t/t4252/am-test-3-1
new file mode 100644
index 0000000..608e5ab
--- /dev/null
+++ b/t/t4252/am-test-3-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected even with -p2 because the
+preimage line in the context does not match.
+
+diff --git i/junk/file-1 w/junk/file-1
+index 06e567b..10f8342 100644
+--- i/junk/file-1
++++ w/junk/file-1
+@@ -1,6 +1,6 @@
+ 1
+ 2
+-Tres
++Three 
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-3-2 b/t/t4252/am-test-3-2
new file mode 100644
index 0000000..0081b96
--- /dev/null
+++ b/t/t4252/am-test-3-2
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with -p2 should be successful even though
+the patch is against a wrong level.
+
+diff --git i/junk/file-2 w/junk/file-2
+index 06e567b..b6f3a16 100644
+--- i/junk/file-2
++++ w/junk/file-2
+@@ -1,7 +1,7 @@
+ 1
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-4-1 b/t/t4252/am-test-4-1
new file mode 100644
index 0000000..e48cd6c
--- /dev/null
+++ b/t/t4252/am-test-4-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected even with -C1 -p2 because
+the preimage line in the context does not match.
+
+diff --git i/junk/file-1 w/junk/file-1
+index 06e567b..10f8342 100644
+--- i/junk/file-1
++++ w/junk/file-1
+@@ -1,6 +1,6 @@
+ 1
+ 2
+-Tres
++Three 
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-4-2 b/t/t4252/am-test-4-2
new file mode 100644
index 0000000..0e69bfa
--- /dev/null
+++ b/t/t4252/am-test-4-2
@@ -0,0 +1,22 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with -C1 -p2 should be successful even though
+the patch is against a wrong level and the first context line does
+not match.
+
+diff --git i/junk/file-2 w/junk/file-2
+index 06e567b..b6f3a16 100644
+--- i/junk/file-2
++++ w/junk/file-2
+@@ -1,7 +1,7 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-5-1 b/t/t4252/am-test-5-1
new file mode 100644
index 0000000..da7bf29
--- /dev/null
+++ b/t/t4252/am-test-5-1
@@ -0,0 +1,20 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with --directory='frotz nitfol' should fail
+
+diff --git i/junk/file-2 w/junk/file-2
+index 06e567b..b6f3a16 100644
+--- i/junk/file-2
++++ w/junk/file-2
+@@ -1,7 +1,7 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-5-2 b/t/t4252/am-test-5-2
new file mode 100644
index 0000000..373025b
--- /dev/null
+++ b/t/t4252/am-test-5-2
@@ -0,0 +1,15 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with --directory='frotz nitfol' should succeed
+
+diff --git i/file-5 w/file-5
+new file mode 100644
+index 000000..1d6ed9f
+--- /dev/null
++++ w/file-5
+@@ -0,0 +1,3 @@
++One
++two
++three
diff --git a/t/t4252/am-test-6-1 b/t/t4252/am-test-6-1
new file mode 100644
index 0000000..a8859e9
--- /dev/null
+++ b/t/t4252/am-test-6-1
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Huh
+
+Should fail and leave rejects
+
+diff --git i/file-2 w/file-2
+index 06e567b..b6f3a16 100644
+--- i/file-2
++++ w/file-2
+@@ -1,3 +1,3 @@
+-0
++One
+ 2
+ 3
+@@ -4,4 +4,4 @@
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/file-1-0 b/t/t4252/file-1-0
new file mode 100644
index 0000000..06e567b
--- /dev/null
+++ b/t/t4252/file-1-0
@@ -0,0 +1,7 @@
+1
+2
+3
+4
+5
+6
+7
diff --git a/t/t4252/file-2-0 b/t/t4252/file-2-0
new file mode 100644
index 0000000..06e567b
--- /dev/null
+++ b/t/t4252/file-2-0
@@ -0,0 +1,7 @@
+1
+2
+3
+4
+5
+6
+7
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index c942c8b..abb41b0 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -37,7 +37,11 @@
      cp /bin/sh a/bin &&
      printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
      printf "A not substituted O" >a/substfile2 &&
-     ln -s a a/l1 &&
+     if test_have_prereq SYMLINKS; then
+	ln -s a a/l1
+     else
+	printf %s a > a/l1
+     fi &&
      (p=long_path_to_a_file && cd a &&
       for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
       echo text >file_with_long_path) &&
@@ -46,7 +50,7 @@
 test_expect_success \
     'add ignored file' \
     'echo ignore me >a/ignored &&
-     echo ignored export-ignore >.gitattributes'
+     echo ignored export-ignore >.git/info/attributes'
 
 test_expect_success \
     'add files to repository' \
@@ -60,7 +64,7 @@
 test_expect_success \
     'create bare clone' \
     'git clone --bare . bare.git &&
-     cp .gitattributes bare.git/info/attributes'
+     cp .git/info/attributes bare.git/info/attributes'
 
 test_expect_success \
     'remove ignored file' \
@@ -76,7 +80,7 @@
 
 test_expect_success \
     'git archive vs. git tar-tree' \
-    'diff b.tar b2.tar'
+    'test_cmp b.tar b2.tar'
 
 test_expect_success \
     'git archive in a bare repo' \
@@ -86,18 +90,22 @@
     'git archive vs. the same in a bare repo' \
     'test_cmp b.tar b3.tar'
 
+test_expect_success 'git archive with --output' \
+    'git archive --output=b4.tar HEAD &&
+    test_cmp b.tar b4.tar'
+
 test_expect_success \
     'validate file modification time' \
     'mkdir extract &&
      "$TAR" xf b.tar -C extract a/a &&
      test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
      echo "1117231200" >expected.mtime &&
-     diff expected.mtime b.mtime'
+     test_cmp expected.mtime b.mtime'
 
 test_expect_success \
     'git get-tar-commit-id' \
     'git get-tar-commit-id <b.tar >b.commitid &&
-     diff .git/$(git symbolic-ref HEAD) b.commitid'
+     test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
 
 test_expect_success \
     'extract tar archive' \
@@ -106,7 +114,7 @@
 test_expect_success \
     'validate filenames' \
     '(cd b/a && find .) | sort >b.lst &&
-     diff a.lst b.lst'
+     test_cmp a.lst b.lst'
 
 test_expect_success \
     'validate file contents' \
@@ -123,7 +131,7 @@
 test_expect_success \
     'validate filenames with prefix' \
     '(cd c/prefix/a && find .) | sort >c.lst &&
-     diff a.lst c.lst'
+     test_cmp a.lst c.lst'
 
 test_expect_success \
     'validate file contents with prefix' \
@@ -131,10 +139,11 @@
 
 test_expect_success \
     'create archives with substfiles' \
-    'echo "substfile?" export-subst >a/.gitattributes &&
+    'cp .git/info/attributes .git/info/attributes.before &&
+     echo "substfile?" export-subst >>.git/info/attributes &&
      git archive HEAD >f.tar &&
      git archive --prefix=prefix/ HEAD >g.tar &&
-     rm a/.gitattributes'
+     mv .git/info/attributes.before .git/info/attributes'
 
 test_expect_success \
     'extract substfiles' \
@@ -144,8 +153,8 @@
      'validate substfile contents' \
      'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
       >f/a/substfile1.expected &&
-      diff f/a/substfile1.expected f/a/substfile1 &&
-      diff a/substfile2 f/a/substfile2
+      test_cmp f/a/substfile1.expected f/a/substfile1 &&
+      test_cmp a/substfile2 f/a/substfile2
 '
 
 test_expect_success \
@@ -156,8 +165,8 @@
      'validate substfile contents from archive with prefix' \
      'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
       >g/prefix/a/substfile1.expected &&
-      diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
-      diff a/substfile2 g/prefix/a/substfile2
+      test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
+      test_cmp a/substfile2 g/prefix/a/substfile2
 '
 
 test_expect_success \
@@ -172,23 +181,27 @@
     'git archive --format=zip vs. the same in a bare repo' \
     'test_cmp d.zip d1.zip'
 
+test_expect_success 'git archive --format=zip with --output' \
+    'git archive --format=zip --output=d2.zip HEAD &&
+    test_cmp d.zip d2.zip'
+
 $UNZIP -v >/dev/null 2>&1
 if [ $? -eq 127 ]; then
-	echo "Skipping ZIP tests, because unzip was not found"
-	test_done
-	exit
+	say "Skipping ZIP tests, because unzip was not found"
+else
+	test_set_prereq UNZIP
 fi
 
-test_expect_success \
+test_expect_success UNZIP \
     'extract ZIP archive' \
     '(mkdir d && cd d && $UNZIP ../d.zip)'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate filenames' \
     '(cd d/a && find .) | sort >d.lst &&
-     diff a.lst d.lst'
+     test_cmp a.lst d.lst'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate file contents' \
     'diff -r a d/a'
 
@@ -196,16 +209,16 @@
     'git archive --format=zip with prefix' \
     'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
 
-test_expect_success \
+test_expect_success UNZIP \
     'extract ZIP archive with prefix' \
     '(mkdir e && cd e && $UNZIP ../e.zip)'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate filenames with prefix' \
     '(cd e/prefix/a && find .) | sort >e.lst &&
-     diff a.lst e.lst'
+     test_cmp a.lst e.lst'
 
-test_expect_success \
+test_expect_success UNZIP \
     'validate file contents with prefix' \
     'diff -r a e/prefix/a'
 
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
new file mode 100755
index 0000000..426b319
--- /dev/null
+++ b/t/t5001-archive-attr.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='git archive attribute tests'
+
+. ./test-lib.sh
+
+SUBSTFORMAT=%H%n
+
+test_expect_exists() {
+	test_expect_success " $1 exists" "test -e $1"
+}
+
+test_expect_missing() {
+	test_expect_success " $1 does not exist" "test ! -e $1"
+}
+
+test_expect_success 'setup' '
+	echo ignored >ignored &&
+	echo ignored export-ignore >>.git/info/attributes &&
+	git add ignored &&
+
+	echo ignored by tree >ignored-by-tree &&
+	echo ignored-by-tree export-ignore >.gitattributes &&
+	git add ignored-by-tree .gitattributes &&
+
+	echo ignored by worktree >ignored-by-worktree &&
+	echo ignored-by-worktree export-ignore >.gitattributes &&
+	git add ignored-by-worktree &&
+
+	printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
+	printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
+	printf "A not substituted O" >substfile2 &&
+	echo "substfile?" export-subst >>.git/info/attributes &&
+	git add nosubstfile substfile1 substfile2 &&
+
+	git commit -m. &&
+
+	git clone --bare . bare &&
+	cp .git/info/attributes bare/info/attributes
+'
+
+test_expect_success 'git archive' '
+	git archive HEAD >archive.tar &&
+	(mkdir archive && cd archive && "$TAR" xf -) <archive.tar
+'
+
+test_expect_missing	archive/ignored
+test_expect_missing	archive/ignored-by-tree
+test_expect_exists	archive/ignored-by-worktree
+
+test_expect_success 'git archive with worktree attributes' '
+	git archive --worktree-attributes HEAD >worktree.tar &&
+	(mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
+'
+
+test_expect_missing	worktree/ignored
+test_expect_exists	worktree/ignored-by-tree
+test_expect_missing	worktree/ignored-by-worktree
+
+test_expect_success 'git archive vs. bare' '
+	(cd bare && git archive HEAD) >bare-archive.tar &&
+	test_cmp archive.tar bare-archive.tar
+'
+
+test_expect_success 'git archive with worktree attributes, bare' '
+	(cd bare && git archive --worktree-attributes HEAD) >bare-worktree.tar &&
+	(mkdir bare-worktree && cd bare-worktree && "$TAR" xf -) <bare-worktree.tar
+'
+
+test_expect_missing	bare-worktree/ignored
+test_expect_exists	bare-worktree/ignored-by-tree
+test_expect_exists	bare-worktree/ignored-by-worktree
+
+test_expect_success 'export-subst' '
+	git log "--pretty=format:A${SUBSTFORMAT}O" HEAD >substfile1.expected &&
+	test_cmp nosubstfile archive/nosubstfile &&
+	test_cmp substfile1.expected archive/substfile1 &&
+	test_cmp substfile2 archive/substfile2
+'
+
+test_expect_success 'git tar-tree vs. git archive with worktree attributes' '
+	git tar-tree HEAD >tar-tree.tar &&
+	test_cmp worktree.tar tar-tree.tar
+'
+
+test_expect_success 'git tar-tree vs. git archive with worktree attrs, bare' '
+	(cd bare && git tar-tree HEAD) >bare-tar-tree.tar &&
+	test_cmp bare-worktree.tar bare-tar-tree.tar
+'
+
+test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index b851b3a..e70ea94 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -8,21 +8,22 @@
 . ./test-lib.sh
 
 test_expect_success 'split sample box' \
-	'git mailsplit -o. ../t5100/sample.mbox >last &&
+	'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
 	last=`cat last` &&
 	echo total is $last &&
 	test `cat last` = 13'
 
 for mail in `echo 00*`
 do
-	test_expect_success "mailinfo $mail" \
-		"git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
+	test_expect_success "mailinfo $mail" '
+		git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
 		echo msg &&
-		diff ../t5100/msg$mail msg$mail &&
+		test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
 		echo patch &&
-		diff ../t5100/patch$mail patch$mail &&
+		test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
 		echo info &&
-		diff ../t5100/info$mail info$mail"
+		test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+	'
 done
 
 
@@ -49,8 +50,8 @@
 
 test_expect_success 'respect NULs' '
 
-	git mailsplit -d3 -o. ../t5100/nul-plain &&
-	cmp ../t5100/nul-plain 001 &&
+	git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
+	test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
 	(cat 001 | git mailinfo msg patch) &&
 	test 4 = $(wc -l < patch)
 
@@ -58,10 +59,10 @@
 
 test_expect_success 'Preserve NULs out of MIME encoded message' '
 
-	git mailsplit -d5 -o. ../t5100/nul-b64.in &&
-	cmp ../t5100/nul-b64.in 00001 &&
+	git mailsplit -d5 -o. "$TEST_DIRECTORY"/t5100/nul-b64.in &&
+	test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.in 00001 &&
 	git mailinfo msg patch <00001 &&
-	cmp ../t5100/nul-b64.expect patch
+	test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.expect patch
 
 '
 
diff --git a/t/t5100/info0001 b/t/t5100/info0001
index 8c05277..f951538 100644
--- a/t/t5100/info0001
+++ b/t/t5100/info0001
@@ -1,4 +1,4 @@
-Author: A U Thor
+Author: A (zzz) U Thor (Comment)
 Email: a.u.thor@example.com
 Subject: a commit.
 Date: Fri, 9 Jun 2006 00:44:16 -0700
diff --git a/t/t5100/rfc2047-info-0004 b/t/t5100/rfc2047-info-0004
index 0ca7ff0..f67a90a 100644
--- a/t/t5100/rfc2047-info-0004
+++ b/t/t5100/rfc2047-info-0004
@@ -1,4 +1,4 @@
-Author: Nathaniel Borenstein   (םולש ןב ילטפנ)
+Author: Nathaniel Borenstein (םולש ןב ילטפנ)
 Email: nsb@thumper.bellcore.com
 Subject: Test of new header generator
 
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index 85df55f..c5ad206 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -2,10 +2,10 @@
 	
     
 From nobody Mon Sep 17 00:00:00 2001
-From: A
+From: A (zzz)
       U
       Thor
-      <a.u.thor@example.com>
+      <a.u.thor@example.com> (Comment)
 Date: Fri, 9 Jun 2006 00:44:16 -0700
 Subject: [PATCH] a commit.
 
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 2852a03..e2aa254 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -13,11 +13,10 @@
 test_expect_success \
     'setup' \
     'rm -f .git/index*
-     for i in a b c
-     do
-	     dd if=/dev/zero bs=4k count=1 | perl -pe "y/\\000/$i/" >$i &&
-	     git update-index --add $i || return 1
-     done &&
+     perl -e "print \"a\" x 4096;" > a &&
+     perl -e "print \"b\" x 4096;" > b &&
+     perl -e "print \"c\" x 4096;" > c &&
+     git update-index --add a b c &&
      cat c >d && echo foo >>d && git update-index --add d &&
      tree=`git write-tree` &&
      commit=`git commit-tree $tree </dev/null` && {
@@ -180,6 +179,23 @@
 
 unset GIT_OBJECT_DIRECTORY
 
+test_expect_success 'survive missing objects/pack directory' '
+	(
+		rm -fr missing-pack &&
+		mkdir missing-pack &&
+		cd missing-pack &&
+		git init &&
+		GOP=.git/objects/pack
+		rm -fr $GOP &&
+		git index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&
+		test -f $GOP/pack-${packname_3}.pack &&
+		test_cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack &&
+		test -f $GOP/pack-${packname_3}.idx &&
+		test_cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx &&
+		test -f $GOP/pack-${packname_3}.keep
+	)
+'
+
 test_expect_success \
     'verify pack' \
     'git verify-pack	test-1-${packname_1}.idx \
@@ -204,7 +220,7 @@
 test_expect_success \
     'verify-pack catches a corrupted pack signature' \
     'cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
      if git verify-pack test-3.idx
      then false
      else :;
@@ -213,7 +229,7 @@
 test_expect_success \
     'verify-pack catches a corrupted pack version' \
     'cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
      if git verify-pack test-3.idx
      then false
      else :;
@@ -222,7 +238,7 @@
 test_expect_success \
     'verify-pack catches a corrupted type/size of the 1st packed object data' \
     'cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
      if git verify-pack test-3.idx
      then false
      else :;
@@ -233,7 +249,7 @@
     'l=`wc -c <test-3.idx` &&
      l=`expr $l - 20` &&
      cat test-1-${packname_1}.pack >test-3.pack &&
-     dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+     printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
      if git verify-pack test-3.pack
      then false
      else :;
@@ -272,7 +288,8 @@
 
 test_expect_success \
     'make sure index-pack detects the SHA1 collision' \
-    'test_must_fail git index-pack -o bad.idx test-3.pack'
+    'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
+     grep "SHA1 COLLISION FOUND" msg'
 
 test_expect_success \
     'honor pack.packSizeLimit' \
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 344ab25..4360e77 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -10,14 +10,20 @@
     'setup' \
     'rm -rf .git
      git init &&
+     git config pack.threads 1 &&
      i=1 &&
-	 while test $i -le 100
+     while test $i -le 100
      do
-		 i=`printf '%03i' $i`
-         echo $i >file_$i &&
-         test-genrandom "$i" 8192 >>file_$i &&
-         git update-index --add file_$i &&
-		 i=`expr $i + 1` || return 1
+         iii=`printf '%03i' $i`
+         test-genrandom "bar" 200 > wide_delta_$iii &&
+         test-genrandom "baz $iii" 50 >> wide_delta_$iii &&
+         test-genrandom "foo"$i 100 > deep_delta_$iii &&
+         test-genrandom "foo"`expr $i + 1` 100 >> deep_delta_$iii &&
+         test-genrandom "foo"`expr $i + 2` 100 >> deep_delta_$iii &&
+         echo $iii >file_$iii &&
+         test-genrandom "$iii" 8192 >>file_$iii &&
+         git update-index --add file_$iii deep_delta_$iii wide_delta_$iii &&
+         i=`expr $i + 1` || return 1
      done &&
      { echo 101 && test-genrandom 100 8192; } >file_101 &&
      git update-index --add file_101 &&
@@ -63,35 +69,55 @@
     'index v2: force some 64-bit offsets with pack-objects' \
     'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
 
-have_64bits=
 if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
 	! (echo "$msg" | grep "pack too large .* off_t")
 then
-	have_64bits=t
+	test_set_prereq OFF64_T
 else
 	say "skipping tests concerning 64-bit offsets"
 fi
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     'index v2: verify a pack with some 64-bit offsets' \
     'git verify-pack -v "test-3-${pack3}.pack"'
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     '64-bit offsets: should be different from previous index v2 results' \
     '! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     'index v2: force some 64-bit offsets with index-pack' \
     'git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
 
-test "$have_64bits" &&
-test_expect_success \
+test_expect_success OFF64_T \
     '64-bit offsets: index-pack result should match pack-objects one' \
     'cmp "test-3-${pack3}.idx" "3.idx"'
 
+# returns the object number for given object in given pack index
+index_obj_nr()
+{
+    idx_file=$1
+    object_sha1=$2
+    nr=0
+    git show-index < $idx_file |
+    while read offs sha1 extra
+    do
+      nr=$(($nr + 1))
+      test "$sha1" = "$object_sha1" || continue
+      echo "$(($nr - 1))"
+      break
+    done
+}
+
+# returns the pack offset for given object as found in given pack index
+index_obj_offset()
+{
+    idx_file=$1
+    object_sha1=$2
+    git show-index < $idx_file | grep $object_sha1 |
+    ( read offs extra && echo "$offs" )
+}
+
 test_expect_success \
     '[index v1] 1) stream pack to repository' \
     'git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
@@ -102,19 +128,22 @@
 
 test_expect_success \
     '[index v1] 2) create a stealth corruption in a delta base reference' \
-    '# this test assumes a delta smaller than 16 bytes at the end of the pack
-     git show-index <1.idx | sort -n | sed -ne \$p | (
-       read delta_offs delta_sha1 &&
-       git cat-file blob "$delta_sha1" > blob_1 &&
-       chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
-       dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
-	  if=".git/objects/pack/pack-${pack1}.idx" skip=$((256 * 4 + 4)) \
-	  bs=1 count=20 conv=notrunc &&
-       git cat-file blob "$delta_sha1" > blob_2 )'
+    '# This test assumes file_101 is a delta smaller than 16 bytes.
+     # It should be against file_100 but we substitute its base for file_099
+     sha1_101=`git hash-object file_101` &&
+     sha1_099=`git hash-object file_099` &&
+     offs_101=`index_obj_offset 1.idx $sha1_101` &&
+     nr_099=`index_obj_nr 1.idx $sha1_099` &&
+     chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+     dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
+        if=".git/objects/pack/pack-${pack1}.idx" \
+        skip=$((4 + 256 * 4 + $nr_099 * 24)) \
+        bs=1 count=20 conv=notrunc &&
+     git cat-file blob $sha1_101 > file_101_foo1'
 
 test_expect_success \
     '[index v1] 3) corrupted delta happily returned wrong data' \
-    '! cmp blob_1 blob_2'
+    'test -f file_101_foo1 && ! cmp file_101 file_101_foo1'
 
 test_expect_success \
     '[index v1] 4) confirm that the pack is actually corrupted' \
@@ -140,19 +169,22 @@
 
 test_expect_success \
     '[index v2] 2) create a stealth corruption in a delta base reference' \
-    '# this test assumes a delta smaller than 16 bytes at the end of the pack
-     git show-index <1.idx | sort -n | sed -ne \$p | (
-       read delta_offs delta_sha1 delta_crc &&
-       git cat-file blob "$delta_sha1" > blob_3 &&
-       chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
-       dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
-	  if=".git/objects/pack/pack-${pack1}.idx" skip=$((8 + 256 * 4)) \
-	  bs=1 count=20 conv=notrunc &&
-       git cat-file blob "$delta_sha1" > blob_4 )'
+    '# This test assumes file_101 is a delta smaller than 16 bytes.
+     # It should be against file_100 but we substitute its base for file_099
+     sha1_101=`git hash-object file_101` &&
+     sha1_099=`git hash-object file_099` &&
+     offs_101=`index_obj_offset 1.idx $sha1_101` &&
+     nr_099=`index_obj_nr 1.idx $sha1_099` &&
+     chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+     dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
+        if=".git/objects/pack/pack-${pack1}.idx" \
+        skip=$((8 + 256 * 4 + $nr_099 * 20)) \
+        bs=1 count=20 conv=notrunc &&
+     git cat-file blob $sha1_101 > file_101_foo2'
 
 test_expect_success \
     '[index v2] 3) corrupted delta happily returned wrong data' \
-    '! cmp blob_3 blob_4'
+    'test -f file_101_foo2 && ! cmp file_101 file_101_foo2'
 
 test_expect_success \
     '[index v2] 4) confirm that the pack is actually corrupted' \
@@ -160,16 +192,19 @@
 
 test_expect_success \
     '[index v2] 5) pack-objects refuses to reuse corrupted data' \
-    'test_must_fail git pack-objects test-5 <obj-list'
+    'test_must_fail git pack-objects test-5 <obj-list &&
+     test_must_fail git pack-objects --no-reuse-object test-6 <obj-list'
 
 test_expect_success \
     '[index v2] 6) verify-pack detects CRC mismatch' \
     'rm -f .git/objects/pack/* &&
      git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
      git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
+     obj=`git hash-object file_001` &&
+     nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` &&
      chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
-     dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
-        bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + 0)) &&
+     printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
+        bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + $nr * 4)) &&
      ( while read obj
        do git cat-file -p $obj >/dev/null || exit 1
        done <obj-list ) &&
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 31b20b2..5132d41 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -41,14 +41,22 @@
     git verify-pack -v ${pack}.pack
 }
 
+do_repack() {
+    pack=`printf "$blob_1\n$blob_2\n$blob_3\n" |
+          git pack-objects $@ .git/objects/pack/pack` &&
+    pack=".git/objects/pack/pack-${pack}"
+}
+
 do_corrupt_object() {
     ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
     ofs=$(($ofs + $2)) &&
     chmod +w ${pack}.pack &&
-    dd if=/dev/zero of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
+    dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
     test_must_fail git verify-pack ${pack}.pack
 }
 
+printf '\0' > zero
+
 test_expect_success \
     'initial setup validation' \
     'create_test_files &&
@@ -60,7 +68,7 @@
 
 test_expect_success \
     'create corruption in header of first object' \
-    'do_corrupt_object $blob_1 0 &&
+    'do_corrupt_object $blob_1 0 < zero &&
      test_must_fail git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -119,7 +127,7 @@
     'create corruption in header of first delta' \
     'create_new_pack &&
      git prune-packed &&
-     do_corrupt_object $blob_2 0 &&
+     do_corrupt_object $blob_2 0 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -134,6 +142,15 @@
      git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
     'create corruption in data of first delta' \
     'create_new_pack &&
      git prune-packed &&
@@ -153,10 +170,19 @@
      git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
     'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
     'create_new_pack &&
      git prune-packed &&
-     do_corrupt_object $blob_2 2 &&
+     do_corrupt_object $blob_2 2 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -171,17 +197,75 @@
      git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
-    'corruption in delta base reference of first delta (OBJ_OFS_DELTA)' \
+    '... and then a repack "clears" the corruption' \
+    'do_repack &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
     'create_new_pack --delta-base-offset &&
      git prune-packed &&
-     do_corrupt_object $blob_2 2 &&
+     do_corrupt_object $blob_2 2 < zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
-    '... and a redundant pack allows for full recovery too' \
+    '... but having a loose copy allows for full recovery' \
     'mv ${pack}.idx tmp &&
+     git hash-object -t blob -w file_2 &&
+     mv tmp ${pack}.idx &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack --delta-base-offset &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
+    'create_new_pack --delta-base-offset &&
+     git prune-packed &&
+     printf "\001" | do_corrupt_object $blob_2 2 &&
+     git cat-file blob $blob_1 > /dev/null &&
+     test_must_fail git cat-file blob $blob_2 > /dev/null &&
+     test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... but having a loose copy allows for full recovery' \
+    'mv ${pack}.idx tmp &&
+     git hash-object -t blob -w file_2 &&
+     mv tmp ${pack}.idx &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack --delta-base-offset &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and a redundant pack allows for full recovery too' \
+    'do_corrupt_object $blob_2 2 < zero &&
+     git cat-file blob $blob_1 > /dev/null &&
+     test_must_fail git cat-file blob $blob_2 > /dev/null &&
+     test_must_fail git cat-file blob $blob_3 > /dev/null &&
+     mv ${pack}.idx tmp &&
      git hash-object -t blob -w file_1 &&
      git hash-object -t blob -w file_2 &&
      printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 771c0a0..55ed7c7 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -112,4 +112,42 @@
 
 '
 
+test_expect_success 'gc --no-prune' '
+
+	before=$(git count-objects | sed "s/ .*//") &&
+	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE &&
+	test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+	git config gc.pruneExpire 2.days.ago &&
+	git gc --no-prune &&
+	test 1 = $(git count-objects | sed "s/ .*//") &&
+	test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc respects gc.pruneExpire' '
+
+	git config gc.pruneExpire 5002.days.ago &&
+	git gc &&
+	test -f $BLOB_FILE &&
+	git config gc.pruneExpire 5000.days.ago &&
+	git gc &&
+	test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc --prune=<date>' '
+
+	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+	test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+	git gc --prune=5002.days.ago &&
+	test -f $BLOB_FILE &&
+	git gc --prune=5000.days.ago &&
+	test ! -f $BLOB_FILE
+
+'
+
 test_done
diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh
new file mode 100755
index 0000000..ae52a18
--- /dev/null
+++ b/t/t5307-pack-missing-commit.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='pack should notice missing commit objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	for i in 1 2 3 4 5
+	do
+		echo "$i" >"file$i" &&
+		git add "file$i" &&
+		test_tick &&
+		git commit -m "$i" &&
+		git tag "tag$i"
+	done &&
+	obj=$(git rev-parse --verify tag3) &&
+	fanout=$(expr "$obj" : "\(..\)") &&
+	remainder=$(expr "$obj" : "..\(.*\)") &&
+	rm -f ".git/objects/$fanout/$remainder"
+'
+
+test_expect_success 'check corruption' '
+	test_must_fail git fsck
+'
+
+test_expect_success 'rev-list notices corruption (1)' '
+	test_must_fail git rev-list HEAD
+'
+
+test_expect_success 'rev-list notices corruption (2)' '
+	test_must_fail git rev-list --objects HEAD
+'
+
+test_expect_success 'pack-objects notices corruption' '
+	echo HEAD |
+	test_must_fail git pack-objects --revs pack
+'
+
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 544771d..f2d5581 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -32,9 +32,7 @@
 	done &&
 	git update-ref HEAD "$commit" &&
 	git clone ./. victim &&
-	cd victim &&
-	git log &&
-	cd .. &&
+	( cd victim && git log ) &&
 	git update-ref HEAD "$zero" &&
 	parent=$zero &&
 	i=0 &&
@@ -59,77 +57,84 @@
 '
 
 test_expect_success 'pack the destination repository' '
+    (
 	cd victim &&
 	git repack -a -d &&
-	git prune &&
-	cd ..
+	git prune
+    )
 '
 
-test_expect_success \
-        'pushing rewound head should not barf but require --force' '
-	# should not fail but refuse to update.
-	if git send-pack ./victim/.git/ master
-	then
-		# now it should fail with Pasky patch
-		echo >&2 Gaah, it should have failed.
-		false
-	else
-		echo >&2 Thanks, it correctly failed.
-		true
-	fi &&
-	if cmp victim/.git/refs/heads/master .git/refs/heads/master
-	then
-		# should have been left as it was!
-		false
-	else
-		true
-	fi &&
+test_expect_success 'refuse pushing rewound head without --force' '
+	pushed_head=$(git rev-parse --verify master) &&
+	victim_orig=$(cd victim && git rev-parse --verify master) &&
+	test_must_fail git send-pack ./victim master &&
+	victim_head=$(cd victim && git rev-parse --verify master) &&
+	test "$victim_head" = "$victim_orig" &&
 	# this should update
-	git send-pack --force ./victim/.git/ master &&
-	cmp victim/.git/refs/heads/master .git/refs/heads/master
+	git send-pack --force ./victim master &&
+	victim_head=$(cd victim && git rev-parse --verify master) &&
+	test "$victim_head" = "$pushed_head"
 '
 
 test_expect_success \
         'push can be used to delete a ref' '
-	cd victim &&
-	git branch extra master &&
-	cd .. &&
-	test -f victim/.git/refs/heads/extra &&
-	git send-pack ./victim/.git/ :extra master &&
-	! test -f victim/.git/refs/heads/extra
+	( cd victim && git branch extra master ) &&
+	git send-pack ./victim :extra master &&
+	( cd victim &&
+	  test_must_fail git rev-parse --verify extra )
 '
 
-unset GIT_CONFIG GIT_CONFIG_LOCAL
-HOME=`pwd`/no-such-directory
-export HOME ;# this way we force the victim/.git/config to be used.
-
-test_expect_success \
-        'pushing with --force should be denied with denyNonFastforwards' '
-	cd victim &&
-	git config receive.denyNonFastforwards true &&
-	cd .. &&
-	git update-ref refs/heads/master master^ || return 1
-	git send-pack --force ./victim/.git/ master && return 1
-	! test_cmp .git/refs/heads/master victim/.git/refs/heads/master
+test_expect_success 'refuse deleting push with denyDeletes' '
+	(
+	    cd victim &&
+	    ( git branch -D extra || : ) &&
+	    git config receive.denyDeletes true &&
+	    git branch extra master
+	) &&
+	test_must_fail git send-pack ./victim :extra master
 '
 
-test_expect_success \
-	'pushing does not include non-head refs' '
-	mkdir parent && cd parent &&
-	git init && touch file && git add file && git commit -m add &&
-	cd .. &&
-	git clone parent child && cd child && git push --all &&
-	cd ../parent &&
-	git branch -a >branches && ! grep origin/master branches
+test_expect_success 'denyNonFastforwards trumps --force' '
+	(
+	    cd victim &&
+	    ( git branch -D extra || : ) &&
+	    git config receive.denyNonFastforwards true
+	) &&
+	victim_orig=$(cd victim && git rev-parse --verify master) &&
+	test_must_fail git send-pack --force ./victim master^:master &&
+	victim_head=$(cd victim && git rev-parse --verify master) &&
+	test "$victim_orig" = "$victim_head"
+'
+
+test_expect_success 'push --all excludes remote tracking hierarchy' '
+	mkdir parent &&
+	(
+	    cd parent &&
+	    git init && : >file && git add file && git commit -m add
+	) &&
+	git clone parent child &&
+	(
+	    cd child && git push --all
+	) &&
+	(
+	    cd parent &&
+	    test -z "$(git for-each-ref refs/remotes/origin)"
+	)
 '
 
 rewound_push_setup() {
 	rm -rf parent child &&
-	mkdir parent && cd parent &&
-	git init && echo one >file && git add file && git commit -m one &&
-	echo two >file && git commit -a -m two &&
-	cd .. &&
-	git clone parent child && cd child && git reset --hard HEAD^
+	mkdir parent &&
+	(
+	    cd parent &&
+	    git init &&
+	    echo one >file && git add file && git commit -m one &&
+	    echo two >file && git commit -a -m two
+	) &&
+	git clone parent child &&
+	(
+	    cd child && git reset --hard HEAD^
+	)
 }
 
 rewound_push_succeeded() {
@@ -145,30 +150,57 @@
 	fi
 }
 
-test_expect_success \
-	'pushing explicit refspecs respects forcing' '
+test_expect_success 'pushing explicit refspecs respects forcing' '
 	rewound_push_setup &&
-	if git send-pack ../parent/.git refs/heads/master:refs/heads/master
-	then
-		false
-	else
-		true
-	fi && rewound_push_failed &&
-	git send-pack ../parent/.git +refs/heads/master:refs/heads/master &&
-	rewound_push_succeeded
+	parent_orig=$(cd parent && git rev-parse --verify master) &&
+	(
+	    cd child &&
+	    test_must_fail git send-pack ../parent \
+		refs/heads/master:refs/heads/master
+	) &&
+	parent_head=$(cd parent && git rev-parse --verify master) &&
+	test "$parent_orig" = "$parent_head" &&
+	(
+	    cd child &&
+	    git send-pack ../parent \
+	        +refs/heads/master:refs/heads/master
+	) &&
+	parent_head=$(cd parent && git rev-parse --verify master) &&
+	child_head=$(cd parent && git rev-parse --verify master) &&
+	test "$parent_head" = "$child_head"
 '
 
-test_expect_success \
-	'pushing wildcard refspecs respects forcing' '
+test_expect_success 'pushing wildcard refspecs respects forcing' '
 	rewound_push_setup &&
-	if git send-pack ../parent/.git refs/heads/*:refs/heads/*
-	then
-		false
-	else
-		true
-	fi && rewound_push_failed &&
-	git send-pack ../parent/.git +refs/heads/*:refs/heads/* &&
-	rewound_push_succeeded
+	parent_orig=$(cd parent && git rev-parse --verify master) &&
+	(
+	    cd child &&
+	    test_must_fail git send-pack ../parent \
+	        "refs/heads/*:refs/heads/*"
+	) &&
+	parent_head=$(cd parent && git rev-parse --verify master) &&
+	test "$parent_orig" = "$parent_head" &&
+	(
+	    cd child &&
+	    git send-pack ../parent \
+	        "+refs/heads/*:refs/heads/*"
+	) &&
+	parent_head=$(cd parent && git rev-parse --verify master) &&
+	child_head=$(cd parent && git rev-parse --verify master) &&
+	test "$parent_head" = "$child_head"
+'
+
+test_expect_success 'warn pushing to delete current branch' '
+	rewound_push_setup &&
+	(
+	    cd child &&
+	    git send-pack ../parent :refs/heads/master 2>errs
+	) &&
+	grep "warning: to refuse deleting" child/errs &&
+	(
+		cd parent &&
+		test_must_fail git rev-parse --verify master
+	)
 '
 
 test_done
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 9b2e1a9..5858b86 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -71,4 +71,18 @@
         test $old = $new -a $flag = 0
 '
 
+if test "$(git config --bool core.filemode)" = true; then
+mkdir -p templates/hooks
+cat >templates/hooks/post-checkout <<'EOF'
+#!/bin/sh
+echo $@ > $GIT_DIR/post-checkout.args
+EOF
+chmod +x templates/hooks/post-checkout
+
+test_expect_success 'post-checkout hook is triggered by clone' '
+	git clone --template=templates . clone3 &&
+	test -f clone3/.git/post-checkout.args
+'
+fi
+
 test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 448ec71..c450f33 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -137,7 +137,7 @@
 	"test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\""
 
 count_output () {
-	sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/: 0$/d' "$1"
+	sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1"
 }
 
 test_expect_success "clone shallow object count (part 2)" '
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 4074e23..d5db75d 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -4,6 +4,12 @@
 
 . ./test-lib.sh
 
+case $(uname -s) in
+*MINGW*)
+	say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
+	test_done
+esac
+
 # End state of the repository:
 #
 #         T - tag1          S - tag2
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 0103e1a..5ec668d 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -28,7 +28,7 @@
 }
 
 check_remote_track () {
-	actual=$(git remote show "$1" | sed -n -e '$p') &&
+	actual=$(git remote show "$1" | sed -ne 's|^    \(.*\) tracked$|\1|p')
 	shift &&
 	tokens_match "$*" "$actual"
 }
@@ -107,46 +107,102 @@
 )
 '
 
+test_expect_success 'remove remote protects non-remote branches' '
+(
+	cd test &&
+	(cat >expect1 <<EOF
+Note: A non-remote branch was not removed; to delete it, use:
+  git branch -d master
+EOF
+    cat >expect2 <<EOF
+Note: Non-remote branches were not removed; to delete them, use:
+  git branch -d foobranch
+  git branch -d master
+EOF
+) &&
+	git tag footag
+	git config --add remote.oops.fetch "+refs/*:refs/*" &&
+	git remote rm oops 2>actual1 &&
+	git branch foobranch &&
+	git config --add remote.oops.fetch "+refs/*:refs/*" &&
+	git remote rm oops 2>actual2 &&
+	git branch -d foobranch &&
+	git tag -d footag &&
+	test_cmp expect1 actual1 &&
+	test_cmp expect2 actual2
+)
+'
+
 cat > test/expect << EOF
 * remote origin
   URL: $(pwd)/one
-  Remote branch merged with 'git pull' while on branch master
+  HEAD branch: master
+  Remote branches:
+    master new (next fetch will store in remotes/origin)
+    side   tracked
+  Local branches configured for 'git pull':
+    ahead    merges with remote master
+    master   merges with remote master
+    octopus  merges with remote topic-a
+                and with remote topic-b
+                and with remote topic-c
+    rebase  rebases onto remote master
+  Local refs configured for 'git push':
+    master pushes to master   (local out of date)
+    master pushes to upstream (create)
+* remote two
+  URL: ../two
+  HEAD branch (remote HEAD is ambiguous, may be one of the following):
+    another
     master
-  New remote branch (next fetch will store in remotes/origin)
-    master
-  Tracked remote branches
-    side master
-  Local branches pushed with 'git push'
-    master:upstream +refs/tags/lastbackup
+  Local refs configured for 'git push':
+    ahead  forces to master  (fast forwardable)
+    master pushes to another (up to date)
 EOF
 
 test_expect_success 'show' '
 	(cd test &&
-	 git config --add remote.origin.fetch \
-		refs/heads/master:refs/heads/upstream &&
+	 git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
 	 git fetch &&
+	 git checkout -b ahead origin/master &&
+	 echo 1 >> file &&
+	 test_tick &&
+	 git commit -m update file &&
+	 git checkout master &&
+	 git branch --track octopus origin/master &&
+	 git branch --track rebase origin/master &&
 	 git branch -d -r origin/master &&
+	 git config --add remote.two.url ../two &&
+	 git config branch.rebase.rebase true &&
+	 git config branch.octopus.merge "topic-a topic-b topic-c" &&
 	 (cd ../one &&
 	  echo 1 > file &&
 	  test_tick &&
 	  git commit -m update file) &&
-	 git config remote.origin.push \
-		refs/heads/master:refs/heads/upstream &&
-	 git config --add remote.origin.push \
-		+refs/tags/lastbackup &&
-	 git remote show origin > output &&
+	 git config --add remote.origin.push : &&
+	 git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+	 git config --add remote.origin.push +refs/tags/lastbackup &&
+	 git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+	 git config --add remote.two.push refs/heads/master:refs/heads/another &&
+	 git remote show origin two > output &&
+	 git branch -d rebase octopus &&
 	 test_cmp expect output)
 '
 
 cat > test/expect << EOF
 * remote origin
   URL: $(pwd)/one
-  Remote branch merged with 'git pull' while on branch master
+  HEAD branch: (not queried)
+  Remote branches: (status not queried)
     master
-  Tracked remote branches
-    master side
-  Local branches pushed with 'git push'
-    master:upstream +refs/tags/lastbackup
+    side
+  Local branches configured for 'git pull':
+    ahead  merges with remote master
+    master merges with remote master
+  Local refs configured for 'git push' (status not queried):
+    (matching)           pushes to (matching)
+    refs/heads/master    pushes to refs/heads/upstream
+    refs/tags/lastbackup forces to refs/tags/lastbackup
 EOF
 
 test_expect_success 'show -n' '
@@ -167,6 +223,46 @@
 	 test_must_fail git rev-parse refs/remotes/origin/side)
 '
 
+test_expect_success 'set-head --delete' '
+	(cd test &&
+	 git symbolic-ref refs/remotes/origin/HEAD &&
+	 git remote set-head --delete origin &&
+	 test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+'
+
+test_expect_success 'set-head --auto' '
+	(cd test &&
+	 git remote set-head --auto origin &&
+	 echo refs/remotes/origin/master >expect &&
+	 git symbolic-ref refs/remotes/origin/HEAD >output &&
+	 test_cmp expect output
+	)
+'
+
+cat >test/expect <<EOF
+error: Multiple remote HEAD branches. Please choose one explicitly with:
+  git remote set-head two another
+  git remote set-head two master
+EOF
+
+test_expect_success 'set-head --auto fails w/multiple HEADs' '
+	(cd test &&
+	 test_must_fail git remote set-head --auto two >output 2>&1 &&
+	test_cmp expect output)
+'
+
+cat >test/expect <<EOF
+refs/remotes/origin/side2
+EOF
+
+test_expect_success 'set-head explicit' '
+	(cd test &&
+	 git remote set-head origin side2 &&
+	 git symbolic-ref refs/remotes/origin/HEAD >output &&
+	 git remote set-head origin master &&
+	 test_cmp expect output)
+'
+
 cat > test/expect << EOF
 Pruning origin
 URL: $(pwd)/one
@@ -313,7 +409,7 @@
 	git clone one three &&
 	(cd three &&
 	 git remote show origin > output &&
-	 ! grep HEAD < output &&
+	 ! grep "^ *HEAD$" < output &&
 	 ! grep -i stale < output)
 
 '
@@ -324,4 +420,79 @@
 
 '
 
+# The first three test if the tracking branches are properly renamed,
+# the last two ones check if the config is updated.
+
+test_expect_success 'rename a remote' '
+
+	git clone one four &&
+	(cd four &&
+	 git remote rename origin upstream &&
+	 rmdir .git/refs/remotes/origin &&
+	 test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+	 test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+	 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+	 test "$(git config branch.master.remote)" = "upstream")
+
+'
+
+cat > remotes_origin << EOF
+URL: $(pwd)/one
+Push: refs/heads/master:refs/heads/upstream
+Pull: refs/heads/master:refs/heads/origin
+EOF
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
+	git clone one five &&
+	origin_url=$(pwd)/one &&
+	(cd five &&
+	 git remote rm origin &&
+	 mkdir -p .git/remotes &&
+	 cat ../remotes_origin > .git/remotes/origin &&
+	 git remote rename origin origin &&
+	 ! test -f .git/remotes/origin &&
+	 test "$(git config remote.origin.url)" = "$origin_url" &&
+	 test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
+	 test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+'
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
+	git clone one six &&
+	origin_url=$(pwd)/one &&
+	(cd six &&
+	 git remote rm origin &&
+	 echo "$origin_url" > .git/branches/origin &&
+	 git remote rename origin origin &&
+	 ! test -f .git/branches/origin &&
+	 test "$(git config remote.origin.url)" = "$origin_url" &&
+	 test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+'
+
+test_expect_success 'remote prune to cause a dangling symref' '
+	git clone one seven &&
+	(
+		cd one &&
+		git checkout side2 &&
+		git branch -D master
+	) &&
+	(
+		cd seven &&
+		git remote prune origin
+	) 2>err &&
+	grep "has become dangling" err &&
+
+	: And the dangling symref will not cause other annoying errors
+	(
+		cd seven &&
+		git branch -a
+	) 2>err &&
+	! grep "points nowhere" err
+	(
+		cd seven &&
+		test_must_fail git branch nomore origin
+	) 2>err &&
+	grep "dangling symref" err
+'
+
 test_done
+
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
new file mode 100755
index 0000000..2a1806b
--- /dev/null
+++ b/t/t5506-remote-groups.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='git remote group handling'
+. ./test-lib.sh
+
+mark() {
+	echo "$1" >mark
+}
+
+update_repo() {
+	(cd $1 &&
+	echo content >>file &&
+	git add file &&
+	git commit -F ../mark)
+}
+
+update_repos() {
+	update_repo one $1 &&
+	update_repo two $1
+}
+
+repo_fetched() {
+	if test "`git log -1 --pretty=format:%s $1 --`" = "`cat mark`"; then
+		echo >&2 "repo was fetched: $1"
+		return 0
+	fi
+	echo >&2 "repo was not fetched: $1"
+	return 1
+}
+
+test_expect_success 'setup' '
+	mkdir one && (cd one && git init) &&
+	mkdir two && (cd two && git init) &&
+	git remote add -m master one one &&
+	git remote add -m master two two
+'
+
+test_expect_success 'no group updates all' '
+	mark update-all &&
+	update_repos &&
+	git remote update &&
+	repo_fetched one &&
+	repo_fetched two
+'
+
+test_expect_success 'nonexistant group produces error' '
+	mark nonexistant &&
+	update_repos &&
+	test_must_fail git remote update nonexistant &&
+	! repo_fetched one &&
+	! repo_fetched two
+'
+
+test_expect_success 'updating group updates all members' '
+	mark group-all &&
+	update_repos &&
+	git config --add remotes.all one &&
+	git config --add remotes.all two &&
+	git remote update all &&
+	repo_fetched one &&
+	repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members' '
+	mark group-some &&
+	update_repos &&
+	git config --add remotes.some one &&
+	git remote update some &&
+	repo_fetched one &&
+	! repo_fetched two
+'
+
+test_expect_success 'updating remote name updates that remote' '
+	mark remote-name &&
+	update_repos &&
+	git remote update one &&
+	repo_fetched one &&
+	! repo_fetched two
+'
+
+test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 52094e7..bee3424 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -191,38 +191,39 @@
 
 '
 
-test "$TEST_RSYNC" && {
+! rsync --help > /dev/null 2> /dev/null &&
+say 'Skipping rsync tests because rsync was not found' || {
 test_expect_success 'fetch via rsync' '
 	git pack-refs &&
 	mkdir rsynced &&
-	cd rsynced &&
-	git init &&
-	git fetch rsync://127.0.0.1$(pwd)/../.git master:refs/heads/master &&
-	git gc --prune &&
-	test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
-	git fsck --full
+	(cd rsynced &&
+	 git init --bare &&
+	 git fetch "rsync:$(pwd)/../.git" master:refs/heads/master &&
+	 git gc --prune &&
+	 test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
+	 git fsck --full)
 '
 
 test_expect_success 'push via rsync' '
-	mkdir ../rsynced2 &&
-	(cd ../rsynced2 &&
+	mkdir rsynced2 &&
+	(cd rsynced2 &&
 	 git init) &&
-	git push rsync://127.0.0.1$(pwd)/../rsynced2/.git master &&
-	cd ../rsynced2 &&
-	git gc --prune &&
-	test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
-	git fsck --full
+	(cd rsynced &&
+	 git push "rsync:$(pwd)/../rsynced2/.git" master) &&
+	(cd rsynced2 &&
+	 git gc --prune &&
+	 test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
+	 git fsck --full)
 '
 
 test_expect_success 'push via rsync' '
-	cd .. &&
 	mkdir rsynced3 &&
 	(cd rsynced3 &&
 	 git init) &&
-	git push --all rsync://127.0.0.1$(pwd)/rsynced3/.git &&
-	cd rsynced3 &&
-	test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
-	git fsck --full
+	git push --all "rsync:$(pwd)/rsynced3/.git" &&
+	(cd rsynced3 &&
+	 test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
+	 git fsck --full)
 '
 }
 
@@ -303,6 +304,26 @@
 
 '
 
+test_expect_success 'auto tag following fetches minimum' '
+
+	cd "$D" &&
+	git clone .git follow &&
+	git checkout HEAD^0 &&
+	(
+		for i in 1 2 3 4 5 6 7
+		do
+			echo $i >>file &&
+			git commit -m $i -a &&
+			git tag -a -m $i excess-$i || exit 1
+		done
+	) &&
+	git checkout master &&
+	(
+		cd follow &&
+		git fetch
+	)
+'
+
 test_expect_success 'refuse to fetch into the current branch' '
 
 	test_must_fail git fetch . side:master
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index 22ba380..c289322 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -72,4 +72,16 @@
 test_refspec push ':refs/remotes/frotz/delete me'		invalid
 test_refspec fetch ':refs/remotes/frotz/HEAD to me'		invalid
 
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+
+test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
+
 test_done
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 8becbc3..dbb927d 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -129,11 +129,10 @@
 	'' | '#'*) continue ;;
 	esac
 	test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
-	cnt=`expr $test_count + 1`
-	pfx=`printf "%04d" $cnt`
-	expect_f="../../t5515/fetch.$test"
+	pfx=`printf "%04d" $test_count`
+	expect_f="$TEST_DIRECTORY/t5515/fetch.$test"
 	actual_f="$pfx-fetch.$test"
-	expect_r="../../t5515/refs.$test"
+	expect_r="$TEST_DIRECTORY/t5515/refs.$test"
 	actual_r="$pfx-refs.$test"
 
 	test_expect_success "$cmd" '
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index f9e8780..89649e7 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -39,6 +39,11 @@
 	)
 }
 
+mk_child() {
+	rm -rf "$1" &&
+	git clone testrepo "$1"
+}
+
 check_push_result () {
 	(
 		cd testrepo &&
@@ -425,13 +430,10 @@
 
 test_expect_success 'push updates local refs' '
 
-	rm -rf parent child &&
-	mkdir parent &&
-	(cd parent && git init &&
-		echo one >foo && git add foo && git commit -m one) &&
-	git clone parent child &&
+	mk_test heads/master &&
+	mk_child child &&
 	(cd child &&
-		echo two >foo && git commit -a -m two &&
+		git pull .. master &&
 		git push &&
 	test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
 
@@ -439,15 +441,10 @@
 
 test_expect_success 'push updates up-to-date local refs' '
 
-	rm -rf parent child &&
-	mkdir parent &&
-	(cd parent && git init &&
-		echo one >foo && git add foo && git commit -m one) &&
-	git clone parent child1 &&
-	git clone parent child2 &&
-	(cd child1 &&
-		echo two >foo && git commit -a -m two &&
-		git push) &&
+	mk_test heads/master &&
+	mk_child child1 &&
+	mk_child child2 &&
+	(cd child1 && git pull .. master && git push) &&
 	(cd child2 &&
 		git pull ../child1 master &&
 		git push &&
@@ -457,11 +454,8 @@
 
 test_expect_success 'push preserves up-to-date packed refs' '
 
-	rm -rf parent child &&
-	mkdir parent &&
-	(cd parent && git init &&
-		echo one >foo && git add foo && git commit -m one) &&
-	git clone parent child &&
+	mk_test heads/master &&
+	mk_child child &&
 	(cd child &&
 		git push &&
 	! test -f .git/refs/remotes/origin/master)
@@ -470,15 +464,13 @@
 
 test_expect_success 'push does not update local refs on failure' '
 
-	rm -rf parent child &&
-	mkdir parent &&
-	(cd parent && git init &&
-		echo one >foo && git add foo && git commit -m one &&
-		echo exit 1 >.git/hooks/pre-receive &&
-		chmod +x .git/hooks/pre-receive) &&
-	git clone parent child &&
+	mk_test heads/master &&
+	mk_child child &&
+	mkdir testrepo/.git/hooks &&
+	echo exit 1 >testrepo/.git/hooks/pre-receive &&
+	chmod +x testrepo/.git/hooks/pre-receive &&
 	(cd child &&
-		echo two >foo && git commit -a -m two &&
+		git pull .. master
 		test_must_fail git push &&
 		test $(git rev-parse master) != \
 			$(git rev-parse remotes/origin/master))
@@ -487,13 +479,50 @@
 
 test_expect_success 'allow deleting an invalid remote ref' '
 
-	pwd &&
+	mk_test heads/master &&
 	rm -f testrepo/.git/objects/??/* &&
 	git push testrepo :refs/heads/master &&
 	(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
 
 '
 
+test_expect_success 'warn on push to HEAD of non-bare repository' '
+	mk_test heads/master
+	(cd testrepo &&
+		git checkout master &&
+		git config receive.denyCurrentBranch warn) &&
+	git push testrepo master 2>stderr &&
+	grep "warning: updating the current branch" stderr
+'
+
+test_expect_success 'deny push to HEAD of non-bare repository' '
+	mk_test heads/master
+	(cd testrepo &&
+		git checkout master &&
+		git config receive.denyCurrentBranch true) &&
+	test_must_fail git push testrepo master
+'
+
+test_expect_success 'allow push to HEAD of bare repository (bare)' '
+	mk_test heads/master
+	(cd testrepo &&
+		git checkout master &&
+		git config receive.denyCurrentBranch true &&
+		git config core.bare true) &&
+	git push testrepo master 2>stderr &&
+	! grep "warning: updating the current branch" stderr
+'
+
+test_expect_success 'allow push to HEAD of non-bare repository (config)' '
+	mk_test heads/master
+	(cd testrepo &&
+		git checkout master &&
+		git config receive.denyCurrentBranch false
+	) &&
+	git push testrepo master 2>stderr &&
+	! grep "warning: updating the current branch" stderr
+'
+
 test_expect_success 'fetch with branches' '
 	mk_empty &&
 	git branch second $the_first_commit &&
diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh
new file mode 100755
index 0000000..96be523
--- /dev/null
+++ b/t/t5519-push-alternates.sh
@@ -0,0 +1,143 @@
+#!/bin/sh
+
+test_description='push to a repository that borrows from elsewhere'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	mkdir alice-pub &&
+	(
+		cd alice-pub &&
+		GIT_DIR=. git init
+	) &&
+	mkdir alice-work &&
+	(
+		cd alice-work &&
+		git init &&
+		>file &&
+		git add . &&
+		git commit -m initial &&
+		git push ../alice-pub master
+	) &&
+
+	# Project Bob is a fork of project Alice
+	mkdir bob-pub &&
+	(
+		cd bob-pub &&
+		GIT_DIR=. git init &&
+		mkdir -p objects/info &&
+		echo ../../alice-pub/objects >objects/info/alternates
+	) &&
+	git clone alice-pub bob-work &&
+	(
+		cd bob-work &&
+		git push ../bob-pub master
+	)
+'
+
+test_expect_success 'alice works and pushes' '
+	(
+		cd alice-work &&
+		echo more >file &&
+		git commit -a -m second &&
+		git push ../alice-pub
+	)
+'
+
+test_expect_success 'bob fetches from alice, works and pushes' '
+	(
+		# Bob acquires what Alice did in his work tree first.
+		# Even though these objects are not directly in
+		# the public repository of Bob, this push does not
+		# need to send the commit Bob received from Alice
+		# to his public repository, as all the object Alice
+		# has at her public repository are available to it
+		# via its alternates.
+		cd bob-work &&
+		git pull ../alice-pub master &&
+		echo more bob >file &&
+		git commit -a -m third &&
+		git push ../bob-pub
+	) &&
+
+	# Check that the second commit by Alice is not sent
+	# to ../bob-pub
+	(
+		cd bob-pub &&
+		second=$(git rev-parse HEAD^) &&
+		rm -f objects/info/alternates &&
+		test_must_fail git cat-file -t $second &&
+		echo ../../alice-pub/objects >objects/info/alternates
+	)
+'
+
+test_expect_success 'clean-up in case the previous failed' '
+	(
+		cd bob-pub &&
+		echo ../../alice-pub/objects >objects/info/alternates
+	)
+'
+
+test_expect_success 'alice works and pushes again' '
+	(
+		# Alice does not care what Bob does.  She does not
+		# even have to be aware of his existence.  She just
+		# keeps working and pushing
+		cd alice-work &&
+		echo more alice >file &&
+		git commit -a -m fourth &&
+		git push ../alice-pub
+	)
+'
+
+test_expect_success 'bob works and pushes' '
+	(
+		# This time Bob does not pull from Alice, and
+		# the master branch at her public repository points
+		# at a commit Bob does not know about.  This should
+		# not prevent the push by Bob from succeeding.
+		cd bob-work &&
+		echo yet more bob >file &&
+		git commit -a -m fifth &&
+		git push ../bob-pub
+	)
+'
+
+test_expect_success 'alice works and pushes yet again' '
+	(
+		# Alice does not care what Bob does.  She does not
+		# even have to be aware of his existence.  She just
+		# keeps working and pushing
+		cd alice-work &&
+		echo more and more alice >file &&
+		git commit -a -m sixth.1 &&
+		echo more and more alice >>file &&
+		git commit -a -m sixth.2 &&
+		echo more and more alice >>file &&
+		git commit -a -m sixth.3 &&
+		git push ../alice-pub
+	)
+'
+
+test_expect_success 'bob works and pushes again' '
+	(
+		cd alice-pub &&
+		git cat-file commit master >../bob-work/commit
+	)
+	(
+		# This time Bob does not pull from Alice, and
+		# the master branch at her public repository points
+		# at a commit Bob does not fully know about, but
+		# he happens to have the commit object (but not the
+		# necessary tree) in his repository from Alice.
+		# This should not prevent the push by Bob from
+		# succeeding.
+		cd bob-work &&
+		git hash-object -t commit -w commit &&
+		echo even more bob >file &&
+		git commit -a -m seventh &&
+		git push ../bob-pub
+	)
+'
+
+test_done
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
new file mode 100755
index 0000000..83e2e8a
--- /dev/null
+++ b/t/t5521-pull-options.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='pull options'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+test_expect_success 'setup' '
+	mkdir parent &&
+	(cd parent && git init &&
+	 echo one >file && git add file &&
+	 git commit -m one)
+'
+
+cd "$D"
+
+test_expect_success 'git pull -q' '
+	mkdir clonedq &&
+	cd clonedq &&
+	git pull -q "$D/parent" >out 2>err &&
+	test ! -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull' '
+	mkdir cloned &&
+	cd cloned &&
+	git pull "$D/parent" >out 2>err &&
+	test -s out
+'
+cd "$D"
+
+test_expect_success 'git pull -v' '
+	mkdir clonedv &&
+	cd clonedv &&
+	git pull -v "$D/parent" >out 2>err &&
+	test -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull -v -q' '
+	mkdir clonedvq &&
+	cd clonedvq &&
+	git pull -v -q "$D/parent" >out 2>err &&
+	test ! -s out
+'
+
+cd "$D"
+
+test_expect_success 'git pull -q -v' '
+	mkdir clonedqv &&
+	cd clonedqv &&
+	git pull -q -v "$D/parent" >out 2>err &&
+	test -s out
+'
+
+test_done
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
new file mode 100755
index 0000000..86bbd7d
--- /dev/null
+++ b/t/t5522-pull-symlink.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='pulling from symlinked subdir'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+	say 'Symbolic links not supported, skipping tests.'
+	test_done
+fi
+
+# The scenario we are building:
+#
+#   trash\ directory/
+#     clone-repo/
+#       subdir/
+#         bar
+#     subdir-link -> clone-repo/subdir/
+#
+# The working directory is subdir-link.
+
+mkdir subdir
+echo file >subdir/file
+git add subdir/file
+git commit -q -m file
+git clone -q . clone-repo
+ln -s clone-repo/subdir/ subdir-link
+
+
+# Demonstrate that things work if we just avoid the symlink
+#
+test_expect_success 'pulling from real subdir' '
+	(
+		echo real >subdir/file &&
+		git commit -m real subdir/file &&
+		cd clone-repo/subdir/ &&
+		git pull &&
+		test real = $(cat file)
+	)
+'
+
+# From subdir-link, pulling should work as it does from
+# clone-repo/subdir/.
+#
+# Instead, the error pull gave was:
+#
+#   fatal: 'origin': unable to chdir or not a git archive
+#   fatal: The remote end hung up unexpectedly
+#
+# because git would find the .git/config for the "trash directory"
+# repo, not for the clone-repo repo.  The "trash directory" repo
+# had no entry for origin.  Git found the wrong .git because
+# git rev-parse --show-cdup printed a path relative to
+# clone-repo/subdir/, not subdir-link/.  Git rev-parse --show-cdup
+# used the correct .git, but when the git pull shell script did
+# "cd `git rev-parse --show-cdup`", it ended up in the wrong
+# directory.  A POSIX shell's "cd" works a little differently
+# than chdir() in C; "cd -P" is much closer to chdir().
+#
+test_expect_success 'pulling from symlinked subdir' '
+	(
+		echo link >subdir/file &&
+		git commit -m link subdir/file &&
+		cd subdir-link/ &&
+		git pull &&
+		test link = $(cat file)
+	)
+'
+
+# Prove that the remote end really is a repo, and other commands
+# work fine in this context.  It's just that "git pull" breaks.
+#
+test_expect_success 'pushing from symlinked subdir' '
+	(
+		cd subdir-link/ &&
+		echo push >file &&
+		git commit -m push ./file &&
+		git push
+	) &&
+	test push = $(git show HEAD:subdir/file)
+'
+
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index b0d242e..5fe479e 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -11,22 +11,16 @@
 
 ROOT_PATH="$PWD"
 LIB_HTTPD_DAV=t
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
 
 if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
 then
 	say "skipping test, USE_CURL_MULTI is not defined"
 	test_done
-	exit
 fi
 
-. ../lib-httpd.sh
-
-if ! start_httpd >&3 2>&4
-then
-	say "skipping test, web server setup failed"
-	test_done
-	exit
-fi
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
 
 test_expect_success 'setup remote repository' '
 	cd "$ROOT_PATH" &&
@@ -51,17 +45,29 @@
 	git clone $HTTPD_URL/test_repo.git test_repo_clone
 '
 
-test_expect_failure 'push to remote repository' '
+test_expect_failure 'push to remote repository with packed refs' '
 	cd "$ROOT_PATH"/test_repo_clone &&
 	: >path2 &&
 	git add path2 &&
 	test_tick &&
 	git commit -m path2 &&
+	HEAD=$(git rev-parse --verify HEAD) &&
 	git push &&
-	[ -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/refs/heads/master" ]
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
 '
 
-test_expect_failure 'create and delete remote branch' '
+test_expect_success ' push to remote repository with unpacked refs' '
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 rm packed-refs &&
+	 git update-ref refs/heads/master \
+		0c973ae9bd51902a28466f3850b543fa66a6aaf4) &&
+	git push &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'create and delete remote branch' '
 	cd "$ROOT_PATH"/test_repo_clone &&
 	git checkout -b dev &&
 	: >path3 &&
@@ -76,6 +82,24 @@
 	test_must_fail git show-ref --verify refs/remotes/origin/dev
 '
 
+test_expect_success 'MKCOL sends directory names with trailing slashes' '
+
+	! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
+
+'
+
+x1="[0-9a-f]"
+x2="$x1$x1"
+x5="$x1$x1$x1$x1$x1"
+x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
+x40="$x38$x2"
+
+test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
+	sed -e "s/PUT /OP /" -e "s/MOVE /OP /" "$HTTPD_ROOT_PATH"/access.log |
+	grep -e "\"OP .*/objects/$x2/${x38}_$x40 HTTP/[.0-9]*\" 20[0-9] "
+
+'
+
 stop_httpd
 
 test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
new file mode 100755
index 0000000..05b1b62
--- /dev/null
+++ b/t/t5550-http-fetch.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='test fetching over http'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+	say 'skipping test, git built without http support'
+	test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
+start_httpd
+
+test_expect_success 'setup repository' '
+	echo content >file &&
+	git add file &&
+	git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	 git --bare init &&
+	 echo "exec git update-server-info" >hooks/post-update &&
+	 chmod +x hooks/post-update
+	) &&
+	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	git push public master:master
+'
+
+test_expect_success 'clone http repository' '
+	git clone $HTTPD_URL/repo.git clone &&
+	test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via http' '
+	echo content >>file &&
+	git commit -a -m two &&
+	git push public
+	(cd clone && git pull) &&
+	test_cmp file clone/file
+'
+
+test_expect_success 'http remote detects correct HEAD' '
+	git push public master:other &&
+	(cd clone &&
+	 git remote set-head origin -d &&
+	 git remote set-head origin -a &&
+	 git symbolic-ref refs/remotes/origin/HEAD > output &&
+	 echo refs/remotes/origin/master > expect &&
+	 test_cmp expect output
+	)
+'
+
+stop_httpd
+test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 78a3fa6..2335d8b 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -125,4 +125,53 @@
 
 '
 
+test_expect_success 'clone to an existing empty directory' '
+	mkdir target-3 &&
+	git clone src target-3 &&
+	T=$( cd target-3 && git rev-parse HEAD ) &&
+	S=$( cd src && git rev-parse HEAD ) &&
+	test "$T" = "$S"
+'
+
+test_expect_success 'clone to an existing non-empty directory' '
+	mkdir target-4 &&
+	>target-4/Fakefile &&
+	test_must_fail git clone src target-4
+'
+
+test_expect_success 'clone to an existing path' '
+	>target-5 &&
+	test_must_fail git clone src target-5
+'
+
+test_expect_success 'clone a void' '
+	mkdir src-0 &&
+	(
+		cd src-0 && git init
+	) &&
+	git clone src-0 target-6 &&
+	(
+		cd src-0 && test_commit A
+	) &&
+	git clone src-0 target-7 &&
+	# There is no reason to insist they are bit-for-bit
+	# identical, but this test should suffice for now.
+	test_cmp target-6/.git/config target-7/.git/config
+'
+
+test_expect_success 'clone respects global branch.autosetuprebase' '
+	(
+		HOME=$(pwd) &&
+		export HOME &&
+		test_config="$HOME/.gitconfig" &&
+		unset GIT_CONFIG_NOGLOBAL &&
+		git config -f "$test_config" branch.autosetuprebase remote &&
+		rm -fr dst &&
+		git clone src dst &&
+		cd dst &&
+		actual="z$(git config branch.master.rebase)" &&
+		test ztrue = $actual
+	)
+'
+
 test_done
diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh
index 82b1d1e..deffdae 100755
--- a/t/t5602-clone-remote-exec.sh
+++ b/t/t5602-clone-remote-exec.sh
@@ -18,8 +18,8 @@
 '
 
 test_expect_success 'clone calls specified git upload-pack with -u option' '
-	GIT_SSH=./not_ssh git clone -u /something/bin/git-upload-pack localhost:/path/to/repo junk
-	echo "localhost /something/bin/git-upload-pack '\''/path/to/repo'\''" >expected
+	GIT_SSH=./not_ssh git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk
+	echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected
 	test_cmp expected not_ssh_output
 '
 
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
index 8dfaaa4..3559d17 100755
--- a/t/t5701-clone-local.sh
+++ b/t/t5701-clone-local.sh
@@ -11,8 +11,8 @@
 	git clone --bare . x &&
 	test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
 	test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
-	git bundle create b1.bundle --all HEAD &&
-	git bundle create b2.bundle --all &&
+	git bundle create b1.bundle --all &&
+	git bundle create b2.bundle master &&
 	mkdir dir &&
 	cp b1.bundle dir/b3
 	cp b1.bundle b4
@@ -116,4 +116,20 @@
 	test ! -e .git/refs/heads/master
 '
 
+test_expect_success 'clone empty repository' '
+	cd "$D" &&
+	mkdir empty &&
+	(cd empty && git init) &&
+	git clone empty empty-clone &&
+	test_tick &&
+	(cd empty-clone
+	 echo "content" >> foo &&
+	 git add foo &&
+	 git commit -m "Initial commit" &&
+	 git push origin master &&
+	 expected=$(git rev-parse master) &&
+	 actual=$(git --git-dir=../empty/.git rev-parse master) &&
+	 test $actual = $expected)
+'
+
 test_done
diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh
index 328e4d9..27825f5 100755
--- a/t/t5702-clone-options.sh
+++ b/t/t5702-clone-options.sh
@@ -19,4 +19,17 @@
 
 '
 
+test_expect_success 'redirected clone' '
+
+	git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
+	test ! -s err
+
+'
+test_expect_success 'redirected clone -v' '
+
+	git clone -v "file://$(pwd)/parent" clone-redirected-v >out 2>err &&
+	test -s err
+
+'
+
 test_done
diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh
new file mode 100755
index 0000000..a8f4419
--- /dev/null
+++ b/t/t5704-bundle.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='some bundle related tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	: > file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	test_tick &&
+	git tag -m tag tag &&
+	: > file2 &&
+	git add file2 &&
+	: > file3 &&
+	test_tick &&
+	git commit -m second &&
+	git add file3 &&
+	test_tick &&
+	git commit -m third
+
+'
+
+test_expect_success 'tags can be excluded by rev-list options' '
+
+	git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
+	git ls-remote bundle > output &&
+	! grep tag output
+
+'
+
+test_done
diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh
new file mode 100755
index 0000000..9f52154
--- /dev/null
+++ b/t/t5705-clone-2gb.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='Test cloning a repository larger than 2 gigabyte'
+. ./test-lib.sh
+
+test -z "$GIT_TEST_CLONE_2GB" &&
+say "Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
+test_done &&
+exit
+
+test_expect_success 'setup' '
+
+	git config pack.compression 0 &&
+	git config pack.depth 0 &&
+	blobsize=$((20*1024*1024)) &&
+	blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
+	i=1 &&
+	(while test $i -le $blobcount
+	 do
+		printf "Generating blob $i/$blobcount\r" >&2 &&
+		printf "blob\nmark :$i\ndata $blobsize\n" &&
+		#test-genrandom $i $blobsize &&
+		printf "%-${blobsize}s" $i &&
+		echo "M 100644 :$i $i" >> commit
+		i=$(($i+1)) ||
+		echo $? > exit-status
+	 done &&
+	 echo "commit refs/heads/master" &&
+	 echo "author A U Thor <author@email.com> 123456789 +0000" &&
+	 echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+	 echo "data 5" &&
+	 echo ">2gb" &&
+	 cat commit) |
+	git fast-import &&
+	test ! -f exit-status
+
+'
+
+test_expect_success 'clone' '
+
+	git clone --bare --no-hardlinks . clone
+
+'
+
+test_done
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index 8f5de09..b4e8fba 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -5,7 +5,7 @@
 test_description='Tests git rev-list --bisect functionality'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 # usage: test_bisection max-diff bisect-option head ^prune...
 #
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 5daa0be..2c73f2d 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -6,7 +6,7 @@
 test_description='Tests git rev-list --topo-order functionality'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 list_duplicates()
 {
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 86bf7e1..59d1f62 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -14,7 +14,7 @@
 test_format() {
 	cat >expect.$1
 	test_expect_success "format $1" "
-git rev-list --pretty=format:$2 master >output.$1 &&
+git rev-list --pretty=format:'$2' master >output.$1 &&
 test_cmp expect.$1 output.$1
 "
 }
@@ -101,6 +101,13 @@
 foobarbazxyzzy
 EOF
 
+test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+foo
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+foo
+EOF
+
 cat >commit-msg <<'EOF'
 Test printing of complex bodies
 
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index b6e57b2..04e4b7c 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -108,4 +108,52 @@
     'MB=$(git merge-base --all PL PR) &&
      expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
 
+# Another set to demonstrate base between one commit and a merge
+# in the documentation.
+
+test_expect_success 'merge-base for octopus-step (setup)' '
+	test_tick && git commit --allow-empty -m root && git tag MMR &&
+	test_tick && git commit --allow-empty -m 1 && git tag MM1 &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m A && git tag MMA &&
+	git checkout MM1 &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m B && git tag MMB &&
+	git checkout MMR &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m C && git tag MMC
+'
+
+test_expect_success 'merge-base A B C' '
+	MB=$(git merge-base --all MMA MMB MMC) &&
+	MM1=$(git rev-parse --verify MM1) &&
+	test "$MM1" = "$MB"
+'
+
+test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+	git reset --hard MMR &&
+	test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+	git reset --hard E &&
+	test_tick && git commit --allow-empty -m 2 && git tag CC2 &&
+	test_tick && git merge -s ours CC1 &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m B && git tag CCB &&
+	git reset --hard CC1 &&
+	test_tick && git merge -s ours CC2 &&
+	test_tick && git commit --allow-empty -m A && git tag CCA
+'
+
+test_expect_success 'merge-base B A^^ A^^2' '
+	MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) &&
+	MB1=$(git rev-parse CC1 CC2 | sort) &&
+	test "$MB0" = "$MB1"
+'
+
 test_done
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
new file mode 100755
index 0000000..510bb96
--- /dev/null
+++ b/t/t6012-rev-list-simplify.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='merge simplification'
+
+. ./test-lib.sh
+
+note () {
+	git tag "$1"
+}
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+
+unnote () {
+	git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g"
+}
+
+test_expect_success setup '
+	echo "Hi there" >file &&
+	git add file &&
+	test_tick && git commit -m "Initial file" &&
+	note A &&
+
+	git branch other-branch &&
+
+	echo "Hello" >file &&
+	git add file &&
+	test_tick && git commit -m "Modified file" &&
+	note B &&
+
+	git checkout other-branch &&
+
+	echo "Hello" >file &&
+	git add file &&
+	test_tick && git commit -m "Modified the file identically" &&
+	note C &&
+
+	echo "This is a stupid example" >another-file &&
+	git add another-file &&
+	test_tick && git commit -m "Add another file" &&
+	note D &&
+
+	test_tick && git merge -m "merge" master &&
+	note E &&
+
+	echo "Yet another" >elif &&
+	git add elif &&
+	test_tick && git commit -m "Irrelevant change" &&
+	note F &&
+
+	git checkout master &&
+	echo "Yet another" >elif &&
+	git add elif &&
+	test_tick && git commit -m "Another irrelevant change" &&
+	note G &&
+
+	test_tick && git merge -m "merge" other-branch &&
+	note H &&
+
+	echo "Final change" >file &&
+	test_tick && git commit -a -m "Final change" &&
+	note I
+'
+
+FMT='tformat:%P 	%H | %s'
+
+check_result () {
+	for c in $1
+	do
+		echo "$c"
+	done >expect &&
+	shift &&
+	param="$*" &&
+	test_expect_success "log $param" '
+		git log --pretty="$FMT" --parents $param |
+		unnote >actual &&
+		sed -e "s/^.*	\([^ ]*\) .*/\1/" >check <actual &&
+		test_cmp expect check || {
+			cat actual
+			false
+		}
+	'
+}
+
+check_result 'I H G F E D C B A' --full-history
+check_result 'I H E C B A' --full-history -- file
+check_result 'I H E C B A' --full-history --topo-order -- file
+check_result 'I H E C B A' --full-history --date-order -- file
+check_result 'I E C B A' --simplify-merges -- file
+check_result 'I B A' -- file
+check_result 'I B A' --topo-order -- file
+
+test_done
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
new file mode 100755
index 0000000..59fc2f0
--- /dev/null
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='--reverse combines with --parents'
+
+. ./test-lib.sh
+
+
+commit () {
+	test_tick &&
+	echo $1 > foo &&
+	git add foo &&
+	git commit -m "$1"
+}
+
+test_expect_success 'set up --reverse example' '
+	commit one &&
+	git tag root &&
+	commit two &&
+	git checkout -b side HEAD^ &&
+	commit three &&
+	git checkout master &&
+	git merge -s ours side &&
+	commit five
+	'
+
+test_expect_success '--reverse --parents --full-history combines correctly' '
+	git rev-list --parents --full-history master -- foo |
+		perl -e "print reverse <>" > expected &&
+	git rev-list --reverse --parents --full-history master -- foo \
+		> actual &&
+	test_cmp actual expected
+	'
+
+test_expect_success '--boundary does too' '
+	git rev-list --boundary --parents --full-history master ^root -- foo |
+		perl -e "print reverse <>" > expected &&
+	git rev-list --boundary --reverse --parents --full-history \
+		master ^root -- foo > actual &&
+	test_cmp actual expected
+	'
+
+test_done
diff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh
new file mode 100755
index 0000000..991ab4a
--- /dev/null
+++ b/t/t6014-rev-list-all.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='--all includes detached HEADs'
+
+. ./test-lib.sh
+
+
+commit () {
+	test_tick &&
+	echo $1 > foo &&
+	git add foo &&
+	git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+
+	commit one &&
+	commit two &&
+	git checkout HEAD^ &&
+	commit detached
+
+'
+
+test_expect_success 'rev-list --all lists detached HEAD' '
+
+	test 3 = $(git rev-list --all | wc -l)
+
+'
+
+test_expect_success 'repack does not lose detached HEAD' '
+
+	git gc &&
+	git prune --expire=now &&
+	git show HEAD
+
+'
+
+test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index f674c48..f8942bc 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -136,7 +136,7 @@
 
 test_expect_success 'binary files cannot be merged' '
 	test_must_fail git merge-file -p \
-		orig.txt ../test4012.png new1.txt 2> merge.err &&
+		orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err &&
 	grep "Cannot merge binary files" merge.err
 '
 
@@ -150,8 +150,8 @@
 
 '
 
-sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit;/" < new6.txt > new8.txt
-sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit --/" < new7.txt > new9.txt
+sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit;/"< new6.txt | tr '%' '\012' > new8.txt
+sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit --/" < new7.txt | tr '%' '\012' > new9.txt
 
 test_expect_success 'ZEALOUS_ALNUM' '
 
@@ -161,4 +161,48 @@
 
 '
 
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+|||||||
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+=======
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>> new9.txt
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam TU mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success '"diff3 -m" style output (1)' '
+	test_must_fail git merge-file -p --diff3 \
+		new8.txt new5.txt new9.txt >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '"diff3 -m" style output (2)' '
+	git config merge.conflictstyle diff3 &&
+	test_must_fail git merge-file -p \
+		new8.txt new5.txt new9.txt >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 802d0d0..129fa30 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -97,4 +97,27 @@
 		merge.err
 '
 
+test_expect_success 'mark rename/delete as unmerged' '
+
+	git reset --hard &&
+	git checkout -b delete &&
+	git rm a1 &&
+	test_tick &&
+	git commit -m delete &&
+	git checkout -b rename HEAD^ &&
+	git mv a1 a2
+	test_tick &&
+	git commit -m rename &&
+	test_must_fail git merge delete &&
+	test 1 = $(git ls-files --unmerged | wc -l) &&
+	git rev-parse --verify :2:a2 &&
+	test_must_fail git rev-parse --verify :3:a2 &&
+	git checkout -f delete &&
+	test_must_fail git merge rename &&
+	test 1 = $(git ls-files --unmerged | wc -l) &&
+	test_must_fail git rev-parse --verify :2:a2 &&
+	git rev-parse --verify :3:a2
+
+'
+
 test_done
diff --git a/t/t6025-merge-symlinks.sh b/t/t6025-merge-symlinks.sh
index 53892a5..433c4de 100755
--- a/t/t6025-merge-symlinks.sh
+++ b/t/t6025-merge-symlinks.sh
@@ -18,11 +18,11 @@
 git commit -m initial &&
 git branch b-symlink &&
 git branch b-file &&
-l=$(echo -n file | git hash-object -t blob -w --stdin) &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
 echo "120000 $l	symlink" | git update-index --index-info &&
 git commit -m master &&
 git checkout b-symlink &&
-l=$(echo -n file-different | git hash-object -t blob -w --stdin) &&
+l=$(printf file-different | git hash-object -t blob -w --stdin) &&
 echo "120000 $l	symlink" | git update-index --index-info &&
 git commit -m b-symlink &&
 git checkout b-file &&
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 4b423e9..1ba0a25 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -142,4 +142,26 @@
 	rm -f $o $a $b
 '
 
+test_expect_success 'up-to-date merge without common ancestor' '
+	test_create_repo repo1 &&
+	test_create_repo repo2 &&
+	test_tick &&
+	(
+		cd repo1 &&
+		>a &&
+		git add a &&
+		git commit -m initial
+	) &&
+	test_tick &&
+	(
+		cd repo2 &&
+		git commit --allow-empty -m initial
+	) &&
+	test_tick &&
+	(
+		cd repo1 &&
+		git pull ../repo2 master
+	)
+'
+
 test_done
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
index 92ca1f0..b519626 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6027-merge-binary.sh
@@ -6,7 +6,7 @@
 
 test_expect_success setup '
 
-	cat ../test4012.png >m &&
+	cat "$TEST_DIRECTORY"/test4012.png >m &&
 	git add m &&
 	git ls-files -s | sed -e "s/ 0	/ 1	/" >E1 &&
 	test_tick &&
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 0b81e65..54b7ea6 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -338,8 +338,25 @@
 	grep "$HASH6 is first bad commit" my_bisect_log.txt
 '
 
-test_expect_success 'bisect starting with a detached HEAD' '
+test_expect_success 'bisect skip only one range' '
+	git bisect reset &&
+	git bisect start $HASH7 $HASH1 &&
+	git bisect skip $HASH1..$HASH5 &&
+	test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
+	test_must_fail git bisect bad > my_bisect_log.txt &&
+	grep "first bad commit could be any of" my_bisect_log.txt
+'
 
+test_expect_success 'bisect skip many ranges' '
+	git bisect start $HASH7 $HASH1 &&
+	test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+	git bisect skip $HASH2 $HASH2.. ..$HASH5 &&
+	test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
+	test_must_fail git bisect bad > my_bisect_log.txt &&
+	grep "first bad commit could be any of" my_bisect_log.txt
+'
+
+test_expect_success 'bisect starting with a detached HEAD' '
 	git bisect reset &&
 	git checkout master^ &&
 	HEAD=$(git rev-parse --verify HEAD) &&
@@ -375,6 +392,180 @@
 	git branch -D bisect
 '
 
+# This creates a "side" branch to test "siblings" cases.
+#
+# H1-H2-H3-H4-H5-H6-H7  <--other
+#            \
+#             S5-S6-S7  <--side
+#
+test_expect_success 'side branch creation' '
+	git bisect reset &&
+	git checkout -b side $HASH4 &&
+	add_line_into_file "5(side): first line on a side branch" hello2 &&
+	SIDE_HASH5=$(git rev-parse --verify HEAD) &&
+	add_line_into_file "6(side): second line on a side branch" hello2 &&
+	SIDE_HASH6=$(git rev-parse --verify HEAD) &&
+	add_line_into_file "7(side): third line on a side branch" hello2 &&
+	SIDE_HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'good merge base when good and bad are siblings' '
+	git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
+	grep "merge base must be tested" my_bisect_log.txt &&
+	grep $HASH4 my_bisect_log.txt &&
+	git bisect good > my_bisect_log.txt &&
+	test_must_fail grep "merge base must be tested" my_bisect_log.txt &&
+	grep $HASH6 my_bisect_log.txt &&
+	git bisect reset
+'
+test_expect_success 'skipped merge base when good and bad are siblings' '
+	git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
+	grep "merge base must be tested" my_bisect_log.txt &&
+	grep $HASH4 my_bisect_log.txt &&
+	git bisect skip > my_bisect_log.txt 2>&1 &&
+	grep "Warning" my_bisect_log.txt &&
+	grep $SIDE_HASH6 my_bisect_log.txt &&
+	git bisect reset
+'
+
+test_expect_success 'bad merge base when good and bad are siblings' '
+	git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
+	grep "merge base must be tested" my_bisect_log.txt &&
+	grep $HASH4 my_bisect_log.txt &&
+	test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
+	grep "merge base $HASH4 is bad" my_bisect_log.txt &&
+	grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
+	git bisect reset
+'
+
+# This creates a few more commits (A and B) to test "siblings" cases
+# when a good and a bad rev have many merge bases.
+#
+# We should have the following:
+#
+# H1-H2-H3-H4-H5-H6-H7
+#            \  \     \
+#             S5-A     \
+#              \        \
+#               S6-S7----B
+#
+# And there A and B have 2 merge bases (S5 and H5) that should be
+# reported by "git merge-base --all A B".
+#
+test_expect_success 'many merge bases creation' '
+	git checkout "$SIDE_HASH5" &&
+	git merge -m "merge HASH5 and SIDE_HASH5" "$HASH5" &&
+	A_HASH=$(git rev-parse --verify HEAD) &&
+	git checkout side &&
+	git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
+	B_HASH=$(git rev-parse --verify HEAD) &&
+	git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
+	test $(wc -l < merge_bases.txt) = "2" &&
+	grep "$HASH5" merge_bases.txt &&
+	grep "$SIDE_HASH5" merge_bases.txt
+'
+
+test_expect_success 'good merge bases when good and bad are siblings' '
+	git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
+	grep "merge base must be tested" my_bisect_log.txt &&
+	git bisect good > my_bisect_log2.txt &&
+	grep "merge base must be tested" my_bisect_log2.txt &&
+	{
+		{
+			grep "$SIDE_HASH5" my_bisect_log.txt &&
+			grep "$HASH5" my_bisect_log2.txt
+		} || {
+			grep "$SIDE_HASH5" my_bisect_log2.txt &&
+			grep "$HASH5" my_bisect_log.txt
+		}
+	} &&
+	git bisect reset
+'
+
+check_trace() {
+	grep "$1" "$GIT_TRACE" | grep "\^$2" | grep "$3" >/dev/null
+}
+
+test_expect_success 'optimized merge base checks' '
+	GIT_TRACE="$(pwd)/trace.log" &&
+	export GIT_TRACE &&
+	git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
+	grep "merge base must be tested" my_bisect_log.txt &&
+	grep "$HASH4" my_bisect_log.txt &&
+	check_trace "rev-list" "$HASH7" "$SIDE_HASH7" &&
+	git bisect good > my_bisect_log2.txt &&
+	test -f ".git/BISECT_ANCESTORS_OK" &&
+	test "$HASH6" = $(git rev-parse --verify HEAD) &&
+	: > "$GIT_TRACE" &&
+	git bisect bad > my_bisect_log3.txt &&
+	test_must_fail check_trace "rev-list" "$HASH6" "$SIDE_HASH7" &&
+	git bisect good "$A_HASH" > my_bisect_log4.txt &&
+	grep "merge base must be tested" my_bisect_log4.txt &&
+	test_must_fail test -f ".git/BISECT_ANCESTORS_OK" &&
+	check_trace "rev-list" "$HASH6" "$A_HASH" &&
+	unset GIT_TRACE
+'
+
+# This creates another side branch called "parallel" with some files
+# in some directories, to test bisecting with paths.
+#
+# We should have the following:
+#
+#    P1-P2-P3-P4-P5-P6-P7
+#   /        /        /
+# H1-H2-H3-H4-H5-H6-H7
+#            \  \     \
+#             S5-A     \
+#              \        \
+#               S6-S7----B
+#
+test_expect_success '"parallel" side branch creation' '
+	git bisect reset &&
+	git checkout -b parallel $HASH1 &&
+	mkdir dir1 dir2 &&
+	add_line_into_file "1(para): line 1 on parallel branch" dir1/file1 &&
+	PARA_HASH1=$(git rev-parse --verify HEAD) &&
+	add_line_into_file "2(para): line 2 on parallel branch" dir2/file2 &&
+	PARA_HASH2=$(git rev-parse --verify HEAD) &&
+	add_line_into_file "3(para): line 3 on parallel branch" dir2/file3 &&
+	PARA_HASH3=$(git rev-parse --verify HEAD)
+	git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
+	PARA_HASH4=$(git rev-parse --verify HEAD)
+	add_line_into_file "5(para): add line on parallel branch" dir1/file1 &&
+	PARA_HASH5=$(git rev-parse --verify HEAD)
+	add_line_into_file "6(para): add line on parallel branch" dir2/file2 &&
+	PARA_HASH6=$(git rev-parse --verify HEAD)
+	git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
+	PARA_HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'restricting bisection on one dir' '
+	git bisect reset &&
+	git bisect start HEAD $HASH1 -- dir1 &&
+	para1=$(git rev-parse --verify HEAD) &&
+	test "$para1" = "$PARA_HASH1" &&
+	git bisect bad > my_bisect_log.txt &&
+	grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'restricting bisection on one dir and a file' '
+	git bisect reset &&
+	git bisect start HEAD $HASH1 -- dir1 hello &&
+	para4=$(git rev-parse --verify HEAD) &&
+	test "$para4" = "$PARA_HASH4" &&
+	git bisect bad &&
+	hash3=$(git rev-parse --verify HEAD) &&
+	test "$hash3" = "$HASH3" &&
+	git bisect good &&
+	hash4=$(git rev-parse --verify HEAD) &&
+	test "$hash4" = "$HASH4" &&
+	git bisect good &&
+	para1=$(git rev-parse --verify HEAD) &&
+	test "$para1" = "$PARA_HASH1" &&
+	git bisect good > my_bisect_log.txt &&
+	grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
+'
+
 #
 #
 test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
index 8073e0c..8a3304f 100755
--- a/t/t6031-merge-recursive.sh
+++ b/t/t6031-merge-recursive.sh
@@ -3,8 +3,10 @@
 test_description='merge-recursive: handle file mode'
 . ./test-lib.sh
 
-# Note that we follow "chmod +x F" with "update-index --chmod=+x F" to
-# help filesystems that do not have the executable bit.
+if ! test "$(git config --bool core.filemode)" = false
+then
+	test_set_prereq FILEMODE
+fi
 
 test_expect_success 'mode change in one branch: keep changed version' '
 	: >file1 &&
@@ -15,11 +17,14 @@
 	git add dummy &&
 	git commit -m a &&
 	git checkout -b b1 master &&
-	chmod +x file1 &&
-	git update-index --chmod=+x file1 &&
+	test_chmod +x file1 &&
 	git commit -m b1 &&
 	git checkout a1 &&
 	git merge-recursive master -- a1 b1 &&
+	git ls-files -s file1 | grep ^100755
+'
+
+test_expect_success FILEMODE 'verify executable bit on file' '
 	test -x file1
 '
 
@@ -28,8 +33,7 @@
 	git checkout -b a2 master &&
 	: >file2 &&
 	H=$(git hash-object file2) &&
-	chmod +x file2 &&
-	git update-index --add --chmod=+x file2 &&
+	test_chmod +x file2 &&
 	git commit -m a2 &&
 	git checkout -b b2 master &&
 	: >file2 &&
@@ -46,6 +50,10 @@
 		echo "100644 $H 3	file2"
 	) >expect &&
 	test_cmp actual expect &&
+	git ls-files -s file2 | grep ^100755
+'
+
+test_expect_success FILEMODE 'verify executable bit on file' '
 	test -x file2
 '
 
diff --git a/t/t6023-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
similarity index 100%
rename from t/t6023-merge-rename-nocruft.sh
rename to t/t6034-merge-rename-nocruft.sh
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index aac212e..3d6db4d 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -29,7 +29,9 @@
 		git checkout -b b4 origin &&
 		advance e &&
 		advance f
-	)
+	) &&
+	git checkout -b follower --track master &&
+	advance g
 '
 
 script='s/^..\(b.\)[	 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
@@ -53,7 +55,13 @@
 	(
 		cd test && git checkout b1
 	) >actual &&
-	grep -e "have 1 and 1 different" actual
+	grep "have 1 and 1 different" actual
+'
+
+test_expect_success 'checkout with local tracked branch' '
+	git checkout master &&
+	git checkout follower >actual
+	grep "is ahead of" actual
 '
 
 test_expect_success 'status' '
@@ -63,7 +71,7 @@
 		# reports nothing to commit
 		test_must_fail git status
 	) >actual &&
-	grep -e "have 1 and 1 different" actual
+	grep "have 1 and 1 different" actual
 '
 
 
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 919552a..f105fab 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -6,7 +6,7 @@
 test_description='Test git rev-parse with different parent options'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 date >path0
 git update-index --add path0
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 16cc635..8c7e081 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -91,15 +91,21 @@
 check_describe A-* HEAD^^2
 check_describe B HEAD^^2^
 
-check_describe A-* --tags HEAD
-check_describe A-* --tags HEAD^
-check_describe D-* --tags HEAD^^
-check_describe A-* --tags HEAD^^2
+check_describe c-* --tags HEAD
+check_describe c-* --tags HEAD^
+check_describe e-* --tags HEAD^^
+check_describe c-* --tags HEAD^^2
 check_describe B --tags HEAD^^2^
 
 check_describe B-0-* --long HEAD^^2^
 check_describe A-3-* --long HEAD^^2
 
+: >err.expect
+check_describe A --all A^0
+test_expect_success 'no warning was displayed for A' '
+	test_cmp err.expect err.actual
+'
+
 test_expect_success 'rename tag A to Q locally' '
 	mv .git/refs/tags/A .git/refs/tags/Q
 '
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index bc74349..2049ab6 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -83,13 +83,13 @@
 '
 
 cat >expected <<EOF
-Merge branch 'left' of ../$test
+Merge branch 'left' of $(pwd)
 EOF
 
 test_expect_success 'merge-msg test #2' '
 
 	git checkout master &&
-	git fetch ../"$test" left &&
+	git fetch "$(pwd)" left &&
 
 	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
 	test_cmp expected actual
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 26995b3..8052c86 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -26,6 +26,13 @@
 	git tag -a -m "Tagging at $datestamp" testtag
 '
 
+test_expect_success 'Create upstream config' '
+	git update-ref refs/remotes/origin/master master &&
+	git remote add origin nowhere &&
+	git config branch.master.remote origin &&
+	git config branch.master.merge refs/heads/master
+'
+
 test_atom() {
 	case "$1" in
 		head) ref=refs/heads/master ;;
@@ -39,6 +46,7 @@
 }
 
 test_atom head refname refs/heads/master
+test_atom head upstream refs/remotes/origin/master
 test_atom head objecttype commit
 test_atom head objectsize 171
 test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581
@@ -68,6 +76,7 @@
 '
 
 test_atom tag refname refs/tags/testtag
+test_atom tag upstream ''
 test_atom tag objecttype tag
 test_atom tag objectsize 154
 test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322
@@ -203,6 +212,7 @@
 
 cat >expected <<\EOF
 refs/heads/master
+refs/remotes/origin/master
 refs/tags/testtag
 EOF
 
@@ -214,6 +224,7 @@
 
 cat >expected <<\EOF
 refs/tags/testtag
+refs/remotes/origin/master
 refs/heads/master
 EOF
 
@@ -224,6 +235,7 @@
 
 cat >expected <<\EOF
 'refs/heads/master'
+'refs/remotes/origin/master'
 'refs/tags/testtag'
 EOF
 
@@ -244,6 +256,7 @@
 
 cat >expected <<\EOF
 "refs/heads/master"
+"refs/remotes/origin/master"
 "refs/tags/testtag"
 EOF
 
@@ -262,6 +275,71 @@
 	"
 done
 
+cat >expected <<\EOF
+master
+testtag
+EOF
+
+test_expect_success 'Check short refname format' '
+	(git for-each-ref --format="%(refname:short)" refs/heads &&
+	git for-each-ref --format="%(refname:short)" refs/tags) >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<EOF
+origin/master
+EOF
+
+test_expect_success 'Check short upstream format' '
+	git for-each-ref --format="%(upstream:short)" refs/heads >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Check for invalid refname format' '
+	test_must_fail git for-each-ref --format="%(refname:INVALID)"
+'
+
+cat >expected <<\EOF
+heads/master
+tags/master
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (strict)' '
+	git config --bool core.warnambiguousrefs true &&
+	git checkout -b newtag &&
+	echo "Using $datestamp" > one &&
+	git add one &&
+	git commit -m "Branch" &&
+	setdate_and_increment &&
+	git tag -m "Tagging at $datestamp" master &&
+	git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/master
+master
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (loose)' '
+	git config --bool core.warnambiguousrefs false &&
+	git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/ambiguous
+ambiguous
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs II (loose)' '
+	git checkout master &&
+	git tag ambiguous testtag^0 &&
+	git branch ambiguous testtag^0 &&
+	git for-each-ref --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'an unusual tag with an incomplete line' '
 
 	git tag -m "bogo" bogo &&
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index d2ec550..10b8f8c 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -6,7 +6,7 @@
 test_expect_success \
     'prepare reference tree' \
     'mkdir path0 path1 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
      git commit -m add -a'
 
@@ -73,7 +73,7 @@
 
 test_expect_success \
     'adding another file' \
-    'cp ../../README path0/README &&
+    'cp "$TEST_DIRECTORY"/../README path0/README &&
      git add path0/README &&
      git commit -m add2 -a'
 
@@ -206,7 +206,7 @@
 
 rm -f dirty dirty2
 
-test_expect_success 'git mv should overwrite symlink to a file' '
+test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
 
 	rm -fr .git &&
 	git init &&
@@ -225,7 +225,7 @@
 
 rm -f moved symlink
 
-test_expect_success 'git mv should overwrite file with a symlink' '
+test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' '
 
 	rm -fr .git &&
 	git init &&
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 5e359cb..c493854 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -109,7 +109,7 @@
 	'
 
 	test_expect_success "grep -c $L (no /dev/null)" '
-		! git grep -c test $H | grep -q /dev/null
+		! git grep -c test $H | grep /dev/null
         '
 
 done
@@ -161,7 +161,14 @@
 	git log --author=-0700  --pretty=tformat:%s >actual &&
 	>expect &&
 	test_cmp expect actual
+'
 
+test_expect_success 'grep with CE_VALID file' '
+	git update-index --assume-unchanged t/t &&
+	rm t/t &&
+	test "$(git grep --no-ext-grep t)" = "t/t:test" &&
+	git update-index --no-assume-unchanged t/t &&
+	git checkout t/t
 '
 
 test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 182aea4..329c851 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -39,13 +39,31 @@
 '
 
 test_expect_success 'rewrite bare repository identically' '
-	(git config core.bare true && cd .git && git filter-branch branch)
+	(git config core.bare true && cd .git &&
+	 git filter-branch branch > filter-output 2>&1 &&
+	! fgrep fatal filter-output)
 '
 git config core.bare false
 test_expect_success 'result is really identical' '
 	test $H = $(git rev-parse HEAD)
 '
 
+TRASHDIR=$(pwd)
+test_expect_success 'correct GIT_DIR while using -d' '
+	mkdir drepo &&
+	( cd drepo &&
+	git init &&
+	test_commit drepo &&
+	git filter-branch -d "$TRASHDIR/dfoo" \
+		--index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \
+	) &&
+	grep drepo "$TRASHDIR/backup-refs"
+'
+
+test_expect_success 'Fail if commit filter fails' '
+	test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD
+'
+
 test_expect_success 'rewrite, renaming a specific file' '
 	git filter-branch -f --tree-filter "mv d doh || :" HEAD
 '
@@ -96,13 +114,17 @@
 	test_tick &&
 	git commit -m "again not subdir" &&
 	git branch sub &&
-	git filter-branch -f --subdirectory-filter subdir refs/heads/sub
+	git branch sub-earlier HEAD~2 &&
+	git filter-branch -f --subdirectory-filter subdir \
+		refs/heads/sub refs/heads/sub-earlier
 '
 
 test_expect_success 'subdirectory filter result looks okay' '
 	test 2 = $(git rev-list sub | wc -l) &&
 	git show sub:new &&
-	test_must_fail git show sub:subdir
+	test_must_fail git show sub:subdir &&
+	git show sub-earlier:new &&
+	test_must_fail git show sub-earlier:subdir
 '
 
 test_expect_success 'more setup' '
@@ -250,4 +272,20 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'Tag name filtering allows slashes in tag names' '
+	git tag -m tag-with-slash X/1 &&
+	git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
+	git filter-branch -f --tag-name-filter "echo X/2" &&
+	git cat-file tag X/2 > actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'Prune empty commits' '
+	git rev-list HEAD > expect &&
+	make_commit to_remove &&
+	git filter-branch -f --index-filter "git update-index --remove to_remove" --prune-empty HEAD &&
+	git rev-list HEAD > actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index c616deb..73dbc43 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -185,8 +185,9 @@
 EOF
 test_expect_success \
 	'listing tags with substring as pattern must print those matching' '
-	git tag -l "*a*" > actual &&
-	test_cmp expect actual
+	rm *a* &&
+	git tag -l "*a*" > current &&
+	test_cmp expect current
 '
 
 cat >expect <<EOF
@@ -580,28 +581,38 @@
 '
 
 # subsequent tests require gpg; check if it is available
-gpg --version >/dev/null
+gpg --version >/dev/null 2>/dev/null
 if [ $? -eq 127 ]; then
-	echo "gpg not found - skipping tag signing and verification tests"
-	test_done
-	exit
+	say "gpg not found - skipping tag signing and verification tests"
+else
+	# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+	# the gpg version 1.0.6 didn't parse trust packets correctly, so for
+	# that version, creation of signed tags using the generated key fails.
+	case "$(gpg --version)" in
+	'gpg (GnuPG) 1.0.6'*)
+		say "Skipping signed tag tests, because a bug in 1.0.6 version"
+		;;
+	*)
+		test_set_prereq GPG
+		;;
+	esac
 fi
 
 # trying to verify annotated non-signed tags:
 
-test_expect_success \
+test_expect_success GPG \
 	'trying to verify an annotated non-signed tag should fail' '
 	tag_exists annotated-tag &&
 	test_must_fail git tag -v annotated-tag
 '
 
-test_expect_success \
+test_expect_success GPG \
 	'trying to verify a file-annotated non-signed tag should fail' '
 	tag_exists file-annotated-tag &&
 	test_must_fail git tag -v file-annotated-tag
 '
 
-test_expect_success \
+test_expect_success GPG \
 	'trying to verify two annotated non-signed tags should fail' '
 	tag_exists annotated-tag file-annotated-tag &&
 	test_must_fail git tag -v annotated-tag file-annotated-tag
@@ -609,23 +620,12 @@
 
 # creating and verifying signed tags:
 
-# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
-# the gpg version 1.0.6 didn't parse trust packets correctly, so for
-# that version, creation of signed tags using the generated key fails.
-case "$(gpg --version)" in
-'gpg (GnuPG) 1.0.6'*)
-	echo "Skipping signed tag tests, because a bug in 1.0.6 version"
-	test_done
-	exit
-	;;
-esac
-
 # key generation info: gpg --homedir t/t7004 --gen-key
 # Type DSA and Elgamal, size 2048 bits, no expiration date.
 # Name and email: C O Mitter <committer@example.com>
 # No password given, to enable non-interactive operation.
 
-cp -R ../t7004 ./gpghome
+cp -R "$TEST_DIRECTORY"/t7004 ./gpghome
 chmod 0700 gpghome
 GNUPGHOME="$(pwd)/gpghome"
 export GNUPGHOME
@@ -633,7 +633,7 @@
 get_tag_header signed-tag $commit commit $time >expect
 echo 'A signed tag message' >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success 'creating a signed tag with -m message should succeed' '
+test_expect_success GPG 'creating a signed tag with -m message should succeed' '
 	git tag -s -m "A signed tag message" signed-tag &&
 	get_tag_msg signed-tag >actual &&
 	test_cmp expect actual
@@ -642,7 +642,7 @@
 get_tag_header u-signed-tag $commit commit $time >expect
 echo 'Another message' >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success 'sign with a given key id' '
+test_expect_success GPG 'sign with a given key id' '
 
 	git tag -u committer@example.com -m "Another message" u-signed-tag &&
 	get_tag_msg u-signed-tag >actual &&
@@ -650,14 +650,14 @@
 
 '
 
-test_expect_success 'sign with an unknown id (1)' '
+test_expect_success GPG 'sign with an unknown id (1)' '
 
 	test_must_fail git tag -u author@example.com \
 		-m "Another message" o-signed-tag
 
 '
 
-test_expect_success 'sign with an unknown id (2)' '
+test_expect_success GPG 'sign with an unknown id (2)' '
 
 	test_must_fail git tag -u DEADBEEF -m "Another message" o-signed-tag
 
@@ -674,7 +674,7 @@
 get_tag_header implied-sign $commit commit $time >expect
 ./fakeeditor >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success '-u implies signed tag' '
+test_expect_success GPG '-u implies signed tag' '
 	GIT_EDITOR=./fakeeditor git tag -u CDDE430D implied-sign &&
 	get_tag_msg implied-sign >actual &&
 	test_cmp expect actual
@@ -687,7 +687,7 @@
 get_tag_header file-signed-tag $commit commit $time >expect
 cat sigmsgfile >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with -F messagefile should succeed' '
 	git tag -s -F sigmsgfile file-signed-tag &&
 	get_tag_msg file-signed-tag >actual &&
@@ -701,7 +701,7 @@
 get_tag_header stdin-signed-tag $commit commit $time >expect
 cat siginputmsg >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success 'creating a signed tag with -F - should succeed' '
+test_expect_success GPG 'creating a signed tag with -F - should succeed' '
 	git tag -s -F - stdin-signed-tag <siginputmsg &&
 	get_tag_msg stdin-signed-tag >actual &&
 	test_cmp expect actual
@@ -710,13 +710,13 @@
 get_tag_header implied-annotate $commit commit $time >expect
 ./fakeeditor >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success '-s implies annotated tag' '
+test_expect_success GPG '-s implies annotated tag' '
 	GIT_EDITOR=./fakeeditor git tag -s implied-annotate &&
 	get_tag_msg implied-annotate >actual &&
 	test_cmp expect actual
 '
 
-test_expect_success \
+test_expect_success GPG \
 	'trying to create a signed tag with non-existing -F file should fail' '
 	! test -f nonexistingfile &&
 	! tag_exists nosigtag &&
@@ -724,13 +724,13 @@
 	! tag_exists nosigtag
 '
 
-test_expect_success 'verifying a signed tag should succeed' \
+test_expect_success GPG 'verifying a signed tag should succeed' \
 	'git tag -v signed-tag'
 
-test_expect_success 'verifying two signed tags in one command should succeed' \
+test_expect_success GPG 'verifying two signed tags in one command should succeed' \
 	'git tag -v signed-tag file-signed-tag'
 
-test_expect_success \
+test_expect_success GPG \
 	'verifying many signed and non-signed tags should fail' '
 	test_must_fail git tag -v signed-tag annotated-tag &&
 	test_must_fail git tag -v file-annotated-tag file-signed-tag &&
@@ -739,7 +739,7 @@
 	test_must_fail git tag -v signed-tag annotated-tag file-signed-tag
 '
 
-test_expect_success 'verifying a forged tag should fail' '
+test_expect_success GPG 'verifying a forged tag should fail' '
 	forged=$(git cat-file tag signed-tag |
 		sed -e "s/signed-tag/forged-tag/" |
 		git mktag) &&
@@ -751,7 +751,7 @@
 
 get_tag_header empty-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with an empty -m message should succeed' '
 	git tag -s -m "" empty-signed-tag &&
 	get_tag_msg empty-signed-tag >actual &&
@@ -762,7 +762,7 @@
 >sigemptyfile
 get_tag_header emptyfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with an empty -F messagefile should succeed' '
 	git tag -s -F sigemptyfile emptyfile-signed-tag &&
 	get_tag_msg emptyfile-signed-tag >actual &&
@@ -785,7 +785,7 @@
 Trailing blank lines
 EOF
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'extra blanks in the message for a signed tag should be removed' '
 	git tag -s -F sigblanksfile blanks-signed-tag &&
 	get_tag_msg blanks-signed-tag >actual &&
@@ -795,7 +795,7 @@
 
 get_tag_header blank-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with a blank -m message should succeed' '
 	git tag -s -m "     " blank-signed-tag &&
 	get_tag_msg blank-signed-tag >actual &&
@@ -808,7 +808,7 @@
 echo '  '    >>sigblankfile
 get_tag_header blankfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with blank -F file with spaces should succeed' '
 	git tag -s -F sigblankfile blankfile-signed-tag &&
 	get_tag_msg blankfile-signed-tag >actual &&
@@ -819,7 +819,7 @@
 printf '      ' >sigblanknonlfile
 get_tag_header blanknonlfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with spaces and no newline should succeed' '
 	git tag -s -F sigblanknonlfile blanknonlfile-signed-tag &&
 	get_tag_msg blanknonlfile-signed-tag >actual &&
@@ -856,7 +856,7 @@
 Last line.
 EOF
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with a -F file with #comments should succeed' '
 	git tag -s -F sigcommentsfile comments-signed-tag &&
 	get_tag_msg comments-signed-tag >actual &&
@@ -866,7 +866,7 @@
 
 get_tag_header comment-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with #commented -m message should succeed' '
 	git tag -s -m "#comment" comment-signed-tag &&
 	get_tag_msg comment-signed-tag >actual &&
@@ -879,7 +879,7 @@
 echo '####'     >>sigcommentfile
 get_tag_header commentfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with #commented -F messagefile should succeed' '
 	git tag -s -F sigcommentfile commentfile-signed-tag &&
 	get_tag_msg commentfile-signed-tag >actual &&
@@ -890,7 +890,7 @@
 printf '#comment' >sigcommentnonlfile
 get_tag_header commentnonlfile-signed-tag $commit commit $time >expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag with a #comment and no newline should succeed' '
 	git tag -s -F sigcommentnonlfile commentnonlfile-signed-tag &&
 	get_tag_msg commentnonlfile-signed-tag >actual &&
@@ -900,7 +900,7 @@
 
 # listing messages for signed tags:
 
-test_expect_success \
+test_expect_success GPG \
 	'listing the one-line message of a signed tag should succeed' '
 	git tag -s -m "A message line signed" stag-one-line &&
 
@@ -925,7 +925,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success \
+test_expect_success GPG \
 	'listing the zero-lines message of a signed tag should succeed' '
 	git tag -s -m "" stag-zero-lines &&
 
@@ -953,7 +953,7 @@
 echo 'stag line one' >sigtagmsg
 echo 'stag line two' >>sigtagmsg
 echo 'stag line three' >>sigtagmsg
-test_expect_success \
+test_expect_success GPG \
 	'listing many message lines of a signed tag should succeed' '
 	git tag -s -F sigtagmsg stag-lines &&
 
@@ -998,12 +998,12 @@
 
 tree=$(git rev-parse HEAD^{tree})
 blob=$(git rev-parse HEAD:foo)
-tag=$(git rev-parse signed-tag)
+tag=$(git rev-parse signed-tag 2>/dev/null)
 
 get_tag_header tree-signed-tag $tree tree $time >expect
 echo "A message for a tree" >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag pointing to a tree should succeed' '
 	git tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} &&
 	get_tag_msg tree-signed-tag >actual &&
@@ -1013,7 +1013,7 @@
 get_tag_header blob-signed-tag $blob blob $time >expect
 echo "A message for a blob" >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag pointing to a blob should succeed' '
 	git tag -s -m "A message for a blob" blob-signed-tag HEAD:foo &&
 	get_tag_msg blob-signed-tag >actual &&
@@ -1023,7 +1023,7 @@
 get_tag_header tag-signed-tag $tag tag $time >expect
 echo "A message for another tag" >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
-test_expect_success \
+test_expect_success GPG \
 	'creating a signed tag pointing to another tag should succeed' '
 	git tag -s -m "A message for another tag" tag-signed-tag signed-tag &&
 	get_tag_msg tag-signed-tag >actual &&
@@ -1032,7 +1032,7 @@
 
 # try to sign with bad user.signingkey
 git config user.signingkey BobTheMouse
-test_expect_success \
+test_expect_success GPG \
 	'git tag -s fails if gpg is misconfigured' \
 	'test_must_fail git tag -s -m tail tag-gpg-failure'
 git config --unset user.signingkey
@@ -1040,7 +1040,7 @@
 # try to verify without gpg:
 
 rm -rf gpghome
-test_expect_success \
+test_expect_success GPG \
 	'verify signed tag fails when public key is not present' \
 	'test_must_fail git tag -v signed-tag'
 
@@ -1090,6 +1090,121 @@
 	git cat-file tag tag-from-subdir-2 | grep "in sub directory"
 '
 
+# create a few more commits to test --contains
+
+hash1=$(git rev-parse HEAD)
+
+test_expect_success 'creating second commit and tag' '
+	echo foo-2.0 >foo &&
+	git add foo &&
+	git commit -m second
+	git tag v2.0
+'
+
+hash2=$(git rev-parse HEAD)
+
+test_expect_success 'creating third commit without tag' '
+	echo foo-dev >foo &&
+	git add foo &&
+	git commit -m third
+'
+
+hash3=$(git rev-parse HEAD)
+
+# simple linear checks of --continue
+
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+EOF
+
+test_expect_success 'checking that first commit is in all tags (hash)' "
+	git tag -l --contains $hash1 v* >actual
+	test_cmp expected actual
+"
+
+# other ways of specifying the commit
+test_expect_success 'checking that first commit is in all tags (tag)' "
+	git tag -l --contains v1.0 v* >actual
+	test_cmp expected actual
+"
+
+test_expect_success 'checking that first commit is in all tags (relative)' "
+	git tag -l --contains HEAD~2 v* >actual
+	test_cmp expected actual
+"
+
+cat > expected <<EOF
+v2.0
+EOF
+
+test_expect_success 'checking that second commit only has one tag' "
+	git tag -l --contains $hash2 v* >actual
+	test_cmp expected actual
+"
+
+
+cat > expected <<EOF
+EOF
+
+test_expect_success 'checking that third commit has no tags' "
+	git tag -l --contains $hash3 v* >actual
+	test_cmp expected actual
+"
+
+# how about a simple merge?
+
+test_expect_success 'creating simple branch' '
+	git branch stable v2.0 &&
+        git checkout stable &&
+	echo foo-3.0 > foo &&
+	git commit foo -m fourth
+	git tag v3.0
+'
+
+hash4=$(git rev-parse HEAD)
+
+cat > expected <<EOF
+v3.0
+EOF
+
+test_expect_success 'checking that branch head only has one tag' "
+	git tag -l --contains $hash4 v* >actual
+	test_cmp expected actual
+"
+
+test_expect_success 'merging original branch into this branch' '
+	git merge --strategy=ours master &&
+        git tag v4.0
+'
+
+cat > expected <<EOF
+v4.0
+EOF
+
+test_expect_success 'checking that original branch head has one tag now' "
+	git tag -l --contains $hash3 v* >actual
+	test_cmp expected actual
+"
+
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+v3.0
+v4.0
+EOF
+
+test_expect_success 'checking that initial commit is in all tags' "
+	git tag -l --contains $hash1 v* >actual
+	test_cmp expected actual
+"
+
 # mixing modes and options:
 
 test_expect_success 'mixing incompatibles modes and options is forbidden' '
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 2d919d6..b647957 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -7,6 +7,7 @@
 for i in GIT_EDITOR core_editor EDITOR VISUAL vi
 do
 	cat >e-$i.sh <<-EOF
+	#!$SHELL_PATH
 	echo "Edited by $i" >"\$1"
 	EOF
 	chmod +x e-$i.sh
@@ -87,30 +88,26 @@
 	'
 done
 
+if ! echo 'echo space > "$1"' > "e space.sh"
+then
+	say "Skipping; FS does not support spaces in filenames"
+	test_done
+fi
+
 test_expect_success 'editor with a space' '
 
-	if echo "echo space > \"\$1\"" > "e space.sh"
-	then
-		chmod a+x "e space.sh" &&
-		GIT_EDITOR="./e\ space.sh" git commit --amend &&
-		test space = "$(git show -s --pretty=format:%s)"
-	else
-		say "Skipping; FS does not support spaces in filenames"
-	fi
+	chmod a+x "e space.sh" &&
+	GIT_EDITOR="./e\ space.sh" git commit --amend &&
+	test space = "$(git show -s --pretty=format:%s)"
 
 '
 
 unset GIT_EDITOR
 test_expect_success 'core.editor with a space' '
 
-	if test -f "e space.sh"
-	then
-		git config core.editor \"./e\ space.sh\" &&
-		git commit --amend &&
-		test space = "$(git show -s --pretty=format:%s)"
-	else
-		say "Skipping; FS does not support spaces in filenames"
-	fi
+	git config core.editor \"./e\ space.sh\" &&
+	git commit --amend &&
+	test space = "$(git show -s --pretty=format:%s)"
 
 '
 
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
index c4ef19e..96e163f 100755
--- a/t/t7101-reset.sh
+++ b/t/t7101-reset.sh
@@ -9,7 +9,7 @@
 test_expect_success \
     'creating initial files' \
     'mkdir path0 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
      git commit -m add -a'
 
@@ -17,10 +17,10 @@
     'creating second files' \
     'mkdir path1 &&
      mkdir path1/path2 &&
-     cp ../../COPYING path1/path2/COPYING &&
-     cp ../../COPYING path1/COPYING &&
-     cp ../../COPYING COPYING &&
-     cp ../../COPYING path0/COPYING-TOO &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
      git add path1/path2/COPYING &&
      git add path1/COPYING &&
      git add COPYING &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index c9abed6..bdb808a 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -171,7 +171,7 @@
 	git checkout -f renamer && git clean -f &&
 	git checkout renamer^ 2>messages &&
 	(cat >messages.expect <<EOF
-Note: moving to "renamer^" which isn'"'"'t a local branch
+Note: moving to '\''renamer^'\'' which isn'\''t a local branch
 If you want to create a new branch from this checkout, you may do so
 (now or later) by using -b with the checkout command again. Example:
   git checkout -b <new_branch_name>
@@ -351,7 +351,39 @@
     test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
 '
 
-test_expect_success 'checkout an unmerged path should fail' '
+test_expect_success \
+    'checkout with --track fakes a sensible -b <name>' '
+    git update-ref refs/remotes/origin/koala/bear renamer &&
+    git update-ref refs/new/koala/bear renamer &&
+
+    git checkout --track origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track refs/remotes/origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track remotes/origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track refs/new/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+'
+
+test_expect_success \
+    'checkout with --track, but without -b, fails with too short tracked name' '
+    test_must_fail git checkout --track renamer'
+
+setup_conflicting_index () {
 	rm -f .git/index &&
 	O=$(echo original | git hash-object -w --stdin) &&
 	A=$(echo ourside | git hash-object -w --stdin) &&
@@ -362,7 +394,11 @@
 		echo "100644 $A 2	file" &&
 		echo "100644 $B 3	file" &&
 		echo "100644 $A 0	filf"
-	) | git update-index --index-info &&
+	) | git update-index --index-info
+}
+
+test_expect_success 'checkout an unmerged path should fail' '
+	setup_conflicting_index &&
 	echo "none of the above" >sample &&
 	cat sample >fild &&
 	cat sample >file &&
@@ -373,6 +409,121 @@
 	test_cmp sample file
 '
 
+test_expect_success 'checkout with an unmerged path can be ignored' '
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout -f fild file filf &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp sample file
+'
+
+test_expect_success 'checkout unmerged stage' '
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout --ours . &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp expect file &&
+	git checkout --theirs file &&
+	test ztheirside = "z$(cat file)"
+'
+
+test_expect_success 'checkout with --merge' '
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout -m -- fild file filf &&
+	(
+		echo "<<<<<<< ours"
+		echo ourside
+		echo "======="
+		echo theirside
+		echo ">>>>>>> theirs"
+	) >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
+test_expect_success 'checkout with --merge, in diff3 -m style' '
+	git config merge.conflictstyle diff3 &&
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout -m -- fild file filf &&
+	(
+		echo "<<<<<<< ours"
+		echo ourside
+		echo "|||||||"
+		echo original
+		echo "======="
+		echo theirside
+		echo ">>>>>>> theirs"
+	) >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
+test_expect_success 'checkout --conflict=merge, overriding config' '
+	git config merge.conflictstyle diff3 &&
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout --conflict=merge -- fild file filf &&
+	(
+		echo "<<<<<<< ours"
+		echo ourside
+		echo "======="
+		echo theirside
+		echo ">>>>>>> theirs"
+	) >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
+test_expect_success 'checkout --conflict=diff3' '
+	git config --unset merge.conflictstyle
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout --conflict=diff3 -- fild file filf &&
+	(
+		echo "<<<<<<< ours"
+		echo ourside
+		echo "|||||||"
+		echo original
+		echo "======="
+		echo theirside
+		echo ">>>>>>> theirs"
+	) >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
 test_expect_success 'failing checkout -b should not break working tree' '
 	git reset --hard master &&
 	git symbolic-ref HEAD refs/heads/master &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 1636fac..929d5d4 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -373,9 +373,9 @@
 
 	mkdir foo &&
 	touch foo/bar &&
-	exec <foo/bar &&
-	chmod 0 foo &&
-	test_must_fail git clean -f -d
+	(exec <foo/bar &&
+	 chmod 0 foo &&
+	 test_must_fail git clean -f -d)
 
 '
 chmod 755 foo
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index be73f7b..af690ec 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -47,6 +47,55 @@
 	GIT_CONFIG=.gitmodules git config submodule.example.url git://example.com/init.git
 '
 
+test_expect_success 'Prepare submodule add testing' '
+	submodurl=$(pwd)
+	(
+		mkdir addtest &&
+		cd addtest &&
+		git init
+	)
+'
+
+test_expect_success 'submodule add' '
+	(
+		cd addtest &&
+		git submodule add "$submodurl" submod &&
+		git submodule init
+	)
+'
+
+test_expect_success 'submodule add with ./ in path' '
+	(
+		cd addtest &&
+		git submodule add "$submodurl" ././dotsubmod/./frotz/./ &&
+		git submodule init
+	)
+'
+
+test_expect_success 'submodule add with // in path' '
+	(
+		cd addtest &&
+		git submodule add "$submodurl" slashslashsubmod///frotz// &&
+		git submodule init
+	)
+'
+
+test_expect_success 'submodule add with /.. in path' '
+	(
+		cd addtest &&
+		git submodule add "$submodurl" dotdotsubmod/../realsubmod/frotz/.. &&
+		git submodule init
+	)
+'
+
+test_expect_success 'submodule add with ./, /.. and // in path' '
+	(
+		cd addtest &&
+		git submodule add "$submodurl" dot/dotslashsubmod/./../..////realsubmod2/a/b/c/d/../../../../frotz//.. &&
+		git submodule init
+	)
+'
+
 test_expect_success 'status should fail for unmapped paths' '
 	if git submodule status
 	then
@@ -209,4 +258,42 @@
 
 '
 
+test_expect_success 'do not add files from a submodule' '
+
+	git reset --hard &&
+	test_must_fail git add init/a
+
+'
+
+test_expect_success 'gracefully add submodule with a trailing slash' '
+
+	git reset --hard &&
+	git commit -m "commit subproject" init &&
+	(cd init &&
+	 echo b > a) &&
+	git add init/ &&
+	git diff --exit-code --cached init &&
+	commit=$(cd init &&
+	 git commit -m update a >/dev/null &&
+	 git rev-parse HEAD) &&
+	git add init/ &&
+	test_must_fail git diff --exit-code --cached init &&
+	test $commit = $(git ls-files --stage |
+		sed -n "s/^160000 \([^ ]*\).*/\1/p")
+
+'
+
+test_expect_success 'ls-files gracefully handles trailing slash' '
+
+	test "init" = "$(git ls-files init/)"
+
+'
+
+test_expect_success 'submodule <invalid-path> warns' '
+
+	git submodule no-such-submodule 2> output.err &&
+	grep "^error: .*no-such-submodule" output.err
+
+'
+
 test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
new file mode 100755
index 0000000..7538756
--- /dev/null
+++ b/t/t7403-submodule-sync.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 David Aguilar
+#
+
+test_description='git submodule sync
+
+These tests exercise the "git submodule sync" subcommand.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo file > file &&
+	git add file &&
+	test_tick &&
+	git commit -m upstream
+	git clone . super &&
+	git clone super submodule &&
+	(cd super &&
+	 git submodule add ../submodule submodule &&
+	 test_tick &&
+	 git commit -m "submodule"
+	) &&
+	git clone super super-clone &&
+	(cd super-clone && git submodule update --init)
+'
+
+test_expect_success 'change submodule' '
+	(cd submodule &&
+	 echo second line >> file &&
+	 test_tick &&
+	 git commit -a -m "change submodule"
+	)
+'
+
+test_expect_success 'change submodule url' '
+	(cd super &&
+	 cd submodule &&
+	 git checkout master &&
+	 git pull
+	) &&
+	mv submodule moved-submodule &&
+	(cd super &&
+	 git config -f .gitmodules submodule.submodule.url ../moved-submodule
+	 test_tick &&
+	 git commit -a -m moved-submodule
+	)
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs' '
+	(cd super-clone &&
+	 git pull &&
+	 git submodule sync
+	) &&
+	test -d "$(git config -f super-clone/submodule/.git/config \
+	                        remote.origin.url)" &&
+	(cd super-clone/submodule &&
+	 git checkout master &&
+	 git pull
+	)
+'
+
+test_done
diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh
new file mode 100755
index 0000000..aa6c44c
--- /dev/null
+++ b/t/t7405-submodule-merge.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='merging with submodules'
+
+. ./test-lib.sh
+
+#
+# history
+#
+#        a --- c
+#      /   \ /
+# root      X
+#      \   / \
+#        b --- d
+#
+
+test_expect_success setup '
+
+	mkdir sub &&
+	(cd sub &&
+	 git init &&
+	 echo original > file &&
+	 git add file &&
+	 test_tick &&
+	 git commit -m sub-root) &&
+	git add sub &&
+	test_tick &&
+	git commit -m root &&
+
+	git checkout -b a master &&
+	(cd sub &&
+	 echo A > file &&
+	 git add file &&
+	 test_tick &&
+	 git commit -m sub-a) &&
+	git add sub &&
+	test_tick &&
+	git commit -m a &&
+
+	git checkout -b b master &&
+	(cd sub &&
+	 echo B > file &&
+	 git add file &&
+	 test_tick &&
+	 git commit -m sub-b) &&
+	git add sub &&
+	test_tick &&
+	git commit -m b
+
+	git checkout -b c a &&
+	git merge -s ours b &&
+
+	git checkout -b d b &&
+	git merge -s ours a
+'
+
+test_expect_failure 'merging with modify/modify conflict' '
+
+	git checkout -b test1 a &&
+	test_must_fail git merge b &&
+	test -f .git/MERGE_MSG &&
+	git diff
+
+'
+
+test_expect_success 'merging with a modify/modify conflict between merge bases' '
+
+	git reset --hard HEAD &&
+	git checkout -b test2 c &&
+	git merge d
+
+'
+
+test_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 0fe745e..5998baf 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -46,15 +46,24 @@
 '
 
 test_expect_success 'a Signed-off-by line by itself should not commit' '
-	! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE"
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
+		test_must_fail git commit --template "$TEMPLATE"
+	)
 '
 
 test_expect_success 'adding comments to a template should not commit' '
-	! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE"
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
+		test_must_fail git commit --template "$TEMPLATE"
+	)
 '
 
 test_expect_success 'adding real content to a template should commit' '
-	GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit --template "$TEMPLATE"
+	) &&
 	commit_msg_is "template linecommit message"
 '
 
@@ -62,7 +71,10 @@
 	echo "short template" > "$TEMPLATE" &&
 	echo "new content" >> foo &&
 	git add foo &&
-	GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit -t "$TEMPLATE"
+	) &&
 	commit_msg_is "short templatecommit message"
 '
 
@@ -71,7 +83,10 @@
 	git config commit.template "$TEMPLATE" &&
 	echo "more content" >> foo &&
 	git add foo &&
-	GIT_EDITOR=../t7500/add-content git commit &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit
+	) &&
 	git config --unset commit.template &&
 	commit_msg_is "new templatecommit message"
 '
@@ -79,7 +94,7 @@
 test_expect_success 'explicit commit message should override template' '
 	echo "still more content" >> foo &&
 	git add foo &&
-	GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \
+	GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-content git commit --template "$TEMPLATE" \
 		-m "command line msg" &&
 	commit_msg_is "command line msg"
 '
@@ -88,8 +103,10 @@
 	echo "content galore" >> foo &&
 	git add foo &&
 	echo "standard input msg" |
-		GIT_EDITOR=../t7500/add-content git commit \
-			--template "$TEMPLATE" --file - &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit --template "$TEMPLATE" --file -
+	) &&
 	commit_msg_is "standard input msg"
 '
 
@@ -132,10 +149,9 @@
 
 test_expect_success '--signoff' '
 	echo "yet another content *narf*" >> foo &&
-	echo "zort" |
-		GIT_EDITOR=../t7500/add-content git commit -s -F - foo &&
+	echo "zort" | git commit -s -F - foo &&
 	git cat-file commit HEAD | sed "1,/^$/d" > output &&
-	diff expect output
+	test_cmp expect output
 '
 
 test_expect_success 'commit message from file (1)' '
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 63bfc6d..e2ef532 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -38,7 +38,7 @@
 	"echo King of the bongo >file &&
 	test_must_fail git commit -m foo -a file"
 
-test_expect_success \
+test_expect_success PERL \
 	"using paths with --interactive" \
 	"echo bong-o-bong >file &&
 	! (echo 7 | git commit -m foo --interactive file)"
@@ -119,7 +119,7 @@
 	"echo 'gak' >file && \
 	 git commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
 
-test_expect_success \
+test_expect_success PERL \
 	"interactive add" \
 	"echo 7 | git commit --interactive | grep 'What now'"
 
@@ -127,6 +127,26 @@
 	"showing committed revisions" \
 	"git rev-list HEAD >current"
 
+cat >editor <<\EOF
+#!/bin/sh
+sed -e "s/good/bad/g" < "$1" > "$1-"
+mv "$1-" "$1"
+EOF
+chmod 755 editor
+
+cat >msg <<EOF
+A good commit message.
+EOF
+
+test_expect_success \
+	'editor not invoked if -F is given' '
+	 echo "moo" >file &&
+	 VISUAL=./editor git commit -a -F msg &&
+	 git show -s --pretty=format:"%s" | grep -q good &&
+	 echo "quack" >file &&
+	 echo "Another good message." | VISUAL=./editor git commit -a -F - &&
+	 git show -s --pretty=format:"%s" | grep -q good
+	 '
 # We could just check the head sha1, but checking each commit makes it
 # easier to isolate bugs.
 
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 3eb9fae..56cd866 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -89,6 +89,14 @@
 
 '
 
+test_expect_success 'verbose respects diff config' '
+
+	git config color.diff always &&
+	git status -v >actual &&
+	grep "\[1mdiff --git" actual &&
+	git config --unset color.diff
+'
+
 test_expect_success 'cleanup commit messages (verbatim,-t)' '
 
 	echo >>negative &&
@@ -226,7 +234,7 @@
 # kill -TERM command added below.
 EOF
 
-test_expect_success 'a SIGTERM should break locks' '
+test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '
 	echo >>negative &&
 	! "$SHELL_PATH" -c '\''
 	  echo kill -TERM $$ >> .git/FAKE_EDITOR
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index b069095..8528f64 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -69,7 +69,7 @@
 '
 
 chmod -x "$HOOK"
-test_expect_success 'with non-executable hook' '
+test_expect_success POSIXPERM 'with non-executable hook' '
 
 	echo "content" >> file &&
 	git add file &&
@@ -77,7 +77,7 @@
 
 '
 
-test_expect_success '--no-verify with non-executable hook' '
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
 
 	echo "more content" >> file &&
 	git add file &&
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 47680e6..1f53ea8 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -136,7 +136,7 @@
 '
 
 chmod -x "$HOOK"
-test_expect_success 'with non-executable hook' '
+test_expect_success POSIXPERM 'with non-executable hook' '
 
 	echo "content" >> file &&
 	git add file &&
@@ -144,7 +144,7 @@
 
 '
 
-test_expect_success 'with non-executable hook (editor)' '
+test_expect_success POSIXPERM 'with non-executable hook (editor)' '
 
 	echo "content again" >> file &&
 	git add file &&
@@ -153,7 +153,7 @@
 
 '
 
-test_expect_success '--no-verify with non-executable hook' '
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
 
 	echo "more content" >> file &&
 	git add file &&
@@ -161,7 +161,7 @@
 
 '
 
-test_expect_success '--no-verify with non-executable hook (editor)' '
+test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' '
 
 	echo "even more content" >> file &&
 	git add file &&
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index 519adba..da5bd3b 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -22,7 +22,7 @@
 	git commit -F message
 '
 
-test_expect_failure 'initial commit shows verbose diff' '
+test_expect_success 'initial commit shows verbose diff' '
 	git commit --amend -v
 '
 
diff --git a/t/t7502-status.sh b/t/t7508-status.sh
similarity index 90%
rename from t/t7502-status.sh
rename to t/t7508-status.sh
index 187a13e..93f875f 100755
--- a/t/t7502-status.sh
+++ b/t/t7508-status.sh
@@ -46,6 +46,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -76,6 +77,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -104,6 +106,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -138,6 +141,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -174,6 +178,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   modified
 #
@@ -204,6 +209,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -267,6 +273,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -303,6 +310,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -332,6 +340,7 @@
 # On branch master
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -363,6 +372,7 @@
 #
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 5abce31..e5b210b 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -230,6 +230,10 @@
 	test_must_fail git merge
 '
 
+test_expect_success 'reject non-strategy with a git-merge-foo name' '
+	test_must_fail git merge -s index c1
+'
+
 test_expect_success 'merge c0 with c1' '
 	git reset --hard c0 &&
 	git merge c1 &&
@@ -507,6 +511,13 @@
 
 test_debug 'gitk --all'
 
+test_expect_success 'refresh the index before merging' '
+	git reset --hard c1 &&
+	sleep 1 &&
+	touch file &&
+	git merge c3
+'
+
 cat >expected <<EOF
 Merge branch 'c5' (early part)
 EOF
@@ -533,4 +544,20 @@
 
 test_debug 'gitk --all'
 
+test_expect_success 'merge --no-ff --no-commit && commit' '
+	git reset --hard c0 &&
+	git merge --no-ff --no-commit c1 &&
+	EDITOR=: git commit &&
+	verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'amending no-ff merge commit' '
+	EDITOR=: git commit --amend &&
+	verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
 test_done
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index b47b7b9..7e17eb4 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -60,4 +60,57 @@
 	test -f c5.c
 '
 
+test_expect_success 'setup' '
+	for i in A B C D E
+	do
+		echo $i > $i.c &&
+		git add $i.c &&
+		git commit -m $i &&
+		git tag $i
+	done &&
+	git reset --hard A &&
+	for i in F G H I
+	do
+		echo $i > $i.c &&
+		git add $i.c &&
+		git commit -m $i &&
+		git tag $i
+	done
+'
+
+test_expect_success 'merge E and I' '
+	git reset --hard A &&
+	git merge E I
+'
+
+test_expect_success 'verify merge result' '
+	test $(git rev-parse HEAD^1) = $(git rev-parse E) &&
+	test $(git rev-parse HEAD^2) = $(git rev-parse I)
+'
+
+test_expect_success 'add conflicts' '
+	git reset --hard E &&
+	echo foo > file.c &&
+	git add file.c &&
+	git commit -m E2 &&
+	git tag E2 &&
+	git reset --hard I &&
+	echo bar >file.c &&
+	git add file.c &&
+	git commit -m I2 &&
+	git tag I2
+'
+
+test_expect_success 'merge E2 and I2, causing a conflict and resolve it' '
+	git reset --hard A &&
+	test_must_fail git merge E2 I2 &&
+	echo baz > file.c &&
+	git add file.c &&
+	git commit -m "resolve conflict"
+'
+
+test_expect_success 'verify merge result' '
+	test $(git rev-parse HEAD^1) = $(git rev-parse E2) &&
+	test $(git rev-parse HEAD^2) = $(git rev-parse I2)
+'
 test_done
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
new file mode 100755
index 0000000..52a451dd
--- /dev/null
+++ b/t/t7606-merge-custom.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing a custom strategy.'
+
+. ./test-lib.sh
+
+cat >git-merge-theirs <<EOF
+#!$SHELL_PATH
+eval git read-tree --reset -u \\\$\$#
+EOF
+chmod +x git-merge-theirs
+PATH=.:$PATH
+export PATH
+
+test_expect_success 'setup' '
+	echo c0 >c0.c &&
+	git add c0.c &&
+	git commit -m c0 &&
+	git tag c0 &&
+	echo c1 >c1.c &&
+	git add c1.c &&
+	git commit -m c1 &&
+	git tag c1 &&
+	git reset --hard c0 &&
+	echo c1c1 >c1.c &&
+	echo c2 >c2.c &&
+	git add c1.c c2.c &&
+	git commit -m c2 &&
+	git tag c2
+'
+
+test_expect_success 'merge c2 with a custom strategy' '
+	git reset --hard c1 &&
+	git merge -s theirs c2 &&
+	test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+	test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+	test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+	test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" &&
+	git diff --exit-code &&
+	git diff --exit-code c2 HEAD &&
+	git diff --exit-code c2 &&
+	test -f c0.c &&
+	grep c1c1 c1.c &&
+	test -f c2.c
+'
+
+test_done
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
new file mode 100755
index 0000000..49f4e15
--- /dev/null
+++ b/t/t7607-merge-overwrite.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Do not overwrite changes.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo c0 > c0.c &&
+	git add c0.c &&
+	git commit -m c0 &&
+	git tag c0 &&
+	echo c1 > c1.c &&
+	git add c1.c &&
+	git commit -m c1 &&
+	git tag c1 &&
+	git reset --hard c0 &&
+	echo c2 > c2.c &&
+	git add c2.c &&
+	git commit -m c2 &&
+	git tag c2 &&
+	git reset --hard c1 &&
+	echo "c1 a" > c1.c &&
+	git add c1.c &&
+	git commit -m "c1 a" &&
+	git tag c1a &&
+	echo "VERY IMPORTANT CHANGES" > important
+'
+
+test_expect_success 'will not overwrite untracked file' '
+	git reset --hard c1 &&
+	cat important > c2.c &&
+	! git merge c2 &&
+	test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite new file' '
+	git reset --hard c1 &&
+	cat important > c2.c &&
+	git add c2.c &&
+	! git merge c2 &&
+	test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite staged changes' '
+	git reset --hard c1 &&
+	cat important > c2.c &&
+	git add c2.c &&
+	rm c2.c &&
+	! git merge c2 &&
+	git checkout c2.c &&
+	test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite removed file' '
+	git reset --hard c1 &&
+	git rm c1.c &&
+	git commit -m "rm c1.c" &&
+	cat important > c1.c &&
+	! git merge c1a &&
+	test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite re-added file' '
+	git reset --hard c1 &&
+	git rm c1.c &&
+	git commit -m "rm c1.c" &&
+	cat important > c1.c &&
+	git add c1.c &&
+	! git merge c1a &&
+	test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite removed file with staged changes' '
+	git reset --hard c1 &&
+	git rm c1.c &&
+	git commit -m "rm c1.c" &&
+	cat important > c1.c &&
+	git add c1.c &&
+	rm c1.c &&
+	! git merge c1a &&
+	git checkout c1.c &&
+	test_cmp important c1.c
+'
+
+test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 09fa5f1..e768c3e 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -9,38 +9,81 @@
 
 . ./test-lib.sh
 
+# All the mergetool test work by checking out a temporary branch based
+# off 'branch1' and then merging in master and checking the results of
+# running mergetool
+
 test_expect_success 'setup' '
     echo master >file1 &&
-    git add file1 &&
+    mkdir subdir &&
+    echo master sub >subdir/file3 &&
+    git add file1 subdir/file3 &&
     git commit -m "added file1" &&
+
     git checkout -b branch1 master &&
     echo branch1 change >file1 &&
     echo branch1 newfile >file2 &&
-    git add file1 file2 &&
+    echo branch1 sub >subdir/file3 &&
+    git add file1 file2 subdir/file3 &&
     git commit -m "branch1 changes" &&
-    git checkout -b branch2 master &&
-    echo branch2 change >file1 &&
-    echo branch2 newfile >file2 &&
-    git add file1 file2 &&
-    git commit -m "branch2 changes" &&
+
     git checkout master &&
     echo master updated >file1 &&
     echo master new >file2 &&
-    git add file1 file2 &&
-    git commit -m "master updates"
+    echo master new sub >subdir/file3 &&
+    git add file1 file2 subdir/file3 &&
+    git commit -m "master updates" &&
+
+    git config merge.tool mytool &&
+    git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+    git config mergetool.mytool.trustExitCode true
 '
 
 test_expect_success 'custom mergetool' '
-    git config merge.tool mytool &&
-    git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
-    git config mergetool.mytool.trustExitCode true &&
-	git checkout branch1 &&
+    git checkout -b test1 branch1 &&
     test_must_fail git merge master >/dev/null 2>&1 &&
-    ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
-    ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
     test "$(cat file1)" = "master updated" &&
     test "$(cat file2)" = "master new" &&
-	git commit -m "branch1 resolved with mergetool"
+    test "$(cat subdir/file3)" = "master new sub" &&
+    git commit -m "branch1 resolved with mergetool"
 '
 
+test_expect_success 'mergetool crlf' '
+    git config core.autocrlf true &&
+    git checkout -b test2 branch1
+    test_must_fail git merge master >/dev/null 2>&1 &&
+    ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
+    test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
+    test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
+    git commit -m "branch1 resolved with mergetool - autocrlf" &&
+    git config core.autocrlf false &&
+    git reset --hard
+'
+
+test_expect_success 'mergetool in subdir' '
+    git checkout -b test3 branch1
+    cd subdir && (
+    test_must_fail git merge master >/dev/null 2>&1 &&
+    ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+    test "$(cat file3)" = "master new sub" )
+'
+
+# We can't merge files from parent directories when running mergetool
+# from a subdir. Is this a bug?
+#
+#test_expect_failure 'mergetool in subdir' '
+#    cd subdir && (
+#    ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+#    ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+#    test "$(cat ../file1)" = "master updated" &&
+#    test "$(cat ../file2)" = "master new" &&
+#    git commit -m "branch1 resolved with mergetool - subdir" )
+#'
+
 test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 3f602ea..6b29bff 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -69,5 +69,85 @@
 	done
 '
 
+test_expect_failure 'packed obs in alt ODB are repacked when local repo has packs' '
+	rm -f .git/objects/pack/* &&
+	echo new_content >> file1 &&
+	git add file1 &&
+	git commit -m more_content &&
+	git repack &&
+	git repack -a -d &&
+	myidx=$(ls -1 .git/objects/pack/*.idx) &&
+	test -f "$myidx" &&
+	for p in alt_objects/pack/*.idx; do
+		git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
+	done | while read sha1 rest; do
+		if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
+			echo "Missing object in local pack: $sha1"
+			return 1
+		fi
+	done
+'
+
+test_expect_success 'packed obs in alternate ODB kept pack are repacked' '
+	# swap the .keep so the commit object is in the pack with .keep
+	for p in alt_objects/pack/*.pack
+	do
+		base_name=$(basename $p .pack)
+		if test -f alt_objects/pack/$base_name.keep
+		then
+			rm alt_objects/pack/$base_name.keep
+		else
+			touch alt_objects/pack/$base_name.keep
+		fi
+	done
+	git repack -a -d &&
+	myidx=$(ls -1 .git/objects/pack/*.idx) &&
+	test -f "$myidx" &&
+	for p in alt_objects/pack/*.idx; do
+		git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
+	done | while read sha1 rest; do
+		if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
+			echo "Missing object in local pack: $sha1"
+			return 1
+		fi
+	done
+'
+
+test_expect_success 'packed unreachable obs in alternate ODB are not loosened' '
+	rm -f alt_objects/pack/*.keep &&
+	mv .git/objects/pack/* alt_objects/pack/ &&
+	csha1=$(git rev-parse HEAD^{commit}) &&
+	git reset --hard HEAD^ &&
+	sleep 1 &&
+	git reflog expire --expire=now --expire-unreachable=now --all &&
+	# The pack-objects call on the next line is equivalent to
+	# git repack -A -d without the call to prune-packed
+	git pack-objects --honor-pack-keep --non-empty --all --reflog \
+	    --unpack-unreachable </dev/null pack &&
+	rm -f .git/objects/pack/* &&
+	mv pack-* .git/objects/pack/ &&
+	test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+		egrep "^$csha1 " | sort | uniq | wc -l) &&
+	echo > .git/objects/info/alternates &&
+	test_must_fail git show $csha1
+'
+
+test_expect_success 'local packed unreachable obs that exist in alternate ODB are not loosened' '
+	echo `pwd`/alt_objects > .git/objects/info/alternates &&
+	echo "$csha1" | git pack-objects --non-empty --all --reflog pack &&
+	rm -f .git/objects/pack/* &&
+	mv pack-* .git/objects/pack/ &&
+	# The pack-objects call on the next line is equivalent to
+	# git repack -A -d without the call to prune-packed
+	git pack-objects --honor-pack-keep --non-empty --all --reflog \
+	    --unpack-unreachable </dev/null pack &&
+	rm -f .git/objects/pack/* &&
+	mv pack-* .git/objects/pack/ &&
+	test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+		egrep "^$csha1 " | sort | uniq | wc -l) &&
+	echo > .git/objects/info/alternates &&
+	test_must_fail git show $csha1
+'
+
 test_done
 
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index 9813f11..5babdf2 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -29,7 +29,7 @@
 	git repack -A -d -l &&
 	# verify objects are packed in repository
 	test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
-		   grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+		   egrep "^($fsha1|$csha1|$tsha1) " |
 		   sort | uniq | wc -l) &&
 	git show $fsha1 &&
 	git show $csha1 &&
@@ -41,7 +41,7 @@
 	git repack -A -d -l &&
 	# verify objects are retained unpacked
 	test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
-		   grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+		   egrep "^($fsha1|$csha1|$tsha1) " |
 		   sort | uniq | wc -l) &&
 	git show $fsha1 &&
 	git show $csha1 &&
@@ -50,12 +50,10 @@
 
 compare_mtimes ()
 {
-	perl -e 'my $reference = shift;
-		 foreach my $file (@ARGV) {
-			exit(1) unless(-f $file && -M $file == -M $reference);
-		 }
-		 exit(0);
-		' -- "$@"
+	read tref rest &&
+	while read t rest; do
+		test "$tref" = "$t" || break
+	done
 }
 
 test_expect_success '-A without -d option leaves unreachable objects packed' '
@@ -87,7 +85,9 @@
 	tmppack=".git/objects/pack/tmp_pack" &&
 	ln "$packfile" "$tmppack" &&
 	git repack -A -l -d &&
-	compare_mtimes "$tmppack" "$fsha1path" "$csha1path" "$tsha1path"
+	test-chmtime -v +0 "$tmppack" "$fsha1path" "$csha1path" "$tsha1path" \
+		> mtimes &&
+	compare_mtimes < mtimes
 '
 
 test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
new file mode 100755
index 0000000..2586f86
--- /dev/null
+++ b/t/t7800-difftool.sh
@@ -0,0 +1,211 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 David Aguilar
+#
+
+test_description='git-difftool
+
+Testing basic diff tool invocation
+'
+
+. ./test-lib.sh
+
+remove_config_vars()
+{
+	# Unset all config variables used by git-difftool
+	git config --unset diff.tool
+	git config --unset difftool.test-tool.cmd
+	git config --unset difftool.prompt
+	git config --unset merge.tool
+	git config --unset mergetool.test-tool.cmd
+	return 0
+}
+
+restore_test_defaults()
+{
+	# Restores the test defaults used by several tests
+	remove_config_vars
+	unset GIT_DIFF_TOOL
+	unset GIT_MERGE_TOOL
+	unset GIT_DIFFTOOL_PROMPT
+	unset GIT_DIFFTOOL_NO_PROMPT
+	git config diff.tool test-tool &&
+	git config difftool.test-tool.cmd 'cat $LOCAL'
+}
+
+prompt_given()
+{
+	prompt="$1"
+	test "$prompt" = "Hit return to launch 'test-tool': branch"
+}
+
+# Create a file on master and change it on branch
+test_expect_success 'setup' '
+	echo master >file &&
+	git add file &&
+	git commit -m "added file" &&
+
+	git checkout -b branch master &&
+	echo branch >file &&
+	git commit -a -m "branch changed file" &&
+	git checkout master
+'
+
+# Configure a custom difftool.<tool>.cmd and use it
+test_expect_success 'custom commands' '
+	restore_test_defaults &&
+	git config difftool.test-tool.cmd "cat \$REMOTE" &&
+
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "master" &&
+
+	restore_test_defaults &&
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "branch"
+'
+
+# Ensures that git-difftool ignores bogus --tool values
+test_expect_success 'difftool ignores bad --tool values' '
+	diff=$(git difftool --no-prompt --tool=bogus-tool branch)
+	test "$?" = 1 &&
+	test "$diff" = ""
+'
+
+# Specify the diff tool using $GIT_DIFF_TOOL
+test_expect_success 'GIT_DIFF_TOOL variable' '
+	git config --unset diff.tool
+	GIT_DIFF_TOOL=test-tool &&
+	export GIT_DIFF_TOOL &&
+
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+# Test the $GIT_*_TOOL variables and ensure
+# that $GIT_DIFF_TOOL always wins unless --tool is specified
+test_expect_success 'GIT_DIFF_TOOL overrides' '
+	git config diff.tool bogus-tool &&
+	git config merge.tool bogus-tool &&
+
+	GIT_MERGE_TOOL=test-tool &&
+	export GIT_MERGE_TOOL &&
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "branch" &&
+	unset GIT_MERGE_TOOL &&
+
+	GIT_MERGE_TOOL=bogus-tool &&
+	GIT_DIFF_TOOL=test-tool &&
+	export GIT_MERGE_TOOL &&
+	export GIT_DIFF_TOOL &&
+
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "branch" &&
+
+	GIT_DIFF_TOOL=bogus-tool &&
+	export GIT_DIFF_TOOL &&
+
+	diff=$(git difftool --no-prompt --tool=test-tool branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+# Test that we don't have to pass --no-prompt to difftool
+# when $GIT_DIFFTOOL_NO_PROMPT is true
+test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
+	GIT_DIFFTOOL_NO_PROMPT=true &&
+	export GIT_DIFFTOOL_NO_PROMPT &&
+
+	diff=$(git difftool branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+# git-difftool supports the difftool.prompt variable.
+# Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
+test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
+	git config difftool.prompt false &&
+	GIT_DIFFTOOL_PROMPT=true &&
+	export GIT_DIFFTOOL_PROMPT &&
+
+	prompt=$(echo | git difftool --prompt branch | tail -1) &&
+	prompt_given "$prompt" &&
+
+	restore_test_defaults
+'
+
+# Test that we don't have to pass --no-prompt when difftool.prompt is false
+test_expect_success 'difftool.prompt config variable is false' '
+	git config difftool.prompt false &&
+
+	diff=$(git difftool branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+# Test that the -y flag can override difftool.prompt = true
+test_expect_success 'difftool.prompt can overridden with -y' '
+	git config difftool.prompt true &&
+
+	diff=$(git difftool -y branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+# Test that the --prompt flag can override difftool.prompt = false
+test_expect_success 'difftool.prompt can overridden with --prompt' '
+	git config difftool.prompt false &&
+
+	prompt=$(echo | git difftool --prompt branch | tail -1) &&
+	prompt_given "$prompt" &&
+
+	restore_test_defaults
+'
+
+# Test that the last flag passed on the command-line wins
+test_expect_success 'difftool last flag wins' '
+	diff=$(git difftool --prompt --no-prompt branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults &&
+
+	prompt=$(echo | git difftool --no-prompt --prompt branch | tail -1) &&
+	prompt_given "$prompt" &&
+
+	restore_test_defaults
+'
+
+# git-difftool falls back to git-mergetool config variables
+# so test that behavior here
+test_expect_success 'difftool + mergetool config variables' '
+	remove_config_vars
+	git config merge.tool test-tool &&
+	git config mergetool.test-tool.cmd "cat \$LOCAL" &&
+
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "branch" &&
+
+	# set merge.tool to something bogus, diff.tool to test-tool
+	git config merge.tool bogus-tool &&
+	git config diff.tool test-tool &&
+
+	diff=$(git difftool --no-prompt branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
+test_expect_success 'difftool.<tool>.path' '
+	git config difftool.tkdiff.path echo &&
+	diff=$(git difftool --tool=tkdiff --no-prompt branch) &&
+	git config --unset difftool.tkdiff.path &&
+	lines=$(echo "$diff" | grep file | wc -l) &&
+	test "$lines" -eq 1
+'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index eabec2e..45cb60e 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -4,7 +4,7 @@
 . ./test-lib.sh
 
 PROG='git annotate'
-. ../annotate-tests.sh
+. "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_expect_success \
     'Annotating an old revision works' \
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 92ece30..597cf04 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -4,6 +4,6 @@
 . ./test-lib.sh
 
 PROG='git blame -c'
-. ../annotate-tests.sh
+. "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_done
diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh
new file mode 100755
index 0000000..4470a92
--- /dev/null
+++ b/t/t8005-blame-i18n.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='git blame encoding conversion'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/t8005/utf8.txt
+. "$TEST_DIRECTORY"/t8005/cp1251.txt
+. "$TEST_DIRECTORY"/t8005/sjis.txt
+
+test_expect_success 'setup the repository' '
+	# Create the file
+	echo "UTF-8 LINE" > file &&
+	git add file &&
+	git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" &&
+
+	echo "CP1251 LINE" >> file &&
+	git add file &&
+	git config i18n.commitencoding cp1251 &&
+	git commit --author "$CP1251_NAME <cp1251@localhost>" -m "$CP1251_MSG" &&
+
+	echo "SJIS LINE" >> file &&
+	git add file &&
+	git config i18n.commitencoding shift-jis &&
+	git commit --author "$SJIS_NAME <sjis@localhost>" -m "$SJIS_MSG"
+'
+
+cat >expected <<EOF
+author $SJIS_NAME
+summary $SJIS_MSG
+author $SJIS_NAME
+summary $SJIS_MSG
+author $SJIS_NAME
+summary $SJIS_MSG
+EOF
+
+test_expect_success \
+	'blame respects i18n.commitencoding' '
+	git blame --incremental file | \
+		grep "^\(author\|summary\) " > actual &&
+	test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $CP1251_NAME
+summary $CP1251_MSG
+author $CP1251_NAME
+summary $CP1251_MSG
+author $CP1251_NAME
+summary $CP1251_MSG
+EOF
+
+test_expect_success \
+	'blame respects i18n.logoutputencoding' '
+	git config i18n.logoutputencoding cp1251 &&
+	git blame --incremental file | \
+		grep "^\(author\|summary\) " > actual &&
+	test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $UTF8_NAME
+summary $UTF8_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+EOF
+
+test_expect_success \
+	'blame respects --encoding=utf-8' '
+	git blame --incremental --encoding=utf-8 file | \
+		grep "^\(author\|summary\) " > actual &&
+	test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $SJIS_NAME
+summary $SJIS_MSG
+author $CP1251_NAME
+summary $CP1251_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+EOF
+
+test_expect_success \
+	'blame respects --encoding=none' '
+	git blame --incremental --encoding=none file | \
+		grep "^\(author\|summary\) " > actual &&
+	test_cmp actual expected
+'
+
+test_done
diff --git a/t/t8005/cp1251.txt b/t/t8005/cp1251.txt
new file mode 100644
index 0000000..ce41e98
--- /dev/null
+++ b/t/t8005/cp1251.txt
@@ -0,0 +1,2 @@
+CP1251_NAME="Èâàí Ïåòðîâè÷ Ñèäîðîâ"
+CP1251_MSG="Òåñòîâîå ñîîáùåíèå"
diff --git a/t/t8005/sjis.txt b/t/t8005/sjis.txt
new file mode 100644
index 0000000..2ccfbad
--- /dev/null
+++ b/t/t8005/sjis.txt
@@ -0,0 +1,2 @@
+SJIS_NAME="„I„r„p„~ „P„u„„„‚„€„r„y„‰ „R„y„t„€„‚„€„r"
+SJIS_MSG="„S„u„ƒ„„„€„r„€„u „ƒ„€„€„q„‹„u„~„y„u"
diff --git a/t/t8005/utf8.txt b/t/t8005/utf8.txt
new file mode 100644
index 0000000..f46cfc5
--- /dev/null
+++ b/t/t8005/utf8.txt
@@ -0,0 +1,2 @@
+UTF8_NAME="Иван Петрович Сидоров"
+UTF8_MSG="Тестовое сообщение"
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index d098a01..d9420e0 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -3,6 +3,11 @@
 test_description='git send-email'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping git send-email tests, perl not available'
+	test_done
+fi
+
 PROG='git send-email'
 test_expect_success \
     'prepare reference tree' \
@@ -32,31 +37,76 @@
 }
 
 test_expect_success 'Extract patches' '
-    patches=`git format-patch -n HEAD^1`
+    patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1`
 '
 
+# Test no confirm early to ensure remaining tests will not hang
+test_no_confirm () {
+	rm -f no_confirm_okay
+	echo n | \
+		GIT_SEND_EMAIL_NOTTY=1 \
+		git send-email \
+		--from="Example <from@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$@ \
+		$patches > stdout &&
+		test_must_fail grep "Send this email" stdout &&
+		> no_confirm_okay
+}
+
+# Exit immediately to prevent hang if a no-confirm test fails
+check_no_confirm () {
+	test -f no_confirm_okay || {
+		say 'No confirm test failed; skipping remaining tests to prevent hanging'
+		test_done
+	}
+}
+
+test_expect_success 'No confirm with --suppress-cc' '
+	test_no_confirm --suppress-cc=sob
+'
+check_no_confirm
+
+test_expect_success 'No confirm with --confirm=never' '
+	test_no_confirm --confirm=never
+'
+check_no_confirm
+
+# leave sendemail.confirm set to never after this so that none of the
+# remaining tests prompt unintentionally.
+test_expect_success 'No confirm with sendemail.confirm=never' '
+	git config sendemail.confirm never &&
+	test_no_confirm --compose --subject=foo
+'
+check_no_confirm
+
 test_expect_success 'Send patches' '
-     git send-email --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+     git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
 '
 
 cat >expected <<\EOF
 !nobody@example.com!
 !author@example.com!
+!one@example.com!
+!two@example.com!
 EOF
 test_expect_success \
     'Verify commandline' \
-    'diff commandline1 expected'
+    'test_cmp expected commandline1'
 
 cat >expected-show-all-headers <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<bcc@example.com>
+RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<bcc@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: cc@example.com, A <author@example.com>
+Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -70,6 +120,7 @@
 test_expect_success 'Show all headers' '
 	git send-email \
 		--dry-run \
+		--suppress-cc=sob \
 		--from="Example <from@example.com>" \
 		--to=to@example.com \
 		--cc=cc@example.com \
@@ -84,6 +135,19 @@
 	test_cmp expected-show-all-headers actual-show-all-headers
 '
 
+test_expect_success 'Prompting works' '
+	clean_fake_sendmail &&
+	(echo "Example <from@example.com>"
+	 echo "to@example.com"
+	 echo ""
+	) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches \
+		2>errors &&
+		grep "^From: Example <from@example.com>$" msgtxt1 &&
+		grep "^To: to@example.com$" msgtxt1
+'
+
 z8=zzzzzzzz
 z64=$z8$z8$z8$z8$z8$z8$z8$z8
 z512=$z64$z64$z64$z64$z64$z64$z64$z64
@@ -104,12 +168,34 @@
 	! test -e commandline1
 '
 
+test_expect_success 'Author From: in message body' '
+	clean_fake_sendmail &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches &&
+	sed "1,/^$/d" < msgtxt1 > msgbody1
+	grep "From: A <author@example.com>" msgbody1
+'
+
+test_expect_success 'Author From: not in message body' '
+	clean_fake_sendmail &&
+	git send-email \
+		--from="A <author@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches &&
+	sed "1,/^$/d" < msgtxt1 > msgbody1
+	! grep "From: A <author@example.com>" msgbody1
+'
+
 test_expect_success 'allow long lines with --no-validate' '
 	git send-email \
 		--from="Example <nobody@example.com>" \
 		--to=nobody@example.com \
 		--smtp-server="$(pwd)/fake.sendmail" \
-		--no-validate \
+		--novalidate \
 		$patches longline.patch \
 		2>errors
 '
@@ -148,15 +234,13 @@
 
 test_expect_success '--compose works' '
 	clean_fake_sendmail &&
-	echo y | \
-		GIT_SEND_EMAIL_NOTTY=1 \
-		git send-email \
-		--compose --subject foo \
-		--from="Example <nobody@example.com>" \
-		--to=nobody@example.com \
-		--smtp-server="$(pwd)/fake.sendmail" \
-		$patches \
-		2>errors
+	git send-email \
+	--compose --subject foo \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	--smtp-server="$(pwd)/fake.sendmail" \
+	$patches \
+	2>errors
 '
 
 test_expect_success 'first message is compose text' '
@@ -167,16 +251,18 @@
 	grep "Subject:.*Second" msgtxt2
 '
 
-cat >expected-show-all-headers <<\EOF
+cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>
+RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: cc@example.com, A <author@example.com>
+Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -185,10 +271,10 @@
 Result: OK
 EOF
 
-test_expect_success 'sendemail.cc set' '
-	git config sendemail.cc cc@example.com &&
+test_suppression () {
 	git send-email \
 		--dry-run \
+		--suppress-cc=$1 \
 		--from="Example <from@example.com>" \
 		--to=to@example.com \
 		--smtp-server relay.example.com \
@@ -196,20 +282,27 @@
 	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \
 		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
 		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
-		>actual-show-all-headers &&
-	test_cmp expected-show-all-headers actual-show-all-headers
+		>actual-suppress-$1 &&
+	test_cmp expected-suppress-$1 actual-suppress-$1
+}
+
+test_expect_success 'sendemail.cc set' '
+	git config sendemail.cc cc@example.com &&
+	test_suppression sob
 '
 
-cat >expected-show-all-headers <<\EOF
+cat >expected-suppress-sob <<\EOF
 0001-Second.patch
 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
 Dry-OK. Log says:
 Server: relay.example.com
 MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
 From: Example <from@example.com>
 To: to@example.com
-Cc: A <author@example.com>
+Cc: A <author@example.com>, One <one@example.com>, two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
@@ -220,17 +313,227 @@
 
 test_expect_success 'sendemail.cc unset' '
 	git config --unset sendemail.cc &&
+	test_suppression sob
+'
+
+cat >expected-suppress-all <<\EOF
+0001-Second.patch
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=all' '
+	test_suppression all
+'
+
+cat >expected-suppress-body <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=body' '
+	test_suppression body
+'
+
+cat >expected-suppress-sob <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=sob' '
+	test_suppression sob
+'
+
+cat >expected-suppress-bodycc <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=bodycc' '
+	test_suppression bodycc
+'
+
+cat >expected-suppress-cc <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>,<committer@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>, C O Mitter <committer@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=cc' '
+	test_suppression cc
+'
+
+test_confirm () {
+	echo y | \
+		GIT_SEND_EMAIL_NOTTY=1 \
+		git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$@ $patches > stdout &&
+	grep "Send this email" stdout
+}
+
+test_expect_success '--confirm=always' '
+	test_confirm --confirm=always --suppress-cc=all
+'
+
+test_expect_success '--confirm=auto' '
+	test_confirm --confirm=auto
+'
+
+test_expect_success '--confirm=cc' '
+	test_confirm --confirm=cc
+'
+
+test_expect_success '--confirm=compose' '
+	test_confirm --confirm=compose --compose
+'
+
+test_expect_success 'confirm by default (due to cc)' '
+	CONFIRM=$(git config --get sendemail.confirm) &&
+	git config --unset sendemail.confirm &&
+	test_confirm
+	ret="$?"
+	git config sendemail.confirm ${CONFIRM:-never}
+	test $ret = "0"
+'
+
+test_expect_success 'confirm by default (due to --compose)' '
+	CONFIRM=$(git config --get sendemail.confirm) &&
+	git config --unset sendemail.confirm &&
+	test_confirm --suppress-cc=all --compose
+	ret="$?"
+	git config sendemail.confirm ${CONFIRM:-never}
+	test $ret = "0"
+'
+
+test_expect_success 'confirm detects EOF (inform assumes y)' '
+	CONFIRM=$(git config --get sendemail.confirm) &&
+	git config --unset sendemail.confirm &&
+	rm -fr outdir &&
+	git format-patch -2 -o outdir &&
+	GIT_SEND_EMAIL_NOTTY=1 \
+		git send-email \
+			--from="Example <nobody@example.com>" \
+			--to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			outdir/*.patch < /dev/null
+	ret="$?"
+	git config sendemail.confirm ${CONFIRM:-never}
+	test $ret = "0"
+'
+
+test_expect_success 'confirm detects EOF (auto causes failure)' '
+	CONFIRM=$(git config --get sendemail.confirm) &&
+	git config sendemail.confirm auto &&
+	GIT_SEND_EMAIL_NOTTY=1 &&
+	export GIT_SEND_EMAIL_NOTTY &&
+		test_must_fail git send-email \
+			--from="Example <nobody@example.com>" \
+			--to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			$patches < /dev/null
+	ret="$?"
+	git config sendemail.confirm ${CONFIRM:-never}
+	test $ret = "0"
+'
+
+test_expect_success 'confirm doesnt loop forever' '
+	CONFIRM=$(git config --get sendemail.confirm) &&
+	git config sendemail.confirm auto &&
+	GIT_SEND_EMAIL_NOTTY=1 &&
+	export GIT_SEND_EMAIL_NOTTY &&
+		yes "bogus" | test_must_fail git send-email \
+			--from="Example <nobody@example.com>" \
+			--to=nobody@example.com \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			$patches
+	ret="$?"
+	git config sendemail.confirm ${CONFIRM:-never}
+	test $ret = "0"
+'
+
+test_expect_success 'utf8 Cc is rfc2047 encoded' '
+	clean_fake_sendmail &&
+	rm -fr outdir &&
+	git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" &&
 	git send-email \
-		--dry-run \
-		--from="Example <from@example.com>" \
-		--to=to@example.com \
-		--smtp-server relay.example.com \
-		$patches |
-	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \
-		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
-		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
-		>actual-show-all-headers &&
-	test_cmp expected-show-all-headers actual-show-all-headers
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	--smtp-server="$(pwd)/fake.sendmail" \
+	outdir/*.patch &&
+	grep "^Cc:" msgtxt1 |
+	grep "=?utf-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
 '
 
 test_expect_success '--compose adds MIME for utf8 body' '
@@ -239,9 +542,7 @@
 	 echo "echo utf8 body: àéìöú >>\"\$1\""
 	) >fake-editor-utf8 &&
 	chmod +x fake-editor-utf8 &&
-	echo y | \
 	  GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
-	  GIT_SEND_EMAIL_NOTTY=1 \
 	  git send-email \
 	  --compose --subject foo \
 	  --from="Example <nobody@example.com>" \
@@ -263,9 +564,7 @@
 	 echo " echo utf8 body: àéìöú) >\"\$1\""
 	) >fake-editor-utf8-mime &&
 	chmod +x fake-editor-utf8-mime &&
-	echo y | \
 	  GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \
-	  GIT_SEND_EMAIL_NOTTY=1 \
 	  git send-email \
 	  --compose --subject foo \
 	  --from="Example <nobody@example.com>" \
@@ -279,9 +578,7 @@
 
 test_expect_success '--compose adds MIME for utf8 subject' '
 	clean_fake_sendmail &&
-	echo y | \
 	  GIT_EDITOR="\"$(pwd)/fake-editor\"" \
-	  GIT_SEND_EMAIL_NOTTY=1 \
 	  git send-email \
 	  --compose --subject utf8-sübjëct \
 	  --from="Example <nobody@example.com>" \
@@ -292,4 +589,36 @@
 	grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
+test_expect_success 'detects ambiguous reference/file conflict' '
+	echo master > master &&
+	git add master &&
+	git commit -m"add master" &&
+	test_must_fail git send-email --dry-run master 2>errors &&
+	grep disambiguate errors
+'
+
+test_expect_success 'feed two files' '
+	rm -fr outdir &&
+	git format-patch -2 -o outdir &&
+	git send-email \
+	--dry-run \
+	--from="Example <nobody@example.com>" \
+	--to=nobody@example.com \
+	outdir/000?-*.patch 2>errors >out &&
+	grep "^Subject: " out >subjects &&
+	test "z$(sed -n -e 1p subjects)" = "zSubject: [PATCH 1/2] Second." &&
+	test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master"
+'
+
+test_expect_success 'in-reply-to but no threading' '
+	git send-email \
+		--dry-run \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--in-reply-to="<in-reply-id@example.com>" \
+		--no-thread \
+		$patches |
+	grep "In-Reply-To: <in-reply-id@example.com>"
+'
+
 test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 843a501..4eee2e9 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -3,24 +3,24 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-test_description='git-svn basic tests'
+test_description='git svn basic tests'
 GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
 
-case "$GIT_SVN_LC_ALL" in
-*.UTF-8)
-	have_utf8=t
-	;;
-*)
-	have_utf8=
-	;;
-esac
-
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git-svn tests'
+say 'define NO_SVN_TESTS to skip git svn tests'
+
+case "$GIT_SVN_LC_ALL" in
+*.UTF-8)
+	test_set_prereq UTF8
+	;;
+*)
+	say "UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
+	;;
+esac
 
 test_expect_success \
-    'initialize git-svn' '
+    'initialize git svn' '
 	mkdir import &&
 	cd import &&
 	echo foo > foo &&
@@ -31,26 +31,26 @@
 	echo "zzz" > bar/zzz &&
 	echo "#!/bin/sh" > exec.sh &&
 	chmod +x exec.sh &&
-	svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+	svn import -m "import for git svn" . "$svnrepo" >/dev/null &&
 	cd .. &&
 	rm -rf import &&
-	git-svn init "$svnrepo"'
+	git svn init "$svnrepo"'
 
 test_expect_success \
     'import an SVN revision into git' \
-    'git-svn fetch'
+    'git svn fetch'
 
 test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
 name='try a deep --rmdir with a commit'
 test_expect_success "$name" '
-	git checkout -f -b mybranch remotes/git-svn &&
+	git checkout -f -b mybranch ${remotes_git_svn} &&
 	mv dir/a/b/c/d/e/file dir/file &&
 	cp dir/file file &&
 	git update-index --add --remove dir/a/b/c/d/e/file dir/file file &&
 	git commit -m "$name" &&
-	git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch &&
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch &&
 	svn up "$SVN_TREE" &&
 	test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
 
@@ -63,61 +63,61 @@
 	git update-index --remove dir/file &&
 	git update-index --add dir/file/file &&
 	git commit -m '$name' &&
-	test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch" || true
+	test_must_fail git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch" || true
 
 
 name='detect node change from directory to file #1'
 test_expect_success "$name" '
 	rm -rf dir "$GIT_DIR"/index &&
-	git checkout -f -b mybranch2 remotes/git-svn &&
+	git checkout -f -b mybranch2 ${remotes_git_svn} &&
 	mv bar/zzz zzz &&
 	rm -rf bar &&
 	mv zzz bar &&
 	git update-index --remove -- bar/zzz &&
 	git update-index --add -- bar &&
 	git commit -m "$name" &&
-	test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch2' || true
+	test_must_fail git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch2' || true
 
 
 name='detect node change from file to directory #2'
 test_expect_success "$name" '
 	rm -f "$GIT_DIR"/index &&
-	git checkout -f -b mybranch3 remotes/git-svn &&
+	git checkout -f -b mybranch3 ${remotes_git_svn} &&
 	rm bar/zzz &&
 	git update-index --remove bar/zzz &&
 	mkdir bar/zzz &&
 	echo yyy > bar/zzz/yyy &&
 	git update-index --add bar/zzz/yyy &&
 	git commit -m "$name" &&
-	test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch3' || true
+	test_must_fail git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch3' || true
 
 
 name='detect node change from directory to file #2'
 test_expect_success "$name" '
 	rm -f "$GIT_DIR"/index &&
-	git checkout -f -b mybranch4 remotes/git-svn &&
+	git checkout -f -b mybranch4 ${remotes_git_svn} &&
 	rm -rf dir &&
 	git update-index --remove -- dir/file &&
 	touch dir &&
 	echo asdf > dir &&
 	git update-index --add -- dir &&
 	git commit -m "$name" &&
-	test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch4' || true
+	test_must_fail git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch4' || true
 
 
 name='remove executable bit from a file'
 test_expect_success "$name" '
 	rm -f "$GIT_DIR"/index &&
-	git checkout -f -b mybranch5 remotes/git-svn &&
+	git checkout -f -b mybranch5 ${remotes_git_svn} &&
 	chmod -x exec.sh &&
 	git update-index exec.sh &&
 	git commit -m "$name" &&
-	git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch5 &&
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch5 &&
 	svn up "$SVN_TREE" &&
 	test ! -x "$SVN_TREE"/exec.sh'
 
@@ -127,8 +127,8 @@
 	chmod +x exec.sh &&
 	git update-index exec.sh &&
 	git commit -m "$name" &&
-	git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch5 &&
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch5 &&
 	svn up "$SVN_TREE" &&
 	test -x "$SVN_TREE"/exec.sh'
 
@@ -139,8 +139,8 @@
 	ln -s bar/zzz exec.sh &&
 	git update-index exec.sh &&
 	git commit -m "$name" &&
-	git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch5 &&
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch5 &&
 	svn up "$SVN_TREE" &&
 	test -L "$SVN_TREE"/exec.sh'
 
@@ -151,8 +151,8 @@
 	ln -s bar/zzz exec-2.sh &&
 	git update-index --add bar/zzz exec-2.sh &&
 	git commit -m "$name" &&
-	git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch5 &&
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch5 &&
 	svn up "$SVN_TREE" &&
 	test -x "$SVN_TREE"/bar/zzz &&
 	test -L "$SVN_TREE"/exec-2.sh'
@@ -164,40 +164,35 @@
 	cp help exec-2.sh &&
 	git update-index exec-2.sh &&
 	git commit -m "$name" &&
-	git-svn set-tree --find-copies-harder --rmdir \
-		remotes/git-svn..mybranch5 &&
+	git svn set-tree --find-copies-harder --rmdir \
+		${remotes_git_svn}..mybranch5 &&
 	svn up "$SVN_TREE" &&
 	test -f "$SVN_TREE"/exec-2.sh &&
 	test ! -L "$SVN_TREE"/exec-2.sh &&
 	test_cmp help "$SVN_TREE"/exec-2.sh'
 
-if test "$have_utf8" = t
-then
-	name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
-	LC_ALL="$GIT_SVN_LC_ALL"
-	export LC_ALL
-	test_expect_success "$name" "
-		echo '# hello' >> exec-2.sh &&
-		git update-index exec-2.sh &&
-		git commit -m 'éï∏' &&
-		git-svn set-tree HEAD"
-	unset LC_ALL
-else
-	say "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)"
-fi
+name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
+LC_ALL="$GIT_SVN_LC_ALL"
+export LC_ALL
+test_expect_success UTF8 "$name" "
+	echo '# hello' >> exec-2.sh &&
+	git update-index exec-2.sh &&
+	git commit -m 'éï∏' &&
+	git svn set-tree HEAD"
+unset LC_ALL
 
 name='test fetch functionality (svn => git) with alternate GIT_SVN_ID'
 GIT_SVN_ID=alt
 export GIT_SVN_ID
 test_expect_success "$name" \
-    'git-svn init "$svnrepo" && git-svn fetch &&
-     git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a &&
+    'git svn init "$svnrepo" && git svn fetch &&
+     git rev-list --pretty=raw ${remotes_git_svn} | grep ^tree | uniq > a &&
      git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
      test_cmp a b'
 
 name='check imported tree checksums expected tree checksums'
 rm -f expected
-if test "$have_utf8" = t
+if test_have_prereq UTF8
 then
 	echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
 fi
@@ -215,45 +210,45 @@
 
 test_expect_success 'exit if remote refs are ambigious' "
         git config --add svn-remote.svn.fetch \
-                              bar:refs/remotes/git-svn &&
-	test_must_fail git-svn migrate
+                              bar:refs/${remotes_git_svn} &&
+	test_must_fail git svn migrate
 "
 
 test_expect_success 'exit if init-ing a would clobber a URL' '
         svnadmin create "${PWD}/svnrepo2" &&
         svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
         git config --unset svn-remote.svn.fetch \
-                                "^bar:refs/remotes/git-svn$" &&
-	test_must_fail git-svn init "${svnrepo}2/bar"
+                                "^bar:refs/${remotes_git_svn}$" &&
+	test_must_fail git svn init "${svnrepo}2/bar"
         '
 
 test_expect_success \
   'init allows us to connect to another directory in the same repo' '
-        git-svn init --minimize-url -i bar "$svnrepo/bar" &&
+        git svn init --minimize-url -i bar "$svnrepo/bar" &&
         git config --get svn-remote.svn.fetch \
                               "^bar:refs/remotes/bar$" &&
         git config --get svn-remote.svn.fetch \
-                              "^:refs/remotes/git-svn$"
+                              "^:refs/${remotes_git_svn}$"
         '
 
 test_expect_success 'able to dcommit to a subdirectory' "
-	git-svn fetch -i bar &&
+	git svn fetch -i bar &&
 	git checkout -b my-bar refs/remotes/bar &&
 	echo abc > d &&
 	git update-index --add d &&
 	git commit -m '/bar/d should be in the log' &&
-	git-svn dcommit -i bar &&
+	git svn dcommit -i bar &&
 	test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\" &&
 	mkdir newdir &&
 	echo new > newdir/dir &&
 	git update-index --add newdir/dir &&
 	git commit -m 'add a new directory' &&
-	git-svn dcommit -i bar &&
+	git svn dcommit -i bar &&
 	test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\" &&
 	echo foo >> newdir/dir &&
 	git update-index newdir/dir &&
 	git commit -m 'modify a file in new directory' &&
-	git-svn dcommit -i bar &&
+	git svn dcommit -i bar &&
 	test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
 	"
 
@@ -261,8 +256,17 @@
 	echo cba > d &&
 	git update-index d &&
 	git commit -m 'update /bar/d' &&
-	git-svn set-tree -i bar HEAD &&
+	git svn set-tree -i bar HEAD &&
 	test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
 	"
 
+test_expect_success 'git-svn works in a bare repository' '
+	mkdir bare-repo &&
+	( cd bare-repo &&
+	git init --bare &&
+	GIT_DIR=. git svn init "$svnrepo" &&
+	git svn fetch ) &&
+	rm -rf bare-repo
+	'
+
 test_done
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index f420796..1e31d6e 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-test_description='git-svn property tests'
+test_description='git svn property tests'
 . ./lib-git-svn.sh
 
 mkdir import
@@ -26,29 +26,29 @@
 EOF
 
 	printf "Hello\r\nWorld\r\n" > crlf
-	a_crlf=`git-hash-object -w crlf`
+	a_crlf=`git hash-object -w crlf`
 	printf "Hello\rWorld\r" > cr
-	a_cr=`git-hash-object -w cr`
+	a_cr=`git hash-object -w cr`
 	printf "Hello\nWorld\n" > lf
-	a_lf=`git-hash-object -w lf`
+	a_lf=`git hash-object -w lf`
 
 	printf "Hello\r\nWorld" > ne_crlf
-	a_ne_crlf=`git-hash-object -w ne_crlf`
+	a_ne_crlf=`git hash-object -w ne_crlf`
 	printf "Hello\nWorld" > ne_lf
-	a_ne_lf=`git-hash-object -w ne_lf`
+	a_ne_lf=`git hash-object -w ne_lf`
 	printf "Hello\rWorld" > ne_cr
-	a_ne_cr=`git-hash-object -w ne_cr`
+	a_ne_cr=`git hash-object -w ne_cr`
 
 	touch empty
-	a_empty=`git-hash-object -w empty`
+	a_empty=`git hash-object -w empty`
 	printf "\n" > empty_lf
-	a_empty_lf=`git-hash-object -w empty_lf`
+	a_empty_lf=`git hash-object -w empty_lf`
 	printf "\r" > empty_cr
-	a_empty_cr=`git-hash-object -w empty_cr`
+	a_empty_cr=`git hash-object -w empty_cr`
 	printf "\r\n" > empty_crlf
-	a_empty_crlf=`git-hash-object -w empty_crlf`
+	a_empty_crlf=`git hash-object -w empty_crlf`
 
-	svn import --no-auto-props -m 'import for git-svn' . "$svnrepo" >/dev/null
+	svn import --no-auto-props -m 'import for git svn' . "$svnrepo" >/dev/null
 cd ..
 
 rm -rf import
@@ -66,16 +66,16 @@
 		svn commit -m "Propset Id" &&
 	cd ..'
 
-test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"'
-test_expect_success 'fetch revisions from svn' 'git-svn fetch'
+test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
+test_expect_success 'fetch revisions from svn' 'git svn fetch'
 
 name='test svn:keywords ignoring'
 test_expect_success "$name" \
-	'git checkout -b mybranch remotes/git-svn &&
+	'git checkout -b mybranch ${remotes_git_svn} &&
 	echo Hi again >> kw.c &&
 	git commit -a -m "test keywords ignoring" &&
-	git-svn set-tree remotes/git-svn..mybranch &&
-	git pull . remotes/git-svn'
+	git svn set-tree ${remotes_git_svn}..mybranch &&
+	git pull . ${remotes_git_svn}'
 
 expect='/* $Id$ */'
 got="`sed -ne 2p kw.c`"
@@ -90,8 +90,8 @@
 	 cd ..'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
-	'git-svn fetch &&
-	 git pull . remotes/git-svn &&
+	'git svn fetch &&
+	 git pull . ${remotes_git_svn} &&
 	 svn co "$svnrepo" new_wc'
 
 for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
@@ -103,8 +103,8 @@
 cd test_wc
 	printf '$Id$\rHello\rWorld\r' > cr
 	printf '$Id$\rHello\rWorld' > ne_cr
-	a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin`
-	a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin`
+	a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git hash-object --stdin`
+	a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git hash-object --stdin`
 	test_expect_success 'Set CRLF on cr files' \
 	'svn propset svn:eol-style CRLF cr &&
 	 svn propset svn:eol-style CRLF ne_cr &&
@@ -113,10 +113,10 @@
 	 svn commit -m "propset CRLF on cr files"'
 cd ..
 test_expect_success 'fetch and pull latest from svn' \
-	'git-svn fetch && git pull . remotes/git-svn'
+	'git svn fetch && git pull . ${remotes_git_svn}'
 
-b_cr="`git-hash-object cr`"
-b_ne_cr="`git-hash-object ne_cr`"
+b_cr="`git hash-object cr`"
+b_ne_cr="`git hash-object ne_cr`"
 
 test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'"
 test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'"
@@ -145,7 +145,7 @@
 	svn propset -R svn:ignore 'no-such-file*' .
 	svn commit -m 'propset svn:ignore'
 	cd .. &&
-	git-svn show-ignore > show-ignore.got &&
+	git svn show-ignore > show-ignore.got &&
 	cmp show-ignore.expect show-ignore.got
 	"
 
@@ -161,8 +161,8 @@
 EOF
 
 test_expect_success 'test create-ignore' "
-	git-svn fetch && git pull . remotes/git-svn &&
-	git-svn create-ignore &&
+	git svn fetch && git pull . ${remotes_git_svn} &&
+	git svn create-ignore &&
 	cmp ./.gitignore create-ignore.expect &&
 	cmp ./deeply/.gitignore create-ignore.expect &&
 	cmp ./deeply/nested/.gitignore create-ignore.expect &&
@@ -182,15 +182,15 @@
 # pattern, it can pass even though the propget did not execute on the
 # right directory.
 test_expect_success 'test propget' "
-	git-svn propget svn:ignore . | cmp - prop.expect &&
+	git svn propget svn:ignore . | cmp - prop.expect &&
 	cd deeply &&
-	git-svn propget svn:ignore . | cmp - ../prop.expect &&
-	git-svn propget svn:entry:committed-rev nested/directory/.keep \
+	git svn propget svn:ignore . | cmp - ../prop.expect &&
+	git svn propget svn:entry:committed-rev nested/directory/.keep \
 	  | cmp - ../prop2.expect &&
-	git-svn propget svn:ignore .. | cmp - ../prop.expect &&
-	git-svn propget svn:ignore nested/ | cmp - ../prop.expect &&
-	git-svn propget svn:ignore ./nested | cmp - ../prop.expect &&
-	git-svn propget svn:ignore .././deeply/nested | cmp - ../prop.expect
+	git svn propget svn:ignore .. | cmp - ../prop.expect &&
+	git svn propget svn:ignore nested/ | cmp - ../prop.expect &&
+	git svn propget svn:ignore ./nested | cmp - ../prop.expect &&
+	git svn propget svn:ignore .././deeply/nested | cmp - ../prop.expect
 	"
 
 cat >prop.expect <<\EOF
@@ -210,8 +210,8 @@
 EOF
 
 test_expect_success 'test proplist' "
-	git-svn proplist . | cmp - prop.expect &&
-	git-svn proplist nested/directory/.keep | cmp - prop2.expect
+	git svn proplist . | cmp - prop.expect &&
+	git svn proplist nested/directory/.keep | cmp - prop2.expect
 	"
 
 test_done
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
index 0e7ce34..e223218 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-test_description='git-svn rmdir'
+test_description='git svn rmdir'
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
@@ -9,20 +9,20 @@
 	mkdir -p deeply/nested/directory/number/2 &&
 	echo foo > deeply/nested/directory/number/1/file &&
 	echo foo > deeply/nested/directory/number/2/another &&
-	svn import -m "import for git-svn" . "$svnrepo" &&
+	svn import -m "import for git svn" . "$svnrepo" &&
 	cd ..
 	'
 
-test_expect_success 'mirror via git-svn' '
-	git-svn init "$svnrepo" &&
-	git-svn fetch &&
-	git checkout -f -b test-rmdir remotes/git-svn
+test_expect_success 'mirror via git svn' '
+	git svn init "$svnrepo" &&
+	git svn fetch &&
+	git checkout -f -b test-rmdir ${remotes_git_svn}
 	'
 
 test_expect_success 'Try a commit on rmdir' '
 	git rm -f deeply/nested/directory/number/2/another &&
 	git commit -a -m "remove another" &&
-	git-svn set-tree --rmdir HEAD &&
+	git svn set-tree --rmdir HEAD &&
 	svn ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1
 	'
 
diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh
index 9ffd845..963dd95 100755
--- a/t/t9103-git-svn-tracked-directory-removed.sh
+++ b/t/t9103-git-svn-tracked-directory-removed.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2007 Eric Wong
 #
 
-test_description='git-svn tracking removed top-level path'
+test_description='git svn tracking removed top-level path'
 . ./lib-git-svn.sh
 
 test_expect_success 'make history for tracking' '
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index d80ea64..ab9fa32 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-test_description='git-svn fetching'
+test_description='git svn fetching'
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
@@ -27,8 +27,8 @@
 	'
 
 test_expect_success 'init and fetch a moved directory' '
-	git-svn init --minimize-url -i thunk "$svnrepo"/thunk &&
-	git-svn fetch -i thunk &&
+	git svn init --minimize-url -i thunk "$svnrepo"/thunk &&
+	git svn fetch -i thunk &&
 	test "`git rev-parse --verify refs/remotes/thunk@2`" \
            = "`git rev-parse --verify refs/remotes/thunk~1`" &&
         test "`git cat-file blob refs/remotes/thunk:readme |\
@@ -43,7 +43,7 @@
           trunk:refs/remotes/svn/trunk &&
         git config --add svn-remote.svn.fetch \
           thunk:refs/remotes/svn/thunk &&
-        git-svn fetch -i svn/thunk &&
+        git svn fetch -i svn/thunk &&
 	test "`git rev-parse --verify refs/remotes/svn/trunk`" \
            = "`git rev-parse --verify refs/remotes/svn/thunk~1`" &&
         test "`git cat-file blob refs/remotes/svn/thunk:readme |\
@@ -57,8 +57,8 @@
                -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
         git config --add svn-remote.svn.fetch \
           junk:refs/remotes/svn/junk &&
-        git-svn fetch -i svn/thunk &&
-        git-svn fetch -i svn/junk &&
+        git svn fetch -i svn/thunk &&
+        git svn fetch -i svn/junk &&
         test -z "`git diff svn/junk svn/trunk`" &&
         test "`git merge-base svn/junk svn/trunk`" \
            = "`git rev-parse svn/trunk`"
@@ -69,9 +69,9 @@
         echo hi > import/trunk/thunk/bump/thud/file &&
         svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
         svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
-        git-svn init --minimize-url -i larger \
+        git svn init --minimize-url -i larger \
           "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
-        git-svn fetch -i larger &&
+        git svn fetch -i larger &&
         git rev-parse --verify refs/remotes/larger &&
         git rev-parse --verify \
            refs/remotes/larger-parent/trunk/thunk/bump/thud &&
@@ -92,15 +92,15 @@
                 cd ..
         svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
         svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
-        git-svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
-        git-svn fetch -i blob
+        git svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
+        git svn fetch -i blob
         '
 
 test_expect_success 'follow deleted directory' '
 	svn mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye &&
 	svn rm -m "remove glob" "$svnrepo"/glob &&
-	git-svn init --minimize-url -i glob "$svnrepo"/glob &&
-	git-svn fetch -i glob &&
+	git svn init --minimize-url -i glob "$svnrepo"/glob &&
+	git svn fetch -i glob &&
 	test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi &&
 	test "`git ls-tree refs/remotes/glob | wc -l `" -eq 1
 	'
@@ -129,9 +129,9 @@
 	  poke native/t/c.t &&
 	  svn commit -m "reorg test" &&
 	cd .. &&
-	git-svn init --minimize-url -i r9270-t \
+	git svn init --minimize-url -i r9270-t \
 	  "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t &&
-	git-svn fetch -i r9270-t &&
+	git svn fetch -i r9270-t &&
 	test `git rev-list r9270-t | wc -l` -eq 2 &&
 	test "`git ls-tree --name-only r9270-t~1`" = \
 	     "`git ls-tree --name-only r9270-t`"
@@ -139,9 +139,9 @@
 
 test_expect_success "track initial change if it was only made to parent" '
 	svn cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk &&
-	git-svn init --minimize-url -i r9270-d \
+	git svn init --minimize-url -i r9270-d \
 	  "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t &&
-	git-svn fetch -i r9270-d &&
+	git svn fetch -i r9270-d &&
 	test `git rev-list r9270-d | wc -l` -eq 3 &&
 	test "`git ls-tree --name-only r9270-t`" = \
 	     "`git ls-tree --name-only r9270-d`" &&
@@ -193,19 +193,19 @@
 
 test_expect_success "track multi-parent paths" '
 	svn cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob &&
-	git-svn multi-fetch &&
+	git svn multi-fetch &&
 	test `git cat-file commit refs/remotes/glob | \
 	       grep "^parent " | wc -l` -eq 2
 	'
 
 test_expect_success "multi-fetch continues to work" "
-	git-svn multi-fetch
+	git svn multi-fetch
 	"
 
 test_expect_success "multi-fetch works off a 'clean' repository" '
 	rm -r "$GIT_DIR/svn" "$GIT_DIR/refs/remotes" "$GIT_DIR/logs" &&
 	mkdir "$GIT_DIR/svn" &&
-	git-svn multi-fetch
+	git svn multi-fetch
 	'
 
 test_debug 'gitk --all &'
diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
index 6323036..ba99abb 100755
--- a/t/t9105-git-svn-commit-diff.sh
+++ b/t/t9105-git-svn-commit-diff.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2006 Eric Wong
-test_description='git-svn commit-diff'
+test_description='git svn commit-diff'
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
@@ -26,16 +26,16 @@
 
 test_expect_success 'test the commit-diff command' '
 	test -n "$prev" && test -n "$head" &&
-	git-svn commit-diff -r1 "$prev" "$head" "$svnrepo" &&
+	git svn commit-diff -r1 "$prev" "$head" "$svnrepo" &&
 	svn co "$svnrepo" wc &&
 	cmp readme wc/readme
 	'
 
-test_expect_success 'commit-diff to a sub-directory (with git-svn config)' '
+test_expect_success 'commit-diff to a sub-directory (with git svn config)' '
 	svn import -m "sub-directory" import "$svnrepo"/subdir &&
-	git-svn init --minimize-url "$svnrepo"/subdir &&
-	git-svn fetch &&
-	git-svn commit-diff -r3 "$prev" "$head" &&
+	git svn init --minimize-url "$svnrepo"/subdir &&
+	git svn fetch &&
+	git svn commit-diff -r3 "$prev" "$head" &&
 	svn cat "$svnrepo"/subdir/readme > readme.2 &&
 	cmp readme readme.2
 	'
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index 83896e9..6eb0fd8 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2006 Eric Wong
-test_description='git-svn commit-diff clobber'
+test_description='git svn commit-diff clobber'
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
@@ -27,7 +27,7 @@
 test_expect_success 'commit conflicting change from git' '
 	echo second line from git >> file &&
 	git commit -a -m "second line from git" &&
-	test_must_fail git-svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
+	test_must_fail git svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
 '
 
 test_expect_success 'commit complementing change from git' '
@@ -36,13 +36,13 @@
 	git commit -a -m "second line from svn" &&
 	echo third line from git >> file &&
 	git commit -a -m "third line from git" &&
-	git-svn commit-diff -r2 HEAD~1 HEAD "$svnrepo"
+	git svn commit-diff -r2 HEAD~1 HEAD "$svnrepo"
 	'
 
 test_expect_success 'dcommit fails to commit because of conflict' '
-	git-svn init "$svnrepo" &&
-	git-svn fetch &&
-	git reset --hard refs/remotes/git-svn &&
+	git svn init "$svnrepo" &&
+	git svn fetch &&
+	git reset --hard refs/${remotes_git_svn} &&
 	svn co "$svnrepo" t.svn &&
 	cd t.svn &&
 	echo fourth line from svn >> file &&
@@ -52,18 +52,18 @@
 	rm -rf t.svn &&
 	echo "fourth line from git" >> file &&
 	git commit -a -m "fourth line from git" &&
-	test_must_fail git-svn dcommit
+	test_must_fail git svn dcommit
 	'
 
 test_expect_success 'dcommit does the svn equivalent of an index merge' "
-	git reset --hard refs/remotes/git-svn &&
+	git reset --hard refs/${remotes_git_svn} &&
 	echo 'index merge' > file2 &&
 	git update-index --add file2 &&
 	git commit -a -m 'index merge' &&
 	echo 'more changes' >> file2 &&
 	git update-index file2 &&
 	git commit -a -m 'more changes' &&
-	git-svn dcommit
+	git svn dcommit
 	"
 
 test_expect_success 'commit another change from svn side' '
@@ -76,8 +76,8 @@
 	rm -rf t.svn
 	'
 
-test_expect_success 'multiple dcommit from git-svn will not clobber svn' "
-	git reset --hard refs/remotes/git-svn &&
+test_expect_success 'multiple dcommit from git svn will not clobber svn' "
+	git reset --hard refs/${remotes_git_svn} &&
 	echo new file >> new-file &&
 	git update-index --add new-file &&
 	git commit -a -m 'new file' &&
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index d9b553a..acad16a 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright (c) 2006 Eric Wong
-test_description='git-svn metadata migrations from previous versions'
+test_description='git svn metadata migrations from previous versions'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup old-looking metadata' '
@@ -14,34 +14,34 @@
 		done && \
 		svn import -m test . "$svnrepo"
 		cd .. &&
-	git-svn init "$svnrepo" &&
-	git-svn fetch &&
+	git svn init "$svnrepo" &&
+	git svn fetch &&
 	mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
 	mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
 	rmdir "$GIT_DIR"/svn &&
-	git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn &&
-	git update-ref refs/heads/svn-HEAD refs/remotes/git-svn &&
-	git update-ref -d refs/remotes/git-svn refs/remotes/git-svn
+	git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
+	git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
+	git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
 	'
 
 head=`git rev-parse --verify refs/heads/git-svn-HEAD^0`
 test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'"
 
-test_expect_success 'initialize old-style (v0) git-svn layout' '
+test_expect_success 'initialize old-style (v0) git svn layout' '
 	mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
 	echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url &&
 	echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
-	git-svn migrate &&
-	! test -d "$GIT_DIR"/git-svn &&
-	git rev-parse --verify refs/remotes/git-svn^0 &&
+	git svn migrate &&
+	! test -d "$GIT_DIR"/git svn &&
+	git rev-parse --verify refs/${remotes_git_svn}^0 &&
 	git rev-parse --verify refs/remotes/svn^0 &&
 	test "$(git config --get svn-remote.svn.url)" = "$svnrepo" &&
 	test `git config --get svn-remote.svn.fetch` = \
-             ":refs/remotes/git-svn"
+             ":refs/${remotes_git_svn}"
 	'
 
 test_expect_success 'initialize a multi-repository repo' '
-	git-svn init "$svnrepo" -T trunk -t tags -b branches &&
+	git svn init "$svnrepo" -T trunk -t tags -b branches &&
 	git config --get-all svn-remote.svn.fetch > fetch.out &&
 	grep "^trunk:refs/remotes/trunk$" fetch.out &&
 	test -n "`git config --get svn-remote.svn.branches \
@@ -61,7 +61,7 @@
 
 # refs should all be different, but the trees should all be the same:
 test_expect_success 'multi-fetch works on partial urls + paths' "
-	git-svn multi-fetch &&
+	git svn multi-fetch &&
 	for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
 		git rev-parse --verify refs/remotes/\$i^0 >> refs.out || exit 1;
 	    done &&
@@ -85,7 +85,7 @@
 		( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
 		echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
 	done &&
-	git-svn migrate --minimize &&
+	git svn migrate --minimize &&
 	test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
 	git config --get-all svn-remote.svn.fetch > fetch.out &&
 	grep "^trunk:refs/remotes/trunk$" fetch.out &&
@@ -94,11 +94,11 @@
 	grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
 	grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
 	grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
-	grep "^:refs/remotes/git-svn" fetch.out
+	grep "^:refs/${remotes_git_svn}" fetch.out
 	'
 
 test_expect_success  ".rev_db auto-converted to .rev_map.UUID" '
-	git-svn fetch -i trunk &&
+	git svn fetch -i trunk &&
 	test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
 	expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
 	test -n "$expect" &&
@@ -106,7 +106,7 @@
 	convert_to_rev_db "$expect" "$rev_db" &&
 	rm -f "$expect" &&
 	test -f "$rev_db" &&
-	git-svn fetch -i trunk &&
+	git svn fetch -i trunk &&
 	test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
 	test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
 	test -f "$expect"
diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh
index 8b792a1..d8582b1 100755
--- a/t/t9108-git-svn-glob.sh
+++ b/t/t9108-git-svn-glob.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright (c) 2007 Eric Wong
-test_description='git-svn globbing refspecs'
+test_description='git svn globbing refspecs'
 . ./lib-git-svn.sh
 
 cat > expect.end <<EOF
@@ -46,7 +46,7 @@
 	                 "branches/*/src/a:refs/remotes/branches/*" &&
 	git config --add svn-remote.svn.tags\
 	                 "tags/*/src/a:refs/remotes/tags/*" &&
-	git-svn multi-fetch &&
+	git svn multi-fetch &&
 	git log --pretty=oneline refs/remotes/tags/end | \
 	    sed -e "s/^.\{41\}//" > output.end &&
 	test_cmp expect.end output.end &&
@@ -74,7 +74,7 @@
 		poke tags/end/src/b/readme &&
 		svn commit -m "try to try"
 	) &&
-	git-svn fetch two &&
+	git svn fetch two &&
 	test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
 	test `git rev-list refs/remotes/two/branches/start | wc -l` -eq 3 &&
 	test `git rev-parse refs/remotes/two/branches/start~2` = \
@@ -104,7 +104,7 @@
 		poke tags/end/src/b/readme &&
 		svn commit -m "try to try"
 	) &&
-	test_must_fail git-svn fetch three 2> stderr.three &&
+	test_must_fail git svn fetch three 2> stderr.three &&
 	test_cmp expect.three stderr.three
 	'
 
diff --git a/t/t9108-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh
similarity index 96%
rename from t/t9108-git-svn-multi-glob.sh
rename to t/t9109-git-svn-multi-glob.sh
index 3583721..8f79c3f 100755
--- a/t/t9108-git-svn-multi-glob.sh
+++ b/t/t9109-git-svn-multi-glob.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright (c) 2007 Eric Wong
-test_description='git-svn globbing refspecs'
+test_description='git svn globbing refspecs'
 . ./lib-git-svn.sh
 
 cat > expect.end <<EOF
@@ -46,7 +46,7 @@
 	                 "branches/*/*/src/a:refs/remotes/branches/*/*" &&
 	git config --add svn-remote.svn.tags\
 	                 "tags/*/src/a:refs/remotes/tags/*" &&
-	git-svn multi-fetch &&
+	git svn multi-fetch &&
 	git log --pretty=oneline refs/remotes/tags/end | \
 	    sed -e "s/^.\{41\}//" > output.end &&
 	test_cmp expect.end output.end &&
@@ -74,7 +74,7 @@
 		poke tags/end/src/b/readme &&
 		svn commit -m "try to try"
 	) &&
-	git-svn fetch two &&
+	git svn fetch two &&
 	test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
 	test `git rev-list refs/remotes/two/branches/v1/start | wc -l` -eq 3 &&
 	test `git rev-parse refs/remotes/two/branches/v1/start~2` = \
@@ -123,7 +123,7 @@
 	                 "branches/*/*:refs/remotes/four/branches/*/*" &&
 	git config --add svn-remote.four.tags \
 	                 "tags/*:refs/remotes/four/tags/*" &&
-	git-svn fetch four &&
+	git svn fetch four &&
 	test `git rev-list refs/remotes/four/tags/next | wc -l` -eq 5 &&
 	test `git rev-list refs/remotes/four/branches/v2/start | wc -l` -eq 3 &&
 	test `git rev-parse refs/remotes/four/branches/v2/start~2` = \
@@ -153,7 +153,7 @@
 		poke tags/end/src/b/readme &&
 		svn commit -m "try to try"
 	) &&
-	test_must_fail git-svn fetch three 2> stderr.three &&
+	test_must_fail git svn fetch three 2> stderr.three &&
 	test_cmp expect.three stderr.three
 	'
 
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index 04d2a65..a06e4c5 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -3,18 +3,18 @@
 # Copyright (c) 2007 Eric Wong
 #
 
-test_description='git-svn useSvmProps test'
+test_description='git svn useSvmProps test'
 
 . ./lib-git-svn.sh
 
 test_expect_success 'load svm repo' '
-	svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump &&
-	git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
-	git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
-	git-svn init --minimize-url -R argh -i e \
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9110/svm.dump &&
+	git svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
+	git svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
+	git svn init --minimize-url -R argh -i e \
 	  "$svnrepo"/mirror/argh/a/b/c/d/e &&
 	git config svn.useSvmProps true &&
-	git-svn fetch --all
+	git svn fetch --all
 	'
 
 uuid=161ce429-a9dd-4828-af4a-52023f968c89
@@ -22,40 +22,40 @@
 bar_url=http://mayonaise/svnrepo/bar
 test_expect_success 'verify metadata for /bar' "
 	git cat-file commit refs/remotes/bar | \
-	   grep '^git-svn-id: $bar_url@12 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@12 $uuid$' &&
 	git cat-file commit refs/remotes/bar~1 | \
-	   grep '^git-svn-id: $bar_url@11 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
 	git cat-file commit refs/remotes/bar~2 | \
-	   grep '^git-svn-id: $bar_url@10 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
 	git cat-file commit refs/remotes/bar~3 | \
-	   grep '^git-svn-id: $bar_url@9 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
 	git cat-file commit refs/remotes/bar~4 | \
-	   grep '^git-svn-id: $bar_url@6 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
 	git cat-file commit refs/remotes/bar~5 | \
-	   grep '^git-svn-id: $bar_url@1 $uuid$'
+	   grep '^${git_svn_id}: $bar_url@1 $uuid$'
 	"
 
 e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
 test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
 	git cat-file commit refs/remotes/e | \
-	   grep '^git-svn-id: $e_url@1 $uuid$'
+	   grep '^${git_svn_id}: $e_url@1 $uuid$'
 	"
 
 dir_url=http://mayonaise/svnrepo/dir
 test_expect_success 'verify metadata for /dir' "
 	git cat-file commit refs/remotes/dir | \
-	   grep '^git-svn-id: $dir_url@2 $uuid$' &&
+	   grep '^${git_svn_id}: $dir_url@2 $uuid$' &&
 	git cat-file commit refs/remotes/dir~1 | \
-	   grep '^git-svn-id: $dir_url@1 $uuid$'
+	   grep '^${git_svn_id}: $dir_url@1 $uuid$'
 	"
 
 test_expect_success 'find commit based on SVN revision number' "
-        git-svn find-rev r12 |
+        git svn find-rev r12 |
 	    grep `git rev-parse HEAD`
         "
 
 test_expect_success 'empty rebase' "
-	git-svn rebase
+	git svn rebase
 	"
 
 test_done
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
index a8d74dc..bd081c2 100755
--- a/t/t9111-git-svn-use-svnsync-props.sh
+++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -3,17 +3,17 @@
 # Copyright (c) 2007 Eric Wong
 #
 
-test_description='git-svn useSvnsyncProps test'
+test_description='git svn useSvnsyncProps test'
 
 . ./lib-git-svn.sh
 
 test_expect_success 'load svnsync repo' '
-	svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump &&
-	git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
-	git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
-	git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9111/svnsync.dump &&
+	git svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
+	git svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
+	git svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
 	git config svn.useSvnsyncProps true &&
-	git-svn fetch --all
+	git svn fetch --all
 	'
 
 uuid=161ce429-a9dd-4828-af4a-52023f968c89
@@ -21,31 +21,31 @@
 bar_url=http://mayonaise/svnrepo/bar
 test_expect_success 'verify metadata for /bar' "
 	git cat-file commit refs/remotes/bar | \
-	   grep '^git-svn-id: $bar_url@12 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@12 $uuid$' &&
 	git cat-file commit refs/remotes/bar~1 | \
-	   grep '^git-svn-id: $bar_url@11 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
 	git cat-file commit refs/remotes/bar~2 | \
-	   grep '^git-svn-id: $bar_url@10 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
 	git cat-file commit refs/remotes/bar~3 | \
-	   grep '^git-svn-id: $bar_url@9 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
 	git cat-file commit refs/remotes/bar~4 | \
-	   grep '^git-svn-id: $bar_url@6 $uuid$' &&
+	   grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
 	git cat-file commit refs/remotes/bar~5 | \
-	   grep '^git-svn-id: $bar_url@1 $uuid$'
+	   grep '^${git_svn_id}: $bar_url@1 $uuid$'
 	"
 
 e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
 test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
 	git cat-file commit refs/remotes/e | \
-	   grep '^git-svn-id: $e_url@1 $uuid$'
+	   grep '^${git_svn_id}: $e_url@1 $uuid$'
 	"
 
 dir_url=http://mayonaise/svnrepo/dir
 test_expect_success 'verify metadata for /dir' "
 	git cat-file commit refs/remotes/dir | \
-	   grep '^git-svn-id: $dir_url@2 $uuid$' &&
+	   grep '^${git_svn_id}: $dir_url@2 $uuid$' &&
 	git cat-file commit refs/remotes/dir~1 | \
-	   grep '^git-svn-id: $dir_url@1 $uuid$'
+	   grep '^${git_svn_id}: $dir_url@1 $uuid$'
 	"
 
 test_done
diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh
index d470a92..a61d671 100755
--- a/t/t9112-git-svn-md5less-file.sh
+++ b/t/t9112-git-svn-md5less-file.sh
@@ -42,6 +42,6 @@
 
 test_expect_success 'load svn dumpfile' 'svnadmin load "$rawsvnrepo" < dumpfile.svn'
 
-test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"'
-test_expect_success 'fetch revisions from svn' 'git-svn fetch'
+test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
+test_expect_success 'fetch revisions from svn' 'git svn fetch'
 test_done
diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh
index c2b24a4..e9b6128 100755
--- a/t/t9113-git-svn-dcommit-new-file.sh
+++ b/t/t9113-git-svn-dcommit-new-file.sh
@@ -8,7 +8,7 @@
 # daemon running on a users system if the test fails.
 # Not all git users will need to interact with SVN.
 
-test_description='git-svn dcommit new files over svn:// test'
+test_description='git svn dcommit new files over svn:// test'
 
 . ./lib-git-svn.sh
 
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 61d7781..17b2855 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2007 Eric Wong
 # Based on a script by Joakim Tjernlund <joakim.tjernlund@transmode.se>
 
-test_description='git-svn dcommit handles merges'
+test_description='git svn dcommit handles merges'
 
 . ./lib-git-svn.sh
 
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index f0fbd3a..9be7aef 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -3,12 +3,12 @@
 # Copyright (c) 2007 Eric Wong
 
 
-test_description='git-svn dcommit can commit renames of files with ugly names'
+test_description='git svn dcommit can commit renames of files with ugly names'
 
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
-	svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump &&
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
 	start_httpd gtk+
 	'
 
@@ -75,7 +75,7 @@
 		git svn dcommit
 	'
 
-test_expect_success 'git-svn rebase works inside a fresh-cloned repository' '
+test_expect_success 'git svn rebase works inside a fresh-cloned repository' '
 	cd test-rebase &&
 		git svn rebase &&
 		test -e test-rebase-main &&
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 4b2cc87..fd6d1d2 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2007 Eric Wong
 #
 
-test_description='git-svn log tests'
+test_description='git svn log tests'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
@@ -16,8 +16,8 @@
 		done && \
 		svn import -m test . "$svnrepo"
 		cd .. &&
-	git-svn init "$svnrepo" -T trunk -b branches -t tags &&
-	git-svn fetch &&
+	git svn init "$svnrepo" -T trunk -b branches -t tags &&
+	git svn fetch &&
 	git reset --hard trunk &&
 	echo bye >> README &&
 	git commit -a -m bye &&
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index 7a689bb..dde46cd 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2007 Eric Wong
 #
 
-test_description='git-svn init/clone tests'
+test_description='git svn init/clone tests'
 
 . ./lib-git-svn.sh
 
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index 43ceb75..7a7c128 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2007 Eric Wong
 #
 
-test_description='git-svn funky branch names'
+test_description='git svn funky branch names'
 . ./lib-git-svn.sh
 
 # Abo-Uebernahme (Bug #994)
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 5fd36a1..27dd7c2 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -2,16 +2,14 @@
 #
 # Copyright (c) 2007 David D. Kilzer
 
-test_description='git-svn info'
+test_description='git svn info'
 
 . ./lib-git-svn.sh
 
-set -e
-
 # Tested with: svn, version 1.4.4 (r25188)
-v=`svn --version | sed -n -e 's/^svn, version \(1\.4\.[0-9]\).*$/\1/p'`
+v=`svn --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'`
 case $v in
-1.4.*)
+1.[45].*)
 	;;
 *)
 	say "skipping svn-info test (SVN version: $v not supported)"
@@ -36,6 +34,8 @@
 	' "`svn info $2 | grep '^Text Last Updated:'`" "$1"
 }
 
+quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
+
 test_expect_success 'setup repository and import' '
 	mkdir info &&
 	cd info &&
@@ -47,80 +47,92 @@
 		ln -s directory symlink-directory &&
 		svn import -m "initial" . "$svnrepo" &&
 	cd .. &&
+	svn co "$svnrepo" svnwc &&
+	cd svnwc &&
+		echo foo > foo &&
+		svn add foo &&
+		svn commit -m "change outside directory" &&
+		svn update &&
+	cd .. &&
 	mkdir gitwc &&
 	cd gitwc &&
-		git-svn init "$svnrepo" &&
-		git-svn fetch &&
+		git svn init "$svnrepo" &&
+		git svn fetch &&
 	cd .. &&
-	svn co "$svnrepo" svnwc &&
-	ptouch svnwc/file gitwc/file &&
-	ptouch svnwc/directory gitwc/directory &&
-	ptouch svnwc/symlink-file gitwc/symlink-file &&
-	ptouch svnwc/symlink-directory gitwc/symlink-directory
+	ptouch gitwc/file svnwc/file &&
+	ptouch gitwc/directory svnwc/directory &&
+	ptouch gitwc/symlink-file svnwc/symlink-file &&
+	ptouch gitwc/symlink-directory svnwc/symlink-directory
 	'
 
 test_expect_success 'info' "
 	(cd svnwc; svn info) > expected.info &&
-	(cd gitwc; git-svn info) > actual.info &&
-	git-diff expected.info actual.info
+	(cd gitwc; git svn info) > actual.info &&
+	test_cmp expected.info actual.info
 	"
 
 test_expect_success 'info --url' '
-	test "$(cd gitwc; git-svn info --url)" = "$svnrepo"
+	test "$(cd gitwc; git svn info --url)" = "$quoted_svnrepo"
 	'
 
 test_expect_success 'info .' "
 	(cd svnwc; svn info .) > expected.info-dot &&
-	(cd gitwc; git-svn info .) > actual.info-dot &&
-	git-diff expected.info-dot actual.info-dot
+	(cd gitwc; git svn info .) > actual.info-dot &&
+	test_cmp expected.info-dot actual.info-dot
 	"
 
 test_expect_success 'info --url .' '
-	test "$(cd gitwc; git-svn info --url .)" = "$svnrepo"
+	test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
 	'
 
 test_expect_success 'info file' "
 	(cd svnwc; svn info file) > expected.info-file &&
-	(cd gitwc; git-svn info file) > actual.info-file &&
-	git-diff expected.info-file actual.info-file
+	(cd gitwc; git svn info file) > actual.info-file &&
+	test_cmp expected.info-file actual.info-file
 	"
 
 test_expect_success 'info --url file' '
-	test "$(cd gitwc; git-svn info --url file)" = "$svnrepo/file"
+	test "$(cd gitwc; git svn info --url file)" = "$quoted_svnrepo/file"
 	'
 
 test_expect_success 'info directory' "
 	(cd svnwc; svn info directory) > expected.info-directory &&
-	(cd gitwc; git-svn info directory) > actual.info-directory &&
-	git-diff expected.info-directory actual.info-directory
+	(cd gitwc; git svn info directory) > actual.info-directory &&
+	test_cmp expected.info-directory actual.info-directory
+	"
+
+test_expect_success 'info inside directory' "
+	(cd svnwc/directory; svn info) > expected.info-inside-directory &&
+	(cd gitwc/directory; git svn info) > actual.info-inside-directory &&
+	test_cmp expected.info-inside-directory actual.info-inside-directory
 	"
 
 test_expect_success 'info --url directory' '
-	test "$(cd gitwc; git-svn info --url directory)" = "$svnrepo/directory"
+	test "$(cd gitwc; git svn info --url directory)" = "$quoted_svnrepo/directory"
 	'
 
 test_expect_success 'info symlink-file' "
 	(cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
-	(cd gitwc; git-svn info symlink-file) > actual.info-symlink-file &&
-	git-diff expected.info-symlink-file actual.info-symlink-file
+	(cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
+	test_cmp expected.info-symlink-file actual.info-symlink-file
 	"
 
 test_expect_success 'info --url symlink-file' '
-	test "$(cd gitwc; git-svn info --url symlink-file)" \
-	     = "$svnrepo/symlink-file"
+	test "$(cd gitwc; git svn info --url symlink-file)" \
+	     = "$quoted_svnrepo/symlink-file"
 	'
 
 test_expect_success 'info symlink-directory' "
 	(cd svnwc; svn info symlink-directory) \
 		> expected.info-symlink-directory &&
-	(cd gitwc; git-svn info symlink-directory) \
+	(cd gitwc; git svn info symlink-directory) \
 		> actual.info-symlink-directory &&
-	git-diff expected.info-symlink-directory actual.info-symlink-directory
+	test_cmp expected.info-symlink-directory actual.info-symlink-directory
 	"
 
 test_expect_success 'info --url symlink-directory' '
-	test "$(cd gitwc; git-svn info --url symlink-directory)" \
-	     = "$svnrepo/symlink-directory"
+	test "$(cd gitwc; git svn info --url symlink-directory)" \
+	     = "$quoted_svnrepo/symlink-directory"
 	'
 
 test_expect_success 'info added-file' "
@@ -134,13 +146,13 @@
 		svn add added-file > /dev/null &&
 	cd .. &&
 	(cd svnwc; svn info added-file) > expected.info-added-file &&
-	(cd gitwc; git-svn info added-file) > actual.info-added-file &&
-	git-diff expected.info-added-file actual.info-added-file
+	(cd gitwc; git svn info added-file) > actual.info-added-file &&
+	test_cmp expected.info-added-file actual.info-added-file
 	"
 
 test_expect_success 'info --url added-file' '
-	test "$(cd gitwc; git-svn info --url added-file)" \
-	     = "$svnrepo/added-file"
+	test "$(cd gitwc; git svn info --url added-file)" \
+	     = "$quoted_svnrepo/added-file"
 	'
 
 test_expect_success 'info added-directory' "
@@ -155,14 +167,14 @@
 	cd .. &&
 	(cd svnwc; svn info added-directory) \
 		> expected.info-added-directory &&
-	(cd gitwc; git-svn info added-directory) \
+	(cd gitwc; git svn info added-directory) \
 		> actual.info-added-directory &&
-	git-diff expected.info-added-directory actual.info-added-directory
+	test_cmp expected.info-added-directory actual.info-added-directory
 	"
 
 test_expect_success 'info --url added-directory' '
-	test "$(cd gitwc; git-svn info --url added-directory)" \
-	     = "$svnrepo/added-directory"
+	test "$(cd gitwc; git svn info --url added-directory)" \
+	     = "$quoted_svnrepo/added-directory"
 	'
 
 test_expect_success 'info added-symlink-file' "
@@ -177,15 +189,15 @@
 	ptouch gitwc/added-symlink-file svnwc/added-symlink-file &&
 	(cd svnwc; svn info added-symlink-file) \
 		> expected.info-added-symlink-file &&
-	(cd gitwc; git-svn info added-symlink-file) \
+	(cd gitwc; git svn info added-symlink-file) \
 		> actual.info-added-symlink-file &&
-	git-diff expected.info-added-symlink-file \
+	test_cmp expected.info-added-symlink-file \
 		 actual.info-added-symlink-file
 	"
 
 test_expect_success 'info --url added-symlink-file' '
-	test "$(cd gitwc; git-svn info --url added-symlink-file)" \
-	     = "$svnrepo/added-symlink-file"
+	test "$(cd gitwc; git svn info --url added-symlink-file)" \
+	     = "$quoted_svnrepo/added-symlink-file"
 	'
 
 test_expect_success 'info added-symlink-directory' "
@@ -200,15 +212,15 @@
 	ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory &&
 	(cd svnwc; svn info added-symlink-directory) \
 		> expected.info-added-symlink-directory &&
-	(cd gitwc; git-svn info added-symlink-directory) \
+	(cd gitwc; git svn info added-symlink-directory) \
 		> actual.info-added-symlink-directory &&
-	git-diff expected.info-added-symlink-directory \
+	test_cmp expected.info-added-symlink-directory \
 		 actual.info-added-symlink-directory
 	"
 
 test_expect_success 'info --url added-symlink-directory' '
-	test "$(cd gitwc; git-svn info --url added-symlink-directory)" \
-	     = "$svnrepo/added-symlink-directory"
+	test "$(cd gitwc; git svn info --url added-symlink-directory)" \
+	     = "$quoted_svnrepo/added-symlink-directory"
 	'
 
 # The next few tests replace the "Text Last Updated" value with a
@@ -226,15 +238,15 @@
 	(cd svnwc; svn info file) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		> expected.info-deleted-file &&
-	(cd gitwc; git-svn info file) |
+	(cd gitwc; git svn info file) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		> actual.info-deleted-file &&
-	git-diff expected.info-deleted-file actual.info-deleted-file
+	test_cmp expected.info-deleted-file actual.info-deleted-file
 	"
 
 test_expect_success 'info --url file (deleted)' '
-	test "$(cd gitwc; git-svn info --url file)" \
-	     = "$svnrepo/file"
+	test "$(cd gitwc; git svn info --url file)" \
+	     = "$quoted_svnrepo/file"
 	'
 
 test_expect_success 'info deleted-directory' "
@@ -247,15 +259,15 @@
 	(cd svnwc; svn info directory) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		> expected.info-deleted-directory &&
-	(cd gitwc; git-svn info directory) |
+	(cd gitwc; git svn info directory) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		> actual.info-deleted-directory &&
-	git-diff expected.info-deleted-directory actual.info-deleted-directory
+	test_cmp expected.info-deleted-directory actual.info-deleted-directory
 	"
 
 test_expect_success 'info --url directory (deleted)' '
-	test "$(cd gitwc; git-svn info --url directory)" \
-	     = "$svnrepo/directory"
+	test "$(cd gitwc; git svn info --url directory)" \
+	     = "$quoted_svnrepo/directory"
 	'
 
 test_expect_success 'info deleted-symlink-file' "
@@ -268,16 +280,16 @@
 	(cd svnwc; svn info symlink-file) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		> expected.info-deleted-symlink-file &&
-	(cd gitwc; git-svn info symlink-file) |
+	(cd gitwc; git svn info symlink-file) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		> actual.info-deleted-symlink-file &&
-	git-diff expected.info-deleted-symlink-file \
+	test_cmp expected.info-deleted-symlink-file \
 		 actual.info-deleted-symlink-file
 	"
 
 test_expect_success 'info --url symlink-file (deleted)' '
-	test "$(cd gitwc; git-svn info --url symlink-file)" \
-	     = "$svnrepo/symlink-file"
+	test "$(cd gitwc; git svn info --url symlink-file)" \
+	     = "$quoted_svnrepo/symlink-file"
 	'
 
 test_expect_success 'info deleted-symlink-directory' "
@@ -290,16 +302,16 @@
 	(cd svnwc; svn info symlink-directory) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		 > expected.info-deleted-symlink-directory &&
-	(cd gitwc; git-svn info symlink-directory) |
+	(cd gitwc; git svn info symlink-directory) |
 	sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
 		 > actual.info-deleted-symlink-directory &&
-	git-diff expected.info-deleted-symlink-directory \
+	test_cmp expected.info-deleted-symlink-directory \
 		 actual.info-deleted-symlink-directory
 	"
 
 test_expect_success 'info --url symlink-directory (deleted)' '
-	test "$(cd gitwc; git-svn info --url symlink-directory)" \
-	     = "$svnrepo/symlink-directory"
+	test "$(cd gitwc; git svn info --url symlink-directory)" \
+	     = "$quoted_svnrepo/symlink-directory"
 	'
 
 # NOTE: git does not have the concept of replaced objects,
@@ -307,82 +319,59 @@
 
 test_expect_success 'info unknown-file' "
 	echo two > gitwc/unknown-file &&
-	cp gitwc/unknown-file svnwc/unknown-file &&
-	ptouch gitwc/unknown-file svnwc/unknown-file &&
-	(cd svnwc; svn info unknown-file) 2> expected.info-unknown-file &&
-	(cd gitwc; git-svn info unknown-file) 2> actual.info-unknown-file &&
-	git-diff expected.info-unknown-file actual.info-unknown-file
+	(cd gitwc; test_must_fail git svn info unknown-file) \
+		 2> actual.info-unknown-file &&
+	grep unknown-file actual.info-unknown-file
 	"
 
 test_expect_success 'info --url unknown-file' '
-	test -z "$(cd gitwc; git-svn info --url unknown-file \
-			2> ../actual.info--url-unknown-file)" &&
-	git-diff expected.info-unknown-file actual.info--url-unknown-file
+	echo two > gitwc/unknown-file &&
+	(cd gitwc; test_must_fail git svn info --url unknown-file) \
+		 2> actual.info-url-unknown-file &&
+	grep unknown-file actual.info-url-unknown-file
 	'
 
 test_expect_success 'info unknown-directory' "
 	mkdir gitwc/unknown-directory svnwc/unknown-directory &&
-	ptouch gitwc/unknown-directory svnwc/unknown-directory &&
-	touch gitwc/unknown-directory/.placeholder &&
-	(cd svnwc; svn info unknown-directory) \
-		2> expected.info-unknown-directory &&
-	(cd gitwc; git-svn info unknown-directory) \
-		2> actual.info-unknown-directory &&
-	git-diff expected.info-unknown-directory actual.info-unknown-directory
+	(cd gitwc; test_must_fail git svn info unknown-directory) \
+		 2> actual.info-unknown-directory &&
+	grep unknown-directory actual.info-unknown-directory
 	"
 
 test_expect_success 'info --url unknown-directory' '
-	test -z "$(cd gitwc; git-svn info --url unknown-directory \
-			2> ../actual.info--url-unknown-directory)" &&
-	git-diff expected.info-unknown-directory \
-		 actual.info--url-unknown-directory
+	(cd gitwc; test_must_fail git svn info --url unknown-directory) \
+		 2> actual.info-url-unknown-directory &&
+	grep unknown-directory actual.info-url-unknown-directory
 	'
 
 test_expect_success 'info unknown-symlink-file' "
 	cd gitwc &&
 		ln -s unknown-file unknown-symlink-file &&
 	cd .. &&
-	cd svnwc &&
-		ln -s unknown-file unknown-symlink-file &&
-	cd .. &&
-	ptouch gitwc/unknown-symlink-file svnwc/unknown-symlink-file &&
-	(cd svnwc; svn info unknown-symlink-file) \
-		2> expected.info-unknown-symlink-file &&
-	(cd gitwc; git-svn info unknown-symlink-file) \
-		2> actual.info-unknown-symlink-file &&
-	git-diff expected.info-unknown-symlink-file \
-		 actual.info-unknown-symlink-file
+	(cd gitwc; test_must_fail git svn info unknown-symlink-file) \
+		 2> actual.info-unknown-symlink-file &&
+	grep unknown-symlink-file actual.info-unknown-symlink-file
 	"
 
 test_expect_success 'info --url unknown-symlink-file' '
-	test -z "$(cd gitwc; git-svn info --url unknown-symlink-file \
-			2> ../actual.info--url-unknown-symlink-file)" &&
-	git-diff expected.info-unknown-symlink-file \
-		 actual.info--url-unknown-symlink-file
+	(cd gitwc; test_must_fail git svn info --url unknown-symlink-file) \
+		 2> actual.info-url-unknown-symlink-file &&
+	grep unknown-symlink-file actual.info-url-unknown-symlink-file
 	'
 
 test_expect_success 'info unknown-symlink-directory' "
 	cd gitwc &&
 		ln -s unknown-directory unknown-symlink-directory &&
 	cd .. &&
-	cd svnwc &&
-		ln -s unknown-directory unknown-symlink-directory &&
-	cd .. &&
-	ptouch gitwc/unknown-symlink-directory \
-	       svnwc/unknown-symlink-directory &&
-	(cd svnwc; svn info unknown-symlink-directory) \
-		2> expected.info-unknown-symlink-directory &&
-	(cd gitwc; git-svn info unknown-symlink-directory) \
-		2> actual.info-unknown-symlink-directory &&
-	git-diff expected.info-unknown-symlink-directory \
-		 actual.info-unknown-symlink-directory
+	(cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
+		 2> actual.info-unknown-symlink-directory &&
+	grep unknown-symlink-directory actual.info-unknown-symlink-directory
 	"
 
 test_expect_success 'info --url unknown-symlink-directory' '
-	test -z "$(cd gitwc; git-svn info --url unknown-symlink-directory \
-			2> ../actual.info--url-unknown-symlink-directory)" &&
-	git-diff expected.info-unknown-symlink-directory \
-		 actual.info--url-unknown-symlink-directory
+	(cd gitwc; test_must_fail git svn info --url unknown-symlink-directory) \
+		 2> actual.info-url-unknown-symlink-directory &&
+	grep unknown-symlink-directory actual.info-url-unknown-symlink-directory
 	'
 
 test_done
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index 5979e13..ef2c052 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2008 Kevin Ballard
 #
 
-test_description='git-svn clone with percent escapes'
+test_description='git svn clone with percent escapes'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svnrepo' '
@@ -21,7 +21,7 @@
 	test_expect_success 'test clone with percent escapes' '
 		git svn clone "$svnrepo/pr%20ject" clone &&
 		cd clone &&
-			git rev-parse refs/remotes/git-svn &&
+			git rev-parse refs/${remotes_git_svn} &&
 		cd ..
 	'
 fi
diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh
index 99230b0..000cad3 100755
--- a/t/t9121-git-svn-fetch-renamed-dir.sh
+++ b/t/t9121-git-svn-fetch-renamed-dir.sh
@@ -3,12 +3,12 @@
 # Copyright (c) 2008 Santhosh Kumar Mani
 
 
-test_description='git-svn can fetch renamed directories'
+test_description='git svn can fetch renamed directories'
 
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with renamed directory' '
-	svnadmin load -q "$rawsvnrepo" < ../t9121/renamed-dir.dump
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9121/renamed-dir.dump
 	'
 
 test_expect_success 'init and fetch repository' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 1190576..1b1cf47 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -13,7 +13,7 @@
 	)
 '
 
-test_expect_success 'interact with it via git-svn' '
+test_expect_success 'interact with it via git svn' '
 	mkdir work.git &&
 	(
 		cd work.git &&
diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
index c18878f..cf04152 100755
--- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh
+++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
@@ -3,21 +3,21 @@
 # Copyright (c) 2008 Jan Krüger
 #
 
-test_description='git-svn respects rewriteRoot during rebuild'
+test_description='git svn respects rewriteRoot during rebuild'
 
 . ./lib-git-svn.sh
 
 mkdir import
 cd import
 	touch foo
-	svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
+	svn import -m 'import for git svn' . "$svnrepo" >/dev/null
 cd ..
 rm -rf import
 
 test_expect_success 'init, fetch and checkout repository' '
 	git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" &&
 	git svn fetch
-	git checkout -b mybranch remotes/git-svn
+	git checkout -b mybranch ${remotes_git_svn}
 	'
 
 test_expect_success 'remove rev_map' '
diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh
index 8223c59..263dbf5 100755
--- a/t/t9124-git-svn-dcommit-auto-props.sh
+++ b/t/t9124-git-svn-dcommit-auto-props.sh
@@ -2,7 +2,7 @@
 #
 # Copyright (c) 2008 Brad King
 
-test_description='git-svn dcommit honors auto-props'
+test_description='git svn dcommit honors auto-props'
 
 . ./lib-git-svn.sh
 
@@ -16,26 +16,24 @@
 EOF
 }
 
-test_expect_success 'initialize git-svn' '
+test_expect_success 'initialize git svn' '
 	mkdir import &&
 	(
 		cd import &&
 		echo foo >foo &&
-		svn import -m "import for git-svn" . "$svnrepo"
+		svn import -m "import for git svn" . "$svnrepo"
 	) &&
 	rm -rf import &&
-	git-svn init "$svnrepo"
-	git-svn fetch
+	git svn init "$svnrepo"
+	git svn fetch
 '
 
 test_expect_success 'enable auto-props config' '
-	cd "$gittestrepo" &&
 	mkdir user &&
 	generate_auto_props yes >user/config
 '
 
 test_expect_success 'add files matching auto-props' '
-	cd "$gittestrepo" &&
 	echo "#!$SHELL_PATH" >exec1.sh &&
 	chmod +x exec1.sh &&
 	echo "hello" >hello.txt &&
@@ -46,12 +44,10 @@
 '
 
 test_expect_success 'disable auto-props config' '
-	cd "$gittestrepo" &&
 	generate_auto_props no >user/config
 '
 
 test_expect_success 'add files matching disabled auto-props' '
-	cd "$gittestrepo" &&
 	echo "#$SHELL_PATH" >exec2.sh &&
 	chmod +x exec2.sh &&
 	echo "world" >world.txt &&
@@ -62,6 +58,7 @@
 '
 
 test_expect_success 'check resulting svn repository' '
+(
 	mkdir work &&
 	cd work &&
 	svn co "$svnrepo" &&
@@ -81,6 +78,24 @@
 	test "x$(svn propget svn:mime-type world.txt)" = "x" &&
 	test "x$(svn propget svn:eol-style world.txt)" = "x" &&
 	test "x$(svn propget svn:mime-type zot)" = "x"
+)
+'
+
+test_expect_success 'check renamed file' '
+	test -d user &&
+	generate_auto_props yes > user/config &&
+	git mv foo foo.sh &&
+	git commit -m "foo => foo.sh" &&
+	git svn dcommit --config-dir=user &&
+	(
+		cd work/svnrepo &&
+		svn up &&
+		test ! -e foo &&
+		test -e foo.sh &&
+		test "x$(svn propget svn:mime-type foo.sh)" = \
+		     "xapplication/x-shellscript" &&
+		test "x$(svn propget svn:eol-style foo.sh)" = "xLF"
+	)
 '
 
 test_done
diff --git a/t/t9125-git-svn-multi-glob-branch-names.sh b/t/t9125-git-svn-multi-glob-branch-names.sh
index 6b62b52..475c751 100755
--- a/t/t9125-git-svn-multi-glob-branch-names.sh
+++ b/t/t9125-git-svn-multi-glob-branch-names.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Copyright (c) 2008 Marcus Griep
 
-test_description='git-svn multi-glob branch names'
+test_description='git svn multi-glob branch names'
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svnrepo' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
new file mode 100755
index 0000000..87696a9
--- /dev/null
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Deskin Miller
+#
+
+test_description='git svn partial-rebuild tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+	mkdir import &&
+	(
+		cd import &&
+		mkdir trunk branches tags &&
+		cd trunk &&
+		echo foo > foo &&
+		cd .. &&
+		svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+		svn copy "$svnrepo"/trunk "$svnrepo"/branches/a \
+			-m "created branch a" &&
+		cd .. &&
+		rm -rf import &&
+		svn co "$svnrepo"/trunk trunk &&
+		cd trunk &&
+		echo bar >> foo &&
+		svn ci -m "updated trunk" &&
+		cd .. &&
+		svn co "$svnrepo"/branches/a a &&
+		cd a &&
+		echo baz >> a &&
+		svn add a &&
+		svn ci -m "updated a" &&
+		cd .. &&
+		git svn init --stdlayout "$svnrepo"
+	)
+'
+
+test_expect_success 'import an early SVN revision into git' '
+	git svn fetch -r1:2
+'
+
+test_expect_success 'make full git mirror of SVN' '
+	mkdir mirror &&
+	(
+		cd mirror &&
+		git init &&
+		git svn init --stdlayout "$svnrepo" &&
+		git svn fetch &&
+		cd ..
+	)
+'
+
+test_expect_success 'fetch from git mirror and partial-rebuild' '
+	git config --add remote.origin.url "file://$PWD/mirror/.git" &&
+	git config --add remote.origin.fetch refs/remotes/*:refs/remotes/* &&
+	git fetch origin &&
+	git svn fetch
+'
+
+test_done
diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh
new file mode 100755
index 0000000..252daa7
--- /dev/null
+++ b/t/t9128-git-svn-cmd-branch.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Deskin Miller
+#
+
+test_description='git svn partial-rebuild tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+	mkdir import &&
+	(
+		cd import &&
+		mkdir trunk branches tags &&
+		cd trunk &&
+		echo foo > foo &&
+		cd .. &&
+		svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+		cd .. &&
+		rm -rf import &&
+		svn co "$svnrepo"/trunk trunk &&
+		cd trunk &&
+		echo bar >> foo &&
+		svn ci -m "updated trunk" &&
+		cd .. &&
+		rm -rf trunk
+	)
+'
+
+test_expect_success 'import into git' '
+	git svn init --stdlayout "$svnrepo" &&
+	git svn fetch &&
+	git checkout remotes/trunk
+'
+
+test_expect_success 'git svn branch tests' '
+	git svn branch a &&
+	base=$(git rev-parse HEAD:) &&
+	test $base = $(git rev-parse remotes/a:) &&
+	git svn branch -m "created branch b blah" b &&
+	test $base = $(git rev-parse remotes/b:) &&
+	test_must_fail git branch -m "no branchname" &&
+	git svn branch -n c &&
+	test_must_fail git rev-parse remotes/c &&
+	test_must_fail git svn branch a &&
+	git svn branch -t tag1 &&
+	test $base = $(git rev-parse remotes/tags/tag1:) &&
+	git svn branch --tag tag2 &&
+	test $base = $(git rev-parse remotes/tags/tag2:) &&
+	git svn tag tag3 &&
+	test $base = $(git rev-parse remotes/tags/tag3:) &&
+	git svn tag -m "created tag4 foo" tag4 &&
+	test $base = $(git rev-parse remotes/tags/tag4:) &&
+	test_must_fail git svn tag -m "no tagname" &&
+	git svn tag -n tag5 &&
+	test_must_fail git rev-parse remotes/tags/tag5 &&
+	test_must_fail git svn tag tag1
+'
+
+test_expect_success 'branch uses correct svn-remote' '
+	(svn co "$svnrepo" svn &&
+	cd svn &&
+	mkdir mirror &&
+	svn add mirror &&
+	svn copy trunk mirror/ &&
+	svn copy tags mirror/ &&
+	svn copy branches mirror/ &&
+	svn ci -m "made mirror" ) &&
+	rm -rf svn &&
+	git svn init -s -R mirror --prefix=mirror/ "$svnrepo"/mirror &&
+	git svn fetch -R mirror &&
+	git checkout mirror/trunk &&
+	base=$(git rev-parse HEAD:) &&
+	git svn branch -m "branch in mirror" d &&
+	test $base = $(git rev-parse remotes/mirror/d:) &&
+	test_must_fail git rev-parse remotes/d
+'
+
+test_done
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
new file mode 100755
index 0000000..3200ab3
--- /dev/null
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Eric Wong
+
+test_description='git svn honors i18n.commitEncoding in config'
+
+. ./lib-git-svn.sh
+
+compare_git_head_with () {
+	nr=`wc -l < "$1"`
+	a=7
+	b=$(($a + $nr - 1))
+	git cat-file commit HEAD | sed -ne "$a,${b}p" >current &&
+	test_cmp current "$1"
+}
+
+compare_svn_head_with () {
+	# extract just the log message and strip out committer info.
+	# don't use --limit here since svn 1.1.x doesn't have it,
+	LC_ALL=en_US.UTF-8 svn log `git svn info --url` | perl -w -e '
+		use bytes;
+		$/ = ("-"x72) . "\n";
+		my @x = <STDIN>;
+		@x = split(/\n/, $x[1]);
+		splice(@x, 0, 2);
+		$x[-1] = "";
+		print join("\n", @x);
+	' > current &&
+	test_cmp current "$1"
+}
+
+for H in ISO-8859-1 EUCJP ISO-2022-JP
+do
+	test_expect_success "$H setup" '
+		mkdir $H &&
+		svn import -m "$H test" $H "$svnrepo"/$H &&
+		git svn clone "$svnrepo"/$H $H
+	'
+done
+
+for H in ISO-8859-1 EUCJP ISO-2022-JP
+do
+	test_expect_success "$H commit on git side" '
+	(
+		cd $H &&
+		git config i18n.commitencoding $H &&
+		git checkout -b t refs/remotes/git-svn &&
+		echo $H >F &&
+		git add F &&
+		git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+		E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
+		test "z$E" = "z$H"
+		compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
+	)
+	'
+done
+
+for H in ISO-8859-1 EUCJP ISO-2022-JP
+do
+	test_expect_success "$H dcommit to svn" '
+	(
+		cd $H &&
+		git svn dcommit &&
+		git cat-file commit HEAD | grep git-svn-id: &&
+		E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
+		test "z$E" = "z$H" &&
+		compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
+	)
+	'
+done
+
+if locale -a |grep -q en_US.utf8; then
+	test_set_prereq UTF8
+else
+	say "UTF-8 locale not available, test skipped"
+fi
+
+test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
+	(
+		cd ISO-8859-1 &&
+		compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
+	)
+'
+
+for H in EUCJP ISO-2022-JP
+do
+	test_expect_success UTF8 "$H should match UTF-8 in svn" '
+		(
+			cd $H &&
+			compare_svn_head_with "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
+		)
+	'
+done
+
+test_done
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
new file mode 100755
index 0000000..b8fb277
--- /dev/null
+++ b/t/t9130-git-svn-authors-file.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Eric Wong
+#
+
+test_description='git svn authors file tests'
+
+. ./lib-git-svn.sh
+
+cat > svn-authors <<EOF
+aa = AAAAAAA AAAAAAA <aa@example.com>
+bb = BBBBBBB BBBBBBB <bb@example.com>
+EOF
+
+test_expect_success 'setup svnrepo' '
+	for i in aa bb cc dd
+	do
+		svn mkdir -m $i --username $i "$svnrepo"/$i
+	done
+	'
+
+test_expect_success 'start import with incomplete authors file' '
+	! git svn clone --authors-file=svn-authors "$svnrepo" x
+	'
+
+test_expect_success 'imported 2 revisions successfully' '
+	(
+		cd x
+		test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 2 &&
+		git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+		  grep "^author BBBBBBB BBBBBBB <bb@example\.com> " &&
+		git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+		  grep "^author AAAAAAA AAAAAAA <aa@example\.com> "
+	)
+	'
+
+cat >> svn-authors <<EOF
+cc = CCCCCCC CCCCCCC <cc@example.com>
+dd = DDDDDDD DDDDDDD <dd@example.com>
+EOF
+
+test_expect_success 'continues to import once authors have been added' '
+	(
+		cd x
+		git svn fetch --authors-file=../svn-authors &&
+		test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 4 &&
+		git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+		  grep "^author DDDDDDD DDDDDDD <dd@example\.com> " &&
+		git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+		  grep "^author CCCCCCC CCCCCCC <cc@example\.com> "
+	)
+	'
+
+test_expect_success 'authors-file against globs' '
+	svn mkdir -m globs --username aa \
+	  "$svnrepo"/aa/trunk "$svnrepo"/aa/branches "$svnrepo"/aa/tags &&
+	git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
+	for i in bb ee cc
+	do
+		branch="aa/branches/$i"
+		svn mkdir -m "$branch" --username $i "$svnrepo/$branch"
+	done
+	'
+
+test_expect_success 'fetch fails on ee' '
+	( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+	'
+
+tmp_config_get () {
+	GIT_CONFIG=.git/svn/.metadata git config --get "$1"
+}
+
+test_expect_success 'failure happened without negative side effects' '
+	(
+		cd aa-work &&
+		test 6 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+		test 6 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+	)
+	'
+
+cat >> svn-authors <<EOF
+ee = EEEEEEE EEEEEEE <ee@example.com>
+EOF
+
+test_expect_success 'fetch continues after authors-file is fixed' '
+	(
+		cd aa-work &&
+		git svn fetch --authors-file=../svn-authors &&
+		test 8 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+		test 8 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+	)
+	'
+
+test_done
diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh
new file mode 100755
index 0000000..9a24a65
--- /dev/null
+++ b/t/t9131-git-svn-empty-symlink.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='test that git handles an svn repository with empty symlinks'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile' '
+	svnadmin load "$rawsvnrepo" <<EOF
+SVN-fs-dump-format-version: 2
+
+UUID: 60780f9a-7df5-43b4-83ab-60e2c0673ef7
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-11-26T07:17:27.590577Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 4
+test
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-26T07:18:03.511836Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 33
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 13
+bar => doink
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-27T03:55:31.601672Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 92ca4fe7a9721f877f765c252dcd66c9
+Content-length: 10
+
+link doink
+
+EOF
+'
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
+test_expect_success 'enable broken symlink workaround' \
+  '(cd x && git config svn.brokenSymlinkWorkaround true)'
+test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+		'(cd x && git svn rebase)'
+test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -L x/bar'
+
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
+test_expect_success 'disable broken symlink workaround' \
+  '(cd y && git config svn.brokenSymlinkWorkaround false)'
+test_expect_success '"bar" is an empty file' 'test -f y/bar && ! test -s y/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+		'(cd y && git svn rebase)'
+test_expect_success '"bar" does not become a symlink' '! test -L y/bar'
+
+# svn.brokenSymlinkWorkaround is unset
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" z'
+test_expect_success '"bar" is an empty file' 'test -f z/bar && ! test -s z/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+		'(cd z && git svn rebase)'
+test_expect_success '"bar" does not become a symlink' '! test -L z/bar'
+
+
+test_done
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
new file mode 100755
index 0000000..6c4c90b
--- /dev/null
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='test that git handles an svn repository with empty symlinks'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile' '
+	svnadmin load "$rawsvnrepo" <<EOF
+SVN-fs-dump-format-version: 2
+
+UUID: 60780f9a-7df5-43b4-83ab-60e2c0673ef7
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-11-26T07:17:27.590577Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 4
+test
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-26T07:18:03.511836Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 4
+Text-content-md5: 912ec803b2ce49e4a541068d495ab570
+Content-length: 37
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+asdf
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 13
+bar => doink
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-27T03:55:31.601672Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 92ca4fe7a9721f877f765c252dcd66c9
+Content-length: 10
+
+link doink
+
+EOF
+'
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
+
+test_expect_success SYMLINKS '"bar" is a symlink that points to "asdf"' '
+	test -L x/bar &&
+	(cd x && test xasdf = x"`git cat-file blob HEAD:bar`")
+'
+
+test_expect_success 'get "bar" => symlink fix from svn' '
+	(cd x && git svn rebase)
+'
+
+test_expect_success SYMLINKS '"bar" remains a proper symlink' '
+	test -L x/bar &&
+	(cd x && test xdoink = x"`git cat-file blob HEAD:bar`")
+'
+
+test_done
diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
new file mode 100755
index 0000000..893f57e
--- /dev/null
+++ b/t/t9133-git-svn-nested-git-repo.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup repo with a git repo inside it' '
+	svn co "$svnrepo" s &&
+	(
+		cd s &&
+		git init &&
+		test -f .git/HEAD &&
+		> .git/a &&
+		echo a > a &&
+		svn add .git a &&
+		svn commit -m "create a nested git repo" &&
+		svn up &&
+		echo hi >> .git/a &&
+		svn commit -m "modify .git/a" &&
+		svn up
+	)
+'
+
+test_expect_success 'clone an SVN repo containing a git repo' '
+	git svn clone "$svnrepo" g &&
+	echo a > expect &&
+	test_cmp expect g/a
+'
+
+test_expect_success 'SVN-side change outside of .git' '
+	(
+		cd s &&
+		echo b >> a &&
+		svn commit -m "SVN-side change outside of .git" &&
+		svn up &&
+		svn log -v | fgrep "SVN-side change outside of .git"
+	)
+'
+
+test_expect_success 'update git svn-cloned repo' '
+	(
+		cd g &&
+		git svn rebase &&
+		echo a > expect &&
+		echo b >> expect &&
+		test_cmp a expect &&
+		rm expect
+	)
+'
+
+test_expect_success 'SVN-side change inside of .git' '
+	(
+		cd s &&
+		git add a &&
+		git commit -m "add a inside an SVN repo" &&
+		git log &&
+		svn add --force .git &&
+		svn commit -m "SVN-side change inside of .git" &&
+		svn up &&
+		svn log -v | fgrep "SVN-side change inside of .git"
+	)
+'
+
+test_expect_success 'update git svn-cloned repo' '
+	(
+		cd g &&
+		git svn rebase &&
+		echo a > expect &&
+		echo b >> expect &&
+		test_cmp a expect &&
+		rm expect
+	)
+'
+
+test_expect_success 'SVN-side change in and out of .git' '
+	(
+		cd s &&
+		echo c >> a &&
+		git add a &&
+		git commit -m "add a inside an SVN repo" &&
+		svn commit -m "SVN-side change in and out of .git" &&
+		svn up &&
+		svn log -v | fgrep "SVN-side change in and out of .git"
+	)
+'
+
+test_expect_success 'update git svn-cloned repo again' '
+	(
+		cd g &&
+		git svn rebase &&
+		echo a > expect &&
+		echo b >> expect &&
+		echo c >> expect &&
+		test_cmp a expect &&
+		rm expect
+	)
+'
+
+test_done
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
new file mode 100755
index 0000000..71fdc4a
--- /dev/null
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Vitaly Shukela
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+	svn co "$svnrepo" s &&
+	(
+		cd s &&
+		mkdir qqq www &&
+		echo test_qqq > qqq/test_qqq.txt &&
+		echo test_www > www/test_www.txt &&
+		svn add qqq &&
+		svn add www &&
+		svn commit -m "create some files" &&
+		svn up &&
+		echo hi >> www/test_www.txt &&
+		svn commit -m "modify www/test_www.txt" &&
+		svn up
+	)
+'
+
+test_expect_success 'clone an SVN repository with ignored www directory' '
+	git svn clone --ignore-paths="^www" "$svnrepo" g &&
+	echo test_qqq > expect &&
+	for i in g/*/*.txt; do cat $i >> expect2; done &&
+	test_cmp expect expect2
+'
+
+test_expect_success 'init+fetch an SVN repository with ignored www directory' '
+	git svn init "$svnrepo" c &&
+	( cd c && git svn fetch --ignore-paths="^www" ) &&
+	rm expect2 &&
+	echo test_qqq > expect &&
+	for i in c/*/*.txt; do cat $i >> expect2; done &&
+	test_cmp expect expect2
+'
+
+test_expect_success 'verify ignore-paths config saved by clone' '
+	(
+	    cd g &&
+	    git config --get svn-remote.svn.ignore-paths | fgrep "www"
+	)
+'
+
+test_expect_success 'SVN-side change outside of www' '
+	(
+		cd s &&
+		echo b >> qqq/test_qqq.txt &&
+		svn commit -m "SVN-side change outside of www" &&
+		svn up &&
+		svn log -v | fgrep "SVN-side change outside of www"
+	)
+'
+
+test_expect_success 'update git svn-cloned repo (config ignore)' '
+	(
+		cd g &&
+		git svn rebase &&
+		printf "test_qqq\nb\n" > expect &&
+		for i in */*.txt; do cat $i >> expect2; done &&
+		test_cmp expect2 expect &&
+		rm expect expect2
+	)
+'
+
+test_expect_success 'update git svn-cloned repo (option ignore)' '
+	(
+		cd c &&
+		git svn rebase --ignore-paths="^www" &&
+		printf "test_qqq\nb\n" > expect &&
+		for i in */*.txt; do cat $i >> expect2; done &&
+		test_cmp expect2 expect &&
+		rm expect expect2
+	)
+'
+
+test_expect_success 'SVN-side change inside of ignored www' '
+	(
+		cd s &&
+		echo zaq >> www/test_www.txt
+		svn commit -m "SVN-side change inside of www/test_www.txt" &&
+		svn up &&
+		svn log -v | fgrep "SVN-side change inside of www/test_www.txt"
+	)
+'
+
+test_expect_success 'update git svn-cloned repo (config ignore)' '
+	(
+		cd g &&
+		git svn rebase &&
+		printf "test_qqq\nb\n" > expect &&
+		for i in */*.txt; do cat $i >> expect2; done &&
+		test_cmp expect2 expect &&
+		rm expect expect2
+	)
+'
+
+test_expect_success 'update git svn-cloned repo (option ignore)' '
+	(
+		cd c &&
+		git svn rebase --ignore-paths="^www" &&
+		printf "test_qqq\nb\n" > expect &&
+		for i in */*.txt; do cat $i >> expect2; done &&
+		test_cmp expect2 expect &&
+		rm expect expect2
+	)
+'
+
+test_expect_success 'SVN-side change in and out of ignored www' '
+	(
+		cd s &&
+		echo cvf >> www/test_www.txt
+		echo ygg >> qqq/test_qqq.txt
+		svn commit -m "SVN-side change in and out of ignored www" &&
+		svn up &&
+		svn log -v | fgrep "SVN-side change in and out of ignored www"
+	)
+'
+
+test_expect_success 'update git svn-cloned repo again (config ignore)' '
+	(
+		cd g &&
+		git svn rebase &&
+		printf "test_qqq\nb\nygg\n" > expect &&
+		for i in */*.txt; do cat $i >> expect2; done &&
+		test_cmp expect2 expect &&
+		rm expect expect2
+	)
+'
+
+test_expect_success 'update git svn-cloned repo again (option ignore)' '
+	(
+		cd c &&
+		git svn rebase --ignore-paths="^www" &&
+		printf "test_qqq\nb\nygg\n" > expect &&
+		for i in */*.txt; do cat $i >> expect2; done &&
+		test_cmp expect2 expect &&
+		rm expect expect2
+	)
+'
+
+test_done
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
new file mode 100755
index 0000000..03705fa
--- /dev/null
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+test_description='test moved svn branch with missing empty files'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile'  '
+	svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9135/svn.dump"
+	'
+
+test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
+
+test_expect_success 'test that b1 exists and is empty' '
+	(cd x && test -f b1 && ! test -s b1)
+	'
+
+test_done
diff --git a/t/t9135/svn.dump b/t/t9135/svn.dump
new file mode 100644
index 0000000..b51c0cc
--- /dev/null
+++ b/t/t9135/svn.dump
@@ -0,0 +1,192 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1f80e919-e9e3-4d80-a3ae-d9f21095e27b
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-02-10T19:23:16.424027Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 20
+init standard layout
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:17.195072Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 18
+branch-b off trunk
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:19.160095Z
+PROPS-END
+
+Node-path: branches/branch-b
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Revision-number: 3
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 17
+add empty file b1
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:20.194568Z
+PROPS-END
+
+Node-path: branches/branch-b/b1
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 4
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 8
+branch-c
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.169100Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: trunk
+
+
+Revision-number: 5
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 23
+oops, wrong branchpoint
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.253557Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 127
+Content-length: 127
+
+K 7
+svn:log
+V 24
+branch-c off of branch-b
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.314659Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: branches/branch-b
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
diff --git a/t/t9136-git-svn-recreated-branch-empty-file.sh b/t/t9136-git-svn-recreated-branch-empty-file.sh
new file mode 100755
index 0000000..733d16e
--- /dev/null
+++ b/t/t9136-git-svn-recreated-branch-empty-file.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='test recreated svn branch with empty files'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile'  '
+	svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9136/svn.dump"
+	'
+
+test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
+
+test_done
diff --git a/t/t9136/svn.dump b/t/t9136/svn.dump
new file mode 100644
index 0000000..6b1ce0b
--- /dev/null
+++ b/t/t9136/svn.dump
@@ -0,0 +1,192 @@
+SVN-fs-dump-format-version: 2
+
+UUID: eecae021-8f16-48da-969d-79beb8ae6ea5
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-02-22T00:50:56.292890Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 4
+init
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:57.192384Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: tags
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 105
+Content-length: 105
+
+K 7
+svn:log
+V 3
+1.0
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.124724Z
+PROPS-END
+
+Node-path: tags/1.0
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 3
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 9
+1.0.1-bad
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.151727Z
+PROPS-END
+
+Node-path: tags/1.0.1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: tags/1.0
+
+
+Revision-number: 4
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 9
+Wrong tag
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.167427Z
+PROPS-END
+
+Node-path: tags/1.0.1
+Node-action: delete
+
+
+Revision-number: 5
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 10
+1.0-branch
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.184498Z
+PROPS-END
+
+Node-path: branches/1.0
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: tags/1.0
+
+
+Revision-number: 6
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 10
+1.0.1-good
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.200695Z
+PROPS-END
+
+Node-path: tags/1.0.1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: branches/1.0
+
+
diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
similarity index 97%
rename from t/t9106-git-svn-dcommit-clobber-series.sh
rename to t/t9137-git-svn-dcommit-clobber-series.sh
index bc37db9..fd18501 100755
--- a/t/t9106-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2007 Eric Wong
-test_description='git-svn dcommit clobber series'
+test_description='git svn dcommit clobber series'
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 3e32e84..56b7c06 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -6,12 +6,16 @@
 
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping git cvsexportcommit tests, perl not available'
+	test_done
+fi
+
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-cvsexportcommit tests, cvs not found' :
+    say 'skipping git cvsexportcommit tests, cvs not found'
     test_done
-    exit
 fi
 
 CVSROOT=$(pwd)/cvsroot
@@ -45,8 +49,8 @@
     'mkdir A B C D E F &&
      echo hello1 >A/newfile1.txt &&
      echo hello2 >B/newfile2.txt &&
-     cp ../test9200a.png C/newfile3.png &&
-     cp ../test9200a.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png &&
      git add A/newfile1.txt &&
      git add B/newfile2.txt &&
      git add C/newfile3.png &&
@@ -71,8 +75,8 @@
      rm -f B/newfile2.txt &&
      rm -f C/newfile3.png &&
      echo Hello5  >E/newfile5.txt &&
-     cp ../test9200b.png D/newfile4.png &&
-     cp ../test9200a.png F/newfile6.png &&
+     cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png &&
      git add E/newfile5.txt &&
      git add F/newfile6.png &&
      git commit -a -m "Test: Remove, add and update" &&
@@ -91,7 +95,7 @@
      diff F/newfile6.png ../F/newfile6.png
      )'
 
-# Should fail (but only on the git-cvsexportcommit stage)
+# Should fail (but only on the git cvsexportcommit stage)
 test_expect_success \
     'Fail to change binary more than one generation old' \
     'cat F/newfile6.png >>D/newfile4.png &&
@@ -160,24 +164,24 @@
      'mkdir "G g" &&
       echo ok then >"G g/with spaces.txt" &&
       git add "G g/with spaces.txt" && \
-      cp ../test9200a.png "G g/with spaces.png" && \
+      cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "With spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
       (cd "$CVSWORK" &&
-      git-cvsexportcommit -c $id &&
+      git cvsexportcommit -c $id &&
       check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
       )'
 
 test_expect_success \
      'Update file with spaces in file name' \
      'echo Ok then >>"G g/with spaces.txt" &&
-      cat ../test9200a.png >>"G g/with spaces.png" && \
+      cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
       (cd "$CVSWORK" &&
-      git-cvsexportcommit -c $id
+      git cvsexportcommit -c $id
       check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
       )'
 
@@ -197,12 +201,12 @@
      'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
       echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      cp ../test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+      cp "$TEST_DIRECTORY"/test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git commit -a -m "Går det så går det" && \
       id=$(git rev-list --max-count=1 HEAD) &&
       (cd "$CVSWORK" &&
-      git-cvsexportcommit -v -c $id &&
+      git cvsexportcommit -v -c $id &&
       check_entries \
       "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
       "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
@@ -222,14 +226,15 @@
       git commit -a -m "Update two" &&
       id=$(git rev-list --max-count=1 HEAD) &&
       (cd "$CVSWORK" &&
-      test_must_fail git-cvsexportcommit -c $id
+      test_must_fail git cvsexportcommit -c $id
       )'
 
-case "$(git config --bool core.filemode)" in
-false)
-	;;
-*)
-test_expect_success \
+if ! test "$(git config --bool core.filemode)" = false
+then
+	test_set_prereq FILEMODE
+fi
+
+test_expect_success FILEMODE \
      'Retain execute bit' \
      'mkdir G &&
       echo executeon >G/on &&
@@ -239,12 +244,10 @@
       git add G/off &&
       git commit -a -m "Execute test" &&
       (cd "$CVSWORK" &&
-      git-cvsexportcommit -c HEAD
+      git cvsexportcommit -c HEAD
       test -x G/on &&
       ! test -x G/off
       )'
-	;;
-esac
 
 test_expect_success '-w option should work with relative GIT_DIR' '
       mkdir W &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 8b79de5..821be7c 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -3,9 +3,9 @@
 # Copyright (c) 2007 Shawn Pearce
 #
 
-test_description='test git-fast-import utility'
+test_description='test git fast-import utility'
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 file2_data='file2
 second line of EOF'
@@ -65,7 +65,7 @@
 INPUT_END
 test_expect_success \
     'A: create pack from stdin' \
-    'git-fast-import --export-marks=marks.out <input &&
+    'git fast-import --export-marks=marks.out <input &&
 	 git whatchanged master'
 test_expect_success \
 	'A: verify pack' \
@@ -131,7 +131,7 @@
 
 test_expect_success \
 	'A: verify marks import' \
-	'git-fast-import \
+	'git fast-import \
 		--import-marks=marks.out \
 		--export-marks=marks.new \
 		</dev/null &&
@@ -151,7 +151,7 @@
 INPUT_END
 test_expect_success \
 	'A: verify marks import does not crash' \
-	'git-fast-import --import-marks=marks.out <input &&
+	'git fast-import --import-marks=marks.out <input &&
 	 git whatchanged verify--import-marks'
 test_expect_success \
 	'A: verify pack' \
@@ -184,7 +184,7 @@
 
 INPUT_END
 test_expect_success 'B: fail on invalid blob sha1' '
-    test_must_fail git-fast-import <input
+    test_must_fail git fast-import <input
 '
 rm -f .git/objects/pack_* .git/objects/index_*
 
@@ -199,7 +199,7 @@
 
 INPUT_END
 test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
-    test_must_fail git-fast-import <input
+    test_must_fail git fast-import <input
 '
 rm -f .git/objects/pack_* .git/objects/index_*
 
@@ -214,7 +214,7 @@
 
 INPUT_END
 test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
-    test_must_fail git-fast-import <input
+    test_must_fail git fast-import <input
 '
 rm -f .git/objects/pack_* .git/objects/index_*
 
@@ -230,7 +230,7 @@
 INPUT_END
 test_expect_success \
     'B: accept branch name "TEMP_TAG"' \
-    'git-fast-import <input &&
+    'git fast-import <input &&
 	 test -f .git/TEMP_TAG &&
 	 test `git rev-parse master` = `git rev-parse TEMP_TAG^`'
 rm -f .git/TEMP_TAG
@@ -239,7 +239,7 @@
 ### series C
 ###
 
-newf=`echo hi newf | git-hash-object -w --stdin`
+newf=`echo hi newf | git hash-object -w --stdin`
 oldf=`git rev-parse --verify master:file2`
 test_tick
 cat >input <<INPUT_END
@@ -257,7 +257,7 @@
 INPUT_END
 test_expect_success \
     'C: incremental import create pack from stdin' \
-    'git-fast-import <input &&
+    'git fast-import <input &&
 	 git whatchanged branch'
 test_expect_success \
 	'C: verify pack' \
@@ -315,7 +315,7 @@
 INPUT_END
 test_expect_success \
     'D: inline data in commit' \
-    'git-fast-import <input &&
+    'git fast-import <input &&
 	 git whatchanged branch'
 test_expect_success \
 	'D: verify pack' \
@@ -358,11 +358,11 @@
 
 INPUT_END
 test_expect_success 'E: rfc2822 date, --date-format=raw' '
-    test_must_fail git-fast-import --date-format=raw <input
+    test_must_fail git fast-import --date-format=raw <input
 '
 test_expect_success \
     'E: rfc2822 date, --date-format=rfc2822' \
-    'git-fast-import --date-format=rfc2822 <input'
+    'git fast-import --date-format=rfc2822 <input'
 test_expect_success \
 	'E: verify pack' \
 	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
@@ -399,7 +399,7 @@
 INPUT_END
 test_expect_success \
     'F: non-fast-forward update skips' \
-    'if git-fast-import <input
+    'if git fast-import <input
 	 then
 		echo BAD gfi did not fail
 		return 1
@@ -449,7 +449,7 @@
 INPUT_END
 test_expect_success \
     'G: non-fast-forward update forced' \
-    'git-fast-import --force <input'
+    'git fast-import --force <input'
 test_expect_success \
 	'G: verify pack' \
 	'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
@@ -485,7 +485,7 @@
 INPUT_END
 test_expect_success \
     'H: deletall, add 1' \
-    'git-fast-import <input &&
+    'git fast-import <input &&
 	 git whatchanged H'
 test_expect_success \
 	'H: verify pack' \
@@ -525,7 +525,7 @@
 INPUT_END
 test_expect_success \
     'I: export-pack-edges' \
-    'git-fast-import --export-pack-edges=edges.list <input'
+    'git fast-import --export-pack-edges=edges.list <input'
 
 cat >expect <<EOF
 .git/objects/pack/pack-.pack: `git rev-parse --verify export-boundary`
@@ -559,7 +559,7 @@
 INPUT_END
 test_expect_success \
     'J: reset existing branch creates empty commit' \
-    'git-fast-import <input'
+    'git fast-import <input'
 test_expect_success \
 	'J: branch has 1 commit, empty tree' \
 	'test 1 = `git rev-list J | wc -l` &&
@@ -589,7 +589,7 @@
 INPUT_END
 test_expect_success \
     'K: reinit branch with from' \
-    'git-fast-import <input'
+    'git fast-import <input'
 test_expect_success \
     'K: verify K^1 = branch^1' \
     'test `git rev-parse --verify branch^1` \
@@ -641,7 +641,7 @@
 
 test_expect_success \
     'L: verify internal tree sorting' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git diff-tree --abbrev --raw L^ L >output &&
 	 test_cmp expect output'
 
@@ -667,7 +667,7 @@
 EOF
 test_expect_success \
 	'M: rename file in same subdirectory' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git diff-tree -M -r M1^ M1 >actual &&
 	 compare_diff_raw expect actual'
 
@@ -688,7 +688,7 @@
 EOF
 test_expect_success \
 	'M: rename file to new subdirectory' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git diff-tree -M -r M2^ M2 >actual &&
 	 compare_diff_raw expect actual'
 
@@ -709,7 +709,7 @@
 EOF
 test_expect_success \
 	'M: rename subdirectory to new subdirectory' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git diff-tree -M -r M3^ M3 >actual &&
 	 compare_diff_raw expect actual'
 
@@ -735,7 +735,7 @@
 EOF
 test_expect_success \
 	'N: copy file in same subdirectory' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
 	 compare_diff_raw expect actual'
 
@@ -769,7 +769,7 @@
 EOF
 test_expect_success \
 	'N: copy then modify subdirectory' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
 	 compare_diff_raw expect actual'
 
@@ -793,8 +793,8 @@
 
 test_expect_success \
 	'N: copy dirty subdirectory' \
-	'git-fast-import <input &&
-	 test `git-rev-parse N2^{tree}` = `git-rev-parse N3^{tree}`'
+	'git fast-import <input &&
+	 test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
 
 ###
 ### series O
@@ -833,8 +833,8 @@
 
 test_expect_success \
 	'O: comments are all skipped' \
-	'git-fast-import <input &&
-	 test `git-rev-parse N3` = `git-rev-parse O1`'
+	'git fast-import <input &&
+	 test `git rev-parse N3` = `git rev-parse O1`'
 
 cat >input <<INPUT_END
 commit refs/heads/O2
@@ -854,8 +854,8 @@
 
 test_expect_success \
 	'O: blank lines not necessary after data commands' \
-	'git-fast-import <input &&
-	 test `git-rev-parse N3` = `git-rev-parse O2`'
+	'git fast-import <input &&
+	 test `git rev-parse N3` = `git rev-parse O2`'
 
 test_expect_success \
 	'O: repack before next test' \
@@ -899,7 +899,7 @@
 INPUT_END
 test_expect_success \
 	'O: blank lines not necessary after other commands' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 test 8 = `find .git/objects/pack -type f | wc -l` &&
 	 test `git rev-parse refs/tags/O3-2nd` = `git rev-parse O3^` &&
 	 git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
@@ -932,7 +932,7 @@
 INPUT_END
 test_expect_success \
 	'O: progress outputs as requested by input' \
-	'git-fast-import <input >actual &&
+	'git fast-import <input >actual &&
 	 grep "progress " <input >expect &&
 	 test_cmp expect actual'
 
@@ -997,7 +997,7 @@
 
 test_expect_success \
 	'P: supermodule & submodule mix' \
-	'git-fast-import <input &&
+	'git fast-import <input &&
 	 git checkout subuse1 &&
 	 rm -rf sub && mkdir sub && cd sub &&
 	 git init &&
@@ -1007,8 +1007,8 @@
 	 git submodule init &&
 	 git submodule update'
 
-SUBLAST=$(git-rev-parse --verify sub)
-SUBPREV=$(git-rev-parse --verify sub^)
+SUBLAST=$(git rev-parse --verify sub)
+SUBPREV=$(git rev-parse --verify sub^)
 
 cat >input <<INPUT_END
 blob
@@ -1042,8 +1042,8 @@
 	'P: verbatim SHA gitlinks' \
 	'git branch -D sub &&
 	 git gc && git prune &&
-	 git-fast-import <input &&
-	 test $(git-rev-parse --verify subuse2) = $(git-rev-parse --verify subuse1)'
+	 git fast-import <input &&
+	 test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)'
 
 test_tick
 cat >input <<INPUT_END
@@ -1063,7 +1063,7 @@
 INPUT_END
 
 test_expect_success 'P: fail on inline gitlink' '
-    test_must_fail git-fast-import <input'
+    test_must_fail git fast-import <input'
 
 test_tick
 cat >input <<INPUT_END
@@ -1086,6 +1086,6 @@
 INPUT_END
 
 test_expect_success 'P: fail on blob mark in gitlink' '
-    test_must_fail git-fast-import <input'
+    test_must_fail git fast-import <input'
 
 test_done
diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh
index 3a6509a..8da9ce5 100755
--- a/t/t9301-fast-export.sh
+++ b/t/t9301-fast-export.sh
@@ -3,11 +3,14 @@
 # Copyright (c) 2007 Johannes E. Schindelin
 #
 
-test_description='git-fast-export'
+test_description='git fast-export'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
 
+	echo break it > file0 &&
+	git add file0 &&
+	test_tick &&
 	echo Wohlauf > file &&
 	git add file &&
 	test_tick &&
@@ -57,8 +60,8 @@
 		(cd new &&
 		 git fast-import &&
 		 test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
-		 git diff master..partial &&
-		 git diff master^..partial^ &&
+		 git diff --exit-code master partial &&
+		 git diff --exit-code master^ partial^ &&
 		 test_must_fail git rev-parse partial~2)
 
 '
@@ -67,7 +70,7 @@
 
 	git config i18n.commitencoding ISO-8859-1 &&
 	# use author and committer name in ISO-8859-1 to match it.
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 	test_tick &&
 	echo rosten >file &&
 	git commit -s -m den file &&
@@ -185,8 +188,8 @@
 
 '
 
-export GIT_AUTHOR_NAME='A U Thor'
-export GIT_COMMITTER_NAME='C O Mitter'
+GIT_AUTHOR_NAME='A U Thor'; export GIT_AUTHOR_NAME
+GIT_COMMITTER_NAME='C O Mitter'; export GIT_COMMITTER_NAME
 
 test_expect_success 'setup copies' '
 
@@ -259,4 +262,19 @@
 
 '
 
+test_expect_success 'set-up a few more tags for tag export tests' '
+	git checkout -f master &&
+	HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
+	git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
+	git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
+	git tag    tag-obj_tag     -m "tagging a tag" tree_tag-obj &&
+	git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
+'
+
+# NEEDSWORK: not just check return status, but validate the output
+test_expect_success 'tree_tag'        'git fast-export tree_tag'
+test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
+test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'
+test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
+
 test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index c1850d2..64f947d 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -10,17 +10,19 @@
 
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping git cvsserver tests, perl not available'
+	test_done
+fi
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-cvsserver tests, cvs not found' :
+    say 'skipping git-cvsserver tests, cvs not found'
     test_done
-    exit
 fi
 perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
-    test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
+    say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
-    exit
 }
 
 unset GIT_DIR GIT_CONFIG
@@ -44,7 +46,7 @@
   git add secondrootfile &&
   git commit -m "second root") &&
   git pull secondroot master &&
-  git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+  git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
   GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
 '
@@ -267,7 +269,7 @@
 
 rm -fr "$SERVERDIR"
 cd "$WORKDIR" &&
-git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
 GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
 exit 1
@@ -424,7 +426,7 @@
 test_expect_success 'cvs update (-p)' '
     touch really-empty &&
     echo Line 1 > no-lf &&
-    echo -n Line 2 >> no-lf &&
+    printf "Line 2" >> no-lf &&
     git add really-empty no-lf &&
     git commit -q -m "Update -p test" &&
     git push gitcvs.git >/dev/null &&
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index e27a1c5..aca40c1 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -49,14 +49,17 @@
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-cvsserver tests, cvs not found' :
+    say 'skipping git-cvsserver tests, cvs not found'
     test_done
-    exit
+fi
+if ! test_have_prereq PERL
+then
+    say 'skipping git-cvsserver tests, perl not available'
+    test_done
 fi
 perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
-    test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' :
+    say 'skipping git-cvsserver tests, Perl SQLite interface unavailable'
     test_done
-    exit
 }
 
 unset GIT_DIR GIT_CONFIG
@@ -84,7 +87,7 @@
     echo "subdir/file.h crlf" >> .gitattributes &&
     git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* &&
     git commit -q -m "First Commit" &&
-    git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+    git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
     GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
     GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
 '
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index d8f278f..f4210fb 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -25,9 +25,9 @@
 our \$site_header = "";
 our \$site_footer = "";
 our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
-our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
-our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
+our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
+our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
+our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
 our \$projects_list = "";
 our \$export_ok = "";
 our \$strict_export = "";
@@ -43,9 +43,11 @@
 	GATEWAY_INTERFACE="CGI/1.1"
 	HTTP_ACCEPT="*/*"
 	REQUEST_METHOD="GET"
+	SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
 	QUERY_STRING=""$1""
 	PATH_INFO=""$2""
-	export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD QUERY_STRING PATH_INFO
+	export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
+		SCRIPT_NAME QUERY_STRING PATH_INFO
 
 	GITWEB_CONFIG=$(pwd)/gitweb_config.perl
 	export GITWEB_CONFIG
@@ -54,27 +56,23 @@
 	# written to web server logs, so we are not interested in that:
 	# we are interested only in properly formatted errors/warnings
 	rm -f gitweb.log &&
-	perl -- "$(pwd)/../../gitweb/gitweb.perl" \
+	perl -- "$SCRIPT_NAME" \
 		>/dev/null 2>gitweb.log &&
-	if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
+	if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi
 
 	# gitweb.log is left for debugging
 }
 
-safe_chmod () {
-	chmod "$1" "$2" &&
-	if [ "$(git config --get core.filemode)" = false ]
-	then
-		git update-index --chmod="$1" "$2"
-	fi
-}
-
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping gitweb tests, perl not available'
+	test_done
+fi
+
 perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
-    test_expect_success 'skipping gitweb tests, perl version is too old' :
+    say 'skipping gitweb tests, perl version is too old'
     test_done
-    exit
 }
 
 gitweb_init
@@ -240,7 +238,7 @@
 
 test_expect_success \
 	'commitdiff(0): mode change' \
-	'safe_chmod +x new_file &&
+	'test_chmod +x new_file &&
 	 git commit -a -m "Mode changed." &&
 	 gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
@@ -252,7 +250,7 @@
 	 gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
 
-test_expect_success \
+test_expect_success SYMLINKS \
 	'commitdiff(0): file to symlink' \
 	'rm renamed_file &&
 	 ln -s file renamed_file &&
@@ -279,7 +277,7 @@
 test_expect_success \
 	'commitdiff(0): mode change and modified' \
 	'echo "New line" >> file2 &&
-	 safe_chmod +x file2 &&
+	 test_chmod +x file2 &&
 	 git commit -a -m "Mode change and modification." &&
 	 gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
@@ -306,7 +304,7 @@
 	'commitdiff(0): renamed, mode change and modified' \
 	'git mv file3 file2 &&
 	 echo "Propter nomen suum." >> file2 &&
-	 safe_chmod +x file2 &&
+	 test_chmod +x file2 &&
 	 git commit -a -m "File rename, mode change and modification." &&
 	 gitweb_run "p=.git;a=commitdiff"'
 test_debug 'cat gitweb.log'
@@ -314,7 +312,7 @@
 # ----------------------------------------------------------------------
 # commitdiff testing (taken from t4114-apply-typechange.sh)
 
-test_expect_success 'setup typechange commits' '
+test_expect_success SYMLINKS 'setup typechange commits' '
 	echo "hello world" > foo &&
 	echo "hi planet" > bar &&
 	git update-index --add foo bar &&
@@ -423,10 +421,15 @@
 	 git add 03-new &&
 	 git mv 04-rename-from 04-rename-to &&
 	 echo "Changed" >> 04-rename-to &&
-	 safe_chmod +x 05-mode-change &&
-	 rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink &&
+	 test_chmod +x 05-mode-change &&
+	 rm -f 06-file-or-symlink &&
+	 if test_have_prereq SYMLINKS; then
+		ln -s 01-change 06-file-or-symlink
+	 else
+		printf %s 01-change > 06-file-or-symlink
+	 fi &&
 	 echo "Changed and have mode changed" > 07-change-mode-change	&&
-	 safe_chmod +x 07-change-mode-change &&
+	 test_chmod +x 07-change-mode-change &&
 	 git commit -a -m "Large commit" &&
 	 git checkout master'
 
@@ -574,20 +577,20 @@
 
 test_expect_success \
 	'encode(commit): utf8' \
-	'. ../t3901-utf8.txt &&
+	'. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 	 echo "UTF-8" >> file &&
 	 git add file &&
-	 git commit -F ../t3900/1-UTF-8.txt &&
+	 git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt &&
 	 gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'encode(commit): iso-8859-1' \
-	'. ../t3901-8859-1.txt &&
+	'. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 	 echo "ISO-8859-1" >> file &&
 	 git add file &&
 	 git config i18n.commitencoding ISO-8859-1 &&
-	 git commit -F ../t3900/ISO-8859-1.txt &&
+	 git commit -F "$TEST_DIRECTORY"/t3900/ISO-8859-1.txt &&
 	 git config --unset i18n.commitencoding &&
 	 gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
@@ -660,6 +663,11 @@
 EOF
 
 test_expect_success \
+	'config override: tree view, features not overridden in repo config' \
+	'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
 	'config override: tree view, features disabled in repo config' \
 	'git config gitweb.blame no &&
 	 git config gitweb.snapshot none &&
@@ -667,10 +675,31 @@
 test_debug 'cat gitweb.log'
 
 test_expect_success \
-	'config override: tree view, features enabled in repo config' \
+	'config override: tree view, features enabled in repo config (1)' \
 	'git config gitweb.blame yes &&
 	 git config gitweb.snapshot "zip,tgz, tbz2" &&
 	 gitweb_run "p=.git;a=tree"'
 test_debug 'cat gitweb.log'
 
+cat >.git/config <<\EOF
+# testing noval and alternate separator
+[gitweb]
+	blame
+	snapshot = zip tgz
+EOF
+test_expect_success \
+	'config override: tree view, features enabled in repo config (2)' \
+	'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# non-ASCII in README.html
+
+test_expect_success \
+	'README.html with non-ASCII characters (utf-8)' \
+	'echo "<b>UTF-8 example:</b><br />" > .git/README.html &&
+	 cat "$TEST_DIRECTORY"/t3900/1-UTF-8.txt >> .git/README.html &&
+	 gitweb_run "p=.git;a=summary"'
+test_debug 'cat gitweb.log'
+
 test_done
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 0d7786a..4322a0c 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -1,8 +1,13 @@
 #!/bin/sh
 
-test_description='git-cvsimport basic tests'
+test_description='git cvsimport basic tests'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping git cvsimport tests, perl not available'
+	test_done
+fi
+
 CVSROOT=$(pwd)/cvsroot
 export CVSROOT
 unset CVS_SERVER
@@ -14,7 +19,6 @@
 then
 	say 'skipping cvsimport tests, cvs not found'
 	test_done
-	exit
 fi
 
 cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
@@ -24,12 +28,10 @@
 '')
 	say 'skipping cvsimport tests, cvsps not found'
 	test_done
-	exit
 	;;
 *)
 	say 'skipping cvsimport tests, unsupported cvsps version'
 	test_done
-	exit
 	;;
 esac
 
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index 9706ee5..b4ca244 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -6,8 +6,13 @@
 test_description='perl interface (Git.pm)'
 . ./test-lib.sh
 
+if ! test_have_prereq PERL; then
+	say 'skipping perl interface tests, perl not available'
+	test_done
+fi
+
 perl -MTest::More -e 0 2>/dev/null || {
-	say_color skip "Perl Test::More unavailable, skipping test"
+	say "Perl Test::More unavailable, skipping test"
 	test_done
 }
 
@@ -27,18 +32,18 @@
      echo "changed file 1" > file1 &&
      git commit -a -m "second commit" &&
 
-     git-config --add color.test.slot1 green &&
-     git-config --add test.string value &&
-     git-config --add test.dupstring value1 &&
-     git-config --add test.dupstring value2 &&
-     git-config --add test.booltrue true &&
-     git-config --add test.boolfalse no &&
-     git-config --add test.boolother other &&
-     git-config --add test.int 2k
+     git config --add color.test.slot1 green &&
+     git config --add test.string value &&
+     git config --add test.dupstring value1 &&
+     git config --add test.dupstring value2 &&
+     git config --add test.booltrue true &&
+     git config --add test.boolfalse no &&
+     git config --add test.boolother other &&
+     git config --add test.int 2k
      '
 
 test_external_without_stderr \
     'Perl API' \
-    perl ../t9700/test.pl
+    perl "$TEST_DIRECTORY"/t9700/test.pl
 
 test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index 504f95a..697daf3 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -13,10 +13,7 @@
 BEGIN { use_ok('Git') }
 
 # set up
-our $repo_dir = "trash directory";
 our $abs_repo_dir = Cwd->cwd;
-die "this must be run by calling the t/t97* shell script(s)\n"
-    if basename(Cwd->cwd) ne $repo_dir;
 ok(our $r = Git->repository(Directory => "."), "open repository");
 
 # config
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 689ac2f..4bd986f 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -3,6 +3,22 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+# if --tee was passed, write the output not only to the terminal, but
+# additionally to the file test-results/$BASENAME.out, too.
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+	# do not redirect again
+	;;
+*' --tee '*|*' --va'*)
+	mkdir -p test-results
+	BASE=test-results/$(basename "$0" .sh)
+	(GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+	 echo $? > $BASE.exit) | tee $BASE.out
+	test "$(cat $BASE.exit)" = 0
+	exit
+	;;
+esac
+
 # Keep the original TERM for say_color
 ORIGINAL_TERM=$TERM
 
@@ -82,7 +98,7 @@
 	-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
 		immediate=t; shift ;;
 	-l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
-		export GIT_TEST_LONG=t; shift ;;
+		GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;;
 	-h|--h|--he|--hel|--help)
 		help=t; shift ;;
 	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
@@ -94,6 +110,10 @@
 	--no-python)
 		# noop now...
 		shift ;;
+	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+		valgrind=t; verbose=t; shift ;;
+	--tee)
+		shift ;; # was handled already
 	*)
 		break ;;
 	esac
@@ -127,7 +147,7 @@
 
 error () {
 	say_color error "error: $*"
-	trap - exit
+	trap - EXIT
 	exit 1
 }
 
@@ -163,7 +183,7 @@
 	exit 1
 }
 
-trap 'die' exit
+trap 'die' EXIT
 
 # The semantics of the editor variables are that of invoking
 # sh -c "$EDITOR \"$@\"" files ...
@@ -193,32 +213,87 @@
 	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
+# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+#
+# This will commit a file with the given contents and the given commit
+# message.  It will also add a tag with <message> as name.
+#
+# Both <file> and <contents> default to <message>.
+
+test_commit () {
+	file=${2:-"$1.t"}
+	echo "${3-$1}" > "$file" &&
+	git add "$file" &&
+	test_tick &&
+	git commit -m "$1" &&
+	git tag "$1"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+	test_tick &&
+	git merge -m "$1" "$2" &&
+	git tag "$1"
+}
+
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+	chmod "$@" &&
+	git update-index --add "--chmod=$@"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+#   test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+	satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+	case $satisfied in
+	*" $1 "*)
+		: yes, have it ;;
+	*)
+		! : nope ;;
+	esac
+}
+
 # You are not expected to call test_ok_ and test_failure_ directly, use
 # the text_expect_* functions instead.
 
 test_ok_ () {
-	test_count=$(expr "$test_count" + 1)
-	test_success=$(expr "$test_success" + 1)
+	test_success=$(($test_success + 1))
 	say_color "" "  ok $test_count: $@"
 }
 
 test_failure_ () {
-	test_count=$(expr "$test_count" + 1)
-	test_failure=$(expr "$test_failure" + 1);
+	test_failure=$(($test_failure + 1))
 	say_color error "FAIL $test_count: $1"
 	shift
 	echo "$@" | sed -e 's/^/	/'
-	test "$immediate" = "" || { trap - exit; exit 1; }
+	test "$immediate" = "" || { trap - EXIT; exit 1; }
 }
 
 test_known_broken_ok_ () {
-	test_count=$(expr "$test_count" + 1)
 	test_fixed=$(($test_fixed+1))
 	say_color "" "  FIXED $test_count: $@"
 }
 
 test_known_broken_failure_ () {
-	test_count=$(expr "$test_count" + 1)
 	test_broken=$(($test_broken+1))
 	say_color skip "  still broken $test_count: $@"
 }
@@ -234,20 +309,23 @@
 }
 
 test_skip () {
-	this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
-	this_test="$this_test.$(expr "$test_count" + 1)"
+	test_count=$(($test_count+1))
 	to_skip=
 	for skp in $GIT_SKIP_TESTS
 	do
-		case "$this_test" in
+		case $this_test.$test_count in
 		$skp)
 			to_skip=t
 		esac
 	done
+	if test -z "$to_skip" && test -n "$prereq" &&
+	   ! test_have_prereq "$prereq"
+	then
+		to_skip=t
+	fi
 	case "$to_skip" in
 	t)
 		say_color skip >&3 "skipping test: $@"
-		test_count=$(expr "$test_count" + 1)
 		say_color skip "skip $test_count: $1"
 		: true
 		;;
@@ -258,8 +336,9 @@
 }
 
 test_expect_failure () {
+	test "$#" = 3 && { prereq=$1; shift; } || prereq=
 	test "$#" = 2 ||
-	error "bug in the test script: not 2 parameters to test-expect-failure"
+	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
 	if ! test_skip "$@"
 	then
 		say >&3 "checking known breakage: $2"
@@ -275,8 +354,9 @@
 }
 
 test_expect_success () {
+	test "$#" = 3 && { prereq=$1; shift; } || prereq=
 	test "$#" = 2 ||
-	error "bug in the test script: not 2 parameters to test-expect-success"
+	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
 	if ! test_skip "$@"
 	then
 		say >&3 "expecting success: $2"
@@ -292,8 +372,9 @@
 }
 
 test_expect_code () {
+	test "$#" = 4 && { prereq=$1; shift; } || prereq=
 	test "$#" = 3 ||
-	error "bug in the test script: not 3 parameters to test-expect-code"
+	error "bug in the test script: not 3 or 4 parameters to test-expect-code"
 	if ! test_skip "$@"
 	then
 		say >&3 "expecting exit code $1: $3"
@@ -317,15 +398,16 @@
 # Usage: test_external description command arguments...
 # Example: test_external 'Perl API' perl ../path/to/test.pl
 test_external () {
-	test "$#" -eq 3 ||
-	error >&5 "bug in the test script: not 3 parameters to test_external"
+	test "$#" = 4 && { prereq=$1; shift; } || prereq=
+	test "$#" = 3 ||
+	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
 	descr="$1"
 	shift
 	if ! test_skip "$descr" "$@"
 	then
 		# Announce the script to reduce confusion about the
 		# test output that follows.
-		say_color "" " run $(expr "$test_count" + 1): $descr ($*)"
+		say_color "" " run $test_count: $descr ($*)"
 		# Run command; redirect its stderr to &4 as in
 		# test_run_, but keep its stdout on our stdout even in
 		# non-verbose mode.
@@ -407,19 +489,19 @@
 	error "bug in the test script: not 1 parameter to test-create-repo"
 	owd=`pwd`
 	repo="$1"
-	mkdir "$repo"
+	mkdir -p "$repo"
 	cd "$repo" || error "Cannot setup test environment"
-	"$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 ||
+	"$GIT_EXEC_PATH/git-init" "--template=$owd/../templates/blt/" >&3 2>&4 ||
 	error "cannot run git init -- have you built things yet?"
 	mv .git/hooks .git/hooks-disabled
 	cd "$owd"
 }
 
 test_done () {
-	trap - exit
+	trap - EXIT
 	test_results_dir="$TEST_DIRECTORY/test-results"
 	mkdir -p "$test_results_dir"
-	test_results_path="$test_results_dir/${0%-*}-$$"
+	test_results_path="$test_results_dir/${0%.sh}-$$"
 
 	echo "total $test_count" >> $test_results_path
 	echo "success $test_success" >> $test_results_path
@@ -441,15 +523,12 @@
 	fi
 	case "$test_failure" in
 	0)
-		# We could:
-		# cd .. && rm -fr 'trash directory'
-		# but that means we forbid any tests that use their own
-		# subdirectory from calling test_done without coming back
-		# to where they started from.
-		# The Makefile provided will clean this test area so
-		# we will leave things as they are.
-
 		say_color pass "passed all $msg"
+
+		test -d "$remove_trash" &&
+		cd "$(dirname "$remove_trash")" &&
+		rm -rf "$(basename "$remove_trash")"
+
 		exit 0 ;;
 
 	*)
@@ -462,11 +541,83 @@
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
 TEST_DIRECTORY=$(pwd)
-PATH=$TEST_DIRECTORY/..:$PATH
-GIT_EXEC_PATH=$(pwd)/..
+if test -z "$valgrind"
+then
+	if test -z "$GIT_TEST_INSTALLED"
+	then
+		PATH=$TEST_DIRECTORY/..:$PATH
+		GIT_EXEC_PATH=$TEST_DIRECTORY/..
+	else
+		GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
+		error "Cannot run git from $GIT_TEST_INSTALLED."
+		PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+		GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
+	fi
+else
+	make_symlink () {
+		test -h "$2" &&
+		test "$1" = "$(readlink "$2")" || {
+			# be super paranoid
+			if mkdir "$2".lock
+			then
+				rm -f "$2" &&
+				ln -s "$1" "$2" &&
+				rm -r "$2".lock
+			else
+				while test -d "$2".lock
+				do
+					say "Waiting for lock on $2."
+					sleep 1
+				done
+			fi
+		}
+	}
+
+	make_valgrind_symlink () {
+		# handle only executables
+		test -x "$1" || return
+
+		base=$(basename "$1")
+		symlink_target=$TEST_DIRECTORY/../$base
+		# do not override scripts
+		if test -x "$symlink_target" &&
+		    test ! -d "$symlink_target" &&
+		    test "#!" != "$(head -c 2 < "$symlink_target")"
+		then
+			symlink_target=../valgrind.sh
+		fi
+		case "$base" in
+		*.sh|*.perl)
+			symlink_target=../unprocessed-script
+		esac
+		# create the link, or replace it if it is out of date
+		make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
+	}
+
+	# override all git executables in TEST_DIRECTORY/..
+	GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+	mkdir -p "$GIT_VALGRIND"/bin
+	for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-*
+	do
+		make_valgrind_symlink $file
+	done
+	OLDIFS=$IFS
+	IFS=:
+	for path in $PATH
+	do
+		ls "$path"/git-* 2> /dev/null |
+		while read file
+		do
+			make_valgrind_symlink "$file"
+		done
+	done
+	IFS=$OLDIFS
+	PATH=$GIT_VALGRIND/bin:$PATH
+	GIT_EXEC_PATH=$GIT_VALGRIND/bin
+	export GIT_VALGRIND
+fi
 GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
 unset GIT_CONFIG
-unset GIT_CONFIG_LOCAL
 GIT_CONFIG_NOSYSTEM=1
 GIT_CONFIG_NOGLOBAL=1
 export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
@@ -486,9 +637,10 @@
 . ../GIT-BUILD-OPTIONS
 
 # Test repository
-test="trash directory"
+test="trash directory.$(basename "$0" .sh)"
+test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
 rm -fr "$test" || {
-	trap - exit
+	trap - EXIT
 	echo >&5 "FATAL: Cannot prepare test area"
 	exit 1
 }
@@ -498,7 +650,8 @@
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$test" || exit 1
 
-this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
+this_test=${0##*/}
+this_test=${this_test%%-*}
 for skp in $GIT_SKIP_TESTS
 do
 	to_skip=
@@ -516,3 +669,37 @@
 		test_done
 	esac
 done
+
+# Fix some commands on Windows
+case $(uname -s) in
+*MINGW*)
+	# Windows has its own (incompatible) sort and find
+	sort () {
+		/usr/bin/sort "$@"
+	}
+	find () {
+		/usr/bin/find "$@"
+	}
+	sum () {
+		md5sum "$@"
+	}
+	# git sees Windows-style pwd
+	pwd () {
+		builtin pwd -W
+	}
+	# no POSIX permissions
+	# backslashes in pathspec are converted to '/'
+	# exec does not inherit the PID
+	;;
+*)
+	test_set_prereq POSIXPERM
+	test_set_prereq BSLASHPSPEC
+	test_set_prereq EXECKEEPSPID
+	;;
+esac
+
+test -z "$NO_PERL" && test_set_prereq PERL
+
+# test whether the filesystem supports symbolic links
+ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
+rm -f y
diff --git a/t/valgrind/.gitignore b/t/valgrind/.gitignore
new file mode 100644
index 0000000..d4ae667
--- /dev/null
+++ b/t/valgrind/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/templates
diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh
new file mode 100755
index 0000000..d8105d9
--- /dev/null
+++ b/t/valgrind/analyze.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+out_prefix=$(dirname "$0")/../test-results/valgrind.out
+output=
+count=0
+total_count=0
+missing_message=
+new_line='
+'
+
+# start outputting the current valgrind error in $out_prefix.++$count,
+# and the test case which failed in the corresponding .message file
+start_output () {
+	test -z "$output" || return
+
+	# progress
+	total_count=$(($total_count+1))
+	test -t 2 && printf "\rFound %d errors" $total_count >&2
+
+	count=$(($count+1))
+	output=$out_prefix.$count
+	: > $output
+
+	echo "*** $1 ***" > $output.message
+}
+
+finish_output () {
+	test ! -z "$output" || return
+	output=
+
+	# if a test case has more than one valgrind error, we need to
+	# copy the last .message file to the previous errors
+	test -z "$missing_message" || {
+		while test $missing_message -lt $count
+		do
+			cp $out_prefix.$count.message \
+				$out_prefix.$missing_message.message
+			missing_message=$(($missing_message+1))
+		done
+		missing_message=
+	}
+}
+
+# group the valgrind errors by backtrace
+output_all () {
+	last_line=
+	j=0
+	i=1
+	while test $i -le $count
+	do
+		# output <number> <backtrace-in-one-line>
+		echo "$i $(tr '\n' ' ' < $out_prefix.$i)"
+		i=$(($i+1))
+	done |
+	sort -t ' ' -k 2 | # order by <backtrace-in-one-line>
+	while read number line
+	do
+		# find duplicates, do not output backtrace twice
+		if test "$line" != "$last_line"
+		then
+			last_line=$line
+			j=$(($j+1))
+			printf "\nValgrind error $j:\n\n"
+			cat $out_prefix.$number
+			printf "\nfound in:\n"
+		fi
+		# print the test case where this came from
+		printf "\n"
+		cat $out_prefix.$number.message
+	done
+}
+
+handle_one () {
+	OLDIFS=$IFS
+	IFS="$new_line"
+	while read line
+	do
+		case "$line" in
+		# backtrace, possibly a new one
+		==[0-9]*)
+
+			# Does the current valgrind error have a message yet?
+			case "$output" in
+			*.message)
+				test -z "$missing_message" &&
+				missing_message=$count
+				output=
+			esac
+
+			start_output $(basename $1)
+			echo "$line" |
+			sed 's/==[0-9]*==/==valgrind==/' >> $output
+			;;
+		# end of backtrace
+		'}')
+			test -z "$output" || {
+				echo "$line" >> $output
+				test $output = ${output%.message} &&
+				output=$output.message
+			}
+			;;
+		# end of test case
+		'')
+			finish_output
+			;;
+		# normal line; if $output is set, print the line
+		*)
+			test -z "$output" || echo "$line" >> $output
+			;;
+		esac
+	done < $1
+	IFS=$OLDIFS
+
+	# just to be safe
+	finish_output
+}
+
+for test_script in "$(dirname "$0")"/../test-results/*.out
+do
+	handle_one $test_script
+done
+
+output_all
diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp
new file mode 100644
index 0000000..9e013fa
--- /dev/null
+++ b/t/valgrind/default.supp
@@ -0,0 +1,45 @@
+{
+	ignore-zlib-errors-cond
+	Memcheck:Cond
+	obj:*libz.so*
+}
+
+{
+	ignore-zlib-errors-value8
+	Memcheck:Value8
+	obj:*libz.so*
+}
+
+{
+	ignore-zlib-errors-value4
+	Memcheck:Value4
+	obj:*libz.so*
+}
+
+{
+	ignore-ldso-cond
+	Memcheck:Cond
+	obj:*ld-*.so
+}
+
+{
+	ignore-ldso-addr8
+	Memcheck:Addr8
+	obj:*ld-*.so
+}
+
+{
+	ignore-ldso-addr4
+	Memcheck:Addr4
+	obj:*ld-*.so
+}
+
+{
+	writing-data-from-zlib-triggers-even-more-errors
+	Memcheck:Param
+	write(buf)
+	obj:/lib/ld-*.so
+	fun:write_in_full
+	fun:write_buffer
+	fun:write_loose_object
+}
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
new file mode 100755
index 0000000..582b4dc
--- /dev/null
+++ b/t/valgrind/valgrind.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+base=$(basename "$0")
+
+TRACK_ORIGINS=
+
+VALGRIND_VERSION=$(valgrind --version)
+VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
+VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
+test 3 -gt "$VALGRIND_MAJOR" ||
+test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+TRACK_ORIGINS=--track-origins=yes
+
+exec valgrind -q --error-exitcode=126 \
+	--leak-check=no \
+	--suppressions="$GIT_VALGRIND/default.supp" \
+	--gen-suppressions=all \
+	$TRACK_ORIGINS \
+	--log-fd=4 \
+	--input-fd=4 \
+	$GIT_VALGRIND_OPTIONS \
+	"$GIT_VALGRIND"/../../"$base" "$@"
diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample
index 93c6055..f8bf490 100755
--- a/templates/hooks--update.sample
+++ b/templates/hooks--update.sample
@@ -16,6 +16,9 @@
 # hooks.allowdeletebranch
 #   This boolean sets whether deleting branches will be allowed in the
 #   repository.  By default they won't be.
+# hooks.denycreatebranch
+#   This boolean sets whether remotely creating branches will be denied
+#   in the repository.  By default this is allowed.
 #
 
 # --- Command line
@@ -39,18 +42,22 @@
 # --- Config
 allowunannotated=$(git config --bool hooks.allowunannotated)
 allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
 allowdeletetag=$(git config --bool hooks.allowdeletetag)
 
 # check for no description
 projectdesc=$(sed -e '1q' "$GIT_DIR/description")
-if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then
+case "$projectdesc" in
+"Unnamed repository"* | "")
 	echo "*** Project description file hasn't been set" >&2
 	exit 1
-fi
+	;;
+esac
 
 # --- Check types
 # if $newrev is 0000...0000, it's a commit to delete a ref.
-if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
 	newrev_type=delete
 else
 	newrev_type=$(git-cat-file -t $newrev)
@@ -78,6 +85,10 @@
 		;;
 	refs/heads/*,commit)
 		# branch
+		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+			echo "*** Creating a branch is not allowed in this repository" >&2
+			exit 1
+		fi
 		;;
 	refs/heads/*,delete)
 		# delete branch
diff --git a/templates/this--description b/templates/this--description
index c6f25e8..498b267 100644
--- a/templates/this--description
+++ b/templates/this--description
@@ -1 +1 @@
-Unnamed repository; edit this file to name it for gitweb.
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/test-ctype.c b/test-ctype.c
new file mode 100644
index 0000000..033c749
--- /dev/null
+++ b/test-ctype.c
@@ -0,0 +1,78 @@
+#include "cache.h"
+
+
+static int test_isdigit(int c)
+{
+	return isdigit(c);
+}
+
+static int test_isspace(int c)
+{
+	return isspace(c);
+}
+
+static int test_isalpha(int c)
+{
+	return isalpha(c);
+}
+
+static int test_isalnum(int c)
+{
+	return isalnum(c);
+}
+
+static int test_is_glob_special(int c)
+{
+	return is_glob_special(c);
+}
+
+static int test_is_regex_special(int c)
+{
+	return is_regex_special(c);
+}
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+static const struct ctype_class {
+	const char *name;
+	int (*test_fn)(int);
+	const char *members;
+} classes[] = {
+	{ "isdigit", test_isdigit, DIGIT },
+	{ "isspace", test_isspace, " \n\r\t" },
+	{ "isalpha", test_isalpha, LOWER UPPER },
+	{ "isalnum", test_isalnum, LOWER UPPER DIGIT },
+	{ "is_glob_special", test_is_glob_special, "*?[\\" },
+	{ "is_regex_special", test_is_regex_special, "$()*+.?[\\^{|" },
+	{ NULL }
+};
+
+static int test_class(const struct ctype_class *test)
+{
+	int i, rc = 0;
+
+	for (i = 0; i < 256; i++) {
+		int expected = i ? !!strchr(test->members, i) : 0;
+		int actual = test->test_fn(i);
+
+		if (actual != expected) {
+			rc = 1;
+			printf("%s classifies char %d (0x%02x) wrongly\n",
+			       test->name, i, i);
+		}
+	}
+	return rc;
+}
+
+int main(int argc, char **argv)
+{
+	const struct ctype_class *test;
+	int rc = 0;
+
+	for (test = classes; test->name; test++)
+		rc |= test_class(test);
+
+	return rc;
+}
diff --git a/test-path-utils.c b/test-path-utils.c
index 2c0f5a3..d261398 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -2,11 +2,13 @@
 
 int main(int argc, char **argv)
 {
-	if (argc == 3 && !strcmp(argv[1], "normalize_absolute_path")) {
+	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
 		char *buf = xmalloc(PATH_MAX + 1);
-		int rv = normalize_absolute_path(buf, argv[2]);
-		assert(strlen(buf) == rv);
+		int rv = normalize_path_copy(buf, argv[2]);
+		if (rv)
+			buf = "++failed++";
 		puts(buf);
+		return 0;
 	}
 
 	if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) {
@@ -15,12 +17,22 @@
 			argc--;
 			argv++;
 		}
+		return 0;
 	}
 
 	if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
 		int len = longest_ancestor_length(argv[2], argv[3]);
 		printf("%d\n", len);
+		return 0;
 	}
 
-	return 0;
+	if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) {
+		char *prefix = strip_path_suffix(argv[2], argv[3]);
+		printf("%s\n", prefix ? prefix : "(null)");
+		return 0;
+	}
+
+	fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+		argv[1] ? argv[1] : "(there was none)");
+	return 1;
 }
diff --git a/test-sha1.c b/test-sha1.c
index 78d7e98..9b98d07 100644
--- a/test-sha1.c
+++ b/test-sha1.c
@@ -2,7 +2,7 @@
 
 int main(int ac, char **av)
 {
-	SHA_CTX ctx;
+	git_SHA_CTX ctx;
 	unsigned char sha1[20];
 	unsigned bufsz = 8192;
 	char *buffer;
@@ -20,7 +20,7 @@
 			die("OOPS");
 	}
 
-	SHA1_Init(&ctx);
+	git_SHA1_Init(&ctx);
 
 	while (1) {
 		ssize_t sz, this_sz;
@@ -39,9 +39,9 @@
 		}
 		if (this_sz == 0)
 			break;
-		SHA1_Update(&ctx, buffer, this_sz);
+		git_SHA1_Update(&ctx, buffer, this_sz);
 	}
-	SHA1_Final(sha1, &ctx);
+	git_SHA1_Final(sha1, &ctx);
 	puts(sha1_to_hex(sha1));
 	exit(0);
 }
diff --git a/test-sigchain.c b/test-sigchain.c
new file mode 100644
index 0000000..42db234
--- /dev/null
+++ b/test-sigchain.c
@@ -0,0 +1,22 @@
+#include "sigchain.h"
+#include "cache.h"
+
+#define X(f) \
+static void f(int sig) { \
+	puts(#f); \
+	fflush(stdout); \
+	sigchain_pop(sig); \
+	raise(sig); \
+}
+X(one)
+X(two)
+X(three)
+#undef X
+
+int main(int argc, char **argv) {
+	sigchain_push(SIGTERM, one);
+	sigchain_push(SIGTERM, two);
+	sigchain_push(SIGTERM, three);
+	raise(SIGTERM);
+	return 0;
+}
diff --git a/trace.c b/trace.c
index 4713f91..4229ae1 100644
--- a/trace.c
+++ b/trace.c
@@ -50,7 +50,7 @@
 		return fd;
 	}
 
-	fprintf(stderr, "What does '%s' for GIT_TRACE means ?\n", trace);
+	fprintf(stderr, "What does '%s' for GIT_TRACE mean?\n", trace);
 	fprintf(stderr, "If you want to trace into a file, "
 		"then please set GIT_TRACE to an absolute pathname "
 		"(starting with /).\n");
diff --git a/transport.c b/transport.c
index 35cac44..3dfb03c 100644
--- a/transport.c
+++ b/transport.c
@@ -50,9 +50,7 @@
 	memset (&list, 0, sizeof(list));
 
 	while ((de = readdir(dir))) {
-		if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
-				(de->d_name[1] == '.' &&
-				 de->d_name[2] == '\0')))
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
 		ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
 		list.entries[list.nr++] = xstrdup(de->d_name);
@@ -75,7 +73,7 @@
 
 			if (fd < 0)
 				continue;
-			next = alloc_ref(path->len - name_offset + 1);
+			next = alloc_ref(path->buf + name_offset);
 			if (read_in_full(fd, buffer, 40) != 40 ||
 					get_sha1_hex(buffer, next->old_sha1)) {
 				close(fd);
@@ -83,7 +81,6 @@
 				continue;
 			}
 			close(fd);
-			strcpy(next->name, path->buf + name_offset);
 			(*tail)->next = next;
 			*tail = next;
 		}
@@ -127,14 +124,13 @@
 				      (*list)->next->name)) > 0)
 			list = &(*list)->next;
 		if (!(*list)->next || cmp < 0) {
-			struct ref *next = alloc_ref(len - 40);
+			struct ref *next = alloc_ref(buffer + 41);
 			buffer[40] = '\0';
 			if (get_sha1_hex(buffer, next->old_sha1)) {
 				warning ("invalid SHA-1: %s", buffer);
 				free(next);
 				continue;
 			}
-			strcpy(next->name, buffer + 41);
 			next->next = (*list)->next;
 			(*list)->next = next;
 			list = &(*list)->next;
@@ -142,7 +138,12 @@
 	}
 }
 
-static struct ref *get_refs_via_rsync(struct transport *transport)
+static const char *rsync_url(const char *url)
+{
+	return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
+}
+
+static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 {
 	struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
 	struct ref dummy, *tail = &dummy;
@@ -150,6 +151,9 @@
 	const char *args[5];
 	int temp_dir_len;
 
+	if (for_push)
+		return NULL;
+
 	/* copy the refs to the temporary directory */
 
 	strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
@@ -157,7 +161,7 @@
 		die ("Could not make temporary directory");
 	temp_dir_len = temp_dir.len;
 
-	strbuf_addstr(&buf, transport->url);
+	strbuf_addstr(&buf, rsync_url(transport->url));
 	strbuf_addstr(&buf, "/refs");
 
 	memset(&rsync, 0, sizeof(rsync));
@@ -173,7 +177,7 @@
 		die ("Could not run rsync to get refs");
 
 	strbuf_reset(&buf);
-	strbuf_addstr(&buf, transport->url);
+	strbuf_addstr(&buf, rsync_url(transport->url));
 	strbuf_addstr(&buf, "/packed-refs");
 
 	args[2] = buf.buf;
@@ -210,7 +214,7 @@
 	const char *args[8];
 	int result;
 
-	strbuf_addstr(&buf, transport->url);
+	strbuf_addstr(&buf, rsync_url(transport->url));
 	strbuf_addstr(&buf, "/objects/");
 
 	memset(&rsync, 0, sizeof(rsync));
@@ -289,7 +293,7 @@
 
 	/* first push the objects */
 
-	strbuf_addstr(&buf, transport->url);
+	strbuf_addstr(&buf, rsync_url(transport->url));
 	strbuf_addch(&buf, '/');
 
 	memset(&rsync, 0, sizeof(rsync));
@@ -310,7 +314,8 @@
 	args[i++] = NULL;
 
 	if (run_command(&rsync))
-		return error("Could not push objects to %s", transport->url);
+		return error("Could not push objects to %s",
+				rsync_url(transport->url));
 
 	/* copy the refs to the temporary directory; they could be packed. */
 
@@ -331,10 +336,11 @@
 	if (!(flags & TRANSPORT_PUSH_FORCE))
 		args[i++] = "--ignore-existing";
 	args[i++] = temp_dir.buf;
-	args[i++] = transport->url;
+	args[i++] = rsync_url(transport->url);
 	args[i++] = NULL;
 	if (run_command(&rsync))
-		result = error("Could not push to %s", transport->url);
+		result = error("Could not push to %s",
+				rsync_url(transport->url));
 
 	if (remove_dir_recursively(&temp_dir, 0))
 		warning ("Could not remove temporary directory %s.",
@@ -426,7 +432,7 @@
 	return !!err;
 }
 
-static struct ref *get_refs_via_curl(struct transport *transport)
+static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
 {
 	struct strbuf buffer = STRBUF_INIT;
 	char *data, *start, *mid;
@@ -443,6 +449,9 @@
 
 	struct walker *walker;
 
+	if (for_push)
+		return NULL;
+
 	if (!transport->data)
 		transport->data = get_http_walker(transport->url,
 						transport->remote);
@@ -501,7 +510,7 @@
 
 	strbuf_release(&buffer);
 
-	ref = alloc_ref_from_str("HEAD");
+	ref = alloc_ref("HEAD");
 	if (!walker->fetch_ref(walker, ref) &&
 	    !resolve_remote_symref(ref, refs)) {
 		ref->next = refs;
@@ -529,12 +538,15 @@
 	struct bundle_header header;
 };
 
-static struct ref *get_refs_from_bundle(struct transport *transport)
+static struct ref *get_refs_from_bundle(struct transport *transport, int for_push)
 {
 	struct bundle_transport_data *data = transport->data;
 	struct ref *result = NULL;
 	int i;
 
+	if (for_push)
+		return NULL;
+
 	if (data->fd > 0)
 		close(data->fd);
 	data->fd = read_bundle_header(transport->url, &data->header);
@@ -542,7 +554,7 @@
 		die ("Could not read bundle '%s'.", transport->url);
 	for (i = 0; i < data->header.references.nr; i++) {
 		struct ref_list_entry *e = data->header.references.list + i;
-		struct ref *ref = alloc_ref_from_str(e->name);
+		struct ref *ref = alloc_ref(e->name);
 		hashcpy(ref->old_sha1, e->sha1);
 		ref->next = result;
 		result = ref;
@@ -575,6 +587,7 @@
 	int fd[2];
 	const char *uploadpack;
 	const char *receivepack;
+	struct extra_have_objects extra_have;
 };
 
 static int set_git_option(struct transport *connection,
@@ -606,20 +619,23 @@
 	return 1;
 }
 
-static int connect_setup(struct transport *transport)
+static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
 	struct git_transport_data *data = transport->data;
-	data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);
+	data->conn = git_connect(data->fd, transport->url,
+				 for_push ? data->receivepack : data->uploadpack,
+				 verbose ? CONNECT_VERBOSE : 0);
 	return 0;
 }
 
-static struct ref *get_refs_via_connect(struct transport *transport)
+static struct ref *get_refs_via_connect(struct transport *transport, int for_push)
 {
 	struct git_transport_data *data = transport->data;
 	struct ref *refs;
 
-	connect_setup(transport);
-	get_remote_heads(data->fd[0], &refs, 0, NULL, 0);
+	connect_setup(transport, for_push, 0);
+	get_remote_heads(data->fd[0], &refs, 0, NULL,
+			 for_push ? REF_NORMAL : 0, &data->extra_have);
 
 	return refs;
 }
@@ -644,15 +660,15 @@
 	args.include_tag = data->followtags;
 	args.verbose = (transport->verbose > 0);
 	args.quiet = (transport->verbose < 0);
-	args.no_progress = args.quiet || !isatty(1);
+	args.no_progress = args.quiet || (!transport->progress && !isatty(1));
 	args.depth = data->depth;
 
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 
 	if (!data->conn) {
-		connect_setup(transport);
-		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0);
+		connect_setup(transport, 0, 0);
+		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
 	}
 
 	refs = fetch_pack(&args, data->fd, data->conn,
@@ -674,20 +690,216 @@
 	return (refs ? 0 : -1);
 }
 
-static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
+static int refs_pushed(struct ref *ref)
+{
+	for (; ref; ref = ref->next) {
+		switch(ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+			break;
+		default:
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+{
+	struct refspec rs;
+
+	if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
+		return;
+
+	rs.src = ref->name;
+	rs.dst = NULL;
+
+	if (!remote_find_tracking(remote, &rs)) {
+		if (verbose)
+			fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
+		if (ref->deletion) {
+			delete_ref(rs.dst, NULL, 0);
+		} else
+			update_ref("update by push", rs.dst,
+					ref->new_sha1, NULL, 0, 0);
+		free(rs.dst);
+	}
+}
+
+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
+
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+{
+	fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+	if (from)
+		fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+	else
+		fputs(prettify_ref(to), stderr);
+	if (msg) {
+		fputs(" (", stderr);
+		fputs(msg, stderr);
+		fputc(')', stderr);
+	}
+	fputc('\n', stderr);
+}
+
+static const char *status_abbrev(unsigned char sha1[20])
+{
+	return find_unique_abbrev(sha1, DEFAULT_ABBREV);
+}
+
+static void print_ok_ref_status(struct ref *ref)
+{
+	if (ref->deletion)
+		print_ref_status('-', "[deleted]", ref, NULL, NULL);
+	else if (is_null_sha1(ref->old_sha1))
+		print_ref_status('*',
+			(!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+			  "[new branch]"),
+			ref, ref->peer_ref, NULL);
+	else {
+		char quickref[84];
+		char type;
+		const char *msg;
+
+		strcpy(quickref, status_abbrev(ref->old_sha1));
+		if (ref->nonfastforward) {
+			strcat(quickref, "...");
+			type = '+';
+			msg = "forced update";
+		} else {
+			strcat(quickref, "..");
+			type = ' ';
+			msg = NULL;
+		}
+		strcat(quickref, status_abbrev(ref->new_sha1));
+
+		print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+	}
+}
+
+static int print_one_push_status(struct ref *ref, const char *dest, int count)
+{
+	if (!count)
+		fprintf(stderr, "To %s\n", dest);
+
+	switch(ref->status) {
+	case REF_STATUS_NONE:
+		print_ref_status('X', "[no match]", ref, NULL, NULL);
+		break;
+	case REF_STATUS_REJECT_NODELETE:
+		print_ref_status('!', "[rejected]", ref, NULL,
+				"remote does not support deleting refs");
+		break;
+	case REF_STATUS_UPTODATE:
+		print_ref_status('=', "[up to date]", ref,
+				ref->peer_ref, NULL);
+		break;
+	case REF_STATUS_REJECT_NONFASTFORWARD:
+		print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+				"non-fast forward");
+		break;
+	case REF_STATUS_REMOTE_REJECT:
+		print_ref_status('!', "[remote rejected]", ref,
+				ref->deletion ? NULL : ref->peer_ref,
+				ref->remote_status);
+		break;
+	case REF_STATUS_EXPECTING_REPORT:
+		print_ref_status('!', "[remote failure]", ref,
+				ref->deletion ? NULL : ref->peer_ref,
+				"remote failed to report status");
+		break;
+	case REF_STATUS_OK:
+		print_ok_ref_status(ref);
+		break;
+	}
+
+	return 1;
+}
+
+static void print_push_status(const char *dest, struct ref *refs, int verbose)
+{
+	struct ref *ref;
+	int n = 0;
+
+	if (verbose) {
+		for (ref = refs; ref; ref = ref->next)
+			if (ref->status == REF_STATUS_UPTODATE)
+				n += print_one_push_status(ref, dest, n);
+	}
+
+	for (ref = refs; ref; ref = ref->next)
+		if (ref->status == REF_STATUS_OK)
+			n += print_one_push_status(ref, dest, n);
+
+	for (ref = refs; ref; ref = ref->next) {
+		if (ref->status != REF_STATUS_NONE &&
+		    ref->status != REF_STATUS_UPTODATE &&
+		    ref->status != REF_STATUS_OK)
+			n += print_one_push_status(ref, dest, n);
+	}
+}
+
+static void verify_remote_names(int nr_heads, const char **heads)
+{
+	int i;
+
+	for (i = 0; i < nr_heads; i++) {
+		const char *local = heads[i];
+		const char *remote = strrchr(heads[i], ':');
+
+		if (*local == '+')
+			local++;
+
+		/* A matching refspec is okay.  */
+		if (remote == local && remote[1] == '\0')
+			continue;
+
+		remote = remote ? (remote + 1) : local;
+		switch (check_ref_format(remote)) {
+		case 0: /* ok */
+		case CHECK_REF_FORMAT_ONELEVEL:
+			/* ok but a single level -- that is fine for
+			 * a match pattern.
+			 */
+		case CHECK_REF_FORMAT_WILDCARD:
+			/* ok but ends with a pattern-match character */
+			continue;
+		}
+		die("remote part of refspec is not a valid name in %s",
+		    heads[i]);
+	}
+}
+
+static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)
 {
 	struct git_transport_data *data = transport->data;
 	struct send_pack_args args;
+	int ret;
 
-	args.receivepack = data->receivepack;
-	args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
+	if (!data->conn) {
+		struct ref *tmp_refs;
+		connect_setup(transport, 1, 0);
+
+		get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
+				 NULL);
+	}
+
 	args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
 	args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
 	args.use_thin_pack = data->thin;
 	args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
 	args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
 
-	return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
+	ret = send_pack(&args, data->fd, data->conn, remote_refs,
+			&data->extra_have);
+
+	close(data->fd[1]);
+	close(data->fd[0]);
+	ret |= finish_connect(data->conn);
+	data->conn = NULL;
+
+	return ret;
 }
 
 static int disconnect_git(struct transport *transport)
@@ -727,7 +939,7 @@
 	ret->remote = remote;
 	ret->url = url;
 
-	if (!prefixcmp(url, "rsync://")) {
+	if (!prefixcmp(url, "rsync:")) {
 		ret->get_refs_list = get_refs_via_rsync;
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
@@ -757,7 +969,7 @@
 		ret->set_option = set_git_option;
 		ret->get_refs_list = get_refs_via_connect;
 		ret->fetch = fetch_refs_via_pack;
-		ret->push = git_transport_push;
+		ret->push_refs = git_transport_push;
 		ret->disconnect = disconnect_git;
 
 		data->thin = 1;
@@ -784,15 +996,53 @@
 int transport_push(struct transport *transport,
 		   int refspec_nr, const char **refspec, int flags)
 {
-	if (!transport->push)
-		return 1;
-	return transport->push(transport, refspec_nr, refspec, flags);
+	verify_remote_names(refspec_nr, refspec);
+
+	if (transport->push)
+		return transport->push(transport, refspec_nr, refspec, flags);
+	if (transport->push_refs) {
+		struct ref *remote_refs =
+			transport->get_refs_list(transport, 1);
+		struct ref **remote_tail;
+		struct ref *local_refs = get_local_heads();
+		int match_flags = MATCH_REFS_NONE;
+		int verbose = flags & TRANSPORT_PUSH_VERBOSE;
+		int ret;
+
+		if (flags & TRANSPORT_PUSH_ALL)
+			match_flags |= MATCH_REFS_ALL;
+		if (flags & TRANSPORT_PUSH_MIRROR)
+			match_flags |= MATCH_REFS_MIRROR;
+
+		remote_tail = &remote_refs;
+		while (*remote_tail)
+			remote_tail = &((*remote_tail)->next);
+		if (match_refs(local_refs, remote_refs, &remote_tail,
+			       refspec_nr, refspec, match_flags)) {
+			return -1;
+		}
+
+		ret = transport->push_refs(transport, remote_refs, flags);
+
+		print_push_status(transport->url, remote_refs, verbose);
+
+		if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
+			struct ref *ref;
+			for (ref = remote_refs; ref; ref = ref->next)
+				update_tracking_ref(transport->remote, ref, verbose);
+		}
+
+		if (!ret && !refs_pushed(remote_refs))
+			fprintf(stderr, "Everything up-to-date\n");
+		return ret;
+	}
+	return 1;
 }
 
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
 	if (!transport->remote_refs)
-		transport->remote_refs = transport->get_refs_list(transport);
+		transport->remote_refs = transport->get_refs_list(transport, 0);
 	return transport->remote_refs;
 }
 
diff --git a/transport.h b/transport.h
index d0b5205..b1c2252 100644
--- a/transport.h
+++ b/transport.h
@@ -18,13 +18,16 @@
 	int (*set_option)(struct transport *connection, const char *name,
 			  const char *value);
 
-	struct ref *(*get_refs_list)(struct transport *transport);
+	struct ref *(*get_refs_list)(struct transport *transport, int for_push);
 	int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs);
+	int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 
 	int (*disconnect)(struct transport *connection);
 	char *pack_lockfile;
 	signed verbose : 2;
+	/* Force progress even if the output is not a tty */
+	unsigned progress : 1;
 };
 
 #define TRANSPORT_PUSH_ALL 1
diff --git a/tree-diff.c b/tree-diff.c
index 9f67af6..b05d0f4 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -118,10 +118,16 @@
 				continue;
 
 			/*
-			 * The base is a subdirectory of a path which
-			 * was specified, so all of them are interesting.
+			 * If the base is a subdirectory of a path which
+			 * was specified, all of them are interesting.
 			 */
-			return 2;
+			if (!matchlen ||
+			    base[matchlen] == '/' ||
+			    match[matchlen - 1] == '/')
+				return 2;
+
+			/* Just a random prefix match */
+			continue;
 		}
 
 		/* Does the base match? */
diff --git a/tree.c b/tree.c
index 03e782a..0d703a0 100644
--- a/tree.c
+++ b/tree.c
@@ -60,8 +60,12 @@
 			/* If it doesn't match, move along... */
 			if (strncmp(base, match, matchlen))
 				continue;
-			/* The base is a subdirectory of a path which was specified. */
-			return 1;
+			/* pathspecs match only at the directory boundaries */
+			if (!matchlen ||
+			    base[matchlen] == '/' ||
+			    match[matchlen - 1] == '/')
+				return 1;
+			continue;
 		}
 
 		/* Does the base match? */
@@ -110,7 +114,7 @@
 		case 0:
 			continue;
 		case READ_TREE_RECURSIVE:
-			break;;
+			break;
 		default:
 			return -1;
 		}
@@ -131,6 +135,34 @@
 			if (retval)
 				return -1;
 			continue;
+		} else if (S_ISGITLINK(entry.mode)) {
+			int retval;
+			struct strbuf path;
+			unsigned int entrylen;
+			struct commit *commit;
+
+			entrylen = tree_entry_len(entry.path, entry.sha1);
+			strbuf_init(&path, baselen + entrylen + 1);
+			strbuf_add(&path, base, baselen);
+			strbuf_add(&path, entry.path, entrylen);
+			strbuf_addch(&path, '/');
+
+			commit = lookup_commit(entry.sha1);
+			if (!commit)
+				die("Commit %s in submodule path %s not found",
+				    sha1_to_hex(entry.sha1), path.buf);
+
+			if (parse_commit(commit))
+				die("Invalid commit %s in submodule path %s",
+				    sha1_to_hex(entry.sha1), path.buf);
+
+			retval = read_tree_recursive(commit->tree,
+						     path.buf, path.len,
+						     stage, match, fn, context);
+			strbuf_release(&path);
+			if (retval)
+				return -1;
+			continue;
 		}
 	}
 	return 0;
diff --git a/unimplemented.sh b/unimplemented.sh
new file mode 100644
index 0000000..5252de4
--- /dev/null
+++ b/unimplemented.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo >&2 "fatal: git was built without support for `basename $0` (@@REASON@@)."
+exit 128
diff --git a/unpack-file.c b/unpack-file.c
index bcdc8bb..75cd2f1 100644
--- a/unpack-file.c
+++ b/unpack-file.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "blob.h"
+#include "exec_cmd.h"
 
 static char *create_temp_file(unsigned char *sha1)
 {
@@ -25,8 +26,10 @@
 {
 	unsigned char sha1[20];
 
+	git_extract_argv0_path(argv[0]);
+
 	if (argc != 2)
-		usage("git-unpack-file <sha1>");
+		usage("git unpack-file <sha1>");
 	if (get_sha1(argv[1], sha1))
 		die("Not a valid object name %s", argv[1]);
 
diff --git a/unpack-trees.c b/unpack-trees.c
index 4229eec..e4eb8fa 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -7,6 +7,7 @@
 #include "unpack-trees.h"
 #include "progress.h"
 #include "refs.h"
+#include "attr.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -49,39 +50,20 @@
 	memcpy(new, ce, size);
 	new->next = NULL;
 	new->ce_flags = (new->ce_flags & ~clear) | set;
-	add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
+	add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
-/* Unlink the last component and attempt to remove leading
- * directories, in case this unlink is the removal of the
- * last entry in the directory -- empty directories are removed.
+/*
+ * Unlink the last component and schedule the leading directories for
+ * removal, such that empty directories get removed.
  */
 static void unlink_entry(struct cache_entry *ce)
 {
-	char *cp, *prev;
-	char *name = ce->name;
-
-	if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+	if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
 		return;
-	if (unlink(name))
+	if (unlink(ce->name))
 		return;
-	prev = NULL;
-	while (1) {
-		int status;
-		cp = strrchr(name, '/');
-		if (prev)
-			*prev = '/';
-		if (!cp)
-			break;
-
-		*cp = 0;
-		status = rmdir(name);
-		if (status) {
-			*cp = '/';
-			break;
-		}
-		prev = cp;
-	}
+	schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
 
 static struct checkout state;
@@ -105,6 +87,8 @@
 		cnt = 0;
 	}
 
+	if (o->update)
+		git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
@@ -112,11 +96,10 @@
 			display_progress(progress, ++cnt);
 			if (o->update)
 				unlink_entry(ce);
-			remove_index_entry_at(&o->result, i);
-			i--;
-			continue;
 		}
 	}
+	remove_marked_cache_entries(&o->result);
+	remove_scheduled_dirs();
 
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
@@ -130,6 +113,8 @@
 		}
 	}
 	stop_progress(&progress);
+	if (o->update)
+		git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
 	return errs != 0;
 }
 
@@ -286,9 +271,9 @@
 	if (o->merge)
 		return call_unpack_fn(src, o);
 
-	n += o->merge;
 	for (i = 0; i < n; i++)
-		add_entry(o, src[i], 0, 0);
+		if (src[i] && src[i] != o->df_conflict_entry)
+			add_entry(o, src[i], 0, 0);
 	return 0;
 }
 
@@ -380,8 +365,10 @@
 
 	memset(&o->result, 0, sizeof(o->result));
 	o->result.initialized = 1;
-	if (o->src_index)
-		o->result.timestamp = o->src_index->timestamp;
+	if (o->src_index) {
+		o->result.timestamp.sec = o->src_index->timestamp.sec;
+		o->result.timestamp.nsec = o->src_index->timestamp.nsec;
+	}
 	o->merge_size = len;
 
 	if (!dfc)
@@ -446,7 +433,7 @@
 {
 	struct stat st;
 
-	if (o->index_only || o->reset)
+	if (o->index_only || o->reset || ce_uptodate(ce))
 		return 0;
 
 	if (!lstat(ce->name, &st)) {
@@ -497,7 +484,7 @@
 	 * anything in the existing directory there.
 	 */
 	int namelen;
-	int pos, i;
+	int i;
 	struct dir_struct d;
 	char *pathbuf;
 	int cnt = 0;
@@ -518,24 +505,20 @@
 	 * in that directory.
 	 */
 	namelen = strlen(ce->name);
-	pos = index_name_pos(o->src_index, ce->name, namelen);
-	if (0 <= pos)
-		return cnt; /* we have it as nondirectory */
-	pos = -pos - 1;
-	for (i = pos; i < o->src_index->cache_nr; i++) {
-		struct cache_entry *ce = o->src_index->cache[i];
-		int len = ce_namelen(ce);
+	for (i = o->pos; i < o->src_index->cache_nr; i++) {
+		struct cache_entry *ce2 = o->src_index->cache[i];
+		int len = ce_namelen(ce2);
 		if (len < namelen ||
-		    strncmp(ce->name, ce->name, namelen) ||
-		    ce->name[namelen] != '/')
+		    strncmp(ce->name, ce2->name, namelen) ||
+		    ce2->name[namelen] != '/')
 			break;
 		/*
-		 * ce->name is an entry in the subdirectory.
+		 * ce2->name is an entry in the subdirectory.
 		 */
-		if (!ce_stage(ce)) {
-			if (verify_uptodate(ce, o))
+		if (!ce_stage(ce2)) {
+			if (verify_uptodate(ce2, o))
 				return -1;
-			add_entry(o, ce, CE_REMOVE, 0);
+			add_entry(o, ce2, CE_REMOVE, 0);
 		}
 		cnt++;
 	}
@@ -587,11 +570,11 @@
 	if (o->index_only || o->reset || !o->update)
 		return 0;
 
-	if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+	if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
 		return 0;
 
 	if (!lstat(ce->name, &st)) {
-		int cnt;
+		int ret;
 		int dtype = ce_to_dtype(ce);
 		struct cache_entry *result;
 
@@ -619,13 +602,15 @@
 			 * files that are in "foo/" we would lose
 			 * it.
 			 */
-			cnt = verify_clean_subdirectory(ce, action, o);
+			ret = verify_clean_subdirectory(ce, action, o);
+			if (ret < 0)
+				return ret;
 
 			/*
 			 * If this removed entries from the index,
 			 * what that means is:
 			 *
-			 * (1) the caller unpack_trees_rec() saw path/foo
+			 * (1) the caller unpack_callback() saw path/foo
 			 * in the index, and it has not removed it because
 			 * it thinks it is handling 'path' as blob with
 			 * D/F conflict;
@@ -638,7 +623,7 @@
 			 * We need to increment it by the number of
 			 * deleted entries here.
 			 */
-			o->pos += cnt;
+			o->pos += ret;
 			return 0;
 		}
 
diff --git a/update-server-info.c b/update-server-info.c
index 7e8209e..7b38fd8 100644
--- a/update-server-info.c
+++ b/update-server-info.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "exec_cmd.h"
 
 static const char update_server_info_usage[] =
 "git update-server-info [--force]";
@@ -19,6 +20,8 @@
 	if (i != ac)
 		usage(update_server_info_usage);
 
+	git_extract_argv0_path(av[0]);
+
 	setup_git_directory();
 
 	return !!update_server_info(force);
diff --git a/upload-pack.c b/upload-pack.c
index d8ce306..edc7861 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -11,7 +11,7 @@
 #include "list-objects.h"
 #include "run-command.h"
 
-static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
+static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=nn] <dir>";
 
 /* bits #0..7 in revision.h, #8..10 in commit.c */
 #define THEY_HAVE	(1u << 11)
@@ -66,7 +66,7 @@
 }
 
 static FILE *pack_pipe = NULL;
-static void show_commit(struct commit *commit)
+static void show_commit(struct commit *commit, void *data)
 {
 	if (commit->object.flags & BOUNDARY)
 		fputc('-', pack_pipe);
@@ -136,7 +136,7 @@
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
 	mark_edges_uninteresting(revs.commits, &revs, show_edge);
-	traverse_commit_list(&revs, show_commit, show_object);
+	traverse_commit_list(&revs, show_commit, show_object, NULL);
 	fflush(pack_pipe);
 	fclose(pack_pipe);
 	return 0;
@@ -399,12 +399,11 @@
 	static char line[1000];
 	unsigned char sha1[20];
 	char hex[41], last_hex[41];
-	int len;
 
 	save_commit_buffer = 0;
 
 	for(;;) {
-		len = packet_read_line(0, line, sizeof(line));
+		int len = packet_read_line(0, line, sizeof(line));
 		reset_timeout();
 
 		if (!len) {
@@ -412,7 +411,7 @@
 				packet_write(1, "NAK\n");
 			continue;
 		}
-		len = strip(line, len);
+		strip(line, len);
 		if (!prefixcmp(line, "have ")) {
 			switch (got_sha1(line+5, sha1)) {
 			case -1: /* they have what we do not */
@@ -618,6 +617,8 @@
 	int i;
 	int strict = 0;
 
+	git_extract_argv0_path(argv[0]);
+
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
@@ -645,7 +646,7 @@
 	dir = argv[i];
 
 	if (!enter_repo(dir, strict))
-		die("'%s': unable to chdir or not a git archive", dir);
+		die("'%s' does not appear to be a git repository", dir);
 	if (is_repository_shallow())
 		die("attempt to fetch/clone from a shallow repository");
 	if (getenv("GIT_DEBUG_SEND_PACK"))
diff --git a/usage.c b/usage.c
index a5fc4ec..820d09f 100644
--- a/usage.c
+++ b/usage.c
@@ -7,7 +7,7 @@
 
 static void report(const char *prefix, const char *err, va_list params)
 {
-	char msg[256];
+	char msg[1024];
 	vsnprintf(msg, sizeof(msg), err, params);
 	fprintf(stderr, "%s%s\n", prefix, msg);
 }
@@ -41,27 +41,11 @@
 static void (*error_routine)(const char *err, va_list params) = error_builtin;
 static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
 
-void set_usage_routine(void (*routine)(const char *err) NORETURN)
-{
-	usage_routine = routine;
-}
-
 void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN)
 {
 	die_routine = routine;
 }
 
-void set_error_routine(void (*routine)(const char *err, va_list params))
-{
-	error_routine = routine;
-}
-
-void set_warn_routine(void (*routine)(const char *warn, va_list params))
-{
-	warn_routine = routine;
-}
-
-
 void usage(const char *err)
 {
 	usage_routine(err);
diff --git a/userdiff.c b/userdiff.c
new file mode 100644
index 0000000..d556da9
--- /dev/null
+++ b/userdiff.c
@@ -0,0 +1,215 @@
+#include "userdiff.h"
+#include "cache.h"
+#include "attr.h"
+
+static struct userdiff_driver *drivers;
+static int ndrivers;
+static int drivers_alloc;
+
+#define PATTERNS(name, pattern, word_regex)			\
+	{ name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
+static struct userdiff_driver builtin_drivers[] = {
+PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+	 "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("java",
+	 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
+	 "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]="
+	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("objc",
+	 /* Negate C statements that can look like functions */
+	 "!^[ \t]*(do|for|if|else|return|switch|while)\n"
+	 /* Objective-C methods */
+	 "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
+	 /* C functions */
+	 "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+	 /* Objective-C class/protocol definitions */
+	 "^(@(implementation|interface|protocol)[ \t].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("pascal",
+	 "^((procedure|function|constructor|destructor|interface|"
+		"implementation|initialization|finalization)[ \t]*.*)$"
+	 "\n"
+	 "^(.*=[ \t]*(class|record).*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+	 "|<>|<=|>=|:=|\\.\\."
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("php", "^[\t ]*((function|class).*)",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
+	 "|[^[:space:]|[\x80-\xff]+"),
+	 /* -- */
+PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+	 /* -- */
+	 "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
+	 "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
+	 "|[^[:space:]|[\x80-\xff]+"),
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 "[={}\"]|[^={}\" \t]+"),
+PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
+	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
+PATTERNS("cpp",
+	 /* Jump targets or access declarations */
+	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
+	 /* C/++ functions/methods at top level */
+	 "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
+	 /* compound type at top level */
+	 "^((struct|class|enum)[^;]*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+{ "default", NULL, -1, { NULL, 0 } },
+};
+#undef PATTERNS
+
+static struct userdiff_driver driver_true = {
+	"diff=true",
+	NULL,
+	0,
+	{ NULL, 0 }
+};
+
+static struct userdiff_driver driver_false = {
+	"!diff",
+	NULL,
+	1,
+	{ NULL, 0 }
+};
+
+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, int len)
+{
+	int i;
+	for (i = 0; i < ndrivers; i++) {
+		struct userdiff_driver *drv = drivers + i;
+		if (!strncmp(drv->name, k, len) && !drv->name[len])
+			return drv;
+	}
+	for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+		struct userdiff_driver *drv = builtin_drivers + i;
+		if (!strncmp(drv->name, k, len) && !drv->name[len])
+			return drv;
+	}
+	return NULL;
+}
+
+static struct userdiff_driver *parse_driver(const char *var,
+		const char *value, const char *type)
+{
+	struct userdiff_driver *drv;
+	const char *dot;
+	const char *name;
+	int namelen;
+
+	if (prefixcmp(var, "diff."))
+		return NULL;
+	dot = strrchr(var, '.');
+	if (dot == var + 4)
+		return NULL;
+	if (strcmp(type, dot+1))
+		return NULL;
+
+	name = var + 5;
+	namelen = dot - name;
+	drv = userdiff_find_by_namelen(name, namelen);
+	if (!drv) {
+		ALLOC_GROW(drivers, ndrivers+1, drivers_alloc);
+		drv = &drivers[ndrivers++];
+		memset(drv, 0, sizeof(*drv));
+		drv->name = xmemdupz(name, namelen);
+		drv->binary = -1;
+	}
+	return drv;
+}
+
+static int parse_funcname(struct userdiff_funcname *f, const char *k,
+		const char *v, int cflags)
+{
+	if (git_config_string(&f->pattern, k, v) < 0)
+		return -1;
+	f->cflags = cflags;
+	return 1;
+}
+
+static int parse_string(const char **d, const char *k, const char *v)
+{
+	if (git_config_string(d, k, v) < 0)
+		return -1;
+	return 1;
+}
+
+static int parse_tristate(int *b, const char *k, const char *v)
+{
+	if (v && !strcasecmp(v, "auto"))
+		*b = -1;
+	else
+		*b = git_config_bool(k, v);
+	return 1;
+}
+
+int userdiff_config(const char *k, const char *v)
+{
+	struct userdiff_driver *drv;
+
+	if ((drv = parse_driver(k, v, "funcname")))
+		return parse_funcname(&drv->funcname, k, v, 0);
+	if ((drv = parse_driver(k, v, "xfuncname")))
+		return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
+	if ((drv = parse_driver(k, v, "binary")))
+		return parse_tristate(&drv->binary, k, v);
+	if ((drv = parse_driver(k, v, "command")))
+		return parse_string(&drv->external, k, v);
+	if ((drv = parse_driver(k, v, "textconv")))
+		return parse_string(&drv->textconv, k, v);
+	if ((drv = parse_driver(k, v, "wordregex")))
+		return parse_string(&drv->word_regex, k, v);
+
+	return 0;
+}
+
+struct userdiff_driver *userdiff_find_by_name(const char *name) {
+	int len = strlen(name);
+	return userdiff_find_by_namelen(name, len);
+}
+
+struct userdiff_driver *userdiff_find_by_path(const char *path)
+{
+	static struct git_attr *attr;
+	struct git_attr_check check;
+
+	if (!attr)
+		attr = git_attr("diff", 4);
+	check.attr = attr;
+
+	if (!path)
+		return NULL;
+	if (git_checkattr(path, 1, &check))
+		return NULL;
+
+	if (ATTR_TRUE(check.value))
+		return &driver_true;
+	if (ATTR_FALSE(check.value))
+		return &driver_false;
+	if (ATTR_UNSET(check.value))
+		return NULL;
+	return userdiff_find_by_name(check.value);
+}
diff --git a/userdiff.h b/userdiff.h
new file mode 100644
index 0000000..c315159
--- /dev/null
+++ b/userdiff.h
@@ -0,0 +1,22 @@
+#ifndef USERDIFF_H
+#define USERDIFF_H
+
+struct userdiff_funcname {
+	const char *pattern;
+	int cflags;
+};
+
+struct userdiff_driver {
+	const char *name;
+	const char *external;
+	int binary;
+	struct userdiff_funcname funcname;
+	const char *word_regex;
+	const char *textconv;
+};
+
+int userdiff_config(const char *k, const char *v);
+struct userdiff_driver *userdiff_find_by_name(const char *name);
+struct userdiff_driver *userdiff_find_by_path(const char *path);
+
+#endif /* USERDIFF */
diff --git a/utf8.c b/utf8.c
index dc37353..ddfdc5e 100644
--- a/utf8.c
+++ b/utf8.c
@@ -246,6 +246,25 @@
 	return git_wcwidth(ch);
 }
 
+/*
+ * Returns the total number of columns required by a null-terminated
+ * string, assuming that the string is utf8.  Returns strlen() instead
+ * if the string does not look like a valid utf8 string.
+ */
+int utf8_strwidth(const char *string)
+{
+	int width = 0;
+	const char *orig = string;
+
+	while (1) {
+		if (!string)
+			return strlen(orig);
+		if (!*string)
+			return width;
+		width += utf8_width(&string, NULL);
+	}
+}
+
 int is_utf8(const char *text)
 {
 	while (*text) {
diff --git a/utf8.h b/utf8.h
index 98cce1b..2f1b14f 100644
--- a/utf8.h
+++ b/utf8.h
@@ -5,6 +5,7 @@
 
 ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p);
 int utf8_width(const char **start, size_t *remainder_p);
+int utf8_strwidth(const char *string);
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 
diff --git a/var.c b/var.c
index f1eb314..7362ed8 100644
--- a/var.c
+++ b/var.c
@@ -4,6 +4,7 @@
  * Copyright (C) Eric Biederman, 2005
  */
 #include "cache.h"
+#include "exec_cmd.h"
 
 static const char var_usage[] = "git var [-l | <variable>]";
 
@@ -56,6 +57,8 @@
 		usage(var_usage);
 	}
 
+	git_extract_argv0_path(argv[0]);
+
 	setup_git_directory_gently(&nongit);
 	val = NULL;
 
diff --git a/walker.c b/walker.c
index 0e68ee6..e57630e 100644
--- a/walker.c
+++ b/walker.c
@@ -18,7 +18,7 @@
 static void report_missing(const struct object *obj)
 {
 	char missing_hex[41];
-	strcpy(missing_hex, sha1_to_hex(obj->sha1));;
+	strcpy(missing_hex, sha1_to_hex(obj->sha1));
 	fprintf(stderr, "Cannot obtain needed %s %s\n",
 		obj->type ? typename(obj->type): "object", missing_hex);
 	if (!is_null_sha1(current_commit_sha1))
@@ -191,7 +191,7 @@
 	if (!get_sha1_hex(target, sha1))
 		return 0;
 	if (!check_ref_format(target)) {
-		struct ref *ref = alloc_ref_from_str(target);
+		struct ref *ref = alloc_ref(target);
 		if (!walker->fetch_ref(walker, ref)) {
 			hashcpy(sha1, ref->old_sha1);
 			free(ref);
@@ -215,9 +215,8 @@
 int walker_targets_stdin(char ***target, const char ***write_ref)
 {
 	int targets = 0, targets_alloc = 0;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 	*target = NULL; *write_ref = NULL;
-	strbuf_init(&buf, 0);
 	while (1) {
 		char *rf_one = NULL;
 		char *tg_one;
diff --git a/wrapper.c b/wrapper.c
index 93562f0..d8efb13 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -196,3 +196,96 @@
 		die("Unable to create temporary file: %s", strerror(errno));
 	return fd;
 }
+
+/*
+ * zlib wrappers to make sure we don't silently miss errors
+ * at init time.
+ */
+void git_inflate_init(z_streamp strm)
+{
+	const char *err;
+
+	switch (inflateInit(strm)) {
+	case Z_OK:
+		return;
+
+	case Z_MEM_ERROR:
+		err = "out of memory";
+		break;
+	case Z_VERSION_ERROR:
+		err = "wrong version";
+		break;
+	default:
+		err = "error";
+	}
+	die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
+}
+
+void git_inflate_end(z_streamp strm)
+{
+	if (inflateEnd(strm) != Z_OK)
+		error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
+}
+
+int git_inflate(z_streamp strm, int flush)
+{
+	int ret = inflate(strm, flush);
+	const char *err;
+
+	switch (ret) {
+	/* Out of memory is fatal. */
+	case Z_MEM_ERROR:
+		die("inflate: out of memory");
+
+	/* Data corruption errors: we may want to recover from them (fsck) */
+	case Z_NEED_DICT:
+		err = "needs dictionary"; break;
+	case Z_DATA_ERROR:
+		err = "data stream error"; break;
+	case Z_STREAM_ERROR:
+		err = "stream consistency error"; break;
+	default:
+		err = "unknown error"; break;
+
+	/* Z_BUF_ERROR: normal, needs more space in the output buffer */
+	case Z_BUF_ERROR:
+	case Z_OK:
+	case Z_STREAM_END:
+		return ret;
+	}
+	error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
+	return ret;
+}
+
+int odb_mkstemp(char *template, size_t limit, const char *pattern)
+{
+	int fd;
+
+	snprintf(template, limit, "%s/%s",
+		 get_object_directory(), pattern);
+	fd = mkstemp(template);
+	if (0 <= fd)
+		return fd;
+
+	/* slow path */
+	/* some mkstemp implementations erase template on failure */
+	snprintf(template, limit, "%s/%s",
+		 get_object_directory(), pattern);
+	safe_create_leading_directories(template);
+	return xmkstemp(template);
+}
+
+int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
+{
+	int fd;
+
+	snprintf(name, namesz, "%s/pack/pack-%s.keep",
+		 get_object_directory(), sha1_to_hex(sha1));
+	fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+	if (0 <= fd)
+		return fd;
+
+	/* slow path */
+	safe_create_leading_directories(name);
+	return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+}
diff --git a/ws.c b/ws.c
index 7a7ff13..b1efcd9 100644
--- a/ws.c
+++ b/ws.c
@@ -99,8 +99,7 @@
 /* The returned string should be freed by the caller. */
 char *whitespace_error_string(unsigned ws)
 {
-	struct strbuf err;
-	strbuf_init(&err, 0);
+	struct strbuf err = STRBUF_INIT;
 	if (ws & WS_TRAILING_SPACE)
 		strbuf_addstr(&err, "trailing whitespace");
 	if (ws & WS_SPACE_BEFORE_TAB) {
diff --git a/wt-status.c b/wt-status.c
index 64cedfc..929b00f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,19 +15,13 @@
 int wt_status_use_color = -1;
 int wt_status_submodule_summary;
 static char wt_status_colors[][COLOR_MAXLEN] = {
-	"",         /* WT_STATUS_HEADER: normal */
-	"\033[32m", /* WT_STATUS_UPDATED: green */
-	"\033[31m", /* WT_STATUS_CHANGED: red */
-	"\033[31m", /* WT_STATUS_UNTRACKED: red */
-	"\033[31m", /* WT_STATUS_NOBRANCH: red */
+	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
+	GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
+	GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
+	GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
+	GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
 };
 
-static const char use_add_msg[] =
-"use \"git add <file>...\" to update what will be committed";
-static const char use_add_rm_msg[] =
-"use \"git add/rm <file>...\" to update what will be committed";
-static const char use_add_to_include_msg[] =
-"use \"git add <file>...\" to include in what will be committed";
 enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 
 static int parse_status_slot(const char *var, int offset)
@@ -76,12 +70,24 @@
 	color_fprintf_ln(s->fp, c, "#");
 }
 
-static void wt_status_print_header(struct wt_status *s,
-				   const char *main, const char *sub)
+static void wt_status_print_dirty_header(struct wt_status *s,
+					 int has_deleted)
 {
 	const char *c = color(WT_STATUS_HEADER);
-	color_fprintf_ln(s->fp, c, "# %s:", main);
-	color_fprintf_ln(s->fp, c, "#   (%s)", sub);
+	color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+	if (!has_deleted)
+		color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
+	else
+		color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
+	color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
+	color_fprintf_ln(s->fp, c, "#");
+}
+
+static void wt_status_print_untracked_header(struct wt_status *s)
+{
+	const char *c = color(WT_STATUS_HEADER);
+	color_fprintf_ln(s->fp, c, "# Untracked files:");
+	color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to include in what will be committed)");
 	color_fprintf_ln(s->fp, c, "#");
 }
 
@@ -97,10 +103,8 @@
 {
 	const char *c = color(t);
 	const char *one, *two;
-	struct strbuf onebuf, twobuf;
+	struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
 
-	strbuf_init(&onebuf, 0);
-	strbuf_init(&twobuf, 0);
 	one = quote_path(p->one->path, -1, &onebuf, s->prefix);
 	two = quote_path(p->two->path, -1, &twobuf, s->prefix);
 
@@ -166,14 +170,14 @@
 	struct wt_status *s = data;
 	int i;
 	if (q->nr) {
-		const char *msg = use_add_msg;
+		int has_deleted = 0;
 		s->workdir_dirty = 1;
 		for (i = 0; i < q->nr; i++)
 			if (q->queue[i]->status == DIFF_STATUS_DELETED) {
-				msg = use_add_rm_msg;
+				has_deleted = 1;
 				break;
 			}
-		wt_status_print_header(s, "Changed but not updated", msg);
+		wt_status_print_dirty_header(s, has_deleted);
 	}
 	for (i = 0; i < q->nr; i++)
 		wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
@@ -181,32 +185,12 @@
 		wt_status_print_trailer(s);
 }
 
-static void wt_status_print_initial(struct wt_status *s)
-{
-	int i;
-	struct strbuf buf;
-
-	strbuf_init(&buf, 0);
-	if (active_nr) {
-		s->commitable = 1;
-		wt_status_print_cached_header(s);
-	}
-	for (i = 0; i < active_nr; i++) {
-		color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-		color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s",
-				quote_path(active_cache[i]->name, -1,
-					   &buf, s->prefix));
-	}
-	if (active_nr)
-		wt_status_print_trailer(s);
-	strbuf_release(&buf);
-}
-
 static void wt_status_print_updated(struct wt_status *s)
 {
 	struct rev_info rev;
 	init_revisions(&rev, NULL);
-	setup_revisions(0, NULL, &rev, s->reference);
+	setup_revisions(0, NULL, &rev,
+		s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
 	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
 	rev.diffopt.format_callback = wt_status_print_updated_cb;
 	rev.diffopt.format_callback_data = s;
@@ -262,15 +246,13 @@
 	struct dir_struct dir;
 	int i;
 	int shown_header = 0;
-	struct strbuf buf;
+	struct strbuf buf = STRBUF_INIT;
 
-	strbuf_init(&buf, 0);
 	memset(&dir, 0, sizeof(dir));
 
-	if (!s->untracked) {
-		dir.show_other_directories = 1;
-		dir.hide_empty_directories = 1;
-	}
+	if (!s->untracked)
+		dir.flags |=
+			DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
 	setup_standard_excludes(&dir);
 
 	read_directory(&dir, ".", "", 0, NULL);
@@ -280,8 +262,7 @@
 			continue;
 		if (!shown_header) {
 			s->workdir_untracked = 1;
-			wt_status_print_header(s, "Untracked files",
-					       use_add_to_include_msg);
+			wt_status_print_untracked_header(s);
 			shown_header = 1;
 		}
 		color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
@@ -297,11 +278,21 @@
 	struct rev_info rev;
 
 	init_revisions(&rev, NULL);
-	setup_revisions(0, NULL, &rev, s->reference);
+	DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+	setup_revisions(0, NULL, &rev,
+		s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
 	rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 	rev.diffopt.detect_rename = 1;
 	rev.diffopt.file = s->fp;
 	rev.diffopt.close_file = 0;
+	/*
+	 * If we're not going to stdout, then we definitely don't
+	 * want color, since we are going to the commit message
+	 * file (and even the "auto" setting won't work, since it
+	 * will have checked isatty on stdout).
+	 */
+	if (s->fp != stdout)
+		DIFF_OPT_CLR(&rev.diffopt, COLOR_DIFF);
 	run_diff_index(&rev, 1);
 }
 
@@ -350,12 +341,9 @@
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
-		wt_status_print_initial(s);
-	}
-	else {
-		wt_status_print_updated(s);
 	}
 
+	wt_status_print_updated(s);
 	wt_status_print_changed(s);
 	if (wt_status_submodule_summary)
 		wt_status_print_submodule_summary(s);
@@ -364,7 +352,7 @@
 	else if (s->commitable)
 		 fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
 
-	if (s->verbose && !s->is_initial)
+	if (s->verbose)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
@@ -421,5 +409,5 @@
 			return error("Invalid untracked files mode '%s'", v);
 		return 0;
 	}
-	return git_color_default_config(k, v, cb);
+	return git_diff_ui_config(k, v, cb);
 }
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 3bf83f8..b9b0db8 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,15 +1,24 @@
 #include "cache.h"
 #include "xdiff-interface.h"
+#include "xdiff/xtypes.h"
+#include "xdiff/xdiffi.h"
+#include "xdiff/xemit.h"
+#include "xdiff/xmacros.h"
+
+struct xdiff_emit_state {
+	xdiff_emit_consume_fn consume;
+	void *consume_callback_data;
+	struct strbuf remainder;
+};
 
 static int parse_num(char **cp_p, int *num_p)
 {
 	char *cp = *cp_p;
 	int num = 0;
-	int read_some;
 
 	while ('0' <= *cp && *cp <= '9')
 		num = num * 10 + *cp++ - '0';
-	if (!(read_some = cp - *cp_p))
+	if (!(cp - *cp_p))
 		return -1;
 	*cp_p = cp;
 	*num_p = num;
@@ -55,13 +64,13 @@
 		unsigned long this_size;
 		ep = memchr(s, '\n', size);
 		this_size = (ep == NULL) ? size : (ep - s + 1);
-		priv->consume(priv, s, this_size);
+		priv->consume(priv->consume_callback_data, s, this_size);
 		size -= this_size;
 		s += this_size;
 	}
 }
 
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
 {
 	struct xdiff_emit_state *priv = priv_;
 	int i;
@@ -69,36 +78,22 @@
 	for (i = 0; i < nbuf; i++) {
 		if (mb[i].ptr[mb[i].size-1] != '\n') {
 			/* Incomplete line */
-			priv->remainder = xrealloc(priv->remainder,
-						   priv->remainder_size +
-						   mb[i].size);
-			memcpy(priv->remainder + priv->remainder_size,
-			       mb[i].ptr, mb[i].size);
-			priv->remainder_size += mb[i].size;
+			strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
 			continue;
 		}
 
 		/* we have a complete line */
-		if (!priv->remainder) {
+		if (!priv->remainder.len) {
 			consume_one(priv, mb[i].ptr, mb[i].size);
 			continue;
 		}
-		priv->remainder = xrealloc(priv->remainder,
-					   priv->remainder_size +
-					   mb[i].size);
-		memcpy(priv->remainder + priv->remainder_size,
-		       mb[i].ptr, mb[i].size);
-		consume_one(priv, priv->remainder,
-			    priv->remainder_size + mb[i].size);
-		free(priv->remainder);
-		priv->remainder = NULL;
-		priv->remainder_size = 0;
+		strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
+		consume_one(priv, priv->remainder.buf, priv->remainder.len);
+		strbuf_reset(&priv->remainder);
 	}
-	if (priv->remainder) {
-		consume_one(priv, priv->remainder, priv->remainder_size);
-		free(priv->remainder);
-		priv->remainder = NULL;
-		priv->remainder_size = 0;
+	if (priv->remainder.len) {
+		consume_one(priv, priv->remainder.buf, priv->remainder.len);
+		strbuf_reset(&priv->remainder);
 	}
 	return 0;
 }
@@ -141,6 +136,69 @@
 	return xdl_diff(&a, &b, xpp, xecfg, xecb);
 }
 
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+		  xdiff_emit_consume_fn fn, void *consume_callback_data,
+		  xpparam_t const *xpp,
+		  xdemitconf_t const *xecfg, xdemitcb_t *xecb)
+{
+	int ret;
+	struct xdiff_emit_state state;
+
+	memset(&state, 0, sizeof(state));
+	state.consume = fn;
+	state.consume_callback_data = consume_callback_data;
+	xecb->outf = xdiff_outf;
+	xecb->priv = &state;
+	strbuf_init(&state.remainder, 0);
+	ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
+	strbuf_release(&state.remainder);
+	return ret;
+}
+
+struct xdiff_emit_hunk_state {
+	xdiff_emit_hunk_consume_fn consume;
+	void *consume_callback_data;
+};
+
+static int process_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+			xdemitconf_t const *xecfg)
+{
+	long s1, s2, same, p_next, t_next;
+	xdchange_t *xch, *xche;
+	struct xdiff_emit_hunk_state *state = ecb->priv;
+	xdiff_emit_hunk_consume_fn fn = state->consume;
+	void *consume_callback_data = state->consume_callback_data;
+
+	for (xch = xscr; xch; xch = xche->next) {
+		xche = xdl_get_hunk(xch, xecfg);
+
+		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
+		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
+		same = s2 + XDL_MAX(xch->i1 - s1, 0);
+		p_next = xche->i1 + xche->chg1;
+		t_next = xche->i2 + xche->chg2;
+
+		fn(consume_callback_data, same, p_next, t_next);
+	}
+	return 0;
+}
+
+int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
+		   xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
+		   xpparam_t const *xpp, xdemitconf_t *xecfg)
+{
+	struct xdiff_emit_hunk_state state;
+	xdemitcb_t ecb;
+
+	memset(&state, 0, sizeof(state));
+	memset(&ecb, 0, sizeof(ecb));
+	state.consume = fn;
+	state.consume_callback_data = consume_callback_data;
+	xecfg->emit_func = (void (*)())process_diff;
+	ecb.priv = &state;
+	return xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
+}
+
 int read_mmfile(mmfile_t *ptr, const char *filename)
 {
 	struct stat st;
@@ -182,7 +240,8 @@
 	char *line_buffer;
 	struct ff_regs *regs = priv;
 	regmatch_t pmatch[2];
-	int result = 0, i;
+	int i;
+	int result = -1;
 
 	/* Exclude terminating newline (and cr) from matching */
 	if (len > 0 && line[len-1] == '\n') {
@@ -196,22 +255,24 @@
 
 	for (i = 0; i < regs->nr; i++) {
 		struct ff_reg *reg = regs->array + i;
-		if (reg->negate ^ !!regexec(&reg->re,
-					line_buffer, 2, pmatch, 0)) {
-			free(line_buffer);
-			return -1;
+		if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
+			if (reg->negate)
+				goto fail;
+			break;
 		}
 	}
+	if (regs->nr <= i)
+		goto fail;
 	i = pmatch[1].rm_so >= 0 ? 1 : 0;
 	line += pmatch[i].rm_so;
 	result = pmatch[i].rm_eo - pmatch[i].rm_so;
 	if (result > buffer_size)
 		result = buffer_size;
 	else
-		while (result > 0 && (isspace(line[result - 1]) ||
-					line[result - 1] == '\n'))
+		while (result > 0 && (isspace(line[result - 1])))
 			result--;
 	memcpy(buffer, line, result);
+ fail:
 	free(line_buffer);
 	return result;
 }
@@ -247,3 +308,22 @@
 		value = ep + 1;
 	}
 }
+
+int git_xmerge_style = -1;
+
+int git_xmerge_config(const char *var, const char *value, void *cb)
+{
+	if (!strcasecmp(var, "merge.conflictstyle")) {
+		if (!value)
+			die("'%s' is not a boolean", var);
+		if (!strcmp(value, "diff3"))
+			git_xmerge_style = XDL_MERGE_DIFF3;
+		else if (!strcmp(value, "merge"))
+			git_xmerge_style = 0;
+		else
+			die("unknown style '%s' given for '%s'",
+			    value, var);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 33cab9d..7352b9a 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -3,18 +3,17 @@
 
 #include "xdiff/xdiff.h"
 
-struct xdiff_emit_state;
-
 typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
-
-struct xdiff_emit_state {
-	xdiff_emit_consume_fn consume;
-	char *remainder;
-	unsigned long remainder_size;
-};
+typedef void (*xdiff_emit_hunk_consume_fn)(void *, long, long, long);
 
 int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+		  xdiff_emit_consume_fn fn, void *consume_callback_data,
+		  xpparam_t const *xpp,
+		  xdemitconf_t const *xecfg, xdemitcb_t *xecb);
+int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
+		   xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
+		   xpparam_t const *xpp, xdemitconf_t *xecfg);
 int parse_hunk_header(char *line, int len,
 		      int *ob, int *on,
 		      int *nb, int *nn);
@@ -22,5 +21,7 @@
 int buffer_is_binary(const char *ptr, unsigned long size);
 
 extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
+extern int git_xmerge_config(const char *var, const char *value, void *cb);
+extern int git_xmerge_style;
 
 #endif
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 413082e..4da052a 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -32,6 +32,7 @@
 #define XDF_IGNORE_WHITESPACE (1 << 2)
 #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
 #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
+#define XDF_PATIENCE_DIFF (1 << 5)
 #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
 
 #define XDL_PATCH_NORMAL '-'
@@ -50,10 +51,16 @@
 #define XDL_BDOP_CPY 2
 #define XDL_BDOP_INSB 3
 
+/* merge simplification levels */
 #define XDL_MERGE_MINIMAL 0
 #define XDL_MERGE_EAGER 1
 #define XDL_MERGE_ZEALOUS 2
 #define XDL_MERGE_ZEALOUS_ALNUM 3
+#define XDL_MERGE_LEVEL_MASK 0x0f
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 0x8000
+#define XDL_MERGE_STYLE_MASK 0x8000
 
 typedef struct s_mmfile {
 	char *ptr;
@@ -78,9 +85,11 @@
 
 typedef struct s_xdemitconf {
 	long ctxlen;
+	long interhunkctxlen;
 	unsigned long flags;
 	find_func_t find_func;
 	void *find_func_priv;
+	void (*emit_func)();
 } xdemitconf_t;
 
 typedef struct s_bdiffparam {
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 1bad846..02184d9 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -293,15 +293,14 @@
 		for (; off1 < lim1; off1++)
 			rchg1[rindex1[off1]] = 1;
 	} else {
-		long ec;
 		xdpsplit_t spl;
 		spl.i1 = spl.i2 = 0;
 
 		/*
 		 * Divide ...
 		 */
-		if ((ec = xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
-				    need_min, &spl, xenv)) < 0) {
+		if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
+			      need_min, &spl, xenv) < 0) {
 
 			return -1;
 		}
@@ -329,6 +328,9 @@
 	xdalgoenv_t xenv;
 	diffdata_t dd1, dd2;
 
+	if (xpp->flags & XDF_PATIENCE_DIFF)
+		return xdl_do_patience_diff(mf1, mf2, xpp, xe);
+
 	if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
 
 		return -1;
@@ -538,6 +540,8 @@
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
 	xdchange_t *xscr;
 	xdfenv_t xe;
+	emit_func_t ef = xecfg->emit_func ?
+		(emit_func_t)xecfg->emit_func : xdl_emit_diff;
 
 	if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
 
@@ -551,7 +555,7 @@
 		return -1;
 	}
 	if (xscr) {
-		if (xdl_emit_diff(&xe, xscr, ecb, xecfg) < 0) {
+		if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
 			xdl_free_script(xscr);
 			xdl_free_env(&xe);
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 3e099dc..ad033a8 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -55,5 +55,7 @@
 void xdl_free_script(xdchange_t *xscr);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		  xdemitconf_t const *xecfg);
+int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		xdfenv_t *env);
 
 #endif /* #if !defined(XDIFFI_H) */
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index d3d9c84..c4bedf0 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -27,7 +27,6 @@
 
 static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
 static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
-static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
 
 
 
@@ -58,11 +57,12 @@
  * Starting at the passed change atom, find the latest change atom to be included
  * inside the differential hunk according to the specified configuration.
  */
-static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
 	xdchange_t *xch, *xchp;
+	long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
 
 	for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
-		if (xch->i1 - (xchp->i1 + xchp->chg1) > 2 * xecfg->ctxlen)
+		if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
 			break;
 
 	return xchp;
@@ -132,7 +132,7 @@
 	if (xecfg->flags & XDL_EMIT_COMMON)
 		return xdl_emit_common(xe, xscr, ecb, xecfg);
 
-	for (xch = xche = xscr; xch; xch = xche->next) {
+	for (xch = xscr; xch; xch = xche->next) {
 		xche = xdl_get_hunk(xch, xecfg);
 
 		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
diff --git a/xdiff/xemit.h b/xdiff/xemit.h
index 440a739..c2e2e83 100644
--- a/xdiff/xemit.h
+++ b/xdiff/xemit.h
@@ -24,7 +24,10 @@
 #define XEMIT_H
 
 
+typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+			   xdemitconf_t const *xecfg);
 
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		  xdemitconf_t const *xecfg);
 
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 82b3573..d9737f0 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -30,17 +30,32 @@
 	 * 2 = no conflict, take second.
 	 */
 	int mode;
+	/*
+	 * These point at the respective postimages.  E.g. <i1,chg1> is
+	 * how side #1 wants to change the common ancestor; if there is no
+	 * overlap, lines before i1 in the postimage of side #1 appear
+	 * in the merge result as a region touched by neither side.
+	 */
 	long i1, i2;
 	long chg1, chg2;
+	/*
+	 * These point at the preimage; of course there is just one
+	 * preimage, that is from the shared common ancestor.
+	 */
+	long i0;
+	long chg0;
 } xdmerge_t;
 
 static int xdl_append_merge(xdmerge_t **merge, int mode,
-		long i1, long chg1, long i2, long chg2)
+			    long i0, long chg0,
+			    long i1, long chg1,
+			    long i2, long chg2)
 {
 	xdmerge_t *m = *merge;
 	if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
 		if (mode != m->mode)
 			m->mode = 0;
+		m->chg0 = i0 + chg0 - m->i0;
 		m->chg1 = i1 + chg1 - m->i1;
 		m->chg2 = i2 + chg2 - m->i2;
 	} else {
@@ -49,6 +64,8 @@
 			return -1;
 		m->next = NULL;
 		m->mode = mode;
+		m->i0 = i0;
+		m->chg0 = chg0;
 		m->i1 = i1;
 		m->chg1 = chg1;
 		m->i2 = i2;
@@ -91,11 +108,13 @@
 	return 0;
 }
 
-static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 {
-	xrecord_t **recs = xe->xdf2.recs + i;
+	xrecord_t **recs;
 	int size = 0;
 
+	recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
 	if (count < 1)
 		return 0;
 
@@ -113,65 +132,109 @@
 	return size;
 }
 
-static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
-		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+}
+
+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+			      xdfenv_t *xe2, const char *name2,
+			      int size, int i, int style,
+			      xdmerge_t *m, char *dest)
 {
 	const int marker_size = 7;
 	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
 	int marker2_size = (name2 ? strlen(name2) + 1 : 0);
-	int conflict_marker_size = 3 * (marker_size + 1)
-		+ marker1_size + marker2_size;
-	int size, i1, j;
+	int j;
 
-	for (size = i1 = 0; m; m = m->next) {
-		if (m->mode == 0) {
-			size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0,
-					dest ? dest + size : NULL);
-			if (dest) {
-				for (j = 0; j < marker_size; j++)
-					dest[size++] = '<';
-				if (marker1_size) {
-					dest[size] = ' ';
-					memcpy(dest + size + 1, name1,
-							marker1_size - 1);
-					size += marker1_size;
-				}
-				dest[size++] = '\n';
-			} else
-				size += conflict_marker_size;
-			size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
-					dest ? dest + size : NULL);
-			if (dest) {
-				for (j = 0; j < marker_size; j++)
-					dest[size++] = '=';
-				dest[size++] = '\n';
-			}
-			size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
-					dest ? dest + size : NULL);
-			if (dest) {
-				for (j = 0; j < marker_size; j++)
-					dest[size++] = '>';
-				if (marker2_size) {
-					dest[size] = ' ';
-					memcpy(dest + size + 1, name2,
-							marker2_size - 1);
-					size += marker2_size;
-				}
-				dest[size++] = '\n';
-			}
-		} else if (m->mode == 1)
-			size += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0,
-					dest ? dest + size : NULL);
+	/* Before conflicting part */
+	size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+			      dest ? dest + size : NULL);
+
+	if (!dest) {
+		size += marker_size + 1 + marker1_size;
+	} else {
+		for (j = 0; j < marker_size; j++)
+			dest[size++] = '<';
+		if (marker1_size) {
+			dest[size] = ' ';
+			memcpy(dest + size + 1, name1, marker1_size - 1);
+			size += marker1_size;
+		}
+		dest[size++] = '\n';
+	}
+
+	/* Postimage from side #1 */
+	size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+			      dest ? dest + size : NULL);
+
+	if (style == XDL_MERGE_DIFF3) {
+		/* Shared preimage */
+		if (!dest) {
+			size += marker_size + 1;
+		} else {
+			for (j = 0; j < marker_size; j++)
+				dest[size++] = '|';
+			dest[size++] = '\n';
+		}
+		size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
+				      dest ? dest + size : NULL);
+	}
+
+	if (!dest) {
+		size += marker_size + 1;
+	} else {
+		for (j = 0; j < marker_size; j++)
+			dest[size++] = '=';
+		dest[size++] = '\n';
+	}
+
+	/* Postimage from side #2 */
+	size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+			      dest ? dest + size : NULL);
+	if (!dest) {
+		size += marker_size + 1 + marker2_size;
+	} else {
+		for (j = 0; j < marker_size; j++)
+			dest[size++] = '>';
+		if (marker2_size) {
+			dest[size] = ' ';
+			memcpy(dest + size + 1, name2, marker2_size - 1);
+			size += marker2_size;
+		}
+		dest[size++] = '\n';
+	}
+	return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+				 xdfenv_t *xe2, const char *name2,
+				 xdmerge_t *m, char *dest, int style)
+{
+	int size, i;
+
+	for (size = i = 0; m; m = m->next) {
+		if (m->mode == 0)
+			size = fill_conflict_hunk(xe1, name1, xe2, name2,
+						  size, i, style, m, dest);
+		else if (m->mode == 1)
+			size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
+					      dest ? dest + size : NULL);
 		else if (m->mode == 2)
-			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1,
-					m->i1 + m->chg2 - i1, 0,
-					dest ? dest + size : NULL);
+			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i,
+					      m->i1 + m->chg2 - i, 0,
+					      dest ? dest + size : NULL);
 		else
 			continue;
-		i1 = m->i1 + m->chg1;
+		i = m->i1 + m->chg1;
 	}
-	size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0,
-			dest ? dest + size : NULL);
+	size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,
+			      dest ? dest + size : NULL);
 	return size;
 }
 
@@ -323,9 +386,20 @@
  */
 static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-		int level, xpparam_t const *xpp, mmbuffer_t *result) {
+		int flags, xpparam_t const *xpp, mmbuffer_t *result) {
 	xdmerge_t *changes, *c;
-	int i1, i2, chg1, chg2;
+	int i0, i1, i2, chg0, chg1, chg2;
+	int level = flags & XDL_MERGE_LEVEL_MASK;
+	int style = flags & XDL_MERGE_STYLE_MASK;
+
+	if (style == XDL_MERGE_DIFF3) {
+		/*
+		 * "diff3 -m" output does not make sense for anything
+		 * more aggressive than XDL_MERGE_EAGER.
+		 */
+		if (XDL_MERGE_EAGER < level)
+			level = XDL_MERGE_EAGER;
+	}
 
 	c = changes = NULL;
 
@@ -333,11 +407,14 @@
 		if (!changes)
 			changes = c;
 		if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+			i0 = xscr1->i1;
 			i1 = xscr1->i2;
 			i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+			chg0 = xscr1->chg1;
 			chg1 = xscr1->chg2;
 			chg2 = xscr1->chg1;
-			if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+			if (xdl_append_merge(&c, 1,
+					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
 				return -1;
 			}
@@ -345,18 +422,21 @@
 			continue;
 		}
 		if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+			i0 = xscr2->i1;
 			i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
 			i2 = xscr2->i2;
+			chg0 = xscr2->chg1;
 			chg1 = xscr2->chg1;
 			chg2 = xscr2->chg2;
-			if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+			if (xdl_append_merge(&c, 2,
+					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
 				return -1;
 			}
 			xscr2 = xscr2->next;
 			continue;
 		}
-		if (level < 1 || xscr1->i1 != xscr2->i1 ||
+		if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
 				xscr1->chg1 != xscr2->chg1 ||
 				xscr1->chg2 != xscr2->chg2 ||
 				xdl_merge_cmp_lines(xe1, xscr1->i2,
@@ -366,19 +446,25 @@
 			int off = xscr1->i1 - xscr2->i1;
 			int ffo = off + xscr1->chg1 - xscr2->chg1;
 
+			i0 = xscr1->i1;
 			i1 = xscr1->i2;
 			i2 = xscr2->i2;
-			if (off > 0)
+			if (off > 0) {
+				i0 -= off;
 				i1 -= off;
+			}
 			else
 				i2 += off;
+			chg0 = xscr1->i1 + xscr1->chg1 - i0;
 			chg1 = xscr1->i2 + xscr1->chg2 - i1;
 			chg2 = xscr2->i2 + xscr2->chg2 - i2;
-			if (ffo > 0)
-				chg2 += ffo;
-			else
+			if (ffo < 0) {
+				chg0 -= ffo;
 				chg1 -= ffo;
-			if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) {
+			} else
+				chg2 += ffo;
+			if (xdl_append_merge(&c, 0,
+					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
 				return -1;
 			}
@@ -395,11 +481,14 @@
 	while (xscr1) {
 		if (!changes)
 			changes = c;
+		i0 = xscr1->i1;
 		i1 = xscr1->i2;
 		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+		chg0 = xscr1->chg1;
 		chg1 = xscr1->chg2;
 		chg2 = xscr1->chg1;
-		if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+		if (xdl_append_merge(&c, 1,
+				     i0, chg0, i1, chg1, i2, chg2)) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
@@ -408,11 +497,14 @@
 	while (xscr2) {
 		if (!changes)
 			changes = c;
+		i0 = xscr2->i1;
 		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
 		i2 = xscr2->i2;
+		chg0 = xscr2->chg1;
 		chg1 = xscr2->chg1;
 		chg2 = xscr2->chg2;
-		if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+		if (xdl_append_merge(&c, 2,
+				     i0, chg0, i1, chg1, i2, chg2)) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
@@ -421,16 +513,17 @@
 	if (!changes)
 		changes = c;
 	/* refine conflicts */
-	if (level > 1 &&
+	if (XDL_MERGE_ZEALOUS <= level &&
 	    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
-	     xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) {
+	     xdl_simplify_non_conflicts(xe1, changes,
+					XDL_MERGE_ZEALOUS < level) < 0)) {
 		xdl_cleanup_merge(changes);
 		return -1;
 	}
 	/* output */
 	if (result) {
 		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
-			changes, NULL);
+			changes, NULL, style);
 		result->ptr = xdl_malloc(size);
 		if (!result->ptr) {
 			xdl_cleanup_merge(changes);
@@ -438,14 +531,14 @@
 		}
 		result->size = size;
 		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
-				result->ptr);
+				      result->ptr, style);
 	}
 	return xdl_cleanup_merge(changes);
 }
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result) {
+		xpparam_t const *xpp, int flags, mmbuffer_t *result) {
 	xdchange_t *xscr1, *xscr2;
 	xdfenv_t xe1, xe2;
 	int status;
@@ -482,7 +575,7 @@
 		} else {
 			status = xdl_do_merge(&xe1, xscr1, name1,
 					      &xe2, xscr2, name2,
-					      level, xpp, result);
+					      flags, xpp, result);
 		}
 		xdl_free_script(xscr1);
 		xdl_free_script(xscr2);
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
new file mode 100644
index 0000000..e42c16a
--- /dev/null
+++ b/xdiff/xpatience.c
@@ -0,0 +1,381 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+/*
+ * The basic idea of patience diff is to find lines that are unique in
+ * both files.  These are intuitively the ones that we want to see as
+ * common lines.
+ *
+ * The maximal ordered sequence of such line pairs (where ordered means
+ * that the order in the sequence agrees with the order of the lines in
+ * both files) naturally defines an initial set of common lines.
+ *
+ * Now, the algorithm tries to extend the set of common lines by growing
+ * the line ranges where the files have identical lines.
+ *
+ * Between those common lines, the patience diff algorithm is applied
+ * recursively, until no unique line pairs can be found; these line ranges
+ * are handled by the well-known Myers algorithm.
+ */
+
+#define NON_UNIQUE ULONG_MAX
+
+/*
+ * This is a hash mapping from line hash to line numbers in the first and
+ * second file.
+ */
+struct hashmap {
+	int nr, alloc;
+	struct entry {
+		unsigned long hash;
+		/*
+		 * 0 = unused entry, 1 = first line, 2 = second, etc.
+		 * line2 is NON_UNIQUE if the line is not unique
+		 * in either the first or the second file.
+		 */
+		unsigned long line1, line2;
+		/*
+		 * "next" & "previous" are used for the longest common
+		 * sequence;
+		 * initially, "next" reflects only the order in file1.
+		 */
+		struct entry *next, *previous;
+	} *entries, *first, *last;
+	/* were common records found? */
+	unsigned long has_matches;
+	mmfile_t *file1, *file2;
+	xdfenv_t *env;
+	xpparam_t const *xpp;
+};
+
+/* The argument "pass" is 1 for the first file, 2 for the second. */
+static void insert_record(int line, struct hashmap *map, int pass)
+{
+	xrecord_t **records = pass == 1 ?
+		map->env->xdf1.recs : map->env->xdf2.recs;
+	xrecord_t *record = records[line - 1], *other;
+	/*
+	 * After xdl_prepare_env() (or more precisely, due to
+	 * xdl_classify_record()), the "ha" member of the records (AKA lines)
+	 * is _not_ the hash anymore, but a linearized version of it.  In
+	 * other words, the "ha" member is guaranteed to start with 0 and
+	 * the second record's ha can only be 0 or 1, etc.
+	 *
+	 * So we multiply ha by 2 in the hope that the hashing was
+	 * "unique enough".
+	 */
+	int index = (int)((record->ha << 1) % map->alloc);
+
+	while (map->entries[index].line1) {
+		other = map->env->xdf1.recs[map->entries[index].line1 - 1];
+		if (map->entries[index].hash != record->ha ||
+				!xdl_recmatch(record->ptr, record->size,
+					other->ptr, other->size,
+					map->xpp->flags)) {
+			if (++index >= map->alloc)
+				index = 0;
+			continue;
+		}
+		if (pass == 2)
+			map->has_matches = 1;
+		if (pass == 1 || map->entries[index].line2)
+			map->entries[index].line2 = NON_UNIQUE;
+		else
+			map->entries[index].line2 = line;
+		return;
+	}
+	if (pass == 2)
+		return;
+	map->entries[index].line1 = line;
+	map->entries[index].hash = record->ha;
+	if (!map->first)
+		map->first = map->entries + index;
+	if (map->last) {
+		map->last->next = map->entries + index;
+		map->entries[index].previous = map->last;
+	}
+	map->last = map->entries + index;
+	map->nr++;
+}
+
+/*
+ * This function has to be called for each recursion into the inter-hunk
+ * parts, as previously non-unique lines can become unique when being
+ * restricted to a smaller part of the files.
+ *
+ * It is assumed that env has been prepared using xdl_prepare().
+ */
+static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env,
+		struct hashmap *result,
+		int line1, int count1, int line2, int count2)
+{
+	result->file1 = file1;
+	result->file2 = file2;
+	result->xpp = xpp;
+	result->env = env;
+
+	/* We know exactly how large we want the hash map */
+	result->alloc = count1 * 2;
+	result->entries = (struct entry *)
+		xdl_malloc(result->alloc * sizeof(struct entry));
+	if (!result->entries)
+		return -1;
+	memset(result->entries, 0, result->alloc * sizeof(struct entry));
+
+	/* First, fill with entries from the first file */
+	while (count1--)
+		insert_record(line1++, result, 1);
+
+	/* Then search for matches in the second file */
+	while (count2--)
+		insert_record(line2++, result, 2);
+
+	return 0;
+}
+
+/*
+ * Find the longest sequence with a smaller last element (meaning a smaller
+ * line2, as we construct the sequence with entries ordered by line1).
+ */
+static int binary_search(struct entry **sequence, int longest,
+		struct entry *entry)
+{
+	int left = -1, right = longest;
+
+	while (left + 1 < right) {
+		int middle = (left + right) / 2;
+		/* by construction, no two entries can be equal */
+		if (sequence[middle]->line2 > entry->line2)
+			right = middle;
+		else
+			left = middle;
+	}
+	/* return the index in "sequence", _not_ the sequence length */
+	return left;
+}
+
+/*
+ * The idea is to start with the list of common unique lines sorted by
+ * the order in file1.  For each of these pairs, the longest (partial)
+ * sequence whose last element's line2 is smaller is determined.
+ *
+ * For efficiency, the sequences are kept in a list containing exactly one
+ * item per sequence length: the sequence with the smallest last
+ * element (in terms of line2).
+ */
+static struct entry *find_longest_common_sequence(struct hashmap *map)
+{
+	struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
+	int longest = 0, i;
+	struct entry *entry;
+
+	for (entry = map->first; entry; entry = entry->next) {
+		if (!entry->line2 || entry->line2 == NON_UNIQUE)
+			continue;
+		i = binary_search(sequence, longest, entry);
+		entry->previous = i < 0 ? NULL : sequence[i];
+		sequence[++i] = entry;
+		if (i == longest)
+			longest++;
+	}
+
+	/* No common unique lines were found */
+	if (!longest) {
+		xdl_free(sequence);
+		return NULL;
+	}
+
+	/* Iterate starting at the last element, adjusting the "next" members */
+	entry = sequence[longest - 1];
+	entry->next = NULL;
+	while (entry->previous) {
+		entry->previous->next = entry;
+		entry = entry->previous;
+	}
+	xdl_free(sequence);
+	return entry;
+}
+
+static int match(struct hashmap *map, int line1, int line2)
+{
+	xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
+	xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
+	return xdl_recmatch(record1->ptr, record1->size,
+		record2->ptr, record2->size, map->xpp->flags);
+}
+
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env,
+		int line1, int count1, int line2, int count2);
+
+static int walk_common_sequence(struct hashmap *map, struct entry *first,
+		int line1, int count1, int line2, int count2)
+{
+	int end1 = line1 + count1, end2 = line2 + count2;
+	int next1, next2;
+
+	for (;;) {
+		/* Try to grow the line ranges of common lines */
+		if (first) {
+			next1 = first->line1;
+			next2 = first->line2;
+			while (next1 > line1 && next2 > line2 &&
+					match(map, next1 - 1, next2 - 1)) {
+				next1--;
+				next2--;
+			}
+		} else {
+			next1 = end1;
+			next2 = end2;
+		}
+		while (line1 < next1 && line2 < next2 &&
+				match(map, line1, line2)) {
+			line1++;
+			line2++;
+		}
+
+		/* Recurse */
+		if (next1 > line1 || next2 > line2) {
+			struct hashmap submap;
+
+			memset(&submap, 0, sizeof(submap));
+			if (patience_diff(map->file1, map->file2,
+					map->xpp, map->env,
+					line1, next1 - line1,
+					line2, next2 - line2))
+				return -1;
+		}
+
+		if (!first)
+			return 0;
+
+		while (first->next &&
+				first->next->line1 == first->line1 + 1 &&
+				first->next->line2 == first->line2 + 1)
+			first = first->next;
+
+		line1 = first->line1 + 1;
+		line2 = first->line2 + 1;
+
+		first = first->next;
+	}
+}
+
+static int fall_back_to_classic_diff(struct hashmap *map,
+		int line1, int count1, int line2, int count2)
+{
+	/*
+	 * This probably does not work outside Git, since
+	 * we have a very simple mmfile structure.
+	 *
+	 * Note: ideally, we would reuse the prepared environment, but
+	 * the libxdiff interface does not (yet) allow for diffing only
+	 * ranges of lines instead of the whole files.
+	 */
+	mmfile_t subfile1, subfile2;
+	xpparam_t xpp;
+	xdfenv_t env;
+
+	subfile1.ptr = (char *)map->env->xdf1.recs[line1 - 1]->ptr;
+	subfile1.size = map->env->xdf1.recs[line1 + count1 - 2]->ptr +
+		map->env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+	subfile2.ptr = (char *)map->env->xdf2.recs[line2 - 1]->ptr;
+	subfile2.size = map->env->xdf2.recs[line2 + count2 - 2]->ptr +
+		map->env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+	xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+	if (xdl_do_diff(&subfile1, &subfile2, &xpp, &env) < 0)
+		return -1;
+
+	memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+	memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+	xdl_free_env(&env);
+
+	return 0;
+}
+
+/*
+ * Recursively find the longest common sequence of unique lines,
+ * and if none was found, ask xdl_do_diff() to do the job.
+ *
+ * This function assumes that env was prepared with xdl_prepare_env().
+ */
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env,
+		int line1, int count1, int line2, int count2)
+{
+	struct hashmap map;
+	struct entry *first;
+	int result = 0;
+
+	/* trivial case: one side is empty */
+	if (!count1) {
+		while(count2--)
+			env->xdf2.rchg[line2++ - 1] = 1;
+		return 0;
+	} else if (!count2) {
+		while(count1--)
+			env->xdf1.rchg[line1++ - 1] = 1;
+		return 0;
+	}
+
+	memset(&map, 0, sizeof(map));
+	if (fill_hashmap(file1, file2, xpp, env, &map,
+			line1, count1, line2, count2))
+		return -1;
+
+	/* are there any matching lines at all? */
+	if (!map.has_matches) {
+		while(count1--)
+			env->xdf1.rchg[line1++ - 1] = 1;
+		while(count2--)
+			env->xdf2.rchg[line2++ - 1] = 1;
+		xdl_free(map.entries);
+		return 0;
+	}
+
+	first = find_longest_common_sequence(&map);
+	if (first)
+		result = walk_common_sequence(&map, first,
+			line1, count1, line2, count2);
+	else
+		result = fall_back_to_classic_diff(&map,
+			line1, count1, line2, count2);
+
+	xdl_free(map.entries);
+	return result;
+}
+
+int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env)
+{
+	if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+		return -1;
+
+	/* environment is cleaned up in xdl_diff() */
+	return patience_diff(file1, file2, xpp, env,
+			1, env->xdf1.nrec, 1, env->xdf2.nrec);
+}
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index a43aa72..1689085 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -290,7 +290,8 @@
 
 	xdl_free_classifier(&cf);
 
-	if (xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
+	if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+			xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
 
 		xdl_free_ctx(&xe->xdf2);
 		xdl_free_ctx(&xe->xdf1);
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index d7974d1..04ad468 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -245,12 +245,14 @@
 			while (ptr + 1 < top && isspace(ptr[1])
 					&& ptr[1] != '\n')
 				ptr++;
-			if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+			if (flags & XDF_IGNORE_WHITESPACE)
+				; /* already handled */
+			else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
 					&& ptr[1] != '\n') {
 				ha += (ha << 5);
 				ha ^= (unsigned long) ' ';
 			}
-			if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
+			else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
 					&& ptr[1] != '\n') {
 				while (ptr2 != ptr + 1) {
 					ha += (ha << 5);