Git development
 help / color / mirror / Atom feed
* [RFC PATCH v4 26/26] test smart http fetch and push
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

The top level directory "/git/" of the test Apache server is mapped
through our git-http-backend CGI, but uses the same underlying
repository space as the server's document root.  This is the most
simple installation possible.

Server logs are checked to verify the client has accessed only the
smart URLs during the test.  During fetch testing the headers are
also logged from libcurl to ensure we are making a reasonably sane
HTTP request, and getting back reasonably sane response headers
from the CGI.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 t/lib-httpd/apache.conf |   20 +++++++++
 t/t5540-http-push.sh    |    2 +-
 t/t5541-http-push.sh    |  103 +++++++++++++++++++++++++++++++++++++++++++++++
 t/t5550-http-fetch.sh   |    8 +++-
 t/t5551-http-fetch.sh   |   87 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 218 insertions(+), 2 deletions(-)
 create mode 100755 t/t5541-http-push.sh
 create mode 100755 t/t5551-http-fetch.sh

diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 21aa42f..2098ce0 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -8,6 +8,26 @@ ErrorLog error.log
 <IfModule !mod_log_config.c>
 	LoadModule log_config_module modules/mod_log_config.so
 </IfModule>
+<IfModule !mod_cgi.c>
+	LoadModule cgi_module modules/mod_cgi.so
+</IfModule>
+<IfModule !mod_alias.c>
+	LoadModule alias_module modules/mod_alias.so
+</IfModule>
+<IfModule !mod_env.c>
+	LoadModule env_module modules/mod_env.so
+</IfModule>
+
+<Location /git/>
+	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+</Location>
+ScriptAlias /git/ ${GIT_EXEC_PATH}/git-http-backend/
+<Directory ${GIT_EXEC_PATH}>
+	Options None
+</Directory>
+<Files ${GIT_EXEC_PATH}/git-http-backend>
+	Options ExecCGI
+</Files>
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 28a746e..5c0f4d7 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
 
-test_description='test http-push
+test_description='test WebDAV http-push
 
 This test runs various sanity checks on http-push.'
 
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
new file mode 100755
index 0000000..690c466
--- /dev/null
+++ b/t/t5541-http-push.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+	say 'skipping test, git built without http support'
+	test_done
+fi
+
+ROOT_PATH="$PWD"
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup remote repository' '
+	cd "$ROOT_PATH" &&
+	mkdir test_repo &&
+	cd test_repo &&
+	git init &&
+	: >path1 &&
+	git add path1 &&
+	test_tick &&
+	git commit -m initial &&
+	cd - &&
+	git clone --bare test_repo test_repo.git &&
+	cd test_repo.git &&
+	git config http.receivepack true &&
+	ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+	cd - &&
+	mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+test_expect_success 'clone remote repository' '
+	cd "$ROOT_PATH" &&
+	git clone $HTTPD_URL/git/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository with packed refs' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	: >path2 &&
+	git add path2 &&
+	test_tick &&
+	git commit -m path2 &&
+	HEAD=$(git rev-parse --verify HEAD) &&
+	git push &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+	git push
+'
+
+test_expect_success 'push to remote repository with unpacked refs' '
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 rm packed-refs &&
+	 git update-ref refs/heads/master $ORIG_HEAD) &&
+	git push &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'create and delete remote branch' '
+	cd "$ROOT_PATH"/test_repo_clone &&
+	git checkout -b dev &&
+	: >path3 &&
+	git add path3 &&
+	test_tick &&
+	git commit -m dev &&
+	git push origin dev &&
+	git push origin :dev &&
+	test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+cat >exp <<EOF
+GET  /git/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /git/test_repo.git/git-upload-pack HTTP/1.1 200
+GET  /git/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /git/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /git/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+GET  /git/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /git/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /git/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /git/test_repo.git/git-receive-pack HTTP/1.1 200
+GET  /git/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /git/test_repo.git/git-receive-pack HTTP/1.1 200
+EOF
+test_expect_success 'used receive-pack service' '
+	sed -e "
+		s/^.* \"//
+		s/\"//
+		s/ [1-9][0-9]*\$//
+		s/^GET /GET  /
+	" >act <"$HTTPD_ROOT_PATH"/access.log &&
+	test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index 0e69324..8d6443f 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='test fetching over http'
+test_description='test dumb fetching over http via static file'
 . ./test-lib.sh
 
 if test -n "$NO_CURL"; then
@@ -61,5 +61,11 @@ test_expect_success 'fetch packed objects' '
 	git clone $HTTPD_URL/repo_pack.git
 '
 
+test_expect_success 'did not use upload-pack service' '
+	grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
+	: >exp
+	test_cmp exp act
+'
+
 stop_httpd
 test_done
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
new file mode 100755
index 0000000..afddc2c
--- /dev/null
+++ b/t/t5551-http-fetch.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+	say 'skipping test, git built without http support'
+	test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+	echo content >file &&
+	git add file &&
+	git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	 git --bare init
+	) &&
+	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	git push public master:master
+'
+
+cat >exp <<EOF
+> GET /git/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+Accept: */*
+Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+< 
+> POST /git/repo.git/git-upload-pack HTTP/1.1
+Accept-Encoding: deflate, gzip
+Content-Type: application/x-git-upload-pack-request
+Accept: application/x-git-upload-pack-response
+Content-Length: xxxx
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-result
+< 
+EOF
+test_expect_success 'clone http repository' '
+	GIT_CURL_VERBOSE=1 git clone $HTTPD_URL/git/repo.git clone 2>err &&
+	test_cmp file clone/file &&
+	egrep "^([<>]|Pragma|Accept|Content-|Transfer-)" err |
+	egrep -v "^< (Server|Expires|Date|Content-Length:|Transfer-Encoding: chunked)" |
+	sed -e "
+		s/
//
+		s/^Content-Length: .*$/Content-Length: xxxx/
+	" >act &&
+	test_cmp exp act
+'
+
+test_expect_success 'fetch changes via http' '
+	echo content >>file &&
+	git commit -a -m two &&
+	git push public
+	(cd clone && git pull) &&
+	test_cmp file clone/file
+'
+
+cat >exp <<EOF
+GET  /git/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /git/repo.git/git-upload-pack HTTP/1.1 200
+GET  /git/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /git/repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'used upload-pack service' '
+	sed -e "
+		s/^.* \"//
+		s/\"//
+		s/ [1-9][0-9]*\$//
+		s/^GET /GET  /
+	" >act <"$HTTPD_ROOT_PATH"/access.log &&
+	test_cmp exp act
+'
+
+stop_httpd
+test_done
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 17/26] http-backend: reword some documentation
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Mark Lodato
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

From: Mark Lodato <lodatom@gmail.com>

Clarify some of the git-http-backend documentation, particularly:

* In the Description, state that smart/dumb HTTP fetch and smart HTTP
  push are supported, state that authenticated clients allow push, and
  remove the note that this is only suited for read-only updates.

* At the start of Examples, state explicitly what URL is mapping to what
  location on disk.

Signed-off-by: Mark Lodato <lodatom@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 Documentation/git-http-backend.txt |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
index 99dbbfb..0b5e951 100644
--- a/Documentation/git-http-backend.txt
+++ b/Documentation/git-http-backend.txt
@@ -14,13 +14,15 @@ DESCRIPTION
 -----------
 A simple CGI program to serve the contents of a Git repository to Git
 clients accessing the repository over http:// and https:// protocols.
+The program supports clients fetching using both the smart HTTP protcol
+and the backwards-compatible dumb HTTP protocol, as well as clients
+pushing using the smart HTTP protocol.
 
 By default, only the `upload-pack` service is enabled, which serves
 'git-fetch-pack' and 'git-ls-remote' clients, which are invoked from
-'git-fetch', 'git-pull', and 'git-clone'.
-
-This is ideally suited for read-only updates, i.e., pulling from
-git repositories.
+'git-fetch', 'git-pull', and 'git-clone'.  If the client is authenticated,
+the `receive-pack` service is enabled, which serves 'git-send-pack'
+clients, which is invoked from 'git-push'.
 
 SERVICES
 --------
@@ -50,6 +52,8 @@ automatically by the web server.
 
 EXAMPLES
 --------
+All of the following examples map 'http://$hostname/git/foo/bar.git'
+to '/var/www/git/foo/bar.git'.
 
 Apache 2.x::
 	Ensure mod_cgi, mod_alias, and mod_env are enabled, set
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 20/26] http-backend: more explict LocationMatch
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Mark Lodato
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

From: Mark Lodato <lodatom@gmail.com>

In the git-http-backend examples, only match git-receive-pack within
/git/.

Signed-off-by: Mark Lodato <lodatom@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 Documentation/git-http-backend.txt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
index 2989c9f..f17251a 100644
--- a/Documentation/git-http-backend.txt
+++ b/Documentation/git-http-backend.txt
@@ -69,7 +69,7 @@ To enable anonymous read access but authenticated write access,
 require authorization with a LocationMatch directive:
 +
 ----------------------------------------------------------------
-<LocationMatch ".*/git-receive-pack$">
+<LocationMatch "^/git/.*/git-receive-pack$">
 	AuthType Basic
 	AuthName "Git Access"
 	Require group committers
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 24/26] Smart HTTP fetch: gzip requests
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Daniel Barkalow
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

The upload-pack requests are mostly plain text and they compress
rather well.  Deflating them with Content-Encoding: gzip can easily
drop the size of the request by 50%, reducing the amount of data
to transfer as we negotiate the common commits.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Daniel Barkalow <barkalow@iabervon.org>
---
 remote-curl.c |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 50 insertions(+), 0 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index 0eb6fc4..0d7cf16 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -289,6 +289,7 @@ struct rpc_state {
 	int in;
 	int out;
 	struct strbuf result;
+	unsigned gzip_request : 1;
 };
 
 static size_t rpc_out(void *ptr, size_t eltsize,
@@ -327,6 +328,8 @@ 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
@@ -340,6 +343,7 @@ static int post_rpc(struct rpc_state *rpc)
 
 		if (left < LARGE_PACKET_MAX) {
 			large_request = 1;
+			use_gzip = 0;
 			break;
 		}
 
@@ -355,6 +359,7 @@ static int post_rpc(struct rpc_state *rpc)
 	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);
@@ -372,6 +377,49 @@ static int post_rpc(struct rpc_state *rpc)
 			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.
@@ -398,6 +446,7 @@ static int post_rpc(struct rpc_state *rpc)
 	}
 
 	curl_slist_free_all(headers);
+	free(gzip_body);
 	return err;
 }
 
@@ -523,6 +572,7 @@ static int fetch_git(struct discovery *heads,
 	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)
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 23/26] Smart fetch over HTTP: client side
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Daniel Barkalow
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

The git-remote-curl backend detects if the remote server supports
the git-upload-pack service, and if so, runs git-fetch-pack locally
in a pipe to generate the want/have commands.

The advertisements from the server that were obtained during the
discovery are passed into git-fetch-pack before the POST request
starts, permitting server capability discovery and enablement.

Common objects that are discovered are appended onto the request as
have lines and are sent again on the next request.  This allows the
remote side to reinitialize its in-memory list of common objects
during the next request.

Because all requests are relatively short, below git-remote-curl's
1 MiB buffer limit, requests will use the standard Content-Length
header and be valid HTTP/1.0 POST requests.  This makes the fetch
client more tolerant of proxy servers which don't support HTTP/1.1
or the chunked transfer encoding.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Daniel Barkalow <barkalow@iabervon.org>
---
 builtin-fetch-pack.c |  110 ++++++++++++++++++++++++++++++++++++++++++--------
 fetch-pack.h         |    3 +-
 remote-curl.c        |   69 +++++++++++++++++++++++++++++--
 3 files changed, 160 insertions(+), 22 deletions(-)

diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 615f549..8ed4a6f 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -165,6 +165,24 @@ enum ack_type {
 	ACK_ready
 };
 
+static void consume_shallow_list(int fd)
+{
+	if (args.stateless_rpc && args.depth > 0) {
+		/* If we sent a depth we will get back "duplicate"
+		 * shallow and unshallow commands every time there
+		 * is a block of have lines exchanged.
+		 */
+		char line[1000];
+		while (packet_read_line(fd, line, sizeof(line))) {
+			if (!prefixcmp(line, "shallow "))
+				continue;
+			if (!prefixcmp(line, "unshallow "))
+				continue;
+			die("git fetch-pack: expected shallow list");
+		}
+	}
+}
+
 static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
 	static char line[1000];
@@ -190,6 +208,15 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 	die("git fetch_pack: expected ACK/NAK, got '%s'", line);
 }
 
+static void send_request(int fd, struct strbuf *buf)
+{
+	if (args.stateless_rpc) {
+		send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
+		packet_flush(fd);
+	} else
+		safe_write(fd, buf->buf, buf->len);
+}
+
 static int find_common(int fd[2], unsigned char *result_sha1,
 		       struct ref *refs)
 {
@@ -199,7 +226,10 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 	unsigned in_vain = 0;
 	int got_continue = 0;
 	struct strbuf req_buf = STRBUF_INIT;
+	size_t state_len = 0;
 
+	if (args.stateless_rpc && multi_ack == 1)
+		die("--stateless-rpc requires multi_ack_detailed");
 	if (marked)
 		for_each_ref(clear_marks, NULL);
 	marked = 1;
@@ -256,13 +286,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 	if (args.depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args.depth);
 	packet_buf_flush(&req_buf);
-
-	safe_write(fd[1], req_buf.buf, req_buf.len);
+	state_len = req_buf.len;
 
 	if (args.depth > 0) {
 		char line[1024];
 		unsigned char sha1[20];
 
+		send_request(fd[1], &req_buf);
 		while (packet_read_line(fd[0], line, sizeof(line))) {
 			if (!prefixcmp(line, "shallow ")) {
 				if (get_sha1_hex(line + 8, sha1))
@@ -284,28 +314,40 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 			}
 			die("expected shallow/unshallow, got %s", line);
 		}
+	} else if (!args.stateless_rpc)
+		send_request(fd[1], &req_buf);
+
+	if (!args.stateless_rpc) {
+		/* If we aren't using the stateless-rpc interface
+		 * we don't need to retain the headers.
+		 */
+		strbuf_setlen(&req_buf, 0);
+		state_len = 0;
 	}
 
 	flushes = 0;
 	retval = -1;
 	while ((sha1 = get_rev())) {
-		packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
+		packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
 		if (args.verbose)
 			fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
 		in_vain++;
 		if (!(31 & ++count)) {
 			int ack;
 
-			packet_flush(fd[1]);
+			packet_buf_flush(&req_buf);
+			send_request(fd[1], &req_buf);
+			strbuf_setlen(&req_buf, state_len);
 			flushes++;
 
 			/*
 			 * We keep one window "ahead" of the other side, and
 			 * will wait for an ACK only on the next one
 			 */
-			if (count == 32)
+			if (!args.stateless_rpc && count == 32)
 				continue;
 
+			consume_shallow_list(fd[0]);
 			do {
 				ack = get_ack(fd[0], result_sha1);
 				if (args.verbose && ack)
@@ -322,6 +364,17 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 				case ACK_continue: {
 					struct commit *commit =
 						lookup_commit(result_sha1);
+					if (args.stateless_rpc
+					 && ack == ACK_common
+					 && !(commit->object.flags & COMMON)) {
+						/* We need to replay the have for this object
+						 * on the next RPC request so the peer knows
+						 * it is in common with us.
+						 */
+						const char *hex = sha1_to_hex(result_sha1);
+						packet_buf_write(&req_buf, "have %s\n", hex);
+						state_len = req_buf.len;
+					}
 					mark_common(commit, 0, 1);
 					retval = 0;
 					in_vain = 0;
@@ -339,7 +392,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 		}
 	}
 done:
-	packet_write(fd[1], "done\n");
+	packet_buf_write(&req_buf, "done\n");
+	send_request(fd[1], &req_buf);
 	if (args.verbose)
 		fprintf(stderr, "done\n");
 	if (retval != 0) {
@@ -348,6 +402,7 @@ done:
 	}
 	strbuf_release(&req_buf);
 
+	consume_shallow_list(fd[0]);
 	while (flushes || multi_ack) {
 		int ack = get_ack(fd[0], result_sha1);
 		if (ack) {
@@ -672,6 +727,8 @@ static struct ref *do_fetch_pack(int fd[2],
 			 */
 			warning("no common commits");
 
+	if (args.stateless_rpc)
+		packet_flush(fd[1]);
 	if (get_pack(fd, pack_lockfile))
 		die("git fetch-pack: fetch failed.");
 
@@ -742,6 +799,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 	struct ref *ref = NULL;
 	char *dest = NULL, **heads;
 	int fd[2];
+	char *pack_lockfile = NULL;
+	char **pack_lockfile_ptr = NULL;
 	struct child_process *conn;
 
 	nr_heads = 0;
@@ -791,6 +850,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 				args.no_progress = 1;
 				continue;
 			}
+			if (!strcmp("--stateless-rpc", arg)) {
+				args.stateless_rpc = 1;
+				continue;
+			}
+			if (!strcmp("--lock-pack", arg)) {
+				args.lock_pack = 1;
+				pack_lockfile_ptr = &pack_lockfile;
+				continue;
+			}
 			usage(fetch_pack_usage);
 		}
 		dest = (char *)arg;
@@ -801,19 +869,27 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 	if (!dest)
 		usage(fetch_pack_usage);
 
-	conn = git_connect(fd, (char *)dest, args.uploadpack,
-			   args.verbose ? CONNECT_VERBOSE : 0);
-	if (conn) {
-		get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
-
-		ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
-		close(fd[0]);
-		close(fd[1]);
-		if (finish_connect(conn))
-			ref = NULL;
+	if (args.stateless_rpc) {
+		conn = NULL;
+		fd[0] = 0;
+		fd[1] = 1;
 	} else {
-		ref = NULL;
+		conn = git_connect(fd, (char *)dest, args.uploadpack,
+				   args.verbose ? CONNECT_VERBOSE : 0);
+	}
+
+	get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+
+	ref = fetch_pack(&args, fd, conn, ref, dest,
+		nr_heads, heads, pack_lockfile_ptr);
+	if (pack_lockfile) {
+		printf("lock %s\n", pack_lockfile);
+		fflush(stdout);
 	}
+	close(fd[0]);
+	close(fd[1]);
+	if (finish_connect(conn))
+		ref = NULL;
 	ret = !ref;
 
 	if (!ret && nr_heads) {
diff --git a/fetch-pack.h b/fetch-pack.h
index 8bd9c32..fbe85ac 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -13,7 +13,8 @@ struct fetch_pack_args
 		fetch_all:1,
 		verbose:1,
 		no_progress:1,
-		include_tag:1;
+		include_tag:1,
+		stateless_rpc:1;
 };
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/remote-curl.c b/remote-curl.c
index f1206cb..0eb6fc4 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -45,7 +45,7 @@ static int set_option(const char *name, const char *value)
 			options.progress = 0;
 		else
 			return -1;
-		return 1 /* TODO implement later */;
+		return 0;
 	}
 	else if (!strcmp(name, "depth")) {
 		char *end;
@@ -53,7 +53,7 @@ static int set_option(const char *name, const char *value)
 		if (value == end || *end)
 			return -1;
 		options.depth = v;
-		return 1 /* TODO implement later */;
+		return 0;
 	}
 	else if (!strcmp(name, "followtags")) {
 		if (!strcmp(value, "true"))
@@ -62,7 +62,7 @@ static int set_option(const char *name, const char *value)
 			options.followtags = 0;
 		else
 			return -1;
-		return 1 /* TODO implement later */;
+		return 0;
 	}
 	else if (!strcmp(name, "dry-run")) {
 		if (!strcmp(value, "true"))
@@ -463,6 +463,8 @@ 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));
 
@@ -481,6 +483,65 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
 	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;
+
+	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;
@@ -523,7 +584,7 @@ static void parse_fetch(struct strbuf *buf)
 			break;
 	} while (1);
 
-	if (fetch_dumb(nr_heads, to_fetch))
+	if (fetch(nr_heads, to_fetch))
 		exit(128); /* error already reported */
 	free_refs(list_head);
 	free(to_fetch);
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 25/26] t5540-http-push: remove redundant fetches
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Tay Ray Chuan
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

From: Tay Ray Chuan <rctay89@gmail.com>

Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 t/t5540-http-push.sh |    2 --
 1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 2ece661..28a746e 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -116,9 +116,7 @@ test_expect_success 'create and delete remote branch' '
 	test_tick &&
 	git commit -m dev &&
 	git push origin dev &&
-	git fetch &&
 	git push origin :dev &&
-	git fetch &&
 	test_must_fail git show-ref --verify refs/remotes/origin/dev
 '
 
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 13/26] Git-aware CGI to provide dumb HTTP transport
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

The git-http-backend CGI can be configured into any Apache server
using ScriptAlias, such as with the following configuration:

  LoadModule cgi_module /usr/libexec/apache2/mod_cgi.so
  LoadModule alias_module /usr/libexec/apache2/mod_alias.so
  ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

Repositories are accessed via the translated PATH_INFO.

The CGI is backwards compatible with the dumb client, allowing all
older HTTP clients to continue to download repositories which are
managed by the CGI.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .gitignore                         |    1 +
 Documentation/git-http-backend.txt |  105 +++++++++++++
 Makefile                           |    1 +
 http-backend.c                     |  290 ++++++++++++++++++++++++++++++++++++
 4 files changed, 397 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-http-backend.txt
 create mode 100644 http-backend.c

diff --git a/.gitignore b/.gitignore
index 51a37b1..353d22f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,7 @@ git-get-tar-commit-id
 git-grep
 git-hash-object
 git-help
+git-http-backend
 git-http-fetch
 git-http-push
 git-imap-send
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
new file mode 100644
index 0000000..867675f
--- /dev/null
+++ b/Documentation/git-http-backend.txt
@@ -0,0 +1,105 @@
+git-http-backend(1)
+===================
+
+NAME
+----
+git-http-backend - Server side implementation of Git over HTTP
+
+SYNOPSIS
+--------
+[verse]
+'git-http-backend'
+
+DESCRIPTION
+-----------
+A simple CGI program to serve the contents of a Git repository to Git
+clients accessing the repository over http:// and https:// protocols.
+
+By default, only the `upload-pack` service is enabled, which serves
+'git-fetch-pack' and 'git-ls-remote' clients, which are invoked from
+'git-fetch', 'git-pull', and 'git-clone'.
+
+This is ideally suited for read-only updates, i.e., pulling from
+git repositories.
+
+URL TRANSLATION
+---------------
+'git-http-backend' relies on the invoking web server to perform
+URL to path translation, and store the repository path into the
+PATH_TRANSLATED environment variable.  Most web servers will do
+this translation automatically, resolving the suffix after the
+CGI name relative to the server's document root.
+
+EXAMPLES
+--------
+
+Apache 2.x::
+	To serve all Git repositories contained within the '/git/'
+	subdirectory of the DocumentRoot, ensure mod_cgi and
+	mod_alias are enabled, and create a ScriptAlias to the CGI:
++
+----------------------------------------------------------------
+ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/git/
+
+<Directory /usr/libexec/git-core>
+	Options None
+</Directory>
+<Files /usr/libexec/git-core/git-http-backend>
+	Options ExecCGI
+</Files>
+----------------------------------------------------------------
++
+To require authentication for reads, use a Directory
+directive around the repository, or one of its parent directories:
++
+----------------------------------------------------------------
+<Directory /var/www/git/private>
+	AuthType Basic
+	AuthName "Private Git Access"
+	Require group committers
+	...
+</Directory>
+----------------------------------------------------------------
+
+Accelerated static Apache 2.x::
+	Similar to the above, but Apache can be used to return static
+	files that are stored on disk.	On many systems this may
+	be more efficient as Apache can ask the kernel to copy the
+	file contents from the file system directly to the network:
++
+----------------------------------------------------------------
+DocumentRoot /var/www
+
+ScriptAlias /git/        /usr/libexec/git-core/git-http-backend/git/
+Alias       /git_static/ /var/www/git/
+
+RewriteEngine on
+RewriteRule ^/git/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$    /git_static/$1 [PT]
+RewriteRule ^/git/(.*/objects/pack/pack-[0-9a-f]{40}.pack)$ /git_static/$1 [PT]
+RewriteRule ^/git/(.*/objects/pack/pack-[0-9a-f]{40}.idx)$  /git_static/$1 [PT]
+----------------------------------------------------------------
+
+
+ENVIRONMENT
+-----------
+'git-http-backend' relies upon the CGI environment variables set
+by the invoking web server, including:
+
+* PATH_TRANSLATED
+* REMOTE_USER
+* REMOTE_ADDR
+* CONTENT_TYPE
+* QUERY_STRING
+* REQUEST_METHOD
+
+Author
+------
+Written by Shawn O. Pearce <spearce@spearce.org>.
+
+Documentation
+--------------
+Documentation by Shawn O. Pearce <spearce@spearce.org>.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index fea237b..271c290 100644
--- a/Makefile
+++ b/Makefile
@@ -365,6 +365,7 @@ PROGRAMS += git-show-index$X
 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.
diff --git a/http-backend.c b/http-backend.c
new file mode 100644
index 0000000..374f60d
--- /dev/null
+++ b/http-backend.c
@@ -0,0 +1,290 @@
+#include "cache.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include "object.h"
+#include "tag.h"
+#include "exec_cmd.h"
+
+static const char content_type[] = "Content-Type";
+static const char content_length[] = "Content-Length";
+static const char last_modified[] = "Last-Modified";
+
+static void format_write(int fd, const char *fmt, ...)
+{
+	static char buffer[1024];
+
+	va_list args;
+	unsigned n;
+
+	va_start(args, fmt);
+	n = vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+	if (n >= sizeof(buffer))
+		die("protocol error: impossibly long line");
+
+	safe_write(fd, buffer, n);
+}
+
+static void http_status(unsigned code, const char *msg)
+{
+	format_write(1, "Status: %u %s\r\n", code, msg);
+}
+
+static void hdr_str(const char *name, const char *value)
+{
+	format_write(1, "%s: %s\r\n", name, value);
+}
+
+static void hdr_int(const char *name, size_t value)
+{
+	format_write(1, "%s: %" PRIuMAX "\r\n", name, value);
+}
+
+static void hdr_date(const char *name, unsigned long when)
+{
+	const char *value = show_date(when, 0, DATE_RFC2822);
+	hdr_str(name, value);
+}
+
+static void hdr_nocache(void)
+{
+	hdr_str("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+	hdr_str("Pragma", "no-cache");
+	hdr_str("Cache-Control", "no-cache, max-age=0, must-revalidate");
+}
+
+static void hdr_cache_forever(void)
+{
+	unsigned long now = time(NULL);
+	hdr_date("Date", now);
+	hdr_date("Expires", now + 31536000);
+	hdr_str("Cache-Control", "public, max-age=31536000");
+}
+
+static void end_headers(void)
+{
+	safe_write(1, "\r\n", 2);
+}
+
+static NORETURN void not_found(const char *err, ...)
+{
+	va_list params;
+
+	http_status(404, "Not Found");
+	hdr_nocache();
+	end_headers();
+
+	va_start(params, err);
+	if (err && *err)
+		vfprintf(stderr, err, params);
+	va_end(params);
+	exit(0);
+}
+
+static void send_strbuf(const char *type, struct strbuf *buf)
+{
+	hdr_int(content_length, buf->len);
+	hdr_str(content_type, type);
+	end_headers();
+	safe_write(1, buf->buf, buf->len);
+}
+
+static void send_file(const char *the_type, const char *name)
+{
+	const char *p = git_path("%s", name);
+	size_t buf_alloc = 8192;
+	char *buf = xmalloc(buf_alloc);
+	int fd;
+	struct stat sb;
+	size_t size;
+
+	fd = open(p, O_RDONLY);
+	if (fd < 0)
+		not_found("Cannot open '%s': %s", p, strerror(errno));
+	if (fstat(fd, &sb) < 0)
+		die_errno("Cannot stat '%s'", p);
+
+	size = xsize_t(sb.st_size);
+
+	hdr_int(content_length, size);
+	hdr_str(content_type, the_type);
+	hdr_date(last_modified, sb.st_mtime);
+	end_headers();
+
+	while (size) {
+		ssize_t n = xread(fd, buf, buf_alloc);
+		if (n < 0)
+			die_errno("Cannot read '%s'", p);
+		if (!n)
+			break;
+		safe_write(1, buf, n);
+	}
+	close(fd);
+	free(buf);
+}
+
+static void get_text_file(char *name)
+{
+	hdr_nocache();
+	send_file("text/plain", name);
+}
+
+static void get_loose_object(char *name)
+{
+	hdr_cache_forever();
+	send_file("application/x-git-loose-object", name);
+}
+
+static void get_pack_file(char *name)
+{
+	hdr_cache_forever();
+	send_file("application/x-git-packed-objects", name);
+}
+
+static void get_idx_file(char *name)
+{
+	hdr_cache_forever();
+	send_file("application/x-git-packed-objects-toc", name);
+}
+
+static int show_text_ref(const char *name, const unsigned char *sha1,
+	int flag, void *cb_data)
+{
+	struct strbuf *buf = cb_data;
+	struct object *o = parse_object(sha1);
+	if (!o)
+		return 0;
+
+	strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name);
+	if (o->type == OBJ_TAG) {
+		o = deref_tag(o, name, 0);
+		if (!o)
+			return 0;
+		strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1), name);
+	}
+	return 0;
+}
+
+static void get_info_refs(char *arg)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	for_each_ref(show_text_ref, &buf);
+	hdr_nocache();
+	send_strbuf("text/plain", &buf);
+	strbuf_release(&buf);
+}
+
+static void get_info_packs(char *arg)
+{
+	size_t objdirlen = strlen(get_object_directory());
+	struct strbuf buf = STRBUF_INIT;
+	struct packed_git *p;
+	size_t cnt = 0;
+
+	prepare_packed_git();
+	for (p = packed_git; p; p = p->next) {
+		if (p->pack_local)
+			cnt++;
+	}
+
+	strbuf_grow(&buf, cnt * 53 + 2);
+	for (p = packed_git; p; p = p->next) {
+		if (p->pack_local)
+			strbuf_addf(&buf, "P %s\n", p->pack_name + objdirlen + 6);
+	}
+	strbuf_addch(&buf, '\n');
+
+	hdr_nocache();
+	send_strbuf("text/plain; charset=utf-8", &buf);
+	strbuf_release(&buf);
+}
+
+static NORETURN void die_webcgi(const char *err, va_list params)
+{
+	char buffer[1000];
+
+	http_status(500, "Internal Server Error");
+	hdr_nocache();
+	end_headers();
+
+	vsnprintf(buffer, sizeof(buffer), err, params);
+	fprintf(stderr, "fatal: %s\n", buffer);
+	exit(0);
+}
+
+static struct service_cmd {
+	const char *method;
+	const char *pattern;
+	void (*imp)(char *);
+} services[] = {
+	{"GET", "/HEAD$", get_text_file},
+	{"GET", "/info/refs$", get_info_refs},
+	{"GET", "/objects/info/alternates$", get_text_file},
+	{"GET", "/objects/info/http-alternates$", get_text_file},
+	{"GET", "/objects/info/packs$", get_info_packs},
+	{"GET", "/objects/info/[^/]*$", get_text_file},
+	{"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object},
+	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file},
+	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file}
+};
+
+int main(int argc, char **argv)
+{
+	char *method = getenv("REQUEST_METHOD");
+	char *dir = getenv("PATH_TRANSLATED");
+	struct service_cmd *cmd = NULL;
+	char *cmd_arg = NULL;
+	int i;
+
+	git_extract_argv0_path(argv[0]);
+	set_die_routine(die_webcgi);
+
+	if (!method)
+		die("No REQUEST_METHOD from server");
+	if (!strcmp(method, "HEAD"))
+		method = "GET";
+	if (!dir)
+		die("No PATH_TRANSLATED from server");
+
+	for (i = 0; i < ARRAY_SIZE(services); i++) {
+		struct service_cmd *c = &services[i];
+		regex_t re;
+		regmatch_t out[1];
+
+		if (regcomp(&re, c->pattern, REG_EXTENDED))
+			die("Bogus regex in service table: %s", c->pattern);
+		if (!regexec(&re, dir, 1, out, 0)) {
+			size_t n = out[0].rm_eo - out[0].rm_so;
+
+			if (strcmp(method, c->method)) {
+				const char *proto = getenv("SERVER_PROTOCOL");
+				if (proto && !strcmp(proto, "HTTP/1.1"))
+					http_status(405, "Method Not Allowed");
+				else
+					http_status(400, "Bad Request");
+				hdr_nocache();
+				end_headers();
+				return 0;
+			}
+
+			cmd = c;
+			cmd_arg = xmalloc(n);
+			strncpy(cmd_arg, dir + out[0].rm_so + 1, n);
+			cmd_arg[n] = '\0';
+			dir[out[0].rm_so] = 0;
+			break;
+		}
+		regfree(&re);
+	}
+
+	if (!cmd)
+		not_found("Request not supported: '%s'", dir);
+
+	setup_path();
+	if (!enter_repo(dir, 0))
+		not_found("Not a git repository: '%s'", dir);
+
+	cmd->imp(cmd_arg);
+	return 0;
+}
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 10/26] remote-helpers: Support custom transport options
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Daniel Barkalow
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

Some transports, like the native pack transport implemented by
fetch-pack, support useful features like depth or include tags.
These should be exposed if the underlying helper knows how to
use them.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Daniel Barkalow <barkalow@iabervon.org>
---
 Documentation/git-remote-helpers.txt |   38 +++++++++++++++
 remote-curl.c                        |   74 ++++++++++++++++++++++++++++-
 transport-helper.c                   |   88 +++++++++++++++++++++++++++++++++-
 3 files changed, 198 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index e44d821..1133f04 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -35,6 +35,16 @@ Commands are given by the caller on the helper's standard input, one per line.
 	the name; unrecognized attributes are ignored. After the
 	complete list, outputs a blank line.
 
+'option' <name> <value>::
+	Set the transport helper option <name> to <value>.  Outputs a
+	single line containing one of 'ok' (option successfully set),
+	'unsupported' (option not recognized) or 'error <msg>'
+	(option <name> is supported but <value> is not correct
+	for it).  Options should be set before other commands,
+	and may how those commands behave.
++
+Supported if the helper has the "option" capability.
+
 'fetch' <sha1> <name>::
 	Fetches the given object, writing the necessary objects
 	to the database.  Fetch commands are sent in a batch, one
@@ -63,11 +73,39 @@ CAPABILITIES
 'fetch'::
 	This helper supports the 'fetch' command.
 
+'option'::
+	This helper supports the option command.
+
 REF LIST ATTRIBUTES
 -------------------
 
 None are defined yet, but the caller must accept any which are supplied.
 
+OPTIONS
+-------
+'option verbosity' <N>::
+	Change the level of messages displayed by the helper.
+	When N is 0 the end-user has asked the process to be
+	quiet, and the helper should produce only error output.
+	N of 1 is the default level of verbosity, higher values
+	of N correspond to the number of -v flags passed on the
+	command line.
+
+'option progress' \{'true'|'false'\}::
+	Enable (or disable) progress messages displayed by the
+	transport helper during a command.
+
+'option depth' <depth>::
+	Deepen the history of a shallow repository.
+
+'option followtags' \{'true'|'false'\}::
+	If enabled the helper should automatically fetch annotated
+	tag objects if the object the tag points at was transferred
+	during the fetch command.  If the tag is not fetched by
+	the helper a second fetch command will usually be sent to
+	ask for the tag specifically.  Some helpers may be able to
+	use this option to avoid a second network connection.
+
 Documentation
 -------------
 Documentation by Daniel Barkalow.
diff --git a/remote-curl.c b/remote-curl.c
index 22cd5c5..0951f11 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -9,12 +9,61 @@ static struct remote *remote;
 static const char *url;
 static struct walker *walker;
 
+struct options {
+	int verbosity;
+	unsigned long depth;
+	unsigned progress : 1,
+		followtags : 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 1 /* TODO implement later */;
+	}
+	else if (!strcmp(name, "depth")) {
+		char *end;
+		unsigned long v = strtoul(value, &end, 10);
+		if (value == end || *end)
+			return -1;
+		options.depth = v;
+		return 1 /* TODO implement later */;
+	}
+	else if (!strcmp(name, "followtags")) {
+		if (!strcmp(value, "true"))
+			options.followtags = 1;
+		else if (!strcmp(value, "false"))
+			options.followtags = 0;
+		else
+			return -1;
+		return 1 /* TODO implement later */;
+	}
+	else {
+		return 1 /* unsupported */;
+	}
+}
+
 static struct ref *get_refs(void)
 {
 	struct strbuf buffer = STRBUF_INIT;
@@ -99,7 +148,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
 	walker->get_all = 1;
 	walker->get_tree = 1;
 	walker->get_history = 1;
-	walker->get_verbosely = 0;
+	walker->get_verbosely = options.verbosity >= 3;
 	walker->get_recover = 0;
 	ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
 
@@ -173,6 +222,9 @@ int main(int argc, const char **argv)
 		return 1;
 	}
 
+	options.verbosity = 1;
+	options.progress = !!isatty(2);
+
 	remote = remote_get(argv[1]);
 
 	if (argc > 2) {
@@ -198,8 +250,28 @@ int main(int argc, const char **argv)
 			}
 			printf("\n");
 			fflush(stdout);
+		} 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("\n");
 			fflush(stdout);
 		} else {
diff --git a/transport-helper.c b/transport-helper.c
index 9de3408..577abc6 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -5,13 +5,15 @@
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
+#include "quote.h"
 
 struct helper_data
 {
 	const char *name;
 	struct child_process *helper;
 	FILE *out;
-	unsigned fetch : 1;
+	unsigned fetch : 1,
+		option : 1;
 };
 
 static struct child_process *get_helper(struct transport *transport)
@@ -48,6 +50,8 @@ static struct child_process *get_helper(struct transport *transport)
 			break;
 		if (!strcmp(buf.buf, "fetch"))
 			data->fetch = 1;
+		if (!strcmp(buf.buf, "option"))
+			data->option = 1;
 	}
 	return data->helper;
 }
@@ -65,9 +69,88 @@ static int disconnect_helper(struct transport *transport)
 		free(data->helper);
 		data->helper = NULL;
 	}
+	free(data);
 	return 0;
 }
 
+static const char *unsupported_options[] = {
+	TRANS_OPT_UPLOADPACK,
+	TRANS_OPT_RECEIVEPACK,
+	TRANS_OPT_THIN,
+	TRANS_OPT_KEEP
+	};
+static const char *boolean_options[] = {
+	TRANS_OPT_THIN,
+	TRANS_OPT_KEEP,
+	TRANS_OPT_FOLLOWTAGS
+	};
+
+static int set_helper_option(struct transport *transport,
+			  const char *name, const char *value)
+{
+	struct helper_data *data = transport->data;
+	struct child_process *helper = get_helper(transport);
+	struct strbuf buf = STRBUF_INIT;
+	int i, ret, is_bool = 0;
+
+	if (!data->option)
+		return 1;
+
+	for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
+		if (!strcmp(name, unsupported_options[i]))
+			return 1;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
+		if (!strcmp(name, boolean_options[i])) {
+			is_bool = 1;
+			break;
+		}
+	}
+
+	strbuf_addf(&buf, "option %s ", name);
+	if (is_bool)
+		strbuf_addstr(&buf, value ? "true" : "false");
+	else
+		quote_c_style(value, &buf, NULL, 0);
+	strbuf_addch(&buf, '\n');
+
+	if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
+		die_errno("cannot send option to %s", data->name);
+
+	strbuf_reset(&buf);
+	if (strbuf_getline(&buf, data->out, '\n') == EOF)
+		exit(128); /* child died, message supplied already */
+
+	if (!strcmp(buf.buf, "ok"))
+		ret = 0;
+	else if (!prefixcmp(buf.buf, "error")) {
+		ret = -1;
+	} else if (!strcmp(buf.buf, "unsupported"))
+		ret = 1;
+	else {
+		warning("%s unexpectedly said: '%s'", data->name, buf.buf);
+		ret = 1;
+	}
+	strbuf_release(&buf);
+	return ret;
+}
+
+static void standard_options(struct transport *t)
+{
+	char buf[16];
+	int n;
+	int v = t->verbose;
+	int no_progress = v < 0 || (!t->progress && !isatty(1));
+
+	set_helper_option(t, "progress", !no_progress ? "true" : "false");
+
+	n = snprintf(buf, sizeof(buf), "%d", v + 1);
+	if (n >= sizeof(buf))
+		die("impossibly large verbosity value");
+	set_helper_option(t, "verbosity", buf);
+}
+
 static int fetch_with_fetch(struct transport *transport,
 			    int nr_heads, const struct ref **to_fetch)
 {
@@ -75,6 +158,8 @@ static int fetch_with_fetch(struct transport *transport,
 	int i;
 	struct strbuf buf = STRBUF_INIT;
 
+	standard_options(transport);
+
 	for (i = 0; i < nr_heads; i++) {
 		const struct ref *posn = to_fetch[i];
 		if (posn->status & REF_STATUS_UPTODATE)
@@ -178,6 +263,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 	data->name = name;
 
 	transport->data = data;
+	transport->set_option = set_helper_option;
 	transport->get_refs_list = get_refs_list;
 	transport->fetch = fetch;
 	transport->disconnect = disconnect_helper;
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 01/26] http-push: fix check condition on http.c::finish_http_pack_request()
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Tay Ray Chuan
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

From: Tay Ray Chuan <rctay89@gmail.com>

Check that http.c::finish_http_pack_request() returns 0 (for success).

Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 http-push.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/http-push.c b/http-push.c
index 00e83dc..cc5d4b8 100644
--- a/http-push.c
+++ b/http-push.c
@@ -604,7 +604,7 @@ static void finish_request(struct transfer_request *request)
 			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);
 			}
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 09/26] remote-helpers: Fetch more than one ref in a batch
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Daniel Barkalow
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

Some network protocols (e.g. native git://) are able to fetch more
than one ref at a time and reduce the overall transfer cost by
combining the requests into a single exchange.  Instead of feeding
each fetch request one at a time to the helper, feed all of them
at once so the helper can decide whether or not it should batch them.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Daniel Barkalow <barkalow@iabervon.org>
---
 Documentation/git-remote-helpers.txt |   14 ++++--
 remote-curl.c                        |   88 +++++++++++++++++++++++++++++----
 transport-helper.c                   |   39 +++++++++++----
 3 files changed, 115 insertions(+), 26 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 173ee23..e44d821 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -36,10 +36,16 @@ Commands are given by the caller on the helper's standard input, one per line.
 	complete list, outputs a blank line.
 
 'fetch' <sha1> <name>::
-	Fetches the given object, writing the necessary objects to the
-	database. Outputs a blank line when the fetch is
-	complete. Only objects which were reported in the ref list
-	with a sha1 may be fetched this way.
+	Fetches the given object, writing the necessary objects
+	to the database.  Fetch commands are sent in a batch, one
+	per line, and the batch is terminated with a blank line.
+	Outputs a single blank line when all fetch commands in the
+	same batch are complete. Only objects which were reported
+	in the ref list with a sha1 may be fetched this way.
++
+Optionally may output a 'lock <file>' line indicating a file under
+GIT_DIR/objects/pack which is keeping a pack until refs can be
+suitably updated.
 +
 Supported if the helper has the "fetch" capability.
 
diff --git a/remote-curl.c b/remote-curl.c
index 478f3ea..22cd5c5 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -87,6 +87,81 @@ static struct ref *get_refs(void)
 	return refs;
 }
 
+static int fetch_dumb(int nr_heads, struct ref **to_fetch)
+{
+	char **targets = xmalloc(nr_heads * sizeof(char*));
+	int ret, i;
+
+	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 = 0;
+	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 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_dumb(nr_heads, to_fetch))
+		exit(128); /* error already reported */
+	free_refs(list_head);
+	free(to_fetch);
+
+	printf("\n");
+	fflush(stdout);
+	strbuf_reset(buf);
+}
+
 int main(int argc, const char **argv)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -110,17 +185,8 @@ int main(int argc, const char **argv)
 		if (strbuf_getline(&buf, stdin, '\n') == EOF)
 			break;
 		if (!prefixcmp(buf.buf, "fetch ")) {
-			char *obj = buf.buf + strlen("fetch ");
-			init_walker();
-			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);
+			parse_fetch(&buf);
+
 		} else if (!strcmp(buf.buf, "list")) {
 			struct ref *refs = get_refs();
 			struct ref *posn;
diff --git a/transport-helper.c b/transport-helper.c
index f57e84c..9de3408 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -10,6 +10,7 @@ struct helper_data
 {
 	const char *name;
 	struct child_process *helper;
+	FILE *out;
 	unsigned fetch : 1;
 };
 
@@ -18,7 +19,6 @@ static struct child_process *get_helper(struct transport *transport)
 	struct helper_data *data = transport->data;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process *helper;
-	FILE *file;
 
 	if (data->helper)
 		return data->helper;
@@ -39,9 +39,9 @@ static struct child_process *get_helper(struct transport *transport)
 
 	write_str_in_full(helper->in, "capabilities\n");
 
-	file = xfdopen(helper->out, "r");
+	data->out = xfdopen(helper->out, "r");
 	while (1) {
-		if (strbuf_getline(&buf, file, '\n') == EOF)
+		if (strbuf_getline(&buf, data->out, '\n') == EOF)
 			exit(128); /* child died, message supplied already */
 
 		if (!*buf.buf)
@@ -58,6 +58,7 @@ static int disconnect_helper(struct transport *transport)
 	if (data->helper) {
 		write_str_in_full(data->helper->in, "\n");
 		close(data->helper->in);
+		fclose(data->out);
 		finish_command(data->helper);
 		free((char *)data->helper->argv[0]);
 		free(data->helper->argv);
@@ -70,8 +71,7 @@ static int disconnect_helper(struct transport *transport)
 static int fetch_with_fetch(struct transport *transport,
 			    int nr_heads, const struct ref **to_fetch)
 {
-	struct child_process *helper = get_helper(transport);
-	FILE *file = xfdopen(helper->out, "r");
+	struct helper_data *data = transport->data;
 	int i;
 	struct strbuf buf = STRBUF_INIT;
 
@@ -82,12 +82,30 @@ static int fetch_with_fetch(struct transport *transport,
 
 		strbuf_addf(&buf, "fetch %s %s\n",
 			    sha1_to_hex(posn->old_sha1), posn->name);
-		write_in_full(helper->in, buf.buf, buf.len);
-		strbuf_reset(&buf);
+	}
 
-		if (strbuf_getline(&buf, file, '\n') == EOF)
+	strbuf_addch(&buf, '\n');
+	if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
+		die_errno("cannot send fetch to %s", data->name);
+
+	while (1) {
+		strbuf_reset(&buf);
+		if (strbuf_getline(&buf, data->out, '\n') == EOF)
 			exit(128); /* child died, message supplied already */
+
+		if (!prefixcmp(buf.buf, "lock ")) {
+			const char *name = buf.buf + 5;
+			if (transport->pack_lockfile)
+				warning("%s also locked %s", data->name, name);
+			else
+				transport->pack_lockfile = xstrdup(name);
+		}
+		else if (!buf.len)
+			break;
+		else
+			warning("%s unexpectedly said: '%s'", data->name, buf.buf);
 	}
+	strbuf_release(&buf);
 	return 0;
 }
 
@@ -113,21 +131,20 @@ static int fetch(struct transport *transport,
 
 static struct ref *get_refs_list(struct transport *transport, int for_push)
 {
+	struct helper_data *data = transport->data;
 	struct child_process *helper;
 	struct ref *ret = NULL;
 	struct ref **tail = &ret;
 	struct ref *posn;
 	struct strbuf buf = STRBUF_INIT;
-	FILE *file;
 
 	helper = get_helper(transport);
 
 	write_str_in_full(helper->in, "list\n");
 
-	file = xfdopen(helper->out, "r");
 	while (1) {
 		char *eov, *eon;
-		if (strbuf_getline(&buf, file, '\n') == EOF)
+		if (strbuf_getline(&buf, data->out, '\n') == EOF)
 			exit(128); /* child died, message supplied already */
 
 		if (!*buf.buf)
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 08/26] fetch: Allow transport -v -v -v to set verbosity to 3
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Daniel Barkalow
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

Helpers might want a higher level of verbosity than just +1 (the
porcelain default setting) and +2 (-v -v).  Expand the field to
allow verbosity in the range -1..3.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Daniel Barkalow <barkalow@iabervon.org>
---
 builtin-fetch.c |    2 +-
 transport.h     |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin-fetch.c b/builtin-fetch.c
index cb48c57..52a9a42 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -665,7 +665,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
 	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 --git a/transport.h b/transport.h
index c14da6f..e4e6177 100644
--- a/transport.h
+++ b/transport.h
@@ -25,7 +25,7 @@ struct transport {
 
 	int (*disconnect)(struct transport *connection);
 	char *pack_lockfile;
-	signed verbose : 2;
+	signed verbose : 3;
 	/* Force progress even if the output is not a tty */
 	unsigned progress : 1;
 };
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 07/26] remote-curl: Refactor walker initialization
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git; +Cc: Daniel Barkalow
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

We will need the walker, url and remote in other functions as the
code grows larger to support smart HTTP.  Extract this out into a
set of globals we can easily reference once configured.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
CC: Daniel Barkalow <barkalow@iabervon.org>
---
 remote-curl.c |   24 ++++++++++++++----------
 1 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index 2faf1c6..478f3ea 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -5,7 +5,17 @@
 #include "http.h"
 #include "exec_cmd.h"
 
-static struct ref *get_refs(struct walker *walker, const char *url)
+static struct remote *remote;
+static const char *url;
+static struct walker *walker;
+
+static void init_walker(void)
+{
+	if (!walker)
+		walker = get_http_walker(url, remote);
+}
+
+static struct ref *get_refs(void)
 {
 	struct strbuf buffer = STRBUF_INIT;
 	char *data, *start, *mid;
@@ -21,6 +31,7 @@ static struct ref *get_refs(struct walker *walker, const char *url)
 	refs_url = xmalloc(strlen(url) + 11);
 	sprintf(refs_url, "%s/info/refs", url);
 
+	init_walker();
 	http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
 	switch (http_ret) {
 	case HTTP_OK:
@@ -78,10 +89,7 @@ static struct ref *get_refs(struct walker *walker, const char *url)
 
 int main(int argc, const char **argv)
 {
-	struct remote *remote;
 	struct strbuf buf = STRBUF_INIT;
-	const char *url;
-	struct walker *walker = NULL;
 
 	git_extract_argv0_path(argv[0]);
 	setup_git_directory();
@@ -103,8 +111,7 @@ int main(int argc, const char **argv)
 			break;
 		if (!prefixcmp(buf.buf, "fetch ")) {
 			char *obj = buf.buf + strlen("fetch ");
-			if (!walker)
-				walker = get_http_walker(url, remote);
+			init_walker();
 			walker->get_all = 1;
 			walker->get_tree = 1;
 			walker->get_history = 1;
@@ -115,11 +122,8 @@ int main(int argc, const char **argv)
 			printf("\n");
 			fflush(stdout);
 		} else if (!strcmp(buf.buf, "list")) {
-			struct ref *refs;
+			struct ref *refs = get_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);
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 04/26] fetch-pack: Use a strbuf to compose the want list
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

This change is being offered as a refactoring to make later
commits in the smart HTTP series easier.

By changing the enabled capabilities to be formatted in a strbuf
it is easier to add a new capability to the set of supported
capabilities.

By formatting the want portion of the request into a strbuf and
writing it as a whole block we can later decide to hold onto
the req_buf (instead of releasing it) to recycle in stateless
communications.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 builtin-fetch-pack.c |   52 ++++++++++++++++++++++++++++++++-----------------
 commit.c             |   10 +++-----
 commit.h             |    2 +-
 3 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c
index 629735f..783c2b0 100644
--- a/builtin-fetch-pack.c
+++ b/builtin-fetch-pack.c
@@ -165,6 +165,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 	const unsigned char *sha1;
 	unsigned in_vain = 0;
 	int got_continue = 0;
+	struct strbuf req_buf = STRBUF_INIT;
 
 	if (marked)
 		for_each_ref(clear_marks, NULL);
@@ -175,6 +176,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 	fetching = 0;
 	for ( ; refs ; refs = refs->next) {
 		unsigned char *remote = refs->old_sha1;
+		const char *remote_hex;
 		struct object *o;
 
 		/*
@@ -192,27 +194,36 @@ static int find_common(int fd[2], unsigned char *result_sha1,
 			continue;
 		}
 
-		if (!fetching)
-			packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n",
-				     sha1_to_hex(remote),
-				     (multi_ack ? " multi_ack" : ""),
-				     (use_sideband == 2 ? " side-band-64k" : ""),
-				     (use_sideband == 1 ? " side-band" : ""),
-				     (args.use_thin_pack ? " thin-pack" : ""),
-				     (args.no_progress ? " no-progress" : ""),
-				     (args.include_tag ? " include-tag" : ""),
-				     (prefer_ofs_delta ? " ofs-delta" : ""));
-		else
-			packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
+		remote_hex = sha1_to_hex(remote);
+		if (!fetching) {
+			struct strbuf c = STRBUF_INIT;
+			if (multi_ack)          strbuf_addstr(&c, " multi_ack");
+			if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
+			if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
+			if (args.use_thin_pack) strbuf_addstr(&c, " thin-pack");
+			if (args.no_progress)   strbuf_addstr(&c, " no-progress");
+			if (args.include_tag)   strbuf_addstr(&c, " include-tag");
+			if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
+			packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
+			strbuf_release(&c);
+		} else
+			packet_buf_write(&req_buf, "want %s\n", remote_hex);
 		fetching++;
 	}
+
+	if (!fetching) {
+		strbuf_release(&req_buf);
+		packet_flush(fd[1]);
+		return 1;
+	}
+
 	if (is_repository_shallow())
-		write_shallow_commits(fd[1], 1);
+		write_shallow_commits(&req_buf, 1);
 	if (args.depth > 0)
-		packet_write(fd[1], "deepen %d", args.depth);
-	packet_flush(fd[1]);
-	if (!fetching)
-		return 1;
+		packet_buf_write(&req_buf, "deepen %d", args.depth);
+	packet_buf_flush(&req_buf);
+
+	safe_write(fd[1], req_buf.buf, req_buf.len);
 
 	if (args.depth > 0) {
 		char line[1024];
@@ -296,6 +307,8 @@ done:
 		multi_ack = 0;
 		flushes++;
 	}
+	strbuf_release(&req_buf);
+
 	while (flushes || multi_ack) {
 		int ack = get_ack(fd[0], result_sha1);
 		if (ack) {
@@ -809,6 +822,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
 
 	if (args.depth > 0) {
 		struct cache_time mtime;
+		struct strbuf sb = STRBUF_INIT;
 		char *shallow = git_path("shallow");
 		int fd;
 
@@ -826,12 +840,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
 
 		fd = hold_lock_file_for_update(&lock, shallow,
 					       LOCK_DIE_ON_ERROR);
-		if (!write_shallow_commits(fd, 0)) {
+		if (!write_shallow_commits(&sb, 0)
+		 || write_in_full(fd, sb.buf, sb.len) != sb.len) {
 			unlink_or_warn(shallow);
 			rollback_lock_file(&lock);
 		} else {
 			commit_lock_file(&lock);
 		}
+		strbuf_release(&sb);
 	}
 
 	reprepare_packed_git();
diff --git a/commit.c b/commit.c
index fedbd5e..471efb0 100644
--- a/commit.c
+++ b/commit.c
@@ -199,7 +199,7 @@ struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
 	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 @@ int write_shallow_commits(int fd, int use_pack_protocol)
 				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 --git a/commit.h b/commit.h
index f4fc5c5..817c75c 100644
--- a/commit.h
+++ b/commit.h
@@ -131,7 +131,7 @@ extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 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);
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [RFC PATCH v4 02/26] pkt-line: Add strbuf based functions
From: Shawn O. Pearce @ 2009-10-29  0:00 UTC (permalink / raw)
  To: git
In-Reply-To: <1256774448-7625-1-git-send-email-spearce@spearce.org>

These routines help to work with pkt-line values inside of a strbuf,
permitting simple formatting of buffered network messages.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 pkt-line.c |   84 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 pkt-line.h |    4 +++
 2 files changed, 76 insertions(+), 12 deletions(-)

diff --git a/pkt-line.c b/pkt-line.c
index b691abe..bd603f8 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -42,17 +42,19 @@ void packet_flush(int fd)
 	safe_write(fd, "0000", 4);
 }
 
+void packet_buf_flush(struct strbuf *buf)
+{
+	strbuf_add(buf, "0000", 4);
+}
+
 #define hex(a) (hexchar[(a) & 15])
-void packet_write(int fd, const char *fmt, ...)
+static char buffer[1000];
+static unsigned format_packet(const char *fmt, va_list args)
 {
-	static char buffer[1000];
 	static char hexchar[] = "0123456789abcdef";
-	va_list args;
 	unsigned n;
 
-	va_start(args, fmt);
 	n = vsnprintf(buffer + 4, sizeof(buffer) - 4, fmt, args);
-	va_end(args);
 	if (n >= sizeof(buffer)-4)
 		die("protocol error: impossibly long line");
 	n += 4;
@@ -60,9 +62,31 @@ void packet_write(int fd, const char *fmt, ...)
 	buffer[1] = hex(n >> 8);
 	buffer[2] = hex(n >> 4);
 	buffer[3] = hex(n);
+	return n;
+}
+
+void packet_write(int fd, const char *fmt, ...)
+{
+	va_list args;
+	unsigned n;
+
+	va_start(args, fmt);
+	n = format_packet(fmt, args);
+	va_end(args);
 	safe_write(fd, buffer, n);
 }
 
+void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
+{
+	va_list args;
+	unsigned n;
+
+	va_start(args, fmt);
+	n = format_packet(fmt, args);
+	va_end(args);
+	strbuf_add(buf, buffer, n);
+}
+
 static void safe_read(int fd, void *buffer, unsigned size)
 {
 	ssize_t ret = read_in_full(fd, buffer, size);
@@ -72,15 +96,11 @@ static void safe_read(int fd, void *buffer, unsigned size)
 		die("The remote end hung up unexpectedly");
 }
 
-int packet_read_line(int fd, char *buffer, unsigned size)
+static int packet_length(const char *linelen)
 {
 	int n;
-	unsigned len;
-	char linelen[4];
-
-	safe_read(fd, linelen, 4);
+	int len = 0;
 
-	len = 0;
 	for (n = 0; n < 4; n++) {
 		unsigned char c = linelen[n];
 		len <<= 4;
@@ -96,8 +116,20 @@ int packet_read_line(int fd, char *buffer, unsigned size)
 			len += c - 'A' + 10;
 			continue;
 		}
-		die("protocol error: bad line length character");
+		return -1;
 	}
+	return len;
+}
+
+int packet_read_line(int fd, char *buffer, unsigned size)
+{
+	int len;
+	char linelen[4];
+
+	safe_read(fd, linelen, 4);
+	len = packet_length(linelen);
+	if (len < 0)
+		die("protocol error: bad line length character");
 	if (!len)
 		return 0;
 	len -= 4;
@@ -107,3 +139,31 @@ int packet_read_line(int fd, char *buffer, unsigned size)
 	buffer[len] = 0;
 	return len;
 }
+
+int packet_get_line(struct strbuf *out,
+	char **src_buf, size_t *src_len)
+{
+	int len;
+
+	if (*src_len < 4)
+		return -1;
+	len = packet_length(*src_buf);
+	if (len < 0)
+		return -1;
+	if (!len) {
+		*src_buf += 4;
+		*src_len -= 4;
+		return 0;
+	}
+	if (*src_len < len)
+		return -2;
+
+	*src_buf += 4;
+	*src_len -= 4;
+	len -= 4;
+
+	strbuf_add(out, *src_buf, len);
+	*src_buf += len;
+	*src_len -= len;
+	return len;
+}
diff --git a/pkt-line.h b/pkt-line.h
index 9df653f..1e5dcfe 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -2,14 +2,18 @@
 #define PKTLINE_H
 
 #include "git-compat-util.h"
+#include "strbuf.h"
 
 /*
  * Silly packetized line writing interface
  */
 void packet_flush(int fd);
 void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+void packet_buf_flush(struct strbuf *buf);
+void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 
 int packet_read_line(int fd, char *buffer, unsigned size);
+int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len);
 ssize_t safe_write(int, const void *, ssize_t);
 
 #endif
-- 
1.6.5.2.181.gd6f41

^ permalink raw reply related

* [PATCH] git-rebase -i: improve usage message
From: Brian Ewins @ 2009-10-29  0:02 UTC (permalink / raw)
  To: git; +Cc: kusmabite, Brian Ewins

The usage message was confusing as it implied that interactive
mode was optional but the default. Change the message to more
appropriately report usage when the -i flag is supplied.
In addition, use the same division into 3 command formats as
the man page.

Signed-off-by: Brian Ewins <brian.ewins@gmail.com>
---
 git-rebase--interactive.sh |   13 +++++++------
 1 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index a1879e3..b988c30 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -12,22 +12,23 @@
 
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
-git-rebase [-i] [options] [--] <upstream> [<branch>]
-git-rebase [-i] (--continue | --abort | --skip)
+git rebase -i [options] [--onto <newbase>] [--] <upstream> [<branch>]
+git rebase -i [options] --onto <newbase> --root [--] [<branch>]
+git rebase (--continue | --abort | --skip)
 --
  Available options are
 v,verbose          display a diffstat of what changed upstream
 onto=              rebase onto given branch instead of upstream
 p,preserve-merges  try to recreate merges instead of ignoring them
 s,strategy=        use the given merge strategy
-m,merge            always used (no-op)
-i,interactive      always used (no-op)
+i,interactive      interactively edit commits. Implies -m.
+m,merge            use merging strategies
+no-verify          override pre-rebase hook from stopping the operation
+root               rebase all reachable commmits up to the root(s)
  Actions:
 continue           continue rebasing process
 abort              abort rebasing process and restore original branch
 skip               skip current patch and continue rebasing process
-no-verify          override pre-rebase hook from stopping the operation
-root               rebase all reachable commmits up to the root(s)
 "
 
 . git-sh-setup
-- 
1.6.5

^ permalink raw reply related

* Re: [PATCH] imap-send.c: fix pointer to be const
From: Vietor Liu @ 2009-10-29  0:13 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Michael J Gruber, git
In-Reply-To: <7vhbtjo10s.fsf@alter.siamese.dyndns.org>

On Wed, 2009-10-28 at 10:56 -0700, Junio C Hamano wrote:
> Michael J Gruber <git@drmicha.warpmail.net> writes:
> 
> > Since this is only about warnings, maybe git 1.7.0 is the right time
> > frame to adjust this to the upcoming standard?
> 
> This does not look like "one group wants this way, but the others want
> differently.  We have to pick one and sacrifice the other because it is
> impossible to have it both ways"; there is no excuse to bring up 1.7.0 for
> something like this.
> 
> Doesn't inclusing "ssl.h" give us some indication whether "const" is
> needed to allow us to use #if/#else/#endif in order to compile with
> headers from either versions?  I.e. something like...
> 
> diff --git a/imap-send.c b/imap-send.c
> index 3847fd1..a199db8 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -273,7 +273,11 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
>  	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
>  	return -1;
>  #else
> +#if (OPENSSL_VERSION_NUMBER >= 0x1000000fL)
> +	const SSL_METHOD *meth;
> +#else
>  	SSL_METHOD *meth;
> +#endif
>  	SSL_CTX *ctx;
>  	int ret;
>  

It's better than my patch, thanks.

^ permalink raw reply

* [PATCH 0/3] increase user-friendliness
From: Jeff King @ 2009-10-29  0:22 UTC (permalink / raw)
  To: git

Git has a reputation as being unfriendly to users. Let's fix that by
adding some features implemented by more user-friendly programs.

-Peff

^ permalink raw reply

* [PATCH 1/3] add splash screen
From: Jeff King @ 2009-10-29  0:24 UTC (permalink / raw)
  To: git
In-Reply-To: <20091029002229.GA986@sigill.intra.peff.net>

Because bash completion is so slow to start, we need to
entertain users with a splash screen, so reuse the one from
git-gui.

Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>
Signed-off-by: Sam Vilain <sam@vilain.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Nick Edelen <sirnot@gmail.com>
Signed-off-by: "J.H." <warthog9@kernel.org>
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
---
 .gitignore    |    1 +
 Makefile      |    3 +++
 git-splash.sh |    4 ++++
 git.c         |    6 ++++++
 4 files changed, 14 insertions(+), 0 deletions(-)
 create mode 100644 git-splash.sh

diff --git a/.gitignore b/.gitignore
index 51a37b1..1e547e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -190,3 +190,4 @@ cscope*
 *.pdb
 Debug/
 Release/
+git-splash
diff --git a/Makefile b/Makefile
index fea237b..36e1a61 100644
--- a/Makefile
+++ b/Makefile
@@ -329,6 +329,7 @@ SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-sh-setup.sh
+SCRIPT_SH += git-splash.sh
 SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -1352,6 +1353,7 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+sharedir_SQ = $(subst ','\'',$(sharedir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
@@ -1428,6 +1430,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
 	    -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+	    -e 's|@@SHAREDIR@@|$(sharedir_SQ)|' \
 	    -e $(BROKEN_PATH_FIX) \
 	    $@.sh >$@+ && \
 	chmod +x $@+ && \
diff --git a/git-splash.sh b/git-splash.sh
new file mode 100644
index 0000000..fc2e6f7
--- /dev/null
+++ b/git-splash.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo 'source @@SHAREDIR@@/git-gui/lib/logo.tcl; pack [git_logo .logo]; after 3000 exit' |
+wish
diff --git a/git.c b/git.c
index 9883009..86dcfee 100644
--- a/git.c
+++ b/git.c
@@ -459,6 +459,12 @@ int main(int argc, const char **argv)
 	if (!cmd)
 		cmd = "git-help";
 
+	if (!getenv("GIT_NOSPLASH") && !(argv[1] && !strcmp(argv[1], "splash"))) {
+		const char *a[] = { "splash", NULL };
+		const char *e[] = { "GIT_NOSPLASH=1", NULL };
+		run_command_v_opt_cd_env(a, RUN_GIT_CMD, NULL, e);
+	}
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
-- 
1.6.5.1.3.g9d77a

^ permalink raw reply related

* [PATCH 2/3] add config-mak git command
From: Jeff King @ 2009-10-29  0:24 UTC (permalink / raw)
  To: git
In-Reply-To: <20091029002229.GA986@sigill.intra.peff.net>

This records the contents of your config.mak at build time
and prints them later.

Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>
Signed-off-by: Sam Vilain <sam@vilain.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Nick Edelen <sirnot@gmail.com>
Signed-off-by: "J.H." <warthog9@kernel.org>
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
---
 .gitignore |    1 +
 Makefile   |    8 ++++++++
 builtin.h  |    1 +
 git.c      |    1 +
 help.c     |    7 +++++++
 5 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore
index 1e547e7..cf0d8b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -191,3 +191,4 @@ cscope*
 Debug/
 Release/
 git-splash
+config-mak.c
diff --git a/Makefile b/Makefile
index 36e1a61..ae4b9fc 100644
--- a/Makefile
+++ b/Makefile
@@ -480,6 +480,7 @@ LIB_OBJS += color.o
 LIB_OBJS += combine-diff.o
 LIB_OBJS += commit.o
 LIB_OBJS += config.o
+LIB_OBJS += config-mak.o
 LIB_OBJS += connect.o
 LIB_OBJS += convert.o
 LIB_OBJS += copy.o
@@ -1931,6 +1932,13 @@ coverage-clean:
 COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
 COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
 
+config-mak.c: config.mak
+	(echo 'const char *config_mak ='; \
+	 sed < config.mak -e 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$$/\\n"/'; \
+	 echo ';' \
+	) >$@+
+	mv $@+ $@
+
 coverage-build: coverage-clean
 	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
 	$(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
diff --git a/builtin.h b/builtin.h
index a2174dc..2ac8f3a 100644
--- a/builtin.h
+++ b/builtin.h
@@ -113,5 +113,6 @@ extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
 extern int cmd_replace(int argc, const char **argv, const char *prefix);
+extern int cmd_config_mak(int argc, const char **argv, const char *prefix);
 
 #endif
diff --git a/git.c b/git.c
index 86dcfee..01ddf06 100644
--- a/git.c
+++ b/git.c
@@ -368,6 +368,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "verify-pack", cmd_verify_pack },
 		{ "show-ref", cmd_show_ref, RUN_SETUP },
 		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
+		{ "config-mak", cmd_config_mak, },
 	};
 	int i;
 	static const char ext[] = STRIP_EXTENSION;
diff --git a/help.c b/help.c
index e8db31f..422259a 100644
--- a/help.c
+++ b/help.c
@@ -365,3 +365,10 @@ int cmd_version(int argc, const char **argv, const char *prefix)
 	printf("git version %s\n", git_version_string);
 	return 0;
 }
+
+extern const char *config_mak;
+int cmd_config_mak(int argc, const char **argv, const char *prefix)
+{
+	fputs(config_mak, stdout);
+	return 0;
+}
-- 
1.6.5.1.3.g9d77a

^ permalink raw reply related

* [PATCH 3/3] add autoupdate feature
From: Jeff King @ 2009-10-29  0:24 UTC (permalink / raw)
  To: git
In-Reply-To: <20091029002229.GA986@sigill.intra.peff.net>

Users can't be bothered to keep their software up to date, so
we must do it for them.  Whenever any git command is
invoked, this patch checks for new releases of git at
kernel.org, and automatically upgrades your version of git.

Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>
Signed-off-by: Sam Vilain <sam@vilain.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Nick Edelen <sirnot@gmail.com>
Signed-off-by: "J.H." <warthog9@kernel.org>
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
---
 .gitignore          |    1 +
 Makefile            |    1 +
 git-autoupdate.perl |   58 +++++++++++++++++++++++++++++++++++++++++++++++++++
 git.c               |    9 +++++++-
 4 files changed, 68 insertions(+), 1 deletions(-)
 create mode 100644 git-autoupdate.perl

diff --git a/.gitignore b/.gitignore
index cf0d8b9..5a2703d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ git-annotate
 git-apply
 git-archimport
 git-archive
+git-autoupdate
 git-bisect
 git-bisect--helper
 git-blame
diff --git a/Makefile b/Makefile
index ae4b9fc..ba386a4 100644
--- a/Makefile
+++ b/Makefile
@@ -335,6 +335,7 @@ SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_PERL += git-add--interactive.perl
+SCRIPT_PERL += git-autoupdate.perl
 SCRIPT_PERL += git-difftool.perl
 SCRIPT_PERL += git-archimport.perl
 SCRIPT_PERL += git-cvsexportcommit.perl
diff --git a/git-autoupdate.perl b/git-autoupdate.perl
new file mode 100644
index 0000000..c8ca10b
--- /dev/null
+++ b/git-autoupdate.perl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+use LWP::Simple;
+use strict;
+
+my $ROOT = "http://kernel.org/pub/software/scm/git";
+
+my $us = our_git_version();
+my $them = latest_git_version();
+
+if (compare_versions($us, $them) < 0) {
+	print STDERR <<EOF;
+A new version of git is available! Auto-installing version $them.
+EOF
+}
+else {
+	exit 0;
+}
+
+upgrade($them);
+exit 42;
+
+sub our_git_version {
+	local $_ = `git version`;
+	/^git version (.*?)(\.\d+\.g[a-f0-9]+)?(\.dirty)?$/
+		or die "unable to read git version: $_";
+	return $1;
+}
+
+sub latest_git_version {
+	local $_ = get("$ROOT/");
+	my @versions = /git-([0-9.]+)\.tar\.gz/g
+		or die "unable to find any git versions at $ROOT";
+	# git version numbers have always sorted lexicographically so far,
+	# so let's just assume that will be the case forever
+	return (sort @versions)[-1];
+}
+
+sub compare_versions {
+	# let's assume lexicographical sorting again
+	return $_[0] cmp $_[1];
+}
+
+sub upgrade {
+	my $version = shift;
+	my $fn = "git-$version.tar.gz";
+	getstore("$ROOT/$fn", "/tmp/$fn") == 200
+		or die "unable to fetch $ROOT/$fn";
+	my $rc = system qq(
+		cd /tmp &&
+		gunzip -c $fn | tar xf - &&
+		cd git-$version &&
+		git config-mak >config.mak &&
+		make install
+	);
+	$rc == 0 or die "failed to upgrade git";
+	system("less /tmp/git-$version/RelNotes");
+}
diff --git a/git.c b/git.c
index 01ddf06..959ad52 100644
--- a/git.c
+++ b/git.c
@@ -462,10 +462,17 @@ int main(int argc, const char **argv)
 
 	if (!getenv("GIT_NOSPLASH") && !(argv[1] && !strcmp(argv[1], "splash"))) {
 		const char *a[] = { "splash", NULL };
-		const char *e[] = { "GIT_NOSPLASH=1", NULL };
+		const char *e[] = { "GIT_NOSPLASH=1", "GIT_NOAUTOUPDATE=1", NULL };
 		run_command_v_opt_cd_env(a, RUN_GIT_CMD, NULL, e);
 	}
 
+	if (!getenv("GIT_NOAUTOUPDATE")) {
+		const char *a[] = { "autoupdate", NULL };
+		const char *e[] = { "GIT_NOSPLASH=1", "GIT_NOAUTOUPDATE=1", NULL };
+		if (run_command_v_opt_cd_env(a, RUN_GIT_CMD, NULL, e) == 42)
+			exit(run_command_v_opt_cd_env(argv, 0, NULL, e));
+	}
+
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
 	 *
-- 
1.6.5.1.3.g9d77a

^ permalink raw reply related

* Re: [RFC PATCH v4 26/26] test smart http fetch and push
From: Clemens Buchacher @ 2009-10-29  0:31 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git
In-Reply-To: <1256774448-7625-27-git-send-email-spearce@spearce.org>

On Wed, Oct 28, 2009 at 05:00:48PM -0700, Shawn O. Pearce wrote:

> --- /dev/null
> +++ b/t/t5541-http-push.sh
[...]
> +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}

This should be 5541. We need different ports to be able to run the tests
simultenously.

> --- a/t/t5550-http-fetch.sh
> +++ b/t/t5550-http-fetch.sh

There is also a http port related bug in t5550. I'm attaching the patch
below. Maybe you want to squash this in here.

> --- /dev/null
> +++ b/t/t5551-http-fetch.sh
[...]
> +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}

Ditto, 5551.

Clemens

-->o--
Subject: [PATCH] set httpd port before sourcing lib-httpd

If LIB_HTTPD_PORT is not set already, lib-httpd will set it to the
default 8111.

Signed-off-by: Clemens Buchacher <drizzd@aon.at>
---
 t/t5540-http-push.sh  |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 5c0f4d7..ea46d1e 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -9,17 +9,16 @@ This test runs various sanity checks on http-push.'
 
 . ./test-lib.sh
 
-ROOT_PATH="$PWD"
-LIB_HTTPD_DAV=t
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
-
 if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
 then
 	say "skipping test, USE_CURL_MULTI is not defined"
 	test_done
 fi
 
+LIB_HTTPD_DAV=t
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
 . "$TEST_DIRECTORY"/lib-httpd.sh
+ROOT_PATH="$PWD"
 start_httpd
 
 test_expect_success 'setup remote repository' '
-- 
1.6.5.1.208.gd7b37

^ permalink raw reply related

* Re: [PATCH 1/3] add splash screen
From: Michael Witten @ 2009-10-29  1:28 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20091029002400.GA1057@sigill.intra.peff.net>

On Wed, Oct 28, 2009 at 7:24 PM, Jeff King <peff@peff.net> wrote:
> Because bash completion is so slow to start, we need to
> entertain users with a splash screen, so reuse the one from
> git-gui.

According to Wolfram Alpha, you're about 22 weeks off in your timing.

^ permalink raw reply

* Re: git svn branch tracking + ignore paths
From: Lachlan Deck @ 2009-10-29  1:53 UTC (permalink / raw)
  To: Avery Pennarun; +Cc: git list
In-Reply-To: <32541b130910280900p421e69b1nbcd8dcfa211521ac@mail.gmail.com>

On 29/10/2009, at 3:00 AM, Avery Pennarun wrote:

> On Wed, Oct 28, 2009 at 1:59 AM, Lachlan Deck  
> <lachlan.deck@gmail.com> wrote:
>> On 28/10/2009, at 4:20 PM, Avery Pennarun wrote:
>>> So which are the files you don't want to import from trunk?  It
>>> doesn't sound like there are any... so it's getting simpler already.
>>
>> There are. I've currently (as a workaround) done the following  
>> within the
>> main branch:
>> add the following to .git/info/exclude
>> .settings
>> target
>> .classpath
>> .project
>>
>> The last two of these has no effect of course because .project and
>> .classpath files already exist -- and thus are marked as modified.  
>> So I'm
>> currently doing a git stash && git svn rebase && git svn dcommit &&  
>> git
>> stash pop
>>
>> I'm also wanting to exclude 'lib' folders from trunk (as these are  
>> not
>> needed).
>
> The problem is that as your branch diverges from what you *actually*
> want to commit, it becomes exponentially more complicated to figure
> out what you *do* want to commit.

Sure.

> Note that if you're planning to share your git project with other
> people anyway, then you have an additional problem: you're using git
> svn rebase, which is almost useless for sharing with other people
> (other than through svn, of course), for the same reason any git
> rebase is.
>
> One option you have is to maintain two branches:
>
> 1. (git-svn) The git-svn trunk, which contains only stuff you want  
> upstream
>
> 2. (master) Your live branch, which contains everything from (1) plus
> your local customizations.
>
> When you want to fetch from svn, you do this:
>
>  git checkout master
>  git svn fetch git-svn
>  git merge git-svn
>
> When you want to push to svn, you do this:
>
>  git checkout git-svn
>  git merge --squash --no-commit master
>    (now undo your local customizations)
>  git commit
>  git svn dcommit
>  git checkout master
>  git merge git-svn
>
> Note that master never gets rebased, only merged.  If you can write a
> simple script for "undo your local customizations" - such as reverting
> a particular commit, for example - then you can put the above in a
> shell script and it should work fine most of the time.

Thanks Avery!
  - that gives me something to think about.

with regards,
--

Lachlan Deck

^ permalink raw reply

* Re: [PATCH 1/3] add splash screen
From: A Large Angry SCM @ 2009-10-29  2:25 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20091029002400.GA1057@sigill.intra.peff.net>

Jeff King wrote:
> Because bash completion is so slow to start, we need to
> entertain users with a splash screen, so reuse the one from
> git-gui.
> 
> Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>
> Signed-off-by: Sam Vilain <sam@vilain.net>
> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
> Signed-off-by: Nick Edelen <sirnot@gmail.com>
> Signed-off-by: "J.H." <warthog9@kernel.org>
> Signed-off-by: Brandon Casey <drafnel@gmail.com>
> Signed-off-by: Jeff King <peff@peff.net>
> ---
>  .gitignore    |    1 +
>  Makefile      |    3 +++
>  git-splash.sh |    4 ++++
>  git.c         |    6 ++++++
>  4 files changed, 14 insertions(+), 0 deletions(-)
>  create mode 100644 git-splash.sh
> 

If you're going to assume that the user has a working network connection 
for every git command invoked for part 3 of this series, why not get the 
logo image from kernel.org also so you always have the most up-to-date logo?

^ permalink raw reply

* Re: [RFC PATCH v4 26/26] test smart http fetch and push
From: Junio C Hamano @ 2009-10-29  3:20 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git
In-Reply-To: <1256774448-7625-27-git-send-email-spearce@spearce.org>

"Shawn O. Pearce" <spearce@spearce.org> writes:

> +test_expect_success 'clone http repository' '
> +	GIT_CURL_VERBOSE=1 git clone $HTTPD_URL/git/repo.git clone 2>err &&
> +	test_cmp file clone/file &&
> +	egrep "^([<>]|Pragma|Accept|Content-|Transfer-)" err |
> +	egrep -v "^< (Server|Expires|Date|Content-Length:|Transfer-Encoding: chunked)" |
> +	sed -e "
> +		s/
> //
> +		s/^Content-Length: .*$/Content-Length: xxxx/
> +	" >act &&

This chomped line is so unlike you---what happened?

Also, when the last downstream is sed, why would you even need two egrep
process?

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox