Code

Merge branch 'master' into pb/gitpm
authorJunio C Hamano <junkio@cox.net>
Tue, 8 Aug 2006 00:02:07 +0000 (17:02 -0700)
committerJunio C Hamano <junkio@cox.net>
Tue, 8 Aug 2006 00:02:07 +0000 (17:02 -0700)
This is to resolve the conflicts with Ryan's annotate updates early.

12 files changed:
1  2 
Documentation/git-repo-config.txt
INSTALL
Makefile
builtin-repo-config.c
cache.h
commit.c
environment.c
git-annotate.perl
git-send-email.perl
sha1_file.c
sha1_name.c
t/test-lib.sh

index cc72fa9b74aedd78e82a720e3abcb53bad8b2860,b03d66f61ca4b234e721d671cc2521182a8e12b1..8a1ab61e943aba92aed6734c949b196850914a3a
@@@ -54,8 -54,7 +54,8 @@@ OPTION
  
  --get::
        Get the value for a given key (optionally filtered by a regex
 -      matching the value).
 +      matching the value). Returns error code 1 if the key was not
 +      found and error code 2 if multiple key values were found.
  
  --get-all::
        Like get, but does not fail if the number of values for the key
@@@ -120,8 -119,8 +120,8 @@@ you can set the filemode to true wit
  % git repo-config core.filemode true
  ------------
  
- The hypothetic proxy command entries actually have a postfix to discern
to what URL they apply. Here is how to change the entry for kernel.org
+ The hypothetical proxy command entries actually have a postfix to discern
what URL they apply to. Here is how to change the entry for kernel.org
  to "ssh".
  
  ------------
diff --combined INSTALL
index 4e8f8833848cd26c9adae3f8f130a1a8889a3e79,ba9778cd4de187878b6445be5fbf490d76879f30..f8dfa19edb062dd3b5ba857351d5aa8dc3671721
+++ b/INSTALL
@@@ -13,6 -13,15 +13,15 @@@ that uses $prefix, the built results ha
  which are derived from $prefix, so "make all; make prefix=/usr
  install" would not work.
  
+ Alternatively you can use autoconf generated ./configure script to
+ set up install paths (via config.mak.autogen), so you can write instead
+       $ autoconf ;# as yourself if ./configure doesn't exist yet
+       $ ./configure --prefix=/usr ;# as yourself
+       $ make all doc ;# as yourself
+       # make install install-doc ;# as root
  Issues of note:
  
   - git normally installs a helper script wrapper called "git", which
     has been actively developed since 1997, and people have moved over to
     graphical file managers.
  
 + - You can use git after building but without installing if you
 +   wanted to.  Various git commands need to find other git
 +   commands and scripts to do their work, so you would need to
 +   arrange a few environment variables to tell them that their
 +   friends will be found in your built source area instead of at
 +   their standard installation area.  Something like this works
 +   for me:
 +
 +      GIT_EXEC_PATH=`pwd`
 +      PATH=`pwd`:$PATH
 +      GITPERLLIB=`pwd`/perl/blib/lib:`pwd`/perl/blib/arch/auto/Git
 +      export GIT_EXEC_PATH PATH GITPERLLIB
 +
   - Git is reasonably self-sufficient, but does depend on a few external
     programs and libraries:
  
@@@ -57,7 -53,7 +66,7 @@@
  
        - "libcurl" and "curl" executable.  git-http-fetch and
          git-fetch use them.  If you do not use http
-         transfer, you are probabaly OK if you do not have
+         transfer, you are probably OK if you do not have
          them.
  
        - expat library; git-http-push uses it for remote lock
@@@ -82,7 -78,7 +91,7 @@@
          git, and if you only use git to track other peoples work you'll
          never notice the lack of it. 
  
-         - "wish", the TCL/Tk windowing shell is used in gitk to show the
+         - "wish", the Tcl/Tk windowing shell is used in gitk to show the
            history graphically
  
        - "ssh" is used to push and pull over the net
diff --combined Makefile
index 01b9a948238daef7fe89dace4cba3fbe0be12aa9,0761d6c6eddd7894f2d1625cfed0ef2623661a32..2ab112bbd877e3b84c40b90731ddc97f618c5d43
+++ b/Makefile
@@@ -1,6 -1,11 +1,6 @@@
  # The default target of this Makefile is...
  all:
  
 -# Define MOZILLA_SHA1 environment variable when running make to make use of
 -# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
 -# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
 -# choice) has very fast version optimized for i586.
 -#
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
  # This also implies MOZILLA_SHA1.
  #
  # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
  # d_type in struct dirent (latest Cygwin -- will be fixed soonish).
  #
+ # Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
+ # do not support the 'size specifiers' introduced by C99, namely ll, hh,
+ # j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
+ # some c compilers supported these specifiers prior to C99 as an extension.
+ #
  # Define NO_STRCASESTR if you don't have strcasestr.
  #
  # Define NO_STRLCPY if you don't have strlcpy.
  # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
  # Enable it on Windows.  By default, symrefs are still used.
  #
+ # Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+ # tests.  These tests take up a significant amount of the total test time
+ # but are not needed unless you plan to talk to SVN repos.
+ #
+ # Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+ # installed in /sw, but don't want GIT to link against any libraries
+ # installed there.  If defined you may specify your own (or Fink's)
+ # include directories and library directories by defining CFLAGS
+ # and LDFLAGS appropriately.
+ #
+ # Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+ # have DarwinPorts installed in /opt/local, but don't want GIT to
+ # link against any libraries installed there.  If defined you may
+ # specify your own (or DarwinPort's) include directories and
+ # library directories by defining CFLAGS and LDFLAGS appropriately.
+ #
  # Define PPC_SHA1 environment variable when running make to make use of
  # a bundled SHA1 routine optimized for PowerPC.
  #
  # Define ARM_SHA1 environment variable when running make to make use of
  # a bundled SHA1 routine optimized for ARM.
  #
 +# Define MOZILLA_SHA1 environment variable when running make to make use of
 +# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
 +# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
 +# choice) has very fast version optimized for i586.
 +#
 +# Define USE_PIC if you need the main git objects to be built with -fPIC
 +# in order to build and link perl/Git.so.  x86-64 seems to need this.
 +#
  # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
  #
  # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
  # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
  # a missing newline at the end of the file.
  #
- # Define NO_PYTHON if you want to loose all benefits of the recursive merge.
+ # Define NO_PYTHON if you want to lose all benefits of the recursive merge.
  #
  # Define COLLISION_CHECK below if you believe that SHA1's
  # 1461501637330902918203684832716283019655932542976 hashes do not give you
  # sufficient guarantee that no collisions between objects will ever happen.
 -
 +#
  # Define USE_NSEC below if you want git to care about sub-second file mtimes
  # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
  # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
  # randomly break unless your underlying filesystem supports those sub-second
  # times (my ext3 doesn't).
 -
 +#
  # Define USE_STDEV below if you want git to care about the underlying device
  # change being considered an inode change from the update-cache perspective.
  
@@@ -94,8 -112,6 +115,8 @@@ CFLAGS = -g -O2 -Wal
  LDFLAGS =
  ALL_CFLAGS = $(CFLAGS)
  ALL_LDFLAGS = $(LDFLAGS)
 +PERL_CFLAGS =
 +PERL_LDFLAGS =
  STRIP ?= strip
  
  prefix = $(HOME)
@@@ -105,6 -121,8 +126,8 @@@ template_dir = $(prefix)/share/git-core
  GIT_PYTHON_DIR = $(prefix)/share/git-core/python
  # DESTDIR=
  
+ export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
  CC = gcc
  AR = ar
  TAR = tar
@@@ -119,18 -137,13 +142,18 @@@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__pow
  
  ### --- END CONFIGURATION SECTION ---
  
 +# Those must not be GNU-specific; they are shared with perl/ which may
 +# be built by a different compiler.
 +BASIC_CFLAGS = $(PERL_CFLAGS)
 +BASIC_LDFLAGS = $(PERL_LDFLAGS)
 +
  SCRIPT_SH = \
        git-bisect.sh git-branch.sh git-checkout.sh \
        git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
        git-fetch.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-parse-remote.sh \
-       git-prune.sh git-pull.sh git-rebase.sh \
+       git-pull.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
        git-resolve.sh git-revert.sh git-sh-setup.sh \
        git-tag.sh git-verify-tag.sh \
  
  SCRIPT_PERL = \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
-       git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
+       git-shortlog.perl git-rerere.perl \
        git-annotate.perl git-cvsserver.perl \
-       git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
-       git-send-email.perl
+       git-svnimport.perl git-cvsexportcommit.perl \
+       git-send-email.perl git-svn.perl
  
  SCRIPT_PYTHON = \
        git-merge-recursive.py
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
-         git-cherry-pick git-status
+         git-cherry-pick git-status git-instaweb
  
  # The ones that do not have to link with lcrypto, lz nor xdiff.
  SIMPLE_PROGRAMS = \
@@@ -165,14 -178,14 +188,14 @@@ PROGRAMS = 
        git-hash-object$X git-index-pack$X git-local-fetch$X \
        git-merge-base$X \
        git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
-       git-peek-remote$X git-prune-packed$X git-receive-pack$X \
+       git-peek-remote$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
        git-show-index$X git-ssh-fetch$X \
        git-ssh-upload$X git-unpack-file$X \
        git-unpack-objects$X git-update-server-info$X \
        git-upload-pack$X git-verify-pack$X \
        git-symbolic-ref$X \
-       git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
+       git-name-rev$X git-pack-redundant$X git-var$X \
        git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
  
  BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
        git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
        git-read-tree$X git-commit-tree$X git-write-tree$X \
        git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
-       git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
+       git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
+       git-fmt-merge-msg$X git-prune$X git-mv$X git-prune-packed$X \
+       git-repo-config$X
  
  # what 'all' will build and 'install' will install, in gitexecdir
  ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@@ -212,7 -227,7 +237,7 @@@ LIB_H = 
        blob.h cache.h commit.h csum-file.h delta.h \
        diff.h object.h pack.h pkt-line.h quote.h refs.h \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-       tree-walk.h log-tree.h dir.h
+       tree-walk.h log-tree.h dir.h path-list.h
  
  DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@@ -227,7 -242,7 +252,7 @@@ LIB_OBJS = 
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
        fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
-       alloc.o $(DIFF_OBJS)
+       alloc.o merge-file.o path-list.o $(DIFF_OBJS)
  
  BUILTIN_OBJS = \
        builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
        builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
        builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
        builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
-       builtin-update-ref.o
+       builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \
+       builtin-mv.o builtin-prune-packed.o builtin-repo-config.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 -LIBS = $(GITLIBS) -lz
 +EXTLIBS = -lz
  
  #
  # Platform specific tweaks
  ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
  endif
+ ifeq ($(uname_S),GNU/kFreeBSD)
+       NO_STRLCPY = YesPlease
+ endif
  ifeq ($(uname_S),Darwin)
        NEEDS_SSL_WITH_CRYPTO = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_STRLCPY = YesPlease
-       ## fink
-       ifeq ($(shell test -d /sw/lib && echo y),y)
-               BASIC_CFLAGS += -I/sw/include
-               BASIC_LDFLAGS += -L/sw/lib
+       ifndef NO_FINK
+               ifeq ($(shell test -d /sw/lib && echo y),y)
 -                      ALL_CFLAGS += -I/sw/include
 -                      ALL_LDFLAGS += -L/sw/lib
++                      BASIC_CFLAGS += -I/sw/include
++                      BASIC_LDFLAGS += -L/sw/lib
+               endif
        endif
-       ## darwinports
-       ifeq ($(shell test -d /opt/local/lib && echo y),y)
-               BASIC_CFLAGS += -I/opt/local/include
-               BASIC_LDFLAGS += -L/opt/local/lib
+       ifndef NO_DARWIN_PORTS
+               ifeq ($(shell test -d /opt/local/lib && echo y),y)
 -                      ALL_CFLAGS += -I/opt/local/include
 -                      ALL_LDFLAGS += -L/opt/local/lib
++                      BASIC_CFLAGS += -I/opt/local/include
++                      BASIC_LDFLAGS += -L/opt/local/lib
+               endif
        endif
  endif
  ifeq ($(uname_S),SunOS)
        endif
        INSTALL = ginstall
        TAR = gtar
 -      ALL_CFLAGS += -D__EXTENSIONS__
 +      BASIC_CFLAGS += -D__EXTENSIONS__
  endif
  ifeq ($(uname_O),Cygwin)
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_D_INO_IN_DIRENT = YesPlease
        NO_STRCASESTR = YesPlease
-       NO_STRLCPY = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NEEDS_LIBICONV = YesPlease
+       NO_C99_FORMAT = YesPlease
        # There are conflicting reports about this.
        # On some boxes NO_MMAP is needed, and not so elsewhere.
        # Try uncommenting this if you see things break -- YMMV.
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
 -      ALL_CFLAGS += -I/usr/local/include
 -      ALL_LDFLAGS += -L/usr/local/lib
 +      BASIC_CFLAGS += -I/usr/local/include
 +      BASIC_LDFLAGS += -L/usr/local/lib
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
 -      ALL_CFLAGS += -I/usr/local/include
 -      ALL_LDFLAGS += -L/usr/local/lib
 +      BASIC_CFLAGS += -I/usr/local/include
 +      BASIC_LDFLAGS += -L/usr/local/lib
  endif
  ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
                NEEDS_LIBICONV = YesPlease
        endif
 -      ALL_CFLAGS += -I/usr/pkg/include
 -      ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib
 +      BASIC_CFLAGS += -I/usr/pkg/include
 +      BASIC_LDFLAGS += -L/usr/pkg/lib
 +      ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
  endif
  ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
@@@ -334,17 -354,15 +365,18 @@@ ifeq ($(uname_S),IRIX64
        NO_STRLCPY = YesPlease
        NO_SOCKADDR_STORAGE=YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
 -      ALL_CFLAGS += -DPATH_MAX=1024
 +      BASIC_CFLAGS += -DPATH_MAX=1024
        # for now, build 32-bit version
 -      ALL_LDFLAGS += -L/usr/lib32
 +      BASIC_LDFLAGS += -L/usr/lib32
  endif
  ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
  endif
 +ifeq ($(uname_M),x86_64)
 +      USE_PIC = YesPlease
 +endif
  
+ -include config.mak.autogen
  -include config.mak
  
  ifdef WITH_OWN_SUBPROCESS_PY
@@@ -360,7 -378,7 +392,7 @@@ endi
  ifndef NO_CURL
        ifdef CURLDIR
                # This is still problematic -- gcc does not always want -R.
 -              ALL_CFLAGS += -I$(CURLDIR)/include
 +              BASIC_CFLAGS += -I$(CURLDIR)/include
                CURL_LIBCURL = -L$(CURLDIR)/lib -R$(CURLDIR)/lib -lcurl
        else
                CURL_LIBCURL = -lcurl
@@@ -381,13 -399,13 +413,13 @@@ ifndef NO_OPENSS
        OPENSSL_LIBSSL = -lssl
        ifdef OPENSSLDIR
                # Again this may be problematic -- gcc does not always want -R.
 -              ALL_CFLAGS += -I$(OPENSSLDIR)/include
 +              BASIC_CFLAGS += -I$(OPENSSLDIR)/include
                OPENSSL_LINK = -L$(OPENSSLDIR)/lib -R$(OPENSSLDIR)/lib
        else
                OPENSSL_LINK =
        endif
  else
 -      ALL_CFLAGS += -DNO_OPENSSL
 +      BASIC_CFLAGS += -DNO_OPENSSL
        MOZILLA_SHA1 = 1
        OPENSSL_LIBSSL =
  endif
@@@ -399,29 -417,32 +431,32 @@@ endi
  ifdef NEEDS_LIBICONV
        ifdef ICONVDIR
                # Again this may be problematic -- gcc does not always want -R.
 -              ALL_CFLAGS += -I$(ICONVDIR)/include
 +              BASIC_CFLAGS += -I$(ICONVDIR)/include
                ICONV_LINK = -L$(ICONVDIR)/lib -R$(ICONVDIR)/lib
        else
                ICONV_LINK =
        endif
 -      LIBS += $(ICONV_LINK) -liconv
 +      EXTLIBS += $(ICONV_LINK) -liconv
  endif
  ifdef NEEDS_SOCKET
 -      LIBS += -lsocket
 +      EXTLIBS += -lsocket
        SIMPLE_LIB += -lsocket
  endif
  ifdef NEEDS_NSL
 -      LIBS += -lnsl
 +      EXTLIBS += -lnsl
        SIMPLE_LIB += -lnsl
  endif
  ifdef NO_D_TYPE_IN_DIRENT
 -      ALL_CFLAGS += -DNO_D_TYPE_IN_DIRENT
 +      BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
  endif
  ifdef NO_D_INO_IN_DIRENT
 -      ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
 +      BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
  endif
+ ifdef NO_C99_FORMAT
+       ALL_CFLAGS += -DNO_C99_FORMAT
+ endif
  ifdef NO_SYMLINK_HEAD
 -      ALL_CFLAGS += -DNO_SYMLINK_HEAD
 +      BASIC_CFLAGS += -DNO_SYMLINK_HEAD
  endif
  ifdef NO_STRCASESTR
        COMPAT_CFLAGS += -DNO_STRCASESTR
@@@ -444,13 -465,13 +479,13 @@@ ifdef NO_MMA
        COMPAT_OBJS += compat/mmap.o
  endif
  ifdef NO_IPV6
 -      ALL_CFLAGS += -DNO_IPV6
 +      BASIC_CFLAGS += -DNO_IPV6
  endif
  ifdef NO_SOCKADDR_STORAGE
  ifdef NO_IPV6
 -      ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in
 +      BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
  else
 -      ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
 +      BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6
  endif
  endif
  ifdef NO_INET_NTOP
  endif
  
  ifdef NO_ICONV
 -      ALL_CFLAGS += -DNO_ICONV
 +      BASIC_CFLAGS += -DNO_ICONV
  endif
  
  ifdef PPC_SHA1
@@@ -474,18 -495,15 +509,18 @@@ ifdef MOZILLA_SHA
        LIB_OBJS += mozilla-sha1/sha1.o
  else
        SHA1_HEADER = <openssl/sha.h>
 -      LIBS += $(LIB_4_CRYPTO)
 +      EXTLIBS += $(LIB_4_CRYPTO)
 +endif
  endif
  endif
 +ifdef USE_PIC
 +      ALL_CFLAGS += -fPIC
  endif
  ifdef NO_ACCURATE_DIFF
 -      ALL_CFLAGS += -DNO_ACCURATE_DIFF
 +      BASIC_CFLAGS += -DNO_ACCURATE_DIFF
  endif
  
- # Shell quote (do not use $(call) to accomodate ancient setups);
+ # Shell quote (do not use $(call) to accommodate ancient setups);
  
  SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
  
@@@ -500,23 -518,14 +535,23 @@@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PA
  PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
  GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
  
 -ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
 +LIBS = $(GITLIBS) $(EXTLIBS)
 +
 +BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
  LIB_OBJS += $(COMPAT_OBJS)
 +
 +ALL_CFLAGS += $(BASIC_CFLAGS)
 +ALL_LDFLAGS += $(BASIC_LDFLAGS)
 +
  export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 +
 +
  ### Build rules
  
  all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk
  
 -all:
 +all: perl/Makefile
 +      $(MAKE) -C perl
        $(MAKE) -C templates
  
  strip: $(PROGRAMS) git$X
@@@ -539,6 -548,7 +574,7 @@@ common-cmds.h: Documentation/git-*.tx
  $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        rm -f $@ $@+
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+           -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
            -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
        chmod +x $@+
        mv $@+ $@
  
 -$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
 +$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/Makefile
 +$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        rm -f $@ $@+
 -      sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
 +      INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
 +      sed -e '1{' \
 +          -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
 +          -e '        h' \
 +          -e '        s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
 +          -e '        H' \
 +          -e '        x' \
 +          -e '}' \
 +          -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $@.perl >$@+
        chmod +x $@+
@@@ -580,6 -581,20 +616,20 @@@ git-status: git-commi
        cp $< $@+
        mv $@+ $@
  
+ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
+       rm -f $@ $@+
+       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+           -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
+           -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
+           -e '/@@GITWEB_CGI@@/d' \
+           -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
+           -e '/@@GITWEB_CSS@@/d' \
+           $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+
+       chmod +x $@+
+       mv $@+ $@
  # These can record GIT_VERSION
  git$X git.spec \
        $(patsubst %.sh,%,$(SCRIPT_SH)) \
@@@ -613,6 -628,8 +663,8 @@@ $(SIMPLE_PROGRAMS) : git-%$X : %.
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIB_FILE) $(SIMPLE_LIB)
  
+ ssh-pull.o: ssh-fetch.c
+ ssh-push.o: ssh-upload.c
  git-local-fetch$X: fetch.o
  git-ssh-fetch$X: rsh.o fetch.o
  git-ssh-upload$X: rsh.o
@@@ -622,11 -639,11 +674,11 @@@ git-ssh-push$X: rsh.
  git-imap-send$X: imap-send.o $(LIB_FILE)
  
  http.o http-fetch.o http-push.o: http.h
- git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
+ git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
- git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
+ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
@@@ -643,16 -660,6 +695,16 @@@ $(XDIFF_LIB): $(XDIFF_OBJS
        rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
  
  
 +PERL_DEFINE = $(BASIC_CFLAGS) -DGIT_VERSION='"$(GIT_VERSION)"'
 +PERL_DEFINE_SQ = $(subst ','\'',$(PERL_DEFINE))
 +PERL_LIBS = $(BASIC_LDFLAGS) $(EXTLIBS)
 +PERL_LIBS_SQ = $(subst ','\'',$(PERL_LIBS))
 +perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
 +      (cd perl && $(PERL_PATH) Makefile.PL \
 +              PREFIX='$(prefix_SQ)' \
 +              DEFINE='$(PERL_DEFINE_SQ)' \
 +              LIBS='$(PERL_LIBS_SQ)')
 +
  doc:
        $(MAKE) -C Documentation all
  
@@@ -682,6 -689,7 +734,7 @@@ GIT-CFLAGS: .FORCE-GIT-CFLAG
  # with that.
  
  export NO_PYTHON
+ export NO_SVN_TESTS
  
  test: all
        $(MAKE) -C t/ all
@@@ -695,6 -703,12 +748,12 @@@ test-delta$X: test-delta.c diff-delta.
  test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
+ test-sha1$X: test-sha1.o $(GITLIBS)
+       $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+ check-sha1:: test-sha1$X
+       ./test-sha1.sh
  check:
        for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
  
@@@ -707,8 -721,7 +766,8 @@@ install: al
        $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
-       $(MAKE) -C templates install
+       $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 +      $(MAKE) -C perl install
        $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
        $(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
        if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
@@@ -758,8 -771,8 +817,8 @@@ dist-doc
        rm -fr .doc-tmp-dir
        mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
        $(MAKE) -C Documentation DESTDIR=./ \
-               man1=../.doc-tmp-dir/man1 \
-               man7=../.doc-tmp-dir/man7 \
+               man1dir=../.doc-tmp-dir/man1 \
+               man7dir=../.doc-tmp-dir/man7 \
                install
        cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
        gzip -n -9 -f $(manpages).tar
@@@ -772,13 -785,13 +831,15 @@@ clean
                $(LIB_FILE) $(XDIFF_LIB)
        rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
        rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
+       rm -rf autom4te.cache
+       rm -f config.log config.mak.autogen configure config.status config.cache
        rm -rf $(GIT_TARNAME) .doc-tmp-dir
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        rm -f $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
 -      $(MAKE) -C templates clean
 +      [ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
 +      rm -f perl/ppport.h perl/Makefile.old
 +      $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
        rm -f GIT-VERSION-FILE GIT-CFLAGS
  
diff --combined builtin-repo-config.c
index 0000000000000000000000000000000000000000,c821e22717e35795b74f1c26d16f6c7da63ebdf8..1d9373977d94f0391c068a5ba13a96e561e21d0b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,200 +1,200 @@@
 -              ret =  (seen == 1) ? 0 : 1;
+ #include "builtin.h"
+ #include "cache.h"
+ #include <regex.h>
+ static const char git_config_set_usage[] =
+ "git-repo-config [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
+ static char* key = NULL;
+ static regex_t* key_regexp = NULL;
+ static regex_t* regexp = NULL;
+ static int show_keys = 0;
+ static int use_key_regexp = 0;
+ static int do_all = 0;
+ static int do_not_match = 0;
+ static int seen = 0;
+ static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
+ static int show_all_config(const char *key_, const char *value_)
+ {
+       if (value_)
+               printf("%s=%s\n", key_, value_);
+       else
+               printf("%s\n", key_);
+       return 0;
+ }
+ static int show_config(const char* key_, const char* value_)
+ {
+       char value[256];
+       const char *vptr = value;
+       int dup_error = 0;
+       if (!use_key_regexp && strcmp(key_, key))
+               return 0;
+       if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+               return 0;
+       if (regexp != NULL &&
+                        (do_not_match ^
+                         regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+               return 0;
+       if (show_keys)
+               printf("%s ", key_);
+       if (seen && !do_all)
+               dup_error = 1;
+       if (type == T_INT)
+               sprintf(value, "%d", git_config_int(key_, value_?value_:""));
+       else if (type == T_BOOL)
+               vptr = git_config_bool(key_, value_) ? "true" : "false";
+       else
+               vptr = value_?value_:"";
+       seen++;
+       if (dup_error) {
+               error("More than one value for the key %s: %s",
+                               key_, vptr);
+       }
+       else
+               printf("%s\n", vptr);
+       return 0;
+ }
+ static int get_value(const char* key_, const char* regex_)
+ {
+       int ret = -1;
+       char *tl;
+       char *global = NULL, *repo_config = NULL;
+       const char *local;
+       local = getenv("GIT_CONFIG");
+       if (!local) {
+               const char *home = getenv("HOME");
+               local = getenv("GIT_CONFIG_LOCAL");
+               if (!local)
+                       local = repo_config = strdup(git_path("config"));
+               if (home)
+                       global = strdup(mkpath("%s/.gitconfig", home));
+       }
+       key = strdup(key_);
+       for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
+               *tl = tolower(*tl);
+       for (tl=key; *tl && *tl != '.'; ++tl)
+               *tl = tolower(*tl);
+       if (use_key_regexp) {
+               key_regexp = (regex_t*)malloc(sizeof(regex_t));
+               if (regcomp(key_regexp, key, REG_EXTENDED)) {
+                       fprintf(stderr, "Invalid key pattern: %s\n", key_);
+                       goto free_strings;
+               }
+       }
+       if (regex_) {
+               if (regex_[0] == '!') {
+                       do_not_match = 1;
+                       regex_++;
+               }
+               regexp = (regex_t*)malloc(sizeof(regex_t));
+               if (regcomp(regexp, regex_, REG_EXTENDED)) {
+                       fprintf(stderr, "Invalid pattern: %s\n", regex_);
+                       goto free_strings;
+               }
+       }
+       if (do_all && global)
+               git_config_from_file(show_config, global);
+       git_config_from_file(show_config, local);
+       if (!do_all && !seen && global)
+               git_config_from_file(show_config, global);
+       free(key);
+       if (regexp) {
+               regfree(regexp);
+               free(regexp);
+       }
+       if (do_all)
+               ret = !seen;
+       else
++              ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
+ free_strings:
+       if (repo_config)
+               free(repo_config);
+       if (global)
+               free(global);
+       return ret;
+ }
+ int cmd_repo_config(int argc, const char **argv, const char *prefix)
+ {
+       int nongit = 0;
+       setup_git_directory_gently(&nongit);
+       while (1 < argc) {
+               if (!strcmp(argv[1], "--int"))
+                       type = T_INT;
+               else if (!strcmp(argv[1], "--bool"))
+                       type = T_BOOL;
+               else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
+                       return git_config(show_all_config);
+               else
+                       break;
+               argc--;
+               argv++;
+       }
+       switch (argc) {
+       case 2:
+               return get_value(argv[1], NULL);
+       case 3:
+               if (!strcmp(argv[1], "--unset"))
+                       return git_config_set(argv[2], NULL);
+               else if (!strcmp(argv[1], "--unset-all"))
+                       return git_config_set_multivar(argv[2], NULL, NULL, 1);
+               else if (!strcmp(argv[1], "--get"))
+                       return get_value(argv[2], NULL);
+               else if (!strcmp(argv[1], "--get-all")) {
+                       do_all = 1;
+                       return get_value(argv[2], NULL);
+               } else if (!strcmp(argv[1], "--get-regexp")) {
+                       show_keys = 1;
+                       use_key_regexp = 1;
+                       do_all = 1;
+                       return get_value(argv[2], NULL);
+               } else
+                       return git_config_set(argv[1], argv[2]);
+       case 4:
+               if (!strcmp(argv[1], "--unset"))
+                       return git_config_set_multivar(argv[2], NULL, argv[3], 0);
+               else if (!strcmp(argv[1], "--unset-all"))
+                       return git_config_set_multivar(argv[2], NULL, argv[3], 1);
+               else if (!strcmp(argv[1], "--get"))
+                       return get_value(argv[2], argv[3]);
+               else if (!strcmp(argv[1], "--get-all")) {
+                       do_all = 1;
+                       return get_value(argv[2], argv[3]);
+               } else if (!strcmp(argv[1], "--get-regexp")) {
+                       show_keys = 1;
+                       use_key_regexp = 1;
+                       do_all = 1;
+                       return get_value(argv[2], argv[3]);
+               } else if (!strcmp(argv[1], "--replace-all"))
+                       return git_config_set_multivar(argv[2], argv[3], NULL, 1);
+               else
+                       return git_config_set_multivar(argv[1], argv[2], argv[3], 0);
+       case 5:
+               if (!strcmp(argv[1], "--replace-all"))
+                       return git_config_set_multivar(argv[2], argv[3], argv[4], 1);
+       case 1:
+       default:
+               usage(git_config_set_usage);
+       }
+       return 0;
+ }
diff --combined cache.h
index 962f2fc3467077289df4294b48216050b26e74a7,b8c21e07b2e714de8e4e662b31a41ff06c5e0c9a..ed26b47852722e1c1df65a71df2a4a6aedacdd55
+++ b/cache.h
@@@ -115,10 -115,8 +115,11 @@@ static inline unsigned int create_ce_mo
  extern struct cache_entry **active_cache;
  extern unsigned int active_nr, active_alloc, active_cache_changed;
  extern struct cache_tree *active_cache_tree;
+ extern int cache_errno;
  
 +extern void setup_git(char *new_git_dir, char *new_git_object_dir,
 +                      char *new_git_index_file, char *new_git_graft_file);
 +
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@@ -145,6 -143,7 +146,7 @@@ extern void verify_non_filename(const c
  
  /* Initialize and use the cache information */
  extern int read_cache(void);
+ extern int read_cache_from(const char *path);
  extern int write_cache(int newfd, struct cache_entry **cache, int entries);
  extern int verify_path(const char *path);
  extern int cache_name_pos(const char *name, int namelen);
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  extern int add_cache_entry(struct cache_entry *ce, int option);
+ extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern int remove_cache_entry_at(int pos);
  extern int remove_file_from_cache(const char *path);
+ extern int add_file_to_index(const char *path, int verbose);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
  extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
  extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
@@@ -179,6 -180,7 +183,7 @@@ extern int commit_lock_file(struct lock
  extern void rollback_lock_file(struct lock_file *);
  
  /* Environment bits from configuration mechanism */
+ extern int use_legacy_headers;
  extern int trust_executable_bit;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -186,6 -188,7 +191,7 @@@ extern int log_all_ref_updates
  extern int warn_ambiguous_refs;
  extern int shared_repository;
  extern const char *apply_default_whitespace;
+ extern int zlib_compression_level;
  
  #define GIT_REPO_VERSION 0
  extern int repository_format_version;
@@@ -221,8 -224,6 +227,6 @@@ int safe_create_leading_directories(cha
  char *enter_repo(char *path, int strict);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
- extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);
- extern int parse_sha1_header(char *hdr, char *type, unsigned long *sizep);
  extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
  extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
  extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
@@@ -324,13 -325,17 +328,17 @@@ struct ref 
        char name[FLEX_ARRAY]; /* more */
  };
  
+ #define REF_NORMAL    (1u << 0)
+ #define REF_HEADS     (1u << 1)
+ #define REF_TAGS      (1u << 2)
  extern int git_connect(int fd[2], char *url, const char *prog);
  extern int finish_connect(pid_t pid);
  extern int path_match(const char *path, int nr, char **match);
  extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                      int nr_refspec, char **refspec, int all);
  extern int get_ack(int fd, unsigned char *result_sha1);
- extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, int ignore_funny);
+ extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
  extern int server_supports(const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1);
@@@ -380,6 -385,8 +388,8 @@@ extern int receive_keep_pack(int fd[2]
  
  /* pager.c */
  extern void setup_pager(void);
+ extern int pager_in_use;
+ extern int pager_use_color;
  
  /* base85 */
  int decode_85(char *dst, char *line, int linelen);
diff --combined commit.c
index 17f51c245525172695e6ff6d36990464349558e0,77f0ca175c66b0ef0b7ac5b24672b106c6f17178..4d5c0c294568bb67c6e236ff2f45c61d3bdfd790
+++ b/commit.c
@@@ -56,7 -56,7 +56,7 @@@ static struct commit *check_commit(stru
                                   const unsigned char *sha1,
                                   int quiet)
  {
-       if (obj->type != TYPE_COMMIT) {
+       if (obj->type != OBJ_COMMIT) {
                if (!quiet)
                        error("Object %s is a %s, not a commit",
                              sha1_to_hex(sha1), typename(obj->type));
@@@ -86,11 -86,11 +86,11 @@@ struct commit *lookup_commit(const unsi
        if (!obj) {
                struct commit *ret = alloc_commit_node();
                created_object(sha1, &ret->object);
-               ret->object.type = TYPE_COMMIT;
+               ret->object.type = OBJ_COMMIT;
                return ret;
        }
        if (!obj->type)
-               obj->type = TYPE_COMMIT;
+               obj->type = OBJ_COMMIT;
        return check_commit(obj, sha1, 0);
  }
  
@@@ -163,14 -163,6 +163,14 @@@ int register_commit_graft(struct commit
        return 0;
  }
  
 +void free_commit_grafts(void)
 +{
 +      int pos = commit_graft_nr;
 +      while (pos >= 0)
 +              free(commit_graft[pos--]);
 +      commit_graft_nr = 0;
 +}
 +
  struct commit_graft *read_graft_line(char *buf, int len)
  {
        /* The format is just "Commit Parent1 Parent2 ...\n" */
@@@ -223,18 -215,11 +223,18 @@@ int read_graft_file(const char *graft_f
  static void prepare_commit_graft(void)
  {
        static int commit_graft_prepared;
 -      char *graft_file;
 +      static char *last_graft_file;
 +      char *graft_file = get_graft_file();
 +
 +      if (last_graft_file) {
 +              if (!strcmp(graft_file, last_graft_file))
 +                      return;
 +              free_commit_grafts();
 +      }
 +      if (last_graft_file)
 +              free(last_graft_file);
 +      last_graft_file = strdup(graft_file);
  
 -      if (commit_graft_prepared)
 -              return;
 -      graft_file = get_graft_file();
        read_graft_file(graft_file);
        commit_graft_prepared = 1;
  }
@@@ -412,12 -397,13 +412,13 @@@ void clear_commit_marks(struct commit *
  {
        struct commit_list *parents;
  
-       parents = commit->parents;
        commit->object.flags &= ~mark;
+       parents = commit->parents;
        while (parents) {
                struct commit *parent = parents->item;
-               if (parent && parent->object.parsed &&
-                   (parent->object.flags & mark))
+               /* Have we already cleared this? */
+               if (mark & parent->object.flags)
                        clear_commit_marks(parent, mark);
                parents = parents->next;
        }
@@@ -669,6 -655,9 +670,9 @@@ unsigned long pretty_print_commit(enum 
                        continue;
                }
  
+               if (!subject)
+                       body = 1;
                if (is_empty_line(line, &linelen)) {
                        if (!body)
                                continue;
                                continue;
                        if (fmt == CMIT_FMT_SHORT)
                                break;
-               } else {
-                       body = 1;
                }
  
                if (subject) {
        /* Make sure there is an EOLN for the non-oneline case */
        if (fmt != CMIT_FMT_ONELINE)
                buf[offset++] = '\n';
+       /*
+        * make sure there is another EOLN to separate the headers from whatever
+        * body the caller appends if we haven't already written a body
+        */
+       if (fmt == CMIT_FMT_EMAIL && !body)
+               buf[offset++] = '\n';
        buf[offset] = '\0';
        return offset;
  }
@@@ -861,3 -854,147 +869,147 @@@ void sort_in_topological_order_fn(struc
        }
        free(nodes);
  }
+ /* merge-rebase stuff */
+ /* bits #0..7 in revision.h */
+ #define PARENT1               (1u<< 8)
+ #define PARENT2               (1u<< 9)
+ #define STALE         (1u<<10)
+ #define RESULT                (1u<<11)
+ static struct commit *interesting(struct commit_list *list)
+ {
+       while (list) {
+               struct commit *commit = list->item;
+               list = list->next;
+               if (commit->object.flags & STALE)
+                       continue;
+               return commit;
+       }
+       return NULL;
+ }
+ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
+ {
+       struct commit_list *list = NULL;
+       struct commit_list *result = NULL;
+       if (one == two)
+               /* We do not mark this even with RESULT so we do not
+                * have to clean it up.
+                */
+               return commit_list_insert(one, &result);
+       parse_commit(one);
+       parse_commit(two);
+       one->object.flags |= PARENT1;
+       two->object.flags |= PARENT2;
+       insert_by_date(one, &list);
+       insert_by_date(two, &list);
+       while (interesting(list)) {
+               struct commit *commit;
+               struct commit_list *parents;
+               struct commit_list *n;
+               int flags;
+               commit = list->item;
+               n = list->next;
+               free(list);
+               list = n;
+               flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
+               if (flags == (PARENT1 | PARENT2)) {
+                       if (!(commit->object.flags & RESULT)) {
+                               commit->object.flags |= RESULT;
+                               insert_by_date(commit, &result);
+                       }
+                       /* Mark parents of a found merge stale */
+                       flags |= STALE;
+               }
+               parents = commit->parents;
+               while (parents) {
+                       struct commit *p = parents->item;
+                       parents = parents->next;
+                       if ((p->object.flags & flags) == flags)
+                               continue;
+                       parse_commit(p);
+                       p->object.flags |= flags;
+                       insert_by_date(p, &list);
+               }
+       }
+       /* Clean up the result to remove stale ones */
+       list = result; result = NULL;
+       while (list) {
+               struct commit_list *n = list->next;
+               if (!(list->item->object.flags & STALE))
+                       insert_by_date(list->item, &result);
+               free(list);
+               list = n;
+       }
+       return result;
+ }
+ struct commit_list *get_merge_bases(struct commit *one,
+                                   struct commit *two,
+                                     int cleanup)
+ {
+       const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+       struct commit_list *list;
+       struct commit **rslt;
+       struct commit_list *result;
+       int cnt, i, j;
+       result = merge_bases(one, two);
+       if (one == two)
+               return result;
+       if (!result || !result->next) {
+               if (cleanup) {
+                       clear_commit_marks(one, all_flags);
+                       clear_commit_marks(two, all_flags);
+               }
+               return result;
+       }
+       /* There are more than one */
+       cnt = 0;
+       list = result;
+       while (list) {
+               list = list->next;
+               cnt++;
+       }
+       rslt = xcalloc(cnt, sizeof(*rslt));
+       for (list = result, i = 0; list; list = list->next)
+               rslt[i++] = list->item;
+       free_commit_list(result);
+       clear_commit_marks(one, all_flags);
+       clear_commit_marks(two, all_flags);
+       for (i = 0; i < cnt - 1; i++) {
+               for (j = i+1; j < cnt; j++) {
+                       if (!rslt[i] || !rslt[j])
+                               continue;
+                       result = merge_bases(rslt[i], rslt[j]);
+                       clear_commit_marks(rslt[i], all_flags);
+                       clear_commit_marks(rslt[j], all_flags);
+                       for (list = result; list; list = list->next) {
+                               if (rslt[i] == list->item)
+                                       rslt[i] = NULL;
+                               if (rslt[j] == list->item)
+                                       rslt[j] = NULL;
+                       }
+               }
+       }
+       /* Surviving ones in rslt[] are the independent results */
+       result = NULL;
+       for (i = 0; i < cnt; i++) {
+               if (rslt[i])
+                       insert_by_date(rslt[i], &result);
+       }
+       free(rslt);
+       return result;
+ }
diff --combined environment.c
index 6b64d111f584d5e80fa7dc2e1bf0d5ab2a69ba7b,87162b257254434be356b1a579967d51adff1e5f..1ce34118dd6df33b60b1ea5e0c75c5fdf4b1aa84
@@@ -11,6 -11,7 +11,7 @@@
  
  char git_default_email[MAX_GITNAME];
  char git_default_name[MAX_GITNAME];
+ int use_legacy_headers = 1;
  int trust_executable_bit = 1;
  int assume_unchanged = 0;
  int prefer_symlink_refs = 0;
@@@ -20,62 -21,32 +21,65 @@@ int repository_format_version = 0
  char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
  int shared_repository = PERM_UMASK;
  const char *apply_default_whitespace = NULL;
+ int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+ int pager_in_use;
+ int pager_use_color = 1;
  
 +static int dyn_git_object_dir, dyn_git_index_file, dyn_git_graft_file;
  static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
        *git_graft_file;
 -static void setup_git_env(void)
 +
 +void setup_git(char *new_git_dir, char *new_git_object_dir,
 +               char *new_git_index_file, char *new_git_graft_file)
  {
 -      git_dir = getenv(GIT_DIR_ENVIRONMENT);
 +      git_dir = new_git_dir;
        if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 -      git_object_dir = getenv(DB_ENVIRONMENT);
 +
 +      if (dyn_git_object_dir)
 +              free(git_object_dir);
 +      git_object_dir = new_git_object_dir;
        if (!git_object_dir) {
                git_object_dir = xmalloc(strlen(git_dir) + 9);
                sprintf(git_object_dir, "%s/objects", git_dir);
 +              dyn_git_object_dir = 1;
 +      } else {
 +              dyn_git_object_dir = 0;
        }
 +
 +      if (git_refs_dir)
 +              free(git_refs_dir);
        git_refs_dir = xmalloc(strlen(git_dir) + 6);
        sprintf(git_refs_dir, "%s/refs", git_dir);
 -      git_index_file = getenv(INDEX_ENVIRONMENT);
 +
 +      if (dyn_git_index_file)
 +              free(git_index_file);
 +      git_index_file = new_git_index_file;
        if (!git_index_file) {
                git_index_file = xmalloc(strlen(git_dir) + 7);
                sprintf(git_index_file, "%s/index", git_dir);
 +              dyn_git_index_file = 1;
 +      } else {
 +              dyn_git_index_file = 0;
        }
 -      git_graft_file = getenv(GRAFT_ENVIRONMENT);
 -      if (!git_graft_file)
 +
 +      if (dyn_git_graft_file)
 +              free(git_graft_file);
 +      git_graft_file = new_git_graft_file;
 +      if (!git_graft_file) {
                git_graft_file = strdup(git_path("info/grafts"));
 +              dyn_git_graft_file = 1;
 +      } else {
 +              dyn_git_graft_file = 0;
 +      }
 +}
 +
 +static void setup_git_env(void)
 +{
 +      setup_git(getenv(GIT_DIR_ENVIRONMENT),
 +                getenv(DB_ENVIRONMENT),
 +                getenv(INDEX_ENVIRONMENT),
 +                getenv(GRAFT_ENVIRONMENT));
  }
  
  char *get_git_dir(void)
diff --combined git-annotate.perl
index d924e8771c1df29fdfc7b2305d3e783597cd6835,215ed26f3aff4b12139359ca841a9a80c567a6e6..742a51c50177f6ca253e0548afeb8287e06ab759
@@@ -11,7 -11,6 +11,7 @@@ use strict
  use Getopt::Long;
  use POSIX qw(strftime gmtime);
  use File::Basename qw(basename dirname);
 +use Git;
  
  sub usage() {
        print STDERR "Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
@@@ -30,7 -29,7 +30,7 @@@
        exit(1);
  }
  
 -our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file) = (0, 0, 1);
 +our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file, $repo) = (0, 0, 1);
  
  my $rc = GetOptions(  "long|l" => \$longrev,
                        "time|t" => \$rawtime,
@@@ -53,8 -52,6 +53,8 @@@ my @stack = 
        },
  );
  
 +$repo = Git->repository();
 +
  our @filelines = ();
  
  if (defined $starting_rev) {
@@@ -105,11 -102,15 +105,11 @@@ while (my $bound = pop @stack) 
  push @revqueue, $head;
  init_claim( defined $starting_rev ? $head : 'dirty');
  unless (defined $starting_rev) {
 -      my $diff = open_pipe("git","diff","HEAD", "--",$filename)
 -              or die "Failed to call git diff to check for dirty state: $!";
 -
 -      _git_diff_parse($diff, [$head], "dirty", (
 -                              'author' => gitvar_name("GIT_AUTHOR_IDENT"),
 -                              'author_date' => sprintf("%s +0000",time()),
 -                              )
 -                      );
 -      close($diff);
 +      my %ident;
 +      @ident{'author', 'author_email', 'author_date'} = $repo->ident('author');
 +      my $diff = $repo->command_output_pipe('diff', '-R', 'HEAD', '--', $filename);
-       _git_diff_parse($diff, $head, "dirty", %ident);
++      _git_diff_parse($diff, [$head], "dirty", %ident);
 +      $repo->command_close_pipe($diff);
  }
  handle_rev();
  
@@@ -146,21 -147,20 +146,20 @@@ sub init_claim 
  
  
  sub handle_rev {
-       my $i = 0;
+       my $revseen = 0;
        my %seen;
        while (my $rev = shift @revqueue) {
                next if $seen{$rev}++;
  
                my %revinfo = git_commit_info($rev);
  
-               foreach my $p (@{$revs{$rev}{'parents'}}) {
-                       git_diff_parse($p, $rev, %revinfo);
-                       push @revqueue, $p;
-               }
+               if (exists $revs{$rev}{parents} &&
+                   scalar @{$revs{$rev}{parents}} != 0) {
  
+                       git_diff_parse($revs{$rev}{'parents'}, $rev, %revinfo);
+                       push @revqueue, @{$revs{$rev}{'parents'}};
  
-               if (scalar @{$revs{$rev}{parents}} == 0) {
+               } else {
                        # We must be at the initial rev here, so claim everything that is left.
                        for (my $i = 0; $i < @{$revs{$rev}{lines}}; $i++) {
                                if (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {
@@@ -180,7 -180,8 +179,7 @@@ sub git_rev_list 
                open($revlist, '<' . $rev_file)
                    or die "Failed to open $rev_file : $!";
        } else {
 -              $revlist = open_pipe("git-rev-list","--parents","--remove-empty",$rev,"--",$file)
 -                      or die "Failed to exec git-rev-list: $!";
 +              $revlist = $repo->command_output_pipe('rev-list', '--parents', '--remove-empty', $rev, '--', $file);
        }
  
        my @revs;
                my ($rev, @parents) = split /\s+/, $line;
                push @revs, [ $rev, @parents ];
        }
 -      close($revlist);
 +      $repo->command_close_pipe($revlist);
  
        printf("0 revs found for rev %s (%s)\n", $rev, $file) if (@revs == 0);
        return @revs;
  sub find_parent_renames {
        my ($rev, $file) = @_;
  
 -      my $patch = open_pipe("git-diff-tree", "-M50", "-r","--name-status", "-z","$rev")
 -              or die "Failed to exec git-diff: $!";
 +      my $patch = $repo->command_output_pipe('diff-tree', '-M50', '-r', '--name-status', '-z', $rev);
  
        local $/ = "\0";
        my %bound;
                        }
                }
        }
 -      close($patch);
 +      $repo->command_close_pipe($patch);
  
        return \%bound;
  }
  sub git_find_parent {
        my ($rev, $filename) = @_;
  
 -      my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev","--",$filename)
 -              or die "Failed to open git-rev-list to find a single parent: $!";
 -
 -      my $parentline = <$revparent>;
 -      chomp $parentline;
 -      my ($revfound,$parent) = split m/\s+/, $parentline;
 -
 -      close($revparent);
 +      my $parentline = $repo->command_oneline('rev-list', '--remove-empty',
 +                      '--parents', '--max-count=1', $rev, '--', $filename);
 +      my ($revfound, $parent) = split m/\s+/, $parentline;
  
        return $parent;
  }
  
 -      my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev")
 -              or die "Failed to open git-rev-list to find a single parent: $!";
 -
 -      my $parentline = <$revparent>;
 -      chomp $parentline;
+ sub git_find_all_parents {
+       my ($rev) = @_;
 -      close($revparent);
 -
++      my $parentline = $repo->command_oneline("rev-list","--remove-empty", "--parents","--max-count=1","$rev");
+       my ($origrev, @parents) = split m/\s+/, $parentline;
 -      my $mb = open_pipe("git-merge-base", $rev1, $rev2)
 -              or die "Failed to open git-merge-base: $!";
 -
 -      my $base = <$mb>;
 -      chomp $base;
 -
 -      close($mb);
 -
+       return @parents;
+ }
+ sub git_merge_base {
+       my ($rev1, $rev2) = @_;
++      my $base = $repo->command_oneline("merge-base", $rev1, $rev2);
+       return $base;
+ }
+ # Construct a set of pseudo parents that are in the same order,
+ # and the same quantity as the real parents,
+ # but whose SHA1s are as similar to the logical parents
+ # as possible.
+ sub get_pseudo_parents {
+       my ($all, $fake) = @_;
+       my @all = @$all;
+       my @fake = @$fake;
+       my @pseudo;
+       my %fake = map {$_ => 1} @fake;
+       my %seenfake;
+       my $fakeidx = 0;
+       foreach my $p (@all) {
+               if (exists $fake{$p}) {
+                       if ($fake[$fakeidx] ne $p) {
+                               die sprintf("parent mismatch: %s != %s\nall:%s\nfake:%s\n",
+                                           $fake[$fakeidx], $p,
+                                           join(", ", @all),
+                                           join(", ", @fake),
+                                          );
+                       }
+                       push @pseudo, $p;
+                       $fakeidx++;
+                       $seenfake{$p}++;
+               } else {
+                       my $base = git_merge_base($fake[$fakeidx], $p);
+                       if ($base ne $fake[$fakeidx]) {
+                               die sprintf("Result of merge-base doesn't match fake: %s,%s != %s\n",
+                                      $fake[$fakeidx], $p, $base);
+                       }
+                       # The details of how we parse the diffs
+                       # mean that we cannot have a duplicate
+                       # revision in the list, so if we've already
+                       # seen the revision we would normally add, just use
+                       # the actual revision.
+                       if ($seenfake{$base}) {
+                               push @pseudo, $p;
+                       } else {
+                               push @pseudo, $base;
+                               $seenfake{$base}++;
+                       }
+               }
+       }
+       return @pseudo;
+ }
  
  # Get a diff between the current revision and a parent.
  # Record the commit information that results.
  sub git_diff_parse {
-       my ($parent, $rev, %revinfo) = @_;
+       my ($parents, $rev, %revinfo) = @_;
  
-       my $diff = $repo->command_output_pipe('diff-tree', '-M', '-p',
-                       $rev, $parent, '--',
-                       $revs{$rev}{'filename'}, $revs{$parent}{'filename'});
+       my @pseudo_parents;
 -      my @command = ("git-diff-tree");
++      my @command = ("diff-tree");
+       my $revision_spec;
+       if (scalar @$parents == 1) {
+               $revision_spec = join("..", $parents->[0], $rev);
+               @pseudo_parents = @$parents;
+       } else {
+               my @all_parents = git_find_all_parents($rev);
+               if (@all_parents !=  @$parents) {
+                       @pseudo_parents = get_pseudo_parents(\@all_parents, $parents);
+               } else {
+                       @pseudo_parents = @$parents;
+               }
+               $revision_spec = $rev;
+               push @command, "-c";
+       }
  
-       _git_diff_parse($diff, $parent, $rev, %revinfo);
+       my @filenames = ( $revs{$rev}{'filename'} );
+       foreach my $parent (@$parents) {
+               push @filenames, $revs{$parent}{'filename'};
+       }
+       push @command, "-p", "-M", $revision_spec, "--", @filenames;
 -      my $diff = open_pipe( @command )
 -              or die "Failed to call git-diff for annotation: $!";
++      my $diff = $repo->command_output_pipe(@command);
+       _git_diff_parse($diff, \@pseudo_parents, $rev, %revinfo);
  
 -      close($diff);
 +      $repo->command_close_pipe($diff);
  }
  
  sub _git_diff_parse {
-       my ($diff, $parent, $rev, %revinfo) = @_;
+       my ($diff, $parents, $rev, %revinfo) = @_;
+       my $ri = 0;
  
-       my ($ri, $pi) = (0,0);
        my $slines = $revs{$rev}{'lines'};
-       my @plines;
+       my (%plines, %pi);
  
        my $gotheader = 0;
        my ($remstart);
-       my ($hunk_start, $hunk_index);
+       my $parent_count = @$parents;
+       my $diff_header_regexp = "^@";
+       $diff_header_regexp .= "@" x @$parents;
+       $diff_header_regexp .= ' -\d+,\d+' x @$parents;
+       $diff_header_regexp .= ' \+(\d+),\d+';
+       $diff_header_regexp .= " " . ("@" x @$parents);
+       my %claim_regexps;
+       my $allparentplus = '^' . '\\+' x @$parents . '(.*)$';
+       {
+               my $i = 0;
+               foreach my $parent (@$parents) {
+                       $pi{$parent} = 0;
+                       my $r = '^' . '.' x @$parents . '(.*)$';
+                       my $p = $r;
+                       substr($p,$i+1, 1) = '\\+';
+                       my $m = $r;
+                       substr($m,$i+1, 1) = '-';
+                       $claim_regexps{$parent}{plus} = $p;
+                       $claim_regexps{$parent}{minus} = $m;
+                       $plines{$parent} = [];
+                       $i++;
+               }
+       }
+       DIFF:
        while(<$diff>) {
                chomp;
-               if (m/^@@ -(\d+),(\d+) \+(\d+),(\d+)/) {
-                       $remstart = $1;
-                       # Adjust for 0-based arrays
-                       $remstart--;
-                       # Reinit hunk tracking.
-                       $hunk_start = $remstart;
-                       $hunk_index = 0;
+               #printf("%d:%s:\n", $gotheader, $_);
+               if (m/$diff_header_regexp/) {
+                       $remstart = $1 - 1;
+                       # (0-based arrays)
                        $gotheader = 1;
  
-                       for (my $i = $ri; $i < $remstart; $i++) {
-                               $plines[$pi++] = $slines->[$i];
-                               $ri++;
+                       foreach my $parent (@$parents) {
+                               for (my $i = $ri; $i < $remstart; $i++) {
+                                       $plines{$parent}[$pi{$parent}++] = $slines->[$i];
+                               }
                        }
-                       next;
-               } elsif (!$gotheader) {
-                       next;
-               }
+                       $ri = $remstart;
  
-               if (m/^\+(.*)$/) {
-                       my $line = $1;
-                       $plines[$pi++] = [ $line, '', '', '', 0 ];
-                       next;
+                       next DIFF;
  
-               } elsif (m/^-(.*)$/) {
-                       my $line = $1;
-                       if (get_line($slines, $ri) eq $line) {
-                               # Found a match, claim
-                               claim_line($ri, $rev, $slines, %revinfo);
-                       } else {
-                               die sprintf("Sync error: %d/%d\n|%s\n|%s\n%s => %s\n",
-                                               $ri, $hunk_start + $hunk_index,
-                                               $line,
-                                               get_line($slines, $ri),
-                                               $rev, $parent);
-                       }
-                       $ri++;
+               } elsif (!$gotheader) {
+                       # Skip over the leadin.
+                       next DIFF;
+               }
  
-               } elsif (m/^\\/) {
+               if (m/^\\/) {
                        ;
                        # Skip \No newline at end of file.
                        # But this can be internationalized, so only look
                        # for an initial \
  
                } else {
-                       if (substr($_,1) ne get_line($slines,$ri) ) {
-                               die sprintf("Line %d (%d) does not match:\n|%s\n|%s\n%s => %s\n",
-                                               $hunk_start + $hunk_index, $ri,
-                                               substr($_,1),
-                                               get_line($slines,$ri),
-                                               $rev, $parent);
+                       my %claims = ();
+                       my $negclaim = 0;
+                       my $allclaimed = 0;
+                       my $line;
+                       if (m/$allparentplus/) {
+                               claim_line($ri, $rev, $slines, %revinfo);
+                               $allclaimed = 1;
+                       }
+                       PARENT:
+                       foreach my $parent (keys %claim_regexps) {
+                               my $m = $claim_regexps{$parent}{minus};
+                               my $p = $claim_regexps{$parent}{plus};
+                               if (m/$m/) {
+                                       $line = $1;
+                                       $plines{$parent}[$pi{$parent}++] = [ $line, '', '', '', 0 ];
+                                       $negclaim++;
+                               } elsif (m/$p/) {
+                                       $line = $1;
+                                       if (get_line($slines, $ri) eq $line) {
+                                               # Found a match, claim
+                                               $claims{$parent}++;
+                                       } else {
+                                               die sprintf("Sync error: %d\n|%s\n|%s\n%s => %s\n",
+                                                               $ri, $line,
+                                                               get_line($slines, $ri),
+                                                               $rev, $parent);
+                                       }
+                               }
+                       }
+                       if (%claims) {
+                               foreach my $parent (@$parents) {
+                                       next if $claims{$parent} || $allclaimed;
+                                       $plines{$parent}[$pi{$parent}++] = $slines->[$ri];
+                                           #[ $line, '', '', '', 0 ];
+                               }
+                               $ri++;
+                       } elsif ($negclaim) {
+                               next DIFF;
+                       } else {
+                               if (substr($_,scalar @$parents) ne get_line($slines,$ri) ) {
+                                       foreach my $parent (@$parents) {
+                                               printf("parent %s is on line %d\n", $parent, $pi{$parent});
+                                       }
+                                       my @context;
+                                       for (my $i = -2; $i < 2; $i++) {
+                                               push @context, get_line($slines, $ri + $i);
+                                       }
+                                       my $context = join("\n", @context);
+                                       my $justline = substr($_, scalar @$parents);
+                                       die sprintf("Line %d, does not match:\n|%s|\n|%s|\n%s\n",
+                                                   $ri,
+                                                   $justline,
+                                                   $context);
+                               }
+                               foreach my $parent (@$parents) {
+                                       $plines{$parent}[$pi{$parent}++] = $slines->[$ri];
+                               }
+                               $ri++;
                        }
-                       $plines[$pi++] = $slines->[$ri++];
                }
-               $hunk_index++;
        }
        for (my $i = $ri; $i < @{$slines} ; $i++) {
-               push @plines, $slines->[$ri++];
+               foreach my $parent (@$parents) {
+                       push @{$plines{$parent}}, $slines->[$ri];
+               }
+               $ri++;
+       }
+       foreach my $parent (@$parents) {
+               $revs{$parent}{lines} = $plines{$parent};
        }
  
-       $revs{$parent}{lines} = \@plines;
        return;
  }
  
@@@ -343,25 -547,36 +525,25 @@@ sub git_cat_file 
        my $blob = git_ls_tree($rev, $filename);
        die "Failed to find a blob for $filename in rev $rev\n" if !defined $blob;
  
 -      my $catfile = open_pipe("git","cat-file", "blob", $blob)
 -              or die "Failed to git-cat-file blob $blob (rev $rev, file $filename): " . $!;
 -
 -      my @lines;
 -      while(<$catfile>) {
 -              chomp;
 -              push @lines, $_;
 -      }
 -      close($catfile);
 -
 +      my @lines = split(/\n/, $repo->get_object('blob', $blob));
 +      pop @lines unless $lines[$#lines]; # Trailing newline
        return @lines;
  }
  
  sub git_ls_tree {
        my ($rev, $filename) = @_;
  
 -      my $lstree = open_pipe("git","ls-tree",$rev,$filename)
 -              or die "Failed to call git ls-tree: $!";
 -
 +      my $lstree = $repo->command_output_pipe('ls-tree', $rev, $filename);
        my ($mode, $type, $blob, $tfilename);
        while(<$lstree>) {
                chomp;
                ($mode, $type, $blob, $tfilename) = split(/\s+/, $_, 4);
                last if ($tfilename eq $filename);
        }
 -      close($lstree);
 +      $repo->command_close_pipe($lstree);
  
        return $blob if ($tfilename eq $filename);
        die "git-ls-tree failed to find blob for $filename";
 -
  }
  
  
@@@ -377,17 -592,25 +559,17 @@@ sub claim_line 
  
  sub git_commit_info {
        my ($rev) = @_;
 -      my $commit = open_pipe("git-cat-file", "commit", $rev)
 -              or die "Failed to call git-cat-file: $!";
 +      my $commit = $repo->get_object('commit', $rev);
  
        my %info;
 -      while(<$commit>) {
 -              chomp;
 -              last if (length $_ == 0);
 -
 -              if (m/^author (.*) <(.*)> (.*)$/) {
 -                      $info{'author'} = $1;
 -                      $info{'author_email'} = $2;
 -                      $info{'author_date'} = $3;
 -              } elsif (m/^committer (.*) <(.*)> (.*)$/) {
 -                      $info{'committer'} = $1;
 -                      $info{'committer_email'} = $2;
 -                      $info{'committer_date'} = $3;
 +      while ($commit =~ /(.*?)\n/g) {
 +              my $line = $1;
 +              if ($line =~ s/^author //) {
 +                      @info{'author', 'author_email', 'author_date'} = $repo->ident($line);
 +              } elsif ($line =~ s/^committer//) {
 +                      @info{'committer', 'committer_email', 'committer_date'} = $repo->ident($line);
                }
        }
 -      close($commit);
  
        return %info;
  }
@@@ -405,3 -628,81 +587,3 @@@ sub format_date 
        my $t = $timestamp + $minutes * 60;
        return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($t));
  }
 -
 -# Copied from git-send-email.perl - We need a Git.pm module..
 -sub gitvar {
 -    my ($var) = @_;
 -    my $fh;
 -    my $pid = open($fh, '-|');
 -    die "$!" unless defined $pid;
 -    if (!$pid) {
 -      exec('git-var', $var) or die "$!";
 -    }
 -    my ($val) = <$fh>;
 -    close $fh or die "$!";
 -    chomp($val);
 -    return $val;
 -}
 -
 -sub gitvar_name {
 -    my ($name) = @_;
 -    my $val = gitvar($name);
 -    my @field = split(/\s+/, $val);
 -    return join(' ', @field[0...(@field-4)]);
 -}
 -
 -sub open_pipe {
 -      if ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {
 -              return open_pipe_activestate(@_);
 -      } else {
 -              return open_pipe_normal(@_);
 -      }
 -}
 -
 -sub open_pipe_activestate {
 -      tie *fh, "Git::ActiveStatePipe", @_;
 -      return *fh;
 -}
 -
 -sub open_pipe_normal {
 -      my (@execlist) = @_;
 -
 -      my $pid = open my $kid, "-|";
 -      defined $pid or die "Cannot fork: $!";
 -
 -      unless ($pid) {
 -              exec @execlist;
 -              die "Cannot exec @execlist: $!";
 -      }
 -
 -      return $kid;
 -}
 -
 -package Git::ActiveStatePipe;
 -use strict;
 -
 -sub TIEHANDLE {
 -      my ($class, @params) = @_;
 -      my $cmdline = join " ", @params;
 -      my  @data = qx{$cmdline};
 -      bless { i => 0, data => \@data }, $class;
 -}
 -
 -sub READLINE {
 -      my $self = shift;
 -      if ($self->{i} >= scalar @{$self->{data}}) {
 -              return undef;
 -      }
 -      return $self->{'data'}->[ $self->{i}++ ];
 -}
 -
 -sub CLOSE {
 -      my $self = shift;
 -      delete $self->{data};
 -      delete $self->{i};
 -}
 -
 -sub EOF {
 -      my $self = shift;
 -      return ($self->{i} >= scalar @{$self->{data}});
 -}
diff --combined git-send-email.perl
index 79e82f5a8069362ccf41723df7db017228cd97bf,a83c7e90948fc3fe1b1ac82335704d66d060edab..1e2777c8e2bb27f092e987ffde532cb441bd06a1
@@@ -21,11 -21,56 +21,57 @@@ use warnings
  use Term::ReadLine;
  use Getopt::Long;
  use Data::Dumper;
 +use Git;
  
+ package FakeTerm;
+ sub new {
+       my ($class, $reason) = @_;
+       return bless \$reason, shift;
+ }
+ sub readline {
+       my $self = shift;
+       die "Cannot use readline on FakeTerm: $$self";
+ }
+ package main;
  # most mail servers generate the Date: header, but not all...
- $ENV{LC_ALL} = 'C';
- use POSIX qw/strftime/;
+ sub format_2822_time {
+       my ($time) = @_;
+       my @localtm = localtime($time);
+       my @gmttm = gmtime($time);
+       my $localmin = $localtm[1] + $localtm[2] * 60;
+       my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
+       if ($localtm[0] != $gmttm[0]) {
+               die "local zone differs from GMT by a non-minute interval\n";
+       }
+       if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
+               $localmin += 1440;
+       } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
+               $localmin -= 1440;
+       } elsif ($gmttm[6] != $localtm[6]) {
+               die "local time offset greater than or equal to 24 hours\n";
+       }
+       my $offset = $localmin - $gmtmin;
+       my $offhour = $offset / 60;
+       my $offmin = abs($offset % 60);
+       if (abs($offhour) >= 24) {
+               die ("local time offset greater than or equal to 24 hours\n");
+       }
+       return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
+                      qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]],
+                      $localtm[3],
+                      qw(Jan Feb Mar Apr May Jun
+                         Jul Aug Sep Oct Nov Dec)[$localtm[4]],
+                      $localtm[5]+1900,
+                      $localtm[2],
+                      $localtm[1],
+                      $localtm[0],
+                      ($offset >= 0) ? '+' : '-',
+                      abs($offhour),
+                      $offmin,
+                      );
+ }
  
  my $have_email_valid = eval { require Email::Valid; 1 };
  my $smtp;
@@@ -47,9 -92,12 +93,13 @@@ my $smtp_server
  # Example reply to:
  #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
  
- my $term = new Term::ReadLine 'git-send-email';
 +my $repo = Git->repository();
+ my $term = eval {
+       new Term::ReadLine 'git-send-email';
+ };
+ if ($@) {
+       $term = new FakeTerm "$@: going non-interactive";
+ }
  
  # Begin by accumulating all the variables (defined above), that we will end up
  # needing, first, from the command line:
@@@ -84,12 -132,33 +134,12 @@@ foreach my $entry (@bcclist) 
  
  # Now, let's fill any that aren't set in with defaults:
  
 -sub gitvar {
 -    my ($var) = @_;
 -    my $fh;
 -    my $pid = open($fh, '-|');
 -    die "$!" unless defined $pid;
 -    if (!$pid) {
 -      exec('git-var', $var) or die "$!";
 -    }
 -    my ($val) = <$fh>;
 -    close $fh or die "$!";
 -    chomp($val);
 -    return $val;
 -}
 -
 -sub gitvar_ident {
 -    my ($name) = @_;
 -    my $val = gitvar($name);
 -    my @field = split(/\s+/, $val);
 -    return join(' ', @field[0...(@field-3)]);
 -}
 -
 -my ($author) = gitvar_ident('GIT_AUTHOR_IDENT');
 -my ($committer) = gitvar_ident('GIT_COMMITTER_IDENT');
 +my ($author) = $repo->ident_person('author');
 +my ($committer) = $repo->ident_person('committer');
  
  my %aliases;
 -chomp(my @alias_files = `git-repo-config --get-all sendemail.aliasesfile`);
 -chomp(my $aliasfiletype = `git-repo-config sendemail.aliasfiletype`);
 +my @alias_files = $repo->config('sendemail.aliasesfile');
 +my $aliasfiletype = $repo->config('sendemail.aliasfiletype');
  my %parse_alias = (
        # multiline formats can be supported in the future
        mutt => sub { my $fh = shift; while (<$fh>) {
                }}}
  );
  
 -if (@alias_files && defined $parse_alias{$aliasfiletype}) {
 +if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
        foreach my $file (@alias_files) {
                open my $fh, '<', $file or die "opening $file: $!\n";
                $parse_alias{$aliasfiletype}->($fh);
@@@ -292,7 -361,7 +342,7 @@@ Options
     --smtp-server  If set, specifies the outgoing SMTP server to use.
                    Defaults to localhost.
  
-   --suppress-from Supress sending emails to yourself if your address
+   --suppress-from Suppress sending emails to yourself if your address
                    appears in a From: line.
  
     --quiet    Make git-send-email less verbose.  One line per email should be
@@@ -353,17 -422,19 +403,16 @@@ sub send_messag
        my @recipients = unique_email_list(@to);
        my $to = join (",\n\t", @recipients);
        @recipients = unique_email_list(@recipients,@cc,@bcclist);
-       my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++));
+       my $date = format_2822_time($time++);
        my $gitversion = '@@GIT_VERSION@@';
        if ($gitversion =~ m/..GIT_VERSION../) {
 -          $gitversion = `git --version`;
 -          chomp $gitversion;
 -          # keep only what's after the last space
 -          $gitversion =~ s/^.* //;
 +          $gitversion = Git::version();
        }
  
        my $header = "From: $from
  To: $to
  Cc: $cc
  Subject: $subject
- Reply-To: $from
  Date: $date
  Message-Id: $message_id
  X-Mailer: git-send-email $gitversion
diff --combined sha1_file.c
index ab64543d4a948ba9aee461d874dfba9a09a67dd6,43bc2ea0cf039bb9fd02c8313981e85bd7398d33..8f279d8d2c7a71b7bc436ced68eab0842bff71e8
@@@ -126,22 -126,16 +126,22 @@@ static void fill_sha1_path(char *pathbu
  char *sha1_file_name(const unsigned char *sha1)
  {
        static char *name, *base;
 +      static const char *last_objdir;
 +      const char *sha1_file_directory = get_object_directory();
  
 -      if (!base) {
 -              const char *sha1_file_directory = get_object_directory();
 +      if (!last_objdir || strcmp(last_objdir, sha1_file_directory)) {
                int len = strlen(sha1_file_directory);
 +              if (base)
 +                      free(base);
                base = xmalloc(len + 60);
                memcpy(base, sha1_file_directory, len);
                memset(base+len, 0, 60);
                base[len] = '/';
                base[len+3] = '/';
                name = base + len + 1;
 +              if (last_objdir)
 +                      free((char *) last_objdir);
 +              last_objdir = strdup(sha1_file_directory);
        }
        fill_sha1_path(name, sha1);
        return base;
@@@ -151,20 -145,14 +151,20 @@@ char *sha1_pack_name(const unsigned cha
  {
        static const char hex[] = "0123456789abcdef";
        static char *name, *base, *buf;
 +      static const char *last_objdir;
 +      const char *sha1_file_directory = get_object_directory();
        int i;
  
 -      if (!base) {
 -              const char *sha1_file_directory = get_object_directory();
 +      if (!last_objdir || strcmp(last_objdir, sha1_file_directory)) {
                int len = strlen(sha1_file_directory);
 +              if (base)
 +                      free(base);
                base = xmalloc(len + 60);
                sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory);
                name = base + len + 11;
 +              if (last_objdir)
 +                      free((char *) last_objdir);
 +              last_objdir = strdup(sha1_file_directory);
        }
  
        buf = name;
@@@ -182,20 -170,14 +182,20 @@@ char *sha1_pack_index_name(const unsign
  {
        static const char hex[] = "0123456789abcdef";
        static char *name, *base, *buf;
 +      static const char *last_objdir;
 +      const char *sha1_file_directory = get_object_directory();
        int i;
  
 -      if (!base) {
 -              const char *sha1_file_directory = get_object_directory();
 +      if (!last_objdir || strcmp(last_objdir, sha1_file_directory)) {
                int len = strlen(sha1_file_directory);
 +              if (base)
 +                      free(base);
                base = xmalloc(len + 60);
                sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.idx", sha1_file_directory);
                name = base + len + 11;
 +              if (last_objdir)
 +                      free((char *) last_objdir);
 +              last_objdir = strdup(sha1_file_directory);
        }
  
        buf = name;
@@@ -471,7 -453,7 +471,7 @@@ int use_packed_git(struct packed_git *p
  {
        if (!p->pack_size) {
                struct stat st;
-               // We created the struct before we had the pack
+               /* We created the struct before we had the pack */
                stat(p->pack_name, &st);
                if (!S_ISREG(st.st_mode))
                        die("packfile %s not a regular file", p->pack_name);
@@@ -702,26 -684,74 +702,74 @@@ static void *map_sha1_file_internal(con
        return map;
  }
  
int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size)
static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
  {
+       unsigned char c;
+       unsigned int word, bits;
+       unsigned long size;
+       static const char *typename[8] = {
+               NULL,   /* OBJ_EXT */
+               "commit", "tree", "blob", "tag",
+               NULL, NULL, NULL
+       };
+       const char *type;
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
        stream->next_in = map;
        stream->avail_in = mapsize;
        stream->next_out = buffer;
-       stream->avail_out = size;
+       stream->avail_out = bufsiz;
+       /*
+        * Is it a zlib-compressed buffer? If so, the first byte
+        * must be 0x78 (15-bit window size, deflated), and the
+        * first 16-bit word is evenly divisible by 31
+        */
+       word = (map[0] << 8) + map[1];
+       if (map[0] == 0x78 && !(word % 31)) {
+               inflateInit(stream);
+               return inflate(stream, 0);
+       }
+       c = *map++;
+       mapsize--;
+       type = typename[(c >> 4) & 7];
+       if (!type)
+               return -1;
+       bits = 4;
+       size = c & 0xf;
+       while ((c & 0x80)) {
+               if (bits >= 8*sizeof(long))
+                       return -1;
+               c = *map++;
+               size += (c & 0x7f) << bits;
+               bits += 7;
+               mapsize--;
+       }
  
+       /* Set up the stream for the rest.. */
+       stream->next_in = map;
+       stream->avail_in = mapsize;
        inflateInit(stream);
-       return inflate(stream, 0);
+       /* And generate the fake traditional header */
+       stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size);
+       return 0;
  }
  
  static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
  {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
+       unsigned long n;
  
-       memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
-       bytes = stream->total_out - bytes;
+       n = stream->total_out - bytes;
+       if (n > size)
+               n = size;
+       memcpy(buf, (char *) buffer + bytes, n);
+       bytes = n;
        if (bytes < size) {
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
   * too permissive for what we want to check. So do an anal
   * object header parse by hand.
   */
- int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
static int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
  {
        int i;
        unsigned long size;
@@@ -1349,31 -1379,29 +1397,29 @@@ char *write_sha1_file_prepare(void *buf
  static int link_temp_to_file(const char *tmpfile, char *filename)
  {
        int ret;
+       char *dir;
  
        if (!link(tmpfile, filename))
                return 0;
  
        /*
-        * Try to mkdir the last path component if that failed
-        * with an ENOENT.
+        * Try to mkdir the last path component if that failed.
         *
         * Re-try the "link()" regardless of whether the mkdir
         * succeeds, since a race might mean that somebody
         * else succeeded.
         */
        ret = errno;
-       if (ret == ENOENT) {
-               char *dir = strrchr(filename, '/');
-               if (dir) {
-                       *dir = 0;
-                       mkdir(filename, 0777);
-                       if (adjust_shared_perm(filename))
-                               return -2;
-                       *dir = '/';
-                       if (!link(tmpfile, filename))
-                               return 0;
-                       ret = errno;
-               }
+       dir = strrchr(filename, '/');
+       if (dir) {
+               *dir = 0;
+               mkdir(filename, 0777);
+               if (adjust_shared_perm(filename))
+                       return -2;
+               *dir = '/';
+               if (!link(tmpfile, filename))
+                       return 0;
+               ret = errno;
        }
        return ret;
  }
@@@ -1432,6 -1460,49 +1478,49 @@@ static int write_buffer(int fd, const v
        return 0;
  }
  
+ static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
+ {
+       int hdr_len;
+       unsigned char c;
+       c = (type << 4) | (len & 15);
+       len >>= 4;
+       hdr_len = 1;
+       while (len) {
+               *hdr++ = c | 0x80;
+               hdr_len++;
+               c = (len & 0x7f);
+               len >>= 7;
+       }
+       *hdr = c;
+       return hdr_len;
+ }
+ static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
+ {
+       int obj_type, hdr;
+       if (use_legacy_headers) {
+               while (deflate(stream, 0) == Z_OK)
+                       /* nothing */;
+               return;
+       }
+       if (!strcmp(type, blob_type))
+               obj_type = OBJ_BLOB;
+       else if (!strcmp(type, tree_type))
+               obj_type = OBJ_TREE;
+       else if (!strcmp(type, commit_type))
+               obj_type = OBJ_COMMIT;
+       else if (!strcmp(type, tag_type))
+               obj_type = OBJ_TAG;
+       else
+               die("trying to generate bogus object of type '%s'", type);
+       hdr = write_binary_header(stream->next_out, obj_type, len);
+       stream->total_out = hdr;
+       stream->next_out += hdr;
+       stream->avail_out -= hdr;
+ }
  int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
  {
        int size;
  
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, Z_BEST_COMPRESSION);
-       size = deflateBound(&stream, len+hdrlen);
+       deflateInit(&stream, zlib_compression_level);
+       size = 8 + deflateBound(&stream, len+hdrlen);
        compressed = xmalloc(size);
  
        /* Compress it */
        /* First header.. */
        stream.next_in = hdr;
        stream.avail_in = hdrlen;
-       while (deflate(&stream, 0) == Z_OK)
-               /* nothing */;
+       setup_object_header(&stream, type, len);
  
        /* Then the data itself.. */
        stream.next_in = buf;
@@@ -1522,14 -1592,14 +1610,14 @@@ static void *repack_object(const unsign
        int hdrlen;
        void *buf;
  
-       // need to unpack and recompress it by itself
+       /* need to unpack and recompress it by itself */
        unpacked = read_packed_sha1(sha1, type, &len);
  
        hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
  
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, Z_BEST_COMPRESSION);
+       deflateInit(&stream, zlib_compression_level);
        size = deflateBound(&stream, len + hdrlen);
        buf = xmalloc(size);
  
@@@ -1678,7 -1748,7 +1766,7 @@@ int has_sha1_file(const unsigned char *
  
  /*
   * reads from fd as long as possible into a supplied buffer of size bytes.
-  * If neccessary the buffer's size is increased using realloc()
+  * If necessary the buffer's size is increased using realloc()
   *
   * returns 0 if anything went fine and -1 otherwise
   *
diff --combined sha1_name.c
index c698c1b0b00dcb761e93779797795e8ce8cb5313,5fe8e5d4bf25d79c3fa76610d1617ee07c1f1e2c..bbb9f1b6ec51f94a44cac4c9f8eb21f6d0679790
@@@ -12,21 -12,15 +12,21 @@@ static int find_short_object_filename(i
        char hex[40];
        int found = 0;
        static struct alternate_object_database *fakeent;
 +      static const char *last_objdir;
 +      const char *objdir = get_object_directory();
  
 -      if (!fakeent) {
 -              const char *objdir = get_object_directory();
 +      if (!last_objdir || strcmp(last_objdir, objdir)) {
                int objdir_len = strlen(objdir);
                int entlen = objdir_len + 43;
 +              if (fakeent)
 +                      free(fakeent);
                fakeent = xmalloc(sizeof(*fakeent) + entlen);
                memcpy(fakeent->base, objdir, objdir_len);
                fakeent->name = fakeent->base + objdir_len + 1;
                fakeent->name[-1] = '/';
 +              if (last_objdir)
 +                      free((char *) last_objdir);
 +              last_objdir = strdup(objdir);
        }
        fakeent->next = alt_odb_list;
  
@@@ -387,13 -381,13 +387,13 @@@ static int peel_onion(const char *name
  
        sp++; /* beginning of type name, or closing brace for empty */
        if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
-               expected_type = TYPE_COMMIT;
+               expected_type = OBJ_COMMIT;
        else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
-               expected_type = TYPE_TREE;
+               expected_type = OBJ_TREE;
        else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
-               expected_type = TYPE_BLOB;
+               expected_type = OBJ_BLOB;
        else if (sp[0] == '}')
-               expected_type = TYPE_NONE;
+               expected_type = OBJ_NONE;
        else
                return -1;
  
                                memcpy(sha1, o->sha1, 20);
                                return 0;
                        }
-                       if (o->type == TYPE_TAG)
+                       if (o->type == OBJ_TAG)
                                o = ((struct tag*) o)->tagged;
-                       else if (o->type == TYPE_COMMIT)
+                       else if (o->type == OBJ_COMMIT)
                                o = &(((struct commit *) o)->tree->object);
                        else
                                return error("%.*s: expected %s type, but the object dereferences to %s type",
diff --combined t/test-lib.sh
index ad9796ee98315e4ffe9cdc0cba12ddf746a6a742,470a909891bc1358163af91513972ce7d0c702c0..b6d119af953eeba0a83d09543884dca600c850b4
@@@ -9,6 -9,8 +9,8 @@@ LC_ALL=
  PAGER=cat
  TZ=UTC
  export LANG LC_ALL PAGER TZ
+ EDITOR=:
+ VISUAL=:
  unset AUTHOR_DATE
  unset AUTHOR_EMAIL
  unset AUTHOR_NAME
@@@ -26,10 -28,12 +28,12 @@@ unset GIT_DI
  unset GIT_EXTERNAL_DIFF
  unset GIT_INDEX_FILE
  unset GIT_OBJECT_DIRECTORY
+ unset GIT_TRACE
  unset SHA1_FILE_DIRECTORIES
  unset SHA1_FILE_DIRECTORY
  export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
  export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+ export EDITOR VISUAL
  
  # Each test should start with something like this, after copyright notices:
  #
@@@ -206,8 -210,6 +210,8 @@@ PYTHON=`sed -e '1
        PYTHONPATH=$(pwd)/../compat
        export PYTHONPATH
  }
 +GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
 +export GITPERLLIB
  test -d ../templates/blt || {
        error "You haven't built things yet, have you?"
  }