Merge branch 'mh/multimail'

An enhanced "post-receive" hook to send e-mail messages.

* mh/multimail:
  post-receive-email: deprecate script in favor of git-multimail
  git-multimail: an improved replacement for post-receive-email
diff --git a/.gitignore b/.gitignore
index 6669bf0..6b1fd1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
 /git-cat-file
 /git-check-attr
 /git-check-ignore
+/git-check-mailmap
 /git-check-ref-format
 /git-checkout
 /git-checkout-index
@@ -125,6 +126,7 @@
 /git-remote-ftps
 /git-remote-fd
 /git-remote-ext
+/git-remote-testgit
 /git-remote-testpy
 /git-remote-testsvn
 /git-repack
@@ -190,6 +192,8 @@
 /test-mktemp
 /test-parse-options
 /test-path-utils
+/test-prio-queue
+/test-read-cache
 /test-regex
 /test-revision-walking
 /test-run-command
@@ -228,6 +232,7 @@
 /cscope*
 *.obj
 *.lib
+*.res
 *.sln
 *.suo
 *.ncb
diff --git a/.mailmap b/.mailmap
index 48d7acf..57070b5 100644
--- a/.mailmap
+++ b/.mailmap
@@ -5,98 +5,216 @@
 # same person appearing not to be so.
 #
 
+<nico@fluxnic.net> <nico@cam.org>
+Alejandro R. Sedeño <asedeno@MIT.EDU> <asedeno@mit.edu>
 Alex Bennée <kernel-hacker@bennee.com>
+Alex Riesen <raa.lkml@gmail.com> <fork0@t-online.de>
+Alex Riesen <raa.lkml@gmail.com> <raa@limbo.localdomain>
+Alex Riesen <raa.lkml@gmail.com> <raa@steel.home>
+Alex Vandiver <alex@chmrr.net> <alexmv@MIT.EDU>
 Alexander Gavrilov <angavrilov@gmail.com>
+Alexey Shumkin <alex.crezoff@gmail.com> <zapped@mail.ru>
+Anders Kaseorg <andersk@MIT.EDU> <andersk@ksplice.com>
+Anders Kaseorg <andersk@MIT.EDU> <andersk@mit.edu>
 Aneesh Kumar K.V <aneesh.kumar@gmail.com>
+Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca>
+Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca>
+Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
 Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
+Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
+Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com>
 Cheng Renquan <crquan@gmail.com>
 Chris Shoemaker <c.shoemaker@cox.net>
+Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org>
+Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu>
 Dan Johnson <computerdruid@gmail.com>
-Dana L. How <danahow@gmail.com>
-Dana L. How <how@deathvalley.cswitch.com>
+Dana L. How <danahow@gmail.com> <how@deathvalley.cswitch.com>
 Daniel Barkalow <barkalow@iabervon.org>
+David Brown <git@davidb.org> <davidb@quicinc.com>
 David D. Kilzer <ddkilzer@kilzer.net>
 David Kågedal <davidk@lysator.liu.se>
+David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)>
 David S. Miller <davem@davemloft.net>
 Deskin Miller <deskinm@umich.edu>
 Dirk Süsserott <newsletter@dirk.my1.cc>
+Eric Blake <eblake@redhat.com> <ebb9@byu.net>
+Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net>
 Eric S. Raymond <esr@thyrsus.com>
 Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
-Fredrik Kuivinen <freku045@student.liu.se>
+Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com> <eyvind-git@orakel.ntnu.no>
+Florian Achleitner <florian.achleitner.2.6.31@gmail.com> <florian.achleitner2.6.31@gmail.com>
+Franck Bui-Huu <vagabon.xyz@gmail.com> <fbuihuu@gmail.com>
+Frank Lichtenheld <frank@lichtenheld.de> <djpig@debian.org>
+Frank Lichtenheld <frank@lichtenheld.de> <flichtenheld@astaro.com>
+Fredrik Kuivinen <frekui@gmail.com> <freku045@student.liu.se>
 Frédéric Heitzmann <frederic.heitzmann@gmail.com>
-H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
-H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
-H. Peter Anvin <hpa@trantor.hos.anvin.org>
+Garry Dolley <gdolley@ucla.edu> <gdolley@arpnetworks.com>
+Greg Price <price@mit.edu> <price@MIT.EDU>
+Greg Price <price@mit.edu> <price@ksplice.com>
+Heiko Voigt <hvoigt@hvoigt.net> <git-list@hvoigt.net>
+H. Merijn Brand <h.m.brand@xs4all.nl> H.Merijn Brand <h.m.brand@xs4all.nl>
+H. Peter Anvin <hpa@zytor.com> <hpa@bonde.sc.orionmulti.com>
+H. Peter Anvin <hpa@zytor.com> <hpa@smyrno.hos.anvin.org>
+H. Peter Anvin <hpa@zytor.com> <hpa@tazenda.sc.orionmulti.com>
+H. Peter Anvin <hpa@zytor.com> <hpa@trantor.hos.anvin.org>
+Han-Wen Nienhuys <hanwen@google.com> Han-Wen Nienhuys <hanwen@xs4all.nl>
 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-İsmail Dönmez <ismail@pardus.org.tr>
+J. Bruce Fields <bfields@citi.umich.edu> <bfields@fieldses.org>
+J. Bruce Fields <bfields@citi.umich.edu> <bfields@pig.linuxdev.us.dell.com>
+J. Bruce Fields <bfields@citi.umich.edu> <bfields@puzzle.fieldses.org>
 Jakub Narębski <jnareb@gmail.com>
-Jay Soffian <jaysoffian+git@gmail.com>
+Jason Riedy <ejr@eecs.berkeley.edu> <ejr@EECS.Berkeley.EDU>
+Jason Riedy <ejr@eecs.berkeley.edu> <ejr@cs.berkeley.edu>
+Jay Soffian <jaysoffian@gmail.com> <jaysoffian+git@gmail.com>
 Jeff King <peff@peff.net> <peff@github.com>
+Jeff Muizelaar <jmuizelaar@mozilla.com> <jeff@infidigm.net>
+Jim Meyering <jim@meyering.net> <meyering@redhat.com>
 Joachim Berdal Haga <cjhaga@fys.uio.no>
-Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at>
-Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net>
+Johannes Schindelin <Johannes.Schindelin@gmx.de> <johannes.schindelin@gmx.de>
 Johannes Sixt <j6t@kdbg.org> <J.Sixt@eudaptics.com>
-Jon Loeliger <jdl@freescale.com>
-Jon Seymour <jon@blackcubes.dyndns.org>
-Jonathan Nieder <jrnieder@uchicago.edu>
+Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net>
+Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at>
+Jon Loeliger <jdl@jdl.com> <jdl@freescale.com>
+Jon Loeliger <jdl@jdl.com> <jdl@freescale.org>
+Jon Seymour <jon.seymour@gmail.com> <jon@blackcubes.dyndns.org>
+Jonathan Nieder <jrnieder@gmail.com> <jrnieder@uchicago.edu>
+Jonathan del Strother <jon.delStrother@bestbefore.tv> <maillist@steelskies.com>
+Josh Triplett <josh@joshtriplett.org> <josh@freedesktop.org>
+Josh Triplett <josh@joshtriplett.org> <josht@us.ibm.com>
+Julian Phillips <julian@quantumfyre.co.uk> <jp3@quantumfyre.co.uk>
 Junio C Hamano <gitster@pobox.com> <gitster@pobox.com>
-Junio C Hamano <gitster@pobox.com> <junio@pobox.com>
-Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
-Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
 Junio C Hamano <gitster@pobox.com> <junio@hera.kernel.org>
 Junio C Hamano <gitster@pobox.com> <junio@kernel.org>
+Junio C Hamano <gitster@pobox.com> <junio@pobox.com>
+Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
 Junio C Hamano <gitster@pobox.com> <junkio@cox.net>
-Karl Hasselström <kha@treskal.com>
-Kevin Leung <kevinlsk@gmail.com>
+Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
+Karl Wiberg <kha@treskal.com> Karl Hasselström <kha@treskal.com>
+Karl Wiberg <kha@treskal.com> Karl Hasselström <kha@yoghurt.hemma.treskal.com>
+Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de>
+Karsten Blees <blees@dcon.de> <karsten.blees@gmail.com>
+Kay Sievers <kay.sievers@vrfy.org> <kay.sievers@suse.de>
+Kay Sievers <kay.sievers@vrfy.org> <kay@mam.(none)>
+Keith Cascio <keith@CS.UCLA.EDU> <keith@cs.ucla.edu>
 Kent Engstrom <kent@lysator.liu.se>
+Kevin Leung <kevinlsk@gmail.com>
+Kirill Smelkov <kirr@navytux.spb.ru> <kirr@landau.phys.spbu.ru>
+Kirill Smelkov <kirr@navytux.spb.ru> <kirr@mns.spb.ru>
+Knut Franke <Knut.Franke@gmx.de> <k.franke@science-computing.de>
 Lars Doelle <lars.doelle@on-line ! de>
 Lars Doelle <lars.doelle@on-line.de>
+Lars Noschinski <lars@public.noschinski.de> <lars.noschinski@rwth-aachen.de>
 Li Hong <leehong@pku.edu.cn>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@woody.linux-foundation.org>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@evo.osdl.org>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org.(none)>
-Lukas Sandström <lukass@etek.chalmers.se>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@woody.linux-foundation.org>
+Lukas Sandström <luksan@gmail.com> <lukass@etek.chalmers.se>
+Marc Khouzam <marc.khouzam@ericsson.com> <marc.khouzam@gmail.com>
 Marc-André Lureau <marcandre.lureau@gmail.com>
+Marco Costalba <mcostalba@gmail.com> <mcostalba@yahoo.it>
+Mark Levedahl <mdl123@verizon.net> <mlevedahl@gmail.com>
 Mark Rada <marada@uwaterloo.ca>
 Martin Langhoff <martin@laptop.org> <martin@catalyst.net.nz>
 Martin von Zweigbergk <martinvonz@gmail.com> <martin.von.zweigbergk@gmail.com>
+Matt Draisey <matt@draisey.ca> <mattdraisey@sympatico.ca>
+Matt Kraai <kraai@ftbfs.org> <matt.kraai@amo.abbott.com>
+Matt McCutchen <matt@mattmccutchen.net> <hashproduct@gmail.com>
+Matthias Kestenholz <matthias@spinlock.ch> <mk@spinlock.ch>
+Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)>
+Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de>
 Michael Coleman <tutufan@gmail.com>
 Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
 Michael W. Olson <mwolson@gnu.org>
+Michael Witten <mfwitten@gmail.com> <mfwitten@MIT.EDU>
+Michael Witten <mfwitten@gmail.com> <mfwitten@mit.edu>
+Michal Rokos <michal.rokos@nextsoft.cz> <rokos@nextsoft.cz>
 Michele Ballabio <barra_cuda@katamail.com>
+Miklos Vajna <vmiklos@frugalware.org> <vmiklos@suse.cz>
+Namhyung Kim <namhyung@gmail.com> <namhyung.kim@lge.com>
+Namhyung Kim <namhyung@gmail.com> <namhyung@kernel.org>
 Nanako Shiraishi <nanako3@bluebottle.com>
 Nanako Shiraishi <nanako3@lavabit.com>
+Nelson Elhage <nelhage@mit.edu> <nelhage@MIT.EDU>
+Nelson Elhage <nelhage@mit.edu> <nelhage@ksplice.com>
 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
-<nico@fluxnic.net> <nico@cam.org>
-Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se>
+Nick Stokoe <nick@noodlefactory.co.uk> Nick Woolley <nick@noodlefactory.co.uk>
+Nick Stokoe <nick@noodlefactory.co.uk> Nick Woolley <nickwoolley@yahoo.co.uk>
+Nicolas Morey-Chaisemartin <devel-git@morey-chaisemartin.com> <nicolas.morey@free.fr>
+Nicolas Morey-Chaisemartin <devel-git@morey-chaisemartin.com> <nmorey@kalray.eu>
+Nicolas Sebrecht <nicolas.s.dev@gmx.fr> <ni.s@laposte.net>
+Paolo Bonzini <bonzini@gnu.org> <paolo.bonzini@lu.unisi.ch>
+Pascal Obry <pascal@obry.net> <pascal.obry@gmail.com>
+Pascal Obry <pascal@obry.net> <pascal.obry@wanadoo.fr>
+Pat Notz <patnotz@gmail.com> <pknotz@sandia.gov>
+Paul Mackerras <paulus@samba.org> <paulus@dorrigo.(none)>
+Paul Mackerras <paulus@samba.org> <paulus@pogo.(none)>
+Peter Baumann <waste.manager@gmx.de> <Peter.B.Baumann@stud.informatik.uni-erlangen.de>
+Peter Baumann <waste.manager@gmx.de> <siprbaum@stud.informatik.uni-erlangen.de>
 Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se>
+Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se>
 Petr Baudis <pasky@ucw.cz> <pasky@suse.cz>
+Petr Baudis <pasky@ucw.cz> <xpasky@machine>
+Phil Hord <hordp@cisco.com> <phil.hord@gmail.com>
+Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com>
+Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de>
 Philippe Bruhat <book@cpan.org>
 Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
 Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
-René Scharfe <rene.scharfe@lsrfire.ath.cx>
+René Scharfe <l.s.r@web.de> <rene.scharfe@lsrfire.ath.cx>
 Robert Fitzsimons <robfitz@273k.net>
+Robert Shearman <robertshearman@gmail.com> <rob@codeweavers.com>
 Robert Zeh <robert.a.zeh@gmail.com>
-Sam Vilain <sam@vilain.net>
-Santi Béjar <sbejar@gmail.com>
+Robin Rosenberg <robin.rosenberg@dewire.com> <robin.rosenberg.lists@dewire.com>
+Ryan Anderson <ryan@michonline.com> <rda@google.com>
+Salikh Zakirov <salikh.zakirov@gmail.com> <Salikh.Zakirov@Intel.com>
+Sam Vilain <sam@vilain.net> <sam.vilain@catalyst.net.nz>
+Santi Béjar <santi@agolina.net> <sbejar@gmail.com>
 Sean Estabrooks <seanlkml@sympatico.ca>
+Sebastian Schuberth <sschuberth@gmail.com> <sschuberth@visageimaging.com>
+Seth Falcon <seth@userprimary.net> <sfalcon@fhcrc.org>
 Shawn O. Pearce <spearce@spearce.org>
-Steven Grimm <koreth@midwinter.com>
+Simon Hausmann <hausmann@kde.org> <simon@lst.de>
+Simon Hausmann <hausmann@kde.org> <shausman@trolltech.com>
+Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@atlas-elektronik.com>
+Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@googlemail.com>
+Stefan Sperling <stsp@elego.de> <stsp@stsp.name>
+Stephen Boyd <bebarino@gmail.com> <sboyd@codeaurora.org>
+Steven Drake <sdrake@xnet.co.nz> <sdrake@ihug.co.nz>
+Steven Grimm <koreth@midwinter.com> <sgrimm@sgrimm-mbp.local>
+Steven Walter <stevenrwalter@gmail.com> <swalter@lexmark.com>
+Steven Walter <stevenrwalter@gmail.com> <swalter@lpdev.prtdev.lexmark.com>
+Sven Verdoolaege <skimo@kotnet.org> <Sven.Verdoolaege@cs.kuleuven.ac.be>
+Sven Verdoolaege <skimo@kotnet.org> <skimo@liacs.nl>
 Tay Ray Chuan <rctay89@gmail.com>
+Ted Percival <ted@midg3t.net> <ted.percival@quest.com>
 Theodore Ts'o <tytso@mit.edu>
+Thomas Ackermann <th.acker@arcor.de> <th.acker66@arcor.de>
 Thomas Rast <trast@inf.ethz.ch> <trast@student.ethz.ch>
+Timo Hirvonen <tihirvon@gmail.com> <tihirvon@ee.oulu.fi>
+Toby Allsopp <Toby.Allsopp@navman.co.nz> <toby.allsopp@navman.co.nz>
+Tom Grennan <tmgrennan@gmail.com> <tgrennan@redback.com>
+Tommi Virtanen <tv@debian.org> <tv@eagain.net>
+Tommi Virtanen <tv@debian.org> <tv@inoi.fi>
+Tommy Thorn <tommy-git@thorn.ws> <tt1729@yahoo.com>
 Tony Luck <tony.luck@intel.com>
-Uwe Kleine-König <Uwe_Zeisberger@digi.com>
-Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
-Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
-Uwe Kleine-König <uzeisberger@io.fsforth.de>
-Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
-Ville Skyttä <scop@xemacs.org>
+Tor Arne Vestbø <torarnv@gmail.com> <tavestbo@trolltech.com>
+Trent Piepho <tpiepho@gmail.com> <tpiepho@freescale.com>
+Trent Piepho <tpiepho@gmail.com> <xyzzy@speakeasy.org>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <Uwe.Kleine-Koenig@digi.com>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <ukleinek@informatik.uni-freiburg.de>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <uzeisberger@io.fsforth.de>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <zeisberg@informatik.uni-freiburg.de>
+Ville Skyttä <ville.skytta@iki.fi> <scop@xemacs.org>
 Vitaly "_Vi" Shukela <public_vi@tut.by>
+W. Trevor King <wking@tremily.us> <wking@drexel.edu>
 William Pursell <bill.pursell@gmail.com>
+YONETANI Tomokazu <y0n3t4n1@gmail.com> <qhwt+git@les.ath.cx>
+YONETANI Tomokazu <y0n3t4n1@gmail.com> <y0netan1@dragonflybsd.org>
 YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+# the two anonymous contributors are different persons:
 anonymous <linux@horizon.com>
 anonymous <linux@horizon.net>
+İsmail Dönmez <ismail@pardus.org.tr>
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 62dbd9a..0cfdc36 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -31,11 +31,11 @@
 MAN7_TXT += gitworkflows.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
-MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
-MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
+MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT))
+MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
 
 OBSOLETE_HTML = git-remote-helpers.html
-DOC_HTML=$(MAN_HTML) $(OBSOLETE_HTML)
+DOC_HTML = $(MAN_HTML) $(OBSOLETE_HTML)
 
 ARTICLES = howto-index
 ARTICLES += everyday
@@ -74,35 +74,35 @@
 
 DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
 
-DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
-DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
-DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
+DOC_MAN1 = $(patsubst %.txt,%.1,$(MAN1_TXT))
+DOC_MAN5 = $(patsubst %.txt,%.5,$(MAN5_TXT))
+DOC_MAN7 = $(patsubst %.txt,%.7,$(MAN7_TXT))
 
-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
-man7dir=$(mandir)/man7
-# DESTDIR=
+prefix ?= $(HOME)
+bindir ?= $(prefix)/bin
+htmldir ?= $(prefix)/share/doc/git-doc
+infodir ?= $(prefix)/share/info
+pdfdir ?= $(prefix)/share/doc/git-doc
+mandir ?= $(prefix)/share/man
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+man7dir = $(mandir)/man7
+# DESTDIR =
 
 ASCIIDOC = asciidoc
 ASCIIDOC_EXTRA =
 MANPAGE_XSL = manpage-normal.xsl
 XMLTO = xmlto
 XMLTO_EXTRA =
-INSTALL?=install
+INSTALL ?= install
 RM ?= rm -f
 MAN_REPO = ../../git-manpages
 HTML_REPO = ../../git-htmldocs
 
-infodir?=$(prefix)/share/info
-MAKEINFO=makeinfo
-INSTALL_INFO=install-info
-DOCBOOK2X_TEXI=docbook2x-texi
-DBLATEX=dblatex
+MAKEINFO = makeinfo
+INSTALL_INFO = install-info
+DOCBOOK2X_TEXI = docbook2x-texi
+DBLATEX = dblatex
 ifndef PERL_PATH
 	PERL_PATH = /usr/bin/perl
 endif
diff --git a/Documentation/RelNotes/1.7.10.1.txt b/Documentation/RelNotes/1.7.10.1.txt
index 806a965..be68524 100644
--- a/Documentation/RelNotes/1.7.10.1.txt
+++ b/Documentation/RelNotes/1.7.10.1.txt
@@ -14,7 +14,7 @@
    not exclude them and tried to apply funny patches only to fail.
 
  * "git blame" started missing quite a few changes from the origin
-   since we stopped using the diff minimalization by default in v1.7.2
+   since we stopped using the diff minimization by default in v1.7.2
    era.
 
  * When PATH contains an unreadable directory, alias expansion code
diff --git a/Documentation/RelNotes/1.7.5.4.txt b/Documentation/RelNotes/1.7.5.4.txt
index cf3f455..7796df3 100644
--- a/Documentation/RelNotes/1.7.5.4.txt
+++ b/Documentation/RelNotes/1.7.5.4.txt
@@ -5,7 +5,7 @@
 --------------------
 
  * The single-key mode of "git add -p" was easily fooled into thinking
-   that it was told to add everthing ('a') when up-arrow was pressed by
+   that it was told to add everything ('a') when up-arrow was pressed by
    mistake.
 
  * Setting a git command that uses custom configuration via "-c var=val"
diff --git a/Documentation/RelNotes/1.7.8.2.txt b/Documentation/RelNotes/1.7.8.2.txt
index e74f4ef..b9c66aa 100644
--- a/Documentation/RelNotes/1.7.8.2.txt
+++ b/Documentation/RelNotes/1.7.8.2.txt
@@ -12,11 +12,11 @@
  * The configuration file parser used for sizes (e.g. bigFileThreshold)
    did not correctly interpret 'g' suffix.
 
- * The replacement implemention for snprintf used on platforms with
+ * The replacement implementation for snprintf used on platforms with
    native snprintf that is broken did not use va_copy correctly.
 
  * LF-to-CRLF streaming filter replaced all LF with CRLF, which might
-   be techinically correct but not friendly to people who are trying
+   be technically correct but not friendly to people who are trying
    to recover from earlier mistakes of using CRLF in the repository
    data in the first place. It now refrains from doing so for LF that
    follows a CR.
diff --git a/Documentation/RelNotes/1.7.8.txt b/Documentation/RelNotes/1.7.8.txt
index b4d90bb..2493113 100644
--- a/Documentation/RelNotes/1.7.8.txt
+++ b/Documentation/RelNotes/1.7.8.txt
@@ -9,7 +9,7 @@
  * Updates to bash completion scripts.
 
  * The build procedure has been taught to take advantage of computed
-   dependency automatically when the complier supports it.
+   dependency automatically when the compiler supports it.
 
  * The date parser now accepts timezone designators that lack minutes
    part and also has a colon between "hh:mm".
@@ -31,7 +31,7 @@
  * Variants of "git cherry-pick" and "git revert" that take multiple
    commits learned to "--continue" and "--abort".
 
- * "git daemon" gives more human readble error messages to clients
+ * "git daemon" gives more human readable error messages to clients
    using ERR packets when appropriate.
 
  * Errors at the network layer is logged by "git daemon".
diff --git a/Documentation/RelNotes/1.8.2.1.txt b/Documentation/RelNotes/1.8.2.1.txt
index 1354ad0..769a6fc 100644
--- a/Documentation/RelNotes/1.8.2.1.txt
+++ b/Documentation/RelNotes/1.8.2.1.txt
@@ -49,7 +49,7 @@
    common prefix and suffix between the two filenames overlapped.
 
  * "git submodule update", when recursed into sub-submodules, did not
-   acccumulate the prefix paths.
+   accumulate the prefix paths.
 
  * "git am $maildir/" applied messages in an unexpected order; sort
    filenames read from the maildir/ in a way that is more likely to
diff --git a/Documentation/RelNotes/1.8.2.2.txt b/Documentation/RelNotes/1.8.2.2.txt
index dab4831..708df1a 100644
--- a/Documentation/RelNotes/1.8.2.2.txt
+++ b/Documentation/RelNotes/1.8.2.2.txt
@@ -58,4 +58,4 @@
    conflicts have been applied.
 
  * "git bundle" did not like a bundle created using a commit without
-   any message as its one of the prerequistes.
+   any message as its one of the prerequisites.
diff --git a/Documentation/RelNotes/1.8.3.1.txt b/Documentation/RelNotes/1.8.3.1.txt
new file mode 100644
index 0000000..fc3ea18
--- /dev/null
+++ b/Documentation/RelNotes/1.8.3.1.txt
@@ -0,0 +1,14 @@
+Git v1.8.3.1 Release Notes
+========================
+
+Fixes since v1.8.3
+------------------
+
+ * When $HOME is misconfigured to point at an unreadable directory, we
+   used to complain and die. The check has been loosened.
+
+ * Handling of negative exclude pattern for directories "!dir" was
+   broken in the update to v1.8.3.
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
diff --git a/Documentation/RelNotes/1.8.3.2.txt b/Documentation/RelNotes/1.8.3.2.txt
new file mode 100644
index 0000000..26ae142
--- /dev/null
+++ b/Documentation/RelNotes/1.8.3.2.txt
@@ -0,0 +1,59 @@
+Git v1.8.3.2 Release Notes
+==========================
+
+Fixes since v1.8.3.1
+--------------------
+
+ * Cloning with "git clone --depth N" while fetch.fsckobjects (or
+   transfer.fsckobjects) is set to true did not tell the cut-off
+   points of the shallow history to the process that validates the
+   objects and the history received, causing the validation to fail.
+
+ * "git checkout foo" DWIMs the intended "upstream" and turns it into
+   "git checkout -t -b foo remotes/origin/foo". This codepath has been
+   updated to correctly take existing remote definitions into account.
+
+ * "git fetch" into a shallow repository from a repository that does
+   not know about the shallow boundary commits (e.g. a different fork
+   from the repository the current shallow repository was cloned from)
+   did not work correctly.
+
+ * "git subtree" (in contrib/) had one codepath with loose error
+   checks to lose data at the remote side.
+
+ * "git log --ancestry-path A...B" did not work as expected, as it did
+   not pay attention to the fact that the merge base between A and B
+   was the bottom of the range being specified.
+
+ * "git diff -c -p" was not showing a deleted line from a hunk when
+   another hunk immediately begins where the earlier one ends.
+
+ * "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
+   incorrectly when your previous branch was "frotz" (it should be
+   rewritten to "git merge frotz~22" instead).
+
+ * "git commit --allow-empty-message -m ''" should not start an
+   editor.
+
+ * "git push --[no-]verify" was not documented.
+
+ * An entry for "file://" scheme in the enumeration of URL types Git
+   can take in the HTML documentation was made into a clickable link
+   by mistake.
+
+ * zsh prompt script that borrowed from bash prompt script did not
+   work due to slight differences in array variable notation between
+   these two shells.
+
+ * The bash prompt code (in contrib/) displayed the name of the branch
+   being rebased when "rebase -i/-m/-p" modes are in use, but not the
+   plain vanilla "rebase".
+
+ * "git push $there HEAD:branch" did not resolve HEAD early enough, so
+   it was easy to flip it around while push is still going on and push
+   out a branch that the user did not originally intended when the
+   command was started.
+
+ * "difftool --dir-diff" did not copy back changes made by the
+   end-user in the diff tool backend to the working tree in some
+   cases.
diff --git a/Documentation/RelNotes/1.8.3.3.txt b/Documentation/RelNotes/1.8.3.3.txt
new file mode 100644
index 0000000..9ba4f4d
--- /dev/null
+++ b/Documentation/RelNotes/1.8.3.3.txt
@@ -0,0 +1,47 @@
+Git v1.8.3.3 Release Notes
+==========================
+
+Fixes since v1.8.3.2
+--------------------
+
+ * "git apply" parsed patches that add new files, generated by programs
+   other than Git, incorrectly.  This is an old breakage in v1.7.11.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+   we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+   index.
+
+ * Many "git submodule" operations did not work on a submodule at a
+   path whose name is not in ASCII.
+
+ * "cherry-pick" had a small leak in its error codepath.
+
+ * Logic used by git-send-email to suppress cc mishandled names like
+   "A U. Thor" <author@example.xz>, where the human readable part
+   needs to be quoted (the user input may not have the double quotes
+   around the name, and comparison was done between quoted and
+   unquoted strings).  It also mishandled names that need RFC2047
+   quoting.
+
+ * "gitweb" forgot to clear a global variable $search_regexp upon each
+   request, mistakenly carrying over the previous search to a new one
+   when used as a persistent CGI.
+
+ * The wildmatch engine did not honor WM_CASEFOLD option correctly.
+
+ * "git log -c --follow $path" segfaulted upon hitting the commit that
+   renamed the $path being followed.
+
+ * When a reflog notation is used for implicit "current branch",
+   e.g. "git log @{u}", we did not say which branch and worse said
+   "branch ''" in the error messages.
+
+ * Mac OS X does not like to write(2) more than INT_MAX number of
+   bytes; work it around by chopping write(2) into smaller pieces.
+
+ * Newer MacOS X encourages the programs to compile and link with
+   their CommonCrypto, not with OpenSSL.
+
+Also contains various minor documentation updates.
diff --git a/Documentation/RelNotes/1.8.3.4.txt b/Documentation/RelNotes/1.8.3.4.txt
new file mode 100644
index 0000000..56f106e
--- /dev/null
+++ b/Documentation/RelNotes/1.8.3.4.txt
@@ -0,0 +1,20 @@
+Git v1.8.3.4 Release Notes
+==========================
+
+This update is mostly to propagate documentation fixes and test
+updates from the master front back to the maintenance track.
+
+Fixes since v1.8.3.3
+--------------------
+
+ * The bisect log listed incorrect commits when bisection ends with
+   only skipped ones.
+
+ * The test coverage framework was left broken for some time.
+
+ * The test suite for HTTP transport did not run with Apache 2.4.
+
+ * "git diff" used to fail when core.safecrlf is set and the working
+   tree contents had mixed CRLF/LF line endings. Committing such a
+   content must be prohibited, but "git diff" should help the user to
+   locate and fix such problems without failing.
diff --git a/Documentation/RelNotes/1.8.4.txt b/Documentation/RelNotes/1.8.4.txt
new file mode 100644
index 0000000..ad58ac6
--- /dev/null
+++ b/Documentation/RelNotes/1.8.4.txt
@@ -0,0 +1,480 @@
+Git v1.8.4 Release Notes
+========================
+
+Backward compatibility notes (for Git 2.0)
+------------------------------------------
+
+When "git push [$there]" does not say what to push, we have used the
+traditional "matching" semantics so far (all your branches were sent
+to the remote as long as there already are branches of the same name
+over there).  In Git 2.0, the default will change to the "simple"
+semantics that pushes:
+
+ - only the current branch to the branch with the same name, and only
+   when the current branch is set to integrate with that remote
+   branch, if you are pushing to the same remote as you fetch from; or
+
+ - only the current branch to the branch with the same name, if you
+   are pushing to a remote that is not where you usually fetch from.
+
+Use the user preference configuration variable "push.default" to
+change this.  If you are an old-timer who is used to the "matching"
+semantics, you can set the variable to "matching" to keep the
+traditional behaviour.  If you want to live in the future early, you
+can set it to "simple" today without waiting for Git 2.0.
+
+When "git add -u" (and "git add -A") is run inside a subdirectory and
+does not specify which paths to add on the command line, it
+will operate on the entire tree in Git 2.0 for consistency
+with "git commit -a" and other commands.  There will be no
+mechanism to make plain "git add -u" behave like "git add -u .".
+Current users of "git add -u" (without a pathspec) should start
+training their fingers to explicitly say "git add -u ."
+before Git 2.0 comes.  A warning is issued when these commands are
+run without a pathspec and when you have local changes outside the
+current directory, because the behaviour in Git 2.0 will be different
+from today's version in such a situation.
+
+In Git 2.0, "git add <path>" will behave as "git add -A <path>", so
+that "git add dir/" will notice paths you removed from the directory
+and record the removal.  Versions before Git 2.0, including this
+release, will keep ignoring removals, but the users who rely on this
+behaviour are encouraged to start using "git add --ignore-removal <path>"
+now before 2.0 is released.
+
+
+Updates since v1.8.3
+--------------------
+
+Foreign interfaces, subsystems and ports.
+
+ * "git rebase -i" now honors --strategy and -X options.
+
+ * Git-gui has been updated to its 0.18.0 version.
+
+ * MediaWiki remote helper (in contrib/) has been updated to use the
+   credential helper interface from Git.pm.
+
+ * Update build for Cygwin 1.[57].  Torsten Bögershausen reports that
+   this is fine with Cygwin 1.7 ($gmane/225824) so let's try moving it
+   ahead.
+
+ * The credential helper to talk to keychain on OS X (in contrib/) has
+   been updated to kick in not just when talking http/https but also
+   imap(s) and smtp.
+
+ * Remote transport helper has been updated to report errors and
+   maintain ref hierarchy used to keep track of its own state better.
+
+ * With "export" remote-helper protocol, (1) a push that tries to
+   update a remote ref whose name is different from the pushing side
+   does not work yet, and (2) the helper may not know how to do
+   --dry-run; these problematic cases are disabled for now.
+
+ * git-remote-hg/bzr (in contrib/) updates.
+
+ * git-remote-mw (in contrib/) hints users to check the certificate,
+   when https:// connection failed.
+
+ * git-remote-mw (in contrib/) adds a command to allow previewing the
+   contents locally before pushing it out, when working with a
+   MediaWiki remote.
+
+
+UI, Workflows & Features
+
+ * "git clean" command learned an interactive mode.
+
+ * The "--head" option to "git show-ref" was only to add "HEAD" to the
+   list of candidate refs to be filtered by the usual rules
+   (e.g. "--heads" that only show refs under refs/heads).  The meaning
+   of the option has been changed to always show "HEAD" regardless of
+   what filtering will be applied to any other ref.
+
+   This is a backward incompatible change and might cause breakages to
+   people's existing scripts.
+
+ * "git show -s" was less discoverable than it should have been.  It
+   now has a natural synonym "git show --no-patch".
+
+ * "git check-mailmap" is a new command that lets you map usernames
+   and e-mail addresses through the mailmap mechanism, just like many
+   built-in commands do.
+
+ * "git name-rev" learned to name an annotated tag object back to its
+   tagname; "git name-rev $(git rev-parse v1.0.0)" gives "tags/v1.0.0",
+   for example.
+
+ * "git cat-file --batch-check=<format>" is added, primarily to allow
+   on-disk footprint of objects in packfiles (often they are a lot
+   smaller than their true size, when expressed as deltas) to be
+   reported.
+
+ * "git rebase [-i]" used to leave just "rebase" as its reflog messages
+   for some operations. They have been reworded to be more informative.
+
+ * In addition to the choice from "rebase, merge, or checkout-detach",
+   "submodule update" can allow a custom command to be used in to
+   update the working tree of submodules via the "submodule.*.update"
+   configuration variable.
+
+ * "git submodule update" can optionally clone the submodule
+   repositories shallowly.
+
+ * "git format-patch" learned "--from[=whom]" option, which sets the
+   "From: " header to the specified person (or the person who runs the
+   command, if "=whom" part is missing) and move the original author
+   information to an in-body From: header as necessary.
+
+ * The configuration variable "merge.ff" was cleary a tri-state to
+   choose one from "favor fast-forward when possible", "always create
+   a merge even when the history could fast-forward" and "do not
+   create any merge, only update when the history fast-forwards", but
+   the command line parser did not implement the usual convention of
+   "last one wins, and command line overrides the configuration"
+   correctly.
+
+ * "gitweb" learned to optionally place extra links that point at the
+   levels higher than the Gitweb pages themselves in the breadcrumbs,
+   so that it can be used as part of a larger installation.
+
+ * "git log --format=" now honors i18n.logoutputencoding configuration
+   variable.
+
+ * The "push.default=simple" mode of "git push" has been updated to
+   behave like "current" without requiring a remote tracking
+   information, when you push to a remote that is different from where
+   you fetch from (i.e. a triangular workflow).
+
+ * Having multiple "fixup!" on a line in the rebase instruction sheet
+   did not work very well with "git rebase -i --autosquash".
+
+ * "git log" learned the "--author-date-order" option, with which the
+   output is topologically sorted and commits in parallel histories
+   are shown intermixed together based on the author timestamp.
+
+ * Various subcommands of "git submodule" refused to run from anywhere
+   other than the top of the working tree of the superproject, but
+   they have been taught to let you run from a subdirectory.
+
+ * "git diff" learned a mode that ignores hunks whose change consists
+   only of additions and removals of blank lines, which is the same as
+   "diff -B" (ignore blank lines) of GNU diff.
+
+ * "git rm" gives a single message followed by list of paths to report
+   multiple paths that cannot be removed.
+
+ * "git rebase" can be told with ":/look for this string" syntax commits
+   to replay the changes onto and where the work to be replayed begins.
+
+ * Many tutorials teach users to set "color.ui" to "auto" as the first
+   thing after you set "user.name/email" to introduce yourselves to
+   Git.  Now the variable defaults to "auto".
+
+ * On Cygwin, "cygstart" is now recognised as a possible way to start
+   a web browser (used in "help -w" and "instaweb" among others).
+
+ * "git status" learned status.branch and status.short configuration
+   variables to use --branch and --short options by default (override
+   with --no-branch and --no-short options from the command line).
+
+ * "git cmd <name>", when <name> happens to be a 40-hex string,
+   directly uses the 40-hex string as an object name, even if a ref
+   "refs/<some hierarchy>/<name>" exists.  This disambiguation order
+   is unlikely to change, but we should warn about the ambiguity just
+   like we warn when more than one refs/ hierachies share the same
+   name.
+
+ * "git rebase" learned "--[no-]autostash" option to save local
+   changes instead of refusing to run (to which people's normal
+   response was to stash them and re-run).  This introduced a corner
+   case breakage to "git am --abort" but it has been fixed.
+
+ * Instead of typing four capital letters "HEAD", you can say "@" now,
+   e.g. "git log @".
+
+ * "check-ignore" (new feature since 1.8.2) has been updated to work
+   more like "check-attr" over bidi-pipes.
+
+ * "git describe" learned "--first-parent" option to limit its closest
+   tagged commit search to the first-parent chain.
+
+ * "git merge foo" that might have meant "git merge origin/foo" is
+   diagnosed with a more informative error message.
+
+ * "git log -L<line>,<range>:<filename>" has been added.  This may
+   still have leaks and rough edges, though.
+
+ * We used the approxidate() parser for "--expire=<timestamp>" options
+   of various commands, but it is better to treat --expire=all and
+   --expire=now a bit more specially than using the current timestamp.
+   "git gc" and "git reflog" have been updated with a new parsing
+   function for expiry dates.
+
+ * Updates to completion (both bash and zsh) helpers.
+
+ * The behaviour of the "--chain-reply-to" option of "git send-email"
+   have changed at 1.7.0, and we added a warning/advice message to
+   help users adjust to the new behaviour back then, but we kept it
+   around for too long.  The message has finally been removed.
+
+ * "git fetch origin master" unlike "git fetch origin" or "git fetch"
+   did not update "refs/remotes/origin/master"; this was an early
+   design decision to keep the update of remote tracking branches
+   predictable, but in practice it turns out that people find it more
+   convenient to opportunistically update them whenever we have a
+   chance, and we have been updating them when we run "git push" which
+   already breaks the original "predictability" anyway.
+
+ * The configuration variable core.checkstat was advertised in the
+   documentation but the code expected core.statinfo instead.
+   For now, we accept both core.checkstat and core.statinfo, but the
+   latter will be removed in the longer term.
+
+
+Performance, Internal Implementation, etc.
+
+ * The function attributes extensions are used to catch mistakes in
+   use of our own variadic functions that use NULL sentinel at the end
+   (i.e. like execl(3)) and format strings (i.e. like printf(3)).
+
+ * The code to allow configuration data to be read from in-tree blob
+   objects is in.  This may help working in a bare repository and
+   submodule updates.
+
+ * Fetching between repositories with many refs employed O(n^2)
+   algorithm to match up the common objects, which has been corrected.
+
+ * The original way to specify remote repository using .git/branches/
+   used to have a nifty feature.  The code to support the feature was
+   still in a function but the caller was changed not to call it 5
+   years ago, breaking that feature and leaving the supporting code
+   unreachable.  The dead code has been removed.
+
+ * "git pack-refs" that races with new ref creation or deletion have
+   been susceptible to lossage of refs under right conditions, which
+   has been tightened up.
+
+ * We read loose and packed rerferences in two steps, but after
+   deciding to read a loose ref but before actually opening it to read
+   it, another process racing with us can unlink it, which would cause
+   us to barf.  The codepath has been updated to retry when such a
+   race is detected, instead of outright failing.
+
+ * Uses of the platform fnmatch(3) function (many places in the code,
+   matching pathspec, .gitignore and .gitattributes to name a few)
+   have been replaced with wildmatch, allowing "foo/**/bar" that would
+   match foo/bar, foo/a/bar, foo/a/b/bar, etc.
+
+ * Memory ownership and lifetime rules for what for-each-ref feeds to
+   its callbacks have been clarified (in short, "you do not own it, so
+   make a copy if you want to keep it").
+
+ * The revision traversal logic to improve culling of irrelevant
+   parents while traversing a mergy history has been updated.
+
+ * Some leaks in unpack-trees (used in merge, cherry-pick and other
+   codepaths) have been plugged.
+
+ * The codepath to read from marks files in fast-import/export did not
+   have to accept anything but 40-hex representation of the object
+   name.  Further, fast-export did not need full in-core object
+   representation to have parsed wen reading from them.  These
+   codepaths have been optimized by taking advantage of these access
+   patterns.
+
+ * Object lookup logic, when the object hashtable starts to become
+   crowded, has been optimized.
+
+ * When TEST_OUTPUT_DIRECTORY setting is used, it was handled somewhat
+   inconsistently between the test framework and t/Makefile, and logic
+   to summarize the results looked at a wrong place.
+
+ * "git clone" uses a lighter-weight implementation when making sure
+   that the history behind refs are complete.
+
+ * Many warnings from sparse source checker in compat/ area has been
+   squelched.
+
+ * The code to reading and updating packed-refs file has been updated,
+   correcting corner case bugs.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v1.8.3
+------------------
+
+Unless otherwise noted, all the fixes since v1.8.3 in the maintenance
+track are contained in this release (see release notes to them for
+details).
+
+ * Newer Net::SMTP::SSL module does not want the user programs to use
+   the default behaviour to let server certificate go without
+   verification, so by default enable the verification with a
+   mechanism to turn it off if needed.
+   (merge 35035bb rr/send-email-ssl-verify later to maint).
+
+ * When "git" is spawned in such a way that any of the low 3 file
+   descriptors is closed, our first open() may yield file descriptor 2,
+   and writing error message to it would screw things up in a big way.
+   (merge a11c396 tr/protect-low-3-fds later to maint).
+
+ * The mailmap mechanism unnecessarily downcased the e-mail addresses
+   in the output, and also ignored the human name when it is a single
+   character name.
+   (merge bd23794 jc/mailmap-case-insensitivity later to maint).
+
+ * In two places we did not check return value (expected to be a file
+   descriptor) correctly.
+   (merge a77f106 tr/fd-gotcha-fixes later to maint).
+
+ * Logic to auto-detect character encodings in the commit log message
+   did not reject overlong and invalid UTF-8 characters.
+   (merge 81050ac bc/commit-invalid-utf8 later to maint).
+
+ * Pass port number as a separate argument when "send-email" initializes
+   Net::SMTP, instead of as a part of the hostname, i.e. host:port.
+   This allows GSSAPI codepath to match with the hostname given.
+   (merge 1a741bf bc/send-email-use-port-as-separate-param later to maint).
+
+ * "git diff" refused to even show difference when core.safecrlf is
+   set to true (i.e. error out) and there are offending lines in the
+   working tree files.
+   (merge 5430bb2 jc/maint-diff-core-safecrlf later to maint).
+
+ * A test that should have failed but didn't revealed a bug that needs
+   to be corrected.
+   (merge 94d75d1 jc/t1512-fix later to maint).
+
+ * An overlong path to a .git directory may have overflown the
+   temporary path buffer used to create a name for lockfiles.
+   (merge 2fbd4f9 mh/maint-lockfile-overflow later to maint).
+
+ * Invocations of "git checkout" used internally by "git rebase" were
+   counted as "checkout", and affected later "git checkout -" to the
+   the user to an unexpected place.
+   (merge 3bed291 rr/rebase-checkout-reflog later to maint).
+
+ * "git stash save", when your local change turns a tracked file into
+   a directory, has to remove files in that directory in order to
+   revert your working tree to a pristine state.  This will lose
+   untracked files in such a directory, and the command now requires
+   you to "--force" it.
+
+ * The configuration variable column.ui was poorly documented.
+   (merge 5e62cc1 rr/column-doc later to maint).
+
+ * "git name-rev --refs=tags/v*" were forbidden, which was a bit
+   inconvenient (you had to give a pattern to match refs fully, like
+   --refs=refs/tags/v*).
+   (merge 98c5c4a nk/name-rev-abbreviated-refs later to maint).
+
+ * "git apply" parsed patches that add new files, generated by
+   programs other than Git, incorrectly.  This is an old breakage in
+   v1.7.11 and will need to be merged down to the maintanance tracks.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+   we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+   index, and this avoids it.
+
+ * Many "git submodule" operations do not work on a submodule at a
+   path whose name is not in ASCII.
+
+ * "cherry-pick" had a small leak in an error codepath.
+
+ * Logic used by git-send-email to suppress cc mishandled names like
+   "A U. Thor" <author@example.xz>, where the human readable part
+   needs to be quoted (the user input may not have the double quotes
+   around the name, and comparison was done between quoted and
+   unquoted strings).  It also mishandled names that need RFC2047
+   quoting.
+
+ * Call to discard_cache/discard_index (used when we use different
+   contents of the index in-core, in many operations like commit,
+   apply, and merge) used to leak memory that held the array of index
+   entries, which has been plugged.
+   (merge a0fc4db rs/discard-index-discard-array later to maint).
+
+ * "gitweb" forgot to clear a global variable $search_regexp upon each
+   request, mistakenly carrying over the previous search to a new one
+   when used as a persistent CGI.
+
+ * The wildmatch engine did not honor WM_CASEFOLD option correctly.
+
+ * "git log -c --follow $path" segfaulted upon hitting the commit that
+   renamed the $path being followed.
+
+ * When a reflog notation is used for implicit "current branch", we
+   did not say which branch and worse said "branch ''".
+
+ * "difftool --dir-diff" did not copy back changes made by the
+   end-user in the diff tool backend to the working tree in some
+   cases.
+
+ * "git push $there HEAD:branch" did not resolve HEAD early enough, so
+   it was easy to flip it around while push is still going on and push
+   out a branch that the user did not originally intended when the
+   command was started.
+
+ * The bash prompt code (in contrib/) displayed the name of the branch
+   being rebased when "rebase -i/-m/-p" modes are in use, but not the
+   plain vanilla "rebase".
+
+ * Handling of negative exclude pattern for directories "!dir" was
+   broken in the update to v1.8.3.
+
+ * zsh prompt script that borrowed from bash prompt script did not
+   work due to slight differences in array variable notation between
+   these two shells.
+
+ * An entry for "file://" scheme in the enumeration of URL types Git
+   can take in the HTML documentation was made into a clickable link
+   by mistake.
+
+ * "git push --[no-]verify" was not documented.
+
+ * Stop installing the git-remote-testpy script that is only used for
+   testing.
+
+ * "git commit --allow-empty-message -m ''" should not start an
+   editor.
+
+ * "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
+   incorrectly when your previous branch was "frotz" (it should be
+   rewritten to "git merge frotz~22" instead).
+
+ * "git diff -c -p" was not showing a deleted line from a hunk when
+   another hunk immediately begins where the earlier one ends.
+
+ * "git log --ancestry-path A...B" did not work as expected, as it did
+   not pay attention to the fact that the merge base between A and B
+   was the bottom of the range being specified.
+
+ * Mac OS X does not like to write(2) more than INT_MAX number of
+   bytes; work it around by chopping write(2) into smaller pieces.
+
+ * Newer MacOS X encourages the programs to compile and link with
+   their CommonCrypto, not with OpenSSL.
+
+ * "git clone foo/bar:baz" cannot be a request to clone from a remote
+   over git-over-ssh specified in the scp style.  This case is now
+   detected and clones from a local repository at "foo/bar:baz".
+
+ * When $HOME is misconfigured to point at an unreadable directory, we
+   used to complain and die. Loosen the check.
+
+ * "git subtree" (in contrib/) had one codepath with loose error
+   checks to lose data at the remote side.
+
+ * "git fetch" into a shallow repository from a repository that does
+   not know about the shallow boundary commits (e.g. a different fork
+   from the repository the current shallow repository was cloned from)
+   did not work correctly.
+
+ * "git checkout foo" DWIMs the intended "upstream" and turns it into
+   "git checkout -t -b foo remotes/origin/foo". This codepath has been
+   updated to correctly take existing remote definitions into account.
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index b0d31df..4e55b15 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -10,27 +10,14 @@
 	Include additional statistics at the end of blame output.
 
 -L <start>,<end>::
-	Annotate only the given line range.  <start> and <end> can take
-	one of these forms:
+-L :<regex>::
+	Annotate only the given line range.  <start> and <end> are optional.
+	``-L <start>'' or ``-L <start>,'' spans from <start> to end of file.
+	``-L ,<end>'' spans from start of file to <end>.
++
+<start> and <end> can take one of these forms:
 
-	- number
-+
-If <start> or <end> is a number, it specifies an
-absolute line number (lines count from 1).
-+
-
-- /regex/
-+
-This form will use the first line matching the given
-POSIX regex.  If <end> is a regex, it will search
-starting at the line given by <start>.
-+
-
-- +offset or -offset
-+
-This is only valid for <end> and will specify a number
-of lines before or after the line given by <start>.
-+
+include::line-range-format.txt[]
 
 -l::
 	Show long rev (Default: off).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 6e53fc5..e0b923f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -199,6 +199,9 @@
 	amWorkDir::
 		Advice that shows the location of the patch file when
 		linkgit:git-am[1] fails to apply it.
+	rmHints::
+		In case of failure in the output of linkgit:git-rm[1],
+		show directions on how to proceed from the current state.
 --
 
 core.fileMode::
@@ -876,16 +879,17 @@
 
 color.interactive::
 	When set to `always`, always use colors for interactive prompts
-	and displays (such as those used by "git-add --interactive").
-	When false (or `never`), never.  When set to `true` or `auto`, use
-	colors only when the output is to the terminal. Defaults to false.
+	and displays (such as those used by "git-add --interactive" and
+	"git-clean --interactive"). When false (or `never`), never.
+	When set to `true` or `auto`, use colors only when the output is
+	to the terminal. Defaults to false.
 
 color.interactive.<slot>::
-	Use customized color for 'git add --interactive'
-	output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
-	four distinct types of normal output from interactive
-	commands.  The values of these variables may be specified as
-	in color.branch.<slot>.
+	Use customized color for 'git add --interactive' and 'git clean
+	--interactive' output. `<slot>` may be `prompt`, `header`, `help`
+	or `error`, for four distinct types of normal output from
+	interactive commands.  The values of these variables may be
+	specified as in color.branch.<slot>.
 
 color.pager::
 	A boolean to enable/disable colored output when the pager is in
@@ -919,17 +923,21 @@
 	as `color.diff` and `color.grep` that control the use of color
 	per command family. Its scope will expand as more commands learn
 	configuration to set a default for the `--color` option.  Set it
-	to `always` if you want all output not intended for machine
-	consumption to use color, to `true` or `auto` if you want such
-	output to use color when written to the terminal, or to `false` or
-	`never` if you prefer Git commands not to use color unless enabled
-	explicitly with some other configuration or the `--color` option.
+	to `false` or `never` if you prefer Git commands not to use
+	color unless enabled explicitly with some other configuration
+	or the `--color` option. Set it to `always` if you want all
+	output not intended for machine consumption to use color, to
+	`true` or `auto` (this is the default since Git 1.8.4) if you
+	want such output to use color when written to the terminal.
 
 column.ui::
 	Specify whether supported commands should output in columns.
 	This variable consists of a list of tokens separated by spaces
 	or commas:
 +
+These options control when the feature should be enabled
+(defaults to 'never'):
++
 --
 `always`;;
 	always show in columns
@@ -937,24 +945,39 @@
 	never show in columns
 `auto`;;
 	show in columns if the output is to the terminal
+--
++
+These options control layout (defaults to 'column').  Setting any
+of these implies 'always' if none of 'always', 'never', or 'auto' are
+specified.
++
+--
 `column`;;
-	fill columns before rows (default)
+	fill columns before rows
 `row`;;
 	fill rows before columns
 `plain`;;
 	show in one column
+--
++
+Finally, these options can be combined with a layout option (defaults
+to 'nodense'):
++
+--
 `dense`;;
 	make unequal size columns to utilize more space
 `nodense`;;
 	make equal size columns
 --
-+
-This option defaults to 'never'.
 
 column.branch::
 	Specify whether to output branch listing in `git branch` in columns.
 	See `column.ui` for details.
 
+column.clean::
+	Specify the layout when list items in `git clean -i`, which always
+	shows files and directories in columns. See `column.ui` for details.
+
 column.status::
 	Specify whether to output untracked files in `git status` in columns.
 	See `column.ui` for details.
@@ -1826,39 +1849,59 @@
 	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:
+	Defines the action `git push` should take if no refspec is
+	explicitly given.  Different values are well-suited for
+	specific workflows; for instance, in a purely central workflow
+	(i.e. the fetch source is equal to the push destination),
+	`upstream` is probably what you want.  Possible values are:
 +
 --
-* `nothing` - do not push anything.
-* `matching` - push all branches having the same name in both ends.
-  This is for those who prepare all the branches into a publishable
-  shape and then push them out with a single command.  It is not
-  appropriate for pushing into a repository shared by multiple users,
-  since locally stalled branches will attempt a non-fast forward push
-  if other users updated the branch.
-  +
-  This is currently the default, but Git 2.0 will change the default
-  to `simple`.
-* `upstream` - push the current branch to its upstream branch
-  (`tracking` is a deprecated synonym for this).
-  With this, `git push` will update the same remote ref as the one which
-  is merged by `git pull`, making `push` and `pull` symmetrical.
-  See "branch.<name>.merge" for how to configure the upstream branch.
-* `simple` - like `upstream`, but refuses to push if the upstream
-  branch's name is different from the local one. This is the safest
-  option and is well-suited for beginners. It will become the default
-  in Git 2.0.
-* `current` - push the current branch to a branch of the same name.
---
+
+* `nothing` - do not push anything (error out) unless a refspec is
+  explicitly given. This is primarily meant for people who want to
+  avoid mistakes by always being explicit.
+
+* `current` - push the current branch to update a branch with the same
+  name on the receiving end.  Works in both central and non-central
+  workflows.
+
+* `upstream` - push the current branch back to the branch whose
+  changes are usually integrated into the current branch (which is
+  called `@{upstream}`).  This mode only makes sense if you are
+  pushing to the same repository you would normally pull from
+  (i.e. central workflow).
+
+* `simple` - in centralized workflow, work like `upstream` with an
+  added safety to refuse to push if the upstream branch's name is
+  different from the local one.
 +
-The `simple`, `current` and `upstream` modes are for those who want to
-push out a single branch after finishing work, even when the other
-branches are not yet ready to be pushed out. If you are working with
-other people to push into the same shared repository, you would want
-to use one of these.
+When pushing to a remote that is different from the remote you normally
+pull from, work as `current`.  This is the safest option and is suited
+for beginners.
++
+This mode will become the default in Git 2.0.
+
+* `matching` - push all branches having the same name on both ends.
+  This makes the repository you are pushing to remember the set of
+  branches that will be pushed out (e.g. if you always push 'maint'
+  and 'master' there and no other branches, the repository you push
+  to will have these two branches, and your local 'maint' and
+  'master' will be pushed there).
++
+To use this mode effectively, you have to make sure _all_ the
+branches you would push out are ready to be pushed out before
+running 'git push', as the whole point of this mode is to allow you
+to push all of the branches in one go.  If you usually finish work
+on only one branch and push out the result, while other branches are
+unfinished, this mode is not for you.  Also this mode is not
+suitable for pushing into a shared central repository, as other
+people may add new branches there, or update the tip of existing
+branches outside your control.
++
+This is currently the default, but Git 2.0 will change the default
+to `simple`.
+
+--
 
 rebase.stat::
 	Whether to show a diffstat of what changed upstream since the last
@@ -1867,6 +1910,14 @@
 rebase.autosquash::
 	If set to true enable '--autosquash' option by default.
 
+rebase.autostash::
+	When set to true, automatically create a temporary stash
+	before the operation begins, and apply it after the operation
+	ends.  This means that you can run rebase on a dirty worktree.
+	However, use with care: the final stash application after a
+	successful rebase might result in non-trivial conflicts.
+	Defaults to false.
+
 receive.autogc::
 	By default, git-receive-pack will run "git-gc --auto" after
 	receiving data from git-push and updating refs.  You can stop
@@ -2022,6 +2073,10 @@
 sendemail.smtpssl::
 	Deprecated alias for 'sendemail.smtpencryption = ssl'.
 
+sendemail.smtpsslcertpath::
+	Path to ca-certificates (either a directory or a single file).
+	Set it to an empty string to disable certificate verification.
+
 sendemail.<identity>.*::
 	Identity-specific versions of the 'sendemail.*' parameters
 	found below, taking precedence over those when the this
@@ -2066,6 +2121,14 @@
 	relative to the repository root (this was the default for Git
 	prior to v1.5.4).
 
+status.short::
+	Set to true to enable --short by default in linkgit:git-status[1].
+	The option --no-short takes precedence over this variable.
+
+status.branch::
+	Set to true to enable --branch by default in linkgit:git-status[1].
+	The option --no-branch takes precedence over this variable.
+
 status.showUntrackedFiles::
 	By default, linkgit:git-status[1] and linkgit:git-commit[1] show
 	files which are not currently tracked by Git. Directories which
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 104579d..bbed2cd 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -26,6 +26,11 @@
 	{git-diff? This is the default.}
 endif::git-format-patch[]
 
+-s::
+--no-patch::
+	Suppress diff output. Useful for commands like `git show` that
+	show the patch by default, or to cancel the effect of `--patch`.
+
 -U<n>::
 --unified=<n>::
 	Generate diffs with <n> lines of context instead of
@@ -333,7 +338,7 @@
 	a fraction, with a decimal point before it.  I.e., `-M5` becomes
 	0.5, and is thus the same as `-M50%`.  Similarly, `-M05` is
 	the same as `-M5%`.  To limit detection to exact renames, use
-	`-M100%`.
+	`-M100%`.  The default similarity index is 50%.
 
 -C[<n>]::
 --find-copies[=<n>]::
@@ -383,14 +388,36 @@
 	that matches other criteria, nothing is selected.
 
 -S<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.
+	Look for differences that change the number of occurrences of
+	the specified string (i.e. addition/deletion) in a file.
+	Intended for the scripter's use.
++
+It is useful when you're looking for an exact block of code (like a
+struct), and want to know the history of that block since it first
+came into being: use the feature iteratively to feed the interesting
+block in the preimage back into `-S`, and keep going until you get the
+very first version of the block.
 
 -G<regex>::
-	Look for differences whose added or removed line matches
-	the given <regex>.
+	Look for differences whose patch text contains added/removed
+	lines that match <regex>.
++
+To illustrate the difference between `-S<regex> --pickaxe-regex` and
+`-G<regex>`, consider a commit with the following diff in the same
+file:
++
+----
++    return !regexec(regexp, two->ptr, 1, &regmatch, 0);
+...
+-    hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);
+----
++
+While `git log -G"regexec\(regexp"` will show this commit, `git log
+-S"regexec\(regexp" --pickaxe-regex` will not (because the number of
+occurrences of that string did not change).
++
+See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more
+information.
 
 --pickaxe-all::
 	When `-S` or `-G` finds a change, show all the changes in that
@@ -398,8 +425,8 @@
 	in <string>.
 
 --pickaxe-regex::
-	Make the <string> not a plain string but an extended POSIX
-	regex to match.
+	Treat the <string> given to `-S` as an extended POSIX regular
+	expression to match.
 endif::git-format-patch[]
 
 -O<orderfile>::
@@ -439,6 +466,9 @@
 	differences even if one line has whitespace where the other
 	line has none.
 
+--ignore-blank-lines::
+	Ignore changes whose lines are all blank.
+
 --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.
@@ -480,7 +510,7 @@
 
 --ignore-submodules[=<when>]::
 	Ignore changes to submodules in the diff generation. <when> can be
-	either "none", "untracked", "dirty" or "all", which is the default
+	either "none", "untracked", "dirty" or "all", which is the default.
 	Using "none" will consider the submodule modified when it either contains
 	untracked or modified files or its HEAD differs from the commit recorded
 	in the superproject and can be used to override any settings of the
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 9cb6496..ba1fe49 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -61,7 +61,7 @@
 ifndef::git-pull[]
 -t::
 --tags::
-	This is a short-hand for giving "refs/tags/*:refs/tags/*"
+	This is a short-hand for giving `refs/tags/*:refs/tags/*`
 	refspec from the command line, to ask all tags to be fetched
 	and stored locally.  Because this acts as an explicit
 	refspec, the default refspecs (configured with the
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 5bbe7b6..54d8461 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -132,7 +132,7 @@
 --resolvemsg=<msg>::
 	When a patch failure occurs, <msg> will be printed
 	to the screen before exiting.  This overrides the
-	standard message informing you to use `--resolved`
+	standard message informing you to use `--continue`
 	or `--skip` to handle the failure.  This is solely
 	for internal use between 'git rebase' and 'git am'.
 
@@ -176,7 +176,7 @@
 
 . hand resolve the conflict in the working directory, and update
   the index file to bring it into a state that the patch should
-  have produced.  Then run the command with the '--resolved' option.
+  have produced.  Then run the command with the '--continue' option.
 
 The command refuses to process new mailboxes until the current
 operation is finished, so if you decide to start over from scratch,
diff --git a/Documentation/git-bisect-lk2009.txt b/Documentation/git-bisect-lk2009.txt
index 0eed3e3..afeb86c 100644
--- a/Documentation/git-bisect-lk2009.txt
+++ b/Documentation/git-bisect-lk2009.txt
@@ -1320,7 +1320,7 @@
 ;-)
 _____________
 
-Acknowledgements
+Acknowledgments
 ----------------
 
 Many thanks to Junio Hamano for his help in reviewing this paper, for
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 9a05c2b..6cea7f1 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -8,9 +8,9 @@
 SYNOPSIS
 --------
 [verse]
-'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
-	    [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>]
-	    [<rev> | --contents <file> | --reverse <rev>] [--] <file>
+'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
+	    [-L n,m | -L :fn] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
+	    [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
 DESCRIPTION
 -----------
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 30d585a..3ddec0b 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -58,12 +58,16 @@
 	to apply the filter to the content recorded in the index at <path>.
 
 --batch::
-	Print the SHA-1, type, size, and contents of each object provided on
-	stdin. May not be combined with any other options or arguments.
+--batch=<format>::
+	Print object information and contents for each object provided
+	on stdin.  May not be combined with any other options or arguments.
+	See the section `BATCH OUTPUT` below for details.
 
 --batch-check::
-	Print the SHA-1, type, and size of each object provided on stdin. May not
-	be combined with any other options or arguments.
+--batch-check=<format>::
+	Print object information for each object provided on stdin.  May
+	not be combined with any other options or arguments.  See the
+	section `BATCH OUTPUT` below for details.
 
 OUTPUT
 ------
@@ -78,28 +82,81 @@
 If <type> is specified, the raw (though uncompressed) contents of the <object>
 will be returned.
 
-If '--batch' is specified, output of the following form is printed for each
-object specified on stdin:
+BATCH OUTPUT
+------------
+
+If `--batch` or `--batch-check` is given, `cat-file` will read objects
+from stdin, one per line, and print information about them.
+
+Each line is split at the first whitespace boundary. All characters
+before that whitespace are considered as a whole object name, and are
+parsed as if given to linkgit:git-rev-parse[1]. Characters after that
+whitespace can be accessed using the `%(rest)` atom (see below).
+
+You can specify the information shown for each object by using a custom
+`<format>`. The `<format>` is copied literally to stdout for each
+object, with placeholders of the form `%(atom)` expanded, followed by a
+newline. The available atoms are:
+
+`objectname`::
+	The 40-hex object name of the object.
+
+`objecttype`::
+	The type of of the object (the same as `cat-file -t` reports).
+
+`objectsize`::
+	The size, in bytes, of the object (the same as `cat-file -s`
+	reports).
+
+`objectsize:disk`::
+	The size, in bytes, that the object takes up on disk. See the
+	note about on-disk sizes in the `CAVEATS` section below.
+
+`rest`::
+	The text (if any) found after the first run of whitespace on the
+	input line (i.e., the "rest" of the line).
+
+If no format is specified, the default format is `%(objectname)
+%(objecttype) %(objectsize)`.
+
+If `--batch` is specified, the object information is followed by the
+object contents (consisting of `%(objectsize)` bytes), followed by a
+newline.
+
+For example, `--batch` without a custom format would produce:
 
 ------------
 <sha1> SP <type> SP <size> LF
 <contents> LF
 ------------
 
-If '--batch-check' is specified, output of the following form is printed for
-each object specified on stdin:
+Whereas `--batch-check='%(objectname) %(objecttype)'` would produce:
 
 ------------
-<sha1> SP <type> SP <size> LF
+<sha1> SP <type> LF
 ------------
 
-For both '--batch' and '--batch-check', output of the following form is printed
-for each object specified on stdin that does not exist in the repository:
+If a name is specified on stdin that cannot be resolved to an object in
+the repository, then `cat-file` will ignore any custom format and print:
 
 ------------
 <object> SP missing LF
 ------------
 
+
+CAVEATS
+-------
+
+Note that the sizes of objects on disk are reported accurately, but care
+should be taken in drawing conclusions about which refs or objects are
+responsible for disk usage. The size of a packed non-delta object may be
+much larger than the size of objects which delta against it, but the
+choice of which object is the base and which is the delta is arbitrary
+and is subject to change during a repack. Note also that multiple copies
+of an object may be present in the object database; in this case, it is
+undefined which copy's size will be reported.
+
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index 5abdbaa..a7be80d 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -56,6 +56,11 @@
 'set';;		when the attribute is defined as true.
 <value>;;	when a value has been assigned to the attribute.
 
+Buffering happens as documented under the `GIT_FLUSH` option in
+linkgit:git[1].  The caller is responsible for avoiding deadlocks
+caused by overfilling an input buffer or reading from an empty output
+buffer.
+
 EXAMPLES
 --------
 
diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt
index 854e4d0..d2df487 100644
--- a/Documentation/git-check-ignore.txt
+++ b/Documentation/git-check-ignore.txt
@@ -39,6 +39,12 @@
 	below).  If `--stdin` is also given, input paths are separated
 	with a NUL character instead of a linefeed character.
 
+-n, --non-matching::
+	Show given paths which don't match any pattern.	 This only
+	makes sense when `--verbose` is enabled, otherwise it would
+	not be possible to distinguish between paths which match a
+	pattern and those which don't.
+
 OUTPUT
 ------
 
@@ -65,6 +71,20 @@
 
 <source> <NULL> <linenum> <NULL> <pattern> <NULL> <pathname> <NULL>
 
+If `-n` or `--non-matching` are specified, non-matching pathnames will
+also be output, in which case all fields in each output record except
+for <pathname> will be empty.  This can be useful when running
+non-interactively, so that files can be incrementally streamed to
+STDIN of a long-running check-ignore process, and for each of these
+files, STDOUT will indicate whether that file matched a pattern or
+not.  (Without this option, it would be impossible to tell whether the
+absence of output for a given file meant that it didn't match any
+pattern, or that the output hadn't been generated yet.)
+
+Buffering happens as documented under the `GIT_FLUSH` option in
+linkgit:git[1].  The caller is responsible for avoiding deadlocks
+caused by overfilling an input buffer or reading from an empty output
+buffer.
 
 EXIT STATUS
 -----------
@@ -82,7 +102,7 @@
 --------
 linkgit:gitignore[5]
 linkgit:gitconfig[5]
-linkgit:git-ls-files[5]
+linkgit:git-ls-files[1]
 
 GIT
 ---
diff --git a/Documentation/git-check-mailmap.txt b/Documentation/git-check-mailmap.txt
new file mode 100644
index 0000000..39028ee
--- /dev/null
+++ b/Documentation/git-check-mailmap.txt
@@ -0,0 +1,47 @@
+git-check-mailmap(1)
+====================
+
+NAME
+----
+git-check-mailmap - Show canonical names and email addresses of contacts
+
+
+SYNOPSIS
+--------
+[verse]
+'git check-mailmap' [options] <contact>...
+
+
+DESCRIPTION
+-----------
+
+For each ``Name $$<user@host>$$'' or ``$$<user@host>$$'' from the command-line
+or standard input (when using `--stdin`), look up the person's canonical name
+and email address (see "Mapping Authors" below). If found, print them;
+otherwise print the input as-is.
+
+
+OPTIONS
+-------
+--stdin::
+	Read contacts, one per line, from the standard input after exhausting
+	contacts provided on the command-line.
+
+
+OUTPUT
+------
+
+For each contact, a single line is output, terminated by a newline.  If the
+name is provided or known to the 'mailmap', ``Name $$<user@host>$$'' is
+printed; otherwise only ``$$<user@host>$$'' is printed.
+
+
+MAPPING AUTHORS
+---------------
+
+include::mailmap.txt[]
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index a49be1b..fc02959 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -54,6 +54,8 @@
 
 . They cannot contain a sequence `@{`.
 
+. They cannot be the single character `@`.
+
 . They cannot contain a `\`.
 
 These rules make it easy for shell script based tools to parse
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 23a9413..ca118ac 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -131,9 +131,9 @@
 	"--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-tracking 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.
+derived from the remote-tracking branch, by looking at the local part of
+the refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".
 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
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index bdc3ab8..75fb543 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -34,7 +34,13 @@
 -f::
 --force::
 	If the Git configuration variable clean.requireForce is not set
-	to false, 'git clean' will refuse to run unless given -f or -n.
+	to false, 'git clean' will refuse to run unless given -f, -n or
+	-i.
+
+-i::
+--interactive::
+	Show what would be done and clean files interactively. See
+	``Interactive mode'' for details.
 
 -n::
 --dry-run::
@@ -63,6 +69,67 @@
 	Remove only files ignored by Git.  This may be useful to rebuild
 	everything from scratch, but keep manually created files.
 
+Interactive mode
+----------------
+When the command enters the interactive mode, it shows the
+files and directories to be cleaned, and goes into its
+interactive command loop.
+
+The command loop shows the list of subcommands available, and
+gives a prompt "What now> ".  In general, when the prompt ends
+with a single '>', you can pick only one of the choices given
+and type return, like this:
+
+------------
+    *** Commands ***
+	1: clean                2: filter by pattern    3: select by numbers
+	4: ask each             5: quit                 6: help
+    What now> 1
+------------
+
+You also could say `c` or `clean` above as long as the choice is unique.
+
+The main command loop has 6 subcommands.
+
+clean::
+
+   Start cleaning files and directories, and then quit.
+
+filter by pattern::
+
+   This shows the files and directories to be deleted and issues an
+   "Input ignore patterns>>" prompt. You can input space-seperated
+   patterns to exclude files and directories from deletion.
+   E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
+   deletion. When you are satisfied with the filtered result, press
+   ENTER (empty) back to the main menu.
+
+select by numbers::
+
+   This shows the files and directories to be deleted and issues an
+   "Select items to delete>>" prompt. When the prompt ends with double
+   '>>' like this, 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 omitted, all remaining patches are taken.  E.g. "7-" to
+   choose 7,8,9 from the list.  You can say '*' to choose everything.
+   Also when you are satisfied with the filtered result, press ENTER
+   (empty) back to the main menu.
+
+ask each::
+
+  This will start to clean, and you must confirm one by one in order
+  to delete items. Please note that this action is not as efficient
+  as the above two actions.
+
+quit::
+
+  This lets you quit without do cleaning.
+
+help::
+
+  Show brief usage of interactive git-clean.
+
 SEE ALSO
 --------
 linkgit:gitignore[5]
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index a0727d7..450f158 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -239,8 +239,8 @@
 * Clone from upstream:
 +
 ------------
-$ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
-$ cd my2.6
+$ git clone git://git.kernel.org/pub/scm/.../linux.git my-linux
+$ cd my-linux
 $ make
 ------------
 
@@ -257,10 +257,10 @@
 * Clone from upstream while borrowing from an existing local directory:
 +
 ------------
-$ git clone --reference my2.6 \
-	git://git.kernel.org/pub/scm/.../linux-2.7 \
-	my2.7
-$ cd my2.7
+$ git clone --reference /git/linux.git \
+	git://git.kernel.org/pub/scm/.../linux.git \
+	my-linux
+$ cd my-linux
 ------------
 
 
@@ -271,13 +271,6 @@
 ------------
 
 
-* Create a repository on the kernel.org machine that borrows from Linus:
-+
-------------
-$ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
-    /pub/scm/.../me/subsys-2.6.git
-------------
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index d88a6fc..34b0894 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -82,7 +82,7 @@
 --get::
 	Get the value for a given key (optionally filtered by a regex
 	matching the value). Returns error code 1 if the key was not
-	found and error code 2 if multiple key values were found.
+	found and the last value if multiple key values were found.
 
 --get-all::
 	Like get, but does not fail if the number of values for the key
@@ -114,10 +114,26 @@
 +
 See also <<FILES>>.
 
+--local::
+	For writing options: write to the repository .git/config file.
+	This is	the default behavior.
++
+For reading options: read only from the repository .git/config rather than
+from all available files.
++
+See also <<FILES>>.
+
 -f config-file::
 --file config-file::
 	Use the given config file instead of the one specified by GIT_CONFIG.
 
+--blob blob::
+	Similar to '--file' but use the given blob instead of a file. E.g.
+	you can use 'master:.gitmodules' to read values from the file
+	'.gitmodules' in the master branch. See "SPECIFYING REVISIONS"
+	section in linkgit:gitrevisions[7] for a more complete list of
+	ways to spell blob names.
+
 --remove-section::
 	Remove the given section from the configuration file.
 
@@ -197,12 +213,8 @@
 If not set explicitly with '--file', there are four files where
 'git config' will search for configuration options:
 
-$GIT_DIR/config::
-	Repository specific configuration file.
-
-~/.gitconfig::
-	User-specific configuration file. Also called "global"
-	configuration file.
+$(prefix)/etc/gitconfig::
+	System-wide configuration file.
 
 $XDG_CONFIG_HOME/git/config::
 	Second user-specific configuration file. If $XDG_CONFIG_HOME is not set
@@ -212,8 +224,12 @@
 	you sometimes use older versions of Git, as support for this
 	file was added fairly recently.
 
-$(prefix)/etc/gitconfig::
-	System-wide configuration file.
+~/.gitconfig::
+	User-specific configuration file. Also called "global"
+	configuration file.
+
+$GIT_DIR/config::
+	Repository specific configuration file.
 
 If no further options are given, all reading options will read all of these
 files that are available. If the global or the system-wide configuration
@@ -221,6 +237,10 @@
 file is not available or readable, 'git config' will exit with a non-zero
 error code. However, in neither case will an error message be issued.
 
+The files are read in the order given above, with last value found taking
+precedence over values read earlier.  When multiple values are taken then all
+values of a key from all files will be used.
+
 All writing options will per default write to the repository specific
 configuration file. Note that this also affects options like '--replace-all'
 and '--unset'. *'git config' will only ever change one file at a time*.
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 28e5ec0..9439cd6 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -88,6 +88,11 @@
 --always::
 	Show uniquely abbreviated commit object as fallback.
 
+--first-parent::
+	Follow only the first parent commit upon seeing a merge commit.
+	This is useful when you wish to not match tags on branches merged
+	in the history of the target commit.
+
 EXAMPLES
 --------
 
@@ -149,7 +154,9 @@
 If an exact match was not found, 'git describe' will walk back
 through the commit history to locate an ancestor commit which
 has been tagged.  The ancestor's tag will be output along with an
-abbreviation of the input committish's SHA-1.
+abbreviation of the input committish's SHA-1. If '--first-parent' was
+specified then the walk will only consider the first parent of each
+commit.
 
 If multiple tags were found during the walk then the tag which
 has the fewest commits different from the input committish will be
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index c0b7c58..a86cf62 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-diff-index - Compares content and mode of blobs between the index and repository
+git-diff-index - Compare a tree to the working tree or index
 
 
 SYNOPSIS
@@ -13,11 +13,11 @@
 
 DESCRIPTION
 -----------
-Compares the content and mode of the blobs found via a tree
-object with the content of the current index and, optionally
-ignoring the stat state of the file on disk.  When paths are
-specified, compares only those named paths.  Otherwise all
-entries in the index are compared.
+Compares the content and mode of the blobs found in a tree object
+with the corresponding tracked files in the working tree, or with the
+corresponding paths in the index.  When <path> arguments are present,
+compares only paths matching those patterns.  Otherwise all tracked
+files are compared.
 
 OPTIONS
 -------
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index a7b4620..78d6d50 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -18,8 +18,8 @@
 DESCRIPTION
 -----------
 Show changes between the working tree and the index or a tree, changes
-between the index and a tree, changes between two trees, or changes
-between two files on disk.
+between the index and a tree, changes between two trees, changes between
+two blob objects, or changes between two files on disk.
 
 'git diff' [--options] [--] [<path>...]::
 
@@ -56,11 +56,6 @@
 	This is to view the changes between two arbitrary
 	<commit>.
 
-'git diff' [options] <blob> <blob>::
-
-	This form is to view the differences between the raw
-	contents of two blob objects.
-
 'git diff' [--options] <commit>..<commit> [--] [<path>...]::
 
 	This is synonymous to the previous form.  If <commit> on
@@ -87,6 +82,11 @@
 "<commit>\...<commit>") do not mean a range as defined in the
 "SPECIFYING RANGES" section in linkgit:gitrevisions[7].
 
+'git diff' [options] <blob> <blob>::
+
+	This form is to view the differences between the raw
+	contents of two blob objects.
+
 OPTIONS
 -------
 :git-diff: 1
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index efb0380..85f1f30 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -141,7 +141,7 @@
 -----------
 
 Since 'git fast-import' cannot tag trees, you will not be
-able to export the linux-2.6.git repository completely, as it contains
+able to export the linux.git repository completely, as it contains
 a tag referencing a tree instead of a commit.
 
 GIT
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3911877..e394276 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -187,6 +187,21 @@
 	The negated form `--no-cc` discards all `Cc:` headers added so
 	far (from config or command line).
 
+--from::
+--from=<ident>::
+	Use `ident` in the `From:` header of each commit email. If the
+	author ident of the commit is not textually identical to the
+	provided `ident`, place a `From:` header in the body of the
+	message with the original author. If no `ident` is given, use
+	the committer ident.
++
+Note that this option is only useful if you are actually sending the
+emails and want to identify yourself as the sender, but retain the
+original author (and `git am` will correctly pick up the in-body
+header). Note also that `git send-email` already handles this
+transformation for you, and this option should not be used if you are
+feeding the result to `git send-email`.
+
 --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.
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index b370b02..2402ed6 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -62,8 +62,9 @@
 
 --prune=<date>::
 	Prune loose objects older than date (default is 2 weeks ago,
-	overridable by the config variable `gc.pruneExpire`).  This
-	option is on by default.
+	overridable by the config variable `gc.pruneExpire`).
+	--prune=all prunes loose objects regardless of their age.
+	--prune is on by default.
 
 --no-prune::
 	Do not prune any loose objects.
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index bde8eec..7a4e055 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -74,6 +74,9 @@
 --strict::
 	Die, if the pack contains broken objects or links.
 
+--check-self-contained-and-connected::
+	Die if the pack contains broken links. For internal use only.
+
 --threads=<n>::
 	Specifies the number of threads to spawn when resolving
 	deltas. This requires that index-pack be compiled with
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index a976534..ac2694d 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -62,6 +62,19 @@
 	Note that only message is considered, if also a diff is shown
 	its size is not included.
 
+-L <start>,<end>:<file>, -L :<regex>:<file>::
+
+	Trace the evolution of the line range given by "<start>,<end>"
+	(or the funcname regex <regex>) within the <file>.  You may
+	not give any pathspec limiters.  This is currently limited to
+	a walk starting from a single revision, i.e., you may only
+	give zero or one positive revision arguments.
+	You can specify this option more than once.
++
+<start> and <end> can take one of these forms:
+
+include::line-range-format.txt[]
+
 <revision range>::
 	Show only commits in the specified revision range.  When no
 	<revision range> is specified, it defaults to `HEAD` (i.e. the
@@ -84,7 +97,7 @@
 
 include::pretty-formats.txt[]
 
-Common diff options
+COMMON DIFF OPTIONS
 -------------------
 
 :git-log: 1
@@ -92,7 +105,7 @@
 
 include::diff-generate-patch.txt[]
 
-Examples
+EXAMPLES
 --------
 `git log --no-merges`::
 
@@ -115,9 +128,9 @@
 	in the "release" branch, along with the list of paths
 	each commit modifies.
 
-`git log --follow builtin-rev-list.c`::
+`git log --follow builtin/rev-list.c`::
 
-	Shows the commits that changed builtin-rev-list.c, including
+	Shows the commits that changed builtin/rev-list.c, including
 	those commits that occurred before the file was given its
 	present name.
 
@@ -140,15 +153,20 @@
 	This makes sense only when following a strict policy of merging all
 	topic branches when staying on a single integration branch.
 
+`git log -L '/int main/',/^}/:main.c`::
+
+	Shows how the function `main()` in the file 'main.c' evolved
+	over time.
+
 `git log -3`::
 	Limits the number of commits to show to 3.
 
-Discussion
+DISCUSSION
 ----------
 
 include::i18n.txt[]
 
-Configuration
+CONFIGURATION
 -------------
 
 See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 774de5e..2e22915 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -48,9 +48,9 @@
 	exit without talking to the remote.
 
 <repository>::
-	Location of the repository.  The shorthand defined in
-	$GIT_DIR/branches/ can be used. Use "." (dot) to list references in
-	the local repository.
+	The "remote" repository to query.  This parameter can be
+	either a URL or the name of a remote (see the GIT URLS and
+	REMOTES sections of linkgit:git-fetch[1]).
 
 <refs>...::
 	When unspecified, all references, after filtering done
@@ -70,9 +70,8 @@
 	$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
 	5fe978a5381f1fbad26a80e682ddd2a401966740	refs/heads/master
 	c781a84b5204fb294c9ccc79f8b3baceeb32c061	refs/heads/pu
-	b1d096f2926c4e37c9c0b6a7bf2119bedaa277cb	refs/heads/rc
-	$ echo http://www.kernel.org/pub/scm/git/git.git >.git/branches/public
-	$ git ls-remote --tags public v\*
+	$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
+	$ git ls-remote --tags korg v\*
 	d6602ec5194c87b0fc87103ca4d67251c76f233a	refs/tags/v0.99
 	f25a265a342aed6041ab0cc484224d9ca54b6f41	refs/tags/v0.99.1
 	c5db5456ae3b0873fc659c19fafdde22313cc441	refs/tags/v0.99.2
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 67ca99c..8c7f2f6 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -56,8 +56,8 @@
 was started), 'git merge --abort' will in some cases be unable to
 reconstruct the original (pre-merge) changes. Therefore:
 
-*Warning*: Running 'git merge' with uncommitted changes is
-discouraged: while possible, it leaves you in a state that is hard to
+*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+discouraged: while possible, it may leave you in a state that is hard to
 back out of in the case of a conflict.
 
 
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index ad1d146..15b00e0 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -25,14 +25,17 @@
 	Do not use branch names, but only tags to name the commits
 
 --refs=<pattern>::
-	Only use refs whose names match a given shell pattern.
+	Only use refs whose names match a given shell pattern.  The pattern
+	can be one of branch name, tag name or fully qualified ref name.
 
 --all::
 	List all commits reachable from all refs
 
 --stdin::
-	Read from stdin, append "(<rev_name>)" to all sha1's of nameable
-	commits, and pass to stdout
+	Transform stdin by substituting all the 40-character SHA-1
+	hexes (say $hex) with "$hex ($rev_name)".  When used with
+	--name-only, substitute with "$rev_name", omitting $hex
+	altogether.  Intended for the scripter's use.
 
 --name-only::
 	Instead of printing both the SHA-1 and the name, print only
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index c579fbc..8cba16d 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -176,13 +176,16 @@
 These options can be used in the initial 'clone' as well as in
 subsequent 'sync' operations.
 
---branch <branch>::
-	Import changes into given branch.  If the branch starts with
-	'refs/', it will be used as is.  Otherwise if it does not start
-	with 'p4/', that prefix is added.  The branch is assumed to
-	name a remote tracking, but this can be modified using
-	'--import-local', or by giving a full ref name.  The default
-	branch is 'master'.
+--branch <ref>::
+	Import changes into <ref> instead of refs/remotes/p4/master.
+	If <ref> starts with refs/, it is used as is.  Otherwise, if
+	it does not start with p4/, that prefix is added.
++
+By default a <ref> not starting with refs/ is treated as the
+name of a remote-tracking branch (under refs/remotes/).  This
+behavior can be modified using the --import-local option.
++
+The default <ref> is "master".
 +
 This example imports a new remote "p4/proj2" into an existing
 Git repository:
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
index f131677..154081f 100644
--- a/Documentation/git-pack-refs.txt
+++ b/Documentation/git-pack-refs.txt
@@ -33,8 +33,8 @@
 `$GIT_DIR/refs` directory hierarchy.
 
 A recommended practice to deal with a repository with too many
-refs is to pack its refs with `--all --prune` once, and
-occasionally run `git pack-refs --prune`.  Tags are by
+refs is to pack its refs with `--all` once, and
+occasionally run `git pack-refs`.  Tags are by
 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,
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 80d01b0..bf82410 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -59,7 +59,7 @@
 `.git/objects/info/alternates`:
 
 ------------
-$ git prune $(cd ../another && $(git rev-parse --all))
+$ git prune $(cd ../another && git rev-parse --all)
 ------------
 
 Notes
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 24ab07a..6ef8d59 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-pull - Fetch from and merge with another repository or a local branch
+git-pull - Fetch from and integrate with another repository or a local branch
 
 
 SYNOPSIS
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index d514813..f7dfe48 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -11,7 +11,7 @@
 [verse]
 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
 	   [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
-	   [<repository> [<refspec>...]]
+	   [--no-verify] [<repository> [<refspec>...]]
 
 DESCRIPTION
 -----------
@@ -136,6 +136,15 @@
 	not an ancestor of the local ref used to overwrite it.
 	This flag disables the check.  This can cause the
 	remote repository to lose commits; use it with care.
+	Note that `--force` applies to all the refs that are pushed,
+	hence using it with `push.default` set to `matching` or with
+	multiple push destinations configured with `remote.*.push`
+	may overwrite refs other than the current branch (including
+	local refs that are strictly behind their remote counterpart).
+	To force a push to only one branch, use a `+` in front of the
+	refspec to push	(e.g `git push origin +master` to force a push
+	to the `master`	branch). See the `<refspec>...` section above
+	for details.
 
 --repo=<repository>::
 	This option is only relevant if no <repository> argument is
@@ -195,6 +204,11 @@
 	be pushed. If on-demand was not able to push all necessary
 	revisions it will also be aborted and exit with non-zero status.
 
+--[no-]verify::
+	Toggle the pre-push hook (see linkgit:githooks[5]).  The
+	default is \--verify, giving the hook a chance to prevent the
+	push.  With \--no-verify, the hook is bypassed completely.
+
 
 include::urls-remotes.txt[]
 
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index aca8405..6b2e1c8 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -208,6 +208,9 @@
 rebase.autosquash::
 	If set to true enable '--autosquash' option by default.
 
+rebase.autostash::
+	If set to true enable '--autostash' option by default.
+
 OPTIONS
 -------
 --onto <newbase>::
@@ -386,7 +389,9 @@
 	the same ..., automatically modify the todo list of rebase -i
 	so that the commit marked for squashing comes right after the
 	commit to be modified, and change the action of the moved
-	commit from `pick` to `squash` (or `fixup`).
+	commit from `pick` to `squash` (or `fixup`).  Ignores subsequent
+	"fixup! " or "squash! " after the first, in case you referred to an
+	earlier fixup/squash with `git commit --fixup/--squash`.
 +
 This option is only valid when the '--interactive' option is used.
 +
@@ -394,6 +399,13 @@
 configuration variable `rebase.autosquash`, this option can be
 used to override and disable this setting.
 
+--[no-]autostash::
+	Automatically create a temporary stash before the operation
+	begins, and apply it after the operation ends.  This means
+	that you can run rebase on a dirty worktree.  However, use
+	with care: the final stash application after a successful
+	rebase might result in non-trivial conflicts.
+
 --no-ff::
 	With --interactive, cherry-pick all rebased commits instead of
 	fast-forwarding over the unchanged ones.  This ensures that the
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index fb8697e..70791b9 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -67,14 +67,19 @@
 --expire=<time>::
 	Entries older than this time are pruned.  Without the
 	option it is taken from configuration `gc.reflogExpire`,
-	which in turn defaults to 90 days.
+	which in turn defaults to 90 days.  --expire=all prunes
+	entries regardless of their age; --expire=never turns off
+	pruning of reachable entries (but see --expire-unreachable).
 
 --expire-unreachable=<time>::
 	Entries older than this time and not reachable from
 	the current tip of the branch are pruned.  Without the
 	option it is taken from configuration
 	`gc.reflogExpireUnreachable`, which in turn defaults to
-	30 days.
+	30 days.  --expire-unreachable=all prunes unreachable
+	entries regardless of their age; --expire-unreachable=never
+	turns off early pruning of unreachable entries (but see
+	--expire).
 
 --all::
 	Instead of listing <refs> explicitly, prune all refs.
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 581bb4c..9c3e3bf 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -187,18 +187,25 @@
 $ git remote
 origin
 $ git branch -r
-origin/master
-$ git remote add linux-nfs git://linux-nfs.org/pub/linux/nfs-2.6.git
+  origin/HEAD -> origin/master
+  origin/master
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
 $ git remote
-linux-nfs
 origin
-$ git fetch
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
-  commit: bf81b46
+staging
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch]      master     -> staging/master
+ * [new branch]      staging-linus -> staging/staging-linus
+ * [new branch]      staging-next -> staging/staging-next
 $ git branch -r
-origin/master
-linux-nfs/master
-$ git checkout -b nfs linux-nfs/master
+  origin/HEAD -> origin/master
+  origin/master
+  staging/master
+  staging/staging-linus
+  staging/staging-next
+$ git checkout -b staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index a404b47..f445cb3 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,7 +9,7 @@
 --------
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-sh>] [--] [<paths>...]
+'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
 'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 947d62f..2b126c0 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -59,6 +59,22 @@
 	If there is no parameter given by the user, use `<arg>`
 	instead.
 
+--prefix <arg>::
+	Behave as if 'git rev-parse' was invoked from the `<arg>`
+	subdirectory of the working tree.  Any relative filenames are
+	resolved as if they are prefixed by `<arg>` and will be printed
+	in that form.
++
+This can be used to convert arguments to a command run in a subdirectory
+so that they can still be used after moving to the top-level of the
+repository.  For example:
++
+----
+prefix=$(git rev-parse --show-prefix)
+cd "$(git rev-parse --show-toplevel)"
+eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
+----
+
 --verify::
 	Verify that exactly one parameter is provided, and that it
 	can be turned into a raw 20-byte SHA-1 that can be used to
@@ -67,7 +83,7 @@
 +
 If you want to make sure that the output actually names an object in
 your object database and/or can be used as a specific type of object
-you require, you can add "^{type}" peeling operator to the parmeter.
+you require, you can add "^{type}" peeling operator to the parameter.
 For example, `git rev-parse "$VAR^{commit}"` will make sure `$VAR`
 names an existing object that is a commit-ish (i.e. a commit, or an
 annotated tag that points at a commit).  To make sure that `$VAR`
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 40a9a9a..f0e57a5 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -198,6 +198,12 @@
 --smtp-ssl::
 	Legacy alias for '--smtp-encryption ssl'.
 
+--smtp-ssl-cert-path::
+	Path to ca-certificates (either a directory or a single file).
+	Set it to an empty string to disable certificate verification.
+	Defaults to the value set to the 'sendemail.smtpsslcertpath'
+	configuration variable, if set, or `/etc/ssl/certs` otherwise.
+
 --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'),
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index de4d352..b0a309b 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -21,6 +21,8 @@
 dereferenced into object IDs. Additionally, it can be used to test whether a
 particular ref exists.
 
+By default, shows the tags, heads, and remote refs.
+
 The --exclude-existing form is a filter that does the inverse, it shows the
 refs from stdin that don't exist in the local repository.
 
@@ -32,14 +34,14 @@
 
 --head::
 
-	Show the HEAD reference.
+	Show the HEAD reference, even if it would normally be filtered out.
 
 --tags::
 --heads::
 
-	Limit to only "refs/heads" and "refs/tags", respectively.  These
-	options are not mutually exclusive; when given both, references stored
-	in "refs/heads" and "refs/tags" are displayed.
+	Limit to "refs/heads" and "refs/tags", respectively.  These options
+	are not mutually exclusive; when given both, references stored in
+	"refs/heads" and "refs/tags" are displayed.
 
 -d::
 --dereference::
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index ae4edcc..4e617e6 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -45,6 +45,15 @@
 include::pretty-formats.txt[]
 
 
+COMMON DIFF OPTIONS
+-------------------
+
+:git-log: 1
+include::diff-options.txt[]
+
+include::diff-generate-patch.txt[]
+
+
 EXAMPLES
 --------
 
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 711ffe1..7c8b648 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -13,10 +13,12 @@
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
-	     [-u|--include-untracked] [-a|--all] [<message>]]
+'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+	     [-u|--include-untracked] [-a|--all] [-f|--force]
+	     [<message>]]
 'git stash' clear
-'git stash' create
+'git stash' create [<message>]
+'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
 
 DESCRIPTION
 -----------
@@ -43,7 +45,7 @@
 OPTIONS
 -------
 
-save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-f|--force] [<message>]::
 
 	Save your local modifications to a new 'stash', and run `git reset
 	--hard` to revert them.  The <message> part is optional and gives
@@ -70,6 +72,13 @@
 +
 The `--patch` option implies `--keep-index`.  You can use
 `--no-keep-index` to override this.
++
+In some cases, saving a stash could mean irretrievably removing some
+data - if a directory with untracked files replaces a tracked file of
+the same name, the new untracked files are not saved (except in case
+of `--include-untracked`) but the original tracked file shall be restored.
+By default, `stash save` will abort in such a case; `--force` will allow
+it to remove the untracked files.
 
 list [<options>]::
 
@@ -151,7 +160,15 @@
 
 	Create a stash (which is a regular commit object) and return its
 	object name, without storing it anywhere in the ref namespace.
+	This is intended to be useful for scripts.  It is probably not
+	the command you want to use; see "save" above.
 
+store::
+
+	Store a given stash created via 'git stash create' (which is a
+	dangling merge commit) in the stash ref, updating the stash
+	reflog.  This is intended to be useful for scripts.  It is
+	probably not the command you want to use; see "save" above.
 
 DISCUSSION
 ----------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index e576713..bfef8a0 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -10,12 +10,12 @@
 --------
 [verse]
 'git submodule' [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
-	      [--reference <repository>] [--] <repository> [<path>]
+	      [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
-	      [-f|--force] [--rebase] [--reference <repository>]
+	      [-f|--force] [--rebase] [--reference <repository>] [--depth <depth>]
 	      [--merge] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
 	      [commit] [--] [<path>...]
@@ -159,7 +159,9 @@
 	This will make the submodules HEAD be detached unless `--rebase` or
 	`--merge` is specified or the key `submodule.$name.update` is set to
 	`rebase`, `merge` or `none`. `none` can be overridden by specifying
-	`--checkout`.
+	`--checkout`. Setting the key `submodule.$name.update` to `!command`
+	will cause `command` to be run. `command` can be any arbitrary shell
+	command that takes a single argument, namely the sha1 to update to.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -262,7 +264,7 @@
 --remote::
 	This option is only valid for the update command.  Instead of using
 	the superproject's recorded SHA-1 to update the submodule, use the
-	status of the submodule's remote tracking branch.  The remote used
+	status of the submodule's remote-tracking branch.  The remote used
 	is branch's remote (`branch.<name>.remote`), defaulting to `origin`.
 	The remote branch used defaults to `master`, but the branch name may
 	be overridden by setting the `submodule.<name>.branch` option in
@@ -328,6 +330,12 @@
 	only in the submodules of the current repo, but also
 	in any nested submodules inside those submodules (and so on).
 
+--depth::
+	This option is valid for add and update commands. Create a 'shallow'
+	clone with a history truncated to the specified number of revisions.
+	See linkgit:git-clone[1]
+
+
 <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-web--browse.txt b/Documentation/git-web--browse.txt
index ba79cb4..5aec4ec 100644
--- a/Documentation/git-web--browse.txt
+++ b/Documentation/git-web--browse.txt
@@ -34,6 +34,7 @@
 * dillo
 * open (this is the default under Mac OS X GUI)
 * start (this is the default under MinGW)
+* cygstart (this is the default under Cygwin)
 
 Custom commands may also be specified.
 
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 9e302b0..3bdd56e 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,12 +43,21 @@
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
+* link:v1.8.3.4/git.html[documentation for release 1.8.3.4]
+
+* release notes for
+  link:RelNotes/1.8.3.4.txt[1.8.3.4],
+  link:RelNotes/1.8.3.3.txt[1.8.3.3],
+  link:RelNotes/1.8.3.2.txt[1.8.3.2],
+  link:RelNotes/1.8.3.1.txt[1.8.3.1],
+  link:RelNotes/1.8.3.txt[1.8.3].
+
 * link:v1.8.2.3/git.html[documentation for release 1.8.2.3]
 
 * release notes for
-  link:RelNotes/1.8.2.3.txt[1.8.2.3].
-  link:RelNotes/1.8.2.2.txt[1.8.2.2].
-  link:RelNotes/1.8.2.1.txt[1.8.2.1].
+  link:RelNotes/1.8.2.3.txt[1.8.2.3],
+  link:RelNotes/1.8.2.2.txt[1.8.2.2],
+  link:RelNotes/1.8.2.1.txt[1.8.2.1],
   link:RelNotes/1.8.2.txt[1.8.2].
 
 * link:v1.8.1.6/git.html[documentation for release 1.8.1.6]
@@ -677,9 +686,7 @@
 	The '--git-dir' command-line option also sets this value.
 
 'GIT_WORK_TREE'::
-	Set the path to the working tree.  The value will not be
-	used in combination with repositories found automatically in
-	a .git directory (i.e. $GIT_DIR is not set).
+	Set the path to the root of the working tree.
 	This can also be controlled by the '--work-tree' command line
 	option and the core.worktree configuration variable.
 
@@ -811,8 +818,9 @@
 'GIT_FLUSH'::
 	If this environment variable is set to "1", then commands such
 	as 'git blame' (in incremental mode), 'git rev-list', 'git log',
-	and 'git whatchanged' will force a flush of the output stream
-	after each commit-oriented record have been flushed.   If this
+	'git check-attr', 'git check-ignore', and 'git whatchanged' will
+	force a flush of the output stream after each record have been
+	flushed. If this
 	variable is set to "0", the output of these commands will be done
 	using completely buffered I/O.   If this environment variable is
 	not set, Git will choose buffered or record-oriented flushing
@@ -832,6 +840,19 @@
 	as a file path and will try to write the trace messages
 	into it.
 
+'GIT_TRACE_PACK_ACCESS'::
+	If this variable is set to a path, a file will be created at
+	the given path logging all accesses to any packs. For each
+	access, the pack file name and an offset in the pack is
+	recorded. This may be helpful for troubleshooting some
+	pack-related performance problems.
+
+'GIT_TRACE_PACKET'::
+	If this variable is set, it shows a trace of all packets
+	coming in or out of a given program. This can help with
+	debugging object negotiation or other protocol issues. Tracing
+	is turned off at a packet starting with "PACK".
+
 GIT_LITERAL_PATHSPECS::
 	Setting this variable to `1` will cause Git to treat all
 	pathspecs literally, rather than as glob patterns. For example,
diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt
index 568d757..c8b3e51 100644
--- a/Documentation/gitdiffcore.txt
+++ b/Documentation/gitdiffcore.txt
@@ -222,26 +222,35 @@
 diffcore-pickaxe: For Detecting Addition/Deletion of Specified String
 ---------------------------------------------------------------------
 
-This transformation is used to find filepairs that represent
-changes that touch a specified string, and is controlled by the
--S option and the `--pickaxe-all` option to the 'git diff-*'
-commands.
+This transformation limits the set of filepairs to those that change
+specified strings between the preimage and the postimage in a certain
+way.  -S<block of text> and -G<regular expression> options are used to
+specify different ways these strings are sought.
 
-When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "result" side and whose "origin" side have
-different number of specified string.  Such a filepair represents
-"the string appeared in this changeset".  It also checks for the
-opposite case that loses the specified string.
+"-S<block of text>" detects filepairs whose preimage and postimage
+have different number of occurrences of the specified block of text.
+By definition, it will not detect in-file moves.  Also, when a
+changeset moves a file wholesale without affecting the interesting
+string, diffcore-rename kicks in as usual, and `-S` omits the filepair
+(since the number of occurrences of that string didn't change in that
+rename-detected filepair).  When used with `--pickaxe-regex`, treat
+the <block of text> as an extended POSIX regular expression to match,
+instead of a literal string.
 
-When `--pickaxe-all` is not in effect, diffcore-pickaxe leaves
-only such filepairs that touch the specified string in its
-output.  When `--pickaxe-all` is used, diffcore-pickaxe leaves all
-filepairs intact if there is such a filepair, or makes the
-output empty otherwise.  The latter behaviour is designed to
-make reviewing of the changes in the context of the whole
+"-G<regular expression>" (mnemonic: grep) detects filepairs whose
+textual diff has an added or a deleted line that matches the given
+regular expression.  This means that it will detect in-file (or what
+rename-detection considers the same file) moves, which is noise.  The
+implementation runs diff twice and greps, and this can be quite
+expensive.
+
+When `-S` or `-G` are used without `--pickaxe-all`, only filepairs
+that match their respective criterion are kept in the output.  When
+`--pickaxe-all` is used, if even one filepair matches their respective
+criterion in a changeset, the entire changeset is kept.  This behavior
+is designed to make reviewing changes in the context of the whole
 changeset easier.
 
-
 diffcore-order: For Sorting the Output Based on Filenames
 ---------------------------------------------------------
 
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index da74641..0827f69 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -159,11 +159,11 @@
 	carried out.
 
 'refspec' <refspec>::
-	This modifies the 'import' capability, allowing the produced
-	fast-import stream to modify refs in a private namespace
-	instead of writing to refs/heads or refs/remotes directly.
+	For remote helpers that implement 'import' or 'export', this capability
+	allows the refs to be constrained to a private namespace, instead of
+	writing to refs/heads or refs/remotes directly.
 	It is recommended that all importers providing the 'import'
-	capability use this.
+	capability use this. It's mandatory for 'export'.
 +
 A helper advertising the capability
 `refspec refs/heads/*:refs/svn/origin/branches/*`
@@ -174,8 +174,8 @@
 This capability can be advertised multiple times.  The first
 applicable refspec takes precedence.  The left-hand of refspecs
 advertised with this capability must cover all refs reported by
-the list command.  If a helper does not need a specific 'refspec'
-capability then it should advertise `refspec *:*`.
+the list command.  If no 'refspec' capability is advertised,
+there is an implied `refspec *:*`.
 
 'bidi-import'::
 	This modifies the 'import' capability.
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index d6f3393..aa03882 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -211,6 +211,9 @@
 	and maintained by shallow clone mechanism.  See `--depth`
 	option to linkgit:git-clone[1] and linkgit:git-fetch[1].
 
+modules::
+	Contains the git-repositories of the submodules.
+
 SEE ALSO
 --------
 linkgit:git-init[1],
diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt
index ea0526e..305db63 100644
--- a/Documentation/gitweb.conf.txt
+++ b/Documentation/gitweb.conf.txt
@@ -336,8 +336,26 @@
 	used as the first component of gitweb's "breadcrumb trail":
 	`<home link> / <project> / <action>`.  Can be set at build time using
 	the `GITWEB_HOME_LINK_STR` variable.  By default it is set to "projects",
-	as this link leads to the list of projects.  Other popular choice it to
-	set it to the name of site.
+	as this link leads to the list of projects.  Another popular choice is to
+	set it to the name of site.  Note that it is treated as raw HTML so it
+	should not be set from untrusted sources.
+
+@extra_breadcrumbs::
+	Additional links to be added to the start of the breadcrumb trail before
+	the home link, to pages that are logically "above" the gitweb projects
+	list, such as the organization and department which host the gitweb
+	server. Each element of the list is a reference to an array, in which
+	element 0 is the link text (equivalent to `$home_link_str`) and element
+	1 is the target URL (equivalent to `$home_link`).
++
+For example, the following setting produces a breadcrumb trail like
+"home / dev / projects / ..." where "projects" is the home link.
+----------------------------------------------------------------------------
+    our @extra_breadcrumbs = (
+      [ 'home' => 'https://www.example.org/' ],
+      [ 'dev'  => 'https://dev.example.org/' ],
+    );
+----------------------------------------------------------------------------
 
 $logo_url::
 $logo_label::
diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt
index 40969f1..cca14b8 100644
--- a/Documentation/gitweb.txt
+++ b/Documentation/gitweb.txt
@@ -504,7 +504,7 @@
 
 The above configuration expects your public repositories to live under
 '/pub/git' and will serve them as `http://git.domain.org/dir-under-pub-git`,
-both as cloneable Git URL and as browseable gitweb interface.  If you then
+both as clonable Git URL and as browseable gitweb interface.  If you then
 start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
 then you can even use the `git://` URL with exactly the same path.
 
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 68a18e1..dba5062 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -113,7 +113,7 @@
 while the HEAD is detached. They update the HEAD to point at the tip
 of the updated history without affecting any branch.  Commands that
 update or inquire information _about_ the current branch (e.g. `git
-branch --set-upstream-to` that sets what remote tracking branch the
+branch --set-upstream-to` that sets what remote-tracking branch the
 current branch integrates with) obviously do not work, as there is no
 (real) current branch to ask about in this state.
 
@@ -267,7 +267,7 @@
 	The default upstream <<def_repository,repository>>. Most projects have
 	at least one upstream project which they track. By default
 	'origin' is used for that purpose. New upstream updates
-	will be fetched into remote <<def_remote_tracking_branch,remote-tracking branches>> named
+	will be fetched into <<def_remote_tracking_branch,remote-tracking branches>> named
 	origin/name-of-upstream-branch, which you can see using
 	`git branch -r`.
 
@@ -400,12 +400,13 @@
 	<<def_ref,ref>> and local ref.
 
 [[def_remote_tracking_branch]]remote-tracking branch::
-	A regular Git <<def_branch,branch>> that is used to follow changes from
-	another <<def_repository,repository>>. A remote-tracking
-	branch should not contain direct modifications or have local commits
-	made to it. A remote-tracking branch can usually be
-	identified as the right-hand-side <<def_ref,ref>> in a Pull:
-	<<def_refspec,refspec>>.
+	A <<def_ref,ref>> that is used to follow changes from another
+	<<def_repository,repository>>. It typically looks like
+	'refs/remotes/foo/bar' (indicating that it tracks a branch named
+	'bar' in a remote named 'foo'), and matches the right-hand-side of
+	a configured fetch <<def_refspec,refspec>>. A remote-tracking
+	branch should not contain direct modifications or have local
+	commits made to it.
 
 [[def_repository]]repository::
 	A collection of <<def_ref,refs>> together with an
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
index 84dd839..0d5419e 100644
--- a/Documentation/howto/revert-branch-rebase.txt
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -12,7 +12,7 @@
 ================================
 
 One of the changes I pulled into the 'master' branch turns out to
-break building Git with GCC 2.95.  While they were well intentioned
+break building Git with GCC 2.95.  While they were well-intentioned
 portability fixes, keeping things working with gcc-2.95 was also
 important.  Here is what I did to revert the change in the 'master'
 branch and to adjust the 'pu' branch, using core Git tools and
diff --git a/Documentation/line-range-format.txt b/Documentation/line-range-format.txt
new file mode 100644
index 0000000..3e7ce72
--- /dev/null
+++ b/Documentation/line-range-format.txt
@@ -0,0 +1,25 @@
+- number
++
+If <start> or <end> is a number, it specifies an
+absolute line number (lines count from 1).
++
+
+- /regex/
++
+This form will use the first line matching the given
+POSIX regex.  If <end> is a regex, it will search
+starting at the line given by <start>.
++
+
+- +offset or -offset
++
+This is only valid for <end> and will specify a number
+of lines before or after the line given by <start>.
++
+
+- :regex
++
+If the option's argument is of the form :regex, it denotes the range
+from the first funcname line that matches <regex>, up to the next
+funcname line.
++
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 2adccf8..afba8d4 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -8,12 +8,13 @@
 inspect and further tweak the merge result before committing.
 
 --edit::
+-e::
 --no-edit::
 	Invoke an editor before committing successful mechanical merge to
 	further edit the auto-generated merge message, so that the user
 	can explain and justify the merge. The `--no-edit` option can be
 	used to accept the auto-generated message (this is generally
-	discouraged). The `--edit` option is still useful if you are
+	discouraged). The `--edit` (or `-e`) option is still useful if you are
 	giving a draft message with the `-m` option from the command line
 	and want to edit it in the editor.
 +
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 94a9d32..18cffc2 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -68,6 +68,11 @@
 +
 * `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
   it requests fetching everything up to the given tag.
-* A parameter <ref> without a colon is equivalent to
-  <ref>: when pulling/fetching, so it merges <ref> into the current
-  branch without storing the remote branch anywhere locally
+ifndef::git-pull[]
+* A parameter <ref> without a colon fetches that ref into FETCH_HEAD,
+endif::git-pull[]
+ifdef::git-pull[]
+* A parameter <ref> without a colon merges <ref> into the current
+  branch,
+endif::git-pull[]
+  and updates the remote-tracking branches (if any).
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 3bdbf5e..27f8de3 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -271,8 +271,8 @@
 
 --boundary::
 
-	Output uninteresting commits at the boundary, which are usually
-	not shown.
+	Output excluded boundary commits. Boundary commits are
+	prefixed with `-`.
 
 --
 
@@ -342,13 +342,13 @@
 illustrate the differences between simplification settings.  We assume
 that you are filtering for a file `foo` in this commit graph:
 -----------------------------------------------------------------------
-	  .-A---M---N---O---P
-	 /     /   /   /   /
-	I     B   C   D   E
-	 \   /   /   /   /
-	  `-------------'
+	  .-A---M---N---O---P---Q
+	 /     /   /   /   /   /
+	I     B   C   D   E   Y
+	 \   /   /   /   /   /
+	  `-------------'   X
 -----------------------------------------------------------------------
-The horizontal line of history A---P is taken to be the first parent of
+The horizontal line of history A---Q is taken to be the first parent of
 each merge.  The commits are:
 
 * `I` is the initial commit, in which `foo` exists with contents
@@ -367,8 +367,11 @@
   `N` and `D` to "foobarbaz"; i.e., it is not TREESAME to any parent.
 
 * `E` changes `quux` to "xyzzy", and its merge `P` combines the
-  strings to "quux xyzzy".  Despite appearing interesting, `P` is
-  TREESAME to all parents.
+  strings to "quux xyzzy".  `P` is TREESAME to `O`, but not to `E`.
+
+* `X` is an independent root commit that added a new file `side`, and `Y`
+  modified it. `Y` is TREESAME to `X`. Its merge `Q` added `side` to `P`, and
+  `Q` is TREESAME to `P`, but not to `Y`.
 
 'rev-list' walks backwards through history, including or excluding
 commits based on whether '\--full-history' and/or parent rewriting
@@ -410,10 +413,10 @@
 	the example, we get
 +
 -----------------------------------------------------------------------
-	I  A  B  N  D  O
+	I  A  B  N  D  O  P  Q
 -----------------------------------------------------------------------
 +
-`P` and `M` were excluded because they are TREESAME to a parent.  `E`,
+`M` was excluded because it is TREESAME to both parents.  `E`,
 `C` and `B` were all walked, but only `B` was !TREESAME, so the others
 do not appear.
 +
@@ -431,7 +434,7 @@
 themselves.  This results in
 +
 -----------------------------------------------------------------------
-	  .-A---M---N---O---P
+	  .-A---M---N---O---P---Q
 	 /     /   /   /   /
 	I     B   /   D   /
 	 \   /   /   /   /
@@ -441,7 +444,7 @@
 Compare to '\--full-history' without rewriting above.  Note that `E`
 was pruned away because it is TREESAME, but the parent list of P was
 rewritten to contain `E`'s parent `I`.  The same happened for `C` and
-`N`.  Note also that `P` was included despite being TREESAME.
+`N`, and `X`, `Y` and `Q`.
 
 In addition to the above settings, you can change whether TREESAME
 affects inclusion:
@@ -471,8 +474,9 @@
 * 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.
+  the process, drop parents that are ancestors of other parents or that are
+  root commits TREESAME to an empty tree, and remove duplicates, but take care
+  to never drop all parents that we are TREESAME to.
 +
 * If after this parent rewriting, `C'` is a root or merge commit (has
   zero or >1 parents), a boundary commit, or !TREESAME, it remains.
@@ -490,7 +494,7 @@
 	  `---------'
 -----------------------------------------------------------------------
 +
-Note the major differences in `N` and `P` over '--full-history':
+Note the major differences in `N`, `P` and `Q` over '--full-history':
 +
 --
 * `N`'s parent list had `I` removed, because it is an ancestor of the
@@ -498,6 +502,10 @@
 +
 * `P`'s parent list similarly had `I` removed.  `P` was then
   removed completely, because it had one parent and is TREESAME.
++
+* `Q`'s parent list had `Y` simplified to `X`. `X` was then removed, because it
+  was a TREESAME root. `Q` was then removed completely, because it had one
+  parent and is TREESAME.
 --
 
 Finally, there is a fifth simplification mode available:
@@ -617,6 +625,10 @@
 	Show no parents before all of its children are shown, but
 	otherwise show commits in the commit timestamp order.
 
+--author-date-order::
+	Show no parents before all of its children are shown, but
+	otherwise show commits in the author timestamp order.
+
 --topo-order::
 	Show no parents before all of its children are shown, and
 	avoid showing commits on multiple lines of history
@@ -837,7 +849,4 @@
 -t::
 
 	Show the tree objects in the diff output. This implies '-r'.
-
--s::
-	Suppress diff output.
 endif::git-rev-list[]
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index d477b3f..09896a3 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -58,6 +58,9 @@
 While the ref name encoding is unspecified, UTF-8 is preferred as
 some output processing may assume ref names in UTF-8.
 
+'@'::
+  '@' alone is a shortcut for 'HEAD'.
+
 '<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}'::
   A ref followed by the suffix '@' with a date specification
   enclosed in a brace
diff --git a/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt
index 4a4228b..f3c1357 100644
--- a/Documentation/technical/api-builtin.txt
+++ b/Documentation/technical/api-builtin.txt
@@ -39,7 +39,7 @@
 	on bare repositories.
 	This only makes sense when `RUN_SETUP` is also set.
 
-. Add `builtin-foo.o` to `BUILTIN_OBJS` in `Makefile`.
+. Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
 
 Additionally, if `foo` is a new command, there are 3 more things to do:
 
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 32ddc1c..0be2b51 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -41,6 +41,8 @@
 * Boolean long options can be 'negated' (or 'unset') by prepending
   `no-`, e.g. `--no-abbrev` instead of `--abbrev`. Conversely,
   options that begin with `no-` can be 'negated' by removing it.
+  Other long options can be unset (e.g., set string to NULL, set
+  integer to 0) by prepending `no-`.
 
 * Options and non-option arguments can clearly be separated using the `--`
   option, e.g. `-a -b --option -- --this-is-a-file` indicates that
@@ -174,6 +176,10 @@
 	Introduce an option with date argument, see `approxidate()`.
 	The timestamp is put into `int_var`.
 
+`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+	Introduce an option with expiry date argument, see `parse_expiry_date()`.
+	The timestamp is put into `int_var`.
+
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
 	The argument will be fed into the function given by `func_ptr`
@@ -269,10 +275,10 @@
 --------
 
 See `test-parse-options.c` and
-`builtin-add.c`,
-`builtin-clone.c`,
-`builtin-commit.c`,
-`builtin-fetch.c`,
-`builtin-fsck.c`,
-`builtin-rm.c`
+`builtin/add.c`,
+`builtin/clone.c`,
+`builtin/commit.c`,
+`builtin/fetch.c`,
+`builtin/fsck.c`,
+`builtin/rm.c`
 for real-world examples.
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
index 0810251..f352a9b 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -175,7 +175,7 @@
 
   A conflict is represented in the index as a set of higher stage entries.
   When a conflict is resolved (e.g. with "git add path"), these higher
-  stage entries will be removed and a stage-0 entry with proper resoluton
+  stage entries will be removed and a stage-0 entry with proper resolution
   is added.
 
   When these higher stage entries are removed, they are saved in the
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index f1a51ed..b898e97 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -228,8 +228,7 @@
 The client MUST write all obj-ids which it only has shallow copies
 of (meaning that it does not have the parents of a commit) as
 'shallow' lines so that the server is aware of the limitations of
-the client's history. Clients MUST NOT mention an obj-id which
-it does not know exists on the server.
+the client's history.
 
 The client now sends the maximum commit history depth it wants for
 this transaction, which is the number of commits it wants from the
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index 6dc82ca..242a044 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -46,7 +46,7 @@
 on-disk timestamps, resulting in meaningless changes when an
 inode is evicted from the inode cache.  See commit 8ce13b0
 of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
-([PATCH] Sync in core time granuality with filesystems,
+([PATCH] Sync in core time granularity with filesystems,
 2005-01-04).
 
 Racy Git
@@ -135,9 +135,9 @@
   $ git ls-files | git update-index --stdin
   $ touch -r .datestamp .git/index
 
-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 Athlon 64 X2 3800+, after the above:
+This will make all index entries racily clean.  The linux project, for
+example, there are over 20,000 files in the working 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 3ca122f..9ccb246 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -11,6 +11,9 @@
 and pushing, but these are inefficient and deprecated; do not use
 them).
 
+The native transport (i.e. git:// URL) does no authentication and
+should be used with caution on unsecured networks.
+
 The following syntaxes may be used with them:
 
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
@@ -23,6 +26,12 @@
 
 - {startsb}user@{endsb}host.xz:path/to/repo.git/
 
+This syntax is only recognized if there are no slashes before the
+first colon. This helps differentiate a local path that contains a
+colon. For example the local path `foo:bar` could be specified as an
+absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
+url.
+
 The ssh and git protocols additionally support ~username expansion:
 
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
@@ -33,7 +42,7 @@
 syntaxes may be used:
 
 - /path/to/repo.git/
-- file:///path/to/repo.git/
+- \file:///path/to/repo.git/
 
 ifndef::git-clone[]
 These two syntaxes are mostly equivalent, except when cloning, when
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index e831cc2..fe723e4 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -57,17 +57,17 @@
 project in mind, here are some interesting examples:
 
 ------------------------------------------------
-	# Git itself (approx. 10MB download):
+	# Git itself (approx. 40MB download):
 $ git clone git://git.kernel.org/pub/scm/git/git.git
-	# the Linux kernel (approx. 150MB download):
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+	# the Linux kernel (approx. 640MB download):
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
 ------------------------------------------------
 
 The initial clone may be time-consuming for a large project, but you
 will only need to clone once.
 
-The clone command creates a new directory named after the project (`git`
-or `linux-2.6` in the examples above).  After you cd into this
+The clone command creates a new directory named after the project
+(`git` or `linux` in the examples above).  After you cd into this
 directory, you will see that it contains a copy of the project files,
 called the <<def_working_tree,working tree>>, together with a special
 top-level directory named `.git`, which contains all the information
@@ -431,19 +431,25 @@
 cloned from, using linkgit:git-remote[1]:
 
 -------------------------------------------------
-$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
-$ git fetch linux-nfs
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
-  commit: bf81b46
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch]      master     -> staging/master
+ * [new branch]      staging-linus -> staging/staging-linus
+ * [new branch]      staging-next -> staging/staging-next
 -------------------------------------------------
 
 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 `staging`:
 
 -------------------------------------------------
 $ git branch -r
-linux-nfs/master
-origin/master
+  origin/HEAD -> origin/master
+  origin/master
+  staging/master
+  staging/staging-linus
+  staging/staging-next
 -------------------------------------------------
 
 If you run `git fetch <remote>` later, the remote-tracking branches
@@ -455,9 +461,9 @@
 -------------------------------------------------
 $ cat .git/config
 ...
-[remote "linux-nfs"]
-	url = git://linux-nfs.org/pub/nfs-2.6.git
-	fetch = +refs/heads/*:refs/remotes/linux-nfs/*
+[remote "staging"]
+	url = git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+	fetch = +refs/heads/*:refs/remotes/staging/*
 ...
 -------------------------------------------------
 
@@ -1835,7 +1841,7 @@
 resolution, instead of creating a new commit, just run
 
 -------------------------------------------------
-$ git am --resolved
+$ git am --continue
 -------------------------------------------------
 
 and Git will create the commit for you and continue applying the
@@ -2156,7 +2162,7 @@
 tree:
 
 -------------------------------------------------
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git work
 $ cd work
 -------------------------------------------------
 
@@ -2198,7 +2204,7 @@
 -------------------------------------------------
 $ cat >> .git/config <<EOF
 [remote "mytree"]
-	url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
+	url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux.git
 	push = release
 	push = test
 EOF
@@ -4256,15 +4262,16 @@
 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`,
-  and declared in `builtin.h`,
+- a function called `cmd_<bla>`, typically defined in `builtin/<bla.c>`
+  (note that older versions of Git used to have it in `builtin-<bla>.c`
+  instead), and declared in `builtin.h`.
 
 - an entry in the `commands[]` array in `git.c`, and
 
 - an entry in `BUILTIN_OBJECTS` in the `Makefile`.
 
 Sometimes, more than one builtin is contained in one source file.  For
-example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin-log.c`,
+example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin/log.c`,
 since they share quite a bit of code.  In that case, the commands which are
 _not_ named like the `.c` file in which they live have to be listed in
 `BUILT_INS` in the `Makefile`.
@@ -4287,10 +4294,10 @@
 - is plumbing, and
 
 - was around even in the initial commit (it literally went only through
-  some 20 revisions as `cat-file.c`, was renamed to `builtin-cat-file.c`
+  some 20 revisions as `cat-file.c`, was renamed to `builtin/cat-file.c`
   when made a builtin, and then saw less than 10 versions).
 
-So, look into `builtin-cat-file.c`, search for `cmd_cat_file()` and look what
+So, look into `builtin/cat-file.c`, search for `cmd_cat_file()` and look what
 it does.
 
 ------------------------------------------------------------------
@@ -4366,7 +4373,7 @@
 builtin:
 
 -------------------------------------------------
-$ git log --no-merges --diff-filter=A builtin-*.c
+$ git log --no-merges --diff-filter=A builtin/*.c
 -------------------------------------------------
 
 You see, Git is actually the best tool to find out about the source of Git
@@ -4668,5 +4675,5 @@
 Alternates, clone -reference, etc.
 
 More on recovery from repository corruption.  See:
-	http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2
-	http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
+	http://marc.info/?l=git&m=117263864820799&w=2
+	http://marc.info/?l=git&m=117147855503798&w=2
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index aa87dbf..b4d4e50 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.3-rc3
+DEF_VER=v1.8.3.GIT
 
 LF='
 '
@@ -11,7 +11,7 @@
 if test -f version
 then
 	VN=$(cat version) || VN="$DEF_VER"
-elif test -d .git -o -f .git &&
+elif test -d ${GIT_DIR:-.git} -o -f .git &&
 	VN=$(git describe --match "v[0-9]*" --abbrev=7 HEAD 2>/dev/null) &&
 	case "$VN" in
 	*$LF*) (exit 1) ;;
diff --git a/Makefile b/Makefile
index 0f931a2..ef442eb 100644
--- a/Makefile
+++ b/Makefile
@@ -69,6 +69,9 @@
 # Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt
 # doesn't support GNU extensions like --check and --statistics
 #
+# Define NEEDS_CLIPPED_WRITE if your write(2) cannot write more than
+# INT_MAX bytes at once (e.g. MacOS X).
+#
 # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
 # it specifies.
 #
@@ -106,7 +109,7 @@
 # Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
 # FNM_CASEFOLD GNU extension.
 #
-# Define USE_WILDMATCH if you want to use Git's wildmatch
+# Define NO_WILDMATCH if you do not want to use Git's wildmatch
 # implementation as fnmatch
 #
 # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
@@ -137,6 +140,10 @@
 # specify your own (or DarwinPort's) include directories and
 # library directories by defining CFLAGS and LDFLAGS appropriately.
 #
+# Define NO_APPLE_COMMON_CRYPTO if you are building on Darwin/Mac OS X
+# and do not want to use Apple's CommonCrypto library.  This allows you
+# to provide your own OpenSSL library, for example from MacPorts.
+#
 # Define BLK_SHA1 environment variable to make use of the bundled
 # optimized C SHA1 routine.
 #
@@ -290,10 +297,6 @@
 #
 # Define NO_REGEX if you have no or inferior regex support in your C library.
 #
-# Define CYGWIN_V15_WIN32API if you are using Cygwin v1.7.x but are not
-# using the current w32api packages. The recommended approach, however,
-# is to update your installation if compilation errors occur.
-#
 # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
 # user.
 #
@@ -460,6 +463,7 @@
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
+SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -487,11 +491,18 @@
 SCRIPT_PYTHON += git-remote-testpy.py
 SCRIPT_PYTHON += git-p4.py
 
+NO_INSTALL += git-remote-testgit
+NO_INSTALL += git-remote-testpy
+
 # Generated files for scripts
 SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
 SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
 SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
 
+SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))
+SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))
+SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))
+
 # Individual rules to allow e.g.
 # "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
 # from subdirectories like contrib/*/
@@ -501,12 +512,12 @@
 build-python-script: $(SCRIPT_PYTHON_GEN)
 
 .PHONY: install-perl-script install-sh-script install-python-script
-install-sh-script: $(SCRIPT_SH_GEN)
-	$(INSTALL) $(SCRIPT_SH_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-perl-script: $(SCRIPT_PERL_GEN)
-	$(INSTALL) $(SCRIPT_PERL_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-python-script: $(SCRIPT_PYTHON_GEN)
-	$(INSTALL) $(SCRIPT_PYTHON_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+install-sh-script: $(SCRIPT_SH_INS)
+	$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+install-perl-script: $(SCRIPT_PERL_INS)
+	$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+install-python-script: $(SCRIPT_PYTHON_INS)
+	$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 
 .PHONY: clean-perl-script clean-sh-script clean-python-script
 clean-sh-script:
@@ -516,9 +527,9 @@
 clean-python-script:
 	$(RM) $(SCRIPT_PYTHON_GEN)
 
-SCRIPTS = $(SCRIPT_SH_GEN) \
-	  $(SCRIPT_PERL_GEN) \
-	  $(SCRIPT_PYTHON_GEN) \
+SCRIPTS = $(SCRIPT_SH_INS) \
+	  $(SCRIPT_PERL_INS) \
+	  $(SCRIPT_PYTHON_INS) \
 	  git-instaweb
 
 ETAGS_TARGET = TAGS
@@ -558,6 +569,8 @@
 TEST_PROGRAMS_NEED_X += test-mktemp
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-prio-queue
+TEST_PROGRAMS_NEED_X += test-read-cache
 TEST_PROGRAMS_NEED_X += test-regex
 TEST_PROGRAMS_NEED_X += test-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
@@ -673,6 +686,8 @@
 LIB_H += http.h
 LIB_H += kwset.h
 LIB_H += levenshtein.h
+LIB_H += line-log.h
+LIB_H += line-range.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
@@ -682,15 +697,16 @@
 LIB_H += mergesort.h
 LIB_H += notes-cache.h
 LIB_H += notes-merge.h
+LIB_H += notes-utils.h
 LIB_H += notes.h
 LIB_H += object.h
-LIB_H += pack-refs.h
 LIB_H += pack-revindex.h
 LIB_H += pack.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
 LIB_H += pathspec.h
 LIB_H += pkt-line.h
+LIB_H += prio-queue.h
 LIB_H += progress.h
 LIB_H += prompt.h
 LIB_H += quote.h
@@ -801,6 +817,8 @@
 LIB_OBJS += ident.o
 LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
+LIB_OBJS += line-log.o
+LIB_OBJS += line-range.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
@@ -815,9 +833,9 @@
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
+LIB_OBJS += notes-utils.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
-LIB_OBJS += pack-refs.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
 LIB_OBJS += pager.o
@@ -830,6 +848,7 @@
 LIB_OBJS += pkt-line.o
 LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
+LIB_OBJS += prio-queue.o
 LIB_OBJS += progress.o
 LIB_OBJS += prompt.o
 LIB_OBJS += quote.o
@@ -893,6 +912,7 @@
 BUILTIN_OBJS += builtin/cat-file.o
 BUILTIN_OBJS += builtin/check-attr.o
 BUILTIN_OBJS += builtin/check-ignore.o
+BUILTIN_OBJS += builtin/check-mailmap.o
 BUILTIN_OBJS += builtin/check-ref-format.o
 BUILTIN_OBJS += builtin/checkout-index.o
 BUILTIN_OBJS += builtin/checkout.o
@@ -1054,6 +1074,11 @@
 			BASIC_LDFLAGS += -L/opt/local/lib
 		endif
 	endif
+	ifndef NO_APPLE_COMMON_CRYPTO
+		APPLE_COMMON_CRYPTO = YesPlease
+		COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
+	endif
+	NO_REGEX = YesPlease
 	PTHREAD_LIBS =
 endif
 
@@ -1261,7 +1286,7 @@
 	COMPAT_OBJS += compat/fnmatch/fnmatch.o
 endif
 endif
-ifdef USE_WILDMATCH
+ifndef NO_WILDMATCH
 	COMPAT_CFLAGS += -DUSE_WILDMATCH
 endif
 ifdef NO_SETENV
@@ -1388,10 +1413,16 @@
 	LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
 	LIB_H += ppc/sha1.h
 else
+ifdef APPLE_COMMON_CRYPTO
+	COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
+	SHA1_HEADER = <CommonCrypto/CommonDigest.h>
+else
 	SHA1_HEADER = <openssl/sha.h>
 	EXTLIBS += $(LIB_4_CRYPTO)
 endif
 endif
+endif
+
 ifdef NO_PERL_MAKEMAKER
 	export NO_PERL_MAKEMAKER
 endif
@@ -1449,9 +1480,6 @@
 	COMPAT_CFLAGS += -Icompat/regex
 	COMPAT_OBJS += compat/regex/regex.o
 endif
-ifdef CYGWIN_V15_WIN32API
-	COMPAT_CFLAGS += -DCYGWIN_V15_WIN32API
-endif
 
 ifdef USE_NED_ALLOCATOR
        COMPAT_CFLAGS += -Icompat/nedmalloc
@@ -1466,6 +1494,11 @@
 	MSGFMT += --check --statistics
 endif
 
+ifdef NEEDS_CLIPPED_WRITE
+	BASIC_CFLAGS += -DNEEDS_CLIPPED_WRITE
+	COMPAT_OBJS += compat/clipped-write.o
+endif
+
 ifneq (,$(XDL_FAST_HASH))
 	BASIC_CFLAGS += -DXDL_FAST_HASH
 endif
@@ -1503,6 +1536,7 @@
 	QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
 	QUIET_GCOV     = @echo '   ' GCOV $@;
 	QUIET_SP       = @echo '   ' SP $<;
+	QUIET_RC       = @echo '   ' RC $@;
 	QUIET_SUBDIR0  = +@subdir=
 	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
 			 $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1645,7 +1679,7 @@
 shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
 
 strip: $(PROGRAMS) git$X
-	$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
+	$(STRIP) $(STRIP_OPTS) $^
 
 ### Target-specific flags and dependencies
 
@@ -1705,9 +1739,9 @@
 
 $(BUILT_INS): git$X
 	$(QUIET_BUILT_IN)$(RM) $@ && \
-	ln git$X $@ 2>/dev/null || \
-	ln -s git$X $@ 2>/dev/null || \
-	cp git$X $@
+	ln $< $@ 2>/dev/null || \
+	ln -s $< $@ 2>/dev/null || \
+	cp $< $@
 
 common-cmds.h: ./generate-cmdlist.sh command-list.txt
 
@@ -1748,6 +1782,11 @@
 	$(QUIET_GEN)$(cmd_munge_script) && \
 	mv $@+ $@
 
+git.res: git.rc GIT-VERSION-FILE
+	$(QUIET_RC)$(RC) \
+	  $(join -DMAJOR= -DMINOR= -DPATCH=, $(wordlist 1,3,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
+	  -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
+
 ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
@@ -1772,7 +1811,7 @@
 	    -e '	x' \
 	    -e '}' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-	    $@.perl >$@+ && \
+	    $< >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
 
@@ -1796,8 +1835,8 @@
 endif # NO_PERL
 
 ifndef NO_PYTHON
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
+$(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
+$(SCRIPT_PYTHON_GEN): % : %.py
 	$(QUIET_GEN)$(RM) $@ $@+ && \
 	INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
 		--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
@@ -1805,11 +1844,11 @@
 	sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
 	    -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
 	    -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
-	    $@.py >$@+ && \
+	    $< >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
 else # NO_PYTHON
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
+$(SCRIPT_PYTHON_GEN): % : unimplemented.sh
 	$(QUIET_GEN)$(RM) $@ $@+ && \
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 	    -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
@@ -2004,6 +2043,7 @@
 ifdef USE_NED_ALLOCATOR
 compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
 	-DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
+compat/nedmalloc/nedmalloc.sp: SPARSE_FLAGS += -Wno-non-pointer-null
 endif
 
 git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
@@ -2035,13 +2075,13 @@
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
 $(LIB_FILE): $(LIB_OBJS)
-	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
 
 $(XDIFF_LIB): $(XDIFF_OBJS)
-	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
 
 $(VCSSVN_LIB): $(VCSSVN_OBJS)
-	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
 
 export DEFAULT_EDITOR DEFAULT_PAGER
 
@@ -2159,6 +2199,9 @@
 	@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 	@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
 	@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
+ifdef TEST_OUTPUT_DIRECTORY
+	@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@
+endif
 ifdef GIT_TEST_OPTS
 	@echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
 endif
@@ -2198,6 +2241,7 @@
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
 all:: $(TEST_PROGRAMS) $(test_bindir_programs)
+all:: $(NO_INSTALL)
 
 bin-wrappers/%: wrap-for-bin.sh
 	@mkdir -p bin-wrappers
@@ -2262,9 +2306,6 @@
 		exit 1; \
 	fi
 
-remove-dashes:
-	./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
-
 ### Installation rules
 
 ifneq ($(filter /%,$(firstword $(template_dir))),)
@@ -2443,11 +2484,11 @@
 	$(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 	$(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 
-clean: profile-clean
-	$(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
+clean: profile-clean coverage-clean
+	$(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
 		builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
-	$(RM) $(TEST_PROGRAMS)
+	$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
 	$(RM) -r bin-wrappers $(dep_dirs)
 	$(RM) -r po/build/
 	$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
@@ -2524,29 +2565,34 @@
 
 ### Test suite coverage testing
 #
-.PHONY: coverage coverage-clean coverage-build coverage-report
+.PHONY: coverage coverage-clean coverage-compile coverage-test coverage-report
+.PHONY: coverage-clean-results
 
 coverage:
-	$(MAKE) coverage-build
-	$(MAKE) coverage-report
+	$(MAKE) coverage-test
+	$(MAKE) coverage-untested-functions
 
 object_dirs := $(sort $(dir $(OBJECTS)))
-coverage-clean:
+coverage-clean-results:
 	$(RM) $(addsuffix *.gcov,$(object_dirs))
 	$(RM) $(addsuffix *.gcda,$(object_dirs))
-	$(RM) $(addsuffix *.gcno,$(object_dirs))
 	$(RM) coverage-untested-functions
 	$(RM) -r cover_db/
 	$(RM) -r cover_db_html/
 
+coverage-clean: coverage-clean-results
+	$(RM) $(addsuffix *.gcno,$(object_dirs))
+
 COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
 COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
 GCOVFLAGS = --preserve-paths --branch-probabilities --all-blocks
 
-coverage-build: coverage-clean
+coverage-compile:
 	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
+
+coverage-test: coverage-clean-results coverage-compile
 	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
-		-j1 test
+		DEFAULT_TEST_TARGET=test -j1 test
 
 coverage-report:
 	$(QUIET_GCOV)for dir in $(object_dirs); do \
diff --git a/RelNotes b/RelNotes
index 80b7e38..fce99fb 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.8.3.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.4.txt
\ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 40cdc46..64adbe2 100644
--- a/abspath.c
+++ b/abspath.c
@@ -216,7 +216,7 @@
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
 	static char path[PATH_MAX];
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 	if (!pfx_len || is_absolute_path(arg))
 		return arg;
 	memcpy(path, pfx, pfx_len);
diff --git a/advice.c b/advice.c
index a8deee6..2a52098 100644
--- a/advice.c
+++ b/advice.c
@@ -14,6 +14,8 @@
 int advice_implicit_identity = 1;
 int advice_detached_head = 1;
 int advice_set_upstream_failure = 1;
+int advice_object_name_warning = 1;
+int advice_rm_hints = 1;
 
 static struct {
 	const char *name;
@@ -33,6 +35,8 @@
 	{ "implicitidentity", &advice_implicit_identity },
 	{ "detachedhead", &advice_detached_head },
 	{ "setupstreamfailure", &advice_set_upstream_failure },
+	{ "object_name_warning", &advice_object_name_warning },
+	{ "rmhints", &advice_rm_hints },
 
 	/* make this an alias for backward compatibility */
 	{ "pushnonfastforward", &advice_push_update_rejected }
diff --git a/advice.h b/advice.h
index 94caa32..08fbc8e 100644
--- a/advice.h
+++ b/advice.h
@@ -17,8 +17,11 @@
 extern int advice_implicit_identity;
 extern int advice_detached_head;
 extern int advice_set_upstream_failure;
+extern int advice_object_name_warning;
+extern int advice_rm_hints;
 
 int git_default_advice_config(const char *var, const char *value);
+__attribute__((format (printf, 1, 2)))
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
diff --git a/archive-zip.c b/archive-zip.c
index b2c4fe0..4bde019 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -232,7 +232,6 @@
 		size = 0;
 		compressed_size = 0;
 		buffer = NULL;
-		size = 0;
 	} else if (S_ISREG(mode) || S_ISLNK(mode)) {
 		enum object_type type = sha1_object_info(sha1, &size);
 
diff --git a/argv-array.h b/argv-array.h
index 40248d4..85ba438 100644
--- a/argv-array.h
+++ b/argv-array.h
@@ -15,6 +15,7 @@
 void argv_array_push(struct argv_array *, const char *);
 __attribute__((format (printf,2,3)))
 void argv_array_pushf(struct argv_array *, const char *fmt, ...);
+LAST_ARG_MUST_BE_NULL
 void argv_array_pushl(struct argv_array *, ...);
 void argv_array_pop(struct argv_array *);
 void argv_array_clear(struct argv_array *);
diff --git a/bisect.c b/bisect.c
index 374d9e2..71c1958 100644
--- a/bisect.c
+++ b/bisect.c
@@ -15,7 +15,7 @@
 static struct sha1_array good_revs;
 static struct sha1_array skipped_revs;
 
-static const unsigned char *current_bad_sha1;
+static unsigned char *current_bad_sha1;
 
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
@@ -404,7 +404,8 @@
 			int flags, void *cb_data)
 {
 	if (!strcmp(refname, "bad")) {
-		current_bad_sha1 = sha1;
+		current_bad_sha1 = xmalloc(20);
+		hashcpy(current_bad_sha1, sha1);
 	} else if (!prefixcmp(refname, "good-")) {
 		sha1_array_append(&good_revs, sha1);
 	} else if (!prefixcmp(refname, "skip-")) {
diff --git a/branch.c b/branch.c
index 97c72bf..c5c6984 100644
--- a/branch.c
+++ b/branch.c
@@ -197,6 +197,21 @@
 	return 1;
 }
 
+static int check_tracking_branch(struct remote *remote, void *cb_data)
+{
+	char *tracking_branch = cb_data;
+	struct refspec query;
+	memset(&query, 0, sizeof(struct refspec));
+	query.dst = tracking_branch;
+	return !(remote_find_tracking(remote, &query) ||
+		 prefixcmp(query.src, "refs/heads/"));
+}
+
+static int validate_remote_tracking_branch(char *ref)
+{
+	return !for_each_remote(check_tracking_branch, ref);
+}
+
 static const char upstream_not_branch[] =
 N_("Cannot setup tracking information; starting point '%s' is not a branch.");
 static const char upstream_missing[] =
@@ -259,7 +274,7 @@
 	case 1:
 		/* Unique completion -- good, only if it is a real branch */
 		if (prefixcmp(real_ref, "refs/heads/") &&
-		    prefixcmp(real_ref, "refs/remotes/")) {
+		    validate_remote_tracking_branch(real_ref)) {
 			if (explicit_tracking)
 				die(_(upstream_not_branch), start_name);
 			else
diff --git a/builtin.h b/builtin.h
index faef559..8afa2de 100644
--- a/builtin.h
+++ b/builtin.h
@@ -5,13 +5,15 @@
 #include "strbuf.h"
 #include "cache.h"
 #include "commit.h"
-#include "notes.h"
 
 #define DEFAULT_MERGE_LOG_LEN 20
 
 extern const char git_usage_string[];
 extern const char git_more_info_string[];
 
+#define PRUNE_PACKED_DRY_RUN 01
+#define PRUNE_PACKED_VERBOSE 02
+
 extern void prune_packed_objects(int);
 
 struct fmt_merge_msg_opts {
@@ -23,21 +25,6 @@
 extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			 struct fmt_merge_msg_opts *);
 
-struct notes_rewrite_cfg {
-	struct notes_tree **trees;
-	const char *cmd;
-	int enabled;
-	combine_notes_fn combine;
-	struct string_list *refs;
-	int refs_from_env;
-	int mode_from_env;
-};
-
-struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
-int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
-			  const unsigned char *from_obj, const unsigned char *to_obj);
-void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
-
 extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
@@ -53,6 +40,7 @@
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
+extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index f45d9d4..8266a9c 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -343,6 +343,7 @@
 
 	argc = setup_revisions(argc, argv, &rev, NULL);
 	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+	rev.diffopt.use_color = 0;
 	DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
 	out = open(file, O_CREAT | O_WRONLY, 0666);
 	if (out < 0)
diff --git a/builtin/apply.c b/builtin/apply.c
index 30eefc3..50912c9 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -473,7 +473,7 @@
 
 	/*
 	 * Proposed "new-style" GNU patch/diff format; see
-	 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+	 * http://marc.info/?l=git&m=112927316408690&w=2
 	 */
 	if (unquote_c_style(&name, line, NULL)) {
 		strbuf_release(&name);
@@ -722,7 +722,7 @@
 
 static char *find_name_traditional(const char *line, char *def, int p_value)
 {
-	size_t len = strlen(line);
+	size_t len;
 	size_t date_len;
 
 	if (*line == '"') {
@@ -906,7 +906,7 @@
 			patch->old_name = name;
 		} else {
 			patch->old_name = name;
-			patch->new_name = xstrdup(name);
+			patch->new_name = null_strdup(name);
 		}
 	}
 	if (!name)
@@ -2999,7 +2999,7 @@
 	return 0;
 }
 
-static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
+static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf)
 {
 	if (!ce)
 		return 0;
@@ -3117,7 +3117,7 @@
 	return previous;
 }
 
-static int verify_index_match(struct cache_entry *ce, struct stat *st)
+static int verify_index_match(const struct cache_entry *ce, struct stat *st)
 {
 	if (S_ISGITLINK(ce->ce_mode)) {
 		if (!S_ISDIR(st->st_mode))
@@ -3130,7 +3130,7 @@
 #define SUBMODULE_PATCH_WITHOUT_INDEX 1
 
 static int load_patch_target(struct strbuf *buf,
-			     struct cache_entry *ce,
+			     const struct cache_entry *ce,
 			     struct stat *st,
 			     const char *name,
 			     unsigned expected_mode)
@@ -3160,7 +3160,8 @@
  * we read from the result of a previous diff.
  */
 static int load_preimage(struct image *image,
-			 struct patch *patch, struct stat *st, struct cache_entry *ce)
+			 struct patch *patch, struct stat *st,
+			 const struct cache_entry *ce)
 {
 	struct strbuf buf = STRBUF_INIT;
 	size_t len;
@@ -3273,7 +3274,7 @@
 }
 
 static int try_threeway(struct image *image, struct patch *patch,
-			struct stat *st, struct cache_entry *ce)
+			struct stat *st, const struct cache_entry *ce)
 {
 	unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
 	struct strbuf buf = STRBUF_INIT;
@@ -3343,7 +3344,7 @@
 	return 0;
 }
 
-static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
+static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
 {
 	struct image image;
 
@@ -3525,7 +3526,7 @@
 		ok_if_exists = 0;
 
 	if (new_name &&
-	    ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
+	    ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
 		int err = check_to_create(new_name, ok_if_exists);
 
 		if (err && threeway) {
@@ -3847,7 +3848,7 @@
 		const char *s = buf;
 
 		if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
-			die(_("corrupt patch for subproject %s"), path);
+			die(_("corrupt patch for submodule %s"), path);
 	} else {
 		if (!cached) {
 			if (lstat(path, &st) < 0)
diff --git a/builtin/blame.c b/builtin/blame.c
index 57a487e..079dcd3 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -21,6 +21,7 @@
 #include "parse-options.h"
 #include "utf8.h"
 #include "userdiff.h"
+#include "line-range.h"
 
 static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file");
 
@@ -566,11 +567,16 @@
 	dst->score = 0;
 }
 
-static const char *nth_line(struct scoreboard *sb, int lno)
+static const char *nth_line(struct scoreboard *sb, long lno)
 {
 	return sb->final_buf + sb->lineno[lno];
 }
 
+static const char *nth_line_cb(void *data, long lno)
+{
+	return nth_line((struct scoreboard *)data, lno);
+}
+
 /*
  * It is known that lines between tlno to same came from parent, and e
  * has an overlap with that range.  it also is known that parent's
@@ -1932,83 +1938,6 @@
 }
 
 /*
- * Parsing of (comma separated) one item in the -L option
- */
-static const char *parse_loc(const char *spec,
-			     struct scoreboard *sb, long lno,
-			     long begin, long *ret)
-{
-	char *term;
-	const char *line;
-	long num;
-	int reg_error;
-	regex_t regexp;
-	regmatch_t match[1];
-
-	/* Allow "-L <something>,+20" to mean starting at <something>
-	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
-	 * <something>.
-	 */
-	if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
-		num = strtol(spec + 1, &term, 10);
-		if (term != spec + 1) {
-			if (spec[0] == '-')
-				num = 0 - num;
-			if (0 < num)
-				*ret = begin + num - 2;
-			else if (!num)
-				*ret = begin;
-			else
-				*ret = begin + num;
-			return term;
-		}
-		return spec;
-	}
-	num = strtol(spec, &term, 10);
-	if (term != spec) {
-		*ret = num;
-		return term;
-	}
-	if (spec[0] != '/')
-		return spec;
-
-	/* it could be a regexp of form /.../ */
-	for (term = (char *) spec + 1; *term && *term != '/'; term++) {
-		if (*term == '\\')
-			term++;
-	}
-	if (*term != '/')
-		return spec;
-
-	/* try [spec+1 .. term-1] as regexp */
-	*term = 0;
-	begin--; /* input is in human terms */
-	line = nth_line(sb, begin);
-
-	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
-	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
-		const char *cp = line + match[0].rm_so;
-		const char *nline;
-
-		while (begin++ < lno) {
-			nline = nth_line(sb, begin);
-			if (line <= cp && cp < nline)
-				break;
-			line = nline;
-		}
-		*ret = begin;
-		regfree(&regexp);
-		*term++ = '/';
-		return term;
-	}
-	else {
-		char errbuf[1024];
-		regerror(reg_error, &regexp, errbuf, 1024);
-		die("-L parameter '%s': %s", spec + 1, errbuf);
-	}
-}
-
-/*
  * Parsing of -L option
  */
 static void prepare_blame_range(struct scoreboard *sb,
@@ -2016,15 +1945,7 @@
 				long lno,
 				long *bottom, long *top)
 {
-	const char *term;
-
-	term = parse_loc(bottomtop, sb, lno, 1, bottom);
-	if (*term == ',') {
-		term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
-		if (*term)
-			usage(blame_usage);
-	}
-	if (*term)
+	if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path))
 		usage(blame_usage);
 }
 
@@ -2574,10 +2495,6 @@
 	bottom = top = 0;
 	if (bottomtop)
 		prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
-	if (bottom && top && top < bottom) {
-		long tmp;
-		tmp = top; top = bottom; bottom = tmp;
-	}
 	if (bottom < 1)
 		bottom = 1;
 	if (top < 1)
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 045cee7..163ce6c 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -13,9 +13,6 @@
 #include "userdiff.h"
 #include "streaming.h"
 
-#define BATCH 1
-#define BATCH_CHECK 2
-
 static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 {
 	unsigned char sha1[20];
@@ -117,54 +114,184 @@
 	return 0;
 }
 
-static int batch_one_object(const char *obj_name, int print_contents)
-{
+struct expand_data {
 	unsigned char sha1[20];
-	enum object_type type = 0;
+	enum object_type type;
 	unsigned long size;
-	void *contents = NULL;
+	unsigned long disk_size;
+	const char *rest;
+
+	/*
+	 * If mark_query is true, we do not expand anything, but rather
+	 * just mark the object_info with items we wish to query.
+	 */
+	int mark_query;
+
+	/*
+	 * After a mark_query run, this object_info is set up to be
+	 * passed to sha1_object_info_extended. It will point to the data
+	 * elements above, so you can retrieve the response from there.
+	 */
+	struct object_info info;
+};
+
+static int is_atom(const char *atom, const char *s, int slen)
+{
+	int alen = strlen(atom);
+	return alen == slen && !memcmp(atom, s, alen);
+}
+
+static void expand_atom(struct strbuf *sb, const char *atom, int len,
+			void *vdata)
+{
+	struct expand_data *data = vdata;
+
+	if (is_atom("objectname", atom, len)) {
+		if (!data->mark_query)
+			strbuf_addstr(sb, sha1_to_hex(data->sha1));
+	} else if (is_atom("objecttype", atom, len)) {
+		if (data->mark_query)
+			data->info.typep = &data->type;
+		else
+			strbuf_addstr(sb, typename(data->type));
+	} else if (is_atom("objectsize", atom, len)) {
+		if (data->mark_query)
+			data->info.sizep = &data->size;
+		else
+			strbuf_addf(sb, "%lu", data->size);
+	} else if (is_atom("objectsize:disk", atom, len)) {
+		if (data->mark_query)
+			data->info.disk_sizep = &data->disk_size;
+		else
+			strbuf_addf(sb, "%lu", data->disk_size);
+	} else if (is_atom("rest", atom, len)) {
+		if (!data->mark_query && data->rest)
+			strbuf_addstr(sb, data->rest);
+	} else
+		die("unknown format element: %.*s", len, atom);
+}
+
+static size_t expand_format(struct strbuf *sb, const char *start, void *data)
+{
+	const char *end;
+
+	if (*start != '(')
+		return 0;
+	end = strchr(start + 1, ')');
+	if (!end)
+		die("format element '%s' does not end in ')'", start);
+
+	expand_atom(sb, start + 1, end - start - 1, data);
+
+	return end - start + 1;
+}
+
+static void print_object_or_die(int fd, const unsigned char *sha1,
+				enum object_type type, unsigned long size)
+{
+	if (type == OBJ_BLOB) {
+		if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
+			die("unable to stream %s to stdout", sha1_to_hex(sha1));
+	}
+	else {
+		enum object_type rtype;
+		unsigned long rsize;
+		void *contents;
+
+		contents = read_sha1_file(sha1, &rtype, &rsize);
+		if (!contents)
+			die("object %s disappeared", sha1_to_hex(sha1));
+		if (rtype != type)
+			die("object %s changed type!?", sha1_to_hex(sha1));
+		if (rsize != size)
+			die("object %s change size!?", sha1_to_hex(sha1));
+
+		write_or_die(fd, contents, size);
+		free(contents);
+	}
+}
+
+struct batch_options {
+	int enabled;
+	int print_contents;
+	const char *format;
+};
+
+static int batch_one_object(const char *obj_name, struct batch_options *opt,
+			    struct expand_data *data)
+{
+	struct strbuf buf = STRBUF_INIT;
 
 	if (!obj_name)
 	   return 1;
 
-	if (get_sha1(obj_name, sha1)) {
+	if (get_sha1(obj_name, data->sha1)) {
 		printf("%s missing\n", obj_name);
 		fflush(stdout);
 		return 0;
 	}
 
-	if (print_contents == BATCH)
-		contents = read_sha1_file(sha1, &type, &size);
-	else
-		type = sha1_object_info(sha1, &size);
-
-	if (type <= 0) {
+	if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
 		printf("%s missing\n", obj_name);
 		fflush(stdout);
-		if (print_contents == BATCH)
-			free(contents);
 		return 0;
 	}
 
-	printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
-	fflush(stdout);
+	strbuf_expand(&buf, opt->format, expand_format, data);
+	strbuf_addch(&buf, '\n');
+	write_or_die(1, buf.buf, buf.len);
+	strbuf_release(&buf);
 
-	if (print_contents == BATCH) {
-		write_or_die(1, contents, size);
-		printf("\n");
-		fflush(stdout);
-		free(contents);
+	if (opt->print_contents) {
+		print_object_or_die(1, data->sha1, data->type, data->size);
+		write_or_die(1, "\n", 1);
 	}
-
 	return 0;
 }
 
-static int batch_objects(int print_contents)
+static int batch_objects(struct batch_options *opt)
 {
 	struct strbuf buf = STRBUF_INIT;
+	struct expand_data data;
+
+	if (!opt->format)
+		opt->format = "%(objectname) %(objecttype) %(objectsize)";
+
+	/*
+	 * Expand once with our special mark_query flag, which will prime the
+	 * object_info to be handed to sha1_object_info_extended for each
+	 * object.
+	 */
+	memset(&data, 0, sizeof(data));
+	data.mark_query = 1;
+	strbuf_expand(&buf, opt->format, expand_format, &data);
+	data.mark_query = 0;
+
+	/*
+	 * We are going to call get_sha1 on a potentially very large number of
+	 * objects. In most large cases, these will be actual object sha1s. The
+	 * cost to double-check that each one is not also a ref (just so we can
+	 * warn) ends up dwarfing the actual cost of the object lookups
+	 * themselves. We can work around it by just turning off the warning.
+	 */
+	warn_on_object_refname_ambiguity = 0;
 
 	while (strbuf_getline(&buf, stdin, '\n') != EOF) {
-		int error = batch_one_object(buf.buf, print_contents);
+		char *p;
+		int error;
+
+		/*
+		 * Split at first whitespace, tying off the beginning of the
+		 * string and saving the remainder (or NULL) in data.rest.
+		 */
+		p = strpbrk(buf.buf, " \t");
+		if (p) {
+			while (*p && strchr(" \t", *p))
+				*p++ = '\0';
+		}
+		data.rest = p;
+
+		error = batch_one_object(buf.buf, opt, &data);
 		if (error)
 			return error;
 	}
@@ -186,10 +313,29 @@
 	return git_default_config(var, value, cb);
 }
 
+static int batch_option_callback(const struct option *opt,
+				 const char *arg,
+				 int unset)
+{
+	struct batch_options *bo = opt->value;
+
+	if (unset) {
+		memset(bo, 0, sizeof(*bo));
+		return 0;
+	}
+
+	bo->enabled = 1;
+	bo->print_contents = !strcmp(opt->long_name, "batch");
+	bo->format = arg;
+
+	return 0;
+}
+
 int cmd_cat_file(int argc, const char **argv, const char *prefix)
 {
-	int opt = 0, batch = 0;
+	int opt = 0;
 	const char *exp_type = NULL, *obj_name = NULL;
+	struct batch_options batch = {0};
 
 	const struct option options[] = {
 		OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
@@ -200,12 +346,12 @@
 		OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
 		OPT_SET_INT(0, "textconv", &opt,
 			    N_("for blob objects, run textconv on object's content"), 'c'),
-		OPT_SET_INT(0, "batch", &batch,
-			    N_("show info and content of objects fed from the standard input"),
-			    BATCH),
-		OPT_SET_INT(0, "batch-check", &batch,
-			    N_("show info about objects fed from the standard input"),
-			    BATCH_CHECK),
+		{ OPTION_CALLBACK, 0, "batch", &batch, "format",
+			N_("show info and content of objects fed from the standard input"),
+			PARSE_OPT_OPTARG, batch_option_callback },
+		{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
+			N_("show info about objects fed from the standard input"),
+			PARSE_OPT_OPTARG, batch_option_callback },
 		OPT_END()
 	};
 
@@ -222,19 +368,19 @@
 		else
 			usage_with_options(cat_file_usage, options);
 	}
-	if (!opt && !batch) {
+	if (!opt && !batch.enabled) {
 		if (argc == 2) {
 			exp_type = argv[0];
 			obj_name = argv[1];
 		} else
 			usage_with_options(cat_file_usage, options);
 	}
-	if (batch && (opt || argc)) {
+	if (batch.enabled && (opt || argc)) {
 		usage_with_options(cat_file_usage, options);
 	}
 
-	if (batch)
-		return batch_objects(batch);
+	if (batch.enabled)
+		return batch_objects(&batch);
 
 	return cat_one_file(opt, exp_type, obj_name);
 }
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 854a88a..4a8fc70 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -5,7 +5,7 @@
 #include "pathspec.h"
 #include "parse-options.h"
 
-static int quiet, verbose, stdin_paths;
+static int quiet, verbose, stdin_paths, show_non_matching;
 static const char * const check_ignore_usage[] = {
 "git check-ignore [options] pathname...",
 "git check-ignore [options] --stdin < <list-of-paths>",
@@ -22,21 +22,28 @@
 		    N_("read file names from stdin")),
 	OPT_BOOLEAN('z', NULL, &null_term_line,
 		    N_("input paths are terminated by a null character")),
+	OPT_BOOLEAN('n', "non-matching", &show_non_matching,
+		    N_("show non-matching input paths")),
 	OPT_END()
 };
 
 static void output_exclude(const char *path, struct exclude *exclude)
 {
-	char *bang  = exclude->flags & EXC_FLAG_NEGATIVE  ? "!" : "";
-	char *slash = exclude->flags & EXC_FLAG_MUSTBEDIR ? "/" : "";
+	char *bang  = (exclude && exclude->flags & EXC_FLAG_NEGATIVE)  ? "!" : "";
+	char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!null_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
 		} else {
-			quote_c_style(exclude->el->src, NULL, stdout, 0);
-			printf(":%d:%s%s%s\t",
-			       exclude->srcpos,
-			       bang, exclude->pattern, slash);
+			if (exclude) {
+				quote_c_style(exclude->el->src, NULL, stdout, 0);
+				printf(":%d:%s%s%s\t",
+				       exclude->srcpos,
+				       bang, exclude->pattern, slash);
+			}
+			else {
+				printf("::\t");
+			}
 			quote_c_style(path, NULL, stdout, 0);
 			fputc('\n', stdout);
 		}
@@ -44,30 +51,26 @@
 		if (!verbose) {
 			printf("%s%c", path, '\0');
 		} else {
-			printf("%s%c%d%c%s%s%s%c%s%c",
-			       exclude->el->src, '\0',
-			       exclude->srcpos, '\0',
-			       bang, exclude->pattern, slash, '\0',
-			       path, '\0');
+			if (exclude)
+				printf("%s%c%d%c%s%s%s%c%s%c",
+				       exclude->el->src, '\0',
+				       exclude->srcpos, '\0',
+				       bang, exclude->pattern, slash, '\0',
+				       path, '\0');
+			else
+				printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0');
 		}
 	}
 }
 
-static int check_ignore(const char *prefix, const char **pathspec)
+static int check_ignore(struct dir_struct *dir,
+			const char *prefix, const char **pathspec)
 {
-	struct dir_struct dir;
 	const char *path, *full_path;
 	char *seen;
 	int num_ignored = 0, dtype = DT_UNKNOWN, i;
 	struct exclude *exclude;
 
-	/* read_cache() is only necessary so we can watch out for submodules. */
-	if (read_cache() < 0)
-		die(_("index file corrupt"));
-
-	memset(&dir, 0, sizeof(dir));
-	setup_standard_excludes(&dir);
-
 	if (!pathspec || !*pathspec) {
 		if (!quiet)
 			fprintf(stderr, "no pathspec given.\n");
@@ -86,28 +89,26 @@
 					? strlen(prefix) : 0, path);
 		full_path = check_path_for_gitlink(full_path);
 		die_if_path_beyond_symlink(full_path, prefix);
+		exclude = NULL;
 		if (!seen[i]) {
-			exclude = last_exclude_matching(&dir, full_path, &dtype);
-			if (exclude) {
-				if (!quiet)
-					output_exclude(path, exclude);
-				num_ignored++;
-			}
+			exclude = last_exclude_matching(dir, full_path, &dtype);
 		}
+		if (!quiet && (exclude || show_non_matching))
+			output_exclude(path, exclude);
+		if (exclude)
+			num_ignored++;
 	}
 	free(seen);
-	clear_directory(&dir);
 
 	return num_ignored;
 }
 
-static int check_ignore_stdin_paths(const char *prefix)
+static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
 {
 	struct strbuf buf, nbuf;
-	char **pathspec = NULL;
-	size_t nr = 0, alloc = 0;
+	char *pathspec[2] = { NULL, NULL };
 	int line_termination = null_term_line ? 0 : '\n';
-	int num_ignored;
+	int num_ignored = 0;
 
 	strbuf_init(&buf, 0);
 	strbuf_init(&nbuf, 0);
@@ -118,23 +119,19 @@
 				die("line is badly quoted");
 			strbuf_swap(&buf, &nbuf);
 		}
-		ALLOC_GROW(pathspec, nr + 1, alloc);
-		pathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));
-		strcpy(pathspec[nr++], buf.buf);
+		pathspec[0] = buf.buf;
+		num_ignored += check_ignore(dir, prefix, (const char **)pathspec);
+		maybe_flush_or_die(stdout, "check-ignore to stdout");
 	}
-	ALLOC_GROW(pathspec, nr + 1, alloc);
-	pathspec[nr] = NULL;
-	num_ignored = check_ignore(prefix, (const char **)pathspec);
-	maybe_flush_or_die(stdout, "attribute to stdout");
 	strbuf_release(&buf);
 	strbuf_release(&nbuf);
-	free(pathspec);
 	return num_ignored;
 }
 
 int cmd_check_ignore(int argc, const char **argv, const char *prefix)
 {
 	int num_ignored;
+	struct dir_struct dir;
 
 	git_config(git_default_config, NULL);
 
@@ -156,13 +153,24 @@
 		if (verbose)
 			die(_("cannot have both --quiet and --verbose"));
 	}
+	if (show_non_matching && !verbose)
+		die(_("--non-matching is only valid with --verbose"));
+
+	/* read_cache() is only necessary so we can watch out for submodules. */
+	if (read_cache() < 0)
+		die(_("index file corrupt"));
+
+	memset(&dir, 0, sizeof(dir));
+	setup_standard_excludes(&dir);
 
 	if (stdin_paths) {
-		num_ignored = check_ignore_stdin_paths(prefix);
+		num_ignored = check_ignore_stdin_paths(&dir, prefix);
 	} else {
-		num_ignored = check_ignore(prefix, argv);
+		num_ignored = check_ignore(&dir, prefix, argv);
 		maybe_flush_or_die(stdout, "ignore to stdout");
 	}
 
+	clear_directory(&dir);
+
 	return !num_ignored;
 }
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
new file mode 100644
index 0000000..8f4d809
--- /dev/null
+++ b/builtin/check-mailmap.c
@@ -0,0 +1,66 @@
+#include "builtin.h"
+#include "mailmap.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static int use_stdin;
+static const char * const check_mailmap_usage[] = {
+N_("git check-mailmap [options] <contact>..."),
+NULL
+};
+
+static const struct option check_mailmap_options[] = {
+	OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")),
+	OPT_END()
+};
+
+static void check_mailmap(struct string_list *mailmap, const char *contact)
+{
+	const char *name, *mail;
+	size_t namelen, maillen;
+	struct ident_split ident;
+
+	if (split_ident_line(&ident, contact, strlen(contact)))
+		die(_("unable to parse contact: %s"), contact);
+
+	name = ident.name_begin;
+	namelen = ident.name_end - ident.name_begin;
+	mail = ident.mail_begin;
+	maillen = ident.mail_end - ident.mail_begin;
+
+	map_user(mailmap, &mail, &maillen, &name, &namelen);
+
+	if (namelen)
+		printf("%.*s ", (int)namelen, name);
+	printf("<%.*s>\n", (int)maillen, mail);
+}
+
+int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
+	git_config(git_default_config, NULL);
+	argc = parse_options(argc, argv, prefix, check_mailmap_options,
+			     check_mailmap_usage, 0);
+	if (argc == 0 && !use_stdin)
+		die(_("no contacts specified"));
+
+	read_mailmap(&mailmap, NULL);
+
+	for (i = 0; i < argc; ++i)
+		check_mailmap(&mailmap, argv[i]);
+	maybe_flush_or_die(stdout, "stdout");
+
+	if (use_stdin) {
+		struct strbuf buf = STRBUF_INIT;
+		while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+			check_mailmap(&mailmap, buf.buf);
+			maybe_flush_or_die(stdout, "stdout");
+		}
+		strbuf_release(&buf);
+	}
+
+	clear_mailmap(&mailmap);
+	return 0;
+}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 81b4419..7025938 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -97,7 +97,7 @@
 	return 0;
 }
 
-static int skip_same_name(struct cache_entry *ce, int pos)
+static int skip_same_name(const struct cache_entry *ce, int pos)
 {
 	while (++pos < active_nr &&
 	       !strcmp(active_cache[pos]->name, ce->name))
@@ -105,7 +105,7 @@
 	return pos;
 }
 
-static int check_stage(int stage, struct cache_entry *ce, int pos)
+static int check_stage(int stage, const struct cache_entry *ce, int pos)
 {
 	while (pos < active_nr &&
 	       !strcmp(active_cache[pos]->name, ce->name)) {
@@ -119,7 +119,7 @@
 		return error(_("path '%s' does not have their version"), ce->name);
 }
 
-static int check_stages(unsigned stages, struct cache_entry *ce, int pos)
+static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
 {
 	unsigned seen = 0;
 	const char *name = ce->name;
@@ -321,7 +321,7 @@
 
 	/* Any unmerged paths? */
 	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
+		const struct cache_entry *ce = active_cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
@@ -587,7 +587,7 @@
 				   struct branch_info *new)
 {
 	struct strbuf msg = STRBUF_INIT;
-	const char *old_desc;
+	const char *old_desc, *reflog_msg;
 	if (opts->new_branch) {
 		if (opts->new_orphan_branch) {
 			if (opts->new_branch_log && !log_all_ref_updates) {
@@ -620,8 +620,13 @@
 	old_desc = old->name;
 	if (!old_desc && old->commit)
 		old_desc = sha1_to_hex(old->commit->object.sha1);
-	strbuf_addf(&msg, "checkout: moving from %s to %s",
-		    old_desc ? old_desc : "(invalid)", new->name);
+
+	reflog_msg = getenv("GIT_REFLOG_ACTION");
+	if (!reflog_msg)
+		strbuf_addf(&msg, "checkout: moving from %s to %s",
+			old_desc ? old_desc : "(invalid)", new->name);
+	else
+		strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
 
 	if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
 		/* Nothing to do. */
@@ -825,38 +830,43 @@
 }
 
 struct tracking_name_data {
-	const char *name;
-	char *remote;
+	/* const */ char *src_ref;
+	char *dst_ref;
+	unsigned char *dst_sha1;
 	int unique;
 };
 
-static int check_tracking_name(const char *refname, const unsigned char *sha1,
-			       int flags, void *cb_data)
+static int check_tracking_name(struct remote *remote, void *cb_data)
 {
 	struct tracking_name_data *cb = cb_data;
-	const char *slash;
-
-	if (prefixcmp(refname, "refs/remotes/"))
+	struct refspec query;
+	memset(&query, 0, sizeof(struct refspec));
+	query.src = cb->src_ref;
+	if (remote_find_tracking(remote, &query) ||
+	    get_sha1(query.dst, cb->dst_sha1)) {
+		free(query.dst);
 		return 0;
-	slash = strchr(refname + 13, '/');
-	if (!slash || strcmp(slash + 1, cb->name))
-		return 0;
-	if (cb->remote) {
+	}
+	if (cb->dst_ref) {
+		free(query.dst);
 		cb->unique = 0;
 		return 0;
 	}
-	cb->remote = xstrdup(refname);
+	cb->dst_ref = query.dst;
 	return 0;
 }
 
-static const char *unique_tracking_name(const char *name)
+static const char *unique_tracking_name(const char *name, unsigned char *sha1)
 {
-	struct tracking_name_data cb_data = { NULL, NULL, 1 };
-	cb_data.name = name;
-	for_each_ref(check_tracking_name, &cb_data);
+	struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
+	char src_ref[PATH_MAX];
+	snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
+	cb_data.src_ref = src_ref;
+	cb_data.dst_sha1 = sha1;
+	for_each_remote(check_tracking_name, &cb_data);
 	if (cb_data.unique)
-		return cb_data.remote;
-	free(cb_data.remote);
+		return cb_data.dst_ref;
+	free(cb_data.dst_ref);
 	return NULL;
 }
 
@@ -919,8 +929,8 @@
 		if (dwim_new_local_branch_ok &&
 		    !check_filename(NULL, arg) &&
 		    argc == 1) {
-			const char *remote = unique_tracking_name(arg);
-			if (!remote || get_sha1(remote, rev))
+			const char *remote = unique_tracking_name(arg, rev);
+			if (!remote)
 				return argcount;
 			*new_branch = arg;
 			arg = remote;
diff --git a/builtin/clean.c b/builtin/clean.c
index 04e396b..dba8387 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -13,11 +13,16 @@
 #include "refs.h"
 #include "string-list.h"
 #include "quote.h"
+#include "column.h"
+#include "color.h"
 
 static int force = -1; /* unset */
+static int interactive;
+static struct string_list del_list = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
 
 static const char *const builtin_clean_usage[] = {
-	N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+	N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
 	NULL
 };
 
@@ -27,11 +32,112 @@
 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
 static const char *msg_warn_remove_failed = N_("failed to remove %s");
 
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RESET,
+	GIT_COLOR_NORMAL,	/* PLAIN */
+	GIT_COLOR_BOLD_BLUE,	/* PROMPT */
+	GIT_COLOR_BOLD,		/* HEADER */
+	GIT_COLOR_BOLD_RED,	/* HELP */
+	GIT_COLOR_BOLD_RED,	/* ERROR */
+};
+enum color_clean {
+	CLEAN_COLOR_RESET = 0,
+	CLEAN_COLOR_PLAIN = 1,
+	CLEAN_COLOR_PROMPT = 2,
+	CLEAN_COLOR_HEADER = 3,
+	CLEAN_COLOR_HELP = 4,
+	CLEAN_COLOR_ERROR = 5,
+};
+
+#define MENU_OPTS_SINGLETON		01
+#define MENU_OPTS_IMMEDIATE		02
+#define MENU_OPTS_LIST_ONLY		04
+
+struct menu_opts {
+	const char *header;
+	const char *prompt;
+	int flags;
+};
+
+#define MENU_RETURN_NO_LOOP		10
+
+struct menu_item {
+	char hotkey;
+	const char *title;
+	int selected;
+	int (*fn)();
+};
+
+enum menu_stuff_type {
+	MENU_STUFF_TYPE_STRING_LIST = 1,
+	MENU_STUFF_TYPE_MENU_ITEM
+};
+
+struct menu_stuff {
+	enum menu_stuff_type type;
+	int nr;
+	void *stuff;
+};
+
+static int parse_clean_color_slot(const char *var)
+{
+	if (!strcasecmp(var, "reset"))
+		return CLEAN_COLOR_RESET;
+	if (!strcasecmp(var, "plain"))
+		return CLEAN_COLOR_PLAIN;
+	if (!strcasecmp(var, "prompt"))
+		return CLEAN_COLOR_PROMPT;
+	if (!strcasecmp(var, "header"))
+		return CLEAN_COLOR_HEADER;
+	if (!strcasecmp(var, "help"))
+		return CLEAN_COLOR_HELP;
+	if (!strcasecmp(var, "error"))
+		return CLEAN_COLOR_ERROR;
+	return -1;
+}
+
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "clean.requireforce"))
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "clean", &colopts);
+
+	/* honors the color.interactive* config variables which also
+	   applied in git-add--interactive and git-stash */
+	if (!strcmp(var, "color.interactive")) {
+		clean_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+	if (!prefixcmp(var, "color.interactive.")) {
+		int slot = parse_clean_color_slot(var +
+						  strlen("color.interactive."));
+		if (slot < 0)
+			return 0;
+		if (!value)
+			return config_error_nonbool(var);
+		color_parse(value, var, clean_colors[slot]);
+		return 0;
+	}
+
+	if (!strcmp(var, "clean.requireforce")) {
 		force = !git_config_bool(var, value);
-	return git_default_config(var, value, cb);
+		return 0;
+	}
+
+	/* inspect the color.ui config variable and others */
+	return git_color_default_config(var, value, cb);
+}
+
+static const char *clean_get_color(enum color_clean ix)
+{
+	if (want_color(clean_use_color))
+		return clean_colors[ix];
+	return "";
+}
+
+static void clean_print_color(enum color_clean ix)
+{
+	printf("%s", clean_get_color(ix));
 }
 
 static int exclude_cb(const struct option *opt, const char *arg, int unset)
@@ -56,7 +162,7 @@
 	if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
 			!resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
 		if (!quiet) {
-			quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+			quote_path_relative(path->buf, prefix, &quoted);
 			printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
 					quoted.buf);
 		}
@@ -70,7 +176,7 @@
 		/* an empty dir could be removed even if it is unreadble */
 		res = dry_run ? 0 : rmdir(path->buf);
 		if (res) {
-			quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+			quote_path_relative(path->buf, prefix, &quoted);
 			warning(_(msg_warn_remove_failed), quoted.buf);
 			*dir_gone = 0;
 		}
@@ -94,7 +200,7 @@
 			if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
 				ret = 1;
 			if (gone) {
-				quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+				quote_path_relative(path->buf, prefix, &quoted);
 				string_list_append(&dels, quoted.buf);
 			} else
 				*dir_gone = 0;
@@ -102,10 +208,10 @@
 		} else {
 			res = dry_run ? 0 : unlink(path->buf);
 			if (!res) {
-				quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+				quote_path_relative(path->buf, prefix, &quoted);
 				string_list_append(&dels, quoted.buf);
 			} else {
-				quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+				quote_path_relative(path->buf, prefix, &quoted);
 				warning(_(msg_warn_remove_failed), quoted.buf);
 				*dir_gone = 0;
 				ret = 1;
@@ -127,7 +233,7 @@
 		if (!res)
 			*dir_gone = 1;
 		else {
-			quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+			quote_path_relative(path->buf, prefix, &quoted);
 			warning(_(msg_warn_remove_failed), quoted.buf);
 			*dir_gone = 0;
 			ret = 1;
@@ -142,24 +248,609 @@
 	return ret;
 }
 
+static void pretty_print_dels(void)
+{
+	struct string_list list = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	struct strbuf buf = STRBUF_INIT;
+	const char *qname;
+	struct column_options copts;
+
+	for_each_string_list_item(item, &del_list) {
+		qname = quote_path_relative(item->string, NULL, &buf);
+		string_list_append(&list, qname);
+	}
+
+	/*
+	 * always enable column display, we only consult column.*
+	 * about layout strategy and stuff
+	 */
+	colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(&list, colopts, &copts);
+	strbuf_release(&buf);
+	string_list_clear(&list, 0);
+}
+
+static void pretty_print_menus(struct string_list *menu_list)
+{
+	unsigned int local_colopts = 0;
+	struct column_options copts;
+
+	local_colopts = COL_ENABLED | COL_ROW;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(menu_list, local_colopts, &copts);
+}
+
+static void prompt_help_cmd(int singleton)
+{
+	clean_print_color(CLEAN_COLOR_HELP);
+	printf_ln(singleton ?
+		  _("Prompt help:\n"
+		    "1          - select a numbered item\n"
+		    "foo        - select item based on unique prefix\n"
+		    "           - (empty) select nothing") :
+		  _("Prompt help:\n"
+		    "1          - select a single item\n"
+		    "3-5        - select a range of items\n"
+		    "2-3,6-9    - select multiple ranges\n"
+		    "foo        - select item based on unique prefix\n"
+		    "-...       - unselect specified items\n"
+		    "*          - choose all items\n"
+		    "           - (empty) finish selecting"));
+	clean_print_color(CLEAN_COLOR_RESET);
+}
+
+/*
+ * display menu stuff with number prefix and hotkey highlight
+ */
+static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
+{
+	struct string_list menu_list = STRING_LIST_INIT_DUP;
+	struct strbuf menu = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
+	int i;
+
+	switch (stuff->type) {
+	default:
+		die("Bad type of menu_staff when print menu");
+	case MENU_STUFF_TYPE_MENU_ITEM:
+		menu_item = (struct menu_item *)stuff->stuff;
+		for (i = 0; i < stuff->nr; i++, menu_item++) {
+			const char *p;
+			int highlighted = 0;
+
+			p = menu_item->title;
+			if ((*chosen)[i] < 0)
+				(*chosen)[i] = menu_item->selected ? 1 : 0;
+			strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
+			for (; *p; p++) {
+				if (!highlighted && *p == menu_item->hotkey) {
+					strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
+					strbuf_addch(&menu, *p);
+					strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
+					highlighted = 1;
+				} else {
+					strbuf_addch(&menu, *p);
+				}
+			}
+			string_list_append(&menu_list, menu.buf);
+			strbuf_reset(&menu);
+		}
+		break;
+	case MENU_STUFF_TYPE_STRING_LIST:
+		i = 0;
+		for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
+			if ((*chosen)[i] < 0)
+				(*chosen)[i] = 0;
+			strbuf_addf(&menu, "%s%2d: %s",
+				    (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
+			string_list_append(&menu_list, menu.buf);
+			strbuf_reset(&menu);
+			i++;
+		}
+		break;
+	}
+
+	pretty_print_menus(&menu_list);
+
+	strbuf_release(&menu);
+	strbuf_release(&buf);
+	string_list_clear(&menu_list, 0);
+}
+
+/*
+ * Parse user input, and return choice(s) for menu (menu_stuff).
+ *
+ * Input
+ *     (for single choice)
+ *         1          - select a numbered item
+ *         foo        - select item based on menu title
+ *                    - (empty) select nothing
+ *
+ *     (for multiple choice)
+ *         1          - select a single item
+ *         3-5        - select a range of items
+ *         2-3,6-9    - select multiple ranges
+ *         foo        - select item based on menu title
+ *         -...       - unselect specified items
+ *         *          - choose all items
+ *                    - (empty) finish selecting
+ *
+ * The parse result will be saved in array **chosen, and
+ * return number of total selections.
+ */
+static int parse_choice(struct menu_stuff *menu_stuff,
+			int is_single,
+			struct strbuf input,
+			int **chosen)
+{
+	struct strbuf **choice_list, **ptr;
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
+	int nr = 0;
+	int i;
+
+	if (is_single) {
+		choice_list = strbuf_split_max(&input, '\n', 0);
+	} else {
+		char *p = input.buf;
+		do {
+			if (*p == ',')
+				*p = ' ';
+		} while (*p++);
+		choice_list = strbuf_split_max(&input, ' ', 0);
+	}
+
+	for (ptr = choice_list; *ptr; ptr++) {
+		char *p;
+		int choose = 1;
+		int bottom = 0, top = 0;
+		int is_range, is_number;
+
+		strbuf_trim(*ptr);
+		if (!(*ptr)->len)
+			continue;
+
+		/* Input that begins with '-'; unchoose */
+		if (*(*ptr)->buf == '-') {
+			choose = 0;
+			strbuf_remove((*ptr), 0, 1);
+		}
+
+		is_range = 0;
+		is_number = 1;
+		for (p = (*ptr)->buf; *p; p++) {
+			if ('-' == *p) {
+				if (!is_range) {
+					is_range = 1;
+					is_number = 0;
+				} else {
+					is_number = 0;
+					is_range = 0;
+					break;
+				}
+			} else if (!isdigit(*p)) {
+				is_number = 0;
+				is_range = 0;
+				break;
+			}
+		}
+
+		if (is_number) {
+			bottom = atoi((*ptr)->buf);
+			top = bottom;
+		} else if (is_range) {
+			bottom = atoi((*ptr)->buf);
+			/* a range can be specified like 5-7 or 5- */
+			if (!*(strchr((*ptr)->buf, '-') + 1))
+				top = menu_stuff->nr;
+			else
+				top = atoi(strchr((*ptr)->buf, '-') + 1);
+		} else if (!strcmp((*ptr)->buf, "*")) {
+			bottom = 1;
+			top = menu_stuff->nr;
+		} else {
+			switch (menu_stuff->type) {
+			default:
+				die("Bad type of menu_stuff when parse choice");
+			case MENU_STUFF_TYPE_MENU_ITEM:
+				menu_item = (struct menu_item *)menu_stuff->stuff;
+				for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
+					if (((*ptr)->len == 1 &&
+					     *(*ptr)->buf == menu_item->hotkey) ||
+					    !strcasecmp((*ptr)->buf, menu_item->title)) {
+						bottom = i + 1;
+						top = bottom;
+						break;
+					}
+				}
+				break;
+			case MENU_STUFF_TYPE_STRING_LIST:
+				string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
+				for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
+					if (!strcasecmp((*ptr)->buf, string_list_item->string)) {
+						bottom = i + 1;
+						top = bottom;
+						break;
+					}
+				}
+				break;
+			}
+		}
+
+		if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
+		    (is_single && bottom != top)) {
+			clean_print_color(CLEAN_COLOR_ERROR);
+			printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+			clean_print_color(CLEAN_COLOR_RESET);
+			continue;
+		}
+
+		for (i = bottom; i <= top; i++)
+			(*chosen)[i-1] = choose;
+	}
+
+	strbuf_list_free(choice_list);
+
+	for (i = 0; i < menu_stuff->nr; i++)
+		nr += (*chosen)[i];
+	return nr;
+}
+
+/*
+ * Implement a git-add-interactive compatible UI, which is borrowed
+ * from git-add--interactive.perl.
+ *
+ * Return value:
+ *
+ *   - Return an array of integers
+ *   - , and it is up to you to free the allocated memory.
+ *   - The array ends with EOF.
+ *   - If user pressed CTRL-D (i.e. EOF), no selection returned.
+ */
+static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
+{
+	struct strbuf choice = STRBUF_INIT;
+	int *chosen, *result;
+	int nr = 0;
+	int eof = 0;
+	int i;
+
+	chosen = xmalloc(sizeof(int) * stuff->nr);
+	/* set chosen as uninitialized */
+	for (i = 0; i < stuff->nr; i++)
+		chosen[i] = -1;
+
+	for (;;) {
+		if (opts->header) {
+			printf_ln("%s%s%s",
+				  clean_get_color(CLEAN_COLOR_HEADER),
+				  _(opts->header),
+				  clean_get_color(CLEAN_COLOR_RESET));
+		}
+
+		/* chosen will be initialized by print_highlight_menu_stuff */
+		print_highlight_menu_stuff(stuff, &chosen);
+
+		if (opts->flags & MENU_OPTS_LIST_ONLY)
+			break;
+
+		if (opts->prompt) {
+			printf("%s%s%s%s",
+			       clean_get_color(CLEAN_COLOR_PROMPT),
+			       _(opts->prompt),
+			       opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
+			       clean_get_color(CLEAN_COLOR_RESET));
+		}
+
+		if (strbuf_getline(&choice, stdin, '\n') != EOF) {
+			strbuf_trim(&choice);
+		} else {
+			eof = 1;
+			break;
+		}
+
+		/* help for prompt */
+		if (!strcmp(choice.buf, "?")) {
+			prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
+			continue;
+		}
+
+		/* for a multiple-choice menu, press ENTER (empty) will return back */
+		if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
+			break;
+
+		nr = parse_choice(stuff,
+				  opts->flags & MENU_OPTS_SINGLETON,
+				  choice,
+				  &chosen);
+
+		if (opts->flags & MENU_OPTS_SINGLETON) {
+			if (nr)
+				break;
+		} else if (opts->flags & MENU_OPTS_IMMEDIATE) {
+			break;
+		}
+	}
+
+	if (eof) {
+		result = xmalloc(sizeof(int));
+		*result = EOF;
+	} else {
+		int j = 0;
+
+		/*
+		 * recalculate nr, if return back from menu directly with
+		 * default selections.
+		 */
+		if (!nr) {
+			for (i = 0; i < stuff->nr; i++)
+				nr += chosen[i];
+		}
+
+		result = xmalloc(sizeof(int) * (nr + 1));
+		memset(result, 0, sizeof(int) * (nr + 1));
+		for (i = 0; i < stuff->nr && j < nr; i++) {
+			if (chosen[i])
+				result[j++] = i;
+		}
+		result[j] = EOF;
+	}
+
+	free(chosen);
+	strbuf_release(&choice);
+	return result;
+}
+
+static int clean_cmd(void)
+{
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int filter_by_patterns_cmd(void)
+{
+	struct dir_struct dir;
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf **ignore_list;
+	struct string_list_item *item;
+	struct exclude_list *el;
+	int changed = -1, i;
+
+	for (;;) {
+		if (!del_list.nr)
+			break;
+
+		if (changed)
+			pretty_print_dels();
+
+		clean_print_color(CLEAN_COLOR_PROMPT);
+		printf(_("Input ignore patterns>> "));
+		clean_print_color(CLEAN_COLOR_RESET);
+		if (strbuf_getline(&confirm, stdin, '\n') != EOF)
+			strbuf_trim(&confirm);
+		else
+			putchar('\n');
+
+		/* quit filter_by_pattern mode if press ENTER or Ctrl-D */
+		if (!confirm.len)
+			break;
+
+		memset(&dir, 0, sizeof(dir));
+		el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+		ignore_list = strbuf_split_max(&confirm, ' ', 0);
+
+		for (i = 0; ignore_list[i]; i++) {
+			strbuf_trim(ignore_list[i]);
+			if (!ignore_list[i]->len)
+				continue;
+
+			add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+		}
+
+		changed = 0;
+		for_each_string_list_item(item, &del_list) {
+			int dtype = DT_UNKNOWN;
+
+			if (is_excluded(&dir, item->string, &dtype)) {
+				*item->string = '\0';
+				changed++;
+			}
+		}
+
+		if (changed) {
+			string_list_remove_empty_items(&del_list, 0);
+		} else {
+			clean_print_color(CLEAN_COLOR_ERROR);
+			printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
+			clean_print_color(CLEAN_COLOR_RESET);
+		}
+
+		strbuf_list_free(ignore_list);
+		clear_directory(&dir);
+	}
+
+	strbuf_release(&confirm);
+	return 0;
+}
+
+static int select_by_numbers_cmd(void)
+{
+	struct menu_opts menu_opts;
+	struct menu_stuff menu_stuff;
+	struct string_list_item *items;
+	int *chosen;
+	int i, j;
+
+	menu_opts.header = NULL;
+	menu_opts.prompt = N_("Select items to delete");
+	menu_opts.flags = 0;
+
+	menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
+	menu_stuff.stuff = &del_list;
+	menu_stuff.nr = del_list.nr;
+
+	chosen = list_and_choose(&menu_opts, &menu_stuff);
+	items = del_list.items;
+	for (i = 0, j = 0; i < del_list.nr; i++) {
+		if (i < chosen[j]) {
+			*(items[i].string) = '\0';
+		} else if (i == chosen[j]) {
+			/* delete selected item */
+			j++;
+			continue;
+		} else {
+			/* end of chosen (chosen[j] == EOF), won't delete */
+			*(items[i].string) = '\0';
+		}
+	}
+
+	string_list_remove_empty_items(&del_list, 0);
+
+	free(chosen);
+	return 0;
+}
+
+static int ask_each_cmd(void)
+{
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	const char *qname;
+	int changed = 0, eof = 0;
+
+	for_each_string_list_item(item, &del_list) {
+		/* Ctrl-D should stop removing files */
+		if (!eof) {
+			qname = quote_path_relative(item->string, NULL, &buf);
+			printf(_("remove %s? "), qname);
+			if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+				strbuf_trim(&confirm);
+			} else {
+				putchar('\n');
+				eof = 1;
+			}
+		}
+		if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
+			*item->string = '\0';
+			changed++;
+		}
+	}
+
+	if (changed)
+		string_list_remove_empty_items(&del_list, 0);
+
+	strbuf_release(&buf);
+	strbuf_release(&confirm);
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int quit_cmd(void)
+{
+	string_list_clear(&del_list, 0);
+	printf_ln(_("Bye."));
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int help_cmd(void)
+{
+	clean_print_color(CLEAN_COLOR_HELP);
+	printf_ln(_(
+		    "clean               - start cleaning\n"
+		    "filter by pattern   - exclude items from deletion\n"
+		    "select by numbers   - select items to be deleted by numbers\n"
+		    "ask each            - confirm each deletion (like \"rm -i\")\n"
+		    "quit                - stop cleaning\n"
+		    "help                - this screen\n"
+		    "?                   - help for prompt selection"
+		   ));
+	clean_print_color(CLEAN_COLOR_RESET);
+	return 0;
+}
+
+static void interactive_main_loop(void)
+{
+	while (del_list.nr) {
+		struct menu_opts menu_opts;
+		struct menu_stuff menu_stuff;
+		struct menu_item menus[] = {
+			{'c', "clean",			0, clean_cmd},
+			{'f', "filter by pattern",	0, filter_by_patterns_cmd},
+			{'s', "select by numbers",	0, select_by_numbers_cmd},
+			{'a', "ask each",		0, ask_each_cmd},
+			{'q', "quit",			0, quit_cmd},
+			{'h', "help",			0, help_cmd},
+		};
+		int *chosen;
+
+		menu_opts.header = N_("*** Commands ***");
+		menu_opts.prompt = N_("What now");
+		menu_opts.flags = MENU_OPTS_SINGLETON;
+
+		menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
+		menu_stuff.stuff = menus;
+		menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
+
+		clean_print_color(CLEAN_COLOR_HEADER);
+		printf_ln(Q_("Would remove the following item:",
+			     "Would remove the following items:",
+			     del_list.nr));
+		clean_print_color(CLEAN_COLOR_RESET);
+
+		pretty_print_dels();
+
+		chosen = list_and_choose(&menu_opts, &menu_stuff);
+
+		if (*chosen != EOF) {
+			int ret;
+			ret = menus[*chosen].fn();
+			if (ret != MENU_RETURN_NO_LOOP) {
+				free(chosen);
+				chosen = NULL;
+				if (!del_list.nr) {
+					clean_print_color(CLEAN_COLOR_ERROR);
+					printf_ln(_("No more files to clean, exiting."));
+					clean_print_color(CLEAN_COLOR_RESET);
+					break;
+				}
+				continue;
+			}
+		} else {
+			quit_cmd();
+		}
+
+		free(chosen);
+		chosen = NULL;
+		break;
+	}
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
 	int i, res;
 	int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
 	int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
 	int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
-	struct strbuf directory = STRBUF_INIT;
+	struct strbuf abs_path = STRBUF_INIT;
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
 	struct string_list exclude_list = STRING_LIST_INIT_NODUP;
 	struct exclude_list *el;
+	struct string_list_item *item;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("do not print names of files removed")),
 		OPT__DRY_RUN(&dry_run, N_("dry run")),
 		OPT__FORCE(&force, N_("force")),
+		OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				N_("remove whole directories")),
 		{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
@@ -186,12 +877,12 @@
 	if (ignored && ignored_only)
 		die(_("-x and -X cannot be used together"));
 
-	if (!dry_run && !force) {
+	if (!interactive && !dry_run && !force) {
 		if (config_set)
-			die(_("clean.requireForce set to true and neither -n nor -f given; "
+			die(_("clean.requireForce set to true and neither -i, -n nor -f given; "
 				  "refusing to clean"));
 		else
-			die(_("clean.requireForce defaults to true and neither -n nor -f given; "
+			die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; "
 				  "refusing to clean"));
 	}
 
@@ -221,8 +912,9 @@
 		struct dir_entry *ent = dir.entries[i];
 		int len, pos;
 		int matches = 0;
-		struct cache_entry *ce;
+		const struct cache_entry *ce;
 		struct stat st;
+		const char *rel;
 
 		/*
 		 * Remove the '/' at the end that directory
@@ -242,13 +934,8 @@
 				continue; /* Yup, this one exists unmerged */
 		}
 
-		/*
-		 * we might have removed this as part of earlier
-		 * recursive directory removal, so lstat() here could
-		 * fail with ENOENT.
-		 */
 		if (lstat(ent->name, &st))
-			continue;
+			die_errno("Cannot lstat '%s'", ent->name);
 
 		if (pathspec) {
 			memset(seen, 0, argc > 0 ? argc : 1);
@@ -257,33 +944,62 @@
 		}
 
 		if (S_ISDIR(st.st_mode)) {
-			strbuf_addstr(&directory, ent->name);
 			if (remove_directories || (matches == MATCHED_EXACTLY)) {
-				if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
-					errors++;
-				if (gone && !quiet) {
-					qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-					printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
-				}
+				rel = relative_path(ent->name, prefix, &buf);
+				string_list_append(&del_list, rel);
 			}
-			strbuf_reset(&directory);
 		} else {
 			if (pathspec && !matches)
 				continue;
-			res = dry_run ? 0 : unlink(ent->name);
+			rel = relative_path(ent->name, prefix, &buf);
+			string_list_append(&del_list, rel);
+		}
+	}
+
+	if (interactive && del_list.nr > 0)
+		interactive_main_loop();
+
+	for_each_string_list_item(item, &del_list) {
+		struct stat st;
+
+		if (prefix)
+			strbuf_addstr(&abs_path, prefix);
+
+		strbuf_addstr(&abs_path, item->string);
+
+		/*
+		 * we might have removed this as part of earlier
+		 * recursive directory removal, so lstat() here could
+		 * fail with ENOENT.
+		 */
+		if (lstat(abs_path.buf, &st))
+			continue;
+
+		if (S_ISDIR(st.st_mode)) {
+			if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
+				errors++;
+			if (gone && !quiet) {
+				qname = quote_path_relative(item->string, NULL, &buf);
+				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+			}
+		} else {
+			res = dry_run ? 0 : unlink(abs_path.buf);
 			if (res) {
-				qname = quote_path_relative(ent->name, -1, &buf, prefix);
+				qname = quote_path_relative(item->string, NULL, &buf);
 				warning(_(msg_warn_remove_failed), qname);
 				errors++;
 			} else if (!quiet) {
-				qname = quote_path_relative(ent->name, -1, &buf, prefix);
+				qname = quote_path_relative(item->string, NULL, &buf);
 				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
 			}
 		}
+		strbuf_reset(&abs_path);
 	}
 	free(seen);
 
-	strbuf_release(&directory);
+	strbuf_release(&abs_path);
+	strbuf_release(&buf);
+	string_list_clear(&del_list, 0);
 	string_list_clear(&exclude_list, 0);
 	return (errors != 0);
 }
diff --git a/builtin/clone.c b/builtin/clone.c
index 035ab64..430307b 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -18,7 +18,6 @@
 #include "transport.h"
 #include "strbuf.h"
 #include "dir.h"
-#include "pack-refs.h"
 #include "sigchain.h"
 #include "branch.h"
 #include "remote.h"
@@ -494,13 +493,16 @@
 {
 	const struct ref *r;
 
+	lock_packed_refs(LOCK_DIE_ON_ERROR);
+
 	for (r = local_refs; r; r = r->next) {
 		if (!r->peer_ref)
 			continue;
 		add_packed_ref(r->peer_ref->name, r->old_sha1);
 	}
 
-	pack_refs(PACK_REFS_ALL);
+	if (commit_packed_refs())
+		die_errno("unable to overwrite old ref-pack file");
 }
 
 static void write_followtags(const struct ref *refs, const char *msg)
@@ -542,12 +544,21 @@
 			       const struct ref *mapped_refs,
 			       const struct ref *remote_head_points_at,
 			       const char *branch_top,
-			       const char *msg)
+			       const char *msg,
+			       struct transport *transport,
+			       int check_connectivity)
 {
 	const struct ref *rm = mapped_refs;
 
-	if (check_everything_connected(iterate_ref_map, 0, &rm))
-		die(_("remote did not send all necessary objects"));
+	if (check_connectivity) {
+		if (0 <= option_verbosity)
+			printf(_("Checking connectivity... "));
+		if (check_everything_connected_with_transport(iterate_ref_map,
+							      0, &rm, transport))
+			die(_("remote did not send all necessary objects"));
+		if (0 <= option_verbosity)
+			printf(_("done\n"));
+	}
 
 	if (refs) {
 		write_remote_refs(mapped_refs);
@@ -693,7 +704,7 @@
 			/*
 			 * otherwise, the next "git fetch" will
 			 * simply fetch from HEAD without updating
-			 * any remote tracking branch, which is what
+			 * any remote-tracking branch, which is what
 			 * we want.
 			 */
 		} else {
@@ -783,6 +794,8 @@
 	is_local = option_local != 0 && path && !is_bundle;
 	if (is_local && option_depth)
 		warning(_("--depth is ignored in local clones; use file:// instead."));
+	if (option_local > 0 && !is_local)
+		warning(_("--local is ignored"));
 
 	if (argc == 2)
 		dir = xstrdup(argv[1]);
@@ -889,6 +902,9 @@
 		if (option_upload_pack)
 			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 					     option_upload_pack);
+
+		if (transport->smart_options && !option_depth)
+			transport->smart_options->check_self_contained_and_connected = 1;
 	}
 
 	refs = transport_get_remote_refs(transport);
@@ -950,7 +966,7 @@
 		transport_fetch_refs(transport, mapped_refs);
 
 	update_remote_refs(refs, mapped_refs, remote_head_points_at,
-			   branch_top.buf, reflog_msg.buf);
+			   branch_top.buf, reflog_msg.buf, transport, !is_local);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
diff --git a/builtin/commit.c b/builtin/commit.c
index d2f30d9..003bd7d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -29,6 +29,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
+#include "notes-utils.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [options] [--] <pathspec>..."),
@@ -107,23 +108,27 @@
 
 static enum commit_whence whence;
 static int use_editor = 1, include_status = 1;
-static int show_ignored_in_status;
+static int show_ignored_in_status, have_option_m;
 static const char *only_include_assumed;
 static struct strbuf message = STRBUF_INIT;
 
-static enum {
+static enum status_format {
 	STATUS_FORMAT_NONE = 0,
 	STATUS_FORMAT_LONG,
 	STATUS_FORMAT_SHORT,
-	STATUS_FORMAT_PORCELAIN
-} status_format;
+	STATUS_FORMAT_PORCELAIN,
+
+	STATUS_FORMAT_UNSPECIFIED
+} status_format = STATUS_FORMAT_UNSPECIFIED;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
 	struct strbuf *buf = opt->value;
-	if (unset)
+	if (unset) {
+		have_option_m = 0;
 		strbuf_setlen(buf, 0);
-	else {
+	} else {
+		have_option_m = 1;
 		if (buf->len)
 			strbuf_addch(buf, '\n');
 		strbuf_addstr(buf, arg);
@@ -202,7 +207,7 @@
 	}
 
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		struct string_list_item *item;
 
 		if (ce->ce_flags & CE_UPDATE)
@@ -457,6 +462,9 @@
 	case STATUS_FORMAT_PORCELAIN:
 		wt_porcelain_print(s);
 		break;
+	case STATUS_FORMAT_UNSPECIFIED:
+		die("BUG: finalize_deferred_config() should have been called");
+		break;
 	case STATUS_FORMAT_NONE:
 	case STATUS_FORMAT_LONG:
 		wt_status_print(s);
@@ -526,7 +534,6 @@
 					(lb - strlen(" ") -
 					 (a + strlen("\nauthor "))));
 		email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
-		date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
 		len = eol - (rb + strlen("> "));
 		date = xmalloc(len + 2);
 		*date = '@';
@@ -958,6 +965,42 @@
 	return logmsg_reencode(commit, NULL, out_enc);
 }
 
+/*
+ * Enumerate what needs to be propagated when --porcelain
+ * is not in effect here.
+ */
+static struct status_deferred_config {
+	enum status_format status_format;
+	int show_branch;
+} status_deferred_config = {
+	STATUS_FORMAT_UNSPECIFIED,
+	-1 /* unspecified */
+};
+
+static void finalize_deferred_config(struct wt_status *s)
+{
+	int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+				   !s->null_termination);
+
+	if (s->null_termination) {
+		if (status_format == STATUS_FORMAT_NONE ||
+		    status_format == STATUS_FORMAT_UNSPECIFIED)
+			status_format = STATUS_FORMAT_PORCELAIN;
+		else if (status_format == STATUS_FORMAT_LONG)
+			die(_("--long and -z are incompatible"));
+	}
+
+	if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
+		status_format = status_deferred_config.status_format;
+	if (status_format == STATUS_FORMAT_UNSPECIFIED)
+		status_format = STATUS_FORMAT_NONE;
+
+	if (use_deferred_config && s->show_branch < 0)
+		s->show_branch = status_deferred_config.show_branch;
+	if (s->show_branch < 0)
+		s->show_branch = 0;
+}
+
 static int parse_and_validate_options(int argc, const char *argv[],
 				      const struct option *options,
 				      const char * const usage[],
@@ -968,6 +1011,7 @@
 	int f = 0;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
+	finalize_deferred_config(s);
 
 	if (force_author && !strchr(force_author, '>'))
 		force_author = find_author_by_nickname(force_author);
@@ -975,7 +1019,7 @@
 	if (force_author && renew_authorship)
 		die(_("Using both --reset-author and --author does not make sense"));
 
-	if (logfile || message.len || use_message || fixup_message)
+	if (logfile || have_option_m || use_message || fixup_message)
 		use_editor = 0;
 	if (0 <= edit_flag)
 		use_editor = edit_flag;
@@ -1052,12 +1096,6 @@
 	if (all && argc > 0)
 		die(_("Paths with -a does not make sense."));
 
-	if (s->null_termination) {
-		if (status_format == STATUS_FORMAT_NONE)
-			status_format = STATUS_FORMAT_PORCELAIN;
-		else if (status_format == STATUS_FORMAT_LONG)
-			die(_("--long and -z are incompatible"));
-	}
 	if (status_format != STATUS_FORMAT_NONE)
 		dry_run = 1;
 
@@ -1110,6 +1148,17 @@
 			s->submodule_summary = -1;
 		return 0;
 	}
+	if (!strcmp(k, "status.short")) {
+		if (git_config_bool(k, v))
+			status_deferred_config.status_format = STATUS_FORMAT_SHORT;
+		else
+			status_deferred_config.status_format = STATUS_FORMAT_NONE;
+		return 0;
+	}
+	if (!strcmp(k, "status.branch")) {
+		status_deferred_config.show_branch = git_config_bool(k, v);
+		return 0;
+	}
 	if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
 		s->use_color = git_config_colorbool(k, v);
 		return 0;
@@ -1152,8 +1201,8 @@
 		OPT__VERBOSE(&verbose, N_("be verbose")),
 		OPT_SET_INT('s', "short", &status_format,
 			    N_("show status concisely"), STATUS_FORMAT_SHORT),
-		OPT_BOOLEAN('b', "branch", &s.show_branch,
-			    N_("show branch information")),
+		OPT_BOOL('b', "branch", &s.show_branch,
+			 N_("show branch information")),
 		OPT_SET_INT(0, "porcelain", &status_format,
 			    N_("machine-readable output"),
 			    STATUS_FORMAT_PORCELAIN),
@@ -1186,13 +1235,7 @@
 			     builtin_status_options,
 			     builtin_status_usage, 0);
 	finalize_colopts(&s.colopts, -1);
-
-	if (s.null_termination) {
-		if (status_format == STATUS_FORMAT_NONE)
-			status_format = STATUS_FORMAT_PORCELAIN;
-		else if (status_format == STATUS_FORMAT_LONG)
-			die(_("--long and -z are incompatible"));
-	}
+	finalize_deferred_config(&s);
 
 	handle_untracked_files_arg(&s);
 	if (show_ignored_in_status)
@@ -1221,6 +1264,9 @@
 	case STATUS_FORMAT_PORCELAIN:
 		wt_porcelain_print(&s);
 		break;
+	case STATUS_FORMAT_UNSPECIFIED:
+		die("BUG: finalize_deferred_config() should have been called");
+		break;
 	case STATUS_FORMAT_NONE:
 	case STATUS_FORMAT_LONG:
 		s.verbose = verbose;
@@ -1389,7 +1435,7 @@
 		OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
 		OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
 			    STATUS_FORMAT_SHORT),
-		OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
+		OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
 		OPT_SET_INT(0, "porcelain", &status_format,
 			    N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
 		OPT_SET_INT(0, "long", &status_format,
@@ -1430,6 +1476,7 @@
 	wt_status_prepare(&s);
 	gitmodules_config();
 	git_config(git_commit_config, &s);
+	status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
 	determine_whence(&s);
 	s.colopts = 0;
 
@@ -1591,7 +1638,7 @@
 		if (cfg) {
 			/* we are amending, so current_head is not NULL */
 			copy_note_for_rewrite(cfg, current_head->object.sha1, sha1);
-			finish_copy_notes_for_rewrite(cfg);
+			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
 		}
 		run_rewrite_hook(current_head->object.sha1, sha1);
 	}
diff --git a/builtin/config.c b/builtin/config.c
index 33c9bf9..4010c43 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -21,6 +21,7 @@
 
 static int use_global_config, use_system_config, use_local_config;
 static const char *given_config_file;
+static const char *given_config_blob;
 static int actions, types;
 static const char *get_color_slot, *get_colorbool_slot;
 static int end_null;
@@ -53,6 +54,7 @@
 	OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
 	OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
 	OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
+	OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
 	OPT_GROUP(N_("Action")),
 	OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
 	OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
@@ -218,7 +220,8 @@
 	}
 
 	git_config_with_options(collect_config, &values,
-				given_config_file, respect_includes);
+				given_config_file, given_config_blob,
+				respect_includes);
 
 	ret = !values.nr;
 
@@ -302,7 +305,8 @@
 	get_color_found = 0;
 	parsed_color[0] = '\0';
 	git_config_with_options(git_get_color_config, NULL,
-				given_config_file, respect_includes);
+				given_config_file, given_config_blob,
+				respect_includes);
 
 	if (!get_color_found && def_color)
 		color_parse(def_color, "command line", parsed_color);
@@ -329,8 +333,10 @@
 {
 	get_colorbool_found = -1;
 	get_diff_color_found = -1;
+	get_color_ui_found = -1;
 	git_config_with_options(git_get_colorbool_config, NULL,
-				given_config_file, respect_includes);
+				given_config_file, given_config_blob,
+				respect_includes);
 
 	if (get_colorbool_found < 0) {
 		if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -339,6 +345,10 @@
 			get_colorbool_found = get_color_ui_found;
 	}
 
+	if (get_colorbool_found < 0)
+		/* default value if none found in config */
+		get_colorbool_found = GIT_COLOR_AUTO;
+
 	get_colorbool_found = want_color(get_colorbool_found);
 
 	if (print) {
@@ -348,6 +358,12 @@
 		return get_colorbool_found ? 0 : 1;
 }
 
+static void check_blob_write(void)
+{
+	if (given_config_blob)
+		die("writing config blobs is not supported");
+}
+
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
 	int nongit = !startup_info->have_repository;
@@ -359,7 +375,8 @@
 			     builtin_config_usage,
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 
-	if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
+	if (use_global_config + use_system_config + use_local_config +
+	    !!given_config_file + !!given_config_blob > 1) {
 		error("only one config file at a time.");
 		usage_with_options(builtin_config_usage, builtin_config_options);
 	}
@@ -379,8 +396,8 @@
 			 */
 			die("$HOME not set");
 
-		if (access_or_warn(user_config, R_OK) &&
-		    xdg_config && !access_or_warn(xdg_config, R_OK))
+		if (access_or_warn(user_config, R_OK, 0) &&
+		    xdg_config && !access_or_warn(xdg_config, R_OK, 0))
 			given_config_file = xdg_config;
 		else
 			given_config_file = user_config;
@@ -438,6 +455,7 @@
 		check_argc(argc, 0, 0);
 		if (git_config_with_options(show_all_config, NULL,
 					    given_config_file,
+					    given_config_blob,
 					    respect_includes) < 0) {
 			if (given_config_file)
 				die_errno("unable to read config file '%s'",
@@ -450,6 +468,8 @@
 		check_argc(argc, 0, 0);
 		if (!given_config_file && nongit)
 			die("not in a git directory");
+		if (given_config_blob)
+			die("editing blobs is not supported");
 		git_config(git_default_config, NULL);
 		launch_editor(given_config_file ?
 			      given_config_file : git_path("config"),
@@ -457,6 +477,7 @@
 	}
 	else if (actions == ACTION_SET) {
 		int ret;
+		check_blob_write();
 		check_argc(argc, 2, 2);
 		value = normalize_value(argv[0], argv[1]);
 		ret = git_config_set_in_file(given_config_file, argv[0], value);
@@ -466,18 +487,21 @@
 		return ret;
 	}
 	else if (actions == ACTION_SET_ALL) {
+		check_blob_write();
 		check_argc(argc, 2, 3);
 		value = normalize_value(argv[0], argv[1]);
 		return git_config_set_multivar_in_file(given_config_file,
 						       argv[0], value, argv[2], 0);
 	}
 	else if (actions == ACTION_ADD) {
+		check_blob_write();
 		check_argc(argc, 2, 2);
 		value = normalize_value(argv[0], argv[1]);
 		return git_config_set_multivar_in_file(given_config_file,
 						       argv[0], value, "^$", 0);
 	}
 	else if (actions == ACTION_REPLACE_ALL) {
+		check_blob_write();
 		check_argc(argc, 2, 3);
 		value = normalize_value(argv[0], argv[1]);
 		return git_config_set_multivar_in_file(given_config_file,
@@ -500,6 +524,7 @@
 		return get_value(argv[0], argv[1]);
 	}
 	else if (actions == ACTION_UNSET) {
+		check_blob_write();
 		check_argc(argc, 1, 2);
 		if (argc == 2)
 			return git_config_set_multivar_in_file(given_config_file,
@@ -509,12 +534,14 @@
 						      argv[0], NULL);
 	}
 	else if (actions == ACTION_UNSET_ALL) {
+		check_blob_write();
 		check_argc(argc, 1, 2);
 		return git_config_set_multivar_in_file(given_config_file,
 						       argv[0], NULL, argv[1], 1);
 	}
 	else if (actions == ACTION_RENAME_SECTION) {
 		int ret;
+		check_blob_write();
 		check_argc(argc, 2, 2);
 		ret = git_config_rename_section_in_file(given_config_file,
 							argv[0], argv[1]);
@@ -525,6 +552,7 @@
 	}
 	else if (actions == ACTION_REMOVE_SECTION) {
 		int ret;
+		check_blob_write();
 		check_argc(argc, 1, 1);
 		ret = git_config_rename_section_in_file(given_config_file,
 							argv[0], NULL);
diff --git a/builtin/describe.c b/builtin/describe.c
index 6636a68..7d73722 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -7,6 +7,7 @@
 #include "parse-options.h"
 #include "diff.h"
 #include "hash.h"
+#include "argv-array.h"
 
 #define SEEN		(1u<<0)
 #define MAX_TAGS	(FLAG_BITS - 1)
@@ -21,6 +22,7 @@
 static int all;	/* Any valid ref can be used */
 static int tags;	/* Allow lightweight tags */
 static int longformat;
+static int first_parent;
 static int abbrev = -1; /* unspecified */
 static int max_candidates = 10;
 static struct hash_table names;
@@ -42,7 +44,7 @@
 	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
 	unsigned name_checked:1;
 	unsigned char sha1[20];
-	const char *path;
+	char *path;
 };
 static const char *prio_names[] = {
 	"head", "lightweight", "annotated",
@@ -126,12 +128,14 @@
 			} else {
 				e->next = NULL;
 			}
+			e->path = NULL;
 		}
 		e->tag = tag;
 		e->prio = prio;
 		e->name_checked = 0;
 		hashcpy(e->sha1, sha1);
-		e->path = path;
+		free(e->path);
+		e->path = xstrdup(path);
 	}
 }
 
@@ -336,6 +340,9 @@
 				commit_list_insert_by_date(p, &list);
 			p->object.flags |= c->object.flags;
 			parents = parents->next;
+
+			if (first_parent)
+				break;
 		}
 	}
 
@@ -404,6 +411,7 @@
 		OPT_BOOLEAN(0, "all",        &all, N_("use any ref")),
 		OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag, even unannotated")),
 		OPT_BOOLEAN(0, "long",       &longformat, N_("always use long format")),
+		OPT_BOOLEAN(0, "first-parent", &first_parent, N_("only follow first parent")),
 		OPT__ABBREV(&abbrev),
 		OPT_SET_INT(0, "exact-match", &max_candidates,
 			    N_("only output exact matches"), 0),
@@ -435,24 +443,24 @@
 		die(_("--long is incompatible with --abbrev=0"));
 
 	if (contains) {
-		const char **args = xmalloc((7 + argc) * sizeof(char *));
-		int i = 0;
-		args[i++] = "name-rev";
-		args[i++] = "--name-only";
-		args[i++] = "--no-undefined";
+		struct argv_array args;
+
+		argv_array_init(&args);
+		argv_array_pushl(&args, "name-rev",
+				 "--peel-tag", "--name-only", "--no-undefined",
+				 NULL);
 		if (always)
-			args[i++] = "--always";
+			argv_array_push(&args, "--always");
 		if (!all) {
-			args[i++] = "--tags";
-			if (pattern) {
-				char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
-				sprintf(s, "--refs=refs/tags/%s", pattern);
-				args[i++] = s;
-			}
+			argv_array_push(&args, "--tags");
+			if (pattern)
+				argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
 		}
-		memcpy(args + i, argv, argc * sizeof(char *));
-		args[i + argc] = NULL;
-		return cmd_name_rev(i + argc, args, prefix);
+		while (*argv) {
+			argv_array_push(&args, *argv);
+			argv++;
+		}
+		return cmd_name_rev(args.argc, args.argv, prefix);
 	}
 
 	init_hash(&names);
diff --git a/builtin/diff.c b/builtin/diff.c
index 8c2af6c..9fc273d 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -153,7 +153,8 @@
 
 static int builtin_diff_tree(struct rev_info *revs,
 			     int argc, const char **argv,
-			     struct object_array_entry *ent)
+			     struct object_array_entry *ent0,
+			     struct object_array_entry *ent1)
 {
 	const unsigned char *(sha1[2]);
 	int swap = 0;
@@ -161,13 +162,14 @@
 	if (argc > 1)
 		usage(builtin_diff_usage);
 
-	/* We saw two trees, ent[0] and ent[1].
-	 * if ent[1] is uninteresting, they are swapped
+	/*
+	 * We saw two trees, ent0 and ent1.  If ent1 is uninteresting,
+	 * swap them.
 	 */
-	if (ent[1].item->flags & UNINTERESTING)
+	if (ent1->item->flags & UNINTERESTING)
 		swap = 1;
-	sha1[swap] = ent[0].item->sha1;
-	sha1[1-swap] = ent[1].item->sha1;
+	sha1[swap] = ent0->item->sha1;
+	sha1[1-swap] = ent1->item->sha1;
 	diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
 	log_tree_diff_flush(revs);
 	return 0;
@@ -251,8 +253,8 @@
 {
 	int i;
 	struct rev_info rev;
-	struct object_array_entry ent[100];
-	int ents = 0, blobs = 0, paths = 0;
+	struct object_array ent = OBJECT_ARRAY_INIT;
+	int blobs = 0, paths = 0;
 	const char *path = NULL;
 	struct blobinfo blob[2];
 	int nongit;
@@ -337,9 +339,9 @@
 	}
 
 	for (i = 0; i < rev.pending.nr; i++) {
-		struct object_array_entry *list = rev.pending.objects+i;
-		struct object *obj = list->item;
-		const char *name = list->name;
+		struct object_array_entry *entry = &rev.pending.objects[i];
+		struct object *obj = entry->item;
+		const char *name = entry->name;
 		int flags = (obj->flags & UNINTERESTING);
 		if (!obj->parsed)
 			obj = parse_object(obj->sha1);
@@ -348,27 +350,21 @@
 			die(_("invalid object '%s' given."), name);
 		if (obj->type == OBJ_COMMIT)
 			obj = &((struct commit *)obj)->tree->object;
+
 		if (obj->type == OBJ_TREE) {
-			if (ARRAY_SIZE(ent) <= ents)
-				die(_("more than %d trees given: '%s'"),
-				    (int) ARRAY_SIZE(ent), name);
 			obj->flags |= flags;
-			ent[ents].item = obj;
-			ent[ents].name = name;
-			ents++;
-			continue;
-		}
-		if (obj->type == OBJ_BLOB) {
+			add_object_array(obj, name, &ent);
+		} else if (obj->type == OBJ_BLOB) {
 			if (2 <= blobs)
 				die(_("more than two blobs given: '%s'"), name);
 			hashcpy(blob[blobs].sha1, obj->sha1);
 			blob[blobs].name = name;
-			blob[blobs].mode = list->mode;
+			blob[blobs].mode = entry->mode;
 			blobs++;
-			continue;
 
+		} else {
+			die(_("unhandled object '%s' given."), name);
 		}
-		die(_("unhandled object '%s' given."), name);
 	}
 	if (rev.prune_data.nr) {
 		if (!path)
@@ -379,7 +375,7 @@
 	/*
 	 * Now, do the arguments look reasonable?
 	 */
-	if (!ents) {
+	if (!ent.nr) {
 		switch (blobs) {
 		case 0:
 			result = builtin_diff_files(&rev, argc, argv);
@@ -400,23 +396,26 @@
 	}
 	else if (blobs)
 		usage(builtin_diff_usage);
-	else if (ents == 1)
+	else if (ent.nr == 1)
 		result = builtin_diff_index(&rev, argc, argv);
-	else if (ents == 2)
-		result = builtin_diff_tree(&rev, argc, argv, ent);
-	else if (ent[0].item->flags & UNINTERESTING) {
+	else if (ent.nr == 2)
+		result = builtin_diff_tree(&rev, argc, argv,
+					   &ent.objects[0], &ent.objects[1]);
+	else if (ent.objects[0].item->flags & UNINTERESTING) {
 		/*
 		 * diff A...B where there is at least one merge base
-		 * between A and B.  We have ent[0] == merge-base,
-		 * ent[ents-2] == A, and ent[ents-1] == B.  Show diff
-		 * between the base and B.  Note that we pick one
-		 * merge base at random if there are more than one.
+		 * between A and B.  We have ent.objects[0] ==
+		 * merge-base, ent.objects[ents-2] == A, and
+		 * ent.objects[ents-1] == B.  Show diff between the
+		 * base and B.  Note that we pick one merge base at
+		 * random if there are more than one.
 		 */
-		ent[1] = ent[ents-1];
-		result = builtin_diff_tree(&rev, argc, argv, ent);
+		result = builtin_diff_tree(&rev, argc, argv,
+					   &ent.objects[0],
+					   &ent.objects[ent.nr-1]);
 	} else
 		result = builtin_diff_combined(&rev, argc, argv,
-					       ent, ents);
+					       ent.objects, ent.nr);
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index d60d675..d1d68e9 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -613,6 +613,8 @@
 		char *line_end, *mark_end;
 		unsigned char sha1[20];
 		struct object *object;
+		struct commit *commit;
+		enum object_type type;
 
 		line_end = strchr(line, '\n');
 		if (line[0] != ':' || !line_end)
@@ -621,23 +623,29 @@
 
 		mark = strtoumax(line + 1, &mark_end, 10);
 		if (!mark || mark_end == line + 1
-			|| *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
+			|| *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
 			die("corrupt mark line: %s", line);
 
 		if (last_idnum < mark)
 			last_idnum = mark;
 
-		object = parse_object(sha1);
-		if (!object)
+		type = sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("object not found: %s", sha1_to_hex(sha1));
+
+		if (type != OBJ_COMMIT)
+			/* only commits */
 			continue;
 
+		commit = lookup_commit(sha1);
+		if (!commit)
+			die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+
+		object = &commit->object;
+
 		if (object->flags & SHOWN)
 			error("Object %s already has a mark", sha1_to_hex(sha1));
 
-		if (object->type != OBJ_COMMIT)
-			/* only commits */
-			continue;
-
 		mark_object(object, mark);
 
 		object->flags |= SHOWN;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 4b6b1df..d784b2e 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -119,7 +119,7 @@
 
 		for (rm = *head; rm; rm = rm->next) {
 			if (branch_merge_matches(branch, i, rm->name)) {
-				rm->merge = 1;
+				rm->fetch_head_status = FETCH_HEAD_MERGE;
 				break;
 			}
 		}
@@ -140,7 +140,7 @@
 		refspec.src = branch->merge[i]->src;
 		get_fetch_map(remote_refs, &refspec, tail, 1);
 		for (rm = *old_tail; rm; rm = rm->next)
-			rm->merge = 1;
+			rm->fetch_head_status = FETCH_HEAD_MERGE;
 	}
 }
 
@@ -160,6 +160,8 @@
 	const struct ref *remote_refs = transport_get_remote_refs(transport);
 
 	if (ref_count || tags == TAGS_SET) {
+		struct ref **old_tail;
+
 		for (i = 0; i < ref_count; i++) {
 			get_fetch_map(remote_refs, &refs[i], &tail, 0);
 			if (refs[i].dst && refs[i].dst[0])
@@ -167,9 +169,23 @@
 		}
 		/* Merge everything on the command line, but not --tags */
 		for (rm = ref_map; rm; rm = rm->next)
-			rm->merge = 1;
+			rm->fetch_head_status = FETCH_HEAD_MERGE;
 		if (tags == TAGS_SET)
 			get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+
+		/*
+		 * For any refs that we happen to be fetching via command-line
+		 * arguments, take the opportunity to update their configured
+		 * counterparts. However, we do not want to mention these
+		 * entries in FETCH_HEAD at all, as they would simply be
+		 * duplicates of existing entries.
+		 */
+		old_tail = tail;
+		for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
+			get_fetch_map(ref_map, &transport->remote->fetch[i],
+				      &tail, 1);
+		for (rm = *old_tail; rm; rm = rm->next)
+			rm->fetch_head_status = FETCH_HEAD_IGNORE;
 	} else {
 		/* Use the defaults */
 		struct remote *remote = transport->remote;
@@ -186,7 +202,7 @@
 					*autotags = 1;
 				if (!i && !has_merge && ref_map &&
 				    !remote->fetch[0].pattern)
-					ref_map->merge = 1;
+					ref_map->fetch_head_status = FETCH_HEAD_MERGE;
 			}
 			/*
 			 * if the remote we're fetching from is the same
@@ -202,7 +218,7 @@
 			ref_map = get_remote_ref(remote_refs, "HEAD");
 			if (!ref_map)
 				die(_("Couldn't find remote ref HEAD"));
-			ref_map->merge = 1;
+			ref_map->fetch_head_status = FETCH_HEAD_MERGE;
 			tail = &ref_map->next;
 		}
 	}
@@ -389,7 +405,7 @@
 	const char *what, *kind;
 	struct ref *rm;
 	char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
-	int want_merge;
+	int want_status;
 
 	fp = fopen(filename, "a");
 	if (!fp)
@@ -407,19 +423,22 @@
 	}
 
 	/*
-	 * The first pass writes objects to be merged and then the
-	 * second pass writes the rest, in order to allow using
-	 * FETCH_HEAD as a refname to refer to the ref to be merged.
+	 * We do a pass for each fetch_head_status type in their enum order, so
+	 * merged entries are written before not-for-merge. That lets readers
+	 * use FETCH_HEAD as a refname to refer to the ref to be merged.
 	 */
-	for (want_merge = 1; 0 <= want_merge; want_merge--) {
+	for (want_status = FETCH_HEAD_MERGE;
+	     want_status <= FETCH_HEAD_IGNORE;
+	     want_status++) {
 		for (rm = ref_map; rm; rm = rm->next) {
 			struct ref *ref = NULL;
+			const char *merge_status_marker = "";
 
 			commit = lookup_commit_reference_gently(rm->old_sha1, 1);
 			if (!commit)
-				rm->merge = 0;
+				rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
 
-			if (rm->merge != want_merge)
+			if (rm->fetch_head_status != want_status)
 				continue;
 
 			if (rm->peer_ref) {
@@ -465,16 +484,26 @@
 					strbuf_addf(&note, "%s ", kind);
 				strbuf_addf(&note, "'%s' of ", what);
 			}
-			fprintf(fp, "%s\t%s\t%s",
-				sha1_to_hex(rm->old_sha1),
-				rm->merge ? "" : "not-for-merge",
-				note.buf);
-			for (i = 0; i < url_len; ++i)
-				if ('\n' == url[i])
-					fputs("\\n", fp);
-				else
-					fputc(url[i], fp);
-			fputc('\n', fp);
+			switch (rm->fetch_head_status) {
+			case FETCH_HEAD_NOT_FOR_MERGE:
+				merge_status_marker = "not-for-merge";
+				/* fall-through */
+			case FETCH_HEAD_MERGE:
+				fprintf(fp, "%s\t%s\t%s",
+					sha1_to_hex(rm->old_sha1),
+					merge_status_marker,
+					note.buf);
+				for (i = 0; i < url_len; ++i)
+					if ('\n' == url[i])
+						fputs("\\n", fp);
+					else
+						fputc(url[i], fp);
+				fputc('\n', fp);
+				break;
+			default:
+				/* do not write anything to FETCH_HEAD */
+				break;
+			}
 
 			strbuf_reset(&note);
 			if (ref) {
@@ -571,7 +600,8 @@
 {
 	struct string_list *list = (struct string_list *)cbdata;
 	struct string_list_item *item = string_list_insert(list, refname);
-	item->util = (void *)sha1;
+	item->util = xmalloc(20);
+	hashcpy(item->util, sha1);
 	return 0;
 }
 
@@ -590,7 +620,7 @@
 			struct ref **head,
 			struct ref ***tail)
 {
-	struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+	struct string_list existing_refs = STRING_LIST_INIT_DUP;
 	struct string_list remote_refs = STRING_LIST_INIT_NODUP;
 	const struct ref *ref;
 	struct string_list_item *item = NULL;
@@ -636,7 +666,7 @@
 		item = string_list_insert(&remote_refs, ref->name);
 		item->util = (void *)ref->old_sha1;
 	}
-	string_list_clear(&existing_refs, 0);
+	string_list_clear(&existing_refs, 1);
 
 	/*
 	 * We may have a final lightweight tag that needs to be
@@ -693,11 +723,11 @@
 static int do_fetch(struct transport *transport,
 		    struct refspec *refs, int ref_count)
 {
-	struct string_list existing_refs = STRING_LIST_INIT_NODUP;
-	struct string_list_item *peer_item = NULL;
+	struct string_list existing_refs = STRING_LIST_INIT_DUP;
 	struct ref *ref_map;
 	struct ref *rm;
 	int autotags = (transport->remote->fetch_tags == 1);
+	int retcode = 0;
 
 	for_each_ref(add_existing, &existing_refs);
 
@@ -713,9 +743,9 @@
 
 	/* if not appending, truncate FETCH_HEAD */
 	if (!append && !dry_run) {
-		int errcode = truncate_fetch_head();
-		if (errcode)
-			return errcode;
+		retcode = truncate_fetch_head();
+		if (retcode)
+			goto cleanup;
 	}
 
 	ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
@@ -724,8 +754,9 @@
 
 	for (rm = ref_map; rm; rm = rm->next) {
 		if (rm->peer_ref) {
-			peer_item = string_list_lookup(&existing_refs,
-						       rm->peer_ref->name);
+			struct string_list_item *peer_item =
+				string_list_lookup(&existing_refs,
+						   rm->peer_ref->name);
 			if (peer_item)
 				hashcpy(rm->peer_ref->old_sha1,
 					peer_item->util);
@@ -736,7 +767,8 @@
 		transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
 	if (fetch_refs(transport, ref_map)) {
 		free_refs(ref_map);
-		return 1;
+		retcode = 1;
+		goto cleanup;
 	}
 	if (prune) {
 		/* If --tags was specified, pretend the user gave us the canonical tags refspec */
@@ -779,7 +811,9 @@
 		free_refs(ref_map);
 	}
 
-	return 0;
+ cleanup:
+	string_list_clear(&existing_refs, 1);
+	return retcode;
 }
 
 static void set_option(const char *name, const char *value)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index bb9a2cd..9909b6d 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -112,7 +112,7 @@
 		return 1;
 	}
 
-	add_object_array(obj, (void *) parent, &pending);
+	add_object_array(obj, NULL, &pending);
 	return 0;
 }
 
diff --git a/builtin/grep.c b/builtin/grep.c
index 159e65d..d3b3b1d 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -286,8 +286,7 @@
 	struct strbuf pathbuf = STRBUF_INIT;
 
 	if (opt->relative && opt->prefix_length) {
-		quote_path_relative(filename + tree_name_len, -1, &pathbuf,
-				    opt->prefix);
+		quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
 		strbuf_insert(&pathbuf, 0, filename, tree_name_len);
 	} else {
 		strbuf_addstr(&pathbuf, filename);
@@ -318,7 +317,7 @@
 	struct strbuf buf = STRBUF_INIT;
 
 	if (opt->relative && opt->prefix_length)
-		quote_path_relative(filename, -1, &buf, opt->prefix);
+		quote_path_relative(filename, opt->prefix, &buf);
 	else
 		strbuf_addstr(&buf, filename);
 
@@ -376,7 +375,7 @@
 	read_cache();
 
 	for (nr = 0; nr < active_nr; nr++) {
-		struct cache_entry *ce = active_cache[nr];
+		const struct cache_entry *ce = active_cache[nr];
 		if (!S_ISREG(ce->ce_mode))
 			continue;
 		if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
diff --git a/builtin/help.c b/builtin/help.c
index 062957f..f1e236b 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -1,6 +1,4 @@
 /*
- * builtin-help.c
- *
  * Builtin help command
  */
 #include "cache.h"
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 79dfe47..9c1cfac 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -77,8 +77,10 @@
 
 static int from_stdin;
 static int strict;
+static int do_fsck_object;
 static int verbose;
 static int show_stat;
+static int check_self_contained_and_connected;
 
 static struct progress *progress;
 
@@ -187,13 +189,13 @@
 
 /* The content of each linked object must have been checked
    or it must be already present in the object database */
-static void check_object(struct object *obj)
+static unsigned check_object(struct object *obj)
 {
 	if (!obj)
-		return;
+		return 0;
 
 	if (!(obj->flags & FLAG_LINK))
-		return;
+		return 0;
 
 	if (!(obj->flags & FLAG_CHECKED)) {
 		unsigned long size;
@@ -201,17 +203,20 @@
 		if (type != obj->type || type <= 0)
 			die(_("object of unexpected type"));
 		obj->flags |= FLAG_CHECKED;
-		return;
+		return 1;
 	}
+
+	return 0;
 }
 
-static void check_objects(void)
+static unsigned check_objects(void)
 {
-	unsigned i, max;
+	unsigned i, max, foreign_nr = 0;
 
 	max = get_max_object_index();
 	for (i = 0; i < max; i++)
-		check_object(get_indexed_object(i));
+		foreign_nr += check_object(get_indexed_object(i));
+	return foreign_nr;
 }
 
 
@@ -747,8 +752,7 @@
 			int eaten;
 			void *buf = (void *) data;
 
-			if (!buf)
-				buf = new_data = get_data_from_pack(obj_entry);
+			assert(data && "data can only be NULL for large _blobs_");
 
 			/*
 			 * we do not need to free the memory here, as the
@@ -757,7 +761,8 @@
 			obj = parse_object_buffer(sha1, type, size, buf, &eaten);
 			if (!obj)
 				die(_("invalid %s"), typename(type));
-			if (fsck_object(obj, 1, fsck_error_function))
+			if (do_fsck_object &&
+			    fsck_object(obj, 1, fsck_error_function))
 				die(_("Error in object"));
 			if (fsck_walk(obj, mark_link, NULL))
 				die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
@@ -1491,6 +1496,7 @@
 	struct pack_idx_entry **idx_objects;
 	struct pack_idx_option opts;
 	unsigned char pack_sha1[20];
+	unsigned foreign_nr = 1;	/* zero is a "good" value, assume bad */
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(index_pack_usage);
@@ -1512,6 +1518,10 @@
 				fix_thin_pack = 1;
 			} else if (!strcmp(arg, "--strict")) {
 				strict = 1;
+				do_fsck_object = 1;
+			} else if (!strcmp(arg, "--check-self-contained-and-connected")) {
+				strict = 1;
+				check_self_contained_and_connected = 1;
 			} else if (!strcmp(arg, "--verify")) {
 				verify = 1;
 			} else if (!strcmp(arg, "--verify-stat")) {
@@ -1625,7 +1635,7 @@
 	conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
 	free(deltas);
 	if (strict)
-		check_objects();
+		foreign_nr = check_objects();
 
 	if (show_stat)
 		show_pack_info(stat_only);
@@ -1651,5 +1661,11 @@
 	if (index_name == NULL)
 		free((void *) curr_index);
 
+	/*
+	 * Let the caller know this pack is not self contained
+	 */
+	if (check_self_contained_and_connected && foreign_nr)
+		return 1;
+
 	return 0;
 }
diff --git a/builtin/log.c b/builtin/log.c
index 6e56a50..2625f98 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -19,6 +19,7 @@
 #include "remote.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "line-log.h"
 #include "branch.h"
 #include "streaming.h"
 #include "version.h"
@@ -42,6 +43,12 @@
 	NULL
 };
 
+struct line_opt_callback_data {
+	struct rev_info *rev;
+	const char *prefix;
+	struct string_list args;
+};
+
 static int parse_decoration_style(const char *var, const char *value)
 {
 	switch (git_config_maybe_bool(var, value)) {
@@ -76,6 +83,19 @@
 	return 0;
 }
 
+static int log_line_range_callback(const struct option *option, const char *arg, int unset)
+{
+	struct line_opt_callback_data *data = option->value;
+
+	if (!arg)
+		return -1;
+
+	data->rev->line_level_traverse = 1;
+	string_list_append(&data->args, arg);
+
+	return 0;
+}
+
 static void cmd_log_init_defaults(struct rev_info *rev)
 {
 	if (fmt_pretty)
@@ -98,6 +118,7 @@
 {
 	struct userformat_want w;
 	int quiet = 0, source = 0, mailmap = 0;
+	static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
 
 	const struct option builtin_log_options[] = {
 		OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
@@ -105,9 +126,15 @@
 		OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
 		{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
 		  PARSE_OPT_OPTARG, decorate_callback},
+		OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
+			     "Process line range n,m in file, counting from 1",
+			     log_line_range_callback),
 		OPT_END()
 	};
 
+	line_cb.rev = rev;
+	line_cb.prefix = prefix;
+
 	mailmap = use_mailmap_config;
 	argc = parse_options(argc, argv, prefix,
 			     builtin_log_options, builtin_log_usage,
@@ -161,6 +188,10 @@
 		rev->show_decorations = 1;
 		load_ref_decorations(decoration_style);
 	}
+
+	if (rev->line_level_traverse)
+		line_log_init(rev, line_cb.prefix, &line_cb.args);
+
 	setup_pager();
 }
 
@@ -206,7 +237,7 @@
 	int i = revs->early_output;
 	int show_header = 1;
 
-	sort_in_topological_order(&list, revs->lifo);
+	sort_in_topological_order(&list, revs->sort_order);
 	while (list && i) {
 		struct commit *commit = list->item;
 		switch (simplify_commit(revs, commit)) {
@@ -1081,6 +1112,21 @@
 	return 0;
 }
 
+static int from_callback(const struct option *opt, const char *arg, int unset)
+{
+	char **from = opt->value;
+
+	free(*from);
+
+	if (unset)
+		*from = NULL;
+	else if (arg)
+		*from = xstrdup(arg);
+	else
+		*from = xstrdup(git_committer_info(IDENT_NO_DATE));
+	return 0;
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
 	struct commit *commit;
@@ -1103,6 +1149,7 @@
 	int quiet = 0;
 	int reroll_count = -1;
 	char *branch_name = NULL;
+	char *from = NULL;
 	const struct option builtin_format_patch_options[] = {
 		{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
 			    N_("use [PATCH n/m] even with a single patch"),
@@ -1146,6 +1193,9 @@
 			    0, to_callback },
 		{ OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
 			    0, cc_callback },
+		{ OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+			    N_("set From address to <ident> (or committer ident if absent)"),
+			    PARSE_OPT_OPTARG, from_callback },
 		OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
 			    N_("make first mail a reply to <message-id>")),
 		{ OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
@@ -1233,6 +1283,11 @@
 
 	rev.extra_headers = strbuf_detach(&buf, NULL);
 
+	if (from) {
+		if (split_ident_line(&rev.from_ident, from, strlen(from)))
+			die(_("invalid ident line: %s"), from);
+	}
+
 	if (start_number < 0)
 		start_number = 1;
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 2202072..5cf3e31 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -46,10 +46,14 @@
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
-static void write_name(const char* name, size_t len)
+static void write_name(const char *name)
 {
-	write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
-			line_terminator);
+	/*
+	 * With "--full-name", prefix_len=0; this caller needs to pass
+	 * an empty string in that case (a NULL is good for "").
+	 */
+	write_name_quoted_relative(name, prefix_len ? prefix : NULL,
+				   stdout, line_terminator);
 }
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
@@ -63,7 +67,7 @@
 		return;
 
 	fputs(tag, stdout);
-	write_name(ent->name, ent->len);
+	write_name(ent->name);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -127,7 +131,7 @@
 	}
 }
 
-static void show_ce_entry(const char *tag, struct cache_entry *ce)
+static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
 	int len = max_prefix_len;
 
@@ -163,13 +167,15 @@
 		       find_unique_abbrev(ce->sha1,abbrev),
 		       ce_stage(ce));
 	}
-	write_name(ce->name, ce_namelen(ce));
+	write_name(ce->name);
 	if (debug_mode) {
-		printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
-		printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
-		printf("  dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
-		printf("  uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
-		printf("  size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+		const struct stat_data *sd = &ce->ce_stat_data;
+
+		printf("  ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+		printf("  mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+		printf("  dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+		printf("  uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+		printf("  size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
 	}
 }
 
@@ -196,12 +202,12 @@
 			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
 			       find_unique_abbrev(ui->sha1[i], abbrev),
 			       i + 1);
-			write_name(path, len);
+			write_name(path);
 		}
 	}
 }
 
-static int ce_excluded(struct dir_struct *dir, struct cache_entry *ce)
+static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce)
 {
 	int dtype = ce_to_dtype(ce);
 	return is_excluded(dir, ce->name, &dtype);
@@ -219,9 +225,9 @@
 		if (show_killed)
 			show_killed_files(dir);
 	}
-	if (show_cached | show_stage) {
+	if (show_cached || show_stage) {
 		for (i = 0; i < active_nr; i++) {
-			struct cache_entry *ce = active_cache[i];
+			const struct cache_entry *ce = active_cache[i];
 			if ((dir->flags & DIR_SHOW_IGNORED) &&
 			    !ce_excluded(dir, ce))
 				continue;
@@ -233,9 +239,9 @@
 				(ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
 		}
 	}
-	if (show_deleted | show_modified) {
+	if (show_deleted || show_modified) {
 		for (i = 0; i < active_nr; i++) {
-			struct cache_entry *ce = active_cache[i];
+			const struct cache_entry *ce = active_cache[i];
 			struct stat st;
 			int err;
 			if ((dir->flags & DIR_SHOW_IGNORED) &&
@@ -271,7 +277,7 @@
 	last = active_nr;
 	while (last > first) {
 		int next = (last + first) >> 1;
-		struct cache_entry *ce = active_cache[next];
+		const struct cache_entry *ce = active_cache[next];
 		if (!strncmp(ce->name, prefix, max_prefix_len)) {
 			first = next+1;
 			continue;
@@ -389,7 +395,7 @@
 		if (found_dup)
 			continue;
 
-		name = quote_path_relative(pathspec[num], -1, &sb, prefix);
+		name = quote_path_relative(pathspec[num], prefix, &sb);
 		error("pathspec '%s' did not match any file(s) known to git.",
 		      name);
 		errors++;
@@ -571,8 +577,8 @@
 		die("ls-files --ignored needs some exclude pattern");
 
 	/* With no flags, we default to showing the cached files */
-	if (!(show_stage | show_deleted | show_others | show_unmerged |
-	      show_killed | show_modified | show_resolve_undo))
+	if (!(show_stage || show_deleted || show_others || show_unmerged ||
+	      show_killed || show_modified || show_resolve_undo))
 		show_cached = 1;
 
 	if (max_prefix)
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 1bc7991..0c4cd2f 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -107,7 +107,7 @@
 	argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
 	if (!octopus && !reduce && argc < 2)
 		usage_with_options(merge_base_usage, options);
-	if (is_ancestor && (show_all | octopus | reduce))
+	if (is_ancestor && (show_all || octopus || reduce))
 		die("--is-ancestor cannot be used with other options");
 	if (is_ancestor)
 		return handle_is_ancestor(argc, argv);
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index be5e514..b416d92 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -16,7 +16,7 @@
 		die("git merge-index: %s not in the cache", path);
 	found = 0;
 	do {
-		struct cache_entry *ce = active_cache[pos];
+		const struct cache_entry *ce = active_cache[pos];
 		int stage = ce_stage(ce);
 
 		if (strcmp(ce->name, path))
@@ -58,7 +58,7 @@
 {
 	int i;
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		if (!ce_stage(ce))
 			continue;
 		i += merge_entry(i, ce->name)-1;
diff --git a/builtin/merge.c b/builtin/merge.c
index 3e2daa3..34a6166 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -47,8 +47,8 @@
 };
 
 static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only, option_edit = -1;
+static int option_commit = 1;
+static int option_edit = -1;
 static int allow_trivial = 1, have_message, verify_signatures;
 static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
@@ -76,6 +76,14 @@
 
 static const char *pull_twohead, *pull_octopus;
 
+enum ff_type {
+	FF_NO,
+	FF_ALLOW,
+	FF_ONLY
+};
+
+static enum ff_type fast_forward = FF_ALLOW;
+
 static int option_parse_message(const struct option *opt,
 				const char *arg, int unset)
 {
@@ -178,6 +186,13 @@
 	return 0;
 }
 
+static int option_parse_ff_only(const struct option *opt,
+			  const char *arg, int unset)
+{
+	fast_forward = FF_ONLY;
+	return 0;
+}
+
 static struct option builtin_merge_options[] = {
 	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
 		N_("do not show a diffstat at the end of the merge"),
@@ -194,10 +209,10 @@
 		N_("perform a commit if the merge succeeds (default)")),
 	OPT_BOOL('e', "edit", &option_edit,
 		N_("edit message before committing")),
-	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-		N_("allow fast-forward (default)")),
-	OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
-		N_("abort if fast-forward is not possible")),
+	OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
+	{ OPTION_CALLBACK, 0, "ff-only", NULL, NULL,
+		N_("abort if fast-forward is not possible"),
+		PARSE_OPT_NOARG | PARSE_OPT_NONEG, option_parse_ff_only },
 	OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
 	OPT_BOOL(0, "verify-signatures", &verify_signatures,
 		N_("Verify that the named commit has a valid GPG signature")),
@@ -581,10 +596,9 @@
 	else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_config_maybe_bool(k, v);
 		if (0 <= boolval) {
-			allow_fast_forward = boolval;
+			fast_forward = boolval ? FF_ALLOW : FF_NO;
 		} else if (v && !strcmp(v, "only")) {
-			allow_fast_forward = 1;
-			fast_forward_only = 1;
+			fast_forward = FF_ONLY;
 		} /* do not barf on values from future versions of git */
 		return 0;
 	} else if (!strcmp(k, "merge.defaulttoupstream")) {
@@ -863,7 +877,7 @@
 
 	free_commit_list(common);
 	parents = remoteheads;
-	if (!head_subsumed || !allow_fast_forward)
+	if (!head_subsumed || fast_forward == FF_NO)
 		commit_list_insert(head, &parents);
 	strbuf_addch(&merge_msg, '\n');
 	prepare_to_commit(remoteheads);
@@ -889,7 +903,7 @@
 		die_errno(_("Could not open '%s' for writing"), filename);
 	fprintf(fp, "\nConflicts:\n");
 	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
+		const struct cache_entry *ce = active_cache[pos];
 
 		if (ce_stage(ce)) {
 			fprintf(fp, "\t%s\n", ce->name);
@@ -948,7 +962,7 @@
 }
 
 /*
- * Pretend as if the user told us to merge with the tracking
+ * Pretend as if the user told us to merge with the remote-tracking
  * branch we have for the upstream of the current branch
  */
 static int setup_with_upstream(const char ***argv)
@@ -967,7 +981,7 @@
 	args = xcalloc(branch->merge_nr + 1, sizeof(char *));
 	for (i = 0; i < branch->merge_nr; i++) {
 		if (!branch->merge[i]->dst)
-			die(_("No remote tracking branch for %s from %s"),
+			die(_("No remote-tracking branch for %s from %s"),
 			    branch->merge[i]->src, branch->remote_name);
 		args[i] = branch->merge[i]->dst;
 	}
@@ -1008,7 +1022,7 @@
 	if (fd < 0)
 		die_errno(_("Could not open '%s' for writing"), filename);
 	strbuf_reset(&buf);
-	if (!allow_fast_forward)
+	if (fast_forward == FF_NO)
 		strbuf_addf(&buf, "no-ff");
 	if (write_in_full(fd, buf.buf, buf.len) != buf.len)
 		die_errno(_("Could not write to '%s'"), filename);
@@ -1054,7 +1068,8 @@
 	for (i = 0; i < argc; i++) {
 		struct commit *commit = get_merge_parent(argv[i]);
 		if (!commit)
-			die(_("%s - not something we can merge"), argv[i]);
+			help_unknown_ref(argv[i], "merge",
+					 "not something we can merge");
 		remotes = &commit_list_insert(commit, remotes)->next;
 	}
 	*remotes = NULL;
@@ -1156,14 +1171,11 @@
 		show_diffstat = 0;
 
 	if (squash) {
-		if (!allow_fast_forward)
+		if (fast_forward == FF_NO)
 			die(_("You cannot combine --squash with --no-ff."));
 		option_commit = 0;
 	}
 
-	if (!allow_fast_forward && fast_forward_only)
-		die(_("You cannot combine --no-ff with --ff-only."));
-
 	if (!abort_current_merge) {
 		if (!argc) {
 			if (default_to_upstream)
@@ -1205,7 +1217,7 @@
 				"empty head"));
 		if (squash)
 			die(_("Squash commit into empty head not supported yet"));
-		if (!allow_fast_forward)
+		if (fast_forward == FF_NO)
 			die(_("Non-fast-forward commit does not make sense into "
 			    "an empty head"));
 		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
@@ -1293,11 +1305,11 @@
 			    sha1_to_hex(commit->object.sha1));
 		setenv(buf.buf, merge_remote_util(commit)->name, 1);
 		strbuf_reset(&buf);
-		if (!fast_forward_only &&
+		if (fast_forward != FF_ONLY &&
 		    merge_remote_util(commit) &&
 		    merge_remote_util(commit)->obj &&
 		    merge_remote_util(commit)->obj->type == OBJ_TAG)
-			allow_fast_forward = 0;
+			fast_forward = FF_NO;
 	}
 
 	if (option_edit < 0)
@@ -1314,7 +1326,7 @@
 
 	for (i = 0; i < use_strategies_nr; i++) {
 		if (use_strategies[i]->attr & NO_FAST_FORWARD)
-			allow_fast_forward = 0;
+			fast_forward = FF_NO;
 		if (use_strategies[i]->attr & NO_TRIVIAL)
 			allow_trivial = 0;
 	}
@@ -1344,7 +1356,7 @@
 		 */
 		finish_up_to_date("Already up-to-date.");
 		goto done;
-	} else if (allow_fast_forward && !remoteheads->next &&
+	} else if (fast_forward != FF_NO && !remoteheads->next &&
 			!common->next &&
 			!hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
 		/* Again the most common case of merging one remote. */
@@ -1391,7 +1403,7 @@
 		 * only one common.
 		 */
 		refresh_cache(REFRESH_QUIET);
-		if (allow_trivial && !fast_forward_only) {
+		if (allow_trivial && fast_forward != FF_ONLY) {
 			/* See if it is really trivial. */
 			git_committer_info(IDENT_STRICT);
 			printf(_("Trying really trivial in-index merge...\n"));
@@ -1432,7 +1444,7 @@
 		}
 	}
 
-	if (fast_forward_only)
+	if (fast_forward == FF_ONLY)
 		die(_("Not possible to fast-forward, aborting."));
 
 	/* We are going to make a new commit. */
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 6238247..0aaa19e 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -4,6 +4,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "sha1-lookup.h"
 
 #define CUTOFF_DATE_SLOP 86400 /* one day */
 
@@ -82,23 +83,88 @@
 	}
 }
 
+static int subpath_matches(const char *path, const char *filter)
+{
+	const char *subpath = path;
+
+	while (subpath) {
+		if (!fnmatch(filter, subpath, 0))
+			return subpath - path;
+		subpath = strchr(subpath, '/');
+		if (subpath)
+			subpath++;
+	}
+	return -1;
+}
+
+static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
+{
+	if (shorten_unambiguous)
+		refname = shorten_unambiguous_ref(refname, 0);
+	else if (!prefixcmp(refname, "refs/heads/"))
+		refname = refname + 11;
+	else if (!prefixcmp(refname, "refs/"))
+		refname = refname + 5;
+	return refname;
+}
+
 struct name_ref_data {
 	int tags_only;
 	int name_only;
 	const char *ref_filter;
 };
 
+static struct tip_table {
+	struct tip_table_entry {
+		unsigned char sha1[20];
+		const char *refname;
+	} *table;
+	int nr;
+	int alloc;
+	int sorted;
+} tip_table;
+
+static void add_to_tip_table(const unsigned char *sha1, const char *refname,
+			     int shorten_unambiguous)
+{
+	refname = name_ref_abbrev(refname, shorten_unambiguous);
+
+	ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
+	hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
+	tip_table.table[tip_table.nr].refname = xstrdup(refname);
+	tip_table.nr++;
+	tip_table.sorted = 0;
+}
+
+static int tipcmp(const void *a_, const void *b_)
+{
+	const struct tip_table_entry *a = a_, *b = b_;
+	return hashcmp(a->sha1, b->sha1);
+}
+
 static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
 {
 	struct object *o = parse_object(sha1);
 	struct name_ref_data *data = cb_data;
+	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
 
 	if (data->tags_only && prefixcmp(path, "refs/tags/"))
 		return 0;
 
-	if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
-		return 0;
+	if (data->ref_filter) {
+		switch (subpath_matches(path, data->ref_filter)) {
+		case -1: /* did not match */
+			return 0;
+		case 0:  /* matched fully */
+			break;
+		default: /* matched subpath */
+			can_abbreviate_output = 1;
+			break;
+		}
+	}
+
+	add_to_tip_table(sha1, path, can_abbreviate_output);
 
 	while (o && o->type == OBJ_TAG) {
 		struct tag *t = (struct tag *) o;
@@ -110,20 +176,38 @@
 	if (o && o->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)o;
 
-		if (!prefixcmp(path, "refs/heads/"))
-			path = path + 11;
-		else if (data->tags_only
-		    && data->name_only
-		    && !prefixcmp(path, "refs/tags/"))
-			path = path + 10;
-		else if (!prefixcmp(path, "refs/"))
-			path = path + 5;
-
+		path = name_ref_abbrev(path, can_abbreviate_output);
 		name_rev(commit, xstrdup(path), 0, 0, deref);
 	}
 	return 0;
 }
 
+static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
+{
+	struct tip_table_entry *table = table_;
+	return table[ix].sha1;
+}
+
+static const char *get_exact_ref_match(const struct object *o)
+{
+	int found;
+
+	if (!tip_table.table || !tip_table.nr)
+		return NULL;
+
+	if (!tip_table.sorted) {
+		qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table),
+		      tipcmp);
+		tip_table.sorted = 1;
+	}
+
+	found = sha1_pos(o->sha1, tip_table.table, tip_table.nr,
+			 nth_tip_table_ent);
+	if (0 <= found)
+		return tip_table.table[found].refname;
+	return NULL;
+}
+
 /* returns a static buffer */
 static const char *get_rev_name(const struct object *o)
 {
@@ -132,7 +216,7 @@
 	struct commit *c;
 
 	if (o->type != OBJ_COMMIT)
-		return NULL;
+		return get_exact_ref_match(o);
 	c = (struct commit *) o;
 	n = c->util;
 	if (!n)
@@ -223,7 +307,7 @@
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
 	struct object_array revs = OBJECT_ARRAY_INIT;
-	int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
+	int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
 	struct name_ref_data data = { 0, 0, NULL };
 	struct option opts[] = {
 		OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
@@ -236,6 +320,12 @@
 		OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")),
 		OPT_BOOLEAN(0, "always",     &always,
 			   N_("show abbreviated commit object as fallback")),
+		{
+			/* A Hidden OPT_BOOL */
+			OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
+			N_("dereference tags in the input (internal use)"),
+			PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
+		},
 		OPT_END(),
 	};
 
@@ -250,7 +340,7 @@
 
 	for (; argc; argc--, argv++) {
 		unsigned char sha1[20];
-		struct object *o;
+		struct object *object;
 		struct commit *commit;
 
 		if (get_sha1(*argv, sha1)) {
@@ -259,17 +349,34 @@
 			continue;
 		}
 
-		o = deref_tag(parse_object(sha1), *argv, 0);
-		if (!o || o->type != OBJ_COMMIT) {
-			fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+		commit = NULL;
+		object = parse_object(sha1);
+		if (object) {
+			struct object *peeled = deref_tag(object, *argv, 0);
+			if (peeled && peeled->type == OBJ_COMMIT)
+				commit = (struct commit *)peeled;
+		}
+
+		if (!object) {
+			fprintf(stderr, "Could not get object for %s. Skipping.\n",
 					*argv);
 			continue;
 		}
 
-		commit = (struct commit *)o;
-		if (cutoff > commit->date)
-			cutoff = commit->date;
-		add_object_array((struct object *)commit, *argv, &revs);
+		if (commit) {
+			if (cutoff > commit->date)
+				cutoff = commit->date;
+		}
+
+		if (peel_tag) {
+			if (!commit) {
+				fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+					*argv);
+				continue;
+			}
+			object = (struct object *)commit;
+		}
+		add_object_array(object, *argv, &revs);
 	}
 
 	if (cutoff)
diff --git a/builtin/notes.c b/builtin/notes.c
index 57748a6..e4100c4 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -4,7 +4,7 @@
  * Copyright (c) 2010 Johan Herland <johan@herland.net>
  *
  * Based on git-notes.sh by Johannes Schindelin,
- * and builtin-tag.c by Kristian Høgsberg and Carlos Rica.
+ * and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
  */
 
 #include "cache.h"
@@ -18,9 +18,7 @@
 #include "parse-options.h"
 #include "string-list.h"
 #include "notes-merge.h"
-
-static void commit_notes(struct notes_tree *t, const char *msg);
-static combine_notes_fn parse_combine_notes_fn(const char *v);
+#include "notes-utils.h"
 
 static const char * const git_notes_usage[] = {
 	N_("git notes [--ref <notes_ref>] [list [<object>]]"),
@@ -287,139 +285,13 @@
 	return parse_reuse_arg(opt, arg, unset);
 }
 
-static void commit_notes(struct notes_tree *t, const char *msg)
-{
-	struct strbuf buf = STRBUF_INIT;
-	unsigned char commit_sha1[20];
-
-	if (!t)
-		t = &default_notes_tree;
-	if (!t->initialized || !t->ref || !*t->ref)
-		die(_("Cannot commit uninitialized/unreferenced notes tree"));
-	if (!t->dirty)
-		return; /* don't have to commit an unchanged tree */
-
-	/* Prepare commit message and reflog message */
-	strbuf_addstr(&buf, msg);
-	if (buf.buf[buf.len - 1] != '\n')
-		strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
-
-	create_notes_commit(t, NULL, &buf, commit_sha1);
-	strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
-	update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
-
-	strbuf_release(&buf);
-}
-
-static combine_notes_fn parse_combine_notes_fn(const char *v)
-{
-	if (!strcasecmp(v, "overwrite"))
-		return combine_notes_overwrite;
-	else if (!strcasecmp(v, "ignore"))
-		return combine_notes_ignore;
-	else if (!strcasecmp(v, "concatenate"))
-		return combine_notes_concatenate;
-	else if (!strcasecmp(v, "cat_sort_uniq"))
-		return combine_notes_cat_sort_uniq;
-	else
-		return NULL;
-}
-
-static int notes_rewrite_config(const char *k, const char *v, void *cb)
-{
-	struct notes_rewrite_cfg *c = cb;
-	if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
-		c->enabled = git_config_bool(k, v);
-		return 0;
-	} else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
-		if (!v)
-			config_error_nonbool(k);
-		c->combine = parse_combine_notes_fn(v);
-		if (!c->combine) {
-			error(_("Bad notes.rewriteMode value: '%s'"), v);
-			return 1;
-		}
-		return 0;
-	} else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
-		/* note that a refs/ prefix is implied in the
-		 * underlying for_each_glob_ref */
-		if (!prefixcmp(v, "refs/notes/"))
-			string_list_add_refs_by_glob(c->refs, v);
-		else
-			warning(_("Refusing to rewrite notes in %s"
-				" (outside of refs/notes/)"), v);
-		return 0;
-	}
-
-	return 0;
-}
-
-
-struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
-{
-	struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
-	const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
-	const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
-	c->cmd = cmd;
-	c->enabled = 1;
-	c->combine = combine_notes_concatenate;
-	c->refs = xcalloc(1, sizeof(struct string_list));
-	c->refs->strdup_strings = 1;
-	c->refs_from_env = 0;
-	c->mode_from_env = 0;
-	if (rewrite_mode_env) {
-		c->mode_from_env = 1;
-		c->combine = parse_combine_notes_fn(rewrite_mode_env);
-		if (!c->combine)
-			/* TRANSLATORS: The first %s is the name of the
-			   environment variable, the second %s is its value */
-			error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
-					rewrite_mode_env);
-	}
-	if (rewrite_refs_env) {
-		c->refs_from_env = 1;
-		string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
-	}
-	git_config(notes_rewrite_config, c);
-	if (!c->enabled || !c->refs->nr) {
-		string_list_clear(c->refs, 0);
-		free(c->refs);
-		free(c);
-		return NULL;
-	}
-	c->trees = load_notes_trees(c->refs);
-	string_list_clear(c->refs, 0);
-	free(c->refs);
-	return c;
-}
-
-int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
-			  const unsigned char *from_obj, const unsigned char *to_obj)
-{
-	int ret = 0;
-	int i;
-	for (i = 0; c->trees[i]; i++)
-		ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
-	return ret;
-}
-
-void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
-{
-	int i;
-	for (i = 0; c->trees[i]; i++) {
-		commit_notes(c->trees[i], "Notes added by 'git notes copy'");
-		free_notes(c->trees[i]);
-	}
-	free(c->trees);
-	free(c);
-}
-
 static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct notes_rewrite_cfg *c = NULL;
 	struct notes_tree *t = NULL;
 	int ret = 0;
+	const char *msg = "Notes added by 'git notes copy'";
 
 	if (rewrite_cmd) {
 		c = init_copy_notes_for_rewrite(rewrite_cmd);
@@ -461,10 +333,10 @@
 	}
 
 	if (!rewrite_cmd) {
-		commit_notes(t, "Notes added by 'git notes copy'");
+		commit_notes(t, msg);
 		free_notes(t);
 	} else {
-		finish_copy_notes_for_rewrite(c);
+		finish_copy_notes_for_rewrite(c, msg);
 	}
 	return ret;
 }
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index b5a0f88..b20b1ec 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -1,6 +1,6 @@
 #include "builtin.h"
 #include "parse-options.h"
-#include "pack-refs.h"
+#include "refs.h"
 
 static char const * const pack_refs_usage[] = {
 	N_("git pack-refs [options]"),
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index 83382c1..fa6ce42 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -8,9 +8,6 @@
 	NULL
 };
 
-#define DRY_RUN 01
-#define VERBOSE 02
-
 static struct progress *progress;
 
 static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
@@ -29,7 +26,7 @@
 		if (!has_sha1_pack(sha1))
 			continue;
 		memcpy(pathname + len, de->d_name, 38);
-		if (opts & DRY_RUN)
+		if (opts & PRUNE_PACKED_DRY_RUN)
 			printf("rm -f %s\n", pathname);
 		else
 			unlink_or_warn(pathname);
@@ -44,7 +41,7 @@
 	const char *dir = get_object_directory();
 	int len = strlen(dir);
 
-	if (opts == VERBOSE)
+	if (opts & PRUNE_PACKED_VERBOSE)
 		progress = start_progress_delay("Removing duplicate objects",
 			256, 95, 2);
 
@@ -71,10 +68,12 @@
 
 int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
-	int opts = isatty(2) ? VERBOSE : 0;
+	int opts = isatty(2) ? PRUNE_PACKED_VERBOSE : 0;
 	const struct option prune_packed_options[] = {
-		OPT_BIT('n', "dry-run", &opts, N_("dry run"), DRY_RUN),
-		OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"), VERBOSE),
+		OPT_BIT('n', "dry-run", &opts, N_("dry run"),
+			PRUNE_PACKED_DRY_RUN),
+		OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"),
+			   PRUNE_PACKED_VERBOSE),
 		OPT_END()
 	};
 
diff --git a/builtin/prune.c b/builtin/prune.c
index 85843d4..6366917 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -132,8 +132,8 @@
 		OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
 		OPT__VERBOSE(&verbose, N_("report pruned objects")),
 		OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
-		OPT_DATE(0, "expire", &expire,
-			 N_("expire objects older than <time>")),
+		OPT_EXPIRY_DATE(0, "expire", &expire,
+				N_("expire objects older than <time>")),
 		OPT_END()
 	};
 	char *s;
@@ -165,7 +165,7 @@
 	stop_progress(&progress);
 	prune_object_dir(get_object_directory());
 
-	prune_packed_objects(show_only);
+	prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
 	remove_temporary_files(get_object_directory());
 	s = mkpathdup("%s/pack", get_object_directory());
 	remove_temporary_files(s);
diff --git a/builtin/push.c b/builtin/push.c
index 909c34d..6d36c24 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -113,17 +113,20 @@
 	    remote->name, branch->name, advice_maybe);
 }
 
-static void setup_push_upstream(struct remote *remote, int simple)
+static const char message_detached_head_die[] =
+	N_("You are not currently on a branch.\n"
+	   "To push the history leading to the current (detached HEAD)\n"
+	   "state now, use\n"
+	   "\n"
+	   "    git push %s HEAD:<name-of-remote-branch>\n");
+
+static void setup_push_upstream(struct remote *remote, struct branch *branch,
+				int triangular)
 {
 	struct strbuf refspec = STRBUF_INIT;
-	struct branch *branch = branch_get(NULL);
+
 	if (!branch)
-		die(_("You are not currently on a branch.\n"
-		    "To push the history leading to the current (detached HEAD)\n"
-		    "state now, use\n"
-		    "\n"
-		    "    git push %s HEAD:<name-of-remote-branch>\n"),
-		    remote->name);
+		die(_(message_detached_head_die), remote->name);
 	if (!branch->merge_nr || !branch->merge || !branch->remote_name)
 		die(_("The current branch %s has no upstream branch.\n"
 		    "To push the current branch and set the remote as upstream, use\n"
@@ -135,18 +138,29 @@
 	if (branch->merge_nr != 1)
 		die(_("The current branch %s has multiple upstream branches, "
 		    "refusing to push."), branch->name);
-	if (strcmp(branch->remote_name, remote->name))
+	if (triangular)
 		die(_("You are pushing to remote '%s', which is not the upstream of\n"
 		      "your current branch '%s', without telling me what to push\n"
 		      "to update which remote branch."),
 		    remote->name, branch->name);
-	if (simple && strcmp(branch->refname, branch->merge[0]->src))
-		die_push_simple(branch, remote);
+
+	if (push_default == PUSH_DEFAULT_SIMPLE) {
+		/* Additional safety */
+		if (strcmp(branch->refname, branch->merge[0]->src))
+			die_push_simple(branch, remote);
+	}
 
 	strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
 	add_refspec(refspec.buf);
 }
 
+static void setup_push_current(struct remote *remote, struct branch *branch)
+{
+	if (!branch)
+		die(_(message_detached_head_die), remote->name);
+	add_refspec(branch->name);
+}
+
 static char warn_unspecified_push_default_msg[] =
 N_("push.default is unset; its implicit value is changing in\n"
    "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -171,8 +185,17 @@
 	warning("%s\n", _(warn_unspecified_push_default_msg));
 }
 
+static int is_workflow_triangular(struct remote *remote)
+{
+	struct remote *fetch_remote = remote_get(NULL);
+	return (fetch_remote && fetch_remote != remote);
+}
+
 static void setup_default_push_refspecs(struct remote *remote)
 {
+	struct branch *branch = branch_get(NULL);
+	int triangular = is_workflow_triangular(remote);
+
 	switch (push_default) {
 	default:
 	case PUSH_DEFAULT_UNSPECIFIED:
@@ -184,15 +207,18 @@
 		break;
 
 	case PUSH_DEFAULT_SIMPLE:
-		setup_push_upstream(remote, 1);
+		if (triangular)
+			setup_push_current(remote, branch);
+		else
+			setup_push_upstream(remote, branch, triangular);
 		break;
 
 	case PUSH_DEFAULT_UPSTREAM:
-		setup_push_upstream(remote, 0);
+		setup_push_upstream(remote, branch, triangular);
 		break;
 
 	case PUSH_DEFAULT_CURRENT:
-		add_refspec("HEAD");
+		setup_push_current(remote, branch);
 		break;
 
 	case PUSH_DEFAULT_NOTHING:
@@ -204,8 +230,8 @@
 
 static const char message_advice_pull_before_push[] =
 	N_("Updates were rejected because the tip of your current branch is behind\n"
-	   "its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-	   "before pushing again.\n"
+	   "its remote counterpart. Integrate the remote changes (e.g.\n"
+	   "'git pull ...') before pushing again.\n"
 	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_use_upstream[] =
@@ -216,15 +242,15 @@
 
 static const char message_advice_checkout_pull_push[] =
 	N_("Updates were rejected because a pushed branch tip is behind its remote\n"
-	   "counterpart. Check out this branch and merge the remote changes\n"
-	   "(e.g. 'git pull') before pushing again.\n"
+	   "counterpart. Check out this branch and integrate the remote changes\n"
+	   "(e.g. 'git pull ...') before pushing again.\n"
 	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_fetch_first[] =
 	N_("Updates were rejected because the remote contains work that you do\n"
 	   "not have locally. This is usually caused by another repository pushing\n"
-	   "to the same ref. You may want to first merge the remote changes (e.g.,\n"
-	   "'git pull') before pushing again.\n"
+	   "to the same ref. You may want to first integrate the remote changes\n"
+	   "(e.g., 'git pull ...') before pushing again.\n"
 	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_already_exists[] =
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 042ac1b..0f5d7fe 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -66,7 +66,7 @@
 	return 0;
 }
 
-static void debug_stage(const char *label, struct cache_entry *ce,
+static void debug_stage(const char *label, const struct cache_entry *ce,
 			struct unpack_trees_options *o)
 {
 	printf("%s ", label);
@@ -80,7 +80,8 @@
 		       sha1_to_hex(ce->sha1));
 }
 
-static int debug_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+static int debug_merge(const struct cache_entry * const *stages,
+		       struct unpack_trees_options *o)
 {
 	int i;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 72a0af7..54184b3 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -496,11 +496,9 @@
 {
 	if (!value)
 		return config_error_nonbool(var);
-	if (!strcmp(value, "never") || !strcmp(value, "false")) {
-		*expire = 0;
-		return 0;
-	}
-	*expire = approxidate(value);
+	if (parse_expiry_date(value, expire))
+		return error(_("%s' for '%s' is not a valid timestamp"),
+			     value, var);
 	return 0;
 }
 
@@ -614,11 +612,13 @@
 		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
 			cb.dry_run = 1;
 		else if (!prefixcmp(arg, "--expire=")) {
-			cb.expire_total = approxidate(arg + 9);
+			if (parse_expiry_date(arg + 9, &cb.expire_total))
+				die(_("'%s' is not a valid timestamp"), arg);
 			explicit_expiry |= EXPIRE_TOTAL;
 		}
 		else if (!prefixcmp(arg, "--expire-unreachable=")) {
-			cb.expire_unreachable = approxidate(arg + 21);
+			if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
+				die(_("'%s' is not a valid timestamp"), arg);
 			explicit_expiry |= EXPIRE_UNREACH;
 		}
 		else if (!strcmp(arg, "--stale-fix"))
diff --git a/builtin/replace.c b/builtin/replace.c
index 398ccd5..59d3115 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
  *
- * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * Based on builtin/tag.c by Kristian Høgsberg <krh@redhat.com>
  * and Carlos Rica <jasampler@gmail.com> that was itself based on
  * git-tag.sh and mktag.c by Linus Torvalds.
  */
diff --git a/builtin/reset.c b/builtin/reset.c
index 6032131..afa6e02 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -93,10 +93,12 @@
 static void print_new_head_line(struct commit *commit)
 {
 	const char *hex, *body;
+	char *msg;
 
 	hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
 	printf(_("HEAD is now at %s"), hex);
-	body = strstr(commit->buffer, "\n\n");
+	msg = logmsg_reencode(commit, NULL, get_log_output_encoding());
+	body = strstr(msg, "\n\n");
 	if (body) {
 		const char *eol;
 		size_t len;
@@ -107,6 +109,7 @@
 	}
 	else
 		printf("\n");
+	logmsg_free(msg, commit);
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 67701be..a5ec30d 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -111,6 +111,7 @@
 		ctx.date_mode = revs->date_mode;
 		ctx.date_mode_explicit = revs->date_mode_explicit;
 		ctx.fmt = revs->commit_format;
+		ctx.output_encoding = get_log_output_encoding();
 		pretty_print_commit(&ctx, commit, &buf);
 		if (revs->graph) {
 			if (buf.len) {
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index f267a1d..de894c7 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -212,11 +212,17 @@
 	show(buffer);
 }
 
-static int show_file(const char *arg)
+static int show_file(const char *arg, int output_prefix)
 {
 	show_default();
 	if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
-		show(arg);
+		if (output_prefix) {
+			const char *prefix = startup_info->prefix;
+			show(prefix_filename(prefix,
+					     prefix ? strlen(prefix) : 0,
+					     arg));
+		} else
+			show(arg);
 		return 1;
 	}
 	return 0;
@@ -470,6 +476,7 @@
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
 	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+	int output_prefix = 0;
 	unsigned char sha1[20];
 	const char *name = NULL;
 
@@ -503,7 +510,7 @@
 		const char *arg = argv[i];
 
 		if (as_is) {
-			if (show_file(arg) && as_is < 2)
+			if (show_file(arg, output_prefix) && as_is < 2)
 				verify_filename(prefix, arg, 0);
 			continue;
 		}
@@ -527,7 +534,7 @@
 				as_is = 2;
 				/* Pass on the "--" if we show anything but files.. */
 				if (filter & (DO_FLAGS | DO_REVS))
-					show_file(arg);
+					show_file(arg, 0);
 				continue;
 			}
 			if (!strcmp(arg, "--default")) {
@@ -535,6 +542,13 @@
 				i++;
 				continue;
 			}
+			if (!strcmp(arg, "--prefix")) {
+				prefix = argv[i+1];
+				startup_info->prefix = prefix;
+				output_prefix = 1;
+				i++;
+				continue;
+			}
 			if (!strcmp(arg, "--revs-only")) {
 				filter &= ~DO_NOREV;
 				continue;
@@ -754,7 +768,7 @@
 		if (verify)
 			die_no_single_rev(quiet);
 		as_is = 1;
-		if (!show_file(arg))
+		if (!show_file(arg, output_prefix))
 			continue;
 		verify_filename(prefix, arg, 1);
 	}
diff --git a/builtin/revert.c b/builtin/revert.c
index 0401fdb..1d2648b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -54,6 +54,7 @@
 	return 0;
 }
 
+LAST_ARG_MUST_BE_NULL
 static void verify_opt_compatible(const char *me, const char *base_opt, ...)
 {
 	const char *this_opt;
@@ -70,6 +71,7 @@
 		die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
 }
 
+LAST_ARG_MUST_BE_NULL
 static void verify_opt_mutually_compatible(const char *me, ...)
 {
 	const char *opt1, *opt2 = NULL;
diff --git a/builtin/rm.c b/builtin/rm.c
index 7b91d52..18916e0 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -9,6 +9,7 @@
 #include "cache-tree.h"
 #include "tree-walk.h"
 #include "parse-options.h"
+#include "string-list.h"
 #include "submodule.h"
 
 static const char * const builtin_rm_usage[] = {
@@ -36,15 +37,37 @@
 	return -1;
 }
 
+static void print_error_files(struct string_list *files_list,
+			      const char *main_msg,
+			      const char *hints_msg,
+			      int *errs)
+{
+	if (files_list->nr) {
+		int i;
+		struct strbuf err_msg = STRBUF_INIT;
+
+		strbuf_addstr(&err_msg, main_msg);
+		for (i = 0; i < files_list->nr; i++)
+			strbuf_addf(&err_msg,
+				    "\n    %s",
+				    files_list->items[i].string);
+		if (advice_rm_hints)
+			strbuf_addstr(&err_msg, hints_msg);
+		*errs = error("%s", err_msg.buf);
+		strbuf_release(&err_msg);
+	}
+}
+
 static int check_submodules_use_gitfiles(void)
 {
 	int i;
 	int errs = 0;
+	struct string_list files = STRING_LIST_INIT_NODUP;
 
 	for (i = 0; i < list.nr; i++) {
 		const char *name = list.entry[i].name;
 		int pos;
-		struct cache_entry *ce;
+		const struct cache_entry *ce;
 		struct stat st;
 
 		pos = cache_name_pos(name, strlen(name));
@@ -61,11 +84,18 @@
 			continue;
 
 		if (!submodule_uses_gitfile(name))
-			errs = error(_("submodule '%s' (or one of its nested "
-				     "submodules) uses a .git directory\n"
-				     "(use 'rm -rf' if you really want to remove "
-				     "it including all of its history)"), name);
+			string_list_append(&files, name);
 	}
+	print_error_files(&files,
+			  Q_("the following submodule (or one of its nested "
+			     "submodules)\n uses a .git directory:",
+			     "the following submodules (or one of its nested "
+			     "submodules)\n use a .git directory:",
+			     files.nr),
+			  _("\n(use 'rm -rf' if you really want to remove "
+			    "it including all of its history)"),
+			  &errs);
+	string_list_clear(&files, 0);
 
 	return errs;
 }
@@ -81,12 +111,16 @@
 	 */
 	int i, no_head;
 	int errs = 0;
+	struct string_list files_staged = STRING_LIST_INIT_NODUP;
+	struct string_list files_cached = STRING_LIST_INIT_NODUP;
+	struct string_list files_submodule = STRING_LIST_INIT_NODUP;
+	struct string_list files_local = STRING_LIST_INIT_NODUP;
 
 	no_head = is_null_sha1(head);
 	for (i = 0; i < list.nr; i++) {
 		struct stat st;
 		int pos;
-		struct cache_entry *ce;
+		const struct cache_entry *ce;
 		const char *name = list.entry[i].name;
 		unsigned char sha1[20];
 		unsigned mode;
@@ -171,29 +205,58 @@
 		 */
 		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);
+				string_list_append(&files_staged, name);
 		}
 		else if (!index_only) {
 			if (staged_changes)
-				errs = error(_("'%s' has changes staged in the index\n"
-					     "(use --cached to keep the file, "
-					     "or -f to force removal)"), name);
+				string_list_append(&files_cached, name);
 			if (local_changes) {
 				if (S_ISGITLINK(ce->ce_mode) &&
-				    !submodule_uses_gitfile(name)) {
-					errs = error(_("submodule '%s' (or one of its nested "
-						     "submodules) uses a .git directory\n"
-						     "(use 'rm -rf' if you really want to remove "
-						     "it including all of its history)"), name);
-				} else
-					errs = error(_("'%s' has local modifications\n"
-						     "(use --cached to keep the file, "
-						     "or -f to force removal)"), name);
+				    !submodule_uses_gitfile(name))
+					string_list_append(&files_submodule, name);
+				else
+					string_list_append(&files_local, name);
 			}
 		}
 	}
+	print_error_files(&files_staged,
+			  Q_("the following file has staged content different "
+			     "from both the\nfile and the HEAD:",
+			     "the following files have staged content different"
+			     " from both the\nfile and the HEAD:",
+			     files_staged.nr),
+			  _("\n(use -f to force removal)"),
+			  &errs);
+	string_list_clear(&files_staged, 0);
+	print_error_files(&files_cached,
+			  Q_("the following file has changes "
+			     "staged in the index:",
+			     "the following files have changes "
+			     "staged in the index:", files_cached.nr),
+			  _("\n(use --cached to keep the file,"
+			    " or -f to force removal)"),
+			  &errs);
+	string_list_clear(&files_cached, 0);
+	print_error_files(&files_submodule,
+			  Q_("the following submodule (or one of its nested "
+			     "submodule)\nuses a .git directory:",
+			     "the following submodules (or one of its nested "
+			     "submodule)\nuse a .git directory:",
+			     files_submodule.nr),
+			  _("\n(use 'rm -rf' if you really "
+			    "want to remove it including all "
+			    "of its history)"),
+			  &errs);
+	string_list_clear(&files_submodule, 0);
+	print_error_files(&files_local,
+			  Q_("the following file has local modifications:",
+			     "the following files have local modifications:",
+			     files_local.nr),
+			  _("\n(use --cached to keep the file,"
+			    " or -f to force removal)"),
+			  &errs);
+	string_list_clear(&files_local, 0);
+
 	return errs;
 }
 
@@ -258,7 +321,7 @@
 	seen = xcalloc(i, 1);
 
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
 			continue;
 		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 1fd6f8a..1434f8f 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -137,6 +137,7 @@
 		ctx.subject = "";
 		ctx.after_subject = "";
 		ctx.date_mode = DATE_NORMAL;
+		ctx.output_encoding = get_log_output_encoding();
 		pretty_print_commit(&ctx, commit, &ufbuf);
 		buffer = ufbuf.buf;
 	} else if (*buffer) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 90fc6b1..9788eb1 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -630,7 +630,7 @@
 	int num_rev, i, extra = 0;
 	int all_heads = 0, all_remotes = 0;
 	int all_mask, all_revs;
-	int lifo = 1;
+	enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER;
 	char head[128];
 	const char *head_p;
 	int head_len;
@@ -665,15 +665,17 @@
 			    N_("show possible merge bases")),
 		OPT_BOOLEAN(0, "independent", &independent,
 			    N_("show refs unreachable from any other ref")),
-		OPT_BOOLEAN(0, "topo-order", &lifo,
-			    N_("show commits in topological order")),
+		OPT_SET_INT(0, "topo-order", &sort_order,
+			    N_("show commits in topological order"),
+			    REV_SORT_IN_GRAPH_ORDER),
 		OPT_BOOLEAN(0, "topics", &topics,
 			    N_("show only commits not on the first branch")),
 		OPT_SET_INT(0, "sparse", &dense,
 			    N_("show merges reachable from only one tip"), 0),
-		OPT_SET_INT(0, "date-order", &lifo,
-			    N_("show commits where no parent comes before its "
-			       "children"), 0),
+		OPT_SET_INT(0, "date-order", &sort_order,
+			    N_("topologically sort, maintaining date order "
+			       "where possible"),
+			    REV_SORT_BY_COMMIT_DATE),
 		{ OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
 			    N_("show <n> most recent ref-log entries starting at "
 			       "base"),
@@ -900,7 +902,7 @@
 		exit(0);
 
 	/* Sort topologically */
-	sort_in_topological_order(&seen, lifo);
+	sort_in_topological_order(&seen, sort_order);
 
 	/* Give names to commits */
 	if (!sha1_name && !no_name)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 8d9b76a..87806ad 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,6 +31,9 @@
 	const char *hex;
 	unsigned char peeled[20];
 
+	if (show_head && !strcmp(refname, "HEAD"))
+		goto match;
+
 	if (tags_only || heads_only) {
 		int match;
 
@@ -103,7 +106,7 @@
  */
 static int exclude_existing(const char *match)
 {
-	static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+	static struct string_list existing_refs = STRING_LIST_INIT_DUP;
 	char buf[1024];
 	int matchlen = match ? strlen(match) : 0;
 
@@ -167,9 +170,10 @@
 	OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, "
 		    "requires exact ref path")),
 	{ OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
-	  N_("show the HEAD reference"),
+	  N_("show the HEAD reference, even if it would be filtered out"),
 	  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-	OPT_BOOLEAN(0, "head", &show_head, N_("show the HEAD reference")),
+	OPT_BOOLEAN(0, "head", &show_head,
+	  N_("show the HEAD reference, even if it would be filtered out")),
 	OPT_BOOLEAN('d', "dereference", &deref_tags,
 		    N_("dereference tags into object IDs")),
 	{ OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 5c7762e..c317981 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -83,7 +83,7 @@
 	return error("lstat(\"%s\"): %s", path, strerror(errno));
 }
 
-static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
+static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
 {
 	int option, size;
 	struct cache_entry *ce;
@@ -142,7 +142,7 @@
 
 	/* Exact match: file or existing gitlink */
 	if (pos >= 0) {
-		struct cache_entry *ce = active_cache[pos];
+		const struct cache_entry *ce = active_cache[pos];
 		if (S_ISGITLINK(ce->ce_mode)) {
 
 			/* Do nothing to the index if there is no HEAD! */
@@ -158,7 +158,7 @@
 	/* Inexact match: is there perhaps a subdirectory match? */
 	pos = -pos-1;
 	while (pos < active_nr) {
-		struct cache_entry *ce = active_cache[pos++];
+		const struct cache_entry *ce = active_cache[pos++];
 
 		if (strncmp(ce->name, path, len))
 			break;
@@ -183,7 +183,7 @@
 {
 	int pos, len;
 	struct stat st;
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 
 	len = strlen(path);
 	if (has_symlink_leading_path(path, len))
@@ -448,7 +448,7 @@
 		/* already merged */
 		pos = unmerge_cache_entry_at(pos);
 		if (pos < active_nr) {
-			struct cache_entry *ce = active_cache[pos];
+			const struct cache_entry *ce = active_cache[pos];
 			if (ce_stage(ce) &&
 			    ce_namelen(ce) == namelen &&
 			    !memcmp(ce->name, path, namelen))
@@ -462,7 +462,7 @@
 		 */
 		pos = -pos-1;
 		if (pos < active_nr) {
-			struct cache_entry *ce = active_cache[pos];
+			const struct cache_entry *ce = active_cache[pos];
 			if (ce_namelen(ce) == namelen &&
 			    !memcmp(ce->name, path, namelen)) {
 				fprintf(stderr,
@@ -558,7 +558,7 @@
 		has_head = 0;
  redo:
 	for (pos = 0; pos < active_nr; pos++) {
-		struct cache_entry *ce = active_cache[pos];
+		const struct cache_entry *ce = active_cache[pos];
 		struct cache_entry *old = NULL;
 		int save_nr;
 
diff --git a/bundle.c b/bundle.c
index 4b0e5cd..3d64311 100644
--- a/bundle.c
+++ b/bundle.c
@@ -281,7 +281,7 @@
 			if (!get_sha1_hex(buf.buf + 1, sha1)) {
 				struct object *object = parse_object_or_die(sha1, buf.buf);
 				object->flags |= UNINTERESTING;
-				add_pending_object(&revs, object, xstrdup(buf.buf));
+				add_pending_object(&revs, object, buf.buf);
 			}
 		} else if (!get_sha1_hex(buf.buf, sha1)) {
 			struct object *object = parse_object_or_die(sha1, buf.buf);
diff --git a/cache-tree.c b/cache-tree.c
index 37e4d00..0bbec43 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -149,7 +149,7 @@
 		cache_tree_invalidate_path(down->cache_tree, slash + 1);
 }
 
-static int verify_cache(struct cache_entry **cache,
+static int verify_cache(const struct cache_entry * const *cache,
 			int entries, int flags)
 {
 	int i, funny;
@@ -158,7 +158,7 @@
 	/* Verify that the tree is merged */
 	funny = 0;
 	for (i = 0; i < entries; i++) {
-		struct cache_entry *ce = cache[i];
+		const struct cache_entry *ce = cache[i];
 		if (ce_stage(ce)) {
 			if (silent)
 				return -1;
@@ -234,7 +234,7 @@
 }
 
 static int update_one(struct cache_tree *it,
-		      struct cache_entry **cache,
+		      const struct cache_entry * const *cache,
 		      int entries,
 		      const char *base,
 		      int baselen,
@@ -265,7 +265,7 @@
 	 */
 	i = 0;
 	while (i < entries) {
-		struct cache_entry *ce = cache[i];
+		const struct cache_entry *ce = cache[i];
 		struct cache_tree_sub *sub;
 		const char *path, *slash;
 		int pathlen, sublen, subcnt, subskip;
@@ -312,7 +312,7 @@
 
 	i = 0;
 	while (i < entries) {
-		struct cache_entry *ce = cache[i];
+		const struct cache_entry *ce = cache[i];
 		struct cache_tree_sub *sub;
 		const char *path, *slash;
 		int pathlen, entlen;
@@ -397,7 +397,7 @@
 }
 
 int cache_tree_update(struct cache_tree *it,
-		      struct cache_entry **cache,
+		      const struct cache_entry * const *cache,
 		      int entries,
 		      int flags)
 {
@@ -599,8 +599,8 @@
 	was_valid = cache_tree_fully_valid(active_cache_tree);
 	if (!was_valid) {
 		if (cache_tree_update(active_cache_tree,
-				      active_cache, active_nr,
-				      flags) < 0)
+				      (const struct cache_entry * const *)active_cache,
+				      active_nr, flags) < 0)
 			return WRITE_TREE_UNMERGED_INDEX;
 		if (0 <= newfd) {
 			if (!write_cache(newfd, active_cache, active_nr) &&
@@ -701,5 +701,6 @@
 	if (!the_index.cache_tree)
 		the_index.cache_tree = cache_tree();
 	return cache_tree_update(the_index.cache_tree,
-				 the_index.cache, the_index.cache_nr, flags);
+				 (const struct cache_entry * const *)the_index.cache,
+				 the_index.cache_nr, flags);
 }
diff --git a/cache-tree.h b/cache-tree.h
index 55d0f59..f1923ad 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -30,7 +30,7 @@
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
 int cache_tree_fully_valid(struct cache_tree *);
-int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int);
+int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int);
 
 int update_main_cache_tree(int);
 
diff --git a/cache.h b/cache.h
index 94ca1ac..3a69638 100644
--- a/cache.h
+++ b/cache.h
@@ -119,15 +119,19 @@
 	unsigned int nsec;
 };
 
+struct stat_data {
+	struct cache_time sd_ctime;
+	struct cache_time sd_mtime;
+	unsigned int sd_dev;
+	unsigned int sd_ino;
+	unsigned int sd_uid;
+	unsigned int sd_gid;
+	unsigned int sd_size;
+};
+
 struct cache_entry {
-	struct cache_time ce_ctime;
-	struct cache_time ce_mtime;
-	unsigned int ce_dev;
-	unsigned int ce_ino;
+	struct stat_data ce_stat_data;
 	unsigned int ce_mode;
-	unsigned int ce_uid;
-	unsigned int ce_gid;
-	unsigned int ce_size;
 	unsigned int ce_flags;
 	unsigned int ce_namelen;
 	unsigned char sha1[20];
@@ -190,7 +194,8 @@
  * another. But we never change the name, or the hash state!
  */
 #define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
-static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
+static inline void copy_cache_entry(struct cache_entry *dst,
+				    const struct cache_entry *src)
 {
 	unsigned int state = dst->ce_flags & CE_STATE_MASK;
 
@@ -222,7 +227,8 @@
 		return S_IFGITLINK;
 	return S_IFREG | ce_permissions(mode);
 }
-static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
+static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+					     unsigned int mode)
 {
 	extern int trust_executable_bit, has_symlinks;
 	if (!has_symlinks && S_ISREG(mode) &&
@@ -419,6 +425,8 @@
 extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
 extern int init_db(const char *template_dir, unsigned int flags);
 
+extern void sanitize_stdfds(void);
+
 #define alloc_nr(x) (((x)+16)*3/2)
 
 /*
@@ -470,7 +478,7 @@
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
-extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
+extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 extern int index_name_is_other(const struct index_state *, const char *, int);
 extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
 
@@ -480,8 +488,8 @@
 #define CE_MATCH_RACY_IS_DIRTY		02
 /* do stat comparison even if CE_SKIP_WORKTREE is true */
 #define CE_MATCH_IGNORE_SKIP_WORKTREE	04
-extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
 #define PATHSPEC_ONESTAR 1	/* the pathspec pattern sastisfies GFNM_ONESTAR */
 
@@ -509,6 +517,21 @@
 #define HASH_FORMAT_CHECK 2
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+extern void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled.  If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+extern int match_stat_data(const struct stat_data *sd, struct stat *st);
+
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY		0x0001	/* ignore_valid */
@@ -554,6 +577,7 @@
 extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
+extern int warn_on_object_refname_ambiguity;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
 extern const char *apply_default_ignorewhitespace;
@@ -737,7 +761,7 @@
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
-const char *relative_path(const char *abs, const char *base);
+const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
@@ -772,9 +796,6 @@
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
 
-/* for development: log offset of pack access */
-extern const char *log_pack_access;
-
 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);
@@ -910,6 +931,7 @@
 			struct strbuf *timebuf);
 int parse_date(const char *date, char *buf, int bufsize);
 int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
+int parse_expiry_date(const char *date, unsigned long *timestamp);
 void datestamp(char *buf, int bufsize);
 #define approxidate(s) approxidate_careful((s), NULL)
 unsigned long approxidate_careful(const char *, int *);
@@ -1024,9 +1046,21 @@
 	unsigned int
 		force:1,
 		forced_update:1,
-		merge:1,
 		deletion:1,
 		matched:1;
+
+	/*
+	 * Order is important here, as we write to FETCH_HEAD
+	 * in numeric order. And the default NOT_FOR_MERGE
+	 * should be 0, so that xcalloc'd structures get it
+	 * by default.
+	 */
+	enum {
+		FETCH_HEAD_MERGE = -1,
+		FETCH_HEAD_NOT_FOR_MERGE = 0,
+		FETCH_HEAD_IGNORE = 1
+	} fetch_head_status;
+
 	enum {
 		REF_STATUS_NONE = 0,
 		REF_STATUS_OK,
@@ -1098,7 +1132,9 @@
 
 struct object_info {
 	/* Request */
+	enum object_type *typep;
 	unsigned long *sizep;
+	unsigned long *disk_sizep;
 
 	/* Response */
 	enum {
@@ -1142,11 +1178,15 @@
 typedef int (*config_fn_t)(const char *, const char *, void *);
 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_from_buf(config_fn_t fn, const char *name,
+			       const char *buf, size_t len, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern int git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
-				   const char *filename, int respect_includes);
+				   const char *filename,
+				   const char *blob_ref,
+				   int respect_includes);
 extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
@@ -1326,4 +1366,31 @@
 
 int sane_execvp(const char *file, char *const argv[]);
 
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+	struct stat_data *sd;
+};
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise.  A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
 #endif /* CACHE_H */
diff --git a/color.c b/color.c
index e8e2681..f672885 100644
--- a/color.c
+++ b/color.c
@@ -1,7 +1,7 @@
 #include "cache.h"
 #include "color.h"
 
-static int git_use_color_default = 0;
+static int git_use_color_default = GIT_COLOR_AUTO;
 int color_stdout_is_tty = -1;
 
 /*
diff --git a/combine-diff.c b/combine-diff.c
index 77d7872..6dc0609 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -518,8 +518,11 @@
 		unsigned long k;
 
 		/* Paint a few lines before the first interesting line. */
-		while (j < i)
-			sline[j++].flag |= mark | no_pre_delete;
+		while (j < i) {
+			if (!(sline[j].flag & mark))
+				sline[j].flag |= no_pre_delete;
+			sline[j++].flag |= mark;
+		}
 
 	again:
 		/* we know up to i is to be included.  where does the
@@ -1302,6 +1305,7 @@
 	int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
 
 	diffopts = *opt;
+	diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts);
 	diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
 	DIFF_OPT_SET(&diffopts, RECURSIVE);
 	DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
@@ -1372,6 +1376,8 @@
 		paths = paths->next;
 		free(tmp);
 	}
+
+	diff_tree_release_paths(&diffopts);
 }
 
 void diff_tree_combined_merge(const struct commit *commit, int dense,
diff --git a/command-list.txt b/command-list.txt
index bf83303..08b04e2 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -13,6 +13,7 @@
 git-cat-file                            plumbinginterrogators
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
+git-check-mailmap                       purehelpers
 git-checkout                            mainporcelain common
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
diff --git a/commit-slab.h b/commit-slab.h
new file mode 100644
index 0000000..7d48163
--- /dev/null
+++ b/commit-slab.h
@@ -0,0 +1,98 @@
+#ifndef COMMIT_SLAB_H
+#define COMMIT_SLAB_H
+
+/*
+ * define_commit_slab(slabname, elemtype) creates boilerplate code to define
+ * a new struct (struct slabname) that is used to associate a piece of data
+ * of elemtype to commits, and a few functions to use that struct.
+ *
+ * After including this header file, using:
+ *
+ * define_commit_slab(indegee, int);
+ *
+ * will let you call the following functions:
+ *
+ * - int *indegree_at(struct indegree *, struct commit *);
+ *
+ *   This function locates the data associated with the given commit in
+ *   the indegree slab, and returns the pointer to it.
+ *
+ * - void init_indegree(struct indegree *);
+ *   void init_indegree_with_stride(struct indegree *, int);
+ *
+ *   Initializes the indegree slab that associates an array of integers
+ *   to each commit. 'stride' specifies how big each array is.  The slab
+ *   that id initialied by the variant without "_with_stride" associates
+ *   each commit with an array of one integer.
+ */
+
+/* allocate ~512kB at once, allowing for malloc overhead */
+#ifndef COMMIT_SLAB_SIZE
+#define COMMIT_SLAB_SIZE (512*1024-32)
+#endif
+
+#define define_commit_slab(slabname, elemtype) 				\
+									\
+struct slabname {							\
+	unsigned slab_size;						\
+	unsigned stride;						\
+	unsigned slab_count;						\
+	elemtype **slab;						\
+};									\
+static int stat_ ##slabname## realloc;					\
+									\
+static void init_ ##slabname## _with_stride(struct slabname *s,		\
+					    unsigned stride)		\
+{									\
+	unsigned int elem_size;						\
+	if (!stride)							\
+		stride = 1;						\
+	s->stride = stride;						\
+	elem_size = sizeof(struct slabname) * stride;			\
+	s->slab_size = COMMIT_SLAB_SIZE / elem_size;			\
+	s->slab_count = 0;						\
+	s->slab = NULL;							\
+}									\
+									\
+static void init_ ##slabname(struct slabname *s)			\
+{									\
+	init_ ##slabname## _with_stride(s, 1);				\
+}									\
+									\
+static void clear_ ##slabname(struct slabname *s)			\
+{									\
+	int i;								\
+	for (i = 0; i < s->slab_count; i++)				\
+		free(s->slab[i]);					\
+	s->slab_count = 0;						\
+	free(s->slab);							\
+	s->slab = NULL;							\
+}									\
+									\
+static elemtype *slabname## _at(struct slabname *s,			\
+				const struct commit *c)			\
+{									\
+	int nth_slab, nth_slot, ix;					\
+									\
+	ix = c->index * s->stride;					\
+	nth_slab = ix / s->slab_size;					\
+	nth_slot = ix % s->slab_size;					\
+									\
+	if (s->slab_count <= nth_slab) {				\
+		int i;							\
+		s->slab = xrealloc(s->slab,				\
+				   (nth_slab + 1) * sizeof(s->slab));	\
+		stat_ ##slabname## realloc++;				\
+		for (i = s->slab_count; i <= nth_slab; i++)		\
+			s->slab[i] = NULL;				\
+		s->slab_count = nth_slab + 1;				\
+	}								\
+	if (!s->slab[nth_slab])						\
+		s->slab[nth_slab] = xcalloc(s->slab_size,		\
+					    sizeof(**s->slab));		\
+	return &s->slab[nth_slab][nth_slot];				\
+}									\
+									\
+static int stat_ ##slabname## realloc
+
+#endif /* COMMIT_SLAB_H */
diff --git a/commit.c b/commit.c
index 888e02a..e5862f6 100644
--- a/commit.c
+++ b/commit.c
@@ -8,12 +8,15 @@
 #include "notes.h"
 #include "gpg-interface.h"
 #include "mergesort.h"
+#include "commit-slab.h"
+#include "prio-queue.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
+static int commit_count;
 
 static struct commit *check_commit(struct object *obj,
 				   const unsigned char *sha1,
@@ -58,8 +61,11 @@
 struct commit *lookup_commit(const unsigned char *sha1)
 {
 	struct object *obj = lookup_object(sha1);
-	if (!obj)
-		return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
+	if (!obj) {
+		struct commit *c = alloc_commit_node();
+		c->index = commit_count++;
+		return create_object(sha1, OBJ_COMMIT, c);
+	}
 	if (!obj->type)
 		obj->type = OBJ_COMMIT;
 	return check_commit(obj, sha1, 0);
@@ -507,32 +513,136 @@
 }
 
 /*
+ * Topological sort support
+ */
+
+/* count number of children that have not been emitted */
+define_commit_slab(indegree_slab, int);
+
+/* record author-date for each commit object */
+define_commit_slab(author_date_slab, unsigned long);
+
+static void record_author_date(struct author_date_slab *author_date,
+			       struct commit *commit)
+{
+	const char *buf, *line_end;
+	char *buffer = NULL;
+	struct ident_split ident;
+	char *date_end;
+	unsigned long date;
+
+	if (!commit->buffer) {
+		unsigned long size;
+		enum object_type type;
+		buffer = read_sha1_file(commit->object.sha1, &type, &size);
+		if (!buffer)
+			return;
+	}
+
+	for (buf = commit->buffer ? commit->buffer : buffer;
+	     buf;
+	     buf = line_end + 1) {
+		line_end = strchrnul(buf, '\n');
+		if (prefixcmp(buf, "author ")) {
+			if (!line_end[0] || line_end[1] == '\n')
+				return; /* end of header */
+			continue;
+		}
+		if (split_ident_line(&ident,
+				     buf + strlen("author "),
+				     line_end - (buf + strlen("author "))) ||
+		    !ident.date_begin || !ident.date_end)
+			goto fail_exit; /* malformed "author" line */
+		break;
+	}
+
+	date = strtoul(ident.date_begin, &date_end, 10);
+	if (date_end != ident.date_end)
+		goto fail_exit; /* malformed date */
+	*(author_date_slab_at(author_date, commit)) = date;
+
+fail_exit:
+	free(buffer);
+}
+
+static int compare_commits_by_author_date(const void *a_, const void *b_,
+					  void *cb_data)
+{
+	const struct commit *a = a_, *b = b_;
+	struct author_date_slab *author_date = cb_data;
+	unsigned long a_date = *(author_date_slab_at(author_date, a));
+	unsigned long b_date = *(author_date_slab_at(author_date, b));
+
+	/* newer commits with larger date first */
+	if (a_date < b_date)
+		return 1;
+	else if (a_date > b_date)
+		return -1;
+	return 0;
+}
+
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+{
+	const struct commit *a = a_, *b = b_;
+	/* newer commits with larger date first */
+	if (a->date < b->date)
+		return 1;
+	else if (a->date > b->date)
+		return -1;
+	return 0;
+}
+
+/*
  * Performs an in-place topological sort on the list supplied.
  */
-void sort_in_topological_order(struct commit_list ** list, int lifo)
+void sort_in_topological_order(struct commit_list **list, enum rev_sort_order sort_order)
 {
 	struct commit_list *next, *orig = *list;
-	struct commit_list *work, **insert;
 	struct commit_list **pptr;
+	struct indegree_slab indegree;
+	struct prio_queue queue;
+	struct commit *commit;
+	struct author_date_slab author_date;
 
 	if (!orig)
 		return;
 	*list = NULL;
 
+	init_indegree_slab(&indegree);
+	memset(&queue, '\0', sizeof(queue));
+
+	switch (sort_order) {
+	default: /* REV_SORT_IN_GRAPH_ORDER */
+		queue.compare = NULL;
+		break;
+	case REV_SORT_BY_COMMIT_DATE:
+		queue.compare = compare_commits_by_commit_date;
+		break;
+	case REV_SORT_BY_AUTHOR_DATE:
+		init_author_date_slab(&author_date);
+		queue.compare = compare_commits_by_author_date;
+		queue.cb_data = &author_date;
+		break;
+	}
+
 	/* Mark them and clear the indegree */
 	for (next = orig; next; next = next->next) {
 		struct commit *commit = next->item;
-		commit->indegree = 1;
+		*(indegree_slab_at(&indegree, commit)) = 1;
+		/* also record the author dates, if needed */
+		if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+			record_author_date(&author_date, commit);
 	}
 
 	/* update the indegree */
 	for (next = orig; next; next = next->next) {
-		struct commit_list * parents = next->item->parents;
+		struct commit_list *parents = next->item->parents;
 		while (parents) {
 			struct commit *parent = parents->item;
+			int *pi = indegree_slab_at(&indegree, parent);
 
-			if (parent->indegree)
-				parent->indegree++;
+			if (*pi)
+				(*pi)++;
 			parents = parents->next;
 		}
 	}
@@ -544,34 +654,33 @@
 	 *
 	 * the tips serve as a starting set for the work queue.
 	 */
-	work = NULL;
-	insert = &work;
 	for (next = orig; next; next = next->next) {
 		struct commit *commit = next->item;
 
-		if (commit->indegree == 1)
-			insert = &commit_list_insert(commit, insert)->next;
+		if (*(indegree_slab_at(&indegree, commit)) == 1)
+			prio_queue_put(&queue, commit);
 	}
 
-	/* process the list in topological order */
-	if (!lifo)
-		commit_list_sort_by_date(&work);
+	/*
+	 * This is unfortunate; the initial tips need to be shown
+	 * in the order given from the revision traversal machinery.
+	 */
+	if (sort_order == REV_SORT_IN_GRAPH_ORDER)
+		prio_queue_reverse(&queue);
+
+	/* We no longer need the commit list */
+	free_commit_list(orig);
 
 	pptr = list;
 	*list = NULL;
-	while (work) {
-		struct commit *commit;
-		struct commit_list *parents, *work_item;
+	while ((commit = prio_queue_get(&queue)) != NULL) {
+		struct commit_list *parents;
 
-		work_item = work;
-		work = work_item->next;
-		work_item->next = NULL;
-
-		commit = work_item->item;
 		for (parents = commit->parents; parents ; parents = parents->next) {
 			struct commit *parent = parents->item;
+			int *pi = indegree_slab_at(&indegree, parent);
 
-			if (!parent->indegree)
+			if (!*pi)
 				continue;
 
 			/*
@@ -579,21 +688,22 @@
 			 * when all their children have been emitted thereby
 			 * guaranteeing topological order.
 			 */
-			if (--parent->indegree == 1) {
-				if (!lifo)
-					commit_list_insert_by_date(parent, &work);
-				else
-					commit_list_insert(parent, &work);
-			}
+			if (--(*pi) == 1)
+				prio_queue_put(&queue, parent);
 		}
 		/*
-		 * work_item is a commit all of whose children
-		 * have already been emitted. we can emit it now.
+		 * all children of commit have already been
+		 * emitted. we can emit it now.
 		 */
-		commit->indegree = 0;
-		*pptr = work_item;
-		pptr = &work_item->next;
+		*(indegree_slab_at(&indegree, commit)) = 0;
+
+		pptr = &commit_list_insert(commit, pptr)->next;
 	}
+
+	clear_indegree_slab(&indegree);
+	clear_prio_queue(&queue);
+	if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+		clear_author_date_slab(&author_date);
 }
 
 /* merge-base stuff */
@@ -1240,10 +1350,15 @@
 static int find_invalid_utf8(const char *buf, int len)
 {
 	int offset = 0;
+	static const unsigned int max_codepoint[] = {
+		0x7f, 0x7ff, 0xffff, 0x10ffff
+	};
 
 	while (len) {
 		unsigned char c = *buf++;
 		int bytes, bad_offset;
+		unsigned int codepoint;
+		unsigned int min_val, max_val;
 
 		len--;
 		offset++;
@@ -1264,24 +1379,48 @@
 			bytes++;
 		}
 
-		/* Must be between 1 and 5 more bytes */
-		if (bytes < 1 || bytes > 5)
+		/*
+		 * Must be between 1 and 3 more bytes.  Longer sequences result in
+		 * codepoints beyond U+10FFFF, which are guaranteed never to exist.
+		 */
+		if (bytes < 1 || 3 < bytes)
 			return bad_offset;
 
 		/* Do we *have* that many bytes? */
 		if (len < bytes)
 			return bad_offset;
 
+		/*
+		 * Place the encoded bits at the bottom of the value and compute the
+		 * valid range.
+		 */
+		codepoint = (c & 0x7f) >> bytes;
+		min_val = max_codepoint[bytes-1] + 1;
+		max_val = max_codepoint[bytes];
+
 		offset += bytes;
 		len -= bytes;
 
 		/* And verify that they are good continuation bytes */
 		do {
+			codepoint <<= 6;
+			codepoint |= *buf & 0x3f;
 			if ((*buf++ & 0xc0) != 0x80)
 				return bad_offset;
 		} while (--bytes);
 
-		/* We could/should check the value and length here too */
+		/* Reject codepoints that are out of range for the sequence length. */
+		if (codepoint < min_val || codepoint > max_val)
+			return bad_offset;
+		/* Surrogates are only for UTF-16 and cannot be encoded in UTF-8. */
+		if ((codepoint & 0x1ff800) == 0xd800)
+			return bad_offset;
+		/* U+xxFFFE and U+xxFFFF are guaranteed non-characters. */
+		if ((codepoint & 0xffffe) == 0xfffe)
+			return bad_offset;
+		/* So are anything in the range U+FDD0..U+FDEF. */
+		if (codepoint >= 0xfdd0 && codepoint <= 0xfdef)
+			return bad_offset;
 	}
 	return -1;
 }
@@ -1291,9 +1430,6 @@
  *
  * If it isn't, it assumes any non-utf8 characters are Latin1,
  * and does the conversion.
- *
- * Fixme: we should probably also disallow overlong forms and
- * invalid characters. But we don't do that currently.
  */
 static int verify_utf8(struct strbuf *buf)
 {
diff --git a/commit.h b/commit.h
index 67bd509..35cc4e2 100644
--- a/commit.h
+++ b/commit.h
@@ -6,6 +6,7 @@
 #include "strbuf.h"
 #include "decorate.h"
 #include "gpg-interface.h"
+#include "string-list.h"
 
 struct commit_list {
 	struct commit *item;
@@ -15,7 +16,7 @@
 struct commit {
 	struct object object;
 	void *util;
-	unsigned int indegree;
+	unsigned int index;
 	unsigned long date;
 	struct commit_list *parents;
 	struct tree *tree;
@@ -79,6 +80,9 @@
 };
 
 struct pretty_print_context {
+	/*
+	 * Callers should tweak these to change the behavior of pp_* functions.
+	 */
 	enum cmit_fmt fmt;
 	int abbrev;
 	const char *subject;
@@ -92,6 +96,15 @@
 	const char *output_encoding;
 	struct string_list *mailmap;
 	int color;
+	struct ident_split *from_ident;
+
+	/*
+	 * Fields below here are manipulated internally by pp_* functions and
+	 * should not be counted on by callers.
+	 */
+
+	/* Manipulated by the pp_* functions internally. */
+	struct string_list in_body_headers;
 };
 
 struct userformat_want {
@@ -111,20 +124,20 @@
 extern void format_commit_message(const struct commit *commit,
 				  const char *format, struct strbuf *sb,
 				  const struct pretty_print_context *context);
-extern void pretty_print_commit(const struct pretty_print_context *pp,
+extern void pretty_print_commit(struct pretty_print_context *pp,
 				const struct commit *commit,
 				struct strbuf *sb);
 extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
 			   struct strbuf *sb);
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
 		  const char *what, struct strbuf *sb,
 		  const char *line, const char *encoding);
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
 		   const char **msg_p,
 		   struct strbuf *sb,
 		   const char *encoding,
 		   int need_8bit_cte);
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
 		  const char **msg_p,
 		  struct strbuf *sb,
 		  int indent);
@@ -142,15 +155,24 @@
 void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
 void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
 
+
+enum rev_sort_order {
+	REV_SORT_IN_GRAPH_ORDER = 0,
+	REV_SORT_BY_COMMIT_DATE,
+	REV_SORT_BY_AUTHOR_DATE
+};
+
 /*
  * Performs an in-place topological sort of list supplied.
  *
  *   invariant of resulting list is:
  *      a reachable from b => ord(b) < ord(a)
- *   in addition, when lifo == 0, commits on parallel tracks are
- *   sorted in the dates order.
+ *   sort_order further specifies:
+ *   REV_SORT_IN_GRAPH_ORDER: try to show a commit on a single-parent
+ *                            chain together.
+ *   REV_SORT_BY_COMMIT_DATE: show eligible commits in committer-date order.
  */
-void sort_in_topological_order(struct commit_list ** list, int lifo);
+void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
 
 struct commit_graft {
 	unsigned char sha1[20];
@@ -176,6 +198,8 @@
 extern int is_repository_shallow(void);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
 		int depth, int shallow_flag, int not_shallow_flag);
+extern void check_shallow_file_for_update(void);
+extern void set_alternate_shallow_file(const char *path);
 
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit *);
@@ -243,4 +267,6 @@
  */
 extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
 
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+
 #endif /* COMMIT_H */
diff --git a/compat/clipped-write.c b/compat/clipped-write.c
new file mode 100644
index 0000000..b8f98ff
--- /dev/null
+++ b/compat/clipped-write.c
@@ -0,0 +1,13 @@
+#include "../git-compat-util.h"
+#undef write
+
+/*
+ * Version of write that will write at most INT_MAX bytes.
+ * Workaround a xnu bug on Mac OS X
+ */
+ssize_t clipped_write(int fildes, const void *buf, size_t nbyte)
+{
+	if (nbyte > INT_MAX)
+		nbyte = INT_MAX;
+	return write(fildes, buf, nbyte);
+}
diff --git a/compat/cygwin.c b/compat/cygwin.c
index 871b41d..91ce5d4 100644
--- a/compat/cygwin.c
+++ b/compat/cygwin.c
@@ -1,14 +1,9 @@
 #define CYGWIN_C
 #define WIN32_LEAN_AND_MEAN
-#ifdef CYGWIN_V15_WIN32API
-#include "../git-compat-util.h"
-#include "win32.h"
-#else
 #include <sys/stat.h>
 #include <sys/errno.h>
 #include "win32.h"
 #include "../git-compat-util.h"
-#endif
 #include "../cache.h" /* to read configuration */
 
 /*
diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c
index 5ef0685..378c467 100644
--- a/compat/fnmatch/fnmatch.c
+++ b/compat/fnmatch/fnmatch.c
@@ -25,6 +25,7 @@
 # define _GNU_SOURCE	1
 #endif
 
+#include <stddef.h>
 #include <errno.h>
 #include <fnmatch.h>
 #include <ctype.h>
@@ -121,7 +122,7 @@
    whose names are inconsistent.  */
 
 # if !defined _LIBC && !defined getenv
-extern char *getenv ();
+extern char *getenv (const char *name);
 # endif
 
 # ifndef errno
diff --git a/compat/mingw.c b/compat/mingw.c
index b673625..bb92c43 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -841,8 +841,8 @@
 	struct pinfo_t *next;
 	pid_t pid;
 	HANDLE proc;
-} pinfo_t;
-struct pinfo_t *pinfo = NULL;
+};
+static struct pinfo_t *pinfo = NULL;
 CRITICAL_SECTION pinfo_cs;
 
 static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
@@ -1253,7 +1253,7 @@
 	else
 		sin->sin_addr.s_addr = INADDR_LOOPBACK;
 	ai->ai_addr = (struct sockaddr *)sin;
-	ai->ai_next = 0;
+	ai->ai_next = NULL;
 	return 0;
 }
 
@@ -1677,14 +1677,16 @@
 #undef signal
 sig_handler_t mingw_signal(int sig, sig_handler_t handler)
 {
-	sig_handler_t old = timer_fn;
+	sig_handler_t old;
 
 	switch (sig) {
 	case SIGALRM:
+		old = timer_fn;
 		timer_fn = handler;
 		break;
 
 	case SIGINT:
+		old = sigint_fn;
 		sigint_fn = handler;
 		break;
 
diff --git a/compat/mingw.h b/compat/mingw.h
index 685cd2c..bd0a88b 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -334,13 +334,20 @@
 void free_environ(char **env);
 
 /*
+ * A critical section used in the implementation of the spawn
+ * functions (mingw_spawnv[p]e()) and waitpid(). Intialised in
+ * the replacement main() macro below.
+ */
+extern CRITICAL_SECTION pinfo_cs;
+
+/*
  * A replacement of main() that ensures that argv[0] has a path
  * and that default fmode and std(in|out|err) are in binary mode
  */
 
 #define main(c,v) dummy_decl_mingw_main(); \
-static int mingw_main(); \
-int main(int argc, const char **argv) \
+static int mingw_main(c,v); \
+int main(int argc, char **argv) \
 { \
 	extern CRITICAL_SECTION pinfo_cs; \
 	_fmode = _O_BINARY; \
diff --git a/compat/nedmalloc/Readme.txt b/compat/nedmalloc/Readme.txt
index e46d8f1..07cbf50 100644
--- a/compat/nedmalloc/Readme.txt
+++ b/compat/nedmalloc/Readme.txt
@@ -97,7 +97,7 @@
 
 v1.04alpha_svn915 7th October 2006:
  * Fixed failure to unlock thread cache list if allocating a new list failed.
-Thanks to Dmitry Chichkov for reporting this. Futher thanks to Aleksey Sanin.
+Thanks to Dmitry Chichkov for reporting this. Further thanks to Aleksey Sanin.
  * Fixed realloc(0, <size>) segfaulting. Thanks to Dmitry Chichkov for
 reporting this.
  * Made config defines #ifndef so they can be overridden by the build system.
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
index 1401a67..ed4f1fa 100644
--- a/compat/nedmalloc/malloc.c.h
+++ b/compat/nedmalloc/malloc.c.h
@@ -484,6 +484,10 @@
 #define DLMALLOC_VERSION 20804
 #endif /* DLMALLOC_VERSION */
 
+#if defined(linux)
+#define _GNU_SOURCE 1
+#endif
+
 #ifndef WIN32
 #ifdef _WIN32
 #define WIN32 1
@@ -1802,7 +1806,7 @@
 
 static MLOCK_T malloc_global_mutex = { 0, 0, 0};
 
-static FORCEINLINE long win32_getcurrentthreadid() {
+static FORCEINLINE long win32_getcurrentthreadid(void) {
 #ifdef _MSC_VER
 #if defined(_M_IX86)
   long *threadstruct=(long *)__readfsdword(0x18);
@@ -3598,8 +3602,8 @@
      and choose its bk node as its replacement.
   2. If x was the last node of its size, but not a leaf node, it must
      be replaced with a leaf node (not merely one with an open left or
-     right), to make sure that lefts and rights of descendents
-     correspond properly to bit masks.  We use the rightmost descendent
+     right), to make sure that lefts and rights of descendants
+     correspond properly to bit masks.  We use the rightmost descendant
      of x.  We could use any other leaf, but this is easy to locate and
      tends to counteract removal of leftmosts elsewhere, and so keeps
      paths shorter than minimally guaranteed.  This doesn't loop much
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
index 91c4e7f..609ebba 100644
--- a/compat/nedmalloc/nedmalloc.c
+++ b/compat/nedmalloc/nedmalloc.c
@@ -159,8 +159,8 @@
 #endif
 int    nedmallopt(int parno, int value) THROWSPEC	{ return nedpmallopt(0, parno, value); }
 int    nedmalloc_trim(size_t pad) THROWSPEC			{ return nedpmalloc_trim(0, pad); }
-void   nedmalloc_stats() THROWSPEC					{ nedpmalloc_stats(0); }
-size_t nedmalloc_footprint() THROWSPEC				{ return nedpmalloc_footprint(0); }
+void   nedmalloc_stats(void) THROWSPEC					{ nedpmalloc_stats(0); }
+size_t nedmalloc_footprint(void) THROWSPEC				{ return nedpmalloc_footprint(0); }
 void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC	{ return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
 void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC	{ return nedpindependent_comalloc(0, elems, sizes, chunks); }
 
diff --git a/compat/poll/poll.c b/compat/poll/poll.c
index 7d226ec..4410310 100644
--- a/compat/poll/poll.c
+++ b/compat/poll/poll.c
@@ -576,7 +576,7 @@
 	{
 	  /* It's a socket.  */
 	  WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
-	  WSAEventSelect ((SOCKET) h, 0, 0);
+	  WSAEventSelect ((SOCKET) h, NULL, 0);
 
 	  /* If we're lucky, WSAEnumNetworkEvents already provided a way
 	     to distinguish FD_READ and FD_ACCEPT; this saves a recv later.  */
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index d0025bd..b2c5d46 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -2617,7 +2617,7 @@
      Build the range expression which starts from START_ELEM, and ends
      at END_ELEM.  The result are written to MBCSET and SBCSET.
      RANGE_ALLOC is the allocated size of mbcset->range_starts, and
-     mbcset->range_ends, is a pointer argument sinse we may
+     mbcset->range_ends, is a pointer argument since we may
      update it.  */
 
 static reg_errcode_t
@@ -2788,7 +2788,7 @@
   const int32_t *symb_table;
   const unsigned char *extra;
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Seek the collating symbol entry correspondings to NAME.
      Return the index of the symbol in the SYMB_TABLE.  */
 
@@ -2892,11 +2892,11 @@
       return UINT_MAX;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Build the range expression which starts from START_ELEM, and ends
      at END_ELEM.  The result are written to MBCSET and SBCSET.
      RANGE_ALLOC is the allocated size of mbcset->range_starts, and
-     mbcset->range_ends, is a pointer argument sinse we may
+     mbcset->range_ends, is a pointer argument since we may
      update it.  */
 
   auto inline reg_errcode_t
@@ -2976,11 +2976,11 @@
       return REG_NOERROR;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Build the collating element which is represented by NAME.
      The result are written to MBCSET and SBCSET.
      COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
-     pointer argument sinse we may update it.  */
+     pointer argument since we may update it.  */
 
   auto inline reg_errcode_t
   __attribute ((always_inline))
@@ -3419,7 +3419,7 @@
      Build the equivalence class which is represented by NAME.
      The result are written to MBCSET and SBCSET.
      EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
-     is a pointer argument sinse we may update it.  */
+     is a pointer argument since we may update it.  */
 
 static reg_errcode_t
 #ifdef RE_ENABLE_I18N
@@ -3515,7 +3515,7 @@
      Build the character class which is represented by NAME.
      The result are written to MBCSET and SBCSET.
      CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
-     is a pointer argument sinse we may update it.  */
+     is a pointer argument since we may update it.  */
 
 static reg_errcode_t
 #ifdef RE_ENABLE_I18N
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
index 0194965..eb5e1d4 100644
--- a/compat/regex/regexec.c
+++ b/compat/regex/regexec.c
@@ -455,7 +455,7 @@
 
   rval = 0;
 
-  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  /* I hope we needn't fill their regs with -1's when no match was found.  */
   if (result != REG_NOERROR)
     rval = -1;
   else if (regs != NULL)
@@ -1071,7 +1071,7 @@
    FL_LONGEST_MATCH means we want the POSIX longest matching.
    If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
    next place where we may want to try matching.
-   Note that the matcher assume that the maching starts from the current
+   Note that the matcher assume that the matching starts from the current
    index of the buffer.  */
 
 static int
@@ -2239,7 +2239,7 @@
 			    dfa->nexts[node_idx]))
     /* The node can't accept the `multi byte', or the
        destination was already thrown away, then the node
-       could't accept the current input `multi byte'.   */
+       couldn't accept the current input `multi byte'.   */
     naccepted = 0;
   /* Otherwise, it is sure that the node could accept
      `naccepted' bytes input.  */
@@ -2313,7 +2313,7 @@
 }
 
 /* Update the state_log if we need */
-re_dfastate_t *
+static re_dfastate_t *
 internal_function
 merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
 		      re_dfastate_t *next_state)
@@ -2326,7 +2326,7 @@
       mctx->state_log[cur_idx] = next_state;
       mctx->state_log_top = cur_idx;
     }
-  else if (mctx->state_log[cur_idx] == 0)
+  else if (mctx->state_log[cur_idx] == NULL)
     {
       mctx->state_log[cur_idx] = next_state;
     }
@@ -2392,7 +2392,7 @@
 /* Skip bytes in the input that correspond to part of a
    multi-byte match, then look in the log for a state
    from which to restart matching.  */
-re_dfastate_t *
+static re_dfastate_t *
 internal_function
 find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
 {
diff --git a/compat/terminal.c b/compat/terminal.c
index 9b5e3d1..313897d 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -3,7 +3,7 @@
 #include "sigchain.h"
 #include "strbuf.h"
 
-#if defined(HAVE_DEV_TTY) || defined(WIN32)
+#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
 
 static void restore_term(void);
 
@@ -53,7 +53,7 @@
 	return -1;
 }
 
-#elif defined(WIN32)
+#elif defined(GIT_WINDOWS_NATIVE)
 
 #define INPUT_PATH "CONIN$"
 #define OUTPUT_PATH "CONOUT$"
diff --git a/compat/unsetenv.c b/compat/unsetenv.c
index eb29f5e..bf5fd70 100644
--- a/compat/unsetenv.c
+++ b/compat/unsetenv.c
@@ -2,7 +2,9 @@
 
 void gitunsetenv (const char *name)
 {
+#if !defined(__MINGW32__)
      extern char **environ;
+#endif
      int src, dst;
      size_t nmln;
 
diff --git a/compat/win32.h b/compat/win32.h
index 8ce9104..a97e880 100644
--- a/compat/win32.h
+++ b/compat/win32.h
@@ -2,7 +2,7 @@
 #define WIN32_H
 
 /* common Win32 functions for MinGW and Cygwin */
-#ifndef WIN32         /* Not defined by Cygwin */
+#ifndef GIT_WINDOWS_NATIVE	/* Not defined for Cygwin */
 #include <windows.h>
 #endif
 
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
index 010e875..e18f5c6 100644
--- a/compat/win32/pthread.c
+++ b/compat/win32/pthread.c
@@ -52,7 +52,7 @@
 
 pthread_t pthread_self(void)
 {
-	pthread_t t = { 0 };
+	pthread_t t = { NULL };
 	t.tid = GetCurrentThreadId();
 	return t;
 }
diff --git a/compat/win32mmap.c b/compat/win32mmap.c
index 61d2ef8..80a8c9a 100644
--- a/compat/win32mmap.c
+++ b/compat/win32mmap.c
@@ -21,8 +21,8 @@
 	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);
+	hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), NULL,
+		PAGE_WRITECOPY, 0, 0, NULL);
 
 	if (!hmap)
 		return MAP_FAILED;
diff --git a/config.c b/config.c
index aefd80b..e13a7b6 100644
--- a/config.c
+++ b/config.c
@@ -10,20 +10,69 @@
 #include "strbuf.h"
 #include "quote.h"
 
-typedef struct config_file {
-	struct config_file *prev;
-	FILE *f;
+struct config_source {
+	struct config_source *prev;
+	union {
+		FILE *file;
+		struct config_buf {
+			const char *buf;
+			size_t len;
+			size_t pos;
+		} buf;
+	} u;
 	const char *name;
+	int die_on_error;
 	int linenr;
 	int eof;
 	struct strbuf value;
 	struct strbuf var;
-} config_file;
 
-static config_file *cf;
+	int (*fgetc)(struct config_source *c);
+	int (*ungetc)(int c, struct config_source *conf);
+	long (*ftell)(struct config_source *c);
+};
+
+static struct config_source *cf;
 
 static int zlib_compression_seen;
 
+static int config_file_fgetc(struct config_source *conf)
+{
+	return fgetc(conf->u.file);
+}
+
+static int config_file_ungetc(int c, struct config_source *conf)
+{
+	return ungetc(c, conf->u.file);
+}
+
+static long config_file_ftell(struct config_source *conf)
+{
+	return ftell(conf->u.file);
+}
+
+
+static int config_buf_fgetc(struct config_source *conf)
+{
+	if (conf->u.buf.pos < conf->u.buf.len)
+		return conf->u.buf.buf[conf->u.buf.pos++];
+
+	return EOF;
+}
+
+static int config_buf_ungetc(int c, struct config_source *conf)
+{
+	if (conf->u.buf.pos > 0)
+		return conf->u.buf.buf[--conf->u.buf.pos];
+
+	return EOF;
+}
+
+static long config_buf_ftell(struct config_source *conf)
+{
+	return conf->u.buf.pos;
+}
+
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] =
 "exceeded maximum include depth (%d) while including\n"
@@ -58,7 +107,7 @@
 		path = buf.buf;
 	}
 
-	if (!access_or_die(path, R_OK)) {
+	if (!access_or_die(path, R_OK, 0)) {
 		if (++inc->depth > MAX_INCLUDE_DEPTH)
 			die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
 			    cf && cf->name ? cf->name : "the command line");
@@ -168,26 +217,21 @@
 
 static int get_next_char(void)
 {
-	int c;
-	FILE *f;
+	int c = cf->fgetc(cf);
 
-	c = '\n';
-	if (cf && ((f = cf->f) != NULL)) {
-		c = fgetc(f);
-		if (c == '\r') {
-			/* DOS like systems */
-			c = fgetc(f);
-			if (c != '\n') {
-				ungetc(c, f);
-				c = '\r';
-			}
+	if (c == '\r') {
+		/* DOS like systems */
+		c = cf->fgetc(cf);
+		if (c != '\n') {
+			cf->ungetc(c, cf);
+			c = '\r';
 		}
-		if (c == '\n')
-			cf->linenr++;
-		if (c == EOF) {
-			cf->eof = 1;
-			c = '\n';
-		}
+	}
+	if (c == '\n')
+		cf->linenr++;
+	if (c == EOF) {
+		cf->eof = 1;
+		c = '\n';
 	}
 	return c;
 }
@@ -339,7 +383,7 @@
 	}
 }
 
-static int git_parse_file(config_fn_t fn, void *data)
+static int git_parse_source(config_fn_t fn, void *data)
 {
 	int comment = 0;
 	int baselen = 0;
@@ -399,7 +443,10 @@
 		if (get_value(fn, data, var) < 0)
 			break;
 	}
-	die("bad config file line %d in %s", cf->linenr, cf->name);
+	if (cf->die_on_error)
+		die("bad config file line %d in %s", cf->linenr, cf->name);
+	else
+		return error("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
 static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -566,7 +613,20 @@
 		trust_ctime = git_config_bool(var, value);
 		return 0;
 	}
-	if (!strcmp(var, "core.statinfo")) {
+	if (!strcmp(var, "core.statinfo") ||
+	    !strcmp(var, "core.checkstat")) {
+		/*
+		 * NEEDSWORK: statinfo was a typo in v1.8.2 that has
+		 * never been advertised.  we will remove it at Git
+		 * 2.0 boundary.
+		 */
+		if (!strcmp(var, "core.statinfo")) {
+			static int warned;
+			if (!warned++) {
+				warning("'core.statinfo' will be removed in Git 2.0; "
+					"use 'core.checkstat' instead.");
+			}
+		}
 		if (!strcasecmp(value, "default"))
 			check_stat = 1;
 		else if (!strcasecmp(value, "minimal"))
@@ -675,9 +735,6 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.logpackaccess"))
-		return git_config_string(&log_pack_access, var, value);
-
 	if (!strcmp(var, "core.autocrlf")) {
 		if (value && !strcasecmp(value, "input")) {
 			if (core_eol == EOL_CRLF)
@@ -896,6 +953,33 @@
 	return 0;
 }
 
+/*
+ * All source specific fields in the union, die_on_error, name and the callbacks
+ * fgetc, ungetc, ftell of top need to be initialized before calling
+ * this function.
+ */
+static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
+{
+	int ret;
+
+	/* push config-file parsing state stack */
+	top->prev = cf;
+	top->linenr = 1;
+	top->eof = 0;
+	strbuf_init(&top->value, 1024);
+	strbuf_init(&top->var, 1024);
+	cf = top;
+
+	ret = git_parse_source(fn, data);
+
+	/* pop config-file parsing state stack */
+	strbuf_release(&top->value);
+	strbuf_release(&top->var);
+	cf = top->prev;
+
+	return ret;
+}
+
 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 {
 	int ret;
@@ -903,30 +987,74 @@
 
 	ret = -1;
 	if (f) {
-		config_file top;
+		struct config_source top;
 
-		/* push config-file parsing state stack */
-		top.prev = cf;
-		top.f = f;
+		top.u.file = f;
 		top.name = filename;
-		top.linenr = 1;
-		top.eof = 0;
-		strbuf_init(&top.value, 1024);
-		strbuf_init(&top.var, 1024);
-		cf = &top;
+		top.die_on_error = 1;
+		top.fgetc = config_file_fgetc;
+		top.ungetc = config_file_ungetc;
+		top.ftell = config_file_ftell;
 
-		ret = git_parse_file(fn, data);
-
-		/* pop config-file parsing state stack */
-		strbuf_release(&top.value);
-		strbuf_release(&top.var);
-		cf = top.prev;
+		ret = do_config_from(&top, fn, data);
 
 		fclose(f);
 	}
 	return ret;
 }
 
+int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
+			size_t len, void *data)
+{
+	struct config_source top;
+
+	top.u.buf.buf = buf;
+	top.u.buf.len = len;
+	top.u.buf.pos = 0;
+	top.name = name;
+	top.die_on_error = 0;
+	top.fgetc = config_buf_fgetc;
+	top.ungetc = config_buf_ungetc;
+	top.ftell = config_buf_ftell;
+
+	return do_config_from(&top, fn, data);
+}
+
+static int git_config_from_blob_sha1(config_fn_t fn,
+				     const char *name,
+				     const unsigned char *sha1,
+				     void *data)
+{
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	int ret;
+
+	buf = read_sha1_file(sha1, &type, &size);
+	if (!buf)
+		return error("unable to load config blob object '%s'", name);
+	if (type != OBJ_BLOB) {
+		free(buf);
+		return error("reference '%s' does not point to a blob", name);
+	}
+
+	ret = git_config_from_buf(fn, name, buf, size, data);
+	free(buf);
+
+	return ret;
+}
+
+static int git_config_from_blob_ref(config_fn_t fn,
+				    const char *name,
+				    void *data)
+{
+	unsigned char sha1[20];
+
+	if (get_sha1(name, sha1) < 0)
+		return error("unable to resolve config blob '%s'", name);
+	return git_config_from_blob_sha1(fn, name, sha1, data);
+}
+
 const char *git_etc_gitconfig(void)
 {
 	static const char *system_wide;
@@ -954,23 +1082,23 @@
 
 	home_config_paths(&user_config, &xdg_config, "config");
 
-	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK)) {
+	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
 		ret += git_config_from_file(fn, git_etc_gitconfig(),
 					    data);
 		found += 1;
 	}
 
-	if (xdg_config && !access_or_die(xdg_config, R_OK)) {
+	if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) {
 		ret += git_config_from_file(fn, xdg_config, data);
 		found += 1;
 	}
 
-	if (user_config && !access_or_die(user_config, R_OK)) {
+	if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) {
 		ret += git_config_from_file(fn, user_config, data);
 		found += 1;
 	}
 
-	if (repo_config && !access_or_die(repo_config, R_OK)) {
+	if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
 		ret += git_config_from_file(fn, repo_config, data);
 		found += 1;
 	}
@@ -992,7 +1120,9 @@
 }
 
 int git_config_with_options(config_fn_t fn, void *data,
-			    const char *filename, int respect_includes)
+			    const char *filename,
+			    const char *blob_ref,
+			    int respect_includes)
 {
 	char *repo_config = NULL;
 	int ret;
@@ -1011,6 +1141,8 @@
 	 */
 	if (filename)
 		return git_config_from_file(fn, filename, data);
+	else if (blob_ref)
+		return git_config_from_blob_ref(fn, blob_ref, data);
 
 	repo_config = git_pathdup("config");
 	ret = git_config_early(fn, data, repo_config);
@@ -1021,7 +1153,7 @@
 
 int git_config(config_fn_t fn, void *data)
 {
-	return git_config_with_options(fn, data, NULL, 1);
+	return git_config_with_options(fn, data, NULL, NULL, 1);
 }
 
 /*
@@ -1053,7 +1185,6 @@
 {
 	const char *ep;
 	size_t section_len;
-	FILE *f = cf->f;
 
 	switch (store.state) {
 	case KEY_SEEN:
@@ -1065,7 +1196,7 @@
 				return 1;
 			}
 
-			store.offset[store.seen] = ftell(f);
+			store.offset[store.seen] = cf->ftell(cf);
 			store.seen++;
 		}
 		break;
@@ -1092,19 +1223,19 @@
 		 * Do not increment matches: this is no match, but we
 		 * just made sure we are in the desired section.
 		 */
-		store.offset[store.seen] = ftell(f);
+		store.offset[store.seen] = cf->ftell(cf);
 		/* fallthru */
 	case SECTION_END_SEEN:
 	case START:
 		if (matches(key, value)) {
-			store.offset[store.seen] = ftell(f);
+			store.offset[store.seen] = cf->ftell(cf);
 			store.state = KEY_SEEN;
 			store.seen++;
 		} else {
 			if (strrchr(key, '.') - key == store.baselen &&
 			      !strncmp(key, store.key, store.baselen)) {
 					store.state = SECTION_SEEN;
-					store.offset[store.seen] = ftell(f);
+					store.offset[store.seen] = cf->ftell(cf);
 			}
 		}
 	}
diff --git a/config.mak.uname b/config.mak.uname
index d78fd3d..b45b910 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -95,6 +95,7 @@
 	NO_MEMMEM = YesPlease
 	USE_ST_TIMESPEC = YesPlease
 	HAVE_DEV_TTY = YesPlease
+	NEEDS_CLIPPED_WRITE = YesPlease
 	COMPAT_OBJS += compat/precompose_utf8.o
 	BASIC_CFLAGS += -DPRECOMPOSE_UNICODE
 endif
@@ -158,17 +159,17 @@
 		NO_SYMLINK_HEAD = YesPlease
 		NO_IPV6 = YesPlease
 		OLD_ICONV = UnfortunatelyYes
-		CYGWIN_V15_WIN32API = YesPlease
+		NO_THREAD_SAFE_PREAD = YesPlease
+		# There are conflicting reports about this.
+		# On some boxes NO_MMAP is needed, and not so elsewhere.
+		# Try commenting this out if you suspect MMAP is more efficient
+		NO_MMAP = YesPlease
+	else
+		NO_REGEX = UnfortunatelyYes
 	endif
-	NO_THREAD_SAFE_PREAD = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
-	NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
 	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
-	# There are conflicting reports about this.
-	# On some boxes NO_MMAP is needed, and not so elsewhere.
-	# Try commenting this out if you suspect MMAP is more efficient
-	NO_MMAP = YesPlease
 	X = .exe
 	COMPAT_OBJS += compat/cygwin.o
 	UNRELIABLE_FSTAT = UnfortunatelyYes
@@ -480,7 +481,6 @@
 	NO_FNMATCH = YesPlease
 	NO_MEMMEM = YesPlease
 	NEEDS_LIBICONV = YesPlease
-	OLD_ICONV = YesPlease
 	NO_STRTOUMAX = YesPlease
 	NO_MKDTEMP = YesPlease
 	NO_MKSTEMPS = YesPlease
@@ -500,13 +500,17 @@
 	NO_INET_PTON = YesPlease
 	NO_INET_NTOP = YesPlease
 	NO_POSIX_GOODIES = UnfortunatelyYes
+	DEFAULT_HELP_FORMAT = html
 	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 	COMPAT_OBJS += compat/mingw.o compat/winansi.o \
 		compat/win32/pthread.o compat/win32/syslog.o \
 		compat/win32/dirent.o
+	BASIC_LDFLAGS += -Wl,--large-address-aware
 	EXTLIBS += -lws2_32
+	GITLIBS += git.res
 	PTHREAD_LIBS =
+	RC = windres -O coff
 	NATIVE_CRLF = YesPlease
 	X = .exe
 	SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
@@ -518,6 +522,7 @@
 	NO_R_TO_GCC_LINKER = YesPlease
 	INTERNAL_QSORT = YesPlease
 	HAVE_LIBCHARSET_H = YesPlease
+	NO_GETTEXT = YesPlease
 else
 	NO_CURL = YesPlease
 endif
diff --git a/configure.ac b/configure.ac
index f3462d9..2f43393 100644
--- a/configure.ac
+++ b/configure.ac
@@ -193,7 +193,7 @@
   [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.])],
+  [--disable-pthreads will disable threading.])],
 [
 if test "x$enableval" = "xyes"; then
    AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads])
diff --git a/connect.c b/connect.c
index f57efd0..a0783d4 100644
--- a/connect.c
+++ b/connect.c
@@ -551,8 +551,11 @@
 	path = strchr(end, c);
 	if (path && !has_dos_drive_prefix(end)) {
 		if (c == ':') {
-			protocol = PROTO_SSH;
-			*path++ = '\0';
+			if (path < strchrnul(host, '/')) {
+				protocol = PROTO_SSH;
+				*path++ = '\0';
+			} else /* '/' in the host part, assume local path */
+				path = end;
 		}
 	} else
 		path = end;
diff --git a/connected.c b/connected.c
index 1e89c1c..fae8d64 100644
--- a/connected.c
+++ b/connected.c
@@ -2,7 +2,12 @@
 #include "run-command.h"
 #include "sigchain.h"
 #include "connected.h"
+#include "transport.h"
 
+int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
+{
+	return check_everything_connected_with_transport(fn, quiet, cb_data, NULL);
+}
 /*
  * If we feed all the commits we want to verify to this command
  *
@@ -14,7 +19,10 @@
  *
  * Returns 0 if everything is connected, non-zero otherwise.
  */
-int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
+int check_everything_connected_with_transport(sha1_iterate_fn fn,
+					      int quiet,
+					      void *cb_data,
+					      struct transport *transport)
 {
 	struct child_process rev_list;
 	const char *argv[] = {"rev-list", "--objects",
@@ -22,10 +30,23 @@
 	char commit[41];
 	unsigned char sha1[20];
 	int err = 0;
+	struct packed_git *new_pack = NULL;
 
 	if (fn(cb_data, sha1))
 		return err;
 
+	if (transport && transport->smart_options &&
+	    transport->smart_options->self_contained_and_connected &&
+	    transport->pack_lockfile &&
+	    !suffixcmp(transport->pack_lockfile, ".keep")) {
+		struct strbuf idx_file = STRBUF_INIT;
+		strbuf_addstr(&idx_file, transport->pack_lockfile);
+		strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
+		strbuf_addstr(&idx_file, ".idx");
+		new_pack = add_packed_git(idx_file.buf, idx_file.len, 1);
+		strbuf_release(&idx_file);
+	}
+
 	if (quiet)
 		argv[5] = "--quiet";
 
@@ -42,6 +63,17 @@
 
 	commit[40] = '\n';
 	do {
+		/*
+		 * If index-pack already checked that:
+		 * - there are no dangling pointers in the new pack
+		 * - the pack is self contained
+		 * Then if the updated ref is in the new pack, then we
+		 * are sure the ref is good and not sending it to
+		 * rev-list for verification.
+		 */
+		if (new_pack && find_pack_entry_one(sha1, new_pack))
+			continue;
+
 		memcpy(commit, sha1_to_hex(sha1), 40);
 		if (write_in_full(rev_list.in, commit, 41) < 0) {
 			if (errno != EPIPE && errno != EINVAL)
diff --git a/connected.h b/connected.h
index 7e4585a..0b060b7 100644
--- a/connected.h
+++ b/connected.h
@@ -1,6 +1,8 @@
 #ifndef CONNECTED_H
 #define CONNECTED_H
 
+struct transport;
+
 /*
  * Take callback data, and return next object name in the buffer.
  * When called after returning the name for the last object, return -1
@@ -16,5 +18,8 @@
  * Return 0 if Ok, non zero otherwise (i.e. some missing objects)
  */
 extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
+extern int check_everything_connected_with_transport(sha1_iterate_fn, int quiet,
+						     void *cb_data,
+						     struct transport *transport);
 
 #endif /* CONNECTED_H */
diff --git a/contrib/blameview/README b/contrib/blameview/README
deleted file mode 100644
index fada5ce..0000000
--- a/contrib/blameview/README
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a sample program to use 'git-blame --incremental', based
-on this message.
-
-From: Jeff King <peff@peff.net>
-Subject: Re: More precise tag following
-To: Linus Torvalds <torvalds@linux-foundation.org>
-Cc: git@vger.kernel.org
-Date: Sat, 27 Jan 2007 18:52:38 -0500
-Message-ID: <20070127235238.GA28706@coredump.intra.peff.net>
diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl
deleted file mode 100755
index 1dec001..0000000
--- a/contrib/blameview/blameview.perl
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/perl
-
-use Gtk2 -init;
-use Gtk2::SimpleList;
-
-my $hash;
-my $fn;
-if ( @ARGV == 1 ) {
-	$hash = "HEAD";
-	$fn = shift;
-} elsif ( @ARGV == 2 ) {
-	$hash = shift;
-	$fn = shift;
-} else {
-	die "Usage blameview [<rev>] <filename>";
-}
-
-Gtk2::Rc->parse_string(<<'EOS');
-style "treeview_style"
-{
-  GtkTreeView::vertical-separator = 0
-}
-class "GtkTreeView" style "treeview_style"
-EOS
-
-my $window = Gtk2::Window->new('toplevel');
-$window->signal_connect(destroy => sub { Gtk2->main_quit });
-my $vpan = Gtk2::VPaned->new();
-$window->add($vpan);
-my $scrolled_window = Gtk2::ScrolledWindow->new;
-$vpan->pack1($scrolled_window, 1, 1);
-my $fileview = Gtk2::SimpleList->new(
-    'Commit' => 'text',
-    'FileLine' => 'text',
-    'Data' => 'text'
-);
-$scrolled_window->add($fileview);
-$fileview->get_column(0)->set_spacing(0);
-$fileview->set_size_request(1024, 768);
-$fileview->set_rules_hint(1);
-$fileview->signal_connect (row_activated => sub {
-		my ($sl, $path, $column) = @_;
-		my $row_ref = $sl->get_row_data_from_path ($path);
-		system("blameview @$row_ref[0]~1 $fn &");
-		});
-
-my $commitwindow = Gtk2::ScrolledWindow->new();
-$commitwindow->set_policy ('GTK_POLICY_AUTOMATIC','GTK_POLICY_AUTOMATIC');
-$vpan->pack2($commitwindow, 1, 1);
-my $commit_text = Gtk2::TextView->new();
-my $commit_buffer = Gtk2::TextBuffer->new();
-$commit_text->set_buffer($commit_buffer);
-$commitwindow->add($commit_text);
-
-$fileview->signal_connect (cursor_changed => sub {
-		my ($sl) = @_;
-		my ($path, $focus_column) = $sl->get_cursor();
-		my $row_ref = $sl->get_row_data_from_path ($path);
-		my $c_fh;
-		open($c_fh,  '-|', "git cat-file commit @$row_ref[0]")
-					or die "unable to find commit @$row_ref[0]";
-		my @buffer = <$c_fh>;
-		$commit_buffer->set_text("@buffer");
-		close($c_fh);
-		});
-
-my $fh;
-open($fh, '-|', "git cat-file blob $hash:$fn")
-  or die "unable to open $fn: $!";
-
-while(<$fh>) {
-  chomp;
-  $fileview->{data}->[$.] = ['HEAD', "$fn:$.", $_];
-}
-
-my $blame;
-open($blame, '-|', qw(git blame --incremental --), $fn, $hash)
-    or die "cannot start git-blame $fn";
-
-Glib::IO->add_watch(fileno($blame), 'in', \&read_blame_line);
-
-$window->show_all;
-Gtk2->main;
-exit 0;
-
-my %commitinfo = ();
-
-sub flush_blame_line {
-	my ($attr) = @_;
-
-	return unless defined $attr;
-
-	my ($commit, $s_lno, $lno, $cnt) =
-	    @{$attr}{qw(COMMIT S_LNO LNO CNT)};
-
-	my ($filename, $author, $author_time, $author_tz) =
-	    @{$commitinfo{$commit}}{qw(FILENAME AUTHOR AUTHOR-TIME AUTHOR-TZ)};
-	my $info = $author . ' ' . format_time($author_time, $author_tz);
-
-	for(my $i = 0; $i < $cnt; $i++) {
-		@{$fileview->{data}->[$lno+$i-1]}[0,1,2] =
-		(substr($commit, 0, 8), $filename . ':' . ($s_lno+$i));
-	}
-}
-
-my $buf;
-my $current;
-sub read_blame_line {
-
-	my $r = sysread($blame, $buf, 1024, length($buf));
-	die "I/O error" unless defined $r;
-
-	if ($r == 0) {
-		flush_blame_line($current);
-		$current = undef;
-		return 0;
-	}
-
-	while ($buf =~ s/([^\n]*)\n//) {
-		my $line = $1;
-
-		if (($commit, $s_lno, $lno, $cnt) =
-		    ($line =~ /^([0-9a-f]{40}) (\d+) (\d+) (\d+)$/)) {
-			flush_blame_line($current);
-			$current = +{
-				COMMIT => $1,
-				S_LNO => $2,
-				LNO => $3,
-				CNT => $4,
-			};
-			next;
-		}
-
-		# extended attribute values
-		if ($line =~ /^(author|author-mail|author-time|author-tz|committer|committer-mail|committer-time|committer-tz|summary|filename) (.*)$/) {
-			my $commit = $current->{COMMIT};
-			$commitinfo{$commit}{uc($1)} = $2;
-			next;
-		}
-	}
-	return 1;
-}
-
-sub format_time {
-  my $time = shift;
-  my $tz = shift;
-
-  my $minutes = $tz < 0 ? 0-$tz : $tz;
-  $minutes = ($minutes / 100)*60 + ($minutes % 100);
-  $minutes = $tz < 0 ? 0-$minutes : $minutes;
-  $time += $minutes * 60;
-  my @t = gmtime($time);
-  return sprintf('%04d-%02d-%02d %02d:%02d:%02d %s',
-		 $t[5] + 1900, @t[4,3,2,1,0], $tz);
-}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 1c35eef..cd509a5 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -33,8 +33,6 @@
 # returns location of .git repo
 __gitdir ()
 {
-	# Note: this function is duplicated in git-prompt.sh
-	# When updating it, make sure you update the other one to match.
 	if [ -z "${1-}" ]; then
 		if [ -n "${__git_dir-}" ]; then
 			echo "$__git_dir"
@@ -252,106 +250,50 @@
 	# since tilde expansion is not applied.
 	# This means that COMPREPLY will be empty and Bash default
 	# completion will be used.
-	COMPREPLY=($(compgen -P "${2-}" -W "$1" -- "${3-$cur}"))
+	__gitcompadd "$1" "${2-}" "${3-$cur}" ""
 
-	# Tell Bash that compspec generates filenames.
-	compopt -o filenames 2>/dev/null
+	# use a hack to enable file mode in bash < 4
+	compopt -o filenames +o nospace 2>/dev/null ||
+	compgen -f /non-existing-dir/ > /dev/null
 }
 
-__git_index_file_list_filter_compat ()
-{
-	local path
-
-	while read -r path; do
-		case "$path" in
-		?*/*) echo "${path%%/*}/" ;;
-		*) echo "$path" ;;
-		esac
-	done
-}
-
-__git_index_file_list_filter_bash ()
-{
-	local path
-
-	while read -r path; do
-		case "$path" in
-		?*/*)
-			# XXX if we append a slash to directory names when using
-			# `compopt -o filenames`, Bash will append another slash.
-			# This is pretty stupid, and this the reason why we have to
-			# define a compatible version for this function.
-			echo "${path%%/*}" ;;
-		*)
-			echo "$path" ;;
-		esac
-	done
-}
-
-# Process path list returned by "ls-files" and "diff-index --name-only"
-# commands, in order to list only file names relative to a specified
-# directory, and append a slash to directory names.
-__git_index_file_list_filter ()
-{
-	# Default to Bash >= 4.x
-	__git_index_file_list_filter_bash
-}
-
-# Execute git ls-files, returning paths relative to the directory
-# specified in the first argument, and using the options specified in
-# the second argument.
+# Execute 'git ls-files', unless the --committable option is specified, in
+# which case it runs 'git diff-index' to find out the files that can be
+# committed.  It return paths relative to the directory specified in the first
+# argument, and using the options specified in the second argument.
 __git_ls_files_helper ()
 {
 	(
 		test -n "${CDPATH+set}" && unset CDPATH
-		# NOTE: $2 is not quoted in order to support multiple options
-		cd "$1" && git ls-files --exclude-standard $2
+		cd "$1"
+		if [ "$2" == "--committable" ]; then
+			git diff-index --name-only --relative HEAD
+		else
+			# NOTE: $2 is not quoted in order to support multiple options
+			git ls-files --exclude-standard $2
+		fi
 	) 2>/dev/null
 }
 
 
-# Execute git diff-index, returning paths relative to the directory
-# specified in the first argument, and using the tree object id
-# specified in the second argument.
-__git_diff_index_helper ()
-{
-	(
-		test -n "${CDPATH+set}" && unset CDPATH
-		cd "$1" && git diff-index --name-only --relative "$2"
-	) 2>/dev/null
-}
-
 # __git_index_files accepts 1 or 2 arguments:
 # 1: Options to pass to ls-files (required).
-#    Supported options are --cached, --modified, --deleted, --others,
-#    and --directory.
 # 2: A directory path (optional).
 #    If provided, only files within the specified directory are listed.
 #    Sub directories are never recursed.  Path must have a trailing
 #    slash.
 __git_index_files ()
 {
-	local dir="$(__gitdir)" root="${2-.}"
+	local dir="$(__gitdir)" root="${2-.}" file
 
 	if [ -d "$dir" ]; then
-		__git_ls_files_helper "$root" "$1" | __git_index_file_list_filter |
-			sort | uniq
-	fi
-}
-
-# __git_diff_index_files accepts 1 or 2 arguments:
-# 1) The id of a tree object.
-# 2) A directory path (optional).
-#    If provided, only files within the specified directory are listed.
-#    Sub directories are never recursed.  Path must have a trailing
-#    slash.
-__git_diff_index_files ()
-{
-	local dir="$(__gitdir)" root="${2-.}"
-
-	if [ -d "$dir" ]; then
-		__git_diff_index_helper "$root" "$1" | __git_index_file_list_filter |
-			sort | uniq
+		__git_ls_files_helper "$root" "$1" |
+		while read -r file; do
+			case "$file" in
+			?*/*) echo "${file%%/*}" ;;
+			*) echo "$file" ;;
+			esac
+		done | sort | uniq
 	fi
 }
 
@@ -427,14 +369,8 @@
 		done
 		;;
 	*)
-		git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
-		while read -r hash i; do
-			case "$i" in
-			*^{}) ;;
-			refs/*) echo "${i#refs/*/}" ;;
-			*) echo "$i" ;;
-			esac
-		done
+		echo "HEAD"
+		git for-each-ref --format="%(refname:short)" -- "refs/remotes/$dir/" | sed -e "s#^$dir/##"
 		;;
 	esac
 }
@@ -552,44 +488,23 @@
 }
 
 
-# __git_complete_index_file requires 1 argument: the options to pass to
-# ls-file
+# __git_complete_index_file requires 1 argument:
+# 1: the options to pass to ls-file
+#
+# The exception is --committable, which finds the files appropriate commit.
 __git_complete_index_file ()
 {
-	local pfx cur_="$cur"
+	local pfx="" cur_="$cur"
 
 	case "$cur_" in
 	?*/*)
 		pfx="${cur_%/*}"
 		cur_="${cur_##*/}"
 		pfx="${pfx}/"
-
-		__gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
-		;;
-	*)
-		__gitcomp_file "$(__git_index_files "$1")" "" "$cur_"
 		;;
 	esac
-}
 
-# __git_complete_diff_index_file requires 1 argument: the id of a tree
-# object
-__git_complete_diff_index_file ()
-{
-	local pfx cur_="$cur"
-
-	case "$cur_" in
-	?*/*)
-		pfx="${cur_%/*}"
-		cur_="${cur_##*/}"
-		pfx="${pfx}/"
-
-		__gitcomp_file "$(__git_diff_index_files "$1" "$pfx")" "$pfx" "$cur_"
-		;;
-	*)
-		__gitcomp_file "$(__git_diff_index_files "$1")" "" "$cur_"
-		;;
-	esac
+	__gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
 }
 
 __git_complete_file ()
@@ -733,6 +648,7 @@
 		cat-file)         : plumbing;;
 		check-attr)       : plumbing;;
 		check-ignore)     : plumbing;;
+		check-mailmap)    : plumbing;;
 		check-ref-format) : plumbing;;
 		checkout-index)   : plumbing;;
 		commit-tree)      : plumbing;;
@@ -1213,7 +1129,7 @@
 	esac
 
 	if git rev-parse --verify --quiet HEAD >/dev/null; then
-		__git_complete_diff_index_file "HEAD"
+		__git_complete_index_file "--committable"
 	else
 		# This is the first commit
 		__git_complete_index_file "--cached"
@@ -1246,7 +1162,7 @@
 			--no-prefix --src-prefix= --dst-prefix=
 			--inter-hunk-context=
 			--patience --histogram --minimal
-			--raw
+			--raw --word-diff
 			--dirstat --dirstat= --dirstat-by-file
 			--dirstat-by-file= --cumulative
 			--diff-algorithm=
@@ -1294,7 +1210,7 @@
 		return
 		;;
 	esac
-	__git_complete_file
+	__git_complete_revlist_file
 }
 
 __git_fetch_options="
@@ -2360,7 +2276,7 @@
 		return
 		;;
 	esac
-	__git_complete_file
+	__git_complete_revlist_file
 }
 
 _git_show_branch ()
@@ -2575,9 +2491,10 @@
 		i="${words[c]}"
 		case "$i" in
 		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
+		--git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
 		--bare)      __git_dir="." ;;
 		--help) command="help"; break ;;
-		-c) c=$((++c)) ;;
+		-c|--work-tree|--namespace) ((c++)) ;;
 		-*) ;;
 		*) command="$i"; break ;;
 		esac
@@ -2595,6 +2512,7 @@
 			--exec-path
 			--exec-path=
 			--html-path
+			--man-path
 			--info-path
 			--work-tree=
 			--namespace=
@@ -2663,7 +2581,7 @@
 				--*=*|*.) ;;
 				*) c="$c " ;;
 				esac
-				array[$#array+1]="$c"
+				array+=("$c")
 			done
 			compset -P '*[=:]'
 			compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
@@ -2689,35 +2607,19 @@
 		compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 	}
 
-	__git_zsh_helper ()
-	{
-		emulate -L ksh
-		local cur cword prev
-		cur=${words[CURRENT-1]}
-		prev=${words[CURRENT-2]}
-		let cword=CURRENT-1
-		__${service}_main
-	}
-
 	_git ()
 	{
-		emulate -L zsh
-		local _ret=1
-		__git_zsh_helper
-		let _ret && _default -S '' && _ret=0
+		local _ret=1 cur cword prev
+		cur=${words[CURRENT]}
+		prev=${words[CURRENT-1]}
+		let cword=CURRENT-1
+		emulate ksh -c __${service}_main
+		let _ret && _default && _ret=0
 		return _ret
 	}
 
 	compdef _git git gitk
 	return
-elif [[ -n ${BASH_VERSION-} ]]; then
-	if ((${BASH_VERSINFO[0]} < 4)); then
-		# compopt is not supported
-		__git_index_file_list_filter ()
-		{
-			__git_index_file_list_filter_compat
-		}
-	fi
 fi
 
 __git_func_wrap ()
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index 2565d2e..fac5e71 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -4,18 +4,17 @@
 #
 # Copyright (c) 2012-2013 Felipe Contreras <felipe.contreras@gmail.com>
 #
-# You need git's bash completion script installed somewhere, by default on the
-# same directory as this script.
+# You need git's bash completion script installed somewhere, by default it
+# would be the location bash-completion uses.
 #
-# If your script is on ~/.git-completion.sh instead, you can configure it on
-# your ~/.zshrc:
+# If your script is somewhere else, you can configure it on your ~/.zshrc:
 #
 #  zstyle ':completion:*:*:git:*' script ~/.git-completion.sh
 #
-# The recommended way to install this script is to copy to
-# '~/.zsh/completion/_git', and then add the following to your ~/.zshrc file:
+# The recommended way to install this script is to copy to '~/.zsh/_git', and
+# then add the following to your ~/.zshrc file:
 #
-#  fpath=(~/.zsh/completion $fpath)
+#  fpath=(~/.zsh $fpath)
 
 complete ()
 {
@@ -27,7 +26,19 @@
 	zstyle ':completion:*:*:git:*' tag-order 'common-commands'
 
 zstyle -s ":completion:*:*:git:*" script script
-test -z "$script" && script="$(dirname ${funcsourcetrace[1]%:*})"/git-completion.bash
+if [ -z "$script" ]; then
+	local -a locations
+	local e
+	locations=(
+		'/etc/bash_completion.d/git' # fedora, old debian
+		'/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian
+		'/usr/share/bash-completion/git' # gentoo
+		$(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
+		)
+	for e in $locations; do
+		test -f $e && script="$e" && break
+	done
+fi
 ZSH_VERSION='' . "$script"
 
 __gitcomp ()
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index eaf5c36..a81ef5a 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -3,7 +3,7 @@
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Distributed under the GNU General Public License, version 2.0.
 #
-# This script allows you to see the current branch in your prompt.
+# This script allows you to see repository status in your prompt.
 #
 # To enable:
 #
@@ -13,23 +13,27 @@
 #    3a) Change your PS1 to call __git_ps1 as
 #        command-substitution:
 #        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-#        ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+#        ZSH:  setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
 #        the optional argument will be used as format string.
-#    3b) Alternatively, if you are using bash, __git_ps1 can be
-#        used for PROMPT_COMMAND with two parameters, <pre> and
-#        <post>, which are strings you would put in $PS1 before
-#        and after the status string generated by the git-prompt
-#        machinery.  e.g.
-#           PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
-#        will show username, at-sign, host, colon, cwd, then
-#        various status string, followed by dollar and SP, as
-#        your prompt.
+#    3b) Alternatively, for a slightly faster prompt, __git_ps1 can
+#        be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
+#        with two parameters, <pre> and <post>, which are strings
+#        you would put in $PS1 before and after the status string
+#        generated by the git-prompt machinery.  e.g.
+#        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
+#          will show username, at-sign, host, colon, cwd, then
+#          various status string, followed by dollar and SP, as
+#          your prompt.
+#        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
+#          will show username, pipe, then various status string,
+#          followed by colon, cwd, dollar and SP, as your prompt.
 #        Optionally, you can supply a third argument with a printf
 #        format string to finetune the output of the branch status
 #
-# The argument to __git_ps1 will be displayed only if you are currently
-# in a git repository.  The %s token will be the name of the current
-# branch.
+# The repository status will be displayed only if you are currently in a
+# git repository. The %s token is the placeholder for the shown status.
+#
+# The prompt status always includes the current branch name.
 #
 # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 # unstaged (*) and staged (+) changes will be shown next to the branch
@@ -77,31 +81,8 @@
 #
 # If you would like a colored hint about the current dirty state, set
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
-
-# __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
-__gitdir ()
-{
-	# Note: this function is duplicated in git-completion.bash
-	# When updating it, make sure you update the other one to match.
-	if [ -z "${1-}" ]; then
-		if [ -n "${__git_dir-}" ]; then
-			echo "$__git_dir"
-		elif [ -n "${GIT_DIR-}" ]; then
-			test -d "${GIT_DIR-}" || return 1
-			echo "$GIT_DIR"
-		elif [ -d .git ]; then
-			echo .git
-		else
-			git rev-parse --git-dir 2>/dev/null
-		fi
-	elif [ -d "$1/.git" ]; then
-		echo "$1/.git"
-	else
-		echo "$1"
-	fi
-}
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
 
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
@@ -124,7 +105,7 @@
 			fi
 			;;
 		svn-remote.*.url)
-			svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
+			svn_remote[$((${#svn_remote[@]} + 1))]="$value"
 			svn_url_pattern+="\\|$value"
 			upstream=svn+git # default upstream is SVN if available, else git
 			;;
@@ -146,10 +127,11 @@
 	svn*)
 		# get the upstream from the "git-svn-id: ..." in a commit message
 		# (git-svn uses essentially the same procedure internally)
-		local svn_upstream=($(git log --first-parent -1 \
+		local -a svn_upstream
+		svn_upstream=($(git log --first-parent -1 \
 					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
 		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
-			svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
+			svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
 			svn_upstream=${svn_upstream%@*}
 			local n_stop="${#svn_remote[@]}"
 			for ((n=1; n <= n_stop; n++)); do
@@ -222,6 +204,51 @@
 
 }
 
+# Helper function that is meant to be called from __git_ps1.  It
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
+__git_ps1_colorize_gitstring ()
+{
+	if [[ -n ${ZSH_VERSION-} ]]; then
+		local c_red='%F{red}'
+		local c_green='%F{green}'
+		local c_lblue='%F{blue}'
+		local c_clear='%f'
+	else
+		# Using \[ and \] around colors is necessary to prevent
+		# issues with command line editing/browsing/completion!
+		local c_red='\[\e[31m\]'
+		local c_green='\[\e[32m\]'
+		local c_lblue='\[\e[1;34m\]'
+		local c_clear='\[\e[0m\]'
+	fi
+	local bad_color=$c_red
+	local ok_color=$c_green
+	local flags_color="$c_lblue"
+
+	local branch_color=""
+	if [ $detached = no ]; then
+		branch_color="$ok_color"
+	else
+		branch_color="$bad_color"
+	fi
+	c="$branch_color$c"
+
+	z="$c_clear$z"
+	if [ "$w" = "*" ]; then
+		w="$bad_color$w"
+	fi
+	if [ -n "$i" ]; then
+		i="$ok_color$i"
+	fi
+	if [ -n "$s" ]; then
+		s="$flags_color$s"
+	fi
+	if [ -n "$u" ]; then
+		u="$bad_color$u"
+	fi
+	r="$c_clear$r"
+}
 
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
 # when called from PS1 using command substitution
@@ -254,48 +281,83 @@
 		;;
 	esac
 
-	local g="$(__gitdir)"
-	if [ -z "$g" ]; then
+	local repo_info rev_parse_exit_code
+	repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
+		--is-bare-repository --is-inside-work-tree \
+		--short HEAD 2>/dev/null)"
+	rev_parse_exit_code="$?"
+
+	if [ -z "$repo_info" ]; then
 		if [ $pcmode = yes ]; then
 			#In PC mode PS1 always needs to be set
 			PS1="$ps1pc_start$ps1pc_end"
 		fi
-	else
-		local r=""
-		local b=""
-		local step=""
-		local total=""
-		if [ -d "$g/rebase-merge" ]; then
-			b="$(cat "$g/rebase-merge/head-name")"
-			step=$(cat "$g/rebase-merge/msgnum")
-			total=$(cat "$g/rebase-merge/end")
-			if [ -f "$g/rebase-merge/interactive" ]; then
-				r="|REBASE-i"
-			else
-				r="|REBASE-m"
-			fi
-		else
-			if [ -d "$g/rebase-apply" ]; then
-				step=$(cat "$g/rebase-apply/next")
-				total=$(cat "$g/rebase-apply/last")
-				if [ -f "$g/rebase-apply/rebasing" ]; then
-					r="|REBASE"
-				elif [ -f "$g/rebase-apply/applying" ]; then
-					r="|AM"
-				else
-					r="|AM/REBASE"
-				fi
-			elif [ -f "$g/MERGE_HEAD" ]; then
-				r="|MERGING"
-			elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
-				r="|CHERRY-PICKING"
-			elif [ -f "$g/REVERT_HEAD" ]; then
-				r="|REVERTING"
-			elif [ -f "$g/BISECT_LOG" ]; then
-				r="|BISECTING"
-			fi
+		return
+	fi
 
-			b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+	local short_sha
+	if [ "$rev_parse_exit_code" = "0" ]; then
+		short_sha="${repo_info##*$'\n'}"
+		repo_info="${repo_info%$'\n'*}"
+	fi
+	local inside_worktree="${repo_info##*$'\n'}"
+	repo_info="${repo_info%$'\n'*}"
+	local bare_repo="${repo_info##*$'\n'}"
+	repo_info="${repo_info%$'\n'*}"
+	local inside_gitdir="${repo_info##*$'\n'}"
+	local g="${repo_info%$'\n'*}"
+
+	local r=""
+	local b=""
+	local step=""
+	local total=""
+	if [ -d "$g/rebase-merge" ]; then
+		read b 2>/dev/null <"$g/rebase-merge/head-name"
+		read step 2>/dev/null <"$g/rebase-merge/msgnum"
+		read total 2>/dev/null <"$g/rebase-merge/end"
+		if [ -f "$g/rebase-merge/interactive" ]; then
+			r="|REBASE-i"
+		else
+			r="|REBASE-m"
+		fi
+	else
+		if [ -d "$g/rebase-apply" ]; then
+			read step 2>/dev/null <"$g/rebase-apply/next"
+			read total 2>/dev/null <"$g/rebase-apply/last"
+			if [ -f "$g/rebase-apply/rebasing" ]; then
+				read b 2>/dev/null <"$g/rebase-apply/head-name"
+				r="|REBASE"
+			elif [ -f "$g/rebase-apply/applying" ]; then
+				r="|AM"
+			else
+				r="|AM/REBASE"
+			fi
+		elif [ -f "$g/MERGE_HEAD" ]; then
+			r="|MERGING"
+		elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+			r="|CHERRY-PICKING"
+		elif [ -f "$g/REVERT_HEAD" ]; then
+			r="|REVERTING"
+		elif [ -f "$g/BISECT_LOG" ]; then
+			r="|BISECTING"
+		fi
+
+		if [ -n "$b" ]; then
+			:
+		elif [ -h "$g/HEAD" ]; then
+			# symlink symbolic ref
+			b="$(git symbolic-ref HEAD 2>/dev/null)"
+		else
+			local head=""
+			if ! read head 2>/dev/null <"$g/HEAD"; then
+				if [ $pcmode = yes ]; then
+					PS1="$ps1pc_start$ps1pc_end"
+				fi
+				return
+			fi
+			# is it a symbolic ref?
+			b="${head#ref: }"
+			if [ "$head" = "$b" ]; then
 				detached=yes
 				b="$(
 				case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -309,104 +371,75 @@
 					git describe --tags --exact-match HEAD ;;
 				esac 2>/dev/null)" ||
 
-				b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
-				b="unknown"
+				b="$short_sha..."
 				b="($b)"
-			}
-		fi
-
-		if [ -n "$step" ] && [ -n "$total" ]; then
-			r="$r $step/$total"
-		fi
-
-		local w=""
-		local i=""
-		local s=""
-		local u=""
-		local c=""
-		local p=""
-
-		if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
-			if [ "true" = "$(git rev-parse --is-bare-repository 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-}" ] &&
-			   [ "$(git config --bool bash.showDirtyState)" != "false" ]
-			then
-				git diff --no-ext-diff --quiet --exit-code || w="*"
-				if git rev-parse --quiet --verify HEAD >/dev/null; then
-					git diff-index --cached --quiet HEAD -- || i="+"
-				else
-					i="#"
-				fi
-			fi
-			if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
-				git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
-			fi
-
-			if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
-			   [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
-			   [ -n "$(git ls-files --others --exclude-standard)" ]
-			then
-				u="%${ZSH_VERSION+%}"
-			fi
-
-			if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
-				__git_ps1_show_upstream
 			fi
 		fi
+	fi
 
-		local f="$w$i$s$u"
-		if [ $pcmode = yes ]; then
-			local gitstring=
-			if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-				local c_red='\e[31m'
-				local c_green='\e[32m'
-				local c_lblue='\e[1;34m'
-				local c_clear='\e[0m'
-				local bad_color=$c_red
-				local ok_color=$c_green
-				local branch_color="$c_clear"
-				local flags_color="$c_lblue"
-				local branchstring="$c${b##refs/heads/}"
+	if [ -n "$step" ] && [ -n "$total" ]; then
+		r="$r $step/$total"
+	fi
 
-				if [ $detached = no ]; then
-					branch_color="$ok_color"
-				else
-					branch_color="$bad_color"
-				fi
+	local w=""
+	local i=""
+	local s=""
+	local u=""
+	local c=""
+	local p=""
 
-				# Setting gitstring directly with \[ and \] around colors
-				# is necessary to prevent wrapping issues!
-				gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
-
-				if [ -n "$w$i$s$u$r$p" ]; then
-					gitstring="$gitstring "
-				fi
-				if [ "$w" = "*" ]; then
-					gitstring="$gitstring\[$bad_color\]$w"
-				fi
-				if [ -n "$i" ]; then
-					gitstring="$gitstring\[$ok_color\]$i"
-				fi
-				if [ -n "$s" ]; then
-					gitstring="$gitstring\[$flags_color\]$s"
-				fi
-				if [ -n "$u" ]; then
-					gitstring="$gitstring\[$bad_color\]$u"
-				fi
-				gitstring="$gitstring\[$c_clear\]$r$p"
-			else
-				gitstring="$c${b##refs/heads/}${f:+ $f}$r$p"
-			fi
-			gitstring=$(printf -- "$printf_format" "$gitstring")
-			PS1="$ps1pc_start$gitstring$ps1pc_end"
+	if [ "true" = "$inside_gitdir" ]; then
+		if [ "true" = "$bare_repo" ]; then
+			c="BARE:"
 		else
-			# NO color option unless in PROMPT_COMMAND mode
-			printf -- "$printf_format" "$c${b##refs/heads/}${f:+ $f}$r$p"
+			b="GIT_DIR!"
 		fi
+	elif [ "true" = "$inside_worktree" ]; then
+		if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
+		   [ "$(git config --bool bash.showDirtyState)" != "false" ]
+		then
+			git diff --no-ext-diff --quiet --exit-code || w="*"
+			if [ -n "$short_sha" ]; then
+				git diff-index --cached --quiet HEAD -- || i="+"
+			else
+				i="#"
+			fi
+		fi
+		if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+		   [ -r "$g/refs/stash" ]; then
+			s="$"
+		fi
+
+		if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
+		   [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
+		   git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
+		then
+			u="%${ZSH_VERSION+%}"
+		fi
+
+		if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+			__git_ps1_show_upstream
+		fi
+	fi
+
+	local z="${GIT_PS1_STATESEPARATOR-" "}"
+
+	# NO color option unless in PROMPT_COMMAND mode
+	if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+		__git_ps1_colorize_gitstring
+	fi
+
+	local f="$w$i$s$u"
+	local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
+
+	if [ $pcmode = yes ]; then
+		if [[ -n ${ZSH_VERSION-} ]]; then
+			gitstring=$(printf -- "$printf_format" "$gitstring")
+		else
+			printf -v gitstring -- "$printf_format" "$gitstring"
+		fi
+		PS1="$ps1pc_start$gitstring$ps1pc_end"
+	else
+		printf -- "$printf_format" "$gitstring"
 	fi
 }
diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
new file mode 100755
index 0000000..d80f7d1
--- /dev/null
+++ b/contrib/contacts/git-contacts
@@ -0,0 +1,188 @@
+#!/usr/bin/perl
+
+# List people who might be interested in a patch.  Useful as the argument to
+# git-send-email --cc-cmd option, and in other situations.
+#
+# Usage: git contacts <file | rev-list option> ...
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+my $since = '5-years-ago';
+my $min_percent = 10;
+my $labels_rx = qr/Signed-off-by|Reviewed-by|Acked-by|Cc/i;
+my %seen;
+
+sub format_contact {
+	my ($name, $email) = @_;
+	return "$name <$email>";
+}
+
+sub parse_commit {
+	my ($commit, $data) = @_;
+	my $contacts = $commit->{contacts};
+	my $inbody = 0;
+	for (split(/^/m, $data)) {
+		if (not $inbody) {
+			if (/^author ([^<>]+) <(\S+)> .+$/) {
+				$contacts->{format_contact($1, $2)} = 1;
+			} elsif (/^$/) {
+				$inbody = 1;
+			}
+		} elsif (/^$labels_rx:\s+([^<>]+)\s+<(\S+?)>$/o) {
+			$contacts->{format_contact($1, $2)} = 1;
+		}
+	}
+}
+
+sub import_commits {
+	my ($commits) = @_;
+	return unless %$commits;
+	my $pid = open2 my $reader, my $writer, qw(git cat-file --batch);
+	for my $id (keys(%$commits)) {
+		print $writer "$id\n";
+		my $line = <$reader>;
+		if ($line =~ /^([0-9a-f]{40}) commit (\d+)/) {
+			my ($cid, $len) = ($1, $2);
+			die "expected $id but got $cid\n" unless $id eq $cid;
+			my $data;
+			# cat-file emits newline after data, so read len+1
+			read $reader, $data, $len + 1;
+			parse_commit($commits->{$id}, $data);
+		}
+	}
+	close $reader;
+	close $writer;
+	waitpid($pid, 0);
+	die "git-cat-file error: $?\n" if $?;
+}
+
+sub get_blame {
+	my ($commits, $source, $start, $len, $from) = @_;
+	$len = 1 unless defined($len);
+	return if $len == 0;
+	open my $f, '-|',
+		qw(git blame --porcelain -C), '-L', "$start,+$len",
+		'--since', $since, "$from^", '--', $source or die;
+	while (<$f>) {
+		if (/^([0-9a-f]{40}) \d+ \d+ \d+$/) {
+			my $id = $1;
+			$commits->{$id} = { id => $id, contacts => {} }
+				unless $seen{$id};
+			$seen{$id} = 1;
+		}
+	}
+	close $f;
+}
+
+sub scan_patches {
+	my ($commits, $id, $f) = @_;
+	my $source;
+	while (<$f>) {
+		if (/^From ([0-9a-f]{40}) Mon Sep 17 00:00:00 2001$/) {
+			$id = $1;
+			$seen{$id} = 1;
+		}
+		next unless $id;
+		if (m{^--- (?:a/(.+)|/dev/null)$}) {
+			$source = $1;
+		} elsif (/^--- /) {
+			die "Cannot parse hunk source: $_\n";
+		} elsif (/^@@ -(\d+)(?:,(\d+))?/ && $source) {
+			get_blame($commits, $source, $1, $2, $id);
+		}
+	}
+}
+
+sub scan_patch_file {
+	my ($commits, $file) = @_;
+	open my $f, '<', $file or die "read failure: $file: $!\n";
+	scan_patches($commits, undef, $f);
+	close $f;
+}
+
+sub parse_rev_args {
+	my @args = @_;
+	open my $f, '-|',
+		qw(git rev-parse --revs-only --default HEAD --symbolic), @args
+		or die;
+	my @revs;
+	while (<$f>) {
+		chomp;
+		push @revs, $_;
+	}
+	close $f;
+	return @revs if scalar(@revs) != 1;
+	return "^$revs[0]", 'HEAD' unless $revs[0] =~ /^-/;
+	return $revs[0], 'HEAD';
+}
+
+sub scan_rev_args {
+	my ($commits, $args) = @_;
+	my @revs = parse_rev_args(@$args);
+	open my $f, '-|', qw(git rev-list --reverse), @revs or die;
+	while (<$f>) {
+		chomp;
+		my $id = $_;
+		$seen{$id} = 1;
+		open my $g, '-|', qw(git show -C --oneline), $id or die;
+		scan_patches($commits, $id, $g);
+		close $g;
+	}
+	close $f;
+}
+
+sub mailmap_contacts {
+	my ($contacts) = @_;
+	my %mapped;
+	my $pid = open2 my $reader, my $writer, qw(git check-mailmap --stdin);
+	for my $contact (keys(%$contacts)) {
+		print $writer "$contact\n";
+		my $canonical = <$reader>;
+		chomp $canonical;
+		$mapped{$canonical} += $contacts->{$contact};
+	}
+	close $reader;
+	close $writer;
+	waitpid($pid, 0);
+	die "git-check-mailmap error: $?\n" if $?;
+	return \%mapped;
+}
+
+if (!@ARGV) {
+	die "No input revisions or patch files\n";
+}
+
+my (@files, @rev_args);
+for (@ARGV) {
+	if (-e) {
+		push @files, $_;
+	} else {
+		push @rev_args, $_;
+	}
+}
+
+my %commits;
+for (@files) {
+	scan_patch_file(\%commits, $_);
+}
+if (@rev_args) {
+	scan_rev_args(\%commits, \@rev_args)
+}
+import_commits(\%commits);
+
+my $contacts = {};
+for my $commit (values %commits) {
+	for my $contact (keys %{$commit->{contacts}}) {
+		$contacts->{$contact}++;
+	}
+}
+$contacts = mailmap_contacts($contacts);
+
+my $ncommits = scalar(keys %commits);
+for my $contact (keys %$contacts) {
+	my $percent = $contacts->{$contact} * 100 / $ncommits;
+	next if $percent < $min_percent;
+	print "$contact\n";
+}
diff --git a/contrib/contacts/git-contacts.txt b/contrib/contacts/git-contacts.txt
new file mode 100644
index 0000000..dd914d1
--- /dev/null
+++ b/contrib/contacts/git-contacts.txt
@@ -0,0 +1,94 @@
+git-contacts(1)
+===============
+
+NAME
+----
+git-contacts - List people who might be interested in a set of changes
+
+
+SYNOPSIS
+--------
+[verse]
+'git contacts' (<patch>|<range>|<rev>)...
+
+
+DESCRIPTION
+-----------
+
+Given a set of changes, specified as patch files or revisions, determine people
+who might be interested in those changes.  This is done by consulting the
+history of each patch or revision hunk to find people mentioned by commits
+which touched the lines of files under consideration.
+
+Input consists of one or more patch files or revision arguments.  A revision
+argument can be a range or a single `<rev>` which is interpreted as
+`<rev>..HEAD`, thus the same revision arguments are accepted as for
+linkgit:git-format-patch[1]. Patch files and revision arguments can be combined
+in the same invocation.
+
+This command can be useful for determining the list of people with whom to
+discuss proposed changes, or for finding the list of recipients to Cc: when
+submitting a patch series via `git send-email`. For the latter case, `git
+contacts` can be used as the argument to `git send-email`'s `--cc-cmd` option.
+
+
+DISCUSSION
+----------
+
+`git blame` is invoked for each hunk in a patch file or revision.  For each
+commit mentioned by `git blame`, the commit message is consulted for people who
+authored, reviewed, signed, acknowledged, or were Cc:'d.  Once the list of
+participants is known, each person's relevance is computed by considering how
+many commits mentioned that person compared with the total number of commits
+under consideration.  The final output consists only of participants who exceed
+a minimum threshold of participation.
+
+
+OUTPUT
+------
+
+For each person of interest, a single line is output, terminated by a newline.
+If the person's name is known, ``Name $$<user@host>$$'' is printed; otherwise
+only ``$$<user@host>$$'' is printed.
+
+
+EXAMPLES
+--------
+
+* Consult patch files:
++
+------------
+$ git contacts feature/*.patch
+------------
+
+* Revision range:
++
+------------
+$ git contacts R1..R2
+------------
+
+* From a single revision to `HEAD`:
++
+------------
+$ git contacts origin
+------------
+
+* Helper for `git send-email`:
++
+------------
+$ git send-email --cc-cmd='git contacts' feature/*.patch
+------------
+
+
+LIMITATIONS
+-----------
+
+Several conditions controlling a person's significance are currently
+hard-coded, such as minimum participation level (10%), blame date-limiting (5
+years), and `-C` level for detecting moved and copied lines (a single `-C`). In
+the future, these conditions may become configurable.
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/contrib/continuous/cidaemon b/contrib/continuous/cidaemon
deleted file mode 100644
index 4009a15..0000000
--- a/contrib/continuous/cidaemon
+++ /dev/null
@@ -1,503 +0,0 @@
-#!/usr/bin/perl
-#
-# A daemon that waits for update events sent by its companion
-# post-receive-cinotify hook, checks out a new copy of source,
-# compiles it, and emails the guilty parties if the compile
-# (and optionally test suite) fails.
-#
-# To use this daemon, configure it and run it.  It will disconnect
-# from your terminal and fork into the background.  The daemon must
-# have local filesystem access to the source repositories, as it
-# uses objects/info/alternates to avoid copying objects.
-#
-# Add its companion post-receive-cinotify hook as the post-receive
-# hook to each repository that the daemon should monitor.  Yes, a
-# single daemon can monitor more than one repository.
-#
-# To use multiple daemons on the same system, give them each a
-# unique queue file and tmpdir.
-#
-# Global Config
-# -------------
-# Reads from a Git style configuration file.  This will be
-# ~/.gitconfig by default but can be overridden by setting
-# the GIT_CONFIG_FILE environment variable before starting.
-#
-# cidaemon.smtpHost
-#   Hostname of the SMTP server the daemon will send email
-#   through.  Defaults to 'localhost'.
-#
-# cidaemon.smtpUser
-#   Username to authenticate to the SMTP server as.  This
-#   variable is optional; if it is not supplied then no
-#   authentication will be performed.
-#
-# cidaemon.smtpPassword
-#   Password to authenticate to the SMTP server as.  This
-#   variable is optional.  If not supplied but smtpUser was,
-#   the daemon prompts for the password before forking into
-#   the background.
-#
-# cidaemon.smtpAuth
-#   Type of authentication to perform with the SMTP server.
-#   If set to 'login' and smtpUser was defined, this will
-#   use the AUTH LOGIN command, which is suitable for use
-#   with at least one version of Microsoft Exchange Server.
-#   If not set the daemon will use whatever auth methods
-#   are supported by your version of Net::SMTP.
-#
-# cidaemon.email
-#   Email address that daemon generated emails will be sent
-#   from.  This should be a useful email address within your
-#   organization.  Required.
-#
-# cidaemon.name
-#   Human friendly name that the daemon will send emails as.
-#   Defaults to 'cidaemon'.
-#
-# cidaemon.scanDelay
-#   Number of seconds to sleep between polls of the queue file.
-#   Defaults to 60.
-#
-# cidaemon.recentCache
-#   Number of recent commit SHA-1s per repository to cache and
-#   skip building if they appear again.  This is useful to avoid
-#   rebuilding the same commit multiple times just because it was
-#   pushed into more than one branch.  Defaults to 100.
-#
-# cidaemon.tmpdir
-#   Scratch directory to create the builds within.  The daemon
-#   makes a new subdirectory for each build, then deletes it when
-#   the build has finished.  The pid file is also placed here.
-#   Defaults to '/tmp'.
-#
-# cidaemon.queue
-#   Path to the queue file that the post-receive-cinotify hook
-#   appends events to.  This file is polled by the daemon.  It
-#   must not be on an NFS mount (uses flock).  Required.
-#
-# cidaemon.nocc
-#   Perl regex patterns to match against author and committer
-#   lines.  If a pattern matches, that author or committer will
-#   not be notified of a build failure.
-#
-# Per Repository Config
-# ----------------------
-# Read from the source repository's config file.
-#
-# builder.command
-#   Shell command to execute the build.  This command must
-#   return 0 on "success" and non-zero on failure.  If you
-#   also want to run a test suite, make sure your command
-#   does that too.  Required.
-#
-# builder.queue
-#   Queue file to notify the cidaemon through.  Should match
-#   cidaemon.queue.  If not set the hook will not notify the
-#   cidaemon.
-#
-# builder.skip
-#   Perl regex patterns of refs that should not be sent to
-#   cidaemon.  Updates of these refs will be ignored.
-#
-# builder.newBranchBase
-#   Glob patterns of refs that should be used to form the
-#   'old' revions of a newly created ref.  This should set
-#   to be globs that match your 'mainline' branches.  This
-#   way a build failure of a brand new topic branch does not
-#   attempt to email everyone since the beginning of time;
-#   instead it only emails those authors of commits not in
-#   these 'mainline' branches.
-
-local $ENV{PATH} = join ':', qw(
-	/opt/git/bin
-	/usr/bin
-	/bin
-	);
-
-use strict;
-use warnings;
-use FindBin qw($RealBin);
-use File::Spec;
-use lib File::Spec->catfile($RealBin, '..', 'perl5');
-use Storable qw(retrieve nstore);
-use Fcntl ':flock';
-use POSIX qw(strftime);
-use Getopt::Long qw(:config no_auto_abbrev auto_help);
-
-sub git_config ($;$)
-{
-	my $var = shift;
-	my $required = shift || 0;
-	local *GIT;
-	open GIT, '-|','git','config','--get',$var;
-	my $r = <GIT>;
-	chop $r if $r;
-	close GIT;
-	die "error: $var not set.\n" if ($required && !$r);
-	return $r;
-}
-
-package EXCHANGE_NET_SMTP;
-
-# Microsoft Exchange Server requires an 'AUTH LOGIN'
-# style of authentication.  This is different from
-# the default supported by Net::SMTP so we subclass
-# and override the auth method to support that.
-
-use Net::SMTP;
-use Net::Cmd;
-use MIME::Base64 qw(encode_base64);
-our @ISA = qw(Net::SMTP);
-our $auth_type = ::git_config 'cidaemon.smtpAuth';
-
-sub new
-{
-	my $self = shift;
-	my $type = ref($self) || $self;
-	$type->SUPER::new(@_);
-}
-
-sub auth
-{
-	my $self = shift;
-	return $self->SUPER::auth(@_) unless $auth_type eq 'login';
-
-	my $user = encode_base64 shift, '';
-	my $pass = encode_base64 shift, '';
-	return 0 unless CMD_MORE == $self->command("AUTH LOGIN")->response;
-	return 0 unless CMD_MORE == $self->command($user)->response;
-	CMD_OK == $self->command($pass)->response;
-}
-
-package main;
-
-my ($debug_flag, %recent);
-
-my $ex_host = git_config('cidaemon.smtpHost') || 'localhost';
-my $ex_user = git_config('cidaemon.smtpUser');
-my $ex_pass = git_config('cidaemon.smtpPassword');
-
-my $ex_from_addr = git_config('cidaemon.email', 1);
-my $ex_from_name = git_config('cidaemon.name') || 'cidaemon';
-
-my $scan_delay = git_config('cidaemon.scanDelay') || 60;
-my $recent_size = git_config('cidaemon.recentCache') || 100;
-my $tmpdir = git_config('cidaemon.tmpdir') || '/tmp';
-my $queue_name = git_config('cidaemon.queue', 1);
-my $queue_lock = "$queue_name.lock";
-
-my @nocc_list;
-open GIT,'git config --get-all cidaemon.nocc|';
-while (<GIT>) {
-	chop;
-	push @nocc_list, $_;
-}
-close GIT;
-
-sub nocc_author ($)
-{
-	local $_ = shift;
-	foreach my $pat (@nocc_list) {
-		return 1 if /$pat/;
-	}
-	0;
-}
-
-sub input_echo ($)
-{
-	my $prompt = shift;
-
-	local $| = 1;
-	print $prompt;
-	my $input = <STDIN>;
-	chop $input;
-	return $input;
-}
-
-sub input_noecho ($)
-{
-	my $prompt = shift;
-
-	my $end = sub {system('stty','echo');print "\n";exit};
-	local $SIG{TERM} = $end;
-	local $SIG{INT} = $end;
-	system('stty','-echo');
-
-	local $| = 1;
-	print $prompt;
-	my $input = <STDIN>;
-	system('stty','echo');
-	print "\n";
-	chop $input;
-	return $input;
-}
-
-sub rfc2822_date ()
-{
-	 strftime("%a, %d %b %Y %H:%M:%S %Z", localtime);
-}
-
-sub send_email ($$$)
-{
-	my ($subj, $body, $to) = @_;
-	my $now = rfc2822_date;
-	my $to_str = '';
-	my @rcpt_to;
-	foreach (@$to) {
-		my $s = $_;
-		$s =~ s/^/"/;
-		$s =~ s/(\s+<)/"$1/;
-		$to_str .= ', ' if $to_str;
-		$to_str .= $s;
-		push @rcpt_to, $1 if $s =~ /<(.*)>/;
-	}
-	die "Nobody to send to.\n" unless @rcpt_to;
-	my $msg = <<EOF;
-From: "$ex_from_name" <$ex_from_addr>
-To: $to_str
-Date: $now
-Subject: $subj
-
-$body
-EOF
-
-	my $smtp = EXCHANGE_NET_SMTP->new(Host => $ex_host)
-		or die "Cannot connect to $ex_host: $!\n";
-	if ($ex_user && $ex_pass) {
-		$smtp->auth($ex_user,$ex_pass)
-			or die "$ex_host rejected $ex_user\n";
-	}
-	$smtp->mail($ex_from_addr)
-		or die "$ex_host rejected $ex_from_addr\n";
-	scalar($smtp->recipient(@rcpt_to, { SkipBad => 1 }))
-		or die "$ex_host did not accept any addresses.\n";
-	$smtp->data($msg)
-		or die "$ex_host rejected message data\n";
-	$smtp->quit;
-}
-
-sub pop_queue ()
-{
-	open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
-	flock LOCK, LOCK_EX;
-
-	my $queue = -f $queue_name ? retrieve $queue_name : [];
-	my $ent = shift @$queue;
-	nstore $queue, $queue_name;
-
-	flock LOCK, LOCK_UN;
-	close LOCK;
-	$ent;
-}
-
-sub git_exec (@)
-{
-	system('git',@_) == 0 or die "Cannot git " . join(' ', @_) . "\n";
-}
-
-sub git_val (@)
-{
-	open(C, '-|','git',@_);
-	my $r = <C>;
-	chop $r if $r;
-	close C;
-	$r;
-}
-
-sub do_build ($$)
-{
-	my ($git_dir, $new) = @_;
-
-	my $tmp = File::Spec->catfile($tmpdir, "builder$$");
-	system('rm','-rf',$tmp) == 0 or die "Cannot clear $tmp\n";
-	die "Cannot clear $tmp.\n" if -e $tmp;
-
-	my $result = 1;
-	eval {
-		my $command;
-		{
-			local $ENV{GIT_DIR} = $git_dir;
-			$command = git_val 'config','builder.command';
-		}
-		die "No builder.command for $git_dir.\n" unless $command;
-
-		git_exec 'clone','-n','-l','-s',$git_dir,$tmp;
-		chmod 0700, $tmp or die "Cannot lock $tmp\n";
-		chdir $tmp or die "Cannot enter $tmp\n";
-
-		git_exec 'update-ref','HEAD',$new;
-		git_exec 'read-tree','-m','-u','HEAD','HEAD';
-		system $command;
-		if ($? == -1) {
-			print STDERR "failed to execute '$command': $!\n";
-			$result = 1;
-		} elsif ($? & 127) {
-			my $sig = $? & 127;
-			print STDERR "'$command' died from signal $sig\n";
-			$result = 1;
-		} else {
-			my $r = $? >> 8;
-			print STDERR "'$command' exited with $r\n" if $r;
-			$result = $r;
-		}
-	};
-	if ($@) {
-		$result = 2;
-		print STDERR "$@\n";
-	}
-
-	chdir '/';
-	system('rm','-rf',$tmp);
-	rmdir $tmp;
-	$result;
-}
-
-sub build_failed ($$$$$)
-{
-	my ($git_dir, $ref, $old, $new, $msg) = @_;
-
-	$git_dir =~ m,/([^/]+)$,;
-	my $repo_name = $1;
-	$ref =~ s,^refs/(heads|tags)/,,;
-
-	my %authors;
-	my $shortlog;
-	my $revstr;
-	{
-		local $ENV{GIT_DIR} = $git_dir;
-		my @revs = ($new);
-		push @revs, '--not', @$old if @$old;
-		open LOG,'-|','git','rev-list','--pretty=raw',@revs;
-		while (<LOG>) {
-			if (s/^(author|committer) //) {
-				chomp;
-				s/>.*$/>/;
-				$authors{$_} = 1 unless nocc_author $_;
-			}
-		}
-		close LOG;
-		open LOG,'-|','git','shortlog',@revs;
-		$shortlog .= $_ while <LOG>;
-		close LOG;
-		$revstr = join(' ', @revs);
-	}
-
-	my @to = sort keys %authors;
-	unless (@to) {
-		print STDERR "error: No authors in $revstr\n";
-		return;
-	}
-
-	my $subject = "[$repo_name] $ref : Build Failed";
-	my $body = <<EOF;
-Project: $git_dir
-Branch:  $ref
-Commits: $revstr
-
-$shortlog
-Build Output:
---------------------------------------------------------------
-$msg
-EOF
-	send_email($subject, $body, \@to);
-}
-
-sub run_build ($$$$)
-{
-	my ($git_dir, $ref, $old, $new) = @_;
-
-	if ($debug_flag) {
-		my @revs = ($new);
-		push @revs, '--not', @$old if @$old;
-		print "BUILDING $git_dir\n";
-		print "  BRANCH: $ref\n";
-		print "  COMMITS: ", join(' ', @revs), "\n";
-	}
-
-	local(*R, *W);
-	pipe R, W or die "cannot pipe builder: $!";
-
-	my $builder = fork();
-	if (!defined $builder) {
-		die "cannot fork builder: $!";
-	} elsif (0 == $builder) {
-		close R;
-		close STDIN;open(STDIN, '/dev/null');
-		open(STDOUT, '>&W');
-		open(STDERR, '>&W');
-		exit do_build $git_dir, $new;
-	} else {
-		close W;
-		my $out = '';
-		$out .= $_ while <R>;
-		close R;
-		waitpid $builder, 0;
-		build_failed $git_dir, $ref, $old, $new, $out if $?;
-	}
-
-	print "DONE\n\n" if $debug_flag;
-}
-
-sub daemon_loop ()
-{
-	my $run = 1;
-	my $stop_sub = sub {$run = 0};
-	$SIG{HUP} = $stop_sub;
-	$SIG{INT} = $stop_sub;
-	$SIG{TERM} = $stop_sub;
-
-	mkdir $tmpdir, 0755;
-	my $pidfile = File::Spec->catfile($tmpdir, "cidaemon.pid");
-	open(O, ">$pidfile"); print O "$$\n"; close O;
-
-	while ($run) {
-		my $ent = pop_queue;
-		if ($ent) {
-			my ($git_dir, $ref, $old, $new) = @$ent;
-
-			$ent = $recent{$git_dir};
-			$recent{$git_dir} = $ent = [[], {}] unless $ent;
-			my ($rec_arr, $rec_hash) = @$ent;
-			next if $rec_hash->{$new}++;
-			while (@$rec_arr >= $recent_size) {
-				my $to_kill = shift @$rec_arr;
-				delete $rec_hash->{$to_kill};
-			}
-			push @$rec_arr, $new;
-
-			run_build $git_dir, $ref, $old, $new;
-		} else {
-			sleep $scan_delay;
-		}
-	}
-
-	unlink $pidfile;
-}
-
-$debug_flag = 0;
-GetOptions(
-	'debug|d' => \$debug_flag,
-	'smtp-user=s' => \$ex_user,
-) or die "usage: $0 [--debug] [--smtp-user=user]\n";
-
-$ex_pass = input_noecho("$ex_user SMTP password: ")
-	if ($ex_user && !$ex_pass);
-
-if ($debug_flag) {
-	daemon_loop;
-	exit 0;
-}
-
-my $daemon = fork();
-if (!defined $daemon) {
-	die "cannot fork daemon: $!";
-} elsif (0 == $daemon) {
-	close STDIN;open(STDIN, '/dev/null');
-	close STDOUT;open(STDOUT, '>/dev/null');
-	close STDERR;open(STDERR, '>/dev/null');
-	daemon_loop;
-	exit 0;
-} else {
-	print "Daemon $daemon running in the background.\n";
-}
diff --git a/contrib/continuous/post-receive-cinotify b/contrib/continuous/post-receive-cinotify
deleted file mode 100644
index b8f5a60..0000000
--- a/contrib/continuous/post-receive-cinotify
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/perl
-#
-# A hook that notifies its companion cidaemon through a simple
-# queue file that a ref has been updated via a push (actually
-# by a receive-pack running on the server).
-#
-# See cidaemon for per-repository configuration details.
-#
-# To use this hook, add it as the post-receive hook, make it
-# executable, and set its configuration options.
-#
-
-local $ENV{PATH} = '/opt/git/bin';
-
-use strict;
-use warnings;
-use File::Spec;
-use Storable qw(retrieve nstore);
-use Fcntl ':flock';
-
-my $git_dir = File::Spec->rel2abs($ENV{GIT_DIR});
-my $queue_name = `git config --get builder.queue`;chop $queue_name;
-$queue_name =~ m,^([^\s]+)$,; $queue_name = $1; # untaint
-unless ($queue_name) {
-	1 while <STDIN>;
-	print STDERR "\nerror: builder.queue not set.  Not enqueing.\n\n";
-	exit;
-}
-my $queue_lock = "$queue_name.lock";
-
-my @skip;
-open S, "git config --get-all builder.skip|";
-while (<S>) {
-	chop;
-	push @skip, $_;
-}
-close S;
-
-my @new_branch_base;
-open S, "git config --get-all builder.newBranchBase|";
-while (<S>) {
-	chop;
-	push @new_branch_base, $_;
-}
-close S;
-
-sub skip ($)
-{
-	local $_ = shift;
-	foreach my $p (@skip) {
-		return 1 if /^$p/;
-	}
-	0;
-}
-
-open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
-flock LOCK, LOCK_EX;
-
-my $queue = -f $queue_name ? retrieve $queue_name : [];
-my %existing;
-foreach my $r (@$queue) {
-	my ($gd, $ref) = @$r;
-	$existing{$gd}{$ref} = $r;
-}
-
-my @new_branch_commits;
-my $loaded_new_branch_commits = 0;
-
-while (<STDIN>) {
-	chop;
-	my ($old, $new, $ref) = split / /, $_, 3;
-
-	next if $old eq $new;
-	next if $new =~ /^0{40}$/;
-	next if skip $ref;
-
-	my $r = $existing{$git_dir}{$ref};
-	if ($r) {
-		$r->[3] = $new;
-	} else {
-		if ($old =~ /^0{40}$/) {
-			if (!$loaded_new_branch_commits && @new_branch_base) {
-				open M,'-|','git','show-ref',@new_branch_base;
-				while (<M>) {
-					($_) = split / /, $_;
-					push @new_branch_commits, $_;
-				}
-				close M;
-				$loaded_new_branch_commits = 1;
-			}
-			$old = [@new_branch_commits];
-		} else {
-			$old = [$old];
-		}
-
-		$r = [$git_dir, $ref, $old, $new];
-		$existing{$git_dir}{$ref} = $r;
-		push @$queue, $r;
-	}
-}
-nstore $queue, $queue_name;
-
-flock LOCK, LOCK_UN;
-close LOCK;
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 3940202..bcd3f57 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -127,10 +127,20 @@
 		*v++ = '\0';
 
 		if (!strcmp(buf, "protocol")) {
-			if (!strcmp(v, "https"))
+			if (!strcmp(v, "imap"))
+				protocol = kSecProtocolTypeIMAP;
+			else if (!strcmp(v, "imaps"))
+				protocol = kSecProtocolTypeIMAPS;
+			else if (!strcmp(v, "ftp"))
+				protocol = kSecProtocolTypeFTP;
+			else if (!strcmp(v, "ftps"))
+				protocol = kSecProtocolTypeFTPS;
+			else if (!strcmp(v, "https"))
 				protocol = kSecProtocolTypeHTTPS;
 			else if (!strcmp(v, "http"))
 				protocol = kSecProtocolTypeHTTP;
+			else if (!strcmp(v, "smtp"))
+				protocol = kSecProtocolTypeSMTP;
 			else /* we don't yet handle other protocols */
 				exit(0);
 		}
diff --git a/contrib/mw-to-git/.perlcriticrc b/contrib/mw-to-git/.perlcriticrc
new file mode 100644
index 0000000..5a9955d
--- /dev/null
+++ b/contrib/mw-to-git/.perlcriticrc
@@ -0,0 +1,28 @@
+# These 3 rules demand to add the s, m and x flag to *every* regexp. This is
+# overkill and would be harmful for readability.
+[-RegularExpressions::RequireExtendedFormatting]
+[-RegularExpressions::RequireDotMatchAnything]
+[-RegularExpressions::RequireLineBoundaryMatching]
+
+# This rule says that builtin functions should not be called with parentheses
+# e.g.: (taken from CPAN's documentation)
+# open($handle, '>', $filename); #not ok
+# open $handle, '>', $filename;  #ok
+# Applying such a rule would mean modifying a huge number of lines for a
+# question of style.
+[-CodeLayout::ProhibitParensWithBuiltins]
+
+# This rule states that each system call should have its return value checked
+# The problem is that it includes the print call. Checking every print call's
+# return value would be harmful to the code readabilty.
+# This configuration keeps all default function but print.
+[InputOutput::RequireCheckedSyscalls]
+functions = open say close
+
+# This rules demands to add a dependancy for the Readonly module. This is not
+# wished.
+[-ValuesAndExpressions::ProhibitConstantPragma]
+
+# This rule is not really useful (rather a question of style) and produces many
+# warnings among the code.
+[-ValuesAndExpressions::ProhibitNoisyQuotes]
diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm
new file mode 100644
index 0000000..d13c4df
--- /dev/null
+++ b/contrib/mw-to-git/Git/Mediawiki.pm
@@ -0,0 +1,100 @@
+package Git::Mediawiki;
+
+use 5.008;
+use strict;
+use Git;
+
+BEGIN {
+
+our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);
+
+# Totally unstable API.
+$VERSION = '0.01';
+
+require Exporter;
+
+@ISA = qw(Exporter);
+
+@EXPORT = ();
+
+# Methods which can be called as standalone functions as well:
+@EXPORT_OK = qw(clean_filename smudge_filename connect_maybe
+				EMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND);
+}
+
+# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
+use constant SLASH_REPLACEMENT => '%2F';
+
+# Used to test for empty strings
+use constant EMPTY => q{};
+
+# HTTP codes
+use constant HTTP_CODE_OK => 200;
+use constant HTTP_CODE_PAGE_NOT_FOUND => 404;
+
+sub clean_filename {
+	my $filename = shift;
+	$filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g;
+	# [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.
+	# Do a variant of URL-encoding, i.e. looks like URL-encoding,
+	# but with _ added to prevent MediaWiki from thinking this is
+	# an actual special character.
+	$filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge;
+	# If we use the uri escape before
+	# we should unescape here, before anything
+
+	return $filename;
+}
+
+sub smudge_filename {
+	my $filename = shift;
+	$filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g;
+	$filename =~ s/ /_/g;
+	# Decode forbidden characters encoded in clean_filename
+	$filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge;
+	return $filename;
+}
+
+sub connect_maybe {
+	my $wiki = shift;
+	if ($wiki) {
+		return $wiki;
+	}
+
+	my $remote_name = shift;
+	my $remote_url = shift;
+	my ($wiki_login, $wiki_password, $wiki_domain);
+
+	$wiki_login = Git::config("remote.${remote_name}.mwLogin");
+	$wiki_password = Git::config("remote.${remote_name}.mwPassword");
+	$wiki_domain = Git::config("remote.${remote_name}.mwDomain");
+
+	$wiki = MediaWiki::API->new;
+	$wiki->{config}->{api_url} = "${remote_url}/api.php";
+	if ($wiki_login) {
+		my %credential = (
+			'url' => $remote_url,
+			'username' => $wiki_login,
+			'password' => $wiki_password
+		);
+		Git::credential(\%credential);
+		my $request = {lgname => $credential{username},
+			       lgpassword => $credential{password},
+			       lgdomain => $wiki_domain};
+		if ($wiki->login($request)) {
+			Git::credential(\%credential, 'approve');
+			print {*STDERR} qq(Logged in mediawiki user "$credential{username}".\n);
+		} else {
+			print {*STDERR} qq(Failed to log in mediawiki user "$credential{username}" on ${remote_url}\n);
+			print {*STDERR} '  (error ' .
+				$wiki->{error}->{code} . ': ' .
+				$wiki->{error}->{details} . ")\n";
+			Git::credential(\%credential, 'reject');
+			exit 1;
+		}
+	}
+
+	return $wiki;
+}
+
+1; # Famous last words
diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile
index f149719..76fcd4d 100644
--- a/contrib/mw-to-git/Makefile
+++ b/contrib/mw-to-git/Makefile
@@ -2,16 +2,43 @@
 # Copyright (C) 2013
 #     Matthieu Moy <Matthieu.Moy@imag.fr>
 #
-## Build git-remote-mediawiki
+# To build and test:
+#
+#   make
+#   bin-wrapper/git mw preview Some_page.mw
+#   bin-wrapper/git clone mediawiki::http://example.com/wiki/
+#
+# To install, run Git's toplevel 'make install' then run:
+#
+#   make install
 
+GIT_MEDIAWIKI_PM=Git/Mediawiki.pm
 SCRIPT_PERL=git-remote-mediawiki.perl
+SCRIPT_PERL+=git-mw.perl
 GIT_ROOT_DIR=../..
 HERE=contrib/mw-to-git/
 
 SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL))
+INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/perl \
+                -s --no-print-directory instlibdir)
 
 all: build
 
-build install clean:
-	$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=$(SCRIPT_PERL_FULL) \
-                $@-perl-script
+install_pm:
+	install $(GIT_MEDIAWIKI_PM) $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
+
+build:
+	$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                build-perl-script
+
+install: install_pm
+	$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                install-perl-script
+
+clean:
+	$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                clean-perl-script
+	rm $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
+
+perlcritic:
+	perlcritic -2 *.perl
diff --git a/contrib/mw-to-git/bin-wrapper/git b/contrib/mw-to-git/bin-wrapper/git
new file mode 100755
index 0000000..6663ae5
--- /dev/null
+++ b/contrib/mw-to-git/bin-wrapper/git
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# git executable wrapper script for Git-Mediawiki to run tests without
+# installing all the scripts and perl packages.
+
+GIT_ROOT_DIR=../../..
+GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd ${GIT_ROOT_DIR} && pwd)
+
+GITPERLLIB="$GIT_EXEC_PATH"'/contrib/mw-to-git'"${GITPERLLIB:+:$GITPERLLIB}"
+PATH="$GIT_EXEC_PATH"'/contrib/mw-to-git:'"$PATH"
+
+export GITPERLLIB PATH
+
+exec "${GIT_EXEC_PATH}/bin-wrappers/git" "$@"
diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl
new file mode 100755
index 0000000..28df3ee
--- /dev/null
+++ b/contrib/mw-to-git/git-mw.perl
@@ -0,0 +1,368 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2013
+#     Benoit Person <benoit.person@ensimag.imag.fr>
+#     Celestin Matte <celestin.matte@ensimag.imag.fr>
+# License: GPL v2 or later
+
+# Set of tools for git repo with a mediawiki remote.
+# Documentation & bugtracker: https://github.com/moy/Git-Mediawiki/
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use URI::URL qw(url);
+use LWP::UserAgent;
+use HTML::TreeBuilder;
+
+use Git;
+use MediaWiki::API;
+use Git::Mediawiki qw(clean_filename connect_maybe
+					EMPTY HTTP_CODE_PAGE_NOT_FOUND);
+
+# By default, use UTF-8 to communicate with Git and the user
+binmode STDERR, ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
+
+# Global parameters
+my $verbose = 0;
+sub v_print {
+	if ($verbose) {
+		return print {*STDERR} @_;
+	}
+	return;
+}
+
+# Preview parameters
+my $file_name = EMPTY;
+my $remote_name = EMPTY;
+my $preview_file_name = EMPTY;
+my $autoload = 0;
+sub file {
+	$file_name = shift;
+	return $file_name;
+}
+
+my %commands = (
+	'help' =>
+		[\&help, {}, \&help],
+	'preview' =>
+		[\&preview, {
+			'<>' => \&file,
+			'output|o=s' => \$preview_file_name,
+			'remote|r=s' => \$remote_name,
+			'autoload|a' => \$autoload
+		}, \&preview_help]
+);
+
+# Search for sub-command
+my $cmd = $commands{'help'};
+for (0..@ARGV-1) {
+	if (defined $commands{$ARGV[$_]}) {
+		$cmd = $commands{$ARGV[$_]};
+		splice @ARGV, $_, 1;
+		last;
+	}
+};
+GetOptions( %{$cmd->[1]},
+	'help|h' => \&{$cmd->[2]},
+	'verbose|v'  => \$verbose);
+
+# Launch command
+&{$cmd->[0]};
+
+############################# Preview Functions ################################
+
+sub preview_help {
+	print {*STDOUT} <<'END';
+USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a]
+                      [--output|-o <output filename>] [--verbose|-v]
+                      <blob> | <filename>
+
+DESCRIPTION:
+Preview is an utiliy to preview local content of a mediawiki repo as if it was
+pushed on the remote.
+
+For that, preview searches for the remote name of the current branch's
+upstream if --remote is not set. If that remote is not found or if it
+is not a mediawiki, it lists all mediawiki remotes configured and asks
+you to replay your command with the --remote option set properly.
+
+Then, it searches for a file named 'filename'. If it's not found in
+the current dir, it will assume it's a blob.
+
+The content retrieved in the file (or in the blob) will then be parsed
+by the remote mediawiki and combined with a template retrieved from
+the mediawiki.
+
+Finally, preview will save the HTML result in a file. and autoload it
+in your default web browser if the option --autoload is present.
+
+OPTIONS:
+    -r <remote name>, --remote <remote name>
+        If the remote is a mediawiki, the template and the parse engine
+        used for the preview will be those of that remote.
+        If not, a list of valid remotes will be shown.
+
+    -a, --autoload
+        Try to load the HTML output in a new tab (or new window) of your
+        default web browser.
+
+    -o <output filename>, --output <output filename>
+        Change the HTML output filename. Default filename is based on the
+        input filename with its extension replaced by '.html'.
+
+    -v, --verbose
+        Show more information on what's going on under the hood.
+END
+	exit;
+}
+
+sub preview {
+	my $wiki;
+	my ($remote_url, $wiki_page_name);
+	my ($new_content, $template);
+	my $file_content;
+
+	if ($file_name eq EMPTY) {
+		die "Missing file argument, see `git mw help`\n";
+	}
+
+	v_print("### Selecting remote\n");
+	if ($remote_name eq EMPTY) {
+		$remote_name = find_upstream_remote_name();
+		if ($remote_name) {
+			$remote_url = mediawiki_remote_url_maybe($remote_name);
+		}
+
+		if (! $remote_url) {
+			my @valid_remotes = find_mediawiki_remotes();
+
+			if ($#valid_remotes == 0) {
+				print {*STDERR} "No mediawiki remote in this repo. \n";
+				exit 1;
+			} else {
+				my $remotes_list = join("\n\t", @valid_remotes);
+				print {*STDERR} <<"MESSAGE";
+There are multiple mediawiki remotes, which of:
+	${remotes_list}
+do you want ? Use the -r option to specify the remote.
+MESSAGE
+			}
+
+			exit 1;
+		}
+	} else {
+		if (!is_valid_remote($remote_name)) {
+			die "${remote_name} is not a remote\n";
+		}
+
+		$remote_url = mediawiki_remote_url_maybe($remote_name);
+		if (! $remote_url) {
+			die "${remote_name} is not a mediawiki remote\n";
+		}
+	}
+	v_print("selected remote:\n\tname: ${remote_name}\n\turl: ${remote_url}\n");
+
+	$wiki = connect_maybe($wiki, $remote_name, $remote_url);
+
+	# Read file content
+	if (! -e $file_name) {
+		$file_content = git_cmd_try {
+			Git::command('cat-file', 'blob', $file_name); }
+			"%s failed w/ code %d";
+
+		if ($file_name =~ /(.+):(.+)/) {
+			$file_name = $2;
+		}
+	} else {
+		open my $read_fh, "<", $file_name
+			or die "could not open ${file_name}: $!\n";
+		$file_content = do { local $/ = undef; <$read_fh> };
+		close $read_fh
+			or die "unable to close: $!\n";
+	}
+
+	v_print("### Retrieving template\n");
+	($wiki_page_name = clean_filename($file_name)) =~ s/\.[^.]+$//;
+	$template = get_template($remote_url, $wiki_page_name);
+
+	v_print("### Parsing local content\n");
+	$new_content = $wiki->api({
+		action => 'parse',
+		text => $file_content,
+		title => $wiki_page_name
+	}, {
+		skip_encoding => 1
+	}) or die "No response from remote mediawiki\n";
+	$new_content = $new_content->{'parse'}->{'text'}->{'*'};
+
+	v_print("### Merging contents\n");
+	if ($preview_file_name eq EMPTY) {
+		($preview_file_name = $file_name) =~ s/\.[^.]+$/.html/;
+	}
+	open(my $save_fh, '>:encoding(UTF-8)', $preview_file_name)
+		or die "Could not open: $!\n";
+	print {$save_fh} merge_contents($template, $new_content, $remote_url);
+	close($save_fh)
+		or die "Could not close: $!\n";
+
+	v_print("### Results\n");
+	if ($autoload) {
+		v_print("Launching browser w/ file: ${preview_file_name}");
+		system('git', 'web--browse', $preview_file_name);
+	} else {
+		print {*STDERR} "Preview file saved as: ${preview_file_name}\n";
+	}
+
+	exit;
+}
+
+# uses global scope variable: $remote_name
+sub merge_contents {
+	my $template = shift;
+	my $content = shift;
+	my $remote_url = shift;
+	my ($content_tree, $html_tree, $mw_content_text);
+	my $template_content_id = 'bodyContent';
+
+	$html_tree = HTML::TreeBuilder->new;
+	$html_tree->parse($template);
+
+	$content_tree = HTML::TreeBuilder->new;
+	$content_tree->parse($content);
+
+	$template_content_id = Git::config("remote.${remote_name}.mwIDcontent")
+		|| $template_content_id;
+	v_print("Using '${template_content_id}' as the content ID\n");
+
+	$mw_content_text = $html_tree->look_down('id', $template_content_id);
+	if (!defined $mw_content_text) {
+		print {*STDERR} <<"CONFIG";
+Could not combine the new content with the template. You might want to
+configure `mediawiki.IDContent` in your config:
+	git config --add remote.${remote_name}.mwIDcontent <id>
+and re-run the command afterward.
+CONFIG
+		exit 1;
+	}
+	$mw_content_text->delete_content();
+	$mw_content_text->push_content($content_tree);
+
+	make_links_absolute($html_tree, $remote_url);
+
+	return $html_tree->as_HTML;
+}
+
+sub make_links_absolute {
+	my $html_tree = shift;
+	my $remote_url = shift;
+	for (@{ $html_tree->extract_links() }) {
+		my ($link, $element, $attr) = @{ $_ };
+		my $url = url($link)->canonical;
+		if ($url !~ /#/) {
+			$element->attr($attr, URI->new_abs($url, $remote_url));
+		}
+	}
+	return $html_tree;
+}
+
+sub is_valid_remote {
+	my $remote = shift;
+	my @remotes = git_cmd_try {
+		Git::command('remote') }
+		"%s failed w/ code %d";
+	my $found_remote = 0;
+	foreach my $remote (@remotes) {
+		if ($remote eq $remote) {
+			$found_remote = 1;
+			last;
+		}
+	}
+	return $found_remote;
+}
+
+sub find_mediawiki_remotes {
+	my @remotes = git_cmd_try {
+		Git::command('remote'); }
+		"%s failed w/ code %d";
+	my $remote_url;
+	my @valid_remotes = ();
+	foreach my $remote (@remotes) {
+		$remote_url = mediawiki_remote_url_maybe($remote);
+		if ($remote_url) {
+			push(@valid_remotes, $remote);
+		}
+	}
+	return @valid_remotes;
+}
+
+sub find_upstream_remote_name {
+	my $current_branch = git_cmd_try {
+		Git::command_oneline('symbolic-ref', '--short', 'HEAD') }
+		"%s failed w/ code %d";
+	return Git::config("branch.${current_branch}.remote");
+}
+
+sub mediawiki_remote_url_maybe {
+	my $remote = shift;
+
+	# Find remote url
+	my $remote_url = Git::config("remote.${remote}.url");
+	if ($remote_url =~ s/mediawiki::(.*)/$1/) {
+		return url($remote_url)->canonical;
+	}
+
+	return;
+}
+
+sub get_template {
+	my $url = shift;
+	my $page_name = shift;
+	my ($req, $res, $code, $url_after);
+
+	$req = LWP::UserAgent->new;
+	if ($verbose) {
+		$req->show_progress(1);
+	}
+
+	$res = $req->get("${url}/index.php?title=${page_name}");
+	if (!$res->is_success) {
+		$code = $res->code;
+		$url_after = $res->request()->uri(); # resolve all redirections
+		if ($code == HTTP_CODE_PAGE_NOT_FOUND) {
+			if ($verbose) {
+				print {*STDERR} <<"WARNING";
+Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want
+all the links to work properly.
+Trying to use the mediawiki homepage as a fallback template ...
+WARNING
+			}
+
+			# LWP automatically redirects GET request
+			$res = $req->get("${url}/index.php");
+			if (!$res->is_success) {
+				$url_after = $res->request()->uri(); # resolve all redirections
+				die "Failed to get homepage @ ${url_after} w/ code ${code}\n";
+			}
+		} else {
+			die "Failed to get '${page_name}' @ ${url_after} w/ code ${code}\n";
+		}
+	}
+
+	return $res->decoded_content;
+}
+
+############################## Help Functions ##################################
+
+sub help {
+	print {*STDOUT} <<'END';
+usage: git mw <command> <args>
+
+git mw commands are:
+    help        Display help information about git mw
+    preview     Parse and render local file into HTML
+END
+	exit;
+}
diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl
index 9c14c1f..f8d7d2c 100755
--- a/contrib/mw-to-git/git-remote-mediawiki.perl
+++ b/contrib/mw-to-git/git-remote-mediawiki.perl
@@ -13,19 +13,17 @@
 
 use strict;
 use MediaWiki::API;
+use Git;
+use Git::Mediawiki qw(clean_filename smudge_filename connect_maybe
+					EMPTY HTTP_CODE_OK);
 use DateTime::Format::ISO8601;
-
-# By default, use UTF-8 to communicate with Git and the user
-binmode STDERR, ":utf8";
-binmode STDOUT, ":utf8";
-
-use URI::Escape;
-use IPC::Open2;
-
 use warnings;
 
-# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
-use constant SLASH_REPLACEMENT => "%2F";
+# By default, use UTF-8 to communicate with Git and the user
+binmode STDERR, ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
+
+use URI::Escape;
 
 # It's not always possible to delete pages (may require some
 # privileges). Deleted pages are replaced with this content.
@@ -36,45 +34,57 @@
 use constant EMPTY_CONTENT => "<!-- empty page -->\n";
 
 # used to reflect file creation or deletion in diff.
-use constant NULL_SHA1 => "0000000000000000000000000000000000000000";
+use constant NULL_SHA1 => '0000000000000000000000000000000000000000';
 
 # Used on Git's side to reflect empty edit messages on the wiki
 use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*';
 
+# Number of pages taken into account at once in submodule get_mw_page_list
+use constant SLICE_SIZE => 50;
+
+# Number of linked mediafile to get at once in get_linked_mediafiles
+# The query is split in small batches because of the MW API limit of
+# the number of links to be returned (500 links max).
+use constant BATCH_SIZE => 10;
+
+if (@ARGV != 2) {
+	exit_error_usage();
+}
+
 my $remotename = $ARGV[0];
 my $url = $ARGV[1];
 
 # Accept both space-separated and multiple keys in config file.
 # Spaces should be written as _ anyway because we'll use chomp.
-my @tracked_pages = split(/[ \n]/, run_git("config --get-all remote.". $remotename .".pages"));
+my @tracked_pages = split(/[ \n]/, run_git("config --get-all remote.${remotename}.pages"));
 chomp(@tracked_pages);
 
 # Just like @tracked_pages, but for MediaWiki categories.
-my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.". $remotename .".categories"));
+my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.${remotename}.categories"));
 chomp(@tracked_categories);
 
 # Import media files on pull
-my $import_media = run_git("config --get --bool remote.". $remotename .".mediaimport");
+my $import_media = run_git("config --get --bool remote.${remotename}.mediaimport");
 chomp($import_media);
-$import_media = ($import_media eq "true");
+$import_media = ($import_media eq 'true');
 
 # Export media files on push
-my $export_media = run_git("config --get --bool remote.". $remotename .".mediaexport");
+my $export_media = run_git("config --get --bool remote.${remotename}.mediaexport");
 chomp($export_media);
-$export_media = !($export_media eq "false");
+$export_media = !($export_media eq 'false');
 
-my $wiki_login = run_git("config --get remote.". $remotename .".mwLogin");
+my $wiki_login = run_git("config --get remote.${remotename}.mwLogin");
 # Note: mwPassword is discourraged. Use the credential system instead.
-my $wiki_passwd = run_git("config --get remote.". $remotename .".mwPassword");
-my $wiki_domain = run_git("config --get remote.". $remotename .".mwDomain");
+my $wiki_passwd = run_git("config --get remote.${remotename}.mwPassword");
+my $wiki_domain = run_git("config --get remote.${remotename}.mwDomain");
 chomp($wiki_login);
 chomp($wiki_passwd);
 chomp($wiki_domain);
 
 # Import only last revisions (both for clone and fetch)
-my $shallow_import = run_git("config --get --bool remote.". $remotename .".shallow");
+my $shallow_import = run_git("config --get --bool remote.${remotename}.shallow");
 chomp($shallow_import);
-$shallow_import = ($shallow_import eq "true");
+$shallow_import = ($shallow_import eq 'true');
 
 # Fetch (clone and pull) by revisions instead of by pages. This behavior
 # is more efficient when we have a wiki with lots of pages and we fetch
@@ -82,15 +92,18 @@
 # Possible values:
 # - by_rev: perform one query per new revision on the remote wiki
 # - by_page: query each tracked page for new revision
-my $fetch_strategy = run_git("config --get remote.$remotename.fetchStrategy");
-unless ($fetch_strategy) {
-	$fetch_strategy = run_git("config --get mediawiki.fetchStrategy");
+my $fetch_strategy = run_git("config --get remote.${remotename}.fetchStrategy");
+if (!$fetch_strategy) {
+	$fetch_strategy = run_git('config --get mediawiki.fetchStrategy');
 }
 chomp($fetch_strategy);
-unless ($fetch_strategy) {
-	$fetch_strategy = "by_page";
+if (!$fetch_strategy) {
+	$fetch_strategy = 'by_page';
 }
 
+# Remember the timestamp corresponding to a revision id.
+my %basetimestamps;
+
 # Dumb push: don't update notes and mediawiki ref to reflect the last push.
 #
 # Configurable with mediawiki.dumbPush, or per-remote with
@@ -105,48 +118,25 @@
 # will get the history with information lost). If the import is
 # deterministic, this means everybody gets the same sha1 for each
 # MediaWiki revision.
-my $dumb_push = run_git("config --get --bool remote.$remotename.dumbPush");
-unless ($dumb_push) {
-	$dumb_push = run_git("config --get --bool mediawiki.dumbPush");
+my $dumb_push = run_git("config --get --bool remote.${remotename}.dumbPush");
+if (!$dumb_push) {
+	$dumb_push = run_git('config --get --bool mediawiki.dumbPush');
 }
 chomp($dumb_push);
-$dumb_push = ($dumb_push eq "true");
+$dumb_push = ($dumb_push eq 'true');
 
 my $wiki_name = $url;
-$wiki_name =~ s/[^\/]*:\/\///;
+$wiki_name =~ s{[^/]*://}{};
 # If URL is like http://user:password@example.com/, we clearly don't
 # want the password in $wiki_name. While we're there, also remove user
 # and '@' sign, to avoid author like MWUser@HTTPUser@host.com
 $wiki_name =~ s/^.*@//;
 
 # Commands parser
-my $entry;
-my @cmd;
 while (<STDIN>) {
 	chomp;
-	@cmd = split(/ /);
-	if (defined($cmd[0])) {
-		# Line not blank
-		if ($cmd[0] eq "capabilities") {
-			die("Too many arguments for capabilities") unless (!defined($cmd[1]));
-			mw_capabilities();
-		} elsif ($cmd[0] eq "list") {
-			die("Too many arguments for list") unless (!defined($cmd[2]));
-			mw_list($cmd[1]);
-		} elsif ($cmd[0] eq "import") {
-			die("Invalid arguments for import") unless ($cmd[1] ne "" && !defined($cmd[2]));
-			mw_import($cmd[1]);
-		} elsif ($cmd[0] eq "option") {
-			die("Too many arguments for option") unless ($cmd[1] ne "" && $cmd[2] ne "" && !defined($cmd[3]));
-			mw_option($cmd[1],$cmd[2]);
-		} elsif ($cmd[0] eq "push") {
-			mw_push($cmd[1]);
-		} else {
-			print STDERR "Unknown command. Aborting...\n";
-			last;
-		}
-	} else {
-		# blank line: we should terminate
+
+	if (!parse_command($_)) {
 		last;
 	}
 
@@ -156,107 +146,91 @@
 
 ########################## Functions ##############################
 
-## credential API management (generic functions)
-
-sub credential_read {
-	my %credential;
-	my $reader = shift;
-	my $op = shift;
-	while (<$reader>) {
-		my ($key, $value) = /([^=]*)=(.*)/;
-		if (not defined $key) {
-			die "ERROR receiving response from git credential $op:\n$_\n";
-		}
-		$credential{$key} = $value;
-	}
-	return %credential;
+## error handling
+sub exit_error_usage {
+	die "ERROR: git-remote-mediawiki module was not called with a correct number of\n" .
+	    "parameters\n" .
+	    "You may obtain this error because you attempted to run the git-remote-mediawiki\n" .
+            "module directly.\n" .
+	    "This module can be used the following way:\n" .
+	    "\tgit clone mediawiki://<address of a mediawiki>\n" .
+	    "Then, use git commit, push and pull as with every normal git repository.\n";
 }
 
-sub credential_write {
-	my $credential = shift;
-	my $writer = shift;
-	# url overwrites other fields, so it must come first
-	print $writer "url=$credential->{url}\n" if exists $credential->{url};
-	while (my ($key, $value) = each(%$credential) ) {
-		if (length $value && $key ne 'url') {
-			print $writer "$key=$value\n";
-		}
+sub parse_command {
+	my ($line) = @_;
+	my @cmd = split(/ /, $line);
+	if (!defined $cmd[0]) {
+		return 0;
 	}
-}
-
-sub credential_run {
-	my $op = shift;
-	my $credential = shift;
-	my $pid = open2(my $reader, my $writer, "git credential $op");
-	credential_write($credential, $writer);
-	print $writer "\n";
-	close($writer);
-
-	if ($op eq "fill") {
-		%$credential = credential_read($reader, $op);
+	if ($cmd[0] eq 'capabilities') {
+		die("Too many arguments for capabilities\n")
+		    if (defined($cmd[1]));
+		mw_capabilities();
+	} elsif ($cmd[0] eq 'list') {
+		die("Too many arguments for list\n") if (defined($cmd[2]));
+		mw_list($cmd[1]);
+	} elsif ($cmd[0] eq 'import') {
+		die("Invalid argument for import\n")
+		    if ($cmd[1] eq EMPTY);
+		die("Too many arguments for import\n")
+		    if (defined($cmd[2]));
+		mw_import($cmd[1]);
+	} elsif ($cmd[0] eq 'option') {
+		die("Invalid arguments for option\n")
+		    if ($cmd[1] eq EMPTY || $cmd[2] eq EMPTY);
+		die("Too many arguments for option\n")
+		    if (defined($cmd[3]));
+		mw_option($cmd[1],$cmd[2]);
+	} elsif ($cmd[0] eq 'push') {
+		mw_push($cmd[1]);
 	} else {
-		if (<$reader>) {
-			die "ERROR while running git credential $op:\n$_";
-		}
+		print {*STDERR} "Unknown command. Aborting...\n";
+		return 0;
 	}
-	close($reader);
-	waitpid($pid, 0);
-	my $child_exit_status = $? >> 8;
-	if ($child_exit_status != 0) {
-		die "'git credential $op' failed with code $child_exit_status.";
-	}
+	return 1;
 }
 
 # MediaWiki API instance, created lazily.
 my $mediawiki;
 
-sub mw_connect_maybe {
-	if ($mediawiki) {
-		return;
+sub fatal_mw_error {
+	my $action = shift;
+	print STDERR "fatal: could not $action.\n";
+	print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
+	if ($url =~ /^https/) {
+		print STDERR "fatal: make sure '$url/api.php' is a valid page\n";
+		print STDERR "fatal: and the SSL certificate is correct.\n";
+	} else {
+		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
 	}
-	$mediawiki = MediaWiki::API->new;
-	$mediawiki->{config}->{api_url} = "$url/api.php";
-	if ($wiki_login) {
-		my %credential = (url => $url);
-		$credential{username} = $wiki_login;
-		$credential{password} = $wiki_passwd;
-		credential_run("fill", \%credential);
-		my $request = {lgname => $credential{username},
-			       lgpassword => $credential{password},
-			       lgdomain => $wiki_domain};
-		if ($mediawiki->login($request)) {
-			credential_run("approve", \%credential);
-			print STDERR "Logged in mediawiki user \"$credential{username}\".\n";
-		} else {
-			print STDERR "Failed to log in mediawiki user \"$credential{username}\" on $url\n";
-			print STDERR "  (error " .
-				$mediawiki->{error}->{code} . ': ' .
-				$mediawiki->{error}->{details} . ")\n";
-			credential_run("reject", \%credential);
-			exit 1;
-		}
-	}
+	print STDERR "fatal: (error " .
+	    $mediawiki->{error}->{code} . ': ' .
+	    $mediawiki->{error}->{details} . ")\n";
+	exit 1;
 }
 
 ## Functions for listing pages on the remote wiki
 sub get_mw_tracked_pages {
 	my $pages = shift;
 	get_mw_page_list(\@tracked_pages, $pages);
+	return;
 }
 
 sub get_mw_page_list {
 	my $page_list = shift;
 	my $pages = shift;
-	my @some_pages = @$page_list;
+	my @some_pages = @{$page_list};
 	while (@some_pages) {
-		my $last = 50;
-		if ($#some_pages < $last) {
-			$last = $#some_pages;
+		my $last_page = SLICE_SIZE;
+		if ($#some_pages < $last_page) {
+			$last_page = $#some_pages;
 		}
-		my @slice = @some_pages[0..$last];
+		my @slice = @some_pages[0..$last_page];
 		get_mw_first_pages(\@slice, $pages);
-		@some_pages = @some_pages[51..$#some_pages];
+		@some_pages = @some_pages[(SLICE_SIZE + 1)..$#some_pages];
 	}
+	return;
 }
 
 sub get_mw_tracked_categories {
@@ -266,7 +240,7 @@
 			# Mediawiki requires the Category
 			# prefix, but let's not force the user
 			# to specify it.
-			$category = "Category:" . $category;
+			$category = "Category:${category}";
 		}
 		my $mw_pages = $mediawiki->list( {
 			action => 'query',
@@ -274,11 +248,12 @@
 			cmtitle => $category,
 			cmlimit => 'max' } )
 			|| die $mediawiki->{error}->{code} . ': '
-				. $mediawiki->{error}->{details};
+				. $mediawiki->{error}->{details} . "\n";
 		foreach my $page (@{$mw_pages}) {
 			$pages->{$page->{title}} = $page;
 		}
 	}
+	return;
 }
 
 sub get_mw_all_pages {
@@ -290,14 +265,12 @@
 		aplimit => 'max'
 	});
 	if (!defined($mw_pages)) {
-		print STDERR "fatal: could not get the list of wiki pages.\n";
-		print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
-		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
-		exit 1;
+		fatal_mw_error("get the list of wiki pages");
 	}
 	foreach my $page (@{$mw_pages}) {
 		$pages->{$page->{title}} = $page;
 	}
+	return;
 }
 
 # queries the wiki for a set of pages. Meant to be used within a loop
@@ -316,25 +289,23 @@
 		titles => $titles,
 	});
 	if (!defined($mw_pages)) {
-		print STDERR "fatal: could not query the list of wiki pages.\n";
-		print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
-		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
-		exit 1;
+		fatal_mw_error("query the list of wiki pages");
 	}
 	while (my ($id, $page) = each(%{$mw_pages->{query}->{pages}})) {
 		if ($id < 0) {
-			print STDERR "Warning: page $page->{title} not found on wiki\n";
+			print {*STDERR} "Warning: page $page->{title} not found on wiki\n";
 		} else {
 			$pages->{$page->{title}} = $page;
 		}
 	}
+	return;
 }
 
 # Get the list of pages to be fetched according to configuration.
 sub get_mw_pages {
-	mw_connect_maybe();
+	$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
-	print STDERR "Listing pages on remote wiki...\n";
+	print {*STDERR} "Listing pages on remote wiki...\n";
 
 	my %pages; # hash on page titles to avoid duplicates
 	my $user_defined;
@@ -352,14 +323,14 @@
 		get_mw_all_pages(\%pages);
 	}
 	if ($import_media) {
-		print STDERR "Getting media files for selected pages...\n";
+		print {*STDERR} "Getting media files for selected pages...\n";
 		if ($user_defined) {
 			get_linked_mediafiles(\%pages);
 		} else {
 			get_all_mediafiles(\%pages);
 		}
 	}
-	print STDERR (scalar keys %pages) . " pages found.\n";
+	print {*STDERR} (scalar keys %pages) . " pages found.\n";
 	return %pages;
 }
 
@@ -367,9 +338,13 @@
 #        $out = run_git("command args", "raw"); # don't interpret output as UTF-8.
 sub run_git {
 	my $args = shift;
-	my $encoding = (shift || "encoding(UTF-8)");
-	open(my $git, "-|:$encoding", "git " . $args);
-	my $res = do { local $/; <$git> };
+	my $encoding = (shift || 'encoding(UTF-8)');
+	open(my $git, "-|:${encoding}", "git ${args}")
+	    or die "Unable to fork: $!\n";
+	my $res = do {
+		local $/ = undef;
+		<$git>
+	};
 	close($git);
 
 	return $res;
@@ -384,27 +359,26 @@
 	my $mw_pages = $mediawiki->list({
 		action => 'query',
 		list => 'allpages',
-		apnamespace => get_mw_namespace_id("File"),
+		apnamespace => get_mw_namespace_id('File'),
 		aplimit => 'max'
 	});
 	if (!defined($mw_pages)) {
-		print STDERR "fatal: could not get the list of pages for media files.\n";
-		print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
-		print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
+		print {*STDERR} "fatal: could not get the list of pages for media files.\n";
+		print {*STDERR} "fatal: '$url' does not appear to be a mediawiki\n";
+		print {*STDERR} "fatal: make sure '$url/api.php' is a valid page.\n";
 		exit 1;
 	}
 	foreach my $page (@{$mw_pages}) {
 		$pages->{$page->{title}} = $page;
 	}
+	return;
 }
 
 sub get_linked_mediafiles {
 	my $pages = shift;
-	my @titles = map $_->{title}, values(%{$pages});
+	my @titles = map { $_->{title} } values(%{$pages});
 
-	# The query is split in small batches because of the MW API limit of
-	# the number of links to be returned (500 links max).
-	my $batch = 10;
+	my $batch = BATCH_SIZE;
 	while (@titles) {
 		if ($#titles < $batch) {
 			$batch = $#titles;
@@ -420,7 +394,7 @@
 			action => 'query',
 			prop => 'links|images',
 			titles => $mw_titles,
-			plnamespace => get_mw_namespace_id("File"),
+			plnamespace => get_mw_namespace_id('File'),
 			pllimit => 'max'
 		};
 		my $result = $mediawiki->api($query);
@@ -428,11 +402,13 @@
 		while (my ($id, $page) = each(%{$result->{query}->{pages}})) {
 			my @media_titles;
 			if (defined($page->{links})) {
-				my @link_titles = map $_->{title}, @{$page->{links}};
+				my @link_titles
+				    = map { $_->{title} } @{$page->{links}};
 				push(@media_titles, @link_titles);
 			}
 			if (defined($page->{images})) {
-				my @image_titles = map $_->{title}, @{$page->{images}};
+				my @image_titles
+				    = map { $_->{title} } @{$page->{images}};
 				push(@media_titles, @image_titles);
 			}
 			if (@media_titles) {
@@ -442,6 +418,7 @@
 
 		@titles = @titles[($batch+1)..$#titles];
 	}
+	return;
 }
 
 sub get_mw_mediafile_for_page_revision {
@@ -455,7 +432,7 @@
 	my $query = {
 		action => 'query',
 		prop => 'imageinfo',
-		titles => "File:" . $filename,
+		titles => "File:${filename}",
 		iistart => $timestamp,
 		iiend => $timestamp,
 		iiprop => 'timestamp|archivename|url',
@@ -473,53 +450,50 @@
 		$mediafile{timestamp} = $fileinfo->{timestamp};
 		# Mediawiki::API's download function doesn't support https URLs
 		# and can't download old versions of files.
-		print STDERR "\tDownloading file $mediafile{title}, version $mediafile{timestamp}\n";
+		print {*STDERR} "\tDownloading file $mediafile{title}, version $mediafile{timestamp}\n";
 		$mediafile{content} = download_mw_mediafile($fileinfo->{url});
 	}
 	return %mediafile;
 }
 
 sub download_mw_mediafile {
-	my $url = shift;
+	my $download_url = shift;
 
-	my $response = $mediawiki->{ua}->get($url);
-	if ($response->code == 200) {
+	my $response = $mediawiki->{ua}->get($download_url);
+	if ($response->code == HTTP_CODE_OK) {
 		return $response->decoded_content;
 	} else {
-		print STDERR "Error downloading mediafile from :\n";
-		print STDERR "URL: $url\n";
-		print STDERR "Server response: " . $response->code . " " . $response->message . "\n";
+		print {*STDERR} "Error downloading mediafile from :\n";
+		print {*STDERR} "URL: ${download_url}\n";
+		print {*STDERR} 'Server response: ' . $response->code . q{ } . $response->message . "\n";
 		exit 1;
 	}
 }
 
 sub get_last_local_revision {
 	# Get note regarding last mediawiki revision
-	my $note = run_git("notes --ref=$remotename/mediawiki show refs/mediawiki/$remotename/master 2>/dev/null");
+	my $note = run_git("notes --ref=${remotename}/mediawiki show refs/mediawiki/${remotename}/master 2>/dev/null");
 	my @note_info = split(/ /, $note);
 
 	my $lastrevision_number;
-	if (!(defined($note_info[0]) && $note_info[0] eq "mediawiki_revision:")) {
-		print STDERR "No previous mediawiki revision found";
+	if (!(defined($note_info[0]) && $note_info[0] eq 'mediawiki_revision:')) {
+		print {*STDERR} 'No previous mediawiki revision found';
 		$lastrevision_number = 0;
 	} else {
 		# Notes are formatted : mediawiki_revision: #number
 		$lastrevision_number = $note_info[1];
 		chomp($lastrevision_number);
-		print STDERR "Last local mediawiki revision found is $lastrevision_number";
+		print {*STDERR} "Last local mediawiki revision found is ${lastrevision_number}";
 	}
 	return $lastrevision_number;
 }
 
-# Remember the timestamp corresponding to a revision id.
-my %basetimestamps;
-
 # Get the last remote revision without taking in account which pages are
 # tracked or not. This function makes a single request to the wiki thus
 # avoid a loop onto all tracked pages. This is useful for the fetch-by-rev
 # option.
 sub get_last_global_remote_rev {
-	mw_connect_maybe();
+	$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
 	my $query = {
 		action => 'query',
@@ -535,14 +509,14 @@
 # Get the last remote revision concerning the tracked pages and the tracked
 # categories.
 sub get_last_remote_revision {
-	mw_connect_maybe();
+	$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
 	my %pages_hash = get_mw_pages();
 	my @pages = values(%pages_hash);
 
 	my $max_rev_num = 0;
 
-	print STDERR "Getting last revision id on tracked pages...\n";
+	print {*STDERR} "Getting last revision id on tracked pages...\n";
 
 	foreach my $page (@pages) {
 		my $id = $page->{pageid};
@@ -563,7 +537,7 @@
 		$max_rev_num = ($lastrev->{revid} > $max_rev_num ? $lastrev->{revid} : $max_rev_num);
 	}
 
-	print STDERR "Last remote revision found is $max_rev_num.\n";
+	print {*STDERR} "Last remote revision found is $max_rev_num.\n";
 	return $max_rev_num;
 }
 
@@ -574,7 +548,7 @@
 	# Mediawiki does not allow blank space at the end of a page and ends with a single \n.
 	# This function right trims a string and adds a \n at the end to follow this rule
 	$string =~ s/\s+$//;
-	if ($string eq "" && $page_created) {
+	if ($string eq EMPTY && $page_created) {
 		# Creating empty pages is forbidden.
 		$string = EMPTY_CONTENT;
 	}
@@ -585,38 +559,16 @@
 sub mediawiki_smudge {
 	my $string = shift;
 	if ($string eq EMPTY_CONTENT) {
-		$string = "";
+		$string = EMPTY;
 	}
 	# This \n is important. This is due to mediawiki's way to handle end of files.
-	return $string."\n";
-}
-
-sub mediawiki_clean_filename {
-	my $filename = shift;
-	$filename =~ s/@{[SLASH_REPLACEMENT]}/\//g;
-	# [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.
-	# Do a variant of URL-encoding, i.e. looks like URL-encoding,
-	# but with _ added to prevent MediaWiki from thinking this is
-	# an actual special character.
-	$filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge;
-	# If we use the uri escape before
-	# we should unescape here, before anything
-
-	return $filename;
-}
-
-sub mediawiki_smudge_filename {
-	my $filename = shift;
-	$filename =~ s/\//@{[SLASH_REPLACEMENT]}/g;
-	$filename =~ s/ /_/g;
-	# Decode forbidden characters encoded in mediawiki_clean_filename
-	$filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf("%c", hex($1))/ge;
-	return $filename;
+	return "${string}\n";
 }
 
 sub literal_data {
 	my ($content) = @_;
-	print STDOUT "data ", bytes::length($content), "\n", $content;
+	print {*STDOUT} 'data ', bytes::length($content), "\n", $content;
+	return;
 }
 
 sub literal_data_raw {
@@ -624,33 +576,37 @@
 	my ($content) = @_;
 	# Avoid confusion between size in bytes and in characters
 	utf8::downgrade($content);
-	binmode STDOUT, ":raw";
-	print STDOUT "data ", bytes::length($content), "\n", $content;
-	binmode STDOUT, ":utf8";
+	binmode STDOUT, ':raw';
+	print {*STDOUT} 'data ', bytes::length($content), "\n", $content;
+	binmode STDOUT, ':encoding(UTF-8)';
+	return;
 }
 
 sub mw_capabilities {
 	# Revisions are imported to the private namespace
 	# refs/mediawiki/$remotename/ by the helper and fetched into
 	# refs/remotes/$remotename later by fetch.
-	print STDOUT "refspec refs/heads/*:refs/mediawiki/$remotename/*\n";
-	print STDOUT "import\n";
-	print STDOUT "list\n";
-	print STDOUT "push\n";
-	print STDOUT "\n";
+	print {*STDOUT} "refspec refs/heads/*:refs/mediawiki/${remotename}/*\n";
+	print {*STDOUT} "import\n";
+	print {*STDOUT} "list\n";
+	print {*STDOUT} "push\n";
+	print {*STDOUT} "\n";
+	return;
 }
 
 sub mw_list {
 	# MediaWiki do not have branches, we consider one branch arbitrarily
 	# called master, and HEAD pointing to it.
-	print STDOUT "? refs/heads/master\n";
-	print STDOUT "\@refs/heads/master HEAD\n";
-	print STDOUT "\n";
+	print {*STDOUT} "? refs/heads/master\n";
+	print {*STDOUT} "\@refs/heads/master HEAD\n";
+	print {*STDOUT} "\n";
+	return;
 }
 
 sub mw_option {
-	print STDERR "remote-helper command 'option $_[0]' not yet implemented\n";
-	print STDOUT "unsupported\n";
+	print {*STDERR} "remote-helper command 'option $_[0]' not yet implemented\n";
+	print {*STDOUT} "unsupported\n";
+	return;
 }
 
 sub fetch_mw_revisions_for_page {
@@ -681,15 +637,15 @@
 			push(@page_revs, $page_rev_ids);
 			$revnum++;
 		}
-		last unless $result->{'query-continue'};
+		last if (!$result->{'query-continue'});
 		$query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};
 	}
 	if ($shallow_import && @page_revs) {
-		print STDERR "  Found 1 revision (shallow import).\n";
+		print {*STDERR} "  Found 1 revision (shallow import).\n";
 		@page_revs = sort {$b->{revid} <=> $a->{revid}} (@page_revs);
 		return $page_revs[0];
 	}
-	print STDERR "  Found ", $revnum, " revision(s).\n";
+	print {*STDERR} "  Found ${revnum} revision(s).\n";
 	return @page_revs;
 }
 
@@ -701,8 +657,7 @@
 	my $n = 1;
 	foreach my $page (@pages) {
 		my $id = $page->{pageid};
-
-		print STDERR "page $n/", scalar(@pages), ": ". $page->{title} ."\n";
+		print {*STDERR} "page ${n}/", scalar(@pages), ': ', $page->{title}, "\n";
 		$n++;
 		my @page_revs = fetch_mw_revisions_for_page($page, $id, $fetch_from);
 		@revisions = (@page_revs, @revisions);
@@ -716,7 +671,7 @@
     $path =~ s/\\/\\\\/g;
     $path =~ s/"/\\"/g;
     $path =~ s/\n/\\n/g;
-    return '"' . $path . '"';
+    return qq("${path}");
 }
 
 sub import_file_revision {
@@ -736,42 +691,43 @@
 	my $author = $commit{author};
 	my $date = $commit{date};
 
-	print STDOUT "commit refs/mediawiki/$remotename/master\n";
-	print STDOUT "mark :$n\n";
-	print STDOUT "committer $author <$author\@$wiki_name> ", $date->epoch, " +0000\n";
+	print {*STDOUT} "commit refs/mediawiki/${remotename}/master\n";
+	print {*STDOUT} "mark :${n}\n";
+	print {*STDOUT} "committer ${author} <${author}\@${wiki_name}> " . $date->epoch . " +0000\n";
 	literal_data($comment);
 
 	# If it's not a clone, we need to know where to start from
 	if (!$full_import && $n == 1) {
-		print STDOUT "from refs/mediawiki/$remotename/master^0\n";
+		print {*STDOUT} "from refs/mediawiki/${remotename}/master^0\n";
 	}
 	if ($content ne DELETED_CONTENT) {
-		print STDOUT "M 644 inline " .
-		    fe_escape_path($title . ".mw") . "\n";
+		print {*STDOUT} 'M 644 inline ' .
+		    fe_escape_path("${title}.mw") . "\n";
 		literal_data($content);
 		if (%mediafile) {
-			print STDOUT "M 644 inline "
+			print {*STDOUT} 'M 644 inline '
 			    . fe_escape_path($mediafile{title}) . "\n";
 			literal_data_raw($mediafile{content});
 		}
-		print STDOUT "\n\n";
+		print {*STDOUT} "\n\n";
 	} else {
-		print STDOUT "D " . fe_escape_path($title . ".mw") . "\n";
+		print {*STDOUT} 'D ' . fe_escape_path("${title}.mw") . "\n";
 	}
 
 	# mediawiki revision number in the git note
 	if ($full_import && $n == 1) {
-		print STDOUT "reset refs/notes/$remotename/mediawiki\n";
+		print {*STDOUT} "reset refs/notes/${remotename}/mediawiki\n";
 	}
-	print STDOUT "commit refs/notes/$remotename/mediawiki\n";
-	print STDOUT "committer $author <$author\@$wiki_name> ", $date->epoch, " +0000\n";
-	literal_data("Note added by git-mediawiki during import");
+	print {*STDOUT} "commit refs/notes/${remotename}/mediawiki\n";
+	print {*STDOUT} "committer ${author} <${author}\@${wiki_name}> " . $date->epoch . " +0000\n";
+	literal_data('Note added by git-mediawiki during import');
 	if (!$full_import && $n == 1) {
-		print STDOUT "from refs/notes/$remotename/mediawiki^0\n";
+		print {*STDOUT} "from refs/notes/${remotename}/mediawiki^0\n";
 	}
-	print STDOUT "N inline :$n\n";
-	literal_data("mediawiki_revision: " . $commit{mw_revision});
-	print STDOUT "\n\n";
+	print {*STDOUT} "N inline :${n}\n";
+	literal_data("mediawiki_revision: $commit{mw_revision}");
+	print {*STDOUT} "\n\n";
+	return;
 }
 
 # parse a sequence of
@@ -784,23 +740,25 @@
 	my @refs;
 	while (1) {
 		my $line = <STDIN>;
-		if ($line =~ m/^$cmd (.*)$/) {
+		if ($line =~ /^$cmd (.*)$/) {
 			push(@refs, $1);
 		} elsif ($line eq "\n") {
 			return @refs;
 		} else {
-			die("Invalid command in a '$cmd' batch: ". $_);
+			die("Invalid command in a '$cmd' batch: $_\n");
 		}
 	}
+	return;
 }
 
 sub mw_import {
 	# multiple import commands can follow each other.
-	my @refs = (shift, get_more_refs("import"));
+	my @refs = (shift, get_more_refs('import'));
 	foreach my $ref (@refs) {
 		mw_import_ref($ref);
 	}
-	print STDOUT "done\n";
+	print {*STDOUT} "done\n";
+	return;
 }
 
 sub mw_import_ref {
@@ -810,40 +768,41 @@
 	# Since HEAD is a symbolic ref to master (by convention,
 	# followed by the output of the command "list" that we gave),
 	# we don't need to do anything in this case.
-	if ($ref eq "HEAD") {
+	if ($ref eq 'HEAD') {
 		return;
 	}
 
-	mw_connect_maybe();
+	$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
-	print STDERR "Searching revisions...\n";
+	print {*STDERR} "Searching revisions...\n";
 	my $last_local = get_last_local_revision();
 	my $fetch_from = $last_local + 1;
 	if ($fetch_from == 1) {
-		print STDERR ", fetching from beginning.\n";
+		print {*STDERR} ", fetching from beginning.\n";
 	} else {
-		print STDERR ", fetching from here.\n";
+		print {*STDERR} ", fetching from here.\n";
 	}
 
 	my $n = 0;
-	if ($fetch_strategy eq "by_rev") {
-		print STDERR "Fetching & writing export data by revs...\n";
+	if ($fetch_strategy eq 'by_rev') {
+		print {*STDERR} "Fetching & writing export data by revs...\n";
 		$n = mw_import_ref_by_revs($fetch_from);
-	} elsif ($fetch_strategy eq "by_page") {
-		print STDERR "Fetching & writing export data by pages...\n";
+	} elsif ($fetch_strategy eq 'by_page') {
+		print {*STDERR} "Fetching & writing export data by pages...\n";
 		$n = mw_import_ref_by_pages($fetch_from);
 	} else {
-		print STDERR "fatal: invalid fetch strategy \"$fetch_strategy\".\n";
-		print STDERR "Check your configuration variables remote.$remotename.fetchStrategy and mediawiki.fetchStrategy\n";
+		print {*STDERR} qq(fatal: invalid fetch strategy "${fetch_strategy}".\n);
+		print {*STDERR} "Check your configuration variables remote.${remotename}.fetchStrategy and mediawiki.fetchStrategy\n";
 		exit 1;
 	}
 
 	if ($fetch_from == 1 && $n == 0) {
-		print STDERR "You appear to have cloned an empty MediaWiki.\n";
+		print {*STDERR} "You appear to have cloned an empty MediaWiki.\n";
 		# Something has to be done remote-helper side. If nothing is done, an error is
 		# thrown saying that HEAD is referring to unknown object 0000000000000000000
 		# and the clone fails.
 	}
+	return;
 }
 
 sub mw_import_ref_by_pages {
@@ -855,7 +814,7 @@
 	my ($n, @revisions) = fetch_mw_revisions(\@pages, $fetch_from);
 
 	@revisions = sort {$a->{revid} <=> $b->{revid}} @revisions;
-	my @revision_ids = map $_->{revid}, @revisions;
+	my @revision_ids = map { $_->{revid} } @revisions;
 
 	return mw_import_revids($fetch_from, \@revision_ids, \%pages_hash);
 }
@@ -882,7 +841,7 @@
 	my $n_actual = 0;
 	my $last_timestamp = 0; # Placeholer in case $rev->timestamp is undefined
 
-	foreach my $pagerevid (@$revision_ids) {
+	foreach my $pagerevid (@{$revision_ids}) {
 	        # Count page even if we skip it, since we display
 		# $n/$total and $total includes skipped pages.
 		$n++;
@@ -898,7 +857,7 @@
 		my $result = $mediawiki->api($query);
 
 		if (!$result) {
-			die "Failed to retrieve modified page for revision $pagerevid";
+			die "Failed to retrieve modified page for revision $pagerevid\n";
 		}
 
 		if (defined($result->{query}->{badrevids}->{$pagerevid})) {
@@ -907,7 +866,7 @@
 		}
 
 		if (!defined($result->{query}->{pages})) {
-			die "Invalid revision $pagerevid.";
+			die "Invalid revision ${pagerevid}.\n";
 		}
 
 		my @result_pages = values(%{$result->{query}->{pages}});
@@ -917,8 +876,8 @@
 		my $page_title = $result_page->{title};
 
 		if (!exists($pages->{$page_title})) {
-			print STDERR "$n/", scalar(@$revision_ids),
-				": Skipping revision #$rev->{revid} of $page_title\n";
+			print {*STDERR} "${n}/", scalar(@{$revision_ids}),
+				": Skipping revision #$rev->{revid} of ${page_title}\n";
 			next;
 		}
 
@@ -927,7 +886,7 @@
 		my %commit;
 		$commit{author} = $rev->{user} || 'Anonymous';
 		$commit{comment} = $rev->{comment} || EMPTY_MESSAGE;
-		$commit{title} = mediawiki_smudge_filename($page_title);
+		$commit{title} = smudge_filename($page_title);
 		$commit{mw_revision} = $rev->{revid};
 		$commit{content} = mediawiki_smudge($rev->{'*'});
 
@@ -943,14 +902,14 @@
 		my %mediafile;
 		if ($namespace) {
 			my $id = get_mw_namespace_id($namespace);
-			if ($id && $id == get_mw_namespace_id("File")) {
+			if ($id && $id == get_mw_namespace_id('File')) {
 				%mediafile = get_mw_mediafile_for_page_revision($filename, $rev->{timestamp});
 			}
 		}
 		# If this is a revision of the media page for new version
 		# of a file do one common commit for both file and media page.
 		# Else do commit only for that page.
-		print STDERR "$n/", scalar(@$revision_ids), ": Revision #$rev->{revid} of $commit{title}\n";
+		print {*STDERR} "${n}/", scalar(@{$revision_ids}), ": Revision #$rev->{revid} of $commit{title}\n";
 		import_file_revision(\%commit, ($fetch_from == 1), $n_actual, \%mediafile);
 	}
 
@@ -958,17 +917,17 @@
 }
 
 sub error_non_fast_forward {
-	my $advice = run_git("config --bool advice.pushNonFastForward");
+	my $advice = run_git('config --bool advice.pushNonFastForward');
 	chomp($advice);
-	if ($advice ne "false") {
+	if ($advice ne 'false') {
 		# Native git-push would show this after the summary.
 		# We can't ask it to display it cleanly, so print it
 		# ourselves before.
-		print STDERR "To prevent you from losing history, non-fast-forward updates were rejected\n";
-		print STDERR "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n";
-		print STDERR "'Note about fast-forwards' section of 'git push --help' for details.\n";
+		print {*STDERR} "To prevent you from losing history, non-fast-forward updates were rejected\n";
+		print {*STDERR} "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n";
+		print {*STDERR} "'Note about fast-forwards' section of 'git push --help' for details.\n";
 	}
-	print STDOUT "error $_[0] \"non-fast-forward\"\n";
+	print {*STDOUT} qq(error $_[0] "non-fast-forward"\n);
 	return 0;
 }
 
@@ -979,34 +938,34 @@
 	my $file_deleted = shift;
 	my $summary = shift;
 	my $newrevid;
-	my $path = "File:" . $complete_file_name;
+	my $path = "File:${complete_file_name}";
 	my %hashFiles = get_allowed_file_extensions();
 	if (!exists($hashFiles{$extension})) {
-		print STDERR "$complete_file_name is not a permitted file on this wiki.\n";
-		print STDERR "Check the configuration of file uploads in your mediawiki.\n";
+		print {*STDERR} "${complete_file_name} is not a permitted file on this wiki.\n";
+		print {*STDERR} "Check the configuration of file uploads in your mediawiki.\n";
 		return $newrevid;
 	}
 	# Deleting and uploading a file requires a priviledged user
 	if ($file_deleted) {
-		mw_connect_maybe();
+		$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 		my $query = {
 			action => 'delete',
 			title => $path,
 			reason => $summary
 		};
 		if (!$mediawiki->edit($query)) {
-			print STDERR "Failed to delete file on remote wiki\n";
-			print STDERR "Check your permissions on the remote site. Error code:\n";
-			print STDERR $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};
+			print {*STDERR} "Failed to delete file on remote wiki\n";
+			print {*STDERR} "Check your permissions on the remote site. Error code:\n";
+			print {*STDERR} $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};
 			exit 1;
 		}
 	} else {
 		# Don't let perl try to interpret file content as UTF-8 => use "raw"
-		my $content = run_git("cat-file blob $new_sha1", "raw");
-		if ($content ne "") {
-			mw_connect_maybe();
+		my $content = run_git("cat-file blob ${new_sha1}", 'raw');
+		if ($content ne EMPTY) {
+			$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 			$mediawiki->{config}->{upload_url} =
-				"$url/index.php/Special:Upload";
+				"${url}/index.php/Special:Upload";
 			$mediawiki->edit({
 				action => 'upload',
 				filename => $complete_file_name,
@@ -1018,12 +977,12 @@
 			}, {
 				skip_encoding => 1
 			} ) || die $mediawiki->{error}->{code} . ':'
-				 . $mediawiki->{error}->{details};
+				 . $mediawiki->{error}->{details} . "\n";
 			my $last_file_page = $mediawiki->get_page({title => $path});
 			$newrevid = $last_file_page->{revid};
-			print STDERR "Pushed file: $new_sha1 - $complete_file_name.\n";
+			print {*STDERR} "Pushed file: ${new_sha1} - ${complete_file_name}.\n";
 		} else {
-			print STDERR "Empty file $complete_file_name not pushed.\n";
+			print {*STDERR} "Empty file ${complete_file_name} not pushed.\n";
 		}
 	}
 	return $newrevid;
@@ -1045,24 +1004,24 @@
 	my $newrevid;
 
 	if ($summary eq EMPTY_MESSAGE) {
-		$summary = '';
+		$summary = EMPTY;
 	}
 
 	my $new_sha1 = $diff_info_split[3];
 	my $old_sha1 = $diff_info_split[2];
 	my $page_created = ($old_sha1 eq NULL_SHA1);
 	my $page_deleted = ($new_sha1 eq NULL_SHA1);
-	$complete_file_name = mediawiki_clean_filename($complete_file_name);
+	$complete_file_name = clean_filename($complete_file_name);
 
 	my ($title, $extension) = $complete_file_name =~ /^(.*)\.([^\.]*)$/;
 	if (!defined($extension)) {
-		$extension = "";
+		$extension = EMPTY;
 	}
-	if ($extension eq "mw") {
+	if ($extension eq 'mw') {
 		my $ns = get_mw_namespace_id_for_page($complete_file_name);
-		if ($ns && $ns == get_mw_namespace_id("File") && (!$export_media)) {
-			print STDERR "Ignoring media file related page: $complete_file_name\n";
-			return ($oldrevid, "ok");
+		if ($ns && $ns == get_mw_namespace_id('File') && (!$export_media)) {
+			print {*STDERR} "Ignoring media file related page: ${complete_file_name}\n";
+			return ($oldrevid, 'ok');
 		}
 		my $file_content;
 		if ($page_deleted) {
@@ -1072,10 +1031,10 @@
 			# with this content instead:
 			$file_content = DELETED_CONTENT;
 		} else {
-			$file_content = run_git("cat-file blob $new_sha1");
+			$file_content = run_git("cat-file blob ${new_sha1}");
 		}
 
-		mw_connect_maybe();
+		$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
 		my $result = $mediawiki->edit( {
 			action => 'edit',
@@ -1089,49 +1048,49 @@
 		if (!$result) {
 			if ($mediawiki->{error}->{code} == 3) {
 				# edit conflicts, considered as non-fast-forward
-				print STDERR 'Warning: Error ' .
+				print {*STDERR} 'Warning: Error ' .
 				    $mediawiki->{error}->{code} .
-				    ' from mediwiki: ' . $mediawiki->{error}->{details} .
+				    ' from mediawiki: ' . $mediawiki->{error}->{details} .
 				    ".\n";
-				return ($oldrevid, "non-fast-forward");
+				return ($oldrevid, 'non-fast-forward');
 			} else {
 				# Other errors. Shouldn't happen => just die()
 				die 'Fatal: Error ' .
 				    $mediawiki->{error}->{code} .
-				    ' from mediwiki: ' . $mediawiki->{error}->{details};
+				    ' from mediawiki: ' . $mediawiki->{error}->{details} . "\n";
 			}
 		}
 		$newrevid = $result->{edit}->{newrevid};
-		print STDERR "Pushed file: $new_sha1 - $title\n";
+		print {*STDERR} "Pushed file: ${new_sha1} - ${title}\n";
 	} elsif ($export_media) {
 		$newrevid = mw_upload_file($complete_file_name, $new_sha1,
 					   $extension, $page_deleted,
 					   $summary);
 	} else {
-		print STDERR "Ignoring media file $title\n";
+		print {*STDERR} "Ignoring media file ${title}\n";
 	}
 	$newrevid = ($newrevid or $oldrevid);
-	return ($newrevid, "ok");
+	return ($newrevid, 'ok');
 }
 
 sub mw_push {
 	# multiple push statements can follow each other
-	my @refsspecs = (shift, get_more_refs("push"));
+	my @refsspecs = (shift, get_more_refs('push'));
 	my $pushed;
 	for my $refspec (@refsspecs) {
 		my ($force, $local, $remote) = $refspec =~ /^(\+)?([^:]*):([^:]*)$/
-		    or die("Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>");
+		    or die("Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>\n");
 		if ($force) {
-			print STDERR "Warning: forced push not allowed on a MediaWiki.\n";
+			print {*STDERR} "Warning: forced push not allowed on a MediaWiki.\n";
 		}
-		if ($local eq "") {
-			print STDERR "Cannot delete remote branch on a MediaWiki\n";
-			print STDOUT "error $remote cannot delete\n";
+		if ($local eq EMPTY) {
+			print {*STDERR} "Cannot delete remote branch on a MediaWiki\n";
+			print {*STDOUT} "error ${remote} cannot delete\n";
 			next;
 		}
-		if ($remote ne "refs/heads/master") {
-			print STDERR "Only push to the branch 'master' is supported on a MediaWiki\n";
-			print STDOUT "error $remote only master allowed\n";
+		if ($remote ne 'refs/heads/master') {
+			print {*STDERR} "Only push to the branch 'master' is supported on a MediaWiki\n";
+			print {*STDOUT} "error ${remote} only master allowed\n";
 			next;
 		}
 		if (mw_push_revision($local, $remote)) {
@@ -1140,30 +1099,32 @@
 	}
 
 	# Notify Git that the push is done
-	print STDOUT "\n";
+	print {*STDOUT} "\n";
 
 	if ($pushed && $dumb_push) {
-		print STDERR "Just pushed some revisions to MediaWiki.\n";
-		print STDERR "The pushed revisions now have to be re-imported, and your current branch\n";
-		print STDERR "needs to be updated with these re-imported commits. You can do this with\n";
-		print STDERR "\n";
-		print STDERR "  git pull --rebase\n";
-		print STDERR "\n";
+		print {*STDERR} "Just pushed some revisions to MediaWiki.\n";
+		print {*STDERR} "The pushed revisions now have to be re-imported, and your current branch\n";
+		print {*STDERR} "needs to be updated with these re-imported commits. You can do this with\n";
+		print {*STDERR} "\n";
+		print {*STDERR} "  git pull --rebase\n";
+		print {*STDERR} "\n";
 	}
+	return;
 }
 
 sub mw_push_revision {
 	my $local = shift;
 	my $remote = shift; # actually, this has to be "refs/heads/master" at this point.
 	my $last_local_revid = get_last_local_revision();
-	print STDERR ".\n"; # Finish sentence started by get_last_local_revision()
+	print {*STDERR} ".\n"; # Finish sentence started by get_last_local_revision()
 	my $last_remote_revid = get_last_remote_revision();
 	my $mw_revision = $last_remote_revid;
 
 	# Get sha1 of commit pointed by local HEAD
-	my $HEAD_sha1 = run_git("rev-parse $local 2>/dev/null"); chomp($HEAD_sha1);
+	my $HEAD_sha1 = run_git("rev-parse ${local} 2>/dev/null");
+	chomp($HEAD_sha1);
 	# Get sha1 of commit pointed by remotes/$remotename/master
-	my $remoteorigin_sha1 = run_git("rev-parse refs/remotes/$remotename/master 2>/dev/null");
+	my $remoteorigin_sha1 = run_git("rev-parse refs/remotes/${remotename}/master 2>/dev/null");
 	chomp($remoteorigin_sha1);
 
 	if ($last_local_revid > 0 &&
@@ -1182,22 +1143,22 @@
 	if ($last_local_revid > 0) {
 		my $parsed_sha1 = $remoteorigin_sha1;
 		# Find a path from last MediaWiki commit to pushed commit
-		print STDERR "Computing path from local to remote ...\n";
-		my @local_ancestry = split(/\n/, run_git("rev-list --boundary --parents $local ^$parsed_sha1"));
+		print {*STDERR} "Computing path from local to remote ...\n";
+		my @local_ancestry = split(/\n/, run_git("rev-list --boundary --parents ${local} ^${parsed_sha1}"));
 		my %local_ancestry;
 		foreach my $line (@local_ancestry) {
-			if (my ($child, $parents) = $line =~ m/^-?([a-f0-9]+) ([a-f0-9 ]+)/) {
-				foreach my $parent (split(' ', $parents)) {
+			if (my ($child, $parents) = $line =~ /^-?([a-f0-9]+) ([a-f0-9 ]+)/) {
+				foreach my $parent (split(/ /, $parents)) {
 					$local_ancestry{$parent} = $child;
 				}
-			} elsif (!$line =~ m/^([a-f0-9]+)/) {
-				die "Unexpected output from git rev-list: $line";
+			} elsif (!$line =~ /^([a-f0-9]+)/) {
+				die "Unexpected output from git rev-list: ${line}\n";
 			}
 		}
 		while ($parsed_sha1 ne $HEAD_sha1) {
 			my $child = $local_ancestry{$parsed_sha1};
 			if (!$child) {
-				printf STDERR "Cannot find a path in history from remote commit to last commit\n";
+				print {*STDERR} "Cannot find a path in history from remote commit to last commit\n";
 				return error_non_fast_forward($remote);
 			}
 			push(@commit_pairs, [$parsed_sha1, $child]);
@@ -1206,12 +1167,12 @@
 	} else {
 		# No remote mediawiki revision. Export the whole
 		# history (linearized with --first-parent)
-		print STDERR "Warning: no common ancestor, pushing complete history\n";
-		my $history = run_git("rev-list --first-parent --children $local");
-		my @history = split('\n', $history);
+		print {*STDERR} "Warning: no common ancestor, pushing complete history\n";
+		my $history = run_git("rev-list --first-parent --children ${local}");
+		my @history = split(/\n/, $history);
 		@history = @history[1..$#history];
 		foreach my $line (reverse @history) {
-			my @commit_info_split = split(/ |\n/, $line);
+			my @commit_info_split = split(/[ \n]/, $line);
 			push(@commit_pairs, \@commit_info_split);
 		}
 	}
@@ -1219,12 +1180,12 @@
 	foreach my $commit_info_split (@commit_pairs) {
 		my $sha1_child = @{$commit_info_split}[0];
 		my $sha1_commit = @{$commit_info_split}[1];
-		my $diff_infos = run_git("diff-tree -r --raw -z $sha1_child $sha1_commit");
+		my $diff_infos = run_git("diff-tree -r --raw -z ${sha1_child} ${sha1_commit}");
 		# TODO: we could detect rename, and encode them with a #redirect on the wiki.
 		# TODO: for now, it's just a delete+add
 		my @diff_info_list = split(/\0/, $diff_infos);
 		# Keep the subject line of the commit message as mediawiki comment for the revision
-		my $commit_msg = run_git("log --no-walk --format=\"%s\" $sha1_commit");
+		my $commit_msg = run_git(qq(log --no-walk --format="%s" ${sha1_commit}));
 		chomp($commit_msg);
 		# Push every blob
 		while (@diff_info_list) {
@@ -1236,7 +1197,7 @@
 			my $info = shift(@diff_info_list);
 			my $file = shift(@diff_info_list);
 			($mw_revision, $status) = mw_push_file($info, $file, $commit_msg, $mw_revision);
-			if ($status eq "non-fast-forward") {
+			if ($status eq 'non-fast-forward') {
 				# we may already have sent part of the
 				# commit to MediaWiki, but it's too
 				# late to cancel it. Stop the push in
@@ -1244,22 +1205,22 @@
 				# accurate error message.
 				return error_non_fast_forward($remote);
 			}
-			if ($status ne "ok") {
-				die("Unknown error from mw_push_file()");
+			if ($status ne 'ok') {
+				die("Unknown error from mw_push_file()\n");
 			}
 		}
-		unless ($dumb_push) {
-			run_git("notes --ref=$remotename/mediawiki add -f -m \"mediawiki_revision: $mw_revision\" $sha1_commit");
-			run_git("update-ref -m \"Git-MediaWiki push\" refs/mediawiki/$remotename/master $sha1_commit $sha1_child");
+		if (!$dumb_push) {
+			run_git(qq(notes --ref=${remotename}/mediawiki add -f -m "mediawiki_revision: ${mw_revision}" ${sha1_commit}));
+			run_git(qq(update-ref -m "Git-MediaWiki push" refs/mediawiki/${remotename}/master ${sha1_commit} ${sha1_child}));
 		}
 	}
 
-	print STDOUT "ok $remote\n";
+	print {*STDOUT} "ok ${remote}\n";
 	return 1;
 }
 
 sub get_allowed_file_extensions {
-	mw_connect_maybe();
+	$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
 	my $query = {
 		action => 'query',
@@ -1267,8 +1228,8 @@
 		siprop => 'fileextensions'
 		};
 	my $result = $mediawiki->api($query);
-	my @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};
-	my %hashFile = map {$_ => 1}@file_extensions;
+	my @file_extensions = map { $_->{ext}} @{$result->{query}->{fileextensions}};
+	my %hashFile = map { $_ => 1 } @file_extensions;
 
 	return %hashFile;
 }
@@ -1283,15 +1244,15 @@
 # Return MediaWiki id for a canonical namespace name.
 # Ex.: "File", "Project".
 sub get_mw_namespace_id {
-	mw_connect_maybe();
+	$mediawiki = connect_maybe($mediawiki, $remotename, $url);
 	my $name = shift;
 
 	if (!exists $namespace_id{$name}) {
 		# Look at configuration file, if the record for that namespace is
 		# already cached. Namespaces are stored in form:
 		# "Name_of_namespace:Id_namespace", ex.: "File:6".
-		my @temp = split(/[\n]/, run_git("config --get-all remote."
-						. $remotename .".namespaceCache"));
+		my @temp = split(/\n/,
+				 run_git("config --get-all remote.${remotename}.namespaceCache"));
 		chomp(@temp);
 		foreach my $ns (@temp) {
 			my ($n, $id) = split(/:/, $ns);
@@ -1305,7 +1266,7 @@
 	}
 
 	if (!exists $namespace_id{$name}) {
-		print STDERR "Namespace $name not found in cache, querying the wiki ...\n";
+		print {*STDERR} "Namespace ${name} not found in cache, querying the wiki ...\n";
 		# NS not found => get namespace id from MW and store it in
 	        # configuration file.
 	        my $query = {
@@ -1329,8 +1290,8 @@
 	my $ns = $namespace_id{$name};
 	my $id;
 
-	unless (defined $ns) {
-		print STDERR "No such namespace $name on MediaWiki.\n";
+	if (!defined $ns) {
+		print {*STDERR} "No such namespace ${name} on MediaWiki.\n";
 		$ns = {is_namespace => 0};
 		$namespace_id{$name} = $ns;
 	}
@@ -1344,15 +1305,15 @@
 
 	# Store explicitely requested namespaces on disk
 	if (!exists $cached_mw_namespace_id{$name}) {
-		run_git("config --add remote.". $remotename
-			.".namespaceCache \"". $name .":". $store_id ."\"");
+		run_git(qq(config --add remote.${remotename}.namespaceCache "${name}:${store_id}"));
 		$cached_mw_namespace_id{$name} = 1;
 	}
 	return $id;
 }
 
 sub get_mw_namespace_id_for_page {
-	if (my ($namespace) = $_[0] =~ /^([^:]*):/) {
+	my $namespace = shift;
+	if ($namespace =~ /^([^:]*):/) {
 		return get_mw_namespace_id($namespace);
 	} else {
 		return;
diff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh
index 3b2cfac..ca6860f 100755
--- a/contrib/mw-to-git/t/test-gitmw-lib.sh
+++ b/contrib/mw-to-git/t/test-gitmw-lib.sh
@@ -62,12 +62,8 @@
 		test_done
 	fi
 
-	if [ ! -f "$GIT_BUILD_DIR"/git-remote-mediawiki ];
-	then
-		echo "No remote mediawiki for git found. Copying it in git"
-		echo "cp $GIT_BUILD_DIR/contrib/mw-to-git/git-remote-mediawiki $GIT_BUILD_DIR/"
-		ln -s "$GIT_BUILD_DIR"/contrib/mw-to-git/git-remote-mediawiki "$GIT_BUILD_DIR"
-	fi
+	GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd "../.." && pwd)
+	PATH="$GIT_EXEC_PATH"'/bin-wrapper:'"$PATH"
 
 	if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ];
 	then
@@ -336,20 +332,21 @@
 	fi
 
 	# Fetch MediaWiki's archive if not already present in the TMP directory
+	MW_FILENAME="mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz"
 	cd "$TMP"
-	if [ ! -f "$MW_VERSION.tar.gz" ] ; then
-		echo "Downloading $MW_VERSION sources ..."
-		wget "http://download.wikimedia.org/mediawiki/1.19/mediawiki-1.19.0.tar.gz" ||
+	if [ ! -f $MW_FILENAME ] ; then
+		echo "Downloading $MW_VERSION_MAJOR.$MW_VERSION_MINOR sources ..."
+		wget "http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/$MW_FILENAME" ||
 			error "Unable to download "\
-			"http://download.wikimedia.org/mediawiki/1.19/"\
-			"mediawiki-1.19.0.tar.gz. "\
+			"http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/"\
+			"$MW_FILENAME. "\
 			"Please fix your connection and launch the script again."
-		echo "$MW_VERSION.tar.gz downloaded in `pwd`. "\
+		echo "$MW_FILENAME downloaded in `pwd`. "\
 			"You can delete it later if you want."
 	else
-		echo "Reusing existing $MW_VERSION.tar.gz downloaded in `pwd`."
+		echo "Reusing existing $MW_FILENAME downloaded in `pwd`."
 	fi
-	archive_abs_path=$(pwd)/"$MW_VERSION.tar.gz"
+	archive_abs_path=$(pwd)/$MW_FILENAME
 	cd "$WIKI_DIR_INST/$WIKI_DIR_NAME/" ||
 		error "can't cd to $WIKI_DIR_INST/$WIKI_DIR_NAME/"
 	tar xzf "$archive_abs_path" --strip-components=1 ||
@@ -431,5 +428,5 @@
 	# Delete the wiki's SQLite database
 	rm -f "$TMP/$DB_FILE" || error "Database $TMP/$DB_FILE could not be deleted."
 	rm -f "$FILES_FOLDER/$DB_FILE"
-	rm -rf "$TMP/$MW_VERSION"
+	rm -rf "$TMP/mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz"
 }
diff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config
index 958b37b..4cfebe9 100644
--- a/contrib/mw-to-git/t/test.config
+++ b/contrib/mw-to-git/t/test.config
@@ -30,6 +30,8 @@
 
 # The variables below are used by the script to install a wiki.
 # You should not modify these unless you are modifying the script itself.
-MW_VERSION=mediawiki-1.19.0
+# tested versions: 1.19.X -> 1.21.1
+MW_VERSION_MAJOR=1.21
+MW_VERSION_MINOR=1
 FILES_FOLDER=install-wiki
 DB_INSTALL_SCRIPT=db_install.php
diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch
deleted file mode 100644
index f2b08b4..0000000
--- a/contrib/patches/docbook-xsl-manpages-charmap.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From: Ismail Dönmez <ismail@pardus.org.tr>
-
-Trying to build the documentation with docbook-xsl 1.73 may result in
-the following error.  This patch fixes it.
-
-$ xmlto -m callouts.xsl man git-add.xml
-runtime error: file
-file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line
-129 element call-template
-The called template 'read-character-map' was not found.
-
---- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap	2007-07-23 16:24:23.000000000 +0100
-+++ docbook-xsl-1.73.0/manpages/docbook.xsl	2007-07-23 16:25:16.000000000 +0100
-@@ -37,6 +37,7 @@
-   <xsl:include href="lists.xsl"/>
-   <xsl:include href="endnotes.xsl"/>
-   <xsl:include href="table.xsl"/>
-+  <xsl:include href="../common/charmap.xsl"/>
-
-   <!-- * we rename the following just to avoid using params with "man" -->
-   <!-- * prefixes in the table.xsl stylesheet (because that stylesheet -->
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index 10300c6..c3a3cac 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -116,7 +116,10 @@
         self.last_mark = mark
 
     def get_tip(self, branch):
-        return self.tips.get(branch, None)
+        try:
+            return str(self.tips[branch])
+        except KeyError:
+            return None
 
     def set_tip(self, branch, tip):
         self.tips[branch] = tip
@@ -278,7 +281,7 @@
     ref = '%s/heads/%s' % (prefix, name)
     tip = marks.get_tip(name)
 
-    branch = bzrlib.branch.Branch.open(branches[name])
+    branch = get_remote_branch(name)
     repo = branch.repository
 
     branch.lock_read()
@@ -590,7 +593,7 @@
 
     if ref.startswith('refs/heads/'):
         name = ref[len('refs/heads/'):]
-        branch = bzrlib.branch.Branch.open(branches[name])
+        branch = get_remote_branch(name)
     else:
         die('unknown ref')
 
@@ -621,7 +624,7 @@
             mark = int(mark_ref[1:])
             f = { 'mode' : m, 'mark' : mark }
         elif parser.check('D'):
-            t, path = line.split(' ')
+            t, path = line.split(' ', 1)
             f = { 'deleted' : True }
         else:
             die('Unknown file command: %s' % line)
@@ -692,7 +695,7 @@
     for ref, revid in parsed_refs.iteritems():
         if ref.startswith('refs/heads/'):
             name = ref[len('refs/heads/'):]
-            branch = bzrlib.branch.Branch.open(branches[name])
+            branch = get_remote_branch(name)
             branch.generate_revision_history(revid, marks.get_tip(name))
 
             if name in peers:
@@ -749,7 +752,7 @@
             master_branch = name
         print "? refs/heads/%s" % name
 
-    branch = bzrlib.branch.Branch.open(branches[master_branch])
+    branch = get_remote_branch(master_branch)
     branch.lock_read()
     for tag, revid in branch.tags.get_tag_dict().items():
         try:
@@ -765,30 +768,40 @@
     print "@refs/heads/%s HEAD" % master_branch
     print
 
-def get_remote_branch(origin, remote_branch, name):
-    global dirname, peers
+def clone(path, remote_branch):
+    try:
+        bdir = bzrlib.bzrdir.BzrDir.create(path)
+    except bzrlib.errors.AlreadyControlDirError:
+        bdir = bzrlib.bzrdir.BzrDir.open(path)
+    repo = bdir.find_repository()
+    repo.fetch(remote_branch.repository)
+    return remote_branch.sprout(bdir, repository=repo)
+
+def get_remote_branch(name):
+    global dirname, branches
+
+    remote_branch = bzrlib.branch.Branch.open(branches[name])
+    if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
+        return remote_branch
 
     branch_path = os.path.join(dirname, 'clone', name)
-    if os.path.exists(branch_path):
+
+    try:
+        branch = bzrlib.branch.Branch.open(branch_path)
+    except bzrlib.errors.NotBranchError:
+        # clone
+        branch = clone(branch_path, remote_branch)
+    else:
         # pull
-        d = bzrlib.bzrdir.BzrDir.open(branch_path)
-        branch = d.open_branch()
         try:
-            branch.pull(remote_branch, [], None, False)
+            branch.pull(remote_branch, overwrite=True)
         except bzrlib.errors.DivergedBranches:
             # use remote branch for now
             return remote_branch
-    else:
-        # clone
-        d = origin.sprout(branch_path, None,
-                hardlink=True, create_tree_if_local=False,
-                force_new_repo=False,
-                source_branch=remote_branch)
-        branch = d.open_branch()
 
     return branch
 
-def find_branches(repo, wanted):
+def find_branches(repo):
     transport = repo.bzrdir.root_transport
 
     for fn in transport.iter_files_recursive():
@@ -799,16 +812,13 @@
         name = name if name != '' else 'master'
         name = name.replace('/', '+')
 
-        if wanted and not name in wanted:
-            continue
-
         try:
             cur = transport.clone(subdir)
             branch = bzrlib.branch.Branch.open_from_transport(cur)
         except bzrlib.errors.NotBranchError:
             continue
         else:
-            yield name, branch
+            yield name, branch.base
 
 def get_repo(url, alias):
     global dirname, peer, branches
@@ -841,44 +851,35 @@
             except bzrlib.errors.NoRepositoryPresent:
                 pass
 
-    try:
-        repo = origin.open_repository()
-        if not repo.user_transport.listable():
-            # this repository is not usable for us
-            raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
-    except bzrlib.errors.NoRepositoryPresent:
-        # branch
+    wanted = get_config('remote-bzr.branches').rstrip().split(', ')
+    # stupid python
+    wanted = [e for e in wanted if e]
 
-        name = 'master'
-        remote_branch = origin.open_branch()
+    if not wanted:
+        try:
+            repo = origin.open_repository()
+            if not repo.user_transport.listable():
+                # this repository is not usable for us
+                raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
+        except bzrlib.errors.NoRepositoryPresent:
+            wanted = ['master']
 
-        if not is_local:
-            peers[name] = remote_branch.base
-            branch = get_remote_branch(origin, remote_branch, name)
-        else:
-            branch = remote_branch
+    if wanted:
+        def list_wanted(url, wanted):
+            for name in wanted:
+                subdir = name if name != 'master' else ''
+                yield name, bzrlib.urlutils.join(url, subdir)
 
-        branches[name] = branch.base
-
-        return branch.repository
+        branch_list = list_wanted(url, wanted)
     else:
-        # repository
+        branch_list = find_branches(repo)
 
-        wanted = get_config('remote-bzr.branches').rstrip().split(', ')
-        # stupid python
-        wanted = [e for e in wanted if e]
+    for name, url in branch_list:
+        if not is_local:
+            peers[name] = url
+        branches[name] = url
 
-        for name, remote_branch in find_branches(repo, wanted):
-
-            if not is_local:
-                peers[name] = remote_branch.base
-                branch = get_remote_branch(origin, remote_branch, name)
-            else:
-                branch = remote_branch
-
-            branches[name] = branch.base
-
-        return repo
+    return origin
 
 def fix_path(alias, orig_url):
     url = urlparse.urlparse(orig_url, 'file')
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 1dd3d70..0194c67 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -12,7 +12,7 @@
 # For remote repositories a local clone is stored in
 # "$GIT_DIR/hg/origin/clone/.hg/".
 
-from mercurial import hg, ui, bookmarks, context, encoding, node, error, extensions
+from mercurial import hg, ui, bookmarks, context, encoding, node, error, extensions, discovery, util
 
 import re
 import sys
@@ -29,9 +29,6 @@
 # named branches:
 # git config --global remote-hg.track-branches false
 #
-# If you don't want to force pushes (and thus risk creating new remote heads):
-# git config --global remote-hg.force-push false
-#
 # If you want the equivalent of hg's clone/pull--insecure option:
 # git config --global remote-hg.insecure true
 #
@@ -55,6 +52,8 @@
 AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
 RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
 
+VERSION = 2
+
 def die(msg, *args):
     sys.stderr.write('ERROR: %s\n' % (msg % args))
     sys.exit(1)
@@ -72,8 +71,11 @@
     m = { '100755': 'x', '120000': 'l' }
     return m.get(mode, '')
 
-def hghex(node):
-    return hg.node.hex(node)
+def hghex(n):
+    return node.hex(n)
+
+def hgbin(n):
+    return node.bin(n)
 
 def hgref(ref):
     return ref.replace('___', ' ')
@@ -81,6 +83,11 @@
 def gitref(ref):
     return ref.replace(' ', '___')
 
+def check_version(*check):
+    if not hg_version:
+        return True
+    return hg_version >= check
+
 def get_config(config):
     cmd = ['git', 'config', '--get', config]
     process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
@@ -98,14 +105,27 @@
 
 class Marks:
 
-    def __init__(self, path):
+    def __init__(self, path, repo):
         self.path = path
+        self.repo = repo
+        self.clear()
+        self.load()
+
+        if self.version < VERSION:
+            if self.version == 1:
+                self.upgrade_one()
+
+            # upgraded?
+            if self.version < VERSION:
+                self.clear()
+                self.version = VERSION
+
+    def clear(self):
         self.tips = {}
         self.marks = {}
         self.rev_marks = {}
         self.last_mark = 0
-
-        self.load()
+        self.version = 0
 
     def load(self):
         if not os.path.exists(self.path):
@@ -116,12 +136,21 @@
         self.tips = tmp['tips']
         self.marks = tmp['marks']
         self.last_mark = tmp['last-mark']
+        self.version = tmp.get('version', 1)
 
         for rev, mark in self.marks.iteritems():
-            self.rev_marks[mark] = int(rev)
+            self.rev_marks[mark] = rev
+
+    def upgrade_one(self):
+        def get_id(rev):
+            return hghex(self.repo.changelog.node(int(rev)))
+        self.tips = dict((name, get_id(rev)) for name, rev in self.tips.iteritems())
+        self.marks = dict((get_id(rev), mark) for rev, mark in self.marks.iteritems())
+        self.rev_marks = dict((mark, get_id(rev)) for mark, rev in self.rev_marks.iteritems())
+        self.version = 2
 
     def dict(self):
-        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
+        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version }
 
     def store(self):
         json.dump(self.dict(), open(self.path, 'w'))
@@ -130,10 +159,10 @@
         return str(self.dict())
 
     def from_rev(self, rev):
-        return self.marks[str(rev)]
+        return self.marks[rev]
 
     def to_rev(self, mark):
-        return self.rev_marks[mark]
+        return str(self.rev_marks[mark])
 
     def next_mark(self):
         self.last_mark += 1
@@ -141,19 +170,19 @@
 
     def get_mark(self, rev):
         self.last_mark += 1
-        self.marks[str(rev)] = self.last_mark
+        self.marks[rev] = self.last_mark
         return self.last_mark
 
     def new_mark(self, rev, mark):
-        self.marks[str(rev)] = mark
+        self.marks[rev] = mark
         self.rev_marks[mark] = rev
         self.last_mark = mark
 
     def is_marked(self, rev):
-        return str(rev) in self.marks
+        return rev in self.marks
 
     def get_tip(self, branch):
-        return self.tips.get(branch, 0)
+        return str(self.tips[branch])
 
     def set_tip(self, branch, tip):
         self.tips[branch] = tip
@@ -261,7 +290,7 @@
     removed = set()
 
     # load earliest manifest first for caching reasons
-    prev = repo[parent].manifest().copy()
+    prev = parent.manifest().copy()
     cur = ctx.manifest()
 
     for fn in cur:
@@ -329,6 +358,21 @@
 
     return '%s <%s>' % (name, mail)
 
+def updatebookmarks(repo, peer):
+    remotemarks = peer.listkeys('bookmarks')
+    localmarks = repo._bookmarks
+
+    if not remotemarks:
+        return
+
+    for k, v in remotemarks.iteritems():
+        localmarks[k] = hgbin(v)
+
+    if hasattr(localmarks, 'write'):
+        localmarks.write()
+    else:
+        bookmarks.write(repo)
+
 def get_repo(url, alias):
     global dirname, peer
 
@@ -339,35 +383,41 @@
     if get_config_bool('remote-hg.insecure'):
         myui.setconfig('web', 'cacerts', '')
 
-    try:
-        mod = extensions.load(myui, 'hgext.schemes', None)
-        mod.extsetup(myui)
-    except ImportError:
-        pass
+    extensions.loadall(myui)
 
-    if hg.islocal(url):
+    if hg.islocal(url) and not os.environ.get('GIT_REMOTE_HG_TEST_REMOTE'):
         repo = hg.repository(myui, url)
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
     else:
+        shared_path = os.path.join(gitdir, 'hg')
+        if not os.path.exists(shared_path):
+            try:
+                hg.clone(myui, {}, url, shared_path, update=False, pull=True)
+            except:
+                die('Repository error')
+
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
+
         local_path = os.path.join(dirname, 'clone')
         if not os.path.exists(local_path):
-            try:
-                peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
-            except:
-                die('Repository error')
-            repo = dstpeer.local()
-        else:
-            repo = hg.repository(myui, local_path)
-            try:
-                peer = hg.peer(myui, {}, url)
-            except:
-                die('Repository error')
-            repo.pull(peer, heads=None, force=True)
+            hg.share(myui, shared_path, local_path, update=False)
+
+        repo = hg.repository(myui, local_path)
+        try:
+            peer = hg.peer(myui, {}, url)
+        except:
+            die('Repository error')
+        repo.pull(peer, heads=None, force=True)
+
+        updatebookmarks(repo, peer)
 
     return repo
 
 def rev_to_mark(rev):
     global marks
-    return marks.from_rev(rev)
+    return marks.from_rev(rev.hex())
 
 def mark_to_rev(mark):
     global marks
@@ -377,17 +427,24 @@
     global prefix, marks, mode
 
     ename = '%s/%s' % (kind, name)
-    tip = marks.get_tip(ename)
+    try:
+        tip = marks.get_tip(ename)
+        tip = repo[tip].rev()
+    except:
+        tip = 0
 
     revs = xrange(tip, head.rev() + 1)
-    count = 0
-
-    revs = [rev for rev in revs if not marks.is_marked(rev)]
+    total = len(revs)
 
     for rev in revs:
 
         c = repo[rev]
-        (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
+        node = c.node()
+
+        if marks.is_marked(c.hex()):
+            continue
+
+        (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(node)
         rev_branch = extra['branch']
 
         author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
@@ -397,7 +454,7 @@
         else:
             committer = author
 
-        parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
+        parents = [repo[p] for p in repo.changelog.parentrevs(rev) if p >= 0]
 
         if len(parents) == 0:
             modified = c.manifest().keys()
@@ -439,7 +496,7 @@
         modified_final = export_files(c.filectx(f) for f in modified)
 
         print "commit %s/%s" % (prefix, ename)
-        print "mark :%d" % (marks.get_mark(rev))
+        print "mark :%d" % (marks.get_mark(c.hex()))
         print "author %s" % (author)
         print "committer %s" % (committer)
         print "data %d" % (len(desc))
@@ -450,22 +507,22 @@
             if len(parents) > 1:
                 print "merge :%s" % (rev_to_mark(parents[1]))
 
-        for f in modified_final:
-            print "M %s :%u %s" % f
         for f in removed:
             print "D %s" % (fix_file_path(f))
+        for f in modified_final:
+            print "M %s :%u %s" % f
         print
 
-        count += 1
-        if (count % 100 == 0):
-            print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
+        progress = (rev - tip)
+        if (progress % 100 == 0):
+            print "progress revision %d '%s' (%d/%d)" % (rev, name, progress, total)
 
     # make sure the ref is updated
     print "reset %s/%s" % (prefix, ename)
-    print "from :%u" % rev_to_mark(rev)
+    print "from :%u" % rev_to_mark(head)
     print
 
-    marks.set_tip(ename, rev)
+    marks.set_tip(ename, head.hex())
 
 def export_tag(repo, tag):
     export_ref(repo, tag, 'tags', repo[hgref(tag)])
@@ -497,15 +554,12 @@
     if os.path.exists(path):
         print "*import-marks %s" % path
     print "*export-marks %s" % path
+    print "option"
 
     print
 
-def branch_tip(repo, branch):
-    # older versions of mercurial don't have this
-    if hasattr(repo, 'branchtip'):
-        return repo.branchtip(branch)
-    else:
-        return repo.branchtags()[branch]
+def branch_tip(branch):
+    return branches[branch][-1]
 
 def get_branch_tip(repo, branch):
     global branches
@@ -517,27 +571,21 @@
     # verify there's only one head
     if (len(heads) > 1):
         warn("Branch '%s' has more than one head, consider merging" % branch)
-        return branch_tip(repo, hgref(branch))
+        return branch_tip(hgref(branch))
 
     return heads[0]
 
 def list_head(repo, cur):
-    global g_head, bmarks
+    global g_head, bmarks, fake_bmark
 
-    head = bookmarks.readcurrent(repo)
-    if head:
-        node = repo[head]
-    else:
-        # fake bookmark from current branch
-        head = cur
-        node = repo['.']
-        if not node:
-            node = repo['tip']
-        if not node:
-            return
-        if head == 'default':
-            head = 'master'
-        bmarks[head] = node
+    if 'default' not in branches:
+        # empty repo
+        return
+
+    node = repo[branch_tip('default')]
+    head = 'master' if not 'master' in bmarks else 'default'
+    fake_bmark = head
+    bmarks[head] = node
 
     head = gitref(head)
     print "@refs/heads/%s HEAD" % head
@@ -551,15 +599,17 @@
         bmarks[bmark] = repo[node]
 
     cur = repo.dirstate.branch()
+    orig = peer if peer else repo
+
+    for branch, heads in orig.branchmap().iteritems():
+        # only open heads
+        heads = [h for h in heads if 'close' not in repo.changelog.read(h)[5]]
+        if heads:
+            branches[branch] = heads
 
     list_head(repo, cur)
 
     if track_branches:
-        for branch in repo.branchmap():
-            heads = repo.branchheads(branch)
-            if len(heads):
-                branches[branch] = heads
-
         for branch in branches:
             print "? refs/heads/branches/%s" % gitref(branch)
 
@@ -582,6 +632,7 @@
     if os.path.exists(path):
         print "feature import-marks=%s" % path
     print "feature export-marks=%s" % path
+    print "feature force"
     sys.stdout.flush()
 
     tmp = encoding.encoding
@@ -671,6 +722,11 @@
             die('Unknown file command: %s' % line)
         files[path] = f
 
+    # only export the commits if we are on an internal proxy repo
+    if dry_run and not peer:
+        parsed_refs[ref] = None
+        return
+
     def getfilectx(repo, memctx, f):
         of = files[f]
         if 'deleted' in of:
@@ -692,14 +748,14 @@
         extra['committer'] = "%s %u %u" % committer
 
     if from_mark:
-        p1 = repo.changelog.node(mark_to_rev(from_mark))
+        p1 = mark_to_rev(from_mark)
     else:
-        p1 = '\0' * 20
+        p1 = '0' * 40
 
     if merge_mark:
-        p2 = repo.changelog.node(mark_to_rev(merge_mark))
+        p2 = mark_to_rev(merge_mark)
     else:
-        p2 = '\0' * 20
+        p2 = '0' * 40
 
     #
     # If files changed from any of the parents, hg wants to know, but in git if
@@ -735,14 +791,12 @@
     tmp = encoding.encoding
     encoding.encoding = 'utf-8'
 
-    node = repo.commitctx(ctx)
+    node = hghex(repo.commitctx(ctx))
 
     encoding.encoding = tmp
 
-    rev = repo[node].rev()
-
     parsed_refs[ref] = node
-    marks.new_mark(rev, commit_mark)
+    marks.new_mark(node, commit_mark)
 
 def parse_reset(parser):
     global parsed_refs
@@ -758,8 +812,11 @@
     from_mark = parser.get_mark()
     parser.next()
 
-    node = parser.repo.changelog.node(mark_to_rev(from_mark))
-    parsed_refs[ref] = node
+    try:
+        rev = mark_to_rev(from_mark)
+    except KeyError:
+        rev = None
+    parsed_refs[ref] = rev
 
 def parse_tag(parser):
     name = parser[1]
@@ -775,7 +832,7 @@
 
 def write_tag(repo, tag, node, msg, author):
     branch = repo[node].branch()
-    tip = branch_tip(repo, branch)
+    tip = branch_tip(branch)
     tip = repo[tip]
 
     def getfilectx(repo, memctx, f):
@@ -784,18 +841,28 @@
             data = fctx.data()
         except error.ManifestLookupError:
             data = ""
-        content = data + "%s %s\n" % (hghex(node), tag)
+        content = data + "%s %s\n" % (node, tag)
         return context.memfilectx(f, content, False, False, None)
 
     p1 = tip.hex()
-    p2 = '\0' * 20
-    if not author:
-        author = (None, 0, 0)
-    user, date, tz = author
+    p2 = '0' * 40
+    if author:
+        user, date, tz = author
+        date_tz = (date, tz)
+    else:
+        cmd = ['git', 'var', 'GIT_COMMITTER_IDENT']
+        process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        output, _ = process.communicate()
+        m = re.match('^.* <.*>', output)
+        if m:
+            user = m.group(0)
+        else:
+            user = repo.ui.username()
+        date_tz = None
 
     ctx = context.memctx(repo, (p1, p2), msg,
             ['.hgtags'], getfilectx,
-            user, (date, tz), {'branch' : branch})
+            user, date_tz, {'branch' : branch})
 
     tmp = encoding.encoding
     encoding.encoding = 'utf-8'
@@ -804,12 +871,132 @@
 
     encoding.encoding = tmp
 
-    return tagnode
+    return (tagnode, branch)
+
+def checkheads_bmark(repo, ref, ctx):
+    bmark = ref[len('refs/heads/'):]
+    if not bmark in bmarks:
+        # new bmark
+        return True
+
+    ctx_old = bmarks[bmark]
+    ctx_new = ctx
+    if not repo.changelog.descendant(ctx_old.rev(), ctx_new.rev()):
+        if force_push:
+            print "ok %s forced update" % ref
+        else:
+            print "error %s non-fast forward" % ref
+            return False
+
+    return True
+
+def checkheads(repo, remote, p_revs):
+
+    remotemap = remote.branchmap()
+    if not remotemap:
+        # empty repo
+        return True
+
+    new = {}
+    ret = True
+
+    for node, ref in p_revs.iteritems():
+        ctx = repo[node]
+        branch = ctx.branch()
+        if not branch in remotemap:
+            # new branch
+            continue
+        if not ref.startswith('refs/heads/branches'):
+            if ref.startswith('refs/heads/'):
+                if not checkheads_bmark(repo, ref, ctx):
+                    ret = False
+
+            # only check branches
+            continue
+        new.setdefault(branch, []).append(ctx.rev())
+
+    for branch, heads in new.iteritems():
+        old = [repo.changelog.rev(x) for x in remotemap[branch]]
+        for rev in heads:
+            if check_version(2, 3):
+                ancestors = repo.changelog.ancestors([rev], stoprev=min(old))
+            else:
+                ancestors = repo.changelog.ancestors(rev)
+            found = False
+
+            for x in old:
+                if x in ancestors:
+                    found = True
+                    break
+
+            if found:
+                continue
+
+            node = repo.changelog.node(rev)
+            ref = p_revs[node]
+            if force_push:
+                print "ok %s forced update" % ref
+            else:
+                print "error %s non-fast forward" % ref
+                ret = False
+
+    return ret
+
+def push_unsafe(repo, remote, parsed_refs, p_revs):
+
+    force = force_push
+
+    fci = discovery.findcommonincoming
+    commoninc = fci(repo, remote, force=force)
+    common, _, remoteheads = commoninc
+
+    if not checkheads(repo, remote, p_revs):
+        return None
+
+    cg = repo.getbundle('push', heads=list(p_revs), common=common)
+
+    unbundle = remote.capable('unbundle')
+    if unbundle:
+        if force:
+            remoteheads = ['force']
+        return remote.unbundle(cg, remoteheads, 'push')
+    else:
+        return remote.addchangegroup(cg, 'push', repo.url())
+
+def push(repo, remote, parsed_refs, p_revs):
+    if hasattr(remote, 'canpush') and not remote.canpush():
+        print "error cannot push"
+
+    if not p_revs:
+        # nothing to push
+        return
+
+    lock = None
+    unbundle = remote.capable('unbundle')
+    if not unbundle:
+        lock = remote.lock()
+    try:
+        ret = push_unsafe(repo, remote, parsed_refs, p_revs)
+    finally:
+        if lock is not None:
+            lock.release()
+
+    return ret
+
+def check_tip(ref, kind, name, heads):
+    try:
+        ename = '%s/%s' % (kind, name)
+        tip = marks.get_tip(ename)
+    except KeyError:
+        return True
+    else:
+        return tip in heads
 
 def do_export(parser):
     global parsed_refs, bmarks, peer
 
     p_bmarks = []
+    p_revs = {}
 
     parser.next()
 
@@ -827,72 +1014,114 @@
         else:
             die('unhandled export command: %s' % line)
 
+    need_fetch = False
+
     for ref, node in parsed_refs.iteritems():
+        bnode = hgbin(node) if node else None
         if ref.startswith('refs/heads/branches'):
             branch = ref[len('refs/heads/branches/'):]
-            if branch in branches and node in branches[branch]:
+            if branch in branches and bnode in branches[branch]:
                 # up to date
                 continue
+
+            if peer:
+                remotemap = peer.branchmap()
+                if remotemap and branch in remotemap:
+                    heads = [hghex(e) for e in remotemap[branch]]
+                    if not check_tip(ref, 'branches', branch, heads):
+                        print "error %s fetch first" % ref
+                        need_fetch = True
+                        continue
+
+            p_revs[bnode] = ref
             print "ok %s" % ref
         elif ref.startswith('refs/heads/'):
             bmark = ref[len('refs/heads/'):]
-            p_bmarks.append((bmark, node))
-            continue
+            new = node
+            old = bmarks[bmark].hex() if bmark in bmarks else ''
+
+            if old == new:
+                continue
+
+            print "ok %s" % ref
+            if bmark != fake_bmark and \
+                    not (bmark == 'master' and bmark not in parser.repo._bookmarks):
+                p_bmarks.append((ref, bmark, old, new))
+
+            if peer:
+                remote_old = peer.listkeys('bookmarks').get(bmark)
+                if remote_old:
+                    if not check_tip(ref, 'bookmarks', bmark, remote_old):
+                        print "error %s fetch first" % ref
+                        need_fetch = True
+                        continue
+
+            p_revs[bnode] = ref
         elif ref.startswith('refs/tags/'):
+            if dry_run:
+                print "ok %s" % ref
+                continue
             tag = ref[len('refs/tags/'):]
             tag = hgref(tag)
             author, msg = parsed_tags.get(tag, (None, None))
             if mode == 'git':
                 if not msg:
-                    msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
-                write_tag(parser.repo, tag, node, msg, author)
+                    msg = 'Added tag %s for changeset %s' % (tag, node[:12]);
+                tagnode, branch = write_tag(parser.repo, tag, node, msg, author)
+                p_revs[tagnode] = 'refs/heads/branches/' + gitref(branch)
             else:
                 fp = parser.repo.opener('localtags', 'a')
-                fp.write('%s %s\n' % (hghex(node), tag))
+                fp.write('%s %s\n' % (node, tag))
                 fp.close()
+            p_revs[bnode] = ref
             print "ok %s" % ref
         else:
             # transport-helper/fast-export bugs
             continue
 
+    if need_fetch:
+        print
+        return
+
+    if dry_run:
+        if peer and not force_push:
+            checkheads(parser.repo, peer, p_revs)
+        print
+        return
+
     if peer:
-        parser.repo.push(peer, force=force_push, newbranch=True)
+        if not push(parser.repo, peer, parsed_refs, p_revs):
+            # do not update bookmarks
+            print
+            return
 
-    # handle bookmarks
-    for bmark, node in p_bmarks:
-        ref = 'refs/heads/' + bmark
-        new = hghex(node)
-
-        if bmark in bmarks:
-            old = bmarks[bmark].hex()
-        else:
-            old = ''
-
-        if old == new:
-            continue
-
-        if bmark == 'master' and 'master' not in parser.repo._bookmarks:
-            # fake bookmark
-            print "ok %s" % ref
-            continue
-        elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
-            # updated locally
-            pass
-        else:
-            print "error %s" % ref
-            continue
-
-        if peer:
-            rb = peer.listkeys('bookmarks')
-            old = rb.get(bmark, '')
+        # update remote bookmarks
+        remote_bmarks = peer.listkeys('bookmarks')
+        for ref, bmark, old, new in p_bmarks:
+            if force_push:
+                old = remote_bmarks.get(bmark, '')
             if not peer.pushkey('bookmarks', bmark, old, new):
                 print "error %s" % ref
-                continue
-
-        print "ok %s" % ref
+    else:
+        # update local bookmarks
+        for ref, bmark, old, new in p_bmarks:
+            if not bookmarks.pushbookmark(parser.repo, bmark, old, new):
+                print "error %s" % ref
 
     print
 
+def do_option(parser):
+    global dry_run, force_push
+    _, key, value = parser.line.split(' ')
+    if key == 'dry-run':
+        dry_run = (value == 'true')
+        print 'ok'
+    elif key == 'force':
+        force_push = (value == 'true')
+        print 'ok'
+    else:
+        print 'unsupported'
+
 def fix_path(alias, repo, orig_url):
     url = urlparse.urlparse(orig_url, 'file')
     if url.scheme != 'file' or os.path.isabs(url.path):
@@ -902,12 +1131,14 @@
     subprocess.call(cmd)
 
 def main(args):
-    global prefix, dirname, branches, bmarks
+    global prefix, gitdir, dirname, branches, bmarks
     global marks, blob_marks, parsed_refs
     global peer, mode, bad_mail, bad_name
     global track_branches, force_push, is_tmp
     global parsed_tags
     global filenodes
+    global fake_bmark, hg_version
+    global dry_run
 
     alias = args[1]
     url = args[2]
@@ -915,7 +1146,7 @@
 
     hg_git_compat = get_config_bool('remote-hg.hg-git-compat')
     track_branches = get_config_bool('remote-hg.track-branches', True)
-    force_push = get_config_bool('remote-hg.force-push')
+    force_push = False
 
     if hg_git_compat:
         mode = 'hg'
@@ -941,6 +1172,12 @@
     marks = None
     parsed_tags = {}
     filenodes = {}
+    fake_bmark = None
+    try:
+        hg_version = tuple(int(e) for e in util.version().split('.'))
+    except:
+        hg_version = None
+    dry_run = False
 
     repo = get_repo(url, alias)
     prefix = 'refs/hg/%s' % alias
@@ -948,11 +1185,8 @@
     if not is_tmp:
         fix_path(alias, peer or repo, url)
 
-    if not os.path.exists(dirname):
-        os.makedirs(dirname)
-
     marks_path = os.path.join(dirname, 'marks-hg')
-    marks = Marks(marks_path)
+    marks = Marks(marks_path, repo)
 
     if sys.platform == 'win32':
         import msvcrt
@@ -968,6 +1202,8 @@
             do_import(parser)
         elif parser.check('export'):
             do_export(parser)
+        elif parser.check('option'):
+            do_option(parser)
         else:
             die('unhandled command: %s' % line)
         sys.stdout.flush()
diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh
index 5dfa070..dce281f 100755
--- a/contrib/remote-helpers/test-bzr.sh
+++ b/contrib/remote-helpers/test-bzr.sh
@@ -12,86 +12,90 @@
 	test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import bzrlib'; then
+if ! python -c 'import bzrlib'; then
 	skip_all='skipping remote-bzr tests; bzr not available'
 	test_done
 fi
 
 check () {
-	(cd $1 &&
-	git log --format='%s' -1 &&
-	git symbolic-ref HEAD) > actual &&
-	(echo $2 &&
-	echo "refs/heads/$3") > expected &&
+	echo $3 > expected &&
+	git --git-dir=$1/.git log --format='%s' -1 $2 > actual
 	test_cmp expected actual
 }
 
 bzr whoami "A U Thor <author@example.com>"
 
 test_expect_success 'cloning' '
-  (bzr init bzrrepo &&
-  cd bzrrepo &&
-  echo one > content &&
-  bzr add content &&
-  bzr commit -m one
-  ) &&
+	(
+	bzr init bzrrepo &&
+	cd bzrrepo &&
+	echo one > content &&
+	bzr add content &&
+	bzr commit -m one
+	) &&
 
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  check gitrepo one master
+	git clone "bzr::bzrrepo" gitrepo &&
+	check gitrepo HEAD one
 '
 
 test_expect_success 'pulling' '
-  (cd bzrrepo &&
-  echo two > content &&
-  bzr commit -m two
-  ) &&
+	(
+	cd bzrrepo &&
+	echo two > content &&
+	bzr commit -m two
+	) &&
 
-  (cd gitrepo && git pull) &&
+	(cd gitrepo && git pull) &&
 
-  check gitrepo two master
+	check gitrepo HEAD two
 '
 
 test_expect_success 'pushing' '
-  (cd gitrepo &&
-  echo three > content &&
-  git commit -a -m three &&
-  git push
-  ) &&
+	(
+	cd gitrepo &&
+	echo three > content &&
+	git commit -a -m three &&
+	git push
+	) &&
 
-  echo three > expected &&
-  cat bzrrepo/content > actual &&
-  test_cmp expected actual
+	echo three > expected &&
+	cat bzrrepo/content > actual &&
+	test_cmp expected actual
 '
 
 test_expect_success 'roundtrip' '
-  (cd gitrepo &&
-  git pull &&
-  git log --format="%s" -1 origin/master > actual) &&
-  echo three > expected &&
-  test_cmp expected actual &&
+	(
+	cd gitrepo &&
+	git pull &&
+	git log --format="%s" -1 origin/master > actual
+	) &&
+	echo three > expected &&
+	test_cmp expected actual &&
 
-  (cd gitrepo && git push && git pull) &&
+	(cd gitrepo && git push && git pull) &&
 
-  (cd bzrrepo &&
-  echo four > content &&
-  bzr commit -m four
-  ) &&
+	(
+	cd bzrrepo &&
+	echo four > content &&
+	bzr commit -m four
+	) &&
 
-  (cd gitrepo && git pull && git push) &&
+	(cd gitrepo && git pull && git push) &&
 
-  check gitrepo four master &&
+	check gitrepo HEAD four &&
 
-  (cd gitrepo &&
-  echo five > content &&
-  git commit -a -m five &&
-  git push && git pull
-  ) &&
+	(
+	cd gitrepo &&
+	echo five > content &&
+	git commit -a -m five &&
+	git push && git pull
+	) &&
 
-  (cd bzrrepo && bzr revert) &&
+	(cd bzrrepo && bzr revert) &&
 
-  echo five > expected &&
-  cat bzrrepo/content > actual &&
-  test_cmp expected actual
+	echo five > expected &&
+	cat bzrrepo/content > actual &&
+	test_cmp expected actual
 '
 
 cat > expected <<EOF
@@ -101,29 +105,35 @@
 EOF
 
 test_expect_success 'special modes' '
-  (cd bzrrepo &&
-  echo exec > executable
-  chmod +x executable &&
-  bzr add executable
-  bzr commit -m exec &&
-  ln -s content link
-  bzr add link
-  bzr commit -m link &&
-  mkdir dir &&
-  bzr add dir &&
-  bzr commit -m dir) &&
+	(
+	cd bzrrepo &&
+	echo exec > executable
+	chmod +x executable &&
+	bzr add executable
+	bzr commit -m exec &&
+	ln -s content link
+	bzr add link
+	bzr commit -m link &&
+	mkdir dir &&
+	bzr add dir &&
+	bzr commit -m dir
+	) &&
 
-  (cd gitrepo &&
-  git pull
-  git ls-tree HEAD > ../actual) &&
+	(
+	cd gitrepo &&
+	git pull
+	git ls-tree HEAD > ../actual
+	) &&
 
-  test_cmp expected actual &&
+	test_cmp expected actual &&
 
-  (cd gitrepo &&
-  git cat-file -p HEAD:link > ../actual) &&
+	(
+	cd gitrepo &&
+	git cat-file -p HEAD:link > ../actual
+	) &&
 
-  printf content > expected &&
-  test_cmp expected actual
+	printf content > expected &&
+	test_cmp expected actual
 '
 
 cat > expected <<EOF
@@ -134,134 +144,145 @@
 EOF
 
 test_expect_success 'moving directory' '
-  (cd bzrrepo &&
-  mkdir movedir &&
-  echo one > movedir/one &&
-  echo two > movedir/two &&
-  bzr add movedir &&
-  bzr commit -m movedir &&
-  bzr mv movedir movedir-new &&
-  bzr commit -m movedir-new) &&
+	(
+	cd bzrrepo &&
+	mkdir movedir &&
+	echo one > movedir/one &&
+	echo two > movedir/two &&
+	bzr add movedir &&
+	bzr commit -m movedir &&
+	bzr mv movedir movedir-new &&
+	bzr commit -m movedir-new
+	) &&
 
-  (cd gitrepo &&
-  git pull &&
-  git ls-tree HEAD > ../actual) &&
+	(
+	cd gitrepo &&
+	git pull &&
+	git ls-tree HEAD > ../actual
+	) &&
 
-  test_cmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'different authors' '
-  (cd bzrrepo &&
-  echo john >> content &&
-  bzr commit -m john \
-    --author "Jane Rey <jrey@example.com>" \
-    --author "John Doe <jdoe@example.com>") &&
+	(
+	cd bzrrepo &&
+	echo john >> content &&
+	bzr commit -m john \
+	  --author "Jane Rey <jrey@example.com>" \
+	  --author "John Doe <jdoe@example.com>"
+	) &&
 
-  (cd gitrepo &&
-  git pull &&
-  git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual) &&
+	(
+	cd gitrepo &&
+	git pull &&
+	git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual
+	) &&
 
-  echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected &&
-  test_cmp expected actual
+	echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected &&
+	test_cmp expected actual
 '
 
+# cleanup previous stuff
+rm -rf bzrrepo gitrepo
+
 test_expect_success 'fetch utf-8 filenames' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" &&
+	test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C" &&
 
-  LC_ALL=en_US.UTF-8
-  export LC_ALL
-  (
-  bzr init bzrrepo &&
-  cd bzrrepo &&
+	LC_ALL=en_US.UTF-8
+	export LC_ALL
 
-  echo test >> "ærø" &&
-  bzr add "ærø" &&
-  echo test >> "ø~?" &&
-  bzr add "ø~?" &&
-  bzr commit -m add-utf-8 &&
-  echo test >> "ærø" &&
-  bzr commit -m test-utf-8 &&
-  bzr rm "ø~?" &&
-  bzr mv "ærø" "ø~?" &&
-  bzr commit -m bzr-mv-utf-8
-  ) &&
+	(
+	bzr init bzrrepo &&
+	cd bzrrepo &&
 
-  (
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  cd gitrepo &&
-  git -c core.quotepath=false ls-files > ../actual
-  ) &&
-  echo "ø~?" > expected &&
-  test_cmp expected actual
+	echo test >> "ærø" &&
+	bzr add "ærø" &&
+	echo test >> "ø~?" &&
+	bzr add "ø~?" &&
+	bzr commit -m add-utf-8 &&
+	echo test >> "ærø" &&
+	bzr commit -m test-utf-8 &&
+	bzr rm "ø~?" &&
+	bzr mv "ærø" "ø~?" &&
+	bzr commit -m bzr-mv-utf-8
+	) &&
+
+	(
+	git clone "bzr::bzrrepo" gitrepo &&
+	cd gitrepo &&
+	git -c core.quotepath=false ls-files > ../actual
+	) &&
+	echo "ø~?" > expected &&
+	test_cmp expected actual
 '
 
 test_expect_success 'push utf-8 filenames' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" &&
+	test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C" &&
 
-  LC_ALL=en_US.UTF-8
-  export LC_ALL
+	mkdir -p tmp && cd tmp &&
 
-  (
-  bzr init bzrrepo &&
-  cd bzrrepo &&
+	LC_ALL=en_US.UTF-8
+	export LC_ALL
 
-  echo one >> content &&
-  bzr add content &&
-  bzr commit -m one
-  ) &&
+	(
+	bzr init bzrrepo &&
+	cd bzrrepo &&
 
-  (
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  cd gitrepo &&
+	echo one >> content &&
+	bzr add content &&
+	bzr commit -m one
+	) &&
 
-  echo test >> "ærø" &&
-  git add "ærø" &&
-  git commit -m utf-8 &&
+	(
+	git clone "bzr::bzrrepo" gitrepo &&
+	cd gitrepo &&
 
-  git push
-  ) &&
+	echo test >> "ærø" &&
+	git add "ærø" &&
+	git commit -m utf-8 &&
 
-  (cd bzrrepo && bzr ls > ../actual) &&
-  printf "content\nærø\n" > expected &&
-  test_cmp expected actual
+	git push
+	) &&
+
+	(cd bzrrepo && bzr ls > ../actual) &&
+	printf "content\nærø\n" > expected &&
+	test_cmp expected actual
 '
 
 test_expect_success 'pushing a merge' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf bzrrepo gitrepo" &&
 
-  (
-  bzr init bzrrepo &&
-  cd bzrrepo &&
-  echo one > content &&
-  bzr add content &&
-  bzr commit -m one
-  ) &&
+	(
+	bzr init bzrrepo &&
+	cd bzrrepo &&
+	echo one > content &&
+	bzr add content &&
+	bzr commit -m one
+	) &&
 
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
+	git clone "bzr::bzrrepo" gitrepo &&
 
-  (
-  cd bzrrepo &&
-  echo two > content &&
-  bzr commit -m two
-  ) &&
+	(
+	cd bzrrepo &&
+	echo two > content &&
+	bzr commit -m two
+	) &&
 
-  (
-  cd gitrepo &&
-  echo three > content &&
-  git commit -a -m three &&
-  git fetch &&
-  git merge origin/master || true &&
-  echo three > content &&
-  git commit -a --no-edit &&
-  git push
-  ) &&
+	(
+	cd gitrepo &&
+	echo three > content &&
+	git commit -a -m three &&
+	git fetch &&
+	git merge origin/master || true &&
+	echo three > content &&
+	git commit -a --no-edit &&
+	git push
+	) &&
 
-  echo three > expected &&
-  cat bzrrepo/content > actual &&
-  test_cmp expected actual
+	echo three > expected &&
+	cat bzrrepo/content > actual &&
+	test_cmp expected actual
 '
 
 cat > expected <<EOF
@@ -271,71 +292,70 @@
 EOF
 
 test_expect_success 'proper bzr repo' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf bzrrepo gitrepo" &&
 
-  bzr init-repo bzrrepo &&
+	bzr init-repo bzrrepo &&
 
-  bzr init bzrrepo/trunk &&
-  (
-  cd bzrrepo/trunk &&
-  echo one >> content &&
-  bzr add content &&
-  bzr commit -m one
-  ) &&
+	(
+	bzr init bzrrepo/trunk &&
+	cd bzrrepo/trunk &&
+	echo one >> content &&
+	bzr add content &&
+	bzr commit -m one
+	) &&
 
-  bzr branch bzrrepo/trunk bzrrepo/branch &&
-  (
-  cd bzrrepo/branch &&
-  echo two >> content &&
-  bzr commit -m one
-  ) &&
+	(
+	bzr branch bzrrepo/trunk bzrrepo/branch &&
+	cd bzrrepo/branch &&
+	echo two >> content &&
+	bzr commit -m one
+	) &&
 
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  (
-  cd gitrepo &&
-  git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual
-  ) &&
+	(
+	git clone "bzr::bzrrepo" gitrepo &&
+	cd gitrepo &&
+	git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual
+	) &&
 
-  test_cmp ../expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'strip' '
-  # Do not imitate this style; always chdir inside a subshell instead
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf bzrrepo gitrepo" &&
 
-  (
-  bzr init bzrrepo &&
-  cd bzrrepo &&
+	(
+	bzr init bzrrepo &&
+	cd bzrrepo &&
 
-  echo one >> content &&
-  bzr add content &&
-  bzr commit -m one &&
+	echo one >> content &&
+	bzr add content &&
+	bzr commit -m one &&
 
-  echo two >> content &&
-  bzr commit -m two
-  ) &&
+	echo two >> content &&
+	bzr commit -m two
+	) &&
 
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
+	git clone "bzr::bzrrepo" gitrepo &&
 
-  (
-  cd bzrrepo &&
-  bzr uncommit --force &&
+	(
+	cd bzrrepo &&
+	bzr uncommit --force &&
 
-  echo three >> content &&
-  bzr commit -m three &&
+	echo three >> content &&
+	bzr commit -m three &&
 
-  echo four >> content &&
-  bzr commit -m four &&
-  bzr log --line | sed -e "s/^[0-9][0-9]*: //" > ../expected
-  ) &&
+	echo four >> content &&
+	bzr commit -m four &&
+	bzr log --line | sed -e "s/^[0-9][0-9]*: //" > ../expected
+	) &&
 
-  (cd gitrepo &&
-  git fetch &&
-  git log --format="%an %ad %s" --date=short origin/master > ../actual) &&
+	(
+	cd gitrepo &&
+	git fetch &&
+	git log --format="%an %ad %s" --date=short origin/master > ../actual
+	) &&
 
-  test_cmp expected actual
+	test_cmp expected actual
 '
 
 test_done
diff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh
index f569697..f83d67d 100755
--- a/contrib/remote-helpers/test-hg-bidi.sh
+++ b/contrib/remote-helpers/test-hg-bidi.sh
@@ -15,23 +15,22 @@
 	test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import mercurial'; then
+if ! python -c 'import mercurial'; then
 	skip_all='skipping remote-hg tests; mercurial not available'
 	test_done
 fi
 
 # clone to a git repo
 git_clone () {
-	git clone -q "hg::$PWD/$1" $2
+	git clone -q "hg::$1" $2
 }
 
 # clone to an hg repo
 hg_clone () {
 	(
 	hg init $2 &&
-	hg -R $2 bookmark -i master &&
 	cd $1 &&
-	git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
+	git push -q "hg::../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
 	) &&
 
 	(cd $2 && hg -q update)
@@ -41,17 +40,15 @@
 hg_push () {
 	(
 	cd $2
-	old=$(git symbolic-ref --short HEAD)
 	git checkout -q -b tmp &&
-	git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
-	git checkout -q $old &&
+	git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
+	git checkout -q @{-1} &&
 	git branch -q -D tmp 2> /dev/null || true
 	)
 }
 
 hg_log () {
-	hg -R $1 log --graph --debug >log &&
-	grep -v 'tag: *default/' log
+	hg -R $1 log --graph --debug
 }
 
 setup () {
@@ -67,6 +64,7 @@
 	echo "graphlog ="
 	) >> "$HOME"/.hgrc &&
 	git config --global remote-hg.hg-git-compat true
+	git config --global remote-hg.track-branches true
 
 	HGEDITOR=/usr/bin/true
 	GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
@@ -77,8 +75,7 @@
 setup
 
 test_expect_success 'encoding' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -115,8 +112,7 @@
 '
 
 test_expect_success 'file removal' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -148,8 +144,7 @@
 '
 
 test_expect_success 'git tags' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -177,8 +172,7 @@
 '
 
 test_expect_success 'hg branch' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -194,7 +188,7 @@
 	hg_clone gitrepo hgrepo &&
 
 	cd hgrepo &&
-	hg -q co master &&
+	hg -q co default &&
 	hg mv alpha beta &&
 	hg -q commit -m "rename alpha to beta" &&
 	hg branch gamma | grep -v "permanent and global" &&
@@ -214,8 +208,7 @@
 '
 
 test_expect_success 'hg tags' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -231,7 +224,7 @@
 	hg_clone gitrepo hgrepo &&
 
 	cd hgrepo &&
-	hg co master &&
+	hg co default &&
 	hg tag alpha
 	) &&
 
diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh
index 7f579c8..2219284 100755
--- a/contrib/remote-helpers/test-hg-hg-git.sh
+++ b/contrib/remote-helpers/test-hg-hg-git.sh
@@ -15,19 +15,20 @@
 	test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import mercurial'; then
+if ! python -c 'import mercurial'; then
 	skip_all='skipping remote-hg tests; mercurial not available'
 	test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import hggit'; then
+if ! python -c 'import hggit'; then
 	skip_all='skipping remote-hg tests; hg-git not available'
 	test_done
 fi
 
 # clone to a git repo with git
 git_clone_git () {
-	git clone -q "hg::$PWD/$1" $2
+	git clone -q "hg::$1" $2 &&
+	(cd $2 && git checkout master && git branch -D default)
 }
 
 # clone to an hg repo with git
@@ -36,7 +37,7 @@
 	hg init $2 &&
 	hg -R $2 bookmark -i master &&
 	cd $1 &&
-	git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
+	git push -q "hg::../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
 	) &&
 
 	(cd $2 && hg -q update)
@@ -61,10 +62,10 @@
 hg_push_git () {
 	(
 	cd $2
-	old=$(git symbolic-ref --short HEAD)
 	git checkout -q -b tmp &&
-	git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
-	git checkout -q $old &&
+	git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
+	git branch -D default &&
+	git checkout -q @{-1} &&
 	git branch -q -D tmp 2> /dev/null || true
 	)
 }
@@ -104,18 +105,18 @@
 	git config --global remote-hg.hg-git-compat true
 	git config --global remote-hg.track-branches false
 
-	HGEDITOR=/usr/bin/true
+	HGEDITOR=true
+	HGMERGE=true
 
 	GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
 	GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
-	export HGEDITOR GIT_AUTHOR_DATE GIT_COMMITTER_DATE
+	export HGEDITOR HGMERGE GIT_AUTHOR_DATE GIT_COMMITTER_DATE
 }
 
 setup
 
 test_expect_success 'executable bit' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -150,8 +151,7 @@
 '
 
 test_expect_success 'symlink' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -181,8 +181,7 @@
 '
 
 test_expect_success 'merge conflict 1' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	hg init hgrepo1 &&
@@ -198,7 +197,7 @@
 	echo C > afile &&
 	hg ci -m "A->C" &&
 
-	hg merge -r1 || true &&
+	hg merge -r1 &&
 	echo C > afile &&
 	hg resolve -m afile &&
 	hg ci -m "merge to C"
@@ -216,8 +215,7 @@
 '
 
 test_expect_success 'merge conflict 2' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	hg init hgrepo1 &&
@@ -251,8 +249,7 @@
 '
 
 test_expect_success 'converged merge' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	hg init hgrepo1 &&
@@ -287,8 +284,7 @@
 '
 
 test_expect_success 'encoding' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -327,8 +323,7 @@
 '
 
 test_expect_success 'file removal' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -367,8 +362,7 @@
 '
 
 test_expect_success 'git tags' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	(
 	git init -q gitrepo &&
@@ -394,8 +388,7 @@
 '
 
 test_expect_success 'hg author' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	for x in hg git; do
 		(
@@ -461,8 +454,7 @@
 '
 
 test_expect_success 'hg branch' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	for x in hg git; do
 		(
@@ -498,8 +490,7 @@
 '
 
 test_expect_success 'hg tags' '
-	mkdir -p tmp && cd tmp &&
-	test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
 	for x in hg git; do
 		(
diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh
index 8de2aa7..f7ce8aa 100755
--- a/contrib/remote-helpers/test-hg.sh
+++ b/contrib/remote-helpers/test-hg.sh
@@ -15,143 +15,678 @@
 	test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import mercurial'; then
+if ! python -c 'import mercurial'; then
 	skip_all='skipping remote-hg tests; mercurial not available'
 	test_done
 fi
 
 check () {
-	(cd $1 &&
-	git log --format='%s' -1 &&
-	git symbolic-ref HEAD) > actual &&
-	(echo $2 &&
-	echo "refs/heads/$3") > expected &&
+	echo $3 > expected &&
+	git --git-dir=$1/.git log --format='%s' -1 $2 > actual
 	test_cmp expected actual
 }
 
+check_branch () {
+	if [ -n "$3" ]; then
+		echo $3 > expected &&
+		hg -R $1 log -r $2 --template '{desc}\n' > actual &&
+		test_cmp expected actual
+	else
+		hg -R $1 branches > out &&
+		! grep $2 out
+	fi
+}
+
+check_bookmark () {
+	if [ -n "$3" ]; then
+		echo $3 > expected &&
+		hg -R $1 log -r "bookmark('$2')" --template '{desc}\n' > actual &&
+		test_cmp expected actual
+	else
+		hg -R $1 bookmarks > out &&
+		! grep $2 out
+	fi
+}
+
+check_push () {
+	local expected_ret=$1 ret=0 ref_ret=0 IFS=':'
+
+	shift
+	git push origin "$@" 2> error
+	ret=$?
+	cat error
+
+	while read branch kind
+	do
+		case "$kind" in
+		'new')
+			grep "^ \* \[new branch\] *${branch} -> ${branch}$" error || ref_ret=1
+			;;
+		'non-fast-forward')
+			grep "^ ! \[rejected\] *${branch} -> ${branch} (non-fast-forward)$" error || ref_ret=1
+			;;
+		'fetch-first')
+			grep "^ ! \[rejected\] *${branch} -> ${branch} (fetch first)$" error || ref_ret=1
+			;;
+		'forced-update')
+			grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *${branch} -> ${branch} (forced update)$" error || ref_ret=1
+			;;
+		'')
+			grep "^   [a-f0-9]*\.\.[a-f0-9]* *${branch} -> ${branch}$" error || ref_ret=1
+			;;
+		esac
+		let 'ref_ret' && echo "match for '$branch' failed" && break
+	done
+
+	if let 'expected_ret != ret || ref_ret'
+	then
+		return 1
+	fi
+
+	return 0
+}
+
 setup () {
 	(
 	echo "[ui]"
 	echo "username = H G Wells <wells@example.com>"
-	) >> "$HOME"/.hgrc
+	echo "[extensions]"
+	echo "mq ="
+	) >> "$HOME"/.hgrc &&
+
+	GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" &&
+	GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" &&
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
 setup
 
 test_expect_success 'cloning' '
-  test_when_finished "rm -rf gitrepo*" &&
+	test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  hg init hgrepo &&
-  cd hgrepo &&
-  echo zero > content &&
-  hg add content &&
-  hg commit -m zero
-  ) &&
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+	echo zero > content &&
+	hg add content &&
+	hg commit -m zero
+	) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo zero master
+	git clone "hg::hgrepo" gitrepo &&
+	check gitrepo HEAD zero
 '
 
 test_expect_success 'cloning with branches' '
-  test_when_finished "rm -rf gitrepo*" &&
+	test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  cd hgrepo &&
-  hg branch next &&
-  echo next > content &&
-  hg commit -m next
-  ) &&
+	(
+	cd hgrepo &&
+	hg branch next &&
+	echo next > content &&
+	hg commit -m next
+	) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo next next &&
-
-  (cd hgrepo && hg checkout default) &&
-
-  git clone "hg::$PWD/hgrepo" gitrepo2 &&
-  check gitrepo2 zero master
+	git clone "hg::hgrepo" gitrepo &&
+	check gitrepo origin/branches/next next
 '
 
 test_expect_success 'cloning with bookmarks' '
-  test_when_finished "rm -rf gitrepo*" &&
+	test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  cd hgrepo &&
-  hg bookmark feature-a &&
-  echo feature-a > content &&
-  hg commit -m feature-a
-  ) &&
+	(
+	cd hgrepo &&
+	hg checkout default &&
+	hg bookmark feature-a &&
+	echo feature-a > content &&
+	hg commit -m feature-a
+	) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo feature-a feature-a
-'
-
-test_expect_success 'cloning with detached head' '
-  test_when_finished "rm -rf gitrepo*" &&
-
-  (
-  cd hgrepo &&
-  hg update -r 0
-  ) &&
-
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo zero master
+	git clone "hg::hgrepo" gitrepo &&
+	check gitrepo origin/feature-a feature-a
 '
 
 test_expect_success 'update bookmark' '
-  test_when_finished "rm -rf gitrepo*" &&
+	test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  cd hgrepo &&
-  hg bookmark devel
-  ) &&
+	(
+	cd hgrepo &&
+	hg bookmark devel
+	) &&
 
-  (
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  cd gitrepo &&
-  git checkout devel &&
-  echo devel > content &&
-  git commit -a -m devel &&
-  git push
-  ) &&
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	git checkout --quiet devel &&
+	echo devel > content &&
+	git commit -a -m devel &&
+	git push --quiet
+	) &&
 
-  hg -R hgrepo bookmarks | egrep "devel[	 ]+3:"
+	check_bookmark hgrepo devel devel
 '
 
+test_expect_success 'new bookmark' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	git checkout --quiet -b feature-b &&
+	echo feature-b > content &&
+	git commit -a -m feature-b &&
+	git push --quiet origin feature-b
+	) &&
+
+	check_bookmark hgrepo feature-b feature-b
+'
+
+# cleanup previous stuff
+rm -rf hgrepo
+
 author_test () {
-  echo $1 >> content &&
-  hg commit -u "$2" -m "add $1" &&
-  echo "$3" >> ../expected
+	echo $1 >> content &&
+	hg commit -u "$2" -m "add $1" &&
+	echo "$3" >> ../expected
 }
 
 test_expect_success 'authors' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp" &&
+	test_when_finished "rm -rf hgrepo gitrepo" &&
 
-  (
-  hg init hgrepo &&
-  cd hgrepo &&
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
 
-  touch content &&
-  hg add content &&
+	touch content &&
+	hg add content &&
 
-  author_test alpha "" "H G Wells <wells@example.com>" &&
-  author_test beta "test" "test <unknown>" &&
-  author_test beta "test <test@example.com> (comment)" "test <test@example.com>" &&
-  author_test gamma "<test@example.com>" "Unknown <test@example.com>" &&
-  author_test delta "name<test@example.com>" "name <test@example.com>" &&
-  author_test epsilon "name <test@example.com" "name <test@example.com>" &&
-  author_test zeta " test " "test <unknown>" &&
-  author_test eta "test < test@example.com >" "test <test@example.com>" &&
-  author_test theta "test >test@example.com>" "test <test@example.com>" &&
-  author_test iota "test < test <at> example <dot> com>" "test <unknown>" &&
-  author_test kappa "test@example.com" "Unknown <test@example.com>"
-  ) &&
+	> ../expected &&
+	author_test alpha "" "H G Wells <wells@example.com>" &&
+	author_test beta "test" "test <unknown>" &&
+	author_test beta "test <test@example.com> (comment)" "test <test@example.com>" &&
+	author_test gamma "<test@example.com>" "Unknown <test@example.com>" &&
+	author_test delta "name<test@example.com>" "name <test@example.com>" &&
+	author_test epsilon "name <test@example.com" "name <test@example.com>" &&
+	author_test zeta " test " "test <unknown>" &&
+	author_test eta "test < test@example.com >" "test <test@example.com>" &&
+	author_test theta "test >test@example.com>" "test <test@example.com>" &&
+	author_test iota "test < test <at> example <dot> com>" "test <unknown>" &&
+	author_test kappa "test@example.com" "Unknown <test@example.com>"
+	) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual &&
+	git clone "hg::hgrepo" gitrepo &&
+	git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual &&
 
-  test_cmp expected actual
+	test_cmp expected actual
+'
+
+test_expect_success 'strip' '
+	test_when_finished "rm -rf hgrepo gitrepo" &&
+
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+
+	echo one >> content &&
+	hg add content &&
+	hg commit -m one &&
+
+	echo two >> content &&
+	hg commit -m two
+	) &&
+
+	git clone "hg::hgrepo" gitrepo &&
+
+	(
+	cd hgrepo &&
+	hg strip 1 &&
+
+	echo three >> content &&
+	hg commit -m three &&
+
+	echo four >> content &&
+	hg commit -m four
+	) &&
+
+	(
+	cd gitrepo &&
+	git fetch &&
+	git log --format="%s" origin/master > ../actual
+	) &&
+
+	hg -R hgrepo log --template "{desc}\n" > expected &&
+	test_cmp actual expected
+'
+
+test_expect_success 'remote push with master bookmark' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+	echo zero > content &&
+	hg add content &&
+	hg commit -m zero &&
+	hg bookmark master &&
+	echo one > content &&
+	hg commit -m one
+	) &&
+
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	echo two > content &&
+	git commit -a -m two &&
+	git push
+	) &&
+
+	check_branch hgrepo default two
+'
+
+cat > expected <<EOF
+changeset:   0:6e2126489d3d
+tag:         tip
+user:        A U Thor <author@example.com>
+date:        Mon Jan 01 00:00:00 2007 +0230
+summary:     one
+
+EOF
+
+test_expect_success 'remote push from master branch' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	hg init hgrepo &&
+
+	(
+	git init gitrepo &&
+	cd gitrepo &&
+	git remote add origin "hg::../hgrepo" &&
+	echo one > content &&
+	git add content &&
+	git commit -a -m one &&
+	git push origin master
+	) &&
+
+	hg -R hgrepo log > actual &&
+	cat actual &&
+	test_cmp expected actual &&
+
+	check_branch hgrepo default one
+'
+
+GIT_REMOTE_HG_TEST_REMOTE=1
+export GIT_REMOTE_HG_TEST_REMOTE
+
+test_expect_success 'remote cloning' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+	echo zero > content &&
+	hg add content &&
+	hg commit -m zero
+	) &&
+
+	git clone "hg::hgrepo" gitrepo &&
+	check gitrepo HEAD zero
+'
+
+test_expect_success 'remote update bookmark' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	(
+	cd hgrepo &&
+	hg bookmark devel
+	) &&
+
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	git checkout --quiet devel &&
+	echo devel > content &&
+	git commit -a -m devel &&
+	git push --quiet
+	) &&
+
+	check_bookmark hgrepo devel devel
+'
+
+test_expect_success 'remote new bookmark' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	git checkout --quiet -b feature-b &&
+	echo feature-b > content &&
+	git commit -a -m feature-b &&
+	git push --quiet origin feature-b
+	) &&
+
+	check_bookmark hgrepo feature-b feature-b
+'
+
+test_expect_success 'remote push diverged' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	git clone "hg::hgrepo" gitrepo &&
+
+	(
+	cd hgrepo &&
+	hg checkout default &&
+	echo bump > content &&
+	hg commit -m bump
+	) &&
+
+	(
+	cd gitrepo &&
+	echo diverge > content &&
+	git commit -a -m diverged &&
+	check_push 1 <<-EOF
+	master:non-fast-forward
+	EOF
+	) &&
+
+	check_branch hgrepo default bump
+'
+
+test_expect_success 'remote update bookmark diverge' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	(
+	cd hgrepo &&
+	hg checkout tip^ &&
+	hg bookmark diverge
+	) &&
+
+	git clone "hg::hgrepo" gitrepo &&
+
+	(
+	cd hgrepo &&
+	echo "bump bookmark" > content &&
+	hg commit -m "bump bookmark"
+	) &&
+
+	(
+	cd gitrepo &&
+	git checkout --quiet diverge &&
+	echo diverge > content &&
+	git commit -a -m diverge &&
+	check_push 1 <<-EOF
+	diverge:fetch-first
+	EOF
+	) &&
+
+	check_bookmark hgrepo diverge "bump bookmark"
+'
+
+test_expect_success 'remote new bookmark multiple branch head' '
+	test_when_finished "rm -rf gitrepo*" &&
+
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	git checkout --quiet -b feature-c HEAD^ &&
+	echo feature-c > content &&
+	git commit -a -m feature-c &&
+	git push --quiet origin feature-c
+	) &&
+
+	check_bookmark hgrepo feature-c feature-c
+'
+
+# cleanup previous stuff
+rm -rf hgrepo
+
+setup_big_push () {
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+	echo zero > content &&
+	hg add content &&
+	hg commit -m zero &&
+	hg bookmark bad_bmark1 &&
+	echo one > content &&
+	hg commit -m one &&
+	hg bookmark bad_bmark2 &&
+	hg bookmark good_bmark &&
+	hg bookmark -i good_bmark &&
+	hg -q branch good_branch &&
+	echo "good branch" > content &&
+	hg commit -m "good branch" &&
+	hg -q branch bad_branch &&
+	echo "bad branch" > content &&
+	hg commit -m "bad branch"
+	) &&
+
+	git clone "hg::hgrepo" gitrepo &&
+
+	(
+	cd gitrepo &&
+	echo two > content &&
+	git commit -q -a -m two &&
+
+	git checkout -q good_bmark &&
+	echo three > content &&
+	git commit -q -a -m three &&
+
+	git checkout -q bad_bmark1 &&
+	git reset --hard HEAD^ &&
+	echo four > content &&
+	git commit -q -a -m four &&
+
+	git checkout -q bad_bmark2 &&
+	git reset --hard HEAD^ &&
+	echo five > content &&
+	git commit -q -a -m five &&
+
+	git checkout -q -b new_bmark master &&
+	echo six > content &&
+	git commit -q -a -m six &&
+
+	git checkout -q branches/good_branch &&
+	echo seven > content &&
+	git commit -q -a -m seven &&
+	echo eight > content &&
+	git commit -q -a -m eight &&
+
+	git checkout -q branches/bad_branch &&
+	git reset --hard HEAD^ &&
+	echo nine > content &&
+	git commit -q -a -m nine &&
+
+	git checkout -q -b branches/new_branch master &&
+	echo ten > content &&
+	git commit -q -a -m ten
+	)
+}
+
+test_expect_success 'remote big push' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	setup_big_push
+
+	(
+	cd gitrepo &&
+
+	check_push 1 --all <<-EOF
+	master
+	good_bmark
+	branches/good_branch
+	new_bmark:new
+	branches/new_branch:new
+	bad_bmark1:non-fast-forward
+	bad_bmark2:non-fast-forward
+	branches/bad_branch:non-fast-forward
+	EOF
+	) &&
+
+	check_branch hgrepo default one &&
+	check_branch hgrepo good_branch "good branch" &&
+	check_branch hgrepo bad_branch "bad branch" &&
+	check_branch hgrepo new_branch '' &&
+	check_bookmark hgrepo good_bmark one &&
+	check_bookmark hgrepo bad_bmark1 one &&
+	check_bookmark hgrepo bad_bmark2 one &&
+	check_bookmark hgrepo new_bmark ''
+'
+
+test_expect_success 'remote big push fetch first' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+	echo zero > content &&
+	hg add content &&
+	hg commit -m zero &&
+	hg bookmark bad_bmark &&
+	hg bookmark good_bmark &&
+	hg bookmark -i good_bmark &&
+	hg -q branch good_branch &&
+	echo "good branch" > content &&
+	hg commit -m "good branch" &&
+	hg -q branch bad_branch &&
+	echo "bad branch" > content &&
+	hg commit -m "bad branch"
+	) &&
+
+	git clone "hg::hgrepo" gitrepo &&
+
+	(
+	cd hgrepo &&
+	hg bookmark -f bad_bmark &&
+	echo update_bmark > content &&
+	hg commit -m "update bmark"
+	) &&
+
+	(
+	cd gitrepo &&
+	echo two > content &&
+	git commit -q -a -m two &&
+
+	git checkout -q good_bmark &&
+	echo three > content &&
+	git commit -q -a -m three &&
+
+	git checkout -q bad_bmark &&
+	echo four > content &&
+	git commit -q -a -m four &&
+
+	git checkout -q branches/bad_branch &&
+	echo five > content &&
+	git commit -q -a -m five &&
+
+	check_push 1 --all <<-EOF
+	master
+	good_bmark
+	new_bmark:new
+	new_branch:new
+	bad_bmark:fetch-first
+	branches/bad_branch:festch-first
+	EOF
+
+	git fetch &&
+
+	check_push 1 --all <<-EOF
+	master
+	good_bmark
+	bad_bmark:non-fast-forward
+	branches/bad_branch:non-fast-forward
+	EOF
+	)
+'
+
+test_expect_failure 'remote big push force' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	setup_big_push
+
+	(
+	cd gitrepo &&
+
+	check_push 0 --force --all <<-EOF
+	master
+	good_bmark
+	branches/good_branch
+	new_bmark:new
+	branches/new_branch:new
+	bad_bmark1:forced-update
+	bad_bmark2:forced-update
+	branches/bad_branch:forced-update
+	EOF
+	) &&
+
+	check_branch hgrepo default six &&
+	check_branch hgrepo good_branch eight &&
+	check_branch hgrepo bad_branch nine &&
+	check_branch hgrepo new_branch ten &&
+	check_bookmark hgrepo good_bmark three &&
+	check_bookmark hgrepo bad_bmark1 four &&
+	check_bookmark hgrepo bad_bmark2 five &&
+	check_bookmark hgrepo new_bmark six
+'
+
+test_expect_failure 'remote big push dry-run' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	setup_big_push
+
+	(
+	cd gitrepo &&
+
+	check_push 0 --dry-run --all <<-EOF
+	master
+	good_bmark
+	branches/good_branch
+	new_bmark:new
+	branches/new_branch:new
+	bad_bmark1:non-fast-forward
+	bad_bmark2:non-fast-forward
+	branches/bad_branch:non-fast-forward
+	EOF
+
+	check_push 0 --dry-run master good_bmark new_bmark branches/good_branch branches/new_branch <<-EOF
+	master
+	good_bmark
+	branches/good_branch
+	new_bmark:new
+	branches/new_branch:new
+	EOF
+	) &&
+
+	check_branch hgrepo default one &&
+	check_branch hgrepo good_branch "good branch" &&
+	check_branch hgrepo bad_branch "bad branch" &&
+	check_branch hgrepo new_branch '' &&
+	check_bookmark hgrepo good_bmark one &&
+	check_bookmark hgrepo bad_bmark1 one &&
+	check_bookmark hgrepo bad_bmark2 one &&
+	check_bookmark hgrepo new_bmark ''
+'
+
+test_expect_success 'remote double failed push' '
+	test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+	(
+	hg init hgrepo &&
+	cd hgrepo &&
+	echo zero > content &&
+	hg add content &&
+	hg commit -m zero &&
+	echo one > content &&
+	hg commit -m one
+	) &&
+
+	(
+	git clone "hg::hgrepo" gitrepo &&
+	cd gitrepo &&
+	git reset --hard HEAD^ &&
+	echo two > content &&
+	git commit -a -m two &&
+	test_expect_code 1 git push &&
+	test_expect_code 1 git push
+	)
 '
 
 test_done
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 8a23f58..51ae932 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
Binary files differ
diff --git a/convert.c b/convert.c
index 2a2e46c..11a95fc 100644
--- a/convert.c
+++ b/convert.c
@@ -835,7 +835,7 @@
 
 /*****************************************************************
  *
- * Streaming converison support
+ * Streaming conversion support
  *
  *****************************************************************/
 
diff --git a/convert.h b/convert.h
index ec5fd69..0c2143c 100644
--- a/convert.h
+++ b/convert.h
@@ -48,7 +48,7 @@
 
 /*****************************************************************
  *
- * Streaming converison support
+ * Streaming conversion support
  *
  *****************************************************************/
 
diff --git a/credential-store.c b/credential-store.c
index 26f7589..f9146e5 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -114,7 +114,7 @@
 	return c->username && c->password;
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	const char * const usage[] = {
 		"git credential-store [options] <action>",
@@ -131,7 +131,7 @@
 
 	umask(077);
 
-	argc = parse_options(argc, argv, NULL, options, usage, 0);
+	argc = parse_options(argc, (const char **)argv, NULL, options, usage, 0);
 	if (argc != 1)
 		usage_with_options(usage, options);
 	op = argv[0];
diff --git a/daemon.c b/daemon.c
index 6aeddcb..34916c5 100644
--- a/daemon.c
+++ b/daemon.c
@@ -760,7 +760,7 @@
 		snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
 		    ntohs(sin_addr->sin_port));
 #ifndef NO_IPV6
-	} else if (addr && addr->sa_family == AF_INET6) {
+	} else if (addr->sa_family == AF_INET6) {
 		struct sockaddr_in6 *sin6_addr = (void *) addr;
 
 		char *buf = addrbuf + 12;
@@ -1047,18 +1047,6 @@
 	}
 }
 
-/* if any standard file descriptor is missing open it to /dev/null */
-static void sanitize_stdfds(void)
-{
-	int fd = open("/dev/null", O_RDWR, 0);
-	while (fd != -1 && fd < 2)
-		fd = dup(fd);
-	if (fd == -1)
-		die_errno("open /dev/null or dup failed");
-	if (fd > 2)
-		close(fd);
-}
-
 #ifdef NO_POSIX_GOODIES
 
 struct credentials;
diff --git a/date.c b/date.c
index df20d0b..29f1540 100644
--- a/date.c
+++ b/date.c
@@ -711,6 +711,28 @@
 	return 0; /* success */
 }
 
+int parse_expiry_date(const char *date, unsigned long *timestamp)
+{
+	int errors = 0;
+
+	if (!strcmp(date, "never") || !strcmp(date, "false"))
+		*timestamp = 0;
+	else if (!strcmp(date, "all") || !strcmp(date, "now"))
+		/*
+		 * We take over "now" here, which usually translates
+		 * to the current timestamp.  This is because the user
+		 * really means to expire everything she has done in
+		 * the past, and by definition reflogs are the record
+		 * of the past, and there is nothing from the future
+		 * to be kept.
+		 */
+		*timestamp = ULONG_MAX;
+	else
+		*timestamp = approxidate_careful(date, &errors);
+
+	return errors;
+}
+
 int parse_date(const char *date, char *result, int maxlen)
 {
 	unsigned long timestamp;
diff --git a/decorate.c b/decorate.c
index 2f8a63e..7cb5d29 100644
--- a/decorate.c
+++ b/decorate.c
@@ -49,7 +49,7 @@
 		const struct object *base = old_hash[i].base;
 		void *decoration = old_hash[i].decoration;
 
-		if (!base)
+		if (!decoration)
 			continue;
 		insert_decoration(n, base, decoration);
 	}
diff --git a/diff-lib.c b/diff-lib.c
index f35de0f..b6f4b21 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -64,8 +64,9 @@
  * commits, untracked content and/or modified content).
  */
 static int match_stat_with_submodule(struct diff_options *diffopt,
-				      struct cache_entry *ce, struct stat *st,
-				      unsigned ce_option, unsigned *dirty_submodule)
+				     const struct cache_entry *ce,
+				     struct stat *st, unsigned ce_option,
+				     unsigned *dirty_submodule)
 {
 	int changed = ce_match_stat(ce, st, ce_option);
 	if (S_ISGITLINK(ce->ce_mode)) {
@@ -237,7 +238,7 @@
 /* A file entry went away or appeared */
 static void diff_index_show_file(struct rev_info *revs,
 				 const char *prefix,
-				 struct cache_entry *ce,
+				 const struct cache_entry *ce,
 				 const unsigned char *sha1, int sha1_valid,
 				 unsigned int mode,
 				 unsigned dirty_submodule)
@@ -246,7 +247,7 @@
 		       sha1, sha1_valid, ce->name, dirty_submodule);
 }
 
-static int get_stat_data(struct cache_entry *ce,
+static int get_stat_data(const struct cache_entry *ce,
 			 const unsigned char **sha1p,
 			 unsigned int *modep,
 			 int cached, int match_missing,
@@ -283,7 +284,7 @@
 }
 
 static void show_new_file(struct rev_info *revs,
-			  struct cache_entry *new,
+			  const struct cache_entry *new,
 			  int cached, int match_missing)
 {
 	const unsigned char *sha1;
@@ -302,8 +303,8 @@
 }
 
 static int show_modified(struct rev_info *revs,
-			 struct cache_entry *old,
-			 struct cache_entry *new,
+			 const struct cache_entry *old,
+			 const struct cache_entry *new,
 			 int report_missing,
 			 int cached, int match_missing)
 {
@@ -362,8 +363,8 @@
  * give you the position and number of entries in the index).
  */
 static void do_oneway_diff(struct unpack_trees_options *o,
-	struct cache_entry *idx,
-	struct cache_entry *tree)
+			   const struct cache_entry *idx,
+			   const struct cache_entry *tree)
 {
 	struct rev_info *revs = o->unpack_data;
 	int match_missing, cached;
@@ -423,10 +424,11 @@
  * the fairly complex unpack_trees() semantic requirements, including
  * the skipping, the path matching, the type conflict cases etc.
  */
-static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
+static int oneway_diff(const struct cache_entry * const *src,
+		       struct unpack_trees_options *o)
 {
-	struct cache_entry *idx = src[0];
-	struct cache_entry *tree = src[1];
+	const struct cache_entry *idx = src[0];
+	const struct cache_entry *tree = src[1];
 	struct rev_info *revs = o->unpack_data;
 
 	/*
diff --git a/diff-no-index.c b/diff-no-index.c
index 74da659..e66fdf3 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -45,7 +45,7 @@
 
 	if (!path || !strcmp(path, "/dev/null"))
 		*mode = 0;
-#ifdef _WIN32
+#ifdef GIT_WINDOWS_NATIVE
 	else if (!strcasecmp(path, "nul"))
 		*mode = 0;
 #endif
diff --git a/diff.c b/diff.c
index f0b3e7c..266112c 100644
--- a/diff.c
+++ b/diff.c
@@ -1683,9 +1683,7 @@
 		del = deleted;
 
 		if (graph_width <= max_change) {
-			int total = add + del;
-
-			total = scale_linear(add + del, graph_width, max_change);
+			int total = scale_linear(add + del, graph_width, max_change);
 			if (total < 2 && add && del)
 				/* width >= 2 due to the sanity check */
 				total = 2;
@@ -2586,7 +2584,7 @@
  */
 static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file)
 {
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 	struct stat st;
 	int pos, len;
 
@@ -2677,6 +2675,14 @@
 int diff_populate_filespec(struct diff_filespec *s, int size_only)
 {
 	int err = 0;
+	/*
+	 * demote FAIL to WARN to allow inspecting the situation
+	 * instead of refusing.
+	 */
+	enum safe_crlf crlf_warn = (safe_crlf == SAFE_CRLF_FAIL
+				    ? SAFE_CRLF_WARN
+				    : safe_crlf);
+
 	if (!DIFF_FILE_VALID(s))
 		die("internal error: asking to populate invalid file.");
 	if (S_ISDIR(s->mode))
@@ -2732,7 +2738,7 @@
 		/*
 		 * Convert from working tree format to canonical git format
 		 */
-		if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
+		if (convert_to_git(s->path, s->data, s->size, &buf, crlf_warn)) {
 			size_t size = 0;
 			munmap(s->data, s->size);
 			s->should_munmap = 0;
@@ -3497,6 +3503,11 @@
 	return 1;
 }
 
+static void enable_patch_output(int *fmt) {
+	*fmt &= ~DIFF_FORMAT_NO_OUTPUT;
+	*fmt |= DIFF_FORMAT_PATCH;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
 	const char *arg = av[0];
@@ -3504,15 +3515,15 @@
 	int argcount;
 
 	/* Output format options */
-	if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
-		options->output_format |= DIFF_FORMAT_PATCH;
-	else if (opt_arg(arg, 'U', "unified", &options->context))
-		options->output_format |= DIFF_FORMAT_PATCH;
+	if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
+	    || opt_arg(arg, 'U', "unified", &options->context))
+		enable_patch_output(&options->output_format);
 	else if (!strcmp(arg, "--raw"))
 		options->output_format |= DIFF_FORMAT_RAW;
-	else if (!strcmp(arg, "--patch-with-raw"))
-		options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
-	else if (!strcmp(arg, "--numstat"))
+	else if (!strcmp(arg, "--patch-with-raw")) {
+		enable_patch_output(&options->output_format);
+		options->output_format |= DIFF_FORMAT_RAW;
+	} else if (!strcmp(arg, "--numstat"))
 		options->output_format |= DIFF_FORMAT_NUMSTAT;
 	else if (!strcmp(arg, "--shortstat"))
 		options->output_format |= DIFF_FORMAT_SHORTSTAT;
@@ -3534,13 +3545,14 @@
 		options->output_format |= DIFF_FORMAT_CHECKDIFF;
 	else if (!strcmp(arg, "--summary"))
 		options->output_format |= DIFF_FORMAT_SUMMARY;
-	else if (!strcmp(arg, "--patch-with-stat"))
-		options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
-	else if (!strcmp(arg, "--name-only"))
+	else if (!strcmp(arg, "--patch-with-stat")) {
+		enable_patch_output(&options->output_format);
+		options->output_format |= DIFF_FORMAT_DIFFSTAT;
+	} else if (!strcmp(arg, "--name-only"))
 		options->output_format |= DIFF_FORMAT_NAME;
 	else if (!strcmp(arg, "--name-status"))
 		options->output_format |= DIFF_FORMAT_NAME_STATUS;
-	else if (!strcmp(arg, "-s"))
+	else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
 		options->output_format |= DIFF_FORMAT_NO_OUTPUT;
 	else if (!prefixcmp(arg, "--stat"))
 		/* --stat, --stat-width, --stat-name-width, or --stat-count */
@@ -3593,6 +3605,8 @@
 		DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
 	else if (!strcmp(arg, "--ignore-space-at-eol"))
 		DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+	else if (!strcmp(arg, "--ignore-blank-lines"))
+		DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
 	else if (!strcmp(arg, "--patience"))
 		options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
 	else if (!strcmp(arg, "--histogram"))
@@ -3611,7 +3625,7 @@
 
 	/* flags options */
 	else if (!strcmp(arg, "--binary")) {
-		options->output_format |= DIFF_FORMAT_PATCH;
+		enable_patch_output(&options->output_format);
 		DIFF_OPT_SET(options, BINARY);
 	}
 	else if (!strcmp(arg, "--full-index"))
@@ -4450,7 +4464,7 @@
 	    DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
 		/*
 		 * run diff_flush_patch for the exit status. setting
-		 * options->file to /dev/null should be safe, becaue we
+		 * options->file to /dev/null should be safe, because we
 		 * aren't supposed to produce any output anyway.
 		 */
 		if (options->close_file)
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 63722f8..401eb72 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -122,7 +122,7 @@
 		char errbuf[1024];
 		regerror(err, &regex, errbuf, 1024);
 		regfree(&regex);
-		die("invalid log-grep regex: %s", errbuf);
+		die("invalid regex: %s", errbuf);
 	}
 
 	pickaxe(&diff_queued_diff, o, &regex, NULL, diff_grep);
@@ -131,8 +131,7 @@
 	return;
 }
 
-static unsigned int contains(mmfile_t *mf, struct diff_options *o,
-			     regex_t *regexp, kwset_t kws)
+static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
 {
 	unsigned int cnt;
 	unsigned long sz;
@@ -176,11 +175,9 @@
 		       struct diff_options *o,
 		       regex_t *regexp, kwset_t kws)
 {
-	if (!one)
-		return contains(two, o, regexp, kws) != 0;
-	if (!two)
-		return contains(one, o, regexp, kws) != 0;
-	return contains(one, o, regexp, kws) != contains(two, o, regexp, kws);
+	unsigned int one_contains = one ? contains(one, regexp, kws) : 0;
+	unsigned int two_contains = two ? contains(two, regexp, kws) : 0;
+	return one_contains != two_contains;
 }
 
 static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
@@ -246,7 +243,7 @@
 			char errbuf[1024];
 			regerror(err, &regex, errbuf, 1024);
 			regfree(&regex);
-			die("invalid pickaxe regex: %s", errbuf);
+			die("invalid regex: %s", errbuf);
 		}
 		regexp = &regex;
 	} else {
diff --git a/dir.c b/dir.c
index a5926fb..910bfcd 100644
--- a/dir.c
+++ b/dir.c
@@ -821,6 +821,9 @@
 				dir->basebuf, stk->baselen - 1,
 				dir->basebuf + current, &dt);
 			dir->basebuf[stk->baselen - 1] = '/';
+			if (dir->exclude &&
+			    dir->exclude->flags & EXC_FLAG_NEGATIVE)
+				dir->exclude = NULL;
 			if (dir->exclude) {
 				dir->basebuf[stk->baselen] = 0;
 				dir->exclude_stack = stk;
@@ -930,7 +933,7 @@
  */
 static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
 {
-	struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+	const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
 	unsigned char endchar;
 
 	if (!ce)
@@ -974,7 +977,7 @@
 	if (pos < 0)
 		pos = -pos-1;
 	while (pos < active_nr) {
-		struct cache_entry *ce = active_cache[pos++];
+		const struct cache_entry *ce = active_cache[pos++];
 		unsigned char endchar;
 
 		if (strncmp(ce->name, dirname, len))
@@ -1033,9 +1036,7 @@
 		return path_recurse;
 
 	case index_gitdir:
-		if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
-			return path_none;
-		return path_untracked;
+		return path_none;
 
 	case index_nonexistent:
 		if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
@@ -1112,7 +1113,7 @@
 static int get_index_dtype(const char *path, int len)
 {
 	int pos;
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 
 	ce = cache_name_exists(path, len, 0);
 	if (ce) {
@@ -1542,9 +1543,9 @@
 		home_config_paths(NULL, &xdg_path, "ignore");
 		excludes_file = xdg_path;
 	}
-	if (!access_or_warn(path, R_OK))
+	if (!access_or_warn(path, R_OK, 0))
 		add_excludes_from_file(dir, path);
-	if (excludes_file && !access_or_warn(excludes_file, R_OK))
+	if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
 		add_excludes_from_file(dir, excludes_file);
 }
 
diff --git a/entry.c b/entry.c
index d7c131d..acc892f 100644
--- a/entry.c
+++ b/entry.c
@@ -79,7 +79,7 @@
 	return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
 }
 
-static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
+static void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
 {
 	enum object_type type;
 	void *new = read_sha1_file(ce->sha1, &type, size);
@@ -92,7 +92,7 @@
 	return NULL;
 }
 
-static int open_output_fd(char *path, struct cache_entry *ce, int to_tempfile)
+static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempfile)
 {
 	int symlink = (ce->ce_mode & S_IFMT) != S_IFREG;
 	if (to_tempfile) {
@@ -115,7 +115,7 @@
 	return 0;
 }
 
-static int streaming_write_entry(struct cache_entry *ce, char *path,
+static int streaming_write_entry(const struct cache_entry *ce, char *path,
 				 struct stream_filter *filter,
 				 const struct checkout *state, int to_tempfile,
 				 int *fstat_done, struct stat *statbuf)
@@ -136,7 +136,8 @@
 	return result;
 }
 
-static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
+static int write_entry(struct cache_entry *ce,
+		       char *path, const struct checkout *state, int to_tempfile)
 {
 	unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
 	int fd, ret, fstat_done = 0;
@@ -199,9 +200,9 @@
 		break;
 	case S_IFGITLINK:
 		if (to_tempfile)
-			return error("cannot create temporary subproject %s", path);
+			return error("cannot create temporary submodule %s", path);
 		if (mkdir(path, 0777) < 0)
-			return error("cannot create subproject directory %s", path);
+			return error("cannot create submodule directory %s", path);
 		break;
 	default:
 		return error("unknown file mode for %s in index", path);
@@ -233,7 +234,8 @@
 	return lstat(path, st);
 }
 
-int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
+int checkout_entry(struct cache_entry *ce,
+		   const struct checkout *state, char *topath)
 {
 	static char path[PATH_MAX + 1];
 	struct stat st;
diff --git a/environment.c b/environment.c
index e2e75c1..5398c36 100644
--- a/environment.c
+++ b/environment.c
@@ -22,6 +22,7 @@
 int is_bare_repository_cfg = -1; /* unspecified */
 int log_all_ref_updates = -1; /* unspecified */
 int warn_ambiguous_refs = 1;
+int warn_on_object_refname_ambiguity = 1;
 int repository_format_version;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
@@ -37,7 +38,6 @@
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
-const char *log_pack_access;
 const char *pager_program;
 int pager_use_color = 1;
 const char *editor_program;
diff --git a/exec_cmd.h b/exec_cmd.h
index e2b546b..e4c9702 100644
--- a/exec_cmd.h
+++ b/exec_cmd.h
@@ -7,6 +7,7 @@
 extern void setup_path(void);
 extern const char **prepare_git_cmd(const char **argv);
 extern int execv_git_cmd(const char **argv); /* NULL terminated */
+LAST_ARG_MUST_BE_NULL
 extern int execl_git_cmd(const char *cmd, ...);
 extern const char *system_path(const char *path);
 
diff --git a/fast-import.c b/fast-import.c
index 5f539d7..23f625f 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -297,7 +297,7 @@
 static FILE *pack_edges;
 static unsigned int show_stats = 1;
 static int global_argc;
-static const char **global_argv;
+static char **global_argv;
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -1822,7 +1822,7 @@
 		*end = 0;
 		mark = strtoumax(line + 1, &end, 10);
 		if (!mark || end == line + 1
-			|| *end != ' ' || get_sha1(end + 1, sha1))
+			|| *end != ' ' || get_sha1_hex(end + 1, sha1))
 			die("corrupt mark line: %s", line);
 		e = find_object(sha1);
 		if (!e) {
@@ -3347,7 +3347,7 @@
 		read_marks();
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	unsigned int i;
 
diff --git a/fetch-pack.c b/fetch-pack.c
index f156dd4..6684348 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -11,6 +11,7 @@
 #include "run-command.h"
 #include "transport.h"
 #include "version.h"
+#include "prio-queue.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -20,6 +21,8 @@
 static int fetch_fsck_objects = -1;
 static int transfer_fsck_objects = -1;
 static int agent_supported;
+static struct lock_file shallow_lock;
+static const char *alternate_shallow_file;
 
 #define COMPLETE	(1U << 0)
 #define COMMON		(1U << 1)
@@ -35,7 +38,7 @@
  */
 #define MAX_IN_VAIN 256
 
-static struct commit_list *rev_list;
+static struct prio_queue rev_list = { compare_commits_by_commit_date };
 static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
 
 static void rev_list_push(struct commit *commit, int mark)
@@ -47,7 +50,7 @@
 			if (parse_commit(commit))
 				return;
 
-		commit_list_insert_by_date(commit, &rev_list);
+		prio_queue_put(&rev_list, commit);
 
 		if (!(commit->object.flags & COMMON))
 			non_common_revs++;
@@ -120,10 +123,10 @@
 		unsigned int mark;
 		struct commit_list *parents;
 
-		if (rev_list == NULL || non_common_revs == 0)
+		if (rev_list.nr == 0 || non_common_revs == 0)
 			return NULL;
 
-		commit = rev_list->item;
+		commit = prio_queue_get(&rev_list);
 		if (!commit->object.parsed)
 			parse_commit(commit);
 		parents = commit->parents;
@@ -150,8 +153,6 @@
 				mark_common(parents->item, 1, 0);
 			parents = parents->next;
 		}
-
-		rev_list = rev_list->next;
 	}
 
 	return commit->object.sha1;
@@ -440,7 +441,7 @@
 					in_vain = 0;
 					got_continue = 1;
 					if (ack == ACK_ready) {
-						rev_list = NULL;
+						clear_prio_queue(&rev_list);
 						got_ready = 1;
 					}
 					break;
@@ -503,7 +504,7 @@
 		struct commit *commit = (struct commit *)o;
 		if (!(commit->object.flags & COMPLETE)) {
 			commit->object.flags |= COMPLETE;
-			commit_list_insert_by_date(commit, &complete);
+			commit_list_insert(commit, &complete);
 		}
 	}
 	return 0;
@@ -620,6 +621,7 @@
 	if (!args->depth) {
 		for_each_ref(mark_complete, NULL);
 		for_each_alternate_ref(mark_alternate_complete, NULL);
+		commit_list_sort_by_date(&complete);
 		if (cutoff)
 			mark_recent_complete_commits(args, cutoff);
 	}
@@ -683,12 +685,13 @@
 		    int xd[2], char **pack_lockfile)
 {
 	struct async demux;
-	const char *argv[20];
+	const char *argv[22];
 	char keep_arg[256];
 	char hdr_arg[256];
 	const char **av;
 	int do_keep = args->keep_pack;
 	struct child_process cmd;
+	int ret;
 
 	memset(&demux, 0, sizeof(demux));
 	if (use_sideband) {
@@ -724,6 +727,11 @@
 			do_keep = 1;
 	}
 
+	if (alternate_shallow_file) {
+		*av++ = "--shallow-file";
+		*av++ = alternate_shallow_file;
+	}
+
 	if (do_keep) {
 		if (pack_lockfile)
 			cmd.out = -1;
@@ -740,11 +748,14 @@
 				strcpy(keep_arg + s, "localhost");
 			*av++ = keep_arg;
 		}
+		if (args->check_self_contained_and_connected)
+			*av++ = "--check-self-contained-and-connected";
 	}
 	else {
 		*av++ = "unpack-objects";
 		if (args->quiet || args->no_progress)
 			*av++ = "-q";
+		args->check_self_contained_and_connected = 0;
 	}
 	if (*hdr_arg)
 		*av++ = hdr_arg;
@@ -765,7 +776,12 @@
 		close(cmd.out);
 	}
 
-	if (finish_command(&cmd))
+	ret = finish_command(&cmd);
+	if (!ret || (args->check_self_contained_and_connected && ret == 1))
+		args->self_contained_and_connected =
+			args->check_self_contained_and_connected &&
+			ret == 0;
+	else
 		die("%s failed", argv[0]);
 	if (use_sideband && finish_async(&demux))
 		die("error in sideband demultiplexer");
@@ -779,6 +795,27 @@
 	return strcmp(a->name, b->name);
 }
 
+static void setup_alternate_shallow(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int fd;
+
+	check_shallow_file_for_update();
+	fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
+				       LOCK_DIE_ON_ERROR);
+	if (write_shallow_commits(&sb, 0)) {
+		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+			die_errno("failed to write to %s", shallow_lock.filename);
+		alternate_shallow_file = shallow_lock.filename;
+	} else
+		/*
+		 * is_repository_shallow() sees empty string as "no
+		 * shallow file".
+		 */
+		alternate_shallow_file = "";
+	strbuf_release(&sb);
+}
+
 static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 int fd[2],
 				 const struct ref *orig_ref,
@@ -858,6 +895,8 @@
 
 	if (args->stateless_rpc)
 		packet_flush(fd[1]);
+	if (args->depth > 0)
+		setup_alternate_shallow();
 	if (get_pack(args, fd, pack_lockfile))
 		die("git fetch-pack: fetch failed.");
 
@@ -936,15 +975,9 @@
 		       struct ref **sought, int nr_sought,
 		       char **pack_lockfile)
 {
-	struct stat st;
 	struct ref *ref_cpy;
 
 	fetch_pack_setup();
-	if (args->depth > 0) {
-		if (stat(git_path("shallow"), &st))
-			st.st_mtime = 0;
-	}
-
 	if (nr_sought)
 		nr_sought = remove_duplicates_in_refs(sought, nr_sought);
 
@@ -954,35 +987,12 @@
 	}
 	ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
 
-	if (args->depth > 0) {
-		static struct lock_file lock;
-		struct cache_time mtime;
-		struct strbuf sb = STRBUF_INIT;
-		char *shallow = git_path("shallow");
-		int fd;
-
-		mtime.sec = st.st_mtime;
-		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_MTIME_NSEC(st) != mtime.nsec
-#endif
-			  )
-			die("shallow file was changed during fetch");
-
-		fd = hold_lock_file_for_update(&lock, shallow,
-					       LOCK_DIE_ON_ERROR);
-		if (!write_shallow_commits(&sb, 0)
-		 || write_in_full(fd, sb.buf, sb.len) != sb.len) {
-			unlink_or_warn(shallow);
-			rollback_lock_file(&lock);
-		} else {
-			commit_lock_file(&lock);
-		}
-		strbuf_release(&sb);
+	if (alternate_shallow_file) {
+		if (*alternate_shallow_file == '\0') { /* --unshallow */
+			unlink_or_warn(git_path("shallow"));
+			rollback_lock_file(&shallow_lock);
+		} else
+			commit_lock_file(&shallow_lock);
 	}
 
 	reprepare_packed_git();
diff --git a/fetch-pack.h b/fetch-pack.h
index dc5266c..40f08ba 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -16,7 +16,9 @@
 		verbose:1,
 		no_progress:1,
 		include_tag:1,
-		stateless_rpc:1;
+		stateless_rpc:1,
+		check_self_contained_and_connected:1,
+		self_contained_and_connected:1;
 };
 
 /*
diff --git a/fixup-builtins b/fixup-builtins
deleted file mode 100755
index 63dfa4c..0000000
--- a/fixup-builtins
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-while [ "$1" ]
-do
-	if [ "$1" != "git-sh-setup" -a "$1" != "git-parse-remote" -a "$1" != "git-svn" ]; then
-		old="$1"
-		new=$(echo "$1" | sed 's/git-/git /')
-		echo "Converting '$old' to '$new'"
-		sed -i "s/\\<$old\\>/$new/g" $(git ls-files '*.sh')
-	fi
-	shift
-done
-
-sed -i 's/git merge-one-file/git-merge-one-file/g
-s/git rebase-todo/git-rebase-todo/g' $(git ls-files '*.sh')
-git update-index --refresh >& /dev/null
-exit 0
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index d2c4ce6..75a991f 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -44,6 +44,8 @@
 
 my $normal_color = $repo->get_color("", "reset");
 
+my $diff_algorithm = $repo->config('diff.algorithm');
+
 my $use_readkey = 0;
 my $use_termcap = 0;
 my %term_escapes;
@@ -731,6 +733,9 @@
 sub parse_diff {
 	my ($path) = @_;
 	my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+	if (defined $diff_algorithm) {
+		splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
+	}
 	if (defined $patch_mode_revision) {
 		push @diff_cmd, $patch_mode_revision;
 	}
diff --git a/git-am.sh b/git-am.sh
index c092855..7ea40fe 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -6,7 +6,7 @@
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git am [options] [(<mbox>|<Maildir>)...]
-git am [options] (--resolved | --skip | --abort)
+git am [options] (--continue | --skip | --abort)
 --
 i,interactive   run interactively
 b,binary*       (historical option -- no-op)
@@ -102,7 +102,7 @@
 	    printf '%s\n' "$resolvemsg"
 	    stop_here $1
     fi
-    eval_gettextln "When you have resolved this problem, run \"\$cmdline --resolved\".
+    eval_gettextln "When you have resolved this problem, run \"\$cmdline --continue\".
 If you prefer to skip this patch, run \"\$cmdline --skip\" instead.
 To restore the original branch and stop patching, run \"\$cmdline --abort\"."
 
@@ -446,6 +446,8 @@
 # If the dotest directory exists, but we have finished applying all the
 # patches in them, clear it out.
 if test -d "$dotest" &&
+   test -f "$dotest/last" &&
+   test -f "$dotest/next" &&
    last=$(cat "$dotest/last") &&
    next=$(cat "$dotest/next") &&
    test $# != 0 &&
@@ -454,7 +456,7 @@
    rm -fr "$dotest"
 fi
 
-if test -d "$dotest"
+if test -d "$dotest" && test -f "$dotest/last" && test -f "$dotest/next"
 then
 	case "$#,$skip$resolved$abort" in
 	0,*t*)
@@ -504,7 +506,24 @@
 	esac
 	rm -f "$dotest/dirtyindex"
 else
-	# Make sure we are not given --skip, --resolved, nor --abort
+	# Possible stray $dotest directory in the independent-run
+	# case; in the --rebasing case, it is upto the caller
+	# (git-rebase--am) to take care of stray directories.
+	if test -d "$dotest" && test -z "$rebasing"
+	then
+		case "$skip,$resolved,$abort" in
+		,,t)
+			rm -fr "$dotest"
+			exit 0
+			;;
+		*)
+			die "$(eval_gettext "Stray \$dotest directory found.
+Use \"git am --abort\" to remove it.")"
+			;;
+		esac
+	fi
+
+	# Make sure we are not given --skip, --continue, nor --abort
 	test "$skip$resolved$abort" = "" ||
 		die "$(gettext "Resolve operation not in progress, we are not resuming.")"
 
@@ -651,7 +670,7 @@
 	#  - patch is the patch body.
 	#
 	# When we are resuming, these files are either already prepared
-	# by the user, or the user can tell us to do so by --resolved flag.
+	# by the user, or the user can tell us to do so by --continue flag.
 	case "$resume" in
 	'')
 		if test -f "$dotest/rebasing"
@@ -904,5 +923,10 @@
     fi
 fi
 
-rm -fr "$dotest"
-git gc --auto
+# If am was called with --rebasing (from git-rebase--am), it's up to
+# the caller to take care of housekeeping.
+if ! test -f "$dotest/rebasing"
+then
+	rm -fr "$dotest"
+	git gc --auto
+fi
diff --git a/git-bisect.sh b/git-bisect.sh
index d7518e9..9f064b6 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -320,8 +320,8 @@
 	elif test $res -eq 2
 	then
 		echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
-		good_revs=$(git for-each-ref --format="--not %(objectname)" "refs/bisect/good-*")
-		for skipped in $(git rev-list refs/bisect/bad $good_revs)
+		good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/good-*")
+		for skipped in $(git rev-list refs/bisect/bad --not $good_revs)
 		do
 			skipped_commit=$(git show-branch $skipped)
 			echo "# possible first bad commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
diff --git a/git-compat-util.h b/git-compat-util.h
index e955bb5..cc4ba4d 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -85,13 +85,14 @@
 #define _NETBSD_SOURCE 1
 #define _SGI_SOURCE 1
 
-#ifdef WIN32 /* Both MinGW and MSVC */
+#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
 # if defined (_MSC_VER)
 #  define _WIN32_WINNT 0x0502
 # endif
 #define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
 #include <winsock2.h>
 #include <windows.h>
+#define GIT_WINDOWS_NATIVE
 #endif
 
 #include <unistd.h>
@@ -127,6 +128,9 @@
 #else
 #include <poll.h>
 #endif
+
+extern int get_st_mode_bits(const char *path, int *mode);
+
 #if defined(__MINGW32__)
 /* pull in Windows compatibility stuff */
 #include "compat/mingw.h"
@@ -163,7 +167,6 @@
 typedef long intptr_t;
 typedef unsigned long uintptr_t;
 #endif
-int get_st_mode_bits(const char *path, int *mode);
 #if defined(__CYGWIN__)
 #undef _XOPEN_SOURCE
 #include <grp.h>
@@ -185,6 +188,11 @@
 #define probe_utf8_pathname_composition(a,b)
 #endif
 
+#ifdef NEEDS_CLIPPED_WRITE
+ssize_t clipped_write(int fildes, const void *buf, size_t nbyte);
+#define write(x,y,z) clipped_write((x),(y),(z))
+#endif
+
 #ifdef MKDIR_WO_TRAILING_SLASH
 #define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
 extern int compat_mkdir_wo_trailing_slash(const char*, mode_t);
@@ -295,6 +303,13 @@
 #endif
 #endif
 
+/* The sentinel attribute is valid from gcc version 4.0 */
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#define LAST_ARG_MUST_BE_NULL __attribute__((sentinel))
+#else
+#define LAST_ARG_MUST_BE_NULL
+#endif
+
 #include "compat/bswap.h"
 
 #ifdef USE_WILDMATCH
@@ -692,8 +707,9 @@
  * Call access(2), but warn for any error except "missing file"
  * (ENOENT or ENOTDIR).
  */
-int access_or_warn(const char *path, int mode);
-int access_or_die(const char *path, int mode);
+#define ACCESS_EACCES_OK (1U << 0)
+int access_or_warn(const char *path, int mode, unsigned flag);
+int access_or_die(const char *path, int mode, unsigned flag);
 
 /* Warn on an inaccessible file that ought to be accessible */
 void warn_on_inaccessible(const char *path);
diff --git a/git-difftool.perl b/git-difftool.perl
index 8a75205..e57d3d12 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -85,13 +85,9 @@
 
 sub use_wt_file
 {
-	my ($repo, $workdir, $file, $sha1, $symlinks) = @_;
+	my ($repo, $workdir, $file, $sha1) = @_;
 	my $null_sha1 = '0' x 40;
 
-	if ($sha1 ne $null_sha1 and not $symlinks) {
-		return 0;
-	}
-
 	if (! -e "$workdir/$file") {
 		# If the file doesn't exist in the working tree, we cannot
 		# use it.
@@ -213,8 +209,7 @@
 
 		if ($rmode ne $null_mode) {
 			my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
-							  $dst_path, $rsha1,
-							  $symlinks);
+							  $dst_path, $rsha1);
 			if ($use) {
 				push @working_tree, $dst_path;
 				$wtindex .= "$rmode $wt_sha1\t$dst_path\0";
diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN
index 49b62ea..0a1f8d4 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.17.GITGUI
+DEF_VER=0.18.GITGUI
 
 LF='
 '
diff --git a/git-gui/Makefile b/git-gui/Makefile
index e22ba5c..e9c2bc3 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -254,7 +254,7 @@
 	  auto_mkindex lib '*.tcl' \
 	| $(TCL_PATH) $(QUIET_2DEVNULL); then : ok; \
 	else \
-	 echo 1>&2 "    * $(TCL_PATH) failed; using unoptimized loading"; \
+	 echo >&2 "    * $(TCL_PATH) failed; using unoptimized loading"; \
 	 rm -f $@ ; \
 	 echo '# Autogenerated by git-gui Makefile' >$@ && \
 	 echo >>$@ && \
@@ -274,8 +274,8 @@
 GIT-GUI-VARS: FORCE
 	@VARS='$(TRACK_VARS)'; \
 	if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
-		echo 1>&2 "    * new locations or Tcl/Tk interpreter"; \
-		echo 1>$@ "$$VARS"; \
+		echo >&2 "    * new locations or Tcl/Tk interpreter"; \
+		echo >$@ "$$VARS"; \
 	fi
 
 ifdef GITGUI_MACOSXAPP
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 89f636f..153f85d 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -137,6 +137,20 @@
 
 ######################################################################
 ##
+## On Mac, bring the current Wish process window to front
+
+if {[tk windowingsystem] eq "aqua"} {
+	catch {
+		exec osascript -e [format {
+			tell application "System Events"
+				set frontmost of processes whose unix id is %d to true
+			end tell
+		} [pid]]
+	}
+}
+
+######################################################################
+##
 ## read only globals
 
 set _appname {Git Gui}
@@ -3003,18 +3017,11 @@
 	set jump_spec {}
 	set is_path 0
 	foreach a $argv {
-		if {[file exists $a]} {
-			if {$path ne {}} usage
-			set path [normalize_relpath $a]
-			break
-		} elseif {[file exists $_prefix$a]} {
-			if {$path ne {}} usage
-			set path [normalize_relpath $_prefix$a]
-			break
-		}
+		set p [file join $_prefix $a]
 
-		if {$is_path} {
+		if {$is_path || [file exists $p]} {
 			if {$path ne {}} usage
+			set path [normalize_relpath $p]
 			break
 		} elseif {$a eq {--}} {
 			if {$path ne {}} {
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index 657f7d5..ee58981 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -286,7 +286,9 @@
 	destroy $w_body
 	if {![winfo exists $w_next]} {
 		${NS}::button $w_next -default active
-		pack $w_next -side right -padx 5 -before $w_quit
+		set pos -before
+		if {[tk windowingsystem] eq "win32"} { set pos -after }
+		pack $w_next -side right -padx 5 $pos $w_quit
 	}
 	_do_$action $this
 }
diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl
index ec44055..30d9a79 100644
--- a/git-gui/lib/diff.tcl
+++ b/git-gui/lib/diff.tcl
@@ -764,8 +764,15 @@
 				# context line
 				set ln [$ui_diff get $i_l $next_l]
 				set patch "$patch$pre_context$ln"
-				set n [expr $n+1]
-				set m [expr $m+1]
+				# Skip the "\ No newline at end of
+				# file". Depending on the locale setting
+				# we don't know what this line looks
+				# like exactly. The only thing we do
+				# know is that it starts with "\ "
+				if {![string match {\\ *} $ln]} {
+					set n [expr $n+1]
+					set m [expr $m+1]
+				}
 				set pre_context {}
 			} elseif {$c1 eq $to_context} {
 				# turn change line into context line
diff --git a/git-gui/lib/mergetool.tcl b/git-gui/lib/mergetool.tcl
index 3c8e73b..120bc40 100644
--- a/git-gui/lib/mergetool.tcl
+++ b/git-gui/lib/mergetool.tcl
@@ -189,9 +189,9 @@
 	}
 	bc3 {
 		if {$base_stage ne {}} {
-			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" -mergeoutput="$MERGED"]
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" "-mergeoutput=$MERGED"]
 		} else {
-			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -mergeoutput="$MERGED"]
+			set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "-mergeoutput=$MERGED"]
 		}
 	}
 	ecmerge {
diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl
index 5e4e7f4..4e5c784 100644
--- a/git-gui/lib/remote.tcl
+++ b/git-gui/lib/remote.tcl
@@ -245,7 +245,8 @@
 	set prune_m $remote_m.prune
 	if {$have_remote > 1} {
 		make_sure_remote_submenues_exist $remote_m
-		if {[$fetch_m entrycget end -label] ne "All"} {
+		if {[$fetch_m type end] eq "command" \
+			&& [$fetch_m entrycget end -label] ne "All"} {
 
 			$fetch_m insert end separator
 			$fetch_m insert end command \
@@ -259,7 +260,8 @@
 		}
 	} else {
 		if {[winfo exists $fetch_m]} {
-			if {[$fetch_m entrycget end -label] eq "All"} {
+			if {[$fetch_m type end] eq "command" \
+				&& [$fetch_m entrycget end -label] eq "All"} {
 
 				delete_from_menu $fetch_m end
 				delete_from_menu $fetch_m end
diff --git a/git-gui/po/fr.po b/git-gui/po/fr.po
index 40441db..0aff186 100644
--- a/git-gui/po/fr.po
+++ b/git-gui/po/fr.po
@@ -1139,7 +1139,7 @@
 
 #: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
-msgstr "Copy complète (plus lent, sauvegarde redondante)"
+msgstr "Copie complète (plus lent, sauvegarde redondante)"
 
 #: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index e338be5..6a72106 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -114,6 +114,33 @@
 	test -n "$cmd"
 }
 
+setup_user_tool () {
+	merge_tool_cmd=$(get_merge_tool_cmd "$tool")
+	test -n "$merge_tool_cmd" || return 1
+
+	diff_cmd () {
+		( eval $merge_tool_cmd )
+		status=$?
+		return $status
+	}
+
+	merge_cmd () {
+		trust_exit_code=$(git config --bool \
+			"mergetool.$1.trustExitCode" || echo false)
+		if test "$trust_exit_code" = "false"
+		then
+			touch "$BACKUP"
+			( eval $merge_tool_cmd )
+			status=$?
+			check_unchanged
+		else
+			( eval $merge_tool_cmd )
+			status=$?
+		fi
+		return $status
+	}
+}
+
 setup_tool () {
 	tool="$1"
 
@@ -142,15 +169,15 @@
 
 	if ! test -f "$MERGE_TOOLS_DIR/$tool"
 	then
-		# Use a special return code for this case since we want to
-		# source "defaults" even when an explicit tool path is
-		# configured since the user can use that to override the
-		# default path in the scriptlet.
-		return 2
+		setup_user_tool
+		return $?
 	fi
 
 	# Load the redefined functions
 	. "$MERGE_TOOLS_DIR/$tool"
+	# Now let the user override the default command for the tool.  If
+	# they have not done so then this will return 1 which we ignore.
+	setup_user_tool
 
 	if merge_mode && ! can_merge
 	then
@@ -187,20 +214,7 @@
 	status=0
 
 	# Bring tool-specific functions into scope
-	setup_tool "$1"
-	exitcode=$?
-	case $exitcode in
-	0)
-		:
-		;;
-	2)
-		# The configured tool is not a built-in tool.
-		test -n "$merge_tool_path" || return 1
-		;;
-	*)
-		return $exitcode
-		;;
-	esac
+	setup_tool "$1" || return 1
 
 	if merge_mode
 	then
@@ -213,38 +227,12 @@
 
 # Run a either a configured or built-in diff tool
 run_diff_cmd () {
-	merge_tool_cmd=$(get_merge_tool_cmd "$1")
-	if test -n "$merge_tool_cmd"
-	then
-		( eval $merge_tool_cmd )
-		status=$?
-		return $status
-	else
-		diff_cmd "$1"
-	fi
+	diff_cmd "$1"
 }
 
 # Run a either a configured or built-in merge tool
 run_merge_cmd () {
-	merge_tool_cmd=$(get_merge_tool_cmd "$1")
-	if test -n "$merge_tool_cmd"
-	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 )
-			status=$?
-			check_unchanged
-		else
-			( eval $merge_tool_cmd )
-			status=$?
-		fi
-		return $status
-	else
-		merge_cmd "$1"
-	fi
+	merge_cmd "$1"
 }
 
 list_merge_tool_candidates () {
diff --git a/git-p4.py b/git-p4.py
index 911bbce..88fcf23 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -3168,7 +3168,7 @@
         if os.system("git update-index --refresh") != 0:
             die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
         if len(read_pipe("git diff-index HEAD --")) > 0:
-            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
+            die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
 
         [upstream, settings] = findUpstreamBranchPoint()
         if len(upstream) == 0:
diff --git a/git-pull.sh b/git-pull.sh
index 638aabb..f0df41c 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -5,7 +5,7 @@
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
 USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
-LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
+LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
@@ -266,10 +266,17 @@
 	;;
 esac
 
+# Pulling into unborn branch: a shorthand for branching off
+# FETCH_HEAD, for lazy typers.
 if test -z "$orig_head"
 then
-	git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
-	git read-tree -m -u HEAD || exit 1
+	# Two-way merge: we claim the index is based on an empty tree,
+	# and try to fast-forward to HEAD.  This ensures we will not
+	# lose index/worktree changes that the user already made on
+	# the unborn branch.
+	empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+	git read-tree -m -u $empty_tree $merge_head &&
+	git update-ref -m "initial pull" HEAD $merge_head "$curr_head"
 	exit
 fi
 
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index f84854f..34e3102 100644
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -7,12 +7,12 @@
 continue)
 	git am --resolved --resolvemsg="$resolvemsg" &&
 	move_to_original_branch
-	exit
+	return
 	;;
 skip)
 	git am --skip --resolvemsg="$resolvemsg" &&
 	move_to_original_branch
-	exit
+	return
 	;;
 esac
 
@@ -56,7 +56,7 @@
 
 		As a result, git cannot rebase them.
 		EOF
-		exit $?
+		return $?
 	fi
 
 	git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" <"$GIT_DIR/rebased-patches"
@@ -68,7 +68,7 @@
 if test 0 != $ret
 then
 	test -d "$state_dir" && write_basic_state
-	exit $ret
+	return $ret
 fi
 
 move_to_original_branch
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 5822b2c..83d6d46 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -80,6 +80,18 @@
 rewritten_list="$state_dir"/rewritten-list
 rewritten_pending="$state_dir"/rewritten-pending
 
+strategy_args=
+if test -n "$do_merge"
+then
+	strategy_args=${strategy:+--strategy=$strategy}
+	eval '
+		for strategy_opt in '"$strategy_opts"'
+		do
+			strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
+		done
+	'
+fi
+
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_HELP
 
@@ -239,7 +251,7 @@
 
 	test -d "$rewritten" &&
 		pick_one_preserving_merges "$@" && return
-	output git cherry-pick $empty_args $ff "$@"
+	output eval git cherry-pick "$strategy_args" $empty_args $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -340,9 +352,8 @@
 			msg_content="$(commit_message $sha1)"
 			# No point in merging the first parent, that's HEAD
 			new_parents=${new_parents# $first_parent}
-			if ! do_with_author output \
-				git merge --no-ff ${strategy:+-s $strategy} -m \
-					"$msg_content" $new_parents
+			if ! do_with_author output eval \
+			'git merge --no-ff $strategy_args -m "$msg_content" $new_parents'
 			then
 				printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
 				die_with_patch $sha1 "Error redoing merge $sha1"
@@ -350,7 +361,7 @@
 			echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
 			;;
 		*)
-			output git cherry-pick "$@" ||
+			output eval git cherry-pick "$strategy_args" "$@" ||
 				die_with_patch $sha1 "Could not pick $sha1"
 			;;
 		esac
@@ -628,17 +639,16 @@
 		"$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
 		true # we don't care if this hook failed
 	fi &&
-	rm -rf "$state_dir" &&
-	git gc --auto &&
 	warn "Successfully rebased and updated $head_name."
 
-	exit
+	return 1 # not failure; just to break the do_rest loop
 }
 
+# can only return 0, when the infinite loop breaks
 do_rest () {
 	while :
 	do
-		do_next
+		do_next || break
 	done
 }
 
@@ -690,8 +700,22 @@
 		case "$message" in
 		"squash! "*|"fixup! "*)
 			action="${message%%!*}"
-			rest="${message#*! }"
-			echo "$sha1 $action $rest"
+			rest=$message
+			prefix=
+			# skip all squash! or fixup! (but save for later)
+			while :
+			do
+				case "$rest" in
+				"squash! "*|"fixup! "*)
+					prefix="$prefix${rest%%!*},"
+					rest="${rest#*! }"
+					;;
+				*)
+					break
+					;;
+				esac
+			done
+			echo "$sha1 $action $prefix $rest"
 			# if it's a single word, try to resolve to a full sha1 and
 			# emit a second copy. This allows us to match on both message
 			# and on sha1 prefix
@@ -700,7 +724,7 @@
 				if test -n "$fullsha"; then
 					# prefix the action to uniquely identify this line as
 					# intended for full sha1 match
-					echo "$sha1 +$action $fullsha"
+					echo "$sha1 +$action $prefix $fullsha"
 				fi
 			fi
 		esac
@@ -715,7 +739,7 @@
 		esac
 		printf '%s\n' "$pick $sha1 $message"
 		used="$used$sha1 "
-		while read -r squash action msg_content
+		while read -r squash action msg_prefix msg_content
 		do
 			case " $used" in
 			*" $squash "*) continue ;;
@@ -731,7 +755,8 @@
 				case "$message" in "$msg_content"*) emit=1;; esac ;;
 			esac
 			if test $emit = 1; then
-				printf '%s\n' "$action $squash $action! $msg_content"
+				real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g")
+				printf '%s\n' "$action $squash ${real_prefix}$msg_content"
 				used="$used$squash "
 			fi
 		done <"$1.sq"
@@ -805,11 +830,13 @@
 
 	require_clean_work_tree "rebase"
 	do_rest
+	return 0
 	;;
 skip)
 	git rerere clear
 
 	do_rest
+	return 0
 	;;
 edit-todo)
 	git stripspace --strip-comments <"$todo" >"$todo".new
@@ -837,12 +864,15 @@
 
 if test ! -z "$switch_to"
 then
+	GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
 	output git checkout "$switch_to" -- ||
-		die "Could not checkout $switch_to"
+	die "Could not checkout $switch_to"
+
+	comment_for_reflog start
 fi
 
 orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?"
-mkdir "$state_dir" || die "Could not create temporary $state_dir"
+mkdir -p "$state_dir" || die "Could not create temporary $state_dir"
 
 : > "$state_dir"/interactive || die "Could not mark as interactive"
 write_basic_state
@@ -980,6 +1010,7 @@
 
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
+GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
 output git checkout $onto || die_abort "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 do_rest
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index b10f2cf..16d1817 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -96,7 +96,6 @@
 			"$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
 		fi
 	fi
-	rm -r "$state_dir"
 	say All done.
 }
 
@@ -110,7 +109,7 @@
 		continue_merge
 	done
 	finish_rb_merge
-	exit
+	return
 	;;
 skip)
 	read_state
@@ -122,7 +121,7 @@
 		continue_merge
 	done
 	finish_rb_merge
-	exit
+	return
 	;;
 esac
 
diff --git a/git-rebase.sh b/git-rebase.sh
index 2c692c3..0039ecf 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -13,6 +13,7 @@
  Available options are
 v,verbose!         display a diffstat of what changed upstream
 q,quiet!           be quiet. implies --no-stat
+autostash!         automatically stash/stash pop before and after
 onto=!             rebase onto given branch instead of upstream
 p,preserve-merges! try to recreate merges instead of ignoring them
 s,strategy=!       use the given merge strategy
@@ -64,6 +65,7 @@
 verbose=
 diffstat=
 test "$(git config --bool rebase.stat)" = true && diffstat=t
+autostash="$(git config --bool rebase.autostash || echo false)"
 git_am_opt=
 rebase_root=
 force_rebase=
@@ -82,6 +84,8 @@
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 
 read_basic_state () {
+	test -f "$state_dir/head-name" &&
+	test -f "$state_dir/onto" &&
 	head_name=$(cat "$state_dir"/head-name) &&
 	onto=$(cat "$state_dir"/onto) &&
 	# We always write to orig-head, but interactive rebase used to write to
@@ -143,6 +147,26 @@
 	esac
 }
 
+finish_rebase () {
+	if test -f "$state_dir/autostash"
+	then
+		stash_sha1=$(cat "$state_dir/autostash")
+		if git stash apply $stash_sha1 2>&1 >/dev/null
+		then
+			echo "$(gettext 'Applied autostash.')"
+		else
+			git stash store -m "autostash" -q $stash_sha1 ||
+			die "$(eval_gettext "Cannot store \$stash_sha1")"
+			gettext 'Applying autostash resulted in conflicts.
+Your changes are safe in the stash.
+You can run "git stash pop" or "git stash drop" it at any time.
+'
+		fi
+	fi
+	git gc --auto &&
+	rm -rf "$state_dir"
+}
+
 run_specific_rebase () {
 	if [ "$interactive_rebase" = implied ]; then
 		GIT_EDITOR=:
@@ -150,6 +174,12 @@
 		autosquash=
 	fi
 	. git-rebase--$type
+	ret=$?
+	if test $ret -eq 0
+	then
+		finish_rebase
+	fi
+	exit $ret
 }
 
 run_pre_rebase_hook () {
@@ -241,6 +271,9 @@
 	--stat)
 		diffstat=t
 		;;
+	--autostash)
+		autostash=true
+		;;
 	-v)
 		verbose=t
 		diffstat=t
@@ -341,7 +374,7 @@
 		;;
 	esac
 	output git reset --hard $orig_head
-	rm -r "$state_dir"
+	finish_rebase
 	exit
 	;;
 edit-todo)
@@ -400,7 +433,7 @@
 		shift
 		;;
 	esac
-	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
+	upstream=$(peel_committish "${upstream_name}") ||
 	die "$(eval_gettext "invalid upstream \$upstream_name")"
 	upstream_arg="$upstream_name"
 else
@@ -436,7 +469,7 @@
 	fi
 	;;
 *)
-	onto=$(git rev-parse --verify "${onto_name}^0") ||
+	onto=$(peel_committish "$onto_name") ||
 	die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
 	;;
 esac
@@ -480,6 +513,18 @@
 	;;
 esac
 
+if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
+then
+	stash_sha1=$(git stash create "autostash") ||
+	die "$(gettext 'Cannot autostash')"
+
+	mkdir -p "$state_dir" &&
+	echo $stash_sha1 >"$state_dir/autostash" &&
+	stash_abbrev=$(git rev-parse --short $stash_sha1) &&
+	echo "$(eval_gettext 'Created autostash: $stash_abbrev')" &&
+	git reset --hard
+fi
+
 require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
 
 # Now we are rebasing commits $upstream..$orig_head (or with --root,
@@ -497,8 +542,11 @@
 	if test -z "$force_rebase"
 	then
 		# Lazily switch to the target branch if needed...
-		test -z "$switch_to" || git checkout "$switch_to" --
+		test -z "$switch_to" ||
+		GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
+			git checkout "$switch_to" --
 		say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+		finish_rebase
 		exit 0
 	else
 		say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
@@ -522,7 +570,9 @@
 
 # Detach HEAD and reset the tree
 say "$(gettext "First, rewinding head to replay your work on top of it...")"
-git checkout -q "$onto^0" || die "could not detach HEAD"
+
+GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
+	git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 
 # If the $onto is a proper descendant of the tip of the branch, then
@@ -531,6 +581,7 @@
 then
 	say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
 	move_to_original_branch
+	finish_rebase
 	exit 0
 fi
 
diff --git a/git-remote-testgit b/git-remote-testgit
deleted file mode 100755
index e7ed3a3..0000000
--- a/git-remote-testgit
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2012 Felipe Contreras
-
-alias=$1
-url=$2
-
-dir="$GIT_DIR/testgit/$alias"
-prefix="refs/testgit/$alias"
-
-default_refspec="refs/heads/*:${prefix}/heads/*"
-
-refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
-
-test -z "$refspec" && prefix="refs"
-
-export GIT_DIR="$url/.git"
-
-mkdir -p "$dir"
-
-if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS"
-then
-	gitmarks="$dir/git.marks"
-	testgitmarks="$dir/testgit.marks"
-	test -e "$gitmarks" || >"$gitmarks"
-	test -e "$testgitmarks" || >"$testgitmarks"
-	testgitmarks_args=( "--"{import,export}"-marks=$testgitmarks" )
-fi
-
-while read line
-do
-	case $line in
-	capabilities)
-		echo 'import'
-		echo 'export'
-		test -n "$refspec" && echo "refspec $refspec"
-		if test -n "$gitmarks"
-		then
-			echo "*import-marks $gitmarks"
-			echo "*export-marks $gitmarks"
-		fi
-		test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
-		echo
-		;;
-	list)
-		git for-each-ref --format='? %(refname)' 'refs/heads/'
-		head=$(git symbolic-ref HEAD)
-		echo "@$head HEAD"
-		echo
-		;;
-	import*)
-		# read all import lines
-		while true
-		do
-			ref="${line#* }"
-			refs="$refs $ref"
-			read line
-			test "${line%% *}" != "import" && break
-		done
-
-		if test -n "$gitmarks"
-		then
-			echo "feature import-marks=$gitmarks"
-			echo "feature export-marks=$gitmarks"
-		fi
-		echo "feature done"
-		git fast-export "${testgitmarks_args[@]}" $refs |
-		sed -e "s#refs/heads/#${prefix}/heads/#g"
-		echo "done"
-		;;
-	export)
-		before=$(git for-each-ref --format='%(refname) %(objectname)')
-
-		git fast-import "${testgitmarks_args[@]}" --quiet
-
-		after=$(git for-each-ref --format='%(refname) %(objectname)')
-
-		# figure out which refs were updated
-		join -e 0 -o '0 1.2 2.2' -a 2 <(echo "$before") <(echo "$after") |
-		while read ref a b
-		do
-			test $a == $b && continue
-			echo "ok $ref"
-		done
-
-		echo
-		;;
-	'')
-		exit
-		;;
-	esac
-done
diff --git a/git-remote-testgit.sh b/git-remote-testgit.sh
new file mode 100755
index 0000000..2109070
--- /dev/null
+++ b/git-remote-testgit.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+# Copyright (c) 2012 Felipe Contreras
+
+alias=$1
+url=$2
+
+dir="$GIT_DIR/testgit/$alias"
+prefix="refs/testgit/$alias"
+
+default_refspec="refs/heads/*:${prefix}/heads/*"
+
+refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
+
+test -z "$refspec" && prefix="refs"
+
+export GIT_DIR="$url/.git"
+
+mkdir -p "$dir"
+
+if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS"
+then
+	gitmarks="$dir/git.marks"
+	testgitmarks="$dir/testgit.marks"
+	test -e "$gitmarks" || >"$gitmarks"
+	test -e "$testgitmarks" || >"$testgitmarks"
+fi
+
+while read line
+do
+	case $line in
+	capabilities)
+		echo 'import'
+		echo 'export'
+		test -n "$refspec" && echo "refspec $refspec"
+		if test -n "$gitmarks"
+		then
+			echo "*import-marks $gitmarks"
+			echo "*export-marks $gitmarks"
+		fi
+		test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
+		echo
+		;;
+	list)
+		git for-each-ref --format='? %(refname)' 'refs/heads/'
+		head=$(git symbolic-ref HEAD)
+		echo "@$head HEAD"
+		echo
+		;;
+	import*)
+		# read all import lines
+		while true
+		do
+			ref="${line#* }"
+			refs="$refs $ref"
+			read line
+			test "${line%% *}" != "import" && break
+		done
+
+		if test -n "$gitmarks"
+		then
+			echo "feature import-marks=$gitmarks"
+			echo "feature export-marks=$gitmarks"
+		fi
+
+		if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
+		then
+			echo "feature done"
+			exit 1
+		fi
+
+		echo "feature done"
+		git fast-export \
+			${testgitmarks:+"--import-marks=$testgitmarks"} \
+			${testgitmarks:+"--export-marks=$testgitmarks"} \
+			$refs |
+		sed -e "s#refs/heads/#${prefix}/heads/#g"
+		echo "done"
+		;;
+	export)
+		if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
+		then
+			# consume input so fast-export doesn't get SIGPIPE;
+			# git would also notice that case, but we want
+			# to make sure we are exercising the later
+			# error checks
+			while read line; do
+				test "done" = "$line" && break
+			done
+			exit 1
+		fi
+
+		before=$(git for-each-ref --format=' %(refname) %(objectname) ')
+
+		git fast-import \
+			${testgitmarks:+"--import-marks=$testgitmarks"} \
+			${testgitmarks:+"--export-marks=$testgitmarks"} \
+			--quiet
+
+		# figure out which refs were updated
+		git for-each-ref --format='%(refname) %(objectname)' |
+		while read ref a
+		do
+			case "$before" in
+			*" $ref $a "*)
+				continue ;;	# unchanged
+			esac
+			if test -z "$GIT_REMOTE_TESTGIT_PUSH_ERROR"
+			then
+				echo "ok $ref"
+			else
+				echo "error $ref $GIT_REMOTE_TESTGIT_PUSH_ERROR"
+			fi
+		done
+
+		echo
+		;;
+	'')
+		exit
+		;;
+	esac
+done
diff --git a/git-request-pull.sh b/git-request-pull.sh
index d566015..ebf1269 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -51,8 +51,18 @@
 tag_name=$(git describe --exact "$head^0" 2>/dev/null)
 
 test -n "$base" && test -n "$url" || usage
-baserev=$(git rev-parse --verify "$base"^0) &&
-headrev=$(git rev-parse --verify "$head"^0) || exit
+
+baserev=$(git rev-parse --verify --quiet "$base"^0)
+if test -z "$baserev"
+then
+    die "fatal: Not a valid revision: $base"
+fi
+
+headrev=$(git rev-parse --verify --quiet "$head"^0)
+if test -z "$headrev"
+then
+    die "fatal: Not a valid revision: $head"
+fi
 
 merge_base=$(git merge-base $baserev $headrev) ||
 die "fatal: No commits in common between $base and $head"
diff --git a/git-send-email.perl b/git-send-email.perl
index bd13cc8..2162478 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -69,6 +69,9 @@
     --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'.
+    --smtp-ssl-cert-path    <str>  * Path to ca-certificates (either directory or file).
+                                     Pass an empty string to disable certificate
+                                     verification.
     --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake
     --smtp-debug            <0|1>  * Disable, enable Net::SMTP debug.
 
@@ -194,7 +197,7 @@
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
 my ($to_cmd, $cc_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
-my ($smtp_authuser, $smtp_encryption);
+my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
 my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
@@ -203,11 +206,9 @@
 
 my ($debug_net_smtp) = 0;		# Net::SMTP, see send_message()
 
-my $not_set_by_user = "true but not set by the user";
-
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
-    "chainreplyto" => [\$chain_reply_to, $not_set_by_user],
+    "chainreplyto" => [\$chain_reply_to, 0],
     "suppressfrom" => [\$suppress_from, undef],
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
@@ -222,6 +223,7 @@
     "smtpserveroption" => \@smtp_server_options,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
+    "smtpsslcertpath" => \$smtp_ssl_cert_path,
     "smtpdomain" => \$smtp_domain,
     "to" => \@initial_to,
     "tocmd" => \$to_cmd,
@@ -241,19 +243,6 @@
     "aliasesfile" => \@alias_files,
 );
 
-# Help users prepare for 1.7.0
-sub chain_reply_to {
-	if (defined $chain_reply_to &&
-	    $chain_reply_to eq $not_set_by_user) {
-		print STDERR
-		    "In git 1.7.0, the default has changed to --no-chain-reply-to\n" .
-		    "Set sendemail.chainreplyto configuration variable to true if\n" .
-		    "you want to keep --chain-reply-to as your default.\n";
-		$chain_reply_to = 0;
-	}
-	return $chain_reply_to;
-}
-
 # Handle Uncouth Termination
 sub signal_handler {
 
@@ -302,6 +291,7 @@
 		    "smtp-pass:s" => \$smtp_authpass,
 		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 		    "smtp-encryption=s" => \$smtp_encryption,
+		    "smtp-ssl-cert-path" => \$smtp_ssl_cert_path,
 		    "smtp-debug:i" => \$debug_net_smtp,
 		    "smtp-domain:s" => \$smtp_domain,
 		    "identity=s" => \$identity,
@@ -760,6 +750,11 @@
 	$sender = $repoauthor || $repocommitter || '';
 }
 
+# $sender could be an already sanitized address
+# (e.g. sendemail.from could be manually sanitized by user).
+# But it's a no-op to run sanitize_address on an already sanitized address.
+$sender = sanitize_address($sender);
+
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
 	my $to = ask("Who should the emails be sent to (if any)? ",
@@ -1089,6 +1084,34 @@
 	return $auth;
 }
 
+sub ssl_verify_params {
+	eval {
+		require IO::Socket::SSL;
+		IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
+	};
+	if ($@) {
+		print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";
+		return;
+	}
+
+	if (!defined $smtp_ssl_cert_path) {
+		$smtp_ssl_cert_path = "/etc/ssl/certs";
+	}
+
+	if ($smtp_ssl_cert_path eq "") {
+		return (SSL_verify_mode => SSL_VERIFY_NONE());
+	} elsif (-d $smtp_ssl_cert_path) {
+		return (SSL_verify_mode => SSL_VERIFY_PEER(),
+			SSL_ca_path => $smtp_ssl_cert_path);
+	} elsif (-f $smtp_ssl_cert_path) {
+		return (SSL_verify_mode => SSL_VERIFY_PEER(),
+			SSL_ca_file => $smtp_ssl_cert_path);
+	} else {
+		print STDERR "Not using SSL_VERIFY_PEER because the CA path does not exist.\n";
+		return (SSL_verify_mode => SSL_VERIFY_NONE());
+	}
+}
+
 # Returns 1 if the message was sent, and 0 otherwise.
 # In actuality, the whole program dies when there
 # is an error sending a message.
@@ -1113,10 +1136,9 @@
 	if ($cc ne '') {
 		$ccline = "\nCc: $cc";
 	}
-	my $sanitized_sender = sanitize_address($sender);
 	make_message_id() unless defined($message_id);
 
-	my $header = "From: $sanitized_sender
+	my $header = "From: $sender
 To: $to${ccline}
 Subject: $subject
 Date: $date
@@ -1133,7 +1155,7 @@
 	}
 
 	my @sendmail_parameters = ('-i', @recipients);
-	my $raw_from = $sanitized_sender;
+	my $raw_from = $sender;
 	if (defined $envelope_sender && $envelope_sender ne "auto") {
 		$raw_from = $envelope_sender;
 	}
@@ -1194,20 +1216,24 @@
 			$smtp_domain ||= maildomain();
 			$smtp ||= Net::SMTP::SSL->new($smtp_server,
 						      Hello => $smtp_domain,
-						      Port => $smtp_server_port);
+						      Port => $smtp_server_port,
+						      ssl_verify_params());
 		}
 		else {
 			require Net::SMTP;
 			$smtp_domain ||= maildomain();
-			$smtp ||= Net::SMTP->new(smtp_host_string(),
+			$smtp_server_port ||= 25;
+			$smtp ||= Net::SMTP->new($smtp_server,
 						 Hello => $smtp_domain,
-						 Debug => $debug_net_smtp);
+						 Debug => $debug_net_smtp,
+						 Port => $smtp_server_port);
 			if ($smtp_encryption eq 'tls' && $smtp) {
 				require Net::SMTP::SSL;
 				$smtp->command('STARTTLS');
 				$smtp->response();
 				if ($smtp->code == 220) {
-					$smtp = Net::SMTP::SSL->start_SSL($smtp)
+					$smtp = Net::SMTP::SSL->start_SSL($smtp,
+									  ssl_verify_params())
 						or die "STARTTLS failed! ".$smtp->message;
 					$smtp_encryption = '';
 					# Send EHLO again to receive fresh
@@ -1270,6 +1296,7 @@
 	open my $fh, "<", $t or die "can't open file $t";
 
 	my $author = undef;
+	my $sauthor = undef;
 	my $author_encoding;
 	my $has_content_type;
 	my $body_encoding;
@@ -1308,8 +1335,9 @@
 			}
 			elsif (/^From:\s+(.*)$/i) {
 				($author, $author_encoding) = unquote_rfc2047($1);
+				$sauthor = sanitize_address($author);
 				next if $suppress_cc{'author'};
-				next if $suppress_cc{'self'} and $author eq $sender;
+				next if $suppress_cc{'self'} and $sauthor eq $sender;
 				printf("(mbox) Adding cc: %s from line '%s'\n",
 					$1, $_) unless $quiet;
 				push @cc, $1;
@@ -1323,7 +1351,9 @@
 			}
 			elsif (/^Cc:\s+(.*)$/i) {
 				foreach my $addr (parse_address_line($1)) {
-					if (unquote_rfc2047($addr) eq $sender) {
+					my $qaddr = unquote_rfc2047($addr);
+					my $saddr = sanitize_address($qaddr);
+					if ($saddr eq $sender) {
 						next if ($suppress_cc{'self'});
 					} else {
 						next if ($suppress_cc{'cc'});
@@ -1370,7 +1400,8 @@
 			chomp;
 			my ($what, $c) = ($1, $2);
 			chomp $c;
-			if ($c eq $sender) {
+			my $sc = sanitize_address($c);
+			if ($sc eq $sender) {
 				next if ($suppress_cc{'self'});
 			} else {
 				next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
@@ -1400,7 +1431,7 @@
 		$subject = quote_subject($subject, $auto_8bit_encoding);
 	}
 
-	if (defined $author and $author ne $sender) {
+	if (defined $sauthor and $sauthor ne $sender) {
 		$message = "From: $author\n\n$message";
 		if (defined $author_encoding) {
 			if ($has_content_type) {
@@ -1437,7 +1468,7 @@
 
 	# set up for the next message
 	if ($thread && $message_was_sent &&
-		(chain_reply_to() || !defined $reply_to || length($reply_to) == 0 ||
+		($chain_reply_to || !defined $reply_to || length($reply_to) == 0 ||
 		$message_num == 1)) {
 		$reply_to = $message_id;
 		if (length $references > 0) {
@@ -1454,7 +1485,6 @@
 sub recipients_cmd {
 	my ($prefix, $what, $cmd, $file) = @_;
 
-	my $sanitized_sender = sanitize_address($sender);
 	my @addresses = ();
 	open my $fh, "-|", "$cmd \Q$file\E"
 	    or die "($prefix) Could not execute '$cmd'";
@@ -1462,7 +1492,7 @@
 		$address =~ s/^\s*//g;
 		$address =~ s/\s*$//g;
 		$address = sanitize_address($address);
-		next if ($address eq $sanitized_sender and $suppress_from);
+		next if ($address eq $sender and $suppress_cc{'self'});
 		push @addresses, $address;
 		printf("($prefix) Adding %s: %s from: '%s'\n",
 		       $what, $address, $cmd) unless $quiet;
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 2f78359..7a964ad 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -313,3 +313,15 @@
 	}
 	: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
 fi
+
+peel_committish () {
+	case "$1" in
+	:/*)
+		peeltmp=$(git rev-parse --verify "$1") &&
+		git rev-parse --verify "${peeltmp}^0"
+		;;
+	*)
+		git rev-parse --verify "${1}^0"
+		;;
+	esac
+}
diff --git a/git-stash.sh b/git-stash.sh
index bbefdf6..85c9e2c 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -156,10 +156,46 @@
 	die "$(gettext "Cannot record working tree state")"
 }
 
+store_stash () {
+	while test $# != 0
+	do
+		case "$1" in
+		-m|--message)
+			shift
+			stash_msg="$1"
+			;;
+		-q|--quiet)
+			quiet=t
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+	test $# = 1 ||
+	die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
+
+	w_commit="$1"
+	if test -z "$stash_msg"
+	then
+		stash_msg="Created via \"git stash store\"."
+	fi
+
+	# Make sure the reflog for stash is kept.
+	: >>"$GIT_DIR/logs/$ref_stash"
+	git update-ref -m "$stash_msg" $ref_stash $w_commit
+	ret=$?
+	test $ret != 0 && test -z $quiet &&
+	die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
+	return $ret
+}
+
 save_stash () {
 	keep_index=
 	patch_mode=
 	untracked=
+	force=
 	while test $# != 0
 	do
 		case "$1" in
@@ -180,6 +216,9 @@
 		-u|--include-untracked)
 			untracked=untracked
 			;;
+		-f|--force)
+			force=t
+			;;
 		-a|--all)
 			untracked=all
 			;;
@@ -223,16 +262,20 @@
 		say "$(gettext "No local changes to save")"
 		exit 0
 	fi
+	if test -z "$untracked$force" &&
+	   test -n "$(git ls-files --killed | head -n 1)"
+	then
+		say "$(gettext "The following untracked files would NOT be saved but need to be removed by stash save:")"
+		test -n "$GIT_QUIET" || git ls-files --killed | sed 's/^/\t/'
+		say "$(gettext "Aborting. Consider using either the --force or --include-untracked option.")" >&2
+		exit 1
+	fi
 	test -f "$GIT_DIR/logs/$ref_stash" ||
 		clear_stash || die "$(gettext "Cannot initialize stash")"
 
 	create_stash "$stash_msg" $untracked
-
-	# Make sure the reflog for stash is kept.
-	: >>"$GIT_DIR/logs/$ref_stash"
-
-	git update-ref -m "$stash_msg" $ref_stash $w_commit ||
-		die "$(gettext "Cannot save the current status")"
+	store_stash -m "$stash_msg" -q $w_commit ||
+	die "$(gettext "Cannot save the current status")"
 	say Saved working directory and index state "$stash_msg"
 
 	if test -z "$patch_mode"
@@ -546,12 +589,13 @@
 	clear_stash "$@"
 	;;
 create)
-	if test $# -gt 0 && test "$1" = create
-	then
-		shift
-	fi
+	shift
 	create_stash "$*" && echo "$w_commit"
 	;;
+store)
+	shift
+	store_stash "$@"
+	;;
 drop)
 	shift
 	drop_stash "$@"
diff --git a/git-submodule.sh b/git-submodule.sh
index 79bfaac..2979197 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -14,10 +14,13 @@
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
 OPTIONS_SPEC=
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 . git-sh-i18n
 . git-parse-remote
 require_work_tree
+wt_prefix=$(git rev-parse --show-prefix)
+cd_to_toplevel
 
 command=
 branch=
@@ -32,6 +35,7 @@
 update=
 prefix=
 custom_name=
+depth=
 
 # The function takes at most 2 arguments. The first argument is the
 # URL that navigates to the submodule origin repo. When relative, this URL
@@ -106,14 +110,50 @@
 	echo "${is_relative:+${up_path}}${remoteurl#./}"
 }
 
+# Resolve a path to be relative to another path.  This is intended for
+# converting submodule paths when git-submodule is run in a subdirectory
+# and only handles paths where the directory separator is '/'.
+#
+# The output is the first argument as a path relative to the second argument,
+# which defaults to $wt_prefix if it is omitted.
+relative_path ()
+{
+	local target curdir result
+	target=$1
+	curdir=${2-$wt_prefix}
+	curdir=${curdir%/}
+	result=
+
+	while test -n "$curdir"
+	do
+		case "$target" in
+		"$curdir/"*)
+			target=${target#"$curdir"/}
+			break
+			;;
+		esac
+
+		result="${result}../"
+		if test "$curdir" = "${curdir%/*}"
+		then
+			curdir=
+		else
+			curdir="${curdir%/*}"
+		fi
+	done
+
+	echo "$result$target"
+}
+
 #
 # Get submodule info for registered submodules
 # $@ = path to limit submodule list
 #
 module_list()
 {
+	eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
 	(
-		git ls-files --error-unmatch --stage -- "$@" ||
+		git ls-files -z --error-unmatch --stage -- "$@" ||
 		echo "unmatched pathspec exists"
 	) |
 	perl -e '
@@ -121,6 +161,7 @@
 	my ($null_sha1) = ("0" x 40);
 	my @out = ();
 	my $unmatched = 0;
+	$/ = "\0";
 	while (<STDIN>) {
 		if (/^unmatched pathspec/) {
 			$unmatched = 1;
@@ -211,6 +252,7 @@
 	name=$2
 	url=$3
 	reference="$4"
+	depth="$5"
 	quiet=
 	if test -n "$GIT_QUIET"
 	then
@@ -233,7 +275,7 @@
 		mkdir -p "$gitdir_base"
 		(
 			clear_local_git_env
-			git clone $quiet -n ${reference:+"$reference"} \
+			git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
 				--separate-git-dir "$gitdir" "$url" "$sm_path"
 		) ||
 		die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
@@ -282,6 +324,7 @@
 cmd_add()
 {
 	# parse $args after "submodule ... add".
+	reference_path=
 	while test $# -ne 0
 	do
 		case "$1" in
@@ -298,17 +341,25 @@
 			;;
 		--reference)
 			case "$2" in '') usage ;; esac
-			reference="--reference=$2"
+			reference_path=$2
 			shift
 			;;
 		--reference=*)
-			reference="$1"
+			reference_path="${1#--reference=}"
 			;;
 		--name)
 			case "$2" in '') usage ;; esac
 			custom_name=$2
 			shift
 			;;
+		--depth)
+			case "$2" in '') usage ;; esac
+			depth="--depth=$2"
+			shift
+			;;
+		--depth=*)
+			depth=$1
+			;;
 		--)
 			shift
 			break
@@ -323,6 +374,14 @@
 		shift
 	done
 
+	if test -n "$reference_path"
+	then
+		is_absolute_path "$reference_path" ||
+		reference_path="$wt_prefix$reference_path"
+
+		reference="--reference=$reference_path"
+	fi
+
 	repo=$1
 	sm_path=$2
 
@@ -335,9 +394,14 @@
 		usage
 	fi
 
+	is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
+
 	# assure repo is absolute or relative to parent
 	case "$repo" in
 	./*|../*)
+		test -z "$wt_prefix" ||
+		die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
+
 		# dereference source url relative to parent's url
 		realrepo=$(resolve_relative_url "$repo") || exit
 		;;
@@ -405,7 +469,7 @@
 				echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
 			fi
 		fi
-		module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
+		module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
 		(
 			clear_local_git_env
 			cd "$sm_path" &&
@@ -471,21 +535,23 @@
 		die_if_unmatched "$mode"
 		if test -e "$sm_path"/.git
 		then
-			say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
+			displaypath=$(relative_path "$sm_path")
+			say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
 			name=$(module_name "$sm_path")
 			(
 				prefix="$prefix$sm_path/"
 				clear_local_git_env
-				# we make $path available to scripts ...
-				path=$sm_path
 				cd "$sm_path" &&
+				sm_path=$(relative_path "$sm_path") &&
+				# we make $path available to scripts ...
+				path=$sm_path &&
 				eval "$@" &&
 				if test -n "$recursive"
 				then
 					cmd_foreach "--recursive" "$@"
 				fi
 			) <&3 3<&- ||
-			die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
+			die "$(eval_gettext "Stopping at '\$prefix\$displaypath'; script returned non-zero status.")"
 		fi
 	done
 }
@@ -524,12 +590,14 @@
 		die_if_unmatched "$mode"
 		name=$(module_name "$sm_path") || exit
 
+		displaypath=$(relative_path "$sm_path")
+
 		# Copy url setting when it is not set yet
 		if test -z "$(git config "submodule.$name.url")"
 		then
 			url=$(git config -f .gitmodules submodule."$name".url)
 			test -z "$url" &&
-			die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
+			die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
 
 			# Possibly a url relative to parent
 			case "$url" in
@@ -538,9 +606,9 @@
 				;;
 			esac
 			git config submodule."$name".url "$url" ||
-			die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
+			die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
 
-			say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
+			say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
 		fi
 
 		# Copy "update" setting when it is not set yet
@@ -548,7 +616,7 @@
 		test -z "$upd" ||
 		test -n "$(git config submodule."$name".update)" ||
 		git config submodule."$name".update "$upd" ||
-		die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
+		die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
 	done
 }
 
@@ -594,27 +662,29 @@
 		die_if_unmatched "$mode"
 		name=$(module_name "$sm_path") || exit
 
+		displaypath=$(relative_path "$sm_path")
+
 		# Remove the submodule work tree (unless the user already did it)
 		if test -d "$sm_path"
 		then
 			# Protect submodules containing a .git directory
 			if test -d "$sm_path/.git"
 			then
-				echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
+				echo >&2 "$(eval_gettext "Submodule work tree '\$displaypath' contains a .git directory")"
 				die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
 			fi
 
 			if test -z "$force"
 			then
 				git rm -qn "$sm_path" ||
-				die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
+				die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
 			fi
 			rm -rf "$sm_path" &&
-			say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
-			say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
+			say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
+			say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
 		fi
 
-		mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
+		mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
 
 		# Remove the .git/config entries (unless the user already did it)
 		if test -n "$(git config --get-regexp submodule."$name\.")"
@@ -623,7 +693,7 @@
 			# the user later decides to init this submodule again
 			url=$(git config submodule."$name".url)
 			git config --remove-section submodule."$name" 2>/dev/null &&
-			say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
+			say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
 		fi
 	done
 }
@@ -676,6 +746,14 @@
 		--checkout)
 			update="checkout"
 			;;
+		--depth)
+			case "$2" in '') usage ;; esac
+			depth="--depth=$2"
+			shift
+			;;
+		--depth=*)
+			depth=$1
+			;;
 		--)
 			shift
 			break
@@ -717,9 +795,11 @@
 			update_module=$(git config submodule."$name".update)
 		fi
 
+		displaypath=$(relative_path "$prefix$sm_path")
+
 		if test "$update_module" = "none"
 		then
-			echo "Skipping submodule '$prefix$sm_path'"
+			echo "Skipping submodule '$displaypath'"
 			continue
 		fi
 
@@ -728,20 +808,20 @@
 			# Only mention uninitialized submodules when its
 			# path have been specified
 			test "$#" != "0" &&
-			say "$(eval_gettext "Submodule path '\$prefix\$sm_path' not initialized
+			say "$(eval_gettext "Submodule path '\$displaypath' not initialized
 Maybe you want to use 'update --init'?")"
 			continue
 		fi
 
 		if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
 		then
-			module_clone "$sm_path" "$name" "$url" "$reference" || exit
+			module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
 			cloned_modules="$cloned_modules;$name"
 			subsha1=
 		else
 			subsha1=$(clear_local_git_env; cd "$sm_path" &&
 				git rev-parse --verify HEAD) ||
-			die "$(eval_gettext "Unable to find current revision in submodule path '\$prefix\$sm_path'")"
+			die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
 		fi
 
 		if test -n "$remote"
@@ -774,7 +854,7 @@
 				(clear_local_git_env; cd "$sm_path" &&
 					( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
 					 test -z "$rev") || git-fetch)) ||
-				die "$(eval_gettext "Unable to fetch in submodule path '\$prefix\$sm_path'")"
+				die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
 			fi
 
 			# Is this something we just cloned?
@@ -788,20 +868,26 @@
 			case "$update_module" in
 			rebase)
 				command="git rebase"
-				die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$prefix\$sm_path'")"
-				say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': rebased into '\$sha1'")"
+				die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
+				say_msg="$(eval_gettext "Submodule path '\$displaypath': rebased into '\$sha1'")"
 				must_die_on_failure=yes
 				;;
 			merge)
 				command="git merge"
-				die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$prefix\$sm_path'")"
-				say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': merged in '\$sha1'")"
+				die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$displaypath'")"
+				say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")"
+				must_die_on_failure=yes
+				;;
+			!*)
+				command="${update_module#!}"
+				die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule  path '\$prefix\$sm_path'")"
+				say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': '\$command \$sha1'")"
 				must_die_on_failure=yes
 				;;
 			*)
 				command="git checkout $subforce -q"
-				die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$prefix\$sm_path'")"
-				say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': checked out '\$sha1'")"
+				die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
+				say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
 				;;
 			esac
 
@@ -828,7 +914,7 @@
 			res=$?
 			if test $res -gt 0
 			then
-				die_msg="$(eval_gettext "Failed to recurse into submodule path '\$prefix\$sm_path'")"
+				die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
 				if test $res -eq 1
 				then
 					err="${err};$die_msg"
@@ -942,6 +1028,7 @@
 	fi
 
 	cd_to_toplevel
+	eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
 	# Get modified modules cared by user
 	modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
 		sane_egrep '^:([0-7]* )?160000' |
@@ -991,16 +1078,18 @@
 		! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
 		missing_dst=t
 
+		display_name=$(relative_path "$name")
+
 		total_commits=
 		case "$missing_src,$missing_dst" in
 		t,)
-			errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_src")"
+			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_src")"
 			;;
 		,t)
-			errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_dst")"
+			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_dst")"
 			;;
 		t,t)
-			errmsg="$(eval_gettext "  Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
+			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
 			;;
 		*)
 			errmsg=
@@ -1029,12 +1118,12 @@
 			submodule="$(gettext "submodule")"
 			if test $mod_dst = 160000
 			then
-				echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
+				echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
 			else
-				echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
+				echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
 			fi
 		else
-			echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
+			echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
 		fi
 		if test -n "$errmsg"
 		then
@@ -1118,7 +1207,7 @@
 		die_if_unmatched "$mode"
 		name=$(module_name "$sm_path") || exit
 		url=$(git config submodule."$name".url)
-		displaypath="$prefix$sm_path"
+		displaypath=$(relative_path "$prefix$sm_path")
 		if test "$stage" = U
 		then
 			say "U$sha1 $displaypath"
@@ -1129,16 +1218,16 @@
 			say "-$sha1 $displaypath"
 			continue;
 		fi
-		set_name_rev "$sm_path" "$sha1"
 		if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
 		then
+			set_name_rev "$sm_path" "$sha1"
 			say " $sha1 $displaypath$revname"
 		else
 			if test -z "$cached"
 			then
 				sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
-				set_name_rev "$sm_path" "$sha1"
 			fi
+			set_name_rev "$sm_path" "$sha1"
 			say "+$sha1 $displaypath$revname"
 		fi
 
@@ -1213,7 +1302,8 @@
 
 		if git config "submodule.$name.url" >/dev/null 2>/dev/null
 		then
-			say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
+			displaypath=$(relative_path "$prefix$sm_path")
+			say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
 			git config submodule."$name".url "$super_config_url"
 
 			if test -e "$sm_path"/.git
diff --git a/git-svn.perl b/git-svn.perl
index d070de0..ff1ce3d 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -831,7 +831,7 @@
 sub cmd_dcommit {
 	my $head = shift;
 	command_noisy(qw/update-index --refresh/);
-	git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
+	git_cmd_try { command_oneline(qw/diff-index --quiet HEAD --/) }
 		'Cannot dcommit with a dirty index.  Commit your changes first, '
 		. "or stash them with `git stash'.\n";
 	$head ||= 'HEAD';
@@ -1246,7 +1246,7 @@
 		return;
 	}
 	if (command(qw/diff-index HEAD --/)) {
-		print STDERR "Cannot rebase with uncommited changes:\n";
+		print STDERR "Cannot rebase with uncommitted changes:\n";
 		command_noisy('status');
 		exit 1;
 	}
@@ -1932,7 +1932,7 @@
 sub working_head_info {
 	my ($head, $refs) = @_;
 	my @args = qw/rev-list --first-parent --pretty=medium/;
-	my ($fh, $ctx) = command_output_pipe(@args, $head);
+	my ($fh, $ctx) = command_output_pipe(@args, $head, "--");
 	my $hash;
 	my %max;
 	while (<$fh>) {
diff --git a/git-web--browse.sh b/git-web--browse.sh
index 9f44679..1d72ec7 100755
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -32,8 +32,9 @@
 valid_tool() {
 	case "$1" in
 	firefox | iceweasel | seamonkey | iceape | \
-	chrome | google-chrome | chromium | chromium-browser |\
-	konqueror | opera | w3m | elinks | links | lynx | dillo | open | start)
+	chrome | google-chrome | chromium | chromium-browser | \
+	konqueror | opera | w3m | elinks | links | lynx | dillo | open | \
+	start | cygstart)
 		;; # happy
 	*)
 		valid_custom_tool "$1" || return 1
@@ -127,6 +128,10 @@
 	if test -x /bin/start; then
 		browser_candidates="start $browser_candidates"
 	fi
+	# /usr/bin/cygstart indicates Cygwin
+	if test -x /usr/bin/cygstart; then
+		browser_candidates="cygstart $browser_candidates"
+	fi
 
 	for i in $browser_candidates; do
 		init_browser_path $i
@@ -174,7 +179,7 @@
 		;;
 	esac
 	;;
-w3m|elinks|links|lynx|open)
+w3m|elinks|links|lynx|open|cygstart)
 	"$browser_path" "$@"
 	;;
 start)
diff --git a/git.c b/git.c
index 1ada169..2025f77 100644
--- a/git.c
+++ b/git.c
@@ -4,6 +4,7 @@
 #include "help.h"
 #include "quote.h"
 #include "run-command.h"
+#include "commit.h"
 
 const char git_usage_string[] =
 	"git [--version] [--help] [-c name=value]\n"
@@ -146,6 +147,12 @@
 			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "--shallow-file")) {
+			(*argv)++;
+			(*argc)--;
+			set_alternate_shallow_file((*argv)[0]);
+			if (envchanged)
+				*envchanged = 1;
 		} else {
 			fprintf(stderr, "Unknown option: %s\n", cmd);
 			usage(git_usage_string);
@@ -317,6 +324,7 @@
 		{ "cat-file", cmd_cat_file, RUN_SETUP },
 		{ "check-attr", cmd_check_attr, RUN_SETUP },
 		{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
+		{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
 		{ "check-ref-format", cmd_check_ref_format },
 		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
 		{ "checkout-index", cmd_checkout_index,
@@ -507,8 +515,9 @@
 }
 
 
-int main(int argc, const char **argv)
+int main(int argc, char **av)
 {
+	const char **argv = (const char **) av;
 	const char *cmd;
 
 	startup_info = &git_startup_info;
@@ -517,6 +526,13 @@
 	if (!cmd)
 		cmd = "git-help";
 
+	/*
+	 * Always open file descriptors 0/1/2 to avoid clobbering files
+	 * in die().  It also avoids messing up when the pipes are dup'ed
+	 * onto stdin/stdout/stderr in the child processes we spawn.
+	 */
+	sanitize_stdfds();
+
 	git_setup_gettext();
 
 	/*
diff --git a/git.rc b/git.rc
new file mode 100644
index 0000000..bce6db9
--- /dev/null
+++ b/git.rc
@@ -0,0 +1,22 @@
+1 VERSIONINFO
+FILEVERSION     MAJOR,MINOR,PATCH,0
+PRODUCTVERSION  MAJOR,MINOR,PATCH,0
+BEGIN
+  BLOCK "StringFileInfo"
+  BEGIN
+    BLOCK "040904b0"   /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
+    BEGIN
+      VALUE "CompanyName", "The Git Development Community\0"
+      VALUE "FileDescription", "Git for Windows\0"
+      VALUE "InternalName", "git\0"
+      VALUE "OriginalFilename", "git.exe\0"
+      VALUE "ProductName", "Git\0"
+      VALUE "ProductVersion", GIT_VERSION "\0"
+    END
+  END
+
+  BLOCK "VarFileInfo"
+  BEGIN
+    VALUE "Translation", 0x409, 1200
+  END
+END
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 80950c0..f429f75 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -85,6 +85,9 @@
 # string of the home link on top of all pages
 our $home_link_str = "++GITWEB_HOME_LINK_STR++";
 
+# extra breadcrumbs preceding the home link
+our @extra_breadcrumbs = ();
+
 # name of your site or organization to appear in page titles
 # replace this with something more descriptive for clearer bookmarks
 our $site_name = "++GITWEB_SITENAME++"
@@ -1086,7 +1089,7 @@
 	our $search_use_regexp = $input_params{'search_use_regexp'};
 
 	our $searchtext = $input_params{'searchtext'};
-	our $search_regexp;
+	our $search_regexp = undef;
 	if (defined $searchtext) {
 		if (length($searchtext) < 2) {
 			die_error(403, "At least two characters are required for search parameter");
@@ -3982,7 +3985,9 @@
 sub print_nav_breadcrumbs {
 	my %opts = @_;
 
-	print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+	for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
+		print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+	}
 	if (defined $project) {
 		my @dirname = split '/', $project;
 		my $projectbasename = pop @dirname;
diff --git a/help.c b/help.c
index 02ba043..08c54ef 100644
--- a/help.c
+++ b/help.c
@@ -7,6 +7,7 @@
 #include "string-list.h"
 #include "column.h"
 #include "version.h"
+#include "refs.h"
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
@@ -106,7 +107,7 @@
 	    !S_ISREG(st.st_mode))
 		return 0;
 
-#if defined(WIN32) || defined(__CYGWIN__)
+#if defined(GIT_WINDOWS_NATIVE) || defined(__CYGWIN__)
 #if defined(__CYGWIN__)
 if ((st.st_mode & S_IXUSR) == 0)
 #endif
@@ -404,3 +405,52 @@
 	printf("git version %s\n", git_version_string);
 	return 0;
 }
+
+struct similar_ref_cb {
+	const char *base_ref;
+	struct string_list *similar_refs;
+};
+
+static int append_similar_ref(const char *refname, const unsigned char *sha1,
+			      int flags, void *cb_data)
+{
+	struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
+	char *branch = strrchr(refname, '/') + 1;
+	/* A remote branch of the same name is deemed similar */
+	if (!prefixcmp(refname, "refs/remotes/") &&
+	    !strcmp(branch, cb->base_ref))
+		string_list_append(cb->similar_refs,
+				   refname + strlen("refs/remotes/"));
+	return 0;
+}
+
+static struct string_list guess_refs(const char *ref)
+{
+	struct similar_ref_cb ref_cb;
+	struct string_list similar_refs = STRING_LIST_INIT_NODUP;
+
+	ref_cb.base_ref = ref;
+	ref_cb.similar_refs = &similar_refs;
+	for_each_ref(append_similar_ref, &ref_cb);
+	return similar_refs;
+}
+
+void help_unknown_ref(const char *ref, const char *cmd, const char *error)
+{
+	int i;
+	struct string_list suggested_refs = guess_refs(ref);
+
+	fprintf_ln(stderr, _("%s: %s - %s"), cmd, ref, error);
+
+	if (suggested_refs.nr > 0) {
+		fprintf_ln(stderr,
+			   Q_("\nDid you mean this?",
+			      "\nDid you mean one of these?",
+			      suggested_refs.nr));
+		for (i = 0; i < suggested_refs.nr; i++)
+			fprintf(stderr, "\t%s\n", suggested_refs.items[i].string);
+	}
+
+	string_list_clear(&suggested_refs, 0);
+	exit(1);
+}
diff --git a/help.h b/help.h
index 0ae5a12..b21d7c9 100644
--- a/help.h
+++ b/help.h
@@ -27,4 +27,9 @@
 extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
 extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
 
+/*
+ * call this to die(), when it is suspected that the user mistyped a
+ * ref to the command, to give suggested "correct" refs.
+ */
+extern void help_unknown_ref(const char *ref, const char *cmd, const char *error);
 #endif /* HELP_H */
diff --git a/http-backend.c b/http-backend.c
index 6b85ffa..0324417 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -410,14 +410,14 @@
 	strbuf_release(&buf);
 }
 
-static int show_head_ref(const char *name, const unsigned char *sha1,
+static int show_head_ref(const char *refname, const unsigned char *sha1,
 	int flag, void *cb_data)
 {
 	struct strbuf *buf = cb_data;
 
 	if (flag & REF_ISSYMREF) {
-		unsigned char sha1[20];
-		const char *target = resolve_ref_unsafe(name, sha1, 1, NULL);
+		unsigned char unused[20];
+		const char *target = resolve_ref_unsafe(refname, unused, 1, NULL);
 		const char *target_nons = strip_namespace(target);
 
 		strbuf_addf(buf, "ref: %s\n", target_nons);
diff --git a/http-push.c b/http-push.c
index 395a8cf..6dad188 100644
--- a/http-push.c
+++ b/http-push.c
@@ -663,7 +663,7 @@
 
 static int add_send_request(struct object *obj, struct remote_lock *lock)
 {
-	struct transfer_request *request = request_queue_head;
+	struct transfer_request *request;
 	struct packed_git *target;
 
 	/* Keep locks active */
diff --git a/http.c b/http.c
index 92aba59..2d086ae 100644
--- a/http.c
+++ b/http.c
@@ -228,9 +228,15 @@
 #else
 	{
 		static struct strbuf up = STRBUF_INIT;
-		strbuf_reset(&up);
-		strbuf_addf(&up, "%s:%s",
-			    http_auth.username, http_auth.password);
+		/*
+		 * Note that we assume we only ever have a single set of
+		 * credentials in a given program run, so we do not have
+		 * to worry about updating this buffer, only setting its
+		 * initial value.
+		 */
+		if (!up.len)
+			strbuf_addf(&up, "%s:%s",
+				http_auth.username, http_auth.password);
 		curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
 	}
 #endif
diff --git a/imap-send.c b/imap-send.c
index d9bcfb4..d6b65e2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -29,8 +29,18 @@
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #else
+#ifdef APPLE_COMMON_CRYPTO
+#include <CommonCrypto/CommonHMAC.h>
+#define HMAC_CTX CCHmacContext
+#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len)
+#define HMAC_Update CCHmacUpdate
+#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash)
+#define HMAC_CTX_cleanup(ignore)
+#define EVP_md5() kCCHmacAlgMD5
+#else
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#endif
 #include <openssl/x509v3.h>
 #endif
 
diff --git a/kwset.c b/kwset.c
index 5800999..a0f49b3 100644
--- a/kwset.c
+++ b/kwset.c
@@ -65,7 +65,7 @@
   struct trie *fail;		/* Aho-Corasick failure function. */
   int depth;			/* Depth of this node from the root. */
   int shift;			/* Shift function for search failures. */
-  int maxshift;			/* Max shift of self and descendents. */
+  int maxshift;			/* Max shift of self and descendants. */
 };
 
 /* Structure returned opaquely to the caller, containing everything. */
@@ -308,7 +308,7 @@
   treefails(tree->rlink, fail, recourse);
 
   /* Find, in the chain of fails going back to the root, the first
-     node that has a descendent on the current label. */
+     node that has a descendant on the current label. */
   while (fail)
     {
       link = fail->links;
@@ -426,13 +426,13 @@
 	 computing the delta table, failure function, and shift function. */
       for (curr = last = kwset->trie; curr; curr = curr->next)
 	{
-	  /* Enqueue the immediate descendents in the level order queue. */
+	  /* Enqueue the immediate descendants in the level order queue. */
 	  enqueue(curr->links, &last);
 
 	  curr->shift = kwset->mind;
 	  curr->maxshift = kwset->mind;
 
-	  /* Update the delta table for the descendents of this node. */
+	  /* Update the delta table for the descendants of this node. */
 	  treedelta(curr->links, curr->depth, delta);
 
 	  /* Compute the failure function for the descendants of this node. */
@@ -450,7 +450,7 @@
 		  fail->shift = curr->depth - fail->depth;
 
 	      /* If the current node is accepting then the shift at the
-		 fail and its descendents should be no larger than the
+		 fail and its descendants should be no larger than the
 		 difference of their depths. */
 	      if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
 		fail->maxshift = curr->depth - fail->depth;
diff --git a/line-log.c b/line-log.c
new file mode 100644
index 0000000..c2d01dc
--- /dev/null
+++ b/line-log.c
@@ -0,0 +1,1277 @@
+#include "git-compat-util.h"
+#include "line-range.h"
+#include "cache.h"
+#include "tag.h"
+#include "blob.h"
+#include "tree.h"
+#include "diff.h"
+#include "commit.h"
+#include "decorate.h"
+#include "revision.h"
+#include "xdiff-interface.h"
+#include "strbuf.h"
+#include "log-tree.h"
+#include "graph.h"
+#include "userdiff.h"
+#include "line-log.h"
+
+static void range_set_grow(struct range_set *rs, size_t extra)
+{
+	ALLOC_GROW(rs->ranges, rs->nr + extra, rs->alloc);
+}
+
+/* Either initialization would be fine */
+#define RANGE_SET_INIT {0}
+
+static void range_set_init(struct range_set *rs, size_t prealloc)
+{
+	rs->alloc = rs->nr = 0;
+	rs->ranges = NULL;
+	if (prealloc)
+		range_set_grow(rs, prealloc);
+}
+
+static void range_set_release(struct range_set *rs)
+{
+	free(rs->ranges);
+	rs->alloc = rs->nr = 0;
+	rs->ranges = NULL;
+}
+
+/* dst must be uninitialized! */
+static void range_set_copy(struct range_set *dst, struct range_set *src)
+{
+	range_set_init(dst, src->nr);
+	memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set));
+	dst->nr = src->nr;
+}
+static void range_set_move(struct range_set *dst, struct range_set *src)
+{
+	range_set_release(dst);
+	dst->ranges = src->ranges;
+	dst->nr = src->nr;
+	dst->alloc = src->alloc;
+	src->ranges = NULL;
+	src->alloc = src->nr = 0;
+}
+
+/* tack on a _new_ range _at the end_ */
+static void range_set_append_unsafe(struct range_set *rs, long a, long b)
+{
+	assert(a <= b);
+	range_set_grow(rs, 1);
+	rs->ranges[rs->nr].start = a;
+	rs->ranges[rs->nr].end = b;
+	rs->nr++;
+}
+
+static void range_set_append(struct range_set *rs, long a, long b)
+{
+	assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a);
+	range_set_append_unsafe(rs, a, b);
+}
+
+static int range_cmp(const void *_r, const void *_s)
+{
+	const struct range *r = _r;
+	const struct range *s = _s;
+
+	/* this could be simply 'return r.start-s.start', but for the types */
+	if (r->start == s->start)
+		return 0;
+	if (r->start < s->start)
+		return -1;
+	return 1;
+}
+
+/*
+ * Check that the ranges are non-empty, sorted and non-overlapping
+ */
+static void range_set_check_invariants(struct range_set *rs)
+{
+	int i;
+
+	if (!rs)
+		return;
+
+	if (rs->nr)
+		assert(rs->ranges[0].start < rs->ranges[0].end);
+
+	for (i = 1; i < rs->nr; i++) {
+		assert(rs->ranges[i-1].end < rs->ranges[i].start);
+		assert(rs->ranges[i].start < rs->ranges[i].end);
+	}
+}
+
+/*
+ * In-place pass of sorting and merging the ranges in the range set,
+ * to establish the invariants when we get the ranges from the user
+ */
+static void sort_and_merge_range_set(struct range_set *rs)
+{
+	int i;
+	int o = 0; /* output cursor */
+
+	qsort(rs->ranges, rs->nr, sizeof(struct range), range_cmp);
+
+	for (i = 0; i < rs->nr; i++) {
+		if (rs->ranges[i].start == rs->ranges[i].end)
+			continue;
+		if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) {
+			if (rs->ranges[o-1].end < rs->ranges[i].end)
+				rs->ranges[o-1].end = rs->ranges[i].end;
+		} else {
+			rs->ranges[o].start = rs->ranges[i].start;
+			rs->ranges[o].end = rs->ranges[i].end;
+			o++;
+		}
+	}
+	assert(o <= rs->nr);
+	rs->nr = o;
+
+	range_set_check_invariants(rs);
+}
+
+/*
+ * Union of range sets (i.e., sets of line numbers).  Used to merge
+ * them when searches meet at a common ancestor.
+ *
+ * This is also where the ranges are consolidated into canonical form:
+ * overlapping and adjacent ranges are merged, and empty ranges are
+ * removed.
+ */
+static void range_set_union(struct range_set *out,
+			     struct range_set *a, struct range_set *b)
+{
+	int i = 0, j = 0, o = 0;
+	struct range *ra = a->ranges;
+	struct range *rb = b->ranges;
+	/* cannot make an alias of out->ranges: it may change during grow */
+
+	assert(out->nr == 0);
+	while (i < a->nr || j < b->nr) {
+		struct range *new;
+		if (i < a->nr && j < b->nr) {
+			if (ra[i].start < rb[j].start)
+				new = &ra[i++];
+			else if (ra[i].start > rb[j].start)
+				new = &rb[j++];
+			else if (ra[i].end < rb[j].end)
+				new = &ra[i++];
+			else
+				new = &rb[j++];
+		} else if (i < a->nr)      /* b exhausted */
+			new = &ra[i++];
+		else                       /* a exhausted */
+			new = &rb[j++];
+		if (new->start == new->end)
+			; /* empty range */
+		else if (!o || out->ranges[o-1].end < new->start) {
+			range_set_grow(out, 1);
+			out->ranges[o].start = new->start;
+			out->ranges[o].end = new->end;
+			o++;
+		} else if (out->ranges[o-1].end < new->end) {
+			out->ranges[o-1].end = new->end;
+		}
+	}
+	out->nr = o;
+}
+
+/*
+ * Difference of range sets (out = a \ b).  Pass the "interesting"
+ * ranges as 'a' and the target side of the diff as 'b': it removes
+ * the ranges for which the commit is responsible.
+ */
+static void range_set_difference(struct range_set *out,
+				  struct range_set *a, struct range_set *b)
+{
+	int i, j =  0;
+	for (i = 0; i < a->nr; i++) {
+		long start = a->ranges[i].start;
+		long end = a->ranges[i].end;
+		while (start < end) {
+			while (j < b->nr && start >= b->ranges[j].end)
+				/*
+				 * a:         |-------
+				 * b: ------|
+				 */
+				j++;
+			if (j >= b->nr || end < b->ranges[j].start) {
+				/*
+				 * b exhausted, or
+				 * a:  ----|
+				 * b:         |----
+				 */
+				range_set_append(out, start, end);
+				break;
+			}
+			if (start >= b->ranges[j].start) {
+				/*
+				 * a:     |--????
+				 * b: |------|
+				 */
+				start = b->ranges[j].end;
+			} else if (end > b->ranges[j].start) {
+				/*
+				 * a: |-----|
+				 * b:    |--?????
+				 */
+				if (start < b->ranges[j].start)
+					range_set_append(out, start, b->ranges[j].start);
+				start = b->ranges[j].end;
+			}
+		}
+	}
+}
+
+static void diff_ranges_init(struct diff_ranges *diff)
+{
+	range_set_init(&diff->parent, 0);
+	range_set_init(&diff->target, 0);
+}
+
+static void diff_ranges_release(struct diff_ranges *diff)
+{
+	range_set_release(&diff->parent);
+	range_set_release(&diff->target);
+}
+
+void line_log_data_init(struct line_log_data *r)
+{
+	memset(r, 0, sizeof(struct line_log_data));
+	range_set_init(&r->ranges, 0);
+}
+
+static void line_log_data_clear(struct line_log_data *r)
+{
+	range_set_release(&r->ranges);
+	if (r->pair)
+		diff_free_filepair(r->pair);
+}
+
+static void free_line_log_data(struct line_log_data *r)
+{
+	while (r) {
+		struct line_log_data *next = r->next;
+		line_log_data_clear(r);
+		free(r);
+		r = next;
+	}
+}
+
+static struct line_log_data *
+search_line_log_data(struct line_log_data *list, const char *path,
+		     struct line_log_data **insertion_point)
+{
+	struct line_log_data *p = list;
+	if (insertion_point)
+		*insertion_point = NULL;
+	while (p) {
+		int cmp = strcmp(p->path, path);
+		if (!cmp)
+			return p;
+		if (insertion_point && cmp < 0)
+			*insertion_point = p;
+		p = p->next;
+	}
+	return NULL;
+}
+
+/*
+ * Note: takes ownership of 'path', which happens to be what the only
+ * caller needs.
+ */
+static void line_log_data_insert(struct line_log_data **list,
+				 char *path,
+				 long begin, long end)
+{
+	struct line_log_data *ip;
+	struct line_log_data *p = search_line_log_data(*list, path, &ip);
+
+	if (p) {
+		range_set_append_unsafe(&p->ranges, begin, end);
+		sort_and_merge_range_set(&p->ranges);
+		free(path);
+		return;
+	}
+
+	p = xcalloc(1, sizeof(struct line_log_data));
+	p->path = path;
+	range_set_append(&p->ranges, begin, end);
+	sort_and_merge_range_set(&p->ranges);
+	if (ip) {
+		p->next = ip->next;
+		ip->next = p;
+	} else {
+		p->next = *list;
+		*list = p;
+	}
+}
+
+struct collect_diff_cbdata {
+	struct diff_ranges *diff;
+};
+
+static int collect_diff_cb(long start_a, long count_a,
+			   long start_b, long count_b,
+			   void *data)
+{
+	struct collect_diff_cbdata *d = data;
+
+	if (count_a >= 0)
+		range_set_append(&d->diff->parent, start_a, start_a + count_a);
+	if (count_b >= 0)
+		range_set_append(&d->diff->target, start_b, start_b + count_b);
+
+	return 0;
+}
+
+static void collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
+{
+	struct collect_diff_cbdata cbdata = {NULL};
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	memset(&xpp, 0, sizeof(xpp));
+	memset(&xecfg, 0, sizeof(xecfg));
+	xecfg.ctxlen = xecfg.interhunkctxlen = 0;
+
+	cbdata.diff = out;
+	xecfg.hunk_func = collect_diff_cb;
+	memset(&ecb, 0, sizeof(ecb));
+	ecb.priv = &cbdata;
+	xdi_diff(parent, target, &xpp, &xecfg, &ecb);
+}
+
+/*
+ * These are handy for debugging.  Removing them with #if 0 silences
+ * the "unused function" warning.
+ */
+#if 0
+static void dump_range_set(struct range_set *rs, const char *desc)
+{
+	int i;
+	printf("range set %s (%d items):\n", desc, rs->nr);
+	for (i = 0; i < rs->nr; i++)
+		printf("\t[%ld,%ld]\n", rs->ranges[i].start, rs->ranges[i].end);
+}
+
+static void dump_line_log_data(struct line_log_data *r)
+{
+	char buf[4096];
+	while (r) {
+		snprintf(buf, 4096, "file %s\n", r->path);
+		dump_range_set(&r->ranges, buf);
+		r = r->next;
+	}
+}
+
+static void dump_diff_ranges(struct diff_ranges *diff, const char *desc)
+{
+	int i;
+	assert(diff->parent.nr == diff->target.nr);
+	printf("diff ranges %s (%d items):\n", desc, diff->parent.nr);
+	printf("\tparent\ttarget\n");
+	for (i = 0; i < diff->parent.nr; i++) {
+		printf("\t[%ld,%ld]\t[%ld,%ld]\n",
+		       diff->parent.ranges[i].start,
+		       diff->parent.ranges[i].end,
+		       diff->target.ranges[i].start,
+		       diff->target.ranges[i].end);
+	}
+}
+#endif
+
+
+static int ranges_overlap(struct range *a, struct range *b)
+{
+	return !(a->end <= b->start || b->end <= a->start);
+}
+
+/*
+ * Given a diff and the set of interesting ranges, determine all hunks
+ * of the diff which touch (overlap) at least one of the interesting
+ * ranges in the target.
+ */
+static void diff_ranges_filter_touched(struct diff_ranges *out,
+				       struct diff_ranges *diff,
+				       struct range_set *rs)
+{
+	int i, j = 0;
+
+	assert(out->target.nr == 0);
+
+	for (i = 0; i < diff->target.nr; i++) {
+		while (diff->target.ranges[i].start > rs->ranges[j].end) {
+			j++;
+			if (j == rs->nr)
+				return;
+		}
+		if (ranges_overlap(&diff->target.ranges[i], &rs->ranges[j])) {
+			range_set_append(&out->parent,
+					 diff->parent.ranges[i].start,
+					 diff->parent.ranges[i].end);
+			range_set_append(&out->target,
+					 diff->target.ranges[i].start,
+					 diff->target.ranges[i].end);
+		}
+	}
+}
+
+/*
+ * Adjust the line counts in 'rs' to account for the lines
+ * added/removed in the diff.
+ */
+static void range_set_shift_diff(struct range_set *out,
+				 struct range_set *rs,
+				 struct diff_ranges *diff)
+{
+	int i, j = 0;
+	long offset = 0;
+	struct range *src = rs->ranges;
+	struct range *target = diff->target.ranges;
+	struct range *parent = diff->parent.ranges;
+
+	for (i = 0; i < rs->nr; i++) {
+		while (j < diff->target.nr && src[i].start >= target[j].start) {
+			offset += (parent[j].end-parent[j].start)
+				- (target[j].end-target[j].start);
+			j++;
+		}
+		range_set_append(out, src[i].start+offset, src[i].end+offset);
+	}
+}
+
+/*
+ * Given a diff and the set of interesting ranges, map the ranges
+ * across the diff.  That is: observe that the target commit takes
+ * blame for all the + (target-side) ranges.  So for every pair of
+ * ranges in the diff that was touched, we remove the latter and add
+ * its parent side.
+ */
+static void range_set_map_across_diff(struct range_set *out,
+				      struct range_set *rs,
+				      struct diff_ranges *diff,
+				      struct diff_ranges **touched_out)
+{
+	struct diff_ranges *touched = xmalloc(sizeof(*touched));
+	struct range_set tmp1 = RANGE_SET_INIT;
+	struct range_set tmp2 = RANGE_SET_INIT;
+
+	diff_ranges_init(touched);
+	diff_ranges_filter_touched(touched, diff, rs);
+	range_set_difference(&tmp1, rs, &touched->target);
+	range_set_shift_diff(&tmp2, &tmp1, diff);
+	range_set_union(out, &tmp2, &touched->parent);
+	range_set_release(&tmp1);
+	range_set_release(&tmp2);
+
+	*touched_out = touched;
+}
+
+static struct commit *check_single_commit(struct rev_info *revs)
+{
+	struct object *commit = NULL;
+	int found = -1;
+	int i;
+
+	for (i = 0; i < revs->pending.nr; i++) {
+		struct object *obj = revs->pending.objects[i].item;
+		if (obj->flags & UNINTERESTING)
+			continue;
+		while (obj->type == OBJ_TAG)
+			obj = deref_tag(obj, NULL, 0);
+		if (obj->type != OBJ_COMMIT)
+			die("Non commit %s?", revs->pending.objects[i].name);
+		if (commit)
+			die("More than one commit to dig from: %s and %s?",
+			    revs->pending.objects[i].name,
+			    revs->pending.objects[found].name);
+		commit = obj;
+		found = i;
+	}
+
+	if (!commit)
+		die("No commit specified?");
+
+	return (struct commit *) commit;
+}
+
+static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
+{
+	unsigned mode;
+	unsigned char sha1[20];
+
+	if (get_tree_entry(commit->object.sha1, spec->path,
+			   sha1, &mode))
+		die("There is no path %s in the commit", spec->path);
+	fill_filespec(spec, sha1, 1, mode);
+
+	return;
+}
+
+static void fill_line_ends(struct diff_filespec *spec, long *lines,
+			   unsigned long **line_ends)
+{
+	int num = 0, size = 50;
+	long cur = 0;
+	unsigned long *ends = NULL;
+	char *data = NULL;
+
+	if (diff_populate_filespec(spec, 0))
+		die("Cannot read blob %s", sha1_to_hex(spec->sha1));
+
+	ends = xmalloc(size * sizeof(*ends));
+	ends[cur++] = 0;
+	data = spec->data;
+	while (num < spec->size) {
+		if (data[num] == '\n' || num == spec->size - 1) {
+			ALLOC_GROW(ends, (cur + 1), size);
+			ends[cur++] = num;
+		}
+		num++;
+	}
+
+	/* shrink the array to fit the elements */
+	ends = xrealloc(ends, cur * sizeof(*ends));
+	*lines = cur-1;
+	*line_ends = ends;
+}
+
+struct nth_line_cb {
+	struct diff_filespec *spec;
+	long lines;
+	unsigned long *line_ends;
+};
+
+static const char *nth_line(void *data, long line)
+{
+	struct nth_line_cb *d = data;
+	assert(d && line <= d->lines);
+	assert(d->spec && d->spec->data);
+
+	if (line == 0)
+		return (char *)d->spec->data;
+	else
+		return (char *)d->spec->data + d->line_ends[line] + 1;
+}
+
+static struct line_log_data *
+parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
+{
+	long lines = 0;
+	unsigned long *ends = NULL;
+	struct nth_line_cb cb_data;
+	struct string_list_item *item;
+	struct line_log_data *ranges = NULL;
+
+	for_each_string_list_item(item, args) {
+		const char *name_part, *range_part;
+		char *full_name;
+		struct diff_filespec *spec;
+		long begin = 0, end = 0;
+
+		name_part = skip_range_arg(item->string);
+		if (!name_part || *name_part != ':' || !name_part[1])
+			die("-L argument '%s' not of the form start,end:file",
+			    item->string);
+		range_part = xstrndup(item->string, name_part - item->string);
+		name_part++;
+
+		full_name = prefix_path(prefix, prefix ? strlen(prefix) : 0,
+					name_part);
+
+		spec = alloc_filespec(full_name);
+		fill_blob_sha1(commit, spec);
+		fill_line_ends(spec, &lines, &ends);
+		cb_data.spec = spec;
+		cb_data.lines = lines;
+		cb_data.line_ends = ends;
+
+		if (parse_range_arg(range_part, nth_line, &cb_data,
+				    lines, &begin, &end,
+				    full_name))
+			die("malformed -L argument '%s'", range_part);
+		if (begin < 1)
+			begin = 1;
+		if (end < 1)
+			end = lines;
+		begin--;
+		if (lines < end || lines < begin)
+			die("file %s has only %ld lines", name_part, lines);
+		line_log_data_insert(&ranges, full_name, begin, end);
+
+		free_filespec(spec);
+		free(ends);
+		ends = NULL;
+	}
+
+	return ranges;
+}
+
+static struct line_log_data *line_log_data_copy_one(struct line_log_data *r)
+{
+	struct line_log_data *ret = xmalloc(sizeof(*ret));
+
+	assert(r);
+	line_log_data_init(ret);
+	range_set_copy(&ret->ranges, &r->ranges);
+
+	ret->path = xstrdup(r->path);
+
+	return ret;
+}
+
+static struct line_log_data *
+line_log_data_copy(struct line_log_data *r)
+{
+	struct line_log_data *ret = NULL;
+	struct line_log_data *tmp = NULL, *prev = NULL;
+
+	assert(r);
+	ret = tmp = prev = line_log_data_copy_one(r);
+	r = r->next;
+	while (r) {
+		tmp = line_log_data_copy_one(r);
+		prev->next = tmp;
+		prev = tmp;
+		r = r->next;
+	}
+
+	return ret;
+}
+
+/* merge two range sets across files */
+static struct line_log_data *line_log_data_merge(struct line_log_data *a,
+						 struct line_log_data *b)
+{
+	struct line_log_data *head = NULL, **pp = &head;
+
+	while (a || b) {
+		struct line_log_data *src;
+		struct line_log_data *src2 = NULL;
+		struct line_log_data *d;
+		int cmp;
+		if (!a)
+			cmp = 1;
+		else if (!b)
+			cmp = -1;
+		else
+			cmp = strcmp(a->path, b->path);
+		if (cmp < 0) {
+			src = a;
+			a = a->next;
+		} else if (cmp == 0) {
+			src = a;
+			a = a->next;
+			src2 = b;
+			b = b->next;
+		} else {
+			src = b;
+			b = b->next;
+		}
+		d = xmalloc(sizeof(struct line_log_data));
+		line_log_data_init(d);
+		d->path = xstrdup(src->path);
+		*pp = d;
+		pp = &d->next;
+		if (src2)
+			range_set_union(&d->ranges, &src->ranges, &src2->ranges);
+		else
+			range_set_copy(&d->ranges, &src->ranges);
+	}
+
+	return head;
+}
+
+static void add_line_range(struct rev_info *revs, struct commit *commit,
+			   struct line_log_data *range)
+{
+	struct line_log_data *old = NULL;
+	struct line_log_data *new = NULL;
+
+	old = lookup_decoration(&revs->line_log_data, &commit->object);
+	if (old && range) {
+		new = line_log_data_merge(old, range);
+		free_line_log_data(old);
+	} else if (range)
+		new = line_log_data_copy(range);
+
+	if (new)
+		add_decoration(&revs->line_log_data, &commit->object, new);
+}
+
+static void clear_commit_line_range(struct rev_info *revs, struct commit *commit)
+{
+	struct line_log_data *r;
+	r = lookup_decoration(&revs->line_log_data, &commit->object);
+	if (!r)
+		return;
+	free_line_log_data(r);
+	add_decoration(&revs->line_log_data, &commit->object, NULL);
+}
+
+static struct line_log_data *lookup_line_range(struct rev_info *revs,
+					       struct commit *commit)
+{
+	struct line_log_data *ret = NULL;
+	struct line_log_data *d;
+
+	ret = lookup_decoration(&revs->line_log_data, &commit->object);
+
+	for (d = ret; d; d = d->next)
+		range_set_check_invariants(&d->ranges);
+
+	return ret;
+}
+
+void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
+{
+	struct commit *commit = NULL;
+	struct line_log_data *range;
+
+	commit = check_single_commit(rev);
+	range = parse_lines(commit, prefix, args);
+	add_line_range(rev, commit, range);
+
+	if (!rev->diffopt.detect_rename) {
+		int i, count = 0;
+		struct line_log_data *r = range;
+		const char **paths;
+		while (r) {
+			count++;
+			r = r->next;
+		}
+		paths = xmalloc((count+1)*sizeof(char *));
+		r = range;
+		for (i = 0; i < count; i++) {
+			paths[i] = xstrdup(r->path);
+			r = r->next;
+		}
+		paths[count] = NULL;
+		init_pathspec(&rev->diffopt.pathspec, paths);
+		free(paths);
+	}
+}
+
+static void load_tree_desc(struct tree_desc *desc, void **tree,
+			   const unsigned char *sha1)
+{
+	unsigned long size;
+	*tree = read_object_with_reference(sha1, tree_type, &size, NULL);
+	if (!*tree)
+		die("Unable to read tree (%s)", sha1_to_hex(sha1));
+	init_tree_desc(desc, *tree, size);
+}
+
+static int count_parents(struct commit *commit)
+{
+	struct commit_list *parents = commit->parents;
+	int count = 0;
+	while (parents) {
+		count++;
+		parents = parents->next;
+	}
+	return count;
+}
+
+static void move_diff_queue(struct diff_queue_struct *dst,
+			    struct diff_queue_struct *src)
+{
+	assert(src != dst);
+	memcpy(dst, src, sizeof(struct diff_queue_struct));
+	DIFF_QUEUE_CLEAR(src);
+}
+
+static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions)
+{
+	int i;
+	struct diff_queue_struct outq;
+	DIFF_QUEUE_CLEAR(&outq);
+
+	for (i = 0; i < diff_queued_diff.nr; i++) {
+		struct diff_filepair *p = diff_queued_diff.queue[i];
+		struct line_log_data *rg = NULL;
+
+		if (!DIFF_FILE_VALID(p->two)) {
+			if (keep_deletions)
+				diff_q(&outq, p);
+			else
+				diff_free_filepair(p);
+			continue;
+		}
+		for (rg = range; rg; rg = rg->next) {
+			if (!strcmp(rg->path, p->two->path))
+				break;
+		}
+		if (rg)
+			diff_q(&outq, p);
+		else
+			diff_free_filepair(p);
+	}
+	free(diff_queued_diff.queue);
+	diff_queued_diff = outq;
+}
+
+static inline int diff_might_be_rename(void)
+{
+	int i;
+	for (i = 0; i < diff_queued_diff.nr; i++)
+		if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) {
+			/* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */
+			/* 	diff_queued_diff.queue[i]->two->path); */
+			return 1;
+		}
+	return 0;
+}
+
+static void queue_diffs(struct line_log_data *range,
+			struct diff_options *opt,
+			struct diff_queue_struct *queue,
+			struct commit *commit, struct commit *parent)
+{
+	void *tree1 = NULL, *tree2 = NULL;
+	struct tree_desc desc1, desc2;
+
+	assert(commit);
+	load_tree_desc(&desc2, &tree2, commit->tree->object.sha1);
+	if (parent)
+		load_tree_desc(&desc1, &tree1, parent->tree->object.sha1);
+	else
+		init_tree_desc(&desc1, "", 0);
+
+	DIFF_QUEUE_CLEAR(&diff_queued_diff);
+	diff_tree(&desc1, &desc2, "", opt);
+	if (opt->detect_rename) {
+		filter_diffs_for_paths(range, 1);
+		if (diff_might_be_rename())
+			diffcore_std(opt);
+		filter_diffs_for_paths(range, 0);
+	}
+	move_diff_queue(queue, &diff_queued_diff);
+
+	if (tree1)
+		free(tree1);
+	if (tree2)
+		free(tree2);
+}
+
+static char *get_nth_line(long line, unsigned long *ends, void *data)
+{
+	if (line == 0)
+		return (char *)data;
+	else
+		return (char *)data + ends[line] + 1;
+}
+
+static void print_line(const char *prefix, char first,
+		       long line, unsigned long *ends, void *data,
+		       const char *color, const char *reset)
+{
+	char *begin = get_nth_line(line, ends, data);
+	char *end = get_nth_line(line+1, ends, data);
+	int had_nl = 0;
+
+	if (end > begin && end[-1] == '\n') {
+		end--;
+		had_nl = 1;
+	}
+
+	fputs(prefix, stdout);
+	fputs(color, stdout);
+	putchar(first);
+	fwrite(begin, 1, end-begin, stdout);
+	fputs(reset, stdout);
+	putchar('\n');
+	if (!had_nl)
+		fputs("\\ No newline at end of file\n", stdout);
+}
+
+static char *output_prefix(struct diff_options *opt)
+{
+	char *prefix = "";
+
+	if (opt->output_prefix) {
+		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
+		prefix = sb->buf;
+	}
+
+	return prefix;
+}
+
+static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
+{
+	int i, j = 0;
+	long p_lines, t_lines;
+	unsigned long *p_ends = NULL, *t_ends = NULL;
+	struct diff_filepair *pair = range->pair;
+	struct diff_ranges *diff = &range->diff;
+
+	struct diff_options *opt = &rev->diffopt;
+	char *prefix = output_prefix(opt);
+	const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
+	const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
+	const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
+	const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
+	const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
+	const char *c_plain = diff_get_color(opt->use_color, DIFF_PLAIN);
+
+	if (!pair || !diff)
+		return;
+
+	if (pair->one->sha1_valid)
+		fill_line_ends(pair->one, &p_lines, &p_ends);
+	fill_line_ends(pair->two, &t_lines, &t_ends);
+
+	printf("%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
+	printf("%s%s--- %s%s%s\n", prefix, c_meta,
+	       pair->one->sha1_valid ? "a/" : "",
+	       pair->one->sha1_valid ? pair->one->path : "/dev/null",
+	       c_reset);
+	printf("%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
+	for (i = 0; i < range->ranges.nr; i++) {
+		long p_start, p_end;
+		long t_start = range->ranges.ranges[i].start;
+		long t_end = range->ranges.ranges[i].end;
+		long t_cur = t_start;
+		int j_last;
+
+		while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
+			j++;
+		if (j == diff->target.nr || diff->target.ranges[j].start > t_end)
+			continue;
+
+		/* Scan ahead to determine the last diff that falls in this range */
+		j_last = j;
+		while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end)
+			j_last++;
+		if (j_last > j)
+			j_last--;
+
+		/*
+		 * Compute parent hunk headers: we know that the diff
+		 * has the correct line numbers (but not all hunks).
+		 * So it suffices to shift the start/end according to
+		 * the line numbers of the first/last hunk(s) that
+		 * fall in this range.
+		 */
+		if (t_start < diff->target.ranges[j].start)
+			p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start);
+		else
+			p_start = diff->parent.ranges[j].start;
+		if (t_end > diff->target.ranges[j_last].end)
+			p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end);
+		else
+			p_end = diff->parent.ranges[j_last].end;
+
+		if (!p_start && !p_end) {
+			p_start = -1;
+			p_end = -1;
+		}
+
+		/* Now output a diff hunk for this range */
+		printf("%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
+		       prefix, c_frag,
+		       p_start+1, p_end-p_start, t_start+1, t_end-t_start,
+		       c_reset);
+		while (j < diff->target.nr && diff->target.ranges[j].start < t_end) {
+			int k;
+			for (; t_cur < diff->target.ranges[j].start; t_cur++)
+				print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
+					   c_plain, c_reset);
+			for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
+				print_line(prefix, '-', k, p_ends, pair->one->data,
+					   c_old, c_reset);
+			for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
+				print_line(prefix, '+', t_cur, t_ends, pair->two->data,
+					   c_new, c_reset);
+			j++;
+		}
+		for (; t_cur < t_end; t_cur++)
+			print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
+				   c_plain, c_reset);
+	}
+
+	free(p_ends);
+	free(t_ends);
+}
+
+/*
+ * NEEDSWORK: manually building a diff here is not the Right
+ * Thing(tm).  log -L should be built into the diff pipeline.
+ */
+static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
+{
+	puts(output_prefix(&rev->diffopt));
+	while (range) {
+		dump_diff_hacky_one(rev, range);
+		range = range->next;
+	}
+}
+
+/*
+ * Unlike most other functions, this destructively operates on
+ * 'range'.
+ */
+static int process_diff_filepair(struct rev_info *rev,
+				 struct diff_filepair *pair,
+				 struct line_log_data *range,
+				 struct diff_ranges **diff_out)
+{
+	struct line_log_data *rg = range;
+	struct range_set tmp;
+	struct diff_ranges diff;
+	mmfile_t file_parent, file_target;
+
+	assert(pair->two->path);
+	while (rg) {
+		assert(rg->path);
+		if (!strcmp(rg->path, pair->two->path))
+			break;
+		rg = rg->next;
+	}
+
+	if (!rg)
+		return 0;
+	if (rg->ranges.nr == 0)
+		return 0;
+
+	assert(pair->two->sha1_valid);
+	diff_populate_filespec(pair->two, 0);
+	file_target.ptr = pair->two->data;
+	file_target.size = pair->two->size;
+
+	if (pair->one->sha1_valid) {
+		diff_populate_filespec(pair->one, 0);
+		file_parent.ptr = pair->one->data;
+		file_parent.size = pair->one->size;
+	} else {
+		file_parent.ptr = "";
+		file_parent.size = 0;
+	}
+
+	diff_ranges_init(&diff);
+	collect_diff(&file_parent, &file_target, &diff);
+
+	/* NEEDSWORK should apply some heuristics to prevent mismatches */
+	free(rg->path);
+	rg->path = xstrdup(pair->one->path);
+
+	range_set_init(&tmp, 0);
+	range_set_map_across_diff(&tmp, &rg->ranges, &diff, diff_out);
+	range_set_release(&rg->ranges);
+	range_set_move(&rg->ranges, &tmp);
+
+	diff_ranges_release(&diff);
+
+	return ((*diff_out)->parent.nr > 0);
+}
+
+static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
+{
+	struct diff_filepair *new = xmalloc(sizeof(struct diff_filepair));
+	new->one = pair->one;
+	new->two = pair->two;
+	new->one->count++;
+	new->two->count++;
+	return new;
+}
+
+static void free_diffqueues(int n, struct diff_queue_struct *dq)
+{
+	int i, j;
+	for (i = 0; i < n; i++)
+		for (j = 0; j < dq[i].nr; j++)
+			diff_free_filepair(dq[i].queue[j]);
+	free(dq);
+}
+
+static int process_all_files(struct line_log_data **range_out,
+			     struct rev_info *rev,
+			     struct diff_queue_struct *queue,
+			     struct line_log_data *range)
+{
+	int i, changed = 0;
+
+	*range_out = line_log_data_copy(range);
+
+	for (i = 0; i < queue->nr; i++) {
+		struct diff_ranges *pairdiff = NULL;
+		struct diff_filepair *pair = queue->queue[i];
+		if (process_diff_filepair(rev, pair, *range_out, &pairdiff)) {
+			/*
+			 * Store away the diff for later output.  We
+			 * tuck it in the ranges we got as _input_,
+			 * since that's the commit that caused the
+			 * diff.
+			 *
+			 * NEEDSWORK not enough when we get around to
+			 * doing something interesting with merges;
+			 * currently each invocation on a merge parent
+			 * trashes the previous one's diff.
+			 *
+			 * NEEDSWORK tramples over data structures not owned here
+			 */
+			struct line_log_data *rg = range;
+			changed++;
+			while (rg && strcmp(rg->path, pair->two->path))
+				rg = rg->next;
+			assert(rg);
+			rg->pair = diff_filepair_dup(queue->queue[i]);
+			memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges));
+		}
+	}
+
+	return changed;
+}
+
+int line_log_print(struct rev_info *rev, struct commit *commit)
+{
+	struct line_log_data *range = lookup_line_range(rev, commit);
+
+	show_log(rev);
+	dump_diff_hacky(rev, range);
+	return 1;
+}
+
+static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit,
+					  struct line_log_data *range)
+{
+	struct commit *parent = NULL;
+	struct diff_queue_struct queue;
+	struct line_log_data *parent_range;
+	int changed;
+
+	if (commit->parents)
+		parent = commit->parents->item;
+
+	queue_diffs(range, &rev->diffopt, &queue, commit, parent);
+	changed = process_all_files(&parent_range, rev, &queue, range);
+	if (parent)
+		add_line_range(rev, parent, parent_range);
+	return changed;
+}
+
+static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit,
+				       struct line_log_data *range)
+{
+	struct diff_queue_struct *diffqueues;
+	struct line_log_data **cand;
+	struct commit **parents;
+	struct commit_list *p;
+	int i;
+	int nparents = count_parents(commit);
+
+	diffqueues = xmalloc(nparents * sizeof(*diffqueues));
+	cand = xmalloc(nparents * sizeof(*cand));
+	parents = xmalloc(nparents * sizeof(*parents));
+
+	p = commit->parents;
+	for (i = 0; i < nparents; i++) {
+		parents[i] = p->item;
+		p = p->next;
+		queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]);
+	}
+
+	for (i = 0; i < nparents; i++) {
+		int changed;
+		cand[i] = NULL;
+		changed = process_all_files(&cand[i], rev, &diffqueues[i], range);
+		if (!changed) {
+			/*
+			 * This parent can take all the blame, so we
+			 * don't follow any other path in history
+			 */
+			add_line_range(rev, parents[i], cand[i]);
+			clear_commit_line_range(rev, commit);
+			commit->parents = xmalloc(sizeof(struct commit_list));
+			commit->parents->item = parents[i];
+			commit->parents->next = NULL;
+			free(parents);
+			free(cand);
+			free_diffqueues(nparents, diffqueues);
+			/* NEEDSWORK leaking like a sieve */
+			return 0;
+		}
+	}
+
+	/*
+	 * No single parent took the blame.  We add the candidates
+	 * from the above loop to the parents.
+	 */
+	for (i = 0; i < nparents; i++) {
+		add_line_range(rev, parents[i], cand[i]);
+	}
+
+	clear_commit_line_range(rev, commit);
+	free(parents);
+	free(cand);
+	free_diffqueues(nparents, diffqueues);
+	return 1;
+
+	/* NEEDSWORK evil merge detection stuff */
+	/* NEEDSWORK leaking like a sieve */
+}
+
+static int process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)
+{
+	struct line_log_data *range = lookup_line_range(rev, commit);
+	int changed = 0;
+
+	if (range) {
+		if (!commit->parents || !commit->parents->next)
+			changed = process_ranges_ordinary_commit(rev, commit, range);
+		else
+			changed = process_ranges_merge_commit(rev, commit, range);
+	}
+
+	if (!changed)
+		commit->object.flags |= TREESAME;
+
+	return changed;
+}
+
+static enum rewrite_result line_log_rewrite_one(struct rev_info *rev, struct commit **pp)
+{
+	for (;;) {
+		struct commit *p = *pp;
+		if (p->parents && p->parents->next)
+			return rewrite_one_ok;
+		if (p->object.flags & UNINTERESTING)
+			return rewrite_one_ok;
+		if (!(p->object.flags & TREESAME))
+			return rewrite_one_ok;
+		if (!p->parents)
+			return rewrite_one_noparents;
+		*pp = p->parents->item;
+	}
+}
+
+int line_log_filter(struct rev_info *rev)
+{
+	struct commit *commit;
+	struct commit_list *list = rev->commits;
+	struct commit_list *out = NULL, **pp = &out;
+
+	while (list) {
+		struct commit_list *to_free = NULL;
+		commit = list->item;
+		if (process_ranges_arbitrary_commit(rev, commit)) {
+			*pp = list;
+			pp = &list->next;
+		} else
+			to_free = list;
+		list = list->next;
+		free(to_free);
+	}
+	*pp = NULL;
+
+	for (list = out; list; list = list->next)
+		rewrite_parents(rev, list->item, line_log_rewrite_one);
+
+	rev->commits = out;
+
+	return 0;
+}
diff --git a/line-log.h b/line-log.h
new file mode 100644
index 0000000..8bea45f
--- /dev/null
+++ b/line-log.h
@@ -0,0 +1,53 @@
+#ifndef LINE_LOG_H
+#define LINE_LOG_H
+
+#include "diffcore.h"
+
+struct rev_info;
+struct commit;
+
+/* A range [start,end].  Lines are numbered starting at 0, and the
+ * ranges include start but exclude end. */
+struct range {
+	long start, end;
+};
+
+/* A set of ranges.  The ranges must always be disjoint and sorted. */
+struct range_set {
+	int alloc, nr;
+	struct range *ranges;
+};
+
+/* A diff, encoded as the set of pre- and post-image ranges where the
+ * files differ. A pair of ranges corresponds to a hunk. */
+struct diff_ranges {
+	struct range_set parent;
+	struct range_set target;
+};
+
+/* Linked list of interesting files and their associated ranges.  The
+ * list must be kept sorted by path.
+ *
+ * For simplicity, even though this is highly redundant, each
+ * line_log_data owns its 'path'.
+ */
+struct line_log_data {
+	struct line_log_data *next;
+	char *path;
+	char status;
+	struct range_set ranges;
+	int arg_alloc, arg_nr;
+	const char **args;
+	struct diff_filepair *pair;
+	struct diff_ranges diff;
+};
+
+extern void line_log_data_init(struct line_log_data *r);
+
+extern void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
+
+extern int line_log_filter(struct rev_info *rev);
+
+extern int line_log_print(struct rev_info *rev, struct commit *commit);
+
+#endif /* LINE_LOG_H */
diff --git a/line-range.c b/line-range.c
new file mode 100644
index 0000000..3942475
--- /dev/null
+++ b/line-range.c
@@ -0,0 +1,250 @@
+#include "git-compat-util.h"
+#include "line-range.h"
+#include "xdiff-interface.h"
+#include "strbuf.h"
+#include "userdiff.h"
+
+/*
+ * Parse one item in the -L option
+ */
+static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
+			     void *data, long lines, long begin, long *ret)
+{
+	char *term;
+	const char *line;
+	long num;
+	int reg_error;
+	regex_t regexp;
+	regmatch_t match[1];
+
+	/* Allow "-L <something>,+20" to mean starting at <something>
+	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
+	 * <something>.
+	 */
+	if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
+		num = strtol(spec + 1, &term, 10);
+		if (term != spec + 1) {
+			if (!ret)
+				return term;
+			if (spec[0] == '-')
+				num = 0 - num;
+			if (0 < num)
+				*ret = begin + num - 2;
+			else if (!num)
+				*ret = begin;
+			else
+				*ret = begin + num;
+			return term;
+		}
+		return spec;
+	}
+	num = strtol(spec, &term, 10);
+	if (term != spec) {
+		if (ret)
+			*ret = num;
+		return term;
+	}
+	if (spec[0] != '/')
+		return spec;
+
+	/* it could be a regexp of form /.../ */
+	for (term = (char *) spec + 1; *term && *term != '/'; term++) {
+		if (*term == '\\')
+			term++;
+	}
+	if (*term != '/')
+		return spec;
+
+	/* in the scan-only case we are not interested in the regex */
+	if (!ret)
+		return term+1;
+
+	/* try [spec+1 .. term-1] as regexp */
+	*term = 0;
+	begin--; /* input is in human terms */
+	line = nth_line(data, begin);
+
+	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
+	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
+		const char *cp = line + match[0].rm_so;
+		const char *nline;
+
+		while (begin++ < lines) {
+			nline = nth_line(data, begin);
+			if (line <= cp && cp < nline)
+				break;
+			line = nline;
+		}
+		*ret = begin;
+		regfree(&regexp);
+		*term++ = '/';
+		return term;
+	}
+	else {
+		char errbuf[1024];
+		regerror(reg_error, &regexp, errbuf, 1024);
+		die("-L parameter '%s': %s", spec + 1, errbuf);
+	}
+}
+
+static int match_funcname(xdemitconf_t *xecfg, const char *bol, const char *eol)
+{
+	if (xecfg) {
+		char buf[1];
+		return xecfg->find_func(bol, eol - bol, buf, 1,
+					xecfg->find_func_priv) >= 0;
+	}
+
+	if (bol == eol)
+		return 0;
+	if (isalpha(*bol) || *bol == '_' || *bol == '$')
+		return 1;
+	return 0;
+}
+
+static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char *start,
+						 regex_t *regexp)
+{
+	int reg_error;
+	regmatch_t match[1];
+	while (1) {
+		const char *bol, *eol;
+		reg_error = regexec(regexp, start, 1, match, 0);
+		if (reg_error == REG_NOMATCH)
+			return NULL;
+		else if (reg_error) {
+			char errbuf[1024];
+			regerror(reg_error, regexp, errbuf, 1024);
+			die("-L parameter: regexec() failed: %s", errbuf);
+		}
+		/* determine extent of line matched */
+		bol = start+match[0].rm_so;
+		eol = start+match[0].rm_eo;
+		while (bol > start && *bol != '\n')
+			bol--;
+		if (*bol == '\n')
+			bol++;
+		while (*eol && *eol != '\n')
+			eol++;
+		if (*eol == '\n')
+			eol++;
+		/* is it a funcname line? */
+		if (match_funcname(xecfg, (char*) bol, (char*) eol))
+			return bol;
+		start = eol;
+	}
+}
+
+static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
+					void *cb_data, long lines, long *begin, long *end,
+					const char *path)
+{
+	char *pattern;
+	const char *term;
+	struct userdiff_driver *drv;
+	xdemitconf_t *xecfg = NULL;
+	const char *start;
+	const char *p;
+	int reg_error;
+	regex_t regexp;
+
+	assert(*arg == ':');
+	term = arg+1;
+	while (*term && *term != ':') {
+		if (*term == '\\' && *(term+1))
+			term++;
+		term++;
+	}
+	if (term == arg+1)
+		return NULL;
+	if (!begin) /* skip_range_arg case */
+		return term;
+
+	pattern = xstrndup(arg+1, term-(arg+1));
+
+	start = nth_line_cb(cb_data, 0);
+
+	drv = userdiff_find_by_path(path);
+	if (drv && drv->funcname.pattern) {
+		const struct userdiff_funcname *pe = &drv->funcname;
+		xecfg = xcalloc(1, sizeof(*xecfg));
+		xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
+	}
+
+	reg_error = regcomp(&regexp, pattern, REG_NEWLINE);
+	if (reg_error) {
+		char errbuf[1024];
+		regerror(reg_error, &regexp, errbuf, 1024);
+		die("-L parameter '%s': %s", pattern, errbuf);
+	}
+
+	p = find_funcname_matching_regexp(xecfg, (char*) start, &regexp);
+	if (!p)
+		die("-L parameter '%s': no match", pattern);
+	*begin = 0;
+	while (p > nth_line_cb(cb_data, *begin))
+		(*begin)++;
+
+	if (*begin >= lines)
+		die("-L parameter '%s' matches at EOF", pattern);
+
+	*end = *begin+1;
+	while (*end < lines) {
+		const char *bol = nth_line_cb(cb_data, *end);
+		const char *eol = nth_line_cb(cb_data, *end+1);
+		if (match_funcname(xecfg, bol, eol))
+			break;
+		(*end)++;
+	}
+
+	regfree(&regexp);
+	free(xecfg);
+	free(pattern);
+
+	/* compensate for 1-based numbering */
+	(*begin)++;
+
+	return term;
+}
+
+int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
+		    void *cb_data, long lines, long *begin, long *end,
+		    const char *path)
+{
+	*begin = *end = 0;
+
+	if (*arg == ':') {
+		arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
+		if (!arg || *arg)
+			return -1;
+		return 0;
+	}
+
+	arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
+
+	if (*arg == ',')
+		arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);
+
+	if (*arg)
+		return -1;
+
+	if (*begin && *end && *end < *begin) {
+		long tmp;
+		tmp = *end; *end = *begin; *begin = tmp;
+	}
+
+	return 0;
+}
+
+const char *skip_range_arg(const char *arg)
+{
+	if (*arg == ':')
+		return parse_range_funcname(arg, NULL, NULL, 0, NULL, NULL, NULL);
+
+	arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
+
+	if (*arg == ',')
+		arg = parse_loc(arg+1, NULL, NULL, 0, 0, NULL);
+
+	return arg;
+}
diff --git a/line-range.h b/line-range.h
new file mode 100644
index 0000000..ae3d012
--- /dev/null
+++ b/line-range.h
@@ -0,0 +1,36 @@
+#ifndef LINE_RANGE_H
+#define LINE_RANGE_H
+
+/*
+ * Parse one item in an -L begin,end option w.r.t. the notional file
+ * object 'cb_data' consisting of 'lines' lines.
+ *
+ * The 'nth_line_cb' callback is used to determine the start of the
+ * line 'lno' inside the 'cb_data'.  The caller is expected to already
+ * have a suitable map at hand to make this a constant-time lookup.
+ *
+ * Returns 0 in case of success and -1 if there was an error.  The
+ * actual range is stored in *begin and *end.  The counting starts
+ * at 1!  In case of error, the caller should show usage message.
+ */
+
+typedef const char *(*nth_line_fn_t)(void *data, long lno);
+
+extern int parse_range_arg(const char *arg,
+			   nth_line_fn_t nth_line_cb,
+			   void *cb_data, long lines,
+			   long *begin, long *end,
+			   const char *path);
+
+/*
+ * Scan past a range argument that could be parsed by
+ * 'parse_range_arg', to help the caller determine the start of the
+ * filename in '-L n,m:file' syntax.
+ *
+ * Returns a pointer to the first character after the 'n,m' part, or
+ * NULL in case the argument is obviously malformed.
+ */
+
+extern const char *skip_range_arg(const char *arg);
+
+#endif /* LINE_RANGE_H */
diff --git a/lockfile.c b/lockfile.c
index c6fb77b..8fbcb6a 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -124,15 +124,17 @@
 
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-	if (strlen(path) >= sizeof(lk->filename))
-		return -1;
-	strcpy(lk->filename, path);
 	/*
 	 * subtract 5 from size to make sure there's room for adding
 	 * ".lock" for the lock file name
 	 */
+	static const size_t max_path_len = sizeof(lk->filename) - 5;
+
+	if (strlen(path) >= max_path_len)
+		return -1;
+	strcpy(lk->filename, path);
 	if (!(flags & LOCK_NODEREF))
-		resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+		resolve_symlink(lk->filename, max_path_len);
 	strcat(lk->filename, ".lock");
 	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= lk->fd) {
diff --git a/log-tree.c b/log-tree.c
index 1946e9c..a49d8e8 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -10,6 +10,7 @@
 #include "color.h"
 #include "gpg-interface.h"
 #include "sequencer.h"
+#include "line-log.h"
 
 struct decoration name_decoration = { "object names" };
 
@@ -616,6 +617,9 @@
 	ctx.fmt = opt->commit_format;
 	ctx.mailmap = opt->mailmap;
 	ctx.color = opt->diffopt.use_color;
+	ctx.output_encoding = get_log_output_encoding();
+	if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
+		ctx.from_ident = &opt->from_ident;
 	pretty_print_commit(&ctx, commit, &msgbuf);
 
 	if (opt->add_signoff)
@@ -796,6 +800,9 @@
 	log.parent = NULL;
 	opt->loginfo = &log;
 
+	if (opt->line_level_traverse)
+		return line_log_print(opt, commit);
+
 	shown = log_tree_diff(opt, commit, &log);
 	if (!shown && opt->loginfo && opt->always_show_header) {
 		log.parent = NULL;
diff --git a/mailmap.c b/mailmap.c
index 2a7b366..44614fc 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -5,8 +5,10 @@
 #define DEBUG_MAILMAP 0
 #if DEBUG_MAILMAP
 #define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#define debug_str(X) ((X) ? (X) : "(none)")
 #else
 static inline void debug_mm(const char *format, ...) {}
+static inline const char *debug_str(const char *s) { return s; }
 #endif
 
 const char *git_mailmap_file;
@@ -29,7 +31,8 @@
 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);
+	debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
+		 s, debug_str(mi->name), debug_str(mi->email));
 	free(mi->name);
 	free(mi->email);
 }
@@ -37,8 +40,11 @@
 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);
+	debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n",
+		 s, me->namemap.nr);
+	debug_mm("mailmap: - simple: '%s' <%s>\n",
+		 debug_str(me->name), debug_str(me->email));
+
 	free(me->name);
 	free(me->email);
 
@@ -47,18 +53,11 @@
 }
 
 static void add_mapping(struct string_list *map,
-			char *new_name, char *new_email, char *old_name, char *old_email)
+			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;
@@ -68,16 +67,21 @@
 	if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
 		/* mailmap entry exists, invert index value */
 		index = -1 - index;
+		me = (struct mailmap_entry *)map->items[index].util;
 	} else {
 		/* create mailmap entry */
-		struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
-		item->util = xcalloc(1, sizeof(struct mailmap_entry));
-		((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
+		struct string_list_item *item;
+
+		item = string_list_insert_at_index(map, index, old_email);
+		me = xcalloc(1, sizeof(struct mailmap_entry));
+		me->namemap.strdup_strings = 1;
+		me->namemap.cmp = strcasecmp;
+		item->util = me;
 	}
-	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);
+		debug_mm("mailmap: adding (simple) entry for %s at index %d\n",
+			 old_email, index);
 		/* Replace current name and new email for simple entry */
 		if (new_name) {
 			free(me->name);
@@ -89,7 +93,8 @@
 		}
 	} else {
 		struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
-		debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
+		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)
@@ -98,11 +103,12 @@
 	}
 
 	debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
-		 old_name, old_email, new_name, new_email);
+		 debug_str(old_name), old_email,
+		 debug_str(new_name), debug_str(new_email));
 }
 
 static char *parse_name_and_email(char *buffer, char **name,
-		char **email, int allow_empty_email)
+				  char **email, int allow_empty_email)
 {
 	char *left, *right, *nstart, *nend;
 	*name = *email = NULL;
@@ -122,7 +128,7 @@
 	while (nend > nstart && isspace(*nend))
 		--nend;
 
-	*name = (nstart < nend ? nstart : NULL);
+	*name = (nstart <= nend ? nstart : NULL);
 	*email = left+1;
 	*(nend+1) = '\0';
 	*right++ = '\0';
@@ -306,21 +312,25 @@
 }
 
 int map_user(struct string_list *map,
-			 const char **email, size_t *emaillen,
-			 const char **name, size_t *namelen)
+	     const char **email, size_t *emaillen,
+	     const char **name, size_t *namelen)
 {
 	struct string_list_item *item;
 	struct mailmap_entry *me;
 
 	debug_mm("map_user: map '%.*s' <%.*s>\n",
-		 *name, *namelen, *emaillen, *email);
+		 (int)*namelen, debug_str(*name),
+		 (int)*emaillen, debug_str(*email));
 
 	item = lookup_prefix(map, *email, *emaillen);
 	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      */
+			/*
+			 * 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;
 			subitem = lookup_prefix(&me->namemap, *name, *namelen);
 			if (subitem)
@@ -341,8 +351,9 @@
 				*name = mi->name;
 				*namelen = strlen(*name);
 		}
-		debug_mm("map_user:  to '%.*s' <.*%s>\n", *namelen, *name,
-				 *emaillen, *email);
+		debug_mm("map_user:  to '%.*s' <%.*s>\n",
+			 (int)*namelen, debug_str(*name),
+			 (int)*emaillen, debug_str(*email));
 		return 1;
 	}
 	debug_mm("map_user:  --\n");
diff --git a/match-trees.c b/match-trees.c
index 2bb734d..7873cde 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -47,6 +47,22 @@
 	return score;
 }
 
+static void *fill_tree_desc_strict(struct tree_desc *desc,
+				   const unsigned char *hash)
+{
+	void *buffer;
+	enum object_type type;
+	unsigned long size;
+
+	buffer = read_sha1_file(hash, &type, &size);
+	if (!buffer)
+		die("unable to read tree (%s)", sha1_to_hex(hash));
+	if (type != OBJ_TREE)
+		die("%s is not a tree", sha1_to_hex(hash));
+	init_tree_desc(desc, buffer, size);
+	return buffer;
+}
+
 static int base_name_entries_compare(const struct name_entry *a,
 				     const struct name_entry *b)
 {
@@ -61,23 +77,10 @@
 {
 	struct tree_desc one;
 	struct tree_desc two;
-	void *one_buf, *two_buf;
+	void *one_buf = fill_tree_desc_strict(&one, hash1);
+	void *two_buf = fill_tree_desc_strict(&two, hash2);
 	int score = 0;
-	enum object_type type;
-	unsigned long size;
 
-	one_buf = read_sha1_file(hash1, &type, &size);
-	if (!one_buf)
-		die("unable to read tree (%s)", sha1_to_hex(hash1));
-	if (type != OBJ_TREE)
-		die("%s is not a tree", sha1_to_hex(hash1));
-	init_tree_desc(&one, one_buf, size);
-	two_buf = read_sha1_file(hash2, &type, &size);
-	if (!two_buf)
-		die("unable to read tree (%s)", sha1_to_hex(hash2));
-	if (type != OBJ_TREE)
-		die("%s is not a tree", sha1_to_hex(hash2));
-	init_tree_desc(&two, two_buf, size);
 	for (;;) {
 		struct name_entry e1, e2;
 		int got_entry_from_one = tree_entry(&one, &e1);
@@ -124,16 +127,7 @@
 			int recurse_limit)
 {
 	struct tree_desc one;
-	void *one_buf;
-	enum object_type type;
-	unsigned long size;
-
-	one_buf = read_sha1_file(hash1, &type, &size);
-	if (!one_buf)
-		die("unable to read tree (%s)", sha1_to_hex(hash1));
-	if (type != OBJ_TREE)
-		die("%s is not a tree", sha1_to_hex(hash1));
-	init_tree_desc(&one, one_buf, size);
+	void *one_buf = fill_tree_desc_strict(&one, hash1);
 
 	while (one.size) {
 		const char *path;
diff --git a/merge-recursive.c b/merge-recursive.c
index ea9dbd3..f95933b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -251,7 +251,7 @@
 		int i;
 		fprintf(stderr, "BUG: There are unmerged index entries:\n");
 		for (i = 0; i < active_nr; i++) {
-			struct cache_entry *ce = active_cache[i];
+			const struct cache_entry *ce = active_cache[i];
 			if (ce_stage(ce))
 				fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
 					(int)ce_namelen(ce), ce->name);
@@ -264,7 +264,8 @@
 
 	if (!cache_tree_fully_valid(active_cache_tree) &&
 	    cache_tree_update(active_cache_tree,
-			      active_cache, active_nr, 0) < 0)
+			      (const struct cache_entry * const *)active_cache,
+			      active_nr, 0) < 0)
 		die(_("error building trees"));
 
 	result = lookup_tree(active_cache_tree->sha1);
@@ -339,7 +340,7 @@
 	for (i = 0; i < active_nr; i++) {
 		struct string_list_item *item;
 		struct stage_data *e;
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		if (!ce_stage(ce))
 			continue;
 
diff --git a/notes-merge.c b/notes-merge.c
index 0f67bd3..ab18857 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -9,6 +9,7 @@
 #include "notes.h"
 #include "notes-merge.h"
 #include "strbuf.h"
+#include "notes-utils.h"
 
 struct notes_merge_pair {
 	unsigned char obj[20], base[20], local[20], remote[20];
@@ -530,32 +531,6 @@
 	return conflicts ? -1 : 1;
 }
 
-void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-			 const struct strbuf *msg, unsigned char *result_sha1)
-{
-	unsigned char tree_sha1[20];
-
-	assert(t->initialized);
-
-	if (write_notes_tree(t, tree_sha1))
-		die("Failed to write notes tree to database");
-
-	if (!parents) {
-		/* Deduce parent commit from t->ref */
-		unsigned char parent_sha1[20];
-		if (!read_ref(t->ref, parent_sha1)) {
-			struct commit *parent = lookup_commit(parent_sha1);
-			if (!parent || parse_commit(parent))
-				die("Failed to find/parse commit %s", t->ref);
-			commit_list_insert(parent, &parents);
-		}
-		/* else: t->ref points to nothing, assume root/orphan commit */
-	}
-
-	if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
-		die("Failed to commit notes tree to database");
-}
-
 int notes_merge(struct notes_merge_options *o,
 		struct notes_tree *local_tree,
 		unsigned char *result_sha1)
diff --git a/notes-merge.h b/notes-merge.h
index 0c11b17..1d01f6a 100644
--- a/notes-merge.h
+++ b/notes-merge.h
@@ -26,20 +26,6 @@
 void init_notes_merge_options(struct notes_merge_options *o);
 
 /*
- * Create new notes commit from the given notes tree
- *
- * Properties of the created commit:
- * - tree: the result of converting t to a tree object with write_notes_tree().
- * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
- * - author/committer: the default determined by commmit_tree().
- * - commit message: msg
- *
- * The resulting commit SHA1 is stored in result_sha1.
- */
-void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-			 const struct strbuf *msg, unsigned char *result_sha1);
-
-/*
  * Merge notes from o->remote_ref into o->local_ref
  *
  * The given notes_tree 'local_tree' must be the notes_tree referenced by the
diff --git a/notes-utils.c b/notes-utils.c
new file mode 100644
index 0000000..9107c37
--- /dev/null
+++ b/notes-utils.c
@@ -0,0 +1,157 @@
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+#include "notes-utils.h"
+
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+			 const struct strbuf *msg, unsigned char *result_sha1)
+{
+	unsigned char tree_sha1[20];
+
+	assert(t->initialized);
+
+	if (write_notes_tree(t, tree_sha1))
+		die("Failed to write notes tree to database");
+
+	if (!parents) {
+		/* Deduce parent commit from t->ref */
+		unsigned char parent_sha1[20];
+		if (!read_ref(t->ref, parent_sha1)) {
+			struct commit *parent = lookup_commit(parent_sha1);
+			if (!parent || parse_commit(parent))
+				die("Failed to find/parse commit %s", t->ref);
+			commit_list_insert(parent, &parents);
+		}
+		/* else: t->ref points to nothing, assume root/orphan commit */
+	}
+
+	if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
+		die("Failed to commit notes tree to database");
+}
+
+void commit_notes(struct notes_tree *t, const char *msg)
+{
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char commit_sha1[20];
+
+	if (!t)
+		t = &default_notes_tree;
+	if (!t->initialized || !t->ref || !*t->ref)
+		die(_("Cannot commit uninitialized/unreferenced notes tree"));
+	if (!t->dirty)
+		return; /* don't have to commit an unchanged tree */
+
+	/* Prepare commit message and reflog message */
+	strbuf_addstr(&buf, msg);
+	if (buf.buf[buf.len - 1] != '\n')
+		strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
+
+	create_notes_commit(t, NULL, &buf, commit_sha1);
+	strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
+	update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
+
+	strbuf_release(&buf);
+}
+
+static combine_notes_fn parse_combine_notes_fn(const char *v)
+{
+	if (!strcasecmp(v, "overwrite"))
+		return combine_notes_overwrite;
+	else if (!strcasecmp(v, "ignore"))
+		return combine_notes_ignore;
+	else if (!strcasecmp(v, "concatenate"))
+		return combine_notes_concatenate;
+	else if (!strcasecmp(v, "cat_sort_uniq"))
+		return combine_notes_cat_sort_uniq;
+	else
+		return NULL;
+}
+
+static int notes_rewrite_config(const char *k, const char *v, void *cb)
+{
+	struct notes_rewrite_cfg *c = cb;
+	if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+		c->enabled = git_config_bool(k, v);
+		return 0;
+	} else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
+		if (!v)
+			config_error_nonbool(k);
+		c->combine = parse_combine_notes_fn(v);
+		if (!c->combine) {
+			error(_("Bad notes.rewriteMode value: '%s'"), v);
+			return 1;
+		}
+		return 0;
+	} else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+		/* note that a refs/ prefix is implied in the
+		 * underlying for_each_glob_ref */
+		if (!prefixcmp(v, "refs/notes/"))
+			string_list_add_refs_by_glob(c->refs, v);
+		else
+			warning(_("Refusing to rewrite notes in %s"
+				" (outside of refs/notes/)"), v);
+		return 0;
+	}
+
+	return 0;
+}
+
+
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
+{
+	struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
+	const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
+	const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
+	c->cmd = cmd;
+	c->enabled = 1;
+	c->combine = combine_notes_concatenate;
+	c->refs = xcalloc(1, sizeof(struct string_list));
+	c->refs->strdup_strings = 1;
+	c->refs_from_env = 0;
+	c->mode_from_env = 0;
+	if (rewrite_mode_env) {
+		c->mode_from_env = 1;
+		c->combine = parse_combine_notes_fn(rewrite_mode_env);
+		if (!c->combine)
+			/* TRANSLATORS: The first %s is the name of the
+			   environment variable, the second %s is its value */
+			error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
+					rewrite_mode_env);
+	}
+	if (rewrite_refs_env) {
+		c->refs_from_env = 1;
+		string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
+	}
+	git_config(notes_rewrite_config, c);
+	if (!c->enabled || !c->refs->nr) {
+		string_list_clear(c->refs, 0);
+		free(c->refs);
+		free(c);
+		return NULL;
+	}
+	c->trees = load_notes_trees(c->refs);
+	string_list_clear(c->refs, 0);
+	free(c->refs);
+	return c;
+}
+
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+			  const unsigned char *from_obj, const unsigned char *to_obj)
+{
+	int ret = 0;
+	int i;
+	for (i = 0; c->trees[i]; i++)
+		ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
+	return ret;
+}
+
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c, const char *msg)
+{
+	int i;
+	for (i = 0; c->trees[i]; i++) {
+		commit_notes(c->trees[i], msg);
+		free_notes(c->trees[i]);
+	}
+	free(c->trees);
+	free(c);
+}
diff --git a/notes-utils.h b/notes-utils.h
new file mode 100644
index 0000000..b4cb1bf
--- /dev/null
+++ b/notes-utils.h
@@ -0,0 +1,37 @@
+#ifndef NOTES_UTILS_H
+#define NOTES_UTILS_H
+
+#include "notes.h"
+
+/*
+ * Create new notes commit from the given notes tree
+ *
+ * Properties of the created commit:
+ * - tree: the result of converting t to a tree object with write_notes_tree().
+ * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
+ * - author/committer: the default determined by commmit_tree().
+ * - commit message: msg
+ *
+ * The resulting commit SHA1 is stored in result_sha1.
+ */
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+			 const struct strbuf *msg, unsigned char *result_sha1);
+
+void commit_notes(struct notes_tree *t, const char *msg);
+
+struct notes_rewrite_cfg {
+	struct notes_tree **trees;
+	const char *cmd;
+	int enabled;
+	combine_notes_fn combine;
+	struct string_list *refs;
+	int refs_from_env;
+	int mode_from_env;
+};
+
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+			  const unsigned char *from_obj, const unsigned char *to_obj);
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c, const char *msg);
+
+#endif
diff --git a/notes.c b/notes.c
index f63fd57..b69c0b8 100644
--- a/notes.c
+++ b/notes.c
@@ -918,17 +918,21 @@
 	return ret;
 }
 
-static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
+static int string_list_add_one_ref(const char *refname, const unsigned char *sha1,
 				   int flag, void *cb)
 {
 	struct string_list *refs = cb;
-	if (!unsorted_string_list_has_string(refs, path))
-		string_list_append(refs, path);
+	if (!unsorted_string_list_has_string(refs, refname))
+		string_list_append(refs, refname);
 	return 0;
 }
 
+/*
+ * The list argument must have strdup_strings set on it.
+ */
 void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
 {
+	assert(list->strdup_strings);
 	if (has_glob_specials(glob)) {
 		for_each_glob_ref(string_list_add_one_ref, glob, list);
 	} else {
diff --git a/object.c b/object.c
index 20703f5..d8a4b1f 100644
--- a/object.c
+++ b/object.c
@@ -71,13 +71,13 @@
 
 struct object *lookup_object(const unsigned char *sha1)
 {
-	unsigned int i;
+	unsigned int i, first;
 	struct object *obj;
 
 	if (!obj_hash)
 		return NULL;
 
-	i = hashtable_index(sha1);
+	first = i = hashtable_index(sha1);
 	while ((obj = obj_hash[i]) != NULL) {
 		if (!hashcmp(sha1, obj->sha1))
 			break;
@@ -85,6 +85,16 @@
 		if (i == obj_hash_size)
 			i = 0;
 	}
+	if (obj && i != first) {
+		/*
+		 * Move object to where we started to look for it so
+		 * that we do not need to walk the hash table the next
+		 * time we look for it.
+		 */
+		struct object *tmp = obj_hash[i];
+		obj_hash[i] = obj_hash[first];
+		obj_hash[first] = tmp;
+	}
 	return obj;
 }
 
@@ -135,7 +145,7 @@
 struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
 {
 	struct object *obj;
-	int eaten = 0;
+	*eaten_p = 0;
 
 	obj = NULL;
 	if (type == OBJ_BLOB) {
@@ -154,7 +164,7 @@
 			if (!tree->object.parsed) {
 				if (parse_tree_buffer(tree, buffer, size))
 					return NULL;
-				eaten = 1;
+				*eaten_p = 1;
 			}
 		}
 	} else if (type == OBJ_COMMIT) {
@@ -164,7 +174,7 @@
 				return NULL;
 			if (!commit->buffer) {
 				commit->buffer = buffer;
-				eaten = 1;
+				*eaten_p = 1;
 			}
 			obj = &commit->object;
 		}
@@ -181,7 +191,6 @@
 	}
 	if (obj && obj->type == OBJ_NONE)
 		obj->type = type;
-	*eaten_p = eaten;
 	return obj;
 }
 
@@ -260,11 +269,18 @@
 	add_object_array_with_mode(obj, name, array, S_IFINVALID);
 }
 
+/*
+ * A zero-length string to which object_array_entry::name can be
+ * initialized without requiring a malloc/free.
+ */
+static char object_array_slopbuf[1];
+
 void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode)
 {
 	unsigned nr = array->nr;
 	unsigned alloc = array->alloc;
 	struct object_array_entry *objects = array->objects;
+	struct object_array_entry *entry;
 
 	if (nr >= alloc) {
 		alloc = (alloc + 32) * 2;
@@ -272,28 +288,67 @@
 		array->alloc = alloc;
 		array->objects = objects;
 	}
-	objects[nr].item = obj;
-	objects[nr].name = name;
-	objects[nr].mode = mode;
+	entry = &objects[nr];
+	entry->item = obj;
+	if (!name)
+		entry->name = NULL;
+	else if (!*name)
+		/* Use our own empty string instead of allocating one: */
+		entry->name = object_array_slopbuf;
+	else
+		entry->name = xstrdup(name);
+	entry->mode = mode;
 	array->nr = ++nr;
 }
 
+void object_array_filter(struct object_array *array,
+			 object_array_each_func_t want, void *cb_data)
+{
+	unsigned nr = array->nr, src, dst;
+	struct object_array_entry *objects = array->objects;
+
+	for (src = dst = 0; src < nr; src++) {
+		if (want(&objects[src], cb_data)) {
+			if (src != dst)
+				objects[dst] = objects[src];
+			dst++;
+		} else {
+			if (objects[src].name != object_array_slopbuf)
+				free(objects[src].name);
+		}
+	}
+	array->nr = dst;
+}
+
+/*
+ * Return true iff array already contains an entry with name.
+ */
+static int contains_name(struct object_array *array, const char *name)
+{
+	unsigned nr = array->nr, i;
+	struct object_array_entry *object = array->objects;
+
+	for (i = 0; i < nr; i++, object++)
+		if (!strcmp(object->name, name))
+			return 1;
+	return 0;
+}
+
 void object_array_remove_duplicates(struct object_array *array)
 {
-	unsigned int ref, src, dst;
+	unsigned nr = array->nr, src;
 	struct object_array_entry *objects = array->objects;
 
-	for (ref = 0; ref + 1 < array->nr; 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 = 0;
+	for (src = 0; src < nr; src++) {
+		if (!contains_name(array, objects[src].name)) {
+			if (src != array->nr)
+				objects[array->nr] = objects[src];
+			array->nr++;
+		} else {
+			if (objects[src].name != object_array_slopbuf)
+				free(objects[src].name);
 		}
-		array->nr = dst;
 	}
 }
 
diff --git a/object.h b/object.h
index 97d384b..2ff68c5 100644
--- a/object.h
+++ b/object.h
@@ -11,7 +11,13 @@
 	unsigned int alloc;
 	struct object_array_entry {
 		struct object *item;
-		const char *name;
+		/*
+		 * name or NULL.  If non-NULL, the memory pointed to
+		 * is owned by this object *except* if it points at
+		 * object_array_slopbuf, which is a static copy of the
+		 * empty string.
+		 */
+		char *name;
 		unsigned mode;
 	} *objects;
 };
@@ -85,7 +91,22 @@
 /* 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 *);
+
+typedef int (*object_array_each_func_t)(struct object_array_entry *, void *);
+
+/*
+ * Apply want to each entry in array, retaining only the entries for
+ * which the function returns true.  Preserve the order of the entries
+ * that are retained.
+ */
+void object_array_filter(struct object_array *array,
+			 object_array_each_func_t want, void *cb_data);
+
+/*
+ * Remove from array all but the first entry with a given name.
+ * Warning: this function uses an O(N^2) algorithm.
+ */
+void object_array_remove_duplicates(struct object_array *array);
 
 void clear_object_flags(unsigned flags);
 
diff --git a/pack-refs.c b/pack-refs.c
deleted file mode 100644
index 4461f71..0000000
--- a/pack-refs.c
+++ /dev/null
@@ -1,148 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "tag.h"
-#include "pack-refs.h"
-
-struct ref_to_prune {
-	struct ref_to_prune *next;
-	unsigned char sha1[20];
-	char name[FLEX_ARRAY];
-};
-
-struct pack_refs_cb_data {
-	unsigned int flags;
-	struct ref_to_prune *ref_to_prune;
-	FILE *refs_file;
-};
-
-static int do_not_prune(int flags)
-{
-	/* If it is already packed or if it is a symref,
-	 * do not prune it.
-	 */
-	return (flags & (REF_ISSYMREF|REF_ISPACKED));
-}
-
-static int handle_one_ref(const char *path, const unsigned char *sha1,
-			  int flags, void *cb_data)
-{
-	struct pack_refs_cb_data *cb = cb_data;
-	struct object *o;
-	int is_tag_ref;
-
-	/* Do not pack the symbolic refs */
-	if ((flags & REF_ISSYMREF))
-		return 0;
-	is_tag_ref = !prefixcmp(path, "refs/tags/");
-
-	/* ALWAYS pack refs that were already packed or are tags */
-	if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED))
-		return 0;
-
-	fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
-
-	o = parse_object_or_die(sha1, path);
-	if (o->type == OBJ_TAG) {
-		o = deref_tag(o, path, 0);
-		if (o)
-			fprintf(cb->refs_file, "^%s\n",
-				sha1_to_hex(o->sha1));
-	}
-
-	if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) {
-		int namelen = strlen(path) + 1;
-		struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
-		hashcpy(n->sha1, sha1);
-		strcpy(n->name, path);
-		n->next = cb->ref_to_prune;
-		cb->ref_to_prune = n;
-	}
-	return 0;
-}
-
-/*
- * Remove empty parents, but spare refs/ and immediate subdirs.
- * Note: munges *name.
- */
-static void try_remove_empty_parents(char *name)
-{
-	char *p, *q;
-	int i;
-	p = name;
-	for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
-		while (*p && *p != '/')
-			p++;
-		/* tolerate duplicate slashes; see check_refname_format() */
-		while (*p == '/')
-			p++;
-	}
-	for (q = p; *q; q++)
-		;
-	while (1) {
-		while (q > p && *q != '/')
-			q--;
-		while (q > p && *(q-1) == '/')
-			q--;
-		if (q == p)
-			break;
-		*q = '\0';
-		if (rmdir(git_path("%s", name)))
-			break;
-	}
-}
-
-/* make sure nobody touched the ref, and unlink */
-static void prune_ref(struct ref_to_prune *r)
-{
-	struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
-
-	if (lock) {
-		unlink_or_warn(git_path("%s", r->name));
-		unlock_ref(lock);
-		try_remove_empty_parents(r->name);
-	}
-}
-
-static void prune_refs(struct ref_to_prune *r)
-{
-	while (r) {
-		prune_ref(r);
-		r = r->next;
-	}
-}
-
-static struct lock_file packed;
-
-int pack_refs(unsigned int flags)
-{
-	int fd;
-	struct pack_refs_cb_data cbdata;
-
-	memset(&cbdata, 0, sizeof(cbdata));
-	cbdata.flags = flags;
-
-	fd = hold_lock_file_for_update(&packed, git_path("packed-refs"),
-				       LOCK_DIE_ON_ERROR);
-	cbdata.refs_file = fdopen(fd, "w");
-	if (!cbdata.refs_file)
-		die_errno("unable to create ref-pack file structure");
-
-	/* perhaps other traits later as well */
-	fprintf(cbdata.refs_file, "# pack-refs with: peeled fully-peeled \n");
-
-	for_each_ref(handle_one_ref, &cbdata);
-	if (ferror(cbdata.refs_file))
-		die("failed to write ref-pack file");
-	if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file))
-		die_errno("failed to write ref-pack file");
-	/*
-	 * Since the lock file was fdopen()'ed and then fclose()'ed above,
-	 * assign -1 to the lock file descriptor so that commit_lock_file()
-	 * won't try to close() it.
-	 */
-	packed.fd = -1;
-	if (commit_lock_file(&packed) < 0)
-		die_errno("unable to overwrite old ref-pack file");
-	prune_refs(cbdata.ref_to_prune);
-	return 0;
-}
diff --git a/pack-refs.h b/pack-refs.h
deleted file mode 100644
index 518acfb..0000000
--- a/pack-refs.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef PACK_REFS_H
-#define PACK_REFS_H
-
-/*
- * Flags for controlling behaviour of pack_refs()
- * PACK_REFS_PRUNE: Prune loose refs after packing
- * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
- */
-#define PACK_REFS_PRUNE 0x0001
-#define PACK_REFS_ALL   0x0002
-
-/*
- * Write a packed-refs file for the current repository.
- * flags: Combination of the above PACK_REFS_* flags.
- */
-int pack_refs(unsigned int flags);
-
-#endif /* PACK_REFS_H */
diff --git a/pack-revindex.c b/pack-revindex.c
index 77a0465..b4d2b35 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -59,11 +59,101 @@
 	/* revindex elements are lazily initialized */
 }
 
-static int cmp_offset(const void *a_, const void *b_)
+/*
+ * This is a least-significant-digit radix sort.
+ *
+ * It sorts each of the "n" items in "entries" by its offset field. The "max"
+ * parameter must be at least as large as the largest offset in the array,
+ * and lets us quit the sort early.
+ */
+static void sort_revindex(struct revindex_entry *entries, unsigned n, off_t max)
 {
-	const struct revindex_entry *a = a_;
-	const struct revindex_entry *b = b_;
-	return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
+	/*
+	 * We use a "digit" size of 16 bits. That keeps our memory
+	 * usage reasonable, and we can generally (for a 4G or smaller
+	 * packfile) quit after two rounds of radix-sorting.
+	 */
+#define DIGIT_SIZE (16)
+#define BUCKETS (1 << DIGIT_SIZE)
+	/*
+	 * We want to know the bucket that a[i] will go into when we are using
+	 * the digit that is N bits from the (least significant) end.
+	 */
+#define BUCKET_FOR(a, i, bits) (((a)[(i)].offset >> (bits)) & (BUCKETS-1))
+
+	/*
+	 * We need O(n) temporary storage. Rather than do an extra copy of the
+	 * partial results into "entries", we sort back and forth between the
+	 * real array and temporary storage. In each iteration of the loop, we
+	 * keep track of them with alias pointers, always sorting from "from"
+	 * to "to".
+	 */
+	struct revindex_entry *tmp = xmalloc(n * sizeof(*tmp));
+	struct revindex_entry *from = entries, *to = tmp;
+	int bits;
+	unsigned *pos = xmalloc(BUCKETS * sizeof(*pos));
+
+	/*
+	 * If (max >> bits) is zero, then we know that the radix digit we are
+	 * on (and any higher) will be zero for all entries, and our loop will
+	 * be a no-op, as everybody lands in the same zero-th bucket.
+	 */
+	for (bits = 0; max >> bits; bits += DIGIT_SIZE) {
+		struct revindex_entry *swap;
+		unsigned i;
+
+		memset(pos, 0, BUCKETS * sizeof(*pos));
+
+		/*
+		 * We want pos[i] to store the index of the last element that
+		 * will go in bucket "i" (actually one past the last element).
+		 * To do this, we first count the items that will go in each
+		 * bucket, which gives us a relative offset from the last
+		 * bucket. We can then cumulatively add the index from the
+		 * previous bucket to get the true index.
+		 */
+		for (i = 0; i < n; i++)
+			pos[BUCKET_FOR(from, i, bits)]++;
+		for (i = 1; i < BUCKETS; i++)
+			pos[i] += pos[i-1];
+
+		/*
+		 * Now we can drop the elements into their correct buckets (in
+		 * our temporary array).  We iterate the pos counter backwards
+		 * to avoid using an extra index to count up. And since we are
+		 * going backwards there, we must also go backwards through the
+		 * array itself, to keep the sort stable.
+		 *
+		 * Note that we use an unsigned iterator to make sure we can
+		 * handle 2^32-1 objects, even on a 32-bit system. But this
+		 * means we cannot use the more obvious "i >= 0" loop condition
+		 * for counting backwards, and must instead check for
+		 * wrap-around with UINT_MAX.
+		 */
+		for (i = n - 1; i != UINT_MAX; i--)
+			to[--pos[BUCKET_FOR(from, i, bits)]] = from[i];
+
+		/*
+		 * Now "to" contains the most sorted list, so we swap "from" and
+		 * "to" for the next iteration.
+		 */
+		swap = from;
+		from = to;
+		to = swap;
+	}
+
+	/*
+	 * If we ended with our data in the original array, great. If not,
+	 * we have to move it back from the temporary storage.
+	 */
+	if (from != entries)
+		memcpy(entries, tmp, n * sizeof(*entries));
+	free(tmp);
+	free(pos);
+
+#undef BUCKET_FOR
+#undef BUCKETS
+#undef DIGIT_SIZE
 }
 
 /*
@@ -72,8 +162,8 @@
 static void create_pack_revindex(struct pack_revindex *rix)
 {
 	struct packed_git *p = rix->p;
-	int num_ent = p->num_objects;
-	int i;
+	unsigned num_ent = p->num_objects;
+	unsigned i;
 	const char *index = p->index_data;
 
 	rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
@@ -108,13 +198,13 @@
 	 */
 	rix->revindex[num_ent].offset = p->pack_size - 20;
 	rix->revindex[num_ent].nr = -1;
-	qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
+	sort_revindex(rix->revindex, num_ent, p->pack_size);
 }
 
 struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
 {
 	int num;
-	int lo, hi;
+	unsigned lo, hi;
 	struct pack_revindex *rix;
 	struct revindex_entry *revindex;
 
@@ -132,7 +222,7 @@
 	lo = 0;
 	hi = p->num_objects + 1;
 	do {
-		int mi = (lo + hi) / 2;
+		unsigned mi = lo + (hi - lo) / 2;
 		if (revindex[mi].offset == ofs) {
 			return revindex + mi;
 		} else if (ofs < revindex[mi].offset)
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 0de5fb1..be8c413 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -33,6 +33,12 @@
 	return 0;
 }
 
+int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
+			     int unset)
+{
+	return parse_expiry_date(arg, (unsigned long *)opt->value);
+}
+
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
 			    int unset)
 {
diff --git a/parse-options.h b/parse-options.h
index 1c8bd8d..c378b75 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -140,6 +140,9 @@
 #define OPT_DATE(s, l, v, h) \
 	{ OPTION_CALLBACK, (s), (l), (v), N_("time"),(h), 0,	\
 	  parse_opt_approxidate_cb }
+#define OPT_EXPIRY_DATE(s, l, v, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), N_("expiry date"),(h), 0,	\
+	  parse_opt_expiry_date_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
 	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
 #define OPT_NUMBER_CALLBACK(v, h, f) \
@@ -219,6 +222,7 @@
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);
 extern int parse_opt_color_flag_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);
diff --git a/path.c b/path.c
index 04ff148..7f3324a 100644
--- a/path.c
+++ b/path.c
@@ -441,42 +441,100 @@
 	return 0;
 }
 
-const char *relative_path(const char *abs, const char *base)
+/*
+ * Give path as relative to prefix.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+const char *relative_path(const char *in, const char *prefix,
+			  struct strbuf *sb)
 {
-	static char buf[PATH_MAX + 1];
+	int in_len = in ? strlen(in) : 0;
+	int prefix_len = prefix ? strlen(prefix) : 0;
+	int in_off = 0;
+	int prefix_off = 0;
 	int i = 0, j = 0;
 
-	if (!base || !base[0])
-		return abs;
-	while (base[i]) {
-		if (is_dir_sep(base[i])) {
-			if (!is_dir_sep(abs[j]))
-				return abs;
-			while (is_dir_sep(base[i]))
+	if (!in_len)
+		return "./";
+	else if (!prefix_len)
+		return in;
+
+	while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
+		if (is_dir_sep(prefix[i])) {
+			while (is_dir_sep(prefix[i]))
 				i++;
-			while (is_dir_sep(abs[j]))
+			while (is_dir_sep(in[j]))
 				j++;
+			prefix_off = i;
+			in_off = j;
+		} else {
+			i++;
+			j++;
+		}
+	}
+
+	if (
+	    /* "prefix" seems like prefix of "in" */
+	    i >= prefix_len &&
+	    /*
+	     * but "/foo" is not a prefix of "/foobar"
+	     * (i.e. prefix not end with '/')
+	     */
+	    prefix_off < prefix_len) {
+		if (j >= in_len) {
+			/* in="/a/b", prefix="/a/b" */
+			in_off = in_len;
+		} else if (is_dir_sep(in[j])) {
+			/* in="/a/b/c", prefix="/a/b" */
+			while (is_dir_sep(in[j]))
+				j++;
+			in_off = j;
+		} else {
+			/* in="/a/bbb/c", prefix="/a/b" */
+			i = prefix_off;
+		}
+	} else if (
+		   /* "in" is short than "prefix" */
+		   j >= in_len &&
+		   /* "in" not end with '/' */
+		   in_off < in_len) {
+		if (is_dir_sep(prefix[i])) {
+			/* in="/a/b", prefix="/a/b/c/" */
+			while (is_dir_sep(prefix[i]))
+				i++;
+			in_off = in_len;
+		}
+	}
+	in += in_off;
+	in_len -= in_off;
+
+	if (i >= prefix_len) {
+		if (!in_len)
+			return "./";
+		else
+			return in;
+	}
+
+	strbuf_reset(sb);
+	strbuf_grow(sb, in_len);
+
+	while (i < prefix_len) {
+		if (is_dir_sep(prefix[i])) {
+			strbuf_addstr(sb, "../");
+			while (is_dir_sep(prefix[i]))
+				i++;
 			continue;
-		} else if (abs[j] != base[i]) {
-			return abs;
 		}
 		i++;
-		j++;
 	}
-	if (
-	    /* "/foo" is a prefix of "/foo" */
-	    abs[j] &&
-	    /* "/foo" is not a prefix of "/foobar" */
-	    !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
-	   )
-		return abs;
-	while (is_dir_sep(abs[j]))
-		j++;
-	if (!abs[j])
-		strcpy(buf, ".");
-	else
-		strcpy(buf, abs + j);
-	return buf;
+	if (!is_dir_sep(prefix[prefix_len - 1]))
+		strbuf_addstr(sb, "../");
+
+	strbuf_addstr(sb, in);
+
+	return sb->buf;
 }
 
 /*
diff --git a/pathspec.c b/pathspec.c
index 284f397..6ea0867 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -32,7 +32,7 @@
 	if (!num_unmatched)
 		return;
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
 	}
 }
@@ -67,7 +67,7 @@
 {
 	int i, path_len = strlen(path);
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		if (S_ISGITLINK(ce->ce_mode)) {
 			int ce_len = ce_namelen(ce);
 			if (path_len <= ce_len || path[ce_len] != '/' ||
diff --git a/pretty.c b/pretty.c
index 9e43154..74563c9 100644
--- a/pretty.c
+++ b/pretty.c
@@ -406,7 +406,7 @@
 	return show_date(date, tz, mode);
 }
 
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
 		  const char *what, struct strbuf *sb,
 		  const char *line, const char *encoding)
 {
@@ -432,6 +432,23 @@
 		map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
 
 	if (pp->fmt == CMIT_FMT_EMAIL) {
+		if (pp->from_ident) {
+			struct strbuf buf = STRBUF_INIT;
+
+			strbuf_addstr(&buf, "From: ");
+			strbuf_add(&buf, namebuf, namelen);
+			strbuf_addstr(&buf, " <");
+			strbuf_add(&buf, mailbuf, maillen);
+			strbuf_addstr(&buf, ">\n");
+			string_list_append(&pp->in_body_headers,
+					   strbuf_detach(&buf, NULL));
+
+			mailbuf = pp->from_ident->mail_begin;
+			maillen = pp->from_ident->mail_end - mailbuf;
+			namebuf = pp->from_ident->name_begin;
+			namelen = pp->from_ident->name_end - namebuf;
+		}
+
 		strbuf_addstr(sb, "From: ");
 		if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
 			add_rfc2047(sb, namebuf, namelen,
@@ -1514,7 +1531,7 @@
 	free(context.signature_check.signer);
 }
 
-static void pp_header(const struct pretty_print_context *pp,
+static void pp_header(struct pretty_print_context *pp,
 		      const char *encoding,
 		      const struct commit *commit,
 		      const char **msg_p,
@@ -1575,7 +1592,7 @@
 	}
 }
 
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
 		   const char **msg_p,
 		   struct strbuf *sb,
 		   const char *encoding,
@@ -1602,6 +1619,16 @@
 	}
 	strbuf_addch(sb, '\n');
 
+	if (need_8bit_cte == 0) {
+		int i;
+		for (i = 0; i < pp->in_body_headers.nr; i++) {
+			if (has_non_ascii(pp->in_body_headers.items[i].string)) {
+				need_8bit_cte = 1;
+				break;
+			}
+		}
+	}
+
 	if (need_8bit_cte > 0) {
 		const char *header_fmt =
 			"MIME-Version: 1.0\n"
@@ -1615,10 +1642,21 @@
 	if (pp->fmt == CMIT_FMT_EMAIL) {
 		strbuf_addch(sb, '\n');
 	}
+
+	if (pp->in_body_headers.nr) {
+		int i;
+		for (i = 0; i < pp->in_body_headers.nr; i++) {
+			strbuf_addstr(sb, pp->in_body_headers.items[i].string);
+			free(pp->in_body_headers.items[i].string);
+		}
+		string_list_clear(&pp->in_body_headers, 0);
+		strbuf_addch(sb, '\n');
+	}
+
 	strbuf_release(&title);
 }
 
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
 		  const char **msg_p,
 		  struct strbuf *sb,
 		  int indent)
@@ -1650,7 +1688,7 @@
 	}
 }
 
-void pretty_print_commit(const struct pretty_print_context *pp,
+void pretty_print_commit(struct pretty_print_context *pp,
 			 const struct commit *commit,
 			 struct strbuf *sb)
 {
diff --git a/prio-queue.c b/prio-queue.c
new file mode 100644
index 0000000..c9f8c6d
--- /dev/null
+++ b/prio-queue.c
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "commit.h"
+#include "prio-queue.h"
+
+void prio_queue_reverse(struct prio_queue *queue)
+{
+	int i, j;
+
+	if (queue->compare != NULL)
+		die("BUG: prio_queue_reverse() on non-LIFO queue");
+	for (i = 0; i <= (j = (queue->nr - 1) - i); i++) {
+		struct commit *swap = queue->array[i];
+		queue->array[i] = queue->array[j];
+		queue->array[j] = swap;
+	}
+}
+
+void clear_prio_queue(struct prio_queue *queue)
+{
+	free(queue->array);
+	queue->nr = 0;
+	queue->alloc = 0;
+	queue->array = NULL;
+}
+
+void prio_queue_put(struct prio_queue *queue, void *thing)
+{
+	prio_queue_compare_fn compare = queue->compare;
+	int ix, parent;
+
+	/* Append at the end */
+	ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
+	queue->array[queue->nr++] = thing;
+	if (!compare)
+		return; /* LIFO */
+
+	/* Bubble up the new one */
+	for (ix = queue->nr - 1; ix; ix = parent) {
+		parent = (ix - 1) / 2;
+		if (compare(queue->array[parent], queue->array[ix],
+			    queue->cb_data) <= 0)
+			break;
+
+		thing = queue->array[parent];
+		queue->array[parent] = queue->array[ix];
+		queue->array[ix] = thing;
+	}
+}
+
+void *prio_queue_get(struct prio_queue *queue)
+{
+	void *result, *swap;
+	int ix, child;
+	prio_queue_compare_fn compare = queue->compare;
+
+	if (!queue->nr)
+		return NULL;
+	if (!compare)
+		return queue->array[--queue->nr]; /* LIFO */
+
+	result = queue->array[0];
+	if (!--queue->nr)
+		return result;
+
+	queue->array[0] = queue->array[queue->nr];
+
+	/* Push down the one at the root */
+	for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
+		child = ix * 2 + 1; /* left */
+		if ((child + 1 < queue->nr) &&
+		    (compare(queue->array[child], queue->array[child + 1],
+			     queue->cb_data) >= 0))
+			child++; /* use right child */
+
+		if (compare(queue->array[ix], queue->array[child],
+			    queue->cb_data) <= 0)
+			break;
+
+		swap = queue->array[child];
+		queue->array[child] = queue->array[ix];
+		queue->array[ix] = swap;
+	}
+	return result;
+}
diff --git a/prio-queue.h b/prio-queue.h
new file mode 100644
index 0000000..9c3cd1f
--- /dev/null
+++ b/prio-queue.h
@@ -0,0 +1,48 @@
+#ifndef PRIO_QUEUE_H
+#define PRIO_QUEUE_H
+
+/*
+ * A priority queue implementation, primarily for keeping track of
+ * commits in the 'date-order' so that we process them from new to old
+ * as they are discovered, but can be used to hold any pointer to
+ * struct.  The caller is responsible for supplying a function to
+ * compare two "things".
+ *
+ * Alternatively, this data structure can also be used as a LIFO stack
+ * by specifying NULL as the comparison function.
+ */
+
+/*
+ * Compare two "things", one and two; the third parameter is cb_data
+ * in the prio_queue structure.  The result is returned as a sign of
+ * the return value, being the same as the sign of the result of
+ * subtracting "two" from "one" (i.e. negative if "one" sorts earlier
+ * than "two").
+ */
+typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
+
+struct prio_queue {
+	prio_queue_compare_fn compare;
+	void *cb_data;
+	int alloc, nr;
+	void **array;
+};
+
+/*
+ * Add the "thing" to the queue.
+ */
+extern void prio_queue_put(struct prio_queue *, void *thing);
+
+/*
+ * Extract the "thing" that compares the smallest out of the queue,
+ * or NULL.  If compare function is NULL, the queue acts as a LIFO
+ * stack.
+ */
+extern void *prio_queue_get(struct prio_queue *);
+
+extern void clear_prio_queue(struct prio_queue *);
+
+/* Reverse the LIFO elements */
+extern void prio_queue_reverse(struct prio_queue *);
+
+#endif /* PRIO_QUEUE_H */
diff --git a/quote.c b/quote.c
index 911229f..5c88081 100644
--- a/quote.c
+++ b/quote.c
@@ -312,82 +312,27 @@
 	fputc(terminator, fp);
 }
 
-static const char *path_relative(const char *in, int len,
-				 struct strbuf *sb, const char *prefix,
-				 int prefix_len);
-
-void write_name_quoted_relative(const char *name, size_t len,
-				const char *prefix, size_t prefix_len,
+void write_name_quoted_relative(const char *name, const char *prefix,
 				FILE *fp, int terminator)
 {
 	struct strbuf sb = STRBUF_INIT;
 
-	name = path_relative(name, len, &sb, prefix, prefix_len);
+	name = relative_path(name, prefix, &sb);
 	write_name_quoted(name, fp, terminator);
 
 	strbuf_release(&sb);
 }
 
-/*
- * Give path as relative to prefix.
- *
- * The strbuf may or may not be used, so do not assume it contains the
- * returned path.
- */
-static const char *path_relative(const char *in, int len,
-				 struct strbuf *sb, const char *prefix,
-				 int prefix_len)
-{
-	int off, i;
-
-	if (len < 0)
-		len = strlen(in);
-	if (prefix_len < 0) {
-		if (prefix)
-			prefix_len = strlen(prefix);
-		else
-			prefix_len = 0;
-	}
-
-	off = 0;
-	i = 0;
-	while (i < prefix_len && i < len && prefix[i] == in[i]) {
-		if (prefix[i] == '/')
-			off = i + 1;
-		i++;
-	}
-	in += off;
-	len -= off;
-
-	if (i >= prefix_len)
-		return in;
-
-	strbuf_reset(sb);
-	strbuf_grow(sb, len);
-
-	while (i < prefix_len) {
-		if (prefix[i] == '/')
-			strbuf_addstr(sb, "../");
-		i++;
-	}
-	strbuf_add(sb, in, len);
-
-	return sb->buf;
-}
-
 /* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-			  struct strbuf *out, const char *prefix)
+char *quote_path_relative(const char *in, const char *prefix,
+			  struct strbuf *out)
 {
 	struct strbuf sb = STRBUF_INIT;
-	const char *rel = path_relative(in, len, &sb, prefix, -1);
+	const char *rel = relative_path(in, prefix, &sb);
 	strbuf_reset(out);
 	quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
 	strbuf_release(&sb);
 
-	if (!out->len)
-		strbuf_addstr(out, "./");
-
 	return out->buf;
 }
 
diff --git a/quote.h b/quote.h
index 133155a..ed110a5 100644
--- a/quote.h
+++ b/quote.h
@@ -60,13 +60,12 @@
 extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
-extern void write_name_quoted_relative(const char *name, size_t len,
-		const char *prefix, size_t prefix_len,
+extern void write_name_quoted_relative(const char *name, const char *prefix,
 		FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-extern char *quote_path_relative(const char *in, int len,
-			  struct strbuf *out, const char *prefix);
+extern char *quote_path_relative(const char *in, const char *prefix,
+			  struct strbuf *out);
 
 /* quoting as a string literal for other languages */
 extern void perl_quote_print(FILE *stream, const char *src);
diff --git a/read-cache.c b/read-cache.c
index 04ed561..d4474ea 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -67,6 +67,61 @@
 	add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+	sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+	sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+	sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+	sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+	sd->sd_dev = st->st_dev;
+	sd->sd_ino = st->st_ino;
+	sd->sd_uid = st->st_uid;
+	sd->sd_gid = st->st_gid;
+	sd->sd_size = st->st_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+	int changed = 0;
+
+	if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+		changed |= MTIME_CHANGED;
+	if (trust_ctime && check_stat &&
+	    sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+		changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+	if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+		changed |= MTIME_CHANGED;
+	if (trust_ctime && check_stat &&
+	    sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+		changed |= CTIME_CHANGED;
+#endif
+
+	if (check_stat) {
+		if (sd->sd_uid != (unsigned int) st->st_uid ||
+			sd->sd_gid != (unsigned int) st->st_gid)
+			changed |= OWNER_CHANGED;
+		if (sd->sd_ino != (unsigned int) st->st_ino)
+			changed |= INODE_CHANGED;
+	}
+
+#ifdef USE_STDEV
+	/*
+	 * st_dev breaks on network filesystems where different
+	 * clients will have different views of what "device"
+	 * the filesystem is on
+	 */
+	if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+			changed |= INODE_CHANGED;
+#endif
+
+	if (sd->sd_size != (unsigned int) st->st_size)
+		changed |= DATA_CHANGED;
+
+	return changed;
+}
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -74,15 +129,7 @@
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-	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;
-	ce->ce_gid = st->st_gid;
-	ce->ce_size = st->st_size;
+	fill_stat_data(&ce->ce_stat_data, st);
 
 	if (assume_unchanged)
 		ce->ce_flags |= CE_VALID;
@@ -91,7 +138,7 @@
 		ce_mark_uptodate(ce);
 }
 
-static int ce_compare_data(struct cache_entry *ce, struct stat *st)
+static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
 {
 	int match = -1;
 	int fd = open(ce->name, O_RDONLY);
@@ -105,7 +152,7 @@
 	return match;
 }
 
-static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
+static int ce_compare_link(const struct cache_entry *ce, size_t expected_size)
 {
 	int match = -1;
 	void *buffer;
@@ -126,7 +173,7 @@
 	return match;
 }
 
-static int ce_compare_gitlink(struct cache_entry *ce)
+static int ce_compare_gitlink(const struct cache_entry *ce)
 {
 	unsigned char sha1[20];
 
@@ -143,7 +190,7 @@
 	return hashcmp(sha1, ce->sha1);
 }
 
-static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
+static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
 {
 	switch (st->st_mode & S_IFMT) {
 	case S_IFREG:
@@ -163,7 +210,7 @@
 	return 0;
 }
 
-static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
+static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
 {
 	unsigned int changed = 0;
 
@@ -195,43 +242,11 @@
 	default:
 		die("internal error: ce_mode is %o", ce->ce_mode);
 	}
-	if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
-		changed |= MTIME_CHANGED;
-	if (trust_ctime && check_stat &&
-	    ce->ce_ctime.sec != (unsigned int)st->st_ctime)
-		changed |= CTIME_CHANGED;
 
-#ifdef USE_NSEC
-	if (check_stat && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
-		changed |= MTIME_CHANGED;
-	if (trust_ctime && check_stat &&
-	    ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
-		changed |= CTIME_CHANGED;
-#endif
-
-	if (check_stat) {
-		if (ce->ce_uid != (unsigned int) st->st_uid ||
-			ce->ce_gid != (unsigned int) st->st_gid)
-			changed |= OWNER_CHANGED;
-		if (ce->ce_ino != (unsigned int) st->st_ino)
-			changed |= INODE_CHANGED;
-	}
-
-#ifdef USE_STDEV
-	/*
-	 * st_dev breaks on network filesystems where different
-	 * clients will have different views of what "device"
-	 * the filesystem is on
-	 */
-	if (check_stat && ce->ce_dev != (unsigned int) st->st_dev)
-			changed |= INODE_CHANGED;
-#endif
-
-	if (ce->ce_size != (unsigned int) st->st_size)
-		changed |= DATA_CHANGED;
+	changed |= match_stat_data(&ce->ce_stat_data, st);
 
 	/* Racily smudged entry? */
-	if (!ce->ce_size) {
+	if (!ce->ce_stat_data.sd_size) {
 		if (!is_empty_blob_sha1(ce->sha1))
 			changed |= DATA_CHANGED;
 	}
@@ -239,23 +254,24 @@
 	return changed;
 }
 
-static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
+static int is_racy_timestamp(const struct index_state *istate,
+			     const struct cache_entry *ce)
 {
 	return (!S_ISGITLINK(ce->ce_mode) &&
 		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))
+		(istate->timestamp.sec < ce->ce_stat_data.sd_mtime.sec ||
+		 (istate->timestamp.sec == ce->ce_stat_data.sd_mtime.sec &&
+		  istate->timestamp.nsec <= ce->ce_stat_data.sd_mtime.nsec))
 #else
-		istate->timestamp.sec <= ce->ce_mtime.sec
+		istate->timestamp.sec <= ce->ce_stat_data.sd_mtime.sec
 #endif
 		 );
 }
 
 int ie_match_stat(const struct index_state *istate,
-		  struct cache_entry *ce, struct stat *st,
+		  const struct cache_entry *ce, struct stat *st,
 		  unsigned int options)
 {
 	unsigned int changed;
@@ -311,7 +327,8 @@
 }
 
 int ie_modified(const struct index_state *istate,
-		struct cache_entry *ce, struct stat *st, unsigned int options)
+		const struct cache_entry *ce,
+		struct stat *st, unsigned int options)
 {
 	int changed, changed_fs;
 
@@ -340,7 +357,7 @@
 	 * then we know it is.
 	 */
 	if ((changed & DATA_CHANGED) &&
-	    (S_ISGITLINK(ce->ce_mode) || ce->ce_size != 0))
+	    (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
 		return changed;
 
 	changed_fs = ce_modified_check_fs(ce, st);
@@ -626,7 +643,7 @@
 			if (*ptr == '/') {
 				struct cache_entry *foundce;
 				++ptr;
-				foundce = index_name_exists(&the_index, ce->name, ptr - ce->name, ignore_case);
+				foundce = index_name_exists(istate, ce->name, ptr - ce->name, ignore_case);
 				if (foundce) {
 					memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
 					startPtr = ptr;
@@ -705,7 +722,7 @@
 	return ce;
 }
 
-int ce_same_name(struct cache_entry *a, struct cache_entry *b)
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
 {
 	int len = ce_namelen(a);
 	return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
@@ -979,7 +996,7 @@
 	if (istate->cache_nr == istate->cache_alloc) {
 		istate->cache_alloc = alloc_nr(istate->cache_alloc);
 		istate->cache = xrealloc(istate->cache,
-					istate->cache_alloc * sizeof(struct cache_entry *));
+					istate->cache_alloc * sizeof(*istate->cache));
 	}
 
 	/* Add it in.. */
@@ -1322,16 +1339,16 @@
 {
 	struct cache_entry *ce = xmalloc(cache_entry_size(len));
 
-	ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
-	ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
-	ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
-	ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
-	ce->ce_dev   = ntoh_l(ondisk->dev);
-	ce->ce_ino   = ntoh_l(ondisk->ino);
+	ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
+	ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
+	ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
+	ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
+	ce->ce_stat_data.sd_dev   = ntoh_l(ondisk->dev);
+	ce->ce_stat_data.sd_ino   = ntoh_l(ondisk->ino);
 	ce->ce_mode  = ntoh_l(ondisk->mode);
-	ce->ce_uid   = ntoh_l(ondisk->uid);
-	ce->ce_gid   = ntoh_l(ondisk->gid);
-	ce->ce_size  = ntoh_l(ondisk->size);
+	ce->ce_stat_data.sd_uid   = ntoh_l(ondisk->uid);
+	ce->ce_stat_data.sd_gid   = ntoh_l(ondisk->gid);
+	ce->ce_stat_data.sd_size  = ntoh_l(ondisk->size);
 	ce->ce_flags = flags & ~CE_NAMEMASK;
 	ce->ce_namelen = len;
 	hashcpy(ce->sha1, ondisk->sha1);
@@ -1449,7 +1466,7 @@
 	istate->version = ntohl(hdr->hdr_version);
 	istate->cache_nr = ntohl(hdr->hdr_entries);
 	istate->cache_alloc = alloc_nr(istate->cache_nr);
-	istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
+	istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache));
 	istate->initialized = 1;
 
 	if (istate->version == 4)
@@ -1518,8 +1535,9 @@
 	free_name_hash(istate);
 	cache_tree_free(&(istate->cache_tree));
 	istate->initialized = 0;
-
-	/* no need to throw away allocated active_cache */
+	free(istate->cache);
+	istate->cache = NULL;
+	istate->cache_alloc = 0;
 	return 0;
 }
 
@@ -1608,7 +1626,7 @@
 	 * The only thing we care about in this function is to smudge the
 	 * falsely clean entry due to touch-update-touch race, so we leave
 	 * everything else as they are.  We are called for entries whose
-	 * ce_mtime match the index file mtime.
+	 * ce_stat_data.sd_mtime match the index file mtime.
 	 *
 	 * Note that this actually does not do much for gitlinks, for
 	 * which ce_match_stat_basic() always goes to the actual
@@ -1647,7 +1665,7 @@
 		 * file, and never calls us, so the cached size information
 		 * for "frotz" stays 6 which does not match the filesystem.
 		 */
-		ce->ce_size = 0;
+		ce->ce_stat_data.sd_size = 0;
 	}
 }
 
@@ -1657,16 +1675,16 @@
 {
 	short flags;
 
-	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->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
+	ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
+	ondisk->ctime.nsec = htonl(ce->ce_stat_data.sd_ctime.nsec);
+	ondisk->mtime.nsec = htonl(ce->ce_stat_data.sd_mtime.nsec);
+	ondisk->dev  = htonl(ce->ce_stat_data.sd_dev);
+	ondisk->ino  = htonl(ce->ce_stat_data.sd_ino);
 	ondisk->mode = htonl(ce->ce_mode);
-	ondisk->uid  = htonl(ce->ce_uid);
-	ondisk->gid  = htonl(ce->ce_gid);
-	ondisk->size = htonl(ce->ce_size);
+	ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
+	ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
+	ondisk->size = htonl(ce->ce_stat_data.sd_size);
 	hashcpy(ondisk->sha1, ce->sha1);
 
 	flags = ce->ce_flags;
@@ -1933,3 +1951,33 @@
 		*size = sz;
 	return data;
 }
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+	free(sv->sd);
+	sv->sd = NULL;
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+	struct stat st;
+
+	if (stat(path, &st) < 0)
+		return sv->sd == NULL;
+	if (!sv->sd)
+		return 0;
+	return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+	struct stat st;
+
+	if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+		stat_validity_clear(sv);
+	else {
+		if (!sv->sd)
+			sv->sd = xcalloc(1, sizeof(struct stat_data));
+		fill_stat_data(sv->sd, &st);
+	}
+}
diff --git a/refs.c b/refs.c
index de2d8eb..a3f2302 100644
--- a/refs.c
+++ b/refs.c
@@ -72,6 +72,10 @@
 {
 	int component_len, component_count = 0;
 
+	if (!strcmp(refname, "@"))
+		/* Refname is a single character '@'. */
+		return -1;
+
 	while (1) {
 		/* We are at the start of a path component. */
 		component_len = check_refname_component(refname, flags);
@@ -109,7 +113,20 @@
  * (ref_entry->flag & REF_DIR) is zero.
  */
 struct ref_value {
+	/*
+	 * The name of the object to which this reference resolves
+	 * (which may be a tag object).  If REF_ISBROKEN, this is
+	 * null.  If REF_ISSYMREF, then this is the name of the object
+	 * referred to by the last reference in the symlink chain.
+	 */
 	unsigned char sha1[20];
+
+	/*
+	 * If REF_KNOWS_PEELED, then this field holds the peeled value
+	 * of this reference, or null if the reference is known not to
+	 * be peelable.  See the documentation for peel_ref() for an
+	 * exact definition of "peelable".
+	 */
 	unsigned char peeled[20];
 };
 
@@ -158,7 +175,17 @@
 	struct ref_entry **entries;
 };
 
-/* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */
+/*
+ * Bit values for ref_entry::flag.  REF_ISSYMREF=0x01,
+ * REF_ISPACKED=0x02, and REF_ISBROKEN=0x04 are public values; see
+ * refs.h.
+ */
+
+/*
+ * The field ref_entry->u.value.peeled of this value entry contains
+ * the correct peeled value for the reference, which might be
+ * null_sha1 if the reference is not a tag or if it is broken.
+ */
 #define REF_KNOWS_PEELED 0x08
 
 /* ref_entry represents a directory of references */
@@ -343,18 +370,17 @@
 }
 
 /*
- * Return the entry with the given refname from the ref_dir
- * (non-recursively), sorting dir if necessary.  Return NULL if no
- * such entry is found.  dir must already be complete.
+ * Return the index of the entry with the given refname from the
+ * ref_dir (non-recursively), sorting dir if necessary.  Return -1 if
+ * no such entry is found.  dir must already be complete.
  */
-static struct ref_entry *search_ref_dir(struct ref_dir *dir,
-					const char *refname, size_t len)
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
 {
 	struct ref_entry **r;
 	struct string_slice key;
 
 	if (refname == NULL || !dir->nr)
-		return NULL;
+		return -1;
 
 	sort_ref_dir(dir);
 	key.len = len;
@@ -363,9 +389,9 @@
 		    ref_entry_cmp_sslice);
 
 	if (r == NULL)
-		return NULL;
+		return -1;
 
-	return *r;
+	return r - dir->entries;
 }
 
 /*
@@ -379,8 +405,9 @@
 					 const char *subdirname, size_t len,
 					 int mkdir)
 {
-	struct ref_entry *entry = search_ref_dir(dir, subdirname, len);
-	if (!entry) {
+	int entry_index = search_ref_dir(dir, subdirname, len);
+	struct ref_entry *entry;
+	if (entry_index == -1) {
 		if (!mkdir)
 			return NULL;
 		/*
@@ -391,6 +418,8 @@
 		 */
 		entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
 		add_entry_to_dir(dir, entry);
+	} else {
+		entry = dir->entries[entry_index];
 	}
 	return get_ref_dir(entry);
 }
@@ -429,12 +458,67 @@
  */
 static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
 {
+	int entry_index;
 	struct ref_entry *entry;
 	dir = find_containing_dir(dir, refname, 0);
 	if (!dir)
 		return NULL;
-	entry = search_ref_dir(dir, refname, strlen(refname));
-	return (entry && !(entry->flag & REF_DIR)) ? entry : NULL;
+	entry_index = search_ref_dir(dir, refname, strlen(refname));
+	if (entry_index == -1)
+		return NULL;
+	entry = dir->entries[entry_index];
+	return (entry->flag & REF_DIR) ? NULL : entry;
+}
+
+/*
+ * Remove the entry with the given name from dir, recursing into
+ * subdirectories as necessary.  If refname is the name of a directory
+ * (i.e., ends with '/'), then remove the directory and its contents.
+ * If the removal was successful, return the number of entries
+ * remaining in the directory entry that contained the deleted entry.
+ * If the name was not found, return -1.  Please note that this
+ * function only deletes the entry from the cache; it does not delete
+ * it from the filesystem or ensure that other cache entries (which
+ * might be symbolic references to the removed entry) are updated.
+ * Nor does it remove any containing dir entries that might be made
+ * empty by the removal.  dir must represent the top-level directory
+ * and must already be complete.
+ */
+static int remove_entry(struct ref_dir *dir, const char *refname)
+{
+	int refname_len = strlen(refname);
+	int entry_index;
+	struct ref_entry *entry;
+	int is_dir = refname[refname_len - 1] == '/';
+	if (is_dir) {
+		/*
+		 * refname represents a reference directory.  Remove
+		 * the trailing slash; otherwise we will get the
+		 * directory *representing* refname rather than the
+		 * one *containing* it.
+		 */
+		char *dirname = xmemdupz(refname, refname_len - 1);
+		dir = find_containing_dir(dir, dirname, 0);
+		free(dirname);
+	} else {
+		dir = find_containing_dir(dir, refname, 0);
+	}
+	if (!dir)
+		return -1;
+	entry_index = search_ref_dir(dir, refname, refname_len);
+	if (entry_index == -1)
+		return -1;
+	entry = dir->entries[entry_index];
+
+	memmove(&dir->entries[entry_index],
+		&dir->entries[entry_index + 1],
+		(dir->nr - entry_index - 1) * sizeof(*dir->entries)
+		);
+	dir->nr--;
+	if (dir->sorted > entry_index)
+		dir->sorted--;
+	free_ref_entry(entry);
+	return dir->nr;
 }
 
 /*
@@ -503,27 +587,64 @@
 	dir->sorted = dir->nr = i;
 }
 
-#define DO_FOR_EACH_INCLUDE_BROKEN 01
+/* Include broken references in a do_for_each_ref*() iteration: */
+#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
 
+/*
+ * Return true iff the reference described by entry can be resolved to
+ * an object in the database.  Emit a warning if the referred-to
+ * object does not exist.
+ */
+static int ref_resolves_to_object(struct ref_entry *entry)
+{
+	if (entry->flag & REF_ISBROKEN)
+		return 0;
+	if (!has_sha1_file(entry->u.value.sha1)) {
+		error("%s does not point to a valid object!", entry->name);
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * current_ref is a performance hack: when iterating over references
+ * using the for_each_ref*() functions, current_ref is set to the
+ * current reference's entry before calling the callback function.  If
+ * the callback function calls peel_ref(), then peel_ref() first
+ * checks whether the reference to be peeled is the current reference
+ * (it usually is) and if so, returns that reference's peeled version
+ * if it is available.  This avoids a refname lookup in a common case.
+ */
 static struct ref_entry *current_ref;
 
-static int do_one_ref(const char *base, each_ref_fn fn, int trim,
-		      int flags, void *cb_data, struct ref_entry *entry)
+typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
+
+struct ref_entry_cb {
+	const char *base;
+	int trim;
+	int flags;
+	each_ref_fn *fn;
+	void *cb_data;
+};
+
+/*
+ * Handle one reference in a do_for_each_ref*()-style iteration,
+ * calling an each_ref_fn for each entry.
+ */
+static int do_one_ref(struct ref_entry *entry, void *cb_data)
 {
+	struct ref_entry_cb *data = cb_data;
 	int retval;
-	if (prefixcmp(entry->name, base))
+	if (prefixcmp(entry->name, data->base))
 		return 0;
 
-	if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-		if (entry->flag & REF_ISBROKEN)
-			return 0; /* ignore broken refs e.g. dangling symref */
-		if (!has_sha1_file(entry->u.value.sha1)) {
-			error("%s does not point to a valid object!", entry->name);
-			return 0;
-		}
-	}
+	if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+	      !ref_resolves_to_object(entry))
+		return 0;
+
 	current_ref = entry;
-	retval = fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);
+	retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
+			  entry->flag, data->cb_data);
 	current_ref = NULL;
 	return retval;
 }
@@ -532,11 +653,11 @@
  * Call fn for each reference in dir that has index in the range
  * offset <= index < dir->nr.  Recurse into subdirectories that are in
  * that index range, sorting them before iterating.  This function
- * does not sort dir itself; it should be sorted beforehand.
+ * does not sort dir itself; it should be sorted beforehand.  fn is
+ * called for all references, including broken ones.
  */
-static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
-				  const char *base,
-				  each_ref_fn fn, int trim, int flags, void *cb_data)
+static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
+				    each_ref_entry_fn fn, void *cb_data)
 {
 	int i;
 	assert(dir->sorted == dir->nr);
@@ -546,10 +667,9 @@
 		if (entry->flag & REF_DIR) {
 			struct ref_dir *subdir = get_ref_dir(entry);
 			sort_ref_dir(subdir);
-			retval = do_for_each_ref_in_dir(subdir, 0,
-							base, fn, trim, flags, cb_data);
+			retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
 		} else {
-			retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
+			retval = fn(entry, cb_data);
 		}
 		if (retval)
 			return retval;
@@ -562,12 +682,12 @@
  * by refname.  Recurse into subdirectories.  If a value entry appears
  * in both dir1 and dir2, then only process the version that is in
  * dir2.  The input dirs must already be sorted, but subdirs will be
- * sorted as needed.
+ * sorted as needed.  fn is called for all references, including
+ * broken ones.
  */
-static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
-				   struct ref_dir *dir2,
-				   const char *base, each_ref_fn fn, int trim,
-				   int flags, void *cb_data)
+static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
+				     struct ref_dir *dir2,
+				     each_ref_entry_fn fn, void *cb_data)
 {
 	int retval;
 	int i1 = 0, i2 = 0;
@@ -578,12 +698,10 @@
 		struct ref_entry *e1, *e2;
 		int cmp;
 		if (i1 == dir1->nr) {
-			return do_for_each_ref_in_dir(dir2, i2,
-						      base, fn, trim, flags, cb_data);
+			return do_for_each_entry_in_dir(dir2, i2, fn, cb_data);
 		}
 		if (i2 == dir2->nr) {
-			return do_for_each_ref_in_dir(dir1, i1,
-						      base, fn, trim, flags, cb_data);
+			return do_for_each_entry_in_dir(dir1, i1, fn, cb_data);
 		}
 		e1 = dir1->entries[i1];
 		e2 = dir2->entries[i2];
@@ -595,14 +713,13 @@
 				struct ref_dir *subdir2 = get_ref_dir(e2);
 				sort_ref_dir(subdir1);
 				sort_ref_dir(subdir2);
-				retval = do_for_each_ref_in_dirs(
-						subdir1, subdir2,
-						base, fn, trim, flags, cb_data);
+				retval = do_for_each_entry_in_dirs(
+						subdir1, subdir2, fn, cb_data);
 				i1++;
 				i2++;
 			} else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
 				/* Both are references; ignore the one from dir1. */
-				retval = do_one_ref(base, fn, trim, flags, cb_data, e2);
+				retval = fn(e2, cb_data);
 				i1++;
 				i2++;
 			} else {
@@ -621,26 +738,33 @@
 			if (e->flag & REF_DIR) {
 				struct ref_dir *subdir = get_ref_dir(e);
 				sort_ref_dir(subdir);
-				retval = do_for_each_ref_in_dir(
-						subdir, 0,
-						base, fn, trim, flags, cb_data);
+				retval = do_for_each_entry_in_dir(
+						subdir, 0, fn, cb_data);
 			} else {
-				retval = do_one_ref(base, fn, trim, flags, cb_data, e);
+				retval = fn(e, cb_data);
 			}
 		}
 		if (retval)
 			return retval;
 	}
-	if (i1 < dir1->nr)
-		return do_for_each_ref_in_dir(dir1, i1,
-					      base, fn, trim, flags, cb_data);
-	if (i2 < dir2->nr)
-		return do_for_each_ref_in_dir(dir2, i2,
-					      base, fn, trim, flags, cb_data);
-	return 0;
 }
 
 /*
+ * Load all of the refs from the dir into our in-memory cache. The hard work
+ * of loading loose refs is done by get_ref_dir(), so we just need to recurse
+ * through all of the sub-directories. We do not even need to care about
+ * sorting, as traversal order does not matter to us.
+ */
+static void prime_ref_dir(struct ref_dir *dir)
+{
+	int i;
+	for (i = 0; i < dir->nr; i++) {
+		struct ref_entry *entry = dir->entries[i];
+		if (entry->flag & REF_DIR)
+			prime_ref_dir(get_ref_dir(entry));
+	}
+}
+/*
  * Return true iff refname1 and refname2 conflict with each other.
  * Two reference names conflict if one of them exactly matches the
  * leading components of the other; e.g., "foo/bar" conflicts with
@@ -661,14 +785,13 @@
 	const char *conflicting_refname;
 };
 
-static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
-			    int flags, void *cb_data)
+static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
 {
 	struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
-	if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
+	if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
 		return 0;
-	if (names_conflict(data->refname, existingrefname)) {
-		data->conflicting_refname = existingrefname;
+	if (names_conflict(data->refname, entry->name)) {
+		data->conflicting_refname = entry->name;
 		return 1;
 	}
 	return 0;
@@ -676,7 +799,7 @@
 
 /*
  * Return true iff a reference named refname could be created without
- * conflicting with the name of an existing reference in array.  If
+ * conflicting with the name of an existing reference in dir.  If
  * oldrefname is non-NULL, ignore potential conflicts with oldrefname
  * (e.g., because oldrefname is scheduled for deletion in the same
  * operation).
@@ -690,9 +813,7 @@
 	data.conflicting_refname = NULL;
 
 	sort_ref_dir(dir);
-	if (do_for_each_ref_in_dir(dir, 0, "", name_conflict_fn,
-				   0, DO_FOR_EACH_INCLUDE_BROKEN,
-				   &data)) {
+	if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
 		error("'%s' exists; cannot create '%s'",
 		      data.conflicting_refname, refname);
 		return 0;
@@ -700,6 +821,30 @@
 	return 1;
 }
 
+struct packed_ref_cache {
+	struct ref_entry *root;
+
+	/*
+	 * Count of references to the data structure in this instance,
+	 * including the pointer from ref_cache::packed if any.  The
+	 * data will not be freed as long as the reference count is
+	 * nonzero.
+	 */
+	unsigned int referrers;
+
+	/*
+	 * Iff the packed-refs file associated with this instance is
+	 * currently locked for writing, this points at the associated
+	 * lock (which is owned by somebody else).  The referrer count
+	 * is also incremented when the file is locked and decremented
+	 * when it is unlocked.
+	 */
+	struct lock_file *lock;
+
+	/* The metadata from when this packed-refs cache was read */
+	struct stat_validity validity;
+};
+
 /*
  * Future: need to be in "struct repository"
  * when doing a full libification.
@@ -707,16 +852,51 @@
 static struct ref_cache {
 	struct ref_cache *next;
 	struct ref_entry *loose;
-	struct ref_entry *packed;
-	/* The submodule name, or "" for the main repo. */
-	char name[FLEX_ARRAY];
-} *ref_cache;
+	struct packed_ref_cache *packed;
+	/*
+	 * The submodule name, or "" for the main repo.  We allocate
+	 * length 1 rather than FLEX_ARRAY so that the main ref_cache
+	 * is initialized correctly.
+	 */
+	char name[1];
+} ref_cache, *submodule_ref_caches;
+
+/* Lock used for the main packed-refs file: */
+static struct lock_file packlock;
+
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+	packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs.  If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+	if (!--packed_refs->referrers) {
+		free_ref_entry(packed_refs->root);
+		stat_validity_clear(&packed_refs->validity);
+		free(packed_refs);
+		return 1;
+	} else {
+		return 0;
+	}
+}
 
 static void clear_packed_ref_cache(struct ref_cache *refs)
 {
 	if (refs->packed) {
-		free_ref_entry(refs->packed);
+		struct packed_ref_cache *packed_refs = refs->packed;
+
+		if (packed_refs->lock)
+			die("internal error: packed-ref cache cleared while locked");
 		refs->packed = NULL;
+		release_packed_ref_cache(packed_refs);
 	}
 }
 
@@ -748,18 +928,18 @@
  */
 static struct ref_cache *get_ref_cache(const char *submodule)
 {
-	struct ref_cache *refs = ref_cache;
-	if (!submodule)
-		submodule = "";
-	while (refs) {
+	struct ref_cache *refs;
+
+	if (!submodule || !*submodule)
+		return &ref_cache;
+
+	for (refs = submodule_ref_caches; refs; refs = refs->next)
 		if (!strcmp(submodule, refs->name))
 			return refs;
-		refs = refs->next;
-	}
 
 	refs = create_ref_cache(submodule);
-	refs->next = ref_cache;
-	ref_cache = refs;
+	refs->next = submodule_ref_caches;
+	submodule_ref_caches = refs;
 	return refs;
 }
 
@@ -770,6 +950,16 @@
 	clear_loose_ref_cache(refs);
 }
 
+/* The length of a peeled reference line in packed-refs, including EOL: */
+#define PEELED_LINE_LENGTH 42
+
+/*
+ * The packed-refs header line that we write out.  Perhaps other
+ * traits will be added later.  The trailing space is required.
+ */
+static const char PACKED_REFS_HEADER[] =
+	"# pack-refs with: peeled fully-peeled \n";
+
 /*
  * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
  * Return a pointer to the refname within the line (null-terminated),
@@ -862,8 +1052,8 @@
 		}
 		if (last &&
 		    refline[0] == '^' &&
-		    strlen(refline) == 42 &&
-		    refline[41] == '\n' &&
+		    strlen(refline) == PEELED_LINE_LENGTH &&
+		    refline[PEELED_LINE_LENGTH - 1] == '\n' &&
 		    !get_sha1_hex(refline + 1, sha1)) {
 			hashcpy(last->u.value.peeled, sha1);
 			/*
@@ -876,30 +1066,58 @@
 	}
 }
 
-static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+/*
+ * Get the packed_ref_cache for the specified ref_cache, creating it
+ * if necessary.
+ */
+static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
 {
+	const char *packed_refs_file;
+
+	if (*refs->name)
+		packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+	else
+		packed_refs_file = git_path("packed-refs");
+
+	if (refs->packed &&
+	    !stat_validity_check(&refs->packed->validity, packed_refs_file))
+		clear_packed_ref_cache(refs);
+
 	if (!refs->packed) {
-		const char *packed_refs_file;
 		FILE *f;
 
-		refs->packed = create_dir_entry(refs, "", 0, 0);
-		if (*refs->name)
-			packed_refs_file = git_path_submodule(refs->name, "packed-refs");
-		else
-			packed_refs_file = git_path("packed-refs");
+		refs->packed = xcalloc(1, sizeof(*refs->packed));
+		acquire_packed_ref_cache(refs->packed);
+		refs->packed->root = create_dir_entry(refs, "", 0, 0);
 		f = fopen(packed_refs_file, "r");
 		if (f) {
-			read_packed_refs(f, get_ref_dir(refs->packed));
+			stat_validity_update(&refs->packed->validity, fileno(f));
+			read_packed_refs(f, get_ref_dir(refs->packed->root));
 			fclose(f);
 		}
 	}
-	return get_ref_dir(refs->packed);
+	return refs->packed;
+}
+
+static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+{
+	return get_ref_dir(packed_ref_cache->root);
+}
+
+static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+{
+	return get_packed_ref_dir(get_packed_ref_cache(refs));
 }
 
 void add_packed_ref(const char *refname, const unsigned char *sha1)
 {
-	add_ref(get_packed_refs(get_ref_cache(NULL)),
-			create_ref_entry(refname, sha1, REF_ISPACKED, 1));
+	struct packed_ref_cache *packed_ref_cache =
+		get_packed_ref_cache(&ref_cache);
+
+	if (!packed_ref_cache->lock)
+		die("internal error: packed refs not locked");
+	add_ref(get_packed_ref_dir(packed_ref_cache),
+		create_ref_entry(refname, sha1, REF_ISPACKED, 1));
 }
 
 /*
@@ -1069,18 +1287,43 @@
 }
 
 /*
- * Try to read ref from the packed references.  On success, set sha1
- * and return 0; otherwise, return -1.
+ * Return the ref_entry for the given refname from the packed
+ * references.  If it does not exist, return NULL.
  */
-static int get_packed_ref(const char *refname, unsigned char *sha1)
+static struct ref_entry *get_packed_ref(const char *refname)
 {
-	struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
-	struct ref_entry *entry = find_ref(packed, refname);
+	return find_ref(get_packed_refs(&ref_cache), refname);
+}
+
+/*
+ * A loose ref file doesn't exist; check for a packed ref.  The
+ * options are forwarded from resolve_safe_unsafe().
+ */
+static const char *handle_missing_loose_ref(const char *refname,
+					    unsigned char *sha1,
+					    int reading,
+					    int *flag)
+{
+	struct ref_entry *entry;
+
+	/*
+	 * The loose reference file does not exist; check for a packed
+	 * reference.
+	 */
+	entry = get_packed_ref(refname);
 	if (entry) {
 		hashcpy(sha1, entry->u.value.sha1);
-		return 0;
+		if (flag)
+			*flag |= REF_ISPACKED;
+		return refname;
 	}
-	return -1;
+	/* The reference is not a packed reference, either. */
+	if (reading) {
+		return NULL;
+	} else {
+		hashclr(sha1);
+		return refname;
+	}
 }
 
 const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
@@ -1107,32 +1350,34 @@
 
 		git_snpath(path, sizeof(path), "%s", refname);
 
+		/*
+		 * We might have to loop back here to avoid a race
+		 * condition: first we lstat() the file, then we try
+		 * to read it as a link or as a file.  But if somebody
+		 * changes the type of the file (file <-> directory
+		 * <-> symlink) between the lstat() and reading, then
+		 * we don't want to report that as an error but rather
+		 * try again starting with the lstat().
+		 */
+	stat_ref:
 		if (lstat(path, &st) < 0) {
-			if (errno != ENOENT)
+			if (errno == ENOENT)
+				return handle_missing_loose_ref(refname, sha1,
+								reading, flag);
+			else
 				return NULL;
-			/*
-			 * The loose reference file does not exist;
-			 * check for a packed reference.
-			 */
-			if (!get_packed_ref(refname, sha1)) {
-				if (flag)
-					*flag |= REF_ISPACKED;
-				return refname;
-			}
-			/* The reference is not a packed reference, either. */
-			if (reading) {
-				return NULL;
-			} else {
-				hashclr(sha1);
-				return refname;
-			}
 		}
 
 		/* Follow "normalized" - ie "refs/.." symlinks by hand */
 		if (S_ISLNK(st.st_mode)) {
 			len = readlink(path, buffer, sizeof(buffer)-1);
-			if (len < 0)
-				return NULL;
+			if (len < 0) {
+				if (errno == ENOENT || errno == EINVAL)
+					/* inconsistent with lstat; retry */
+					goto stat_ref;
+				else
+					return NULL;
+			}
 			buffer[len] = 0;
 			if (!prefixcmp(buffer, "refs/") &&
 					!check_refname_format(buffer, 0)) {
@@ -1155,8 +1400,13 @@
 		 * a ref
 		 */
 		fd = open(path, O_RDONLY);
-		if (fd < 0)
-			return NULL;
+		if (fd < 0) {
+			if (errno == ENOENT)
+				/* inconsistent with lstat; retry */
+				goto stat_ref;
+			else
+				return NULL;
+		}
 		len = read_in_full(fd, buffer, sizeof(buffer)-1);
 		close(fd);
 		if (len < 0)
@@ -1168,8 +1418,19 @@
 		/*
 		 * Is it a symbolic ref?
 		 */
-		if (prefixcmp(buffer, "ref:"))
-			break;
+		if (prefixcmp(buffer, "ref:")) {
+			/*
+			 * Please note that FETCH_HEAD has a second
+			 * line containing other data.
+			 */
+			if (get_sha1_hex(buffer, sha1) ||
+			    (buffer[40] != '\0' && !isspace(buffer[40]))) {
+				if (flag)
+					*flag |= REF_ISBROKEN;
+				return NULL;
+			}
+			return refname;
+		}
 		if (flag)
 			*flag |= REF_ISSYMREF;
 		buf = buffer + 4;
@@ -1182,13 +1443,6 @@
 		}
 		refname = strcpy(refname_buffer, buf);
 	}
-	/* Please note that FETCH_HEAD has a second line containing other data. */
-	if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
-		if (flag)
-			*flag |= REF_ISBROKEN;
-		return NULL;
-	}
-	return refname;
 }
 
 char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@ -1231,54 +1485,130 @@
 	return filter->fn(refname, sha1, flags, filter->cb_data);
 }
 
+enum peel_status {
+	/* object was peeled successfully: */
+	PEEL_PEELED = 0,
+
+	/*
+	 * object cannot be peeled because the named object (or an
+	 * object referred to by a tag in the peel chain), does not
+	 * exist.
+	 */
+	PEEL_INVALID = -1,
+
+	/* object cannot be peeled because it is not a tag: */
+	PEEL_NON_TAG = -2,
+
+	/* ref_entry contains no peeled value because it is a symref: */
+	PEEL_IS_SYMREF = -3,
+
+	/*
+	 * ref_entry cannot be peeled because it is broken (i.e., the
+	 * symbolic reference cannot even be resolved to an object
+	 * name):
+	 */
+	PEEL_BROKEN = -4
+};
+
+/*
+ * Peel the named object; i.e., if the object is a tag, resolve the
+ * tag recursively until a non-tag is found.  If successful, store the
+ * result to sha1 and return PEEL_PEELED.  If the object is not a tag
+ * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
+ * and leave sha1 unchanged.
+ */
+static enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
+{
+	struct object *o = lookup_unknown_object(name);
+
+	if (o->type == OBJ_NONE) {
+		int type = sha1_object_info(name, NULL);
+		if (type < 0)
+			return PEEL_INVALID;
+		o->type = type;
+	}
+
+	if (o->type != OBJ_TAG)
+		return PEEL_NON_TAG;
+
+	o = deref_tag_noverify(o);
+	if (!o)
+		return PEEL_INVALID;
+
+	hashcpy(sha1, o->sha1);
+	return PEEL_PEELED;
+}
+
+/*
+ * Peel the entry (if possible) and return its new peel_status.  If
+ * repeel is true, re-peel the entry even if there is an old peeled
+ * value that is already stored in it.
+ *
+ * It is OK to call this function with a packed reference entry that
+ * might be stale and might even refer to an object that has since
+ * been garbage-collected.  In such a case, if the entry has
+ * REF_KNOWS_PEELED then leave the status unchanged and return
+ * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID.
+ */
+static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
+{
+	enum peel_status status;
+
+	if (entry->flag & REF_KNOWS_PEELED) {
+		if (repeel) {
+			entry->flag &= ~REF_KNOWS_PEELED;
+			hashclr(entry->u.value.peeled);
+		} else {
+			return is_null_sha1(entry->u.value.peeled) ?
+				PEEL_NON_TAG : PEEL_PEELED;
+		}
+	}
+	if (entry->flag & REF_ISBROKEN)
+		return PEEL_BROKEN;
+	if (entry->flag & REF_ISSYMREF)
+		return PEEL_IS_SYMREF;
+
+	status = peel_object(entry->u.value.sha1, entry->u.value.peeled);
+	if (status == PEEL_PEELED || status == PEEL_NON_TAG)
+		entry->flag |= REF_KNOWS_PEELED;
+	return status;
+}
+
 int peel_ref(const char *refname, unsigned char *sha1)
 {
 	int flag;
 	unsigned char base[20];
-	struct object *o;
 
 	if (current_ref && (current_ref->name == refname
-		|| !strcmp(current_ref->name, refname))) {
-		if (current_ref->flag & REF_KNOWS_PEELED) {
-			if (is_null_sha1(current_ref->u.value.peeled))
-			    return -1;
-			hashcpy(sha1, current_ref->u.value.peeled);
-			return 0;
-		}
-		hashcpy(base, current_ref->u.value.sha1);
-		goto fallback;
+			    || !strcmp(current_ref->name, refname))) {
+		if (peel_entry(current_ref, 0))
+			return -1;
+		hashcpy(sha1, current_ref->u.value.peeled);
+		return 0;
 	}
 
 	if (read_ref_full(refname, base, 1, &flag))
 		return -1;
 
-	if ((flag & REF_ISPACKED)) {
-		struct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));
-		struct ref_entry *r = find_ref(dir, refname);
-
-		if (r != NULL && r->flag & REF_KNOWS_PEELED) {
+	/*
+	 * If the reference is packed, read its ref_entry from the
+	 * cache in the hope that we already know its peeled value.
+	 * We only try this optimization on packed references because
+	 * (a) forcing the filling of the loose reference cache could
+	 * be expensive and (b) loose references anyway usually do not
+	 * have REF_KNOWS_PEELED.
+	 */
+	if (flag & REF_ISPACKED) {
+		struct ref_entry *r = get_packed_ref(refname);
+		if (r) {
+			if (peel_entry(r, 0))
+				return -1;
 			hashcpy(sha1, r->u.value.peeled);
 			return 0;
 		}
 	}
 
-fallback:
-	o = lookup_unknown_object(base);
-	if (o->type == OBJ_NONE) {
-		int type = sha1_object_info(base, NULL);
-		if (type < 0)
-			return -1;
-		o->type = type;
-	}
-
-	if (o->type == OBJ_TAG) {
-		o = deref_tag_noverify(o);
-		if (o) {
-			hashcpy(sha1, o->sha1);
-			return 0;
-		}
-	}
-	return -1;
+	return peel_object(base, sha1);
 }
 
 struct warn_if_dangling_data {
@@ -1316,40 +1646,84 @@
 	for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
-			   int trim, int flags, void *cb_data)
+/*
+ * Call fn for each reference in the specified ref_cache, omitting
+ * references not in the containing_dir of base.  fn is called for all
+ * references, including broken ones.  If fn ever returns a non-zero
+ * value, stop the iteration and return that value; otherwise, return
+ * 0.
+ */
+static int do_for_each_entry(struct ref_cache *refs, const char *base,
+			     each_ref_entry_fn fn, void *cb_data)
 {
-	struct ref_cache *refs = get_ref_cache(submodule);
-	struct ref_dir *packed_dir = get_packed_refs(refs);
-	struct ref_dir *loose_dir = get_loose_refs(refs);
+	struct packed_ref_cache *packed_ref_cache;
+	struct ref_dir *loose_dir;
+	struct ref_dir *packed_dir;
 	int retval = 0;
 
+	/*
+	 * We must make sure that all loose refs are read before accessing the
+	 * packed-refs file; this avoids a race condition in which loose refs
+	 * are migrated to the packed-refs file by a simultaneous process, but
+	 * our in-memory view is from before the migration. get_packed_ref_cache()
+	 * takes care of making sure our view is up to date with what is on
+	 * disk.
+	 */
+	loose_dir = get_loose_refs(refs);
+	if (base && *base) {
+		loose_dir = find_containing_dir(loose_dir, base, 0);
+	}
+	if (loose_dir)
+		prime_ref_dir(loose_dir);
+
+	packed_ref_cache = get_packed_ref_cache(refs);
+	acquire_packed_ref_cache(packed_ref_cache);
+	packed_dir = get_packed_ref_dir(packed_ref_cache);
 	if (base && *base) {
 		packed_dir = find_containing_dir(packed_dir, base, 0);
-		loose_dir = find_containing_dir(loose_dir, base, 0);
 	}
 
 	if (packed_dir && loose_dir) {
 		sort_ref_dir(packed_dir);
 		sort_ref_dir(loose_dir);
-		retval = do_for_each_ref_in_dirs(
-				packed_dir, loose_dir,
-				base, fn, trim, flags, cb_data);
+		retval = do_for_each_entry_in_dirs(
+				packed_dir, loose_dir, fn, cb_data);
 	} else if (packed_dir) {
 		sort_ref_dir(packed_dir);
-		retval = do_for_each_ref_in_dir(
-				packed_dir, 0,
-				base, fn, trim, flags, cb_data);
+		retval = do_for_each_entry_in_dir(
+				packed_dir, 0, fn, cb_data);
 	} else if (loose_dir) {
 		sort_ref_dir(loose_dir);
-		retval = do_for_each_ref_in_dir(
-				loose_dir, 0,
-				base, fn, trim, flags, cb_data);
+		retval = do_for_each_entry_in_dir(
+				loose_dir, 0, fn, cb_data);
 	}
 
+	release_packed_ref_cache(packed_ref_cache);
 	return retval;
 }
 
+/*
+ * Call fn for each reference in the specified ref_cache for which the
+ * refname begins with base.  If trim is non-zero, then trim that many
+ * characters off the beginning of each refname before passing the
+ * refname to fn.  flags can be DO_FOR_EACH_INCLUDE_BROKEN to include
+ * broken references in the iteration.  If fn ever returns a non-zero
+ * value, stop the iteration and return that value; otherwise, return
+ * 0.
+ */
+static int do_for_each_ref(struct ref_cache *refs, const char *base,
+			   each_ref_fn fn, int trim, int flags, void *cb_data)
+{
+	struct ref_entry_cb data;
+	data.base = base;
+	data.trim = trim;
+	data.flags = flags;
+	data.fn = fn;
+	data.cb_data = cb_data;
+
+	return do_for_each_entry(refs, base, do_one_ref, &data);
+}
+
 static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
 	unsigned char sha1[20];
@@ -1380,23 +1754,23 @@
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+	return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+	return do_for_each_ref(get_ref_cache(submodule), "", 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(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+	return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_ref_in_submodule(const char *submodule, const char *prefix,
 		each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+	return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
@@ -1431,7 +1805,7 @@
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
+	return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
 }
 
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@ -1454,7 +1828,7 @@
 	struct strbuf buf = STRBUF_INIT;
 	int ret;
 	strbuf_addf(&buf, "%srefs/", get_git_namespace());
-	ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+	ret = do_for_each_ref(&ref_cache, buf.buf, fn, 0, 0, cb_data);
 	strbuf_release(&buf);
 	return ret;
 }
@@ -1496,7 +1870,7 @@
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(NULL, "", fn, 0,
+	return do_for_each_ref(&ref_cache, "", fn, 0,
 			       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -1702,7 +2076,7 @@
 	 * name is a proper prefix of our refname.
 	 */
 	if (missing &&
-	     !is_refname_available(refname, NULL, get_packed_refs(get_ref_cache(NULL)))) {
+	     !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
 		last_errno = ENOTDIR;
 		goto error_return;
 	}
@@ -1754,48 +2128,325 @@
 	return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
 }
 
-struct repack_without_ref_sb {
-	const char *refname;
-	int fd;
-};
-
-static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,
-				 int flags, void *cb_data)
+/*
+ * Write an entry to the packed-refs file for the specified refname.
+ * If peeled is non-NULL, write it as the entry's peeled value.
+ */
+static void write_packed_entry(int fd, char *refname, unsigned char *sha1,
+			       unsigned char *peeled)
 {
-	struct repack_without_ref_sb *data = cb_data;
 	char line[PATH_MAX + 100];
 	int len;
 
-	if (!strcmp(data->refname, refname))
-		return 0;
 	len = snprintf(line, sizeof(line), "%s %s\n",
 		       sha1_to_hex(sha1), refname);
 	/* this should not happen but just being defensive */
 	if (len > sizeof(line))
 		die("too long a refname '%s'", refname);
-	write_or_die(data->fd, line, len);
+	write_or_die(fd, line, len);
+
+	if (peeled) {
+		if (snprintf(line, sizeof(line), "^%s\n",
+			     sha1_to_hex(peeled)) != PEELED_LINE_LENGTH)
+			die("internal error");
+		write_or_die(fd, line, PEELED_LINE_LENGTH);
+	}
+}
+
+/*
+ * An each_ref_entry_fn that writes the entry to a packed-refs file.
+ */
+static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
+{
+	int *fd = cb_data;
+	enum peel_status peel_status = peel_entry(entry, 0);
+
+	if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+		error("internal error: %s is not a valid packed reference!",
+		      entry->name);
+	write_packed_entry(*fd, entry->name, entry->u.value.sha1,
+			   peel_status == PEEL_PEELED ?
+			   entry->u.value.peeled : NULL);
 	return 0;
 }
 
-static struct lock_file packlock;
+int lock_packed_refs(int flags)
+{
+	struct packed_ref_cache *packed_ref_cache;
+
+	if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
+		return -1;
+	/*
+	 * Get the current packed-refs while holding the lock.  If the
+	 * packed-refs file has been modified since we last read it,
+	 * this will automatically invalidate the cache and re-read
+	 * the packed-refs file.
+	 */
+	packed_ref_cache = get_packed_ref_cache(&ref_cache);
+	packed_ref_cache->lock = &packlock;
+	/* Increment the reference count to prevent it from being freed: */
+	acquire_packed_ref_cache(packed_ref_cache);
+	return 0;
+}
+
+int commit_packed_refs(void)
+{
+	struct packed_ref_cache *packed_ref_cache =
+		get_packed_ref_cache(&ref_cache);
+	int error = 0;
+
+	if (!packed_ref_cache->lock)
+		die("internal error: packed-refs not locked");
+	write_or_die(packed_ref_cache->lock->fd,
+		     PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+
+	do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
+				 0, write_packed_entry_fn,
+				 &packed_ref_cache->lock->fd);
+	if (commit_lock_file(packed_ref_cache->lock))
+		error = -1;
+	packed_ref_cache->lock = NULL;
+	release_packed_ref_cache(packed_ref_cache);
+	return error;
+}
+
+void rollback_packed_refs(void)
+{
+	struct packed_ref_cache *packed_ref_cache =
+		get_packed_ref_cache(&ref_cache);
+
+	if (!packed_ref_cache->lock)
+		die("internal error: packed-refs not locked");
+	rollback_lock_file(packed_ref_cache->lock);
+	packed_ref_cache->lock = NULL;
+	release_packed_ref_cache(packed_ref_cache);
+	clear_packed_ref_cache(&ref_cache);
+}
+
+struct ref_to_prune {
+	struct ref_to_prune *next;
+	unsigned char sha1[20];
+	char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+	unsigned int flags;
+	struct ref_dir *packed_refs;
+	struct ref_to_prune *ref_to_prune;
+};
+
+/*
+ * An each_ref_entry_fn that is run over loose references only.  If
+ * the loose reference can be packed, add an entry in the packed ref
+ * cache.  If the reference should be pruned, also add it to
+ * ref_to_prune in the pack_refs_cb_data.
+ */
+static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
+{
+	struct pack_refs_cb_data *cb = cb_data;
+	enum peel_status peel_status;
+	struct ref_entry *packed_entry;
+	int is_tag_ref = !prefixcmp(entry->name, "refs/tags/");
+
+	/* ALWAYS pack tags */
+	if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
+		return 0;
+
+	/* Do not pack symbolic or broken refs: */
+	if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry))
+		return 0;
+
+	/* Add a packed ref cache entry equivalent to the loose entry. */
+	peel_status = peel_entry(entry, 1);
+	if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+		die("internal error peeling reference %s (%s)",
+		    entry->name, sha1_to_hex(entry->u.value.sha1));
+	packed_entry = find_ref(cb->packed_refs, entry->name);
+	if (packed_entry) {
+		/* Overwrite existing packed entry with info from loose entry */
+		packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
+		hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+	} else {
+		packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+						REF_ISPACKED | REF_KNOWS_PEELED, 0);
+		add_ref(cb->packed_refs, packed_entry);
+	}
+	hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
+
+	/* Schedule the loose reference for pruning if requested. */
+	if ((cb->flags & PACK_REFS_PRUNE)) {
+		int namelen = strlen(entry->name) + 1;
+		struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+		hashcpy(n->sha1, entry->u.value.sha1);
+		strcpy(n->name, entry->name);
+		n->next = cb->ref_to_prune;
+		cb->ref_to_prune = n;
+	}
+	return 0;
+}
+
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+	char *p, *q;
+	int i;
+	p = name;
+	for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+		while (*p && *p != '/')
+			p++;
+		/* tolerate duplicate slashes; see check_refname_format() */
+		while (*p == '/')
+			p++;
+	}
+	for (q = p; *q; q++)
+		;
+	while (1) {
+		while (q > p && *q != '/')
+			q--;
+		while (q > p && *(q-1) == '/')
+			q--;
+		if (q == p)
+			break;
+		*q = '\0';
+		if (rmdir(git_path("%s", name)))
+			break;
+	}
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+	struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
+
+	if (lock) {
+		unlink_or_warn(git_path("%s", r->name));
+		unlock_ref(lock);
+		try_remove_empty_parents(r->name);
+	}
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+	while (r) {
+		prune_ref(r);
+		r = r->next;
+	}
+}
+
+int pack_refs(unsigned int flags)
+{
+	struct pack_refs_cb_data cbdata;
+
+	memset(&cbdata, 0, sizeof(cbdata));
+	cbdata.flags = flags;
+
+	lock_packed_refs(LOCK_DIE_ON_ERROR);
+	cbdata.packed_refs = get_packed_refs(&ref_cache);
+
+	do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
+				 pack_if_possible_fn, &cbdata);
+
+	if (commit_packed_refs())
+		die_errno("unable to overwrite old ref-pack file");
+
+	prune_refs(cbdata.ref_to_prune);
+	return 0;
+}
+
+/*
+ * If entry is no longer needed in packed-refs, add it to the string
+ * list pointed to by cb_data.  Reasons for deleting entries:
+ *
+ * - Entry is broken.
+ * - Entry is overridden by a loose ref.
+ * - Entry does not point at a valid object.
+ *
+ * In the first and third cases, also emit an error message because these
+ * are indications of repository corruption.
+ */
+static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
+{
+	struct string_list *refs_to_delete = cb_data;
+
+	if (entry->flag & REF_ISBROKEN) {
+		/* This shouldn't happen to packed refs. */
+		error("%s is broken!", entry->name);
+		string_list_append(refs_to_delete, entry->name);
+		return 0;
+	}
+	if (!has_sha1_file(entry->u.value.sha1)) {
+		unsigned char sha1[20];
+		int flags;
+
+		if (read_ref_full(entry->name, sha1, 0, &flags))
+			/* We should at least have found the packed ref. */
+			die("Internal error");
+		if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
+			/*
+			 * This packed reference is overridden by a
+			 * loose reference, so it is OK that its value
+			 * is no longer valid; for example, it might
+			 * refer to an object that has been garbage
+			 * collected.  For this purpose we don't even
+			 * care whether the loose reference itself is
+			 * invalid, broken, symbolic, etc.  Silently
+			 * remove the packed reference.
+			 */
+			string_list_append(refs_to_delete, entry->name);
+			return 0;
+		}
+		/*
+		 * There is no overriding loose reference, so the fact
+		 * that this reference doesn't refer to a valid object
+		 * indicates some kind of repository corruption.
+		 * Report the problem, then omit the reference from
+		 * the output.
+		 */
+		error("%s does not point to a valid object!", entry->name);
+		string_list_append(refs_to_delete, entry->name);
+		return 0;
+	}
+
+	return 0;
+}
 
 static int repack_without_ref(const char *refname)
 {
-	struct repack_without_ref_sb data;
-	struct ref_cache *refs = get_ref_cache(NULL);
-	struct ref_dir *packed = get_packed_refs(refs);
-	if (find_ref(packed, refname) == NULL)
-		return 0;
-	data.refname = refname;
-	data.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
-	if (data.fd < 0) {
+	struct ref_dir *packed;
+	struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+	struct string_list_item *ref_to_delete;
+
+	if (!get_packed_ref(refname))
+		return 0; /* refname does not exist in packed refs */
+
+	if (lock_packed_refs(0)) {
 		unable_to_lock_error(git_path("packed-refs"), errno);
 		return error("cannot delete '%s' from packed refs", refname);
 	}
-	clear_packed_ref_cache(refs);
-	packed = get_packed_refs(refs);
-	do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
-	return commit_lock_file(&packlock);
+	packed = get_packed_refs(&ref_cache);
+
+	/* Remove refname from the cache: */
+	if (remove_entry(packed, refname) == -1) {
+		/*
+		 * The packed entry disappeared while we were
+		 * acquiring the lock.
+		 */
+		rollback_packed_refs();
+		return 0;
+	}
+
+	/* Remove any other accumulated cruft: */
+	do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
+	for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+		if (remove_entry(packed, ref_to_delete->string) == -1)
+			die("internal error");
+	}
+
+	/* Write what remains: */
+	return commit_packed_refs();
 }
 
 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
@@ -1823,7 +2474,7 @@
 	ret |= repack_without_ref(lock->ref_name);
 
 	unlink_or_warn(git_path("logs/%s", lock->ref_name));
-	invalidate_ref_cache(NULL);
+	clear_loose_ref_cache(&ref_cache);
 	unlock_ref(lock);
 	return ret;
 }
@@ -1845,7 +2496,6 @@
 	struct stat loginfo;
 	int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
 	const char *symref = NULL;
-	struct ref_cache *refs = get_ref_cache(NULL);
 
 	if (log && S_ISLNK(loginfo.st_mode))
 		return error("reflog for %s is a symlink", oldrefname);
@@ -1857,10 +2507,10 @@
 	if (!symref)
 		return error("refname %s not found", oldrefname);
 
-	if (!is_refname_available(newrefname, oldrefname, get_packed_refs(refs)))
+	if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
 		return 1;
 
-	if (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))
+	if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
 		return 1;
 
 	if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
@@ -2116,7 +2766,7 @@
 		unlock_ref(lock);
 		return -1;
 	}
-	clear_loose_ref_cache(get_ref_cache(NULL));
+	clear_loose_ref_cache(&ref_cache);
 	if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
 	    (strcmp(lock->ref_name, lock->orig_ref_name) &&
 	     log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
diff --git a/refs.h b/refs.h
index a35eafc..9e5db3a 100644
--- a/refs.h
+++ b/refs.h
@@ -10,18 +10,41 @@
 	int force_write;
 };
 
+/*
+ * Bit values set in the flags argument passed to each_ref_fn():
+ */
+
+/* Reference is a symbolic reference. */
 #define REF_ISSYMREF 0x01
+
+/* Reference is a packed reference. */
 #define REF_ISPACKED 0x02
+
+/*
+ * Reference cannot be resolved to an object name: dangling symbolic
+ * reference (directly or indirectly), corrupt reference file, or
+ * symbolic reference refers to ill-formatted reference name.
+ */
 #define REF_ISBROKEN 0x04
 
 /*
- * Calls the specified function for each ref file until it returns
- * nonzero, and returns the value.  Please note that it is not safe to
- * modify references while an iteration is in progress, unless the
- * same callback function invocation that modifies the reference also
- * returns a nonzero value to immediately stop the iteration.
+ * The signature for the callback function for the for_each_*()
+ * functions below.  The memory pointed to by the refname and sha1
+ * arguments is only guaranteed to be valid for the duration of a
+ * single callback invocation.
  */
-typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
+typedef int each_ref_fn(const char *refname,
+			const unsigned char *sha1, int flags, void *cb_data);
+
+/*
+ * The following functions invoke the specified callback function for
+ * each reference indicated.  If the function ever returns a nonzero
+ * value, stop the iteration and return that value.  Please note that
+ * it is not safe to modify references while an iteration is in
+ * progress, unless the same callback function invocation that
+ * modifies the reference also returns a nonzero value to immediately
+ * stop the iteration.
+ */
 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 *);
@@ -54,13 +77,57 @@
 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
- * Add a reference to the in-memory packed reference cache.  To actually
- * write the reference to the packed-refs file, call pack_refs().
+ * Lock the packed-refs file for writing.  Flags is passed to
+ * hold_lock_file_for_update().  Return 0 on success.
+ */
+extern int lock_packed_refs(int flags);
+
+/*
+ * Add a reference to the in-memory packed reference cache.  This may
+ * only be called while the packed-refs file is locked (see
+ * lock_packed_refs()).  To actually write the packed-refs file, call
+ * commit_packed_refs().
  */
 extern void add_packed_ref(const char *refname, const unsigned char *sha1);
 
+/*
+ * Write the current version of the packed refs cache from memory to
+ * disk.  The packed-refs file must already be locked for writing (see
+ * lock_packed_refs()).  Return zero on success.
+ */
+extern int commit_packed_refs(void);
+
+/*
+ * Rollback the lockfile for the packed-refs file, and discard the
+ * in-memory packed reference cache.  (The packed-refs file will be
+ * read anew if it is needed again after this function is called.)
+ */
+extern void rollback_packed_refs(void);
+
+/*
+ * Flags for controlling behaviour of pack_refs()
+ * PACK_REFS_PRUNE: Prune loose refs after packing
+ * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
+ */
+#define PACK_REFS_PRUNE 0x0001
+#define PACK_REFS_ALL   0x0002
+
+/*
+ * Write a packed-refs file for the current repository.
+ * flags: Combination of the above PACK_REFS_* flags.
+ */
+int pack_refs(unsigned int flags);
+
 extern int ref_exists(const char *);
 
+/*
+ * If refname is a non-symbolic reference that refers to a tag object,
+ * and the tag can be (recursively) dereferenced to a non-tag object,
+ * store the SHA1 of the referred-to object to sha1 and return 0.  If
+ * any of these conditions are not met, return a non-zero value.
+ * Symbolic references are considered unpeelable, even if they
+ * ultimately resolve to a peelable tag.
+ */
 extern int peel_ref(const char *refname, unsigned char *sha1);
 
 /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
diff --git a/remote-curl.c b/remote-curl.c
index 60eda63..5b3ce9e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -7,6 +7,7 @@
 #include "run-command.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "argv-array.h"
 
 static struct remote *remote;
 static const char *url; /* always ends with a trailing slash */
@@ -787,36 +788,35 @@
 static int push_git(struct discovery *heads, int nr_spec, char **specs)
 {
 	struct rpc_state rpc;
-	const char **argv;
-	int argc = 0, i, err;
+	int i, err;
+	struct argv_array args;
 
-	argv = xmalloc((10 + nr_spec) * sizeof(char*));
-	argv[argc++] = "send-pack";
-	argv[argc++] = "--stateless-rpc";
-	argv[argc++] = "--helper-status";
+	argv_array_init(&args);
+	argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
+			 NULL);
+
 	if (options.thin)
-		argv[argc++] = "--thin";
+		argv_array_push(&args, "--thin");
 	if (options.dry_run)
-		argv[argc++] = "--dry-run";
+		argv_array_push(&args, "--dry-run");
 	if (options.verbosity == 0)
-		argv[argc++] = "--quiet";
+		argv_array_push(&args, "--quiet");
 	else if (options.verbosity > 1)
-		argv[argc++] = "--verbose";
-	argv[argc++] = options.progress ? "--progress" : "--no-progress";
-	argv[argc++] = url;
+		argv_array_push(&args, "--verbose");
+	argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
+	argv_array_push(&args, url);
 	for (i = 0; i < nr_spec; i++)
-		argv[argc++] = specs[i];
-	argv[argc++] = NULL;
+		argv_array_push(&args, specs[i]);
 
 	memset(&rpc, 0, sizeof(rpc));
 	rpc.service_name = "git-receive-pack",
-	rpc.argv = argv;
+	rpc.argv = args.argv;
 
 	err = rpc_service(&rpc, heads);
 	if (rpc.result.len)
 		write_or_die(1, rpc.result.buf, rpc.result.len);
 	strbuf_release(&rpc.result);
-	free(argv);
+	argv_array_clear(&args);
 	return err;
 }
 
diff --git a/remote-testsvn.c b/remote-testsvn.c
index 5ddf11c..d7cd5d2 100644
--- a/remote-testsvn.c
+++ b/remote-testsvn.c
@@ -286,7 +286,7 @@
 	return 0;
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
 			private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
diff --git a/remote.c b/remote.c
index 68eb99b..efcba93 100644
--- a/remote.c
+++ b/remote.c
@@ -276,10 +276,9 @@
 
 static void read_branches_file(struct remote *remote)
 {
-	const char *slash = strchr(remote->name, '/');
 	char *frag;
 	struct strbuf branch = STRBUF_INIT;
-	int n = slash ? slash - remote->name : 1000;
+	int n = 1000;
 	FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
 	char *s, *p;
 	int len;
@@ -299,21 +298,11 @@
 	while (isspace(p[-1]))
 		*--p = 0;
 	len = p - s;
-	if (slash)
-		len += strlen(slash);
 	p = xmalloc(len + 1);
 	strcpy(p, s);
-	if (slash)
-		strcat(p, slash);
 
 	/*
-	 * With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
-	 * reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
-	 * the partial URL obtained from the branches file plus
-	 * "/netdev-2.6" and does not store it in any tracking ref.
-	 * #branch specifier in the file is ignored.
-	 *
-	 * Otherwise, the branches file would have URL and optionally
+	 * The branches file would have URL and optionally
 	 * #branch specified.  The "master" (or specified) branch is
 	 * fetched and stored in the local branch of the same name.
 	 */
@@ -323,12 +312,8 @@
 		strbuf_addf(&branch, "refs/heads/%s", frag);
 	} else
 		strbuf_addstr(&branch, "refs/heads/master");
-	if (!slash) {
-		strbuf_addf(&branch, ":refs/heads/%s", remote->name);
-	} else {
-		strbuf_reset(&branch);
-		strbuf_addstr(&branch, "HEAD:");
-	}
+
+	strbuf_addf(&branch, ":refs/heads/%s", remote->name);
 	add_url_alias(remote, p);
 	add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
 	/*
@@ -1317,6 +1302,14 @@
 	free(sent_tips.tip);
 }
 
+static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
+{
+	for ( ; ref; ref = ref->next)
+		string_list_append_nodup(ref_index, ref->name)->util = ref;
+
+	sort_string_list(ref_index);
+}
+
 /*
  * Given the set of refs the local repository has, the set of refs the
  * remote repository has, and the refspec used for push, determine
@@ -1335,6 +1328,7 @@
 	int errs;
 	static const char *default_refspec[] = { ":", NULL };
 	struct ref *ref, **dst_tail = tail_ref(dst);
+	struct string_list dst_ref_index = STRING_LIST_INIT_NODUP;
 
 	if (!nr_refspec) {
 		nr_refspec = 1;
@@ -1345,6 +1339,7 @@
 
 	/* pick the remainder */
 	for (ref = src; ref; ref = ref->next) {
+		struct string_list_item *dst_item;
 		struct ref *dst_peer;
 		const struct refspec *pat = NULL;
 		char *dst_name;
@@ -1353,7 +1348,11 @@
 		if (!dst_name)
 			continue;
 
-		dst_peer = find_ref_by_name(*dst, dst_name);
+		if (!dst_ref_index.nr)
+			prepare_ref_index(&dst_ref_index, *dst);
+
+		dst_item = string_list_lookup(&dst_ref_index, dst_name);
+		dst_peer = dst_item ? dst_item->util : NULL;
 		if (dst_peer) {
 			if (dst_peer->peer_ref)
 				/* We're already sending something to this ref. */
@@ -1370,6 +1369,8 @@
 			/* Create a new one and link it */
 			dst_peer = make_linked_ref(dst_name, &dst_tail);
 			hashcpy(dst_peer->new_sha1, ref->new_sha1);
+			string_list_insert(&dst_ref_index,
+				dst_peer->name)->util = dst_peer;
 		}
 		dst_peer->peer_ref = copy_ref(ref);
 		dst_peer->force = pat->force;
@@ -1377,10 +1378,13 @@
 		free(dst_name);
 	}
 
+	string_list_clear(&dst_ref_index, 0);
+
 	if (flags & MATCH_REFS_FOLLOW_TAGS)
 		add_missing_tags(src, dst, &dst_tail);
 
 	if (send_prune) {
+		struct string_list src_ref_index = STRING_LIST_INIT_NODUP;
 		/* check for missing refs on the remote */
 		for (ref = *dst; ref; ref = ref->next) {
 			char *src_name;
@@ -1391,11 +1395,15 @@
 
 			src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
 			if (src_name) {
-				if (!find_ref_by_name(src, src_name))
+				if (!src_ref_index.nr)
+					prepare_ref_index(&src_ref_index, src);
+				if (!string_list_has_string(&src_ref_index,
+					    src_name))
 					ref->peer_ref = alloc_delete_ref();
 				free(src_name);
 			}
 		}
+		string_list_clear(&src_ref_index, 0);
 	}
 	if (errs)
 		return -1;
@@ -1474,8 +1482,7 @@
 		ret->remote = remote_get(ret->remote_name);
 		if (ret->merge_nr) {
 			int i;
-			ret->merge = xcalloc(sizeof(*ret->merge),
-					     ret->merge_nr);
+			ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
 			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]);
diff --git a/rerere.c b/rerere.c
index 98e3e29..6fde8f9 100644
--- a/rerere.c
+++ b/rerere.c
@@ -299,7 +299,7 @@
 {
 	mmfile_t mmfile[3] = {{NULL}};
 	mmbuffer_t result = {NULL, 0};
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 	int pos, len, i, hunk_no;
 	struct rerere_io_mem io;
 	int marker_size = ll_merge_marker_size(path);
@@ -359,7 +359,7 @@
 
 static int check_one_conflict(int i, int *type)
 {
-	struct cache_entry *e = active_cache[i];
+	const struct cache_entry *e = active_cache[i];
 
 	if (!ce_stage(e)) {
 		*type = RESOLVED;
@@ -374,8 +374,8 @@
 
 	/* Only handle regular files with both stages #2 and #3 */
 	if (i + 1 < active_nr) {
-		struct cache_entry *e2 = active_cache[i];
-		struct cache_entry *e3 = active_cache[i + 1];
+		const struct cache_entry *e2 = active_cache[i];
+		const struct cache_entry *e3 = active_cache[i + 1];
 		if (ce_stage(e2) == 2 &&
 		    ce_stage(e3) == 3 &&
 		    ce_same_name(e, e3) &&
@@ -398,7 +398,7 @@
 
 	for (i = 0; i < active_nr;) {
 		int conflict_type;
-		struct cache_entry *e = active_cache[i];
+		const struct cache_entry *e = active_cache[i];
 		i = check_one_conflict(i, &conflict_type);
 		if (conflict_type == THREE_STAGED)
 			string_list_insert(conflict, (const char *)e->name);
@@ -414,7 +414,7 @@
 
 	for (i = 0; i < active_nr;) {
 		int conflict_type;
-		struct cache_entry *e = active_cache[i];
+		const struct cache_entry *e = active_cache[i];
 		i = check_one_conflict(i, &conflict_type);
 		if (conflict_type == PUNTED)
 			string_list_insert(merge_rr, (const char *)e->name);
diff --git a/resolve-undo.c b/resolve-undo.c
index 639eb9c..77101f5 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -115,7 +115,7 @@
 
 int unmerge_index_entry_at(struct index_state *istate, int pos)
 {
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 	struct string_list_item *item;
 	struct resolve_undo_info *ru;
 	int i, err = 0, matched;
@@ -167,7 +167,7 @@
 		return;
 
 	for (i = 0; i < istate->cache_nr; i++) {
-		struct cache_entry *ce = istate->cache[i];
+		const struct cache_entry *ce = istate->cache[i];
 		if (ce->ce_flags & CE_MATCHED)
 			i = unmerge_index_entry_at(istate, i);
 	}
@@ -181,7 +181,7 @@
 		return;
 
 	for (i = 0; i < istate->cache_nr; i++) {
-		struct cache_entry *ce = istate->cache[i];
+		const struct cache_entry *ce = istate->cache[i];
 		if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
 			continue;
 		i = unmerge_index_entry_at(istate, i);
diff --git a/revision.c b/revision.c
index a67b615..84ccc05 100644
--- a/revision.c
+++ b/revision.c
@@ -13,6 +13,7 @@
 #include "decorate.h"
 #include "log-tree.h"
 #include "string-list.h"
+#include "line-log.h"
 #include "mailmap.h"
 
 volatile show_early_output_fn_t show_early_output;
@@ -70,7 +71,8 @@
 	return ours || emitted;
 }
 
-void show_object_with_name(FILE *out, struct object *obj, const struct name_path *path, const char *component)
+void show_object_with_name(FILE *out, struct object *obj,
+			   const struct name_path *path, const char *component)
 {
 	struct name_path leaf;
 	leaf.up = (struct name_path *)path;
@@ -87,7 +89,9 @@
 		struct name_path *path,
 		const char *name)
 {
-	add_object_array(obj, path_name(path, name), p);
+	char *pn = path_name(path, name);
+	add_object_array(obj, pn, p);
+	free(pn);
 }
 
 static void mark_blob_uninteresting(struct blob *blob)
@@ -186,7 +190,9 @@
 	}
 }
 
-static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
+static void add_pending_object_with_mode(struct rev_info *revs,
+					 struct object *obj,
+					 const char *name, unsigned mode)
 {
 	if (!obj)
 		return;
@@ -209,7 +215,8 @@
 	add_object_array_with_mode(obj, name, &revs->pending, mode);
 }
 
-void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+void add_pending_object(struct rev_info *revs,
+			struct object *obj, const char *name)
 {
 	add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
 }
@@ -226,7 +233,9 @@
 	add_pending_object(revs, obj, "HEAD");
 }
 
-static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
+static struct object *get_reference(struct rev_info *revs, const char *name,
+				    const unsigned char *sha1,
+				    unsigned int flags)
 {
 	struct object *object;
 
@@ -247,7 +256,8 @@
 	add_pending_object(revs, object, name);
 }
 
-static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
+static struct commit *handle_commit(struct rev_info *revs,
+				    struct object *object, const char *name)
 {
 	unsigned long flags = object->flags;
 
@@ -333,6 +343,80 @@
 }
 
 /*
+ * A definition of "relevant" commit that we can use to simplify limited graphs
+ * by eliminating side branches.
+ *
+ * A "relevant" commit is one that is !UNINTERESTING (ie we are including it
+ * in our list), or that is a specified BOTTOM commit. Then after computing
+ * a limited list, during processing we can generally ignore boundary merges
+ * coming from outside the graph, (ie from irrelevant parents), and treat
+ * those merges as if they were single-parent. TREESAME is defined to consider
+ * only relevant parents, if any. If we are TREESAME to our on-graph parents,
+ * we don't care if we were !TREESAME to non-graph parents.
+ *
+ * Treating bottom commits as relevant ensures that a limited graph's
+ * connection to the actual bottom commit is not viewed as a side branch, but
+ * treated as part of the graph. For example:
+ *
+ *   ....Z...A---X---o---o---B
+ *        .     /
+ *         W---Y
+ *
+ * When computing "A..B", the A-X connection is at least as important as
+ * Y-X, despite A being flagged UNINTERESTING.
+ *
+ * And when computing --ancestry-path "A..B", the A-X connection is more
+ * important than Y-X, despite both A and Y being flagged UNINTERESTING.
+ */
+static inline int relevant_commit(struct commit *commit)
+{
+	return (commit->object.flags & (UNINTERESTING | BOTTOM)) != UNINTERESTING;
+}
+
+/*
+ * Return a single relevant commit from a parent list. If we are a TREESAME
+ * commit, and this selects one of our parents, then we can safely simplify to
+ * that parent.
+ */
+static struct commit *one_relevant_parent(const struct rev_info *revs,
+					  struct commit_list *orig)
+{
+	struct commit_list *list = orig;
+	struct commit *relevant = NULL;
+
+	if (!orig)
+		return NULL;
+
+	/*
+	 * For 1-parent commits, or if first-parent-only, then return that
+	 * first parent (even if not "relevant" by the above definition).
+	 * TREESAME will have been set purely on that parent.
+	 */
+	if (revs->first_parent_only || !orig->next)
+		return orig->item;
+
+	/*
+	 * For multi-parent commits, identify a sole relevant parent, if any.
+	 * If we have only one relevant parent, then TREESAME will be set purely
+	 * with regard to that parent, and we can simplify accordingly.
+	 *
+	 * If we have more than one relevant parent, or no relevant parents
+	 * (and multiple irrelevant ones), then we can't select a parent here
+	 * and return NULL.
+	 */
+	while (list) {
+		struct commit *commit = list->item;
+		list = list->next;
+		if (relevant_commit(commit)) {
+			if (relevant)
+				return NULL;
+			relevant = commit;
+		}
+	}
+	return relevant;
+}
+
+/*
  * The goal is to get REV_TREE_NEW as the result only if the
  * diff consists of all '+' (and no other changes), REV_TREE_OLD
  * if the whole diff is removal of old data, and otherwise
@@ -368,7 +452,8 @@
 	DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
-static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
+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;
@@ -429,10 +514,125 @@
 	return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
 
+struct treesame_state {
+	unsigned int nparents;
+	unsigned char treesame[FLEX_ARRAY];
+};
+
+static struct treesame_state *initialise_treesame(struct rev_info *revs, struct commit *commit)
+{
+	unsigned n = commit_list_count(commit->parents);
+	struct treesame_state *st = xcalloc(1, sizeof(*st) + n);
+	st->nparents = n;
+	add_decoration(&revs->treesame, &commit->object, st);
+	return st;
+}
+
+/*
+ * Must be called immediately after removing the nth_parent from a commit's
+ * parent list, if we are maintaining the per-parent treesame[] decoration.
+ * This does not recalculate the master TREESAME flag - update_treesame()
+ * should be called to update it after a sequence of treesame[] modifications
+ * that may have affected it.
+ */
+static int compact_treesame(struct rev_info *revs, struct commit *commit, unsigned nth_parent)
+{
+	struct treesame_state *st;
+	int old_same;
+
+	if (!commit->parents) {
+		/*
+		 * Have just removed the only parent from a non-merge.
+		 * Different handling, as we lack decoration.
+		 */
+		if (nth_parent != 0)
+			die("compact_treesame %u", nth_parent);
+		old_same = !!(commit->object.flags & TREESAME);
+		if (rev_same_tree_as_empty(revs, commit))
+			commit->object.flags |= TREESAME;
+		else
+			commit->object.flags &= ~TREESAME;
+		return old_same;
+	}
+
+	st = lookup_decoration(&revs->treesame, &commit->object);
+	if (!st || nth_parent >= st->nparents)
+		die("compact_treesame %u", nth_parent);
+
+	old_same = st->treesame[nth_parent];
+	memmove(st->treesame + nth_parent,
+		st->treesame + nth_parent + 1,
+		st->nparents - nth_parent - 1);
+
+	/*
+	 * If we've just become a non-merge commit, update TREESAME
+	 * immediately, and remove the no-longer-needed decoration.
+	 * If still a merge, defer update until update_treesame().
+	 */
+	if (--st->nparents == 1) {
+		if (commit->parents->next)
+			die("compact_treesame parents mismatch");
+		if (st->treesame[0] && revs->dense)
+			commit->object.flags |= TREESAME;
+		else
+			commit->object.flags &= ~TREESAME;
+		free(add_decoration(&revs->treesame, &commit->object, NULL));
+	}
+
+	return old_same;
+}
+
+static unsigned update_treesame(struct rev_info *revs, struct commit *commit)
+{
+	if (commit->parents && commit->parents->next) {
+		unsigned n;
+		struct treesame_state *st;
+		struct commit_list *p;
+		unsigned relevant_parents;
+		unsigned relevant_change, irrelevant_change;
+
+		st = lookup_decoration(&revs->treesame, &commit->object);
+		if (!st)
+			die("update_treesame %s", sha1_to_hex(commit->object.sha1));
+		relevant_parents = 0;
+		relevant_change = irrelevant_change = 0;
+		for (p = commit->parents, n = 0; p; n++, p = p->next) {
+			if (relevant_commit(p->item)) {
+				relevant_change |= !st->treesame[n];
+				relevant_parents++;
+			} else
+				irrelevant_change |= !st->treesame[n];
+		}
+		if (relevant_parents ? relevant_change : irrelevant_change)
+			commit->object.flags &= ~TREESAME;
+		else
+			commit->object.flags |= TREESAME;
+	}
+
+	return commit->object.flags & TREESAME;
+}
+
+static inline int limiting_can_increase_treesame(const struct rev_info *revs)
+{
+	/*
+	 * TREESAME is irrelevant unless prune && dense;
+	 * if simplify_history is set, we can't have a mixture of TREESAME and
+	 *    !TREESAME INTERESTING parents (and we don't have treesame[]
+	 *    decoration anyway);
+	 * if first_parent_only is set, then the TREESAME flag is locked
+	 *    against the first parent (and again we lack treesame[] decoration).
+	 */
+	return revs->prune && revs->dense &&
+	       !revs->simplify_history &&
+	       !revs->first_parent_only;
+}
+
 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
 {
 	struct commit_list **pp, *parent;
-	int tree_changed = 0, tree_same = 0, nth_parent = 0;
+	struct treesame_state *ts = NULL;
+	int relevant_change = 0, irrelevant_change = 0;
+	int relevant_parents, nth_parent;
 
 	/*
 	 * If we don't do pruning, everything is interesting
@@ -456,33 +656,54 @@
 	if (!revs->dense && !commit->parents->next)
 		return;
 
-	pp = &commit->parents;
-	while ((parent = *pp) != NULL) {
+	for (pp = &commit->parents, nth_parent = 0, relevant_parents = 0;
+	     (parent = *pp) != NULL;
+	     pp = &parent->next, nth_parent++) {
 		struct commit *p = parent->item;
+		if (relevant_commit(p))
+			relevant_parents++;
 
-		/*
-		 * Do not compare with later parents when we care only about
-		 * the first parent chain, in order to avoid derailing the
-		 * traversal to follow a side branch that brought everything
-		 * in the path we are limited to by the pathspec.
-		 */
-		if (revs->first_parent_only && nth_parent++)
-			break;
+		if (nth_parent == 1) {
+			/*
+			 * This our second loop iteration - so we now know
+			 * we're dealing with a merge.
+			 *
+			 * Do not compare with later parents when we care only about
+			 * the first parent chain, in order to avoid derailing the
+			 * traversal to follow a side branch that brought everything
+			 * in the path we are limited to by the pathspec.
+			 */
+			if (revs->first_parent_only)
+				break;
+			/*
+			 * If this will remain a potentially-simplifiable
+			 * merge, remember per-parent treesame if needed.
+			 * Initialise the array with the comparison from our
+			 * first iteration.
+			 */
+			if (revs->treesame.name &&
+			    !revs->simplify_history &&
+			    !(commit->object.flags & UNINTERESTING)) {
+				ts = initialise_treesame(revs, commit);
+				if (!(irrelevant_change || relevant_change))
+					ts->treesame[0] = 1;
+			}
+		}
 		if (parse_commit(p) < 0)
 			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, commit)) {
 		case REV_TREE_SAME:
-			tree_same = 1;
-			if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
+			if (!revs->simplify_history || !relevant_commit(p)) {
 				/* Even if a merge with an uninteresting
 				 * side branch brought the entire change
 				 * we are interested in, we do not want
 				 * to lose the other branches of this
 				 * merge, so we just keep going.
 				 */
-				pp = &parent->next;
+				if (ts)
+					ts->treesame[nth_parent] = 1;
 				continue;
 			}
 			parent->next = NULL;
@@ -510,15 +731,27 @@
 		/* fallthrough */
 		case REV_TREE_OLD:
 		case REV_TREE_DIFFERENT:
-			tree_changed = 1;
-			pp = &parent->next;
+			if (relevant_commit(p))
+				relevant_change = 1;
+			else
+				irrelevant_change = 1;
 			continue;
 		}
 		die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
 	}
-	if (tree_changed && !tree_same)
-		return;
-	commit->object.flags |= TREESAME;
+
+	/*
+	 * TREESAME is straightforward for single-parent commits. For merge
+	 * commits, it is most useful to define it so that "irrelevant"
+	 * parents cannot make us !TREESAME - if we have any relevant
+	 * parents, then we only consider TREESAMEness with respect to them,
+	 * allowing irrelevant merges from uninteresting branches to be
+	 * simplified away. Only if we have only irrelevant parents do we
+	 * base TREESAME on them. Note that this logic is replicated in
+	 * update_treesame, which should be kept in sync.
+	 */
+	if (relevant_parents ? !relevant_change : !irrelevant_change)
+		commit->object.flags |= TREESAME;
 }
 
 static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
@@ -801,16 +1034,12 @@
  * to filter the result of "A..B" further to the ones that can actually
  * reach A.
  */
-static struct commit_list *collect_bottom_commits(struct rev_info *revs)
+static struct commit_list *collect_bottom_commits(struct commit_list *list)
 {
-	struct commit_list *bottom = NULL;
-	int i;
-	for (i = 0; i < revs->cmdline.nr; i++) {
-		struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
-		if ((elem->flags & UNINTERESTING) &&
-		    elem->item->type == OBJ_COMMIT)
-			commit_list_insert((struct commit *)elem->item, &bottom);
-	}
+	struct commit_list *elem, *bottom = NULL;
+	for (elem = list; elem; elem = elem->next)
+		if (elem->item->object.flags & BOTTOM)
+			commit_list_insert(elem->item, &bottom);
 	return bottom;
 }
 
@@ -841,7 +1070,7 @@
 	struct commit_list *bottom = NULL;
 
 	if (revs->ancestry_path) {
-		bottom = collect_bottom_commits(revs);
+		bottom = collect_bottom_commits(list);
 		if (!bottom)
 			die("--ancestry-path given but there are no bottom commits");
 	}
@@ -894,10 +1123,26 @@
 		free_commit_list(bottom);
 	}
 
+	/*
+	 * Check if any commits have become TREESAME by some of their parents
+	 * becoming UNINTERESTING.
+	 */
+	if (limiting_can_increase_treesame(revs))
+		for (list = newlist; list; list = list->next) {
+			struct commit *c = list->item;
+			if (c->object.flags & (UNINTERESTING | TREESAME))
+				continue;
+			update_treesame(revs, c);
+		}
+
 	revs->commits = newlist;
 	return 0;
 }
 
+/*
+ * Add an entry to refs->cmdline with the specified information.
+ * *name is copied.
+ */
 static void add_rev_cmdline(struct rev_info *revs,
 			    struct object *item,
 			    const char *name,
@@ -909,12 +1154,25 @@
 
 	ALLOC_GROW(info->rev, nr + 1, info->alloc);
 	info->rev[nr].item = item;
-	info->rev[nr].name = name;
+	info->rev[nr].name = xstrdup(name);
 	info->rev[nr].whence = whence;
 	info->rev[nr].flags = flags;
 	info->nr++;
 }
 
+static void add_rev_cmdline_list(struct rev_info *revs,
+				 struct commit_list *commit_list,
+				 int whence,
+				 unsigned flags)
+{
+	while (commit_list) {
+		struct object *object = &commit_list->item->object;
+		add_rev_cmdline(revs, object, sha1_to_hex(object->sha1),
+				whence, flags);
+		commit_list = commit_list->next;
+	}
+}
+
 struct all_refs_cb {
 	int all_flags;
 	int warned_bad_reflog;
@@ -1000,7 +1258,7 @@
 	const char *arg = arg_;
 
 	if (*arg == '^') {
-		flags ^= UNINTERESTING;
+		flags ^= UNINTERESTING | BOTTOM;
 		arg++;
 	}
 	if (get_sha1_committish(arg, sha1))
@@ -1038,7 +1296,7 @@
 	DIFF_OPT_SET(&revs->pruning, QUICK);
 	revs->pruning.add_remove = file_add_remove;
 	revs->pruning.change = file_change;
-	revs->lifo = 1;
+	revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
 	revs->dense = 1;
 	revs->prefix = prefix;
 	revs->max_age = -1;
@@ -1092,14 +1350,15 @@
 	add_pending_object(revs, &head->object, "HEAD");
 	add_pending_object(revs, &other->object, "MERGE_HEAD");
 	bases = get_merge_bases(head, other, 1);
-	add_pending_commit_list(revs, bases, UNINTERESTING);
+	add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
+	add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
 	free_commit_list(bases);
 	head->object.flags |= SYMMETRIC_LEFT;
 
 	if (!active_nr)
 		read_cache();
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		if (!ce_stage(ce))
 			continue;
 		if (ce_path_match(ce, &revs->prune_data)) {
@@ -1128,13 +1387,15 @@
 	int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
 	unsigned get_sha1_flags = 0;
 
+	flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
+
 	dotdot = strstr(arg, "..");
 	if (dotdot) {
 		unsigned char from_sha1[20];
 		const char *next = dotdot + 2;
 		const char *this = arg;
 		int symmetric = *next == '.';
-		unsigned int flags_exclude = flags ^ UNINTERESTING;
+		unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
 		static const char head_by_default[] = "HEAD";
 		unsigned int a_flags;
 
@@ -1179,6 +1440,9 @@
 
 			if (symmetric) {
 				exclude = get_merge_bases(a, b, 1);
+				add_rev_cmdline_list(revs, exclude,
+						     REV_CMD_MERGE_BASE,
+						     flags_exclude);
 				add_pending_commit_list(revs, exclude,
 							flags_exclude);
 				free_commit_list(exclude);
@@ -1207,13 +1471,13 @@
 	dotdot = strstr(arg, "^!");
 	if (dotdot && !dotdot[2]) {
 		*dotdot = 0;
-		if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
+		if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM)))
 			*dotdot = '^';
 	}
 
 	local_flags = 0;
 	if (*arg == '^') {
-		local_flags = UNINTERESTING;
+		local_flags = UNINTERESTING | BOTTOM;
 		arg++;
 	}
 
@@ -1276,7 +1540,7 @@
 			}
 			die("options not supported in --stdin mode");
 		}
-		if (handle_revision_arg(xstrdup(sb.buf), revs, 0,
+		if (handle_revision_arg(sb.buf, revs, 0,
 					REVARG_CANNOT_BE_FILENAME))
 			die("bad revision '%s'", sb.buf);
 	}
@@ -1374,7 +1638,7 @@
 	} else if (!strcmp(arg, "--merge")) {
 		revs->show_merge = 1;
 	} else if (!strcmp(arg, "--topo-order")) {
-		revs->lifo = 1;
+		revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
 		revs->topo_order = 1;
 	} else if (!strcmp(arg, "--simplify-merges")) {
 		revs->simplify_merges = 1;
@@ -1392,7 +1656,10 @@
 		revs->prune = 1;
 		load_ref_decorations(DECORATE_SHORT_REFS);
 	} else if (!strcmp(arg, "--date-order")) {
-		revs->lifo = 0;
+		revs->sort_order = REV_SORT_BY_COMMIT_DATE;
+		revs->topo_order = 1;
+	} else if (!strcmp(arg, "--author-date-order")) {
+		revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
 		revs->topo_order = 1;
 	} else if (!prefixcmp(arg, "--early-output")) {
 		int count = 100;
@@ -1690,7 +1957,7 @@
 		handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
 	} else if (!strcmp(arg, "--bisect")) {
 		handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
-		handle_refs(submodule, revs, *flags ^ UNINTERESTING, for_each_good_bisect_ref);
+		handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
 		revs->bisect = 1;
 	} else if (!strcmp(arg, "--tags")) {
 		handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
@@ -1716,7 +1983,7 @@
 	} else if (!strcmp(arg, "--reflog")) {
 		handle_reflog(revs, *flags);
 	} else if (!strcmp(arg, "--not")) {
-		*flags ^= UNINTERESTING;
+		*flags ^= UNINTERESTING | BOTTOM;
 	} else if (!strcmp(arg, "--no-walk")) {
 		revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
 	} else if (!prefixcmp(arg, "--no-walk=")) {
@@ -1897,6 +2164,12 @@
 	if (revs->combine_merges)
 		revs->ignore_merges = 0;
 	revs->diffopt.abbrev = revs->abbrev;
+
+	if (revs->line_level_traverse) {
+		revs->limited = 1;
+		revs->topo_order = 1;
+	}
+
 	diff_setup_done(&revs->diffopt);
 
 	grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
@@ -1930,28 +2203,32 @@
 	l->next = add_decoration(&revs->children, &parent->object, l);
 }
 
-static int remove_duplicate_parents(struct commit *commit)
+static int remove_duplicate_parents(struct rev_info *revs, struct commit *commit)
 {
+	struct treesame_state *ts = lookup_decoration(&revs->treesame, &commit->object);
 	struct commit_list **pp, *p;
 	int surviving_parents;
 
 	/* Examine existing parents while marking ones we have seen... */
 	pp = &commit->parents;
+	surviving_parents = 0;
 	while ((p = *pp) != NULL) {
 		struct commit *parent = p->item;
 		if (parent->object.flags & TMP_MARK) {
 			*pp = p->next;
+			if (ts)
+				compact_treesame(revs, commit, surviving_parents);
 			continue;
 		}
 		parent->object.flags |= TMP_MARK;
+		surviving_parents++;
 		pp = &p->next;
 	}
-	/* count them while clearing the temporary mark */
-	surviving_parents = 0;
+	/* clear the temporary mark */
 	for (p = commit->parents; p; p = p->next) {
 		p->item->object.flags &= ~TMP_MARK;
-		surviving_parents++;
 	}
+	/* no update_treesame() - removing duplicates can't affect TREESAME */
 	return surviving_parents;
 }
 
@@ -1971,9 +2248,157 @@
 	return st;
 }
 
+static int mark_redundant_parents(struct rev_info *revs, struct commit *commit)
+{
+	struct commit_list *h = reduce_heads(commit->parents);
+	int i = 0, marked = 0;
+	struct commit_list *po, *pn;
+
+	/* Want these for sanity-checking only */
+	int orig_cnt = commit_list_count(commit->parents);
+	int cnt = commit_list_count(h);
+
+	/*
+	 * Not ready to remove items yet, just mark them for now, based
+	 * on the output of reduce_heads(). reduce_heads outputs the reduced
+	 * set in its original order, so this isn't too hard.
+	 */
+	po = commit->parents;
+	pn = h;
+	while (po) {
+		if (pn && po->item == pn->item) {
+			pn = pn->next;
+			i++;
+		} else {
+			po->item->object.flags |= TMP_MARK;
+			marked++;
+		}
+		po=po->next;
+	}
+
+	if (i != cnt || cnt+marked != orig_cnt)
+		die("mark_redundant_parents %d %d %d %d", orig_cnt, cnt, i, marked);
+
+	free_commit_list(h);
+
+	return marked;
+}
+
+static int mark_treesame_root_parents(struct rev_info *revs, struct commit *commit)
+{
+	struct commit_list *p;
+	int marked = 0;
+
+	for (p = commit->parents; p; p = p->next) {
+		struct commit *parent = p->item;
+		if (!parent->parents && (parent->object.flags & TREESAME)) {
+			parent->object.flags |= TMP_MARK;
+			marked++;
+		}
+	}
+
+	return marked;
+}
+
+/*
+ * Awkward naming - this means one parent we are TREESAME to.
+ * cf mark_treesame_root_parents: root parents that are TREESAME (to an
+ * empty tree). Better name suggestions?
+ */
+static int leave_one_treesame_to_parent(struct rev_info *revs, struct commit *commit)
+{
+	struct treesame_state *ts = lookup_decoration(&revs->treesame, &commit->object);
+	struct commit *unmarked = NULL, *marked = NULL;
+	struct commit_list *p;
+	unsigned n;
+
+	for (p = commit->parents, n = 0; p; p = p->next, n++) {
+		if (ts->treesame[n]) {
+			if (p->item->object.flags & TMP_MARK) {
+				if (!marked)
+					marked = p->item;
+			} else {
+				if (!unmarked) {
+					unmarked = p->item;
+					break;
+				}
+			}
+		}
+	}
+
+	/*
+	 * If we are TREESAME to a marked-for-deletion parent, but not to any
+	 * unmarked parents, unmark the first TREESAME parent. This is the
+	 * parent that the default simplify_history==1 scan would have followed,
+	 * and it doesn't make sense to omit that path when asking for a
+	 * simplified full history. Retaining it improves the chances of
+	 * understanding odd missed merges that took an old version of a file.
+	 *
+	 * Example:
+	 *
+	 *   I--------*X       A modified the file, but mainline merge X used
+	 *    \       /        "-s ours", so took the version from I. X is
+	 *     `-*A--'         TREESAME to I and !TREESAME to A.
+	 *
+	 * Default log from X would produce "I". Without this check,
+	 * --full-history --simplify-merges would produce "I-A-X", showing
+	 * the merge commit X and that it changed A, but not making clear that
+	 * it had just taken the I version. With this check, the topology above
+	 * is retained.
+	 *
+	 * Note that it is possible that the simplification chooses a different
+	 * TREESAME parent from the default, in which case this test doesn't
+	 * activate, and we _do_ drop the default parent. Example:
+	 *
+	 *   I------X         A modified the file, but it was reverted in B,
+	 *    \    /          meaning mainline merge X is TREESAME to both
+	 *    *A-*B           parents.
+	 *
+	 * Default log would produce "I" by following the first parent;
+	 * --full-history --simplify-merges will produce "I-A-B". But this is a
+	 * reasonable result - it presents a logical full history leading from
+	 * I to X, and X is not an important merge.
+	 */
+	if (!unmarked && marked) {
+		marked->object.flags &= ~TMP_MARK;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int remove_marked_parents(struct rev_info *revs, struct commit *commit)
+{
+	struct commit_list **pp, *p;
+	int nth_parent, removed = 0;
+
+	pp = &commit->parents;
+	nth_parent = 0;
+	while ((p = *pp) != NULL) {
+		struct commit *parent = p->item;
+		if (parent->object.flags & TMP_MARK) {
+			parent->object.flags &= ~TMP_MARK;
+			*pp = p->next;
+			free(p);
+			removed++;
+			compact_treesame(revs, commit, nth_parent);
+			continue;
+		}
+		pp = &p->next;
+		nth_parent++;
+	}
+
+	/* Removing parents can only increase TREESAMEness */
+	if (removed && !(commit->object.flags & TREESAME))
+		update_treesame(revs, commit);
+
+	return nth_parent;
+}
+
 static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
 {
 	struct commit_list *p;
+	struct commit *parent;
 	struct merge_simplify_state *st, *pst;
 	int cnt;
 
@@ -2015,7 +2440,9 @@
 	}
 
 	/*
-	 * Rewrite our list of parents.
+	 * Rewrite our list of parents. Note that this cannot
+	 * affect our TREESAME flags in any way - a commit is
+	 * always TREESAME to its simplification.
 	 */
 	for (p = commit->parents; p; p = p->next) {
 		pst = locate_simplify_state(revs, p->item);
@@ -2027,43 +2454,53 @@
 	if (revs->first_parent_only)
 		cnt = 1;
 	else
-		cnt = remove_duplicate_parents(commit);
+		cnt = remove_duplicate_parents(revs, 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.
+	 * in such a case, the immediate parent from that branch
+	 * will be rewritten to be the merge base.
 	 *
 	 *      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.
+	 * Further, a merge of an independent branch that doesn't
+	 * touch the path will reduce to a treesame root parent:
+	 *
+	 *  ----o----X		X: the commit we are looking at;
+	 *          /		o: a commit that touches the paths;
+	 *         r		r: a root commit not touching the paths
+	 *
+	 * Detect and simplify both cases.
 	 */
 	if (1 < cnt) {
-		struct commit_list *h = reduce_heads(commit->parents);
-		cnt = commit_list_count(h);
-		free_commit_list(commit->parents);
-		commit->parents = h;
+		int marked = mark_redundant_parents(revs, commit);
+		marked += mark_treesame_root_parents(revs, commit);
+		if (marked)
+			marked -= leave_one_treesame_to_parent(revs, commit);
+		if (marked)
+			cnt = remove_marked_parents(revs, commit);
 	}
 
 	/*
 	 * 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
+	 * merge and its parents don't simplify to one relevant commit
 	 * (the first two cases are already handled at the beginning of
 	 * this function).
 	 *
-	 * Otherwise, it simplifies to what its sole parent simplifies to.
+	 * Otherwise, it simplifies to what its sole relevant parent
+	 * simplifies to.
 	 */
 	if (!cnt ||
 	    (commit->object.flags & UNINTERESTING) ||
 	    !(commit->object.flags & TREESAME) ||
-	    (1 < cnt))
+	    (parent = one_relevant_parent(revs, commit->parents)) == NULL)
 		st->simplified = commit;
 	else {
-		pst = locate_simplify_state(revs, commit->parents->item);
+		pst = locate_simplify_state(revs, parent);
 		st->simplified = pst->simplified;
 	}
 	return tail;
@@ -2159,6 +2596,11 @@
 	if (!revs->leak_pending)
 		free(list);
 
+	/* Signal whether we need per-parent treesame decoration */
+	if (revs->simplify_merges ||
+	    (revs->limited && limiting_can_increase_treesame(revs)))
+		revs->treesame.name = "treesame";
+
 	if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
 		commit_list_sort_by_date(&revs->commits);
 	if (revs->no_walk)
@@ -2167,7 +2609,9 @@
 		if (limit_list(revs) < 0)
 			return -1;
 	if (revs->topo_order)
-		sort_in_topological_order(&revs->commits, revs->lifo);
+		sort_in_topological_order(&revs->commits, revs->sort_order);
+	if (revs->line_level_traverse)
+		line_log_filter(revs);
 	if (revs->simplify_merges)
 		simplify_merges(revs);
 	if (revs->children.name)
@@ -2175,12 +2619,6 @@
 	return 0;
 }
 
-enum rewrite_result {
-	rewrite_one_ok,
-	rewrite_one_noparents,
-	rewrite_one_error
-};
-
 static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
 {
 	struct commit_list *cache = NULL;
@@ -2190,24 +2628,25 @@
 		if (!revs->limited)
 			if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
 				return rewrite_one_error;
-		if (p->parents && p->parents->next)
-			return rewrite_one_ok;
 		if (p->object.flags & UNINTERESTING)
 			return rewrite_one_ok;
 		if (!(p->object.flags & TREESAME))
 			return rewrite_one_ok;
 		if (!p->parents)
 			return rewrite_one_noparents;
-		*pp = p->parents->item;
+		if ((p = one_relevant_parent(revs, p->parents)) == NULL)
+			return rewrite_one_ok;
+		*pp = p;
 	}
 }
 
-static int rewrite_parents(struct rev_info *revs, struct commit *commit)
+int rewrite_parents(struct rev_info *revs, struct commit *commit,
+	rewrite_parent_fn_t rewrite_parent)
 {
 	struct commit_list **pp = &commit->parents;
 	while (*pp) {
 		struct commit_list *parent = *pp;
-		switch (rewrite_one(revs, &parent->item)) {
+		switch (rewrite_parent(revs, &parent->item)) {
 		case rewrite_one_ok:
 			break;
 		case rewrite_one_noparents:
@@ -2218,7 +2657,7 @@
 		}
 		pp = &parent->next;
 	}
-	remove_duplicate_parents(commit);
+	remove_duplicate_parents(revs, commit);
 	return 0;
 }
 
@@ -2342,10 +2781,7 @@
 	if (revs->min_age != -1 && (commit->date > revs->min_age))
 		return commit_ignore;
 	if (revs->min_parents || (revs->max_parents >= 0)) {
-		int n = 0;
-		struct commit_list *p;
-		for (p = commit->parents; p; p = p->next)
-			n++;
+		int n = commit_list_count(commit->parents);
 		if ((n < revs->min_parents) ||
 		    ((revs->max_parents >= 0) && (n > revs->max_parents)))
 			return commit_ignore;
@@ -2355,12 +2791,23 @@
 	if (revs->prune && revs->dense) {
 		/* Commit without changes? */
 		if (commit->object.flags & TREESAME) {
+			int n;
+			struct commit_list *p;
 			/* drop merges unless we want parenthood */
 			if (!want_ancestry(revs))
 				return commit_ignore;
-			/* non-merge - always ignore it */
-			if (!commit->parents || !commit->parents->next)
-				return commit_ignore;
+			/*
+			 * If we want ancestry, then need to keep any merges
+			 * between relevant commits to tie together topology.
+			 * For consistency with TREESAME and simplification
+			 * use "relevant" here rather than just INTERESTING,
+			 * to treat bottom commit(s) as part of the topology.
+			 */
+			for (n = 0, p = commit->parents; p; p = p->next)
+				if (relevant_commit(p->item))
+					if (++n >= 2)
+						return commit_show;
+			return commit_ignore;
 		}
 	}
 	return commit_show;
@@ -2373,7 +2820,7 @@
 	if (action == commit_show &&
 	    !revs->show_all &&
 	    revs->prune && revs->dense && want_ancestry(revs)) {
-		if (rewrite_parents(revs, commit) < 0)
+		if (rewrite_parents(revs, commit, rewrite_one) < 0)
 			return commit_error;
 	}
 	return action;
@@ -2423,25 +2870,23 @@
 	return NULL;
 }
 
+/*
+ * Return true for entries that have not yet been shown.  (This is an
+ * object_array_each_func_t.)
+ */
+static int entry_unshown(struct object_array_entry *entry, void *cb_data_unused)
+{
+	return !(entry->item->flags & SHOWN);
+}
+
+/*
+ * If array is on the verge of a realloc, garbage-collect any entries
+ * that have already been shown to try to free up some space.
+ */
 static void gc_boundary(struct object_array *array)
 {
-	unsigned nr = array->nr;
-	unsigned alloc = array->alloc;
-	struct object_array_entry *objects = array->objects;
-
-	if (alloc <= nr) {
-		unsigned i, j;
-		for (i = j = 0; i < nr; i++) {
-			if (objects[i].item->flags & SHOWN)
-				continue;
-			if (i != j)
-				objects[j] = objects[i];
-			j++;
-		}
-		for (i = j; i < nr; i++)
-			objects[i].item = NULL;
-		array->nr = j;
-	}
+	if (array->nr == array->alloc)
+		object_array_filter(array, entry_unshown, NULL);
 }
 
 static void create_boundary_commit_list(struct rev_info *revs)
@@ -2482,7 +2927,7 @@
 	 * If revs->topo_order is set, sort the boundary commits
 	 * in topological order
 	 */
-	sort_in_topological_order(&revs->commits, revs->lifo);
+	sort_in_topological_order(&revs->commits, revs->sort_order);
 }
 
 static struct commit *get_revision_internal(struct rev_info *revs)
diff --git a/revision.h b/revision.h
index 01bd2b7..95859ba 100644
--- a/revision.h
+++ b/revision.h
@@ -4,6 +4,7 @@
 #include "parse-options.h"
 #include "grep.h"
 #include "notes.h"
+#include "commit.h"
 
 #define SEEN		(1u<<0)
 #define UNINTERESTING   (1u<<1)
@@ -15,7 +16,8 @@
 #define ADDED		(1u<<7)	/* Parents already parsed and added? */
 #define SYMMETRIC_LEFT	(1u<<8)
 #define PATCHSAME	(1u<<9)
-#define ALL_REV_FLAGS	((1u<<10)-1)
+#define BOTTOM		(1u<<10)
+#define ALL_REV_FLAGS	((1u<<11)-1)
 
 #define DECORATE_SHORT_REFS	1
 #define DECORATE_FULL_REFS	2
@@ -35,6 +37,7 @@
 			REV_CMD_PARENTS_ONLY,
 			REV_CMD_LEFT,
 			REV_CMD_RIGHT,
+			REV_CMD_MERGE_BASE,
 			REV_CMD_REV
 		} whence;
 		unsigned flags;
@@ -60,6 +63,10 @@
 	const char *prefix;
 	const char *def;
 	struct pathspec prune_data;
+
+	/* topo-sort */
+	enum rev_sort_order sort_order;
+
 	unsigned int	early_output:1,
 			ignore_missing:1;
 
@@ -70,7 +77,6 @@
 			show_all:1,
 			remove_empty_trees:1,
 			simplify_history:1,
-			lifo:1,
 			topo_order:1,
 			simplify_merges:1,
 			simplify_by_decoration:1,
@@ -96,7 +102,8 @@
 			cherry_mark:1,
 			bisect:1,
 			ancestry_path:1,
-			first_parent_only:1;
+			first_parent_only:1,
+			line_level_traverse:1;
 
 	/* Diff flags */
 	unsigned int	diff:1,
@@ -137,6 +144,7 @@
 	int		numbered_files;
 	int		reroll_count;
 	char		*message_id;
+	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
 	int		add_signoff;
 	const char	*extra_headers;
@@ -167,6 +175,7 @@
 	struct reflog_walk_info *reflog_info;
 	struct decoration children;
 	struct decoration merge_simplification;
+	struct decoration treesame;
 
 	/* notes-specific options: which refs to show */
 	struct display_notes_opt notes_opt;
@@ -175,6 +184,9 @@
 	int count_left;
 	int count_right;
 	int count_same;
+
+	/* line level range that we are chasing */
+	struct decoration line_log_data;
 };
 
 #define REV_TREE_SAME		0
@@ -195,19 +207,23 @@
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *);
+extern int setup_revisions(int argc, const char **argv, struct rev_info *revs,
+			   struct setup_revision_opt *);
 extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
-				 const struct option *options,
-				 const char * const usagestr[]);
+			       const struct option *options,
+			       const char * const usagestr[]);
 #define REVARG_CANNOT_BE_FILENAME 01
 #define REVARG_COMMITTISH 02
-extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
+extern int handle_revision_arg(const char *arg, struct rev_info *revs,
+			       int flags, unsigned revarg_opt);
 
 extern void reset_revision_walk(void);
 extern int prepare_revision_walk(struct rev_info *revs);
 extern struct commit *get_revision(struct rev_info *revs);
-extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
-extern void put_revision_mark(const struct rev_info *revs, const struct commit *commit);
+extern char *get_revision_mark(const struct rev_info *revs,
+			       const struct commit *commit);
+extern void put_revision_mark(const struct rev_info *revs,
+			      const struct commit *commit);
 
 extern void mark_parents_uninteresting(struct commit *commit);
 extern void mark_tree_uninteresting(struct tree *tree);
@@ -220,15 +236,19 @@
 
 char *path_name(const struct name_path *path, const char *name);
 
-extern void show_object_with_name(FILE *, struct object *, const struct name_path *, const char *);
+extern void show_object_with_name(FILE *, struct object *,
+				  const struct name_path *, const char *);
 
 extern void add_object(struct object *obj,
 		       struct object_array *p,
 		       struct name_path *path,
 		       const char *name);
 
-extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
-extern void add_pending_sha1(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags);
+extern void add_pending_object(struct rev_info *revs,
+			       struct object *obj, const char *name);
+extern void add_pending_sha1(struct rev_info *revs,
+			     const char *name, const unsigned char *sha1,
+			     unsigned int flags);
 
 extern void add_head_to_pending(struct rev_info *);
 
@@ -238,7 +258,19 @@
 	commit_error
 };
 
-extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
-extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
+extern enum commit_action get_commit_action(struct rev_info *revs,
+					    struct commit *commit);
+extern enum commit_action simplify_commit(struct rev_info *revs,
+					  struct commit *commit);
 
+enum rewrite_result {
+	rewrite_one_ok,
+	rewrite_one_noparents,
+	rewrite_one_error
+};
+
+typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct commit **pp);
+
+extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
+	rewrite_parent_fn_t rewrite_parent);
 #endif
diff --git a/run-command.c b/run-command.c
index 1b32a12..1b7f88e 100644
--- a/run-command.c
+++ b/run-command.c
@@ -72,11 +72,14 @@
 	close(fd[1]);
 }
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 static inline void dup_devnull(int to)
 {
 	int fd = open("/dev/null", O_RDWR);
-	dup2(fd, to);
+	if (fd < 0)
+		die_errno(_("open /dev/null failed"));
+	if (dup2(fd, to) < 0)
+		die_errno(_("dup2(%d,%d) failed"), fd, to);
 	close(fd);
 }
 #endif
@@ -159,7 +162,7 @@
 		die("BUG: shell command is empty");
 
 	if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 		nargv[nargc++] = SHELL_PATH;
 #else
 		nargv[nargc++] = "sh";
@@ -182,7 +185,7 @@
 	return nargv;
 }
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 static int execv_shell_cmd(const char **argv)
 {
 	const char **nargv = prepare_shell_cmd(argv);
@@ -193,7 +196,7 @@
 }
 #endif
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 static int child_err = 2;
 static int child_notifier = -1;
 
@@ -334,7 +337,7 @@
 	trace_argv_printf(cmd->argv, "trace: run_command:");
 	fflush(NULL);
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 {
 	int notify_pipe[2];
 	if (pipe(notify_pipe))
diff --git a/run-command.h b/run-command.h
index 221ce33..6b985af 100644
--- a/run-command.h
+++ b/run-command.h
@@ -46,6 +46,7 @@
 int run_command(struct child_process *);
 
 extern char *find_hook(const char *name);
+LAST_ARG_MUST_BE_NULL
 extern int run_hook(const char *index_file, const char *name, ...);
 
 #define RUN_COMMAND_NO_STDIN 1
diff --git a/sequencer.c b/sequencer.c
index ab6f8a7..351548f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -270,15 +270,20 @@
 }
 
 static int fast_forward_to(const unsigned char *to, const unsigned char *from,
-			   int unborn)
+			int unborn, struct replay_opts *opts)
 {
 	struct ref_lock *ref_lock;
+	struct strbuf sb = STRBUF_INIT;
+	int ret;
 
 	read_cache();
 	if (checkout_fast_forward(from, to, 1))
 		exit(1); /* the callee should have complained already */
 	ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0);
-	return write_ref_sha1(ref_lock, to, "cherry-pick");
+	strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
+	ret = write_ref_sha1(ref_lock, to, sb.buf);
+	strbuf_release(&sb);
+	return ret;
 }
 
 static int do_recursive_merge(struct commit *base, struct commit *next,
@@ -326,7 +331,7 @@
 		int i;
 		strbuf_addstr(msgbuf, "\nConflicts:\n");
 		for (i = 0; i < active_nr;) {
-			struct cache_entry *ce = active_cache[i++];
+			const struct cache_entry *ce = active_cache[i++];
 			if (ce_stage(ce)) {
 				strbuf_addch(msgbuf, '\t');
 				strbuf_addstr(msgbuf, ce->name);
@@ -366,8 +371,9 @@
 		active_cache_tree = cache_tree();
 
 	if (!cache_tree_fully_valid(active_cache_tree))
-		if (cache_tree_update(active_cache_tree, active_cache,
-				  active_nr, 0))
+		if (cache_tree_update(active_cache_tree,
+				      (const struct cache_entry * const *)active_cache,
+				      active_nr, 0))
 			return error(_("Unable to update cache tree\n"));
 
 	return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
@@ -474,7 +480,7 @@
 	struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
 	char *defmsg = NULL;
 	struct strbuf msgbuf = STRBUF_INIT;
-	int res, unborn = 0;
+	int res, unborn = 0, allow;
 
 	if (opts->no_commit) {
 		/*
@@ -523,7 +529,7 @@
 	if (opts->allow_ff &&
 	    ((parent && !hashcmp(parent->object.sha1, head)) ||
 	     (!parent && unborn)))
-	     return fast_forward_to(commit->object.sha1, head, unborn);
+		return fast_forward_to(commit->object.sha1, head, unborn, opts);
 
 	if (parent && parse_commit(parent) < 0)
 		/* TRANSLATORS: The first %s will be "revert" or
@@ -624,14 +630,18 @@
 		      msg.subject);
 		print_advice(res == 1, opts);
 		rerere(opts->allow_rerere_auto);
-	} else {
-		int allow = allow_empty(opts, commit);
-		if (allow < 0)
-			return allow;
-		if (!opts->no_commit)
-			res = run_git_commit(defmsg, opts, allow);
+		goto leave;
 	}
 
+	allow = allow_empty(opts, commit);
+	if (allow < 0) {
+		res = allow;
+		goto leave;
+	}
+	if (!opts->no_commit)
+		res = run_git_commit(defmsg, opts, allow);
+
+leave:
 	free_message(&msg);
 	free(defmsg);
 
diff --git a/setup.c b/setup.c
index 94c1e61..5262319 100644
--- a/setup.c
+++ b/setup.c
@@ -360,6 +360,7 @@
 
 void setup_work_tree(void)
 {
+	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -379,8 +380,10 @@
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree));
+	set_git_dir(relative_path(git_dir, work_tree, &sb));
 	initialized = 1;
+
+	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
@@ -908,3 +911,15 @@
 		return suspect;
 	return read_gitfile(suspect);
 }
+
+/* if any standard file descriptor is missing open it to /dev/null */
+void sanitize_stdfds(void)
+{
+	int fd = open("/dev/null", O_RDWR, 0);
+	while (fd != -1 && fd < 2)
+		fd = dup(fd);
+	if (fd == -1)
+		die_errno("open /dev/null or dup failed");
+	if (fd > 2)
+		close(fd);
+}
diff --git a/sha1_file.c b/sha1_file.c
index 67e815b..8e27db1 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -36,6 +36,9 @@
 
 const unsigned char null_sha1[20];
 
+static const char *no_log_pack_access = "no_log_pack_access";
+static const char *log_pack_access;
+
 /*
  * This is meant to hold a *small* number of objects that you would
  * want read_sha1_file() to be able to return, but yet you do not want
@@ -1303,6 +1306,26 @@
 	}
 }
 
+static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
+{
+	char *name = sha1_file_name(sha1);
+	struct alternate_object_database *alt;
+
+	if (!lstat(name, st))
+		return 0;
+
+	prepare_alt_odb();
+	errno = ENOENT;
+	for (alt = alt_odb_list; alt; alt = alt->next) {
+		name = alt->name;
+		fill_sha1_path(name, sha1);
+		if (!lstat(alt->base, st))
+			return 0;
+	}
+
+	return -1;
+}
+
 static int open_sha1_file(const unsigned char *sha1)
 {
 	int fd;
@@ -1690,46 +1713,21 @@
 	return type;
 }
 
-
 #define POI_STACK_PREALLOC 64
 
-static int packed_object_info(struct packed_git *p, off_t obj_offset,
-			      unsigned long *sizep, int *rtype)
+static enum object_type packed_to_object_type(struct packed_git *p,
+					      off_t obj_offset,
+					      enum object_type type,
+					      struct pack_window **w_curs,
+					      off_t curpos)
 {
-	struct pack_window *w_curs = NULL;
-	unsigned long size;
-	off_t curpos = obj_offset;
-	enum object_type type;
 	off_t small_poi_stack[POI_STACK_PREALLOC];
 	off_t *poi_stack = small_poi_stack;
 	int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
 
-	type = unpack_object_header(p, &w_curs, &curpos, &size);
-
-	if (rtype)
-		*rtype = type; /* representation type */
-
-	if (sizep) {
-		if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
-			off_t tmp_pos = curpos;
-			off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
-							   type, obj_offset);
-			if (!base_offset) {
-				type = OBJ_BAD;
-				goto out;
-			}
-			*sizep = get_size_from_delta(p, &w_curs, tmp_pos);
-			if (*sizep == 0) {
-				type = OBJ_BAD;
-				goto out;
-			}
-		} else {
-			*sizep = size;
-		}
-	}
-
 	while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 		off_t base_offset;
+		unsigned long size;
 		/* Push the object we're going to leave behind */
 		if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
 			poi_stack_alloc = alloc_nr(poi_stack_nr);
@@ -1740,11 +1738,11 @@
 		}
 		poi_stack[poi_stack_nr++] = obj_offset;
 		/* If parsing the base offset fails, just unwind */
-		base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+		base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
 		if (!base_offset)
 			goto unwind;
 		curpos = obj_offset = base_offset;
-		type = unpack_object_header(p, &w_curs, &curpos, &size);
+		type = unpack_object_header(p, w_curs, &curpos, &size);
 		if (type <= OBJ_NONE) {
 			/* If getting the base itself fails, we first
 			 * retry the base, otherwise unwind */
@@ -1771,7 +1769,6 @@
 out:
 	if (poi_stack != small_poi_stack)
 		free(poi_stack);
-	unuse_pack(&w_curs);
 	return type;
 
 unwind:
@@ -1785,6 +1782,57 @@
 	goto out;
 }
 
+static int packed_object_info(struct packed_git *p, off_t obj_offset,
+			      struct object_info *oi)
+{
+	struct pack_window *w_curs = NULL;
+	unsigned long size;
+	off_t curpos = obj_offset;
+	enum object_type type;
+
+	/*
+	 * We always get the representation type, but only convert it to
+	 * a "real" type later if the caller is interested.
+	 */
+	type = unpack_object_header(p, &w_curs, &curpos, &size);
+
+	if (oi->sizep) {
+		if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+			off_t tmp_pos = curpos;
+			off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
+							   type, obj_offset);
+			if (!base_offset) {
+				type = OBJ_BAD;
+				goto out;
+			}
+			*oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
+			if (*oi->sizep == 0) {
+				type = OBJ_BAD;
+				goto out;
+			}
+		} else {
+			*oi->sizep = size;
+		}
+	}
+
+	if (oi->disk_sizep) {
+		struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+		*oi->disk_sizep = revidx[1].offset - obj_offset;
+	}
+
+	if (oi->typep) {
+		*oi->typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos);
+		if (*oi->typep < 0) {
+			type = OBJ_BAD;
+			goto out;
+		}
+	}
+
+out:
+	unuse_pack(&w_curs);
+	return type;
+}
+
 static void *unpack_compressed_entry(struct packed_git *p,
 				    struct pack_window **w_curs,
 				    off_t curpos,
@@ -1956,12 +2004,19 @@
 {
 	static FILE *log_file;
 
+	if (!log_pack_access)
+		log_pack_access = getenv("GIT_TRACE_PACK_ACCESS");
+	if (!log_pack_access)
+		log_pack_access = no_log_pack_access;
+	if (log_pack_access == no_log_pack_access)
+		return;
+
 	if (!log_file) {
 		log_file = fopen(log_pack_access, "w");
 		if (!log_file) {
 			error("cannot open pack access log '%s' for writing: %s",
 			      log_pack_access, strerror(errno));
-			log_pack_access = NULL;
+			log_pack_access = no_log_pack_access;
 			return;
 		}
 	}
@@ -1992,7 +2047,7 @@
 	int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
 	int base_from_cache = 0;
 
-	if (log_pack_access)
+	if (log_pack_access != no_log_pack_access)
 		write_pack_access_log(p, obj_offset);
 
 	/* PHASE 1: drill down to the innermost base object */
@@ -2135,10 +2190,19 @@
 		data = patch_delta(base, base_size,
 				   delta_data, delta_size,
 				   &size);
-		if (!data)
-			die("failed to apply delta");
 
-		free (delta_data);
+		/*
+		 * We could not apply the delta; warn the user, but keep going.
+		 * Our failure will be noticed either in the next iteration of
+		 * the loop, or if this is the final delta, in the caller when
+		 * we return NULL. Those code paths will take care of making
+		 * a more explicit warning and retrying with another copy of
+		 * the object.
+		 */
+		if (!data)
+			error("failed to apply delta");
+
+		free(delta_data);
 	}
 
 	*final_type = type;
@@ -2338,7 +2402,8 @@
 
 }
 
-static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep)
+static int sha1_loose_object_info(const unsigned char *sha1,
+				  struct object_info *oi)
 {
 	int status;
 	unsigned long mapsize, size;
@@ -2346,19 +2411,37 @@
 	git_zstream stream;
 	char hdr[32];
 
+	/*
+	 * If we don't care about type or size, then we don't
+	 * need to look inside the object at all.
+	 */
+	if (!oi->typep && !oi->sizep) {
+		if (oi->disk_sizep) {
+			struct stat st;
+			if (stat_sha1_file(sha1, &st) < 0)
+				return -1;
+			*oi->disk_sizep = st.st_size;
+		}
+		return 0;
+	}
+
 	map = map_sha1_file(sha1, &mapsize);
 	if (!map)
-		return error("unable to find %s", sha1_to_hex(sha1));
+		return -1;
+	if (oi->disk_sizep)
+		*oi->disk_sizep = mapsize;
 	if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
 		status = error("unable to unpack %s header",
 			       sha1_to_hex(sha1));
 	else if ((status = parse_sha1_header(hdr, &size)) < 0)
 		status = error("unable to parse %s header", sha1_to_hex(sha1));
-	else if (sizep)
-		*sizep = size;
+	else if (oi->sizep)
+		*oi->sizep = size;
 	git_inflate_end(&stream);
 	munmap(map, mapsize);
-	return status;
+	if (oi->typep)
+		*oi->typep = status;
+	return 0;
 }
 
 /* returns enum object_type or negative */
@@ -2366,34 +2449,37 @@
 {
 	struct cached_object *co;
 	struct pack_entry e;
-	int status, rtype;
+	int rtype;
 
 	co = find_cached_object(sha1);
 	if (co) {
+		if (oi->typep)
+			*(oi->typep) = co->type;
 		if (oi->sizep)
 			*(oi->sizep) = co->size;
+		if (oi->disk_sizep)
+			*(oi->disk_sizep) = 0;
 		oi->whence = OI_CACHED;
-		return co->type;
+		return 0;
 	}
 
 	if (!find_pack_entry(sha1, &e)) {
 		/* Most likely it's a loose object. */
-		status = sha1_loose_object_info(sha1, oi->sizep);
-		if (status >= 0) {
+		if (!sha1_loose_object_info(sha1, oi)) {
 			oi->whence = OI_LOOSE;
-			return status;
+			return 0;
 		}
 
 		/* Not a loose object; someone else may have just packed it. */
 		reprepare_packed_git();
 		if (!find_pack_entry(sha1, &e))
-			return status;
+			return -1;
 	}
 
-	status = packed_object_info(e.p, e.offset, oi->sizep, &rtype);
-	if (status < 0) {
+	rtype = packed_object_info(e.p, e.offset, oi);
+	if (rtype < 0) {
 		mark_bad_packed_object(e.p, sha1);
-		status = sha1_object_info_extended(sha1, oi);
+		return sha1_object_info_extended(sha1, oi);
 	} else if (in_delta_base_cache(e.p, e.offset)) {
 		oi->whence = OI_DBCACHED;
 	} else {
@@ -2404,15 +2490,19 @@
 					 rtype == OBJ_OFS_DELTA);
 	}
 
-	return status;
+	return 0;
 }
 
 int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 {
-	struct object_info oi;
+	enum object_type type;
+	struct object_info oi = {NULL};
 
+	oi.typep = &type;
 	oi.sizep = sizep;
-	return sha1_object_info_extended(sha1, &oi);
+	if (sha1_object_info_extended(sha1, &oi) < 0)
+		return -1;
+	return type;
 }
 
 static void *read_packed_sha1(const unsigned char *sha1,
diff --git a/sha1_name.c b/sha1_name.c
index 3820f28..1d210e3 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -52,7 +52,7 @@
 	}
 
 	if (!ds->candidate_ok) {
-		/* discard the candidate; we know it does not satisify fn */
+		/* discard the candidate; we know it does not satisfy fn */
 		hashcpy(ds->candidate, current);
 		ds->candidate_checked = 0;
 		return;
@@ -241,7 +241,7 @@
 		return 0;
 
 	/* We need to do this the hard way... */
-	obj = deref_tag(lookup_object(sha1), NULL, 0);
+	obj = deref_tag(parse_object(sha1), NULL, 0);
 	if (obj && obj->type == OBJ_COMMIT)
 		return 1;
 	return 0;
@@ -431,22 +431,51 @@
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
+static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
 	static const char *warn_msg = "refname '%.*s' is ambiguous.";
+	static const char *object_name_msg = N_(
+	"Git normally never creates a ref that ends with 40 hex characters\n"
+	"because it will be ignored when you just specify 40-hex. These refs\n"
+	"may be created by mistake. For example,\n"
+	"\n"
+	"  git checkout -b $br $(git rev-parse ...)\n"
+	"\n"
+	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+	"examine these refs and maybe delete them. Turn this message off by\n"
+	"running \"git config advice.object_name_warning false\"");
+	unsigned char tmp_sha1[20];
 	char *real_ref = NULL;
 	int refs_found = 0;
-	int at, reflog_len;
+	int at, reflog_len, nth_prior = 0;
 
-	if (len == 40 && !get_sha1_hex(str, sha1))
+	if (len == 40 && !get_sha1_hex(str, sha1)) {
+		if (warn_on_object_refname_ambiguity) {
+			refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
+			if (refs_found > 0 && warn_ambiguous_refs) {
+				warning(warn_msg, len, str);
+				if (advice_object_name_warning)
+					fprintf(stderr, "%s\n", _(object_name_msg));
+			}
+			free(real_ref);
+		}
 		return 0;
+	}
 
 	/* basic@{time or number or -number} format to query ref-log */
 	reflog_len = at = 0;
 	if (len && str[len-1] == '}') {
-		for (at = len-2; at >= 0; at--) {
+		for (at = len-4; at >= 0; at--) {
 			if (str[at] == '@' && str[at+1] == '{') {
+				if (str[at+2] == '-') {
+					if (at != 0)
+						/* @{-N} not at start */
+						return -1;
+					nth_prior = 1;
+					continue;
+				}
 				if (!upstream_mark(str + at, len - at)) {
 					reflog_len = (len-1) - (at+2);
 					len = at;
@@ -460,20 +489,22 @@
 	if (len && ambiguous_path(str, len))
 		return -1;
 
-	if (!len && reflog_len) {
+	if (nth_prior) {
 		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, 0);
-		} else if (ret == 0) {
-			return -1;
+		int detached;
+
+		if (interpret_nth_prior_checkout(str, &buf) > 0) {
+			detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
+			strbuf_release(&buf);
+			if (detached)
+				return 0;
 		}
+	}
+
+	if (!len && reflog_len)
 		/* allow "@{...}" to mean the current branch reflog */
 		refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
-	} else if (reflog_len)
+	else if (reflog_len)
 		refs_found = dwim_log(str, len, sha1, &real_ref);
 	else
 		refs_found = dwim_ref(str, len, sha1, &real_ref);
@@ -481,7 +512,9 @@
 	if (!refs_found)
 		return -1;
 
-	if (warn_ambiguous_refs && refs_found > 1)
+	if (warn_ambiguous_refs &&
+	    (refs_found > 1 ||
+	     !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
 		warning(warn_msg, len, str);
 
 	if (reflog_len) {
@@ -490,10 +523,6 @@
 		unsigned long co_time;
 		int co_tz, co_cnt;
 
-		/* a @{-N} placed anywhere except the start is an error */
-		if (str[at+2] == '-')
-			return -1;
-
 		/* Is it asking for N-th entry, or approxidate? */
 		for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
 			char ch = str[at+2+i];
@@ -517,12 +546,21 @@
 		}
 		if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
 				&co_time, &co_tz, &co_cnt)) {
+			if (!len) {
+				if (!prefixcmp(real_ref, "refs/heads/")) {
+					str = real_ref + 11;
+					len = strlen(real_ref + 11);
+				} else {
+					/* detached HEAD */
+					str = "HEAD";
+					len = 4;
+				}
+			}
 			if (at_time)
 				warning("Log for '%.*s' only goes "
 					"back to %s.", len, str,
 					show_date(co_time, co_tz, DATE_RFC2822));
 			else {
-				free(real_ref);
 				die("Log for '%.*s' only has %d entries.",
 				    len, str, co_cnt);
 			}
@@ -966,6 +1004,38 @@
 	return st;
 }
 
+/* parse @something syntax, when 'something' is not {.*} */
+static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf)
+{
+	if (len || name[1] == '{')
+		return -1;
+
+	strbuf_reset(buf);
+	strbuf_add(buf, "HEAD", 4);
+	return 1;
+}
+
+static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
+{
+	/* we have extra data, which might need further processing */
+	struct strbuf tmp = STRBUF_INIT;
+	int used = buf->len;
+	int ret;
+
+	strbuf_add(buf, name + len, namelen - len);
+	ret = interpret_branch_name(buf->buf, &tmp);
+	/* that data was not interpreted, remove our cruft */
+	if (ret < 0) {
+		strbuf_setlen(buf, used);
+		return len;
+	}
+	strbuf_reset(buf);
+	strbuf_addbuf(buf, &tmp);
+	strbuf_release(&tmp);
+	/* tweak for size of {-N} versus expanded ref name */
+	return ret - used + len;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -995,36 +1065,27 @@
 	int len = interpret_nth_prior_checkout(name, buf);
 	int tmp_len;
 
-	if (!len)
+	if (!len) {
 		return len; /* syntax Ok, not enough switches */
-	if (0 < len && len == namelen)
-		return len; /* consumed all */
-	else if (0 < len) {
-		/* we have extra data, which might need further processing */
-		struct strbuf tmp = STRBUF_INIT;
-		int used = buf->len;
-		int ret;
-
-		strbuf_add(buf, name + len, namelen - len);
-		ret = interpret_branch_name(buf->buf, &tmp);
-		/* that data was not interpreted, remove our cruft */
-		if (ret < 0) {
-			strbuf_setlen(buf, used);
-			return len;
-		}
-		strbuf_reset(buf);
-		strbuf_addbuf(buf, &tmp);
-		strbuf_release(&tmp);
-		/* tweak for size of {-N} versus expanded ref name */
-		return ret - used + len;
+	} else if (len > 0) {
+		if (len == namelen)
+			return len; /* consumed all */
+		else
+			return reinterpret(name, namelen, len, buf);
 	}
 
 	cp = strchr(name, '@');
 	if (!cp)
 		return -1;
+
+	len = interpret_empty_at(name, namelen, cp - name, buf);
+	if (len > 0)
+		return reinterpret(name, namelen, len, buf);
+
 	tmp_len = upstream_mark(cp, namelen - (cp - name));
 	if (!tmp_len)
 		return -1;
+
 	len = cp + tmp_len - name;
 	cp = xstrndup(name, cp - name);
 	upstream = branch_get(*cp ? cp : NULL);
@@ -1033,14 +1094,15 @@
 	 * points to something different than a branch.
 	 */
 	if (!upstream)
-		return error(_("HEAD does not point to a branch"));
+		die(_("HEAD does not point to a branch"));
 	if (!upstream->merge || !upstream->merge[0]->dst) {
 		if (!ref_exists(upstream->refname))
-			return error(_("No such branch: '%s'"), cp);
-		if (!upstream->merge)
-			return error(_("No upstream configured for branch '%s'"),
-				     upstream->name);
-		return error(
+			die(_("No such branch: '%s'"), cp);
+		if (!upstream->merge) {
+			die(_("No upstream configured for branch '%s'"),
+				upstream->name);
+		}
+		die(
 			_("Upstream branch '%s' not stored as a remote-tracking branch"),
 			upstream->merge[0]->src);
 	}
@@ -1055,9 +1117,13 @@
 int strbuf_branchname(struct strbuf *sb, const char *name)
 {
 	int len = strlen(name);
-	if (interpret_branch_name(name, sb) == len)
+	int used = interpret_branch_name(name, sb);
+
+	if (used == len)
 		return 0;
-	strbuf_add(sb, name, len);
+	if (used < 0)
+		used = 0;
+	strbuf_add(sb, name + used, len - used);
 	return len;
 }
 
@@ -1170,7 +1236,7 @@
 					const char *filename)
 {
 	struct stat st;
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 	int pos;
 	unsigned namelen = strlen(filename);
 	unsigned fullnamelen;
@@ -1264,7 +1330,7 @@
 	 */
 	if (name[0] == ':') {
 		int stage = 0;
-		struct cache_entry *ce;
+		const struct cache_entry *ce;
 		char *new_path = NULL;
 		int pos;
 		if (!only_to_die && namelen > 2 && name[1] == '/') {
diff --git a/shallow.c b/shallow.c
index 6be915f..8a9c96d 100644
--- a/shallow.c
+++ b/shallow.c
@@ -3,6 +3,16 @@
 #include "tag.h"
 
 static int is_shallow = -1;
+static struct stat shallow_stat;
+static char *alternate_shallow_file;
+
+void set_alternate_shallow_file(const char *path)
+{
+	if (is_shallow != -1)
+		die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
+	free(alternate_shallow_file);
+	alternate_shallow_file = path ? xstrdup(path) : NULL;
+}
 
 int register_shallow(const unsigned char *sha1)
 {
@@ -21,12 +31,21 @@
 {
 	FILE *fp;
 	char buf[1024];
+	const char *path = alternate_shallow_file;
 
 	if (is_shallow >= 0)
 		return is_shallow;
 
-	fp = fopen(git_path("shallow"), "r");
-	if (!fp) {
+	if (!path)
+		path = git_path("shallow");
+	/*
+	 * fetch-pack sets '--shallow-file ""' as an indicator that no
+	 * shallow file should be used. We could just open it and it
+	 * will likely fail. But let's do an explicit check instead.
+	 */
+	if (!*path ||
+	    stat(path, &shallow_stat) ||
+	    (fp = fopen(path, "r")) == NULL) {
 		is_shallow = 0;
 		return is_shallow;
 	}
@@ -91,20 +110,34 @@
 					continue;
 				*pointer = cur_depth;
 			}
-			if (cur_depth < depth) {
-				if (p->next)
-					add_object_array(&p->item->object,
-							NULL, &stack);
-				else {
-					commit = p->item;
-					cur_depth = *(int *)commit->util;
-				}
-			} else {
-				commit_list_insert(p->item, &result);
-				p->item->object.flags |= shallow_flag;
+			if (p->next)
+				add_object_array(&p->item->object,
+						NULL, &stack);
+			else {
+				commit = p->item;
+				cur_depth = *(int *)commit->util;
 			}
 		}
 	}
 
 	return result;
 }
+
+void check_shallow_file_for_update(void)
+{
+	struct stat st;
+
+	if (!is_shallow)
+		return;
+	else if (is_shallow == -1)
+		die("BUG: shallow must be initialized by now");
+
+	if (stat(git_path("shallow"), &st))
+		die("shallow file was removed during fetch");
+	else if (st.st_mtime != shallow_stat.st_mtime
+#ifdef USE_NSEC
+		 || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
+#endif
+		   )
+		die("shallow file was changed during fetch");
+}
diff --git a/shell.c b/shell.c
index 1429870..66350b2 100644
--- a/shell.c
+++ b/shell.c
@@ -147,7 +147,6 @@
 	char *prog;
 	const char **user_argv;
 	struct commands *cmd;
-	int devnull_fd;
 	int count;
 
 	git_setup_gettext();
@@ -156,15 +155,10 @@
 
 	/*
 	 * Always open file descriptors 0/1/2 to avoid clobbering files
-	 * in die().  It also avoids not messing up when the pipes are
-	 * dup'ed onto stdin/stdout/stderr in the child processes we spawn.
+	 * in die().  It also avoids messing up when the pipes are dup'ed
+	 * onto stdin/stdout/stderr in the child processes we spawn.
 	 */
-	devnull_fd = open("/dev/null", O_RDWR);
-	while (devnull_fd >= 0 && devnull_fd <= 2)
-		devnull_fd = dup(devnull_fd);
-	if (devnull_fd == -1)
-		die_errno("opening /dev/null failed");
-	close (devnull_fd);
+	sanitize_stdfds();
 
 	/*
 	 * Special hack to pretend to be a CVS server
diff --git a/streaming.c b/streaming.c
index cabcd9d..debe904 100644
--- a/streaming.c
+++ b/streaming.c
@@ -111,11 +111,11 @@
 	unsigned long size;
 	int status;
 
+	oi->typep = type;
 	oi->sizep = &size;
 	status = sha1_object_info_extended(sha1, oi);
 	if (status < 0)
 		return stream_error;
-	*type = status;
 
 	switch (oi->whence) {
 	case OI_LOOSE:
@@ -135,7 +135,7 @@
 				 struct stream_filter *filter)
 {
 	struct git_istream *st;
-	struct object_info oi;
+	struct object_info oi = {NULL};
 	const unsigned char *real = lookup_replace_object(sha1);
 	enum input_source src = istream_source(real, type, &oi);
 
@@ -149,7 +149,7 @@
 			return NULL;
 		}
 	}
-	if (st && filter) {
+	if (filter) {
 		/* Add "&& !is_null_stream_filter(filter)" for performance */
 		struct git_istream *nst = attach_stream_filter(st, filter);
 		if (!nst)
diff --git a/submodule.c b/submodule.c
index e728025..3f0a3f9 100644
--- a/submodule.c
+++ b/submodule.c
@@ -226,6 +226,7 @@
 	while ((commit = get_revision(rev))) {
 		struct pretty_print_context ctx = {0};
 		ctx.date_mode = rev->date_mode;
+		ctx.output_encoding = get_log_output_encoding();
 		strbuf_setlen(&sb, 0);
 		strbuf_addstr(&sb, line_prefix);
 		if (commit->object.flags & SYMMETRIC_LEFT) {
@@ -603,9 +604,8 @@
 	if (!work_tree)
 		goto out;
 
-	if (!the_index.initialized)
-		if (read_cache() < 0)
-			die("index file corrupt");
+	if (read_cache() < 0)
+		die("index file corrupt");
 
 	argv_array_push(&argv, "fetch");
 	for (i = 0; i < options->argc; i++)
@@ -624,7 +624,7 @@
 		struct strbuf submodule_path = STRBUF_INIT;
 		struct strbuf submodule_git_dir = STRBUF_INIT;
 		struct strbuf submodule_prefix = STRBUF_INIT;
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		const char *git_dir, *name, *default_argv;
 
 		if (!S_ISGITLINK(ce->ce_mode))
@@ -846,7 +846,7 @@
 		struct commit *a, struct commit *b)
 {
 	int i, j;
-	struct object_array merges;
+	struct object_array merges = OBJECT_ARRAY_INIT;
 	struct commit *commit;
 	int contains_another;
 
@@ -856,7 +856,6 @@
 	struct rev_info revs;
 	struct setup_revision_opt rev_opts;
 
-	memset(&merges, 0, sizeof(merges));
 	memset(result, 0, sizeof(struct object_array));
 	memset(&rev_opts, 0, sizeof(rev_opts));
 
@@ -894,8 +893,7 @@
 		}
 
 		if (!contains_another)
-			add_object_array(merges.objects[i].item,
-					 merges.objects[i].name, result);
+			add_object_array(merges.objects[i].item, NULL, result);
 	}
 
 	free(merges.objects);
diff --git a/t/Makefile b/t/Makefile
index 44ca7d3..2373a04 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -15,9 +15,16 @@
 DEFAULT_TEST_TARGET ?= test
 TEST_LINT ?= test-lint-duplicates test-lint-executable
 
+ifdef TEST_OUTPUT_DIRECTORY
+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+else
+TEST_RESULTS_DIRECTORY = test-results
+endif
+
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
 
 T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
 TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
@@ -36,10 +43,10 @@
 	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 pre-clean:
-	$(RM) -r test-results
+	$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
 clean-except-prove-cache:
-	$(RM) -r 'trash directory'.* test-results
+	$(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
 	$(RM) -r valgrind/bin
 
 clean: clean-except-prove-cache
@@ -65,7 +72,7 @@
 	$(MAKE) clean
 
 aggregate-results:
-	for f in test-results/t*-*.counts; do \
+	for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
 		echo "$$f"; \
 	done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
 
diff --git a/t/README b/t/README
index e669bb3..2167125 100644
--- a/t/README
+++ b/t/README
@@ -76,6 +76,11 @@
 	command being run and their output if any are also
 	output.
 
+--verbose-only=<pattern>::
+	Like --verbose, but the effect is limited to tests with
+	numbers matching <pattern>.  The number matched against is
+	simply the running count of the test within the file.
+
 --debug::
 	This may help the person who is developing a new test.
 	It causes the command defined with test_debug to run.
@@ -121,6 +126,11 @@
 	the 't/valgrind/' directory and use the commands under
 	't/valgrind/bin/'.
 
+--valgrind-only=<pattern>::
+	Like --valgrind, but the effect is limited to tests with
+	numbers matching <pattern>.  The number matched against is
+	simply the running count of the test within the file.
+
 --tee::
 	In addition to printing the test output to the terminal,
 	write it to files named 't/test-results/$TEST_NAME.out'.
@@ -324,6 +334,9 @@
    use 'test_must_fail git cmd'.  This will signal a failure if git
    dies in an unexpected way (e.g. segfault).
 
+   On the other hand, don't use test_must_fail for running regular
+   platform commands; just use '! cmd'.
+
  - use perl without spelling it as "$PERL_PATH". This is to help our
    friends on Windows where the platform Perl often adds CR before
    the end of line, and they bundle Git with a version of Perl that
@@ -592,6 +605,20 @@
 		test_cmp expected actual
 	'
 
+ - test_ln_s_add <path1> <path2>
+
+   This function helps systems whose filesystem does not support symbolic
+   links. Use it to add a symbolic link entry to the index when it is not
+   important that the file system entry is a symbolic link, i.e., instead
+   of the sequence
+
+	ln -s foo bar &&
+	git add bar
+
+   Sometimes it is possible to split a test in a part that does not need
+   the symbolic link in the file system and a part that does; then only
+   the latter part need be protected by a SYMLINKS prerequisite (see below).
+
 Prerequisites
 -------------
 
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index c56a77d..0bfee00 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -2,11 +2,21 @@
 # sourced from t8001-annotate.sh and t8002-blame.sh.
 
 check_count () {
-	head=
-	case "$1" in -h) head="$2"; shift; shift ;; esac
-	echo "$PROG file $head" >&4
-	$PROG file $head >.result || return 1
-	cat .result | perl -e '
+	head= &&
+	file='file' &&
+	options= &&
+	while :
+	do
+		case "$1" in
+		-h) head="$2"; shift; shift ;;
+		-f) file="$2"; shift; shift ;;
+		-*) options="$options $1"; shift ;;
+		*) break ;;
+		esac
+	done &&
+	echo "$PROG $options $file $head" >&4 &&
+	$PROG $options $file $head >actual &&
+	perl -e '
 		my %expect = (@ARGV);
 		my %count = map { $_ => 0 } keys %expect;
 		while (<STDIN>) {
@@ -31,107 +41,246 @@
 			print STDERR "Author $author (expected $value, attributed $count) $ok\n";
 		}
 		exit($bad);
-	' "$@"
+	' "$@" <actual
 }
 
-test_expect_success \
-    'prepare reference tree' \
-    'echo "1A quick brown fox jumps over the" >file &&
-     echo "lazy dog" >>file &&
-     git add file &&
-     GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."'
+test_expect_success 'setup A lines' '
+	echo "1A quick brown fox jumps over the" >file &&
+	echo "lazy dog" >>file &&
+	git add file &&
+	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
+	git commit -a -m "Initial."
+'
 
-test_expect_success \
-    'check all lines blamed on A' \
-    'check_count A 2'
+test_expect_success 'blame 1 author' '
+	check_count A 2
+'
 
-test_expect_success \
-    'Setup new lines blamed on B' \
-    'echo "2A quick brown fox jumps over the" >>file &&
-     echo "lazy dog" >> file &&
-     GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."'
+test_expect_success 'setup B lines' '
+	echo "2A quick brown fox jumps over the" >>file &&
+	echo "lazy dog" >>file &&
+	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
+	git commit -a -m "Second."
+'
 
-test_expect_success \
-    'Two lines blamed on A, two on B' \
-    'check_count A 2 B 2'
+test_expect_success 'blame 2 authors' '
+	check_count A 2 B 2
+'
 
-test_expect_success \
-    'merge-setup part 1' \
-    'git checkout -b branch1 master &&
-     echo "3A slow green fox jumps into the" >> file &&
-     echo "well." >> file &&
-     GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"'
+test_expect_success 'setup B1 lines (branch1)' '
+	git checkout -b branch1 master &&
+	echo "3A slow green fox jumps into the" >>file &&
+	echo "well." >>file &&
+	GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \
+	git commit -a -m "Branch1-1"
+'
 
-test_expect_success \
-    'Two lines blamed on A, two on B, two on B1' \
-    'check_count A 2 B 2 B1 2'
+test_expect_success 'blame 2 authors + 1 branch1 author' '
+	check_count A 2 B 2 B1 2
+'
 
-test_expect_success \
-    'merge-setup part 2' \
-    'git checkout -b branch2 master &&
-     sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
-     mv file.new file &&
-     GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"'
+test_expect_success 'setup B2 lines (branch2)' '
+	git checkout -b branch2 master &&
+	sed -e "s/2A quick brown/4A quick brown lazy dog/" <file >file.new &&
+	mv file.new file &&
+	GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" \
+	git commit -a -m "Branch2-1"
+'
 
-test_expect_success \
-    'Two lines blamed on A, one on B, one on B2' \
-    'check_count A 2 B 1 B2 1'
+test_expect_success 'blame 2 authors + 1 branch2 author' '
+	check_count A 2 B 1 B2 1
+'
 
-test_expect_success \
-    'merge-setup part 3' \
-    'git pull . branch1'
+test_expect_success 'merge branch1 & branch2' '
+	git pull . branch1
+'
 
-test_expect_success \
-    'Two lines blamed on A, one on B, two on B1, one on B2' \
-    'check_count A 2 B 1 B1 2 B2 1'
+test_expect_success 'blame 2 authors + 2 merged-in authors' '
+	check_count A 2 B 1 B1 2 B2 1
+'
 
-test_expect_success \
-    'Annotating an old revision works' \
-    'check_count -h master A 2 B 2'
+test_expect_success 'blame ancestor' '
+	check_count -h master A 2 B 2
+'
 
-test_expect_success \
-    'Annotating an old revision works' \
-    'check_count -h master^ A 2'
+test_expect_success 'blame great-ancestor' '
+	check_count -h master^ A 2
+'
 
-test_expect_success \
-    'merge-setup part 4' \
-    'echo "evil merge." >>file &&
-     git commit -a --amend'
+test_expect_success 'setup evil merge' '
+	echo "evil merge." >>file &&
+	git commit -a --amend
+'
 
-test_expect_success \
-    'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \
-    'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1'
+test_expect_success 'blame evil merge' '
+	check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
+'
 
-test_expect_success \
-    'an incomplete line added' \
-    'echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"'
+test_expect_success 'setup incomplete line' '
+	echo "incomplete" | tr -d "\\012" >>file &&
+	GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
+	git commit -a -m "Incomplete"
+'
 
-test_expect_success \
-    'With incomplete lines.' \
-    'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1'
+test_expect_success 'blame incomplete line' '
+	check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1
+'
 
-test_expect_success \
-    'some edit' \
-    'mv file file.orig &&
-    {
-	cat file.orig &&
-	echo
-    } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
-    echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"'
+test_expect_success 'setup edits' '
+	mv file file.orig &&
+	{
+		cat file.orig &&
+		echo
+	} | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" >file &&
+	echo "incomplete" | tr -d "\\012" >>file &&
+	GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" \
+	git commit -a -m "edit"
+'
 
-test_expect_success \
-    'some edit' \
-    'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1'
+test_expect_success 'blame edits' '
+	check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1
+'
 
-test_expect_success \
-    'an obfuscated email added' \
-    'echo "No robots allowed" > file.new &&
-     cat file >> file.new &&
-     mv file.new file &&
-     GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"'
+test_expect_success 'setup obfuscated email' '
+	echo "No robots allowed" >file.new &&
+	cat file >>file.new &&
+	mv file.new file &&
+	GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" \
+	git commit -a -m "norobots"
+'
 
-test_expect_success \
-    'obfuscated email parsed' \
-    'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1'
+test_expect_success 'blame obfuscated email' '
+	check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L 1 (all)' '
+	check_count -L1 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L , (all)' '
+	check_count -L, A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X (X to end)' '
+	check_count -L5 B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L X, (X to end)' '
+	check_count -L5, B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L ,Y (up to Y)' '
+	check_count -L,3 A 1 B2 1 E 1
+'
+
+test_expect_success 'blame -L X,X' '
+	check_count -L3,3 B2 1
+'
+
+test_expect_success 'blame -L X,Y' '
+	check_count -L3,6 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L Y,X (undocumented)' '
+	check_count -L6,3 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X,+1' '
+	check_count -L3,+1 B2 1
+'
+
+test_expect_success 'blame -L X,+N' '
+	check_count -L3,+4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X,-1' '
+	check_count -L3,-1 B2 1
+'
+
+test_expect_success 'blame -L X,-N' '
+	check_count -L6,-4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L /RE/ (RE to end)' '
+	check_count -L/evil/ C 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,/RE2/' '
+	check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X,/RE/' '
+	check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,Y' '
+	check_count -L/99/,7 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,+N' '
+	check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,-N' '
+	check_count -L/99/,-3 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X (X > nlines)' '
+	test_must_fail $PROG -L12345 file
+'
+
+test_expect_success 'blame -L ,Y (Y > nlines)' '
+	test_must_fail $PROG -L,12345 file
+'
+
+test_expect_success 'setup -L :regex' '
+	tr Q "\\t" >hello.c <<-\EOF &&
+	int main(int argc, const char *argv[])
+	{
+	Qputs("hello");
+	}
+	EOF
+	git add hello.c &&
+	GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@test.git" \
+	git commit -m "hello" &&
+
+	mv hello.c hello.orig &&
+	sed -e "/}/i\\
+	Qputs(\"goodbye\");" <hello.orig | tr Q "\\t" >hello.c &&
+	GIT_AUTHOR_NAME="G" GIT_AUTHOR_EMAIL="G@test.git" \
+	git commit -a -m "goodbye" &&
+
+	mv hello.c hello.orig &&
+	echo "#include <stdio.h>" >hello.c &&
+	cat hello.orig >>hello.c &&
+	tr Q "\\t" >>hello.c <<-\EOF
+	void mail()
+	{
+	Qputs("mail");
+	}
+	EOF
+	GIT_AUTHOR_NAME="H" GIT_AUTHOR_EMAIL="H@test.git" \
+	git commit -a -m "mail"
+'
+
+test_expect_success 'blame -L :literal' '
+	check_count -f hello.c -L:main F 4 G 1
+'
+
+test_expect_success 'blame -L :regex' '
+	check_count -f hello.c "-L:m[a-z][a-z]l" H 4
+'
+
+test_expect_success 'blame -L :nomatch' '
+	test_must_fail $PROG -L:nomatch hello.c
+'
+
+test_expect_success 'blame -L bogus' '
+	test_must_fail $PROG -L file &&
+	test_must_fail $PROG -L1,+ file &&
+	test_must_fail $PROG -L1,- file &&
+	test_must_fail $PROG -LX file &&
+	test_must_fail $PROG -L1,X file &&
+	test_must_fail $PROG -L1,+N file &&
+	test_must_fail $PROG -L1,-N file
+'
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 8b5a71d..45971f4 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -21,6 +21,7 @@
 	/^\s*declare\s+/ and err 'arrays/declare not portable';
 	/^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
 	/test\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+	/^\s*export\s+[^=]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
 	# this resets our $. for each file
 	close ARGV if eof;
 }
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index b5bce45..dd17e3a 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -1,5 +1,4 @@
 ServerName dummy
-LockFile accept.lock
 PidFile httpd.pid
 DocumentRoot www
 LogFormat "%h %l %u %t \"%r\" %>s %b" common
@@ -24,6 +23,10 @@
 	LoadModule version_module modules/mod_version.so
 </IfModule>
 
+<IfVersion < 2.4>
+LockFile accept.lock
+</IfVersion>
+
 <IfVersion < 2.1>
 <IfModule !mod_auth.c>
 	LoadModule auth_module modules/mod_auth.so
@@ -45,6 +48,21 @@
 </IfModule>
 </IfVersion>
 
+<IfVersion >= 2.4>
+<IfModule !mod_authn_core.c>
+	LoadModule authn_core_module modules/mod_authn_core.so
+</IfModule>
+<IfModule !mod_authz_core.c>
+	LoadModule authz_core_module modules/mod_authz_core.so
+</IfModule>
+<IfModule !mod_access_compat.c>
+	LoadModule access_compat_module modules/mod_access_compat.so
+</IfModule>
+<IfModule !mod_mpm_prefork.c>
+	LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
+</IfVersion>
+
 PassEnv GIT_VALGRIND
 PassEnv GIT_VALGRIND_OPTIONS
 
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 6ccf797..8ff87fb 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -17,51 +17,98 @@
 #       ("squash", "fixup", "edit", or "reword") and the SHA1 taken
 #       from the specified line.
 #
+#   "exec_cmd_with_args" -- add an "exec cmd with args" line.
+#
 #   "#" -- Add a comment line.
 #
 #   ">" -- Add a blank line.
 
 set_fake_editor () {
-	echo "#!$SHELL_PATH" >fake-editor.sh
-	cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
-	test -z "$EXPECT_HEADER_COUNT" ||
-		test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+	write_script fake-editor.sh <<-\EOF
+	case "$1" in
+	*/COMMIT_EDITMSG)
+		test -z "$EXPECT_HEADER_COUNT" ||
+			test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+			exit
+		test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+		test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
 		exit
-	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"
-echo 'rebase -i script before editing:'
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
-	case $line in
-	squash|fixup|edit|reword)
-		action="$line";;
-	exec*)
-		echo "$line" | sed 's/_/ /g' >> "$1";;
-	"#")
-		echo '# comment' >> "$1";;
-	">")
-		echo >> "$1";;
-	*)
-		sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
-		action=pick;;
+		;;
 	esac
-done
-echo 'rebase -i script after editing:'
-cat "$1"
-EOF
+	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"
+	echo 'rebase -i script before editing:'
+	cat "$1".tmp
+	action=pick
+	for line in $FAKE_LINES; do
+		case $line in
+		squash|fixup|edit|reword)
+			action="$line";;
+		exec*)
+			echo "$line" | sed 's/_/ /g' >> "$1";;
+		"#")
+			echo '# comment' >> "$1";;
+		">")
+			echo >> "$1";;
+		*)
+			sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+			action=pick;;
+		esac
+	done
+	echo 'rebase -i script after editing:'
+	cat "$1"
+	EOF
 
 	test_set_editor "$(pwd)/fake-editor.sh"
-	chmod a+x fake-editor.sh
+}
+
+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring
+# blank lines and comments) to stdout, and exit failure (so you should run
+# it with test_must_fail).  This can be used to verify the expected user
+# experience, for todo list changes that do not affect the outcome of
+# rebase; or as an extra check in addition to checking the outcome.
+
+set_cat_todo_editor () {
+	write_script fake-editor.sh <<-\EOF
+	grep "^[^#]" "$1"
+	exit 1
+	EOF
+	test_set_editor "$(pwd)/fake-editor.sh"
+}
+
+# checks that the revisions in "$2" represent a linear range with the
+# subjects in "$1"
+test_linear_range () {
+	revlist_merges=$(git rev-list --merges "$2") &&
+	test -z "$revlist_merges" &&
+	expected=$1
+	set -- $(git log --reverse --format=%s "$2")
+	test "$expected" = "$*"
+}
+
+reset_rebase () {
+	test_might_fail git rebase --abort &&
+	git reset --hard &&
+	git clean -f
+}
+
+cherry_pick () {
+	git cherry-pick -n "$2" &&
+	git commit -m "$1" &&
+	git tag "$1"
+}
+
+revert () {
+	git revert -n "$2" &&
+	git commit -m "$1" &&
+	git tag "$1"
+}
+
+make_empty () {
+	git commit --allow-empty -m "$1" &&
+	git tag "$1"
 }
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
index ea25dd8..4ffd901 100644
--- a/t/lib-t6000.sh
+++ b/t/lib-t6000.sh
@@ -1,55 +1,50 @@
 : included from 6002 and others
 
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+mkdir -p .git/refs/tags
 
-:> sed.script
+>sed.script
 
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
+# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags
+tag () {
 	_tag=$1
-	[ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
-	cat .git/refs/tags/$_tag
+	test -f ".git/refs/tags/$_tag" || error "tag: \"$_tag\" does not exist"
+	cat ".git/refs/tags/$_tag"
 }
 
 # Generate a commit using the text specified to make it unique and the tree
 # named by the tag specified.
-unique_commit()
-{
+unique_commit () {
 	_text=$1
-        _tree=$2
+	_tree=$2
 	shift 2
-	echo $_text | git commit-tree $(tag $_tree) "$@"
+	echo "$_text" | git commit-tree $(tag "$_tree") "$@"
 }
 
 # Save the output of a command into the tag specified. Prepend
 # a substitution script for the tag onto the front of sed.script
-save_tag()
-{
+save_tag () {
 	_tag=$1
-	[ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+	test -n "$_tag" || error "usage: save_tag tag commit-args ..."
 	shift 1
-	"$@" >.git/refs/tags/$_tag
+	"$@" >".git/refs/tags/$_tag"
 
-        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
-	cat sed.script >> sed.script.tmp
+	echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp
+	cat sed.script >>sed.script.tmp
 	rm sed.script
 	mv sed.script.tmp sed.script
 }
 
 # Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
+entag () {
 	sed -f sed.script
 }
 
 # Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
 # tag to a specified value. Restore the original value on return.
-as_author()
-{
+as_author () {
 	_author=$1
 	shift 1
-        _save=$GIT_AUTHOR_EMAIL
+	_save=$GIT_AUTHOR_EMAIL
 
 	GIT_AUTHOR_EMAIL="$_author"
 	export GIT_AUTHOR_EMAIL
@@ -63,45 +58,58 @@
 	fi
 }
 
-commit_date()
-{
-        _commit=$1
-	git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+commit_date () {
+	_commit=$1
+	git cat-file commit $_commit |
+	sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
 }
 
-on_committer_date()
-{
-    _date=$1
-    shift 1
-    GIT_COMMITTER_DATE="$_date"
-    export GIT_COMMITTER_DATE
-    "$@"
-    unset GIT_COMMITTER_DATE
+# Assign the value of fake date to a variable, but
+# allow fairly common "1971-08-16 00:00" to be omittd
+assign_fake_date () {
+	case "$2" in
+	??:??:??)	eval "$1='1971-08-16 $2'" ;;
+	??:??)		eval "$1='1971-08-16 00:$2'" ;;
+	??)		eval "$1='1971-08-16 00:00:$2'" ;;
+	*)		eval "$1='$2'" ;;
+	esac
+}
+
+on_committer_date () {
+	assign_fake_date GIT_COMMITTER_DATE "$1"
+	export GIT_COMMITTER_DATE
+	shift 1
+	"$@"
+}
+
+on_dates () {
+	assign_fake_date GIT_COMMITTER_DATE "$1"
+	assign_fake_date GIT_AUTHOR_DATE "$2"
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+	shift 2
+	"$@"
 }
 
 # Execute a command and suppress any error output.
-hide_error()
-{
+hide_error () {
 	"$@" 2>/dev/null
 }
 
-check_output()
-{
+check_output () {
 	_name=$1
 	shift 1
-	if eval "$*" | entag > $_name.actual
+	if eval "$*" | entag >"$_name.actual"
 	then
-		test_cmp $_name.expected $_name.actual
+		test_cmp "$_name.expected" "$_name.actual"
 	else
-		return 1;
+		return 1
 	fi
 }
 
 # Turn a reasonable test description into a reasonable test name.
 # All alphanums translated into -'s which are then compressed and stripped
 # from front and back.
-name_from_description()
-{
+name_from_description () {
 	perl -pe '
 		s/[^A-Za-z0-9.]/-/g;
 		s/-+/-/g;
@@ -119,9 +127,11 @@
 test_output_expect_success()
 {
 	_description=$1
-        _test=$2
-        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
-        _name=$(echo $_description | name_from_description)
-	cat > $_name.expected
+	_test=$2
+	test $# -eq 2 ||
+	error "usage: test_output_expect_success description test <<EOF ... EOF"
+
+	_name=$(echo $_description | name_from_description)
+	cat >"$_name.expected"
 	test_expect_success "$_description" "check_output $_name \"$_test\""
 }
diff --git a/t/perf/README b/t/perf/README
index c552f56..8848c14 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -66,7 +66,7 @@
     GIT_PERF_LARGE_REPO
 	Repositories to copy for the performance tests.  The normal
 	repo should be at least git.git size.  The large repo should
-	probably be about linux-2.6.git size for optimal results.
+	probably be about linux.git size for optimal results.
 	Both default to the git.git you are running from.
 
 You can also pass the options taken by ordinary git tests; the most
diff --git a/t/perf/p0002-read-cache.sh b/t/perf/p0002-read-cache.sh
new file mode 100755
index 0000000..9180ae9
--- /dev/null
+++ b/t/perf/p0002-read-cache.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description="Tests performance of reading the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+count=1000
+test_perf "read_cache/discard_cache $count times" "
+	test-read-cache $count
+"
+
+test_done
diff --git a/t/perf/p4211-line-log.sh b/t/perf/p4211-line-log.sh
new file mode 100755
index 0000000..3d074b0
--- /dev/null
+++ b/t/perf/p4211-line-log.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='Tests log -L performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# Pick a file to log pseudo-randomly.  The sort key is the blob hash,
+# so it is stable.
+test_expect_success 'select a file' '
+	git ls-tree HEAD | grep ^100644 |
+	sort -k 3 | head -1 | cut -f 2 >filelist
+'
+
+file=$(cat filelist)
+export file
+
+test_perf 'git rev-list --topo-order (baseline)' '
+	git rev-list --topo-order HEAD >/dev/null
+'
+
+test_perf 'git log --follow (baseline for -M)' '
+	git log --oneline --follow -- "$file" >/dev/null
+'
+
+test_perf 'git log -L' '
+	git log -L 1:"$file" >/dev/null
+'
+
+test_perf 'git log -M -L' '
+	git log -M -L 1:"$file" >/dev/null
+'
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index a816fbc..c61d535 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -150,6 +150,7 @@
 
 
 test_perf () {
+	test_start_
 	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
 	test "$#" = 2 ||
 	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -187,7 +188,7 @@
 		base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
 		"$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
 	fi
-	echo >&3 ""
+	test_finish_
 }
 
 # We extend test_done to print timings at the end (./run disables this
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index cefe33d..10be52b 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -47,8 +47,14 @@
 
 run_sub_test_lib_test () {
 	name="$1" descr="$2" # stdin is the body of the test code
+	shift 2
 	mkdir "$name" &&
 	(
+		# Pretend we're a test harness.  This prevents
+		# test-lib from writing the counts to a file that will
+		# later be summarized, showing spurious "failed" tests
+		HARNESS_ACTIVE=t &&
+		export HARNESS_ACTIVE &&
 		cd "$name" &&
 		cat >"$name.sh" <<-EOF &&
 		#!$SHELL_PATH
@@ -65,7 +71,7 @@
 		cat >>"$name.sh" &&
 		chmod +x "$name.sh" &&
 		export TEST_DIRECTORY &&
-		./"$name.sh" >out 2>err
+		./"$name.sh" "$@" >out 2>err
 	)
 }
 
@@ -215,6 +221,60 @@
 	EOF
 "
 
+test_expect_success 'test --verbose' '
+	test_must_fail run_sub_test_lib_test \
+		test-verbose "test verbose" --verbose <<-\EOF &&
+	test_expect_success "passing test" true
+	test_expect_success "test with output" "echo foo"
+	test_expect_success "failing test" false
+	test_done
+	EOF
+	mv test-verbose/out test-verbose/out+
+	grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
+	check_sub_test_lib_test test-verbose <<-\EOF
+	> expecting success: true
+	> Z
+	> ok 1 - passing test
+	> Z
+	> expecting success: echo foo
+	> foo
+	> Z
+	> ok 2 - test with output
+	> Z
+	> expecting success: false
+	> Z
+	> not ok 3 - failing test
+	> #	false
+	> Z
+	> # failed 1 among 3 test(s)
+	> 1..3
+	EOF
+'
+
+test_expect_success 'test --verbose-only' '
+	test_must_fail run_sub_test_lib_test \
+		test-verbose-only-2 "test verbose-only=2" \
+		--verbose-only=2 <<-\EOF &&
+	test_expect_success "passing test" true
+	test_expect_success "test with output" "echo foo"
+	test_expect_success "failing test" false
+	test_done
+	EOF
+	check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+	> ok 1 - passing test
+	> Z
+	> expecting success: echo foo
+	> foo
+	> Z
+	> ok 2 - test with output
+	> Z
+	> not ok 3 - failing test
+	> #	false
+	> # failed 1 among 3 test(s)
+	> 1..3
+	EOF
+'
+
 test_set_prereq HAVEIT
 haveit=no
 test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
@@ -367,22 +427,6 @@
 
 # Various types of objects
 
-# Some filesystems do not support symblic links; on such systems
-# some expected values are different
-if test_have_prereq SYMLINKS
-then
-	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' '
 	mkdir path2 path3 path3/subp3 &&
 	paths="path0 path2/file2 path3/file3 path3/subp3/file3" &&
@@ -390,10 +434,7 @@
 		for p in $paths
 		do
 			echo "hello $p" >$p || exit 1
-			if test_have_prereq SYMLINKS
-			then
-				ln -s "hello $p" ${p}sym || exit 1
-			fi
+			test_ln_s_add "hello $p" ${p}sym || exit 1
 		done
 	) &&
 	find path* ! -type d -print | xargs git update-index --add
@@ -405,7 +446,7 @@
 '
 
 test_expect_success 'validate git ls-files output for a known tree' '
-	$expectfilter >expected <<-\EOF &&
+	cat >expected <<-\EOF &&
 	100644 f87290f8eb2cbbea7857214459a0739927eab154 0	path0
 	120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0	path0sym
 	100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0	path2/file2
@@ -423,14 +464,14 @@
 '
 
 test_expect_success 'validate object ID for a known tree' '
-	test "$tree" = "$expectedtree"
+	test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b
 '
 
 test_expect_success 'showing tree with git ls-tree' '
     git ls-tree $tree >current
 '
 
-test_expect_success SYMLINKS 'git ls-tree output for a known tree' '
+test_expect_success 'git ls-tree output for a known tree' '
 	cat >expected <<-\EOF &&
 	100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
 	120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
@@ -447,7 +488,7 @@
 '
 
 test_expect_success 'git ls-tree -r output for a known tree' '
-	$expectfilter >expected <<-\EOF &&
+	cat >expected <<-\EOF &&
 	100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
 	120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
 	100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
@@ -465,7 +506,7 @@
 	git ls-tree -r -t $tree >current
 '
 
-test_expect_success SYMLINKS 'git ls-tree -r output for a known tree' '
+test_expect_success 'git ls-tree -r output for a known tree' '
 	cat >expected <<-\EOF &&
 	100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
 	120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
@@ -487,7 +528,7 @@
 '
 
 test_expect_success 'validate object ID for a known tree' '
-	test "$ptree" = "$expectedptree1"
+	test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3
 '
 
 test_expect_success 'writing partial tree out with git write-tree --prefix' '
@@ -495,7 +536,7 @@
 '
 
 test_expect_success 'validate object ID for a known tree' '
-	test "$ptree" = "$expectedptree2"
+	test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2
 '
 
 test_expect_success 'put invalid objects into the index' '
@@ -529,7 +570,7 @@
 '
 
 test_expect_success 'validate git diff-files output for a know cache/work tree state' '
-	$expectfilter >expected <<\EOF &&
+	cat >expected <<\EOF &&
 :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M	path0
 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M	path0sym
 :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M	path2/file2
@@ -553,7 +594,7 @@
 '
 
 ################################################################
-P=$expectedtree
+P=087704a96baf1c2d1c869a8b084481e121c88b5b
 
 test_expect_success 'git commit-tree records the correct tree in a commit' '
 	commit0=$(echo NO | git commit-tree $P) &&
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 93e58c0..981437b 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -20,4 +20,11 @@
 	test_cmp expect actual
 '
 
+test_expect_success !MINGW 'signals are propagated using shell convention' '
+	# we use exec here to avoid any sub-shell interpretation
+	# of the exit code
+	git config alias.sigterm "!exec test-sigchain" &&
+	test_expect_code 143 git sigterm
+'
+
 test_done
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 9c1bde1..c29342d 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -66,16 +66,23 @@
 
 	init_vars &&
 	rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
-	echo git $global_args check-ignore $quiet_opt $verbose_opt $args \
+	echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
 		>"$HOME/cmd" &&
+	echo "$expect_code" >"$HOME/expected-exit-code" &&
 	test_expect_code "$expect_code" \
-		git $global_args check-ignore $quiet_opt $verbose_opt $args \
+		git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
 		>"$HOME/stdout" 2>"$HOME/stderr" &&
 	test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
 	stderr_empty_on_success "$expect_code"
 }
 
-# Runs the same code with 3 different levels of output verbosity,
+# Runs the same code with 4 different levels of output verbosity:
+#
+#   1. with -q / --quiet
+#   2. with default verbosity
+#   3. with -v / --verbose
+#   4. with -v / --verbose, *and* -n / --non-matching
+#
 # expecting success each time.  Takes advantage of the fact that
 # check-ignore --verbose output is the same as normal output except
 # for the extra first column.
@@ -83,7 +90,9 @@
 # Arguments:
 #   - (optional) prereqs for this test, e.g. 'SYMLINKS'
 #   - test name
-#   - output to expect from -v / --verbose mode
+#   - output to expect from the fourth verbosity mode (the output
+#     from the other verbosity modes is automatically inferred
+#     from this value)
 #   - code to run (should invoke test_check_ignore)
 test_expect_success_multi () {
 	prereq=
@@ -92,8 +101,9 @@
 		prereq=$1
 		shift
 	fi
-	testname="$1" expect_verbose="$2" code="$3"
+	testname="$1" expect_all="$2" code="$3"
 
+	expect_verbose=$( echo "$expect_all" | grep -v '^::	' )
 	expect=$( echo "$expect_verbose" | sed -e 's/.*	//' )
 
 	test_expect_success $prereq "$testname" '
@@ -101,23 +111,40 @@
 		eval "$code"
 	'
 
-	for quiet_opt in '-q' '--quiet'
-	do
-		test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
+	# --quiet is only valid when a single pattern is passed
+	if test $( echo "$expect_all" | wc -l ) = 1
+	then
+		for quiet_opt in '-q' '--quiet'
+		do
+			test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
 			expect '' &&
 			$code
 		"
-	done
-	quiet_opt=
+		done
+		quiet_opt=
+	fi
 
 	for verbose_opt in '-v' '--verbose'
 	do
-		test_expect_success $prereq "$testname${verbose_opt:+ with $verbose_opt}" "
-			expect '$expect_verbose' &&
-			$code
-		"
+		for non_matching_opt in '' ' -n' ' --non-matching'
+		do
+			if test -n "$non_matching_opt"
+			then
+				my_expect="$expect_all"
+			else
+				my_expect="$expect_verbose"
+			fi
+
+			test_code="
+				expect '$my_expect' &&
+				$code
+			"
+			opts="$verbose_opt$non_matching_opt"
+			test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code"
+		done
 	done
 	verbose_opt=
+	non_matching_opt=
 }
 
 test_expect_success 'setup' '
@@ -178,7 +205,7 @@
 #
 # test invalid inputs
 
-test_expect_success_multi '. corner-case' '' '
+test_expect_success_multi '. corner-case' '::	.' '
 	test_check_ignore . 1
 '
 
@@ -189,11 +216,7 @@
 
 test_expect_success_multi '--stdin with empty STDIN' '' '
 	test_check_ignore "--stdin" 1 </dev/null &&
-	if test -n "$quiet_opt"; then
-		test_stderr ""
-	else
-		test_stderr "no pathspec given."
-	fi
+	test_stderr ""
 '
 
 test_expect_success '-q with multiple args' '
@@ -276,27 +299,39 @@
 		where="in subdir $subdir"
 	fi
 
-	test_expect_success_multi "non-existent file $where not ignored" '' "
-		test_check_ignore '${subdir}non-existent' 1
-	"
+	test_expect_success_multi "non-existent file $where not ignored" \
+		"::	${subdir}non-existent" \
+		"test_check_ignore '${subdir}non-existent' 1"
 
 	test_expect_success_multi "non-existent file $where ignored" \
-		".gitignore:1:one	${subdir}one" "
-		test_check_ignore '${subdir}one'
-	"
+		".gitignore:1:one	${subdir}one" \
+		"test_check_ignore '${subdir}one'"
 
-	test_expect_success_multi "existing untracked file $where not ignored" '' "
-		test_check_ignore '${subdir}not-ignored' 1
-	"
+	test_expect_success_multi "existing untracked file $where not ignored" \
+		"::	${subdir}not-ignored" \
+		"test_check_ignore '${subdir}not-ignored' 1"
 
-	test_expect_success_multi "existing tracked file $where not ignored" '' "
-		test_check_ignore '${subdir}ignored-but-in-index' 1
-	"
+	test_expect_success_multi "existing tracked file $where not ignored" \
+		"::	${subdir}ignored-but-in-index" \
+		"test_check_ignore '${subdir}ignored-but-in-index' 1"
 
 	test_expect_success_multi "existing untracked file $where ignored" \
-		".gitignore:2:ignored-*	${subdir}ignored-and-untracked" "
-		test_check_ignore '${subdir}ignored-and-untracked'
-	"
+		".gitignore:2:ignored-*	${subdir}ignored-and-untracked" \
+		"test_check_ignore '${subdir}ignored-and-untracked'"
+
+	test_expect_success_multi "mix of file types $where" \
+"::	${subdir}non-existent
+.gitignore:1:one	${subdir}one
+::	${subdir}not-ignored
+::	${subdir}ignored-but-in-index
+.gitignore:2:ignored-*	${subdir}ignored-and-untracked" \
+		"test_check_ignore '
+			${subdir}non-existent
+			${subdir}one
+			${subdir}not-ignored
+			${subdir}ignored-but-in-index
+			${subdir}ignored-and-untracked'
+		"
 done
 
 # Having established the above, from now on we mostly test against
@@ -391,7 +426,7 @@
 #
 # test handling of symlinks
 
-test_expect_success_multi SYMLINKS 'symlink' '' '
+test_expect_success_multi SYMLINKS 'symlink' '::	a/symlink' '
 	test_check_ignore "a/symlink" 1
 '
 
@@ -574,37 +609,34 @@
 	globaltwo
 	b/globaltwo
 	../b/globaltwo
+	c/not-ignored
 EOF
-cat <<-\EOF >expected-default
-	../one
-	one
-	b/on
-	b/one
-	b/one one
-	b/one two
-	"b/one\"three"
-	b/two
-	b/twooo
-	../globaltwo
-	globaltwo
-	b/globaltwo
-	../b/globaltwo
-EOF
-cat <<-EOF >expected-verbose
+# N.B. we deliberately end STDIN with a non-matching pattern in order
+# to test that the exit code indicates that one or more of the
+# provided paths is ignored - in other words, that it represents an
+# aggregation of all the results, not just the final result.
+
+cat <<-EOF >expected-all
 	.gitignore:1:one	../one
+	::	../not-ignored
 	.gitignore:1:one	one
+	::	not-ignored
 	a/b/.gitignore:8:!on*	b/on
 	a/b/.gitignore:8:!on*	b/one
 	a/b/.gitignore:8:!on*	b/one one
 	a/b/.gitignore:8:!on*	b/one two
 	a/b/.gitignore:8:!on*	"b/one\"three"
 	a/b/.gitignore:9:!two	b/two
+	::	b/not-ignored
 	a/.gitignore:1:two*	b/twooo
 	$global_excludes:2:!globaltwo	../globaltwo
 	$global_excludes:2:!globaltwo	globaltwo
 	$global_excludes:2:!globaltwo	b/globaltwo
 	$global_excludes:2:!globaltwo	../b/globaltwo
+	::	c/not-ignored
 EOF
+grep -v '^::	' expected-all >expected-verbose
+sed -e 's/.*	//' expected-verbose >expected-default
 
 sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
 	tr "\n" "\0" >stdin0
@@ -629,6 +661,14 @@
 	)
 '
 
+test_expect_success '--stdin from subdirectory with -v -n' '
+	expect_from_stdin <expected-all &&
+	(
+		cd a &&
+		test_check_ignore "--stdin -v -n" <../stdin
+	)
+'
+
 for opts in '--stdin -z' '-z --stdin'
 do
 	test_expect_success "$opts from subdirectory" '
@@ -648,5 +688,31 @@
 	'
 done
 
+test_expect_success PIPE 'streaming support for --stdin' '
+	mkfifo in out &&
+	(git check-ignore -n -v --stdin <in >out &) &&
+
+	# We cannot just "echo >in" because check-ignore would get EOF
+	# after echo exited; instead we open the descriptor in our
+	# shell, and then echo to the fd. We make sure to close it at
+	# the end, so that the subprocess does get EOF and dies
+	# properly.
+	#
+	# Similarly, we must keep "out" open so that check-ignore does
+	# not ever get SIGPIPE trying to write to us. Not only would that
+	# produce incorrect results, but then there would be no writer on the
+	# other end of the pipe, and we would potentially block forever trying
+	# to open it.
+	exec 9>in &&
+	exec 8<out &&
+	test_when_finished "exec 9>&-" &&
+	test_when_finished "exec 8<&-" &&
+	echo >&9 one &&
+	read response <&8 &&
+	echo "$response" | grep "^\.gitignore:1:one	one" &&
+	echo >&9 two &&
+	read response <&8 &&
+	echo "$response" | grep "^::	two"
+'
 
 test_done
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
new file mode 100755
index 0000000..94045c3
--- /dev/null
+++ b/t/t0009-prio-queue.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='basic tests for priority queue implementation'
+. ./test-lib.sh
+
+cat >expect <<'EOF'
+1
+2
+3
+4
+5
+5
+6
+7
+8
+9
+10
+EOF
+test_expect_success 'basic ordering' '
+	test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+2
+3
+4
+1
+5
+6
+EOF
+test_expect_success 'mixed put and get' '
+	test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
+	test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+1
+2
+NULL
+1
+2
+NULL
+EOF
+test_expect_success 'notice empty queue' '
+	test-prio-queue 1 2 get get get 1 2 get get get >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 1a8f44c..e526184 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -81,6 +81,14 @@
 	test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
 '
 
+
+test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
+	git config core.autocrlf input &&
+	git config core.safecrlf true &&
+	git diff HEAD
+'
+
+
 test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
 	git config core.autocrlf false &&
 	git config core.safecrlf false &&
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 244a43c..65606df 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -50,7 +50,7 @@
 
 test_expect_success 'test help' '
 	test_must_fail test-parse-options -h > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_i18ncmp expect output
 '
 
@@ -75,7 +75,7 @@
 	shift &&
 	sed "s/^$what .*/$what $expect/" <expect.template >expect &&
 	test-parse-options $* >output 2>output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 }
 
@@ -86,7 +86,7 @@
 	shift &&
 	sed "s/^$what .*/$what $expect/" <expect.template >expect &&
 	test-parse-options $* >output 2>output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_i18ncmp expect output
 }
 
@@ -99,7 +99,7 @@
 	esac &&
 	cat expect.err >>expect &&
 	test_must_fail test-parse-options $* >output 2>output.err &&
-	test ! -s output &&
+	test_must_be_empty output &&
 	test_cmp expect output.err
 }
 
@@ -112,7 +112,7 @@
 	esac &&
 	cat expect.err >>expect &&
 	test_must_fail test-parse-options $* >output 2>output.err &&
-	test ! -s output &&
+	test_must_be_empty output &&
 	test_i18ncmp expect output.err
 }
 
@@ -149,7 +149,7 @@
 	test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \
 	> output 2> output.err &&
 	test_cmp expect output &&
-	test ! -s output.err
+	test_must_be_empty output.err
 '
 
 cat > expect << EOF
@@ -168,7 +168,7 @@
 	test-parse-options --boolean --integer 1729 --boolean --string2=321 \
 		--verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
 		--obsolete > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -199,7 +199,7 @@
 test_expect_success 'intermingled arguments' '
 	test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
 		> output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -217,13 +217,13 @@
 
 test_expect_success 'unambiguously abbreviated option' '
 	test-parse-options --int 2 --boolean --no-bo > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
 test_expect_success 'unambiguously abbreviated option with "="' '
 	test-parse-options --int=2 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -246,7 +246,7 @@
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
 	test-parse-options --st 123 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -256,7 +256,7 @@
 
 test_expect_success 'detect possible typos' '
 	test_must_fail test-parse-options -boolean > output 2> output.err &&
-	test ! -s output &&
+	test_must_be_empty output &&
 	test_cmp typo.err output.err
 '
 
@@ -266,7 +266,7 @@
 
 test_expect_success 'detect possible typos' '
 	test_must_fail test-parse-options -ambiguous > output 2> output.err &&
-	test ! -s output &&
+	test_must_be_empty output &&
 	test_cmp typo.err output.err
 '
 
@@ -285,7 +285,7 @@
 
 test_expect_success 'keep some options as arguments' '
 	test-parse-options --quux > output 2> output.err &&
-        test ! -s output.err &&
+	test_must_be_empty output.err &&
         test_cmp expect output
 '
 
@@ -305,7 +305,7 @@
 test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' '
 	test-parse-options -t "1970-01-01 00:00:01 +0000" --default-string \
 		foo -q > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -324,7 +324,7 @@
 
 test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
 	test-parse-options --length=four -b -4 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -352,13 +352,13 @@
 
 test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
 	test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
 	test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -376,19 +376,19 @@
 
 test_expect_success 'OPT_BIT() works' '
 	test-parse-options -bb --or4 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() works' '
 	test-parse-options -bb --no-neg-or4 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
 	test-parse-options + + + + + + > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -406,7 +406,7 @@
 
 test_expect_success 'OPT_NUMBER_CALLBACK() works' '
 	test-parse-options -12345 > output 2> output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
@@ -424,7 +424,7 @@
 
 test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
 	test-parse-options --no-ambig >output 2>output.err &&
-	test ! -s output.err &&
+	test_must_be_empty output.err &&
 	test_cmp expect output
 '
 
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 09a42a4..3a48de2 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -8,8 +8,15 @@
 . ./test-lib.sh
 
 norm_path() {
+	expected=$(test-path-utils mingw_path "$2")
 	test_expect_success $3 "normalize path: $1 => $2" \
-	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
+}
+
+relative_path() {
+	expected=$(test-path-utils mingw_path "$3")
+	test_expect_success $4 "relative path: $1 $2 => $3" \
+	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
 # On Windows, we are using MSYS's bash, which mangles the paths.
@@ -34,8 +41,8 @@
 	 test \"\$actual\" = '$expected'"
 }
 
-# Absolute path tests must be skipped on Windows because due to path mangling
-# the test program never sees a POSIX-style absolute path
+# Some absolute path tests should be skipped on Windows due to path mangling
+# on POSIX-style absolute paths
 case $(uname -s) in
 *MINGW*)
 	;;
@@ -68,30 +75,30 @@
 norm_path d1/.../d2 d1/.../d2
 norm_path d1/..././../d2 d1/d2
 
-norm_path / / POSIX
+norm_path / /
 norm_path // / POSIX
 norm_path /// / POSIX
-norm_path /. / POSIX
+norm_path /. /
 norm_path /./ / POSIX
 norm_path /./.. ++failed++ POSIX
-norm_path /../. ++failed++ POSIX
+norm_path /../. ++failed++
 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 /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/ 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
+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
 
 ancestor / / -1
 ancestor /foo / 0
@@ -183,4 +190,33 @@
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
+relative_path /a/b/c/	/a/b/		c/
+relative_path /a/b/c/	/a/b		c/
+relative_path /a//b//c/	//a/b//		c/	POSIX
+relative_path /a/b	/a/b		./
+relative_path /a/b/	/a/b		./
+relative_path /a	/a/b		../
+relative_path /		/a/b/		../../
+relative_path /a/c	/a/b/		../c
+relative_path /a/c	/a/b		../c
+relative_path /x/y	/a/b/		../../x/y
+relative_path /a/b	"<empty>"	/a/b
+relative_path /a/b 	"<null>"	/a/b
+relative_path a/b/c/	a/b/		c/
+relative_path a/b/c/	a/b		c/
+relative_path a/b//c	a//b		c
+relative_path a/b/	a/b/		./
+relative_path a/b/	a/b		./
+relative_path a		a/b		../
+relative_path x/y	a/b		../../x/y
+relative_path a/c	a/b		../c
+relative_path a/b	"<empty>"	a/b
+relative_path a/b 	"<null>"	a/b
+relative_path "<empty>"	/a/b		./
+relative_path "<empty>"	"<empty>"	./
+relative_path "<empty>"	"<null>"	./
+relative_path "<null>"	"<empty>"	./
+relative_path "<null>"	"<null>"	./
+relative_path "<null>"	/a/b		./
+
 test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index da2c504..986b2a8 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -17,7 +17,7 @@
 	grep "doesnotexist/test" err
 '
 
-test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' '
+test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' '
 	mkdir cannotwrite &&
 	chmod -w cannotwrite &&
 	test_when_finished "chmod +w cannotwrite" &&
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 315b9b3..e0a6940 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -27,6 +27,7 @@
 	test_commit B &&
 	git checkout A &&
 	test_commit C &&
+	test_commit D &&
 	git branch -f master B &&
 	git branch -f other &&
 	git checkout other &&
@@ -35,14 +36,24 @@
 	git cat-file commit HEAD | grep "Merge branch '\''other'\''"
 '
 
-test_expect_success 'merge @{-1} when there is not enough switches yet' '
+test_expect_success 'merge @{-1}~1' '
+	git checkout master &&
+	git reset --hard B &&
+	git checkout other &&
+	git checkout master &&
+	git merge @{-1}~1 &&
+	git cat-file commit HEAD >actual &&
+	grep "Merge branch '\''other'\''" actual
+'
+
+test_expect_success 'merge @{-100} before checking out that many branches 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_must_fail git merge @{-100}
 '
 
 test_done
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index b3ae7d5..3e72aff 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -158,7 +158,7 @@
 
 '
 
-test_expect_success SYMLINKS 'funny symlink in work tree' '
+test_expect_success 'funny symlink in work tree' '
 
 	git reset --hard &&
 	git checkout -b sym-b side-b &&
@@ -170,15 +170,14 @@
 	rm -fr a &&
 	git checkout -b sym-a side-a &&
 	mkdir -p a &&
-	ln -s ../b a/b &&
-	git add a/b &&
+	test_ln_s_add ../b a/b &&
 	git commit -m "we add a/b" &&
 
 	read_tree_u_must_succeed -m -u sym-a sym-a sym-b
 
 '
 
-test_expect_success SYMLINKS,SANITY 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
 
 	rm -fr a b &&
 	git reset --hard &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 9cc5c6b..d499d02 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -36,66 +36,54 @@
     '
 
     test_expect_success "Type of $type is correct" '
-        test $type = "$(git cat-file -t $sha1)"
+	echo $type >expect &&
+	git cat-file -t $sha1 >actual &&
+	test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct" '
-        test $size = "$(git cat-file -s $sha1)"
+	echo $size >expect &&
+	git cat-file -s $sha1 >actual &&
+	test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
-	expect="$(maybe_remove_timestamp "$content" $no_ts)"
-	actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)"
-
-        if test "z$expect" = "z$actual"
-	then
-		: happy
-	else
-		echo "Oops: expected $expect"
-		echo "but got $actual"
-		false
-        fi
+	maybe_remove_timestamp "$content" $no_ts >expect &&
+	maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual &&
+	test_cmp expect actual
     '
 
     test_expect_success "Pretty content of $type is correct" '
-	expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)"
-	actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)"
-        if test "z$expect" = "z$actual"
-	then
-		: happy
-	else
-		echo "Oops: expected $expect"
-		echo "but got $actual"
-		false
-        fi
+	maybe_remove_timestamp "$pretty_content" $no_ts >expect &&
+	maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual &&
+	test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "--batch output of $type is correct" '
-	expect="$(maybe_remove_timestamp "$batch_output" $no_ts)"
-	actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)"
-        if test "z$expect" = "z$actual"
-	then
-		: happy
-	else
-		echo "Oops: expected $expect"
-		echo "but got $actual"
-		false
-        fi
+	maybe_remove_timestamp "$batch_output" $no_ts >expect &&
+	maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual &&
+	test_cmp expect actual
     '
 
     test_expect_success "--batch-check output of $type is correct" '
-	expect="$sha1 $type $size"
-	actual="$(echo_without_newline $sha1 | git cat-file --batch-check)"
-        if test "z$expect" = "z$actual"
-	then
-		: happy
-	else
-		echo "Oops: expected $expect"
-		echo "but got $actual"
-		false
-        fi
+	echo "$sha1 $type $size" >expect &&
+	echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+	test_cmp expect actual
+    '
+
+    test_expect_success "custom --batch-check format" '
+	echo "$type $sha1" >expect &&
+	echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+	test_cmp expect actual
+    '
+
+    test_expect_success '--batch-check with %(rest)' '
+	echo "$type this is some extra content" >expect &&
+	echo "$sha1    this is some extra content" |
+		git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
+	test_cmp expect actual
     '
 }
 
diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh
new file mode 100755
index 0000000..fdc257e
--- /dev/null
+++ b/t/t1307-config-blob.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='support for reading config from a blob'
+. ./test-lib.sh
+
+test_expect_success 'create config blob' '
+	cat >config <<-\EOF &&
+	[some]
+		value = 1
+	EOF
+	git add config &&
+	git commit -m foo
+'
+
+test_expect_success 'list config blob contents' '
+	echo some.value=1 >expect &&
+	git config --blob=HEAD:config --list >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'fetch value from blob' '
+	echo true >expect &&
+	git config --blob=HEAD:config --bool some.value >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'reading non-existing value from blob is an error' '
+	test_must_fail git config --blob=HEAD:config non.existing
+'
+
+test_expect_success 'reading from blob and file is an error' '
+	test_must_fail git config --blob=HEAD:config --system --list
+'
+
+test_expect_success 'reading from missing ref is an error' '
+	test_must_fail git config --blob=HEAD:doesnotexist --list
+'
+
+test_expect_success 'reading from non-blob is an error' '
+	test_must_fail git config --blob=HEAD --list
+'
+
+test_expect_success 'setting a value in a blob is an error' '
+	test_must_fail git config --blob=HEAD:config some.value foo
+'
+
+test_expect_success 'deleting a value in a blob is an error' '
+	test_must_fail git config --blob=HEAD:config --unset some.value
+'
+
+test_expect_success 'editing a blob is an error' '
+	test_must_fail git config --blob=HEAD:config --edit
+'
+
+test_expect_success 'parse errors in blobs are properly attributed' '
+	cat >config <<-\EOF &&
+	[some]
+		value = "
+	EOF
+	git add config &&
+	git commit -m broken &&
+
+	test_must_fail git config --blob=HEAD:config some.value 2>err &&
+
+	# just grep for our token as the exact error message is likely to
+	# change or be internationalized
+	grep "HEAD:config" err
+'
+
+test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
new file mode 100755
index 0000000..3e500ed
--- /dev/null
+++ b/t/t1403-show-ref.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='show-ref'
+. ./test-lib.sh
+
+test_expect_success setup '
+	test_commit A &&
+	git tag -f -a -m "annotated A" A &&
+	git checkout -b side &&
+	test_commit B &&
+	git tag -f -a -m "annotated B" B &&
+	git checkout master &&
+	test_commit C &&
+	git branch B A^0
+'
+
+test_expect_success 'show-ref' '
+	echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+	git show-ref A >actual &&
+	test_cmp expect actual &&
+
+	git show-ref tags/A >actual &&
+	test_cmp expect actual &&
+
+	git show-ref refs/tags/A >actual &&
+	test_cmp expect actual &&
+
+	>expect &&
+
+	test_must_fail git show-ref D >actual
+	test_cmp expect actual
+'
+
+test_expect_success 'show-ref -q' '
+	>expect &&
+
+	git show-ref -q A >actual &&
+	test_cmp expect actual &&
+
+	git show-ref -q tags/A >actual &&
+	test_cmp expect actual &&
+
+	git show-ref -q refs/tags/A >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref -q D >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify' '
+	echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+	git show-ref --verify refs/tags/A >actual &&
+	test_cmp expect actual &&
+
+	>expect &&
+
+	test_must_fail git show-ref --verify A >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref --verify tags/A >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref --verify D >actual
+	test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify -q' '
+	>expect &&
+
+	git show-ref --verify -q refs/tags/A >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref --verify -q A >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref --verify -q tags/A >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref --verify -q D >actual
+	test_cmp expect actual
+'
+
+test_expect_success 'show-ref -d' '
+	{
+		echo $(git rev-parse refs/tags/A) refs/tags/A &&
+		echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+		echo $(git rev-parse refs/tags/C) refs/tags/C
+	} >expect &&
+	git show-ref -d A C >actual &&
+	test_cmp expect actual &&
+
+	git show-ref -d tags/A tags/C >actual &&
+	test_cmp expect actual &&
+
+	git show-ref -d refs/tags/A refs/tags/C >actual &&
+	test_cmp expect actual &&
+
+	echo $(git rev-parse refs/heads/master) refs/heads/master >expect &&
+	git show-ref -d master >actual &&
+	test_cmp expect actual &&
+
+	git show-ref -d heads/master >actual &&
+	test_cmp expect actual &&
+
+	git show-ref -d refs/heads/master >actual &&
+	test_cmp expect actual
+
+	git show-ref -d --verify refs/heads/master >actual &&
+	test_cmp expect actual
+
+	>expect &&
+
+	test_must_fail git show-ref -d --verify master >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git show-ref -d --verify heads/master >actual &&
+	test_cmp expect actual
+
+'
+
+test_expect_success 'show-ref --heads, --tags, --head, pattern' '
+	for branch in B master side
+	do
+		echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+	done >expect.heads &&
+	git show-ref --heads >actual &&
+	test_cmp expect.heads actual &&
+
+	for tag in A B C
+	do
+		echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+	done >expect.tags &&
+	git show-ref --tags >actual &&
+	test_cmp expect.tags actual &&
+
+	cat expect.heads expect.tags >expect &&
+	git show-ref --heads --tags >actual &&
+	test_cmp expect actual &&
+
+	{
+		echo $(git rev-parse HEAD) HEAD &&
+		cat expect.heads expect.tags
+	} >expect &&
+	git show-ref --heads --tags --head >actual &&
+	test_cmp expect actual &&
+
+	{
+		echo $(git rev-parse HEAD) HEAD &&
+		echo $(git rev-parse refs/heads/B) refs/heads/B
+		echo $(git rev-parse refs/tags/B) refs/tags/B
+	} >expect &&
+	git show-ref --head B >actual &&
+	test_cmp expect actual &&
+
+	{
+		echo $(git rev-parse HEAD) HEAD &&
+		echo $(git rev-parse refs/heads/B) refs/heads/B
+		echo $(git rev-parse refs/tags/B) refs/tags/B
+		echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
+	} >expect &&
+	git show-ref --head -d B >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index b27a720..2a19e79 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -129,8 +129,7 @@
 
 test_expect_success 'branch@{u} error message when no upstream' '
 	cat >expect <<-EOF &&
-	error: No upstream configured for branch ${sq}non-tracking${sq}
-	fatal: Needed a single revision
+	fatal: No upstream configured for branch ${sq}non-tracking${sq}
 	EOF
 	error_message non-tracking@{u} 2>actual &&
 	test_i18ncmp expect actual
@@ -138,8 +137,7 @@
 
 test_expect_success '@{u} error message when no upstream' '
 	cat >expect <<-EOF &&
-	error: No upstream configured for branch ${sq}master${sq}
-	fatal: Needed a single revision
+	fatal: No upstream configured for branch ${sq}master${sq}
 	EOF
 	test_must_fail git rev-parse --verify @{u} 2>actual &&
 	test_i18ncmp expect actual
@@ -147,8 +145,7 @@
 
 test_expect_success 'branch@{u} error message with misspelt branch' '
 	cat >expect <<-EOF &&
-	error: No such branch: ${sq}no-such-branch${sq}
-	fatal: Needed a single revision
+	fatal: No such branch: ${sq}no-such-branch${sq}
 	EOF
 	error_message no-such-branch@{u} 2>actual &&
 	test_i18ncmp expect actual
@@ -156,8 +153,7 @@
 
 test_expect_success '@{u} error message when not on a branch' '
 	cat >expect <<-EOF &&
-	error: HEAD does not point to a branch
-	fatal: Needed a single revision
+	fatal: HEAD does not point to a branch
 	EOF
 	git checkout HEAD^0 &&
 	test_must_fail git rev-parse --verify @{u} 2>actual &&
@@ -166,8 +162,7 @@
 
 test_expect_success 'branch@{u} error message if upstream branch not fetched' '
 	cat >expect <<-EOF &&
-	error: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
-	fatal: Needed a single revision
+	fatal: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
 	EOF
 	error_message bad-upstream@{u} 2>actual &&
 	test_i18ncmp expect actual
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
index d5d6244..4db1613 100755
--- a/t/t1508-at-combinations.sh
+++ b/t/t1508-at-combinations.sh
@@ -4,17 +4,24 @@
 . ./test-lib.sh
 
 check() {
-test_expect_${3:-success} "$1 = $2" "
-	echo '$2' >expect &&
-	git log -1 --format=%s '$1' >actual &&
-	test_cmp expect actual
-"
+	test_expect_${4:-success} "$1 = $3" "
+		echo '$3' >expect &&
+		if test '$2' = 'commit'
+		then
+			git log -1 --format=%s '$1' >actual
+		else
+			git rev-parse --symbolic-full-name '$1' >actual
+		fi &&
+		test_cmp expect actual
+	"
 }
+
 nonsense() {
-test_expect_${2:-success} "$1 is nonsensical" "
-	test_must_fail git log -1 '$1'
-"
+	test_expect_${2:-success} "$1 is nonsensical" "
+		test_must_fail git rev-parse --verify '$1'
+	"
 }
+
 fail() {
 	"$@" failure
 }
@@ -31,21 +38,42 @@
 	git checkout -b new-branch &&
 	test_commit new-one &&
 	test_commit new-two &&
-	git config branch.old-branch.remote . &&
-	git config branch.old-branch.merge refs/heads/master &&
-	git config branch.new-branch.remote . &&
-	git config branch.new-branch.merge refs/heads/upstream-branch
+	git branch -u master old-branch &&
+	git branch -u upstream-branch new-branch
 '
 
-check HEAD new-two
-check "@{1}" new-one
-check "@{-1}" old-two
-check "@{-1}@{1}" old-one
-check "@{u}" upstream-two
-check "@{u}@{1}" upstream-one
-check "@{-1}@{u}" master-two
-check "@{-1}@{u}@{1}" master-one
+check HEAD ref refs/heads/new-branch
+check "@{1}" commit new-one
+check "HEAD@{1}" commit new-one
+check "@{now}" commit new-two
+check "HEAD@{now}" commit new-two
+check "@{-1}" ref refs/heads/old-branch
+check "@{-1}@{0}" commit old-two
+check "@{-1}@{1}" commit old-one
+check "@{u}" ref refs/heads/upstream-branch
+check "HEAD@{u}" ref refs/heads/upstream-branch
+check "@{u}@{1}" commit upstream-one
+check "@{-1}@{u}" ref refs/heads/master
+check "@{-1}@{u}@{1}" commit master-one
+check "@" commit new-two
+check "@@{u}" ref refs/heads/upstream-branch
 nonsense "@{u}@{-1}"
+nonsense "@{0}@{0}"
 nonsense "@{1}@{u}"
+nonsense "HEAD@{-1}"
+nonsense "@{-1}@{-1}"
+
+# @{N} versus HEAD@{N}
+
+check "HEAD@{3}" commit old-two
+nonsense "@{3}"
+
+test_expect_success 'switch to old-branch' '
+	git checkout old-branch
+'
+
+check HEAD ref refs/heads/old-branch
+check "HEAD@{1}" commit new-two
+check "@{1}" commit old-one
 
 test_done
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 6b3d797..4a155c8 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -77,6 +77,7 @@
 
 test_expect_success 'disambiguate tree' '
 	commit=$(echo "d7xm" | git commit-tree 000000000) &&
+	# this commit is fffff2e and not ambiguous with the 00000* objects
 	test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
 '
 
@@ -99,10 +100,14 @@
 
 test_expect_success 'disambiguate commit' '
 	commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
+	# this commit is ffffffd8 and not ambiguous with the 00000* objects
 	test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
 '
 
 test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
+	# These are underspecified from the prefix-length point of view
+	# to disambiguate the commit with other objects, but there is only
+	# one commit that has 00000* prefix at this point.
 	git log 000000000..000000000 &&
 	git log ..000000000 &&
 	git log 000000000.. &&
@@ -112,16 +117,19 @@
 '
 
 test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
+	# Likewise.
 	git rev-parse 000000000..000000000 &&
 	git rev-parse ..000000000 &&
 	git rev-parse 000000000..
 '
 
 test_expect_success 'git log takes only commit-ish' '
+	# Likewise.
 	git log 000000000
 '
 
 test_expect_success 'git reset takes only commit-ish' '
+	# Likewise.
 	git reset 000000000
 '
 
@@ -131,26 +139,30 @@
 '
 
 test_expect_failure 'two semi-ambiguous commit-ish' '
+	# At this point, we have a tag 0000000000f8f that points
+	# at a commit 0000000000e4f, and a tree and a blob that
+	# share 0000000000 prefix with these tag and commit.
+	#
 	# Once the parser becomes ultra-smart, it could notice that
-	# 110282 before ^{commit} name many different objects, but
+	# 0000000000 before ^{commit} name many different objects, but
 	# that only two (HEAD and v1.0.0 tag) can be peeled to commit,
 	# and that peeling them down to commit yield the same commit
 	# without ambiguity.
-	git rev-parse --verify 110282^{commit} &&
+	git rev-parse --verify 0000000000^{commit} &&
 
 	# likewise
-	git log 000000000..000000000 &&
-	git log ..000000000 &&
-	git log 000000000.. &&
-	git log 000000000...000000000 &&
-	git log ...000000000 &&
-	git log 000000000...
+	git log 0000000000..0000000000 &&
+	git log ..0000000000 &&
+	git log 0000000000.. &&
+	git log 0000000000...0000000000 &&
+	git log ...0000000000 &&
+	git log 0000000000...
 '
 
 test_expect_failure 'three semi-ambiguous tree-ish' '
 	# Likewise for tree-ish.  HEAD, v1.0.0 and HEAD^{tree} share
 	# the prefix but peeling them to tree yields the same thing
-	git rev-parse --verify 000000000^{tree}
+	git rev-parse --verify 0000000000^{tree}
 '
 
 test_expect_success 'parse describe name' '
@@ -241,7 +253,7 @@
 	# Now there are many commits that begin with the
 	# common prefix, none of these should pick one at
 	# random.  They all should result in ambiguity errors.
-	test_must_fail git rev-parse --verify 110282^{commit} &&
+	test_must_fail git rev-parse --verify 00000000^{commit} &&
 
 	# likewise
 	test_must_fail git log 000000000..000000000 &&
@@ -261,4 +273,22 @@
 	test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
 '
 
+test_expect_success 'ambiguous 40-hex ref' '
+	TREE=$(git mktree </dev/null) &&
+	REF=`git rev-parse HEAD` &&
+	VAL=$(git commit-tree $TREE </dev/null) &&
+	git update-ref refs/heads/$REF $VAL &&
+	test `git rev-parse $REF 2>err` = $REF &&
+	grep "refname.*${REF}.*ambiguous" err
+'
+
+test_expect_success 'ambiguous short sha1 ref' '
+	TREE=$(git mktree </dev/null) &&
+	REF=`git rev-parse --short HEAD` &&
+	VAL=$(git commit-tree $TREE </dev/null) &&
+	git update-ref refs/heads/$REF $VAL &&
+	test `git rev-parse $REF 2>err` = $VAL &&
+	grep "refname.*${REF}.*ambiguous" err
+'
+
 test_done
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
new file mode 100755
index 0000000..87ec3ae
--- /dev/null
+++ b/t/t1513-rev-parse-prefix.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='Tests for rev-parse --prefix'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir -p sub1/sub2 &&
+	echo top >top &&
+	echo file1 >sub1/file1 &&
+	echo file2 >sub1/sub2/file2 &&
+	git add top sub1/file1 sub1/sub2/file2 &&
+	git commit -m commit
+'
+
+test_expect_success 'empty prefix -- file' '
+	git rev-parse --prefix "" -- top sub1/file1 >actual &&
+	cat <<-\EOF >expected &&
+	--
+	top
+	sub1/file1
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- file' '
+	git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual &&
+	cat <<-\EOF >expected &&
+	--
+	sub1/file1
+	sub1/sub2/file2
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- ../file' '
+	git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual &&
+	cat <<-\EOF >expected &&
+	--
+	sub1/../top
+	sub1/sub2/file2
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'empty prefix HEAD:./path' '
+	git rev-parse --prefix "" HEAD:./top >actual &&
+	git rev-parse HEAD:top >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:./path' '
+	git rev-parse --prefix sub1/ HEAD:./file1 >actual &&
+	git rev-parse HEAD:sub1/file1 >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:../path' '
+	git rev-parse --prefix sub1/ HEAD:../top >actual &&
+	git rev-parse HEAD:top >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'prefix ignored with HEAD:top' '
+	git rev-parse --prefix sub1/ HEAD:top >actual &&
+	git rev-parse HEAD:top >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'disambiguate path with valid prefix' '
+	git rev-parse --prefix sub1/ file1 >actual &&
+	cat <<-\EOF >expected &&
+	sub1/file1
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'file and refs with prefix' '
+	git rev-parse --prefix sub1/ master file1 >actual &&
+	cat <<-EOF >expected &&
+	$(git rev-parse master)
+	sub1/file1
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'two-levels deep' '
+	git rev-parse --prefix sub1/sub2/ -- file2 >actual &&
+	cat <<-\EOF >expected &&
+	--
+	sub1/sub2/file2
+	EOF
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
index 98aa73e..1fc8e63 100755
--- a/t/t2001-checkout-cache-clash.sh
+++ b/t/t2001-checkout-cache-clash.sh
@@ -59,10 +59,9 @@
     'git read-tree -m $tree1 && git checkout-index -f -a'
 test_debug 'show_files $tree1'
 
-test_expect_success SYMLINKS \
-    'git update-index --add a symlink.' \
-    'ln -s path0 path1 &&
-     git update-index --add path1'
+test_expect_success \
+    'add a symlink' \
+    'test_ln_s_add path0 path1'
 test_expect_success \
     'writing tree out with git write-tree' \
     'tree3=$(git write-tree)'
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index 0f4b289..f171a55 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -194,11 +194,10 @@
  test $(cat ../$s1) = tree1asubdir/path5)
 )'
 
-test_expect_success SYMLINKS \
+test_expect_success \
 'checkout --temp symlink' '
 rm -f path* .merge_* out .git/index &&
-ln -s b a &&
-git update-index --add a &&
+test_ln_s_add b a &&
 t4=$(git write-tree) &&
 rm -f .git/index &&
 git read-tree $t4 &&
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index e6f59f1..fc9aad5 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -6,7 +6,7 @@
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
 	mkdir frotz &&
 	echo hello >frotz/filfre &&
@@ -25,25 +25,25 @@
 
 	git rm --cached frotz/filfre &&
 	mv frotz xyzzy &&
-	ln -s xyzzy frotz &&
-	git add xyzzy/filfre frotz &&
+	test_ln_s_add xyzzy frotz &&
+	git add xyzzy/filfre &&
 	test_tick &&
 	git commit -m "side moves frotz/ to xyzzy/ and adds frotz->xyzzy/"
 
 '
 
-test_expect_success SYMLINKS 'switch from symlink to dir' '
+test_expect_success 'switch from symlink to dir' '
 
 	git checkout master
 
 '
 
-test_expect_success SYMLINKS 'Remove temporary directories & switch to master' '
+test_expect_success 'Remove temporary directories & switch to master' '
 	rm -fr frotz xyzzy nitfol &&
 	git checkout -f master
 '
 
-test_expect_success SYMLINKS 'switch from dir to symlink' '
+test_expect_success 'switch from dir to symlink' '
 
 	git checkout side
 
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index b44de9d..e7ba8c5 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -116,4 +116,38 @@
 	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
 '
 
+test_expect_success '"checkout -" works after a rebase A' '
+	git checkout master &&
+	git checkout other &&
+	git rebase master &&
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase A B' '
+	git branch moodle master~1 &&
+	git checkout master &&
+	git checkout other &&
+	git rebase master moodle &&
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A' '
+	git checkout master &&
+	git checkout other &&
+	git rebase -i master &&
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A B' '
+	git branch foodle master~1 &&
+	git checkout master &&
+	git checkout other &&
+	git rebase master foodle &&
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
 test_done
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
index 5da63e9..c2ada7d 100755
--- a/t/t2021-checkout-overwrite.sh
+++ b/t/t2021-checkout-overwrite.sh
@@ -29,21 +29,25 @@
 	test -f a/b
 '
 
-test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' '
+test_expect_success 'create a commit where dir a/b changed to symlink' '
 
 	rm -rf a/b &&	# cleanup if previous test failed
 	git checkout -f -b symlink start &&
 	rm -rf a/b &&
-	ln -s foo a/b &&
 	git add -A &&
+	test_ln_s_add foo a/b &&
 	git commit -m "dir to symlink"
 '
 
-test_expect_success SYMLINKS 'checkout commit with dir must not remove untracked a/b' '
+test_expect_success 'checkout commit with dir must not remove untracked a/b' '
 
 	git rm --cached a/b &&
 	git commit -m "un-track the symlink" &&
-	test_must_fail git checkout start &&
+	test_must_fail git checkout start
+'
+
+test_expect_success SYMLINKS 'the symlink remained' '
+
 	test -h a/b
 '
 
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
new file mode 100755
index 0000000..dee55e4
--- /dev/null
+++ b/t/t2024-checkout-dwim.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='checkout <branch>
+
+Ensures that checkout on an unborn branch does what the user expects'
+
+. ./test-lib.sh
+
+# Is the current branch "refs/heads/$1"?
+test_branch () {
+	printf "%s\n" "refs/heads/$1" >expect.HEAD &&
+	git symbolic-ref HEAD >actual.HEAD &&
+	test_cmp expect.HEAD actual.HEAD
+}
+
+# Is branch "refs/heads/$1" set to pull from "$2/$3"?
+test_branch_upstream () {
+	printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
+	{
+		git config "branch.$1.remote" &&
+		git config "branch.$1.merge"
+	} >actual.upstream &&
+	test_cmp expect.upstream actual.upstream
+}
+
+test_expect_success 'setup' '
+	test_commit my_master &&
+	git init repo_a &&
+	(
+		cd repo_a &&
+		test_commit a_master &&
+		git checkout -b foo &&
+		test_commit a_foo &&
+		git checkout -b bar &&
+		test_commit a_bar
+	) &&
+	git init repo_b &&
+	(
+		cd repo_b &&
+		test_commit b_master &&
+		git checkout -b foo &&
+		test_commit b_foo &&
+		git checkout -b baz &&
+		test_commit b_baz
+	) &&
+	git remote add repo_a repo_a &&
+	git remote add repo_b repo_b &&
+	git config remote.repo_b.fetch \
+		"+refs/heads/*:refs/remotes/other_b/*" &&
+	git fetch --all
+'
+
+test_expect_success 'checkout of non-existing branch fails' '
+	git checkout -B master &&
+	test_might_fail git branch -D xyzzy &&
+
+	test_must_fail git checkout xyzzy &&
+	test_must_fail git rev-parse --verify refs/heads/xyzzy &&
+	test_branch master
+'
+
+test_expect_success 'checkout of branch from multiple remotes fails #1' '
+	git checkout -B master &&
+	test_might_fail git branch -D foo &&
+
+	test_must_fail git checkout foo &&
+	test_must_fail git rev-parse --verify refs/heads/foo &&
+	test_branch master
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #1' '
+	git checkout -B master &&
+	test_might_fail git branch -D bar &&
+
+	git checkout bar &&
+	test_branch bar &&
+	test_cmp_rev remotes/repo_a/bar HEAD &&
+	test_branch_upstream bar repo_a bar
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #2' '
+	git checkout -B master &&
+	test_might_fail git branch -D baz &&
+
+	git checkout baz &&
+	test_branch baz &&
+	test_cmp_rev remotes/other_b/baz HEAD &&
+	test_branch_upstream baz repo_b baz
+'
+
+test_expect_success '--no-guess suppresses branch auto-vivification' '
+	git checkout -B master &&
+	test_might_fail git branch -D bar &&
+
+	test_must_fail git checkout --no-guess bar &&
+	test_must_fail git rev-parse --verify refs/heads/bar &&
+	test_branch master
+'
+
+test_expect_success 'setup more remotes with unconventional refspecs' '
+	git checkout -B master &&
+	git init repo_c &&
+	(
+		cd repo_c &&
+		test_commit c_master &&
+		git checkout -b bar &&
+		test_commit c_bar
+		git checkout -b spam &&
+		test_commit c_spam
+	) &&
+	git init repo_d &&
+	(
+		cd repo_d &&
+		test_commit d_master &&
+		git checkout -b baz &&
+		test_commit f_baz
+		git checkout -b eggs &&
+		test_commit c_eggs
+	) &&
+	git remote add repo_c repo_c &&
+	git config remote.repo_c.fetch \
+		"+refs/heads/*:refs/remotes/extra_dir/repo_c/extra_dir/*" &&
+	git remote add repo_d repo_d &&
+	git config remote.repo_d.fetch \
+		"+refs/heads/*:refs/repo_d/*" &&
+	git fetch --all
+'
+
+test_expect_success 'checkout of branch from multiple remotes fails #2' '
+	git checkout -B master &&
+	test_might_fail git branch -D bar &&
+
+	test_must_fail git checkout bar &&
+	test_must_fail git rev-parse --verify refs/heads/bar &&
+	test_branch master
+'
+
+test_expect_success 'checkout of branch from multiple remotes fails #3' '
+	git checkout -B master &&
+	test_might_fail git branch -D baz &&
+
+	test_must_fail git checkout baz &&
+	test_must_fail git rev-parse --verify refs/heads/baz &&
+	test_branch master
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #3' '
+	git checkout -B master &&
+	test_might_fail git branch -D spam &&
+
+	git checkout spam &&
+	test_branch spam &&
+	test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
+	test_branch_upstream spam repo_c spam
+'
+
+test_expect_success 'checkout of branch from a single remote succeeds #4' '
+	git checkout -B master &&
+	test_might_fail git branch -D eggs &&
+
+	git checkout eggs &&
+	test_branch eggs &&
+	test_cmp_rev refs/repo_d/eggs HEAD &&
+	test_branch_upstream eggs repo_d eggs
+'
+
+test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index b2bd419..9bf2bdf 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -96,11 +96,10 @@
 	test_cmp expect actual
 '
 
-test_expect_success SYMLINKS 'replace a file with a symlink' '
+test_expect_success 'replace a file with a symlink' '
 
 	rm foo &&
-	ln -s top foo &&
-	git add -u -- foo
+	test_ln_s_add top foo
 
 '
 
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 6a81510..fc8b59e 100755
--- a/t/t2202-add-addremove.sh
+++ b/t/t2202-add-addremove.sh
@@ -41,4 +41,14 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'Just "git add" is a no-op' '
+	git reset --hard &&
+	echo >will-remove &&
+	>will-not-be-added &&
+	git add &&
+	git diff-index --name-status --cached HEAD >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 4e3735f..f0421c0 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -175,6 +175,24 @@
 	grep "^a.1" output
 '
 
+test_expect_success 'excluded directory overrides content patterns' '
+
+	git ls-files --others --exclude="one" --exclude="!one/a.1" >output &&
+	if grep "^one/a.1" output
+	then
+		false
+	fi
+'
+
+test_expect_success 'negated directory doesn'\''t affect content patterns' '
+
+	git ls-files --others --exclude="!one" --exclude="one/a.1" >output &&
+	if grep "^one/a.1" output
+	then
+		false
+	fi
+'
+
 test_expect_success 'subdirectory ignore (setup)' '
 	mkdir -p top/l1/l2 &&
 	(
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 95671c2..f611d79 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -11,6 +11,8 @@
     path1       - a symlink
     path2/file2 - a file in a directory
     path3/file3 - a file in a directory
+    submod1/	- a submodule
+    submod2/	- another submodule
 
 and the following on the filesystem:
 
@@ -21,9 +23,11 @@
     path4	- a file
     path5	- a symlink
     path6/file6 - a file in a directory
+    submod1/	- a submodule (modified from the cache)
+    submod2/	- a submodule (matches the cache)
 
-git ls-files -k should report that existing filesystem
-objects except path4, path5 and path6/file6 to be killed.
+git ls-files -k should report that existing filesystem objects
+path0/*, path1/*, path2 and path3 to be killed.
 
 Also for modification test, the cache and working tree have:
 
@@ -33,75 +37,82 @@
     path10	- a non-empty file, cache dirtied.
 
 We should report path0, path1, path2/file2, path3/file3, path7 and path8
-modified without reporting path9 and path10.
+modified without reporting path9 and path10.  submod1 is also modified.
 '
 . ./test-lib.sh
 
-date >path0
-if test_have_prereq SYMLINKS
-then
-	ln -s xyzzy path1
-else
-	date > path1
-fi
-mkdir path2 path3
-date >path2/file2
-date >path3/file3
-: >path7
-date >path8
-: >path9
-date >path10
-test_expect_success \
-    'git update-index --add to add various paths.' \
-    "git update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
+test_expect_success 'git update-index --add to add various paths.' '
+	date >path0 &&
+	test_ln_s_add xyzzy path1 &&
+	mkdir path2 path3 &&
+	date >path2/file2 &&
+	date >path3/file3 &&
+	: >path7 &&
+	date >path8 &&
+	: >path9 &&
+	date >path10 &&
+	git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+	for i in 1 2
+	do
+		git init submod$i &&
+		(
+			cd submod$i && git commit --allow-empty -m "empty $i"
+		) || break
+	done &&
+	git update-index --add submod[12]
+	(
+		cd submod1 &&
+		git commit --allow-empty -m "empty 1 (updated)"
+	) &&
+	rm -fr path?	# leave path10 alone
+'
 
-rm -fr path? ;# leave path10 alone
-date >path2
-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
-date >path6/file6
-date >path7
-: >path8
-: >path9
-touch path10
+test_expect_success 'git ls-files -k to show killed files.' '
+	date >path2 &&
+	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 &&
+	date >path6/file6 &&
+	date >path7 &&
+	: >path8 &&
+	: >path9 &&
+	touch path10 &&
+	git ls-files -k >.output
+'
 
-test_expect_success \
-    'git ls-files -k to show killed files.' \
-    'git ls-files -k >.output'
-cat >.expected <<EOF
-path0/file0
-path1/file1
-path2
-path3
-EOF
+test_expect_success 'validate git ls-files -k output.' '
+	cat >.expected <<-\EOF &&
+	path0/file0
+	path1/file1
+	path2
+	path3
+	EOF
+	test_cmp .expected .output
+'
 
-test_expect_success \
-    'validate git ls-files -k output.' \
-    'test_cmp .expected .output'
+test_expect_success 'git ls-files -m to show modified files.' '
+	git ls-files -m >.output
+'
 
-test_expect_success \
-    'git ls-files -m to show modified files.' \
-    'git ls-files -m >.output'
-cat >.expected <<EOF
-path0
-path1
-path2/file2
-path3/file3
-path7
-path8
-EOF
-
-test_expect_success \
-    'validate git ls-files -m output.' \
-    'test_cmp .expected .output'
+test_expect_success 'validate git ls-files -m output.' '
+	cat >.expected <<-\EOF &&
+	path0
+	path1
+	path2/file2
+	path3/file3
+	path7
+	path8
+	submod1
+	EOF
+	test_cmp .expected .output
+'
 
 test_done
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index a5e3da7..2f96100 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -25,10 +25,7 @@
 	git branch submod &&
 	git branch copy &&
 	git branch rename &&
-	if test_have_prereq SYMLINKS
-	then
-		git branch rename-ln
-	fi &&
+	git branch rename-ln &&
 
 	echo hello >>a &&
 	cp a d/e &&
@@ -260,16 +257,12 @@
 	git add e &&
 	test_tick &&
 	git commit -m "rename a->e" &&
-	if test_have_prereq SYMLINKS
-	then
-		git checkout rename-ln &&
-		git mv a e &&
-		ln -s e a &&
-		git add a e &&
-		test_tick &&
-		git commit -m "rename a->e, symlink a->e" &&
-		oln=`printf e | git hash-object --stdin`
-	fi
+	git checkout rename-ln &&
+	git mv a e &&
+	test_ln_s_add e a &&
+	test_tick &&
+	git commit -m "rename a->e, symlink a->e" &&
+	oln=`printf e | git hash-object --stdin`
 '
 
 test_expect_success 'setup 9' '
@@ -569,28 +562,25 @@
 	test_cmp expected actual
 '
 
-if test_have_prereq SYMLINKS
-then
-	test_expect_failure 'merge-recursive rename vs. rename/symlink' '
+test_expect_failure 'merge-recursive rename vs. rename/symlink' '
 
-		git checkout -f rename &&
-		git merge rename-ln &&
-		( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
-		(
-			echo "120000 blob $oln	a"
-			echo "100644 blob $o0	b"
-			echo "100644 blob $o0	c"
-			echo "100644 blob $o0	d/e"
-			echo "100644 blob $o0	e"
-			echo "120000 $oln 0	a"
-			echo "100644 $o0 0	b"
-			echo "100644 $o0 0	c"
-			echo "100644 $o0 0	d/e"
-			echo "100644 $o0 0	e"
-		) >expected &&
-		test_cmp expected actual
-	'
-fi
+	git checkout -f rename &&
+	git merge rename-ln &&
+	( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+	(
+		echo "120000 blob $oln	a"
+		echo "100644 blob $o0	b"
+		echo "100644 blob $o0	c"
+		echo "100644 blob $o0	d/e"
+		echo "100644 blob $o0	e"
+		echo "120000 $oln 0	a"
+		echo "100644 $o0 0	b"
+		echo "100644 $o0 0	c"
+		echo "100644 $o0 0	d/e"
+		echo "100644 $o0 0	e"
+	) >expected &&
+	test_cmp expected actual
+'
 
 
 test_done
diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh
index 2b17311..5fd7bbb 100755
--- a/t/t3032-merge-recursive-options.sh
+++ b/t/t3032-merge-recursive-options.sh
@@ -14,7 +14,7 @@
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
-test_have_prereq MINGW && export GREP_OPTIONS=-U
+test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U
 
 test_expect_success 'setup' '
 	conflict_hunks () {
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 4c37057..38446a0 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -6,20 +6,20 @@
 
 match() {
     if [ $1 = 1 ]; then
-	test_expect_success "wildmatch:    match '$3' '$4'" "
+	test_expect_success "wildmatch:     match '$3' '$4'" "
 	    test-wildmatch wildmatch '$3' '$4'
 	"
     else
-	test_expect_success "wildmatch: no match '$3' '$4'" "
+	test_expect_success "wildmatch:  no match '$3' '$4'" "
 	    ! test-wildmatch wildmatch '$3' '$4'
 	"
     fi
     if [ $2 = 1 ]; then
-	test_expect_success "fnmatch:      match '$3' '$4'" "
+	test_expect_success "fnmatch:       match '$3' '$4'" "
 	    test-wildmatch fnmatch '$3' '$4'
 	"
     elif [ $2 = 0 ]; then
-	test_expect_success "fnmatch:   no match '$3' '$4'" "
+	test_expect_success "fnmatch:    no match '$3' '$4'" "
 	    ! test-wildmatch fnmatch '$3' '$4'
 	"
 #    else
@@ -29,13 +29,25 @@
     fi
 }
 
+imatch() {
+    if [ $1 = 1 ]; then
+	test_expect_success "iwildmatch:    match '$2' '$3'" "
+	    test-wildmatch iwildmatch '$2' '$3'
+	"
+    else
+	test_expect_success "iwildmatch: no match '$2' '$3'" "
+	    ! test-wildmatch iwildmatch '$2' '$3'
+	"
+    fi
+}
+
 pathmatch() {
     if [ $1 = 1 ]; then
-	test_expect_success "pathmatch:    match '$2' '$3'" "
+	test_expect_success "pathmatch:     match '$2' '$3'" "
 	    test-wildmatch pathmatch '$2' '$3'
 	"
     else
-	test_expect_success "pathmatch: no match '$2' '$3'" "
+	test_expect_success "pathmatch:  no match '$2' '$3'" "
 	    ! test-wildmatch pathmatch '$2' '$3'
 	"
     fi
@@ -235,4 +247,35 @@
 pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i'
 pathmatch 1 ab/cXd/efXg/hi '*Xg*i'
 
+# Case-sensitivy features
+match 0 x 'a' '[A-Z]'
+match 1 x 'A' '[A-Z]'
+match 0 x 'A' '[a-z]'
+match 1 x 'a' '[a-z]'
+match 0 x 'a' '[[:upper:]]'
+match 1 x 'A' '[[:upper:]]'
+match 0 x 'A' '[[:lower:]]'
+match 1 x 'a' '[[:lower:]]'
+match 0 x 'A' '[B-Za]'
+match 1 x 'a' '[B-Za]'
+match 0 x 'A' '[B-a]'
+match 1 x 'a' '[B-a]'
+match 0 x 'z' '[Z-y]'
+match 1 x 'Z' '[Z-y]'
+
+imatch 1 'a' '[A-Z]'
+imatch 1 'A' '[A-Z]'
+imatch 1 'A' '[a-z]'
+imatch 1 'a' '[a-z]'
+imatch 1 'a' '[[:upper:]]'
+imatch 1 'A' '[[:upper:]]'
+imatch 1 'A' '[[:lower:]]'
+imatch 1 'a' '[[:lower:]]'
+imatch 1 'A' '[B-Za]'
+imatch 1 'a' '[B-Za]'
+imatch 1 'A' '[B-a]'
+imatch 1 'a' '[B-a]'
+imatch 1 'z' '[Z-y]'
+imatch 1 'Z' '[Z-y]'
+
 test_done
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index 81d90b6..eb73c06 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -22,20 +22,8 @@
     'setup' \
     'mkdir path2 path2/baz &&
      echo Hi >path0 &&
-     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 &&
+     test_ln_s_add path0 path1 &&
+     test_ln_s_add ../path1 path2/bazbo &&
      echo Lo >path2/foo &&
      echo Mi >path2/baz/b &&
      find path? \( -type f -o -type l \) -print |
@@ -51,7 +39,7 @@
 test_expect_success \
     'ls-tree plain' \
     'git ls-tree $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 040000 tree X	path2
@@ -61,7 +49,7 @@
 test_expect_success \
     'ls-tree recursive' \
     'git ls-tree -r $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 100644 blob X	path2/baz/b
@@ -73,7 +61,7 @@
 test_expect_success \
     'ls-tree recursive with -t' \
     'git ls-tree -r -t $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 040000 tree X	path2
@@ -87,7 +75,7 @@
 test_expect_success \
     'ls-tree recursive with -d' \
     'git ls-tree -r -d $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X	path2
 040000 tree X	path2/baz
 EOF
@@ -96,7 +84,7 @@
 test_expect_success \
     'ls-tree filtered with path' \
     'git ls-tree $tree path >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 EOF
      test_output'
 
@@ -106,7 +94,7 @@
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git ls-tree $tree path1 path0 >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X	path0
 120000 blob X	path1
 EOF
@@ -115,7 +103,7 @@
 test_expect_success \
     'ls-tree filtered with path0/' \
     'git ls-tree $tree path0/ >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 EOF
      test_output'
 
@@ -124,7 +112,7 @@
 test_expect_success \
     'ls-tree filtered with path2' \
     'git ls-tree $tree path2 >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X	path2
 EOF
      test_output'
@@ -133,7 +121,7 @@
 test_expect_success \
     'ls-tree filtered with path2/' \
     'git ls-tree $tree path2/ >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X	path2/baz
 120000 blob X	path2/bazbo
 100644 blob X	path2/foo
@@ -145,7 +133,7 @@
 test_expect_success \
     'ls-tree filtered with path2/baz' \
     'git ls-tree $tree path2/baz >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X	path2/baz
 EOF
      test_output'
@@ -153,14 +141,14 @@
 test_expect_success \
     'ls-tree filtered with path2/bak' \
     'git ls-tree $tree path2/bak >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 EOF
      test_output'
 
 test_expect_success \
     'ls-tree -t filtered with path2/bak' \
     'git ls-tree -t $tree path2/bak >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X	path2
 EOF
      test_output'
@@ -168,7 +156,7 @@
 test_expect_success \
     'ls-tree with one path a prefix of the other' \
     'git ls-tree $tree path2/baz path2/bazbo >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X	path2/baz
 120000 blob X	path2/bazbo
 EOF
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index d969f0e..44ec6a4 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -317,13 +317,13 @@
 	test $(git config branch.my4.merge) = refs/heads/master
 '
 
-test_expect_success 'test tracking setup (non-wildcard, not matching)' '
+test_expect_success 'tracking setup fails on non-matching refspec' '
 	git config remote.local.url . &&
 	git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
 	(git show-ref -q refs/remotes/local/master || git fetch local) &&
-	git branch --track my5 local/master &&
-	! test "$(git config branch.my5.remote)" = local &&
-	! test "$(git config branch.my5.merge)" = refs/heads/master
+	test_must_fail git branch --track my5 local/master &&
+	test_must_fail git config branch.my5.remote &&
+	test_must_fail git config branch.my5.merge
 '
 
 test_expect_success 'test tracking setup via config' '
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index cd04361..1a2080e 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -118,4 +118,37 @@
 	test_cmp all-of-them again
 '
 
+test_expect_success 'explicit pack-refs with dangling packed reference' '
+	git commit --allow-empty -m "soon to be garbage-collected" &&
+	git pack-refs --all &&
+	git reset --hard HEAD^ &&
+	git reflog expire --expire=all --all &&
+	git prune --expire=all &&
+	git pack-refs --all 2>result &&
+	test_cmp /dev/null result
+'
+
+test_expect_success 'delete ref with dangling packed version' '
+	git checkout -b lamb &&
+	git commit --allow-empty -m "future garbage" &&
+	git pack-refs --all &&
+	git reset --hard HEAD^ &&
+	git checkout master &&
+	git reflog expire --expire=all --all &&
+	git prune --expire=all &&
+	git branch -d lamb 2>result &&
+	test_cmp /dev/null result
+'
+
+test_expect_success 'delete ref while another dangling packed ref' '
+	git branch lamb &&
+	git commit --allow-empty -m "future garbage" &&
+	git pack-refs --all &&
+	git reset --hard HEAD^ &&
+	git reflog expire --expire=all --all &&
+	git prune --expire=all &&
+	git branch -d lamb 2>result &&
+	test_cmp /dev/null result
+'
+
 test_done
diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh
index d4d7792..3b7caca 100755
--- a/t/t3211-peel-ref.sh
+++ b/t/t3211-peel-ref.sh
@@ -61,4 +61,13 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'peeled refs survive deletion of packed ref' '
+	git pack-refs --all &&
+	cp .git/packed-refs fully-peeled &&
+	git branch yadda &&
+	git pack-refs --all &&
+	git branch -d yadda &&
+	test_cmp fully-peeled .git/packed-refs
+'
+
 test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index b58fa1a..ebf93b0 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -40,13 +40,6 @@
 	echo Side >>C &&
 	git add C &&
 	git commit -m "Add C" &&
-	git checkout -b nonlinear my-topic-branch &&
-	echo Edit >>B &&
-	git add B &&
-	git commit -m "Modify B" &&
-	git merge side &&
-	git checkout -b upstream-merged-nonlinear &&
-	git merge master &&
 	git checkout -f my-topic-branch &&
 	git tag topic
 '
@@ -66,26 +59,15 @@
 	git rebase master
 '
 
-test_expect_success 'rebase against master twice' '
-	git rebase master >out &&
-	test_i18ngrep "Current branch my-topic-branch is up to date" out
-'
-
-test_expect_success 'rebase against master twice with --force' '
-	git rebase --force-rebase master >out &&
-	test_i18ngrep "Current branch my-topic-branch is up to date, rebase forced" out
-'
-
-test_expect_success 'rebase against master twice from another branch' '
-	git checkout my-topic-branch^ &&
-	git rebase master my-topic-branch >out &&
-	test_i18ngrep "Current branch my-topic-branch is up to date" out
-'
-
-test_expect_success 'rebase fast-forward to master' '
-	git checkout my-topic-branch^ &&
-	git rebase my-topic-branch >out &&
-	test_i18ngrep "Fast-forwarded HEAD to my-topic-branch" out
+test_expect_success 'rebase, with <onto> and <upstream> specified as :/quuxery' '
+	test_when_finished "git branch -D torebase" &&
+	git checkout -b torebase my-topic-branch^ &&
+	upstream=$(git rev-parse ":/Add B") &&
+	onto=$(git rev-parse ":/Add A") &&
+	git rebase --onto $onto $upstream &&
+	git reset --hard my-topic-branch^ &&
+	git rebase --onto ":/Add A" ":/Add B" &&
+	git checkout my-topic-branch
 '
 
 test_expect_success 'the rebase operation should not have destroyed author information' '
@@ -106,31 +88,9 @@
 	git rebase master
 '
 
-test_expect_success 'rebase after merge master' '
-	git checkout --detach refs/tags/topic &&
-	git branch -D topic &&
-	git reset --hard topic &&
-	git merge master &&
-	git rebase master &&
-	! (git show | grep "^Merge:")
-'
-
-test_expect_success 'rebase of history with merges is linearized' '
-	git checkout nonlinear &&
-	test 4 = $(git rev-list master.. | wc -l) &&
-	git rebase master &&
-	test 3 = $(git rev-list master.. | wc -l)
-'
-
-test_expect_success 'rebase of history with merges after upstream merge is linearized' '
-	git checkout upstream-merged-nonlinear &&
-	test 5 = $(git rev-list master.. | wc -l) &&
-	git rebase master &&
-	test 3 = $(git rev-list master.. | wc -l)
-'
-
 test_expect_success 'rebase a single mode change' '
 	git checkout master &&
+	git branch -D topic &&
 	echo 1 >X &&
 	git add X &&
 	test_tick &&
@@ -185,7 +145,7 @@
 test_expect_success 'rebase -q is quiet' '
 	git checkout -b quiet topic &&
 	git rebase -q master >output.out 2>&1 &&
-	test ! -s output.out
+	test_must_be_empty output.out
 '
 
 test_expect_success 'Rebase a commit that sprinkles CRs in' '
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
deleted file mode 100755
index 58f4823..0000000
--- a/t/t3401-rebase-partial.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
-#
-
-test_description='git rebase should detect patches integrated upstream
-
-This test cherry-picks one local change of two into master branch, and
-checks that git rebase succeeds with only the second patch in the
-local branch.
-'
-. ./test-lib.sh
-
-test_expect_success 'prepare repository with topic branch' '
-	test_commit A &&
-	git checkout -b my-topic-branch &&
-	test_commit B &&
-	test_commit C &&
-	git checkout -f master &&
-	test_commit A2 A.t
-'
-
-test_expect_success 'pick top patch from topic branch into master' '
-	git cherry-pick C &&
-	git checkout -f my-topic-branch
-'
-
-test_debug '
-	git cherry master &&
-	git format-patch -k --stdout --full-index master >/dev/null &&
-	gitk --all & sleep 1
-'
-
-test_expect_success 'rebase topic branch against new master and check git am did not get halted' '
-	git rebase master &&
-	test_path_is_missing .git/rebase-apply
-'
-
-test_expect_success 'rebase --merge topic branch that was partially merged upstream' '
-	git reset --hard C &&
-	git rebase --merge master &&
-	test_path_is_missing .git/rebase-merge
-'
-
-test_expect_success 'rebase ignores empty commit' '
-	git reset --hard A &&
-	git commit --allow-empty -m empty &&
-	test_commit D &&
-	git rebase C &&
-	test "$(git log --format=%s C..)" = "D"
-'
-
-test_expect_success 'rebase --keep-empty' '
-	git reset --hard D &&
-	git rebase --keep-empty C &&
-	test "$(git log --format=%s C..)" = "D
-empty"
-'
-
-test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' '
-	git reset --hard A &&
-	git commit --allow-empty -m also-empty &&
-	git rebase --keep-empty D &&
-	test "$(git log --format=%s A..)" = "also-empty
-D
-empty"
-'
-
-test_done
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 826500b..3968020 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -64,10 +64,11 @@
 
 test_expect_success 'rebase --skip with --merge' '
 	git rebase --skip
-	'
+'
 
-test_expect_success 'merge and reference trees equal' \
-	'test -z "`git diff-tree skip-merge skip-reference`"'
+test_expect_success 'merge and reference trees equal' '
+	test -z "`git diff-tree skip-merge skip-reference`"
+'
 
 test_expect_success 'moved back to branch correctly' '
 	test refs/heads/skip-merge = $(git symbolic-ref HEAD)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index a58406d..49ccb38 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -477,19 +477,11 @@
 	test $one = $(git rev-parse HEAD~2)
 '
 
-test_expect_success 'ignore patch if in upstream' '
-	HEAD=$(git rev-parse HEAD) &&
-	git checkout -b has-cherry-picked HEAD^ &&
+test_expect_success '--continue tries to commit, even for "edit"' '
 	echo unrelated > file7 &&
 	git add file7 &&
 	test_tick &&
 	git commit -m "unrelated change" &&
-	git cherry-pick $HEAD &&
-	EXPECT_COUNT=1 git rebase -i $HEAD &&
-	test $HEAD = $(git rev-parse HEAD^)
-'
-
-test_expect_success '--continue tries to commit, even for "edit"' '
 	parent=$(git rev-parse HEAD^) &&
 	test_tick &&
 	FAKE_LINES="edit 1" git rebase -i HEAD^ &&
@@ -692,7 +684,7 @@
 	test_commit n2 &&
 	test_commit n3 &&
 	git notes add -m"a note" n3 &&
-	git rebase --onto n1 n2 &&
+	git rebase -i --onto n1 n2 &&
 	test "a note" = "$(git notes show HEAD)"
 '
 
@@ -934,6 +926,21 @@
 	test L = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
+test_expect_success 'rebase -i produces readable reflog' '
+	git reset --hard &&
+	git branch -f branch-reflog-test H &&
+	git rebase -i --onto I F branch-reflog-test &&
+	cat >expect <<-\EOF &&
+	rebase -i (start): checkout I
+	rebase -i (pick): G
+	rebase -i (pick): H
+	rebase -i (finish): returning to refs/heads/branch-reflog-test
+	EOF
+	tail -n 4 .git/logs/HEAD |
+	sed -e "s/.*	//" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase -i respects core.commentchar' '
 	git reset --hard &&
 	git checkout E^0 &&
@@ -947,4 +954,26 @@
 	test B = $(git cat-file commit HEAD^ | sed -ne \$p)
 '
 
+test_expect_success 'rebase -i, with <onto> and <upstream> specified as :/quuxery' '
+	test_when_finished "git branch -D torebase" &&
+	git checkout -b torebase branch1 &&
+	upstream=$(git rev-parse ":/J") &&
+	onto=$(git rev-parse ":/A") &&
+	git rebase --onto $onto $upstream &&
+	git reset --hard branch1 &&
+	git rebase --onto ":/A" ":/J" &&
+	git checkout branch1
+'
+
+test_expect_success 'rebase -i with --strategy and -X' '
+	git checkout -b conflict-merge-use-theirs conflict-branch &&
+	git reset --hard HEAD^ &&
+	echo five >conflict &&
+	echo Z >file1 &&
+	git commit -a -m "one file conflict" &&
+	EDITOR=true git rebase -i --strategy=recursive -Xours conflict-branch &&
+	test $(git show conflict-branch:conflict) = $(cat conflict) &&
+	test $(cat file1) = Z
+'
+
 test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index e6a9a0d..0392e36 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -4,27 +4,17 @@
 
 . ./test-lib.sh
 
-quick_one () {
-	echo "$1" >"file$1" &&
-	git add "file$1" &&
-	test_tick &&
-	git commit -m "$1"
-}
+test_expect_success 'setup' '
+	test_commit O fileO &&
+	test_commit X fileX &&
+	test_commit A fileA &&
+	test_commit B fileB &&
+	test_commit Y fileY &&
 
-test_expect_success setup '
-	quick_one O &&
-	git branch topic &&
-	quick_one X &&
-	quick_one A &&
-	quick_one B &&
-	quick_one Y &&
-
-	git checkout topic &&
-	quick_one A &&
-	quick_one B &&
-	quick_one Z &&
+	git checkout -b topic O &&
+	git cherry-pick A B &&
+	test_commit Z fileZ &&
 	git tag start
-
 '
 
 cat >expect <<\EOF
@@ -34,12 +24,32 @@
 EOF
 
 test_expect_success 'rebase -m' '
-
 	git rebase -m master >report &&
 	sed -n -e "/^Already applied: /p" \
 		-e "/^Committed: /p" report >actual &&
 	test_cmp expect actual
+'
 
+test_expect_success 'rebase against master twice' '
+	git rebase master >out &&
+	test_i18ngrep "Current branch topic is up to date" out
+'
+
+test_expect_success 'rebase against master twice with --force' '
+	git rebase --force-rebase master >out &&
+	test_i18ngrep "Current branch topic is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+	git checkout topic^ &&
+	git rebase master topic >out &&
+	test_i18ngrep "Current branch topic is up to date" out
+'
+
+test_expect_success 'rebase fast-forward to master' '
+	git checkout topic^ &&
+	git rebase topic >out &&
+	test_i18ngrep "Fast-forwarded HEAD to topic" out
 '
 
 test_expect_success 'rebase --stat' '
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index 6de4e22..2e0c364 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -11,14 +11,6 @@
 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
@@ -36,16 +28,6 @@
 #     \--A3    <-- topic2
 #      \
 #       B2     <-- origin/topic
-#
-# Clone 4 (merge using second parent as base):
-#
-# A1--A2--B3   <-- origin/master
-#  \
-#   B1--A3--M  <-- topic
-#    \     /
-#     \--A4    <-- topic2
-#      \
-#       B2     <-- origin/topic
 
 test_expect_success 'setup for merge-preserving rebase' \
 	'echo First > A &&
@@ -58,20 +40,6 @@
 	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
-	) &&
-
-	git clone ./. clone4 &&
-	(
-		cd clone4 &&
-		git checkout -b topic origin/topic &&
-		git merge origin/master
-	) &&
-
 	echo Fifth > B &&
 	git add B &&
 	git commit -m "Add different B" &&
@@ -101,16 +69,6 @@
 	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 remote-tracking branch " | wc -l)
-	)
-'
-
 test_expect_success '--continue works after a conflict' '
 	(
 	cd clone2 &&
@@ -138,15 +96,4 @@
 	)
 '
 
-test_expect_success 'rebase -p works when base inside second parent' '
-	(
-	cd clone4 &&
-	git fetch &&
-	git rebase -p HEAD^2 &&
-	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-	test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) &&
-	test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
-	)
-'
-
 test_done
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index a1e86c4..41370ab 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -4,6 +4,8 @@
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success setup '
 	echo 0 >file0 &&
 	git add . &&
@@ -193,4 +195,59 @@
 	test_auto_commit_flags squash 2
 '
 
+test_auto_fixup_fixup () {
+	git reset --hard base &&
+	echo 1 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "$1! first" &&
+	echo 2 >file1 &&
+	git add -u &&
+	test_tick &&
+	git commit -m "$1! $2! first" &&
+	git tag "final-$1-$2" &&
+	test_tick &&
+	(
+		set_cat_todo_editor &&
+		test_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&
+		cat >expected <<-EOF &&
+		pick $(git rev-parse --short HEAD^^^) first commit
+		$1 $(git rev-parse --short HEAD^) $1! first
+		$1 $(git rev-parse --short HEAD) $1! $2! first
+		pick $(git rev-parse --short HEAD^^) second commit
+		EOF
+		test_cmp expected actual
+	) &&
+	git rebase --autosquash -i HEAD^^^^ &&
+	git log --oneline >actual &&
+	test_line_count = 3 actual
+	git diff --exit-code "final-$1-$2" &&
+	test 2 = "$(git cat-file blob HEAD^:file1)" &&
+	if test "$1" = "fixup"
+	then
+		test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+	elif test "$1" = "squash"
+	then
+		test 3 = $(git cat-file commit HEAD^ | grep first | wc -l)
+	else
+		false
+	fi
+}
+
+test_expect_success 'fixup! fixup!' '
+	test_auto_fixup_fixup fixup fixup
+'
+
+test_expect_success 'fixup! squash!' '
+	test_auto_fixup_fixup fixup squash
+'
+
+test_expect_success 'squash! squash!' '
+	test_auto_fixup_fixup squash squash
+'
+
+test_expect_success 'squash! fixup!' '
+	test_auto_fixup_fixup squash fixup
+'
+
 test_done
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
new file mode 100755
index 0000000..90eb264
--- /dev/null
+++ b/t/t3420-rebase-autostash.sh
@@ -0,0 +1,170 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Ramkumar Ramachandra
+#
+
+test_description='git rebase --autostash tests'
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo hello-world >file0 &&
+	git add . &&
+	test_tick &&
+	git commit -m "initial commit" &&
+	git checkout -b feature-branch &&
+	echo another-hello >file1 &&
+	echo goodbye >file2 &&
+	git add . &&
+	test_tick &&
+	git commit -m "second commit" &&
+	echo final-goodbye >file3 &&
+	git add . &&
+	test_tick &&
+	git commit -m "third commit" &&
+	git checkout -b unrelated-onto-branch master &&
+	echo unrelated >file4 &&
+	git add . &&
+	test_tick &&
+	git commit -m "unrelated commit" &&
+	git checkout -b related-onto-branch master &&
+	echo conflicting-change >file2 &&
+	git add . &&
+	test_tick &&
+	git commit -m "related commit"
+'
+
+testrebase() {
+	type=$1
+	dotest=$2
+
+	test_expect_success "rebase$type: dirty worktree, non-conflicting rebase" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >>file3 &&
+		git rebase$type unrelated-onto-branch &&
+		grep unrelated file4 &&
+		grep dirty file3 &&
+		git checkout feature-branch
+	'
+
+	test_expect_success "rebase$type: dirty index, non-conflicting rebase" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >>file3 &&
+		git add file3 &&
+		git rebase$type unrelated-onto-branch &&
+		grep unrelated file4 &&
+		grep dirty file3 &&
+		git checkout feature-branch
+	'
+
+	test_expect_success "rebase$type: conflicting rebase" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >>file3 &&
+		test_must_fail git rebase$type related-onto-branch &&
+		test_path_is_file $dotest/autostash &&
+		! grep dirty file3 &&
+		rm -rf $dotest &&
+		git reset --hard &&
+		git checkout feature-branch
+	'
+
+	test_expect_success "rebase$type: --continue" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >>file3 &&
+		test_must_fail git rebase$type related-onto-branch &&
+		test_path_is_file $dotest/autostash &&
+		! grep dirty file3 &&
+		echo "conflicting-plus-goodbye" >file2 &&
+		git add file2 &&
+		git rebase --continue &&
+		test_path_is_missing $dotest/autostash &&
+		grep dirty file3 &&
+		git checkout feature-branch
+	'
+
+	test_expect_success "rebase$type: --skip" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >>file3 &&
+		test_must_fail git rebase$type related-onto-branch &&
+		test_path_is_file $dotest/autostash &&
+		! grep dirty file3 &&
+		git rebase --skip &&
+		test_path_is_missing $dotest/autostash &&
+		grep dirty file3 &&
+		git checkout feature-branch
+	'
+
+	test_expect_success "rebase$type: --abort" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >>file3 &&
+		test_must_fail git rebase$type related-onto-branch &&
+		test_path_is_file $dotest/autostash &&
+		! grep dirty file3 &&
+		git rebase --abort &&
+		test_path_is_missing $dotest/autostash &&
+		grep dirty file3 &&
+		git checkout feature-branch
+	'
+
+	test_expect_success "rebase$type: non-conflicting rebase, conflicting stash" '
+		test_config rebase.autostash true &&
+		git reset --hard &&
+		git checkout -b rebased-feature-branch feature-branch &&
+		test_when_finished git branch -D rebased-feature-branch &&
+		echo dirty >file4 &&
+		git add file4 &&
+		git rebase$type unrelated-onto-branch &&
+		test_path_is_missing $dotest &&
+		git reset --hard &&
+		grep unrelated file4 &&
+		! grep dirty file4 &&
+		git checkout feature-branch &&
+		git stash pop &&
+		grep dirty file4
+	'
+}
+
+test_expect_success "rebase: fast-forward rebase" '
+	test_config rebase.autostash true &&
+	git reset --hard &&
+	git checkout -b behind-feature-branch feature-branch~1 &&
+	test_when_finished git branch -D behind-feature-branch &&
+	echo dirty >>file1 &&
+	git rebase feature-branch &&
+	grep dirty file1 &&
+	git checkout feature-branch
+'
+
+test_expect_success "rebase: noop rebase" '
+	test_config rebase.autostash true &&
+	git reset --hard &&
+	git checkout -b same-feature-branch feature-branch &&
+	test_when_finished git branch -D same-feature-branch &&
+	echo dirty >>file1 &&
+	git rebase feature-branch &&
+	grep dirty file1 &&
+	git checkout feature-branch
+'
+
+testrebase "" .git/rebase-apply
+testrebase " --merge" .git/rebase-merge
+testrebase " --interactive" .git/rebase-merge
+
+test_done
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
new file mode 100755
index 0000000..9c55cba
--- /dev/null
+++ b/t/t3421-rebase-topology-linear.sh
@@ -0,0 +1,350 @@
+#!/bin/sh
+
+test_description='basic rebase topology tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# a---b---c
+#      \
+#       d---e
+test_expect_success 'setup' '
+	test_commit a &&
+	test_commit b &&
+	test_commit c &&
+	git checkout b &&
+	test_commit d &&
+	test_commit e
+'
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "simple rebase $*" "
+		reset_rebase &&
+		git rebase $* c e &&
+		test_cmp_rev c HEAD~2 &&
+		test_linear_range 'd e' c..
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* is no-op if upstream is an ancestor" "
+		reset_rebase &&
+		git rebase $* b e &&
+		test_cmp_rev e HEAD
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* -f rewrites even if upstream is an ancestor" "
+		reset_rebase &&
+		git rebase $* -f b e &&
+		! test_cmp_rev e HEAD &&
+		test_cmp_rev b HEAD~2 &&
+		test_linear_range 'd e' b..
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* fast-forwards from ancestor of upstream" "
+		reset_rebase &&
+		git rebase $* e b &&
+		test_cmp_rev e HEAD
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+#       f
+#      /
+# a---b---c---g---h
+#      \
+#       d---gp--i
+#
+# gp = cherry-picked g
+# h = reverted g
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for range selection tests' '
+	git checkout c &&
+	test_commit g &&
+	revert h g &&
+	git checkout d &&
+	cherry_pick gp g &&
+	test_commit i &&
+	git checkout b &&
+	test_commit f
+'
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* drops patches in upstream" "
+		reset_rebase &&
+		git rebase $* h i &&
+		test_cmp_rev h HEAD~2 &&
+		test_linear_range 'd i' h..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* can drop last patch if in upstream" "
+		reset_rebase &&
+		git rebase $* h gp &&
+		test_cmp_rev h HEAD^ &&
+		test_linear_range 'd' h..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --onto drops patches in upstream" "
+		reset_rebase &&
+		git rebase $* --onto f h i &&
+		test_cmp_rev f HEAD~2 &&
+		test_linear_range 'd i' f..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --onto does not drop patches in onto" "
+		reset_rebase &&
+		git rebase $* --onto h f i &&
+		test_cmp_rev h HEAD~3 &&
+		test_linear_range 'd gp i' h..
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+# a---b---c---j!
+#      \
+#       d---k!--l
+#
+# ! = empty
+test_expect_success 'setup of linear history for empty commit tests' '
+	git checkout c &&
+	make_empty j &&
+	git checkout d &&
+	make_empty k &&
+	test_commit l
+'
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* drops empty commit" "
+		reset_rebase &&
+		git rebase $* c l &&
+		test_cmp_rev c HEAD~2 &&
+		test_linear_range 'd l' c..
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --keep-empty" "
+		reset_rebase &&
+		git rebase $* --keep-empty c l &&
+		test_cmp_rev c HEAD~3 &&
+		test_linear_range 'd k l' c..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --keep-empty keeps empty even if already in upstream" "
+		reset_rebase &&
+		git rebase $* --keep-empty j l &&
+		test_cmp_rev j HEAD~3 &&
+		test_linear_range 'd k l' j..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+#       m
+#      /
+# a---b---c---g
+#
+# x---y---bp
+#
+# bp = cherry-picked b
+# m = reverted b
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for test involving root' '
+	git checkout b &&
+	revert m b &&
+	git checkout --orphan disjoint &&
+	git rm -rf . &&
+	test_commit x &&
+	test_commit y &&
+	cherry_pick bp b
+'
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --onto --root" "
+		reset_rebase &&
+		git rebase $* --onto c --root y &&
+		test_cmp_rev c HEAD~2 &&
+		test_linear_range 'x y' c..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* without --onto --root with disjoint history" "
+		reset_rebase &&
+		git rebase $* c y &&
+		test_cmp_rev c HEAD~2 &&
+		test_linear_range 'x y' c..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --onto --root drops patch in onto" "
+		reset_rebase &&
+		git rebase $* --onto m --root bp &&
+		test_cmp_rev m HEAD~2 &&
+		test_linear_range 'x y' m..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --onto --root with merge-base does not go to root" "
+		reset_rebase &&
+		git rebase $* --onto m --root g &&
+		test_cmp_rev m HEAD~2 &&
+		test_linear_range 'c g' m..
+	"
+}
+
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* without --onto --root with disjoint history drops patch in onto" "
+		reset_rebase &&
+		git rebase $* m bp &&
+		test_cmp_rev m HEAD~2 &&
+		test_linear_range 'x y' m..
+	"
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* --root on linear history is a no-op" "
+		reset_rebase &&
+		git rebase $* --root c &&
+		test_cmp_rev c HEAD
+	"
+}
+test_run_rebase failure ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* -f --root on linear history causes re-write" "
+		reset_rebase &&
+		git rebase $* -f --root c &&
+		! test_cmp_rev a HEAD~2 &&
+		test_linear_range 'a b c' HEAD
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_done
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
new file mode 100755
index 0000000..1d195fb
--- /dev/null
+++ b/t/t3425-rebase-topology-merges.sh
@@ -0,0 +1,258 @@
+#!/bin/sh
+
+test_description='rebase topology tests with merges'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_revision_subjects () {
+	expected="$1"
+	shift
+	set -- $(git log --format=%s --no-walk=unsorted "$@")
+	test "$expected" = "$*"
+}
+
+# a---b-----------c
+#      \           \
+#       d-------e   \
+#        \       \   \
+#         n---o---w---v
+#              \
+#               z
+test_expect_success 'setup of non-linear-history' '
+	test_commit a &&
+	test_commit b &&
+	test_commit c &&
+	git checkout b &&
+	test_commit d &&
+	test_commit e
+
+	git checkout c &&
+	test_commit g &&
+	revert h g &&
+	git checkout d &&
+	cherry_pick gp g &&
+	test_commit i &&
+	git checkout b &&
+	test_commit f
+
+	git checkout d &&
+	test_commit n &&
+	test_commit o &&
+	test_merge w e &&
+	test_merge v c &&
+	git checkout o &&
+	test_commit z
+'
+
+test_run_rebase () {
+	result=$1
+	shift
+	test_expect_$result "rebase $* after merge from upstream" "
+		reset_rebase &&
+		git rebase $* e w &&
+		test_cmp_rev e HEAD~2 &&
+		test_linear_range 'n o' e..
+	"
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+
+test_run_rebase () {
+	result=$1
+	shift
+	expected=$1
+	shift
+	test_expect_$result "rebase $* of non-linear history is linearized in place" "
+		reset_rebase &&
+		git rebase $* d w &&
+		test_cmp_rev d HEAD~3 &&
+		test_linear_range "\'"$expected"\'" d..
+	"
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'e n o' ''
+test_run_rebase success 'e n o' -m
+test_run_rebase success 'n o e' -i
+
+test_run_rebase () {
+	result=$1
+	shift
+	expected=$1
+	shift
+	test_expect_$result "rebase $* of non-linear history is linearized upstream" "
+		reset_rebase &&
+		git rebase $* c w &&
+		test_cmp_rev c HEAD~4 &&
+		test_linear_range "\'"$expected"\'" c..
+	"
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_run_rebase () {
+	result=$1
+	shift
+	expected=$1
+	shift
+	test_expect_$result "rebase $* of non-linear history with merges after upstream merge is linearized" "
+		reset_rebase &&
+		git rebase $* c v &&
+		test_cmp_rev c HEAD~4 &&
+		test_linear_range "\'"$expected"\'" c..
+	"
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_expect_success "rebase -p is no-op in non-linear history" "
+	reset_rebase &&
+	git rebase -p d w &&
+	test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p is no-op when base inside second parent" "
+	reset_rebase &&
+	git rebase -p e w &&
+	test_cmp_rev w HEAD
+"
+
+test_expect_failure "rebase -p --root on non-linear history is a no-op" "
+	reset_rebase &&
+	git rebase -p --root w &&
+	test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p re-creates merge from side branch" "
+	reset_rebase &&
+	git rebase -p z w &&
+	test_cmp_rev z HEAD^ &&
+	test_cmp_rev w^2 HEAD^2
+"
+
+test_expect_success "rebase -p re-creates internal merge" "
+	reset_rebase &&
+	git rebase -p c w &&
+	test_cmp_rev c HEAD~4 &&
+	test_cmp_rev HEAD^2^ HEAD~3 &&
+	test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p can re-create two branches on onto" "
+	reset_rebase &&
+	git rebase -p --onto c d w &&
+	test_cmp_rev c HEAD~3 &&
+	test_cmp_rev c HEAD^2^ &&
+	test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+#       f
+#      /
+# a---b---c---g---h
+#      \
+#       d---gp--i
+#        \       \
+#         e-------u
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for patch-equivalence tests' '
+	git checkout e &&
+	test_merge u i
+'
+
+test_expect_success "rebase -p re-creates history around dropped commit matching upstream" "
+	reset_rebase &&
+	git rebase -p h u &&
+	test_cmp_rev h HEAD~3 &&
+	test_cmp_rev HEAD^2^ HEAD~2 &&
+	test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history drops patches in upstream" "
+	reset_rebase &&
+	git rebase -p --onto f h u &&
+	test_cmp_rev f HEAD~3 &&
+	test_cmp_rev HEAD^2^ HEAD~2 &&
+	test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history does not drop patches in onto" "
+	reset_rebase &&
+	git rebase -p --onto h f u &&
+	test_cmp_rev h HEAD~3 &&
+	test_cmp_rev HEAD^2~2 HEAD~2 &&
+	test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD
+"
+
+# a---b---c---g---h
+#      \
+#       d---gp--s
+#        \   \ /
+#         \   X
+#          \ / \
+#           e---t
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for dropping whole side' '
+	git checkout gp &&
+	test_merge s e &&
+	git checkout e &&
+	test_merge t gp
+'
+
+test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" "
+	reset_rebase &&
+	git rebase -p h s &&
+	test_cmp_rev h HEAD~2 &&
+	test_linear_range 'd e' h..
+"
+
+test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" "
+	reset_rebase &&
+	git rebase -p h t &&
+	test_cmp_rev h HEAD~2 &&
+	test_linear_range 'd e' h..
+"
+
+# a---b---c
+#      \
+#       d---e
+#        \   \
+#         n---r
+#          \
+#           o
+#
+# r = tree-same with n
+test_expect_success 'setup of non-linear-history for empty commits' '
+	git checkout n &&
+	git merge --no-commit e &&
+	git reset n . &&
+	git commit -m r &&
+	git reset --hard &&
+	git clean -f &&
+	git tag r
+'
+
+test_expect_success "rebase -p re-creates empty internal merge commit" "
+	reset_rebase &&
+	git rebase -p c r &&
+	test_cmp_rev c HEAD~3 &&
+	test_cmp_rev HEAD^2^ HEAD~2 &&
+	test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p re-creates empty merge commit" "
+	reset_rebase &&
+	git rebase -p o r &&
+	test_cmp_rev e HEAD^2 &&
+	test_cmp_rev o HEAD^ &&
+	test_revision_subjects 'r' HEAD
+"
+
+test_done
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index a0c6e30..fbdc47c 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -28,29 +28,21 @@
 '
 
 test_expect_success 'cherry-pick an empty commit' '
-	git checkout master && {
-		git cherry-pick empty-branch^
-		test "$?" = 1
-	}
+	git checkout master &&
+	test_expect_code 1 git cherry-pick empty-branch^
 '
 
 test_expect_success 'index lockfile was removed' '
-
 	test ! -f .git/index.lock
-
 '
 
 test_expect_success 'cherry-pick a commit with an empty message' '
-	git checkout master && {
-		git cherry-pick empty-branch
-		test "$?" = 1
-	}
+	git checkout master &&
+	test_expect_code 1 git cherry-pick empty-branch
 '
 
 test_expect_success 'index lockfile was removed' '
-
 	test ! -f .git/index.lock
-
 '
 
 test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
@@ -101,7 +93,7 @@
 	git reset --hard &&
 	git checkout fork^0 &&
 	git cherry-pick --keep-redundant-commits master &&
-	git show -s --format='%s' >actual &&
+	git show -s --format=%s >actual &&
 	echo "add file2 on master" >expect &&
 	test_cmp expect actual
 '
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
index df921d1..a5b6a5f 100755
--- a/t/t3509-cherry-pick-merge-df.sh
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -10,17 +10,15 @@
 	git commit -m a
 '
 
-test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
+test_expect_success 'Setup rename across paths each below D/F conflicts' '
 	mkdir b &&
-	ln -s ../a b/a &&
-	git add b &&
+	test_ln_s_add ../a b/a &&
 	git commit -m b &&
 
 	git checkout -b branch &&
 	rm b/a &&
-	mv a b/a &&
-	ln -s b/a a &&
-	git add . &&
+	git mv a b/a &&
+	test_ln_s_add b/a a &&
 	git commit -m swap &&
 
 	>f1 &&
@@ -28,7 +26,7 @@
 	git commit -m f1
 '
 
-test_expect_success SYMLINKS 'Cherry-pick succeeds with rename across D/F conflicts' '
+test_expect_success 'Cherry-pick succeeds with rename across D/F conflicts' '
 	git reset --hard &&
 	git checkout master^0 &&
 	git cherry-pick branch
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 0c44e9f..5c87b55 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -687,4 +687,100 @@
 	test_path_is_file e/f
 '
 
+test_expect_success 'setup for testing rm messages' '
+	>bar.txt &&
+	>foo.txt &&
+	git add bar.txt foo.txt
+'
+
+test_expect_success 'rm files with different staged content' '
+	cat >expect <<-\EOF &&
+	error: the following files have staged content different from both the
+	file and the HEAD:
+	    bar.txt
+	    foo.txt
+	(use -f to force removal)
+	EOF
+	echo content1 >foo.txt &&
+	echo content1 >bar.txt &&
+	test_must_fail git rm foo.txt bar.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'rm files with different staged content without hints' '
+	cat >expect <<-\EOF &&
+	error: the following files have staged content different from both the
+	file and the HEAD:
+	    bar.txt
+	    foo.txt
+	EOF
+	echo content2 >foo.txt &&
+	echo content2 >bar.txt &&
+	test_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with local modification' '
+	cat >expect <<-\EOF &&
+	error: the following file has local modifications:
+	    foo.txt
+	(use --cached to keep the file, or -f to force removal)
+	EOF
+	git commit -m "testing rm 3" &&
+	echo content3 >foo.txt &&
+	test_must_fail git rm foo.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with local modification without hints' '
+	cat >expect <<-\EOF &&
+	error: the following file has local modifications:
+	    bar.txt
+	EOF
+	echo content4 >bar.txt &&
+	test_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with changes in the index' '
+	cat >expect <<-\EOF &&
+	error: the following file has changes staged in the index:
+	    foo.txt
+	(use --cached to keep the file, or -f to force removal)
+	EOF
+	git reset --hard &&
+	echo content5 >foo.txt &&
+	git add foo.txt &&
+	test_must_fail git rm foo.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with changes in the index without hints' '
+	cat >expect <<-\EOF &&
+	error: the following file has changes staged in the index:
+	    foo.txt
+	EOF
+	test_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'rm files with two different errors' '
+	cat >expect <<-\EOF &&
+	error: the following file has staged content different from both the
+	file and the HEAD:
+	    foo1.txt
+	(use -f to force removal)
+	error: the following file has changes staged in the index:
+	    bar1.txt
+	(use --cached to keep the file, or -f to force removal)
+	EOF
+	echo content >foo1.txt &&
+	git add foo1.txt &&
+	echo content6 >foo1.txt &&
+	echo content6 >bar1.txt &&
+	git add bar1.txt &&
+	test_must_fail git rm bar1.txt foo1.txt 2>actual &&
+	test_i18ncmp expect actual
+'
+
 test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 874b3a6..aab86e8 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -30,10 +30,9 @@
 	 *) echo fail; git ls-files --stage xfoo1; (exit 1);;
 	 esac'
 
-test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
 	rm -f xfoo1 &&
-	ln -s foo xfoo1 &&
-	git add xfoo1 &&
+	test_ln_s_add foo xfoo1 &&
 	case "`git ls-files --stage xfoo1`" in
 	120000" "*xfoo1) echo pass;;
 	*) echo fail; git ls-files --stage xfoo1; (exit 1);;
@@ -51,21 +50,19 @@
 	 *) echo fail; git ls-files --stage xfoo2; (exit 1);;
 	 esac'
 
-test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
 	rm -f xfoo2 &&
-	ln -s foo xfoo2 &&
-	git update-index --add xfoo2 &&
+	test_ln_s_add foo xfoo2 &&
 	case "`git ls-files --stage xfoo2`" in
 	120000" "*xfoo2) echo pass;;
 	*) echo fail; git ls-files --stage xfoo2; (exit 1);;
 	esac
 '
 
-test_expect_success SYMLINKS \
+test_expect_success \
 	'git update-index --add: Test that executable bit is not used...' \
 	'git config core.filemode 0 &&
-	 ln -s xfoo2 xfoo3 &&
-	 git update-index --add xfoo3 &&
+	 test_ln_s_add xfoo2 xfoo3 &&	# runs git update-index --add
 	 case "`git ls-files --stage xfoo3`" in
 	 120000" "*xfoo3) echo pass;;
 	 *) echo fail; git ls-files --stage xfoo3; (exit 1);;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index 37ddabb..38b00c3 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -39,6 +39,42 @@
 	git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt
 '
 
+test_expect_success 'UTF-8 invalid characters refused' '
+	test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+	echo "UTF-8 characters" >F &&
+	printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
+		>"$HOME/invalid" &&
+	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+	grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 overlong sequences rejected' '
+	test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+	rm -f "$HOME/stderr" "$HOME/invalid" &&
+	echo "UTF-8 overlong" >F &&
+	printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
+		>"$HOME/invalid" &&
+	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+	grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+	test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+	echo "UTF-8 non-character 1" >F &&
+	printf "Commit message\n\nNon-character:\364\217\277\276\n" \
+		>"$HOME/invalid" &&
+	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+	grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+	test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+	echo "UTF-8 non-character 2." >F &&
+	printf "Commit message\n\nNon-character:\357\267\220\n" \
+		>"$HOME/invalid" &&
+	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+	grep "did not conform" "$HOME"/stderr
+'
 
 for H in ISO8859-1 eucJP ISO-2022-JP
 do
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 5dfbda7..5d22f17 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -200,17 +200,17 @@
 	echo foo > file &&
 	git stash &&
 	git stash apply -q > output.out 2>&1 &&
-	test ! -s output.out
+	test_must_be_empty output.out
 '
 
 test_expect_success 'save -q is quiet' '
 	git stash save --quiet > output.out 2>&1 &&
-	test ! -s output.out
+	test_must_be_empty output.out
 '
 
 test_expect_success 'pop -q is quiet' '
 	git stash pop -q > output.out 2>&1 &&
-	test ! -s output.out
+	test_must_be_empty output.out
 '
 
 test_expect_success 'pop -q --index works and is quiet' '
@@ -219,13 +219,13 @@
 	git stash save --quiet &&
 	git stash pop -q --index > output.out 2>&1 &&
 	test foo = "$(git show :file)" &&
-	test ! -s output.out
+	test_must_be_empty output.out
 '
 
 test_expect_success 'drop -q is quiet' '
 	git stash &&
 	git stash drop -q > output.out 2>&1 &&
-	test ! -s output.out
+	test_must_be_empty output.out
 '
 
 test_expect_success 'stash -k' '
@@ -336,41 +336,58 @@
 
 # This test creates a commit with a symlink used for the following tests
 
-test_expect_success SYMLINKS 'stash symlink to file' '
+test_expect_success 'stash symlink to file' '
 	git reset --hard &&
-	ln -s file filelink &&
-	git add filelink &&
+	test_ln_s_add file filelink &&
 	git commit -m "Add symlink" &&
 	rm filelink &&
 	cp file filelink &&
-	git stash save "symlink to file" &&
+	git stash save "symlink to file"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
 	test -h filelink &&
-	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
 	git stash apply &&
 	! test -h filelink &&
 	test bar = "$(cat file)"
 '
 
-test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+test_expect_success 'stash symlink to file (stage rm)' '
 	git reset --hard &&
 	git rm filelink &&
 	cp file filelink &&
-	git stash save "symlink to file (stage rm)" &&
+	git stash save "symlink to file (stage rm)"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
 	test -h filelink &&
-	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
 	git stash apply &&
 	! test -h filelink &&
 	test bar = "$(cat file)"
 '
 
-test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+test_expect_success 'stash symlink to file (full stage)' '
 	git reset --hard &&
 	rm filelink &&
 	cp file filelink &&
 	git add filelink &&
-	git stash save "symlink to file (full stage)" &&
+	git stash save "symlink to file (full stage)"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
 	test -h filelink &&
-	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+	case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
 	git stash apply &&
 	! test -h filelink &&
 	test bar = "$(cat file)"
@@ -637,4 +654,41 @@
 	test_cmp output expect
 '
 
+test_expect_success 'store called with invalid commit' '
+	test_must_fail git stash store foo
+'
+
+test_expect_success 'store updates stash ref and reflog' '
+	git stash clear &&
+	git reset --hard &&
+	echo quux >bazzy &&
+	git add bazzy &&
+	STASH_ID=$(git stash create) &&
+	git reset --hard &&
+	! grep quux bazzy &&
+	git stash store -m quuxery $STASH_ID &&
+	test $(cat .git/refs/stash) = $STASH_ID &&
+	grep $STASH_ID .git/logs/refs/stash &&
+	git stash pop &&
+	grep quux bazzy
+'
+
+test_expect_success 'stash a change to turn a non-directory to a directory' '
+	git reset --hard &&
+	>testfile &&
+	git add testfile &&
+	git commit -m "add testfile as a regular file" &&
+	rm testfile &&
+	mkdir testfile &&
+	>testfile/file &&
+	test_must_fail git stash save "recover regular file" &&
+	test -f testfile/file
+'
+
+test_expect_success 'stash a change to turn a non-directory to a directory (forced)' '
+	git stash save --force "recover regular file (forced)" &&
+	! test -f testfile/file &&
+	test -f testfile
+'
+
 test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index 6ddd469..8de36b7 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -15,17 +15,17 @@
 cat path0 >path1
 chmod +x path1
 
-test_expect_success \
-    'update-index --add two files with and without +x.' \
-    'git update-index --add path0 path1'
+test_expect_success 'update-index --add two files with and without +x.' '
+	git update-index --add path0 path1
+'
 
 mv path0 path0-
 sed -e 's/line/Line/' <path0- >path0
 chmod +x path0
 rm -f path1
-test_expect_success \
-    'git diff-files -p after editing work tree.' \
-    'git diff-files -p >current'
+test_expect_success 'git diff-files -p after editing work tree.' '
+	git diff-files -p >actual
+'
 
 # that's as far as it comes
 if [ "$(git config --get core.filemode)" = false ]
@@ -55,8 +55,38 @@
 -line 3
 EOF
 
-test_expect_success \
-    'validate git diff-files -p output.' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate git diff-files -p output.' '
+	compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files -s after editing work tree' '
+	git diff-files -s >actual 2>err &&
+	test_must_be_empty actual &&
+	test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch as synonym for -s' '
+	git diff-files --no-patch >actual 2>err &&
+	test_must_be_empty actual &&
+	test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch --patch shows the patch' '
+	git diff-files --no-patch --patch >actual &&
+	compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '
+	git diff-files --no-patch --patch-with-raw >actual &&
+	grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M	path0\$" actual &&
+	tail -n +4 actual >actual-patch &&
+	compare_diff_patch expected actual-patch
+'
+
+test_expect_success 'git diff-files --patch --no-patch does not show the patch' '
+	git diff-files --patch --no-patch >actual 2>err &&
+	test_must_be_empty actual &&
+	test_must_be_empty err
+'
 
 test_done
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 73b4a24..27e98a8 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -99,11 +99,11 @@
     'validate result of -B -M (#4)' \
     'compare_diff_raw expected current'
 
-test_expect_success SYMLINKS \
+test_expect_success \
     'make file0 into something completely different' \
     'rm -f file0 &&
-     ln -s frotz file0 &&
-     git update-index file0 file1'
+     test_ln_s_add frotz file0 &&
+     git update-index file1'
 
 test_expect_success \
     'run diff with -B' \
@@ -114,7 +114,7 @@
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100	file1
 EOF
 
-test_expect_success SYMLINKS \
+test_expect_success \
     '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 SYMLINKS \
+test_expect_success \
     '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 SYMLINKS \
+test_expect_success \
     'validate result of -M (#7)' \
     'compare_diff_raw expected current'
 
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index f0d5041..13e7f62 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -9,7 +9,7 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-test_expect_success SYMLINKS 'diff new symlink and file' '
+test_expect_success 'diff new symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	new file mode 120000
@@ -27,22 +27,25 @@
 	@@ -0,0 +1 @@
 	+xyzzy
 	EOF
-	ln -s xyzzy frotz &&
-	echo xyzzy >nitfol &&
+
+	# the empty tree
 	git update-index &&
 	tree=$(git write-tree) &&
-	git update-index --add frotz nitfol &&
+
+	test_ln_s_add xyzzy frotz &&
+	echo xyzzy >nitfol &&
+	git update-index --add nitfol &&
 	GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
+test_expect_success 'diff unchanged symlink and file'  '
 	tree=$(git write-tree) &&
 	git update-index frotz nitfol &&
 	test -z "$(git diff-index --name-only $tree)"
 '
 
-test_expect_success SYMLINKS 'diff removed symlink and file' '
+test_expect_success 'diff removed symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	deleted file mode 120000
@@ -66,12 +69,18 @@
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
+test_expect_success 'diff identical, but newly created symlink and file' '
 	>expected &&
 	rm -f frotz nitfol &&
 	echo xyzzy >nitfol &&
 	test-chmtime +10 nitfol &&
-	ln -s xyzzy frotz &&
+	if test_have_prereq SYMLINKS
+	then
+		ln -s xyzzy frotz
+	else
+		printf xyzzy >frotz
+		# the symlink property propagates from the index
+	fi &&
 	git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current &&
 
@@ -80,7 +89,7 @@
 	compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff different symlink and file' '
+test_expect_success 'diff different symlink and file' '
 	cat >expected <<-\EOF &&
 	diff --git a/frotz b/frotz
 	index 7c465af..df1db54 120000
@@ -100,7 +109,13 @@
 	+yxyyz
 	EOF
 	rm -f frotz &&
-	ln -s yxyyz frotz &&
+	if test_have_prereq SYMLINKS
+	then
+		ln -s yxyyz frotz
+	else
+		printf yxyyz >frotz
+		# the symlink property propagates from the index
+	fi &&
 	echo yxyyz >nitfol &&
 	git diff-index -M -p $tree >current &&
 	compare_diff_patch expected current
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 58d4180..668933b 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -972,6 +972,49 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--from=ident notices bogus ident' '
+	test_must_fail git format-patch -1 --stdout --from=foo >patch
+'
+
+test_expect_success '--from=ident replaces author' '
+	git format-patch -1 --stdout --from="Me <me@example.com>" >patch &&
+	cat >expect <<-\EOF &&
+	From: Me <me@example.com>
+
+	From: A U Thor <author@example.com>
+
+	EOF
+	sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+	test_cmp expect patch.head
+'
+
+test_expect_success '--from uses committer ident' '
+	git format-patch -1 --stdout --from >patch &&
+	cat >expect <<-\EOF &&
+	From: C O Mitter <committer@example.com>
+
+	From: A U Thor <author@example.com>
+
+	EOF
+	sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+	test_cmp expect patch.head
+'
+
+test_expect_success 'in-body headers trigger content encoding' '
+	GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
+	test_when_finished "git reset --hard HEAD^" &&
+	git format-patch -1 --stdout --from >patch &&
+	cat >expect <<-\EOF &&
+	From: C O Mitter <committer@example.com>
+	Content-Type: text/plain; charset=UTF-8
+
+	From: éxötìc <author@example.com>
+
+	EOF
+	sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+	test_cmp expect patch.head
+'
+
 append_signoff()
 {
 	C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index cc3db13..3fb4b97 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -142,6 +142,351 @@
 git diff --ignore-space-at-eol > out
 test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
 
+test_expect_success 'ignore-blank-lines: only new lines' '
+	test_seq 5 >x &&
+	git update-index x &&
+	test_seq 5 | sed "/3/i \\
+" >x &&
+	git diff --ignore-blank-lines >out &&
+	>expect &&
+	test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: only new lines with space' '
+	test_seq 5 >x &&
+	git update-index x &&
+	test_seq 5 | sed "/3/i \ " >x &&
+	git diff -w --ignore-blank-lines >out &&
+	>expect &&
+	test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: after change' '
+	cat <<-\EOF >x &&
+	1
+	2
+
+	3
+	4
+	5
+
+	6
+	7
+	EOF
+	git update-index x &&
+	cat <<-\EOF >x &&
+	change
+
+	1
+	2
+	3
+	4
+	5
+	6
+
+	7
+	EOF
+	git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -1,6 +1,7 @@
+	+change
+	+
+	 1
+	 2
+	-
+	 3
+	 4
+	 5
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: before change' '
+	cat <<-\EOF >x &&
+	1
+	2
+
+	3
+	4
+	5
+	6
+	7
+	EOF
+	git update-index x &&
+	cat <<-\EOF >x &&
+
+	1
+	2
+	3
+	4
+	5
+
+	6
+	7
+	change
+	EOF
+	git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -4,5 +4,7 @@
+	 3
+	 4
+	 5
+	+
+	 6
+	 7
+	+change
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes' '
+	cat <<-\EOF >x &&
+	1
+	2
+	3
+	4
+	5
+
+
+	6
+	7
+	8
+	9
+	10
+	EOF
+	git update-index x &&
+	cat <<-\EOF >x &&
+	change
+	1
+	2
+
+	3
+	4
+	5
+	6
+	7
+	8
+
+	9
+	10
+	change
+	EOF
+	git diff --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -1,5 +1,7 @@
+	+change
+	 1
+	 2
+	+
+	 3
+	 4
+	 5
+	@@ -8,5 +8,7 @@
+	 6
+	 7
+	 8
+	+
+	 9
+	 10
+	+change
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '
+	test_seq 10 >x &&
+	git update-index x &&
+	cat <<-\EOF >x &&
+	change
+	1
+	2
+
+	3
+	4
+	5
+
+	6
+	7
+	8
+	9
+
+	10
+	change
+	EOF
+	git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -1,10 +1,15 @@
+	+change
+	 1
+	 2
+	+
+	 3
+	 4
+	 5
+	+
+	 6
+	 7
+	 8
+	 9
+	+
+	 10
+	+change
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: scattered spaces' '
+	test_seq 10 >x &&
+	git update-index x &&
+	cat <<-\EOF >x &&
+	change
+	1
+	2
+	3
+
+	4
+
+	5
+
+	6
+
+
+	7
+
+	8
+	9
+	10
+	change
+	EOF
+	git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -1,3 +1,4 @@
+	+change
+	 1
+	 2
+	 3
+	@@ -8,3 +15,4 @@
+	 8
+	 9
+	 10
+	+change
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: spaces coalesce' '
+	test_seq 6 >x &&
+	git update-index x &&
+	cat <<-\EOF >x &&
+	change
+	1
+	2
+	3
+
+	4
+
+	5
+
+	6
+	change
+	EOF
+	git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -1,6 +1,11 @@
+	+change
+	 1
+	 2
+	 3
+	+
+	 4
+	+
+	 5
+	+
+	 6
+	+change
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '
+	test_seq 16 >x &&
+	git update-index x &&
+	cat <<-\EOF >x &&
+	change
+	1
+	2
+
+	3
+	4
+	5
+	change
+	6
+	7
+	8
+
+	9
+	10
+	11
+	change
+	12
+	13
+	14
+
+	15
+	16
+	change
+	EOF
+	git diff --ignore-blank-lines >out.tmp &&
+	cat <<-\EOF >expected &&
+	diff --git a/x b/x
+	--- a/x
+	+++ b/x
+	@@ -1,8 +1,11 @@
+	+change
+	 1
+	 2
+	+
+	 3
+	 4
+	 5
+	+change
+	 6
+	 7
+	 8
+	@@ -9,8 +13,11 @@
+	 9
+	 10
+	 11
+	+change
+	 12
+	 13
+	 14
+	+
+	 15
+	 16
+	+change
+	EOF
+	compare_diff_patch expected out.tmp
+'
+
 test_expect_success 'check mixed spaces and tabs in indent' '
 
 	# This is indented with SP HT SP.
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 5d20acf..55d549f 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -4,44 +4,44 @@
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
 	rm -f foo bar &&
 	cat "$TEST_DIRECTORY"/../COPYING >foo &&
-	ln -s linklink bar &&
-	git add foo bar &&
+	test_ln_s_add linklink bar &&
+	git add foo &&
 	git commit -a -m Initial &&
 	git tag one &&
 
-	rm -f foo bar &&
+	git rm -f foo bar &&
 	cat "$TEST_DIRECTORY"/../COPYING >bar &&
-	ln -s linklink foo &&
-	git add foo bar &&
+	test_ln_s_add linklink foo &&
+	git add bar &&
 	git commit -a -m Second &&
 	git tag two &&
 
-	rm -f foo bar &&
+	git rm -f foo bar &&
 	cat "$TEST_DIRECTORY"/../COPYING >foo &&
 	git add foo &&
 	git commit -a -m Third &&
 	git tag three &&
 
 	mv foo bar &&
-	ln -s linklink foo &&
-	git add foo bar &&
+	test_ln_s_add linklink foo &&
+	git add bar &&
 	git commit -a -m Fourth &&
 	git tag four &&
 
 	# This is purely for sanity check
 
-	rm -f foo bar &&
+	git rm -f foo 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 &&
+	git rm -f foo bar &&
 	cat "$TEST_DIRECTORY"/../Makefile >foo &&
 	cat "$TEST_DIRECTORY"/../COPYING >bar &&
 	git add foo bar &&
@@ -50,7 +50,7 @@
 
 '
 
-test_expect_success SYMLINKS 'cross renames to be detected for regular files' '
+test_expect_success 'cross renames to be detected for regular files' '
 
 	git diff-tree five six -r --name-status -B -M | sort >actual &&
 	{
@@ -61,7 +61,7 @@
 
 '
 
-test_expect_success SYMLINKS 'cross renames to be detected for typechange' '
+test_expect_success 'cross renames to be detected for typechange' '
 
 	git diff-tree one two -r --name-status -B -M | sort >actual &&
 	{
@@ -72,7 +72,7 @@
 
 '
 
-test_expect_success SYMLINKS 'moves and renames' '
+test_expect_success 'moves and renames' '
 
 	git diff-tree three four -r --name-status -B -M | sort >actual &&
 	{
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index 53ec330..f75f46f 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -139,12 +139,10 @@
 +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 &&
+	rm -f file &&
+	test_ln_s_add frotz file &&
 	git commit -m typechange &&
 	git show >diff &&
 	find_diff <diff >actual &&
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index 1261dbb..1019d7b 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -353,4 +353,52 @@
 	compare_diff_patch expected actual
 '
 
+# Test for a bug reported at
+# http://thread.gmane.org/gmane.comp.version-control.git/224410
+# where a delete lines were missing from combined diff output when they
+# occurred exactly before the context lines of a later change.
+test_expect_success 'combine diff missing delete bug' '
+	git commit -m initial --allow-empty &&
+	cat <<-\EOF >test &&
+	1
+	2
+	3
+	4
+	EOF
+	git add test &&
+	git commit -a -m side1 &&
+	git checkout -B side1 &&
+	git checkout HEAD^ &&
+	cat <<-\EOF >test &&
+	0
+	1
+	2
+	3
+	4modified
+	EOF
+	git add test &&
+	git commit -m side2 &&
+	git branch -f side2 &&
+	test_must_fail git merge --no-commit side1 &&
+	cat <<-\EOF >test &&
+	1
+	2
+	3
+	4modified
+	EOF
+	git add test &&
+	git commit -a -m merge &&
+	git diff-tree -c -p HEAD >actual.tmp &&
+	sed -e "1,/^@@@/d" < actual.tmp >actual &&
+	tr -d Q <<-\EOF >expected &&
+	- 0
+	  1
+	  2
+	  3
+	 -4
+	 +4modified
+	EOF
+	compare_diff_patch expected actual
+'
+
 test_done
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index 32d4a60..1751c83 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
 #
 
 test_description='Support for verbose submodule differences in git diff
@@ -10,6 +11,9 @@
 
 . ./test-lib.sh
 
+# String "added" in German (translated with Google Translate), encoded in UTF-8,
+# used in sample commit log messages in add_file() function below.
+added=$(printf "hinzugef\303\274gt")
 add_file () {
 	(
 		cd "$1" &&
@@ -19,7 +23,8 @@
 			echo "$name" >"$name" &&
 			git add "$name" &&
 			test_tick &&
-			git commit -m "Add $name" || exit
+			msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t iso8859-1) &&
+			git -c 'i18n.commitEncoding=iso8859-1' commit -m "$msg_added_iso88591"
 		done >/dev/null &&
 		git rev-parse --short --verify HEAD
 	)
@@ -93,7 +98,7 @@
 	git diff-index -p --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head1..$head2:
-	  > Add foo3
+	  > Add foo3 ($added foo3)
 	EOF
 	test_cmp expected actual
 '
@@ -102,7 +107,7 @@
 	git diff --submodule=log >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head1..$head2:
-	  > Add foo3
+	  > Add foo3 ($added foo3)
 	EOF
 	test_cmp expected actual
 '
@@ -111,7 +116,7 @@
 	git diff --submodule >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head1..$head2:
-	  > Add foo3
+	  > Add foo3 ($added foo3)
 	EOF
 	test_cmp expected actual
 '
@@ -142,8 +147,8 @@
 	git diff-index -p --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head2..$head3 (rewind):
-	  < Add foo3
-	  < Add foo2
+	  < Add foo3 ($added foo3)
+	  < Add foo2 ($added foo2)
 	EOF
 	test_cmp expected actual
 '
@@ -153,10 +158,10 @@
 	git diff-index -p --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head2...$head4:
-	  > Add foo5
-	  > Add foo4
-	  < Add foo3
-	  < Add foo2
+	  > Add foo5 ($added foo5)
+	  > Add foo4 ($added foo4)
+	  < Add foo3 ($added foo3)
+	  < Add foo2 ($added foo2)
 	EOF
 	test_cmp expected actual
 '
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
index 7c39843..1618a6d 100755
--- a/t/t4111-apply-subdir.sh
+++ b/t/t4111-apply-subdir.sh
@@ -86,6 +86,20 @@
 	test_cmp expected sub/dir/file
 '
 
+test_expect_success 'apply half-broken patch from subdir of toplevel' '
+	(
+		cd sub/dir &&
+		test_must_fail git apply <<-EOF
+		--- sub/dir/file
+		+++ sub/dir/file
+		@@ -1,0 +1,0 @@
+		--- file_in_root
+		+++ file_in_root
+		@@ -1,0 +1,0 @@
+		EOF
+	)
+'
+
 test_expect_success 'apply from .git dir' '
 	cp postimage expected &&
 	cp preimage .git/file &&
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index f12826f..ebadbc3 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -9,20 +9,19 @@
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS 'setup repository and commits' '
+test_expect_success 'setup repository and commits' '
 	echo "hello world" > foo &&
 	echo "hi planet" > bar &&
 	git update-index --add foo bar &&
 	git commit -m initial &&
 	git branch initial &&
 	rm -f foo &&
-	ln -s bar foo &&
-	git update-index foo &&
+	test_ln_s_add bar foo &&
 	git commit -m "foo symlinked to bar" &&
 	git branch foo-symlinked-to-bar &&
-	rm -f foo &&
+	git rm -f foo &&
 	echo "how far is the sun?" > foo &&
-	git update-index foo &&
+	git update-index --add foo &&
 	git commit -m "foo back to file" &&
 	git branch foo-back-to-file &&
 	printf "\0" > foo &&
@@ -42,7 +41,7 @@
 	git branch foo-baz-renamed-from-foo
 	'
 
-test_expect_success SYMLINKS 'file renamed from foo to foo/baz' '
+test_expect_success 'file renamed from foo to foo/baz' '
 	git checkout -f initial &&
 	git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
 	git apply --index < patch
@@ -50,7 +49,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'file renamed from foo/baz to foo' '
+test_expect_success 'file renamed from foo/baz to foo' '
 	git checkout -f foo-baz-renamed-from-foo &&
 	git diff-tree -M -p HEAD initial > patch &&
 	git apply --index < patch
@@ -58,7 +57,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'directory becomes file' '
+test_expect_success 'directory becomes file' '
 	git checkout -f foo-becomes-a-directory &&
 	git diff-tree -p HEAD initial > patch &&
 	git apply --index < patch
@@ -66,7 +65,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'file becomes directory' '
+test_expect_success 'file becomes directory' '
 	git checkout -f initial &&
 	git diff-tree -p HEAD foo-becomes-a-directory > patch &&
 	git apply --index < patch
@@ -74,7 +73,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'file becomes symlink' '
+test_expect_success 'file becomes symlink' '
 	git checkout -f initial &&
 	git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
 	git apply --index < patch
@@ -82,21 +81,21 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'symlink becomes file' '
+test_expect_success 'symlink becomes file' '
 	git checkout -f foo-symlinked-to-bar &&
 	git diff-tree -p HEAD foo-back-to-file > patch &&
 	git apply --index < patch
 	'
 test_debug 'cat patch'
 
-test_expect_success SYMLINKS 'binary file becomes symlink' '
+test_expect_success 'binary file becomes symlink' '
 	git checkout -f foo-becomes-binary &&
 	git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
 	git apply --index < patch
 	'
 test_debug 'cat patch'
 
-test_expect_success SYMLINKS 'symlink becomes binary file' '
+test_expect_success 'symlink becomes binary file' '
 	git checkout -f foo-symlinked-to-bar &&
 	git diff-tree -p --binary HEAD foo-becomes-binary > patch &&
 	git apply --index < patch
@@ -104,7 +103,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'symlink becomes directory' '
+test_expect_success 'symlink becomes directory' '
 	git checkout -f foo-symlinked-to-bar &&
 	git diff-tree -p HEAD foo-becomes-a-directory > patch &&
 	git apply --index < patch
@@ -112,7 +111,7 @@
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'directory becomes symlink' '
+test_expect_success 'directory becomes symlink' '
 	git checkout -f foo-becomes-a-directory &&
 	git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
 	git apply --index < patch
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 7674dd2..872fcda 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -9,18 +9,16 @@
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
-	ln -s path1/path2/path3/path4/path5 link1 &&
-	git add link? &&
+	test_ln_s_add path1/path2/path3/path4/path5 link1 &&
 	git commit -m initial &&
 
 	git branch side &&
 
 	rm -f link? &&
 
-	ln -s htap6 link1 &&
-	git update-index link? &&
+	test_ln_s_add htap6 link1 &&
 	git commit -m second &&
 
 	git diff-tree -p HEAD^ HEAD >patch  &&
@@ -37,7 +35,7 @@
 
 '
 
-test_expect_success SYMLINKS 'apply --index symlink patch' '
+test_expect_success 'apply --index symlink patch' '
 
 	git checkout -f side &&
 	git apply --index patch &&
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 3940737..70b3a06 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -10,11 +10,11 @@
 	done
 }
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
 	mkdir -p arch/i386/boot arch/x86_64 &&
 	lecho 1 2 3 4 5 >arch/i386/boot/Makefile &&
-	ln -s ../i386/boot arch/x86_64/boot &&
+	test_ln_s_add ../i386/boot arch/x86_64/boot &&
 	git add . &&
 	test_tick &&
 	git commit -m initial &&
@@ -31,7 +31,7 @@
 
 '
 
-test_expect_success SYMLINKS apply '
+test_expect_success apply '
 
 	git checkout test &&
 	git diff --exit-code test &&
@@ -40,7 +40,7 @@
 
 '
 
-test_expect_success SYMLINKS 'check result' '
+test_expect_success 'check result' '
 
 	git diff --exit-code master &&
 	git diff --exit-code --cached master &&
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 12f6b02..5edb79a 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -147,7 +147,7 @@
 	git checkout first &&
 	test_tick &&
 	git am <patch1 &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code second &&
 	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
 	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -158,7 +158,7 @@
 	git reset --hard &&
 	git checkout first &&
 	git am patch1.eml &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code second &&
 	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
 	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -169,7 +169,7 @@
 	git reset --hard &&
 	git checkout first &&
 	git am patch1-crlf.eml &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code second &&
 	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
 	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -180,7 +180,7 @@
 	git reset --hard &&
 	git checkout first &&
 	git am patch1-ws.eml &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code second &&
 	test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
 	test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -206,7 +206,7 @@
 	git reset --hard &&
 	git checkout first &&
 	git am patch2 &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
 	git diff --exit-code master..HEAD &&
 	git diff --exit-code master^..HEAD^ &&
@@ -258,7 +258,7 @@
 	git reset --hard &&
 	git checkout HEAD^ &&
 	git am --keep patch4 &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git cat-file commit HEAD >actual &&
 	grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
 '
@@ -268,7 +268,7 @@
 	git reset --hard &&
 	git checkout HEAD^ &&
 	git am --keep-non-patch patch4 &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git cat-file commit HEAD >actual &&
 	grep "^\[foo\] third" actual
 '
@@ -283,7 +283,7 @@
 	test_tick &&
 	git commit -m "copied stuff" &&
 	git am -3 lorem-move.patch &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code lorem
 '
 
@@ -297,7 +297,7 @@
 	test_tick &&
 	git commit -m "copied stuff" &&
 	git am -3 -p0 lorem-zero.patch &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code lorem
 '
 
@@ -307,7 +307,7 @@
 	git reset --hard &&
 	git checkout lorem^0 &&
 	git am rename.patch &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git update-index --refresh &&
 	git diff --exit-code rename
 '
@@ -318,7 +318,7 @@
 	git reset --hard &&
 	git checkout lorem^0 &&
 	git am -3 rename.patch &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git update-index --refresh &&
 	git diff --exit-code rename
 '
@@ -329,7 +329,7 @@
 	git reset --hard &&
 	git checkout lorem^0 &&
 	git am -3 rename-add.patch &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git update-index --refresh &&
 	git diff --exit-code rename
 '
@@ -358,11 +358,17 @@
 test_expect_success 'am --skip works' '
 	echo goodbye >expected &&
 	git am --skip &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code lorem2^^ -- file &&
 	test_cmp expected another
 '
 
+test_expect_success 'am --abort removes a stray directory' '
+	mkdir .git/rebase-apply &&
+	git am --abort &&
+	test_path_is_missing .git/rebase-apply
+'
+
 test_expect_success 'am --resolved works' '
 	echo goodbye >expected &&
 	rm -fr .git/rebase-apply &&
@@ -373,7 +379,7 @@
 	echo resolved >>file &&
 	git add file &&
 	git am --resolved &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	test_cmp expected another
 '
 
@@ -382,7 +388,7 @@
 	git reset --hard &&
 	git checkout first &&
 	cat pine patch1 | git am &&
-	! test -d .git/rebase-apply &&
+	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code master^..HEAD
 '
 
@@ -391,7 +397,7 @@
 	git reset --hard &&
 	test_must_fail git am <failmail &&
 	git am --abort &&
-	! test -d .git/rebase-apply
+	test_path_is_missing .git/rebase-apply
 '
 
 test_expect_success 'am fails on empty patch' '
@@ -400,7 +406,7 @@
 	echo "---" >>failmail &&
 	test_must_fail git am <failmail &&
 	git am --skip &&
-	! test -d .git/rebase-apply
+	test_path_is_missing .git/rebase-apply
 '
 
 test_expect_success 'am works from stdin in subdirectory' '
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 9243a97..cb03d28 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -530,6 +530,20 @@
 	)
 '
 
+test_expect_success 'git log -c --follow' '
+	test_create_repo follow-c &&
+	(
+		cd follow-c &&
+		test_commit initial file original &&
+		git rm file &&
+		test_commit rename file2 original &&
+		git reset --hard initial &&
+		test_commit modify file foo &&
+		git merge -m merge rename &&
+		git log -c --follow file2
+	)
+'
+
 cat >expect <<\EOF
 *   commit COMMIT_OBJECT_NAME
 |\  Merge: MERGE_PARENTS
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 842b754..baa4685 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -13,6 +13,11 @@
 }
 
 test_expect_success setup '
+	cat >contacts <<-\EOF &&
+	A U Thor <author@example.com>
+	nick1 <bugs@company.xx>
+	EOF
+
 	echo one >one &&
 	git add one &&
 	test_tick &&
@@ -23,6 +28,44 @@
 	git commit --author "nick1 <bugs@company.xx>" -m second
 '
 
+test_expect_success 'check-mailmap no arguments' '
+	test_must_fail git check-mailmap
+'
+
+test_expect_success 'check-mailmap arguments' '
+	cat >expect <<-\EOF &&
+	A U Thor <author@example.com>
+	nick1 <bugs@company.xx>
+	EOF
+	git check-mailmap \
+		"A U Thor <author@example.com>" \
+		"nick1 <bugs@company.xx>" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin' '
+	cat >expect <<-\EOF &&
+	A U Thor <author@example.com>
+	nick1 <bugs@company.xx>
+	EOF
+	git check-mailmap --stdin <contacts >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin arguments' '
+	cat >expect <<-\EOF &&
+	Internal Guy <bugs@company.xy>
+	EOF
+	cat <contacts >>expect &&
+	git check-mailmap --stdin "Internal Guy <bugs@company.xy>" \
+		<contacts >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap bogus contact' '
+	test_must_fail git check-mailmap bogus
+'
+
 cat >expect <<\EOF
 A U Thor (1):
       initial
@@ -247,6 +290,24 @@
 	rm -f .mailmap
 '
 
+test_expect_success 'single-character name' '
+	echo "     1	A <author@example.com>" >expect &&
+	echo "     1	nick1 <bugs@company.xx>" >>expect &&
+	echo "A <author@example.com>" >.mailmap &&
+	test_when_finished "rm .mailmap" &&
+	git shortlog -es HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'preserve canonical email case' '
+	echo "     1	A U Thor <AUTHOR@example.com>" >expect &&
+	echo "     1	nick1 <bugs@company.xx>" >>expect &&
+	echo "<AUTHOR@example.com> <author@example.com>" >.mailmap &&
+	test_when_finished "rm .mailmap" &&
+	git shortlog -es 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):
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 26fbfde..fb00041 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -1,20 +1,38 @@
 #!/bin/sh
 #
 # Copyright (c) 2010, Will Palmer
+# Copyright (c) 2011, Alexey Shumkin (+ non-UTF-8 commit encoding tests)
 #
 
 test_description='Test pretty formats'
 . ./test-lib.sh
 
+sample_utf8_part=$(printf "f\303\244ng")
+
+commit_msg () {
+	# String "initial. initial" partly in German
+	# (translated with Google Translate),
+	# encoded in UTF-8, used as a commit log message below.
+	msg="initial. an${sample_utf8_part}lich\n"
+	if test -n "$1"
+	then
+		printf "$msg" | iconv -f utf-8 -t "$1"
+	else
+		printf "$msg"
+	fi
+}
+
 test_expect_success 'set up basic repos' '
 	>foo &&
 	>bar &&
 	git add foo &&
 	test_tick &&
-	git commit -m initial &&
+	git config i18n.commitEncoding iso8859-1 &&
+	git commit -m "$(commit_msg iso8859-1)" &&
 	git add bar &&
 	test_tick &&
-	git commit -m "add bar"
+	git commit -m "add bar" &&
+	git config --unset i18n.commitEncoding
 '
 
 test_expect_success 'alias builtin format' '
@@ -38,6 +56,20 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'alias user-defined tformat with %s (iso8859-1 encoding)' '
+	git config i18n.logOutputEncoding iso8859-1 &&
+	git log --oneline >expected-s &&
+	git log --pretty="tformat:%h %s" >actual-s &&
+	git config --unset i18n.logOutputEncoding &&
+	test_cmp expected-s actual-s
+'
+
+test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
+	git log --oneline >expected-s &&
+	git log --pretty="tformat:%h %s" >actual-s &&
+	test_cmp expected-s actual-s
+'
+
 test_expect_success 'alias user-defined tformat' '
 	git log --pretty="tformat:%h" >expected &&
 	git config pretty.test-alias "tformat:%h" &&
@@ -72,13 +104,13 @@
 '
 
 test_expect_success 'NUL separation' '
-	printf "add bar\0initial" >expected &&
+	printf "add bar\0$(commit_msg)" >expected &&
 	git log -z --pretty="format:%s" >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'NUL termination' '
-	printf "add bar\0initial\0" >expected &&
+	printf "add bar\0$(commit_msg)\0" >expected &&
 	git log -z --pretty="tformat:%s" >actual &&
 	test_cmp expected actual
 '
@@ -86,7 +118,7 @@
 test_expect_success 'NUL separation with --stat' '
 	stat0_part=$(git diff --stat HEAD^ HEAD) &&
 	stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
-	printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n" >expected &&
+	printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected &&
 	git log -z --stat --pretty="format:%s" >actual &&
 	test_i18ncmp expected actual
 '
@@ -94,25 +126,29 @@
 test_expect_failure 'NUL termination with --stat' '
 	stat0_part=$(git diff --stat HEAD^ HEAD) &&
 	stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
-	printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n\0" >expected &&
+	printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
 	git log -z --stat --pretty="tformat:%s" >actual &&
 	test_i18ncmp expected actual
 '
 
 test_expect_success 'setup more commits' '
 	test_commit "message one" one one message-one &&
-	test_commit "message two" two two message-two
+	test_commit "message two" two two message-two &&
+	head1=$(git rev-parse --verify --short HEAD~0) &&
+	head2=$(git rev-parse --verify --short HEAD~1) &&
+	head3=$(git rev-parse --verify --short HEAD~2) &&
+	head4=$(git rev-parse --verify --short HEAD~3)
 '
 
 test_expect_success 'left alignment formatting' '
 	git log --pretty="format:%<(40)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
+	qz_to_tab_space <<EOF >expected &&
 message two                            Z
 message one                            Z
 add bar                                Z
-initial                                Z
+$(commit_msg)                    Z
 EOF
 	test_cmp expected actual
 '
@@ -121,11 +157,11 @@
 	git log --pretty="format:%h %<|(40)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
-fa33ab1 message two                    Z
-7cd6c63 message one                    Z
-1711bf9 add bar                        Z
-af20c06 initial                        Z
+	qz_to_tab_space <<EOF >expected &&
+$head1 message two                    Z
+$head2 message one                    Z
+$head3 add bar                        Z
+$head4 $(commit_msg)            Z
 EOF
 	test_cmp expected actual
 '
@@ -134,11 +170,11 @@
 	git log --pretty="format:%<(1)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	cat <<\EOF >expected &&
+	cat <<EOF >expected &&
 message two
 message one
 add bar
-initial
+$(commit_msg)
 EOF
 	test_cmp expected actual
 '
@@ -147,11 +183,11 @@
 	git log --pretty="format:%<(10,trunc)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
+	qz_to_tab_space <<EOF >expected &&
 message ..
 message ..
 add bar  Z
-initial  Z
+initial...
 EOF
 	test_cmp expected actual
 '
@@ -160,11 +196,11 @@
 	git log --pretty="format:%<(10,ltrunc)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
+	qz_to_tab_space <<EOF >expected &&
 ..sage two
 ..sage one
 add bar  Z
-initial  Z
+..${sample_utf8_part}lich
 EOF
 	test_cmp expected actual
 '
@@ -173,11 +209,11 @@
 	git log --pretty="format:%<(10,mtrunc)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
+	qz_to_tab_space <<EOF >expected &&
 mess.. two
 mess.. one
 add bar  Z
-initial  Z
+init..lich
 EOF
 	test_cmp expected actual
 '
@@ -186,11 +222,11 @@
 	git log --pretty="format:%>(40)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
+	qz_to_tab_space <<EOF >expected &&
 Z                            message two
 Z                            message one
 Z                                add bar
-Z                                initial
+Z                    $(commit_msg)
 EOF
 	test_cmp expected actual
 '
@@ -199,11 +235,11 @@
 	git log --pretty="format:%h %>|(40)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
-fa33ab1                      message two
-7cd6c63                      message one
-1711bf9                          add bar
-af20c06                          initial
+	qz_to_tab_space <<EOF >expected &&
+$head1                      message two
+$head2                      message one
+$head3                          add bar
+$head4              $(commit_msg)
 EOF
 	test_cmp expected actual
 '
@@ -212,11 +248,11 @@
 	git log --pretty="format:%>(1)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	cat <<\EOF >expected &&
+	cat <<EOF >expected &&
 message two
 message one
 add bar
-initial
+$(commit_msg)
 EOF
 	test_cmp expected actual
 '
@@ -225,11 +261,11 @@
 	git log --pretty="format:%><(40)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
+	qz_to_tab_space <<EOF >expected &&
 Z             message two              Z
 Z             message one              Z
 Z               add bar                Z
-Z               initial                Z
+Z         $(commit_msg)          Z
 EOF
 	test_cmp expected actual
 '
@@ -238,11 +274,11 @@
 	git log --pretty="format:%h %><|(40)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	qz_to_tab_space <<\EOF >expected &&
-fa33ab1           message two          Z
-7cd6c63           message one          Z
-1711bf9             add bar            Z
-af20c06             initial            Z
+	qz_to_tab_space <<EOF >expected &&
+$head1           message two          Z
+$head2           message one          Z
+$head3             add bar            Z
+$head4       $(commit_msg)      Z
 EOF
 	test_cmp expected actual
 '
@@ -251,11 +287,11 @@
 	git log --pretty="format:%><(1)%s" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	cat <<\EOF >expected &&
+	cat <<EOF >expected &&
 message two
 message one
 add bar
-initial
+$(commit_msg)
 EOF
 	test_cmp expected actual
 '
@@ -265,11 +301,11 @@
 	git log --pretty="format:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
 	# complete the incomplete line at the end
 	echo >>actual &&
-	cat <<\EOF >expected &&
+	cat <<EOF >expected &&
 short long  long long
 message ..   A U Thor
 add bar      A U Thor
-initial      A U Thor
+initial...   A U Thor
 EOF
 	test_cmp expected actual
 '
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
new file mode 100755
index 0000000..7665d67
--- /dev/null
+++ b/t/t4211-line-log.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='test log -L'
+. ./test-lib.sh
+
+test_expect_success 'setup (import history)' '
+	git fast-import < "$TEST_DIRECTORY"/t4211/history.export &&
+	git reset --hard
+'
+
+canned_test_1 () {
+	test_expect_$1 "$2" "
+		git log $2 >actual &&
+		test_cmp \"\$TEST_DIRECTORY\"/t4211/expect.$3 actual
+	"
+}
+
+canned_test () {
+	canned_test_1 success "$@"
+}
+canned_test_failure () {
+	canned_test_1 failure "$@"
+}
+
+test_bad_opts () {
+	test_expect_success "invalid args: $1" "
+		test_must_fail git log $1 2>errors &&
+		grep '$2' errors
+	"
+}
+
+canned_test "-L 4,12:a.c simple" simple-f
+canned_test "-L 4,+9:a.c simple" simple-f
+canned_test "-L '/long f/,/^}/:a.c' simple" simple-f
+canned_test "-L :f:a.c simple" simple-f-to-main
+
+canned_test "-L '/main/,/^}/:a.c' simple" simple-main
+canned_test "-L :main:a.c simple" simple-main-to-end
+
+canned_test "-L 1,+4:a.c simple" beginning-of-file
+
+canned_test "-L 20:a.c simple" end-of-file
+
+canned_test "-L '/long f/',/^}/:a.c -L /main/,/^}/:a.c simple" two-ranges
+canned_test "-L 24,+1:a.c simple" vanishes-early
+
+canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f
+canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main
+
+canned_test "-L 4,12:a.c -L :main:a.c simple" multiple
+canned_test "-L 4,18:a.c -L :main:a.c simple" multiple-overlapping
+canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
+canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
+canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
+
+test_bad_opts "-L" "switch.*requires a value"
+test_bad_opts "-L b.c" "argument.*not of the form"
+test_bad_opts "-L 1:" "argument.*not of the form"
+test_bad_opts "-L 1:nonexistent" "There is no path"
+test_bad_opts "-L 1:simple" "There is no path"
+test_bad_opts "-L '/foo:b.c'" "argument.*not of the form"
+test_bad_opts "-L 1000:b.c" "has only.*lines"
+test_bad_opts "-L 1,1000:b.c" "has only.*lines"
+test_bad_opts "-L :b.c" "argument.*not of the form"
+test_bad_opts "-L :foo:b.c" "no match"
+
+# There is a separate bug when an empty -L range is the first -L encountered,
+# thus to demonstrate this particular bug, the empty -L range must follow a
+# non-empty -L range.
+test_expect_success '-L {empty-range} (any -L)' '
+	n=$(expr $(wc -l <b.c) + 1) &&
+	git log -L1,1:b.c -L$n:b.c
+'
+
+test_expect_success '-L {empty-range} (first -L)' '
+	n=$(expr $(wc -l <b.c) + 1) &&
+	git log -L$n:b.c
+'
+
+test_done
diff --git a/t/t4211/expect.beginning-of-file b/t/t4211/expect.beginning-of-file
new file mode 100644
index 0000000..91b4054
--- /dev/null
+++ b/t/t4211/expect.beginning-of-file
@@ -0,0 +1,43 @@
+commit 4a23ae5c98d59a58c6da036156959f2dc9f472ad
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:47:40 2013 +0100
+
+    change at very beginning
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -1,3 +1,4 @@
++#include <unistd.h>
+ #include <stdio.h>
+ 
+ long f(long x)
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -1,3 +1,3 @@
+ #include <stdio.h>
+ 
+-int f(int x)
++long f(long x)
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +1,3 @@
++#include <stdio.h>
++
++int f(int x)
diff --git a/t/t4211/expect.end-of-file b/t/t4211/expect.end-of-file
new file mode 100644
index 0000000..bd25bb2
--- /dev/null
+++ b/t/t4211/expect.end-of-file
@@ -0,0 +1,62 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -20,3 +20,5 @@
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -20,3 +20,3 @@
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -19,3 +19,3 @@
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +18,3 @@
++	printf("%d\n", f(15));
++	return 0;
++}
diff --git a/t/t4211/expect.move-support-f b/t/t4211/expect.move-support-f
new file mode 100644
index 0000000..c905e01
--- /dev/null
+++ b/t/t4211/expect.move-support-f
@@ -0,0 +1,80 @@
+commit 6ce3c4ff690136099bb17e1a8766b75764726ea7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:49:50 2013 +0100
+
+    another simple change
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -4,9 +4,9 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+-		x >>= 1;
++		x /= 2;
+ 		s++;
+ 	}
+ 	return s;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
diff --git a/t/t4211/expect.multiple b/t/t4211/expect.multiple
new file mode 100644
index 0000000..76ad5b5
--- /dev/null
+++ b/t/t4211/expect.multiple
@@ -0,0 +1,104 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
diff --git a/t/t4211/expect.multiple-overlapping b/t/t4211/expect.multiple-overlapping
new file mode 100644
index 0000000..d930b6e
--- /dev/null
+++ b/t/t4211/expect.multiple-overlapping
@@ -0,0 +1,187 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * This is only an example!
+  */
+ 
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * This is only an example!
+  */
+ 
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ 
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+ int main ()
+ {
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,18 +3,19 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+ int main ()
+ {
+ 	printf("%d\n", f(15));
+ 	return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,18 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++	printf("%d\n", f(15));
++	return 0;
++}
diff --git a/t/t4211/expect.multiple-superset b/t/t4211/expect.multiple-superset
new file mode 100644
index 0000000..d930b6e
--- /dev/null
+++ b/t/t4211/expect.multiple-superset
@@ -0,0 +1,187 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * This is only an example!
+  */
+ 
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * This is only an example!
+  */
+ 
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ 
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+ int main ()
+ {
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,18 +3,19 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+ int main ()
+ {
+ 	printf("%d\n", f(15));
+ 	return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,18 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++	printf("%d\n", f(15));
++	return 0;
++}
diff --git a/t/t4211/expect.parallel-change-f-to-main b/t/t4211/expect.parallel-change-f-to-main
new file mode 100644
index 0000000..052def8
--- /dev/null
+++ b/t/t4211/expect.parallel-change-f-to-main
@@ -0,0 +1,160 @@
+commit 0469c60bc4837d52d97b1f081dec5f98dea20fed
+Merge: ba227c6 6ce3c4f
+Author: Thomas Rast <trast@inf.ethz.ch>
+Date:   Fri Apr 12 16:16:24 2013 +0200
+
+    Merge across the rename
+
+
+commit 6ce3c4ff690136099bb17e1a8766b75764726ea7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:49:50 2013 +0100
+
+    another simple change
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -4,14 +4,14 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+-		x >>= 1;
++		x /= 2;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * This is only an example!
+  */
+ 
+
+commit ba227c6632349700fbb957dec2b50f5e2358be3f
+Author: Thomas Rast <trast@inf.ethz.ch>
+Date:   Fri Apr 12 16:15:57 2013 +0200
+
+    change on another line of history while rename happens
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,14 +4,14 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+- * This is only an example!
++ * This is only a short example!
+  */
+ 
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ 
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,13 +3,14 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,13 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
++
++/*
++ * A comment.
++ */
++
diff --git a/t/t4211/expect.simple-f b/t/t4211/expect.simple-f
new file mode 100644
index 0000000..a1f5bc4
--- /dev/null
+++ b/t/t4211/expect.simple-f
@@ -0,0 +1,59 @@
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
diff --git a/t/t4211/expect.simple-f-to-main b/t/t4211/expect.simple-f-to-main
new file mode 100644
index 0000000..a475768
--- /dev/null
+++ b/t/t4211/expect.simple-f-to-main
@@ -0,0 +1,100 @@
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+ long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ 
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,13 +3,14 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+ 
+ /*
+  * A comment.
+  */
+ 
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,13 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
++
++/*
++ * A comment.
++ */
++
diff --git a/t/t4211/expect.simple-main b/t/t4211/expect.simple-main
new file mode 100644
index 0000000..39ce39b
--- /dev/null
+++ b/t/t4211/expect.simple-main
@@ -0,0 +1,68 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++	printf("%d\n", f(15));
++	return 0;
++}
diff --git a/t/t4211/expect.simple-main-to-end b/t/t4211/expect.simple-main-to-end
new file mode 100644
index 0000000..8480bd9
--- /dev/null
+++ b/t/t4211/expect.simple-main-to-end
@@ -0,0 +1,70 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++	printf("%d\n", f(15));
++	return 0;
++}
diff --git a/t/t4211/expect.two-ranges b/t/t4211/expect.two-ranges
new file mode 100644
index 0000000..6109aa0
--- /dev/null
+++ b/t/t4211/expect.two-ranges
@@ -0,0 +1,102 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
+\ No newline at end of file
++}
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+ 	printf("%ld\n", f(15));
+ 	return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
+ 	return s;
+ }
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-	printf("%d\n", f(15));
++	printf("%ld\n", f(15));
+ 	return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+ 	int s = 0;
+ 	while (x) {
+ 		x >>= 1;
+ 		s++;
+ 	}
++	return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++	int s = 0;
++	while (x) {
++		x >>= 1;
++		s++;
++	}
++}
diff --git a/t/t4211/expect.vanishes-early b/t/t4211/expect.vanishes-early
new file mode 100644
index 0000000..1f7cd06
--- /dev/null
+++ b/t/t4211/expect.vanishes-early
@@ -0,0 +1,39 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -22,1 +24,1 @@
+-}
+\ No newline at end of file
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -22,1 +22,1 @@
+-}
++}
+\ No newline at end of file
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +20,1 @@
++}
diff --git a/t/t4211/history.export b/t/t4211/history.export
new file mode 100644
index 0000000..f9f41e2
--- /dev/null
+++ b/t/t4211/history.export
@@ -0,0 +1,406 @@
+blob
+mark :1
+data 157
+#include <stdio.h>
+
+int f(int x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+	printf("%d\n", f(15));
+	return 0;
+}
+
+reset refs/tags/simple
+commit refs/tags/simple
+mark :2
+author Thomas Rast <trast@student.ethz.ch> 1362044688 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044688 +0100
+data 8
+initial
+M 100644 :1 a.c
+
+blob
+mark :3
+data 168
+#include <stdio.h>
+
+int f(int x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+	printf("%d\n", f(15));
+	return 0;
+}
+
+commit refs/tags/simple
+mark :4
+author Thomas Rast <trast@student.ethz.ch> 1362044695 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044695 +0100
+data 11
+change f()
+from :2
+M 100644 :3 a.c
+
+blob
+mark :5
+data 171
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+commit refs/tags/simple
+mark :6
+author Thomas Rast <trast@student.ethz.ch> 1362044716 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044716 +0100
+data 21
+touch both functions
+from :4
+M 100644 :5 a.c
+
+blob
+mark :7
+data 185
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+commit refs/tags/simple
+mark :8
+author Thomas Rast <trast@student.ethz.ch> 1362044741 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044741 +0100
+data 14
+touch comment
+from :6
+M 100644 :7 a.c
+
+blob
+mark :9
+data 205
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+commit refs/tags/simple
+mark :10
+author Thomas Rast <trast@student.ethz.ch> 1362044860 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044860 +0100
+data 25
+change at very beginning
+from :8
+M 100644 :9 a.c
+
+blob
+mark :11
+data 204
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+commit refs/tags/simple
+mark :12
+author Thomas Rast <trast@student.ethz.ch> 1362044890 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044890 +0100
+data 36
+change to an incomplete line at end
+from :10
+M 100644 :11 a.c
+
+blob
+mark :13
+data 238
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/tags/simple
+mark :14
+author Thomas Rast <trast@student.ethz.ch> 1362044923 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044923 +0100
+data 29
+change back to complete line
+from :12
+M 100644 :13 a.c
+
+commit refs/tags/move-support
+mark :15
+author Thomas Rast <trast@student.ethz.ch> 1362044968 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044968 +0100
+data 10
+move file
+from :14
+D a.c
+M 100644 :13 b.c
+
+blob
+mark :16
+data 237
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x /= 2;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/tags/move-support
+mark :17
+author Thomas Rast <trast@student.ethz.ch> 1362044990 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044990 +0100
+data 22
+another simple change
+from :15
+M 100644 :16 b.c
+
+blob
+mark :18
+data 254
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x);
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+/* incomplete lines are bad! */
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x /= 2;
+		s++;
+	}
+	return s;
+}
+
+commit refs/heads/master
+mark :19
+author Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
+data 21
+move within the file
+from :17
+M 100644 :18 b.c
+
+blob
+mark :20
+data 243
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x >>= 1;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only a short example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/heads/parallel-change
+mark :21
+author Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200
+data 55
+change on another line of history while rename happens
+from :14
+M 100644 :20 a.c
+
+blob
+mark :22
+data 242
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+	int s = 0;
+	while (x) {
+		x /= 2;
+		s++;
+	}
+	return s;
+}
+
+/*
+ * This is only a short example!
+ */
+
+int main ()
+{
+	printf("%ld\n", f(15));
+	return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/heads/parallel-change
+mark :23
+author Thomas Rast <trast@inf.ethz.ch> 1365776184 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1365776191 +0200
+data 24
+Merge across the rename
+from :21
+merge :17
+D a.c
+M 100644 :22 b.c
+
+reset refs/heads/parallel-change
+from :23
+
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 3fbd366..c2023b1 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -30,10 +30,76 @@
 
 SUBSTFORMAT=%H%n
 
+test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '
+	(
+		mkdir pax &&
+		cd pax &&
+		"$TAR" xf "$TEST_DIRECTORY"/t5000/pax.tar &&
+		test -f PaxHeaders.1791/file
+	)
+'
+
+get_pax_header() {
+	file=$1
+	header=$2=
+
+	while read len rest
+	do
+		if test "$len" = $(echo "$len $rest" | wc -c)
+		then
+			case "$rest" in
+			$header*)
+				echo "${rest#$header}"
+				;;
+			esac
+		fi
+	done <"$file"
+}
+
+check_tar() {
+	tarfile=$1.tar
+	listfile=$1.lst
+	dir=$1
+	dir_with_prefix=$dir/$2
+
+	test_expect_success ' extract tar archive' '
+		(mkdir $dir && cd $dir && "$TAR" xf -) <$tarfile
+	'
+
+	test_expect_success TAR_NEEDS_PAX_FALLBACK ' interpret pax headers' '
+		(
+			cd $dir &&
+			for header in *.paxheader
+			do
+				data=${header%.paxheader}.data &&
+				if test -h $data -o -e $data
+				then
+					path=$(get_pax_header $header path) &&
+					if test -n "$path"
+					then
+						mv "$data" "$path"
+					fi
+				fi
+			done
+		)
+	'
+
+	test_expect_success ' validate filenames' '
+		(cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+		test_cmp a.lst $listfile
+	'
+
+	test_expect_success ' validate file contents' '
+		diff -r a ${dir_with_prefix}a
+	'
+}
+
 test_expect_success \
     'populate workdir' \
-    'mkdir a b c &&
+    'mkdir a &&
      echo simple textfile >a/a &&
+     ten=0123456789 && hundred=$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten &&
+     echo long filename >a/four$hundred &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
      printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
@@ -62,6 +128,12 @@
      git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
      git commit-tree $treeid </dev/null)'
 
+test_expect_success 'setup export-subst' '
+	echo "substfile?" export-subst >>.git/info/attributes &&
+	git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+		>a/substfile1
+'
+
 test_expect_success \
     'create bare clone' \
     'git clone --bare . bare.git &&
@@ -75,13 +147,19 @@
     'git archive' \
     'git archive HEAD >b.tar'
 
-test_expect_success \
-    'git tar-tree' \
-    'git tar-tree HEAD >b2.tar'
+check_tar b
 
-test_expect_success \
-    'git archive vs. git tar-tree' \
-    'test_cmp b.tar b2.tar'
+test_expect_success 'git archive --prefix=prefix/' '
+	git archive --prefix=prefix/ HEAD >with_prefix.tar
+'
+
+check_tar with_prefix prefix/
+
+test_expect_success 'git-archive --prefix=olde-' '
+	git archive --prefix=olde- HEAD >with_olde-prefix.tar
+'
+
+check_tar with_olde-prefix olde-
 
 test_expect_success 'git archive on large files' '
     test_config core.bigfilethreshold 1 &&
@@ -118,66 +196,14 @@
     'git get-tar-commit-id <b.tar >b.commitid &&
      test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
 
-test_expect_success \
-    'extract tar archive' \
-    '(cd b && "$TAR" xf -) <b.tar'
-
-test_expect_success \
-    'validate filenames' \
-    '(cd b/a && find .) | sort >b.lst &&
-     test_cmp a.lst b.lst'
-
-test_expect_success \
-    'validate file contents' \
-    'diff -r a b/a'
-
-test_expect_success \
-    'git tar-tree with prefix' \
-    'git tar-tree HEAD prefix >c.tar'
-
-test_expect_success \
-    'extract tar archive with prefix' \
-    '(cd c && "$TAR" xf -) <c.tar'
-
-test_expect_success \
-    'validate filenames with prefix' \
-    '(cd c/prefix/a && find .) | sort >c.lst &&
-     test_cmp a.lst c.lst'
-
-test_expect_success \
-    'validate file contents with prefix' \
-    'diff -r a c/prefix/a'
-
-test_expect_success \
-    'create archives with substfiles' \
-    '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 &&
-     mv .git/info/attributes.before .git/info/attributes'
-
-test_expect_success \
-    'extract substfiles' \
-    '(mkdir f && cd f && "$TAR" xf -) <f.tar'
-
-test_expect_success \
-     'validate substfile contents' \
-     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
-      >f/a/substfile1.expected &&
-      test_cmp f/a/substfile1.expected f/a/substfile1 &&
-      test_cmp a/substfile2 f/a/substfile2
+test_expect_success 'git tar-tree' '
+	git tar-tree HEAD >tar-tree.tar &&
+	test_cmp b.tar tar-tree.tar
 '
 
-test_expect_success \
-    'extract substfiles from archive with prefix' \
-    '(mkdir g && cd g && "$TAR" xf -) <g.tar'
-
-test_expect_success \
-     'validate substfile contents from archive with prefix' \
-     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
-      >g/prefix/a/substfile1.expected &&
-      test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
-      test_cmp a/substfile2 g/prefix/a/substfile2
+test_expect_success 'git tar-tree with prefix' '
+	git tar-tree HEAD prefix >tar-tree_with_prefix.tar &&
+	test_cmp with_prefix.tar tar-tree_with_prefix.tar
 '
 
 test_expect_success 'git archive with --output, override inferred format' '
@@ -197,18 +223,6 @@
 	test_must_fail git archive --remote=. $sha1 >remote.tar
 '
 
-test_expect_success 'git-archive --prefix=olde-' '
-	git archive --prefix=olde- >h.tar HEAD &&
-	(
-		mkdir h &&
-		cd h &&
-		"$TAR" xf - <../h.tar
-	) &&
-	test -d h/olde-a &&
-	test -d h/olde-a/bin &&
-	test -f h/olde-a/bin/sh
-'
-
 test_expect_success 'setup tar filters' '
 	git config tar.tar.foo.command "tr ab ba" &&
 	git config tar.bar.command "tr ab ba" &&
diff --git a/t/t5000/pax.tar b/t/t5000/pax.tar
new file mode 100644
index 0000000..d911737
--- /dev/null
+++ b/t/t5000/pax.tar
Binary files differ
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index 4e7b05d..c72f71e 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -37,7 +37,7 @@
 
 test_expect_success \
     'populate workdir' \
-    'mkdir a b c &&
+    'mkdir a &&
      echo simple textfile >a/a &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index 8d1bbd3..67f3b54 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -27,6 +27,21 @@
 	test_cmp expect actual
 }
 
+
+# bsdtar/libarchive versions before 3.1.3 consider a tar file with a
+# global pax header that is not followed by a file record as corrupt.
+if "$TAR" tf "$TEST_DIRECTORY"/t5004/empty-with-pax-header.tar >/dev/null 2>&1
+then
+	test_set_prereq HEADER_ONLY_TAR_OK
+fi
+
+test_expect_success HEADER_ONLY_TAR_OK 'tar archive of commit with empty tree' '
+	git archive --format=tar HEAD >empty-with-pax-header.tar &&
+	make_dir extract &&
+	"$TAR" xf empty-with-pax-header.tar -C extract &&
+	check_dir extract
+'
+
 test_expect_success 'tar archive of empty tree is empty' '
 	git archive --format=tar HEAD: >empty.tar &&
 	perl -e "print \"\\0\" x 10240" >10knuls.tar &&
diff --git a/t/t5004/empty-with-pax-header.tar b/t/t5004/empty-with-pax-header.tar
new file mode 100644
index 0000000..da9e39e
--- /dev/null
+++ b/t/t5004/empty-with-pax-header.tar
Binary files differ
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
index 432f98c..1afa0d5 100755
--- a/t/t5150-request-pull.sh
+++ b/t/t5150-request-pull.sh
@@ -80,12 +80,12 @@
 
 	cat <<-EOT >fuzz.sed
 	#!/bin/sed -nf
+	s/$downstream_url_for_sed/URL/g
 	s/$_x40/OBJECT_NAME/g
 	s/A U Thor/AUTHOR/g
 	s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
 	s/        [^ ].*/        SUBJECT/g
 	s/  [^ ].* (DATE)/  SUBJECT (DATE)/g
-	s/$downstream_url_for_sed/URL/g
 	s/for-upstream/BRANCH/g
 	s/mnemonic.txt/FILENAME/g
 	s/^version [0-9]/VERSION/
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 5b1250f..35926de 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -51,7 +51,7 @@
     ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
     ofs=$(($ofs + $2)) &&
     chmod +w ${pack}.pack &&
-    dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
+    dd of=${pack}.pack bs=1 conv=notrunc seek=$ofs &&
     test_must_fail git verify-pack ${pack}.pack
 }
 
@@ -276,6 +276,33 @@
      git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
+    'corruption of delta base reference pointing to wrong object' \
+    'create_new_pack --delta-base-offset &&
+     git prune-packed &&
+     printf "\220\033" | do_corrupt_object $blob_3 2 &&
+     git cat-file blob $blob_1 >/dev/null &&
+     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_3 &&
+     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 --no-reuse-delta &&
+     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 \
     'corrupting header to have too small output buffer fails unpack' \
     'create_new_pack &&
      git prune-packed &&
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index baa670c..ea2e0d4 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -31,8 +31,8 @@
 }
 
 verify_hook_input () {
-	test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
-	test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
+	test_cmp expected.args "$TRASH_DIRECTORY"/post-rewrite.args &&
+	test_cmp expected.data "$TRASH_DIRECTORY"/post-rewrite.data
 }
 
 test_expect_success 'git commit --amend' '
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index d574085..fd2598e 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -135,6 +135,13 @@
 	test "`git --git-dir=shallow0/.git rev-list --count HEAD`" = 1
 '
 
+test_expect_success 'clone shallow depth 1 with fsck' '
+	git config --global fetch.fsckobjects true &&
+	git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0fsck &&
+	test "`git --git-dir=shallow0fsck/.git rev-list --count HEAD`" = 1 &&
+	git config --global --unset fetch.fsckobjects
+'
+
 test_expect_success 'clone shallow' '
 	git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow
 '
@@ -373,6 +380,20 @@
 	test_cmp count8.expected count8.actual
 '
 
+test_expect_success 'fetch in shallow repo unreachable shallow objects' '
+	(
+		git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog &&
+		git clone --depth 1 "file://$(pwd)/no-reflog" shallow9 &&
+		cd no-reflog &&
+		git tag -d TAGB1 TAGB2 &&
+		git update-ref refs/heads/B B~~ &&
+		git gc --prune=now &&
+		cd ../shallow9 &&
+		git fetch origin &&
+		git fsck --no-dangling
+	)
+'
+
 test_expect_success 'setup tests for the --stdin parameter' '
 	for head in C D E F
 	do
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index dd10ff0..8f6e392 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -42,107 +42,104 @@
 }
 
 test_expect_success setup '
-
 	setup_repository one &&
 	setup_repository two &&
 	(
-		cd two && git branch another
+		cd two &&
+		git branch another
 	) &&
 	git clone one test
-
 '
 
 test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
-(
-	cd test &&
-	tokens_match origin "$(git remote)" &&
-	check_remote_track origin master side &&
-	check_tracking_branch origin HEAD master side
-)
+	(
+		cd test &&
+		tokens_match origin "$(git remote)" &&
+		check_remote_track origin master side &&
+		check_tracking_branch origin HEAD master side
+	)
 '
 
 test_expect_success 'add another remote' '
-(
-	cd test &&
-	git remote add -f second ../two &&
-	tokens_match "origin second" "$(git remote)" &&
-	check_tracking_branch second master side another &&
-	git for-each-ref "--format=%(refname)" refs/remotes |
-	sed -e "/^refs\/remotes\/origin\//d" \
-	    -e "/^refs\/remotes\/second\//d" >actual &&
-	>expect &&
-	test_cmp expect actual
-)
+	(
+		cd test &&
+		git remote add -f second ../two &&
+		tokens_match "origin second" "$(git remote)" &&
+		check_tracking_branch second master side another &&
+		git for-each-ref "--format=%(refname)" refs/remotes |
+		sed -e "/^refs\/remotes\/origin\//d" \
+		    -e "/^refs\/remotes\/second\//d" >actual &&
+		>expect &&
+		test_cmp expect actual
+	)
 '
 
-test_expect_success C_LOCALE_OUTPUT 'check remote tracking' '
-(
-	cd test &&
-	check_remote_track origin master side &&
-	check_remote_track second master side another
-)
+test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
+	(
+		cd test &&
+		check_remote_track origin master side &&
+		check_remote_track second master side another
+	)
 '
 
 test_expect_success 'remote forces tracking branches' '
-(
-	cd test &&
-	case `git config remote.second.fetch` in
-	+*) true ;;
-	 *) false ;;
-	esac
-)
+	(
+		cd test &&
+		case `git config remote.second.fetch` in
+		+*) true ;;
+		 *) false ;;
+		esac
+	)
 '
 
 test_expect_success 'remove remote' '
-(
-	cd test &&
-	git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
-	git remote rm second
-)
+	(
+		cd test &&
+		git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
+		git remote rm second
+	)
 '
 
 test_expect_success C_LOCALE_OUTPUT 'remove remote' '
-(
-	cd test &&
-	tokens_match origin "$(git remote)" &&
-	check_remote_track origin master side &&
-	git for-each-ref "--format=%(refname)" refs/remotes |
-	sed -e "/^refs\/remotes\/origin\//d" >actual &&
-	>expect &&
-	test_cmp expect actual
-)
+	(
+		cd test &&
+		tokens_match origin "$(git remote)" &&
+		check_remote_track origin master side &&
+		git for-each-ref "--format=%(refname)" refs/remotes |
+		sed -e "/^refs\/remotes\/origin\//d" >actual &&
+		>expect &&
+		test_cmp expect actual
+	)
 '
 
 test_expect_success 'remove remote protects local branches' '
-(
-	cd test &&
-	{ cat >expect1 <<EOF
-Note: A branch outside the refs/remotes/ hierarchy was not removed;
-to delete it, use:
-  git branch -d master
-EOF
-	} &&
-	{ cat >expect2 <<EOF
-Note: Some branches outside the refs/remotes/ hierarchy 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 remove 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_i18ncmp expect1 actual1 &&
-	test_i18ncmp expect2 actual2
-)
+	(
+		cd test &&
+		cat >expect1 <<-\EOF &&
+		Note: A branch outside the refs/remotes/ hierarchy was not removed;
+		to delete it, use:
+		  git branch -d master
+		EOF
+		cat >expect2 <<-\EOF &&
+		Note: Some branches outside the refs/remotes/ hierarchy 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 remove 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_i18ncmp expect1 actual1 &&
+		test_i18ncmp expect2 actual2
+	)
 '
 
-cat > test/expect << EOF
+cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one
   Push  URL: $(pwd)/one
@@ -172,36 +169,40 @@
 EOF
 
 test_expect_success 'show' '
-	(cd test &&
-	 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 --add remote.two.pushurl ../three &&
-	 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 --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_i18ncmp expect output)
+	(
+		cd test &&
+		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 --add remote.two.pushurl ../three &&
+		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 --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_i18ncmp expect output
+	)
 '
 
-cat > test/expect << EOF
+cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one
   Push  URL: $(pwd)/one
@@ -219,152 +220,187 @@
 EOF
 
 test_expect_success 'show -n' '
-	(mv one one.unreachable &&
-	 cd test &&
-	 git remote show -n origin > output &&
-	 mv ../one.unreachable ../one &&
-	 test_i18ncmp expect output)
-'
-
-test_expect_success 'prune' '
-	(cd one &&
-	 git branch -m side side2) &&
-	(cd test &&
-	 git fetch origin &&
-	 git remote prune origin &&
-	 git rev-parse refs/remotes/origin/side2 &&
-	 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
+	mv one one.unreachable &&
+	(
+		cd test &&
+		git remote show -n origin >output &&
+		mv ../one.unreachable ../one &&
+		test_i18ncmp expect output
 	)
 '
 
-cat >test/expect <<EOF
+test_expect_success 'prune' '
+	(
+		cd one &&
+		git branch -m side side2
+	) &&
+	(
+		cd test &&
+		git fetch origin &&
+		git remote prune origin &&
+		git rev-parse refs/remotes/origin/side2 &&
+		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_i18ncmp expect output)
+	(
+		cd test &&
+		test_must_fail git remote set-head --auto two >output 2>&1 &&
+		test_i18ncmp expect output
+	)
 '
 
-cat >test/expect <<EOF
+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)
+	(
+		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
+cat >test/expect <<EOF
 Pruning origin
 URL: $(pwd)/one
  * [would prune] origin/side2
 EOF
 
 test_expect_success 'prune --dry-run' '
-	(cd one &&
-	 git branch -m side2 side) &&
-	(cd test &&
-	 git remote prune --dry-run origin > output &&
-	 git rev-parse refs/remotes/origin/side2 &&
-	 test_must_fail git rev-parse refs/remotes/origin/side &&
-	(cd ../one &&
-	 git branch -m side side2) &&
-	 test_i18ncmp expect output)
+	(
+		cd one &&
+		git branch -m side2 side) &&
+	(
+		cd test &&
+		git remote prune --dry-run origin >output &&
+		git rev-parse refs/remotes/origin/side2 &&
+		test_must_fail git rev-parse refs/remotes/origin/side &&
+	(
+		cd ../one &&
+		git branch -m side side2) &&
+		test_i18ncmp expect output
+	)
 '
 
 test_expect_success 'add --mirror && prune' '
-	(mkdir mirror &&
-	 cd mirror &&
-	 git init --bare &&
-	 git remote add --mirror -f origin ../one) &&
-	(cd one &&
-	 git branch -m side2 side) &&
-	(cd mirror &&
-	 git rev-parse --verify refs/heads/side2 &&
-	 test_must_fail git rev-parse --verify refs/heads/side &&
-	 git fetch origin &&
-	 git remote prune origin &&
-	 test_must_fail git rev-parse --verify refs/heads/side2 &&
-	 git rev-parse --verify refs/heads/side)
+	mkdir mirror &&
+	(
+		cd mirror &&
+		git init --bare &&
+		git remote add --mirror -f origin ../one
+	) &&
+	(
+		cd one &&
+		git branch -m side2 side
+	) &&
+	(
+		cd mirror &&
+		git rev-parse --verify refs/heads/side2 &&
+		test_must_fail git rev-parse --verify refs/heads/side &&
+		git fetch origin &&
+		git remote prune origin &&
+		test_must_fail git rev-parse --verify refs/heads/side2 &&
+		git rev-parse --verify refs/heads/side
+	)
 '
 
 test_expect_success 'add --mirror=fetch' '
 	mkdir mirror-fetch &&
 	git init mirror-fetch/parent &&
-	(cd mirror-fetch/parent &&
-	 test_commit one) &&
+	(
+		cd mirror-fetch/parent &&
+		test_commit one
+	) &&
 	git init --bare mirror-fetch/child &&
-	(cd mirror-fetch/child &&
-	 git remote add --mirror=fetch -f parent ../parent)
+	(
+		cd mirror-fetch/child &&
+		git remote add --mirror=fetch -f parent ../parent
+	)
 '
 
 test_expect_success 'fetch mirrors act as mirrors during fetch' '
-	(cd mirror-fetch/parent &&
-	 git branch new &&
-	 git branch -m master renamed
+	(
+		cd mirror-fetch/parent &&
+		git branch new &&
+		git branch -m master renamed
 	) &&
-	(cd mirror-fetch/child &&
-	 git fetch parent &&
-	 git rev-parse --verify refs/heads/new &&
-	 git rev-parse --verify refs/heads/renamed
+	(
+		cd mirror-fetch/child &&
+		git fetch parent &&
+		git rev-parse --verify refs/heads/new &&
+		git rev-parse --verify refs/heads/renamed
 	)
 '
 
 test_expect_success 'fetch mirrors can prune' '
-	(cd mirror-fetch/child &&
-	 git remote prune parent &&
-	 test_must_fail git rev-parse --verify refs/heads/master
+	(
+		cd mirror-fetch/child &&
+		git remote prune parent &&
+		test_must_fail git rev-parse --verify refs/heads/master
 	)
 '
 
 test_expect_success 'fetch mirrors do not act as mirrors during push' '
-	(cd mirror-fetch/parent &&
-	 git checkout HEAD^0
+	(
+		cd mirror-fetch/parent &&
+		git checkout HEAD^0
 	) &&
-	(cd mirror-fetch/child &&
-	 git branch -m renamed renamed2 &&
-	 git push parent :
+	(
+		cd mirror-fetch/child &&
+		git branch -m renamed renamed2 &&
+		git push parent :
 	) &&
-	(cd mirror-fetch/parent &&
-	 git rev-parse --verify renamed &&
-	 test_must_fail git rev-parse --verify refs/heads/renamed2
+	(
+		cd mirror-fetch/parent &&
+		git rev-parse --verify renamed &&
+		test_must_fail git rev-parse --verify refs/heads/renamed2
 	)
 '
 
 test_expect_success 'add fetch mirror with specific branches' '
 	git init --bare mirror-fetch/track &&
-	(cd mirror-fetch/track &&
-	 git remote add --mirror=fetch -t heads/new parent ../parent
+	(
+		cd mirror-fetch/track &&
+		git remote add --mirror=fetch -t heads/new parent ../parent
 	)
 '
 
 test_expect_success 'fetch mirror respects specific branches' '
-	(cd mirror-fetch/track &&
-	 git fetch parent &&
-	 git rev-parse --verify refs/heads/new &&
-	 test_must_fail git rev-parse --verify refs/heads/renamed
+	(
+		cd mirror-fetch/track &&
+		git fetch parent &&
+		git rev-parse --verify refs/heads/new &&
+		test_must_fail git rev-parse --verify refs/heads/renamed
 	)
 '
 
@@ -372,60 +408,72 @@
 	mkdir mirror-push &&
 	git init --bare mirror-push/public &&
 	git init mirror-push/private &&
-	(cd mirror-push/private &&
-	 test_commit one &&
-	 git remote add --mirror=push public ../public
+	(
+		cd mirror-push/private &&
+		test_commit one &&
+		git remote add --mirror=push public ../public
 	)
 '
 
 test_expect_success 'push mirrors act as mirrors during push' '
-	(cd mirror-push/private &&
-	 git branch new &&
-	 git branch -m master renamed &&
-	 git push public
+	(
+		cd mirror-push/private &&
+		git branch new &&
+		git branch -m master renamed &&
+		git push public
 	) &&
-	(cd mirror-push/private &&
-	 git rev-parse --verify refs/heads/new &&
-	 git rev-parse --verify refs/heads/renamed &&
-	 test_must_fail git rev-parse --verify refs/heads/master
+	(
+		cd mirror-push/private &&
+		git rev-parse --verify refs/heads/new &&
+		git rev-parse --verify refs/heads/renamed &&
+		test_must_fail git rev-parse --verify refs/heads/master
 	)
 '
 
 test_expect_success 'push mirrors do not act as mirrors during fetch' '
-	(cd mirror-push/public &&
-	 git branch -m renamed renamed2 &&
-	 git symbolic-ref HEAD refs/heads/renamed2
+	(
+		cd mirror-push/public &&
+		git branch -m renamed renamed2 &&
+		git symbolic-ref HEAD refs/heads/renamed2
 	) &&
-	(cd mirror-push/private &&
-	 git fetch public &&
-	 git rev-parse --verify refs/heads/renamed &&
-	 test_must_fail git rev-parse --verify refs/heads/renamed2
+	(
+		cd mirror-push/private &&
+		git fetch public &&
+		git rev-parse --verify refs/heads/renamed &&
+		test_must_fail git rev-parse --verify refs/heads/renamed2
 	)
 '
 
 test_expect_success 'push mirrors do not allow you to specify refs' '
 	git init mirror-push/track &&
-	(cd mirror-push/track &&
-	 test_must_fail git remote add --mirror=push -t new public ../public
+	(
+		cd mirror-push/track &&
+		test_must_fail git remote add --mirror=push -t new public ../public
 	)
 '
 
 test_expect_success 'add alt && prune' '
-	(mkdir alttst &&
-	 cd alttst &&
-	 git init &&
-	 git remote add -f origin ../one &&
-	 git config remote.alt.url ../one &&
-	 git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") &&
-	(cd one &&
-	 git branch -m side side2) &&
-	(cd alttst &&
-	 git rev-parse --verify refs/remotes/origin/side &&
-	 test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
-	 git fetch alt &&
-	 git remote prune alt &&
-	 test_must_fail git rev-parse --verify refs/remotes/origin/side &&
-	 git rev-parse --verify refs/remotes/origin/side2)
+	mkdir alttst &&
+	(
+		cd alttst &&
+		git init &&
+		git remote add -f origin ../one &&
+		git config remote.alt.url ../one &&
+		git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*"
+	) &&
+	(
+		cd one &&
+		git branch -m side side2
+	) &&
+	(
+		cd alttst &&
+		git rev-parse --verify refs/remotes/origin/side &&
+		test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
+		git fetch alt &&
+		git remote prune alt &&
+		test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+		git rev-parse --verify refs/remotes/origin/side2
+	)
 '
 
 cat >test/expect <<\EOF
@@ -433,20 +481,24 @@
 EOF
 
 test_expect_success 'add with reachable tags (default)' '
-	(cd one &&
-	 >foobar &&
-	 git add foobar &&
-	 git commit -m "Foobar" &&
-	 git tag -a -m "Foobar tag" foobar-tag &&
-	 git reset --hard HEAD~1 &&
-	 git tag -a -m "Some tag" some-tag) &&
-	(mkdir add-tags &&
-	 cd add-tags &&
-	 git init &&
-	 git remote add -f origin ../one &&
-	 git tag -l some-tag >../test/output &&
-	 git tag -l foobar-tag >>../test/output &&
-	 test_must_fail git config remote.origin.tagopt) &&
+	(
+		cd one &&
+		>foobar &&
+		git add foobar &&
+		git commit -m "Foobar" &&
+		git tag -a -m "Foobar tag" foobar-tag &&
+		git reset --hard HEAD~1 &&
+		git tag -a -m "Some tag" some-tag
+	) &&
+	mkdir add-tags &&
+	(
+		cd add-tags &&
+		git init &&
+		git remote add -f origin ../one &&
+		git tag -l some-tag >../test/output &&
+		git tag -l foobar-tag >>../test/output &&
+		test_must_fail git config remote.origin.tagopt
+	) &&
 	test_cmp test/expect test/output
 '
 
@@ -457,14 +509,16 @@
 EOF
 
 test_expect_success 'add --tags' '
-	(rm -rf add-tags &&
-	 mkdir add-tags &&
-	 cd add-tags &&
-	 git init &&
-	 git remote add -f --tags origin ../one &&
-	 git tag -l some-tag >../test/output &&
-	 git tag -l foobar-tag >>../test/output &&
-	 git config remote.origin.tagopt >>../test/output) &&
+	rm -rf add-tags &&
+	(
+		mkdir add-tags &&
+		cd add-tags &&
+		git init &&
+		git remote add -f --tags origin ../one &&
+		git tag -l some-tag >../test/output &&
+		git tag -l foobar-tag >>../test/output &&
+		git config remote.origin.tagopt >>../test/output
+	) &&
 	test_cmp test/expect test/output
 '
 
@@ -473,25 +527,31 @@
 EOF
 
 test_expect_success 'add --no-tags' '
-	(rm -rf add-tags &&
-	 mkdir add-no-tags &&
-	 cd add-no-tags &&
-	 git init &&
-	 git remote add -f --no-tags origin ../one &&
-	 git tag -l some-tag >../test/output &&
-	 git tag -l foobar-tag >../test/output &&
-	 git config remote.origin.tagopt >>../test/output) &&
-	(cd one &&
-	 git tag -d some-tag foobar-tag) &&
+	rm -rf add-tags &&
+	(
+		mkdir add-no-tags &&
+		cd add-no-tags &&
+		git init &&
+		git remote add -f --no-tags origin ../one &&
+		git tag -l some-tag >../test/output &&
+		git tag -l foobar-tag >../test/output &&
+		git config remote.origin.tagopt >>../test/output
+	) &&
+	(
+		cd one &&
+		git tag -d some-tag foobar-tag
+	) &&
 	test_cmp test/expect test/output
 '
 
 test_expect_success 'reject --no-no-tags' '
-	(cd add-no-tags &&
-	 test_must_fail git remote add -f --no-no-tags neworigin ../one)
+	(
+		cd add-no-tags &&
+		test_must_fail git remote add -f --no-no-tags neworigin ../one
+	)
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   apis/master
   apis/side
   drosophila/another
@@ -500,17 +560,17 @@
 EOF
 
 test_expect_success 'update' '
-
-	(cd one &&
-	 git remote add drosophila ../two &&
-	 git remote add apis ../mirror &&
-	 git remote update &&
-	 git branch -r > output &&
-	 test_cmp expect output)
-
+	(
+		cd one &&
+		git remote add drosophila ../two &&
+		git remote add apis ../mirror &&
+		git remote update &&
+		git branch -r >output &&
+		test_cmp expect output
+	)
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   drosophila/another
   drosophila/master
   drosophila/side
@@ -521,34 +581,40 @@
 EOF
 
 test_expect_success 'update with arguments' '
-
-	(cd one &&
-	 for b in $(git branch -r)
-	 do
+	(
+		cd one &&
+		for b in $(git branch -r)
+		do
 		git branch -r -d $b || break
-	 done &&
-	 git remote add manduca ../mirror &&
-	 git remote add megaloprepus ../mirror &&
-	 git config remotes.phobaeticus "drosophila megaloprepus" &&
-	 git config remotes.titanus manduca &&
-	 git remote update phobaeticus titanus &&
-	 git branch -r > output &&
-	 test_cmp expect output)
-
+		done &&
+		git remote add manduca ../mirror &&
+		git remote add megaloprepus ../mirror &&
+		git config remotes.phobaeticus "drosophila megaloprepus" &&
+		git config remotes.titanus manduca &&
+		git remote update phobaeticus titanus &&
+		git branch -r >output &&
+		test_cmp expect output
+	)
 '
 
 test_expect_success 'update --prune' '
-
-	(cd one &&
-	 git branch -m side2 side3) &&
-	(cd test &&
-	 git remote update --prune &&
-	 (cd ../one && git branch -m side3 side2) &&
-	 git rev-parse refs/remotes/origin/side3 &&
-	 test_must_fail git rev-parse refs/remotes/origin/side2)
+	(
+		cd one &&
+		git branch -m side2 side3
+	) &&
+	(
+		cd test &&
+		git remote update --prune &&
+		(
+			cd ../one &&
+			git branch -m side3 side2
+		) &&
+		git rev-parse refs/remotes/origin/side3 &&
+		test_must_fail git rev-parse refs/remotes/origin/side2
+	)
 '
 
-cat > one/expect << EOF
+cat >one/expect <<-\EOF
   apis/master
   apis/side
   manduca/master
@@ -558,176 +624,204 @@
 EOF
 
 test_expect_success 'update default' '
-
-	(cd one &&
-	 for b in $(git branch -r)
-	 do
+	(
+		cd one &&
+		for b in $(git branch -r)
+		do
 		git branch -r -d $b || break
-	 done &&
-	 git config remote.drosophila.skipDefaultUpdate true &&
-	 git remote update default &&
-	 git branch -r > output &&
-	 test_cmp expect output)
-
+		done &&
+		git config remote.drosophila.skipDefaultUpdate true &&
+		git remote update default &&
+		git branch -r >output &&
+		test_cmp expect output
+	)
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   drosophila/another
   drosophila/master
   drosophila/side
 EOF
 
 test_expect_success 'update default (overridden, with funny whitespace)' '
-
-	(cd one &&
-	 for b in $(git branch -r)
-	 do
+	(
+		cd one &&
+		for b in $(git branch -r)
+		do
 		git branch -r -d $b || break
-	 done &&
-	 git config remotes.default "$(printf "\t drosophila  \n")" &&
-	 git remote update default &&
-	 git branch -r > output &&
-	 test_cmp expect output)
-
+		done &&
+		git config remotes.default "$(printf "\t drosophila  \n")" &&
+		git remote update default &&
+		git branch -r >output &&
+		test_cmp expect output
+	)
 '
 
 test_expect_success 'update (with remotes.default defined)' '
-
-	(cd one &&
-	 for b in $(git branch -r)
-	 do
+	(
+		cd one &&
+		for b in $(git branch -r)
+		do
 		git branch -r -d $b || break
-	 done &&
-	 git config remotes.default "drosophila" &&
-	 git remote update &&
-	 git branch -r > output &&
-	 test_cmp expect output)
-
+		done &&
+		git config remotes.default "drosophila" &&
+		git remote update &&
+		git branch -r >output &&
+		test_cmp expect output
+	)
 '
 
 test_expect_success '"remote show" does not show symbolic refs' '
-
 	git clone one three &&
-	(cd three &&
-	 git remote show origin > output &&
-	 ! grep "^ *HEAD$" < output &&
-	 ! grep -i stale < output)
-
+	(
+		cd three &&
+		git remote show origin >output &&
+		! grep "^ *HEAD$" < output &&
+		! grep -i stale < output
+	)
 '
 
 test_expect_success 'reject adding remote with an invalid name' '
-
 	test_must_fail git remote add some:url desired-name
-
 '
 
 # 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")
-
+	(
+		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"
+	)
 '
 
 test_expect_success 'rename does not update a non-default fetch refspec' '
-
 	git clone one four.one &&
-	(cd four.one &&
-	 git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
-	 git remote rename origin upstream &&
-	 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
-	 git rev-parse -q origin/master)
-
+	(
+		cd four.one &&
+		git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
+		git remote rename origin upstream &&
+		test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
+		git rev-parse -q origin/master
+	)
 '
 
 test_expect_success 'rename a remote with name part of fetch spec' '
-
 	git clone one four.two &&
-	(cd four.two &&
-	 git remote rename origin remote &&
-	 git remote rename remote upstream &&
-	 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*")
-
+	(
+		cd four.two &&
+		git remote rename origin remote &&
+		git remote rename remote upstream &&
+		test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*"
+	)
 '
 
 test_expect_success 'rename a remote with name prefix of other remote' '
-
 	git clone one four.three &&
-	(cd four.three &&
-	 git remote add o git://example.com/repo.git &&
-	 git remote rename o upstream &&
-	 test "$(git rev-parse origin/master)" = "$(git rev-parse master)")
-
+	(
+		cd four.three &&
+		git remote add o git://example.com/repo.git &&
+		git remote rename o upstream &&
+		test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+	)
 '
 
-cat > remotes_origin << EOF
+cat >remotes_origin <<EOF
 URL: $(pwd)/one
 Push: refs/heads/master:refs/heads/upstream
+Push: refs/heads/next:refs/heads/upstream2
 Pull: refs/heads/master:refs/heads/origin
+Pull: refs/heads/next:refs/heads/origin2
 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 remove 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")
+	(
+		cd five &&
+		git remote remove origin &&
+		mkdir -p .git/remotes &&
+		cat ../remotes_origin >.git/remotes/origin &&
+		git remote rename origin origin &&
+		test_path_is_missing .git/remotes/origin &&
+		test "$(git config remote.origin.url)" = "$origin_url" &&
+		cat >push_expected <<-\EOF &&
+		refs/heads/master:refs/heads/upstream
+		refs/heads/next:refs/heads/upstream2
+		EOF
+		cat >fetch_expected <<-\EOF &&
+		refs/heads/master:refs/heads/origin
+		refs/heads/next:refs/heads/origin2
+		EOF
+		git config --get-all remote.origin.push >push_actual &&
+		git config --get-all remote.origin.fetch >fetch_actual &&
+		test_cmp push_expected push_actual &&
+		test_cmp fetch_expected fetch_actual
+	)
 '
 
 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")
+	(
+		cd six &&
+		git remote rm origin &&
+		echo "$origin_url" >.git/branches/origin &&
+		git remote rename origin origin &&
+		test_path_is_missing .git/branches/origin &&
+		test "$(git config remote.origin.url)" = "$origin_url" &&
+		test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin" &&
+		test "$(git config remote.origin.push)" = "HEAD:refs/heads/master"
+	)
+'
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
+	git clone one seven &&
+	(
+		cd seven &&
+		git remote rm origin &&
+		echo "quux#foom" > .git/branches/origin &&
+		git remote rename origin origin &&
+		test_path_is_missing .git/branches/origin &&
+		test "$(git config remote.origin.url)" = "quux" &&
+		test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+		test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
+	)
 '
 
 test_expect_success 'remote prune to cause a dangling symref' '
-	git clone one seven &&
+	git clone one eight &&
 	(
 		cd one &&
 		git checkout side2 &&
 		git branch -D master
 	) &&
 	(
-		cd seven &&
+		cd eight &&
 		git remote prune origin
 	) >err 2>&1 &&
 	test_i18ngrep "has become dangling" err &&
 
 	: And the dangling symref will not cause other annoying errors &&
 	(
-		cd seven &&
+		cd eight &&
 		git branch -a
 	) 2>err &&
 	! grep "points nowhere" err &&
 	(
-		cd seven &&
+		cd eight &&
 		test_must_fail git branch nomore origin
 	) 2>err &&
 	grep "dangling symref" err
 '
 
 test_expect_success 'show empty remote' '
-
 	test_create_repo empty &&
 	git clone empty empty-clone &&
 	(
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index d7a19a1..fde6891 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -370,30 +370,39 @@
 
 '
 
-test_expect_success 'explicit fetch should not update tracking' '
+test_expect_success 'mark initial state of origin/master' '
+	(
+		cd three &&
+		git tag base-origin-master refs/remotes/origin/master
+	)
+'
+
+test_expect_success 'explicit fetch should update tracking' '
 
 	cd "$D" &&
 	git branch -f side &&
 	(
 		cd three &&
+		git update-ref refs/remotes/origin/master base-origin-master &&
 		o=$(git rev-parse --verify refs/remotes/origin/master) &&
 		git fetch origin master &&
 		n=$(git rev-parse --verify refs/remotes/origin/master) &&
-		test "$o" = "$n" &&
+		test "$o" != "$n" &&
 		test_must_fail git rev-parse --verify refs/remotes/origin/side
 	)
 '
 
-test_expect_success 'explicit pull should not update tracking' '
+test_expect_success 'explicit pull should update tracking' '
 
 	cd "$D" &&
 	git branch -f side &&
 	(
 		cd three &&
+		git update-ref refs/remotes/origin/master base-origin-master &&
 		o=$(git rev-parse --verify refs/remotes/origin/master) &&
 		git pull origin master &&
 		n=$(git rev-parse --verify refs/remotes/origin/master) &&
-		test "$o" = "$n" &&
+		test "$o" != "$n" &&
 		test_must_fail git rev-parse --verify refs/remotes/origin/side
 	)
 '
@@ -404,6 +413,7 @@
 	git branch -f side &&
 	(
 		cd three &&
+		git update-ref refs/remotes/origin/master base-origin-master &&
 		o=$(git rev-parse --verify refs/remotes/origin/master) &&
 		git fetch origin &&
 		n=$(git rev-parse --verify refs/remotes/origin/master) &&
@@ -412,6 +422,22 @@
 	)
 '
 
+test_expect_success 'non-matching refspecs do not confuse tracking update' '
+	cd "$D" &&
+	git update-ref refs/odd/location HEAD &&
+	(
+		cd three &&
+		git update-ref refs/remotes/origin/master base-origin-master &&
+		git config --add remote.origin.fetch \
+			refs/odd/location:refs/remotes/origin/odd &&
+		o=$(git rev-parse --verify refs/remotes/origin/master) &&
+		git fetch origin master &&
+		n=$(git rev-parse --verify refs/remotes/origin/master) &&
+		test "$o" != "$n" &&
+		test_must_fail git rev-parse --verify refs/remotes/origin/odd
+	)
+'
+
 test_expect_success 'pushing nonexistent branch by mistake should not segv' '
 
 	cd "$D" &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 6af6c63..ed4d9c8 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -57,6 +57,35 @@
 	)
 '
 
+test_expect_success 'pulling into void does not overwrite staged files' '
+	git init cloned-staged-colliding &&
+	(
+		cd cloned-staged-colliding &&
+		echo "alternate content" >file &&
+		git add file &&
+		test_must_fail git pull .. master &&
+		echo "alternate content" >expect &&
+		test_cmp expect file &&
+		git cat-file blob :file >file.index &&
+		test_cmp expect file.index
+	)
+'
+
+
+test_expect_success 'pulling into void does not remove new staged files' '
+	git init cloned-staged-new &&
+	(
+		cd cloned-staged-new &&
+		echo "new tracked file" >newfile &&
+		git add newfile &&
+		git pull .. master &&
+		echo "new tracked file" >expect &&
+		test_cmp expect newfile &&
+		git cat-file blob :newfile >newfile.index &&
+		test_cmp expect newfile.index
+	)
+'
+
 test_expect_success 'test . as a remote' '
 
 	git branch copy master &&
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index aa31abe..453aba5 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -15,19 +15,19 @@
 	mkdir clonedq &&
 	(cd clonedq && git init &&
 	git pull -q "../parent" >out 2>err &&
-	test ! -s err &&
-	test ! -s out)
+	test_must_be_empty err &&
+	test_must_be_empty out)
 '
 
 test_expect_success 'git pull -q --rebase' '
 	mkdir clonedqrb &&
 	(cd clonedqrb && git init &&
 	git pull -q --rebase "../parent" >out 2>err &&
-	test ! -s err &&
-	test ! -s out &&
+	test_must_be_empty err &&
+	test_must_be_empty out &&
 	git pull -q --rebase "../parent" >out 2>err &&
-	test ! -s err &&
-	test ! -s out)
+	test_must_be_empty err &&
+	test_must_be_empty out)
 '
 
 test_expect_success 'git pull' '
@@ -35,7 +35,7 @@
 	(cd cloned && git init &&
 	git pull "../parent" >out 2>err &&
 	test -s err &&
-	test ! -s out)
+	test_must_be_empty out)
 '
 
 test_expect_success 'git pull --rebase' '
@@ -43,7 +43,7 @@
 	(cd clonedrb && git init &&
 	git pull --rebase "../parent" >out 2>err &&
 	test -s err &&
-	test ! -s out)
+	test_must_be_empty out)
 '
 
 test_expect_success 'git pull -v' '
@@ -51,7 +51,7 @@
 	(cd clonedv && git init &&
 	git pull -v "../parent" >out 2>err &&
 	test -s err &&
-	test ! -s out)
+	test_must_be_empty out)
 '
 
 test_expect_success 'git pull -v --rebase' '
@@ -59,22 +59,22 @@
 	(cd clonedvrb && git init &&
 	git pull -v --rebase "../parent" >out 2>err &&
 	test -s err &&
-	test ! -s out)
+	test_must_be_empty out)
 '
 
 test_expect_success 'git pull -v -q' '
 	mkdir clonedvq &&
 	(cd clonedvq && git init &&
 	git pull -v -q "../parent" >out 2>err &&
-	test ! -s out &&
-	test ! -s err)
+	test_must_be_empty out &&
+	test_must_be_empty err)
 '
 
 test_expect_success 'git pull -q -v' '
 	mkdir clonedqv &&
 	(cd clonedqv && git init &&
 	git pull -q -v "../parent" >out 2>err &&
-	test ! -s out &&
+	test_must_be_empty out &&
 	test -s err)
 '
 
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index 4736da8..6a5ac3a 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -15,17 +15,19 @@
 
 # $1 = local revision
 # $2 = remote revision (tested to be equal to the local one)
+# $3 = [optional] repo to check for actual output (repo1 by default)
 check_pushed_commit () {
 	git log -1 --format='%h %s' "$1" >expect &&
-	git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+	git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual &&
 	test_cmp expect actual
 }
 
 # $1 = push.default value
 # $2 = expected target branch for the push
+# $3 = [optional] repo to check for actual output (repo1 by default)
 test_push_success () {
 	git -c push.default="$1" push &&
-	check_pushed_commit HEAD "$2"
+	check_pushed_commit HEAD "$2" "$3"
 }
 
 # $1 = push.default value
@@ -37,6 +39,26 @@
 	test_cmp expect actual
 }
 
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+test_pushdefault_workflow () {
+	workflow=central
+	pushdefault=parent1
+	if test -n "${4-}"; then
+		workflow=triangular
+		pushdefault=parent2
+	fi
+	test_expect_success "push.default = $2 $1 in $workflow workflows" "
+		test_config branch.master.remote parent1 &&
+		test_config branch.master.merge refs/heads/foo &&
+		test_config remote.pushdefault $pushdefault &&
+		test_commit commit-for-$2${4+-triangular} &&
+		test_push_$1 $2 $3 ${4+repo2}
+	"
+}
+
 test_expect_success '"upstream" pushes to configured upstream' '
 	git checkout master &&
 	test_config branch.master.remote parent1 &&
@@ -48,7 +70,6 @@
 test_expect_success '"upstream" does not push on unconfigured remote' '
 	git checkout master &&
 	test_unconfig branch.master.remote &&
-	test_config push.default upstream &&
 	test_commit three &&
 	test_push_failure upstream
 '
@@ -57,7 +78,6 @@
 	git checkout master &&
 	test_config branch.master.remote parent1 &&
 	test_unconfig branch.master.merge &&
-	test_config push.default upstream
 	test_commit four &&
 	test_push_failure upstream
 '
@@ -115,4 +135,41 @@
 	test_cmp expect-other-name actual-other-name
 '
 
+# We are on 'master', which integrates with 'foo' from parent1
+# remote (set in test_pushdefault_workflow helper).  Push to
+# parent1 in centralized, and push to parent2 in triangular workflow.
+# The parent1 repository has 'master' and 'foo' branches, while
+# the parent2 repository has only 'master' branch.
+#
+# test_pushdefault_workflow() arguments:
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+
+# update parent1's master (which is not our upstream)
+test_pushdefault_workflow success current master
+
+# update parent1's foo (which is our upstream)
+test_pushdefault_workflow success upstream foo
+
+# upsream is foo which is not the name of the current branch
+test_pushdefault_workflow failure simple master
+
+# master and foo are updated
+test_pushdefault_workflow success matching master
+
+# master is updated
+test_pushdefault_workflow success current master triangular
+
+# upstream mode cannot be used in triangular
+test_pushdefault_workflow failure upstream foo triangular
+
+# in triangular, 'simple' works as 'current' and update the branch
+# with the same name.
+test_pushdefault_workflow success simple master triangular
+
+# master is updated (parent2 does not have foo)
+test_pushdefault_workflow success matching master triangular
+
 test_done
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
index b23efbb..55a866a 100755
--- a/t/t5551-http-fetch.sh
+++ b/t/t5551-http-fetch.sh
@@ -209,13 +209,17 @@
 
 	# now assign tags to all the dangling commits we created above
 	tag=$("$PERL_PATH" -e "print \"bla\" x 30") &&
-	sed -e "s/^:\(.\+\) \(.\+\)$/\2 refs\/tags\/$tag-\1/" <marks >>packed-refs
+	sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
 	)
 '
 
 test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
 	git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
-	test_line_count = 0 err
+	test_line_count = 0 err &&
+	(
+		cd too-many-refs &&
+		test $(git for-each-ref refs/tags | wc -l) = 50000
+	)
 '
 
 stop_httpd
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
index ef98d95..9be9ae3 100755
--- a/t/t5560-http-backend-noserver.sh
+++ b/t/t5560-http-backend-noserver.sh
@@ -5,7 +5,7 @@
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
 
-test_have_prereq MINGW && export GREP_OPTIONS=-U
+test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U
 
 run_backend() {
 	echo "$2" |
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 67869b4..0629149 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -280,4 +280,9 @@
 	test_cmp fetch.expected fetch.actual
 '
 
+test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
+	cp -R src "foo:bar" &&
+	git clone "./foo:bar" foobar
+'
+
 test_done
diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh
index 02cb024..85cadfa 100755
--- a/t/t5702-clone-options.sh
+++ b/t/t5702-clone-options.sh
@@ -22,7 +22,7 @@
 test_expect_success 'redirected clone' '
 
 	git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
-	test ! -s err
+	test_must_be_empty err
 
 '
 test_expect_success 'redirected clone -v' '
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index 8956c21..5a6e49d 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -58,7 +58,13 @@
 git clone -l -s D E &&
 git clone -l -s E F &&
 git clone -l -s F G &&
-test_must_fail git clone --bare -l -s G H'
+git clone --bare -l -s G H'
+
+test_expect_success 'invalidity of deepest repository' \
+'cd H && {
+	test_valid_repo
+	test $? -ne 0
+}'
 
 cd "$base_dir"
 
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index dbb02e2..8c4c539 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -8,11 +8,6 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
-if ! type "${BASH-bash}" >/dev/null 2>&1; then
-	skip_all='skipping remote-testgit tests, bash not available'
-	test_done
-fi
-
 compare_refs() {
 	git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
 	git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
@@ -101,39 +96,28 @@
 
 test_expect_success 'cloning without refspec' '
 	GIT_REMOTE_TESTGIT_REFSPEC="" \
-	git clone "testgit::${PWD}/server" local2 &&
+	git clone "testgit::${PWD}/server" local2 2>error &&
+	grep "This remote helper should implement refspec capability" error &&
 	compare_refs local2 HEAD server HEAD
 '
 
 test_expect_success 'pulling without refspecs' '
 	(cd local2 &&
 	git reset --hard &&
-	GIT_REMOTE_TESTGIT_REFSPEC="" git pull) &&
+	GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+	grep "This remote helper should implement refspec capability" error &&
 	compare_refs local2 HEAD server HEAD
 '
 
-test_expect_failure 'pushing without refspecs' '
+test_expect_success 'pushing without refspecs' '
 	test_when_finished "(cd local2 && git reset --hard origin)" &&
 	(cd local2 &&
 	echo content >>file &&
 	git commit -a -m ten &&
-	GIT_REMOTE_TESTGIT_REFSPEC="" git push) &&
-	compare_refs local2 HEAD server HEAD
-'
-
-test_expect_success 'pulling with straight refspec' '
-	(cd local2 &&
-	GIT_REMOTE_TESTGIT_REFSPEC="*:*" git pull) &&
-	compare_refs local2 HEAD server HEAD
-'
-
-test_expect_failure 'pushing with straight refspec' '
-	test_when_finished "(cd local2 && git reset --hard origin)" &&
-	(cd local2 &&
-	echo content >>file &&
-	git commit -a -m eleven &&
-	GIT_REMOTE_TESTGIT_REFSPEC="*:*" git push) &&
-	compare_refs local2 HEAD server HEAD
+	GIT_REMOTE_TESTGIT_REFSPEC="" &&
+	export GIT_REMOTE_TESTGIT_REFSPEC &&
+	test_must_fail git push 2>../error) &&
+	grep "remote-helper doesn.t support push; refspec needed" error
 '
 
 test_expect_success 'pulling without marks' '
@@ -186,6 +170,50 @@
 	compare_refs local signed-tag-2 server signed-tag-2
 '
 
+test_expect_success 'push update refs' '
+	(cd local &&
+	git checkout -b update master &&
+	echo update >>file &&
+	git commit -a -m update &&
+	git push origin update &&
+	git rev-parse --verify remotes/origin/update >expect &&
+	git rev-parse --verify testgit/origin/heads/update >actual &&
+	test_cmp expect actual
+	)
+'
+
+test_expect_success 'push update refs failure' '
+	(cd local &&
+	git checkout update &&
+	echo "update fail" >>file &&
+	git commit -a -m "update fail" &&
+	git rev-parse --verify testgit/origin/heads/update >expect &&
+	GIT_REMOTE_TESTGIT_PUSH_ERROR="non-fast forward" &&
+	export GIT_REMOTE_TESTGIT_PUSH_ERROR &&
+	test_expect_code 1 git push origin update &&
+	git rev-parse --verify testgit/origin/heads/update >actual &&
+	test_cmp expect actual
+	)
+'
+
+test_expect_success 'proper failure checks for fetching' '
+	(GIT_REMOTE_TESTGIT_FAILURE=1 &&
+	export GIT_REMOTE_TESTGIT_FAILURE &&
+	cd local &&
+	test_must_fail git fetch 2> error &&
+	cat error &&
+	grep -q "Error while running fast-import" error
+	)
+'
+
+test_expect_success 'proper failure checks for pushing' '
+	(GIT_REMOTE_TESTGIT_FAILURE=1 &&
+	export GIT_REMOTE_TESTGIT_FAILURE &&
+	cd local &&
+	test_must_fail git push --all
+	)
+'
+
 test_expect_success 'push messages' '
 	(cd local &&
 	git checkout -b new_branch master &&
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index fb07536..43ad772 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -39,25 +39,25 @@
 date >path0
 git update-index --add path0
 save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4
 git update-ref HEAD $(tag l5)
 
 
@@ -90,29 +90,29 @@
 #     F
 
 
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
-on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
-on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
-on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
-on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
-on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
-on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
-on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
-on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
-on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
-on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
-on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
-on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
-on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
+on_committer_date "00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1
 
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
-on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
-on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
-on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
-on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
-on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
-on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
-on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+on_committer_date "00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
 
 test_sequence()
 {
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index e4c52b0..24d1836 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -16,39 +16,34 @@
 date >path0
 git update-index --add path0
 save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
-on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
-on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
-on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
-on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
-on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
-on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree
+on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root
+on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2
+on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1
+on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2
+on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4
+on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0
+on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_dates "00:25" "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
 
 
-#
-# note: as of 20/6, it isn't possible to create duplicate parents, so this
-# can't be tested.
-#
-#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
 hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
 save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
 save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
@@ -105,6 +100,50 @@
 root
 EOF
 
+test_output_expect_success 'simple date order' 'git rev-list --date-order  HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+b2
+c1
+b1
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order  HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+c3
+c2
+b3
+b2
+c1
+b1
+a3
+a2
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
 test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order  g4' <<EOF
 g4
 h2
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 0393c9f..9874403 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -1,20 +1,45 @@
 #!/bin/sh
 
+# Copyright (c) 2009 Jens Lehmann
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
+
 test_description='git rev-list --pretty=format test'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_tick
+# String "added" in German
+# (translated with Google Translate),
+# encoded in UTF-8, used as a commit log message below.
+added=$(printf "added (hinzugef\303\274gt) foo")
+added_iso88591=$(echo "$added" | iconv -f utf-8 -t iso8859-1)
+# same but "changed"
+changed=$(printf "changed (ge\303\244ndert) foo")
+changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t iso8859-1)
+
 test_expect_success 'setup' '
-touch foo && git add foo && git commit -m "added foo" &&
-  echo changed >foo && git commit -a -m "changed foo"
+	: >foo &&
+	git add foo &&
+	git config i18n.commitEncoding iso8859-1 &&
+	git commit -m "$added_iso88591" &&
+	head1=$(git rev-parse --verify HEAD) &&
+	head1_short=$(git rev-parse --verify --short $head1) &&
+	tree1=$(git rev-parse --verify HEAD:) &&
+	tree1_short=$(git rev-parse --verify --short $tree1) &&
+	echo "$changed" > foo &&
+	git commit -a -m "$changed_iso88591" &&
+	head2=$(git rev-parse --verify HEAD) &&
+	head2_short=$(git rev-parse --verify --short $head2) &&
+	tree2=$(git rev-parse --verify HEAD:) &&
+	tree2_short=$(git rev-parse --verify --short $tree2)
+	git config --unset i18n.commitEncoding
 '
 
-# usage: test_format name format_string <expected_output
+# usage: test_format name format_string [failure] <expected_output
 test_format () {
 	cat >expect.$1
-	test_expect_success "format $1" "
+	test_expect_${3:-success} "format $1" "
 		git rev-list --pretty=format:'$2' master >output.$1 &&
 		test_cmp expect.$1 output.$1
 	"
@@ -32,49 +57,49 @@
 	test_cmp expect "$1"
 }
 
-test_format percent %%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format percent %%h <<EOF
+commit $head2
 %h
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 %h
 EOF
 
-test_format hash %H%n%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-131a310eb913d107dd3c09a65d1651175898735d
-131a310
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
+test_format hash %H%n%h <<EOF
+commit $head2
+$head2
+$head2_short
+commit $head1
+$head1
+$head1_short
 EOF
 
-test_format tree %T%n%t <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-fe722612f26da5064c32ca3843aa154bdb0b08a0
-fe72261
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-4d5fcadc293a348e88f777dc0920f11e7d71441c
-4d5fcad
+test_format tree %T%n%t <<EOF
+commit $head2
+$tree2
+$tree2_short
+commit $head1
+$tree1
+$tree1_short
 EOF
 
-test_format parents %P%n%p <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format parents %P%n%p <<EOF
+commit $head2
+$head1
+$head1_short
+commit $head1
 
 
 EOF
 
 # we don't test relative here
-test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format author %an%n%ae%n%ad%n%aD%n%at <<EOF
+commit $head2
 A U Thor
 author@example.com
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 A U Thor
 author@example.com
 Thu Apr 7 15:13:13 2005 -0700
@@ -82,14 +107,14 @@
 1112911993
 EOF
 
-test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<EOF
+commit $head2
 C O Mitter
 committer@example.com
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 C O Mitter
 committer@example.com
 Thu Apr 7 15:13:13 2005 -0700
@@ -97,43 +122,45 @@
 1112911993
 EOF
 
-test_format encoding %e <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format encoding %e <<EOF
+commit $head2
+iso8859-1
+commit $head1
+iso8859-1
 EOF
 
-test_format subject %s <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+test_format subject %s <<EOF
+commit $head2
+$changed
+commit $head1
+$added
 EOF
 
-test_format body %b <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format body %b <<EOF
+commit $head2
+commit $head1
 EOF
 
-test_format raw-body %B <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
+test_format raw-body %B <<EOF
+commit $head2
+$changed
 
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head1
+$added
 
 EOF
 
-test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF
+commit $head2
 foobarbazxyzzy
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 foobarbazxyzzy
 EOF
 
-test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF
+commit $head2
 foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 foo
 EOF
 
@@ -179,46 +206,71 @@
 	)
 '
 
-cat >commit-msg <<'EOF'
+iconv -f utf-8 -t iso8859-1 > commit-msg <<EOF
 Test printing of complex bodies
 
 This commit message is much longer than the others,
 and it will be encoded in iso8859-1. We should therefore
-include an iso8859 character: ¡bueno!
+include an iso8859 character: ¡bueno!
 EOF
+
 test_expect_success 'setup complex body' '
-git config i18n.commitencoding iso8859-1 &&
-  echo change2 >foo && git commit -a -F commit-msg
+	git config i18n.commitencoding iso8859-1 &&
+	echo change2 >foo && git commit -a -F commit-msg &&
+	head3=$(git rev-parse --verify HEAD) &&
+	head3_short=$(git rev-parse --short $head3)
 '
 
-test_format complex-encoding %e <<'EOF'
-commit 1ed88da4a5b5ed8c449114ac131efc62178734c3
+test_format complex-encoding %e <<EOF
+commit $head3
 iso8859-1
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head2
+iso8859-1
+commit $head1
+iso8859-1
 EOF
 
-test_format complex-subject %s <<'EOF'
-commit 1ed88da4a5b5ed8c449114ac131efc62178734c3
+test_format complex-subject %s <<EOF
+commit $head3
 Test printing of complex bodies
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head2
+$changed_iso88591
+commit $head1
+$added_iso88591
 EOF
 
-test_format complex-body %b <<'EOF'
-commit 1ed88da4a5b5ed8c449114ac131efc62178734c3
-This commit message is much longer than the others,
-and it will be encoded in iso8859-1. We should therefore
-include an iso8859 character: ¡bueno!
+test_expect_success 'prepare expected messages (for test %b)' '
+	cat <<-EOF >expected.utf-8 &&
+	commit $head3
+	This commit message is much longer than the others,
+	and it will be encoded in iso8859-1. We should therefore
+	include an iso8859 character: ¡bueno!
 
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+	commit $head2
+	commit $head1
+	EOF
+	iconv -f utf-8 -t iso8859-1 expected.utf-8 >expected.iso8859-1
+'
+
+test_format complex-body %b <expected.iso8859-1
+
+# Git uses i18n.commitEncoding if no i18n.logOutputEncoding set
+# so unset i18n.commitEncoding to test encoding conversion
+git config --unset i18n.commitEncoding
+
+test_format complex-subject-commitencoding-unset %s <<EOF
+commit $head3
+Test printing of complex bodies
+commit $head2
+$changed
+commit $head1
+$added
 EOF
 
+test_format complex-body-commitencoding-unset %b <expected.utf-8
+
 test_expect_success '%x00 shows NUL' '
-	echo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&
+	echo  >expect commit $head3 &&
 	echo >>expect fooQbar &&
 	git rev-list -1 --format=foo%x00bar HEAD >actual.nul &&
 	nul_to_q <actual.nul >actual &&
@@ -265,12 +317,12 @@
 
 test_expect_success 'add SP before non-empty (1)' '
 	git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual &&
-	test $(wc -w <actual) = 2
+	test $(wc -w <actual) = 3
 '
 
 test_expect_success 'add SP before non-empty (2)' '
 	git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual &&
-	test $(wc -w <actual) = 4
+	test $(wc -w <actual) = 6
 '
 
 test_expect_success '--abbrev' '
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index dd6dc84..57ce239 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -14,21 +14,24 @@
 
 test_expect_success setup '
 	echo "Hi there" >file &&
-	git add file &&
-	test_tick && git commit -m "Initial file" &&
+	echo "initial" >lost &&
+	git add file lost &&
+	test_tick && git commit -m "Initial file and lost" &&
 	note A &&
 
 	git branch other-branch &&
 
 	echo "Hello" >file &&
-	git add file &&
-	test_tick && git commit -m "Modified file" &&
+	echo "second" >lost &&
+	git add file lost &&
+	test_tick && git commit -m "Modified file and lost" &&
 	note B &&
 
 	git checkout other-branch &&
 
 	echo "Hello" >file &&
-	git add file &&
+	>lost &&
+	git add file lost &&
 	test_tick && git commit -m "Modified the file identically" &&
 	note C &&
 
@@ -37,7 +40,9 @@
 	test_tick && git commit -m "Add another file" &&
 	note D &&
 
-	test_tick && git merge -m "merge" master &&
+	test_tick &&
+	test_must_fail git merge -m "merge" master &&
+	>lost && git commit -a -m "merge" &&
 	note E &&
 
 	echo "Yet another" >elif &&
@@ -105,9 +110,21 @@
 check_result 'K I H E C B A' --full-history -- file
 check_result 'K I H E C B A' --full-history --topo-order -- file
 check_result 'K I H E C B A' --full-history --date-order -- file
-check_outcome failure 'I E C B A' --simplify-merges -- 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
 check_result 'H' --first-parent -- another-file
 
+check_result 'E C B A' --full-history E -- lost
+test_expect_success 'full history simplification without parent' '
+	printf "%s\n" E C B A >expect &&
+	git log --pretty="$FMT" --full-history E -- lost |
+	unnote >actual &&
+	sed -e "s/^.*	\([^ ]*\) .*/\1/" >check <actual &&
+	test_cmp expect check || {
+		cat actual
+		false
+	}
+'
+
 test_done
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index 39b4cb0..dabebae 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -13,6 +13,13 @@
 #
 #  D..M -- M.t                 == M
 #  --ancestry-path D..M -- M.t == M
+#
+#  F...I                 == F G H I
+#  --ancestry-path F...I == F H I
+#
+#  G..M -- G.t                 == [nothing - was dropped in "-s ours" merge L]
+#  --ancestry-path G..M -- G.t == L
+#  --ancestry-path --simplify-merges G^..M -- G.t == G L
 
 . ./test-lib.sh
 
@@ -63,13 +70,52 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'rev-list --ancestry-patch D..M -- M.t' '
+test_expect_success 'rev-list --ancestry-path D..M -- M.t' '
 	echo M >expect &&
 	git rev-list --ancestry-path --format=%s D..M -- M.t |
 	sed -e "/^commit /d" >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'rev-list F...I' '
+	for c in F G H I; do echo $c; done >expect &&
+	git rev-list --format=%s F...I |
+	sed -e "/^commit /d" |
+	sort >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path F...I' '
+	for c in F H I; do echo $c; done >expect &&
+	git rev-list --ancestry-path --format=%s F...I |
+	sed -e "/^commit /d" |
+	sort >actual &&
+	test_cmp expect actual
+'
+
+# G.t is dropped in an "-s ours" merge
+test_expect_success 'rev-list G..M -- G.t' '
+	>expect &&
+	git rev-list --format=%s G..M -- G.t |
+	sed -e "/^commit /d" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
+	echo L >expect &&
+	git rev-list --ancestry-path --format=%s G..M -- G.t |
+	sed -e "/^commit /d" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
+	for c in G L; do echo $c; done >expect &&
+	git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
+	sed -e "/^commit /d" |
+	sort >actual &&
+	test_cmp expect actual
+'
+
 #   b---bc
 #  / \ /
 # a   X
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 331b9b0..d15b313 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Fredrik Kuivinen
 #
 
-# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# See http://marc.info/?l=git&m=111463358500362&w=2 for a
 # nice description of what this is about.
 
 
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
index 2599ae5..9324ea4 100755
--- a/t/t6035-merge-dir-to-symlink.sh
+++ b/t/t6035-merge-dir-to-symlink.sh
@@ -3,7 +3,7 @@
 test_description='merging when a directory was replaced with a symlink'
 . ./test-lib.sh
 
-test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' '
+test_expect_success 'create a commit where dir a/b changed to symlink' '
 	mkdir -p a/b/c a/b-2/c &&
 	> a/b/c/d &&
 	> a/b-2/c/d &&
@@ -12,12 +12,12 @@
 	git commit -m base &&
 	git tag start &&
 	rm -rf a/b &&
-	ln -s b-2 a/b &&
 	git add -A &&
+	test_ln_s_add b-2 a/b &&
 	git commit -m "dir to symlink"
 '
 
-test_expect_success SYMLINKS 'checkout does not clobber untracked symlink' '
+test_expect_success 'checkout does not clobber untracked symlink' '
 	git checkout HEAD^0 &&
 	git reset --hard master &&
 	git rm --cached a/b &&
@@ -25,7 +25,7 @@
 	test_must_fail git checkout start^0
 '
 
-test_expect_success SYMLINKS 'a/b-2/c/d is kept when clobbering symlink b' '
+test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
 	git checkout HEAD^0 &&
 	git reset --hard master &&
 	git rm --cached a/b &&
@@ -34,14 +34,14 @@
 	test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'checkout should not have deleted a/b-2/c/d' '
+test_expect_success 'checkout should not have deleted a/b-2/c/d' '
 	git checkout HEAD^0 &&
 	git reset --hard master &&
 	 git checkout start^0 &&
 	 test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'setup for merge test' '
+test_expect_success 'setup for merge test' '
 	git reset --hard &&
 	test -f a/b-2/c/d &&
 	echo x > a/x &&
@@ -50,39 +50,51 @@
 	git tag baseline
 '
 
-test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s resolve master &&
-	test -h a/b &&
 	test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+	test -h a/b
+'
+
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s recursive master &&
-	test -h a/b &&
 	test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+	test -h a/b
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
 	git reset --hard &&
 	git checkout master^0 &&
 	git merge -s resolve baseline^0 &&
-	test -h a/b &&
 	test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+	test -h a/b
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
 	git reset --hard &&
 	git checkout master^0 &&
 	git merge -s recursive baseline^0 &&
-	test -h a/b &&
 	test -f a/b-2/c/d
 '
 
-test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+	test -h a/b
+'
+
+test_expect_failure 'do not lose untracked in merge (resolve)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	>a/b/c/e &&
@@ -91,7 +103,7 @@
 	test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' '
+test_expect_success 'do not lose untracked in merge (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	>a/b/c/e &&
@@ -100,52 +112,61 @@
 	test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'do not lose modifications in merge (resolve)' '
+test_expect_success 'do not lose modifications in merge (resolve)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	echo more content >>a/b/c/d &&
 	test_must_fail git merge -s resolve master
 '
 
-test_expect_success SYMLINKS 'do not lose modifications in merge (recursive)' '
+test_expect_success 'do not lose modifications in merge (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	echo more content >>a/b/c/d &&
 	test_must_fail git merge -s recursive master
 '
 
-test_expect_success SYMLINKS 'setup a merge where dir a/b-2 changed to symlink' '
+test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
 	git reset --hard &&
 	git checkout start^0 &&
 	rm -rf a/b-2 &&
-	ln -s b a/b-2 &&
 	git add -A &&
+	test_ln_s_add b a/b-2 &&
 	git commit -m "dir a/b-2 to symlink" &&
 	git tag test2
 '
 
-test_expect_success SYMLINKS 'merge should not have D/F conflicts (resolve)' '
+test_expect_success 'merge should not have D/F conflicts (resolve)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s resolve test2 &&
-	test -h a/b-2 &&
 	test -f a/b/c/d
 '
 
-test_expect_success SYMLINKS 'merge should not have D/F conflicts (recursive)' '
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+	test -h a/b-2
+'
+
+test_expect_success 'merge should not have D/F conflicts (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	git merge -s recursive test2 &&
-	test -h a/b-2 &&
 	test -f a/b/c/d
 '
 
-test_expect_success SYMLINKS 'merge should not have F/D conflicts (recursive)' '
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+	test -h a/b-2
+'
+
+test_expect_success 'merge should not have F/D conflicts (recursive)' '
 	git reset --hard &&
 	git checkout -b foo test2 &&
 	git merge -s recursive baseline^0 &&
-	test -h a/b-2 &&
 	test -f a/b/c/d
 '
 
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+	test -h a/b-2
+'
+
 test_done
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
new file mode 100755
index 0000000..88b84df
--- /dev/null
+++ b/t/t6111-rev-list-treesame.sh
@@ -0,0 +1,196 @@
+#!/bin/sh
+#
+#        ,---E--.   *H----------.             * marks !TREESAME parent paths
+#       /        \ /             \*
+# *A--*B---D--*F-*G---------K-*L-*M
+#   \     /*       \       /
+#    `-C-'          `-*I-*J
+#
+# A creates "file", B and F change it.
+# Odd merge G takes the old version from B.
+# I changes it, but J reverts it, so K is TREESAME to both parents.
+# H and L both change "file", and M merges those changes.
+
+test_description='TREESAME and limiting'
+
+. ./test-lib.sh
+
+note () {
+	git tag "$1"
+}
+
+unnote () {
+	git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\))\([ 	]\)|\1\2|g"
+}
+
+test_expect_success setup '
+	test_commit "Initial file" file "Hi there" A &&
+	git branch other-branch &&
+
+	test_commit "file=Hello" file "Hello" B &&
+	git branch third-branch &&
+
+	git checkout other-branch &&
+	test_commit "Added other" other "Hello" C &&
+
+	git checkout master &&
+	test_merge D other-branch &&
+
+	git checkout third-branch &&
+	test_commit "Third file" third "Nothing" E &&
+
+	git checkout master &&
+	test_commit "file=Blah" file "Blah" F &&
+
+	test_tick && git merge --no-commit third-branch &&
+	git checkout third-branch file &&
+	git commit &&
+	note G &&
+	git branch fiddler-branch &&
+
+	git checkout -b part2-branch &&
+	test_commit "file=Part 2" file "Part 2" H &&
+
+	git checkout fiddler-branch &&
+	test_commit "Bad commit" file "Silly" I &&
+
+	test_tick && git revert I && note J &&
+
+	git checkout master &&
+	test_tick && git merge --no-ff fiddler-branch &&
+	note K
+
+	test_commit "file=Part 1" file "Part 1" L &&
+
+	test_tick && test_must_fail git merge part2-branch &&
+	test_commit M file "Parts 1+2"
+'
+
+check_outcome () {
+	outcome=$1
+	shift
+
+	case "$1" in
+	*"("*)
+		FMT="%P	%H | %s"
+		munge_actual="
+			s/^\([^	]*\)	\([^ ]*\) .*/(\1)\2/
+			s/ //g
+			s/()//
+		"
+		;;
+	*)
+		FMT="%H | %s"
+		munge_actual="s/^\([^ ]*\) .*/\1/"
+		;;
+	esac &&
+	printf "%s\n" $1 >expect &&
+	shift
+
+	param="$*" &&
+	test_expect_$outcome "log $param" '
+		git log --format="$FMT" $param |
+		unnote >actual &&
+		sed -e "$munge_actual" <actual >check &&
+		test_cmp expect check || {
+			cat actual
+			false
+		}
+	'
+}
+
+check_result () {
+	check_outcome success "$@"
+}
+
+# Odd merge G drops a change in F. Important that G is listed in all
+# except the most basic list. Achieving this means normal merge D will also be
+# shown in normal full-history, as we can't distinguish unless we do a
+# simplification pass. After simplification, D is dropped but G remains.
+# Also, merge simplification of G should not drop the parent B that the default
+# simple history follows.
+check_result 'M L K J I H G F E D C B A'
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G (D)F (B)E (BC)D (A)C (A)B A'
+check_result 'M H L K J I G E F D C B A' --topo-order
+check_result 'M L H B A' -- file
+check_result '(LH)M (B)L (B)H (A)B A' --parents -- file
+check_result 'M L J I H G F D B A' --full-history -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G (D)F (BA)D (A)B A' --full-history --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G (B)F (A)B A' --simplify-merges -- file
+check_result 'M L K G F D B A' --first-parent
+check_result 'M L G F B A' --first-parent -- file
+
+# Check that odd merge G remains shown when F is the bottom.
+check_result 'M L K J I H G E' F..M
+check_result 'M H L K J I G E' F..M --topo-order
+check_result 'M L H' F..M -- file
+check_result '(LH)M (B)L (B)H' --parents F..M -- file
+check_result 'M L J I H G' F..M --full-history -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G' F..M --full-history --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G' F..M --simplify-merges -- file
+check_result 'M L K J I H G' F..M --ancestry-path
+check_result 'M L J I H G' F..M --ancestry-path -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G' F..M --ancestry-path --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FE)G' F..M --ancestry-path --simplify-merges -- file
+check_result 'M L K G' F..M --first-parent
+check_result 'M L G' F..M --first-parent -- file
+
+# Note that G is pruned when E is the bottom, even if it's the same commit list
+# If we want history since E, then we're quite happy to ignore G that took E.
+check_result 'M L K J I H G' E..M --ancestry-path
+check_result 'M L J I H' E..M --ancestry-path -- file
+check_result '(LH)M (K)L (EJ)K (I)J (E)I (E)H' E..M --ancestry-path --parents -- file
+check_result '(LH)M (E)H (J)L (I)J (E)I' E..M --ancestry-path --simplify-merges -- file
+
+# Should still be able to ignore I-J branch in simple log, despite limiting
+# to G.
+check_result 'M L K J I H' G..M
+check_result 'M H L K J I' G..M --topo-order
+check_result 'M L H' G..M -- file
+check_result '(LH)M (G)L (G)H' G..M --parents -- file
+check_result 'M L J I H' G..M --full-history -- file
+check_result 'M L K J I H' G..M --full-history --parents -- file
+check_result 'M H L J I' G..M --simplify-merges -- file
+check_result 'M L K J I H' G..M --ancestry-path
+check_result 'M L J I H' G..M --ancestry-path -- file
+check_result 'M L K J I H' G..M --ancestry-path --parents -- file
+check_result 'M H L J I' G..M --ancestry-path --simplify-merges -- file
+
+# B..F should be able to simplify the merge D from irrelevant side branch C.
+# Default log should also be free to follow B-D, and ignore C.
+# But --full-history shouldn't drop D on its own - without simplification,
+# we can't decide if the merge from INTERESTING commit C was sensible.
+check_result 'F D C' B..F
+check_result 'F' B..F -- file
+check_result '(B)F' B..F --parents -- file
+check_result 'F D' B..F --full-history -- file
+check_result '(D)F (BA)D' B..F --full-history --parents -- file
+check_result '(B)F' B..F --simplify-merges -- file
+check_result 'F D' B..F --ancestry-path
+check_result 'F' B..F --ancestry-path -- file
+check_result 'F' B..F --ancestry-path --parents -- file
+check_result 'F' B..F --ancestry-path --simplify-merges -- file
+check_result 'F D' B..F --first-parent
+check_result 'F' B..F --first-parent -- file
+
+# E...F should be equivalent to E F ^B, and be able to drop D as above.
+check_result 'F' E F ^B -- file # includes D
+check_result 'F' E...F -- file # includes D
+
+# Any sort of full history of C..F should show D, as it's the connection to C,
+# and it differs from it.
+check_result 'F D B' C..F
+check_result 'F B' C..F -- file
+check_result '(B)F (A)B' C..F --parents -- file
+check_result 'F D B' C..F --full-history -- file
+check_result '(D)F (BC)D (A)B' C..F --full-history --parents -- file
+check_result '(D)F (BC)D (A)B' C..F --simplify-merges -- file
+check_result 'F D' C..F --ancestry-path
+check_result 'F D' C..F --ancestry-path -- file
+check_result 'F D' C..F --ancestry-path --parents -- file
+check_result 'F D' C..F --ancestry-path --simplify-merges -- file
+check_result 'F D B' C..F --first-parent
+check_result 'F B' C..F --first-parent -- file
+
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index f67aa6f..c0e5b2a 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -110,6 +110,9 @@
 check_describe B-0-* --long HEAD^^2^
 check_describe A-3-* --long HEAD^^2
 
+check_describe c-7-* --tags
+check_describe e-3-* --first-parent --tags
+
 : >err.expect
 check_describe A --all A^0
 test_expect_success 'no warning was displayed for A' '
@@ -171,4 +174,28 @@
 
 check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
 
+test_expect_success 'name-rev with exact tags' '
+	echo A >expect &&
+	tag_object=$(git rev-parse refs/tags/A) &&
+	git name-rev --tags --name-only $tag_object >actual &&
+	test_cmp expect actual &&
+
+	echo "A^0" >expect &&
+	tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+	git name-rev --tags --name-only $tagged_commit >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'describe --contains with the exact tags' '
+	echo "A^0" >expect &&
+	tag_object=$(git rev-parse refs/tags/A) &&
+	git describe --contains $tag_object >actual &&
+	test_cmp expect actual &&
+
+	echo "A^0" >expect &&
+	tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+	git describe --contains $tagged_commit >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index a845b15..101816e 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -218,13 +218,13 @@
 
 rm -f dirty dirty2
 
-test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
+test_expect_success 'git mv should overwrite symlink to a file' '
 
 	rm -fr .git &&
 	git init &&
 	echo 1 >moved &&
-	ln -s moved symlink &&
-	git add moved symlink &&
+	test_ln_s_add moved symlink &&
+	git add moved &&
 	test_must_fail git mv moved symlink &&
 	git mv -f moved symlink &&
 	! test -e moved &&
@@ -237,22 +237,26 @@
 
 rm -f moved symlink
 
-test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' '
+test_expect_success 'git mv should overwrite file with a symlink' '
 
 	rm -fr .git &&
 	git init &&
 	echo 1 >moved &&
-	ln -s moved symlink &&
-	git add moved symlink &&
+	test_ln_s_add moved symlink &&
+	git add moved &&
 	test_must_fail git mv symlink moved &&
 	git mv -f symlink moved &&
 	! test -e symlink &&
-	test -h moved &&
 	git update-index --refresh &&
 	git diff-files --quiet
 
 '
 
+test_expect_success SYMLINKS 'check moved symlink' '
+
+	test -h moved
+'
+
 rm -f moved symlink
 
 test_done
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
index 8f3b54d..88d60c1 100755
--- a/t/t7011-skip-worktree-reading.sh
+++ b/t/t7011-skip-worktree-reading.sh
@@ -91,12 +91,12 @@
 	test_cmp expected 1
 '
 
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
 	setup_absent &&
 	test -z "$(git ls-files -d)"
 '
 
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
 	setup_dirty &&
 	test -z "$(git ls-files -d)"
 '
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index df82ec9..8d4b50d 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -9,6 +9,19 @@
 
 . ./test-lib.sh
 
+commit_msg () {
+	# String "modify 2nd file (changed)" partly in German
+	# (translated with Google Translate),
+	# encoded in UTF-8, used as a commit log message below.
+	msg="modify 2nd file (ge\303\244ndert)\n"
+	if test -n "$1"
+	then
+		printf "$msg" | iconv -f utf-8 -t "$1"
+	else
+		printf "$msg"
+	fi
+}
+
 test_expect_success 'creating initial files and commits' '
 	test_tick &&
 	echo "1st file" >first &&
@@ -28,7 +41,7 @@
 
 	echo "1st line 2nd file" >secondfile &&
 	echo "2nd line 2nd file" >>secondfile &&
-	git commit -a -m "modify 2nd file" &&
+	git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
 	head5=$(git rev-parse --verify HEAD)
 '
 # git log --pretty=oneline # to see those SHA1 involved
@@ -44,6 +57,20 @@
 	done | test_cmp .cat_expect -
 }
 
+test_expect_success 'reset --hard message' '
+	hex=$(git log -1 --format="%h") &&
+	git reset --hard > .actual &&
+	echo HEAD is now at $hex $(commit_msg) > .expected &&
+	test_cmp .expected .actual
+'
+
+test_expect_success 'reset --hard message (iso8859-1 logoutputencoding)' '
+	hex=$(git log -1 --format="%h") &&
+	git -c "i18n.logOutputEncoding=iso8859-1" reset --hard > .actual &&
+	echo HEAD is now at $hex $(commit_msg iso8859-1) > .expected &&
+	test_cmp .expected .actual
+'
+
 >.diff_expect
 >.cached_expect
 cat >.cat_expect <<EOF
@@ -192,7 +219,8 @@
 	'changing files and redo the last commit should succeed' '
 	echo "3rd line 2nd file" >>secondfile &&
 	git commit -a -C ORIG_HEAD &&
-	check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+	head4=$(git rev-parse --verify HEAD) &&
+	check_changes $head4 &&
 	test "$(git rev-parse ORIG_HEAD)" = \
 			$head5
 '
@@ -211,7 +239,7 @@
 	git reset --hard HEAD~2 &&
 	check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
 	test "$(git rev-parse ORIG_HEAD)" = \
-			3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+			$head4
 '
 
 >.diff_expect
@@ -303,7 +331,7 @@
 
 	echo "1st line 2nd file" >secondfile &&
 	echo "2nd line 2nd file" >>secondfile &&
-	git commit -a -m "modify 2nd file" &&
+	git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
 	check_changes $head5
 '
 
@@ -326,10 +354,11 @@
 	git checkout branch2 &&
 	echo "3rd line in branch2" >>secondfile &&
 	git commit -a -m "change in branch2" &&
+	head3=$(git rev-parse --verify HEAD) &&
 
 	test_must_fail git pull . branch1 &&
 	git reset --hard &&
-	check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+	check_changes $head3
 '
 
 >.diff_expect
@@ -457,7 +486,7 @@
 	test_must_fail git diff --quiet -- secondfile &&
 	test -z "$(git diff --cached --name-only)" &&
 	test -f secondfile &&
-	test ! -s secondfile
+	test_must_be_empty secondfile
 
 '
 
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index be9672e..0c9ec0a 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -431,6 +431,7 @@
 
 test_expect_success \
     'checkout with --track fakes a sensible -b <name>' '
+    git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
     git update-ref refs/remotes/origin/koala/bear renamer &&
 
     git checkout --track origin/koala/bear &&
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
new file mode 100755
index 0000000..4e6055d
--- /dev/null
+++ b/t/t7301-clean-interactive.sh
@@ -0,0 +1,439 @@
+#!/bin/sh
+
+test_description='git clean -i basic tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	mkdir -p src &&
+	touch src/part1.c Makefile &&
+	echo build >.gitignore &&
+	echo \*.o >>.gitignore &&
+	git add . &&
+	git commit -m setup &&
+	touch src/part2.c README &&
+	git add .
+
+'
+
+test_expect_success 'git clean -i (clean)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	echo c | git clean -i &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (quit)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	echo q | git clean -i &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (Ctrl+D)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	echo "\04" | git clean -i &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter all)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo f; echo "*"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo f; echo "part3.* *.out"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test ! -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns 2)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo f; echo "* !*.out"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - all)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo "*"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - none)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 3; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 2)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 2 3; echo 5; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test ! -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 3)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 3,4 5; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 1,3-4; echo 2; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test ! -f docs/manual.txt &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range 2)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 4- 1; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (inverse select)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask - Ctrl+D)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo a; echo Y; echo no; echo yes; echo "\04") | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (filter)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(cd build/ && \
+	 (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+	 git clean -id ..) &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (select by name)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(cd build/ && \
+	 (echo s; echo "../docs/"; echo "../src/part3.c"; \
+	  echo "../src/part4.c";  echo; echo c) | \
+	 git clean -id ..) &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test ! -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (ask)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(cd build/ && \
+	 (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+	 git clean -id ..) &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index ff26535..5ee97b0 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -78,7 +78,7 @@
 	(
 		cd addtest &&
 		git submodule add -q "$submodurl" submod >actual &&
-		test ! -s actual &&
+		test_must_be_empty actual &&
 		echo "gitdir: ../.git/modules/submod" >expect &&
 		test_cmp expect submod/.git &&
 		(
@@ -212,6 +212,32 @@
 	test_cmp empty untracked
 '
 
+test_expect_success 'submodule add in subdirectory' '
+	echo "refs/heads/master" >expect &&
+	>empty &&
+
+	mkdir addtest/sub &&
+	(
+		cd addtest/sub &&
+		git submodule add "$submodurl" ../realsubmod3 &&
+		git submodule init
+	) &&
+
+	rm -f heads head untracked &&
+	inspect addtest/realsubmod3 ../.. &&
+	test_cmp expect heads &&
+	test_cmp expect head &&
+	test_cmp empty untracked
+'
+
+test_expect_success 'submodule add in subdirectory with relative path should fail' '
+	(
+		cd addtest/sub &&
+		test_must_fail git submodule add ../../ submod3 2>../../output.err
+	) &&
+	test_i18ngrep toplevel output.err
+'
+
 test_expect_success 'setup - add an example entry to .gitmodules' '
 	GIT_CONFIG=.gitmodules \
 	git config submodule.example.url git://example.com/init.git
@@ -308,7 +334,7 @@
 
 	mkdir init &&
 	git submodule update -q >update.out &&
-	test ! -s update.out &&
+	test_must_be_empty update.out &&
 
 	inspect init &&
 	test_cmp expect head-sha1
@@ -319,6 +345,26 @@
 	grep "^ $rev1" list
 '
 
+test_expect_success 'status "up-to-date" from subdirectory' '
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule status >../list
+	) &&
+	grep "^ $rev1" list &&
+	grep "\\.\\./init" list
+'
+
+test_expect_success 'status "up-to-date" from subdirectory with path' '
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule status ../init >../list
+	) &&
+	grep "^ $rev1" list &&
+	grep "\\.\\./init" list
+'
+
 test_expect_success 'status should be "modified" after submodule commit' '
 	(
 		cd init &&
@@ -399,6 +445,25 @@
 	git rev-parse --resolve-git-dir init/.git
 '
 
+test_expect_success 'update --init from subdirectory' '
+	mv init init2 &&
+	git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+	git config --remove-section submodule.example &&
+	test_must_fail git config submodule.example.url &&
+
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule update ../init >update.out &&
+		cat update.out &&
+		test_i18ngrep "not initialized" update.out &&
+		test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
+
+		git submodule update --init ../init
+	) &&
+	git rev-parse --resolve-git-dir init/.git
+'
+
 test_expect_success 'do not add files from a submodule' '
 
 	git reset --hard &&
@@ -696,7 +761,7 @@
 		rm -rf repo &&
 		git rm repo &&
 		git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual &&
-		test ! -s actual &&
+		test_must_be_empty actual &&
 		echo "gitdir: ../.git/modules/submod" >expect &&
 		test_cmp expect submod/.git &&
 		(
@@ -772,6 +837,21 @@
 	rmdir init
 '
 
+test_expect_success 'submodule deinit from subdirectory' '
+	git submodule update --init &&
+	git config submodule.example.foo bar &&
+	mkdir -p sub &&
+	(
+		cd sub &&
+		git submodule deinit ../init >../output
+	) &&
+	grep "\\.\\./init" output &&
+	test -z "$(git config --get-regexp "submodule\.example\.")" &&
+	test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+	test -f example2/.git &&
+	rmdir init
+'
+
 test_expect_success 'submodule deinit . deinits all initialized submodules' '
 	git submodule update --init &&
 	git config submodule.example.foo bar &&
@@ -868,4 +948,35 @@
 	test -n "$(git config --get-regexp "submodule\.example\.")"
 '
 
+test_expect_success 'submodule with UTF-8 name' '
+	svname=$(printf "\303\245 \303\244\303\266") &&
+	mkdir "$svname" &&
+	(
+		cd "$svname" &&
+		git init &&
+		>sub &&
+		git add sub &&
+		git commit -m "init sub"
+	) &&
+	test_config core.precomposeunicode true &&
+	git submodule add ./"$svname" &&
+	git submodule >&2 &&
+	test -n "$(git submodule | grep "$svname")"
+'
+
+test_expect_success 'submodule add clone shallow submodule' '
+	mkdir super &&
+	pwd=$(pwd)
+	(
+		cd super &&
+		git init &&
+		git submodule add --depth=1 file://"$pwd"/example2 submodule &&
+		(
+			cd submodule &&
+			test 1 = $(git log --oneline | wc -l)
+		)
+	)
+'
+
+
 test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 30b429e..ac2434c 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -45,6 +45,42 @@
 	test_cmp expected actual
 "
 
+test_expect_success 'added submodule (subdirectory)' "
+	mkdir sub &&
+	(
+		cd sub &&
+		git submodule summary >../actual
+	) &&
+	cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+	test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory only)' "
+	(
+		cd sub &&
+		git submodule summary . >../actual
+	) &&
+	>expected &&
+	test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory with explicit path)' "
+	(
+		cd sub &&
+		git submodule summary ../sm1 >../actual
+	) &&
+	cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+	test_cmp expected actual
+"
+
 commit_file sm1 &&
 head2=$(add_file sm1 foo3)
 
@@ -76,8 +112,8 @@
 )
 
 test_expect_success 'modified submodule(backward)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head2...$head3 (2):
   < Add foo3
   < Add foo2
@@ -89,8 +125,8 @@
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
@@ -102,15 +138,15 @@
 "
 
 test_expect_success '--summary-limit' "
-    git submodule summary -n 3 >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary -n 3 >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
   < Add foo3
 
 EOF
-    test_cmp expected actual
+	test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -122,8 +158,8 @@
 mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
-    git submodule summary --cached >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary --cached >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob) (3):
   < Add foo5
 
@@ -132,59 +168,59 @@
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
-    git submodule summary --files >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary --files >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head4(submodule) (3):
   > Add foo5
 
 EOF
-    test_i18ncmp actual expected
+	test_i18ncmp actual expected
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
-    test_i18ncmp actual expected
+	test_i18ncmp actual expected
 "
 
 rm -f sm1 &&
 test_create_repo sm1 &&
 head6=$(add_file sm1 foo6 foo7)
 test_expect_success 'nonexistent commit' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head4...$head6:
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
-    test_i18ncmp actual expected
+	test_i18ncmp actual expected
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head6(submodule) (2):
   > Add foo7
 
 EOF
-    test_i18ncmp expected actual
+	test_i18ncmp expected actual
 "
 
 commit_file sm1 &&
 rm -rf sm1
 test_expect_success 'deleted submodule' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 EOF
-    test_cmp expected actual
+	test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -192,43 +228,43 @@
 git add sm2
 
 test_expect_success 'multiple submodules' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+	test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
-    git submodule summary sm2 >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary sm2 >actual &&
+	cat >expected <<-EOF &&
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+	test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
-    git submodule summary HEAD^ >actual &&
-    cat >expected <<-EOF &&
+	git submodule summary HEAD^ >actual &&
+	cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+	test_cmp expected actual
 "
 
 test_expect_success '--for-status' "
-    git submodule summary --for-status HEAD^ >actual &&
-    test_i18ncmp actual - <<EOF
+	git submodule summary --for-status HEAD^ >actual &&
+	test_i18ncmp actual - <<EOF
 # Submodule changes to be committed:
 #
 # * sm1 $head6...0000000:
@@ -240,14 +276,14 @@
 "
 
 test_expect_success 'fail when using --files together with --cached' "
-    test_must_fail git submodule summary --files --cached
+	test_must_fail git submodule summary --files --cached
 "
 
 test_expect_success 'should not fail in an empty repo' "
-    git init xyzzy &&
-    cd xyzzy &&
-    git submodule summary >output 2>&1 &&
-    test_cmp output /dev/null
+	git init xyzzy &&
+	cd xyzzy &&
+	git submodule summary >output 2>&1 &&
+	test_cmp output /dev/null
 "
 
 test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 94e26c4..79bc135 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -11,216 +11,338 @@
 . ./test-lib.sh
 
 test_expect_success setup '
-	echo file > file &&
+	echo file >file &&
 	git add file &&
 	test_tick &&
 	git commit -m upstream &&
 	git clone . super &&
 	git clone super submodule &&
-	(cd submodule &&
-	 git submodule add ../submodule sub-submodule &&
-	 test_tick &&
-	 git commit -m "sub-submodule"
+	(
+		cd submodule &&
+		git submodule add ../submodule sub-submodule &&
+		test_tick &&
+		git commit -m "sub-submodule"
 	) &&
-	(cd super &&
-	 git submodule add ../submodule submodule &&
-	 test_tick &&
-	 git commit -m "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 --recursive) &&
+	(
+		cd super-clone &&
+		git submodule update --init --recursive
+	) &&
 	git clone super empty-clone &&
-	(cd empty-clone && git submodule init) &&
+	(
+		cd empty-clone &&
+		git submodule init
+	) &&
 	git clone super top-only-clone &&
 	git clone super relative-clone &&
-	(cd relative-clone && git submodule update --init --recursive) &&
+	(
+		cd relative-clone &&
+		git submodule update --init --recursive
+	) &&
 	git clone super recursive-clone &&
-	(cd recursive-clone && git submodule update --init --recursive)
-'
-
-test_expect_success 'change submodule' '
-	(cd submodule &&
-	 echo second line >> file &&
-	 test_tick &&
-	 git commit -a -m "change submodule"
+	(
+		cd recursive-clone &&
+		git submodule update --init --recursive
 	)
 '
 
+test_expect_success 'change submodule' '
+	(
+		cd submodule &&
+		echo second line >>file &&
+		test_tick &&
+		git commit -a -m "change submodule"
+	)
+'
+
+reset_submodule_urls () {
+	local root
+	root=$(pwd) &&
+	(
+		cd super-clone/submodule &&
+		git config remote.origin.url "$root/submodule"
+	) &&
+	(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url "$root/submodule"
+	)
+}
+
 test_expect_success 'change submodule url' '
-	(cd super &&
-	 cd submodule &&
-	 git checkout master &&
-	 git pull
+	(
+		cd super &&
+		cd submodule &&
+		git checkout master &&
+		git pull
 	) &&
 	mv submodule moved-submodule &&
-	(cd moved-submodule &&
-	 git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
-	 test_tick &&
-	 git commit -a -m moved-sub-submodule
+	(
+		cd moved-submodule &&
+		git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
+		test_tick &&
+		git commit -a -m moved-sub-submodule
 	) &&
-	(cd super &&
-	 git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
-	 test_tick &&
-	 git commit -a -m 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 --no-recurse-submodules &&
-	 git submodule sync
+	(
+		cd super-clone &&
+		git pull --no-recurse-submodules &&
+		git submodule sync
 	) &&
-	test -d "$(cd super-clone/submodule &&
-	 git config remote.origin.url
+	test -d "$(
+		cd super-clone/submodule &&
+		git config remote.origin.url
 	)" &&
-	test ! -d "$(cd super-clone/submodule/sub-submodule &&
-	 git config remote.origin.url
+	test ! -d "$(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url
 	)" &&
-	(cd super-clone/submodule &&
-	 git checkout master &&
-	 git pull
+	(
+		cd super-clone/submodule &&
+		git checkout master &&
+		git pull
 	) &&
-	(cd super-clone &&
-	 test -d "$(git config submodule.submodule.url)"
+	(
+		cd super-clone &&
+		test -d "$(git config submodule.submodule.url)"
 	)
 '
 
 test_expect_success '"git submodule sync --recursive" should update all submodule URLs' '
-	(cd super-clone &&
-	 (cd submodule &&
-	  git pull --no-recurse-submodules
-	 ) &&
-	 git submodule sync --recursive
+	(
+		cd super-clone &&
+		(
+			cd submodule &&
+			git pull --no-recurse-submodules
+		) &&
+		git submodule sync --recursive
 	) &&
-	test -d "$(cd super-clone/submodule &&
-	 git config remote.origin.url
+	test -d "$(
+		cd super-clone/submodule &&
+		git config remote.origin.url
 	)" &&
-	test -d "$(cd super-clone/submodule/sub-submodule &&
-	 git config remote.origin.url
+	test -d "$(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url
 	)" &&
-	(cd super-clone/submodule/sub-submodule &&
-	 git checkout master &&
-	 git pull
+	(
+		cd super-clone/submodule/sub-submodule &&
+		git checkout master &&
+		git pull
+	)
+'
+
+test_expect_success 'reset submodule URLs' '
+	reset_submodule_urls super-clone
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' '
+	(
+		cd super-clone &&
+		git pull --no-recurse-submodules &&
+		mkdir -p sub &&
+		cd sub &&
+		git submodule sync >../../output
+	) &&
+	grep "\\.\\./submodule" output &&
+	test -d "$(
+		cd super-clone/submodule &&
+		git config remote.origin.url
+	)" &&
+	test ! -d "$(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url
+	)" &&
+	(
+		cd super-clone/submodule &&
+		git checkout master &&
+		git pull
+	) &&
+	(
+		cd super-clone &&
+		test -d "$(git config submodule.submodule.url)"
+	)
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' '
+	(
+		cd super-clone &&
+		(
+			cd submodule &&
+			git pull --no-recurse-submodules
+		) &&
+		mkdir -p sub &&
+		cd sub &&
+		git submodule sync --recursive >../../output
+	) &&
+	grep "\\.\\./submodule/sub-submodule" output &&
+	test -d "$(
+		cd super-clone/submodule &&
+		git config remote.origin.url
+	)" &&
+	test -d "$(
+		cd super-clone/submodule/sub-submodule &&
+		git config remote.origin.url
+	)" &&
+	(
+		cd super-clone/submodule/sub-submodule &&
+		git checkout master &&
+		git pull
 	)
 '
 
 test_expect_success '"git submodule sync" should update known submodule URLs' '
-	(cd empty-clone &&
-	 git pull &&
-	 git submodule sync &&
-	 test -d "$(git config submodule.submodule.url)"
+	(
+		cd empty-clone &&
+		git pull &&
+		git submodule sync &&
+		test -d "$(git config submodule.submodule.url)"
 	)
 '
 
 test_expect_success '"git submodule sync" should not vivify uninteresting submodule' '
-	(cd top-only-clone &&
-	 git pull &&
-	 git submodule sync &&
-	 test -z "$(git config submodule.submodule.url)" &&
-	 git submodule sync submodule &&
-	 test -z "$(git config submodule.submodule.url)"
+	(
+		cd top-only-clone &&
+		git pull &&
+		git submodule sync &&
+		test -z "$(git config submodule.submodule.url)" &&
+		git submodule sync submodule &&
+		test -z "$(git config submodule.submodule.url)"
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form foo' '
-	(cd relative-clone &&
-	 git remote set-url origin foo &&
-	 git submodule sync &&
-	(cd submodule &&
-	 #actual fails with: "cannot strip off url foo
-	 test "$(git config remote.origin.url)" = "../submodule"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin foo &&
+		git submodule sync &&
+		(
+			cd submodule &&
+			#actual fails with: "cannot strip off url foo
+			test "$(git config remote.origin.url)" = "../submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' '
-	(cd relative-clone &&
-	 git remote set-url origin foo/bar &&
-	 git submodule sync &&
-	(cd submodule &&
-	 #actual foo/submodule
-	 test "$(git config remote.origin.url)" = "../foo/submodule"
-	)
-	(cd submodule/sub-submodule &&
-	 test "$(git config remote.origin.url)" != "../../foo/submodule"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin foo/bar &&
+		git submodule sync &&
+		(
+			cd submodule &&
+			#actual foo/submodule
+			test "$(git config remote.origin.url)" = "../foo/submodule"
+		) &&
+		(
+			cd submodule/sub-submodule &&
+			test "$(git config remote.origin.url)" != "../../foo/submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync --recursive" propagates changes in origin' '
-	(cd recursive-clone &&
-	 git remote set-url origin foo/bar &&
-	 git submodule sync --recursive &&
-	(cd submodule &&
-	 #actual foo/submodule
-	 test "$(git config remote.origin.url)" = "../foo/submodule"
-	)
-	(cd submodule/sub-submodule &&
-	 test "$(git config remote.origin.url)" = "../../foo/submodule"
-	)
+	(
+		cd recursive-clone &&
+		git remote set-url origin foo/bar &&
+		git submodule sync --recursive &&
+		(
+			cd submodule &&
+			#actual foo/submodule
+			test "$(git config remote.origin.url)" = "../foo/submodule"
+		) &&
+		(
+			cd submodule/sub-submodule &&
+			test "$(git config remote.origin.url)" = "../../foo/submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ./foo' '
-	(cd relative-clone &&
-	 git remote set-url origin ./foo &&
-	 git submodule sync &&
-	(cd submodule &&
-	 #actual ./submodule
-	 test "$(git config remote.origin.url)" = "../submodule"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin ./foo &&
+		git submodule sync &&
+		(
+			cd submodule &&
+			#actual ./submodule
+			test "$(git config remote.origin.url)" = "../submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' '
-	(cd relative-clone &&
-	 git remote set-url origin ./foo/bar &&
-	 git submodule sync &&
-	(cd submodule &&
-	 #actual ./foo/submodule
-	 test "$(git config remote.origin.url)" = "../foo/submodule"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin ./foo/bar &&
+		git submodule sync &&
+		(
+			cd submodule &&
+			#actual ./foo/submodule
+			test "$(git config remote.origin.url)" = "../foo/submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo' '
-	(cd relative-clone &&
-	 git remote set-url origin ../foo &&
-	 git submodule sync &&
-	(cd submodule &&
-	 #actual ../submodule
-	 test "$(git config remote.origin.url)" = "../../submodule"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin ../foo &&
+		git submodule sync &&
+		(
+			cd submodule &&
+			#actual ../submodule
+			test "$(git config remote.origin.url)" = "../../submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' '
-	(cd relative-clone &&
-	 git remote set-url origin ../foo/bar &&
-	 git submodule sync &&
-	(cd submodule &&
-	 #actual ../foo/submodule
-	 test "$(git config remote.origin.url)" = "../../foo/submodule"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin ../foo/bar &&
+		git submodule sync &&
+		(
+			cd submodule &&
+			#actual ../foo/submodule
+			test "$(git config remote.origin.url)" = "../../foo/submodule"
+		)
 	)
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' '
-	(cd relative-clone &&
-	 git remote set-url origin ../foo/bar &&
-	 mkdir -p a/b/c &&
-	 ( cd a/b/c &&
-	   git init &&
-	   :> .gitignore &&
-	   git add .gitignore &&
-	   test_tick &&
-	   git commit -m "initial commit" ) &&
-	 git submodule add ../bar/a/b/c ./a/b/c &&
-	 git submodule sync &&
-	(cd a/b/c &&
-	 #actual ../foo/bar/a/b/c
-	 test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
-	)
+	(
+		cd relative-clone &&
+		git remote set-url origin ../foo/bar &&
+		mkdir -p a/b/c &&
+		(
+			cd a/b/c &&
+			git init &&
+			>.gitignore &&
+			git add .gitignore &&
+			test_tick &&
+			git commit -m "initial commit"
+		) &&
+		git submodule add ../bar/a/b/c ./a/b/c &&
+		git submodule sync &&
+		(
+			cd a/b/c &&
+			#actual ../foo/bar/a/b/c
+			test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
+		)
 	)
 '
 
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index a4ffea0..b192f93 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -80,6 +80,21 @@
 	)
 '
 
+test_expect_success 'submodule update from subdirectory' '
+	(cd super/submodule &&
+	 git reset --hard HEAD~1
+	) &&
+	mkdir super/sub &&
+	(cd super/sub &&
+	 (cd ../submodule &&
+	  compare_head
+	 ) &&
+	 git submodule update ../submodule &&
+	 cd ../submodule &&
+	 ! compare_head
+	)
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
 	(cd submodule &&
@@ -279,6 +294,35 @@
 	)
 '
 
+test_expect_success 'submodule update - command in .git/config' '
+	(cd super &&
+	 git config submodule.submodule.update "!git checkout"
+	) &&
+	(cd super/submodule &&
+	  git reset --hard HEAD^
+	) &&
+	(cd super &&
+	 (cd submodule &&
+	  compare_head
+	 ) &&
+	 git submodule update submodule &&
+	 cd submodule &&
+	 ! compare_head
+	)
+'
+
+test_expect_success 'submodule update - command in .git/config catches failure' '
+	(cd super &&
+	 git config submodule.submodule.update "!false"
+	) &&
+	(cd super/submodule &&
+	  git reset --hard HEAD^
+	) &&
+	(cd super &&
+	 test_must_fail git submodule update submodule
+	)
+'
+
 test_expect_success 'submodule init picks up rebase' '
 	(cd super &&
 	 git config -f .gitmodules submodule.rebasing.update rebase &&
@@ -685,14 +729,24 @@
 test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
 	mkdir -p linked/dir &&
 	ln -s linked/dir linkto &&
-	(
-		cd linkto &&
-		git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
-		(
-			cd super &&
-			git submodule update --init --recursive
-		)
+	(cd linkto &&
+	 git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+	 (cd super &&
+	  git submodule update --init --recursive
+	 )
 	)
 '
 
+test_expect_success 'submodule update clone shallow submodule' '
+	git clone cloned super3 &&
+	pwd=$(pwd)
+	(cd super3 &&
+	 sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+	 mv -f .gitmodules.tmp .gitmodules &&
+	 git submodule update --init --depth=3
+	 (cd submodule &&
+	  test 1 = $(git log --oneline | wc -l)
+	 )
+	)
+'
 test_done
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 107b4b7..91d4fd1 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -80,6 +80,22 @@
 	test_i18ncmp expect actual
 '
 
+cat >expect <<EOF
+Entering '../sub1'
+$pwd/clone-foo1-../sub1-$sub1sha1
+Entering '../sub3'
+$pwd/clone-foo3-../sub3-$sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach" from subdirectory' '
+	mkdir clone/sub &&
+	(
+		cd clone/sub &&
+		git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+	) &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'setup nested submodules' '
 	git clone submodule nested1 &&
 	git clone submodule nested2 &&
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 436b7b6..bdc1f29 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -13,9 +13,9 @@
 	expect=commit_msg_is.expect
 	actual=commit_msg_is.actual
 
-	printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
-	printf "%s" "$1" >$actual &&
-	test_i18ncmp $expect $actual
+	printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
+	printf "%s" "$1" >"$expect" &&
+	test_i18ncmp "$expect" "$actual"
 }
 
 # A sanity check to see if commit is working at all.
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 195e747..99ce36f 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -524,4 +524,17 @@
 	test_i18ngrep " changed, 5 insertions" output
 '
 
+test_expect_success '--only works on to-be-born branch' '
+	# This test relies on having something in the index, as it
+	# would not otherwise actually prove much.  So check this.
+	test -n "$(git ls-files)" &&
+	git checkout --orphan orphan &&
+	echo foo >newfile &&
+	git add newfile &&
+	git commit --only newfile -m"--only on unborn branch" &&
+	echo newfile >expected &&
+	git ls-tree -r --name-only HEAD >actual &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index a4938b1..6313da2 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -361,6 +361,23 @@
 	test_cmp expect .git/result
 '
 
+test_expect_success 'do not fire editor if -m <msg> was given' '
+	echo tick >file &&
+	git add file &&
+	echo "editor not started" >.git/result &&
+	(GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" git commit -m tick) &&
+	test "$(cat .git/result)" = "editor not started"
+'
+
+test_expect_success 'do not fire editor if -m "" was given' '
+	echo tock >file &&
+	git add file &&
+	echo "editor not started" >.git/result &&
+	(GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" \
+	 git commit -m "" --allow-empty-message) &&
+	test "$(cat .git/result)" = "editor not started"
+'
+
 test_expect_success 'do not fire editor in the presence of conflicts' '
 
 	git clean -f &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index e2ffdac..ac3d0fe 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1335,4 +1335,66 @@
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
+test_expect_success 'setup of test environment' '
+	git config status.showUntrackedFiles no &&
+	git status -s >expected_short &&
+	git status --no-short >expected_noshort
+'
+
+test_expect_success '"status.short=true" same as "-s"' '
+	git -c status.short=true status >actual &&
+	test_cmp expected_short actual
+'
+
+test_expect_success '"status.short=true" weaker than "--no-short"' '
+	git -c status.short=true status --no-short >actual &&
+	test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" same as "--no-short"' '
+	git -c status.short=false status >actual &&
+	test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" weaker than "-s"' '
+	git -c status.short=false status -s >actual &&
+	test_cmp expected_short actual
+'
+
+test_expect_success '"status.branch=true" same as "-b"' '
+	git status -sb >expected_branch &&
+	git -c status.branch=true status -s >actual &&
+	test_cmp expected_branch actual
+'
+
+test_expect_success '"status.branch=true" different from "--no-branch"' '
+	git status -s --no-branch  >expected_nobranch &&
+	git -c status.branch=true status -s >actual &&
+	test_must_fail test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--no-branch"' '
+	git -c status.branch=true status -s --no-branch >actual &&
+	test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--porcelain"' '
+       git -c status.branch=true status --porcelain >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" same as "--no-branch"' '
+	git -c status.branch=false status -s >actual &&
+	test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" weaker than "-b"' '
+	git -c status.branch=false status -sb >actual &&
+	test_cmp expected_branch actual
+'
+
+test_expect_success 'Restore default test environment' '
+	git config --unset status.showUntrackedFiles
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index bf08d4e..31a798f 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -77,7 +77,7 @@
 	ONTO=$(git rev-parse --short HEAD^^) &&
 	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
 	cat >expected <<-EOF &&
-	# HEAD detached at $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
 	#   (fix conflicts and then run "git rebase --continue")
 	#   (use "git rebase --skip" to skip this patch)
@@ -104,7 +104,7 @@
 	echo three >main.txt &&
 	git add main.txt &&
 	cat >expected <<-EOF &&
-	# HEAD detached at $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
 	#   (all conflicts fixed: run "git rebase --continue")
 	#
@@ -136,7 +136,7 @@
 	ONTO=$(git rev-parse --short rebase_i_conflicts) &&
 	test_must_fail git rebase -i rebase_i_conflicts &&
 	cat >expected <<-EOF &&
-	# HEAD detached at $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
 	#   (fix conflicts and then run "git rebase --continue")
 	#   (use "git rebase --skip" to skip this patch)
@@ -162,7 +162,7 @@
 	test_must_fail git rebase -i rebase_i_conflicts &&
 	git add main.txt &&
 	cat >expected <<-EOF &&
-	# HEAD detached at $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
 	#   (all conflicts fixed: run "git rebase --continue")
 	#
@@ -188,10 +188,9 @@
 	export FAKE_LINES &&
 	test_when_finished "git rebase --abort" &&
 	ONTO=$(git rev-parse --short HEAD~2) &&
-	TGT=$(git rev-parse --short two_rebase_i) &&
 	git rebase -i HEAD~2 &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $TGT
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -216,9 +215,8 @@
 	ONTO=$(git rev-parse --short HEAD~3) &&
 	git rebase -i HEAD~3 &&
 	git reset HEAD^ &&
-	TGT=$(git rev-parse --short HEAD) &&
 	cat >expected <<-EOF &&
-	# HEAD detached at $TGT
+	# rebase in progress; onto $ONTO
 	# You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
 	#   (Once your working directory is clean, run "git rebase --continue")
 	#
@@ -246,11 +244,10 @@
 	export FAKE_LINES &&
 	test_when_finished "git rebase --abort" &&
 	ONTO=$(git rev-parse --short HEAD~3) &&
-	TGT=$(git rev-parse --short three_amend) &&
 	git rebase -i HEAD~3 &&
 	git commit --amend -m "foo" &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $TGT
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -280,7 +277,7 @@
 	git rebase -i HEAD~3 &&
 	git rebase --continue &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -302,7 +299,7 @@
 	git rebase --continue &&
 	git reset HEAD^ &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (Once your working directory is clean, run "git rebase --continue")
 	#
@@ -329,7 +326,7 @@
 	git rebase --continue &&
 	git commit --amend -m "foo" &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -351,7 +348,7 @@
 	git commit --amend -m "a" &&
 	git rebase --continue &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -374,7 +371,7 @@
 	git rebase --continue &&
 	git reset HEAD^ &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (Once your working directory is clean, run "git rebase --continue")
 	#
@@ -402,7 +399,7 @@
 	git rebase --continue &&
 	git commit --amend -m "d" &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -426,7 +423,7 @@
 	git commit -m "e" &&
 	git rebase --continue &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -451,7 +448,7 @@
 	git rebase --continue &&
 	git reset HEAD^ &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (Once your working directory is clean, run "git rebase --continue")
 	#
@@ -481,7 +478,7 @@
 	git rebase --continue &&
 	git commit --amend -m "h" &&
 	cat >expected <<-EOF &&
-	# HEAD detached from $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
 	#   (use "git commit --amend" to amend the current commit)
 	#   (use "git rebase --continue" once you are satisfied with your changes)
@@ -510,7 +507,7 @@
 	cat >expected <<-\EOF &&
 	# On branch am_already_exists
 	# You are in the middle of an am session.
-	#   (fix conflicts and then run "git am --resolved")
+	#   (fix conflicts and then run "git am --continue")
 	#   (use "git am --skip" to skip this patch)
 	#   (use "git am --abort" to restore the original branch)
 	#
@@ -532,7 +529,7 @@
 	cat >expected <<-\EOF &&
 	# On branch am_not_exists
 	# You are in the middle of an am session.
-	#   (fix conflicts and then run "git am --resolved")
+	#   (fix conflicts and then run "git am --continue")
 	#   (use "git am --skip" to skip this patch)
 	#   (use "git am --abort" to restore the original branch)
 	#
@@ -601,7 +598,7 @@
 	ONTO=$(git rev-parse --short HEAD^^) &&
 	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
 	cat >expected <<-EOF &&
-	# HEAD detached at $ONTO
+	# rebase in progress; onto $ONTO
 	# You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
 	#
 	# Unmerged paths:
@@ -632,7 +629,8 @@
 	cat >expected <<-\EOF &&
 	# On branch cherry_branch
 	# You are currently cherry-picking.
-	#   (fix conflicts and run "git commit")
+	#   (fix conflicts and run "git cherry-pick --continue")
+	#   (use "git cherry-pick --abort" to cancel the cherry-pick operation)
 	#
 	# Unmerged paths:
 	#   (use "git add <file>..." to mark resolution)
@@ -655,7 +653,8 @@
 	cat >expected <<-\EOF &&
 	# On branch cherry_branch
 	# You are currently cherry-picking.
-	#   (all conflicts fixed: run "git commit")
+	#   (all conflicts fixed: run "git cherry-pick --continue")
+	#   (use "git cherry-pick --abort" to cancel the cherry-pick operation)
 	#
 	# Changes to be committed:
 	#
@@ -667,7 +666,7 @@
 	test_i18ncmp expected actual
 '
 
-test_expect_success 'status showing detached from a tag' '
+test_expect_success 'status showing detached at and from a tag' '
 	test_commit atag tagging &&
 	git checkout atag &&
 	cat >expected <<-\EOF
@@ -675,6 +674,14 @@
 	nothing to commit (use -u to show untracked files)
 	EOF
 	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual &&
+
+	git reset --hard HEAD^ &&
+	cat >expected <<-\EOF
+	# HEAD detached from atag
+	nothing to commit (use -u to show untracked files)
+	EOF
+	git status --untracked-files=no >actual &&
 	test_i18ncmp expected actual
 '
 
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 2f70433..10aa028 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -316,7 +316,7 @@
 
 test_debug 'git log --graph --decorate --oneline --all'
 
-test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+test_expect_success 'unsuccessful merge of c1 with c2 (squash, ff-only)' '
 	git reset --hard c1 &&
 	test_must_fail git merge --squash --ff-only c2
 '
@@ -497,9 +497,15 @@
 	test_must_fail git merge --no-ff --squash c1
 '
 
-test_expect_success 'combining --ff-only and --no-ff is refused' '
-	test_must_fail git merge --ff-only --no-ff c1 &&
-	test_must_fail git merge --no-ff --ff-only c1
+test_expect_success 'option --ff-only overwrites --no-ff' '
+	git merge --no-ff --ff-only c1 &&
+	test_must_fail git merge --no-ff --ff-only c2
+'
+
+test_expect_success 'option --no-ff overrides merge.ff=only config' '
+	git reset --hard c0 &&
+	test_config merge.ff only &&
+	git merge --no-ff c1
 '
 
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
index 6547eb8..758a623 100755
--- a/t/t7607-merge-overwrite.sh
+++ b/t/t7607-merge-overwrite.sh
@@ -141,11 +141,10 @@
 	test_path_is_missing .git/MERGE_HEAD
 '
 
-test_expect_success SYMLINKS 'will not be confused by symlink in leading path' '
+test_expect_success 'will not be confused by symlink in leading path' '
 	git reset --hard c0 &&
 	rm -rf sub &&
-	ln -s sub2 sub &&
-	git add sub &&
+	test_ln_s_add sub2 sub &&
 	git commit -m ln &&
 	git checkout sub
 '
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index d46f041..2418528 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -385,6 +385,25 @@
 	test_cmp actual expect
 '
 
+write_script modify-right-file <<\EOF
+echo "new content" >"$2/file"
+EOF
+
+run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' '
+	test_when_finished git reset --hard &&
+	echo "orig content" >file &&
+	git difftool -d $symlinks --extcmd "$(pwd)/modify-right-file" branch &&
+	echo "new content" >expect &&
+	test_cmp expect file
+'
+
+run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' '
+	test_when_finished git reset --hard &&
+	git difftool -d $symlinks --extcmd "$(pwd)/modify-right-file" branch &&
+	echo "new content" >expect &&
+	test_cmp expect file
+'
+
 write_script modify-file <<\EOF
 echo "new content" >file
 EOF
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 41962f0..72176e4 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -6,9 +6,9 @@
 PROG='git annotate'
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
-test_expect_success 'Annotating an old revision works' '
-	git annotate file master >result &&
-	awk "{ print \$3; }" <result >authors &&
+test_expect_success 'annotate old revision' '
+	git annotate file master >actual &&
+	awk "{ print \$3; }" <actual >authors &&
 	test 2 = $(grep A <authors | wc -l) &&
 	test 2 = $(grep B <authors | wc -l)
 '
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index e2896cf..5cdf3f1 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -7,8 +7,16 @@
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
 PROG='git blame -c -e'
-test_expect_success 'Blame --show-email works' '
-    check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 "<E at test dot git>" 1
+test_expect_success 'blame --show-email' '
+	check_count \
+		"<A@test.git>" 1 \
+		"<B@test.git>" 1 \
+		"<B1@test.git>" 1 \
+		"<B2@test.git>" 1 \
+		"<author@example.com>" 1 \
+		"<C@test.git>" 1 \
+		"<D@test.git>" 1 \
+		"<E at test dot git>" 1
 '
 
 test_done
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 230143c..e7cac1d 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -175,6 +175,12 @@
 	grep "has only 2 lines" errors
 '
 
+test_expect_success 'blame parses <end> part of -L' '
+	git blame -L1,1 tres >out &&
+	cat out &&
+	test $(wc -l < out) -eq 1
+'
+
 test_expect_success 'indent of line numbers, nine lines' '
 	git blame nine_lines >actual &&
 	test $(grep -c "  " actual) = 0
diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh
index bf6caa4..7683515 100755
--- a/t/t8006-blame-textconv.sh
+++ b/t/t8006-blame-textconv.sh
@@ -18,17 +18,13 @@
 	echo "bin: test number 0" >zero.bin &&
 	echo "bin: test 1" >one.bin &&
 	echo "bin: test number 2" >two.bin &&
-	if test_have_prereq SYMLINKS; then
-		ln -s one.bin symlink.bin
-	fi &&
+	test_ln_s_add one.bin symlink.bin &&
 	git add . &&
 	GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
 	echo "bin: test 1 version 2" >one.bin &&
 	echo "bin: test number 2 version 2" >>two.bin &&
-	if test_have_prereq SYMLINKS; then
-		rm symlink.bin &&
-		ln -s two.bin symlink.bin
-	fi &&
+	rm -f symlink.bin &&
+	test_ln_s_add two.bin symlink.bin &&
 	GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
 '
 
@@ -135,7 +131,7 @@
 
 # cp two.bin three.bin  and make small tweak
 # (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
-test_expect_success SYMLINKS 'make another new commit' '
+test_expect_success 'make another new commit' '
 	cat >three.bin <<\EOF &&
 bin: test number 2
 bin: test number 2 version 2
@@ -146,7 +142,7 @@
 	GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
 '
 
-test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
+test_expect_success 'blame on last commit (-C -C, symlink)' '
 	git blame -C -C three.bin >blame &&
 	find_blame <blame >result &&
 	cat >expected <<\EOF &&
diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh
index 78a0085..b95e102 100755
--- a/t/t8007-cat-file-textconv.sh
+++ b/t/t8007-cat-file-textconv.sh
@@ -12,9 +12,7 @@
 
 test_expect_success 'setup ' '
 	echo "bin: test" >one.bin &&
-	if test_have_prereq SYMLINKS; then
-		ln -s one.bin symlink.bin
-	fi &&
+	test_ln_s_add one.bin symlink.bin &&
 	git add . &&
 	GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
 	echo "bin: test version 2" >one.bin &&
@@ -72,14 +70,14 @@
 	test_cmp expected result
 '
 
-test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
+test_expect_success 'cat-file without --textconv (symlink)' '
 	git cat-file blob :symlink.bin >result &&
 	printf "%s" "one.bin" >expected
 	test_cmp expected result
 '
 
 
-test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
+test_expect_success 'cat-file --textconv on index (symlink)' '
 	! git cat-file --textconv :symlink.bin 2>result &&
 	cat >expected <<\EOF &&
 fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
@@ -87,7 +85,7 @@
 	test_cmp expected result
 '
 
-test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
+test_expect_success 'cat-file --textconv on HEAD (symlink)' '
 	! git cat-file --textconv HEAD:symlink.bin 2>result &&
 	cat >expected <<EOF &&
 fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index ebd5c5d..2813aa9 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -171,6 +171,81 @@
 EOF
 "
 
+test_suppress_self () {
+	test_commit $3 &&
+	test_when_finished "git reset --hard HEAD^" &&
+
+	write_script cccmd-sed <<-EOF &&
+		sed -n -e s/^cccmd--//p "\$1"
+	EOF
+
+	git commit --amend --author="$1 <$2>" -F - &&
+	clean_fake_sendmail &&
+	git format-patch --stdout -1 >"suppress-self-$3.patch" &&
+
+	git send-email --from="$1 <$2>" \
+		--to=nobody@example.com \
+		--cc-cmd=./cccmd-sed \
+		--suppress-cc=self \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		suppress-self-$3.patch &&
+
+	mv msgtxt1 msgtxt1-$3 &&
+	sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" &&
+	>"expected-no-cc-$3" &&
+
+	(grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3";
+	 test_cmp expected-no-cc-$3 actual-no-cc-$3)
+}
+
+test_suppress_self_unquoted () {
+	test_suppress_self "$1" "$2" "unquoted-$3" <<-EOF
+		test suppress-cc.self unquoted-$3 with name $1 email $2
+
+		unquoted-$3
+
+		cccmd--$1 <$2>
+
+		Cc: $1 <$2>
+		Signed-off-by: $1 <$2>
+	EOF
+}
+
+test_suppress_self_quoted () {
+	test_suppress_self "$1" "$2" "quoted-$3" <<-EOF
+		test suppress-cc.self quoted-$3 with name $1 email $2
+
+		quoted-$3
+
+		cccmd--"$1" <$2>
+
+		Cc: $1 <$2>
+		Cc: "$1" <$2>
+		Signed-off-by: $1 <$2>
+		Signed-off-by: "$1" <$2>
+	EOF
+}
+
+test_expect_success $PREREQ 'self name is suppressed' "
+	test_suppress_self_unquoted 'A U Thor' 'author@example.com' \
+		'self_name_suppressed'
+"
+
+test_expect_success $PREREQ 'self name with dot is suppressed' "
+	test_suppress_self_quoted 'A U. Thor' 'author@example.com' \
+		'self_name_dot_suppressed'
+"
+
+test_expect_success $PREREQ 'non-ascii self name is suppressed' "
+	test_suppress_self_quoted 'Füñný Nâmé' 'odd_?=mail@example.com' \
+		'non_ascii_self_suppressed'
+"
+
+test_expect_success $PREREQ 'sanitized self name is suppressed' "
+	test_suppress_self_unquoted '\"A U. Thor\"' 'author@example.com' \
+		'self_name_sanitized_suppressed'
+"
+
 test_expect_success $PREREQ 'Show all headers' '
 	git send-email \
 		--dry-run \
@@ -881,6 +956,20 @@
 	grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1
 '
 
+test_expect_success $PREREQ 'utf8 sender is not duplicated' '
+	clean_fake_sendmail &&
+	test_commit weird_sender &&
+	test_when_finished "git reset --hard HEAD^" &&
+	git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+	git format-patch --stdout -1 >funny_name.patch &&
+	git send-email --from="Füñný Nâmé <odd_?=mail@example.com>" \
+	  --to=nobody@example.com \
+	  --smtp-server="$(pwd)/fake.sendmail" \
+	  funny_name.patch &&
+	grep "^From: " msgtxt1 >msgfrom &&
+	test_line_count = 1 msgfrom
+'
+
 test_expect_success $PREREQ 'sendemail.composeencoding works' '
 	clean_fake_sendmail &&
 	git config sendemail.composeencoding iso-8859-1 &&
@@ -1003,55 +1092,6 @@
 	grep "In-Reply-To: " stdout
 '
 
-test_expect_success $PREREQ 'warning with an implicit --chain-reply-to' '
-	git send-email \
-	--dry-run \
-	--from="Example <nobody@example.com>" \
-	--to=nobody@example.com \
-	outdir/000?-*.patch 2>errors >out &&
-	grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with an explicit --chain-reply-to' '
-	git send-email \
-	--dry-run \
-	--from="Example <nobody@example.com>" \
-	--to=nobody@example.com \
-	--chain-reply-to \
-	outdir/000?-*.patch 2>errors >out &&
-	! grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with an explicit --no-chain-reply-to' '
-	git send-email \
-	--dry-run \
-	--from="Example <nobody@example.com>" \
-	--to=nobody@example.com \
-	--nochain-reply-to \
-	outdir/000?-*.patch 2>errors >out &&
-	! grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = false' '
-	git config sendemail.chainreplyto false &&
-	git send-email \
-	--dry-run \
-	--from="Example <nobody@example.com>" \
-	--to=nobody@example.com \
-	outdir/000?-*.patch 2>errors >out &&
-	! grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = true' '
-	git config sendemail.chainreplyto true &&
-	git send-email \
-	--dry-run \
-	--from="Example <nobody@example.com>" \
-	--to=nobody@example.com \
-	outdir/000?-*.patch 2>errors >out &&
-	! grep "no-chain-reply-to" errors
-'
-
 test_expect_success $PREREQ 'sendemail.to works' '
 	git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
 	git send-email \
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 3077851..f524d2f 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -48,7 +48,7 @@
 test_expect_success 'setup git mirror and merge' '
 	git svn init "$svnrepo" -t tags -T trunk -b branches &&
 	git svn fetch &&
-	git checkout --track -b svn remotes/trunk &&
+	git checkout -b svn remotes/trunk &&
 	git checkout -b merge &&
 	echo new file > new_file &&
 	git add new_file &&
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 2471bc6..34c2d8f 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -396,7 +396,7 @@
 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_expect_success SYMLINKS 'directory becomes symlink'        '
+test_expect_success 'directory becomes symlink'        '
 	git init dirtosymlink &&
 	git init result &&
 	(
@@ -408,8 +408,7 @@
 		git add foo/world bar/world &&
 		git commit -q -mone &&
 		git rm -r foo &&
-		ln -s bar foo &&
-		git add foo &&
+		test_ln_s_add bar foo &&
 		git commit -q -mtwo
 	) &&
 	(
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 735a018..db69af2 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -330,7 +330,7 @@
 
 test_expect_success 'validate basic diffs saved during above cvswork2 edits' '
 	test $(grep Index: cvsEdit1.diff | wc -l) = 1 &&
-	test ! -s cvsEdit2-empty.diff &&
+	test_must_be_empty cvsEdit2-empty.diff &&
 	test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 &&
 	test $(grep Index: cvsEdit3.diff | wc -l) = 3 &&
 	rm -rf diffSandbox &&
@@ -456,20 +456,20 @@
 
 test_expect_success 'cvs diff -r v1 -u' '
 	( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log &&
-	test ! -s cvsDiff.out &&
-	test ! -s cvs.log
+	test_must_be_empty cvsDiff.out &&
+	test_must_be_empty cvs.log
 '
 
 test_expect_success 'cvs diff -N -r v2 -u' '
 	( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log &&
-	test ! -s cvs.log &&
+	test_must_be_empty cvs.log &&
 	test -s cvsDiff.out &&
 	check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1
 '
 
 test_expect_success 'cvs diff -N -r v2 -r v1.2' '
 	( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log &&
-	test ! -s cvs.log &&
+	test_must_be_empty cvs.log &&
 	test -s cvsDiff.out &&
 	check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1
 '
@@ -488,7 +488,7 @@
 
 test_expect_success 'check [cvswork3] diff' '
 	( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log &&
-	test ! -s cvs.log &&
+	test_must_be_empty cvs.log &&
 	test -s cvsDiff.out &&
 	test $(grep Index: cvsDiff.out | wc -l) = 3 &&
 	test_cmp cvsDiff.out cvswork3edit.diff &&
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 6783c14..6fca193 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -156,10 +156,10 @@
 	 git commit -a -m "File renamed." &&
 	 gitweb_run "p=.git;a=commitdiff"'
 
-test_expect_success SYMLINKS \
+test_expect_success \
 	'commitdiff(0): file to symlink' \
 	'rm renamed_file &&
-	 ln -s file renamed_file &&
+	 test_ln_s_add file renamed_file &&
 	 git commit -a -m "File to symlink." &&
 	 gitweb_run "p=.git;a=commitdiff"'
 
@@ -212,15 +212,14 @@
 # ----------------------------------------------------------------------
 # commitdiff testing (taken from t4114-apply-typechange.sh)
 
-test_expect_success SYMLINKS 'setup typechange commits' '
+test_expect_success 'setup typechange commits' '
 	echo "hello world" > foo &&
 	echo "hi planet" > bar &&
 	git update-index --add foo bar &&
 	git commit -m initial &&
 	git branch initial &&
 	rm -f foo &&
-	ln -s bar foo &&
-	git update-index foo &&
+	test_ln_s_add bar foo &&
 	git commit -m "foo symlinked to bar" &&
 	git branch foo-symlinked-to-bar &&
 	rm -f foo &&
@@ -361,11 +360,7 @@
 	 echo "Changed" >> 04-rename-to &&
 	 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 &&
+	 test_ln_s_add 01-change 06-file-or-symlink &&
 	 echo "Changed and have mode changed" > 07-change-mode-change	&&
 	 test_chmod +x 07-change-mode-change &&
 	 git commit -a -m "Large commit" &&
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 9730821..2bf142d 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -469,9 +469,11 @@
 		View: //depot/usecs/b1/... //depot/usecs/b3/...
 		EOF
 
-		echo b3/b3-file3 >b3/b3-file3 &&
-		p4 add b3/b3-file3 &&
-		p4 submit -d "b3/b3-file3"
+		echo b3/b3-file3_1 >b3/b3-file3_1 &&
+		echo b3/b3-file3_2 >b3/b3-file3_2 &&
+		p4 add b3/b3-file3_1 &&
+		p4 add b3/b3-file3_2 &&
+		p4 submit -d "b3/b3-file3_1 b3/b3-file3_2"
 	)
 '
 
@@ -487,6 +489,21 @@
 	)
 '
 
+test_expect_success 'use-client-spec detect-branches skips files in branches' '
+	client_view "//depot/usecs/... //client/..." \
+		    "-//depot/usecs/b3/b3-file3_1 //client/b3/b3-file3_1" &&
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+		git checkout -b master p4/usecs/b3 &&
+		test_path_is_file b1-file1 &&
+		test_path_is_file b3-file3_2 &&
+		test_path_is_missing b3-file3_1
+	)
+'
+
 test_expect_success 'kill p4d' '
 	kill_p4d
 '
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index eeefa67..b0d1d94 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -95,7 +95,7 @@
 		git init &&
 		echo "* eol=lf" >.gitattributes &&
 		git p4 sync //depot@all &&
-		git checkout master &&
+		git checkout -b master p4/master &&
 		test_cmp "$cli"/f-unix-orig f-unix &&
 		test_cmp "$cli"/f-win-as-lf f-win
 	)
@@ -109,7 +109,7 @@
 		git init &&
 		echo "* eol=crlf" >.gitattributes &&
 		git p4 sync //depot@all &&
-		git checkout master &&
+		git checkout -b master p4/master &&
 		test_cmp "$cli"/f-unix-as-crlf f-unix &&
 		test_cmp "$cli"/f-win-orig f-win
 	)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 6d9d141..272a071 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -69,7 +69,7 @@
 	local -a COMPREPLY _words
 	local _cword
 	_words=( $1 )
-	test "${1: -1}" == ' ' && _words+=('')
+	test "${1: -1}" = ' ' && _words+=('')
 	(( _cword = ${#_words[@]} - 1 ))
 	__git_wrap__git_main && print_comp
 }
@@ -122,6 +122,140 @@
 
 invalid_variable_name='${foo.bar}'
 
+actual="$TRASH_DIRECTORY/actual"
+
+test_expect_success 'setup for __gitdir tests' '
+	mkdir -p subdir/subsubdir &&
+	git init otherrepo
+'
+
+test_expect_success '__gitdir - from command line (through $__git_dir)' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+	(
+		__git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - repo as argument' '
+	echo "otherrepo/.git" >expected &&
+	__gitdir "otherrepo" >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - remote as argument' '
+	echo "remote" >expected &&
+	__gitdir "remote" >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in cwd' '
+	echo ".git" >expected &&
+	__gitdir >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in parent' '
+	echo "$(pwd -P)/.git" >expected &&
+	(
+		cd subdir/subsubdir &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - cwd is a .git directory' '
+	echo "." >expected &&
+	(
+		cd .git &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - parent is a .git directory' '
+	echo "$(pwd -P)/.git" >expected &&
+	(
+		cd .git/refs/heads &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+	(
+		GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+		export GIT_DIR &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
+	echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+	(
+		GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+		export GIT_DIR &&
+		cd subdir &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - non-existing $GIT_DIR' '
+	(
+		GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+		export GIT_DIR &&
+		test_must_fail __gitdir
+	)
+'
+
+test_expect_success '__gitdir - gitfile in cwd' '
+	echo "$(pwd -P)/otherrepo/.git" >expected &&
+	echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+	test_when_finished "rm -f subdir/.git" &&
+	(
+		cd subdir &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - gitfile in parent' '
+	echo "$(pwd -P)/otherrepo/.git" >expected &&
+	echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+	test_when_finished "rm -f subdir/.git" &&
+	(
+		cd subdir/subsubdir &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
+	echo "$(pwd -P)/otherrepo/.git" >expected &&
+	mkdir otherrepo/dir &&
+	test_when_finished "rm -rf otherrepo/dir" &&
+	ln -s otherrepo/dir link &&
+	test_when_finished "rm -f link" &&
+	(
+		cd link &&
+		__gitdir >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - not a git repository' '
+	(
+		cd subdir/subsubdir &&
+		GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
+		export GIT_CEILING_DIRECTORIES &&
+		test_must_fail __gitdir
+	)
+'
+
 test_expect_success '__gitcomp - trailing space - options' '
 	test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
 		--reset-author" <<-EOF
@@ -231,6 +365,7 @@
 	--exec-path Z
 	--exec-path=
 	--html-path Z
+	--man-path Z
 	--info-path Z
 	--work-tree=
 	--namespace=
@@ -347,4 +482,81 @@
 	test_completion "git send-email ma" "master "
 '
 
+test_expect_success 'complete files' '
+	git init tmp && cd tmp &&
+	test_when_finished "cd .. && rm -rf tmp" &&
+
+	echo "expected" > .gitignore &&
+	echo "out" >> .gitignore &&
+
+	git add .gitignore &&
+	test_completion "git commit " ".gitignore" &&
+
+	git commit -m ignore &&
+
+	touch new &&
+	test_completion "git add " "new" &&
+
+	git add new &&
+	git commit -a -m new &&
+	test_completion "git add " "" &&
+
+	git mv new modified &&
+	echo modify > modified &&
+	test_completion "git add " "modified" &&
+
+	touch untracked &&
+
+	: TODO .gitignore should not be here &&
+	test_completion "git rm " <<-\EOF &&
+	.gitignore
+	modified
+	EOF
+
+	test_completion "git clean " "untracked" &&
+
+	: TODO .gitignore should not be here &&
+	test_completion "git mv " <<-\EOF &&
+	.gitignore
+	modified
+	EOF
+
+	mkdir dir &&
+	touch dir/file-in-dir &&
+	git add dir/file-in-dir &&
+	git commit -m dir &&
+
+	mkdir untracked-dir &&
+
+	: TODO .gitignore should not be here &&
+	test_completion "git mv modified " <<-\EOF &&
+	.gitignore
+	dir
+	modified
+	untracked
+	untracked-dir
+	EOF
+
+	test_completion "git commit " "modified" &&
+
+	: TODO .gitignore should not be here &&
+	test_completion "git ls-files " <<-\EOF
+	.gitignore
+	dir
+	modified
+	EOF
+
+	touch momified &&
+	test_completion "git add mom" "momified"
+'
+
+test_expect_failure 'complete with tilde expansion' '
+	git init tmp && cd tmp &&
+	test_when_finished "cd .. && rm -rf tmp" &&
+
+	touch ~/tmp/file &&
+
+	test_completion "git add ~/tmp/" "~/tmp/file"
+'
+
 test_done
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 083b319..3c3e4e8 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -10,528 +10,558 @@
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
+c_red='\\[\\e[31m\\]'
+c_green='\\[\\e[32m\\]'
+c_lblue='\\[\\e[1;34m\\]'
+c_clear='\\[\\e[0m\\]'
 
 test_expect_success 'setup for prompt tests' '
-	mkdir -p subdir/subsubdir &&
 	git init otherrepo &&
-	echo 1 > file &&
+	echo 1 >file &&
 	git add file &&
 	test_tick &&
 	git commit -m initial &&
 	git tag -a -m msg1 t1 &&
 	git checkout -b b1 &&
-	echo 2 > file &&
+	echo 2 >file &&
 	git commit -m "second b1" file &&
-	echo 3 > file &&
+	echo 3 >file &&
 	git commit -m "third b1" file &&
 	git tag -a -m msg2 t2 &&
 	git checkout -b b2 master &&
-	echo 0 > file &&
+	echo 0 >file &&
 	git commit -m "second b2" file &&
-	echo 00 > file &&
+	echo 00 >file &&
 	git commit -m "another b2" file &&
-	echo 000 > file &&
+	echo 000 >file &&
 	git commit -m "yet another b2" file &&
 	git checkout master
 '
 
-test_expect_success 'gitdir - from command line (through $__git_dir)' '
-	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-	(
-		__git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - repo as argument' '
-	echo "otherrepo/.git" > expected &&
-	__gitdir "otherrepo" > "$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - remote as argument' '
-	echo "remote" > expected &&
-	__gitdir "remote" > "$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in cwd' '
-	echo ".git" > expected &&
-	__gitdir > "$actual" &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in parent' '
-	echo "$(pwd -P)/.git" > expected &&
-	(
-		cd subdir/subsubdir &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - cwd is a .git directory' '
-	echo "." > expected &&
-	(
-		cd .git &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - parent is a .git directory' '
-	echo "$(pwd -P)/.git" > expected &&
-	(
-		cd .git/refs/heads &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' '
-	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-	(
-		GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
-		export GIT_DIR &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' '
-	echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-	(
-		GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
-		export GIT_DIR &&
-		cd subdir &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - non-existing $GIT_DIR' '
-	(
-		GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
-		export GIT_DIR &&
-		test_must_fail __gitdir
-	)
-'
-
-test_expect_success 'gitdir - gitfile in cwd' '
-	echo "$(pwd -P)/otherrepo/.git" > expected &&
-	echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
-	test_when_finished "rm -f subdir/.git" &&
-	(
-		cd subdir &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - gitfile in parent' '
-	echo "$(pwd -P)/otherrepo/.git" > expected &&
-	echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
-	test_when_finished "rm -f subdir/.git" &&
-	(
-		cd subdir/subsubdir &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' '
-	echo "$(pwd -P)/otherrepo/.git" > expected &&
-	mkdir otherrepo/dir &&
-	test_when_finished "rm -rf otherrepo/dir" &&
-	ln -s otherrepo/dir link &&
-	test_when_finished "rm -f link" &&
-	(
-		cd link &&
-		__gitdir > "$actual"
-	) &&
-	test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - not a git repository' '
-	(
-		cd subdir/subsubdir &&
-		GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
-		export GIT_CEILING_DIRECTORIES &&
-		test_must_fail __gitdir
-	)
-'
-
 test_expect_success 'prompt - branch name' '
-	printf " (master)" > expected &&
-	__git_ps1 > "$actual" &&
+	printf " (master)" >expected &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+	printf " (master)" >expected &&
+	test_when_finished "git checkout master" &&
+	test_config core.preferSymlinkRefs true &&
+	git checkout master &&
+	__git_ps1 >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - unborn branch' '
+	printf " (unborn)" >expected &&
+	git checkout --orphan unborn &&
+	test_when_finished "git checkout master" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - detached head' '
-	printf " ((%s...))" $(git log -1 --format="%h" b1^) > expected &&
+	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+	test_config core.abbrev 13 &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - contains' '
-	printf " ((t2~1))" > expected &&
+	printf " ((t2~1))" >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
 	(
 		GIT_PS1_DESCRIBE_STYLE=contains &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - branch' '
-	printf " ((b1~1))" > expected &&
+	printf " ((b1~1))" >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
 	(
 		GIT_PS1_DESCRIBE_STYLE=branch &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - describe' '
-	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) > expected &&
+	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
 	git checkout b1^ &&
 	test_when_finished "git checkout master" &&
 	(
 		GIT_PS1_DESCRIBE_STYLE=describe &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - default' '
-	printf " ((t2))" > expected &&
+	printf " ((t2))" >expected &&
 	git checkout --detach b1 &&
 	test_when_finished "git checkout master" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - inside .git directory' '
-	printf " (GIT_DIR!)" > expected &&
+	printf " (GIT_DIR!)" >expected &&
 	(
 		cd .git &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - deep inside .git directory' '
-	printf " (GIT_DIR!)" > expected &&
+	printf " (GIT_DIR!)" >expected &&
 	(
 		cd .git/refs/heads &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - inside bare repository' '
-	printf " (BARE:master)" > expected &&
+	printf " (BARE:master)" >expected &&
 	git init --bare bare.git &&
 	test_when_finished "rm -rf bare.git" &&
 	(
 		cd bare.git &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - interactive rebase' '
-	printf " (b1|REBASE-i 2/3)" > expected
-	echo "#!$SHELL_PATH" >fake_editor.sh &&
-	cat >>fake_editor.sh <<\EOF &&
-echo "exec echo" > "$1"
-echo "edit $(git log -1 --format="%h")" >> "$1"
-echo "exec echo" >> "$1"
-EOF
+	printf " (b1|REBASE-i 2/3)" >expected
+	write_script fake_editor.sh <<-\EOF &&
+		echo "exec echo" >"$1"
+		echo "edit $(git log -1 --format="%h")" >>"$1"
+		echo "exec echo" >>"$1"
+	EOF
 	test_when_finished "rm -f fake_editor.sh" &&
-	chmod a+x fake_editor.sh &&
 	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
 	git checkout b1 &&
 	test_when_finished "git checkout master" &&
 	git rebase -i HEAD^ &&
 	test_when_finished "git rebase --abort"
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - rebase merge' '
-	printf " (b2|REBASE-m 1/3)" > expected &&
+	printf " (b2|REBASE-m 1/3)" >expected &&
 	git checkout b2 &&
 	test_when_finished "git checkout master" &&
 	test_must_fail git rebase --merge b1 b2 &&
 	test_when_finished "git rebase --abort" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - rebase' '
-	printf " ((t2)|REBASE 1/3)" > expected &&
+	printf " (b2|REBASE 1/3)" >expected &&
 	git checkout b2 &&
 	test_when_finished "git checkout master" &&
 	test_must_fail git rebase b1 b2 &&
 	test_when_finished "git rebase --abort" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - merge' '
-	printf " (b1|MERGING)" > expected &&
+	printf " (b1|MERGING)" >expected &&
 	git checkout b1 &&
 	test_when_finished "git checkout master" &&
 	test_must_fail git merge b2 &&
 	test_when_finished "git reset --hard" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - cherry-pick' '
-	printf " (master|CHERRY-PICKING)" > expected &&
+	printf " (master|CHERRY-PICKING)" >expected &&
 	test_must_fail git cherry-pick b1 &&
 	test_when_finished "git reset --hard" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bisect' '
-	printf " (master|BISECTING)" > expected &&
+	printf " (master|BISECTING)" >expected &&
 	git bisect start &&
 	test_when_finished "git bisect reset" &&
-	__git_ps1 > "$actual" &&
+	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - clean' '
-	printf " (master)" > expected &&
+	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty worktree' '
-	printf " (master *)" > expected &&
-	echo "dirty" > file &&
+	printf " (master *)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index' '
-	printf " (master +)" > expected &&
-	echo "dirty" > file &&
+	printf " (master +)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	git add -u &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
-	printf " (master *+)" > expected &&
-	echo "dirty index" > file &&
+	printf " (master *+)" >expected &&
+	echo "dirty index" >file &&
 	test_when_finished "git reset --hard" &&
 	git add -u &&
-	echo "dirty worktree" > file &&
+	echo "dirty worktree" >file &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - before root commit' '
-	printf " (master #)" > expected &&
+	printf " (master #)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
 		cd otherrepo &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
-	printf " (master)" > expected &&
-	echo "dirty" > file &&
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState false &&
 	(
 		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
-	printf " (master)" > expected &&
-	echo "dirty" > file &&
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState true &&
 	(
 		sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
-	printf " (master)" > expected &&
-	echo "dirty" > file &&
+	printf " (master)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState false &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
-	printf " (master *)" > expected &&
-	echo "dirty" > file &&
+	printf " (master *)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState true &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
-	printf " (GIT_DIR!)" > expected &&
-	echo "dirty" > file &&
+	printf " (GIT_DIR!)" >expected &&
+	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
 		cd .git &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - no stash' '
-	printf " (master)" > expected &&
+	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - stash' '
-	printf " (master $)" > expected &&
+	printf " (master $)" >expected &&
 	echo 2 >file &&
 	git stash &&
 	test_when_finished "git stash drop" &&
+	git pack-refs --all &&
 	(
 		GIT_PS1_SHOWSTASHSTATE=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
-	printf " (GIT_DIR!)" > expected &&
+	printf " (GIT_DIR!)" >expected &&
 	echo 2 >file &&
 	git stash &&
 	test_when_finished "git stash drop" &&
 	(
 		GIT_PS1_SHOWSTASHSTATE=y &&
 		cd .git &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - no untracked files' '
-	printf " (master)" > expected &&
+	printf " (master)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
 		cd otherrepo &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - untracked files' '
-	printf " (master %%)" > expected &&
+	printf " (master %%)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
-	printf " (master)" > expected &&
+	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
 		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
-	printf " (master)" > expected &&
+	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
 		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
-	printf " (master)" > expected &&
+	printf " (master)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
-	printf " (master %%)" > expected &&
+	printf " (master %%)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
-	printf " (GIT_DIR!)" > expected &&
+	printf " (GIT_DIR!)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
 		cd .git &&
-		__git_ps1 > "$actual"
+		__git_ps1 >"$actual"
 	) &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - format string starting with dash' '
-	printf -- "-master" > expected &&
-	__git_ps1 "-%s" > "$actual" &&
+	printf -- "-master" >expected &&
+	__git_ps1 "-%s" >"$actual" &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - pc mode' '
+	printf "BEFORE: (master):AFTER" >expected &&
+	printf "" >expected_output &&
+	(
+		__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+		test_cmp expected_output "$actual" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - branch name' '
+	printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected &&
+	(
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - detached head' '
+	printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected &&
+	git checkout b1^ &&
+	test_when_finished "git checkout master" &&
+	(
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+	printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+	printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+	printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected &&
+	echo "dirty index" >file &&
+	test_when_finished "git reset --hard" &&
+	git add -u &&
+	echo "dirty worktree" >file &&
+	(
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+	printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		cd otherrepo &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+	printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected &&
+	echo "dirty" >file &&
+	test_when_finished "git reset --hard" &&
+	(
+		GIT_PS1_SHOWDIRTYSTATE=y &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		cd .git &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+	printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected &&
+	echo 2 >file &&
+	git stash &&
+	test_when_finished "git stash drop" &&
+	(
+		GIT_PS1_SHOWSTASHSTATE=y &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+	printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected &&
+	(
+		GIT_PS1_SHOWUNTRACKEDFILES=y &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" &&
+		printf "%s" "$PS1" >"$actual"
+	) &&
+	test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - zsh color pc mode' '
+	printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+	(
+		ZSH_VERSION=5.0.0 &&
+		GIT_PS1_SHOWCOLORHINTS=y &&
+		__git_ps1 "BEFORE:" ":AFTER" >"$actual"
+		printf "%s" "$PS1" >"$actual"
+	) &&
 	test_cmp expected "$actual"
 '
 
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 5251009..a7e9aac 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -343,6 +343,7 @@
 }
 
 test_expect_failure () {
+	test_start_
 	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
 	test "$#" = 2 ||
 	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
@@ -357,10 +358,11 @@
 			test_known_broken_failure_ "$1"
 		fi
 	fi
-	echo >&3 ""
+	test_finish_
 }
 
 test_expect_success () {
+	test_start_
 	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
 	test "$#" = 2 ||
 	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -375,7 +377,7 @@
 			test_failure_ "$@"
 		fi
 	fi
-	echo >&3 ""
+	test_finish_
 }
 
 # test_external runs external test scripts that provide continuous
@@ -609,6 +611,18 @@
 	$GIT_TEST_CMP "$@"
 }
 
+# Check if the file expected to be empty is indeed empty, and barfs
+# otherwise.
+
+test_must_be_empty () {
+	if test -s "$1"
+	then
+		echo "'$1' is not empty, it contains:"
+		cat "$1"
+		return 1
+	fi
+}
+
 # Tests that its two parameters refer to the same revision
 test_cmp_rev () {
 	git rev-parse --verify "$1" >expect.rev &&
@@ -679,3 +693,20 @@
 		mv .git/hooks .git/hooks-disabled
 	) || exit
 }
+
+# This function helps on symlink challenged file systems when it is not
+# important that the file system entry is a symbolic link.
+# Use test_ln_s_add instead of "ln -s x y && git add y" to add a
+# symbolic link entry y to the index.
+
+test_ln_s_add () {
+	if test_have_prereq SYMLINKS
+	then
+		ln -s "$1" "$2" &&
+		git update-index --add "$2"
+	else
+		printf '%s' "$1" >"$2" &&
+		ln_s_obj=$(git hash-object -w "$2") &&
+		git update-index --add --cacheinfo 120000 $ln_s_obj "$2"
+	fi
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index ca6bdef..1aa27bd 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -54,8 +54,8 @@
 	# do not redirect again
 	;;
 *' --tee '*|*' --va'*)
-	mkdir -p test-results
-	BASE=test-results/$(basename "$0" .sh)
+	mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
+	BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
 	(GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
 	 echo $? > $BASE.exit) | tee $BASE.out
 	test "$(cat $BASE.exit)" = 0
@@ -92,6 +92,7 @@
 	print join("\n", @vars);
 ')
 unset XDG_CONFIG_HOME
+unset GITPERLLIB
 GIT_AUTHOR_EMAIL=author@example.com
 GIT_AUTHOR_NAME='A U Thor'
 GIT_COMMITTER_EMAIL=committer@example.com
@@ -184,6 +185,9 @@
 		help=t; shift ;;
 	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
 		verbose=t; shift ;;
+	--verbose-only=*)
+		verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
 	-q|--q|--qu|--qui|--quie|--quiet)
 		# Ignore --quiet under a TAP::Harness. Saying how many tests
 		# passed without the ok/not ok details is always an error.
@@ -198,17 +202,39 @@
 	--valgrind=*)
 		valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
 		shift ;;
+	--valgrind-only=*)
+		valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
+	--valgrind-parallel=*)
+		valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
+	--valgrind-only-stride=*)
+		valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
+	--valgrind-only-offset=*)
+		valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
 	--tee)
 		shift ;; # was handled already
 	--root=*)
 		root=$(expr "z$1" : 'z[^=]*=\(.*\)')
 		shift ;;
+	--statusprefix=*)
+		statusprefix=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
 	*)
 		echo "error: unknown test option '$1'" >&2; exit 1 ;;
 	esac
 done
 
-test -n "$valgrind" && verbose=t
+if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+then
+	test -z "$valgrind" && valgrind=memcheck
+	test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+	verbose=t
+fi
 
 if test -n "$color"
 then
@@ -303,12 +329,12 @@
 
 test_ok_ () {
 	test_success=$(($test_success + 1))
-	say_color "" "ok $test_count - $@"
+	say_color "" "${statusprefix}ok $test_count - $@"
 }
 
 test_failure_ () {
 	test_failure=$(($test_failure + 1))
-	say_color error "not ok $test_count - $1"
+	say_color error "${statusprefix}not ok $test_count - $1"
 	shift
 	echo "$@" | sed -e 's/^/#	/'
 	test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
@@ -316,18 +342,83 @@
 
 test_known_broken_ok_ () {
 	test_fixed=$(($test_fixed+1))
-	say_color error "ok $test_count - $@ # TODO known breakage vanished"
+	say_color error "${statusprefix}ok $test_count - $@ # TODO known breakage vanished"
 }
 
 test_known_broken_failure_ () {
 	test_broken=$(($test_broken+1))
-	say_color warn "not ok $test_count - $@ # TODO known breakage"
+	say_color warn "${statusprefix}not ok $test_count - $@ # TODO known breakage"
 }
 
 test_debug () {
 	test "$debug" = "" || eval "$1"
 }
 
+match_pattern_list () {
+	arg="$1"
+	shift
+	test -z "$*" && return 1
+	for pattern_
+	do
+		case "$arg" in
+		$pattern_)
+			return 0
+		esac
+	done
+	return 1
+}
+
+maybe_teardown_verbose () {
+	test -z "$verbose_only" && return
+	exec 4>/dev/null 3>/dev/null
+	verbose=
+}
+
+last_verbose=t
+maybe_setup_verbose () {
+	test -z "$verbose_only" && return
+	if match_pattern_list $test_count $verbose_only ||
+		{ test -n "$valgrind_only_stride" &&
+		expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; }
+	then
+		exec 4>&2 3>&1
+		# Emit a delimiting blank line when going from
+		# non-verbose to verbose.  Within verbose mode the
+		# delimiter is printed by test_expect_*.  The choice
+		# of the initial $last_verbose is such that before
+		# test 1, we do not print it.
+		test -z "$last_verbose" && echo >&3 ""
+		verbose=t
+	else
+		exec 4>/dev/null 3>/dev/null
+		verbose=
+	fi
+	last_verbose=$verbose
+}
+
+maybe_teardown_valgrind () {
+	test -z "$GIT_VALGRIND" && return
+	GIT_VALGRIND_ENABLED=
+}
+
+maybe_setup_valgrind () {
+	test -z "$GIT_VALGRIND" && return
+	if test -z "$valgrind_only" && test -z "$valgrind_only_stride"
+	then
+		GIT_VALGRIND_ENABLED=t
+		return
+	fi
+	GIT_VALGRIND_ENABLED=
+	if match_pattern_list $test_count $valgrind_only
+	then
+		GIT_VALGRIND_ENABLED=t
+	elif test -n "$valgrind_only_stride" &&
+		expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null
+	then
+		GIT_VALGRIND_ENABLED=t
+	fi
+}
+
 test_eval_ () {
 	# This is a separate function because some tests use
 	# "return" to end a test_expect_success block early.
@@ -337,8 +428,10 @@
 test_run_ () {
 	test_cleanup=:
 	expecting_failure=$2
+	setup_malloc_check
 	test_eval_ "$1"
 	eval_ret=$?
+	teardown_malloc_check
 
 	if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
 	then
@@ -353,17 +446,24 @@
 	return "$eval_ret"
 }
 
-test_skip () {
+test_start_ () {
 	test_count=$(($test_count+1))
+	maybe_setup_verbose
+	maybe_setup_valgrind
+}
+
+test_finish_ () {
+	echo >&3 ""
+	maybe_teardown_valgrind
+	maybe_teardown_verbose
+}
+
+test_skip () {
 	to_skip=
-	for skp in $GIT_SKIP_TESTS
-	do
-		case $this_test.$test_count in
-		$skp)
-			to_skip=t
-			break
-		esac
-	done
+	if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+	then
+		to_skip=t
+	fi
 	if test -z "$to_skip" && test -n "$test_prereq" &&
 	   ! test_have_prereq "$test_prereq"
 	then
@@ -377,8 +477,8 @@
 			of_prereq=" of $test_prereq"
 		fi
 
-		say_color skip >&3 "skipping test: $@"
-		say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
+		say_color skip >&3 "${statusprefix}skipping test: $@"
+		say_color skip "${statusprefix}ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
 		: true
 		;;
 	*)
@@ -395,6 +495,8 @@
 test_done () {
 	GIT_EXIT_OK=t
 
+	# Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
+	# output file
 	if test -z "$HARNESS_ACTIVE"
 	then
 		test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
@@ -414,11 +516,11 @@
 
 	if test "$test_fixed" != 0
 	then
-		say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
+		say_color error "${statusprefix}# $test_fixed known breakage(s) vanished; please update test(s)"
 	fi
 	if test "$test_broken" != 0
 	then
-		say_color warn "# still have $test_broken known breakage(s)"
+		say_color warn "${statusprefix}# still have $test_broken known breakage(s)"
 	fi
 	if test "$test_broken" != 0 || test "$test_fixed" != 0
 	then
@@ -441,9 +543,9 @@
 		then
 			if test $test_remaining -gt 0
 			then
-				say_color pass "# passed all $msg"
+				say_color pass "${statusprefix}# passed all $msg"
 			fi
-			say "1..$test_count$skip_all"
+			say "${statusprefix}1..$test_count$skip_all"
 		fi
 
 		test -d "$remove_trash" &&
@@ -457,8 +559,8 @@
 	*)
 		if test $test_external_has_tap -eq 0
 		then
-			say_color error "# failed $test_failure among $msg"
-			say "1..$test_count"
+			say_color error "${statusprefix}# failed $test_failure among $msg"
+			say "${statusprefix}1..$test_count"
 		fi
 
 		exit 1 ;;
@@ -466,6 +568,9 @@
 	esac
 }
 
+
+# Set up a directory that we can put in PATH which redirects all git
+# calls to 'valgrind git ...'.
 if test -n "$valgrind"
 then
 	make_symlink () {
@@ -513,31 +618,43 @@
 		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 $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
-	do
-		make_valgrind_symlink $file
-	done
-	# special-case the mergetools loadables
-	make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
-	OLDIFS=$IFS
-	IFS=:
-	for path in $PATH
-	do
-		ls "$path"/git-* 2> /dev/null |
-		while read file
+	# In the case of --valgrind-parallel, we only need to do the
+	# wrapping once, in the main script.  The worker children all
+	# have $valgrind_only_stride set, so we can skip based on that.
+	if test -z "$valgrind_only_stride"
+	then
+		# override all git executables in TEST_DIRECTORY/..
+		GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+		mkdir -p "$GIT_VALGRIND"/bin
+		for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
 		do
-			make_valgrind_symlink "$file"
+			make_valgrind_symlink $file
 		done
-	done
-	IFS=$OLDIFS
+		# special-case the mergetools loadables
+		make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
+		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
+	fi
 	PATH=$GIT_VALGRIND/bin:$PATH
 	GIT_EXEC_PATH=$GIT_VALGRIND/bin
 	export GIT_VALGRIND
 	GIT_VALGRIND_MODE="$valgrind"
 	export GIT_VALGRIND_MODE
+	GIT_VALGRIND_ENABLED=t
+	if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+	then
+		GIT_VALGRIND_ENABLED=
+	fi
+	export GIT_VALGRIND_ENABLED
 elif test -n "$GIT_TEST_INSTALLED"
 then
 	GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
@@ -622,21 +739,53 @@
 else
 	mkdir -p "$TRASH_DIRECTORY"
 fi
+
+# Gross hack to spawn N sub-instances of the tests in parallel, and
+# summarize the results.  Note that if this is enabled, the script
+# terminates at the end of this 'if' block.
+if test -n "$valgrind_parallel"
+then
+	for i in $(test_seq 1 $valgrind_parallel)
+	do
+		root="$TRASH_DIRECTORY/vgparallel-$i"
+		mkdir "$root"
+		TEST_OUTPUT_DIRECTORY="$root" \
+			${SHELL_PATH} "$0" \
+			--root="$root" --statusprefix="[$i] " \
+			--valgrind="$valgrind" \
+			--valgrind-only-stride="$valgrind_parallel" \
+			--valgrind-only-offset="$i" &
+		pids="$pids $!"
+	done
+	trap "kill $pids" INT TERM HUP
+	wait $pids
+	trap - INT TERM HUP
+	for i in $(test_seq 1 $valgrind_parallel)
+	do
+		root="$TRASH_DIRECTORY/vgparallel-$i"
+		eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts |
+			sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')"
+		test_count=$(expr $test_count + $inner_total)
+		test_success=$(expr $test_success + $inner_success)
+		test_fixed=$(expr $test_fixed + $inner_fixed)
+		test_broken=$(expr $test_broken + $inner_broken)
+		test_failure=$(expr $test_failure + $inner_failed)
+	done
+	test_done
+fi
+
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$TRASH_DIRECTORY" || exit 1
 
 this_test=${0##*/}
 this_test=${this_test%%-*}
-for skp in $GIT_SKIP_TESTS
-do
-	case "$this_test" in
-	$skp)
-		say_color info >&3 "skipping test $this_test altogether"
-		skip_all="skip all tests in $this_test"
-		test_done
-	esac
-done
+if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+then
+	say_color info >&3 "skipping test $this_test altogether"
+	skip_all="skip all tests in $this_test"
+	test_done
+fi
 
 # Provide an implementation of the 'yes' utility
 yes () {
@@ -676,6 +825,7 @@
 	test_set_prereq MINGW
 	test_set_prereq NOT_CYGWIN
 	test_set_prereq SED_STRIPS_CR
+	test_set_prereq GREP_STRIPS_CR
 	;;
 *CYGWIN*)
 	test_set_prereq POSIXPERM
@@ -683,6 +833,7 @@
 	test_set_prereq NOT_MINGW
 	test_set_prereq CYGWIN
 	test_set_prereq SED_STRIPS_CR
+	test_set_prereq GREP_STRIPS_CR
 	;;
 *)
 	test_set_prereq POSIXPERM
@@ -737,7 +888,14 @@
 
 test_lazy_prereq PIPE '
 	# test whether the filesystem supports FIFOs
-	rm -f testfifo && mkfifo testfifo
+	case $(uname -s) in
+	CYGWIN*)
+		false
+		;;
+	*)
+		rm -f testfifo && mkfifo testfifo
+		;;
+	esac
 '
 
 test_lazy_prereq SYMLINKS '
diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh
index d8105d9..2ffc80f 100755
--- a/t/valgrind/analyze.sh
+++ b/t/valgrind/analyze.sh
@@ -1,6 +1,10 @@
 #!/bin/sh
 
-out_prefix=$(dirname "$0")/../test-results/valgrind.out
+# Get TEST_OUTPUT_DIRECTORY from GIT-BUILD-OPTIONS if it's there...
+. "$(dirname "$0")/../../GIT-BUILD-OPTIONS"
+# ... otherwise set it to the default value.
+: ${TEST_OUTPUT_DIRECTORY=$(dirname "$0")/..}
+
 output=
 count=0
 total_count=0
@@ -115,7 +119,7 @@
 	finish_output
 }
 
-for test_script in "$(dirname "$0")"/../test-results/*.out
+for test_script in "$TEST_OUTPUT_DIRECTORY"/test-results/*.out
 do
 	handle_one $test_script
 done
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
index 6b87c91..4215303 100755
--- a/t/valgrind/valgrind.sh
+++ b/t/valgrind/valgrind.sh
@@ -4,6 +4,9 @@
 
 TOOL_OPTIONS='--leak-check=no'
 
+test -z "$GIT_VALGRIND_ENABLED" &&
+exec "$GIT_VALGRIND"/../../"$base" "$@"
+
 case "$GIT_VALGRIND_MODE" in
 memcheck-fast)
 	;;
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
index 18c4829..586e3bf 100755
--- a/templates/hooks--pre-commit.sample
+++ b/templates/hooks--pre-commit.sample
@@ -15,13 +15,13 @@
 	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
 fi
 
-# If you want to allow non-ascii filenames set this variable to true.
+# If you want to allow non-ASCII filenames set this variable to true.
 allownonascii=$(git config hooks.allownonascii)
 
 # Redirect output to stderr.
 exec 1>&2
 
-# Cross platform projects tend to avoid non-ascii filenames; prevent
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
 # them from being added to the repository. We exploit the fact that the
 # printable range starts at the space character and ends with tilde.
 if [ "$allownonascii" != "true" ] &&
@@ -31,18 +31,17 @@
 	test $(git diff --cached --name-only --diff-filter=A -z $against |
 	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
-	echo "Error: Attempt to add a non-ascii file name."
-	echo
-	echo "This can cause problems if you want to work"
-	echo "with people on other platforms."
-	echo
-	echo "To be portable it is advisable to rename the file ..."
-	echo
-	echo "If you know what you are doing you can disable this"
-	echo "check using:"
-	echo
-	echo "  git config hooks.allownonascii true"
-	echo
+	cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+  git config hooks.allownonascii true
+EOF
 	exit 1
 fi
 
diff --git a/templates/hooks--pre-push.sample b/templates/hooks--pre-push.sample
old mode 100644
new mode 100755
diff --git a/test-chmtime.c b/test-chmtime.c
index 02b42ba..0df7487 100644
--- a/test-chmtime.c
+++ b/test-chmtime.c
@@ -56,7 +56,7 @@
 	return 1;
 }
 
-int main(int argc, const char *argv[])
+int main(int argc, char *argv[])
 {
 	static int verbose;
 
@@ -84,15 +84,15 @@
 		if (stat(argv[i], &sb) < 0) {
 			fprintf(stderr, "Failed to stat %s: %s\n",
 			        argv[i], strerror(errno));
-			return -1;
+			return 1;
 		}
 
-#ifdef WIN32
+#ifdef GIT_WINDOWS_NATIVE
 		if (!(sb.st_mode & S_IWUSR) &&
 				chmod(argv[i], sb.st_mode | S_IWUSR)) {
 			fprintf(stderr, "Could not make user-writable %s: %s",
 				argv[i], strerror(errno));
-			return -1;
+			return 1;
 		}
 #endif
 
@@ -107,7 +107,7 @@
 		if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
 			fprintf(stderr, "Failed to modify time on %s: %s\n",
 			        argv[i], strerror(errno));
-			return -1;
+			return 1;
 		}
 	}
 
@@ -115,5 +115,5 @@
 
 usage:
 	fprintf(stderr, "usage: %s %s\n", argv[0], usage_str);
-	return -1;
+	return 1;
 }
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
index a6ffdf3..47eab97 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -59,6 +59,8 @@
 	struct cache_tree *another = cache_tree();
 	if (read_cache() < 0)
 		die("unable to read index file");
-	cache_tree_update(another, active_cache, active_nr, WRITE_TREE_DRY_RUN);
+	cache_tree_update(another,
+			  (const struct cache_entry * const *)active_cache,
+			  active_nr, WRITE_TREE_DRY_RUN);
 	return dump_cache_tree(active_cache_tree, another, "");
 }
diff --git a/test-index-version.c b/test-index-version.c
index bfaad9e..05d4699 100644
--- a/test-index-version.c
+++ b/test-index-version.c
@@ -1,6 +1,6 @@
 #include "cache.h"
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	struct cache_header hdr;
 	int version;
diff --git a/test-mergesort.c b/test-mergesort.c
index 3f388b4..ea3b959 100644
--- a/test-mergesort.c
+++ b/test-mergesort.c
@@ -22,7 +22,7 @@
 	return strcmp(x->text, y->text);
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	struct line *line, *p = NULL, *lines = NULL;
 	struct strbuf sb = STRBUF_INIT;
diff --git a/test-parse-options.c b/test-parse-options.c
index 3c9510a..434e8b8 100644
--- a/test-parse-options.c
+++ b/test-parse-options.c
@@ -29,7 +29,7 @@
 	return 0;
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	const char *prefix = "prefix/";
 	const char *usage[] = {
@@ -81,7 +81,7 @@
 	};
 	int i;
 
-	argc = parse_options(argc, argv, prefix, options, usage, 0);
+	argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
 
 	printf("boolean: %d\n", boolean);
 	printf("integer: %u\n", integer);
diff --git a/test-path-utils.c b/test-path-utils.c
index 0092cbf..bb975e4 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -28,6 +28,19 @@
 	return 1;
 }
 
+static void normalize_argv_string(const char **var, const char *input)
+{
+	if (!strcmp(input, "<null>"))
+		*var = NULL;
+	else if (!strcmp(input, "<empty>"))
+		*var = "";
+	else
+		*var = input;
+
+	if (*var && (**var == '<' || **var == '('))
+		die("Bad value: %s\n", input);
+}
+
 int main(int argc, char **argv)
 {
 	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@@ -103,6 +116,25 @@
 		return 0;
 	}
 
+	if (argc == 3 && !strcmp(argv[1], "mingw_path")) {
+		puts(argv[2]);
+		return 0;
+	}
+
+	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+		struct strbuf sb = STRBUF_INIT;
+		const char *in, *prefix, *rel;
+		normalize_argv_string(&in, argv[2]);
+		normalize_argv_string(&prefix, argv[3]);
+		rel = relative_path(in, prefix, &sb);
+		if (!rel)
+			puts("(null)");
+		else
+			puts(strlen(rel) > 0 ? rel : "(empty)");
+		strbuf_release(&sb);
+		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-prio-queue.c b/test-prio-queue.c
new file mode 100644
index 0000000..7be72f0
--- /dev/null
+++ b/test-prio-queue.c
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data)
+{
+	const int *a = va, *b = vb;
+	return *a - *b;
+}
+
+static void show(int *v)
+{
+	if (!v)
+		printf("NULL\n");
+	else
+		printf("%d\n", *v);
+	free(v);
+}
+
+int main(int argc, char **argv)
+{
+	struct prio_queue pq = { intcmp };
+
+	while (*++argv) {
+		if (!strcmp(*argv, "get"))
+			show(prio_queue_get(&pq));
+		else if (!strcmp(*argv, "dump")) {
+			int *v;
+			while ((v = prio_queue_get(&pq)))
+			       show(v);
+		}
+		else {
+			int *v = malloc(sizeof(*v));
+			*v = atoi(*argv);
+			prio_queue_put(&pq, v);
+		}
+	}
+
+	return 0;
+}
diff --git a/test-read-cache.c b/test-read-cache.c
new file mode 100644
index 0000000..b25bcf1
--- /dev/null
+++ b/test-read-cache.c
@@ -0,0 +1,13 @@
+#include "cache.h"
+
+int main (int argc, char **argv)
+{
+	int i, cnt = 1;
+	if (argc == 2)
+		cnt = strtol(argv[1], NULL, 0);
+	for (i = 0; i < cnt; i++) {
+		read_cache();
+		discard_cache();
+	}
+	return 0;
+}
diff --git a/test-subprocess.c b/test-subprocess.c
index f2d4c0d..93525eb 100644
--- a/test-subprocess.c
+++ b/test-subprocess.c
@@ -1,7 +1,7 @@
 #include "cache.h"
 #include "run-command.h"
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
 	struct child_process cp;
 	int nogit = 0;
@@ -15,6 +15,6 @@
 	}
 	memset(&cp, 0, sizeof(cp));
 	cp.git_cmd = 1;
-	cp.argv = argv + 1;
+	cp.argv = (const char **)argv + 1;
 	return run_command(&cp);
 }
diff --git a/thread-utils.c b/thread-utils.c
index 7f4b76a..97396a7 100644
--- a/thread-utils.c
+++ b/thread-utils.c
@@ -24,7 +24,7 @@
 	long ncpus;
 #endif
 
-#ifdef _WIN32
+#ifdef GIT_WINDOWS_NATIVE
 	SYSTEM_INFO info;
 	GetSystemInfo(&info);
 
diff --git a/trace.c b/trace.c
index 5ec0e3b..3d744d1 100644
--- a/trace.c
+++ b/trace.c
@@ -75,6 +75,7 @@
 	strbuf_release(&buf);
 }
 
+__attribute__((format (printf, 2, 3)))
 static void trace_printf_key(const char *key, const char *fmt, ...)
 {
 	va_list ap;
diff --git a/transport-helper.c b/transport-helper.c
index 522d791..45a35df 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -11,6 +11,7 @@
 #include "thread-utils.h"
 #include "sigchain.h"
 #include "argv-array.h"
+#include "refs.h"
 
 static int debug;
 
@@ -47,7 +48,7 @@
 		die_errno("Full write to remote helper failed");
 }
 
-static int recvline_fh(FILE *helper, struct strbuf *buffer)
+static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name)
 {
 	strbuf_reset(buffer);
 	if (debug)
@@ -65,7 +66,7 @@
 
 static int recvline(struct helper_data *helper, struct strbuf *buffer)
 {
-	return recvline_fh(helper->out, buffer);
+	return recvline_fh(helper->out, buffer, helper->name);
 }
 
 static void xchgline(struct helper_data *helper, struct strbuf *buffer)
@@ -217,6 +218,8 @@
 		for (i = 0; i < refspec_nr; i++)
 			free((char *)refspecs[i]);
 		free(refspecs);
+	} else if (data->import || data->bidi_import || data->export) {
+		warning("This remote helper should implement refspec capability.");
 	}
 	strbuf_release(&buf);
 	if (debug)
@@ -473,7 +476,7 @@
 	 * were fetching.
 	 *
 	 * (If no "refspec" capability was specified, for historical
-	 * reasons we default to *:*.)
+	 * reasons we default to the equivalent of *:*.)
 	 *
 	 * Store the result in to_fetch[i].old_sha1.  Callers such
 	 * as "git fetch" can use the value to write feedback to the
@@ -540,7 +543,7 @@
 		goto exit;
 
 	sendline(data, &cmdbuf);
-	recvline_fh(input, &cmdbuf);
+	recvline_fh(input, &cmdbuf, name);
 	if (!strcmp(cmdbuf.buf, "")) {
 		data->no_disconnect_req = 1;
 		if (debug)
@@ -622,7 +625,7 @@
 	return -1;
 }
 
-static void push_update_ref_status(struct strbuf *buf,
+static int push_update_ref_status(struct strbuf *buf,
 				   struct ref **ref,
 				   struct ref *remote_refs)
 {
@@ -688,7 +691,7 @@
 		*ref = find_ref_by_name(remote_refs, refname);
 	if (!*ref) {
 		warning("helper reported unexpected status of %s", refname);
-		return;
+		return 1;
 	}
 
 	if ((*ref)->status != REF_STATUS_NONE) {
@@ -697,11 +700,12 @@
 		 * status reported by the remote helper if the latter is 'no match'.
 		 */
 		if (status == REF_STATUS_NONE)
-			return;
+			return 1;
 	}
 
 	(*ref)->status = status;
 	(*ref)->remote_status = msg;
+	return !(status == REF_STATUS_OK);
 }
 
 static void push_update_refs_status(struct helper_data *data,
@@ -710,11 +714,24 @@
 	struct strbuf buf = STRBUF_INIT;
 	struct ref *ref = remote_refs;
 	for (;;) {
+		char *private;
+
 		recvline(data, &buf);
 		if (!buf.len)
 			break;
 
-		push_update_ref_status(&buf, &ref, remote_refs);
+		if (push_update_ref_status(&buf, &ref, remote_refs))
+			continue;
+
+		if (!data->refspecs)
+			continue;
+
+		/* propagate back the update to the remote namespace */
+		private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
+		if (!private)
+			continue;
+		update_ref("update by helper", private, ref->new_sha1, NULL, 0, 0);
+		free(private);
 	}
 	strbuf_release(&buf);
 }
@@ -789,6 +806,14 @@
 	struct string_list revlist_args = STRING_LIST_INIT_NODUP;
 	struct strbuf buf = STRBUF_INIT;
 
+	if (!data->refspecs)
+		die("remote-helper doesn't support push; refspec needed");
+
+	if (flags & TRANSPORT_PUSH_DRY_RUN) {
+		if (set_helper_option(transport, "dry-run", "true") != 0)
+			die("helper %s does not support dry-run", data->name);
+	}
+
 	helper = get_helper(transport);
 
 	write_constant(helper->in, "export\n");
@@ -799,8 +824,9 @@
 		char *private;
 		unsigned char sha1[20];
 
-		if (!data->refspecs)
-			continue;
+		if (ref->deletion)
+			die("remote-helpers do not support ref deletion");
+
 		private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
 		if (private && !get_sha1(private, sha1)) {
 			strbuf_addf(&buf, "^%s", private);
@@ -809,13 +835,14 @@
 		}
 		free(private);
 
-		if (ref->deletion) {
+		if (ref->deletion)
 			die("remote-helpers do not support ref deletion");
-		}
 
-		if (ref->peer_ref)
+		if (ref->peer_ref) {
+			if (strcmp(ref->peer_ref->name, ref->name))
+				die("remote-helpers do not support old:new syntax");
 			string_list_append(&revlist_args, ref->peer_ref->name);
-
+		}
 	}
 
 	if (get_exporter(transport, &exporter, &revlist_args))
@@ -955,6 +982,7 @@
 #define PBUFFERSIZE 8192
 
 /* Print bidirectional transfer loop debug message. */
+__attribute__((format (printf, 1, 2)))
 static void transfer_debug(const char *fmt, ...)
 {
 	va_list args;
@@ -1040,7 +1068,7 @@
 		return -1;
 	} else if (bytes == 0) {
 		transfer_debug("%s EOF (with %i bytes in buffer)",
-			t->src_name, t->bufuse);
+			t->src_name, (int)t->bufuse);
 		t->state = SSTATE_FLUSHING;
 	} else if (bytes > 0) {
 		t->bufuse += bytes;
diff --git a/transport.c b/transport.c
index ba5d8af..e15db98 100644
--- a/transport.c
+++ b/transport.c
@@ -534,6 +534,8 @@
 	args.quiet = (transport->verbose < 0);
 	args.no_progress = !transport->progress;
 	args.depth = data->options.depth;
+	args.check_self_contained_and_connected =
+		data->options.check_self_contained_and_connected;
 
 	if (!data->got_remote_heads) {
 		connect_setup(transport, 0, 0);
@@ -551,6 +553,8 @@
 		refs = NULL;
 	data->conn = NULL;
 	data->got_remote_heads = 0;
+	data->options.self_contained_and_connected =
+		args.self_contained_and_connected;
 
 	free_refs(refs_tmp);
 
@@ -1228,7 +1232,7 @@
 		 * then local and remote refs are likely to still be equal.
 		 * Just feed them all to the fetch method in that case.
 		 * This condition shouldn't be met in a non-deepening fetch
-		 * (see builtin-fetch.c:quickfetch()).
+		 * (see builtin/fetch.c:quickfetch()).
 		 */
 		heads = xmalloc(nr_refs * sizeof(*heads));
 		for (rm = refs; rm; rm = rm->next)
diff --git a/transport.h b/transport.h
index fcb1d25..ea70ea7 100644
--- a/transport.h
+++ b/transport.h
@@ -8,6 +8,8 @@
 	unsigned thin : 1;
 	unsigned keep : 1;
 	unsigned followtags : 1;
+	unsigned check_self_contained_and_connected : 1;
+	unsigned self_contained_and_connected : 1;
 	int depth;
 	const char *uploadpack;
 	const char *receivepack;
@@ -168,7 +170,7 @@
 int transport_helper_init(struct transport *transport, const char *name);
 int bidirectional_transfer_loop(int input, int output);
 
-/* common methods used by transport.c and builtin-send-pack.c */
+/* common methods used by transport.c and builtin/send-pack.c */
 void transport_verify_remote_names(int nr_heads, const char **heads);
 
 void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);
diff --git a/tree-walk.c b/tree-walk.c
index 6e30ef9..c626135 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -323,7 +323,6 @@
 
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
-	int ret = 0;
 	int error = 0;
 	struct name_entry *entry = xmalloc(n*sizeof(*entry));
 	int i;
@@ -341,6 +340,7 @@
 		strbuf_setlen(&base, info->pathlen);
 	}
 	for (;;) {
+		int trees_used;
 		unsigned long mask, dirmask;
 		const char *first = NULL;
 		int first_len = 0;
@@ -404,15 +404,14 @@
 		if (interesting < 0)
 			break;
 		if (interesting) {
-			ret = info->fn(n, mask, dirmask, entry, info);
-			if (ret < 0) {
-				error = ret;
+			trees_used = info->fn(n, mask, dirmask, entry, info);
+			if (trees_used < 0) {
+				error = trees_used;
 				if (!info->show_all_errors)
 					break;
 			}
-			mask &= ret;
+			mask &= trees_used;
 		}
-		ret = 0;
 		for (i = 0; i < n; i++)
 			if (mask & (1ul << i))
 				update_extended_entry(tx + i, entry + i);
diff --git a/tree-walk.h b/tree-walk.h
index 2bf0db9..ae04b64 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -46,7 +46,7 @@
 	int pathlen;
 	struct pathspec *pathspec;
 
-	unsigned long conflicts;
+	unsigned long df_conflicts;
 	traverse_callback_t fn;
 	void *data;
 	int show_all_errors;
diff --git a/tree.c b/tree.c
index 62fed63..c0e568c 100644
--- a/tree.c
+++ b/tree.c
@@ -159,7 +159,7 @@
 	 * sort at the end.
 	 */
 	for (i = 0; !fn && i < active_nr; i++) {
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 		if (ce_stage(ce) == stage)
 			fn = read_one_entry;
 	}
diff --git a/unpack-trees.c b/unpack-trees.c
index ede4299..bf01717 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -116,14 +116,20 @@
 			ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 }
 
-static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
-	unsigned int set, unsigned int clear)
+static struct cache_entry *dup_entry(const struct cache_entry *ce)
 {
 	unsigned int size = ce_size(ce);
 	struct cache_entry *new = xmalloc(size);
 
 	memcpy(new, ce, size);
-	do_add_entry(o, new, set, clear);
+	return new;
+}
+
+static void add_entry(struct unpack_trees_options *o,
+		      const struct cache_entry *ce,
+		      unsigned int set, unsigned int clear)
+{
+	do_add_entry(o, dup_entry(ce), set, clear);
 }
 
 /*
@@ -173,7 +179,7 @@
  * 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)
+static void unlink_entry(const struct cache_entry *ce)
 {
 	if (!check_leading_path(ce->name, ce_namelen(ce)))
 		return;
@@ -193,7 +199,7 @@
 
 	if (o->update && o->verbose_update) {
 		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
-			struct cache_entry *ce = index->cache[cnt];
+			const struct cache_entry *ce = index->cache[cnt];
 			if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
 				total++;
 		}
@@ -206,7 +212,7 @@
 	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];
+		const struct cache_entry *ce = index->cache[i];
 
 		if (ce->ce_flags & CE_WT_REMOVE) {
 			display_progress(progress, ++cnt);
@@ -235,8 +241,11 @@
 	return errs != 0;
 }
 
-static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
-static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
+static int verify_uptodate_sparse(const struct cache_entry *ce,
+				  struct unpack_trees_options *o);
+static int verify_absent_sparse(const struct cache_entry *ce,
+				enum unpack_trees_error_types,
+				struct unpack_trees_options *o);
 
 static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
 {
@@ -291,7 +300,8 @@
 	return 0;
 }
 
-static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
+static inline int call_unpack_fn(const struct cache_entry * const *src,
+				 struct unpack_trees_options *o)
 {
 	int ret = o->fn(src, o);
 	if (ret > 0)
@@ -320,7 +330,7 @@
 		index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
 }
 
-static int locate_in_src_index(struct cache_entry *ce,
+static int locate_in_src_index(const struct cache_entry *ce,
 			       struct unpack_trees_options *o)
 {
 	struct index_state *index = o->src_index;
@@ -366,7 +376,7 @@
 	return NULL;
 }
 
-static void add_same_unmerged(struct cache_entry *ce,
+static void add_same_unmerged(const struct cache_entry *ce,
 			      struct unpack_trees_options *o)
 {
 	struct index_state *index = o->src_index;
@@ -388,7 +398,7 @@
 static int unpack_index_entry(struct cache_entry *ce,
 			      struct unpack_trees_options *o)
 {
-	struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
+	const struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
 	int ret;
 
 	src[0] = ce;
@@ -454,7 +464,7 @@
 	newinfo.pathspec = info->pathspec;
 	newinfo.name = *p;
 	newinfo.pathlen += tree_entry_len(p) + 1;
-	newinfo.conflicts |= df_conflicts;
+	newinfo.df_conflicts |= df_conflicts;
 
 	for (i = 0; i < n; i++, dirmask >>= 1) {
 		const unsigned char *sha1 = NULL;
@@ -555,17 +565,12 @@
 {
 	int i;
 	struct unpack_trees_options *o = info->data;
-	unsigned long conflicts;
+	unsigned long conflicts = info->df_conflicts | dirmask;
 
 	/* Do we have *only* directories? Nothing to do */
 	if (mask == dirmask && !src[0])
 		return 0;
 
-	conflicts = info->conflicts;
-	if (o->merge)
-		conflicts >>= 1;
-	conflicts |= dirmask;
-
 	/*
 	 * Ok, we've filled in up to any potential index entry in src[0],
 	 * now do the rest.
@@ -590,8 +595,16 @@
 		src[i + o->merge] = create_ce_entry(info, names + i, stage);
 	}
 
-	if (o->merge)
-		return call_unpack_fn(src, o);
+	if (o->merge) {
+		int rc = call_unpack_fn((const struct cache_entry * const *)src,
+					o);
+		for (i = 0; i < n; i++) {
+			struct cache_entry *ce = src[i + o->merge];
+			if (ce != o->df_conflict_entry)
+				free(ce);
+		}
+		return rc;
+	}
 
 	for (i = 0; i < n; i++)
 		if (src[i] && src[i] != o->df_conflict_entry)
@@ -637,7 +650,7 @@
 	int p_len = tree_entry_len(p);
 
 	for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
-		struct cache_entry *ce = index->cache[pos];
+		const struct cache_entry *ce = index->cache[pos];
 		const char *ce_name, *ce_slash;
 		int cmp, ce_len;
 
@@ -789,13 +802,6 @@
 
 	/* Now handle any directories.. */
 	if (dirmask) {
-		unsigned long conflicts = mask & ~dirmask;
-		if (o->merge) {
-			conflicts <<= 1;
-			if (src[0])
-				conflicts |= 1;
-		}
-
 		/* special case: "diff-index --cached" looking at a tree */
 		if (o->diff_index_cached &&
 		    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
@@ -814,7 +820,7 @@
 			}
 		}
 
-		if (traverse_trees_recursive(n, dirmask, conflicts,
+		if (traverse_trees_recursive(n, dirmask, mask & ~dirmask,
 					     names, info) < 0)
 			return -1;
 		return mask;
@@ -995,7 +1001,9 @@
 		       select_flag, skip_wt_flag, el);
 }
 
-static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *);
+static int verify_absent(const struct cache_entry *,
+			 enum unpack_trees_error_types,
+			 struct unpack_trees_options *);
 /*
  * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
  * resulting index, -2 on failure to reflect the changes to the work tree.
@@ -1165,12 +1173,13 @@
 
 /* Here come the merge functions */
 
-static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
+static int reject_merge(const struct cache_entry *ce,
+			struct unpack_trees_options *o)
 {
 	return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
-static int same(struct cache_entry *a, struct cache_entry *b)
+static int same(const struct cache_entry *a, const struct cache_entry *b)
 {
 	if (!!a != !!b)
 		return 0;
@@ -1187,9 +1196,9 @@
  * When a CE gets turned into an unmerged entry, we
  * want it to be up-to-date
  */
-static int verify_uptodate_1(struct cache_entry *ce,
-				   struct unpack_trees_options *o,
-				   enum unpack_trees_error_types error_type)
+static int verify_uptodate_1(const struct cache_entry *ce,
+			     struct unpack_trees_options *o,
+			     enum unpack_trees_error_types error_type)
 {
 	struct stat st;
 
@@ -1228,7 +1237,7 @@
 		add_rejected_path(o, error_type, ce->name);
 }
 
-static int verify_uptodate(struct cache_entry *ce,
+static int verify_uptodate(const struct cache_entry *ce,
 			   struct unpack_trees_options *o)
 {
 	if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
@@ -1236,13 +1245,14 @@
 	return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
 
-static int verify_uptodate_sparse(struct cache_entry *ce,
+static int verify_uptodate_sparse(const struct cache_entry *ce,
 				  struct unpack_trees_options *o)
 {
 	return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
-static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
+static void invalidate_ce_path(const struct cache_entry *ce,
+			       struct unpack_trees_options *o)
 {
 	if (ce)
 		cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
@@ -1255,16 +1265,16 @@
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce,
-				      enum unpack_trees_error_types error_type,
-				      struct unpack_trees_options *o)
+static int verify_clean_submodule(const struct cache_entry *ce,
+				  enum unpack_trees_error_types error_type,
+				  struct unpack_trees_options *o)
 {
 	return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce,
-				      enum unpack_trees_error_types error_type,
-				      struct unpack_trees_options *o)
+static int verify_clean_subdirectory(const struct cache_entry *ce,
+				     enum unpack_trees_error_types error_type,
+				     struct unpack_trees_options *o)
 {
 	/*
 	 * we are about to extract "ce->name"; we would not want to lose
@@ -1343,18 +1353,18 @@
  */
 static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
 {
-	struct cache_entry *src;
+	const struct cache_entry *src;
 
 	src = index_name_exists(o->src_index, name, len, 1);
 	return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 }
 
 static int check_ok_to_remove(const char *name, int len, int dtype,
-			      struct cache_entry *ce, struct stat *st,
+			      const struct cache_entry *ce, struct stat *st,
 			      enum unpack_trees_error_types error_type,
 			      struct unpack_trees_options *o)
 {
-	struct cache_entry *result;
+	const struct cache_entry *result;
 
 	/*
 	 * It may be that the 'lstat()' succeeded even though
@@ -1405,9 +1415,9 @@
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent_1(struct cache_entry *ce,
-				 enum unpack_trees_error_types error_type,
-				 struct unpack_trees_options *o)
+static int verify_absent_1(const struct cache_entry *ce,
+			   enum unpack_trees_error_types error_type,
+			   struct unpack_trees_options *o)
 {
 	int len;
 	struct stat st;
@@ -1440,7 +1450,7 @@
 	}
 }
 
-static int verify_absent(struct cache_entry *ce,
+static int verify_absent(const struct cache_entry *ce,
 			 enum unpack_trees_error_types error_type,
 			 struct unpack_trees_options *o)
 {
@@ -1449,9 +1459,9 @@
 	return verify_absent_1(ce, error_type, o);
 }
 
-static int verify_absent_sparse(struct cache_entry *ce,
-			 enum unpack_trees_error_types error_type,
-			 struct unpack_trees_options *o)
+static int verify_absent_sparse(const struct cache_entry *ce,
+				enum unpack_trees_error_types error_type,
+				struct unpack_trees_options *o)
 {
 	enum unpack_trees_error_types orphaned_error = error_type;
 	if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
@@ -1460,10 +1470,12 @@
 	return verify_absent_1(ce, orphaned_error, o);
 }
 
-static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
-		struct unpack_trees_options *o)
+static int merged_entry(const struct cache_entry *ce,
+			const struct cache_entry *old,
+			struct unpack_trees_options *o)
 {
 	int update = CE_UPDATE;
+	struct cache_entry *merge = dup_entry(ce);
 
 	if (!old) {
 		/*
@@ -1481,8 +1493,11 @@
 		update |= CE_ADDED;
 		merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
 
-		if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+		if (verify_absent(merge,
+				  ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
+			free(merge);
 			return -1;
+		}
 		invalidate_ce_path(merge, o);
 	} else if (!(old->ce_flags & CE_CONFLICTED)) {
 		/*
@@ -1496,8 +1511,10 @@
 			copy_cache_entry(merge, old);
 			update = 0;
 		} else {
-			if (verify_uptodate(old, o))
+			if (verify_uptodate(old, o)) {
+				free(merge);
 				return -1;
+			}
 			/* Migrate old flags over */
 			update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
 			invalidate_ce_path(old, o);
@@ -1510,12 +1527,13 @@
 		invalidate_ce_path(old, o);
 	}
 
-	add_entry(o, merge, update, CE_STAGEMASK);
+	do_add_entry(o, merge, update, CE_STAGEMASK);
 	return 1;
 }
 
-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
-		struct unpack_trees_options *o)
+static int deleted_entry(const struct cache_entry *ce,
+			 const struct cache_entry *old,
+			 struct unpack_trees_options *o)
 {
 	/* Did it exist in the index? */
 	if (!old) {
@@ -1530,7 +1548,8 @@
 	return 1;
 }
 
-static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+static int keep_entry(const struct cache_entry *ce,
+		      struct unpack_trees_options *o)
 {
 	add_entry(o, ce, 0, 0);
 	return 1;
@@ -1552,11 +1571,12 @@
 }
 #endif
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+int threeway_merge(const struct cache_entry * const *stages,
+		   struct unpack_trees_options *o)
 {
-	struct cache_entry *index;
-	struct cache_entry *head;
-	struct cache_entry *remote = stages[o->head_idx + 1];
+	const struct cache_entry *index;
+	const struct cache_entry *head;
+	const struct cache_entry *remote = stages[o->head_idx + 1];
 	int count;
 	int head_match = 0;
 	int remote_match = 0;
@@ -1641,7 +1661,7 @@
 	if (o->aggressive) {
 		int head_deleted = !head;
 		int remote_deleted = !remote;
-		struct cache_entry *ce = NULL;
+		const struct cache_entry *ce = NULL;
 
 		if (index)
 			ce = index;
@@ -1724,11 +1744,12 @@
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
  */
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
+int twoway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o)
 {
-	struct cache_entry *current = src[0];
-	struct cache_entry *oldtree = src[1];
-	struct cache_entry *newtree = src[2];
+	const struct cache_entry *current = src[0];
+	const struct cache_entry *oldtree = src[1];
+	const struct cache_entry *newtree = src[2];
 
 	if (o->merge_size != 2)
 		return error("Cannot do a twoway merge of %d trees",
@@ -1790,11 +1811,11 @@
  * Keep the index entries at stage0, collapse stage1 but make sure
  * stage0 does not have anything there.
  */
-int bind_merge(struct cache_entry **src,
-		struct unpack_trees_options *o)
+int bind_merge(const struct cache_entry * const *src,
+	       struct unpack_trees_options *o)
 {
-	struct cache_entry *old = src[0];
-	struct cache_entry *a = src[1];
+	const struct cache_entry *old = src[0];
+	const struct cache_entry *a = src[1];
 
 	if (o->merge_size != 1)
 		return error("Cannot do a bind merge of %d trees",
@@ -1814,10 +1835,11 @@
  * The rule is:
  * - take the stat information from stage0, take the data from stage1
  */
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
+int oneway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o)
 {
-	struct cache_entry *old = src[0];
-	struct cache_entry *a = src[1];
+	const struct cache_entry *old = src[0];
+	const struct cache_entry *a = src[1];
 
 	if (o->merge_size != 1)
 		return error("Cannot do a oneway merge of %d trees",
diff --git a/unpack-trees.h b/unpack-trees.h
index 5e432f5..36a73a6 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -8,7 +8,7 @@
 struct unpack_trees_options;
 struct exclude_list;
 
-typedef int (*merge_fn_t)(struct cache_entry **src,
+typedef int (*merge_fn_t)(const struct cache_entry * const *src,
 		struct unpack_trees_options *options);
 
 enum unpack_trees_error_types {
@@ -77,9 +77,13 @@
 extern int unpack_trees(unsigned n, struct tree_desc *t,
 		struct unpack_trees_options *options);
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int threeway_merge(const struct cache_entry * const *stages,
+		   struct unpack_trees_options *o);
+int twoway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o);
+int bind_merge(const struct cache_entry * const *src,
+	       struct unpack_trees_options *o);
+int oneway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o);
 
 #endif
diff --git a/upload-pack.c b/upload-pack.c
index bfa6279c..127e59a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -592,7 +592,7 @@
 				die("invalid shallow line: %s", line);
 			object = parse_object(sha1);
 			if (!object)
-				die("did not find object for %s", line);
+				continue;
 			if (object->type != OBJ_COMMIT)
 				die("invalid shallow object %s", sha1_to_hex(sha1));
 			if (!(object->flags & CLIENT_SHALLOW)) {
diff --git a/utf8.h b/utf8.h
index 32a7bfb..65d0e42 100644
--- a/utf8.h
+++ b/utf8.h
@@ -10,6 +10,7 @@
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 int same_encoding(const char *, const char *);
+__attribute__((format (printf, 2, 3)))
 int utf8_fprintf(FILE *, const char *, ...);
 
 void strbuf_add_wrapped_text(struct strbuf *buf,
diff --git a/wildmatch.c b/wildmatch.c
index 7192bdc..f91ba99 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -196,6 +196,11 @@
 					}
 					if (t_ch <= p_ch && t_ch >= prev_ch)
 						matched = 1;
+					else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
+						uchar t_ch_upper = toupper(t_ch);
+						if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
+							matched = 1;
+					}
 					p_ch = 0; /* This makes "prev_ch" get set to 0. */
 				} else if (p_ch == '[' && p[1] == ':') {
 					const uchar *s;
@@ -245,6 +250,8 @@
 					} else if (CC_EQ(s,i, "upper")) {
 						if (ISUPPER(t_ch))
 							matched = 1;
+						else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
+							matched = 1;
 					} else if (CC_EQ(s,i, "xdigit")) {
 						if (ISXDIGIT(t_ch))
 							matched = 1;
diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh
index 53a8dd0..701d233 100644
--- a/wrap-for-bin.sh
+++ b/wrap-for-bin.sh
@@ -14,7 +14,7 @@
 	GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
 	export GIT_TEMPLATE_DIR
 fi
-GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'
+GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'"${GITPERLLIB:+:$GITPERLLIB}"
 GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
 PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
 export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
diff --git a/wrapper.c b/wrapper.c
index bac59d2..6a015de 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -322,7 +322,7 @@
 		template[5] = letters[v % num_letters]; v /= num_letters;
 
 		fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
-		if (fd > 0)
+		if (fd >= 0)
 			return fd;
 		/*
 		 * Fatal error (EPERM, ENOSPC etc).
@@ -408,18 +408,24 @@
 	warning(_("unable to access '%s': %s"), path, strerror(errno));
 }
 
-int access_or_warn(const char *path, int mode)
+static int access_error_is_ok(int err, unsigned flag)
+{
+	return err == ENOENT || err == ENOTDIR ||
+		((flag & ACCESS_EACCES_OK) && err == EACCES);
+}
+
+int access_or_warn(const char *path, int mode, unsigned flag)
 {
 	int ret = access(path, mode);
-	if (ret && errno != ENOENT && errno != ENOTDIR)
+	if (ret && !access_error_is_ok(errno, flag))
 		warn_on_inaccessible(path);
 	return ret;
 }
 
-int access_or_die(const char *path, int mode)
+int access_or_die(const char *path, int mode, unsigned flag)
 {
 	int ret = access(path, mode);
-	if (ret && errno != ENOENT && errno != ENOTDIR)
+	if (ret && !access_error_is_ok(errno, flag))
 		die_errno(_("unable to access '%s'"), path);
 	return ret;
 }
diff --git a/wt-status.c b/wt-status.c
index bf84a86..cb24f1f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -127,6 +127,7 @@
 	s->change.strdup_strings = 1;
 	s->untracked.strdup_strings = 1;
 	s->ignored.strdup_strings = 1;
+	s->show_branch = -1;  /* unspecified */
 }
 
 static void wt_status_print_unmerged_header(struct wt_status *s)
@@ -243,7 +244,7 @@
 	struct strbuf onebuf = STRBUF_INIT;
 	const char *one, *how = _("bug");
 
-	one = quote_path(it->string, -1, &onebuf, s->prefix);
+	one = quote_path(it->string, s->prefix, &onebuf);
 	status_printf(s, color(WT_STATUS_HEADER, s), "\t");
 	switch (d->stagemask) {
 	case 1: how = _("both deleted:"); break;
@@ -297,8 +298,8 @@
 		    change_type);
 	}
 
-	one = quote_path(one_name, -1, &onebuf, s->prefix);
-	two = quote_path(two_name, -1, &twobuf, s->prefix);
+	one = quote_path(one_name, s->prefix, &onebuf);
+	two = quote_path(two_name, s->prefix, &twobuf);
 
 	status_printf(s, color(WT_STATUS_HEADER, s), "\t");
 	switch (status) {
@@ -371,7 +372,7 @@
 static int unmerged_mask(const char *path)
 {
 	int pos, mask;
-	struct cache_entry *ce;
+	const struct cache_entry *ce;
 
 	pos = cache_name_pos(path, strlen(path));
 	if (0 <= pos)
@@ -475,7 +476,7 @@
 	for (i = 0; i < active_nr; i++) {
 		struct string_list_item *it;
 		struct wt_status_change_data *d;
-		struct cache_entry *ce = active_cache[i];
+		const struct cache_entry *ce = active_cache[i];
 
 		if (!ce_path_match(ce, &pathspec))
 			continue;
@@ -706,8 +707,7 @@
 		struct string_list_item *it;
 		const char *path;
 		it = &(l->items[i]);
-		path = quote_path(it->string, strlen(it->string),
-				  &buf, s->prefix);
+		path = quote_path(it->string, s->prefix, &buf);
 		if (column_active(s->colopts)) {
 			string_list_append(&output, path);
 			continue;
@@ -826,7 +826,7 @@
 	if (advice_status_hints) {
 		if (!state->am_empty_patch)
 			status_printf_ln(s, color,
-				_("  (fix conflicts and then run \"git am --resolved\")"));
+				_("  (fix conflicts and then run \"git am --continue\")"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
@@ -955,10 +955,12 @@
 	if (advice_status_hints) {
 		if (has_unmerged(s))
 			status_printf_ln(s, color,
-				_("  (fix conflicts and run \"git commit\")"));
+				_("  (fix conflicts and run \"git cherry-pick --continue\")"));
 		else
 			status_printf_ln(s, color,
-				_("  (all conflicts fixed: run \"git commit\")"));
+				_("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
+		status_printf_ln(s, color,
+			_("  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
 	}
 	wt_status_print_trailer(s);
 }
@@ -1035,7 +1037,6 @@
 }
 
 struct grab_1st_switch_cbdata {
-	int found;
 	struct strbuf buf;
 	unsigned char nsha1[20];
 };
@@ -1059,7 +1060,6 @@
 	for (end = target; *end && *end != '\n'; end++)
 		;
 	strbuf_add(&cb->buf, target, end - target);
-	cb->found = 1;
 	return 1;
 }
 
@@ -1176,7 +1176,10 @@
 			branch_name += 11;
 		else if (!strcmp(branch_name, "HEAD")) {
 			branch_status_color = color(WT_STATUS_NOBRANCH, s);
-			if (state.detached_from) {
+			if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
+				on_what = _("rebase in progress; onto ");
+				branch_name = state.onto;
+			} else if (state.detached_from) {
 				unsigned char sha1[20];
 				branch_name = state.detached_from;
 				if (!get_sha1("HEAD", sha1) &&
@@ -1289,7 +1292,7 @@
 	} else {
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;
-		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		one = quote_path(it->string, s->prefix, &onebuf);
 		printf(" %s\n", one);
 		strbuf_release(&onebuf);
 	}
@@ -1317,7 +1320,7 @@
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;
 		if (d->head_path) {
-			one = quote_path(d->head_path, -1, &onebuf, s->prefix);
+			one = quote_path(d->head_path, s->prefix, &onebuf);
 			if (*one != '"' && strchr(one, ' ') != NULL) {
 				putchar('"');
 				strbuf_addch(&onebuf, '"');
@@ -1326,7 +1329,7 @@
 			printf("%s -> ", one);
 			strbuf_release(&onebuf);
 		}
-		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		one = quote_path(it->string, s->prefix, &onebuf);
 		if (*one != '"' && strchr(one, ' ') != NULL) {
 			putchar('"');
 			strbuf_addch(&onebuf, '"');
@@ -1345,7 +1348,7 @@
 	} else {
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;
-		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		one = quote_path(it->string, s->prefix, &onebuf);
 		color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
 		printf(" %s\n", one);
 		strbuf_release(&onebuf);
diff --git a/wt-status.h b/wt-status.h
index 4121bc2..fb7152e 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -96,9 +96,9 @@
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
 
-void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)
-	;
-void status_printf(struct wt_status *s, const char *color, const char *fmt, ...)
-	;
+__attribute__((format (printf, 3, 4)))
+void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
+void status_printf(struct wt_status *s, const char *color, const char *fmt, ...);
 
 #endif /* STATUS_H */
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 219a3bb..c033991 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -39,6 +39,8 @@
 #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
 #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
 
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index b2eb6db..2358a2d 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -394,6 +394,7 @@
 	xch->i2 = i2;
 	xch->chg1 = chg1;
 	xch->chg2 = chg2;
+	xch->ignore = 0;
 
 	return xch;
 }
@@ -544,7 +545,9 @@
 	xdchange_t *xch, *xche;
 
 	for (xch = xscr; xch; xch = xche->next) {
-		xche = xdl_get_hunk(xch, xecfg);
+		xche = xdl_get_hunk(&xch, xecfg);
+		if (!xch)
+			break;
 		if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
 				     xch->i2, xche->i2 + xche->chg2 - xch->i2,
 				     ecb->priv) < 0)
@@ -553,6 +556,27 @@
 	return 0;
 }
 
+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+	xdchange_t *xch;
+
+	for (xch = xscr; xch; xch = xch->next) {
+		int ignore = 1;
+		xrecord_t **rec;
+		long i;
+
+		rec = &xe->xdf1.recs[xch->i1];
+		for (i = 0; i < xch->chg1 && ignore; i++)
+			ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+		rec = &xe->xdf2.recs[xch->i2];
+		for (i = 0; i < xch->chg2 && ignore; i++)
+			ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+		xch->ignore = ignore;
+	}
+}
+
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
 	xdchange_t *xscr;
@@ -571,6 +595,9 @@
 		return -1;
 	}
 	if (xscr) {
+		if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+			xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
 		if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
 			xdl_free_script(xscr);
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 7a92ea9..8b81206 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -41,6 +41,7 @@
 	struct s_xdchange *next;
 	long i1, i2;
 	long chg1, chg2;
+	int ignore;
 } xdchange_t;
 
 
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index d11dbf9..4d86458 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -56,16 +56,51 @@
 /*
  * Starting at the passed change atom, find the latest change atom to be included
  * inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
  */
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
-	xdchange_t *xch, *xchp;
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+	xdchange_t *xch, *xchp, *lxch;
 	long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+	long max_ignorable = xecfg->ctxlen;
+	unsigned long ignored = 0; /* number of ignored blank lines */
 
-	for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
-		if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+	/* remove ignorable changes that are too far before other changes */
+	for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+		xch = xchp->next;
+
+		if (xch == NULL ||
+		    xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+			*xscr = xch;
+	}
+
+	if (*xscr == NULL)
+		return NULL;
+
+	lxch = *xscr;
+
+	for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+		long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+		if (distance > max_common)
 			break;
 
-	return xchp;
+		if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+			lxch = xch;
+			ignored = 0;
+		} else if (distance < max_ignorable && xch->ignore) {
+			ignored += xch->chg2;
+		} else if (lxch != xchp &&
+			   xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
+			break;
+		} else if (!xch->ignore) {
+			lxch = xch;
+			ignored = 0;
+		} else {
+			ignored += xch->chg2;
+		}
+	}
+
+	return lxch;
 }
 
 
@@ -139,7 +174,9 @@
 		return xdl_emit_common(xe, xscr, ecb, xecfg);
 
 	for (xch = xscr; xch; xch = xche->next) {
-		xche = xdl_get_hunk(xch, xecfg);
+		xche = xdl_get_hunk(&xch, xecfg);
+		if (!xch)
+			break;
 
 		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
 		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
diff --git a/xdiff/xemit.h b/xdiff/xemit.h
index c2e2e83..d297107 100644
--- a/xdiff/xemit.h
+++ b/xdiff/xemit.h
@@ -27,7 +27,7 @@
 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);
+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/xutils.c b/xdiff/xutils.c
index 9504eae..62cb23d 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -143,6 +143,19 @@
 	return nl + 1;
 }
 
+int xdl_blankline(const char *line, long size, long flags)
+{
+	long i;
+
+	if (!(flags & XDF_WHITESPACE_FLAGS))
+		return (size <= 1);
+
+	for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+		;
+
+	return (i == size);
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
 	int i1, i2;
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index ad1428e..4646ce5 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -32,6 +32,7 @@
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
 unsigned long xdl_hash_record(char const **data, char const *top, long flags);
 unsigned int xdl_hashbits(unsigned int size);