Merge branch 'dr/ceiling'
authorJunio C Hamano <gitster@pobox.com>
Mon, 7 Jul 2008 09:17:23 +0000 (02:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 7 Jul 2008 09:17:23 +0000 (02:17 -0700)
* dr/ceiling:
  Eliminate an unnecessary chdir("..")
  Add support for GIT_CEILING_DIRECTORIES
  Fold test-absolute-path into test-path-utils
  Implement normalize_absolute_path

Conflicts:

cache.h
setup.c

1  2 
.gitignore
Documentation/git.txt
Makefile
cache.h
path.c
setup.c
t/test-lib.sh

diff --combined .gitignore
@@@ -75,6 -75,7 +75,6 @@@ git-merge-one-fil
  git-merge-ours
  git-merge-recursive
  git-merge-resolve
 -git-merge-stupid
  git-merge-subtree
  git-mergetool
  git-mktag
@@@ -141,7 -142,6 +141,6 @@@ git-write-tre
  git-core-*/?*
  gitk-wish
  gitweb/gitweb.cgi
- test-absolute-path
  test-chmtime
  test-date
  test-delta
@@@ -149,6 -149,7 +148,7 @@@ test-dump-cache-tre
  test-genrandom
  test-match-trees
  test-parse-options
+ test-path-utils
  test-sha1
  common-cmds.h
  *.tar.gz
diff --combined Documentation/git.txt
@@@ -1,4 -1,4 +1,4 @@@
 -git(7)
 +git(1)
  ======
  
  NAME
@@@ -20,11 -20,11 +20,11 @@@ Git is a fast, scalable, distributed re
  unusually rich command set that provides both high-level operations
  and full access to internals.
  
 -See this link:tutorial.html[tutorial] to get started, then see
 +See linkgit:gittutorial[7] to get started, then see
  link:everyday.html[Everyday Git] for a useful minimum set of commands, and
  "man git-commandname" for documentation of each command.  CVS users may
 -also want to read link:cvs-migration.html[CVS migration].  See
 -link:user-manual.html[Git User's Manual] for a more in-depth
 +also want to read linkgit:gitcvs-migration[7].  See
 +the link:user-manual.html[Git User's Manual] for a more in-depth
  introduction.
  
  The COMMAND is either a name of a Git command (see below) or an alias
@@@ -43,22 -43,14 +43,22 @@@ unreleased) version of git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
 -* link:v1.5.5/git.html[documentation for release 1.5.5]
 +* link:v1.5.6.2/git.html[documentation for release 1.5.6.2]
  
  * release notes for
 +  link:RelNotes-1.5.6.2.txt[1.5.6.2].
 +  link:RelNotes-1.5.6.1.txt[1.5.6.1].
 +  link:RelNotes-1.5.6.txt[1.5.6].
 +
 +* link:v1.5.5.4/git.html[documentation for release 1.5.5.4]
 +
 +* release notes for
 +  link:RelNotes-1.5.5.4.txt[1.5.5.4],
 +  link:RelNotes-1.5.5.3.txt[1.5.5.3],
 +  link:RelNotes-1.5.5.2.txt[1.5.5.2],
    link:RelNotes-1.5.5.1.txt[1.5.5.1],
    link:RelNotes-1.5.5.txt[1.5.5].
  
 -* link:v1.5.5.1/git.html[documentation for release 1.5.5.1]
 -
  * link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
  
  * release notes for
@@@ -82,8 -74,6 +82,8 @@@
    link:RelNotes-1.5.3.1.txt[1.5.3.1],
    link:RelNotes-1.5.3.txt[1.5.3].
  
 +* link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
 +
  * release notes for
    link:RelNotes-1.5.2.5.txt[1.5.2.5],
    link:RelNotes-1.5.2.4.txt[1.5.2.4],
@@@ -136,17 -126,16 +136,17 @@@ OPTION
  +
  Other options are available to control how the manual page is
  displayed. See linkgit:git-help[1] for more information,
 -because 'git --help ...' is converted internally into 'git
 -help ...'.
 +because `git --help ...` is converted internally into `git
 +help ...`.
  
  --exec-path::
        Path to wherever your core git programs are installed.
        This can also be controlled by setting the GIT_EXEC_PATH
 -      environment variable. If no path is given 'git' will print
 +      environment variable. If no path is given, 'git' will print
        the current setting and then exit.
  
 --p|--paginate::
 +-p::
 +--paginate::
        Pipe all output into 'less' (or if set, $PAGER).
  
  --no-pager::
@@@ -183,14 -172,13 +183,14 @@@ See the references above to get starte
  probably more detail than necessary for a first-time user.
  
  The link:user-manual.html#git-concepts[git concepts chapter of the
 -user-manual] and the link:core-tutorial.html[Core tutorial] both provide
 +user-manual] and linkgit:gitcore-tutorial[7] both provide
  introductions to the underlying git architecture.
  
  See also the link:howto-index.html[howto] documents for some useful
  examples.
  
 -The internals are documented link:technical/api-index.html[here].
 +The internals are documented in the
 +link:technical/api-index.html[GIT API documentation].
  
  GIT COMMANDS
  ------------
@@@ -374,9 -362,9 +374,9 @@@ For a more complete list of ways to spe
  File/Directory Structure
  ------------------------
  
 -Please see the link:repository-layout.html[repository layout] document.
 +Please see the linkgit:gitrepository-layout[5] document.
  
 -Read linkgit:githooks[5][hooks] for more details about each hook.
 +Read linkgit:githooks[5] for more details about each hook.
  
  Higher level SCMs may provide and manage additional information in the
  `$GIT_DIR`.
  
  Terminology
  -----------
 -Please see the link:glossary.html[glossary] document.
 +Please see linkgit:gitglossary[7].
  
  
  Environment Variables
@@@ -411,9 -399,9 +411,9 @@@ git so take care if using Cogito etc
  'GIT_ALTERNATE_OBJECT_DIRECTORIES'::
        Due to the immutable nature of git objects, old objects can be
        archived into shared, read-only directories. This variable
 -      specifies a ":" separated list of git object directories which
 -      can be used to search for git objects. New objects will not be
 -      written to these directories.
 +      specifies a ":" separated (on Windows ";" separated) list
 +      of git object directories which can be used to search for git
 +      objects. New objects will not be written to these directories.
  
  'GIT_DIR'::
        If the 'GIT_DIR' environment variable is set then it
        This can also be controlled by the '--work-tree' command line
        option and the core.worktree configuration variable.
  
+ 'GIT_CEILING_DIRECTORIES'::
+       This should be a colon-separated list of absolute paths.
+       If set, it is a list of directories that git should not chdir
+       up into while looking for a repository directory.
+       It will not exclude the current working directory or
+       a GIT_DIR set on the command line or in the environment.
+       (Useful for excluding slow-loading network directories.)
  git Commits
  ~~~~~~~~~~~
  'GIT_AUTHOR_NAME'::
@@@ -484,10 -480,10 +492,10 @@@ othe
        a pager.
  
  'GIT_SSH'::
 -      If this environment variable is set then linkgit:git-fetch[1]
 -      and linkgit:git-push[1] will use this command instead
 -      of `ssh` when they need to connect to a remote system.
 -      The 'GIT_SSH' command will be given exactly two arguments:
 +      If this environment variable is set then 'git-fetch'
 +      and 'git-push' will use this command instead
 +      of 'ssh' when they need to connect to a remote system.
 +      The '$GIT_SSH' command will be given exactly two arguments:
        the 'username@host' (or just 'host') from the URL and the
        shell command to execute on that remote system.
  +
@@@ -501,8 -497,8 +509,8 @@@ for further details
  
  'GIT_FLUSH'::
        If this environment variable is set to "1", then commands such
 -      as git-blame (in incremental mode), git-rev-list, git-log,
 -      git-whatchanged, etc., will force a flush of the output stream
 +      as 'git-blame' (in incremental mode), 'git-rev-list', 'git-log',
 +      and 'git-whatchanged' will force a flush of the output stream
        after each commit-oriented record have been flushed.   If this
        variable is set to "0", the output of these commands will be done
        using completely buffered I/O.   If this environment variable is
@@@ -528,7 -524,7 +536,7 @@@ Discussion[[Discussion]
  
  More detail on the following is available from the
  link:user-manual.html#git-concepts[git concepts chapter of the
 -user-manual] and the link:core-tutorial.html[Core tutorial].
 +user-manual] and linkgit:gitcore-tutorial[7].
  
  A git project normally consists of a working directory with a ".git"
  subdirectory at the top level.  The .git directory contains, among other
@@@ -589,13 -585,6 +597,13 @@@ The documentation for git suite was sta
  <david@dgreaves.com>, and later enhanced greatly by the
  contributors on the git-list <git@vger.kernel.org>.
  
 +SEE ALSO
 +--------
 +linkgit:gittutorial[7], linkgit:gittutorial-2[7],
 +linkgit:giteveryday[7], linkgit:gitcvs-migration[7],
 +linkgit:gitglossary[7], linkgit:gitcore-tutorial[7],
 +linkgit:gitcli[7], link:user-manual.html[The Git User's Manual]
 +
  GIT
  ---
 -Part of the linkgit:git[7] suite
 +Part of the linkgit:git[1] suite
diff --combined Makefile
+++ b/Makefile
@@@ -13,7 -13,7 +13,7 @@@ all:
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
  # This also implies MOZILLA_SHA1.
  #
 -# Define NO_CURL if you do not have curl installed.  git-http-pull and
 +# Define NO_CURL if you do not have libcurl installed.  git-http-pull and
  # git-http-push are not built, and you cannot use http:// and https://
  # transports.
  #
@@@ -174,7 -174,7 +174,7 @@@ prefix = $(HOME
  bindir = $(prefix)/bin
  mandir = $(prefix)/share/man
  infodir = $(prefix)/share/info
 -gitexecdir = $(bindir)
 +gitexecdir = $(prefix)/libexec/git-core
  sharedir = $(prefix)/share
  template_dir = $(sharedir)/git-core/templates
  htmldir=$(sharedir)/doc/git-doc
@@@ -205,7 -205,7 +205,7 @@@ GITWEB_FAVICON = git-favicon.pn
  GITWEB_SITE_HEADER =
  GITWEB_SITE_FOOTER =
  
 -export prefix bindir gitexecdir sharedir template_dir htmldir sysconfdir
 +export prefix bindir gitexecdir sharedir htmldir sysconfdir
  
  CC = gcc
  AR = ar
@@@ -235,12 -235,14 +235,12 @@@ BASIC_LDFLAGS 
  
  SCRIPT_SH += git-am.sh
  SCRIPT_SH += git-bisect.sh
 -SCRIPT_SH += git-clone.sh
  SCRIPT_SH += git-filter-branch.sh
  SCRIPT_SH += git-lost-found.sh
  SCRIPT_SH += git-merge-octopus.sh
  SCRIPT_SH += git-merge-one-file.sh
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-merge.sh
 -SCRIPT_SH += git-merge-stupid.sh
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-parse-remote.sh
  SCRIPT_SH += git-pull.sh
@@@ -272,9 -274,11 +272,9 @@@ EXTRA_PROGRAMS 
  
  # ... and all the rest that could be moved out of bindir to gitexecdir
  PROGRAMS += $(EXTRA_PROGRAMS)
 -PROGRAMS += git-daemon$X
  PROGRAMS += git-fast-import$X
  PROGRAMS += git-fetch-pack$X
  PROGRAMS += git-hash-object$X
 -PROGRAMS += git-imap-send$X
  PROGRAMS += git-index-pack$X
  PROGRAMS += git-merge-index$X
  PROGRAMS += git-merge-tree$X
@@@ -334,7 -338,6 +334,7 @@@ LIB_H += builtin.
  LIB_H += cache.h
  LIB_H += cache-tree.h
  LIB_H += commit.h
 +LIB_H += compat/mingw.h
  LIB_H += csum-file.h
  LIB_H += decorate.h
  LIB_H += delta.h
@@@ -343,7 -346,6 +343,7 @@@ LIB_H += diff.
  LIB_H += dir.h
  LIB_H += fsck.h
  LIB_H += git-compat-util.h
 +LIB_H += graph.h
  LIB_H += grep.h
  LIB_H += hash.h
  LIB_H += list-objects.h
@@@ -352,7 -354,6 +352,7 @@@ LIB_H += log-tree.
  LIB_H += mailmap.h
  LIB_H += object.h
  LIB_H += pack.h
 +LIB_H += pack-refs.h
  LIB_H += pack-revindex.h
  LIB_H += parse-options.h
  LIB_H += patch-ids.h
@@@ -374,9 -375,7 +374,9 @@@ LIB_H += tree.
  LIB_H += tree-walk.h
  LIB_H += unpack-trees.h
  LIB_H += utf8.h
 +LIB_H += wt-status.h
  
 +LIB_OBJS += abspath.o
  LIB_OBJS += alias.o
  LIB_OBJS += alloc.o
  LIB_OBJS += archive.o
@@@ -405,7 -404,6 +405,7 @@@ LIB_OBJS += diffcore-order.
  LIB_OBJS += diffcore-pickaxe.o
  LIB_OBJS += diffcore-rename.o
  LIB_OBJS += diff-delta.o
 +LIB_OBJS += diff-no-index.o
  LIB_OBJS += diff-lib.o
  LIB_OBJS += diff.o
  LIB_OBJS += dir.o
@@@ -413,7 -411,6 +413,7 @@@ LIB_OBJS += entry.
  LIB_OBJS += environment.o
  LIB_OBJS += exec_cmd.o
  LIB_OBJS += fsck.o
 +LIB_OBJS += graph.o
  LIB_OBJS += grep.o
  LIB_OBJS += hash.o
  LIB_OBJS += help.o
@@@ -429,7 -426,6 +429,7 @@@ LIB_OBJS += merge-file.
  LIB_OBJS += name-hash.o
  LIB_OBJS += object.o
  LIB_OBJS += pack-check.o
 +LIB_OBJS += pack-refs.o
  LIB_OBJS += pack-revindex.o
  LIB_OBJS += pack-write.o
  LIB_OBJS += pager.o
@@@ -468,7 -464,6 +468,7 @@@ LIB_OBJS += unpack-trees.
  LIB_OBJS += usage.o
  LIB_OBJS += utf8.o
  LIB_OBJS += walker.o
 +LIB_OBJS += wrapper.o
  LIB_OBJS += write_or_die.o
  LIB_OBJS += ws.o
  LIB_OBJS += wt-status.o
@@@ -487,7 -482,6 +487,7 @@@ BUILTIN_OBJS += builtin-check-ref-forma
  BUILTIN_OBJS += builtin-checkout-index.o
  BUILTIN_OBJS += builtin-checkout.o
  BUILTIN_OBJS += builtin-clean.o
 +BUILTIN_OBJS += builtin-clone.o
  BUILTIN_OBJS += builtin-commit-tree.o
  BUILTIN_OBJS += builtin-commit.o
  BUILTIN_OBJS += builtin-config.o
@@@ -566,45 -560,6 +566,45 @@@ endi
  ifeq ($(uname_S),GNU/kFreeBSD)
        NO_STRLCPY = YesPlease
  endif
 +ifeq ($(uname_S),UnixWare)
 +      CC = cc
 +      NEEDS_SOCKET = YesPlease
 +      NEEDS_NSL = YesPlease
 +      NEEDS_SSL_WITH_CRYPTO = YesPlease
 +      NEEDS_LIBICONV = YesPlease
 +      SHELL_PATH = /usr/local/bin/bash
 +      NO_IPV6 = YesPlease
 +      NO_HSTRERROR = YesPlease
 +      BASIC_CFLAGS += -Kthread
 +      BASIC_CFLAGS += -I/usr/local/include
 +      BASIC_LDFLAGS += -L/usr/local/lib
 +      INSTALL = ginstall
 +      TAR = gtar
 +      NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
 +endif
 +ifeq ($(uname_S),SCO_SV)
 +      ifeq ($(uname_R),3.2)
 +              CFLAGS = -O2
 +      endif
 +      ifeq ($(uname_R),5)
 +              CC = cc
 +              BASIC_CFLAGS += -Kthread
 +      endif
 +      NEEDS_SOCKET = YesPlease
 +      NEEDS_NSL = YesPlease
 +      NEEDS_SSL_WITH_CRYPTO = YesPlease
 +      NEEDS_LIBICONV = YesPlease
 +      SHELL_PATH = /usr/bin/bash
 +      NO_IPV6 = YesPlease
 +      NO_HSTRERROR = YesPlease
 +      BASIC_CFLAGS += -I/usr/local/include
 +      BASIC_LDFLAGS += -L/usr/local/lib
 +      NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
 +      INSTALL = ginstall
 +      TAR = gtar
 +endif
  ifeq ($(uname_S),Darwin)
        NEEDS_SSL_WITH_CRYPTO = YesPlease
        NEEDS_LIBICONV = YesPlease
@@@ -715,36 -670,6 +715,36 @@@ ifeq ($(uname_S),HP-UX
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
  endif
 +ifneq (,$(findstring MINGW,$(uname_S)))
 +      NO_MMAP = YesPlease
 +      NO_PREAD = YesPlease
 +      NO_OPENSSL = YesPlease
 +      NO_CURL = YesPlease
 +      NO_SYMLINK_HEAD = YesPlease
 +      NO_IPV6 = YesPlease
 +      NO_SETENV = YesPlease
 +      NO_UNSETENV = YesPlease
 +      NO_STRCASESTR = YesPlease
 +      NO_STRLCPY = YesPlease
 +      NO_MEMMEM = YesPlease
 +      NEEDS_LIBICONV = YesPlease
 +      OLD_ICONV = YesPlease
 +      NO_C99_FORMAT = YesPlease
 +      NO_STRTOUMAX = YesPlease
 +      NO_MKDTEMP = YesPlease
 +      SNPRINTF_RETURNS_BOGUS = YesPlease
 +      NO_SVN_TESTS = YesPlease
 +      NO_PERL_MAKEMAKER = YesPlease
 +      NO_POSIX_ONLY_PROGRAMS = YesPlease
 +      COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
 +      COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
 +      COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 +      COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o
 +      EXTLIBS += -lws2_32
 +      X = .exe
 +      template_dir = ../share/git-core/templates/
 +      ETC_GITCONFIG = ../etc/gitconfig
 +endif
  ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
  endif
@@@ -805,10 -730,6 +805,10 @@@ ifdef ZLIB_PAT
  endif
  EXTLIBS += -lz
  
 +ifndef NO_POSIX_ONLY_PROGRAMS
 +      PROGRAMS += git-daemon$X
 +      PROGRAMS += git-imap-send$X
 +endif
  ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
        ifdef OPENSSLDIR
@@@ -1265,7 -1186,7 +1265,7 @@@ endi
  
  ### Testing rules
  
- TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X
+ TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X
  
  all:: $(TEST_PROGRAMS)
  
@@@ -1300,18 -1221,11 +1300,18 @@@ remove-dashes
  
  ### Installation rules
  
 +ifeq ($(firstword $(subst /, ,$(template_dir))),..)
 +template_instdir = $(gitexecdir)/$(template_dir)
 +else
 +template_instdir = $(template_dir)
 +endif
 +export template_instdir
 +
  install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 -      $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
  ifndef NO_TCLTK
@@@ -1329,14 -1243,10 +1329,14 @@@ endi
  ifneq (,$X)
        $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
  endif
 +      ./check_bindir 'z$(bindir_SQ)' 'z$(gitexecdir_SQ)' '$(DESTDIR_SQ)$(bindir_SQ)/git-shell$X'
  
  install-doc:
        $(MAKE) -C Documentation install
  
 +install-html:
 +      $(MAKE) -C Documentation install-html
 +
  install-info:
        $(MAKE) -C Documentation install-info
  
@@@ -1428,7 -1338,7 +1428,7 @@@ check-docs:
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
 -              git-merge-resolve | git-merge-stupid | git-merge-subtree | \
 +              git-merge-resolve | git-merge-subtree | \
                git-fsck-objects | git-init-db | \
                git-?*--?* ) continue ;; \
                esac ; \
                documented,gitmodules | \
                documented,gitcli | \
                documented,git-tools | \
 +              documented,gitcore-tutorial | \
 +              documented,gitcvs-migration | \
 +              documented,gitdiffcore | \
 +              documented,gitglossary | \
 +              documented,githooks | \
 +              documented,gitrepository-layout | \
 +              documented,gittutorial | \
 +              documented,gittutorial-2 | \
                sentinel,not,matching,is,ok ) continue ;; \
                esac; \
                case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
diff --combined cache.h
+++ b/cache.h
@@@ -261,8 -261,8 +261,8 @@@ static inline void remove_name_hash(str
  #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
 -#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose))
 -#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
 +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
 +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@@ -298,7 -298,9 +298,8 @@@ static inline enum object_type object_t
  #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
  #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
  #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 -#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+ #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
@@@ -310,12 -312,12 +311,12 @@@ extern char *git_work_tree_cfg
  extern int is_inside_work_tree(void);
  extern const char *get_git_dir(void);
  extern char *get_object_directory(void);
 -extern char *get_refs_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
  extern const char *get_git_work_tree(void);
  extern const char *read_gitfile_gently(const char *path);
 +extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
@@@ -328,10 -330,6 +329,10 @@@ extern const char *prefix_filename(cons
  extern void verify_filename(const char *prefix, const char *name);
  extern void verify_non_filename(const char *prefix, const char *name);
  
 +#define INIT_DB_QUIET 0x0001
 +
 +extern int init_db(const char *template_dir, unsigned int flags);
 +
  #define alloc_nr(x) (((x)+16)*3/2)
  
  /*
@@@ -369,11 -367,8 +370,11 @@@ extern int add_index_entry(struct index
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern int remove_index_entry_at(struct index_state *, int pos);
  extern int remove_file_from_index(struct index_state *, const char *path);
 -extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);
 -extern int add_file_to_index(struct index_state *, const char *path, int verbose);
 +#define ADD_CACHE_VERBOSE 1
 +#define ADD_CACHE_PRETEND 2
 +#define ADD_CACHE_IGNORE_ERRORS       4
 +extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 +extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
  
@@@ -394,7 -389,6 +395,7 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
  #define REFRESH_QUIET         0x0004  /* be quiet about it */
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
 +#define REFRESH_IGNORE_SUBMODULES     0x0008  /* ignore submodules */
  extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
  
  struct lock_file {
        char filename[PATH_MAX];
  };
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 +extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
@@@ -433,7 -426,6 +434,7 @@@ extern size_t packed_git_window_size
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
  extern int auto_crlf;
 +extern int fsync_object_files;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -517,15 -509,14 +518,17 @@@ enum sharedrepo 
  int git_config_perm(const char *var, const char *value);
  int adjust_shared_perm(const char *path);
  int safe_create_leading_directories(char *path);
 +int safe_create_leading_directories_const(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
 -      return path[0] == '/';
 +      return path[0] == '/' || has_dos_drive_prefix(path);
  }
  const char *make_absolute_path(const char *path);
 +const char *make_nonrelative_path(const char *path);
 +const char *make_relative_path(const char *abs, const char *base);
+ int normalize_absolute_path(char *buf, const char *path);
+ int longest_ancestor_length(const char *path, const char *prefix_list);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
@@@ -533,10 -524,12 +536,10 @@@ extern void * read_sha1_file(const unsi
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
  extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 +extern int force_object_loose(const unsigned char *sha1, time_t mtime);
  
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
 -extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
 -                            size_t bufsize, size_t *bufposn);
 -extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
  
  extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
@@@ -624,7 -617,6 +627,7 @@@ extern struct alternate_object_databas
        char base[FLEX_ARRAY]; /* more */
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
 +extern void add_to_alternates_file(const char *reference);
  
  struct pack_window {
        struct pack_window *next;
@@@ -642,8 -634,6 +645,8 @@@ extern struct packed_git 
        const void *index_data;
        size_t index_size;
        uint32_t num_objects;
 +      uint32_t num_bad_objects;
 +      unsigned char *bad_object_sha1;
        int index_version;
        time_t mtime;
        int pack_fd;
@@@ -697,6 -687,8 +700,6 @@@ extern struct ref **get_remote_heads(in
  extern int server_supports(const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1);
 -extern struct packed_git *parse_pack_index_file(const unsigned char *sha1,
 -                                              const char *idx_path);
  
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
@@@ -712,7 -704,6 +715,7 @@@ extern void close_pack_windows(struct p
  extern void unuse_pack(struct pack_window **);
  extern struct packed_git *add_packed_git(const char *, int, int);
  extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
 +extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
  extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
@@@ -723,10 -714,10 +726,10 @@@ extern int matches_pack_name(struct pac
  /* Dumb servers support */
  extern int update_server_info(int);
  
 -typedef int (*config_fn_t)(const char *, const char *);
 -extern int git_default_config(const char *, const char *);
 -extern int git_config_from_file(config_fn_t fn, const char *);
 -extern int git_config(config_fn_t fn);
 +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(config_fn_t fn, void *);
  extern int git_parse_long(const char *, long *);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
@@@ -738,11 -729,11 +741,11 @@@ extern int git_config_set(const char *
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  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);
 -extern int git_env_bool(const char *, int);
 +extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_config_system(void);
  extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
 +extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
@@@ -761,7 -752,6 +764,7 @@@ extern ssize_t write_in_full(int fd, co
  extern void write_or_die(int fd, const void *buf, size_t count);
  extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
  extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
 +extern void fsync_or_die(int fd, const char *);
  
  /* pager.c */
  extern void setup_pager(void);
@@@ -795,11 -785,7 +798,11 @@@ extern int convert_to_git(const char *p
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* add */
 -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
 +/*
 + * return 0 if success, 1 - if addition of a file failed and
 + * ADD_FILES_IGNORE_ERRORS was specified in flags
 + */
 +int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
@@@ -819,11 -805,11 +822,11 @@@ void shift_tree(const unsigned char *, 
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
 -extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
 -    FILE *stream, const char *set,
 -    const char *reset, const char *ws);
 +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 int ws_blank_line(const char *line, int len, unsigned ws_rule);
  
  /* ls-files */
  int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
diff --combined path.c
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -291,55 -291,165 +291,151 @@@ int adjust_shared_perm(const char *path
        return 0;
  }
  
 -/* We allow "recursive" symbolic links. Only within reason, though. */
 -#define MAXDEPTH 5
 -
 -const char *make_absolute_path(const char *path)
 +static const char *get_pwd_cwd(void)
  {
 -      static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
 -      char cwd[1024] = "";
 -      int buf_index = 1, len;
 -
 -      int depth = MAXDEPTH;
 -      char *last_elem = NULL;
 -      struct stat st;
 -
 -      if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 -              die ("Too long path: %.*s", 60, path);
 -
 -      while (depth--) {
 -              if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
 -                      char *last_slash = strrchr(buf, '/');
 -                      if (last_slash) {
 -                              *last_slash = '\0';
 -                              last_elem = xstrdup(last_slash + 1);
 -                      } else {
 -                              last_elem = xstrdup(buf);
 -                              *buf = '\0';
 -                      }
 +      static char cwd[PATH_MAX + 1];
 +      char *pwd;
 +      struct stat cwd_stat, pwd_stat;
 +      if (getcwd(cwd, PATH_MAX) == NULL)
 +              return NULL;
 +      pwd = getenv("PWD");
 +      if (pwd && strcmp(pwd, cwd)) {
 +              stat(cwd, &cwd_stat);
 +              if (!stat(pwd, &pwd_stat) &&
 +                  pwd_stat.st_dev == cwd_stat.st_dev &&
 +                  pwd_stat.st_ino == cwd_stat.st_ino) {
 +                      strlcpy(cwd, pwd, PATH_MAX);
                }
 +      }
 +      return cwd;
 +}
  
 -              if (*buf) {
 -                      if (!*cwd && !getcwd(cwd, sizeof(cwd)))
 -                              die ("Could not get current working directory");
 -
 -                      if (chdir(buf))
 -                              die ("Could not switch to '%s'", buf);
 -              }
 -              if (!getcwd(buf, PATH_MAX))
 -                      die ("Could not get current working directory");
 -
 -              if (last_elem) {
 -                      int len = strlen(buf);
 -                      if (len + strlen(last_elem) + 2 > PATH_MAX)
 -                              die ("Too long path name: '%s/%s'",
 -                                              buf, last_elem);
 -                      buf[len] = '/';
 -                      strcpy(buf + len + 1, last_elem);
 -                      free(last_elem);
 -                      last_elem = NULL;
 -              }
 +const char *make_nonrelative_path(const char *path)
 +{
 +      static char buf[PATH_MAX + 1];
  
 -              if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
 -                      len = readlink(buf, next_buf, PATH_MAX);
 -                      if (len < 0)
 -                              die ("Invalid symlink: %s", buf);
 -                      next_buf[len] = '\0';
 -                      buf = next_buf;
 -                      buf_index = 1 - buf_index;
 -                      next_buf = bufs[buf_index];
 -              } else
 -                      break;
 +      if (is_absolute_path(path)) {
 +              if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
 +                      die ("Too long path: %.*s", 60, path);
 +      } else {
 +              const char *cwd = get_pwd_cwd();
 +              if (!cwd)
 +                      die("Cannot determine the current working directory");
 +              if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
 +                      die ("Too long path: %.*s", 60, path);
        }
 +      return buf;
 +}
  
 -      if (*cwd && chdir(cwd))
 -              die ("Could not change back to '%s'", cwd);
 -
 +const char *make_relative_path(const char *abs, const char *base)
 +{
 +      static char buf[PATH_MAX + 1];
 +      int baselen;
 +      if (!base)
 +              return abs;
 +      baselen = strlen(base);
 +      if (prefixcmp(abs, base))
 +              return abs;
 +      if (abs[baselen] == '/')
 +              baselen++;
 +      else if (base[baselen - 1] != '/')
 +              return abs;
 +      strcpy(buf, abs + baselen);
        return buf;
  }
+ /*
+  * path = absolute path
+  * buf = buffer of at least max(2, strlen(path)+1) bytes
+  * It is okay if buf == path, but they should not overlap otherwise.
+  *
+  * Performs the following normalizations on path, storing the result in buf:
+  * - Removes trailing slashes.
+  * - Removes empty components.
+  * - Removes "." components.
+  * - Removes ".." components, and the components the precede them.
+  * "" and paths that contain only slashes are normalized to "/".
+  * Returns the length of the output.
+  *
+  * Note that this function is purely textual.  It does not follow symlinks,
+  * verify the existence of the path, or make any system calls.
+  */
+ int normalize_absolute_path(char *buf, const char *path)
+ {
+       const char *comp_start = path, *comp_end = path;
+       char *dst = buf;
+       int comp_len;
+       assert(buf);
+       assert(path);
+       while (*comp_start) {
+               assert(*comp_start == '/');
+               while (*++comp_end && *comp_end != '/')
+                       ; /* nothing */
+               comp_len = comp_end - comp_start;
+               if (!strncmp("/",  comp_start, comp_len) ||
+                   !strncmp("/.", comp_start, comp_len))
+                       goto next;
+               if (!strncmp("/..", comp_start, comp_len)) {
+                       while (dst > buf && *--dst != '/')
+                               ; /* nothing */
+                       goto next;
+               }
+               memcpy(dst, comp_start, comp_len);
+               dst += comp_len;
+       next:
+               comp_start = comp_end;
+       }
+       if (dst == buf)
+               *dst++ = '/';
+       *dst = '\0';
+       return dst - buf;
+ }
+ /*
+  * path = Canonical absolute path
+  * prefix_list = Colon-separated list of absolute paths
+  *
+  * Determines, for each path in parent_list, whether the "prefix" really
+  * is an ancestor directory of path.  Returns the length of the longest
+  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
+  * is an ancestor.  (Note that this means 0 is returned if prefix_list is
+  * "/".) "/foo" is not considered an ancestor of "/foobar".  Directories
+  * are not considered to be their own ancestors.  path must be in a
+  * canonical form: empty components, or "." or ".." components are not
+  * allowed.  prefix_list may be null, which is like "".
+  */
+ int longest_ancestor_length(const char *path, const char *prefix_list)
+ {
+       char buf[PATH_MAX+1];
+       const char *ceil, *colon;
+       int len, max_len = -1;
+       if (prefix_list == NULL || !strcmp(path, "/"))
+               return -1;
+       for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
+               for (colon = ceil; *colon && *colon != ':'; colon++);
+               len = colon - ceil;
+               if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
+                       continue;
+               strlcpy(buf, ceil, len+1);
+               len = normalize_absolute_path(buf, buf);
+               /* Strip "trailing slashes" from "/". */
+               if (len == 1)
+                       len = 0;
+               if (!strncmp(path, buf, len) &&
+                   path[len] == '/' &&
+                   len > max_len) {
+                       max_len = len;
+               }
+       }
+       return max_len;
+ }
diff --combined setup.c
+++ b/setup.c
@@@ -6,17 -6,11 +6,17 @@@ static int inside_work_tree = -1
  
  static int sanitary_path_copy(char *dst, const char *src)
  {
 -      char *dst0 = dst;
 +      char *dst0;
  
 -      if (*src == '/') {
 +      if (has_dos_drive_prefix(src)) {
 +              *dst++ = *src++;
 +              *dst++ = *src++;
 +      }
 +      dst0 = dst;
 +
 +      if (is_dir_sep(*src)) {
                *dst++ = '/';
 -              while (*src == '/')
 +              while (is_dir_sep(*src))
                        src++;
        }
  
                 * (4) "../"          -- strip one, eat slash and continue.
                 */
                if (c == '.') {
 -                      switch (src[1]) {
 -                      case '\0':
 +                      if (!src[1]) {
                                /* (1) */
                                src++;
 -                              break;
 -                      case '/':
 +                      } else if (is_dir_sep(src[1])) {
                                /* (2) */
                                src += 2;
 -                              while (*src == '/')
 +                              while (is_dir_sep(*src))
                                        src++;
                                continue;
 -                      case '.':
 -                              switch (src[2]) {
 -                              case '\0':
 +                      } else if (src[1] == '.') {
 +                              if (!src[2]) {
                                        /* (3) */
                                        src += 2;
                                        goto up_one;
 -                              case '/':
 +                              } else if (is_dir_sep(src[2])) {
                                        /* (4) */
                                        src += 3;
 -                                      while (*src == '/')
 +                                      while (is_dir_sep(*src))
                                                src++;
                                        goto up_one;
                                }
                }
  
                /* copy up to the next '/', and eat all '/' */
 -              while ((c = *src++) != '\0' && c != '/')
 +              while ((c = *src++) != '\0' && !is_dir_sep(c))
                        *dst++ = c;
 -              if (c == '/') {
 -                      *dst++ = c;
 -                      while (c == '/')
 +              if (is_dir_sep(c)) {
 +                      *dst++ = '/';
 +                      while (is_dir_sep(c))
                                c = *src++;
                        src--;
                } else if (!c)
@@@ -80,7 -77,7 +80,7 @@@
                        if (dst <= dst0)
                                break;
                        c = *dst--;
 -                      if (c == '/') {
 +                      if (c == '/') { /* MinGW: cannot be '\\' anymore */
                                dst += 2;
                                break;
                        }
@@@ -129,23 -126,10 +129,23 @@@ const char *prefix_path(const char *pre
  const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
  {
        static char path[PATH_MAX];
 +#ifndef __MINGW32__
        if (!pfx || !*pfx || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
 +#else
 +      char *p;
 +      /* don't add prefix to absolute paths, but still replace '\' by '/' */
 +      if (is_absolute_path(arg))
 +              pfx_len = 0;
 +      else
 +              memcpy(path, pfx, pfx_len);
 +      strcpy(path + pfx_len, arg);
 +      for (p = path + pfx_len; *p; p++)
 +              if (*p == '\\')
 +                      *p = '/';
 +#endif
        return path;
  }
  
@@@ -308,16 -292,15 +308,16 @@@ void setup_work_tree(void
        work_tree = get_git_work_tree();
        git_dir = get_git_dir();
        if (!is_absolute_path(git_dir))
 -              set_git_dir(make_absolute_path(git_dir));
 +              git_dir = make_absolute_path(git_dir);
        if (!work_tree || chdir(work_tree))
                die("This operation must be run in a work tree");
 +      set_git_dir(make_relative_path(git_dir, work_tree));
        initialized = 1;
  }
  
  static int check_repository_format_gently(int *nongit_ok)
  {
 -      git_config(check_repository_format_version);
 +      git_config(check_repository_format_version, NULL);
        if (GIT_REPO_VERSION < repository_format_version) {
                if (!nongit_ok)
                        die ("Expected git repo version <= %d, found %d",
@@@ -376,11 -359,11 +376,11 @@@ const char *read_gitfile_gently(const c
  const char *setup_git_directory_gently(int *nongit_ok)
  {
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
        const char *gitfile_dir;
-       int len, offset;
-       int minoffset = 0;
+       int len, offset, ceil_offset;
  
        /*
         * Let's assume that we are in a git repository.
  
        if (!getcwd(cwd, sizeof(cwd)-1))
                die("Unable to read current working directory");
-       if (has_dos_drive_prefix(cwd))
-               minoffset = 2;
+       ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
++      if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
++              ceil_offset = 1;
  
        /*
         * Test in the following order (relative to the cwd):
                        check_repository_format_gently(nongit_ok);
                        return NULL;
                }
-               chdir("..");
-               do {
-                       if (offset <= minoffset) {
-                               if (nongit_ok) {
-                                       if (chdir(cwd))
-                                               die("Cannot come back to cwd");
-                                       *nongit_ok = 1;
-                                       return NULL;
-                               }
-                               die("Not a git repository");
+               while (--offset > ceil_offset && cwd[offset] != '/');
+               if (offset <= ceil_offset) {
+                       if (nongit_ok) {
+                               if (chdir(cwd))
+                                       die("Cannot come back to cwd");
+                               *nongit_ok = 1;
+                               return NULL;
                        }
-               } while (offset > minoffset && cwd[--offset] != '/');
+                       die("Not a git repository");
+               }
+               chdir("..");
        }
  
        inside_git_dir = 0;
@@@ -544,7 -526,7 +545,7 @@@ int git_config_perm(const char *var, co
        return i & 0666;
  }
  
 -int check_repository_format_version(const char *var, const char *value)
 +int check_repository_format_version(const char *var, const char *value, void *cb)
  {
        if (strcmp(var, "core.repositoryformatversion") == 0)
                repository_format_version = git_config_int(var, value);
diff --combined t/test-lib.sh
@@@ -35,6 -35,7 +35,7 @@@ unset GIT_WORK_TRE
  unset GIT_EXTERNAL_DIFF
  unset GIT_INDEX_FILE
  unset GIT_OBJECT_DIRECTORY
+ unset GIT_CEILING_DIRECTORIES
  unset SHA1_FILE_DIRECTORIES
  unset SHA1_FILE_DIRECTORY
  GIT_MERGE_VERBOSITY=5
@@@ -80,8 -81,6 +81,8 @@@ d
                debug=t; shift ;;
        -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
                immediate=t; shift ;;
 +      -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
 +              export GIT_TEST_LONG=t; shift ;;
        -h|--h|--he|--hel|--help)
                help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
@@@ -154,7 -153,6 +155,7 @@@ test_failure=
  test_count=0
  test_fixed=0
  test_broken=0
 +test_success=0
  
  die () {
        echo >&5 "FATAL: Unexpected exit with code $?"
@@@ -171,7 -169,7 +172,7 @@@ trap 'die' exi
  # environment variables to work around this.
  #
  # In particular, quoting isn't enough, as the path may contain the same quote
 -# that we're using. 
 +# that we're using.
  test_set_editor () {
        FAKE_EDITOR="$1"
        export FAKE_EDITOR
@@@ -196,7 -194,6 +197,7 @@@ test_tick () 
  
  test_ok_ () {
        test_count=$(expr "$test_count" + 1)
 +      test_success=$(expr "$test_success" + 1)
        say_color "" "  ok $test_count: $@"
  }
  
@@@ -306,64 -303,6 +307,64 @@@ test_expect_code () 
        echo >&3 ""
  }
  
 +# test_external runs external test scripts that provide continuous
 +# test output about their progress, and succeeds/fails on
 +# zero/non-zero exit code.  It outputs the test output on stdout even
 +# in non-verbose mode, and announces the external script with "* run
 +# <n>: ..." before running it.  When providing relative paths, keep in
 +# mind that all scripts run in "trash directory".
 +# Usage: test_external description command arguments...
 +# Example: test_external 'Perl API' perl ../path/to/test.pl
 +test_external () {
 +      test "$#" -eq 3 ||
 +      error >&5 "bug in the test script: not 3 parameters to test_external"
 +      descr="$1"
 +      shift
 +      if ! test_skip "$descr" "$@"
 +      then
 +              # Announce the script to reduce confusion about the
 +              # test output that follows.
 +              say_color "" " run $(expr "$test_count" + 1): $descr ($*)"
 +              # Run command; redirect its stderr to &4 as in
 +              # test_run_, but keep its stdout on our stdout even in
 +              # non-verbose mode.
 +              "$@" 2>&4
 +              if [ "$?" = 0 ]
 +              then
 +                      test_ok_ "$descr"
 +              else
 +                      test_failure_ "$descr" "$@"
 +              fi
 +      fi
 +}
 +
 +# Like test_external, but in addition tests that the command generated
 +# no output on stderr.
 +test_external_without_stderr () {
 +      # The temporary file has no (and must have no) security
 +      # implications.
 +      tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
 +      stderr="$tmp/git-external-stderr.$$.tmp"
 +      test_external "$@" 4> "$stderr"
 +      [ -f "$stderr" ] || error "Internal error: $stderr disappeared."
 +      descr="no stderr: $1"
 +      shift
 +      say >&3 "expecting no stderr from previous command"
 +      if [ ! -s "$stderr" ]; then
 +              rm "$stderr"
 +              test_ok_ "$descr"
 +      else
 +              if [ "$verbose" = t ]; then
 +                      output=`echo; echo Stderr is:; cat "$stderr"`
 +              else
 +                      output=
 +              fi
 +              # rm first in case test_failure exits.
 +              rm "$stderr"
 +              test_failure_ "$descr" "$@" "$output"
 +      fi
 +}
 +
  # This is not among top-level (test_expect_success | test_expect_failure)
  # but is a prefix that can be used in the test script, like:
  #
@@@ -407,7 -346,7 +408,7 @@@ test_create_repo () 
        repo="$1"
        mkdir "$repo"
        cd "$repo" || error "Cannot setup test environment"
 -      "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >/dev/null 2>&1 ||
 +      "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 ||
        error "cannot run git init -- have you built things yet?"
        mv .git/hooks .git/hooks-disabled
        cd "$owd"
  
  test_done () {
        trap - exit
 +      test_results_dir="$TEST_DIRECTORY/test-results"
 +      mkdir -p "$test_results_dir"
 +      test_results_path="$test_results_dir/${0%-*}-$$"
 +
 +      echo "total $test_count" >> $test_results_path
 +      echo "success $test_success" >> $test_results_path
 +      echo "fixed $test_fixed" >> $test_results_path
 +      echo "broken $test_broken" >> $test_results_path
 +      echo "failed $test_failure" >> $test_results_path
 +      echo "" >> $test_results_path
  
        if test "$test_fixed" != 0
        then
        case "$test_failure" in
        0)
                # We could:
 -              # cd .. && rm -fr trash
 +              # cd .. && rm -fr 'trash directory'
                # but that means we forbid any tests that use their own
                # subdirectory from calling test_done without coming back
                # to where they started from.
  }
  
  # Test the binaries we have just built.  The tests are kept in
 -# t/ subdirectory and are run in trash subdirectory.
 -PATH=$(pwd)/..:$PATH
 +# t/ subdirectory and are run in 'trash directory' subdirectory.
 +TEST_DIRECTORY=$(pwd)
 +PATH=$TEST_DIRECTORY/..:$PATH
  GIT_EXEC_PATH=$(pwd)/..
  GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
  unset GIT_CONFIG
@@@ -492,9 -420,7 +493,9 @@@ rm -fr "$test" || 
  }
  
  test_create_repo "$test"
 -cd "$test"
 +# Use -P to resolve symlinks in our working directory so that the cwd
 +# in subprocesses like git equals our $PWD (for pathname comparisons).
 +cd -P "$test" || exit 1
  
  this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
  for skp in $GIT_SKIP_TESTS