Code

Merge branch 'gv/portable'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:44 +0000 (06:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:44 +0000 (06:02 -0700)
* gv/portable:
  test-lib: use DIFF definition from GIT-BUILD-OPTIONS
  build: propagate $DIFF to scripts
  Makefile: Tru64 portability fix
  Makefile: HP-UX 10.20 portability fixes
  Makefile: HPUX11 portability fixes
  Makefile: SunOS 5.6 portability fix
  inline declaration does not work on AIX
  Allow disabling "inline"
  Some platforms lack socklen_t type
  Make NO_{INET_NTOP,INET_PTON} configured independently
  Makefile: some platforms do not have hstrerror anywhere
  git-compat-util.h: some platforms with mmap() lack MAP_FAILED definition
  test_cmp: do not use "diff -u" on platforms that lack one
  fixup: do not unconditionally disable "diff -u"
  tests: use "test_cmp", not "diff", when verifying the result
  Do not use "diff" found on PATH while building and installing
  enums: omit trailing comma for portability
  Makefile: -lpthread may still be necessary when libc has only pthread stubs
  Rewrite dynamic structure initializations to runtime assignment
  Makefile: pass CPPFLAGS through to fllow customization

Conflicts:
Makefile
wt-status.h

34 files changed:
1  2 
Makefile
builtin/apply.c
builtin/blame.c
builtin/checkout.c
builtin/commit.c
builtin/receive-pack.c
builtin/remote.c
cache.h
commit.h
config.mak.in
configure.ac
connect.c
convert.c
diff.h
dir.c
fast-import.c
git-compat-util.h
grep.h
http-walker.c
merge-recursive.h
pretty.c
refs.c
remote.c
t/Makefile
t/lib-t6000.sh
t/t0000-basic.sh
t/t3200-branch.sh
t/t3903-stash.sh
t/t4124-apply-ws-rule.sh
t/t9400-git-cvsserver-server.sh
t/test-lib.sh
unpack-trees.c
wt-status.c
wt-status.h

diff --combined Makefile
index a863a068ab3bd5b5055372416060fef586864037,6b3b59bef529b7b297be27322f4fe348a0b3c390..3dc072fc8162be1b1d57db207a976b8b78928f04
+++ b/Makefile
@@@ -8,6 -8,12 +8,12 @@@ all:
  # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
  # to PATH if your tools in /usr/bin are broken.
  #
+ # Define SOCKLEN_T to a suitable type (such as 'size_t') if your
+ # system headers do not define a socklen_t type.
+ #
+ # Define INLINE to a suitable substitute (such as '__inline' or '') if git
+ # fails to compile with errors about undefined inline functions or similar.
+ #
  # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
  # or vsnprintf() return -1 instead of number of characters which would
  # have been written to the final string if enough space had been available.
@@@ -31,9 -37,6 +37,9 @@@
  # Define EXPATDIR=/foo/bar if your expat header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
 +# Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
 +# it specifies.
 +#
  # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
  #
  # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
@@@ -249,7 -252,7 +255,7 @@@ endi
  
  CFLAGS = -g -O2 -Wall
  LDFLAGS =
- ALL_CFLAGS = $(CFLAGS)
+ ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
  ALL_LDFLAGS = $(LDFLAGS)
  STRIP ?= strip
  
@@@ -272,7 -275,6 +278,7 @@@ mandir = share/ma
  infodir = share/info
  gitexecdir = libexec/git-core
  sharedir = $(prefix)/share
 +gitwebdir = $(sharedir)/gitweb
  template_dir = share/git-core/templates
  htmldir = share/doc/git-doc
  ifeq ($(prefix),/usr)
@@@ -286,11 -288,12 +292,12 @@@ lib = li
  # DESTDIR=
  pathsep = :
  
--export prefix bindir sharedir sysconfdir
++export prefix bindir sharedir sysconfdir gitwebdir
  
  CC = gcc
  AR = ar
  RM = rm -f
+ DIFF = diff
  TAR = tar
  FIND = find
  INSTALL = install
@@@ -298,6 -301,7 +305,7 @@@ RPMBUILD = rpmbuil
  TCL_PATH = tclsh
  TCLTK_PATH = wish
  PTHREAD_LIBS = -lpthread
+ PTHREAD_CFLAGS =
  
  export TCL_PATH TCLTK_PATH
  
@@@ -370,8 -374,6 +378,8 @@@ SCRIPT_PERL += git-relink.per
  SCRIPT_PERL += git-send-email.perl
  SCRIPT_PERL += git-svn.perl
  
 +SCRIPT_PYTHON += git-remote-testgit.py
 +
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
@@@ -492,7 -494,6 +500,7 @@@ LIB_H += log-tree.
  LIB_H += mailmap.h
  LIB_H += merge-recursive.h
  LIB_H += notes.h
 +LIB_H += notes-cache.h
  LIB_H += object.h
  LIB_H += pack.h
  LIB_H += pack-refs.h
@@@ -582,7 -583,6 +590,7 @@@ LIB_OBJS += merge-file.
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += notes.o
 +LIB_OBJS += notes-cache.o
  LIB_OBJS += object.o
  LIB_OBJS += pack-check.o
  LIB_OBJS += pack-refs.o
@@@ -628,7 -628,6 +636,7 @@@ LIB_OBJS += tree-diff.
  LIB_OBJS += tree.o
  LIB_OBJS += tree-walk.o
  LIB_OBJS += unpack-trees.o
 +LIB_OBJS += url.o
  LIB_OBJS += usage.o
  LIB_OBJS += userdiff.o
  LIB_OBJS += utf8.o
@@@ -741,15 -740,20 +749,22 @@@ EXTLIBS 
  # because maintaining the nesting to match is a pain.  If
  # we had "elif" things would have been much nicer...
  
+ ifeq ($(uname_S),OSF1)
+       # Need this for u_short definitions et al
+       BASIC_CFLAGS += -D_OSF_SOURCE
+       SOCKLEN_T = int
+       NO_STRTOULL = YesPlease
+       NO_NSEC = YesPlease
+ endif
  ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
 +      HAVE_PATHS_H = YesPlease
  endif
  ifeq ($(uname_S),GNU/kFreeBSD)
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
 +      HAVE_PATHS_H = YesPlease
  endif
  ifeq ($(uname_S),UnixWare)
        CC = cc
@@@ -815,6 -819,18 +830,18 @@@ ifeq ($(uname_S),SunOS
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
+       ifeq ($(uname_R),5.6)
+               SOCKLEN_T = int
+               NO_HSTRERROR = YesPlease
+               NO_IPV6 = YesPlease
+               NO_SOCKADDR_STORAGE = YesPlease
+               NO_UNSETENV = YesPlease
+               NO_SETENV = YesPlease
+               NO_STRLCPY = YesPlease
+               NO_C99_FORMAT = YesPlease
+               NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
+       endif
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
                NO_IPV6 = YesPlease
                NO_STRLCPY = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        ifeq ($(uname_R),5.8)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        INSTALL = /usr/ucb/install
        TAR = gtar
@@@ -878,7 -897,6 +908,7 @@@ ifeq ($(uname_S),FreeBSD
                NO_STRTOUMAX = YesPlease
        endif
        PYTHON_PATH = /usr/local/bin/python
 +      HAVE_PATHS_H = YesPlease
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
 +      HAVE_PATHS_H = YesPlease
  endif
  ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
        BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
        USE_ST_TIMESPEC = YesPlease
        NO_MKSTEMPS = YesPlease
 +      HAVE_PATHS_H = YesPlease
  endif
  ifeq ($(uname_S),AIX)
 +      DEFAULT_PAGER = more
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
        ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
                NO_PTHREADS = YesPlease
+       else
+               PTHREAD_LIBS = -lpthread
+       endif
+       ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+               INLINE=''
        endif
+       GIT_TEST_CMP = cmp
  endif
  ifeq ($(uname_S),GNU)
        # GNU/Hurd
        NO_STRLCPY=YesPlease
        NO_MKSTEMPS = YesPlease
 +      HAVE_PATHS_H = YesPlease
  endif
  ifeq ($(uname_S),IRIX)
        NO_SETENV = YesPlease
@@@ -958,6 -978,7 +994,7 @@@ ifeq ($(uname_S),IRIX64
        NEEDS_LIBGEN = YesPlease
  endif
  ifeq ($(uname_S),HP-UX)
+       INLINE = __inline
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
+       NO_NSEC = YesPlease
+       ifeq ($(uname_R),B.11.00)
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       ifeq ($(uname_R),B.10.20)
+               # Override HP-UX 11.x setting:
+               INLINE =
+               SOCKLEN_T = size_t
+               NO_PREAD = YesPlease
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       GIT_TEST_CMP = cmp
  endif
  ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
@@@ -1042,6 -1077,7 +1093,6 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        NO_STRTOUMAX = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
 -      SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_SVN_TESTS = YesPlease
        NO_PERL_MAKEMAKER = YesPlease
        RUNTIME_PREFIX = YesPlease
@@@ -1078,7 -1114,6 +1129,7 @@@ endi
  -include config.mak
  
  ifdef CHECK_HEADER_DEPENDENCIES
 +COMPUTE_HEADER_DEPENDENCIES =
  USE_COMPUTED_HEADER_DEPENDENCIES =
  endif
  
  BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
  endif
  
+ ifneq (,$(INLINE))
+       BASIC_CFLAGS += -Dinline=$(INLINE)
+ endif
+ ifneq (,$(SOCKLEN_T))
+       BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
+ endif
  ifeq ($(uname_S),Darwin)
        ifndef NO_FINK
                ifeq ($(shell test -d /sw/lib && echo y),y)
@@@ -1365,14 -1408,11 +1424,15 @@@ endi
  ifdef NO_PTHREADS
        BASIC_CFLAGS += -DNO_PTHREADS
  else
+       BASIC_CFLAGS += $(PTHREAD_CFLAGS)
        EXTLIBS += $(PTHREAD_LIBS)
        LIB_OBJS += thread-utils.o
  endif
  
 +ifdef HAVE_PATHS_H
 +      BASIC_CFLAGS += -DHAVE_PATHS_H
 +endif
 +
  ifdef DIR_HAS_BSD_GROUP_SEMANTICS
        COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
  endif
@@@ -1389,6 -1429,10 +1449,10 @@@ ifdef USE_NED_ALLOCATO
         COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
  endif
  
+ ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+       export GIT_TEST_CMP_USE_COPIED_CONTEXT
+ endif
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK=NoThanks
  endif
@@@ -1445,12 -1489,12 +1509,13 @@@ gitexecdir_SQ = $(subst ','\'',$(gitexe
  template_dir_SQ = $(subst ','\'',$(template_dir))
  htmldir_SQ = $(subst ','\'',$(htmldir))
  prefix_SQ = $(subst ','\'',$(prefix))
 +gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
  PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
  PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
  TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+ DIFF_SQ = $(subst ','\'',$(DIFF))
  
  LIBS = $(GITLIBS) $(EXTLIBS)
  
@@@ -1477,7 -1521,7 +1542,7 @@@ endi
  ALL_CFLAGS += $(BASIC_CFLAGS)
  ALL_LDFLAGS += $(BASIC_LDFLAGS)
  
- export TAR INSTALL DESTDIR SHELL_PATH
+ export DIFF TAR INSTALL DESTDIR SHELL_PATH
  
  
  ### Build rules
@@@ -1539,6 -1583,7 +1604,7 @@@ define cmd_munge_scrip
  $(RM) $@ $@+ && \
  sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
      -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+     -e 's|@@DIFF@@|$(DIFF_SQ)|' \
      -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
      -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
      -e $(BROKEN_PATH_FIX) \
@@@ -1566,10 -1611,11 +1632,10 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % 
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
            -e '        h' \
 -          -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
 +          -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
            -e '        H' \
            -e '        x' \
            -e '}' \
 -          -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $@.perl >$@+ && \
        chmod +x $@+ && \
@@@ -1581,38 -1627,45 +1647,38 @@@ gitweb
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  
  ifdef JSMIN
 -GITWEB_PROGRAMS += gitweb/gitweb.min.js
 -GITWEB_JS = gitweb/gitweb.min.js
 +GITWEB_PROGRAMS += gitweb/static/gitweb.min.js
 +GITWEB_JS = gitweb/static/gitweb.min.js
  else
 -GITWEB_JS = gitweb/gitweb.js
 +GITWEB_JS = gitweb/static/gitweb.js
  endif
  ifdef CSSMIN
 -GITWEB_PROGRAMS += gitweb/gitweb.min.css
 -GITWEB_CSS = gitweb/gitweb.min.css
 +GITWEB_PROGRAMS += gitweb/static/gitweb.min.css
 +GITWEB_CSS = gitweb/static/gitweb.min.css
  else
 -GITWEB_CSS = gitweb/gitweb.css
 +GITWEB_CSS = gitweb/static/gitweb.css
  endif
  OTHER_PROGRAMS +=  gitweb/gitweb.cgi  $(GITWEB_PROGRAMS)
  gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
  
  ifdef JSMIN
 -gitweb/gitweb.min.js: gitweb/gitweb.js
 +gitweb/static/gitweb.min.js: gitweb/static/gitweb.js
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
  endif # JSMIN
  ifdef CSSMIN
 -gitweb/gitweb.min.css: gitweb/gitweb.css
 +gitweb/static/gitweb.min.css: gitweb/static/gitweb.css
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
  endif # CSSMIN
  
  
 -git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
 +git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
 -          -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
 -          -e '/@@GITWEB_CGI@@/d' \
 -          -e '/@@GITWEB_CSS@@/r $(GITWEB_CSS)' \
 -          -e '/@@GITWEB_CSS@@/d' \
 -          -e '/@@GITWEB_JS@@/r $(GITWEB_JS)' \
 -          -e '/@@GITWEB_JS@@/d' \
 +          -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
 -            -e 's|@@GITWEB_CSS_NAME@@|$(GITWEB_CSS)|' \
 -            -e 's|@@GITWEB_JS_NAME@@|$(GITWEB_JS)|' \
            $@.sh > $@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@@ -1633,8 -1686,13 +1699,8 @@@ $(patsubst %.py,%,$(SCRIPT_PYTHON)): % 
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
                --no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
                instlibdir` && \
 -      sed -e '1{' \
 -          -e '        s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
 -          -e '}' \
 -          -e 's|^import sys.*|&; \\\
 -                 import os; \\\
 -                 sys.path.insert(0, os.getenv("GITPYTHONLIB",\
 -                                              "@@INSTLIBDIR@@"));|' \
 +      sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
 +          -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
            -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            $@.py >$@+ && \
        chmod +x $@+ && \
@@@ -1664,10 -1722,7 +1730,10 @@@ git.o git.spec 
  
  TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
  GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
 -      git.o http.o http-walker.o remote-curl.o
 +      git.o
 +ifndef NO_CURL
 +      GIT_OBJS += http.o http-walker.o remote-curl.o
 +endif
  XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
        xdiff/xmerge.o xdiff/xpatience.o
  OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)
@@@ -1884,11 -1939,17 +1950,18 @@@ GIT-CFLAGS: FORC
  GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+       @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
 +      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
+ ifdef GIT_TEST_CMP
+       @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
+ endif
+ ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+       @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
+ endif
  
  ### Detect Tck/Tk interpreter path changes
  ifndef NO_TCLTK
@@@ -1983,7 -2044,6 +2056,7 @@@ install: al
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
  ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
-       $(MAKE) -C gitweb gitwebdir=$(gitwebdir_SQ) install
++      $(MAKE) -C gitweb install
  endif
  ifndef NO_PYTHON
        $(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@@ -2009,18 -2069,14 +2082,18 @@@ endi
                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
          done; } && \
 -      { for p in $(REMOTE_CURL_ALIASES); do \
 +      { test x"$(REMOTE_CURL_ALIASES)" = x || \
 +              { for p in $(REMOTE_CURL_ALIASES); do \
                $(RM) "$$execdir/$$p" && \
                ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
                ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
 -        done; } && \
 +        done; } ; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
  
 +install-gitweb:
 +      $(MAKE) -C gitweb install
 +
  install-doc:
        $(MAKE) -C Documentation install
  
@@@ -2115,7 -2171,7 +2188,7 @@@ clean
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
  ifndef NO_PERL
 -      $(RM) gitweb/gitweb.cgi gitweb/gitweb.min.*
 +      $(MAKE) -C gitweb clean
        $(MAKE) -C perl clean
  endif
  ifndef NO_PYTHON
diff --combined builtin/apply.c
index 8fc5ec31deae63122878bdb776921bcf656fb791,83b8ad9e0b65992bb5a431e2f5a75cc2550de07d..562e5345fc969a5ad03b3ade4952668bcec09136
@@@ -56,7 -56,7 +56,7 @@@ static enum ws_error_action 
        nowarn_ws_error,
        warn_on_ws_error,
        die_on_ws_error,
-       correct_ws_error,
+       correct_ws_error
  } ws_error_action = warn_on_ws_error;
  static int whitespace_error;
  static int squelch_whitespace_errors = 5;
@@@ -64,7 -64,7 +64,7 @@@ static int applied_after_fixing_ws
  
  static enum ws_ignore {
        ignore_ws_none,
-       ignore_ws_change,
+       ignore_ws_change
  } ws_ignore_action = ignore_ws_none;
  
  
@@@ -1854,8 -1854,6 +1854,8 @@@ static int match_fragment(struct image 
  {
        int i;
        char *fixed_buf, *buf, *orig, *target;
 +      struct strbuf fixed;
 +      size_t fixed_len;
        int preimage_limit;
  
        if (preimage->nr + try_lno <= img->nr) {
                if (match_end && (preimage->nr + try_lno != img->nr))
                        return 0;
        } else if (ws_error_action == correct_ws_error &&
 -                 (ws_rule & WS_BLANK_AT_EOF) && match_end) {
 +                 (ws_rule & WS_BLANK_AT_EOF)) {
                /*
 -               * This hunk that matches at the end extends beyond
 -               * the end of img, and we are removing blank lines
 -               * at the end of the file.  This many lines from the
 -               * beginning of the preimage must match with img, and
 -               * the remainder of the preimage must be blank.
 +               * This hunk extends beyond the end of img, and we are
 +               * removing blank lines at the end of the file.  This
 +               * many lines from the beginning of the preimage must
 +               * match with img, and the remainder of the preimage
 +               * must be blank.
                 */
                preimage_limit = img->nr - try_lno;
        } else {
                 * use the whitespace from the preimage.
                 */
                extra_chars = preimage_end - preimage_eof;
 -              fixed_buf = xmalloc(imgoff + extra_chars);
 -              memcpy(fixed_buf, img->buf + try, imgoff);
 -              memcpy(fixed_buf + imgoff, preimage_eof, extra_chars);
 -              imgoff += extra_chars;
 +              strbuf_init(&fixed, imgoff + extra_chars);
 +              strbuf_add(&fixed, img->buf + try, imgoff);
 +              strbuf_add(&fixed, preimage_eof, extra_chars);
 +              fixed_buf = strbuf_detach(&fixed, &fixed_len);
                update_pre_post_images(preimage, postimage,
 -                              fixed_buf, imgoff, postlen);
 +                              fixed_buf, fixed_len, postlen);
                return 1;
        }
  
         * but in this loop we will only handle the part of the
         * preimage that falls within the file.
         */
 -      fixed_buf = xmalloc(preimage->len + 1);
 -      buf = fixed_buf;
 +      strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
        target = img->buf + try;
        for (i = 0; i < preimage_limit; i++) {
 -              size_t fixlen; /* length after fixing the preimage */
                size_t oldlen = preimage->line[i].len;
                size_t tgtlen = img->line[try_lno + i].len;
 -              size_t tgtfixlen; /* length after fixing the target line */
 -              char tgtfixbuf[1024], *tgtfix;
 +              size_t fixstart = fixed.len;
 +              struct strbuf tgtfix;
                int match;
  
                /* Try fixing the line in the preimage */
 -              fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
 +              ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
  
                /* Try fixing the line in the target */
 -              if (sizeof(tgtfixbuf) > tgtlen)
 -                      tgtfix = tgtfixbuf;
 -              else
 -                      tgtfix = xmalloc(tgtlen);
 -              tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
 +              strbuf_init(&tgtfix, tgtlen);
 +              ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
  
                /*
                 * If they match, either the preimage was based on
                 * so we might as well take the fix together with their
                 * real change.
                 */
 -              match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
 +              match = (tgtfix.len == fixed.len - fixstart &&
 +                       !memcmp(tgtfix.buf, fixed.buf + fixstart,
 +                                           fixed.len - fixstart));
  
 -              if (tgtfix != tgtfixbuf)
 -                      free(tgtfix);
 +              strbuf_release(&tgtfix);
                if (!match)
                        goto unmatch_exit;
  
                orig += oldlen;
 -              buf += fixlen;
                target += tgtlen;
        }
  
         * false).
         */
        for ( ; i < preimage->nr; i++) {
 -              size_t fixlen; /* length after fixing the preimage */
 +              size_t fixstart = fixed.len; /* start of the fixed preimage */
                size_t oldlen = preimage->line[i].len;
                int j;
  
                /* Try fixing the line in the preimage */
 -              fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
 +              ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
  
 -              for (j = 0; j < fixlen; j++)
 -                      if (!isspace(buf[j]))
 +              for (j = fixstart; j < fixed.len; j++)
 +                      if (!isspace(fixed.buf[j]))
                                goto unmatch_exit;
  
                orig += oldlen;
 -              buf += fixlen;
        }
  
        /*
         * has whitespace breakages unfixed, and fixing them makes the
         * hunk match.  Update the context lines in the postimage.
         */
 +      fixed_buf = strbuf_detach(&fixed, &fixed_len);
        update_pre_post_images(preimage, postimage,
 -                             fixed_buf, buf - fixed_buf, 0);
 +                             fixed_buf, fixed_len, 0);
        return 1;
  
   unmatch_exit:
 -      free(fixed_buf);
 +      strbuf_release(&fixed);
        return 0;
  }
  
@@@ -2241,8 -2244,7 +2241,8 @@@ static int apply_one_fragment(struct im
        int match_beginning, match_end;
        const char *patch = frag->patch;
        int size = frag->size;
 -      char *old, *new, *oldlines, *newlines;
 +      char *old, *oldlines;
 +      struct strbuf newlines;
        int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
        int pos, applied_pos;
        memset(&preimage, 0, sizeof(preimage));
        memset(&postimage, 0, sizeof(postimage));
        oldlines = xmalloc(size);
 -      newlines = xmalloc(size);
 +      strbuf_init(&newlines, size);
  
        old = oldlines;
 -      new = newlines;
        while (size > 0) {
                char first;
                int len = linelen(patch, size);
 -              int plen, added;
 +              int plen;
                int added_blank_line = 0;
                int is_blank_context = 0;
 +              size_t start;
  
                if (!len)
                        break;
                                /* ... followed by '\No newline'; nothing */
                                break;
                        *old++ = '\n';
 -                      *new++ = '\n';
 +                      strbuf_addch(&newlines, '\n');
                        add_line_info(&preimage, "\n", 1, LINE_COMMON);
                        add_line_info(&postimage, "\n", 1, LINE_COMMON);
                        is_blank_context = 1;
                        if (first == '+' && no_add)
                                break;
  
 +                      start = newlines.len;
                        if (first != '+' ||
                            !whitespace_error ||
                            ws_error_action != correct_ws_error) {
 -                              memcpy(new, patch + 1, plen);
 -                              added = plen;
 +                              strbuf_add(&newlines, patch + 1, plen);
                        }
                        else {
 -                              added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
 +                              ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
                        }
 -                      add_line_info(&postimage, new, added,
 +                      add_line_info(&postimage, newlines.buf + start, newlines.len - start,
                                      (first == '+' ? 0 : LINE_COMMON));
 -                      new += added;
                        if (first == '+' &&
                            (ws_rule & WS_BLANK_AT_EOF) &&
                            ws_blank_line(patch + 1, plen, ws_rule))
        }
        if (inaccurate_eof &&
            old > oldlines && old[-1] == '\n' &&
 -          new > newlines && new[-1] == '\n') {
 +          newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
                old--;
 -              new--;
 +              strbuf_setlen(&newlines, newlines.len - 1);
        }
  
        leading = frag->leading;
        pos = frag->newpos ? (frag->newpos - 1) : 0;
        preimage.buf = oldlines;
        preimage.len = old - oldlines;
 -      postimage.buf = newlines;
 -      postimage.len = new - newlines;
 +      postimage.buf = newlines.buf;
 +      postimage.len = newlines.len;
        preimage.line = preimage.line_allocated;
        postimage.line = postimage.line_allocated;
  
        }
  
        free(oldlines);
 -      free(newlines);
 +      strbuf_release(&newlines);
        free(preimage.line_allocated);
        free(postimage.line_allocated);
  
@@@ -3138,7 -3141,11 +3138,7 @@@ static void remove_file(struct patch *p
                        die("unable to remove %s from index", patch->old_name);
        }
        if (!cached) {
 -              if (S_ISGITLINK(patch->old_mode)) {
 -                      if (rmdir(patch->old_name))
 -                              warning("unable to remove submodule %s",
 -                                      patch->old_name);
 -              } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
 +              if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
                        remove_path(patch->old_name);
                }
        }
diff --combined builtin/blame.c
index 8506286dd271d4e92369d81ec2cce9240a559d64,1a42e2b77e9977ba927db839da0f44cfbd89450a..729b43058a8afe01b03a463d9c11f5d74d9c8d43
@@@ -39,7 -39,7 +39,7 @@@ static int show_root
  static int reverse;
  static int blank_boundary;
  static int incremental;
 -static int xdl_opts = XDF_NEED_MINIMAL;
 +static int xdl_opts;
  
  static enum date_mode blame_date_mode = DATE_ISO8601;
  static size_t blame_date_width;
@@@ -733,10 -733,11 +733,11 @@@ static int pass_blame_to_parent(struct 
  {
        int last_in_target;
        mmfile_t file_p, file_o;
-       struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+       struct blame_chunk_cb_data d;
        xpparam_t xpp;
        xdemitconf_t xecfg;
+       memset(&d, 0, sizeof(d));
+       d.sb = sb; d.target = target; d.parent = parent;
        last_in_target = find_last_in_target(sb, target);
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
@@@ -875,10 -876,11 +876,11 @@@ static void find_copy_in_blob(struct sc
        const char *cp;
        int cnt;
        mmfile_t file_o;
-       struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+       struct handle_split_cb_data d;
        xpparam_t xpp;
        xdemitconf_t xecfg;
+       memset(&d, 0, sizeof(d));
+       d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
        /*
         * Prepare mmfile that contains only the lines in ent.
         */
@@@ -1589,7 -1591,7 +1591,7 @@@ static void emit_porcelain(struct score
        strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
        printf("%s%c%d %d %d\n",
               hex,
 -             ent->guilty ? ' ' : '*', // purely for debugging
 +             ent->guilty ? ' ' : '*', /* purely for debugging */
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
diff --combined builtin/checkout.c
index 72e4fbc729f0afcc459ab67926e9c2575fd72094,9820351a30001d6d98d9aaea0ef01b56576bebe0..1994be92c66257da18c31502bda605be170fd092
@@@ -33,7 -33,6 +33,7 @@@ struct checkout_opts 
        int writeout_error;
  
        const char *new_branch;
 +      const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
  };
@@@ -493,26 -492,8 +493,26 @@@ static void update_refs_for_switch(stru
        struct strbuf msg = STRBUF_INIT;
        const char *old_desc;
        if (opts->new_branch) {
 -              create_branch(old->name, opts->new_branch, new->name, 0,
 -                            opts->new_branch_log, opts->track);
 +              if (opts->new_orphan_branch) {
 +                      if (opts->new_branch_log && !log_all_ref_updates) {
 +                              int temp;
 +                              char log_file[PATH_MAX];
 +                              char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
 +
 +                              temp = log_all_ref_updates;
 +                              log_all_ref_updates = 1;
 +                              if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
 +                                      fprintf(stderr, "Can not do reflog for '%s'\n",
 +                                          opts->new_orphan_branch);
 +                                      log_all_ref_updates = temp;
 +                                      return;
 +                              }
 +                              log_all_ref_updates = temp;
 +                      }
 +              }
 +              else
 +                      create_branch(old->name, opts->new_branch, new->name, 0,
 +                                    opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
        }
                                        opts->new_branch ? " a new" : "",
                                        new->name);
                }
 +              if (old->path && old->name) {
 +                      char log_file[PATH_MAX], ref_file[PATH_MAX];
 +
 +                      git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
 +                      git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
 +                      if (!file_exists(ref_file) && file_exists(log_file))
 +                              remove_path(log_file);
 +              }
        } else if (strcmp(new->name, "HEAD")) {
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
                           REF_NODEREF, DIE_ON_ERR);
@@@ -636,7 -609,8 +636,8 @@@ static int check_tracking_name(const ch
  
  static const char *unique_tracking_name(const char *name)
  {
-       struct tracking_name_data cb_data = { name, NULL, 1 };
+       struct tracking_name_data cb_data = { NULL, NULL, 1 };
+       cb_data.name = name;
        for_each_ref(check_tracking_name, &cb_data);
        if (cb_data.unique)
                return cb_data.remote;
@@@ -660,7 -634,6 +661,7 @@@ int cmd_checkout(int argc, const char *
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
                OPT_SET_INT('t', "track",  &opts.track, "track",
                        BRANCH_TRACK_EXPLICIT),
 +              OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
                OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
                opts.new_branch = argv0 + 1;
        }
  
 +      if (opts.new_orphan_branch) {
 +              if (opts.new_branch)
 +                      die("--orphan and -b are mutually exclusive");
 +              if (opts.track > 0)
 +                      die("--orphan cannot be used with -t");
 +              opts.new_branch = opts.new_orphan_branch;
 +      }
 +
        if (conflict_style) {
                opts.merge = 1; /* implied */
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
diff --combined builtin/commit.c
index a8616866435a0a73aaa74ac1742399d7381c018a,30a00e0b633b765221d4717da7af4259c8608254..0e3ae3c11d7d3a582f3cb549d7d17aa1aea67d56
@@@ -57,7 -57,7 +57,7 @@@ static struct lock_file false_lock; /* 
  static enum {
        COMMIT_AS_IS = 1,
        COMMIT_NORMAL,
-       COMMIT_PARTIAL,
+       COMMIT_PARTIAL
  } commit_style;
  
  static const char *logfile, *force_author;
@@@ -66,7 -66,7 +66,7 @@@ static char *edit_message, *use_message
  static char *author_name, *author_email, *author_date;
  static int all, edit_flag, also, interactive, only, amend, signoff;
  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 -static int no_post_rewrite;
 +static int no_post_rewrite, allow_empty_message;
  static char *untracked_files_arg, *force_date;
  /*
   * The default commit message cleanup mode will remove the lines
  static enum {
        CLEANUP_SPACE,
        CLEANUP_NONE,
-       CLEANUP_ALL,
+       CLEANUP_ALL
  } cleanup_mode;
  static char *cleanup_arg;
  
  static int use_editor = 1, initial_commit, in_merge, include_status = 1;
 +static int show_ignored_in_status;
  static const char *only_include_assumed;
  static struct strbuf message;
  
@@@ -91,9 -90,8 +91,9 @@@ static int null_termination
  static enum {
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
-       STATUS_FORMAT_PORCELAIN,
+       STATUS_FORMAT_PORCELAIN
  } status_format = STATUS_FORMAT_LONG;
 +static int status_show_branch;
  
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
@@@ -135,7 -133,6 +135,7 @@@ static struct option builtin_commit_opt
        OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
        OPT_SET_INT(0, "short", &status_format, "show status concisely",
                    STATUS_FORMAT_SHORT),
 +      OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
                    "show porcelain output format", STATUS_FORMAT_PORCELAIN),
        OPT_BOOLEAN('z', "null", &null_termination,
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
        { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 -      OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
        /* end commit contents options */
  
 +      { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
 +        "ok to record an empty change",
 +        PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 +      { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
 +        "ok to record a change with an empty message",
 +        PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 +
        OPT_END()
  };
  
@@@ -426,7 -417,7 +426,7 @@@ static int run_status(FILE *fp, const c
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
 -              wt_shortstatus_print(s, null_termination);
 +              wt_shortstatus_print(s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s, null_termination);
@@@ -464,21 -455,15 +464,21 @@@ static void determine_author_info(void
                if (!a)
                        die("invalid commit: %s", use_message);
  
 -              lb = strstr(a + 8, " <");
 -              rb = strstr(a + 8, "> ");
 -              eol = strchr(a + 8, '\n');
 -              if (!lb || !rb || !eol)
 +              lb = strchrnul(a + strlen("\nauthor "), '<');
 +              rb = strchrnul(lb, '>');
 +              eol = strchrnul(rb, '\n');
 +              if (!*lb || !*rb || !*eol)
                        die("invalid commit: %s", use_message);
  
 -              name = xstrndup(a + 8, lb - (a + 8));
 -              email = xstrndup(lb + 2, rb - (lb + 2));
 -              date = xstrndup(rb + 2, eol - (rb + 2));
 +              if (lb == a + strlen("\nauthor "))
 +                      /* \nauthor <foo@example.com> */
 +                      name = xcalloc(1, 1);
 +              else
 +                      name = xmemdupz(a + strlen("\nauthor "),
 +                                      (lb - strlen(" ") -
 +                                       (a + strlen("\nauthor "))));
 +              email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
 +              date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
        }
  
        if (force_author) {
@@@ -732,7 -717,8 +732,8 @@@ static int prepare_to_commit(const cha
  
        if (use_editor) {
                char index[PATH_MAX];
-               const char *env[2] = { index, NULL };
+               const char *env[2] = { NULL };
+               env[0] =  index;
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
                if (launch_editor(git_path(commit_editmsg), NULL, env)) {
                        fprintf(stderr,
@@@ -1032,14 -1018,11 +1033,14 @@@ static int git_status_config(const cha
  int cmd_status(int argc, const char **argv, const char *prefix)
  {
        struct wt_status s;
 +      int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
 +              OPT_BOOLEAN('b', "branch", &status_show_branch,
 +                          "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
                            "show porcelain output format",
                            STATUS_FORMAT_PORCELAIN),
                  "mode",
                  "show untracked files, optional modes: all, normal, no. (Default: all)",
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
 +              OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
 +                          "show ignored files"),
                OPT_END(),
        };
  
                             builtin_status_options,
                             builtin_status_usage, 0);
        handle_untracked_files_arg(&s);
 -
 +      if (show_ignored_in_status)
 +              s.show_ignored_files = 1;
        if (*argv)
                s.pathspec = get_pathspec(prefix, argv);
  
        read_cache_preload(s.pathspec);
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
 +
 +      fd = hold_locked_index(&index_lock, 0);
 +      if (0 <= fd) {
 +              if (!write_cache(fd, active_cache, active_nr))
 +                      commit_locked_index(&index_lock);
 +              rollback_lock_file(&index_lock);
 +      }
 +
        s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
        s.in_merge = in_merge;
        wt_status_collect(&s);
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
 -              wt_shortstatus_print(&s, null_termination);
 +              wt_shortstatus_print(&s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s, null_termination);
@@@ -1322,7 -1294,7 +1323,7 @@@ int cmd_commit(int argc, const char **a
  
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
 -      if (message_is_empty(&sb)) {
 +      if (message_is_empty(&sb) && !allow_empty_message) {
                rollback_index_files();
                fprintf(stderr, "Aborting commit due to empty commit message.\n");
                exit(1);
diff --combined builtin/receive-pack.c
index bb34757d27f32fff8cb15a8f99d8295b5e38cb9e,9225dae183e66559bc49411c3185b82a204b393a..5a75af415562b6f8f60c9d89ba6042888211170d
@@@ -9,7 -9,6 +9,7 @@@
  #include "object.h"
  #include "remote.h"
  #include "transport.h"
 +#include "string-list.h"
  
  static const char receive_pack_usage[] = "git receive-pack <git-dir>";
  
@@@ -17,7 -16,7 +17,7 @@@ enum deny_action 
        DENY_UNCONFIGURED,
        DENY_IGNORE,
        DENY_WARN,
-       DENY_REFUSE,
+       DENY_REFUSE
  };
  
  static int deny_deletes;
@@@ -130,12 -129,13 +130,12 @@@ static void write_head_info(void
  struct command {
        struct command *next;
        const char *error_string;
 +      unsigned int skip_update;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        char ref_name[FLEX_ARRAY]; /* more */
  };
  
 -static struct command *commands;
 -
  static const char pre_receive_hook[] = "hooks/pre-receive";
  static const char post_receive_hook[] = "hooks/post-receive";
  
@@@ -188,7 -188,7 +188,7 @@@ static int copy_to_sideband(int in, in
        return 0;
  }
  
 -static int run_receive_hook(const char *hook_name)
 +static int run_receive_hook(struct command *commands, const char *hook_name)
  {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
        struct command *cmd;
@@@ -447,15 -447,15 +447,15 @@@ static const char *update(struct comman
  
  static char update_post_hook[] = "hooks/post-update";
  
 -static void run_update_post_hook(struct command *cmd)
 +static void run_update_post_hook(struct command *commands)
  {
 -      struct command *cmd_p;
 +      struct command *cmd;
        int argc;
        const char **argv;
        struct child_process proc;
  
 -      for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
 -              if (cmd_p->error_string)
 +      for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
 +              if (cmd->error_string)
                        continue;
                argc++;
        }
        argv = xmalloc(sizeof(*argv) * (2 + argc));
        argv[0] = update_post_hook;
  
 -      for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
 +      for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
                char *p;
 -              if (cmd_p->error_string)
 +              if (cmd->error_string)
                        continue;
 -              p = xmalloc(strlen(cmd_p->ref_name) + 1);
 -              strcpy(p, cmd_p->ref_name);
 +              p = xmalloc(strlen(cmd->ref_name) + 1);
 +              strcpy(p, cmd->ref_name);
                argv[argc] = p;
                argc++;
        }
        }
  }
  
 -static void execute_commands(const char *unpacker_error)
 +static void check_aliased_update(struct command *cmd, struct string_list *list)
 +{
 +      struct string_list_item *item;
 +      struct command *dst_cmd;
 +      unsigned char sha1[20];
 +      char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
 +      int flag;
 +
 +      const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
 +
 +      if (!(flag & REF_ISSYMREF))
 +              return;
 +
 +      if ((item = string_list_lookup(dst_name, list)) == NULL)
 +              return;
 +
 +      cmd->skip_update = 1;
 +
 +      dst_cmd = (struct command *) item->util;
 +
 +      if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
 +          !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
 +              return;
 +
 +      dst_cmd->skip_update = 1;
 +
 +      strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
 +      strcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
 +      strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
 +      strcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
 +      rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
 +               " its target '%s' (%s..%s)",
 +               cmd->ref_name, cmd_oldh, cmd_newh,
 +               dst_cmd->ref_name, dst_oldh, dst_newh);
 +
 +      cmd->error_string = dst_cmd->error_string =
 +              "inconsistent aliased update";
 +}
 +
 +static void check_aliased_updates(struct command *commands)
 +{
 +      struct command *cmd;
 +      struct string_list ref_list = { NULL, 0, 0, 0 };
 +
 +      for (cmd = commands; cmd; cmd = cmd->next) {
 +              struct string_list_item *item =
 +                      string_list_append(cmd->ref_name, &ref_list);
 +              item->util = (void *)cmd;
 +      }
 +      sort_string_list(&ref_list);
 +
 +      for (cmd = commands; cmd; cmd = cmd->next)
 +              check_aliased_update(cmd, &ref_list);
 +
 +      string_list_clear(&ref_list, 0);
 +}
 +
 +static void execute_commands(struct command *commands, const char *unpacker_error)
  {
 -      struct command *cmd = commands;
 +      struct command *cmd;
        unsigned char sha1[20];
  
        if (unpacker_error) {
 -              while (cmd) {
 +              for (cmd = commands; cmd; cmd = cmd->next)
                        cmd->error_string = "n/a (unpacker error)";
 -                      cmd = cmd->next;
 -              }
                return;
        }
  
 -      if (run_receive_hook(pre_receive_hook)) {
 -              while (cmd) {
 +      if (run_receive_hook(commands, pre_receive_hook)) {
 +              for (cmd = commands; cmd; cmd = cmd->next)
                        cmd->error_string = "pre-receive hook declined";
 -                      cmd = cmd->next;
 -              }
                return;
        }
  
 +      check_aliased_updates(commands);
 +
        head_name = resolve_ref("HEAD", sha1, 0, NULL);
  
 -      while (cmd) {
 -              cmd->error_string = update(cmd);
 -              cmd = cmd->next;
 -      }
 +      for (cmd = commands; cmd; cmd = cmd->next)
 +              if (!cmd->skip_update)
 +                      cmd->error_string = update(cmd);
  }
  
 -static void read_head_info(void)
 +static struct command *read_head_info(void)
  {
 +      struct command *commands = NULL;
        struct command **p = &commands;
        for (;;) {
                static char line[1000];
                        if (strstr(refname + reflen + 1, "side-band-64k"))
                                use_sideband = LARGE_PACKET_MAX;
                }
 -              cmd = xmalloc(sizeof(struct command) + len - 80);
 +              cmd = xcalloc(1, sizeof(struct command) + len - 80);
                hashcpy(cmd->old_sha1, old_sha1);
                hashcpy(cmd->new_sha1, new_sha1);
                memcpy(cmd->ref_name, line + 82, len - 81);
 -              cmd->error_string = NULL;
 -              cmd->next = NULL;
                *p = cmd;
                p = &cmd->next;
        }
 +      return commands;
  }
  
  static const char *parse_pack_header(struct pack_header *hdr)
@@@ -697,7 -643,7 +697,7 @@@ static const char *unpack(void
        }
  }
  
 -static void report(const char *unpack_status)
 +static void report(struct command *commands, const char *unpack_status)
  {
        struct command *cmd;
        struct strbuf buf = STRBUF_INIT;
        strbuf_release(&buf);
  }
  
 -static int delete_only(struct command *cmd)
 +static int delete_only(struct command *commands)
  {
 -      while (cmd) {
 +      struct command *cmd;
 +      for (cmd = commands; cmd; cmd = cmd->next) {
                if (!is_null_sha1(cmd->new_sha1))
                        return 0;
 -              cmd = cmd->next;
        }
        return 1;
  }
@@@ -776,7 -722,6 +776,7 @@@ int cmd_receive_pack(int argc, const ch
        int stateless_rpc = 0;
        int i;
        char *dir = NULL;
 +      struct command *commands;
  
        argv++;
        for (i = 1; i < argc; i++) {
        if (advertise_refs)
                return 0;
  
 -      read_head_info();
 -      if (commands) {
 +      if ((commands = read_head_info()) != NULL) {
                const char *unpack_status = NULL;
  
                if (!delete_only(commands))
                        unpack_status = unpack();
 -              execute_commands(unpack_status);
 +              execute_commands(commands, unpack_status);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
                if (report_status)
 -                      report(unpack_status);
 -              run_receive_hook(post_receive_hook);
 +                      report(commands, unpack_status);
 +              run_receive_hook(commands, post_receive_hook);
                run_update_post_hook(commands);
                if (auto_gc) {
                        const char *argv_gc_auto[] = {
diff --combined builtin/remote.c
index 4745957b9602ed9fe5c983de35ad74cc85e9b537,bd08c0dd8995484e87515f431ba13e019a013ee9..0a52667e0f7cc14d3a760bf0ec5f6857a0ce9051
@@@ -16,7 -16,6 +16,7 @@@ static const char * const builtin_remot
        "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
 +      "git remote set-branches <name> [--add] <branch>...",
        "git remote set-url <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
@@@ -43,12 -42,6 +43,12 @@@ static const char * const builtin_remot
        NULL
  };
  
 +static const char * const builtin_remote_setbranches_usage[] = {
 +      "git remote set-branches <name> <branch>...",
 +      "git remote set-branches --add <name> <branch>...",
 +      NULL
 +};
 +
  static const char * const builtin_remote_show_usage[] = {
        "git remote show [<options>] <name>",
        NULL
@@@ -111,29 -104,9 +111,29 @@@ static int fetch_remote(const char *nam
        return 0;
  }
  
 +enum {
 +      TAGS_UNSET = 0,
 +      TAGS_DEFAULT = 1,
 +      TAGS_SET = 2
 +};
 +
 +static int add_branch(const char *key, const char *branchname,
 +              const char *remotename, int mirror, struct strbuf *tmp)
 +{
 +      strbuf_reset(tmp);
 +      strbuf_addch(tmp, '+');
 +      if (mirror)
 +              strbuf_addf(tmp, "refs/%s:refs/%s",
 +                              branchname, branchname);
 +      else
 +              strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
 +                              branchname, remotename, branchname);
 +      return git_config_set_multivar(key, tmp->buf, "^$", 0);
 +}
 +
  static int add(int argc, const char **argv)
  {
 -      int fetch = 0, mirror = 0;
 +      int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
        struct string_list track = { NULL, 0, 0 };
        const char *master = NULL;
        struct remote *remote;
  
        struct option options[] = {
                OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
 +              OPT_SET_INT(0, "tags", &fetch_tags,
 +                          "import all tags and associated objects when fetching",
 +                          TAGS_SET),
 +              OPT_SET_INT(0, NULL, &fetch_tags,
 +                          "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
                OPT_CALLBACK('t', "track", &track, "branch",
                        "branch(es) to track", opt_parse_track),
                OPT_STRING('m', "master", &master, "branch", "master branch"),
        if (track.nr == 0)
                string_list_append("*", &track);
        for (i = 0; i < track.nr; i++) {
 -              struct string_list_item *item = track.items + i;
 -
 -              strbuf_reset(&buf2);
 -              strbuf_addch(&buf2, '+');
 -              if (mirror)
 -                      strbuf_addf(&buf2, "refs/%s:refs/%s",
 -                                      item->string, item->string);
 -              else
 -                      strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
 -                                      item->string, name, item->string);
 -              if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
 +              if (add_branch(buf.buf, track.items[i].string,
 +                              name, mirror, &buf2))
                        return 1;
        }
  
                        return 1;
        }
  
 +      if (fetch_tags != TAGS_DEFAULT) {
 +              strbuf_reset(&buf);
 +              strbuf_addf(&buf, "remote.%s.tagopt", name);
 +              if (git_config_set(buf.buf,
 +                      fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
 +                      return 1;
 +      }
 +
        if (fetch && fetch_remote(name))
                return 1;
  
@@@ -348,7 -317,7 +348,7 @@@ struct push_info 
                PUSH_STATUS_UPTODATE,
                PUSH_STATUS_FASTFORWARD,
                PUSH_STATUS_OUTOFDATE,
-               PUSH_STATUS_NOTQUERIED,
+               PUSH_STATUS_NOTQUERIED
        } status;
  };
  
@@@ -736,11 -705,14 +736,14 @@@ static int rm(int argc, const char **ar
        struct known_remotes known_remotes = { NULL, NULL };
        struct string_list branches = { NULL, 0, 0, 1 };
        struct string_list skipped = { NULL, 0, 0, 1 };
-       struct branches_for_remote cb_data = {
-               NULL, &branches, &skipped, &known_remotes
-       };
+       struct branches_for_remote cb_data;
        int i, result;
  
+       memset(&cb_data, 0, sizeof(cb_data));
+       cb_data.branches = &branches;
+       cb_data.skipped = &skipped;
+       cb_data.keep = &known_remotes;
        if (argc != 2)
                usage_with_options(builtin_remote_rm_usage, options);
  
@@@ -1296,72 -1268,6 +1299,72 @@@ static int update(int argc, const char 
        return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
  }
  
 +static int remove_all_fetch_refspecs(const char *remote, const char *key)
 +{
 +      return git_config_set_multivar(key, NULL, NULL, 1);
 +}
 +
 +static int add_branches(struct remote *remote, const char **branches,
 +                      const char *key)
 +{
 +      const char *remotename = remote->name;
 +      int mirror = remote->mirror;
 +      struct strbuf refspec = STRBUF_INIT;
 +
 +      for (; *branches; branches++)
 +              if (add_branch(key, *branches, remotename, mirror, &refspec)) {
 +                      strbuf_release(&refspec);
 +                      return 1;
 +              }
 +
 +      strbuf_release(&refspec);
 +      return 0;
 +}
 +
 +static int set_remote_branches(const char *remotename, const char **branches,
 +                              int add_mode)
 +{
 +      struct strbuf key = STRBUF_INIT;
 +      struct remote *remote;
 +
 +      strbuf_addf(&key, "remote.%s.fetch", remotename);
 +
 +      if (!remote_is_configured(remotename))
 +              die("No such remote '%s'", remotename);
 +      remote = remote_get(remotename);
 +
 +      if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
 +              strbuf_release(&key);
 +              return 1;
 +      }
 +      if (add_branches(remote, branches, key.buf)) {
 +              strbuf_release(&key);
 +              return 1;
 +      }
 +
 +      strbuf_release(&key);
 +      return 0;
 +}
 +
 +static int set_branches(int argc, const char **argv)
 +{
 +      int add_mode = 0;
 +      struct option options[] = {
 +              OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
 +              OPT_END()
 +      };
 +
 +      argc = parse_options(argc, argv, NULL, options,
 +                           builtin_remote_setbranches_usage, 0);
 +      if (argc == 0) {
 +              error("no remote specified");
 +              usage_with_options(builtin_remote_seturl_usage, options);
 +      }
 +      argv[argc] = NULL;
 +
 +      return set_remote_branches(argv[0], argv + 1, add_mode);
 +}
 +
  static int set_url(int argc, const char **argv)
  {
        int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@@ -1527,8 -1433,6 +1530,8 @@@ int cmd_remote(int argc, const char **a
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "set-head"))
                result = set_head(argc, argv);
 +      else if (!strcmp(argv[0], "set-branches"))
 +              result = set_branches(argc, argv);
        else if (!strcmp(argv[0], "set-url"))
                result = set_url(argc, argv);
        else if (!strcmp(argv[0], "show"))
diff --combined cache.h
index c96602305f94b50e3f39be124c5e8d5af5a36f51,2e9409cc96fe65b69a5bdd2fcbe64ffabef02712..5e55367bf596d49a0e507d820aa3c104b9a7b6d5
+++ b/cache.h
@@@ -361,7 -361,7 +361,7 @@@ enum object_type 
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
        OBJ_ANY,
-       OBJ_MAX,
+       OBJ_MAX
  };
  
  static inline enum object_type object_type(unsigned int mode)
@@@ -556,7 -556,7 +556,7 @@@ extern int core_apply_sparse_checkout
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
        SAFE_CRLF_FAIL = 1,
-       SAFE_CRLF_WARN = 2,
+       SAFE_CRLF_WARN = 2
  };
  
  extern enum safe_crlf safe_crlf;
@@@ -567,21 -567,21 +567,21 @@@ enum branch_track 
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
-       BRANCH_TRACK_OVERRIDE,
+       BRANCH_TRACK_OVERRIDE
  };
  
  enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
        AUTOREBASE_REMOTE,
-       AUTOREBASE_ALWAYS,
+       AUTOREBASE_ALWAYS
  };
  
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
        PUSH_DEFAULT_TRACKING,
-       PUSH_DEFAULT_CURRENT,
+       PUSH_DEFAULT_CURRENT
  };
  
  extern enum branch_track git_branch_track;
@@@ -590,7 -590,7 +590,7 @@@ extern enum push_default_type push_defa
  
  enum object_creation_mode {
        OBJECT_CREATION_USES_HARDLINKS = 0,
-       OBJECT_CREATION_USES_RENAMES = 1,
+       OBJECT_CREATION_USES_RENAMES = 1
  };
  
  extern enum object_creation_mode object_creation_mode;
@@@ -670,7 -670,7 +670,7 @@@ enum sharedrepo 
        OLD_PERM_GROUP      = 1,
        OLD_PERM_EVERYBODY  = 2,
        PERM_GROUP          = 0660,
-       PERM_EVERYBODY      = 0664,
+       PERM_EVERYBODY      = 0664
  };
  int git_config_perm(const char *var, const char *value);
  int set_shared_perm(const char *path, int mode);
@@@ -718,8 -718,6 +718,8 @@@ extern int has_loose_object_nonlocal(co
  
  extern int has_pack_index(const unsigned char *sha1);
  
 +extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
 +
  extern const signed char hexval_table[256];
  static inline unsigned int hexval(unsigned char c)
  {
@@@ -882,7 -880,7 +882,7 @@@ struct ref 
                REF_STATUS_REJECT_NODELETE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
-               REF_STATUS_EXPECTING_REPORT,
+               REF_STATUS_EXPECTING_REPORT
        } status;
        char *remote_status;
        struct ref *peer_ref; /* when renaming */
@@@ -907,7 -905,7 +907,7 @@@ struct extra_have_objects 
  extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
  
 -extern struct packed_git *parse_pack_index(unsigned char *sha1);
 +extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
@@@ -918,7 -916,6 +918,7 @@@ extern struct packed_git *find_sha1_pac
  
  extern void pack_report(void);
  extern int open_pack_index(struct packed_git *);
 +extern void close_pack_index(struct packed_git *);
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
@@@ -939,15 -936,12 +939,15 @@@ extern int update_server_info(int)
  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_parse_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_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
  extern int git_config_bool_or_int(const char *, const char *, int *);
  extern int git_config_bool(const char *, const char *);
 +extern int git_config_maybe_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
@@@ -955,7 -949,6 +955,7 @@@ extern int git_config_set_multivar(cons
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
 +extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
@@@ -1047,7 -1040,6 +1047,7 @@@ void shift_tree_by(const unsigned char 
  #define WS_INDENT_WITH_NON_TAB        04
  #define WS_CR_AT_EOL           010
  #define WS_BLANK_AT_EOF        020
 +#define WS_TAB_IN_INDENT       040
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
  extern unsigned whitespace_rule_cfg;
@@@ -1056,7 -1048,7 +1056,7 @@@ extern unsigned parse_whitespace_rule(c
  extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
  extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
  extern char *whitespace_error_string(unsigned ws);
 -extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
 +extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
  
  /* ls-files */
diff --combined commit.h
index 6ef88dcf45d6c5b2cd0f26397db2fc7b8859d581,95de81439e578ba2ff8df6aaa708b6dc14b089dd..e958a7c3dfbb3ff76410dc83e47d0ffa950af9ea
+++ b/commit.h
@@@ -60,7 -60,7 +60,7 @@@ enum cmit_fmt 
        CMIT_FMT_EMAIL,
        CMIT_FMT_USERFORMAT,
  
-       CMIT_FMT_UNSPECIFIED,
+       CMIT_FMT_UNSPECIFIED
  };
  
  struct pretty_print_context
@@@ -163,8 -163,4 +163,8 @@@ static inline int single_parent(struct 
  
  struct commit_list *reduce_heads(struct commit_list *heads);
  
 +extern int commit_tree(const char *msg, unsigned char *tree,
 +              struct commit_list *parents, unsigned char *ret,
 +              const char *author);
 +
  #endif /* COMMIT_H */
diff --combined config.mak.in
index 0d4b64d076b8041a3701715a83ca46a4675ac9d6,e2dbd1c0bb862714a3b2b555d8460dde339a72d9..b4e65c32b235eafafefeed1c755be7e1ad5c71ae
@@@ -3,10 -3,12 +3,12 @@@
  
  CC = @CC@
  CFLAGS = @CFLAGS@
+ CPPFLAGS = @CPPFLAGS@
  LDFLAGS = @LDFLAGS@
  CC_LD_DYNPATH = @CC_LD_DYNPATH@
  AR = @AR@
  TAR = @TAR@
+ DIFF = @DIFF@
  #INSTALL = @INSTALL@          # needs install-sh or install.sh in sources
  TCLTK_PATH = @TCLTK_PATH@
  
@@@ -31,7 -33,6 +33,7 @@@ NO_OPENSSL=@NO_OPENSSL
  NO_CURL=@NO_CURL@
  NO_EXPAT=@NO_EXPAT@
  NO_LIBGEN_H=@NO_LIBGEN_H@
 +HAVE_PATHS_H=@HAVE_PATHS_H@
  NEEDS_LIBICONV=@NEEDS_LIBICONV@
  NEEDS_SOCKET=@NEEDS_SOCKET@
  NEEDS_RESOLV=@NEEDS_RESOLV@
@@@ -42,6 -43,7 +44,7 @@@ NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIREN
  NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@
  NO_IPV6=@NO_IPV6@
  NO_C99_FORMAT=@NO_C99_FORMAT@
+ NO_HSTRERROR=@NO_HSTRERROR@
  NO_STRCASESTR=@NO_STRCASESTR@
  NO_MEMMEM=@NO_MEMMEM@
  NO_STRLCPY=@NO_STRLCPY@
@@@ -51,10 -53,15 +54,15 @@@ NO_SETENV=@NO_SETENV
  NO_UNSETENV=@NO_UNSETENV@
  NO_MKDTEMP=@NO_MKDTEMP@
  NO_MKSTEMPS=@NO_MKSTEMPS@
+ NO_INET_NTOP=@NO_INET_NTOP@
+ NO_INET_PTON=@NO_INET_PTON@
  NO_ICONV=@NO_ICONV@
  OLD_ICONV=@OLD_ICONV@
  NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+ INLINE=@INLINE@
+ SOCKLEN_T=@SOCKLEN_T@
  FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
  SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
  NO_PTHREADS=@NO_PTHREADS@
+ PTHREAD_CFLAGS=@PTHREAD_CFLAGS@
  PTHREAD_LIBS=@PTHREAD_LIBS@
diff --combined configure.ac
index 71038fcf1cd04fdfa3bcd133b6fc82974a135141,b33cc6a3163c818daf6a7002394af15268a9c9df..5601e8bac953c670e35f32ffe48d157dd5694ce7
@@@ -327,6 -327,12 +327,12 @@@ GIT_PARSE_WITH(tcltk)
  AC_MSG_NOTICE([CHECKS for programs])
  #
  AC_PROG_CC([cc gcc])
+ AC_C_INLINE
+ case $ac_cv_c_inline in
+   inline | yes | no)  ;;
+   *)                  AC_SUBST([INLINE], [$ac_cv_c_inline]) ;;
+ esac
  # which switch to pass runtime path to dynamic libraries to the linker
  AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
     SAVE_LDFLAGS="${LDFLAGS}"
@@@ -362,6 -368,7 +368,7 @@@ f
  #AC_PROG_INSTALL              # needs install-sh or install.sh in sources
  AC_CHECK_TOOLS(AR, [gar ar], :)
  AC_CHECK_PROGS(TAR, [gtar tar])
+ AC_CHECK_PROGS(DIFF, [gnudiff gdiff diff])
  # TCLTK_PATH will be set to some value if we want Tcl/Tk
  # or will be empty otherwise.
  if test -z "$NO_TCLTK"; then
@@@ -544,13 -551,47 +551,47 @@@ AC_SUBST(NEEDS_SOCKET
  test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
  
  #
- # Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
- # Notably on Solaris hstrerror resides in libresolv and on Solaris 7
- # inet_ntop and inet_pton additionally reside there.
- AC_CHECK_LIB([c], [hstrerror],
- [NEEDS_RESOLV=],
- [NEEDS_RESOLV=YesPlease])
+ # The next few tests will define NEEDS_RESOLV if linking with
+ # libresolv provides some of the functions we would normally get
+ # from libc.
+ NEEDS_RESOLV=
  AC_SUBST(NEEDS_RESOLV)
+ #
+ # Define NO_INET_NTOP if linking with -lresolv is not enough.
+ # Solaris 2.7 in particular hos inet_ntop in -lresolv.
+ NO_INET_NTOP=
+ AC_SUBST(NO_INET_NTOP)
+ AC_CHECK_FUNC([inet_ntop],
+       [],
+     [AC_CHECK_LIB([resolv], [inet_ntop],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_INET_NTOP=YesPlease])
+ ])
+ #
+ # Define NO_INET_PTON if linking with -lresolv is not enough.
+ # Solaris 2.7 in particular hos inet_pton in -lresolv.
+ NO_INET_PTON=
+ AC_SUBST(NO_INET_PTON)
+ AC_CHECK_FUNC([inet_pton],
+       [],
+     [AC_CHECK_LIB([resolv], [inet_pton],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_INET_PTON=YesPlease])
+ ])
+ #
+ # Define NO_HSTRERROR if linking with -lresolv is not enough.
+ # Solaris 2.6 in particular has no hstrerror, even in -lresolv.
+ NO_HSTRERROR=
+ AC_CHECK_FUNC([hstrerror],
+       [],
+     [AC_CHECK_LIB([resolv], [hstrerror],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_HSTRERROR=YesPlease])
+ ])
+ AC_SUBST(NO_HSTRERROR)
+ #
+ # If any of the above tests determined that -lresolv is needed at
+ # build-time, also set it here for remaining configure-time checks.
  test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
  
  AC_CHECK_LIB([c], [basename],
@@@ -598,6 -639,12 +639,12 @@@ AC_SUBST(OLD_ICONV
  ## Checks for typedefs, structures, and compiler characteristics.
  AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
  #
+ TYPE_SOCKLEN_T
+ case $ac_cv_type_socklen_t in
+   yes)        ;;
+   *)          AC_SUBST([SOCKLEN_T], [$git_cv_socklen_t_equiv]) ;;
+ esac
  # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
  AC_CHECK_MEMBER(struct dirent.d_ino,
  [NO_D_INO_IN_DIRENT=],
@@@ -724,12 -771,6 +771,12 @@@ AC_CHECK_HEADER([libgen.h]
  [NO_LIBGEN_H=YesPlease])
  AC_SUBST(NO_LIBGEN_H)
  #
 +# Define HAVE_PATHS_H if you have paths.h.
 +AC_CHECK_HEADER([paths.h],
 +[HAVE_PATHS_H=YesPlease],
 +[HAVE_PATHS_H=])
 +AC_SUBST(HAVE_PATHS_H)
 +#
  # Define NO_STRCASESTR if you don't have strcasestr.
  GIT_CHECK_FUNC(strcasestr,
  [NO_STRCASESTR=],
@@@ -808,7 -849,11 +855,11 @@@ AC_DEFUN([PTHREADTEST_SRC], 
  int main(void)
  {
        pthread_mutex_t test_mutex;
-       return (0);
+       int retcode = 0;
+       retcode |= pthread_mutex_init(&test_mutex,(void *)0);
+       retcode |= pthread_mutex_lock(&test_mutex);
+       retcode |= pthread_mutex_unlock(&test_mutex);
+       return retcode;
  }
  ])
  
@@@ -825,7 -870,8 +876,8 @@@ if test -n "$USER_NOPTHREAD"; the
  # handle these separately since PTHREAD_CFLAGS could be '-lpthreads
  # -D_REENTRANT' or some such.
  elif test -z "$PTHREAD_CFLAGS"; then
-   for opt in -pthread -lpthread; do
+   threads_found=no
+   for opt in -mt -pthread -lpthread; do
       old_CFLAGS="$CFLAGS"
       CFLAGS="$opt $CFLAGS"
       AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
        [AC_MSG_RESULT([yes])
                NO_PTHREADS=
                PTHREAD_LIBS="$opt"
+               PTHREAD_CFLAGS="$opt"
+               threads_found=yes
                break
        ],
        [AC_MSG_RESULT([no])])
        CFLAGS="$old_CFLAGS"
    done
+   if test $threads_found != yes; then
+     AC_CHECK_LIB([pthread], [pthread_create],
+       [PTHREAD_LIBS="-lpthread"],
+       [NO_PTHREADS=UnfortunatelyYes])
+   fi
  else
    old_CFLAGS="$CFLAGS"
    CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
@@@ -854,6 -907,7 +913,7 @@@ f
  
  CFLAGS="$old_CFLAGS"
  
+ AC_SUBST(PTHREAD_CFLAGS)
  AC_SUBST(PTHREAD_LIBS)
  AC_SUBST(NO_PTHREADS)
  
diff --combined connect.c
index 0119bd377b23388c0c9ea41ee9fd134034c5a443,fc8f15502807a2bcb4e4cd1d358b5ac0befb8853..02e738a0146a5c46aaf3f1d8edc3c055a99e98b9
+++ b/connect.c
@@@ -5,7 -5,6 +5,7 @@@
  #include "refs.h"
  #include "run-command.h"
  #include "remote.h"
 +#include "url.h"
  
  static char *server_capabilities;
  
@@@ -132,7 -131,7 +132,7 @@@ int path_match(const char *path, int nr
  enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
-       PROTO_GIT,
+       PROTO_GIT
  };
  
  static enum protocol get_protocol(const char *name)
@@@ -451,7 -450,7 +451,7 @@@ static struct child_process no_fork
  struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
  {
 -      char *url = xstrdup(url_orig);
 +      char *url;
        char *host, *path;
        char *end;
        int c;
         */
        signal(SIGCHLD, SIG_DFL);
  
 +      if (is_url(url_orig))
 +              url = url_decode(url_orig);
 +      else
 +              url = xstrdup(url_orig);
 +
        host = strstr(url, "://");
        if (host) {
                *host = '\0';
diff --combined convert.c
index 5a0b7fbca4cc4999df6ddd05e5387afb3a0774b2,3fea3e95091b5c49a7b38db381524f50436d11cf..824bd047a548800d51abd7ae9d6f3e2eef9b160c
+++ b/convert.c
@@@ -249,7 -249,9 +249,9 @@@ static int filter_buffer(int in, int ou
        struct child_process child_process;
        struct filter_params *params = (struct filter_params *)data;
        int write_err, status;
-       const char *argv[] = { params->cmd, NULL };
+       const char *argv[] = { NULL, NULL };
+       argv[0] = params->cmd;
  
        memset(&child_process, 0, sizeof(child_process));
        child_process.argv = argv;
@@@ -425,8 -427,6 +427,8 @@@ static int count_ident(const char *cp, 
                                cnt++;
                                break;
                        }
 +                      if (ch == '\n')
 +                              break;
                }
        }
        return cnt;
@@@ -457,11 -457,6 +459,11 @@@ static int ident_to_git(const char *pat
                        dollar = memchr(src + 3, '$', len - 3);
                        if (!dollar)
                                break;
 +                      if (memchr(src + 3, '\n', dollar - src - 3)) {
 +                              /* Line break before the next dollar. */
 +                              continue;
 +                      }
 +
                        memcpy(dst, "Id$", 3);
                        dst += 3;
                        len -= dollar + 1 - src;
@@@ -477,7 -472,7 +479,7 @@@ static int ident_to_worktree(const cha
                               struct strbuf *buf, int ident)
  {
        unsigned char sha1[20];
 -      char *to_free = NULL, *dollar;
 +      char *to_free = NULL, *dollar, *spc;
        int cnt;
  
        if (!ident)
                } else if (src[2] == ':') {
                        /*
                         * It's possible that an expanded Id has crept its way into the
 -                       * repository, we cope with that by stripping the expansion out
 +                       * repository, we cope with that by stripping the expansion out.
 +                       * This is probably not a good idea, since it will cause changes
 +                       * on checkout, which won't go away by stash, but let's keep it
 +                       * for git-style ids.
                         */
                        dollar = memchr(src + 3, '$', len - 3);
                        if (!dollar) {
                                break;
                        }
  
 +                      if (memchr(src + 3, '\n', dollar - src - 3)) {
 +                              /* Line break before the next dollar. */
 +                              continue;
 +                      }
 +
 +                      spc = memchr(src + 4, ' ', dollar - src - 4);
 +                      if (spc && spc < dollar-1) {
 +                              /* There are spaces in unexpected places.
 +                               * This is probably an id from some other
 +                               * versioning system. Keep it for now.
 +                               */
 +                              continue;
 +                      }
 +
                        len -= dollar + 1 - src;
                        src  = dollar + 1;
                } else {
diff --combined diff.h
index b4eefa759d87e22a877d668cd1f88e73cb4789d7,965b6c2617c60d2f2044078a72ca31273c06755f..48abe7a96a96df98ef91d5f5d7ba9f57cace777f
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -9,7 -9,6 +9,7 @@@
  struct rev_info;
  struct diff_options;
  struct diff_queue_struct;
 +struct strbuf;
  
  typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
@@@ -26,8 -25,6 +26,8 @@@ typedef void (*add_remove_fn_t)(struct 
  typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
                struct diff_options *options, void *data);
  
 +typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data);
 +
  #define DIFF_FORMAT_RAW               0x0001
  #define DIFF_FORMAT_DIFFSTAT  0x0002
  #define DIFF_FORMAT_NUMSTAT   0x0004
@@@ -57,7 -54,7 +57,7 @@@
  #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
  #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
  #define DIFF_OPT_COLOR_DIFF          (1 <<  8)
 -#define DIFF_OPT_COLOR_DIFF_WORDS    (1 <<  9)
 +/* (1 <<  9) unused */
  #define DIFF_OPT_HAS_CHANGES         (1 << 10)
  #define DIFF_OPT_QUICK               (1 << 11)
  #define DIFF_OPT_NO_INDEX            (1 << 12)
  #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
  #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
  
 +enum diff_words_type {
 +      DIFF_WORDS_NONE = 0,
 +      DIFF_WORDS_PORCELAIN,
 +      DIFF_WORDS_PLAIN,
 +      DIFF_WORDS_COLOR
 +};
 +
  struct diff_options {
        const char *filter;
        const char *orderfile;
        int stat_width;
        int stat_name_width;
        const char *word_regex;
 +      enum diff_words_type word_diff;
  
        /* this is set by diffcore for DIFF_FORMAT_PATCH */
        int found_changes;
        add_remove_fn_t add_remove;
        diff_format_fn_t format_callback;
        void *format_callback_data;
 +      diff_prefix_fn_t output_prefix;
 +      void *output_prefix_data;
  };
  
  enum color_diff {
        DIFF_FILE_NEW = 5,
        DIFF_COMMIT = 6,
        DIFF_WHITESPACE = 7,
-       DIFF_FUNCINFO = 8,
+       DIFF_FUNCINFO = 8
  };
  const char *diff_get_color(int diff_use_color, enum color_diff ix);
  #define diff_get_color_opt(o, ix) \
diff --combined dir.c
index 5615f33af187f381f8c2dfe7ab53910fe165fd59,a4bb0a3d071e1435f49b93a0cee0061abe4bc470..5e36f8e616d63f8a9c5950897e0f06235d8e9acf
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -465,7 -465,7 +465,7 @@@ static struct dir_entry *dir_add_ignore
  enum exist_status {
        index_nonexistent = 0,
        index_directory,
-       index_gitdir,
+       index_gitdir
  };
  
  /*
@@@ -533,7 -533,7 +533,7 @@@ static enum exist_status directory_exis
  enum directory_treatment {
        show_directory,
        ignore_directory,
-       recurse_into_directory,
+       recurse_into_directory
  };
  
  static enum directory_treatment treat_directory(struct dir_struct *dir,
@@@ -684,7 -684,7 +684,7 @@@ static int get_dtype(struct dirent *de
  enum path_treatment {
        path_ignored,
        path_handled,
-       path_recurse,
+       path_recurse
  };
  
  static enum path_treatment treat_one_path(struct dir_struct *dir,
@@@ -958,14 -958,9 +958,14 @@@ char *get_relative_cwd(char *buffer, in
        }
        if (*dir)
                return NULL;
 -      if (*cwd == '/')
 +      switch (*cwd) {
 +      case '\0':
 +              return cwd;
 +      case '/':
                return cwd + 1;
 -      return cwd;
 +      default:
 +              return NULL;
 +      }
  }
  
  int is_inside_dir(const char *dir)
diff --combined fast-import.c
index 129a786832c2dc863e33ae2fc039a0212c1751d5,faa51a97345d9f014701baeb26a0e72a791c693e..1e5d66ed0ab3b605de670758887b13f146bdd8a5
@@@ -267,7 -267,7 +267,7 @@@ struct hash_lis
  typedef enum {
        WHENSPEC_RAW = 1,
        WHENSPEC_RFC2822,
-       WHENSPEC_NOW,
+       WHENSPEC_NOW
  } whenspec_type;
  
  struct recent_command
@@@ -2707,7 -2707,6 +2707,7 @@@ static void option_import_marks(const c
        }
  
        import_marks_file = make_fast_import_path(marks);
 +      safe_create_leading_directories_const(import_marks_file);
        import_marks_file_from_stream = from_stream;
  }
  
@@@ -2738,7 -2737,6 +2738,7 @@@ static void option_active_branches(cons
  static void option_export_marks(const char *marks)
  {
        export_marks_file = make_fast_import_path(marks);
 +      safe_create_leading_directories_const(export_marks_file);
  }
  
  static void option_export_pack_edges(const char *edges)
diff --combined git-compat-util.h
index 81ceb7f906da563df8907d692b2628709ea8f660,cd3022e99f820074feb33d3ee55c75f0534b31b8..c9d53397ad6212cd1a10ab6c9e34d1b537ec486a
@@@ -56,7 -56,7 +56,7 @@@
  # define _XOPEN_SOURCE 500
  # endif
  #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
 -      !defined(_M_UNIX) && !defined(sgi) && !defined(__DragonFly__)
 +      !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__)
  #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
  #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
  #endif
@@@ -164,13 -164,6 +164,13 @@@ extern char *gitbasename(char *)
  #define PATH_SEP ':'
  #endif
  
 +#ifdef HAVE_PATHS_H
 +#include <paths.h>
 +#endif
 +#ifndef _PATH_DEFPATH
 +#define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
 +#endif
 +
  #ifndef STRIP_EXTENSION
  #define STRIP_EXTENSION ""
  #endif
@@@ -224,7 -217,6 +224,6 @@@ static inline const char *skip_prefix(c
  #define PROT_READ 1
  #define PROT_WRITE 2
  #define MAP_PRIVATE 1
- #define MAP_FAILED ((void*)-1)
  #endif
  
  #define mmap git_mmap
@@@ -253,6 -245,10 +252,10 @@@ extern int git_munmap(void *start, size
  
  #endif /* NO_MMAP */
  
+ #ifndef MAP_FAILED
+ #define MAP_FAILED ((void *)-1)
+ #endif
  #ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
  #define on_disk_bytes(st) ((st).st_size)
  #else
@@@ -363,9 -359,6 +366,9 @@@ static inline void *gitmempcpy(void *de
  
  extern void release_pack_memory(size_t, int);
  
 +typedef void (*try_to_free_t)(size_t);
 +extern try_to_free_t set_try_to_free_routine(try_to_free_t);
 +
  extern char *xstrdup(const char *str);
  extern void *xmalloc(size_t size);
  extern void *xmallocz(size_t size);
@@@ -489,14 -482,5 +492,14 @@@ void git_qsort(void *base, size_t nmemb
   * Always returns the return value of unlink(2).
   */
  int unlink_or_warn(const char *path);
 +/*
 + * Likewise for rmdir(2).
 + */
 +int rmdir_or_warn(const char *path);
 +/*
 + * Calls the correct function out of {unlink,rmdir}_or_warn based on
 + * the supplied file mode.
 + */
 +int remove_or_warn(unsigned int mode, const char *path);
  
  #endif
diff --combined grep.h
index 0aebebd96692f94877259bf64a1fece9e47d222e,062ef8dc28151befdb3fc780d52bf4550a901985..efa8cff980af2b2c06dad080876637d16b5c4985
--- 1/grep.h
--- 2/grep.h
+++ b/grep.h
@@@ -10,17 -10,17 +10,17 @@@ enum grep_pat_token 
        GREP_OPEN_PAREN,
        GREP_CLOSE_PAREN,
        GREP_NOT,
-       GREP_OR,
+       GREP_OR
  };
  
  enum grep_context {
        GREP_CONTEXT_HEAD,
-       GREP_CONTEXT_BODY,
+       GREP_CONTEXT_BODY
  };
  
  enum grep_header_field {
        GREP_HEADER_AUTHOR = 0,
-       GREP_HEADER_COMMITTER,
+       GREP_HEADER_COMMITTER
  };
  
  struct grep_pat {
@@@ -29,7 -29,6 +29,7 @@@
        int no;
        enum grep_pat_token token;
        const char *pattern;
 +      size_t patternlen;
        enum grep_header_field field;
        regex_t regexp;
        unsigned fixed:1;
@@@ -41,7 -40,7 +41,7 @@@ enum grep_expr_node 
        GREP_NODE_ATOM,
        GREP_NODE_NOT,
        GREP_NODE_AND,
-       GREP_NODE_OR,
+       GREP_NODE_OR
  };
  
  struct grep_expr {
@@@ -105,7 -104,6 +105,7 @@@ struct grep_opt 
        void *output_priv;
  };
  
 +extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
  extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
  extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
  extern void compile_grep_patterns(struct grep_opt *opt);
diff --combined http-walker.c
index 8ca76d0507bdc1d95283e1f5fee3f88180cdce26,cabac48eee968593fd885180710e95c99d901a24..18bd6504beb99ab68f360c4fa93011efae42fdfb
@@@ -15,7 -15,7 +15,7 @@@ enum object_request_state 
        WAITING,
        ABORTED,
        ACTIVE,
-       COMPLETE,
+       COMPLETE
  };
  
  struct object_request
@@@ -510,7 -510,7 +510,7 @@@ static int fetch_object(struct walker *
                ret = error("File %s has bad hash", hex);
        } else if (req->rename < 0) {
                ret = error("unable to write sha1 filename %s",
 -                          req->filename);
 +                          sha1_file_name(req->sha1));
        }
  
        release_http_object_request(req);
diff --combined merge-recursive.h
index 0cc465ec5d13385dd8ecdbd64387cf937c82bce3,344e47694c1a2561f0c79d59747d49604116e8b7..b831293b3865ae52584565039e4a893c3c6f45ff
@@@ -10,7 -10,7 +10,7 @@@ struct merge_options 
        enum {
                MERGE_RECURSIVE_NORMAL = 0,
                MERGE_RECURSIVE_OURS,
-               MERGE_RECURSIVE_THEIRS,
+               MERGE_RECURSIVE_THEIRS
        } recursive_variant;
        const char *subtree_shift;
        unsigned buffer_output : 1;
@@@ -54,7 -54,4 +54,7 @@@ int merge_recursive_generic(struct merg
  void init_merge_options(struct merge_options *o);
  struct tree *write_tree_from_memory(struct merge_options *o);
  
 +/* builtin/merge.c */
 +int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
 +
  #endif
diff --combined pretty.c
index 8b18efda9cd6eb615900687c0205daa832794c92,9a704ec41ebfc8f5490b0dc117f3a0067b6f6c18..4b85373ba6797069eefa96a63141438bd5de8803
+++ b/pretty.c
  #include "reflog-walk.h"
  
  static char *user_format;
 +static struct cmt_fmt_map {
 +      const char *name;
 +      enum cmit_fmt format;
 +      int is_tformat;
 +      int is_alias;
 +      const char *user_format;
 +} *commit_formats;
 +static size_t builtin_formats_len;
 +static size_t commit_formats_len;
 +static size_t commit_formats_alloc;
 +static struct cmt_fmt_map *find_commit_format(const char *sought);
  
  static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
  {
        rev->commit_format = CMIT_FMT_USERFORMAT;
  }
  
 -void get_commit_format(const char *arg, struct rev_info *rev)
 +static int git_pretty_formats_config(const char *var, const char *value, void *cb)
  {
 +      struct cmt_fmt_map *commit_format = NULL;
 +      const char *name;
 +      const char *fmt;
        int i;
 -      static struct cmt_fmt_map {
 -              const char *n;
 -              size_t cmp_len;
 -              enum cmit_fmt v;
 -      } cmt_fmts[] = {
 -              { "raw",        1,      CMIT_FMT_RAW },
 -              { "medium",     1,      CMIT_FMT_MEDIUM },
 -              { "short",      1,      CMIT_FMT_SHORT },
 -              { "email",      1,      CMIT_FMT_EMAIL },
 -              { "full",       5,      CMIT_FMT_FULL },
 -              { "fuller",     5,      CMIT_FMT_FULLER },
 -              { "oneline",    1,      CMIT_FMT_ONELINE },
 +
 +      if (prefixcmp(var, "pretty."))
 +              return 0;
 +
 +      name = var + strlen("pretty.");
 +      for (i = 0; i < builtin_formats_len; i++) {
 +              if (!strcmp(commit_formats[i].name, name))
 +                      return 0;
 +      }
 +
 +      for (i = builtin_formats_len; i < commit_formats_len; i++) {
 +              if (!strcmp(commit_formats[i].name, name)) {
 +                      commit_format = &commit_formats[i];
 +                      break;
 +              }
 +      }
 +
 +      if (!commit_format) {
 +              ALLOC_GROW(commit_formats, commit_formats_len+1,
 +                         commit_formats_alloc);
 +              commit_format = &commit_formats[commit_formats_len];
 +              memset(commit_format, 0, sizeof(*commit_format));
 +              commit_formats_len++;
 +      }
 +
 +      commit_format->name = xstrdup(name);
 +      commit_format->format = CMIT_FMT_USERFORMAT;
 +      git_config_string(&fmt, var, value);
 +      if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
 +              commit_format->is_tformat = fmt[0] == 't';
 +              fmt = strchr(fmt, ':') + 1;
 +      } else if (strchr(fmt, '%'))
 +              commit_format->is_tformat = 1;
 +      else
 +              commit_format->is_alias = 1;
 +      commit_format->user_format = fmt;
 +
 +      return 0;
 +}
 +
 +static void setup_commit_formats(void)
 +{
 +      struct cmt_fmt_map builtin_formats[] = {
 +              { "raw",        CMIT_FMT_RAW,           0 },
 +              { "medium",     CMIT_FMT_MEDIUM,        0 },
 +              { "short",      CMIT_FMT_SHORT,         0 },
 +              { "email",      CMIT_FMT_EMAIL,         0 },
 +              { "fuller",     CMIT_FMT_FULLER,        0 },
 +              { "full",       CMIT_FMT_FULL,          0 },
 +              { "oneline",    CMIT_FMT_ONELINE,       1 }
        };
 +      commit_formats_len = ARRAY_SIZE(builtin_formats);
 +      builtin_formats_len = commit_formats_len;
 +      ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
 +      memcpy(commit_formats, builtin_formats,
 +             sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
 +
 +      git_config(git_pretty_formats_config, NULL);
 +}
 +
 +static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
 +                                                      const char *original,
 +                                                      int num_redirections)
 +{
 +      struct cmt_fmt_map *found = NULL;
 +      size_t found_match_len = 0;
 +      int i;
 +
 +      if (num_redirections >= commit_formats_len)
 +              die("invalid --pretty format: "
 +                  "'%s' references an alias which points to itself",
 +                  original);
 +
 +      for (i = 0; i < commit_formats_len; i++) {
 +              size_t match_len;
 +
 +              if (prefixcmp(commit_formats[i].name, sought))
 +                      continue;
 +
 +              match_len = strlen(commit_formats[i].name);
 +              if (found == NULL || found_match_len > match_len) {
 +                      found = &commit_formats[i];
 +                      found_match_len = match_len;
 +              }
 +      }
 +
 +      if (found && found->is_alias) {
 +              found = find_commit_format_recursive(found->user_format,
 +                                                   original,
 +                                                   num_redirections+1);
 +      }
 +
 +      return found;
 +}
 +
 +static struct cmt_fmt_map *find_commit_format(const char *sought)
 +{
 +      if (!commit_formats)
 +              setup_commit_formats();
 +
 +      return find_commit_format_recursive(sought, sought, 0);
 +}
 +
 +void get_commit_format(const char *arg, struct rev_info *rev)
 +{
 +      struct cmt_fmt_map *commit_format;
  
        rev->use_terminator = 0;
        if (!arg || !*arg) {
                save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
 -      for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
 -              if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
 -                  !strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
 -                      if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
 -                              rev->use_terminator = 1;
 -                      rev->commit_format = cmt_fmts[i].v;
 -                      return;
 -              }
 -      }
 +
        if (strchr(arg, '%')) {
                save_user_format(rev, arg, 1);
                return;
        }
  
 -      die("invalid --pretty format: %s", arg);
 +      commit_format = find_commit_format(arg);
 +      if (!commit_format)
 +              die("invalid --pretty format: %s", arg);
 +
 +      rev->commit_format = commit_format->format;
 +      rev->use_terminator = commit_format->is_tformat;
 +      if (commit_format->format == CMIT_FMT_USERFORMAT) {
 +              save_user_format(rev, commit_format->user_format,
 +                               commit_format->is_tformat);
 +      }
  }
  
  /*
@@@ -824,7 -716,7 +824,7 @@@ static size_t format_commit_one(struct 
                if (add_again(sb, &c->abbrev_commit_hash))
                        return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
 -                                                   DEFAULT_ABBREV));
 +                                                   c->pretty_ctx->abbrev));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
                return 1;
        case 'T':               /* tree hash */
                if (add_again(sb, &c->abbrev_tree_hash))
                        return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
 -                                                   DEFAULT_ABBREV));
 +                                                   c->pretty_ctx->abbrev));
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
                return 1;
        case 'P':               /* parent hashes */
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
                        strbuf_addstr(sb, find_unique_abbrev(
 -                                      p->item->object.sha1, DEFAULT_ABBREV));
 +                                      p->item->object.sha1,
 +                                      c->pretty_ctx->abbrev));
                }
                c->abbrev_parent_hashes.len = sb->len -
                                              c->abbrev_parent_hashes.off;
        case 'e':       /* encoding */
                strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
                return 1;
 +      case 'B':       /* raw body */
 +              /* message_off is always left at the initial newline */
 +              strbuf_addstr(sb, msg + c->message_off + 1);
 +              return 1;
        }
  
        /* Now we need to parse the commit message. */
@@@ -941,7 -828,7 +941,7 @@@ static size_t format_commit_item(struc
        enum {
                NO_MAGIC,
                ADD_LF_BEFORE_NON_EMPTY,
-               DEL_LF_BEFORE_EMPTY,
+               DEL_LF_BEFORE_EMPTY
        } magic = NO_MAGIC;
  
        switch (placeholder[0]) {
diff --combined refs.c
index 10abda7d0d9b60fd0ddaeb3665f663996d7b79ad,a7ad3fd8fa4f9f57776ba933f6976af661cb17e7..6f486ae62d8b4605520e75286ca36217d0715363
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -314,7 -314,11 +314,11 @@@ static int warn_if_dangling_symref(cons
  
  void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
  {
-       struct warn_if_dangling_data data = { fp, refname, msg_fmt };
+       struct warn_if_dangling_data data;
+       data.fp = fp;
+       data.refname = refname;
+       data.msg_fmt = msg_fmt;
        for_each_rawref(warn_if_dangling_symref, &data);
  }
  
@@@ -1258,65 -1262,52 +1262,65 @@@ static int copy_msg(char *buf, const ch
        return cp - buf;
  }
  
 -static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
 -                       const unsigned char *new_sha1, const char *msg)
 +int log_ref_setup(const char *ref_name, char *logfile, int bufsize)
  {
 -      int logfd, written, oflags = O_APPEND | O_WRONLY;
 -      unsigned maxlen, len;
 -      int msglen;
 -      char log_file[PATH_MAX];
 -      char *logrec;
 -      const char *committer;
 -
 -      if (log_all_ref_updates < 0)
 -              log_all_ref_updates = !is_bare_repository();
 -
 -      git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
 +      int logfd, oflags = O_APPEND | O_WRONLY;
  
 +      git_snpath(logfile, bufsize, "logs/%s", ref_name);
        if (log_all_ref_updates &&
            (!prefixcmp(ref_name, "refs/heads/") ||
             !prefixcmp(ref_name, "refs/remotes/") ||
             !prefixcmp(ref_name, "refs/notes/") ||
             !strcmp(ref_name, "HEAD"))) {
 -              if (safe_create_leading_directories(log_file) < 0)
 +              if (safe_create_leading_directories(logfile) < 0)
                        return error("unable to create directory for %s",
 -                                   log_file);
 +                                   logfile);
                oflags |= O_CREAT;
        }
  
 -      logfd = open(log_file, oflags, 0666);
 +      logfd = open(logfile, oflags, 0666);
        if (logfd < 0) {
                if (!(oflags & O_CREAT) && errno == ENOENT)
                        return 0;
  
                if ((oflags & O_CREAT) && errno == EISDIR) {
 -                      if (remove_empty_directories(log_file)) {
 +                      if (remove_empty_directories(logfile)) {
                                return error("There are still logs under '%s'",
 -                                           log_file);
 +                                           logfile);
                        }
 -                      logfd = open(log_file, oflags, 0666);
 +                      logfd = open(logfile, oflags, 0666);
                }
  
                if (logfd < 0)
                        return error("Unable to append to %s: %s",
 -                                   log_file, strerror(errno));
 +                                   logfile, strerror(errno));
        }
  
 -      adjust_shared_perm(log_file);
 +      adjust_shared_perm(logfile);
 +      close(logfd);
 +      return 0;
 +}
  
 +static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
 +                       const unsigned char *new_sha1, const char *msg)
 +{
 +      int logfd, result, written, oflags = O_APPEND | O_WRONLY;
 +      unsigned maxlen, len;
 +      int msglen;
 +      char log_file[PATH_MAX];
 +      char *logrec;
 +      const char *committer;
 +
 +      if (log_all_ref_updates < 0)
 +              log_all_ref_updates = !is_bare_repository();
 +
 +      result = log_ref_setup(ref_name, log_file, sizeof(log_file));
 +      if (result)
 +              return result;
 +
 +      logfd = open(log_file, oflags);
 +      if (logfd < 0)
 +              return 0;
        msglen = msg ? strlen(msg) : 0;
        committer = git_committer_info(0);
        maxlen = strlen(committer) + msglen + 100;
diff --combined remote.c
index ea2323bac36d6fd0dbf86870a13774cddd320448,ade04246e06ebe52074888790486e88108bc1d76..e51cd22d6bcbd29bd47392434d37fb50d7b97588
+++ b/remote.c
@@@ -443,8 -443,6 +443,8 @@@ static int handle_config(const char *ke
        } else if (!strcmp(subkey, ".tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
 +              else if (!strcmp(value, "--tags"))
 +                      remote->fetch_tags = 2;
        } else if (!strcmp(subkey, ".proxy")) {
                return git_config_string((const char **)&remote->http_proxy,
                                         key, value);
@@@ -478,7 -476,7 +478,7 @@@ static void read_config(void
        unsigned char sha1[20];
        const char *head_ref;
        int flag;
 -      if (default_remote_name) // did this already
 +      if (default_remote_name) /* did this already */
                return;
        default_remote_name = xstrdup("origin");
        current_branch = NULL;
@@@ -659,10 -657,9 +659,9 @@@ static struct refspec *parse_refspec_in
  
  int valid_fetch_refspec(const char *fetch_refspec_str)
  {
-       const char *fetch_refspec[] = { fetch_refspec_str };
        struct refspec *refspec;
  
-       refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+       refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
        free_refspecs(refspec, 1);
        return !!refspec;
  }
diff --combined t/Makefile
index cd008a3c0fdf503d7775a649eb6abdba2397fabe,172948586578fc57c46053998fad8660d8e9ed21..f9de24b4d2baa9fad1d6caa72fea651da78d6878
@@@ -3,6 -3,7 +3,7 @@@
  # Copyright (c) 2005 Junio C Hamano
  #
  
+ -include ../config.mak.autogen
  -include ../config.mak
  
  #GIT_TEST_OPTS=--verbose --debug
@@@ -35,9 -36,7 +36,9 @@@ aggregate-results-and-cleanup: $(T
        $(MAKE) clean
  
  aggregate-results:
 -      '$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
 +      for f in test-results/t*-*; do \
 +              echo "$$f"; \
 +      done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
  
  # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
  full-svn-test:
diff --combined t/lib-t6000.sh
index 985d517a1ce4cf2f80eed3d54c25b5fc912d5c6b,0000000000000000000000000000000000000000..ea25dd89e505525507c16c62d91fb07c092b064a
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,127 @@@
-               diff $_name.expected $_name.actual
 +: included from 6002 and others
 +
 +[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
 +
 +:> sed.script
 +
 +# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
 +tag()
 +{
 +      _tag=$1
 +      [ -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()
 +{
 +      _text=$1
 +        _tree=$2
 +      shift 2
 +      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()
 +{
 +      _tag=$1
 +      [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
 +      shift 1
 +      "$@" >.git/refs/tags/$_tag
 +
 +        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()
 +{
 +      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()
 +{
 +      _author=$1
 +      shift 1
 +        _save=$GIT_AUTHOR_EMAIL
 +
 +      GIT_AUTHOR_EMAIL="$_author"
 +      export GIT_AUTHOR_EMAIL
 +      "$@"
 +      if test -z "$_save"
 +      then
 +              unset GIT_AUTHOR_EMAIL
 +      else
 +              GIT_AUTHOR_EMAIL="$_save"
 +              export GIT_AUTHOR_EMAIL
 +      fi
 +}
 +
 +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
 +}
 +
 +# Execute a command and suppress any error output.
 +hide_error()
 +{
 +      "$@" 2>/dev/null
 +}
 +
 +check_output()
 +{
 +      _name=$1
 +      shift 1
 +      if eval "$*" | entag > $_name.actual
 +      then
++              test_cmp $_name.expected $_name.actual
 +      else
 +              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()
 +{
 +      perl -pe '
 +              s/[^A-Za-z0-9.]/-/g;
 +              s/-+/-/g;
 +              s/-$//;
 +              s/^-//;
 +              y/A-Z/a-z/;
 +      '
 +}
 +
 +
 +# Execute the test described by the first argument, by eval'ing
 +# command line specified in the 2nd argument. Check the status code
 +# is zero and that the output matches the stream read from
 +# stdin.
 +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_expect_success "$_description" "check_output $_name \"$_test\""
 +}
diff --combined t/t0000-basic.sh
index 3ec9cbef2c88f65e5fb254d10cc551c6c4062c88,5dd18c05abadc6af46cfd213e3023e51ae863239..f2c73369a5b93544ee837229da965d7efbdc45bf
@@@ -73,27 -73,6 +73,27 @@@ the
        exit 1
  fi
  
 +clean=no
 +test_expect_success 'tests clean up after themselves' '
 +    test_when_finished clean=yes
 +'
 +
 +cleaner=no
 +test_expect_code 1 'tests clean up even after a failure' '
 +    test_when_finished cleaner=yes &&
 +    (exit 1)
 +'
 +
 +if test $clean$cleaner != yesyes
 +then
 +      say "bug in test framework: cleanup commands do not work reliably"
 +      exit 1
 +fi
 +
 +test_expect_code 2 'failure to clean up causes the test to fail' '
 +    test_when_finished "(exit 2)"
 +'
 +
  ################################################################
  # Basics of the basics
  
@@@ -301,7 -280,7 +301,7 @@@ $expectfilter >expected <<\EO
  EOF
  test_expect_success \
      'validate git diff-files output for a know cache/work tree state.' \
-     'git diff-files >current && diff >/dev/null -b current expected'
+     'git diff-files >current && test_cmp current expected >/dev/null'
  
  test_expect_success \
      'git update-index --refresh should succeed.' \
diff --combined t/t3200-branch.sh
index 9d2c06ea69d5e3a9d1c464a4d619245d6823c082,8818d0de689327ad9e0922dfd3e784804e142f41..859b99abf1cc62c966322fcfc552c0a7eb7356df
@@@ -43,7 -43,7 +43,7 @@@ test_expect_success 
       git branch -l d/e/f &&
         test -f .git/refs/heads/d/e/f &&
         test -f .git/logs/refs/heads/d/e/f &&
-        diff expect .git/logs/refs/heads/d/e/f'
+        test_cmp expect .git/logs/refs/heads/d/e/f'
  
  test_expect_success \
      'git branch -d d/e/f should delete a branch and a log' \
@@@ -222,32 -222,8 +222,32 @@@ test_expect_success 
       git checkout -b g/h/i -l master &&
         test -f .git/refs/heads/g/h/i &&
         test -f .git/logs/refs/heads/g/h/i &&
-        diff expect .git/logs/refs/heads/g/h/i'
+        test_cmp expect .git/logs/refs/heads/g/h/i'
  
 +test_expect_success 'checkout -b makes reflog by default' '
 +      git checkout master &&
 +      git config --unset core.logAllRefUpdates &&
 +      git checkout -b alpha &&
 +      test -f .git/logs/refs/heads/alpha &&
 +      PAGER= git reflog show alpha
 +'
 +
 +test_expect_success 'checkout -b does not make reflog when core.logAllRefUpdates = false' '
 +      git checkout master &&
 +      git config core.logAllRefUpdates false &&
 +      git checkout -b beta &&
 +      ! test -f .git/logs/refs/heads/beta &&
 +      test_must_fail PAGER= git reflog show beta
 +'
 +
 +test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates = false' '
 +      git checkout master &&
 +      git checkout -lb gamma &&
 +      git config --unset core.logAllRefUpdates &&
 +      test -f .git/logs/refs/heads/gamma &&
 +      PAGER= git reflog show gamma
 +'
 +
  test_expect_success 'avoid ambiguous track' '
        git config branch.autosetupmerge true &&
        git config remote.ambi1.url lalala &&
diff --combined t/t3903-stash.sh
index 8fe14ccc5444df95960bee88e5b79eb68156572b,7e47030a15bfe1dba0fe3cd499d7d65cb174083d..62e208aadd592ddaa5c519cf328e636d948221c0
@@@ -81,7 -81,7 +81,7 @@@ test_expect_success 'drop top stash' 
        git stash &&
        git stash drop &&
        git stash list > stashlist2 &&
-       diff stashlist1 stashlist2 &&
+       test_cmp stashlist1 stashlist2 &&
        git stash apply &&
        test 3 = $(cat file) &&
        test 1 = $(git show :file) &&
@@@ -228,154 -228,4 +228,154 @@@ test_expect_success 'stash --invalid-op
        test bar,bar2 = $(cat file),$(cat file2)
  '
  
 +test_expect_success 'stash an added file' '
 +      git reset --hard &&
 +      echo new >file3 &&
 +      git add file3 &&
 +      git stash save "added file" &&
 +      ! test -r file3 &&
 +      git stash apply &&
 +      test new = "$(cat file3)"
 +'
 +
 +test_expect_success 'stash rm then recreate' '
 +      git reset --hard &&
 +      git rm file &&
 +      echo bar7 >file &&
 +      git stash save "rm then recreate" &&
 +      test bar = "$(cat file)" &&
 +      git stash apply &&
 +      test bar7 = "$(cat file)"
 +'
 +
 +test_expect_success 'stash rm and ignore' '
 +      git reset --hard &&
 +      git rm file &&
 +      echo file >.gitignore &&
 +      git stash save "rm and ignore" &&
 +      test bar = "$(cat file)" &&
 +      test file = "$(cat .gitignore)"
 +      git stash apply &&
 +      ! test -r file &&
 +      test file = "$(cat .gitignore)"
 +'
 +
 +test_expect_success 'stash rm and ignore (stage .gitignore)' '
 +      git reset --hard &&
 +      git rm file &&
 +      echo file >.gitignore &&
 +      git add .gitignore &&
 +      git stash save "rm and ignore (stage .gitignore)" &&
 +      test bar = "$(cat file)" &&
 +      ! test -r .gitignore
 +      git stash apply &&
 +      ! test -r file &&
 +      test file = "$(cat .gitignore)"
 +'
 +
 +test_expect_success SYMLINKS 'stash file to symlink' '
 +      git reset --hard &&
 +      rm file &&
 +      ln -s file2 file &&
 +      git stash save "file to symlink" &&
 +      test -f file &&
 +      test bar = "$(cat file)" &&
 +      git stash apply &&
 +      case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
 +'
 +
 +test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
 +      git reset --hard &&
 +      git rm file &&
 +      ln -s file2 file &&
 +      git stash save "file to symlink (stage rm)" &&
 +      test -f file &&
 +      test bar = "$(cat file)" &&
 +      git stash apply &&
 +      case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
 +'
 +
 +test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
 +      git reset --hard &&
 +      rm file &&
 +      ln -s file2 file &&
 +      git add file &&
 +      git stash save "file to symlink (full stage)" &&
 +      test -f file &&
 +      test bar = "$(cat file)" &&
 +      git stash apply &&
 +      case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
 +'
 +
 +# This test creates a commit with a symlink used for the following tests
 +
 +test_expect_success SYMLINKS 'stash symlink to file' '
 +      git reset --hard &&
 +      ln -s file filelink &&
 +      git add filelink &&
 +      git commit -m "Add symlink" &&
 +      rm filelink &&
 +      cp file filelink &&
 +      git stash save "symlink to file" &&
 +      test -h filelink &&
 +      case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
 +      git stash apply &&
 +      ! test -h filelink &&
 +      test bar = "$(cat file)"
 +'
 +
 +test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
 +      git reset --hard &&
 +      git rm filelink &&
 +      cp file filelink &&
 +      git stash save "symlink to file (stage rm)" &&
 +      test -h filelink &&
 +      case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
 +      git stash apply &&
 +      ! test -h filelink &&
 +      test bar = "$(cat file)"
 +'
 +
 +test_expect_success SYMLINKS '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)" &&
 +      test -h filelink &&
 +      case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
 +      git stash apply &&
 +      ! test -h filelink &&
 +      test bar = "$(cat file)"
 +'
 +
 +test_expect_failure 'stash directory to file' '
 +      git reset --hard &&
 +      mkdir dir &&
 +      echo foo >dir/file &&
 +      git add dir/file &&
 +      git commit -m "Add file in dir" &&
 +      rm -fr dir &&
 +      echo bar >dir &&
 +      git stash save "directory to file" &&
 +      test -d dir &&
 +      test foo = "$(cat dir/file)" &&
 +      test_must_fail git stash apply &&
 +      test bar = "$(cat dir)" &&
 +      git reset --soft HEAD^
 +'
 +
 +test_expect_failure 'stash file to directory' '
 +      git reset --hard &&
 +      rm file &&
 +      mkdir file &&
 +      echo foo >file/file &&
 +      git stash save "file to directory" &&
 +      test -f file &&
 +      test bar = "$(cat file)" &&
 +      git stash apply &&
 +      test -f file/file &&
 +      test foo = "$(cat file/file)"
 +'
 +
  test_done
diff --combined t/t4124-apply-ws-rule.sh
index d0af697aa129466172e27456c597f329f9c4f027,bef3bcb1b241f85911a3661569a2973a71898d8c..8a676a5dcd113418c2bd4ea4aa885fddd5951a3a
@@@ -11,22 -11,21 +11,22 @@@ prepare_test_file () 
        #       !  trailing-space
        #       @  space-before-tab
        #       #  indent-with-non-tab
 +      #       %  tab-in-indent
        sed -e "s/_/ /g" -e "s/>/       /" <<-\EOF
                An_SP in an ordinary line>and a HT.
 -              >A HT.
 -              _>A SP and a HT (@).
 -              _>_A SP, a HT and a SP (@).
 +              >A HT (%).
 +              _>A SP and a HT (@%).
 +              _>_A SP, a HT and a SP (@%).
                _______Seven SP.
                ________Eight SP (#).
 -              _______>Seven SP and a HT (@).
 -              ________>Eight SP and a HT (@#).
 -              _______>_Seven SP, a HT and a SP (@).
 -              ________>_Eight SP, a HT and a SP (@#).
 +              _______>Seven SP and a HT (@%).
 +              ________>Eight SP and a HT (@#%).
 +              _______>_Seven SP, a HT and a SP (@%).
 +              ________>_Eight SP, a HT and a SP (@#%).
                _______________Fifteen SP (#).
 -              _______________>Fifteen SP and a HT (@#).
 +              _______________>Fifteen SP and a HT (@#%).
                ________________Sixteen SP (#).
 -              ________________>Sixteen SP and a HT (@#).
 +              ________________>Sixteen SP and a HT (@#%).
                _____a__Five SP, a non WS, two SP.
                A line with a (!) trailing SP_
                A line with a (!) trailing HT>
@@@ -40,11 -39,12 +40,11 @@@ apply_patch () 
  }
  
  test_fix () {
 -
        # fix should not barf
        apply_patch --whitespace=fix || return 1
  
        # find touched lines
-       diff file target | sed -n -e "s/^> //p" >fixed
+       $DIFF file target | sed -n -e "s/^> //p" >fixed
  
        # the changed lines are all expeced to change
        fixed_cnt=$(wc -l <fixed)
@@@ -85,14 -85,14 +85,14 @@@ test_expect_success setup 
  test_expect_success 'whitespace=nowarn, default rule' '
  
        apply_patch --whitespace=nowarn &&
-       diff file target
+       test_cmp file target
  
  '
  
  test_expect_success 'whitespace=warn, default rule' '
  
        apply_patch --whitespace=warn &&
-       diff file target
+       test_cmp file target
  
  '
  
@@@ -108,7 -108,7 +108,7 @@@ test_expect_success 'whitespace=error-a
  
        git config core.whitespace -trailing,-space-before,-indent &&
        apply_patch --whitespace=error-all &&
-       diff file target
+       test_cmp file target
  
  '
  
@@@ -117,7 -117,7 +117,7 @@@ test_expect_success 'whitespace=error-a
        git config --unset core.whitespace &&
        echo "target -whitespace" >.gitattributes &&
        apply_patch --whitespace=error-all &&
-       diff file target
+       test_cmp file target
  
  '
  
                for i in - ''
                do
                        case "$i" in '') ti='#' ;; *) ti= ;; esac
 -                      rule=${t}trailing,${s}space,${i}indent
 -
 -                      rm -f .gitattributes
 -                      test_expect_success "rule=$rule" '
 -                              git config core.whitespace "$rule" &&
 -                              test_fix "$tt$ts$ti"
 -                      '
 -
 -                      test_expect_success "rule=$rule (attributes)" '
 -                              git config --unset core.whitespace &&
 -                              echo "target whitespace=$rule" >.gitattributes &&
 -                              test_fix "$tt$ts$ti"
 -                      '
 -
 +                      for h in - ''
 +                      do
 +                              [ -z "$h$i" ] && continue
 +                              case "$h" in '') th='%' ;; *) th= ;; esac
 +                              rule=${t}trailing,${s}space,${i}indent,${h}tab
 +
 +                              rm -f .gitattributes
 +                              test_expect_success "rule=$rule" '
 +                                      git config core.whitespace "$rule" &&
 +                                      test_fix "$tt$ts$ti$th"
 +                              '
 +
 +                              test_expect_success "rule=$rule (attributes)" '
 +                                      git config --unset core.whitespace &&
 +                                      echo "target whitespace=$rule" >.gitattributes &&
 +                                      test_fix "$tt$ts$ti$th"
 +                              '
 +
 +                      done
                done
        done
  done
@@@ -330,18 -325,6 +330,18 @@@ test_expect_success 'two missing blank 
        test_cmp one expect
  '
  
 +test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
 +      { echo a; echo; } >one &&
 +      git add one &&
 +      { echo b; echo a; echo; } >one &&
 +      cp one expect &&
 +      git diff -- one >patch &&
 +      echo a >one &&
 +      test_must_fail git apply patch &&
 +      git apply --whitespace=fix patch &&
 +      test_cmp one expect
 +'
 +
  test_expect_success 'shrink file with tons of missing blanks at end of file' '
        { echo a; echo b; echo c; } >one &&
        cp one no-blank-lines &&
index 437e9b81120a5797098db404da1c253a2fcebf26,e4ee0782b4fe131b57800e450e4e4c3efc56b09c..86395065cfb8827ba5a999aa97cd5552a72322e3
@@@ -48,9 -48,7 +48,9 @@@ test_expect_success 'setup' 
    git pull secondroot master &&
    git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
    GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
 -  GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
 +  GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" &&
 +  GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" &&
 +  echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db"
  '
  
  # note that cvs doesn't accept absolute pathnames
@@@ -96,14 -94,6 +96,14 @@@ gi
  END VERIFICATION REQUEST
  EOF
  
 +cat >login-git-ok <<EOF
 +BEGIN VERIFICATION REQUEST
 +$SERVERDIR
 +cvsuser
 +Ah<Z:yZZ30 e
 +END VERIFICATION REQUEST
 +EOF
 +
  test_expect_success 'pserver authentication' \
    'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
     sed -ne \$p log | grep "^I LOVE YOU\$"'
@@@ -117,10 -107,6 +117,10 @@@ test_expect_success 'pserver authentica
     fi &&
     sed -ne \$p log | grep "^I HATE YOU\$"'
  
 +test_expect_success 'pserver authentication success (non-anonymous user with password)' \
 +  'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
 +   sed -ne \$p log | grep "^I LOVE YOU\$"'
 +
  test_expect_success 'pserver authentication (login)' \
    'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
     sed -ne \$p log | grep "^I LOVE YOU\$"'
@@@ -449,7 -435,7 +449,7 @@@ test_expect_success 'cvs update (-p)' 
      rm -f failures &&
      for i in merge no-lf empty really-empty; do
          GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out
-         diff $i.out ../$i >>failures 2>&1
+       test_cmp $i.out ../$i >>failures 2>&1
      done &&
      test -z "$(cat failures)"
  '
diff --combined t/test-lib.sh
index 454880ac7d281d901156136900814dee9aae46c5,eafe1466012ac044345535d19ba0c26f880b6414..367f0537cd87c29bfdca15b4744f1aef3ddd8d7e
@@@ -2,18 -2,6 +2,18 @@@
  #
  # Copyright (c) 2005 Junio C Hamano
  #
 +# This program is free software: you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
 +# the Free Software Foundation, either version 2 of the License, or
 +# (at your option) any later version.
 +#
 +# This program is distributed in the hope that it will be useful,
 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +# GNU General Public License for more details.
 +#
 +# You should have received a copy of the GNU General Public License
 +# along with this program.  If not, see http://www.gnu.org/licenses/ .
  
  # if --tee was passed, write the output not only to the terminal, but
  # additionally to the file test-results/$BASENAME.out, too.
@@@ -75,7 -63,6 +75,6 @@@ export GIT_MERGE_VERBOSIT
  export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
  export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
  export EDITOR
- GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
  
  # Protect ourselves from common misconfiguration to export
  # CDPATH into the environment
@@@ -366,10 -353,8 +365,10 @@@ test_debug () 
  }
  
  test_run_ () {
 +      test_cleanup=:
        eval >&3 2>&4 "$1"
 -      eval_ret="$?"
 +      eval_ret=$?
 +      eval >&3 2>&4 "$test_cleanup"
        return 0
  }
  
@@@ -473,9 -458,6 +472,9 @@@ test_external () 
                # Announce the script to reduce confusion about the
                # test output that follows.
                say_color "" " run $test_count: $descr ($*)"
 +              # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
 +              # to be able to use them in script
 +              export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
                # Run command; redirect its stderr to &4 as in
                # test_run_, but keep its stdout on our stdout even in
                # non-verbose mode.
@@@ -533,22 -515,6 +532,22 @@@ test_must_fail () 
        test $? -gt 0 -a $? -le 129 -o $? -gt 192
  }
  
 +# Similar to test_must_fail, but tolerates success, too.  This is
 +# meant to be used in contexts like:
 +#
 +#     test_expect_success 'some command works without configuration' '
 +#             test_might_fail git config --unset all.configuration &&
 +#             do something
 +#     '
 +#
 +# Writing "git config --unset all.configuration || :" would be wrong,
 +# because we want to notice if it fails due to segv.
 +
 +test_might_fail () {
 +      "$@"
 +      test $? -ge 0 -a $? -le 129 -o $? -gt 192
 +}
 +
  # test_cmp is a helper function to compare actual and expected output.
  # You can use it like:
  #
@@@ -566,31 -532,6 +565,31 @@@ test_cmp() 
        $GIT_TEST_CMP "$@"
  }
  
 +# This function can be used to schedule some commands to be run
 +# unconditionally at the end of the test to restore sanity:
 +#
 +#     test_expect_success 'test core.capslock' '
 +#             git config core.capslock true &&
 +#             test_when_finished "git config --unset core.capslock" &&
 +#             hello world
 +#     '
 +#
 +# That would be roughly equivalent to
 +#
 +#     test_expect_success 'test core.capslock' '
 +#             git config core.capslock true &&
 +#             hello world
 +#             git config --unset core.capslock
 +#     '
 +#
 +# except that the greeting and config --unset must both succeed for
 +# the test to pass.
 +
 +test_when_finished () {
 +      test_cleanup="{ $*
 +              } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
 +}
 +
  # Most tests can use the created repository, but some may need to create more.
  # Usage: test_create_repo <directory>
  test_create_repo () {
@@@ -740,6 -681,16 +739,16 @@@ export PATH GIT_EXEC_PATH GIT_TEMPLATE_
  
  . ../GIT-BUILD-OPTIONS
  
+ if test -z "$GIT_TEST_CMP"
+ then
+       if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT"
+       then
+               GIT_TEST_CMP="$DIFF -c"
+       else
+               GIT_TEST_CMP="$DIFF -u"
+       fi
+ fi
  GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
  export GITPERLLIB
  test -d ../templates/blt || {
diff --combined unpack-trees.c
index 490cd5f6f4779cbff68405722b98e522cbeb3cde,69782b1cc2139268fc87effc8e4ce99d3d8ee6d5..85045fd03fd7ab2a2ad8daf5ec584e010372673b
@@@ -67,8 -67,16 +67,8 @@@ static void unlink_entry(struct cache_e
  {
        if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
                return;
 -      if (S_ISGITLINK(ce->ce_mode)) {
 -              if (rmdir(ce->name)) {
 -                      warning("unable to rmdir %s: %s",
 -                              ce->name, strerror(errno));
 -                      return;
 -              }
 -      }
 -      else
 -              if (unlink_or_warn(ce->name))
 -                      return;
 +      if (remove_or_warn(ce->ce_mode, ce->name))
 +              return;
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
  }
  
@@@ -279,9 -287,11 +279,11 @@@ static void add_same_unmerged(struct ca
  static int unpack_index_entry(struct cache_entry *ce,
                              struct unpack_trees_options *o)
  {
-       struct cache_entry *src[5] = { ce, NULL, };
+       struct cache_entry *src[5] = { NULL };
        int ret;
  
+       src[0] = ce;
        mark_ce_used(ce, o);
        if (ce_stage(ce)) {
                if (o->skip_unmerged) {
@@@ -854,7 -864,7 +856,7 @@@ static int verify_uptodate_1(struct cac
  {
        struct stat st;
  
 -      if (o->index_only || (!ce_skip_worktree(ce) && (o->reset || ce_uptodate(ce))))
 +      if (o->index_only || (!((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) && (o->reset || ce_uptodate(ce))))
                return 0;
  
        if (!lstat(ce->name, &st)) {
diff --combined wt-status.c
index 636ecdd896c1ceecf79ede6caf4e61c519681053,24bbd8b9156e89add78dc1dfd451c36b11edfc4c..9d9cb9556225301c98e8cc98d51bc516881afd73
@@@ -9,7 -9,6 +9,7 @@@
  #include "quote.h"
  #include "run-command.h"
  #include "remote.h"
 +#include "refs.h"
  
  static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@@ -18,8 -17,6 +18,8 @@@
        GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
        GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
        GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
 +      GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
 +      GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
  };
  
  static const char *color(int slot, struct wt_status *s)
@@@ -45,7 -42,6 +45,7 @@@ void wt_status_prepare(struct wt_statu
        s->index_file = get_index_file();
        s->change.strdup_strings = 1;
        s->untracked.strdup_strings = 1;
 +      s->ignored.strdup_strings = 1;
  }
  
  static void wt_status_print_unmerged_header(struct wt_status *s)
@@@ -100,15 -96,13 +100,15 @@@ static void wt_status_print_dirty_heade
        color_fprintf_ln(s->fp, c, "#");
  }
  
 -static void wt_status_print_untracked_header(struct wt_status *s)
 +static void wt_status_print_other_header(struct wt_status *s,
 +                                       const char *what,
 +                                       const char *how)
  {
        const char *c = color(WT_STATUS_HEADER, s);
 -      color_fprintf_ln(s->fp, c, "# Untracked files:");
 +      color_fprintf_ln(s->fp, c, "# %s files:", what);
        if (!advice_status_hints)
                return;
 -      color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to include in what will be committed)");
 +      color_fprintf_ln(s->fp, c, "#   (use \"git %s <file>...\" to include in what will be committed)", how);
        color_fprintf_ln(s->fp, c, "#");
  }
  
@@@ -384,26 -378,9 +384,26 @@@ static void wt_status_collect_untracked
                        continue;
                if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
                        continue;
 -              s->workdir_untracked = 1;
                string_list_insert(ent->name, &s->untracked);
 +              free(ent);
        }
 +
 +      if (s->show_ignored_files) {
 +              dir.nr = 0;
 +              dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES;
 +              fill_directory(&dir, s->pathspec);
 +              for (i = 0; i < dir.nr; i++) {
 +                      struct dir_entry *ent = dir.entries[i];
 +                      if (!cache_name_is_other(ent->name, ent->len))
 +                              continue;
 +                      if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
 +                              continue;
 +                      string_list_insert(ent->name, &s->ignored);
 +                      free(ent);
 +              }
 +      }
 +
 +      free(dir.entries);
  }
  
  void wt_status_collect(struct wt_status *s)
@@@ -521,17 -498,18 +521,18 @@@ static void wt_status_print_submodule_s
        struct child_process sm_summary;
        char summary_limit[64];
        char index[PATH_MAX];
-       const char *env[] = { index, NULL };
-       const char *argv[] = {
-               "submodule",
-               "summary",
-               uncommitted ? "--files" : "--cached",
-               "--for-status",
-               "--summary-limit",
-               summary_limit,
-               uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD"),
-               NULL
-       };
+       const char *env[] = { NULL, NULL };
+       const char *argv[8];
+       env[0] =        index;
+       argv[0] =       "submodule";
+       argv[1] =       "summary";
+       argv[2] =       uncommitted ? "--files" : "--cached";
+       argv[3] =       "--for-status";
+       argv[4] =       "--summary-limit";
+       argv[5] =       summary_limit;
+       argv[6] =       uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD");
+       argv[7] =       NULL;
  
        sprintf(summary_limit, "%d", s->submodule_summary);
        snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
        run_command(&sm_summary);
  }
  
 -static void wt_status_print_untracked(struct wt_status *s)
 +static void wt_status_print_other(struct wt_status *s,
 +                                struct string_list *l,
 +                                const char *what,
 +                                const char *how)
  {
        int i;
        struct strbuf buf = STRBUF_INIT;
        if (!s->untracked.nr)
                return;
  
 -      wt_status_print_untracked_header(s);
 -      for (i = 0; i < s->untracked.nr; i++) {
 +      wt_status_print_other_header(s, what, how);
 +
 +      for (i = 0; i < l->nr; i++) {
                struct string_list_item *it;
 -              it = &(s->untracked.items[i]);
 +              it = &(l->items[i]);
                color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
                color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
                                 quote_path(it->string, strlen(it->string),
@@@ -649,14 -623,10 +650,14 @@@ void wt_status_print(struct wt_status *
                wt_status_print_submodule_summary(s, 0);  /* staged */
                wt_status_print_submodule_summary(s, 1);  /* unstaged */
        }
 -      if (s->show_untracked_files)
 -              wt_status_print_untracked(s);
 -      else if (s->commitable)
 -               fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
 +      if (s->show_untracked_files) {
 +              wt_status_print_other(s, &s->untracked, "Untracked", "add");
 +              if (s->show_ignored_files)
 +                      wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
 +      } else if (s->commitable)
 +              fprintf(s->fp, "# Untracked files not listed%s\n",
 +                      advice_status_hints
 +                      ? " (use -u option to show untracked files)" : "");
  
        if (s->verbose)
                wt_status_print_verbose(s);
                else if (s->nowarn)
                        ; /* nothing */
                else if (s->workdir_dirty)
 -                      printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
 +                      printf("no changes added to commit%s\n",
 +                              advice_status_hints
 +                              ? " (use \"git add\" and/or \"git commit -a\")" : "");
                else if (s->untracked.nr)
 -                      printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
 +                      printf("nothing added to commit but untracked files present%s\n",
 +                              advice_status_hints
 +                              ? " (use \"git add\" to track)" : "");
                else if (s->is_initial)
 -                      printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
 +                      printf("nothing to commit%s\n", advice_status_hints
 +                              ? " (create/copy files and use \"git add\" to track)" : "");
                else if (!s->show_untracked_files)
 -                      printf("nothing to commit (use -u to show untracked files)\n");
 +                      printf("nothing to commit%s\n", advice_status_hints
 +                              ? " (use -u to show untracked files)" : "");
                else
 -                      printf("nothing to commit (working directory clean)\n");
 +                      printf("nothing to commit%s\n", advice_status_hints
 +                              ? " (working directory clean)" : "");
        }
  }
  
@@@ -744,84 -707,24 +745,84 @@@ static void wt_shortstatus_status(int n
        }
  }
  
 -static void wt_shortstatus_untracked(int null_termination, struct string_list_item *it,
 -                          struct wt_status *s)
 +static void wt_shortstatus_other(int null_termination, struct string_list_item *it,
 +                               struct wt_status *s, const char *sign)
  {
        if (null_termination) {
 -              fprintf(stdout, "?? %s%c", it->string, 0);
 +              fprintf(stdout, "%s %s%c", sign, it->string, 0);
        } else {
                struct strbuf onebuf = STRBUF_INIT;
                const char *one;
                one = quote_path(it->string, -1, &onebuf, s->prefix);
 -              color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "??");
 +              color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
                printf(" %s\n", one);
                strbuf_release(&onebuf);
        }
  }
  
 -void wt_shortstatus_print(struct wt_status *s, int null_termination)
 +static void wt_shortstatus_print_tracking(struct wt_status *s)
 +{
 +      struct branch *branch;
 +      const char *header_color = color(WT_STATUS_HEADER, s);
 +      const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
 +      const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
 +
 +      const char *base;
 +      const char *branch_name;
 +      int num_ours, num_theirs;
 +
 +      color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
 +
 +      if (!s->branch)
 +              return;
 +      branch_name = s->branch;
 +
 +      if (!prefixcmp(branch_name, "refs/heads/"))
 +              branch_name += 11;
 +      else if (!strcmp(branch_name, "HEAD")) {
 +              branch_name = "HEAD (no branch)";
 +              branch_color_local = color(WT_STATUS_NOBRANCH, s);
 +      }
 +
 +      branch = branch_get(s->branch + 11);
 +      if (s->is_initial)
 +              color_fprintf(s->fp, header_color, "Initial commit on ");
 +      if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
 +              color_fprintf_ln(s->fp, branch_color_local,
 +                      "%s", branch_name);
 +              return;
 +      }
 +
 +      base = branch->merge[0]->dst;
 +      base = shorten_unambiguous_ref(base, 0);
 +      color_fprintf(s->fp, branch_color_local, "%s", branch_name);
 +      color_fprintf(s->fp, header_color, "...");
 +      color_fprintf(s->fp, branch_color_remote, "%s", base);
 +
 +      color_fprintf(s->fp, header_color, " [");
 +      if (!num_ours) {
 +              color_fprintf(s->fp, header_color, "behind ");
 +              color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
 +      } else if (!num_theirs) {
 +              color_fprintf(s->fp, header_color, "ahead ");
 +              color_fprintf(s->fp, branch_color_local, "%d", num_ours);
 +      } else {
 +              color_fprintf(s->fp, header_color, "ahead ");
 +              color_fprintf(s->fp, branch_color_local, "%d", num_ours);
 +              color_fprintf(s->fp, header_color, ", behind ");
 +              color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
 +      }
 +
 +      color_fprintf_ln(s->fp, header_color, "]");
 +}
 +
 +void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch)
  {
        int i;
 +
 +      if (show_branch)
 +              wt_shortstatus_print_tracking(s);
 +
        for (i = 0; i < s->change.nr; i++) {
                struct wt_status_change_data *d;
                struct string_list_item *it;
                struct string_list_item *it;
  
                it = &(s->untracked.items[i]);
 -              wt_shortstatus_untracked(null_termination, it, s);
 +              wt_shortstatus_other(null_termination, it, s, "??");
 +      }
 +      for (i = 0; i < s->ignored.nr; i++) {
 +              struct string_list_item *it;
 +
 +              it = &(s->ignored.items[i]);
 +              wt_shortstatus_other(null_termination, it, s, "!!");
        }
  }
  
@@@ -852,5 -749,5 +853,5 @@@ void wt_porcelain_print(struct wt_statu
        s->use_color = 0;
        s->relative_paths = 0;
        s->prefix = NULL;
 -      wt_shortstatus_print(s, null_termination);
 +      wt_shortstatus_print(s, null_termination, 0);
  }
diff --combined wt-status.h
index 4f190454e5b2ad46b33894fb55aa7dd8dd28d604,389e65f68acca1622551025665b0b88bf70800d2..4cd74c4b32f51dbe575b0a04a894a520df79e9bf
@@@ -11,9 -11,7 +11,9 @@@ enum color_wt_status 
        WT_STATUS_CHANGED,
        WT_STATUS_UNTRACKED,
        WT_STATUS_NOBRANCH,
 -      WT_STATUS_UNMERGED
 +      WT_STATUS_UNMERGED,
 +      WT_STATUS_LOCAL_BRANCH,
-       WT_STATUS_REMOTE_BRANCH,
++      WT_STATUS_REMOTE_BRANCH
  };
  
  enum untracked_status_type {
@@@ -43,26 -41,25 +43,26 @@@ struct wt_status 
        int use_color;
        int relative_paths;
        int submodule_summary;
 +      int show_ignored_files;
        enum untracked_status_type show_untracked_files;
 -      char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
 +      char color_palette[WT_STATUS_REMOTE_BRANCH+1][COLOR_MAXLEN];
  
        /* These are computed during processing of the individual sections */
        int commitable;
        int workdir_dirty;
 -      int workdir_untracked;
        const char *index_file;
        FILE *fp;
        const char *prefix;
        struct string_list change;
        struct string_list untracked;
 +      struct string_list ignored;
  };
  
  void wt_status_prepare(struct wt_status *s);
  void wt_status_print(struct wt_status *s);
  void wt_status_collect(struct wt_status *s);
  
 -void wt_shortstatus_print(struct wt_status *s, int null_termination);
 +void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);
  void wt_porcelain_print(struct wt_status *s, int null_termination);
  
  #endif /* STATUS_H */