Merge branch 'sp/smart-http'
authorJunio C Hamano <gitster@pobox.com>
Sat, 21 Nov 2009 07:51:23 +0000 (23:51 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 21 Nov 2009 07:51:23 +0000 (23:51 -0800)
* sp/smart-http: (37 commits)
http-backend: Let gcc check the format of more printf-type functions.
http-backend: Fix access beyond end of string.
http-backend: Fix bad treatment of uintmax_t in Content-Length
t5551-http-fetch: Work around broken Accept header in libcurl
t5551-http-fetch: Work around some libcurl versions
http-backend: Protect GIT_PROJECT_ROOT from /../ requests
Git-aware CGI to provide dumb HTTP transport
http-backend: Test configuration options
http-backend: Use http.getanyfile to disable dumb HTTP serving
test smart http fetch and push
http tests: use /dumb/ URL prefix
set httpd port before sourcing lib-httpd
t5540-http-push: remove redundant fetches
Smart HTTP fetch: gzip requests
Smart fetch over HTTP: client side
Smart push over HTTP: client side
Discover refs via smart HTTP server when available
http-backend: more explict LocationMatch
http-backend: add example for gitweb on same URL
http-backend: use mod_alias instead of mod_rewrite
...

Conflicts:
.gitignore
remote-curl.c

13 files changed:
1  2 
.gitignore
Documentation/config.txt
Makefile
builtin-fetch.c
builtin-receive-pack.c
builtin-send-pack.c
cache.h
commit.c
commit.h
http-push.c
remote-curl.c
transport.c
upload-pack.c
diff --combined .gitignore
index 338c6d888123f326ce7b82a4c9c0459d618bef3d,353d22fc9a802deffbf8d03f13a94fac5627c703..f63992a6cca190f1b226f746dfc815e7399f47b5
 -GIT-BUILD-OPTIONS
 -GIT-CFLAGS
 -GIT-GUI-VARS
 -GIT-VERSION-FILE
 -git
 -git-add
 -git-add--interactive
 -git-am
 -git-annotate
 -git-apply
 -git-archimport
 -git-archive
 -git-bisect
 -git-bisect--helper
 -git-blame
 -git-branch
 -git-bundle
 -git-cat-file
 -git-check-attr
 -git-check-ref-format
 -git-checkout
 -git-checkout-index
 -git-cherry
 -git-cherry-pick
 -git-clean
 -git-clone
 -git-commit
 -git-commit-tree
 -git-config
 -git-count-objects
 -git-cvsexportcommit
 -git-cvsimport
 -git-cvsserver
 -git-daemon
 -git-diff
 -git-diff-files
 -git-diff-index
 -git-diff-tree
 -git-difftool
 -git-difftool--helper
 -git-describe
 -git-fast-export
 -git-fast-import
 -git-fetch
 -git-fetch--tool
 -git-fetch-pack
 -git-filter-branch
 -git-fmt-merge-msg
 -git-for-each-ref
 -git-format-patch
 -git-fsck
 -git-fsck-objects
 -git-gc
 -git-get-tar-commit-id
 -git-grep
 -git-hash-object
 -git-help
 -git-http-backend
 -git-http-fetch
 -git-http-push
 -git-imap-send
 -git-index-pack
 -git-init
 -git-init-db
 -git-instaweb
 -git-log
 -git-lost-found
 -git-ls-files
 -git-ls-remote
 -git-ls-tree
 -git-mailinfo
 -git-mailsplit
 -git-merge
 -git-merge-base
 -git-merge-index
 -git-merge-file
 -git-merge-tree
 -git-merge-octopus
 -git-merge-one-file
 -git-merge-ours
 -git-merge-recursive
 -git-merge-resolve
 -git-merge-subtree
 -git-mergetool
 -git-mergetool--lib
 -git-mktag
 -git-mktree
 -git-name-rev
 -git-mv
 -git-pack-redundant
 -git-pack-objects
 -git-pack-refs
 -git-parse-remote
 -git-patch-id
 -git-peek-remote
 -git-prune
 -git-prune-packed
 -git-pull
 -git-push
 -git-quiltimport
 -git-read-tree
 -git-rebase
 -git-rebase--interactive
 -git-receive-pack
 -git-reflog
 -git-relink
 -git-remote
 -git-remote-curl
 -git-repack
 -git-replace
 -git-repo-config
 -git-request-pull
 -git-rerere
 -git-reset
 -git-rev-list
 -git-rev-parse
 -git-revert
 -git-rm
 -git-send-email
 -git-send-pack
 -git-sh-setup
 -git-shell
 -git-shortlog
 -git-show
 -git-show-branch
 -git-show-index
 -git-show-ref
 -git-stage
 -git-stash
 -git-status
 -git-stripspace
 -git-submodule
 -git-svn
 -git-symbolic-ref
 -git-tag
 -git-tar-tree
 -git-unpack-file
 -git-unpack-objects
 -git-update-index
 -git-update-ref
 -git-update-server-info
 -git-upload-archive
 -git-upload-pack
 -git-var
 -git-verify-pack
 -git-verify-tag
 -git-web--browse
 -git-whatchanged
 -git-write-tree
 -git-core-*/?*
 -gitk-wish
 -gitweb/gitweb.cgi
 -test-chmtime
 -test-ctype
 -test-date
 -test-delta
 -test-dump-cache-tree
 -test-genrandom
 -test-match-trees
 -test-parse-options
 -test-path-utils
 -test-sha1
 -test-sigchain
 -common-cmds.h
 +/GIT-BUILD-OPTIONS
 +/GIT-CFLAGS
 +/GIT-GUI-VARS
 +/GIT-VERSION-FILE
 +/git
 +/git-add
 +/git-add--interactive
 +/git-am
 +/git-annotate
 +/git-apply
 +/git-archimport
 +/git-archive
 +/git-bisect
 +/git-bisect--helper
 +/git-blame
 +/git-branch
 +/git-bundle
 +/git-cat-file
 +/git-check-attr
 +/git-check-ref-format
 +/git-checkout
 +/git-checkout-index
 +/git-cherry
 +/git-cherry-pick
 +/git-clean
 +/git-clone
 +/git-commit
 +/git-commit-tree
 +/git-config
 +/git-count-objects
 +/git-cvsexportcommit
 +/git-cvsimport
 +/git-cvsserver
 +/git-daemon
 +/git-diff
 +/git-diff-files
 +/git-diff-index
 +/git-diff-tree
 +/git-difftool
 +/git-difftool--helper
 +/git-describe
 +/git-fast-export
 +/git-fast-import
 +/git-fetch
 +/git-fetch--tool
 +/git-fetch-pack
 +/git-filter-branch
 +/git-fmt-merge-msg
 +/git-for-each-ref
 +/git-format-patch
 +/git-fsck
 +/git-fsck-objects
 +/git-gc
 +/git-get-tar-commit-id
 +/git-grep
 +/git-hash-object
 +/git-help
++/git-http-backend
 +/git-http-fetch
 +/git-http-push
 +/git-imap-send
 +/git-index-pack
 +/git-init
 +/git-init-db
 +/git-instaweb
 +/git-log
 +/git-lost-found
 +/git-ls-files
 +/git-ls-remote
 +/git-ls-tree
 +/git-mailinfo
 +/git-mailsplit
 +/git-merge
 +/git-merge-base
 +/git-merge-index
 +/git-merge-file
 +/git-merge-tree
 +/git-merge-octopus
 +/git-merge-one-file
 +/git-merge-ours
 +/git-merge-recursive
 +/git-merge-resolve
 +/git-merge-subtree
 +/git-mergetool
 +/git-mergetool--lib
 +/git-mktag
 +/git-mktree
 +/git-name-rev
 +/git-mv
 +/git-pack-redundant
 +/git-pack-objects
 +/git-pack-refs
 +/git-parse-remote
 +/git-patch-id
 +/git-peek-remote
 +/git-prune
 +/git-prune-packed
 +/git-pull
 +/git-push
 +/git-quiltimport
 +/git-read-tree
 +/git-rebase
 +/git-rebase--interactive
 +/git-receive-pack
 +/git-reflog
 +/git-relink
 +/git-remote
 +/git-remote-curl
 +/git-repack
 +/git-replace
 +/git-repo-config
 +/git-request-pull
 +/git-rerere
 +/git-reset
 +/git-rev-list
 +/git-rev-parse
 +/git-revert
 +/git-rm
 +/git-send-email
 +/git-send-pack
 +/git-sh-setup
 +/git-shell
 +/git-shortlog
 +/git-show
 +/git-show-branch
 +/git-show-index
 +/git-show-ref
 +/git-stage
 +/git-stash
 +/git-status
 +/git-stripspace
 +/git-submodule
 +/git-svn
 +/git-symbolic-ref
 +/git-tag
 +/git-tar-tree
 +/git-unpack-file
 +/git-unpack-objects
 +/git-update-index
 +/git-update-ref
 +/git-update-server-info
 +/git-upload-archive
 +/git-upload-pack
 +/git-var
 +/git-verify-pack
 +/git-verify-tag
 +/git-web--browse
 +/git-whatchanged
 +/git-write-tree
 +/git-core-*/?*
 +/gitk-git/gitk-wish
 +/gitweb/gitweb.cgi
 +/test-chmtime
 +/test-ctype
 +/test-date
 +/test-delta
 +/test-dump-cache-tree
 +/test-genrandom
 +/test-match-trees
 +/test-parse-options
 +/test-path-utils
 +/test-sha1
 +/test-sigchain
 +/common-cmds.h
  *.tar.gz
  *.dsc
  *.deb
 -git.spec
 +/git.spec
  *.exe
  *.[aos]
  *.py[co]
 -config.mak
 -autom4te.cache
 -config.cache
 -config.log
 -config.status
 -config.mak.autogen
 -config.mak.append
 -configure
 -tags
 -TAGS
 -cscope*
 +*+
 +/config.mak
 +/autom4te.cache
 +/config.cache
 +/config.log
 +/config.status
 +/config.mak.autogen
 +/config.mak.append
 +/configure
 +/tags
 +/TAGS
 +/cscope*
  *.obj
  *.lib
  *.sln
  *.user
  *.idb
  *.pdb
 -Debug/
 -Release/
 +/Debug/
 +/Release/
diff --combined Documentation/config.txt
index c9b8db5cf701fa39fc518dd196c4acfb57cac0c4,7130d0723a091f43e04826b99c56895459fc7b42..1ff21938e7cc2924f8637649c7378bacb82da26d
@@@ -387,7 -387,9 +387,7 @@@ core.editor:
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
        variable when it is set, and the environment variable
 -      `GIT_EDITOR` is not set.  The order of preference is
 -      `GIT_EDITOR` environment, `core.editor`, `VISUAL` and
 -      `EDITOR` environment variables and then finally `vi`.
 +      `GIT_EDITOR` is not set.  See linkgit:git-var[1].
  
  core.pager::
        The command that git will use to paginate output.  Can
@@@ -414,17 -416,13 +414,17 @@@ core.whitespace:
        consider them as errors.  You can prefix `-` to disable
        any of them (e.g. `-trailing-space`):
  +
 -* `trailing-space` treats trailing whitespaces at the end of the line
 +* `blank-at-eol` treats trailing whitespaces at the end of the line
    as an error (enabled by default).
  * `space-before-tab` treats a space character that appears immediately
    before a tab character in the initial indent part of the line as an
    error (enabled by default).
  * `indent-with-non-tab` treats a line that is indented with 8 or more
    space characters as an error (not enabled by default).
 +* `blank-at-eof` treats blank lines added at the end of file as an error
 +  (enabled by default).
 +* `trailing-space` is a short-hand to cover both `blank-at-eol` and
 +  `blank-at-eof`.
  * `cr-at-eol` treats a carriage-return at the end of line as
    part of the line terminator, i.e. with it, `trailing-space`
    does not trigger if the character before such a carriage-return
@@@ -1091,6 -1089,14 +1091,14 @@@ http.maxRequests:
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
  
+ http.postBuffer::
+       Maximum size in bytes of the buffer used by smart HTTP
+       transports when POSTing data to the remote system.
+       For requests larger than this buffer size, HTTP/1.1 and
+       Transfer-Encoding: chunked is used to avoid creating a
+       massive pack file locally.  Default is 1 MiB, which is
+       sufficient for most requests.
  http.lowSpeedLimit, http.lowSpeedTime::
        If the HTTP transfer speed is less than 'http.lowSpeedLimit'
        for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
@@@ -1322,11 -1328,6 +1330,11 @@@ rebase.stat:
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
  
 +receive.autogc::
 +      By default, git-receive-pack will run "git-gc --auto" after
 +      receiving data from git-push and updating refs.  You can stop
 +      it by setting this variable to false.
 +
  receive.fsckObjects::
        If it is set to true, git-receive-pack will check all received
        objects. It will abort in the case of a malformed object or a
@@@ -1358,14 -1359,10 +1366,14 @@@ receive.denyCurrentBranch:
  
  receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
 -      not a fast forward. Use this to prevent such an update via a push,
 +      not a fast-forward. Use this to prevent such an update via a push,
        even if that push is forced. This configuration variable is
        set when initializing a shared repository.
  
 +receive.updateserverinfo::
 +      If set to true, git-receive-pack will run git-update-server-info
 +      after receiving data from git-push and updating refs.
 +
  remote.<name>.url::
        The URL of a remote repository.  See linkgit:git-fetch[1] or
        linkgit:git-push[1].
diff --combined Makefile
index f8906618e89bfa0e06fccea8280bae2a4e53d84f,271c2901717b700141234f23d5444b59ac95a3f7..da418c39c9a6f1c77cb47e1d051fd99d6adc9bcc
+++ b/Makefile
@@@ -159,10 -159,6 +159,10 @@@ all:
  # Define ASCIIDOC_NO_ROFF if your DocBook XSL escapes raw roff directives
  # (versions 1.72 and later and 1.68.1 and earlier).
  #
 +# Define GNU_ROFF if your target system uses GNU groff.  This forces
 +# apostrophes to be ASCII so that cut&pasting examples to the shell
 +# will work.
 +#
  # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
  # MakeMaker (e.g. using ActiveState under Cygwin).
  #
  # memory allocators with the nedmalloc allocator written by Niall Douglas.
  #
  # Define NO_REGEX if you have no or inferior regex support in your C library.
 +#
 +# Define DEFAULT_PAGER to a sensible pager command (defaults to "less") if
 +# you want to use something different.  The value will be interpreted by the
 +# shell at runtime when it is used.
 +#
 +# Define DEFAULT_EDITOR to a sensible editor command (defaults to "vi") if you
 +# want to use something different.  The value will be interpreted by the shell
 +# if necessary when it is used.  Examples:
 +#
 +#   DEFAULT_EDITOR='~/bin/vi',
 +#   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
 +#   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
  
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -228,12 -212,6 +228,12 @@@ uname_R := $(shell sh -c 'uname -r 2>/d
  uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
  uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
  
 +ifdef MSVC
 +      # avoid the MingW and Cygwin configuration sections
 +      uname_S := Windows
 +      uname_O := Windows
 +endif
 +
  # CFLAGS and LDFLAGS are for the users to override from the command line.
  
  CFLAGS = -g -O2 -Wall
@@@ -376,7 -354,6 +376,7 @@@ EXTRA_PROGRAMS 
  PROGRAMS += $(EXTRA_PROGRAMS)
  PROGRAMS += git-fast-import$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
@@@ -388,6 -365,7 +388,7 @@@ PROGRAMS += git-show-index$
  PROGRAMS += git-unpack-file$X
  PROGRAMS += git-upload-pack$X
  PROGRAMS += git-var$X
+ PROGRAMS += git-http-backend$X
  
  # List built-in command $C whose implementation cmd_$C() is not in
  # builtin-$C.o but is linked in as part of some other command.
@@@ -435,7 -413,6 +436,7 @@@ LIB_H += builtin.
  LIB_H += cache.h
  LIB_H += cache-tree.h
  LIB_H += commit.h
 +LIB_H += compat/bswap.h
  LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
  LIB_H += csum-file.h
@@@ -476,7 -453,6 +477,7 @@@ LIB_H += sideband.
  LIB_H += sigchain.h
  LIB_H += strbuf.h
  LIB_H += string-list.h
 +LIB_H += submodule.h
  LIB_H += tag.h
  LIB_H += transport.h
  LIB_H += tree.h
@@@ -575,7 -551,6 +576,7 @@@ LIB_OBJS += sideband.
  LIB_OBJS += sigchain.o
  LIB_OBJS += strbuf.o
  LIB_OBJS += string-list.o
 +LIB_OBJS += submodule.o
  LIB_OBJS += symlinks.o
  LIB_OBJS += tag.o
  LIB_OBJS += trace.o
@@@ -620,6 -595,7 +621,6 @@@ BUILTIN_OBJS += builtin-diff-index.
  BUILTIN_OBJS += builtin-diff-tree.o
  BUILTIN_OBJS += builtin-diff.o
  BUILTIN_OBJS += builtin-fast-export.o
 -BUILTIN_OBJS += builtin-fetch--tool.o
  BUILTIN_OBJS += builtin-fetch-pack.o
  BUILTIN_OBJS += builtin-fetch.o
  BUILTIN_OBJS += builtin-fmt-merge-msg.o
@@@ -807,8 -783,6 +808,8 @@@ ifeq ($(uname_O),Cygwin
        NO_MMAP = YesPlease
        NO_IPV6 = YesPlease
        X = .exe
 +      COMPAT_OBJS += compat/cygwin.o
 +      UNRELIABLE_FSTAT = UnfortunatelyYes
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
@@@ -918,11 -892,15 +919,11 @@@ ifeq ($(uname_S),HP-UX
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
  endif
 -ifneq (,$(findstring CYGWIN,$(uname_S)))
 -      COMPAT_OBJS += compat/cygwin.o
 -      UNRELIABLE_FSTAT = UnfortunatelyYes
 -endif
 -ifdef MSVC
 +ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
        pathsep = ;
        NO_PREAD = YesPlease
 -      NO_OPENSSL = YesPlease
 +      NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
        NO_PTHREADS = YesPlease
 +      BLK_SHA1 = YesPlease
  
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@@ -971,11 -948,11 +972,11 @@@ els
        BASIC_CFLAGS += -Zi -MTd
  endif
        X = .exe
 -else
 +endif
  ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
 -      NO_OPENSSL = YesPlease
 +      NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
 +      BLK_SHA1 = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
@@@ -1021,6 -997,7 +1022,6 @@@ els
        NO_PTHREADS = YesPlease
  endif
  endif
 -endif
  
  -include config.mak.autogen
  -include config.mak
@@@ -1099,6 -1076,7 +1100,6 @@@ EXTLIBS += -l
  
  ifndef NO_POSIX_ONLY_PROGRAMS
        PROGRAMS += git-daemon$X
 -      PROGRAMS += git-imap-send$X
  endif
  ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@@ -1386,22 -1364,6 +1387,22 @@@ BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_H
        $(COMPAT_CFLAGS)
  LIB_OBJS += $(COMPAT_OBJS)
  
 +# Quote for C
 +
 +ifdef DEFAULT_EDITOR
 +DEFAULT_EDITOR_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_EDITOR)))"
 +DEFAULT_EDITOR_CQ_SQ = $(subst ','\'',$(DEFAULT_EDITOR_CQ))
 +
 +BASIC_CFLAGS += -DDEFAULT_EDITOR='$(DEFAULT_EDITOR_CQ_SQ)'
 +endif
 +
 +ifdef DEFAULT_PAGER
 +DEFAULT_PAGER_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_PAGER)))"
 +DEFAULT_PAGER_CQ_SQ = $(subst ','\'',$(DEFAULT_PAGER_CQ))
 +
 +BASIC_CFLAGS += -DDEFAULT_PAGER='$(DEFAULT_PAGER_CQ_SQ)'
 +endif
 +
  ALL_CFLAGS += $(BASIC_CFLAGS)
  ALL_LDFLAGS += $(BASIC_LDFLAGS)
  
@@@ -1414,7 -1376,7 +1415,7 @@@ SHELL = $(SHELL_PATH
  
  all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
  ifneq (,$X)
 -      $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
 +      $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
  endif
  
  all::
@@@ -1838,10 -1800,7 +1839,10 @@@ dist: git.spec git-archive$(X) configur
        gzip -f -9 $(GIT_TARNAME).tar
  
  rpm: dist
 -      $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
 +      $(RPMBUILD) \
 +              --define "_source_filedigest_algorithm md5" \
 +              --define "_binary_filedigest_algorithm md5" \
 +              -ta $(GIT_TARNAME).tar.gz
  
  htmldocs = git-htmldocs-$(GIT_VERSION)
  manpages = git-manpages-$(GIT_VERSION)
@@@ -1869,7 -1828,7 +1870,7 @@@ distclean: clea
        $(RM) configure
  
  clean:
 -      $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
 +      $(RM) *.o block-sha1/*.o arm/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
diff --combined builtin-fetch.c
index 2728860399460243cdd57ebca995f418b4c19848,52a9a42fa52948c1ab04a6965a3814d287949740..cd0bcf7140f11793f0e1e7e40149b9b101cf80c8
@@@ -269,7 -269,7 +269,7 @@@ static int update_local_ref(struct ref 
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "..");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
 -              r = s_update_ref("fast forward", ref, 1);
 +              r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
                        SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
                        r ? "unable to update local ref" : "forced update");
                return r;
        } else {
 -              sprintf(display, "! %-*s %-*s -> %s  (non fast forward)",
 +              sprintf(display, "! %-*s %-*s -> %s  (non-fast-forward)",
                        SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
@@@ -489,8 -489,7 +489,8 @@@ static int add_existing(const char *ref
                        int flag, void *cbdata)
  {
        struct string_list *list = (struct string_list *)cbdata;
 -      string_list_insert(refname, list);
 +      struct string_list_item *item = string_list_insert(refname, list);
 +      item->util = (void *)sha1;
        return 0;
  }
  
@@@ -505,98 -504,57 +505,98 @@@ static int will_fetch(struct ref **head
        return 0;
  }
  
 +struct tag_data {
 +      struct ref **head;
 +      struct ref ***tail;
 +};
 +
 +static int add_to_tail(struct string_list_item *item, void *cb_data)
 +{
 +      struct tag_data *data = (struct tag_data *)cb_data;
 +      struct ref *rm = NULL;
 +
 +      /* We have already decided to ignore this item */
 +      if (!item->util)
 +              return 0;
 +
 +      rm = alloc_ref(item->string);
 +      rm->peer_ref = alloc_ref(item->string);
 +      hashcpy(rm->old_sha1, item->util);
 +
 +      **data->tail = rm;
 +      *data->tail = &rm->next;
 +
 +      return 0;
 +}
 +
  static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
  {
        struct string_list existing_refs = { NULL, 0, 0, 0 };
 -      struct string_list new_refs = { NULL, 0, 0, 1 };
 -      char *ref_name;
 -      int ref_name_len;
 -      const unsigned char *ref_sha1;
 -      const struct ref *tag_ref;
 -      struct ref *rm = NULL;
 +      struct string_list remote_refs = { NULL, 0, 0, 0 };
 +      struct tag_data data = {head, tail};
        const struct ref *ref;
 +      struct string_list_item *item = NULL;
  
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
                if (prefixcmp(ref->name, "refs/tags"))
                        continue;
  
 -              ref_name = xstrdup(ref->name);
 -              ref_name_len = strlen(ref_name);
 -              ref_sha1 = ref->old_sha1;
 -
 -              if (!strcmp(ref_name + ref_name_len - 3, "^{}")) {
 -                      ref_name[ref_name_len - 3] = 0;
 -                      tag_ref = transport_get_remote_refs(transport);
 -                      while (tag_ref) {
 -                              if (!strcmp(tag_ref->name, ref_name)) {
 -                                      ref_sha1 = tag_ref->old_sha1;
 -                                      break;
 -                              }
 -                              tag_ref = tag_ref->next;
 -                      }
 +              /*
 +               * The peeled ref always follows the matching base
 +               * ref, so if we see a peeled ref that we don't want
 +               * to fetch then we can mark the ref entry in the list
 +               * as one to ignore by setting util to NULL.
 +               */
 +              if (!strcmp(ref->name + strlen(ref->name) - 3, "^{}")) {
 +                      if (item && !has_sha1_file(ref->old_sha1) &&
 +                          !will_fetch(head, ref->old_sha1) &&
 +                          !has_sha1_file(item->util) &&
 +                          !will_fetch(head, item->util))
 +                              item->util = NULL;
 +                      item = NULL;
 +                      continue;
                }
  
 -              if (!string_list_has_string(&existing_refs, ref_name) &&
 -                  !string_list_has_string(&new_refs, ref_name) &&
 -                  (has_sha1_file(ref->old_sha1) ||
 -                   will_fetch(head, ref->old_sha1))) {
 -                      string_list_insert(ref_name, &new_refs);
 +              /*
 +               * If item is non-NULL here, then we previously saw a
 +               * ref not followed by a peeled reference, so we need
 +               * to check if it is a lightweight tag that we want to
 +               * fetch.
 +               */
 +              if (item && !has_sha1_file(item->util) &&
 +                  !will_fetch(head, item->util))
 +                      item->util = NULL;
  
 -                      rm = alloc_ref(ref_name);
 -                      rm->peer_ref = alloc_ref(ref_name);
 -                      hashcpy(rm->old_sha1, ref_sha1);
 +              item = NULL;
  
 -                      **tail = rm;
 -                      *tail = &rm->next;
 -              }
 -              free(ref_name);
 +              /* skip duplicates and refs that we already have */
 +              if (string_list_has_string(&remote_refs, ref->name) ||
 +                  string_list_has_string(&existing_refs, ref->name))
 +                      continue;
 +
 +              item = string_list_insert(ref->name, &remote_refs);
 +              item->util = (void *)ref->old_sha1;
        }
        string_list_clear(&existing_refs, 0);
 -      string_list_clear(&new_refs, 0);
 +
 +      /*
 +       * We may have a final lightweight tag that needs to be
 +       * checked to see if it needs fetching.
 +       */
 +      if (item && !has_sha1_file(item->util) &&
 +          !will_fetch(head, item->util))
 +              item->util = NULL;
 +
 +      /*
 +       * For all the tags in the remote_refs string list, call
 +       * add_to_tail to add them to the list of refs to be fetched
 +       */
 +      for_each_string_list(add_to_tail, &remote_refs, &data);
 +
 +      string_list_clear(&remote_refs, 0);
  }
  
  static void check_not_current_branch(struct ref *ref_map)
  static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
  {
 +      struct string_list existing_refs = { NULL, 0, 0, 0 };
 +      struct string_list_item *peer_item = NULL;
        struct ref *ref_map;
        struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
 +
 +      for_each_ref(add_existing, &existing_refs);
 +
        if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
                tags = TAGS_SET;
        if (transport->remote->fetch_tags == -1)
                check_not_current_branch(ref_map);
  
        for (rm = ref_map; rm; rm = rm->next) {
 -              if (rm->peer_ref)
 -                      read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
 +              if (rm->peer_ref) {
 +                      peer_item = string_list_lookup(rm->peer_ref->name,
 +                                                     &existing_refs);
 +                      if (peer_item)
 +                              hashcpy(rm->peer_ref->old_sha1,
 +                                      peer_item->util);
 +              }
        }
  
        if (tags == TAGS_DEFAULT && autotags)
@@@ -717,7 -665,7 +717,7 @@@ int cmd_fetch(int argc, const char **ar
  
        transport = transport_get(remote, remote->url[0]);
        if (verbosity >= 2)
-               transport->verbose = 1;
+               transport->verbose = verbosity <= 3 ? verbosity : 3;
        if (verbosity < 0)
                transport->verbose = -1;
        if (upload_pack)
diff --combined builtin-receive-pack.c
index b6895d3f992f17e215d98f185a024a3e4041e507,70ff8c5d754667b1827fe0302d2622fff07b55c3..78c0e69cdc9fe2d21e83947932cb353c0553452e
@@@ -28,8 -28,6 +28,8 @@@ static int transfer_unpack_limit = -1
  static int unpack_limit = 100;
  static int report_status;
  static int prefer_ofs_delta = 1;
 +static int auto_update_server_info;
 +static int auto_gc = 1;
  static const char *head_name;
  static char *capabilities_to_send;
  
@@@ -90,16 -88,6 +90,16 @@@ static int receive_pack_config(const ch
                return 0;
        }
  
 +      if (strcmp(var, "receive.updateserverinfo") == 0) {
 +              auto_update_server_info = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (strcmp(var, "receive.autogc") == 0) {
 +              auto_gc = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, cb);
  }
  
@@@ -341,9 -329,9 +341,9 @@@ static const char *update(struct comman
                                break;
                free_commit_list(bases);
                if (!ent) {
 -                      error("denying non-fast forward %s"
 +                      error("denying non-fast-forward %s"
                              " (you should pull first)", name);
 -                      return "non-fast forward";
 +                      return "non-fast-forward";
                }
        }
        if (run_update_hook(cmd)) {
@@@ -627,6 -615,8 +627,8 @@@ static void add_alternate_refs(void
  
  int cmd_receive_pack(int argc, const char **argv, const char *prefix)
  {
+       int advertise_refs = 0;
+       int stateless_rpc = 0;
        int i;
        char *dir = NULL;
  
                const char *arg = *argv++;
  
                if (*arg == '-') {
-                       /* Do flag handling here */
+                       if (!strcmp(arg, "--advertise-refs")) {
+                               advertise_refs = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--stateless-rpc")) {
+                               stateless_rpc = 1;
+                               continue;
+                       }
                        usage(receive_pack_usage);
                }
                if (dir)
                " report-status delete-refs ofs-delta " :
                " report-status delete-refs ";
  
-       add_alternate_refs();
-       write_head_info();
-       clear_extra_refs();
+       if (advertise_refs || !stateless_rpc) {
+               add_alternate_refs();
+               write_head_info();
+               clear_extra_refs();
  
-       /* EOF */
-       packet_flush(1);
+               /* EOF */
+               packet_flush(1);
+       }
+       if (advertise_refs)
+               return 0;
  
        read_head_info();
        if (commands) {
                        report(unpack_status);
                run_receive_hook(post_receive_hook);
                run_update_post_hook(commands);
 +              if (auto_gc) {
 +                      const char *argv_gc_auto[] = {
 +                              "gc", "--auto", "--quiet", NULL,
 +                      };
 +                      run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 +              }
 +              if (auto_update_server_info)
 +                      update_server_info(0);
        }
        return 0;
  }
diff --combined builtin-send-pack.c
index 37acad5ac190b7e203c61de475c15a0b86b8548a,a0fbad0f610098d283babb7046d3ff07bd3805cb..d26997bcfe28f113e3de63b43ecadb812dbd027e
@@@ -2,9 -2,11 +2,11 @@@
  #include "commit.h"
  #include "refs.h"
  #include "pkt-line.h"
+ #include "sideband.h"
  #include "run-command.h"
  #include "remote.h"
  #include "send-pack.h"
+ #include "quote.h"
  
  static const char send_pack_usage[] =
  "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@@ -59,7 -61,7 +61,7 @@@ static int pack_objects(int fd, struct 
        memset(&po, 0, sizeof(po));
        po.argv = argv;
        po.in = -1;
-       po.out = fd;
+       po.out = args->stateless_rpc ? -1 : fd;
        po.git_cmd = 1;
        if (start_command(&po))
                die_errno("git pack-objects failed");
        }
  
        close(po.in);
+       if (args->stateless_rpc) {
+               char *buf = xmalloc(LARGE_PACKET_MAX);
+               while (1) {
+                       ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
+                       if (n <= 0)
+                               break;
+                       send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
+               }
+               free(buf);
+               close(po.out);
+               po.out = -1;
+       }
        if (finish_command(&po))
                return error("pack-objects died with strange error");
        return 0;
@@@ -246,7 -262,7 +262,7 @@@ static int print_one_push_status(struc
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 -                              "non-fast forward");
 +                              "non-fast-forward");
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
@@@ -303,6 -319,59 +319,59 @@@ static int refs_pushed(struct ref *ref
        return 0;
  }
  
+ static void print_helper_status(struct ref *ref)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       for (; ref; ref = ref->next) {
+               const char *msg = NULL;
+               const char *res;
+               switch(ref->status) {
+               case REF_STATUS_NONE:
+                       res = "error";
+                       msg = "no match";
+                       break;
+               case REF_STATUS_OK:
+                       res = "ok";
+                       break;
+               case REF_STATUS_UPTODATE:
+                       res = "ok";
+                       msg = "up to date";
+                       break;
+               case REF_STATUS_REJECT_NONFASTFORWARD:
+                       res = "error";
+                       msg = "non-fast forward";
+                       break;
+               case REF_STATUS_REJECT_NODELETE:
+               case REF_STATUS_REMOTE_REJECT:
+                       res = "error";
+                       break;
+               case REF_STATUS_EXPECTING_REPORT:
+               default:
+                       continue;
+               }
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "%s %s", res, ref->name);
+               if (ref->remote_status)
+                       msg = ref->remote_status;
+               if (msg) {
+                       strbuf_addch(&buf, ' ');
+                       quote_two_c_style(&buf, "", msg, 0);
+               }
+               strbuf_addch(&buf, '\n');
+               safe_write(1, buf.buf, buf.len);
+       }
+       strbuf_release(&buf);
+ }
  int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
  {
        int in = fd[0];
        int out = fd[1];
+       struct strbuf req_buf = STRBUF_INIT;
        struct ref *ref;
        int new_refs;
        int ask_for_status_report = 0;
                        char *new_hex = sha1_to_hex(ref->new_sha1);
  
                        if (ask_for_status_report) {
-                               packet_write(out, "%s %s %s%c%s",
+                               packet_buf_write(&req_buf, "%s %s %s%c%s",
                                        old_hex, new_hex, ref->name, 0,
                                        "report-status");
                                ask_for_status_report = 0;
                                expect_status_report = 1;
                        }
                        else
-                               packet_write(out, "%s %s %s",
+                               packet_buf_write(&req_buf, "%s %s %s",
                                        old_hex, new_hex, ref->name);
                }
                ref->status = expect_status_report ?
                        REF_STATUS_OK;
        }
  
-       packet_flush(out);
+       if (args->stateless_rpc) {
+               if (!args->dry_run) {
+                       packet_buf_flush(&req_buf);
+                       send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
+               }
+       } else {
+               safe_write(out, req_buf.buf, req_buf.len);
+               packet_flush(out);
+       }
+       strbuf_release(&req_buf);
        if (new_refs && !args->dry_run) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
                        for (ref = remote_refs; ref; ref = ref->next)
                        return -1;
                }
        }
+       if (args->stateless_rpc && !args->dry_run)
+               packet_flush(out);
  
        if (expect_status_report)
                ret = receive_status(in, remote_refs);
        else
                ret = 0;
+       if (args->stateless_rpc)
+               packet_flush(out);
  
        if (ret < 0)
                return ret;
@@@ -478,6 -562,7 +562,7 @@@ int cmd_send_pack(int argc, const char 
        struct extra_have_objects extra_have;
        struct ref *remote_refs, *local_refs;
        int ret;
+       int helper_status = 0;
        int send_all = 0;
        const char *receivepack = "git-receive-pack";
        int flags;
                                args.use_thin_pack = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--stateless-rpc")) {
+                               args.stateless_rpc = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--helper-status")) {
+                               helper_status = 1;
+                               continue;
+                       }
                        usage(send_pack_usage);
                }
                if (!dest) {
                }
        }
  
-       conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0);
+       if (args.stateless_rpc) {
+               conn = NULL;
+               fd[0] = 0;
+               fd[1] = 1;
+       } else {
+               conn = git_connect(fd, dest, receivepack,
+                       args.verbose ? CONNECT_VERBOSE : 0);
+       }
  
        memset(&extra_have, 0, sizeof(extra_have));
  
  
        ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
  
+       if (helper_status)
+               print_helper_status(remote_refs);
        close(fd[1]);
        close(fd[0]);
  
        ret |= finish_connect(conn);
  
-       print_push_status(dest, remote_refs);
+       if (!helper_status)
+               print_push_status(dest, remote_refs);
  
        if (!args.dry_run && remote) {
                struct ref *ref;
diff --combined cache.h
index a9047491e650dc0f6267b578fabeb33cafd20885,ecbd88adba612443d98e505eb1e7303e5fc7c3b0..2cfce27abd703309690ef9bbc69105a32a70910a
+++ b/cache.h
@@@ -396,7 -396,6 +396,7 @@@ extern const char *setup_git_directory_
  extern const char *setup_git_directory(void);
  extern const char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
 +extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
  extern void verify_non_filename(const char *prefix, const char *name);
  
@@@ -657,6 -656,7 +657,7 @@@ const char *make_relative_path(const ch
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, const char *prefix_list);
  char *strip_path_suffix(const char *path, const char *suffix);
+ int daemon_avoid_alias(const char *path);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
@@@ -751,8 -751,6 +752,8 @@@ extern const char *git_author_info(int)
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
 +extern const char *git_editor(void);
 +extern const char *git_pager(void);
  
  struct checkout {
        const char *base_dir;
@@@ -859,7 -857,6 +860,6 @@@ extern struct ref *find_ref_by_name(con
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
- extern int get_ack(int fd, unsigned char *result_sha1);
  struct extra_have_objects {
        int nr, alloc;
        unsigned char (*array)[20];
@@@ -989,12 -986,10 +989,12 @@@ void shift_tree(const unsigned char *, 
   * whitespace rules.
   * used by both diff and apply
   */
 -#define WS_TRAILING_SPACE     01
 +#define WS_BLANK_AT_EOL         01
  #define WS_SPACE_BEFORE_TAB   02
  #define WS_INDENT_WITH_NON_TAB        04
  #define WS_CR_AT_EOL           010
 +#define WS_BLANK_AT_EOF        020
 +#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
diff --combined commit.c
index 6393e1b36218e5f8970b0c43092b07f5d94b0bce,471efb0566368aaf589d6d8390d583bc21bed256..51839fbedb1cb19f3d0cb11c60c7b9f695975654
+++ b/commit.c
@@@ -132,8 -132,8 +132,8 @@@ struct commit_graft *read_graft_line(ch
        int i;
        struct commit_graft *graft = NULL;
  
 -      if (buf[len-1] == '\n')
 -              buf[--len] = 0;
 +      while (len && isspace(buf[len-1]))
 +              buf[--len] = '\0';
        if (buf[0] == '#' || buf[0] == '\0')
                return NULL;
        if ((len + 1) % 41) {
@@@ -199,7 -199,7 +199,7 @@@ struct commit_graft *lookup_commit_graf
        return commit_graft[pos];
  }
  
- int write_shallow_commits(int fd, int use_pack_protocol)
+ int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
  {
        int i, count = 0;
        for (i = 0; i < commit_graft_nr; i++)
                                sha1_to_hex(commit_graft[i]->sha1);
                        count++;
                        if (use_pack_protocol)
-                               packet_write(fd, "shallow %s", hex);
+                               packet_buf_write(out, "shallow %s", hex);
                        else {
-                               if (write_in_full(fd, hex,  40) != 40)
-                                       break;
-                               if (write_str_in_full(fd, "\n") != 1)
-                                       break;
+                               strbuf_addstr(out, hex);
+                               strbuf_addch(out, '\n');
                        }
                }
        return count;
diff --combined commit.h
index 422f778f3f5b997ea1a3d8569cf6fa573481fff1,817c75c02f1c9825c60cd23c586e657b752d2933..e5332efcfc9449e0f3af4d6c49be63adfeb138b1
+++ b/commit.h
@@@ -63,16 -63,6 +63,16 @@@ enum cmit_fmt 
        CMIT_FMT_UNSPECIFIED,
  };
  
 +struct pretty_print_context
 +{
 +      int abbrev;
 +      const char *subject;
 +      const char *after_subject;
 +      enum date_mode date_mode;
 +      int need_8bit_cte;
 +      struct reflog_walk_info *reflog_info;
 +};
 +
  extern int non_ascii(int);
  extern int has_non_ascii(const char *text);
  struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
@@@ -80,11 -70,13 +80,11 @@@ extern char *reencode_commit_message(co
                                     const char **encoding_p);
  extern void get_commit_format(const char *arg, struct rev_info *);
  extern void format_commit_message(const struct commit *commit,
 -                                const void *format, struct strbuf *sb,
 -                                enum date_mode dmode);
 -extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
 -                                struct strbuf *,
 -                                int abbrev, const char *subject,
 -                                const char *after_subject, enum date_mode,
 -                              int need_8bit_cte);
 +                                const char *format, struct strbuf *sb,
 +                                const struct pretty_print_context *context);
 +extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
 +                              struct strbuf *sb,
 +                              const struct pretty_print_context *context);
  void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
                   const char *line, enum date_mode dmode,
                   const char *encoding);
@@@ -139,7 -131,7 +139,7 @@@ extern struct commit_list *get_octopus_
  
  extern int register_shallow(const unsigned char *sha1);
  extern int unregister_shallow(const unsigned char *sha1);
- extern int write_shallow_commits(int fd, int use_pack_protocol);
+ extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
  extern int is_repository_shallow(void);
  extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
diff --combined http-push.c
index ad1a6c90967b36365563de210ec2d066ae9d979a,f10803a389295d079a7b1dc10bf7d8a6ceac11de..0e040f8c9a5f3ba2fa0c3858580292dd84a834e9
@@@ -78,6 -78,7 +78,7 @@@ static int push_verbosely
  static int push_all = MATCH_REFS_NONE;
  static int force_all;
  static int dry_run;
+ static int helper_status;
  
  static struct object_list *objects;
  
@@@ -604,7 -605,7 +605,7 @@@ static void finish_request(struct trans
                        preq = (struct http_pack_request *)request->userData;
  
                        if (preq) {
-                               if (finish_http_pack_request(preq) > 0)
+                               if (finish_http_pack_request(preq) == 0)
                                        fail = 0;
                                release_http_pack_request(preq);
                        }
@@@ -1792,6 -1793,8 +1793,6 @@@ int main(int argc, char **argv
  
        git_extract_argv0_path(argv[0]);
  
 -      setup_git_directory();
 -
        repo = xcalloc(sizeof(*repo), 1);
  
        argv++;
                                dry_run = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--helper-status")) {
+                               helper_status = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--verbose")) {
                                push_verbosely = 1;
                                http_is_verbose = 1;
                                force_delete = 1;
                                continue;
                        }
 +                      if (!strcmp(arg, "-h"))
 +                              usage(http_push_usage);
                }
                if (!repo->url) {
                        char *path = strstr(arg, "//");
        if (delete_branch && nr_refspec != 1)
                die("You must specify only one branch name when deleting a remote branch");
  
 +      setup_git_directory();
 +
        memset(remote_dir_exists, -1, 256);
  
        /*
  
        /* Remove a remote branch if -d or -D was specified */
        if (delete_branch) {
-               if (delete_remote_branch(refspec[0], force_delete) == -1)
+               if (delete_remote_branch(refspec[0], force_delete) == -1) {
                        fprintf(stderr, "Unable to delete remote branch %s\n",
                                refspec[0]);
+                       if (helper_status)
+                               printf("error %s cannot remove\n", refspec[0]);
+               }
                goto cleanup;
        }
  
        }
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+               if (helper_status)
+                       printf("error null no match\n");
                rc = 0;
                goto cleanup;
        }
                if (is_null_sha1(ref->peer_ref->new_sha1)) {
                        if (delete_remote_branch(ref->name, 1) == -1) {
                                error("Could not remove %s", ref->name);
+                               if (helper_status)
+                                       printf("error %s cannot remove\n", ref->name);
                                rc = -4;
                        }
+                       else if (helper_status)
+                               printf("ok %s\n", ref->name);
                        new_refs++;
                        continue;
                }
                if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
                        if (push_verbosely || 1)
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
+                       if (helper_status)
+                               printf("ok %s up to date\n", ref->name);
                        continue;
                }
  
                                      "need to pull first?",
                                      ref->name,
                                      ref->peer_ref->name);
+                               if (helper_status)
+                                       printf("error %s non-fast forward\n", ref->name);
                                rc = -2;
                                continue;
                        }
                if (strcmp(ref->name, ref->peer_ref->name))
                        fprintf(stderr, " using '%s'", ref->peer_ref->name);
                fprintf(stderr, "\n  from %s\n  to   %s\n", old_hex, new_hex);
-               if (dry_run)
+               if (dry_run) {
+                       if (helper_status)
+                               printf("ok %s\n", ref->name);
                        continue;
+               }
  
                /* Lock remote branch ref */
                ref_lock = lock_remote(ref->name, LOCK_TIME);
                if (ref_lock == NULL) {
                        fprintf(stderr, "Unable to lock remote branch %s\n",
                                ref->name);
+                       if (helper_status)
+                               printf("error %s lock error\n", ref->name);
                        rc = 1;
                        continue;
                }
  
                if (!rc)
                        fprintf(stderr, "    done\n");
+               if (helper_status)
+                       printf("%s %s\n", !rc ? "ok" : "error", ref->name);
                unlock_remote(ref_lock);
                check_locks();
        }
diff --combined remote-curl.c
index ebdab3603ec767b42ce04e0de2c3deeb516f2525,0d7cf16e9c06d556cad8d92b6598a8da079426a4..4f28c222f2064b27c41bbcfbb425c6a221b46a58
  #include "walker.h"
  #include "http.h"
  #include "exec_cmd.h"
+ #include "run-command.h"
+ #include "pkt-line.h"
+ #include "sideband.h"
  
- static struct ref *get_refs(struct walker *walker, const char *url)
+ static struct remote *remote;
+ static const char *url;
+ static struct walker *walker;
+ struct options {
+       int verbosity;
+       unsigned long depth;
+       unsigned progress : 1,
+               followtags : 1,
+               dry_run : 1,
+               thin : 1;
+ };
+ static struct options options;
+ static void init_walker(void)
+ {
+       if (!walker)
+               walker = get_http_walker(url, remote);
+ }
+ static int set_option(const char *name, const char *value)
+ {
+       if (!strcmp(name, "verbosity")) {
+               char *end;
+               int v = strtol(value, &end, 10);
+               if (value == end || *end)
+                       return -1;
+               options.verbosity = v;
+               return 0;
+       }
+       else if (!strcmp(name, "progress")) {
+               if (!strcmp(value, "true"))
+                       options.progress = 1;
+               else if (!strcmp(value, "false"))
+                       options.progress = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else if (!strcmp(name, "depth")) {
+               char *end;
+               unsigned long v = strtoul(value, &end, 10);
+               if (value == end || *end)
+                       return -1;
+               options.depth = v;
+               return 0;
+       }
+       else if (!strcmp(name, "followtags")) {
+               if (!strcmp(value, "true"))
+                       options.followtags = 1;
+               else if (!strcmp(value, "false"))
+                       options.followtags = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else if (!strcmp(name, "dry-run")) {
+               if (!strcmp(value, "true"))
+                       options.dry_run = 1;
+               else if (!strcmp(value, "false"))
+                       options.dry_run = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else {
+               return 1 /* unsupported */;
+       }
+ }
+ struct discovery {
+       const char *service;
+       char *buf_alloc;
+       char *buf;
+       size_t len;
+       unsigned proto_git : 1;
+ };
+ static struct discovery *last_discovery;
+ static void free_discovery(struct discovery *d)
+ {
+       if (d) {
+               if (d == last_discovery)
+                       last_discovery = NULL;
+               free(d->buf_alloc);
+               free(d);
+       }
+ }
+ static struct discovery* discover_refs(const char *service)
  {
        struct strbuf buffer = STRBUF_INIT;
-       char *data, *start, *mid;
-       char *ref_name;
+       struct discovery *last = last_discovery;
        char *refs_url;
-       int i = 0;
-       int http_ret;
+       int http_ret, is_http = 0;
  
-       struct ref *refs = NULL;
-       struct ref *ref = NULL;
-       struct ref *last_ref = NULL;
+       if (last && !strcmp(service, last->service))
+               return last;
+       free_discovery(last);
  
-       refs_url = xmalloc(strlen(url) + 11);
-       sprintf(refs_url, "%s/info/refs", url);
+       strbuf_addf(&buffer, "%s/info/refs", url);
+       if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
+               is_http = 1;
+               if (!strchr(url, '?'))
+                       strbuf_addch(&buffer, '?');
+               else
+                       strbuf_addch(&buffer, '&');
+               strbuf_addf(&buffer, "service=%s", service);
+       }
+       refs_url = strbuf_detach(&buffer, NULL);
  
+       init_walker();
        http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
        switch (http_ret) {
        case HTTP_OK:
                die("HTTP request failed");
        }
  
-       data = buffer.buf;
+       last= xcalloc(1, sizeof(*last_discovery));
+       last->service = service;
+       last->buf_alloc = strbuf_detach(&buffer, &last->len);
+       last->buf = last->buf_alloc;
+       if (is_http && 5 <= last->len && last->buf[4] == '#') {
+               /* smart HTTP response; validate that the service
+                * pkt-line matches our request.
+                */
+               struct strbuf exp = STRBUF_INIT;
+               if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
+                       die("%s has invalid packet header", refs_url);
+               if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
+                       strbuf_setlen(&buffer, buffer.len - 1);
+               strbuf_addf(&exp, "# service=%s", service);
+               if (strbuf_cmp(&exp, &buffer))
+                       die("invalid server response; got '%s'", buffer.buf);
+               strbuf_release(&exp);
+               /* The header can include additional metadata lines, up
+                * until a packet flush marker.  Ignore these now, but
+                * in the future we might start to scan them.
+                */
+               strbuf_reset(&buffer);
+               while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
+                       strbuf_reset(&buffer);
+               last->proto_git = 1;
+       }
+       free(refs_url);
+       strbuf_release(&buffer);
+       last_discovery = last;
+       return last;
+ }
+ static int write_discovery(int fd, void *data)
+ {
+       struct discovery *heads = data;
+       int err = 0;
+       if (write_in_full(fd, heads->buf, heads->len) != heads->len)
+               err = 1;
+       close(fd);
+       return err;
+ }
+ static struct ref *parse_git_refs(struct discovery *heads)
+ {
+       struct ref *list = NULL;
+       struct async async;
+       memset(&async, 0, sizeof(async));
+       async.proc = write_discovery;
+       async.data = heads;
+       if (start_async(&async))
+               die("cannot start thread to parse advertised refs");
+       get_remote_heads(async.out, &list, 0, NULL, 0, NULL);
+       close(async.out);
+       if (finish_async(&async))
+               die("ref parsing thread failed");
+       return list;
+ }
+ static struct ref *parse_info_refs(struct discovery *heads)
+ {
+       char *data, *start, *mid;
+       char *ref_name;
+       int i = 0;
+       struct ref *refs = NULL;
+       struct ref *ref = NULL;
+       struct ref *last_ref = NULL;
+       data = heads->buf;
        start = NULL;
        mid = data;
-       while (i < buffer.len) {
+       while (i < heads->len) {
                if (!start) {
                        start = &data[i];
                }
                i++;
        }
  
-       strbuf_release(&buffer);
+       init_walker();
        ref = alloc_ref("HEAD");
        if (!walker->fetch_ref(walker, ref) &&
            !resolve_remote_symref(ref, refs)) {
                free(ref);
        }
  
-       strbuf_release(&buffer);
-       free(refs_url);
        return refs;
  }
  
+ static struct ref *get_refs(int for_push)
+ {
+       struct discovery *heads;
+       if (for_push)
+               heads = discover_refs("git-receive-pack");
+       else
+               heads = discover_refs("git-upload-pack");
+       if (heads->proto_git)
+               return parse_git_refs(heads);
+       return parse_info_refs(heads);
+ }
+ static void output_refs(struct ref *refs)
+ {
+       struct ref *posn;
+       for (posn = refs; posn; posn = posn->next) {
+               if (posn->symref)
+                       printf("@%s %s\n", posn->symref, posn->name);
+               else
+                       printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+       }
+       printf("\n");
+       fflush(stdout);
+       free_refs(refs);
+ }
+ struct rpc_state {
+       const char *service_name;
+       const char **argv;
+       char *service_url;
+       char *hdr_content_type;
+       char *hdr_accept;
+       char *buf;
+       size_t alloc;
+       size_t len;
+       size_t pos;
+       int in;
+       int out;
+       struct strbuf result;
+       unsigned gzip_request : 1;
+ };
+ static size_t rpc_out(void *ptr, size_t eltsize,
+               size_t nmemb, void *buffer_)
+ {
+       size_t max = eltsize * nmemb;
+       struct rpc_state *rpc = buffer_;
+       size_t avail = rpc->len - rpc->pos;
+       if (!avail) {
+               avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+               if (!avail)
+                       return 0;
+               rpc->pos = 0;
+               rpc->len = avail;
+       }
+       if (max < avail);
+               avail = max;
+       memcpy(ptr, rpc->buf + rpc->pos, avail);
+       rpc->pos += avail;
+       return avail;
+ }
+ static size_t rpc_in(const void *ptr, size_t eltsize,
+               size_t nmemb, void *buffer_)
+ {
+       size_t size = eltsize * nmemb;
+       struct rpc_state *rpc = buffer_;
+       write_or_die(rpc->in, ptr, size);
+       return size;
+ }
+ static int post_rpc(struct rpc_state *rpc)
+ {
+       struct active_request_slot *slot;
+       struct slot_results results;
+       struct curl_slist *headers = NULL;
+       int use_gzip = rpc->gzip_request;
+       char *gzip_body = NULL;
+       int err = 0, large_request = 0;
+       /* Try to load the entire request, if we can fit it into the
+        * allocated buffer space we can use HTTP/1.0 and avoid the
+        * chunked encoding mess.
+        */
+       while (1) {
+               size_t left = rpc->alloc - rpc->len;
+               char *buf = rpc->buf + rpc->len;
+               int n;
+               if (left < LARGE_PACKET_MAX) {
+                       large_request = 1;
+                       use_gzip = 0;
+                       break;
+               }
+               n = packet_read_line(rpc->out, buf, left);
+               if (!n)
+                       break;
+               rpc->len += n;
+       }
+       slot = get_active_slot();
+       slot->results = &results;
+       curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
+       curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+       headers = curl_slist_append(headers, rpc->hdr_content_type);
+       headers = curl_slist_append(headers, rpc->hdr_accept);
+       if (large_request) {
+               /* The request body is large and the size cannot be predicted.
+                * We must use chunked encoding to send it.
+                */
+               headers = curl_slist_append(headers, "Expect: 100-continue");
+               headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+               curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
+               curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
+               if (options.verbosity > 1) {
+                       fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
+                       fflush(stderr);
+               }
+       } else if (use_gzip && 1024 < rpc->len) {
+               /* The client backend isn't giving us compressed data so
+                * we can try to deflate it ourselves, this may save on.
+                * the transfer time.
+                */
+               size_t size;
+               z_stream stream;
+               int ret;
+               memset(&stream, 0, sizeof(stream));
+               ret = deflateInit2(&stream, Z_BEST_COMPRESSION,
+                               Z_DEFLATED, (15 + 16),
+                               8, Z_DEFAULT_STRATEGY);
+               if (ret != Z_OK)
+                       die("cannot deflate request; zlib init error %d", ret);
+               size = deflateBound(&stream, rpc->len);
+               gzip_body = xmalloc(size);
+               stream.next_in = (unsigned char *)rpc->buf;
+               stream.avail_in = rpc->len;
+               stream.next_out = (unsigned char *)gzip_body;
+               stream.avail_out = size;
+               ret = deflate(&stream, Z_FINISH);
+               if (ret != Z_STREAM_END)
+                       die("cannot deflate request; zlib deflate error %d", ret);
+               ret = deflateEnd(&stream);
+               if (ret != Z_OK)
+                       die("cannot deflate request; zlib end error %d", ret);
+               size = stream.total_out;
+               headers = curl_slist_append(headers, "Content-Encoding: gzip");
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, size);
+               if (options.verbosity > 1) {
+                       fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
+                               rpc->service_name,
+                               (unsigned long)rpc->len, (unsigned long)size);
+                       fflush(stderr);
+               }
+       } else {
+               /* We know the complete request size in advance, use the
+                * more normal Content-Length approach.
+                */
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len);
+               if (options.verbosity > 1) {
+                       fprintf(stderr, "POST %s (%lu bytes)\n",
+                               rpc->service_name, (unsigned long)rpc->len);
+                       fflush(stderr);
+               }
+       }
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
+       slot->curl_result = curl_easy_perform(slot->curl);
+       finish_active_slot(slot);
+       if (results.curl_result != CURLE_OK) {
+               err |= error("RPC failed; result=%d, HTTP code = %ld",
+                       results.curl_result, results.http_code);
+       }
+       curl_slist_free_all(headers);
+       free(gzip_body);
+       return err;
+ }
+ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
+ {
+       const char *svc = rpc->service_name;
+       struct strbuf buf = STRBUF_INIT;
+       struct child_process client;
+       int err = 0;
+       init_walker();
+       memset(&client, 0, sizeof(client));
+       client.in = -1;
+       client.out = -1;
+       client.git_cmd = 1;
+       client.argv = rpc->argv;
+       if (start_command(&client))
+               exit(1);
+       if (heads)
+               write_or_die(client.in, heads->buf, heads->len);
+       rpc->alloc = http_post_buffer;
+       rpc->buf = xmalloc(rpc->alloc);
+       rpc->in = client.in;
+       rpc->out = client.out;
+       strbuf_init(&rpc->result, 0);
+       strbuf_addf(&buf, "%s/%s", url, svc);
+       rpc->service_url = strbuf_detach(&buf, NULL);
+       strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
+       rpc->hdr_content_type = strbuf_detach(&buf, NULL);
+       strbuf_addf(&buf, "Accept: application/x-%s-response", svc);
+       rpc->hdr_accept = strbuf_detach(&buf, NULL);
+       while (!err) {
+               int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+               if (!n)
+                       break;
+               rpc->pos = 0;
+               rpc->len = n;
+               err |= post_rpc(rpc);
+       }
+       strbuf_read(&rpc->result, client.out, 0);
+       close(client.in);
+       close(client.out);
+       client.in = -1;
+       client.out = -1;
+       err |= finish_command(&client);
+       free(rpc->service_url);
+       free(rpc->hdr_content_type);
+       free(rpc->hdr_accept);
+       free(rpc->buf);
+       strbuf_release(&buf);
+       return err;
+ }
+ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
+ {
+       char **targets = xmalloc(nr_heads * sizeof(char*));
+       int ret, i;
+       if (options.depth)
+               die("dumb http transport does not support --depth");
+       for (i = 0; i < nr_heads; i++)
+               targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
+       init_walker();
+       walker->get_all = 1;
+       walker->get_tree = 1;
+       walker->get_history = 1;
+       walker->get_verbosely = options.verbosity >= 3;
+       walker->get_recover = 0;
+       ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
+       for (i = 0; i < nr_heads; i++)
+               free(targets[i]);
+       free(targets);
+       return ret ? error("Fetch failed.") : 0;
+ }
+ static int fetch_git(struct discovery *heads,
+       int nr_heads, struct ref **to_fetch)
+ {
+       struct rpc_state rpc;
+       char *depth_arg = NULL;
+       const char **argv;
+       int argc = 0, i, err;
+       argv = xmalloc((15 + nr_heads) * sizeof(char*));
+       argv[argc++] = "fetch-pack";
+       argv[argc++] = "--stateless-rpc";
+       argv[argc++] = "--lock-pack";
+       if (options.followtags)
+               argv[argc++] = "--include-tag";
+       if (options.thin)
+               argv[argc++] = "--thin";
+       if (options.verbosity >= 3) {
+               argv[argc++] = "-v";
+               argv[argc++] = "-v";
+       }
+       if (!options.progress)
+               argv[argc++] = "--no-progress";
+       if (options.depth) {
+               struct strbuf buf = STRBUF_INIT;
+               strbuf_addf(&buf, "--depth=%lu", options.depth);
+               depth_arg = strbuf_detach(&buf, NULL);
+               argv[argc++] = depth_arg;
+       }
+       argv[argc++] = url;
+       for (i = 0; i < nr_heads; i++) {
+               struct ref *ref = to_fetch[i];
+               if (!ref->name || !*ref->name)
+                       die("cannot fetch by sha1 over smart http");
+               argv[argc++] = ref->name;
+       }
+       argv[argc++] = NULL;
+       memset(&rpc, 0, sizeof(rpc));
+       rpc.service_name = "git-upload-pack",
+       rpc.argv = argv;
+       rpc.gzip_request = 1;
+       err = rpc_service(&rpc, heads);
+       if (rpc.result.len)
+               safe_write(1, rpc.result.buf, rpc.result.len);
+       strbuf_release(&rpc.result);
+       free(argv);
+       free(depth_arg);
+       return err;
+ }
+ static int fetch(int nr_heads, struct ref **to_fetch)
+ {
+       struct discovery *d = discover_refs("git-upload-pack");
+       if (d->proto_git)
+               return fetch_git(d, nr_heads, to_fetch);
+       else
+               return fetch_dumb(nr_heads, to_fetch);
+ }
+ static void parse_fetch(struct strbuf *buf)
+ {
+       struct ref **to_fetch = NULL;
+       struct ref *list_head = NULL;
+       struct ref **list = &list_head;
+       int alloc_heads = 0, nr_heads = 0;
+       do {
+               if (!prefixcmp(buf->buf, "fetch ")) {
+                       char *p = buf->buf + strlen("fetch ");
+                       char *name;
+                       struct ref *ref;
+                       unsigned char old_sha1[20];
+                       if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
+                               die("protocol error: expected sha/ref, got %s'", p);
+                       if (p[40] == ' ')
+                               name = p + 41;
+                       else if (!p[40])
+                               name = "";
+                       else
+                               die("protocol error: expected sha/ref, got %s'", p);
+                       ref = alloc_ref(name);
+                       hashcpy(ref->old_sha1, old_sha1);
+                       *list = ref;
+                       list = &ref->next;
+                       ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
+                       to_fetch[nr_heads++] = ref;
+               }
+               else
+                       die("http transport does not support %s", buf->buf);
+               strbuf_reset(buf);
+               if (strbuf_getline(buf, stdin, '\n') == EOF)
+                       return;
+               if (!*buf->buf)
+                       break;
+       } while (1);
+       if (fetch(nr_heads, to_fetch))
+               exit(128); /* error already reported */
+       free_refs(list_head);
+       free(to_fetch);
+       printf("\n");
+       fflush(stdout);
+       strbuf_reset(buf);
+ }
+ static int push_dav(int nr_spec, char **specs)
+ {
+       const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
+       int argc = 0, i;
+       argv[argc++] = "http-push";
+       argv[argc++] = "--helper-status";
+       if (options.dry_run)
+               argv[argc++] = "--dry-run";
+       if (options.verbosity > 1)
+               argv[argc++] = "--verbose";
+       argv[argc++] = url;
+       for (i = 0; i < nr_spec; i++)
+               argv[argc++] = specs[i];
+       argv[argc++] = NULL;
+       if (run_command_v_opt(argv, RUN_GIT_CMD))
+               die("git-%s failed", argv[0]);
+       free(argv);
+       return 0;
+ }
+ static int push_git(struct discovery *heads, int nr_spec, char **specs)
+ {
+       struct rpc_state rpc;
+       const char **argv;
+       int argc = 0, i, err;
+       argv = xmalloc((10 + nr_spec) * sizeof(char*));
+       argv[argc++] = "send-pack";
+       argv[argc++] = "--stateless-rpc";
+       argv[argc++] = "--helper-status";
+       if (options.thin)
+               argv[argc++] = "--thin";
+       if (options.dry_run)
+               argv[argc++] = "--dry-run";
+       if (options.verbosity > 1)
+               argv[argc++] = "--verbose";
+       argv[argc++] = url;
+       for (i = 0; i < nr_spec; i++)
+               argv[argc++] = specs[i];
+       argv[argc++] = NULL;
+       memset(&rpc, 0, sizeof(rpc));
+       rpc.service_name = "git-receive-pack",
+       rpc.argv = argv;
+       err = rpc_service(&rpc, heads);
+       if (rpc.result.len)
+               safe_write(1, rpc.result.buf, rpc.result.len);
+       strbuf_release(&rpc.result);
+       free(argv);
+       return err;
+ }
+ static int push(int nr_spec, char **specs)
+ {
+       struct discovery *heads = discover_refs("git-receive-pack");
+       int ret;
+       if (heads->proto_git)
+               ret = push_git(heads, nr_spec, specs);
+       else
+               ret = push_dav(nr_spec, specs);
+       free_discovery(heads);
+       return ret;
+ }
+ static void parse_push(struct strbuf *buf)
+ {
+       char **specs = NULL;
+       int alloc_spec = 0, nr_spec = 0, i;
+       do {
+               if (!prefixcmp(buf->buf, "push ")) {
+                       ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
+                       specs[nr_spec++] = xstrdup(buf->buf + 5);
+               }
+               else
+                       die("http transport does not support %s", buf->buf);
+               strbuf_reset(buf);
+               if (strbuf_getline(buf, stdin, '\n') == EOF)
+                       return;
+               if (!*buf->buf)
+                       break;
+       } while (1);
+       if (push(nr_spec, specs))
+               exit(128); /* error already reported */
+       for (i = 0; i < nr_spec; i++)
+               free(specs[i]);
+       free(specs);
+       printf("\n");
+       fflush(stdout);
+ }
  int main(int argc, const char **argv)
  {
-       struct remote *remote;
        struct strbuf buf = STRBUF_INIT;
-       const char *url;
-       struct walker *walker = NULL;
 +      int nongit;
  
        git_extract_argv0_path(argv[0]);
 -      setup_git_directory();
 +      setup_git_directory_gently(&nongit);
        if (argc < 2) {
                fprintf(stderr, "Remote needed\n");
                return 1;
        }
  
+       options.verbosity = 1;
+       options.progress = !!isatty(2);
+       options.thin = 1;
        remote = remote_get(argv[1]);
  
        if (argc > 2) {
                if (strbuf_getline(&buf, stdin, '\n') == EOF)
                        break;
                if (!prefixcmp(buf.buf, "fetch ")) {
-                       char *obj = buf.buf + strlen("fetch ");
 +                      if (nongit)
 +                              die("Fetch attempted without a local repo");
-                       if (!walker)
-                               walker = get_http_walker(url, remote);
-                       walker->get_all = 1;
-                       walker->get_tree = 1;
-                       walker->get_history = 1;
-                       walker->get_verbosely = 0;
-                       walker->get_recover = 0;
-                       if (walker_fetch(walker, 1, &obj, NULL, NULL))
-                               die("Fetch failed.");
-                       printf("\n");
-                       fflush(stdout);
-               } else if (!strcmp(buf.buf, "list")) {
-                       struct ref *refs;
-                       struct ref *posn;
-                       if (!walker)
-                               walker = get_http_walker(url, remote);
-                       refs = get_refs(walker, url);
-                       for (posn = refs; posn; posn = posn->next) {
-                               if (posn->symref)
-                                       printf("@%s %s\n", posn->symref, posn->name);
-                               else
-                                       printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
-                       }
-                       printf("\n");
+                       parse_fetch(&buf);
+               } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
+                       int for_push = !!strstr(buf.buf + 4, "for-push");
+                       output_refs(get_refs(for_push));
+               } else if (!prefixcmp(buf.buf, "push ")) {
+                       parse_push(&buf);
+               } else if (!prefixcmp(buf.buf, "option ")) {
+                       char *name = buf.buf + strlen("option ");
+                       char *value = strchr(name, ' ');
+                       int result;
+                       if (value)
+                               *value++ = '\0';
+                       else
+                               value = "true";
+                       result = set_option(name, value);
+                       if (!result)
+                               printf("ok\n");
+                       else if (result < 0)
+                               printf("error invalid value\n");
+                       else
+                               printf("unsupported\n");
                        fflush(stdout);
                } else if (!strcmp(buf.buf, "capabilities")) {
                        printf("fetch\n");
+                       printf("option\n");
+                       printf("push\n");
                        printf("\n");
                        fflush(stdout);
                } else {
diff --combined transport.c
index d249203eac88810ffef1cdd646e112a082d2c8b8,2ff16503be70be3b993b0177f6274169f556b340..7362ec09b2cbc6752489286a8280c16d3519f163
@@@ -349,35 -349,6 +349,6 @@@ static int rsync_transport_push(struct 
        return result;
  }
  
- #ifndef NO_CURL
- static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
- {
-       const char **argv;
-       int argc;
-       if (flags & TRANSPORT_PUSH_MIRROR)
-               return error("http transport does not support mirror mode");
-       argv = xmalloc((refspec_nr + 12) * sizeof(char *));
-       argv[0] = "http-push";
-       argc = 1;
-       if (flags & TRANSPORT_PUSH_ALL)
-               argv[argc++] = "--all";
-       if (flags & TRANSPORT_PUSH_FORCE)
-               argv[argc++] = "--force";
-       if (flags & TRANSPORT_PUSH_DRY_RUN)
-               argv[argc++] = "--dry-run";
-       if (flags & TRANSPORT_PUSH_VERBOSE)
-               argv[argc++] = "--verbose";
-       argv[argc++] = transport->url;
-       while (refspec_nr--)
-               argv[argc++] = *refspec++;
-       argv[argc] = NULL;
-       return !!run_command_v_opt(argv, RUN_GIT_CMD);
- }
- #endif
  struct bundle_transport_data {
        int fd;
        struct bundle_header header;
@@@ -668,7 -639,7 +639,7 @@@ static int print_one_push_status(struc
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
 -                                               "non-fast forward", porcelain);
 +                                               "non-fast-forward", porcelain);
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
@@@ -760,6 -731,7 +731,7 @@@ static int git_transport_push(struct tr
                                 NULL);
        }
  
+       memset(&args, 0, sizeof(args));
        args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
        args.use_thin_pack = data->thin;
@@@ -812,9 -784,6 +784,9 @@@ struct transport *transport_get(struct 
  {
        struct transport *ret = xcalloc(1, sizeof(*ret));
  
 +      if (!remote)
 +              die("No remote provided to transport_get()");
 +
        ret->remote = remote;
        ret->url = url;
  
                transport_helper_init(ret, "curl");
  #ifdef NO_CURL
                error("git was compiled without libcurl support.");
- #else
-               ret->push = curl_transport_push;
  #endif
  
        } else if (is_local(url) && is_file(url)) {
                data->thin = 1;
                data->conn = NULL;
                data->uploadpack = "git-upload-pack";
 -              if (remote && remote->uploadpack)
 +              if (remote->uploadpack)
                        data->uploadpack = remote->uploadpack;
                data->receivepack = "git-receive-pack";
 -              if (remote && remote->receivepack)
 +              if (remote->receivepack)
                        data->receivepack = remote->receivepack;
        }
  
diff --combined upload-pack.c
index 953ebe1a6030d8476fbebd0b060d3651cfde8f2f,70badcf6f7345a83b6d92172a0814b1c0627ada1..6bfb500eb4116bc74d6088b5b0621e89453fab3d
@@@ -39,6 -39,8 +39,8 @@@ static unsigned int timeout
   */
  static int use_sideband;
  static int debug_fd;
+ static int advertise_refs;
+ static int stateless_rpc;
  
  static void reset_timeout(void)
  {
@@@ -308,23 -310,6 +310,23 @@@ static void create_pack_file(void
                        }
                        continue;
                }
 +              if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
 +                      /* Status ready; we ship that in the side-band
 +                       * or dump to the standard error.
 +                       */
 +                      sz = xread(pack_objects.err, progress,
 +                                sizeof(progress));
 +                      if (0 < sz)
 +                              send_client_data(2, progress, sz);
 +                      else if (sz == 0) {
 +                              close(pack_objects.err);
 +                              pack_objects.err = -1;
 +                      }
 +                      else
 +                              goto fail;
 +                      /* give priority to status messages */
 +                      continue;
 +              }
                if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
                        /* Data ready; we keep the last byte to ourselves
                         * in case we detect broken rev-list, so that we
                        if (sz < 0)
                                goto fail;
                }
 -              if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
 -                      /* Status ready; we ship that in the side-band
 -                       * or dump to the standard error.
 -                       */
 -                      sz = xread(pack_objects.err, progress,
 -                                sizeof(progress));
 -                      if (0 < sz)
 -                              send_client_data(2, progress, sz);
 -                      else if (sz == 0) {
 -                              close(pack_objects.err);
 -                              pack_objects.err = -1;
 -                      }
 -                      else
 -                              goto fail;
 -              }
        }
  
        if (finish_command(&pack_objects)) {
@@@ -500,7 -500,7 +502,7 @@@ static int get_common_commits(void
  {
        static char line[1000];
        unsigned char sha1[20];
-       char hex[41], last_hex[41];
+       char last_hex[41];
  
        save_commit_buffer = 0;
  
                if (!len) {
                        if (have_obj.nr == 0 || multi_ack)
                                packet_write(1, "NAK\n");
+                       if (stateless_rpc)
+                               exit(0);
                        continue;
                }
                strip(line, len);
                if (!prefixcmp(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
-                               if (multi_ack && ok_to_give_up())
-                                       packet_write(1, "ACK %s continue\n",
-                                                    sha1_to_hex(sha1));
+                               if (multi_ack && ok_to_give_up()) {
+                                       const char *hex = sha1_to_hex(sha1);
+                                       if (multi_ack == 2)
+                                               packet_write(1, "ACK %s ready\n", hex);
+                                       else
+                                               packet_write(1, "ACK %s continue\n", hex);
+                               }
                                break;
                        default:
-                               memcpy(hex, sha1_to_hex(sha1), 41);
-                               if (multi_ack) {
-                                       const char *msg = "ACK %s continue\n";
-                                       packet_write(1, msg, hex);
-                                       memcpy(last_hex, hex, 41);
-                               }
+                               memcpy(last_hex, sha1_to_hex(sha1), 41);
+                               if (multi_ack == 2)
+                                       packet_write(1, "ACK %s common\n", last_hex);
+                               else if (multi_ack)
+                                       packet_write(1, "ACK %s continue\n", last_hex);
                                else if (have_obj.nr == 1)
-                                       packet_write(1, "ACK %s\n", hex);
+                                       packet_write(1, "ACK %s\n", last_hex);
                                break;
                        }
                        continue;
@@@ -589,7 -594,9 +596,9 @@@ static void receive_needs(void
                    get_sha1_hex(line+5, sha1_buf))
                        die("git upload-pack: protocol error, "
                            "expected to get sha, not '%s'", line);
-               if (strstr(line+45, "multi_ack"))
+               if (strstr(line+45, "multi_ack_detailed"))
+                       multi_ack = 2;
+               else if (strstr(line+45, "multi_ack"))
                        multi_ack = 1;
                if (strstr(line+45, "thin-pack"))
                        use_thin_pack = 1;
@@@ -683,7 -690,7 +692,7 @@@ static int send_ref(const char *refname
  {
        static const char *capabilities = "multi_ack thin-pack side-band"
                " side-band-64k ofs-delta shallow no-progress"
-               " include-tag";
+               " include-tag multi_ack_detailed";
        struct object *o = parse_object(sha1);
  
        if (!o)
        return 0;
  }
  
+ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ {
+       struct object *o = parse_object(sha1);
+       if (!o)
+               die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
+       if (!(o->flags & OUR_REF)) {
+               o->flags |= OUR_REF;
+               nr_our_refs++;
+       }
+       return 0;
+ }
  static void upload_pack(void)
  {
-       reset_timeout();
-       head_ref(send_ref, NULL);
-       for_each_ref(send_ref, NULL);
-       packet_flush(1);
+       if (advertise_refs || !stateless_rpc) {
+               reset_timeout();
+               head_ref(send_ref, NULL);
+               for_each_ref(send_ref, NULL);
+               packet_flush(1);
+       } else {
+               head_ref(mark_our_ref, NULL);
+               for_each_ref(mark_our_ref, NULL);
+       }
+       if (advertise_refs)
+               return;
        receive_needs();
        if (want_obj.nr) {
                get_common_commits();
@@@ -734,6 -761,14 +763,14 @@@ int main(int argc, char **argv
  
                if (arg[0] != '-')
                        break;
+               if (!strcmp(arg, "--advertise-refs")) {
+                       advertise_refs = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--stateless-rpc")) {
+                       stateless_rpc = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--strict")) {
                        strict = 1;
                        continue;