Merge branch 'nd/warn-more-for-devs'
authorJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:21 +0000 (15:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:21 +0000 (15:59 +0900)
The build procedure "make DEVELOPER=YesPlease" learned to enable a
bit more warning options depending on the compiler used to help
developers more. There also is "make DEVOPTS=tokens" knob
available now, for those who want to help fixing warnings we
usually ignore, for example.

* nd/warn-more-for-devs:
Makefile: add a DEVOPTS to get all of -Wextra
Makefile: add a DEVOPTS to suppress -Werror under DEVELOPER
Makefile: detect compiler and enable more warnings in DEVELOPER=1
connect.c: mark die_initial_contact() NORETURN

1  2 
Makefile
connect.c
diff --combined Makefile
index f89f2dba5bfe8574fb58fdca376420e5665173c0,1ad1049a408cbe4c3f29e580edff1f01e78d93ab..4541788e50c4faa424c6df717dfcc6bc78fa570b
+++ b/Makefile
@@@ -29,10 -29,10 +29,10 @@@ all:
  # Perl-compatible regular expressions instead of standard or extended
  # POSIX regular expressions.
  #
 -# Currently USE_LIBPCRE is a synonym for USE_LIBPCRE1, define
 -# USE_LIBPCRE2 instead if you'd like to use version 2 of the PCRE
 -# library. The USE_LIBPCRE flag will likely be changed to mean v2 by
 -# default in future releases.
 +# USE_LIBPCRE is a synonym for USE_LIBPCRE2, define USE_LIBPCRE1
 +# instead if you'd like to use the legacy version 1 of the PCRE
 +# library. Support for version 1 will likely be removed in some future
 +# release of Git, as upstream has all but abandoned it.
  #
  # When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if the PCRE v1
  # library is compiled without --enable-jit. We will auto-detect
  #
  # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
  #
 -# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 -# MakeMaker (e.g. using ActiveState under Cygwin).
 -#
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
 +# Define NO_PERL_CPAN_FALLBACKS if you do not want to install bundled
 +# copies of CPAN modules that serve as a fallback in case the modules
 +# are not available on the system. This option is intended for
 +# distributions that want to use their packaged versions of Perl
 +# modules, instead of the fallbacks shipped with Git.
 +#
  # Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python
  # but /usr/bin/python2.7 on some platforms).
  #
  # when hardlinking a file to another name and unlinking the original file right
  # away (some NTFS drivers seem to zero the contents in that scenario).
  #
 +# Define INSTALL_SYMLINKS if you prefer to have everything that can be
 +# symlinked between bin/ and libexec/ to use relative symlinks between
 +# the two. This option overrides NO_CROSS_DIRECTORY_HARDLINKS and
 +# NO_INSTALL_HARDLINKS which will also use symlinking by indirection
 +# within the same directory in some cases, INSTALL_SYMLINKS will
 +# always symlink to the final target directly.
 +#
  # Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
  # programs as a tar, where bin/ and libexec/ might be on different file systems.
  #
  # When cross-compiling, define HOST_CPU as the canonical name of the CPU on
  # which the built Git will run (for instance "x86_64").
  #
 +# Define RUNTIME_PREFIX to configure Git to resolve its ancillary tooling and
 +# support files relative to the location of the runtime binary, rather than
 +# hard-coding them into the binary. Git installations built with RUNTIME_PREFIX
 +# can be moved to arbitrary filesystem locations. RUNTIME_PREFIX also causes
 +# Perl scripts to use a modified entry point header allowing them to resolve
 +# support files at runtime.
 +#
 +# When using RUNTIME_PREFIX, define HAVE_BSD_KERN_PROC_SYSCTL if your platform
 +# supports the KERN_PROC BSD sysctl function.
 +#
 +# When using RUNTIME_PREFIX, define PROCFS_EXECUTABLE_PATH if your platform
 +# mounts a "procfs" filesystem capable of resolving the path of the current
 +# executable. If defined, this must be the canonical path for the "procfs"
 +# current executable path.
 +#
 +# When using RUNTIME_PREFIX, define HAVE_NS_GET_EXECUTABLE_PATH if your platform
 +# supports calling _NSGetExecutablePath to retrieve the path of the running
 +# executable.
 +#
 +# When using RUNTIME_PREFIX, define HAVE_WPGMPTR if your platform offers
 +# the global variable _wpgmptr containing the absolute path of the current
 +# executable (this is the case on Windows).
++#
+ # Define DEVELOPER to enable more compiler warnings. Compiler version
+ # and family are auto detected, but could be overridden by defining
+ # COMPILER_FEATURES (see config.mak.dev)
+ #
+ # When DEVELOPER is set, DEVOPTS can be used to control compiler
+ # options.  This variable contains keywords separated by
+ # whitespace. The following keywords are are recognized:
+ #
+ #    no-error:
+ #
+ #        suppresses the -Werror that implicitly comes with
+ #        DEVELOPER=1. Useful for getting the full set of errors
+ #        without immediately dying, or for logging them.
+ #
+ #    extra-all:
+ #
+ #        The DEVELOPER mode enables -Wextra with a few exceptions. By
+ #        setting this flag the exceptions are removed, and all of
+ #        -Wextra is used.
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
  # CFLAGS and LDFLAGS are for the users to override from the command line.
  
  CFLAGS = -g -O2 -Wall
- DEVELOPER_CFLAGS = -Werror \
-       -Wdeclaration-after-statement \
-       -Wno-format-zero-length \
-       -Wold-style-definition \
-       -Woverflow \
-       -Wpointer-arith \
-       -Wstrict-prototypes \
-       -Wunused \
-       -Wvla
  LDFLAGS =
  ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
  ALL_LDFLAGS = $(LDFLAGS)
@@@ -501,19 -479,17 +512,19 @@@ ARFLAGS = rc
  #   mandir
  #   infodir
  #   htmldir
 +#   localedir
 +#   perllibdir
  # This can help installing the suite in a relocatable way.
  
  prefix = $(HOME)
 -bindir_relative = bin
 -bindir = $(prefix)/$(bindir_relative)
 +bindir = $(prefix)/bin
  mandir = $(prefix)/share/man
  infodir = $(prefix)/share/info
  gitexecdir = libexec/git-core
  mergetoolsdir = $(gitexecdir)/mergetools
  sharedir = $(prefix)/share
  gitwebdir = $(sharedir)/gitweb
 +perllibdir = $(sharedir)/perl5
  localedir = $(sharedir)/locale
  template_dir = share/git-core/templates
  htmldir = $(prefix)/share/doc/git-doc
@@@ -523,15 -499,11 +534,15 @@@ lib = li
  # DESTDIR =
  pathsep = :
  
 +bindir_relative = $(patsubst $(prefix)/%,%,$(bindir))
  mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
  infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
 +gitexecdir_relative = $(patsubst $(prefix)/%,%,$(gitexecdir))
 +localedir_relative = $(patsubst $(prefix)/%,%,$(localedir))
  htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
 +perllibdir_relative = $(patsubst $(prefix)/%,%,$(perllibdir))
  
 -export prefix bindir sharedir sysconfdir gitwebdir localedir
 +export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
  
  CC = cc
  AR = ar
@@@ -581,7 -553,6 +592,7 @@@ SCRIPT_PERL 
  SCRIPT_PYTHON =
  SCRIPT_SH =
  SCRIPT_LIB =
 +TEST_BUILTINS_OBJS =
  TEST_PROGRAMS_NEED_X =
  
  # Having this variable in your environment would break pipelines because
@@@ -679,6 -650,7 +690,6 @@@ PROGRAM_OBJS += imap-send.
  PROGRAM_OBJS += sh-i18n--envsubst.o
  PROGRAM_OBJS += shell.o
  PROGRAM_OBJS += show-index.o
 -PROGRAM_OBJS += upload-pack.o
  PROGRAM_OBJS += remote-testsvn.o
  
  # Binary suffix, set to .exe for Windows builds
@@@ -686,50 -658,47 +697,50 @@@ X 
  
  PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
  
 -TEST_PROGRAMS_NEED_X += test-chmtime
 -TEST_PROGRAMS_NEED_X += test-ctype
 -TEST_PROGRAMS_NEED_X += test-config
 -TEST_PROGRAMS_NEED_X += test-date
 -TEST_PROGRAMS_NEED_X += test-delta
 -TEST_PROGRAMS_NEED_X += test-drop-caches
 -TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 +TEST_BUILTINS_OBJS += test-chmtime.o
 +TEST_BUILTINS_OBJS += test-config.o
 +TEST_BUILTINS_OBJS += test-ctype.o
 +TEST_BUILTINS_OBJS += test-date.o
 +TEST_BUILTINS_OBJS += test-delta.o
 +TEST_BUILTINS_OBJS += test-drop-caches.o
 +TEST_BUILTINS_OBJS += test-dump-cache-tree.o
 +TEST_BUILTINS_OBJS += test-dump-split-index.o
 +TEST_BUILTINS_OBJS += test-example-decorate.o
 +TEST_BUILTINS_OBJS += test-genrandom.o
 +TEST_BUILTINS_OBJS += test-hashmap.o
 +TEST_BUILTINS_OBJS += test-index-version.o
 +TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
 +TEST_BUILTINS_OBJS += test-match-trees.o
 +TEST_BUILTINS_OBJS += test-mergesort.o
 +TEST_BUILTINS_OBJS += test-mktemp.o
 +TEST_BUILTINS_OBJS += test-online-cpus.o
 +TEST_BUILTINS_OBJS += test-path-utils.o
 +TEST_BUILTINS_OBJS += test-prio-queue.o
 +TEST_BUILTINS_OBJS += test-read-cache.o
 +TEST_BUILTINS_OBJS += test-ref-store.o
 +TEST_BUILTINS_OBJS += test-regex.o
 +TEST_BUILTINS_OBJS += test-revision-walking.o
 +TEST_BUILTINS_OBJS += test-run-command.o
 +TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 +TEST_BUILTINS_OBJS += test-sha1-array.o
 +TEST_BUILTINS_OBJS += test-sha1.o
 +TEST_BUILTINS_OBJS += test-sigchain.o
 +TEST_BUILTINS_OBJS += test-strcmp-offset.o
 +TEST_BUILTINS_OBJS += test-string-list.o
 +TEST_BUILTINS_OBJS += test-submodule-config.o
 +TEST_BUILTINS_OBJS += test-subprocess.o
 +TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
 +TEST_BUILTINS_OBJS += test-wildmatch.o
 +TEST_BUILTINS_OBJS += test-write-cache.o
 +
  TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
 -TEST_PROGRAMS_NEED_X += test-dump-split-index
  TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 -TEST_PROGRAMS_NEED_X += test-example-decorate
  TEST_PROGRAMS_NEED_X += test-fake-ssh
 -TEST_PROGRAMS_NEED_X += test-genrandom
 -TEST_PROGRAMS_NEED_X += test-hashmap
 -TEST_PROGRAMS_NEED_X += test-index-version
 -TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash
  TEST_PROGRAMS_NEED_X += test-line-buffer
 -TEST_PROGRAMS_NEED_X += test-match-trees
 -TEST_PROGRAMS_NEED_X += test-mergesort
 -TEST_PROGRAMS_NEED_X += test-mktemp
 -TEST_PROGRAMS_NEED_X += test-online-cpus
  TEST_PROGRAMS_NEED_X += test-parse-options
 -TEST_PROGRAMS_NEED_X += test-path-utils
 -TEST_PROGRAMS_NEED_X += test-prio-queue
 -TEST_PROGRAMS_NEED_X += test-read-cache
 -TEST_PROGRAMS_NEED_X += test-write-cache
 -TEST_PROGRAMS_NEED_X += test-ref-store
 -TEST_PROGRAMS_NEED_X += test-regex
 -TEST_PROGRAMS_NEED_X += test-revision-walking
 -TEST_PROGRAMS_NEED_X += test-run-command
 -TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 -TEST_PROGRAMS_NEED_X += test-sha1
 -TEST_PROGRAMS_NEED_X += test-sha1-array
 -TEST_PROGRAMS_NEED_X += test-sigchain
 -TEST_PROGRAMS_NEED_X += test-strcmp-offset
 -TEST_PROGRAMS_NEED_X += test-string-list
 -TEST_PROGRAMS_NEED_X += test-submodule-config
 -TEST_PROGRAMS_NEED_X += test-subprocess
 +TEST_PROGRAMS_NEED_X += test-pkt-line
  TEST_PROGRAMS_NEED_X += test-svn-fe
 -TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
 -TEST_PROGRAMS_NEED_X += test-wildmatch
 +TEST_PROGRAMS_NEED_X += test-tool
  
  TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))
  
@@@ -810,13 -779,11 +821,13 @@@ LIB_OBJS += branch.
  LIB_OBJS += bulk-checkin.o
  LIB_OBJS += bundle.o
  LIB_OBJS += cache-tree.o
 +LIB_OBJS += chdir-notify.o
  LIB_OBJS += checkout.o
  LIB_OBJS += color.o
  LIB_OBJS += column.o
  LIB_OBJS += combine-diff.o
  LIB_OBJS += commit.o
 +LIB_OBJS += commit-graph.o
  LIB_OBJS += compat/obstack.o
  LIB_OBJS += compat/terminal.o
  LIB_OBJS += config.o
@@@ -847,8 -814,7 +858,8 @@@ LIB_OBJS += ewah/bitmap.
  LIB_OBJS += ewah/ewah_bitmap.o
  LIB_OBJS += ewah/ewah_io.o
  LIB_OBJS += ewah/ewah_rlw.o
 -LIB_OBJS += exec_cmd.o
 +LIB_OBJS += exec-cmd.o
 +LIB_OBJS += fetch-object.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
  LIB_OBJS += fsmonitor.o
@@@ -870,15 -836,14 +881,15 @@@ LIB_OBJS += list-objects-filter-options
  LIB_OBJS += ll-merge.o
  LIB_OBJS += lockfile.o
  LIB_OBJS += log-tree.o
 +LIB_OBJS += ls-refs.o
  LIB_OBJS += mailinfo.o
  LIB_OBJS += mailmap.o
  LIB_OBJS += match-trees.o
 +LIB_OBJS += mem-pool.o
  LIB_OBJS += merge.o
  LIB_OBJS += merge-blobs.o
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += mergesort.o
 -LIB_OBJS += mru.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += notes.o
  LIB_OBJS += notes-cache.o
@@@ -919,7 -884,7 +930,7 @@@ LIB_OBJS += refs/packed-backend.
  LIB_OBJS += refs/ref-cache.o
  LIB_OBJS += ref-filter.o
  LIB_OBJS += remote.o
 -LIB_OBJS += replace_object.o
 +LIB_OBJS += replace-object.o
  LIB_OBJS += repository.o
  LIB_OBJS += rerere.o
  LIB_OBJS += resolve-undo.o
@@@ -927,13 -892,12 +938,13 @@@ LIB_OBJS += revision.
  LIB_OBJS += run-command.o
  LIB_OBJS += send-pack.o
  LIB_OBJS += sequencer.o
 +LIB_OBJS += serve.o
  LIB_OBJS += server-info.o
  LIB_OBJS += setup.o
  LIB_OBJS += sha1-array.o
  LIB_OBJS += sha1-lookup.o
 -LIB_OBJS += sha1_file.o
 -LIB_OBJS += sha1_name.o
 +LIB_OBJS += sha1-file.o
 +LIB_OBJS += sha1-name.o
  LIB_OBJS += shallow.o
  LIB_OBJS += sideband.o
  LIB_OBJS += sigchain.o
@@@ -956,7 -920,6 +967,7 @@@ LIB_OBJS += tree-diff.
  LIB_OBJS += tree.o
  LIB_OBJS += tree-walk.o
  LIB_OBJS += unpack-trees.o
 +LIB_OBJS += upload-pack.o
  LIB_OBJS += url.o
  LIB_OBJS += urlmatch.o
  LIB_OBJS += usage.o
@@@ -969,7 -932,7 +980,7 @@@ LIB_OBJS += walker.
  LIB_OBJS += wildmatch.o
  LIB_OBJS += worktree.o
  LIB_OBJS += wrapper.o
 -LIB_OBJS += write_or_die.o
 +LIB_OBJS += write-or-die.o
  LIB_OBJS += ws.o
  LIB_OBJS += wt-status.o
  LIB_OBJS += xdiff-interface.o
@@@ -996,7 -959,6 +1007,7 @@@ BUILTIN_OBJS += builtin/clone.
  BUILTIN_OBJS += builtin/column.o
  BUILTIN_OBJS += builtin/commit-tree.o
  BUILTIN_OBJS += builtin/commit.o
 +BUILTIN_OBJS += builtin/commit-graph.o
  BUILTIN_OBJS += builtin/config.o
  BUILTIN_OBJS += builtin/count-objects.o
  BUILTIN_OBJS += builtin/credential.o
@@@ -1062,7 -1024,6 +1073,7 @@@ BUILTIN_OBJS += builtin/rev-parse.
  BUILTIN_OBJS += builtin/revert.o
  BUILTIN_OBJS += builtin/rm.o
  BUILTIN_OBJS += builtin/send-pack.o
 +BUILTIN_OBJS += builtin/serve.o
  BUILTIN_OBJS += builtin/shortlog.o
  BUILTIN_OBJS += builtin/show-branch.o
  BUILTIN_OBJS += builtin/show-ref.o
@@@ -1076,7 -1037,6 +1087,7 @@@ BUILTIN_OBJS += builtin/update-index.
  BUILTIN_OBJS += builtin/update-ref.o
  BUILTIN_OBJS += builtin/update-server-info.o
  BUILTIN_OBJS += builtin/upload-archive.o
 +BUILTIN_OBJS += builtin/upload-pack.o
  BUILTIN_OBJS += builtin/var.o
  BUILTIN_OBJS += builtin/verify-commit.o
  BUILTIN_OBJS += builtin/verify-pack.o
@@@ -1098,7 -1058,7 +1109,7 @@@ include config.mak.unam
  -include config.mak
  
  ifdef DEVELOPER
- CFLAGS += $(DEVELOPER_CFLAGS)
+ include config.mak.dev
  endif
  
  comma := ,
@@@ -1217,18 -1177,13 +1228,18 @@@ ifdef NO_LIBGEN_
        COMPAT_OBJS += compat/basename.o
  endif
  
 -USE_LIBPCRE1 ?= $(USE_LIBPCRE)
 +USE_LIBPCRE2 ?= $(USE_LIBPCRE)
  
 -ifneq (,$(USE_LIBPCRE1))
 -      ifdef USE_LIBPCRE2
 -$(error Only set USE_LIBPCRE1 (or its alias USE_LIBPCRE) or USE_LIBPCRE2, not both!)
 +ifneq (,$(USE_LIBPCRE2))
 +      ifdef USE_LIBPCRE1
 +$(error Only set USE_LIBPCRE2 (or its alias USE_LIBPCRE) or USE_LIBPCRE1, not both!)
        endif
  
 +      BASIC_CFLAGS += -DUSE_LIBPCRE2
 +      EXTLIBS += -lpcre2-8
 +endif
 +
 +ifdef USE_LIBPCRE1
        BASIC_CFLAGS += -DUSE_LIBPCRE1
        EXTLIBS += -lpcre
  
@@@ -1237,6 -1192,11 +1248,6 @@@ ifdef NO_LIBPCRE1_JI
  endif
  endif
  
 -ifdef USE_LIBPCRE2
 -      BASIC_CFLAGS += -DUSE_LIBPCRE2
 -      EXTLIBS += -lpcre2-8
 -endif
 -
  ifdef LIBPCREDIR
        BASIC_CFLAGS += -I$(LIBPCREDIR)/include
        EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
@@@ -1566,9 -1526,7 +1577,9 @@@ els
        LIB_OBJS += sha1dc_git.o
  ifdef DC_SHA1_EXTERNAL
        ifdef DC_SHA1_SUBMODULE
 +              ifneq ($(DC_SHA1_SUBMODULE),auto)
  $(error Only set DC_SHA1_EXTERNAL or DC_SHA1_SUBMODULE, not both)
 +              endif
        endif
        BASIC_CFLAGS += -DDC_SHA1_EXTERNAL
        EXTLIBS += -lsha1detectcoll
@@@ -1596,6 -1554,9 +1607,6 @@@ ifdef SHA1_MAX_BLOCK_SIZ
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
  endif
 -ifdef NO_PERL_MAKEMAKER
 -      export NO_PERL_MAKEMAKER
 -endif
  ifdef NO_HSTRERROR
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
@@@ -1699,27 -1660,10 +1710,27 @@@ ifdef HAVE_BSD_SYSCT
        BASIC_CFLAGS += -DHAVE_BSD_SYSCTL
  endif
  
 +ifdef HAVE_BSD_KERN_PROC_SYSCTL
 +      BASIC_CFLAGS += -DHAVE_BSD_KERN_PROC_SYSCTL
 +endif
 +
  ifdef HAVE_GETDELIM
        BASIC_CFLAGS += -DHAVE_GETDELIM
  endif
  
 +ifneq ($(PROCFS_EXECUTABLE_PATH),)
 +      procfs_executable_path_SQ = $(subst ','\'',$(PROCFS_EXECUTABLE_PATH))
 +      BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"'
 +endif
 +
 +ifdef HAVE_NS_GET_EXECUTABLE_PATH
 +      BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH
 +endif
 +
 +ifdef HAVE_WPGMPTR
 +      BASIC_CFLAGS += -DHAVE_WPGMPTR
 +endif
 +
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
@@@ -1799,18 -1743,13 +1810,18 @@@ ETC_GITATTRIBUTES_SQ = $(subst ','\'',$
  DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
  bindir_SQ = $(subst ','\'',$(bindir))
  bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 +mandir_SQ = $(subst ','\'',$(mandir))
  mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
  infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
 +perllibdir_SQ = $(subst ','\'',$(perllibdir))
  localedir_SQ = $(subst ','\'',$(localedir))
 +localedir_relative_SQ = $(subst ','\'',$(localedir_relative))
  gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 +gitexecdir_relative_SQ = $(subst ','\'',$(gitexecdir_relative))
  template_dir_SQ = $(subst ','\'',$(template_dir))
  htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative))
  prefix_SQ = $(subst ','\'',$(prefix))
 +perllibdir_relative_SQ = $(subst ','\'',$(perllibdir_relative))
  gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
@@@ -1821,31 -1760,6 +1832,31 @@@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_
  DIFF_SQ = $(subst ','\'',$(DIFF))
  PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA))
  
 +# RUNTIME_PREFIX's resolution logic requires resource paths to be expressed
 +# relative to each other and share an installation path.
 +#
 +# This is a dependency in:
 +# - Git's binary RUNTIME_PREFIX logic in (see "exec_cmd.c").
 +# - The runtime prefix Perl header (see
 +#   "perl/header_templates/runtime_prefix.template.pl").
 +ifdef RUNTIME_PREFIX
 +
 +ifneq ($(filter /%,$(firstword $(gitexecdir_relative))),)
 +$(error RUNTIME_PREFIX requires a relative gitexecdir, not: $(gitexecdir))
 +endif
 +
 +ifneq ($(filter /%,$(firstword $(localedir_relative))),)
 +$(error RUNTIME_PREFIX requires a relative localedir, not: $(localedir))
 +endif
 +
 +ifndef NO_PERL
 +ifneq ($(filter /%,$(firstword $(perllibdir_relative))),)
 +$(error RUNTIME_PREFIX requires a relative perllibdir, not: $(perllibdir))
 +endif
 +endif
 +
 +endif
 +
  # We must filter out any object files from $(GITLIBS),
  # as it is typically used like:
  #
@@@ -1940,6 -1854,9 +1951,6 @@@ all:
  ifndef NO_TCLTK
        $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
        $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 -endif
 -ifndef NO_PERL
 -      $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
  endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
  
@@@ -2022,8 -1939,7 +2033,8 @@@ common-cmds.h: $(wildcard Documentation
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
 -      $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
 +      $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
 +      $(perllibdir_SQ)
  define cmd_munge_script
  $(RM) $@ $@+ && \
  sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@@ -2066,44 -1982,38 +2077,44 @@@ git.res: git.rc GIT-VERSION-FIL
  # This makes sure we depend on the NO_PERL setting itself.
  $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
  
 +# Used for substitution in Perl modules. Disabled when using RUNTIME_PREFIX
 +# since the locale directory is injected.
 +perl_localedir_SQ = $(localedir_SQ)
 +
  ifndef NO_PERL
 -$(SCRIPT_PERL_GEN): perl/perl.mak
 +PERL_HEADER_TEMPLATE = perl/header_templates/fixed_prefix.template.pl
 +PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ)
  
 -perl/perl.mak: perl/PM.stamp
 +PERL_DEFINES := $(PERL_PATH_SQ) $(PERLLIB_EXTRA_SQ) $(perllibdir_SQ)
 +PERL_DEFINES += $(RUNTIME_PREFIX)
  
 -perl/PM.stamp: FORCE
 -      @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
 -      $(PERL_PATH) -V >>$@+ && \
 -      { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
 -      $(RM) $@+
 +# Support Perl runtime prefix. In this mode, a different header is installed
 +# into Perl scripts.
 +ifdef RUNTIME_PREFIX
 +
 +PERL_HEADER_TEMPLATE = perl/header_templates/runtime_prefix.template.pl
 +
 +# Don't export a fixed $(localedir) path; it will be resolved by the Perl header
 +# at runtime.
 +perl_localedir_SQ =
 +
 +endif
  
 -perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
 -      $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 +PERL_DEFINES += $(gitexecdir) $(perllibdir) $(localedir)
  
 -PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
 -$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
 +$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
 -      INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
 -      INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
 -      INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
 -          -e '        h' \
 -          -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
 -          -e '        H' \
 -          -e '        x' \
 +          -e '        rGIT-PERL-HEADER' \
 +          -e '        G' \
            -e '}' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
  
 +PERL_DEFINES := $(subst $(space),:,$(PERL_DEFINES))
  GIT-PERL-DEFINES: FORCE
        @FLAGS='$(PERL_DEFINES)'; \
            if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
                echo "$$FLAGS" >$@; \
            fi
  
 +GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile
 +      $(QUIET_GEN)$(RM) $@ && \
 +      INSTLIBDIR='$(perllibdir_SQ)' && \
 +      INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
 +      INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
 +      sed -e 's=@@PATHSEP@@=$(pathsep)=g' \
 +          -e "s=@@INSTLIBDIR@@=$$INSTLIBDIR=g" \
 +          -e 's=@@PERLLIBDIR_REL@@=$(perllibdir_relative_SQ)=g' \
 +          -e 's=@@GITEXECDIR_REL@@=$(gitexecdir_relative_SQ)=g' \
 +          -e 's=@@LOCALEDIR_REL@@=$(localedir_relative_SQ)=g' \
 +          $< >$@+ && \
 +      mv $@+ $@
 +
 +.PHONY: perllibdir
 +perllibdir:
 +      @echo '$(perllibdir_SQ)'
  
  .PHONY: gitweb
  gitweb:
@@@ -2208,7 -2102,7 +2219,7 @@@ VCSSVN_OBJS += vcs-svn/fast_export.
  VCSSVN_OBJS += vcs-svn/svndiff.o
  VCSSVN_OBJS += vcs-svn/svndump.o
  
 -TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS))
 +TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
  OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
  $(OBJECTS): $(LIB_H)
  endif
  
 -exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX
 -exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
 +exec-cmd.sp exec-cmd.s exec-cmd.o: GIT-PREFIX
 +exec-cmd.sp exec-cmd.s exec-cmd.o: EXTRA_CPPFLAGS = \
        '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
 +      '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \
        '-DBINDIR="$(bindir_relative_SQ)"' \
        '-DPREFIX="$(prefix_SQ)"'
  
@@@ -2289,7 -2182,7 +2300,7 @@@ attr.sp attr.s attr.o: EXTRA_CPPFLAGS 
  
  gettext.sp gettext.s gettext.o: GIT-PREFIX
  gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
 -      -DGIT_LOCALE_PATH='"$(localedir_SQ)"'
 +      -DGIT_LOCALE_PATH='"$(localedir_relative_SQ)"'
  
  http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
@@@ -2350,15 -2243,13 +2361,15 @@@ $(VCSSVN_LIB): $(VCSSVN_OBJS
  
  export DEFAULT_EDITOR DEFAULT_PAGER
  
 -.PHONY: doc man html info pdf
 -doc:
 +.PHONY: doc man man-perl html info pdf
 +doc: man-perl
        $(MAKE) -C Documentation all
  
 -man:
 +man: man-perl
        $(MAKE) -C Documentation man
  
 +man-perl: perl/build/man/man3/Git.3pm
 +
  html:
        $(MAKE) -C Documentation html
  
@@@ -2434,29 -2325,6 +2445,29 @@@ endi
  po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
        $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
  
 +LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
 +LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
 +LIB_CPAN := $(wildcard perl/FromCPAN/*.pm perl/FromCPAN/*/*.pm)
 +LIB_CPAN_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_CPAN))
 +
 +ifndef NO_PERL
 +all:: $(LIB_PERL_GEN)
 +ifndef NO_PERL_CPAN_FALLBACKS
 +all:: $(LIB_CPAN_GEN)
 +endif
 +NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS))
 +endif
 +
 +perl/build/lib/%.pm: perl/%.pm
 +      $(QUIET_GEN)mkdir -p $(dir $@) && \
 +      sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \
 +          -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
 +      < $< > $@
 +
 +perl/build/man/man3/Git.3pm: perl/Git.pm
 +      $(QUIET_GEN)mkdir -p $(dir $@) && \
 +      pod2man $< $@
 +
  FIND_SOURCE_FILES = ( \
        git ls-files \
                '*.[hcS]' \
@@@ -2620,12 -2488,10 +2631,12 @@@ t/helper/test-svn-fe$X: $(VCSSVN_LIB
  
  .PRECIOUS: $(TEST_OBJS)
  
 +t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
 +
  t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
  
 -check-sha1:: t/helper/test-sha1$X
 +check-sha1:: t/helper/test-tool$X
        t/helper/test-sha1.sh
  
  SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
@@@ -2719,9 -2585,7 +2730,9 @@@ ifndef NO_GETTEX
        (cd '$(DESTDIR_SQ)$(localedir_SQ)' && umask 022 && $(TAR) xof -)
  endif
  ifndef NO_PERL
 -      $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perllibdir_SQ)'
 +      (cd perl/build/lib && $(TAR) cf - .) | \
 +      (cd '$(DESTDIR_SQ)$(perllibdir_SQ)' && umask 022 && $(TAR) xof -)
        $(MAKE) -C gitweb install
  endif
  ifndef NO_TCLTK
@@@ -2734,63 -2598,49 +2745,63 @@@ endi
  
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
 +      destdir_from_execdir_SQ=$$(echo '$(gitexecdir_relative_SQ)' | sed -e 's|[^/][^/]*|..|g') && \
        { test "$$bindir/" = "$$execdir/" || \
          for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
                $(RM) "$$execdir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
 -              ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
 -              cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "$$destdir_from_execdir_SQ/$(bindir_relative_SQ)/$$p" "$$execdir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
 +                ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
 +                cp "$$bindir/$$p" "$$execdir/$$p" || exit; } \
          done; \
        } && \
        for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
                $(RM) "$$bindir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)" && \
 -              ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
 -              ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
 -              cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "git$X" "$$bindir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)" && \
 +                ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
 +                ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
 +                cp "$$bindir/git$X" "$$bindir/$$p" || exit; } \
        done && \
        for p in $(BUILT_INS); do \
                $(RM) "$$execdir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)" && \
 -              ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
 -              ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 -              cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "$$destdir_from_execdir_SQ/$(bindir_relative_SQ)/git$X" "$$execdir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)" && \
 +                ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
 +                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 +                cp "$$execdir/git$X" "$$execdir/$$p" || exit; } \
        done && \
        remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
        for p in $$remote_curl_aliases; do \
                $(RM) "$$execdir/$$p" && \
 -              test -z "$(NO_INSTALL_HARDLINKS)" && \
 -              ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 -              ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 -              cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
 +              test -n "$(INSTALL_SYMLINKS)" && \
 +              ln -s "git-remote-http$X" "$$execdir/$$p" || \
 +              { test -z "$(NO_INSTALL_HARDLINKS)" && \
 +                ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 +                ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
 +                cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; } \
        done && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
  
 -.PHONY: install-gitweb install-doc install-man install-html install-info install-pdf
 +.PHONY: install-gitweb install-doc install-man install-man-perl install-html install-info install-pdf
  .PHONY: quick-install-doc quick-install-man quick-install-html
  install-gitweb:
        $(MAKE) -C gitweb install
  
 -install-doc:
 +install-doc: install-man-perl
        $(MAKE) -C Documentation install
  
 -install-man:
 +install-man: install-man-perl
        $(MAKE) -C Documentation install-man
  
 +install-man-perl: man-perl
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
 +      (cd perl/build/man/man3 && $(TAR) cf - .) | \
 +      (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
 +
  install-html:
        $(MAKE) -C Documentation install-html
  
@@@ -2825,21 -2675,6 +2836,21 @@@ dist: git-archive$(X) configur
                $(GIT_TARNAME)/configure \
                $(GIT_TARNAME)/version \
                $(GIT_TARNAME)/git-gui/version
 +ifdef DC_SHA1_SUBMODULE
 +      @mkdir -p $(GIT_TARNAME)/sha1collisiondetection/lib
 +      @cp sha1collisiondetection/LICENSE.txt \
 +              $(GIT_TARNAME)/sha1collisiondetection/
 +      @cp sha1collisiondetection/LICENSE.txt \
 +              $(GIT_TARNAME)/sha1collisiondetection/
 +      @cp sha1collisiondetection/lib/sha1.[ch] \
 +              $(GIT_TARNAME)/sha1collisiondetection/lib/
 +      @cp sha1collisiondetection/lib/ubc_check.[ch] \
 +              $(GIT_TARNAME)/sha1collisiondetection/lib/
 +      $(TAR) rf $(GIT_TARNAME).tar \
 +              $(GIT_TARNAME)/sha1collisiondetection/LICENSE.txt \
 +              $(GIT_TARNAME)/sha1collisiondetection/lib/sha1.[ch] \
 +              $(GIT_TARNAME)/sha1collisiondetection/lib/ubc_check.[ch]
 +endif
        @$(RM) -r $(GIT_TARNAME)
        gzip -f -9 $(GIT_TARNAME).tar
  
@@@ -2889,7 -2724,7 +2900,7 @@@ clean: profile-clean coverage-clea
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
 -      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
 +      $(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
        $(MAKE) -C Documentation/ clean
  ifndef NO_PERL
        $(MAKE) -C gitweb clean
 -      $(MAKE) -C perl clean
 +      $(RM) -r perl/build/
  endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
@@@ -2907,7 -2742,7 +2918,7 @@@ ifndef NO_TCLT
  endif
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS
        $(RM) GIT-USER-AGENT GIT-PREFIX
 -      $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PYTHON-VARS
 +      $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
  
  .PHONY: all install profile-clean clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --combined connect.c
index 54971166ac4ba6646ea52051f8131dc16ea2e0ae,49eca4646220dc92aca9f58b9c7fb309a243e6be..b34dc80451fb375b112c1338a0aa3326a0a168ad
+++ b/connect.c
  #include "sha1-array.h"
  #include "transport.h"
  #include "strbuf.h"
 +#include "version.h"
  #include "protocol.h"
  
 -static char *server_capabilities;
 +static char *server_capabilities_v1;
 +static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT;
  static const char *parse_feature_value(const char *, const char *, int *);
  
  static int check_ref(const char *name, unsigned int flags)
@@@ -48,14 -46,8 +48,14 @@@ int check_ref_type(const struct ref *re
        return check_ref(ref->name, flags);
  }
  
- static void die_initial_contact(int unexpected)
+ static NORETURN void die_initial_contact(int unexpected)
  {
 +      /*
 +       * A hang-up after seeing some response from the other end
 +       * means that it is unexpected, as we know the other end is
 +       * willing to talk to us.  A hang-up before seeing any
 +       * response does not necessarily mean an ACL problem, though.
 +       */
        if (unexpected)
                die(_("The remote end hung up upon initial contact"));
        else
                      "and the repository exists."));
  }
  
 +/* Checks if the server supports the capability 'c' */
 +int server_supports_v2(const char *c, int die_on_error)
 +{
 +      int i;
 +
 +      for (i = 0; i < server_capabilities_v2.argc; i++) {
 +              const char *out;
 +              if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
 +                  (!*out || *out == '='))
 +                      return 1;
 +      }
 +
 +      if (die_on_error)
 +              die("server doesn't support '%s'", c);
 +
 +      return 0;
 +}
 +
 +int server_supports_feature(const char *c, const char *feature,
 +                          int die_on_error)
 +{
 +      int i;
 +
 +      for (i = 0; i < server_capabilities_v2.argc; i++) {
 +              const char *out;
 +              if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
 +                  (!*out || *(out++) == '=')) {
 +                      if (parse_feature_request(out, feature))
 +                              return 1;
 +                      else
 +                              break;
 +              }
 +      }
 +
 +      if (die_on_error)
 +              die("server doesn't support feature '%s'", feature);
 +
 +      return 0;
 +}
 +
 +static void process_capabilities_v2(struct packet_reader *reader)
 +{
 +      while (packet_reader_read(reader) == PACKET_READ_NORMAL)
 +              argv_array_push(&server_capabilities_v2, reader->line);
 +
 +      if (reader->status != PACKET_READ_FLUSH)
 +              die("expected flush after capabilities");
 +}
 +
 +enum protocol_version discover_version(struct packet_reader *reader)
 +{
 +      enum protocol_version version = protocol_unknown_version;
 +
 +      /*
 +       * Peek the first line of the server's response to
 +       * determine the protocol version the server is speaking.
 +       */
 +      switch (packet_reader_peek(reader)) {
 +      case PACKET_READ_EOF:
 +              die_initial_contact(0);
 +      case PACKET_READ_FLUSH:
 +      case PACKET_READ_DELIM:
 +              version = protocol_v0;
 +              break;
 +      case PACKET_READ_NORMAL:
 +              version = determine_protocol_version_client(reader->line);
 +              break;
 +      }
 +
 +      switch (version) {
 +      case protocol_v2:
 +              process_capabilities_v2(reader);
 +              break;
 +      case protocol_v1:
 +              /* Read the peeked version line */
 +              packet_reader_read(reader);
 +              break;
 +      case protocol_v0:
 +              break;
 +      case protocol_unknown_version:
 +              BUG("unknown protocol version");
 +      }
 +
 +      return version;
 +}
 +
  static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
  {
        char *sym, *target;
@@@ -179,7 -85,7 +179,7 @@@ reject
  static void annotate_refs_with_symref_info(struct ref *ref)
  {
        struct string_list symref = STRING_LIST_INIT_DUP;
 -      const char *feature_list = server_capabilities;
 +      const char *feature_list = server_capabilities_v1;
  
        while (feature_list) {
                int len;
        string_list_clear(&symref, 0);
  }
  
 -/*
 - * Read one line of a server's ref advertisement into packet_buffer.
 - */
 -static int read_remote_ref(int in, char **src_buf, size_t *src_len,
 -                         int *responded)
 +static void process_capabilities(const char *line, int *len)
  {
 -      int len = packet_read(in, src_buf, src_len,
 -                            packet_buffer, sizeof(packet_buffer),
 -                            PACKET_READ_GENTLE_ON_EOF |
 -                            PACKET_READ_CHOMP_NEWLINE);
 -      const char *arg;
 -      if (len < 0)
 -              die_initial_contact(*responded);
 -      if (len > 4 && skip_prefix(packet_buffer, "ERR ", &arg))
 -              die("remote error: %s", arg);
 -
 -      *responded = 1;
 -
 -      return len;
 -}
 -
 -#define EXPECTING_PROTOCOL_VERSION 0
 -#define EXPECTING_FIRST_REF 1
 -#define EXPECTING_REF 2
 -#define EXPECTING_SHALLOW 3
 -
 -/* Returns 1 if packet_buffer is a protocol version pkt-line, 0 otherwise. */
 -static int process_protocol_version(void)
 -{
 -      switch (determine_protocol_version_client(packet_buffer)) {
 -      case protocol_v1:
 -              return 1;
 -      case protocol_v0:
 -              return 0;
 -      default:
 -              die("server is speaking an unknown protocol");
 -      }
 -}
 -
 -static void process_capabilities(int *len)
 -{
 -      int nul_location = strlen(packet_buffer);
 +      int nul_location = strlen(line);
        if (nul_location == *len)
                return;
 -      server_capabilities = xstrdup(packet_buffer + nul_location + 1);
 +      server_capabilities_v1 = xstrdup(line + nul_location + 1);
        *len = nul_location;
  }
  
 -static int process_dummy_ref(void)
 +static int process_dummy_ref(const char *line)
  {
        struct object_id oid;
        const char *name;
  
 -      if (parse_oid_hex(packet_buffer, &oid, &name))
 +      if (parse_oid_hex(line, &oid, &name))
                return 0;
        if (*name != ' ')
                return 0;
        return !oidcmp(&null_oid, &oid) && !strcmp(name, "capabilities^{}");
  }
  
 -static void check_no_capabilities(int len)
 +static void check_no_capabilities(const char *line, int len)
  {
 -      if (strlen(packet_buffer) != len)
 +      if (strlen(line) != len)
                warning("Ignoring capabilities after first line '%s'",
 -                      packet_buffer + strlen(packet_buffer));
 +                      line + strlen(line));
  }
  
 -static int process_ref(int len, struct ref ***list, unsigned int flags,
 -                     struct oid_array *extra_have)
 +static int process_ref(const char *line, int len, struct ref ***list,
 +                     unsigned int flags, struct oid_array *extra_have)
  {
        struct object_id old_oid;
        const char *name;
  
 -      if (parse_oid_hex(packet_buffer, &old_oid, &name))
 +      if (parse_oid_hex(line, &old_oid, &name))
                return 0;
        if (*name != ' ')
                return 0;
                **list = ref;
                *list = &ref->next;
        }
 -      check_no_capabilities(len);
 +      check_no_capabilities(line, len);
        return 1;
  }
  
 -static int process_shallow(int len, struct oid_array *shallow_points)
 +static int process_shallow(const char *line, int len,
 +                         struct oid_array *shallow_points)
  {
        const char *arg;
        struct object_id old_oid;
  
 -      if (!skip_prefix(packet_buffer, "shallow ", &arg))
 +      if (!skip_prefix(line, "shallow ", &arg))
                return 0;
  
        if (get_oid_hex(arg, &old_oid))
        if (!shallow_points)
                die("repository on the other end cannot be shallow");
        oid_array_append(shallow_points, &old_oid);
 -      check_no_capabilities(len);
 +      check_no_capabilities(line, len);
        return 1;
  }
  
 +enum get_remote_heads_state {
 +      EXPECTING_FIRST_REF = 0,
 +      EXPECTING_REF,
 +      EXPECTING_SHALLOW,
 +      EXPECTING_DONE,
 +};
 +
  /*
   * Read all the refs from the other end
   */
 -struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 +struct ref **get_remote_heads(struct packet_reader *reader,
                              struct ref **list, unsigned int flags,
                              struct oid_array *extra_have,
                              struct oid_array *shallow_points)
  {
        struct ref **orig_list = list;
 -
 -      /*
 -       * A hang-up after seeing some response from the other end
 -       * means that it is unexpected, as we know the other end is
 -       * willing to talk to us.  A hang-up before seeing any
 -       * response does not necessarily mean an ACL problem, though.
 -       */
 -      int responded = 0;
 -      int len;
 -      int state = EXPECTING_PROTOCOL_VERSION;
 +      int len = 0;
 +      enum get_remote_heads_state state = EXPECTING_FIRST_REF;
 +      const char *arg;
  
        *list = NULL;
  
 -      while ((len = read_remote_ref(in, &src_buf, &src_len, &responded))) {
 +      while (state != EXPECTING_DONE) {
 +              switch (packet_reader_read(reader)) {
 +              case PACKET_READ_EOF:
 +                      die_initial_contact(1);
 +              case PACKET_READ_NORMAL:
 +                      len = reader->pktlen;
 +                      if (len > 4 && skip_prefix(reader->line, "ERR ", &arg))
 +                              die("remote error: %s", arg);
 +                      break;
 +              case PACKET_READ_FLUSH:
 +                      state = EXPECTING_DONE;
 +                      break;
 +              case PACKET_READ_DELIM:
 +                      die("invalid packet");
 +              }
 +
                switch (state) {
 -              case EXPECTING_PROTOCOL_VERSION:
 -                      if (process_protocol_version()) {
 -                              state = EXPECTING_FIRST_REF;
 -                              break;
 -                      }
 -                      state = EXPECTING_FIRST_REF;
 -                      /* fallthrough */
                case EXPECTING_FIRST_REF:
 -                      process_capabilities(&len);
 -                      if (process_dummy_ref()) {
 +                      process_capabilities(reader->line, &len);
 +                      if (process_dummy_ref(reader->line)) {
                                state = EXPECTING_SHALLOW;
                                break;
                        }
                        state = EXPECTING_REF;
                        /* fallthrough */
                case EXPECTING_REF:
 -                      if (process_ref(len, &list, flags, extra_have))
 +                      if (process_ref(reader->line, len, &list, flags, extra_have))
                                break;
                        state = EXPECTING_SHALLOW;
                        /* fallthrough */
                case EXPECTING_SHALLOW:
 -                      if (process_shallow(len, shallow_points))
 +                      if (process_shallow(reader->line, len, shallow_points))
                                break;
 -                      die("protocol error: unexpected '%s'", packet_buffer);
 -              default:
 -                      die("unexpected state %d", state);
 +                      die("protocol error: unexpected '%s'", reader->line);
 +              case EXPECTING_DONE:
 +                      break;
                }
        }
  
        return list;
  }
  
 +/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
 +static int process_ref_v2(const char *line, struct ref ***list)
 +{
 +      int ret = 1;
 +      int i = 0;
 +      struct object_id old_oid;
 +      struct ref *ref;
 +      struct string_list line_sections = STRING_LIST_INIT_DUP;
 +      const char *end;
 +
 +      /*
 +       * Ref lines have a number of fields which are space deliminated.  The
 +       * first field is the OID of the ref.  The second field is the ref
 +       * name.  Subsequent fields (symref-target and peeled) are optional and
 +       * don't have a particular order.
 +       */
 +      if (string_list_split(&line_sections, line, ' ', -1) < 2) {
 +              ret = 0;
 +              goto out;
 +      }
 +
 +      if (parse_oid_hex(line_sections.items[i++].string, &old_oid, &end) ||
 +          *end) {
 +              ret = 0;
 +              goto out;
 +      }
 +
 +      ref = alloc_ref(line_sections.items[i++].string);
 +
 +      oidcpy(&ref->old_oid, &old_oid);
 +      **list = ref;
 +      *list = &ref->next;
 +
 +      for (; i < line_sections.nr; i++) {
 +              const char *arg = line_sections.items[i].string;
 +              if (skip_prefix(arg, "symref-target:", &arg))
 +                      ref->symref = xstrdup(arg);
 +
 +              if (skip_prefix(arg, "peeled:", &arg)) {
 +                      struct object_id peeled_oid;
 +                      char *peeled_name;
 +                      struct ref *peeled;
 +                      if (parse_oid_hex(arg, &peeled_oid, &end) || *end) {
 +                              ret = 0;
 +                              goto out;
 +                      }
 +
 +                      peeled_name = xstrfmt("%s^{}", ref->name);
 +                      peeled = alloc_ref(peeled_name);
 +
 +                      oidcpy(&peeled->old_oid, &peeled_oid);
 +                      **list = peeled;
 +                      *list = &peeled->next;
 +
 +                      free(peeled_name);
 +              }
 +      }
 +
 +out:
 +      string_list_clear(&line_sections, 0);
 +      return ret;
 +}
 +
 +struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
 +                           struct ref **list, int for_push,
 +                           const struct argv_array *ref_prefixes)
 +{
 +      int i;
 +      *list = NULL;
 +
 +      if (server_supports_v2("ls-refs", 1))
 +              packet_write_fmt(fd_out, "command=ls-refs\n");
 +
 +      if (server_supports_v2("agent", 0))
 +              packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
 +
 +      packet_delim(fd_out);
 +      /* When pushing we don't want to request the peeled tags */
 +      if (!for_push)
 +              packet_write_fmt(fd_out, "peel\n");
 +      packet_write_fmt(fd_out, "symrefs\n");
 +      for (i = 0; ref_prefixes && i < ref_prefixes->argc; i++) {
 +              packet_write_fmt(fd_out, "ref-prefix %s\n",
 +                               ref_prefixes->argv[i]);
 +      }
 +      packet_flush(fd_out);
 +
 +      /* Process response from server */
 +      while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
 +              if (!process_ref_v2(reader->line, &list))
 +                      die("invalid ls-refs response: %s", reader->line);
 +      }
 +
 +      if (reader->status != PACKET_READ_FLUSH)
 +              die("expected flush after ref listing");
 +
 +      return list;
 +}
 +
  static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
  {
        int len;
@@@ -486,7 -323,7 +486,7 @@@ int parse_feature_request(const char *f
  
  const char *server_feature_value(const char *feature, int *len)
  {
 -      return parse_feature_value(server_capabilities, feature, len);
 +      return parse_feature_value(server_capabilities_v1, feature, len);
  }
  
  int server_supports(const char *feature)
@@@ -1035,7 -872,6 +1035,7 @@@ static enum ssh_variant determine_ssh_v
   */
  static struct child_process *git_connect_git(int fd[2], char *hostandport,
                                             const char *path, const char *prog,
 +                                           enum protocol_version version,
                                             int flags)
  {
        struct child_process *conn;
                    target_host, 0);
  
        /* If using a new version put that stuff here after a second null byte */
 -      if (get_protocol_version_config() > 0) {
 +      if (version > 0) {
                strbuf_addch(&request, '\0');
                strbuf_addf(&request, "version=%d%c",
 -                          get_protocol_version_config(), '\0');
 +                          version, '\0');
        }
  
        packet_write(fd[1], request.buf, request.len);
   */
  static void push_ssh_options(struct argv_array *args, struct argv_array *env,
                             enum ssh_variant variant, const char *port,
 -                           int flags)
 +                           enum protocol_version version, int flags)
  {
        if (variant == VARIANT_SSH &&
 -          get_protocol_version_config() > 0) {
 +          version > 0) {
                argv_array_push(args, "-o");
                argv_array_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT);
                argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
 -                               get_protocol_version_config());
 +                               version);
        }
  
        if (flags & CONNECT_IPV4) {
  
  /* Prepare a child_process for use by Git's SSH-tunneled transport. */
  static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
 -                        const char *port, int flags)
 +                        const char *port, enum protocol_version version,
 +                        int flags)
  {
        const char *ssh;
        enum ssh_variant variant;
                argv_array_push(&detect.args, ssh);
                argv_array_push(&detect.args, "-G");
                push_ssh_options(&detect.args, &detect.env_array,
 -                               VARIANT_SSH, port, flags);
 +                               VARIANT_SSH, port, version, flags);
                argv_array_push(&detect.args, ssh_host);
  
                variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH;
        }
  
        argv_array_push(&conn->args, ssh);
 -      push_ssh_options(&conn->args, &conn->env_array, variant, port, flags);
 +      push_ssh_options(&conn->args, &conn->env_array, variant, port, version, flags);
        argv_array_push(&conn->args, ssh_host);
  }
  
@@@ -1216,15 -1051,6 +1216,15 @@@ struct child_process *git_connect(int f
        char *hostandport, *path;
        struct child_process *conn;
        enum protocol protocol;
 +      enum protocol_version version = get_protocol_version_config();
 +
 +      /*
 +       * NEEDSWORK: If we are trying to use protocol v2 and we are planning
 +       * to perform a push, then fallback to v0 since the client doesn't know
 +       * how to push yet using v2.
 +       */
 +      if (version == protocol_v2 && !strcmp("git-receive-pack", prog))
 +              version = protocol_v0;
  
        /* Without this we cannot rely on waitpid() to tell
         * what happened to our children.
                printf("Diag: path=%s\n", path ? path : "NULL");
                conn = NULL;
        } else if (protocol == PROTO_GIT) {
 -              conn = git_connect_git(fd, hostandport, path, prog, flags);
 +              conn = git_connect_git(fd, hostandport, path, prog, version, flags);
        } else {
                struct strbuf cmd = STRBUF_INIT;
                const char *const *var;
                                strbuf_release(&cmd);
                                return NULL;
                        }
 -                      fill_ssh_args(conn, ssh_host, port, flags);
 +                      fill_ssh_args(conn, ssh_host, port, version, flags);
                } else {
                        transport_check_allowed("file");
 -                      if (get_protocol_version_config() > 0) {
 +                      if (version > 0) {
                                argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
 -                                               get_protocol_version_config());
 +                                               version);
                        }
                }
                argv_array_push(&conn->args, cmd.buf);