From: Junio C Hamano Date: Sat, 21 Nov 2009 07:51:23 +0000 (-0800) Subject: Merge branch 'sp/smart-http' X-Git-Tag: v1.6.6-rc0~22 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/905bf7742cf5f4a6dea2e75ba2dbd89d5dfaa793?ds=inline;hp=-c Merge branch 'sp/smart-http' * 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 --- 905bf7742cf5f4a6dea2e75ba2dbd89d5dfaa793 diff --combined .gitignore index 338c6d8881,353d22fc9a..f63992a6cc --- a/.gitignore +++ b/.gitignore @@@ -1,185 -1,185 +1,186 @@@ -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 @@@ -189,5 -189,5 +190,5 @@@ *.user *.idb *.pdb -Debug/ -Release/ +/Debug/ +/Release/ diff --combined Documentation/config.txt index c9b8db5cf7,7130d0723a..1ff21938e7 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -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..url:: The URL of a remote repository. See linkgit:git-fetch[1] or linkgit:git-push[1]. diff --combined Makefile index f8906618e8,271c290171..da418c39c9 --- a/Makefile +++ 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). # @@@ -204,18 -200,6 +204,18 @@@ # 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 @@@ -952,7 -930,6 +953,7 @@@ 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 @@@ -1002,7 -979,6 +1003,7 @@@ 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 2728860399,52a9a42fa5..cd0bcf7140 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@@ -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)" : ""); @@@ -287,7 -287,7 +287,7 @@@ 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) @@@ -616,14 -574,9 +616,14 @@@ 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) @@@ -646,13 -599,8 +646,13 @@@ 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 b6895d3f99,70ff8c5d75..78c0e69cdc --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@@ -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; @@@ -635,7 -625,15 +637,15 @@@ 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) @@@ -664,12 -662,16 +674,16 @@@ " 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) { @@@ -684,14 -686,6 +698,14 @@@ 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 37acad5ac1,a0fbad0f61..d26997bcfe --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@@ -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=] [--verbose] [--thin] [:] [...]\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"); @@@ -83,6 -85,20 +85,20 @@@ } 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, @@@ -310,6 -379,7 +379,7 @@@ { 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; @@@ -391,14 -461,14 +461,14 @@@ 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 ? @@@ -406,7 -476,17 +476,17 @@@ 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) @@@ -414,11 -494,15 +494,15 @@@ 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; @@@ -523,6 -608,14 +608,14 @@@ 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) { @@@ -551,7 -644,14 +644,14 @@@ } } - 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)); @@@ -575,12 -675,16 +675,16 @@@ 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 a9047491e6,ecbd88adba..2cfce27abd --- a/cache.h +++ 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 6393e1b362,471efb0566..51839fbedb --- a/commit.c +++ 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++) @@@ -208,12 -208,10 +208,10 @@@ 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 422f778f3f,817c75c02f..e5332efcfc --- a/commit.h +++ 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 ad1a6c9096,f10803a389..0e040f8c9a --- a/http-push.c +++ b/http-push.c @@@ -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++; @@@ -1811,6 -1814,10 +1812,10 @@@ dry_run = 1; continue; } + if (!strcmp(arg, "--helper-status")) { + helper_status = 1; + continue; + } if (!strcmp(arg, "--verbose")) { push_verbosely = 1; http_is_verbose = 1; @@@ -1825,8 -1832,6 +1830,8 @@@ force_delete = 1; continue; } + if (!strcmp(arg, "-h")) + usage(http_push_usage); } if (!repo->url) { char *path = strstr(arg, "//"); @@@ -1854,8 -1859,6 +1859,8 @@@ 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); /* @@@ -1913,9 -1916,12 +1918,12 @@@ /* 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; } @@@ -1927,6 -1933,8 +1935,8 @@@ } 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; } @@@ -1944,8 -1952,12 +1954,12 @@@ 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; } @@@ -1953,6 -1965,8 +1967,8 @@@ 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; } @@@ -1976,6 -1990,8 +1992,8 @@@ "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; } @@@ -1989,14 -2005,19 +2007,19 @@@ 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; } @@@ -2047,6 -2068,8 +2070,8 @@@ 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 ebdab3603e,0d7cf16e9c..4f28c222f2 --- a/remote-curl.c +++ b/remote-curl.c @@@ -4,23 -4,122 +4,122 @@@ #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: @@@ -33,10 -132,86 +132,86 @@@ 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]; } @@@ -60,8 -235,7 +235,7 @@@ i++; } - strbuf_release(&buffer); - + init_walker(); ref = alloc_ref("HEAD"); if (!walker->fetch_ref(walker, ref) && !resolve_remote_symref(ref, refs)) { @@@ -71,26 -245,518 +245,519 @@@ 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) { @@@ -103,36 -769,38 +770,40 @@@ 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 d249203eac,2ff16503be..7362ec09b2 --- a/transport.c +++ b/transport.c @@@ -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; @@@ -829,8 -798,6 +801,6 @@@ 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)) { @@@ -852,10 -819,10 +822,10 @@@ 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 953ebe1a60,70badcf6f7..6bfb500eb4 --- a/upload-pack.c +++ b/upload-pack.c @@@ -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 @@@ -362,6 -347,21 +364,6 @@@ 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; @@@ -511,25 -511,30 +513,30 @@@ 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) @@@ -707,12 -714,32 +716,32 @@@ 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;