Git development
 help / color / mirror / Atom feed
* [RFC PATCH v3 1/8] Add remote helper debug mode
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Remote helpers deadlock easily, so support debug mode which shows the
interaction steps.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   90 +++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 11f3d7e..914bed0 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -8,6 +8,8 @@
 #include "quote.h"
 #include "remote.h"
 
+static int debug = 0;
+
 struct helper_data
 {
 	const char *name;
@@ -22,6 +24,45 @@ struct helper_data
 	int refspec_nr;
 };
 
+static void sendline(struct helper_data *helper, struct strbuf *buffer)
+{
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
+	if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
+		!= buffer->len)
+		die_errno("Full write to remote helper failed");
+}
+
+static int recvline(struct helper_data *helper, struct strbuf *buffer)
+{
+	strbuf_reset(buffer);
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: Waiting...\n");
+	if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
+		if (debug)
+			fprintf(stderr, "Debug: Remote helper quit.\n");
+		exit(128);
+	}
+
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
+	return 0;
+}
+
+static void xchgline(struct helper_data *helper, struct strbuf *buffer)
+{
+	sendline(helper, buffer);
+	recvline(helper, buffer);
+}
+
+static void write_constant(int fd, const char *str)
+{
+	if (debug)
+		fprintf(stderr, "Debug: Remote helper: -> %s", str);
+	if (write_in_full(fd, str, strlen(str)) != strlen(str))
+		die_errno("Full write to remote helper failed");
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -48,15 +89,15 @@ static struct child_process *get_helper(struct transport *transport)
 		die("Unable to run helper: git %s", helper->argv[0]);
 	data->helper = helper;
 
-	write_str_in_full(helper->in, "capabilities\n");
+	write_constant(helper->in, "capabilities\n");
 
 	data->out = xfdopen(helper->out, "r");
 	while (1) {
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
+		if (debug) fprintf(stderr, "Debug: Got cap %s\n", buf.buf);
 		if (!strcmp(buf.buf, "fetch"))
 			data->fetch = 1;
 		if (!strcmp(buf.buf, "option"))
@@ -82,14 +123,19 @@ static struct child_process *get_helper(struct transport *transport)
 		free(refspecs);
 	}
 	strbuf_release(&buf);
+	if (debug) fprintf(stderr, "Debug: Capabilities complete.\n");
 	return data->helper;
 }
 
 static int disconnect_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
+	struct strbuf buf = STRBUF_INIT;
+
 	if (data->helper) {
-		write_str_in_full(data->helper->in, "\n");
+		if (debug) fprintf(stderr, "Debug: Disconnecting.\n");
+		strbuf_addf(&buf, "\n");
+		sendline(data, &buf);
 		close(data->helper->in);
 		fclose(data->out);
 		finish_command(data->helper);
@@ -117,10 +163,11 @@ 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;
 
+	get_helper(transport);
+
 	if (!data->option)
 		return 1;
 
@@ -143,12 +190,7 @@ static int set_helper_option(struct transport *transport,
 		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 */
+	xchgline(data, &buf);
 
 	if (!strcmp(buf.buf, "ok"))
 		ret = 0;
@@ -208,13 +250,10 @@ static int fetch_with_fetch(struct transport *transport,
 	}
 
 	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);
+	sendline(data, &buf);
 
 	while (1) {
-		strbuf_reset(&buf);
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 
 		if (!prefixcmp(buf.buf, "lock ")) {
 			const char *name = buf.buf + 5;
@@ -249,12 +288,13 @@ static int fetch_with_import(struct transport *transport,
 			     int nr_heads, struct ref **to_fetch)
 {
 	struct child_process fastimport;
-	struct child_process *helper = get_helper(transport);
 	struct helper_data *data = transport->data;
 	int i;
 	struct ref *posn;
 	struct strbuf buf = STRBUF_INIT;
 
+	get_helper(transport);
+
 	if (get_importer(transport, &fastimport))
 		die("Couldn't run fast-import");
 
@@ -264,7 +304,7 @@ static int fetch_with_import(struct transport *transport,
 			continue;
 
 		strbuf_addf(&buf, "import %s\n", posn->name);
-		write_in_full(helper->in, buf.buf, buf.len);
+		sendline(data, &buf);
 		strbuf_reset(&buf);
 	}
 	disconnect_helper(transport);
@@ -369,17 +409,14 @@ static int push_refs(struct transport *transport,
 	}
 
 	strbuf_addch(&buf, '\n');
-	if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
-		exit(128);
+	sendline(data, &buf);
 
 	ref = remote_refs;
 	while (1) {
 		char *refname, *msg;
 		int status;
 
-		strbuf_reset(&buf);
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 		if (!buf.len)
 			break;
 
@@ -471,8 +508,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 	while (1) {
 		char *eov, *eon;
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
-			exit(128); /* child died, message supplied already */
+		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
@@ -497,6 +533,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 		}
 		tail = &((*tail)->next);
 	}
+	if (debug) fprintf(stderr, "Debug: Read ref listing.\n");
 	strbuf_release(&buf);
 
 	for (posn = ret; posn; posn = posn->next)
@@ -510,6 +547,9 @@ int transport_helper_init(struct transport *transport, const char *name)
 	struct helper_data *data = xcalloc(sizeof(*data), 1);
 	data->name = name;
 
+	if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
+		debug = 1;
+
 	transport->data = data;
 	transport->set_option = set_helper_option;
 	transport->get_refs_list = get_refs_list;
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 2/8] Support mandatory capabilities
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Add support for marking capability as mandatory for hosting git version
to understand. This is useful for helpers which require various types
of assistance from main git binary.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 Documentation/git-remote-helpers.txt |    5 ++++-
 transport-helper.c                   |   26 ++++++++++++++++++++------
 2 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 5cfdc0c..20a05fe 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -25,7 +25,10 @@ Commands are given by the caller on the helper's standard input, one per line.
 
 'capabilities'::
 	Lists the capabilities of the helper, one per line, ending
-	with a blank line.
+	with a blank line. Each capability may be preceeded with '*'.
+	This marks them mandatory for git version using the remote
+	helper to understand (unknown mandatory capability is fatal
+	error).
 
 'list'::
 	Lists the refs, one per line, in the format "<value> <name>
diff --git a/transport-helper.c b/transport-helper.c
index 914bed0..f5c585d 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -93,24 +93,38 @@ static struct child_process *get_helper(struct transport *transport)
 
 	data->out = xfdopen(helper->out, "r");
 	while (1) {
+		const char* capname;
+		int mandatory = 0;
 		recvline(data, &buf);
 
 		if (!*buf.buf)
 			break;
-		if (debug) fprintf(stderr, "Debug: Got cap %s\n", buf.buf);
-		if (!strcmp(buf.buf, "fetch"))
+
+		if (*buf.buf == '*') {
+			capname = buf.buf + 1;
+			mandatory = 1;
+		} else
+			capname = buf.buf;
+
+		if (debug) fprintf(stderr, "Debug: Got cap %s\n", capname);
+		if (!strcmp(capname, "fetch"))
 			data->fetch = 1;
-		if (!strcmp(buf.buf, "option"))
+		else if (!strcmp(capname, "option"))
 			data->option = 1;
-		if (!strcmp(buf.buf, "push"))
+		else if (!strcmp(capname, "push"))
 			data->push = 1;
-		if (!strcmp(buf.buf, "import"))
+		else if (!strcmp(capname, "import"))
 			data->import = 1;
-		if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
+		else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
 			ALLOC_GROW(refspecs,
 				   refspec_nr + 1,
 				   refspec_alloc);
 			refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
+		} else if (mandatory) {
+			fflush(stderr);
+			die("Unknown madatory capability %s. This remote "
+			    "helper probably needs newer version of Git.\n",
+			    capname);
 		}
 	}
 	if (refspecs) {
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 4/8] Refactor git transport options parsing
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Refactor the transport options parsing so that protocols that aren't
directly smart transports (file://, git://, ssh:// & co) can record
the smart transport options for the case if it turns that transport
can actually be smart.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport.c |   78 +++++++++++++++++++++++++++++++++++-----------------------
 transport.h |   13 ++++++++++
 2 files changed, 60 insertions(+), 31 deletions(-)

diff --git a/transport.c b/transport.c
index eadd553..deaf983 100644
--- a/transport.c
+++ b/transport.c
@@ -395,41 +395,35 @@ static int close_bundle(struct transport *transport)
 }
 
 struct git_transport_data {
-	unsigned thin : 1;
-	unsigned keep : 1;
-	unsigned followtags : 1;
-	int depth;
+	struct git_transport_options options;
 	struct child_process *conn;
 	int fd[2];
-	const char *uploadpack;
-	const char *receivepack;
 	struct extra_have_objects extra_have;
 };
 
-static int set_git_option(struct transport *connection,
+static int set_git_option(struct git_transport_options *opts,
 			  const char *name, const char *value)
 {
-	struct git_transport_data *data = connection->data;
 	if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
-		data->uploadpack = value;
+		opts->uploadpack = value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
-		data->receivepack = value;
+		opts->receivepack = value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_THIN)) {
-		data->thin = !!value;
+		opts->thin = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
-		data->followtags = !!value;
+		opts->followtags = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_KEEP)) {
-		data->keep = !!value;
+		opts->keep = !!value;
 		return 0;
 	} else if (!strcmp(name, TRANS_OPT_DEPTH)) {
 		if (!value)
-			data->depth = 0;
+			opts->depth = 0;
 		else
-			data->depth = atoi(value);
+			opts->depth = atoi(value);
 		return 0;
 	}
 	return 1;
@@ -439,7 +433,8 @@ static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
 	struct git_transport_data *data = transport->data;
 	data->conn = git_connect(data->fd, transport->url,
-				 for_push ? data->receivepack : data->uploadpack,
+				 for_push ? data->options.receivepack :
+				 data->options.uploadpack,
 				 verbose ? CONNECT_VERBOSE : 0);
 	return 0;
 }
@@ -469,15 +464,15 @@ static int fetch_refs_via_pack(struct transport *transport,
 	struct ref *refs_tmp = NULL;
 
 	memset(&args, 0, sizeof(args));
-	args.uploadpack = data->uploadpack;
-	args.keep_pack = data->keep;
+	args.uploadpack = data->options.uploadpack;
+	args.keep_pack = data->options.keep;
 	args.lock_pack = 1;
-	args.use_thin_pack = data->thin;
-	args.include_tag = data->followtags;
+	args.use_thin_pack = data->options.thin;
+	args.include_tag = data->options.followtags;
 	args.verbose = (transport->verbose > 0);
 	args.quiet = (transport->verbose < 0);
 	args.no_progress = args.quiet || (!transport->progress && !isatty(1));
-	args.depth = data->depth;
+	args.depth = data->options.depth;
 
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
@@ -734,7 +729,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	memset(&args, 0, sizeof(args));
 	args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
 	args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
-	args.use_thin_pack = data->thin;
+	args.use_thin_pack = data->options.thin;
 	args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
 	args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
 	args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
@@ -858,12 +853,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->get_refs_list = get_refs_via_rsync;
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
+		ret->smart_options = NULL;
 	} else if (is_local(url) && is_file(url)) {
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->get_refs_list = get_refs_from_bundle;
 		ret->fetch = fetch_refs_from_bundle;
 		ret->disconnect = close_bundle;
+		ret->smart_options = NULL;
 	} else if (!is_url(url)
 		|| !prefixcmp(url, "file://")
 		|| !prefixcmp(url, "git://")
@@ -873,20 +870,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		/* These are builtin smart transports. */
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
-		ret->set_option = set_git_option;
+		ret->set_option = NULL;
 		ret->get_refs_list = get_refs_via_connect;
 		ret->fetch = fetch_refs_via_pack;
 		ret->push_refs = git_transport_push;
 		ret->disconnect = disconnect_git;
+		ret->smart_options = &(data->options);
 
-		data->thin = 1;
 		data->conn = NULL;
-		data->uploadpack = "git-upload-pack";
-		if (remote->uploadpack)
-			data->uploadpack = remote->uploadpack;
-		data->receivepack = "git-receive-pack";
-		if (remote->receivepack)
-			data->receivepack = remote->receivepack;
 	} else if (!prefixcmp(url, "http://")
 		|| !prefixcmp(url, "https://")
 		|| !prefixcmp(url, "ftp://")) {
@@ -904,14 +895,39 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		transport_helper_init(ret, handler);
 	}
 
+	if (ret->smart_options) {
+		ret->smart_options->thin = 1;
+		ret->smart_options->uploadpack = "git-upload-pack";
+		if (remote->uploadpack)
+			ret->smart_options->uploadpack = remote->uploadpack;
+		ret->smart_options->receivepack = "git-receive-pack";
+		if (remote->receivepack)
+			ret->smart_options->receivepack = remote->receivepack;
+	}
+
 	return ret;
 }
 
 int transport_set_option(struct transport *transport,
 			 const char *name, const char *value)
 {
+	int git_reports = 1, protocol_reports = 1;
+
+	if (transport->smart_options)
+		git_reports = set_git_option(transport->smart_options,
+					     name, value);
+
 	if (transport->set_option)
-		return transport->set_option(transport, name, value);
+		protocol_reports = transport->set_option(transport, name,
+							value);
+
+	/* If either report is 0, report 0 (success). */
+	if (!git_reports || !protocol_reports)
+		return 0;
+	/* If either reports -1 (invalid value), report -1. */
+	if ((git_reports == -1) || (protocol_reports == -1))
+		return -1;
+	/* Otherwise if both report unknown, report unknown. */
 	return 1;
 }
 
diff --git a/transport.h b/transport.h
index 9e74406..d0192f9 100644
--- a/transport.h
+++ b/transport.h
@@ -4,6 +4,15 @@
 #include "cache.h"
 #include "remote.h"
 
+struct git_transport_options {
+	unsigned thin : 1;
+	unsigned keep : 1;
+	unsigned followtags : 1;
+	int depth;
+	const char *uploadpack;
+	const char *receivepack;
+};
+
 struct transport {
 	struct remote *remote;
 	const char *url;
@@ -65,6 +74,10 @@ struct transport {
 	signed verbose : 3;
 	/* Force progress even if the output is not a tty */
 	unsigned progress : 1;
+	/* If transport is at least potentially smart, this points to
+	   git_transport_options structure to use in case transport actually
+	   turns out to be smart. */
+	struct git_transport_options* smart_options;
 };
 
 #define TRANSPORT_PUSH_ALL 1
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 6/8] Support remote helpers implementing smart transports
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 Documentation/git-remote-helpers.txt |   25 ++++++++++-
 transport-helper.c                   |   84 +++++++++++++++++++++++++++++++++-
 2 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
index 20a05fe..b957813 100644
--- a/Documentation/git-remote-helpers.txt
+++ b/Documentation/git-remote-helpers.txt
@@ -93,6 +93,20 @@ Supported if the helper has the "push" capability.
 +
 Supported if the helper has the "import" capability.
 
+'connect' <service>::
+	Connects to given service. Stdin and stdout of helper are
+	connected to specified service (git prefix is included in service
+	name so e.g. fetching uses 'git-upload-pack' as service) on
+	remote side. Valid replies to this command are empty line
+	(connection established), 'fallback' (no smart transport support,
+	fall back to dumb transports) and just exiting with error message
+	printed (can't connect, don't bother trying to fall back). After
+	line feed terminating the positive (empty) response, the output
+	of service starts. After the connection ends, the remote
+	helper exits.
++
+Supported if the helper has the "connect" capability.
+
 If a fatal error occurs, the program writes the error message to
 stderr and exits. The caller should expect that a suitable error
 message has been printed if the child closes the connection without
@@ -126,6 +140,9 @@ CAPABILITIES
 	all, it must cover all refs reported by the list command; if
 	it is not used, it is effectively "*:*"
 
+'connect'::
+	This helper supports the 'connect' command.
+
 REF LIST ATTRIBUTES
 -------------------
 
@@ -168,9 +185,15 @@ OPTIONS
 	but don't actually change any repository data.	For most
 	helpers this only applies to the 'push', if supported.
 
+'option servpath <c-style-quoted-path>'::
+	Set service path (--upload-pack, --receive-pack etc.) for
+	next connect. Remote helper MAY support this option. Remote
+	helper MUST NOT rely on this option being set before
+	connect request occurs.
+
 Documentation
 -------------
-Documentation by Daniel Barkalow.
+Documentation by Daniel Barkalow and Ilari Liusvaara
 
 GIT
 ---
diff --git a/transport-helper.c b/transport-helper.c
index 3d99fe1..e0254bc 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -18,7 +18,8 @@ struct helper_data
 	unsigned fetch : 1,
 		import : 1,
 		option : 1,
-		push : 1;
+		push : 1,
+		connect : 1;
 	/* These go from remote name (as in "list") to private name */
 	struct refspec *refspecs;
 	int refspec_nr;
@@ -163,7 +164,10 @@ static struct child_process *get_helper(struct transport *transport)
 				   refspec_nr + 1,
 				   refspec_alloc);
 			refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
-		} else if (mandatory) {
+		}
+		else if (!strcmp(capname, "connect"))
+			data->connect = 1;
+		else if (mandatory) {
 			fflush(stderr);
 			die("Unknown madatory capability %s. This remote "
 			    "helper probably needs newer version of Git.\n",
@@ -386,12 +390,78 @@ static int fetch_with_import(struct transport *transport,
 	return 0;
 }
 
+static int _process_connect(struct transport *transport,
+				      const char *name, const char *exec)
+{
+	struct helper_data *data = transport->data;
+	struct strbuf cmdbuf = STRBUF_INIT;
+	struct child_process *helper;
+	int r;
+
+	helper = get_helper(transport);
+
+	/* Handle --upload-pack and friends. This is fire and forget...
+	   just warn if it fails. */
+	if (strcmp(name, exec)) {
+		r = set_helper_option(transport, "servpath", exec);
+		if (r > 0)
+			fprintf(stderr, "Warning: Setting remote service path "
+				"not supported by protocol.\n");
+		else if (r < 0)
+			fprintf(stderr, "Warning: Invalid remote service "
+				"path.\n");
+	}
+
+	if (data->connect)
+		strbuf_addf(&cmdbuf, "connect %s\n", name);
+	else
+		return 0;
+
+	xchgline(data, &cmdbuf);
+	if (!strcmp(cmdbuf.buf, "")) {
+		if (debug)
+			fprintf(stderr, "Debug: Smart transport connection "
+				"ready.\n");
+		return 1;
+	} else if (!strcmp(cmdbuf.buf, "fallback")) {
+		if (debug)
+			fprintf(stderr, "Debug: Falling back to dumb "
+				"transport.\n");
+		return 0;
+	} else
+		die("Unknown response to connect: %s",
+			cmdbuf.buf);
+
+	return 0;	/* Shouldn't be here. */
+}
+
+static int process_connect(struct transport* transport,
+				     int for_push)
+{
+	struct helper_data *data = transport->data;
+	const char *name;
+	const char *exec;
+
+	name = for_push ? "git-receive-pack" : "git-upload-pack";
+	if (for_push)
+		exec = data->gitoptions.receivepack;
+	else
+		exec = data->gitoptions.uploadpack;
+
+	return _process_connect(transport, name, exec);
+}
+
 static int fetch(struct transport *transport,
 		 int nr_heads, struct ref **to_fetch)
 {
 	struct helper_data *data = transport->data;
 	int i, count;
 
+	if (process_connect(transport, 0)) {
+		transport_take_over(transport);
+		return transport->fetch(transport, nr_heads, to_fetch);
+	}
+
 	count = 0;
 	for (i = 0; i < nr_heads; i++)
 		if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -419,6 +489,11 @@ static int push_refs(struct transport *transport,
 	struct child_process *helper;
 	struct ref *ref;
 
+	if (process_connect(transport, 1)) {
+		transport_take_over(transport);
+		return transport->push_refs(transport, remote_refs, flags);
+	}
+
 	if (!remote_refs)
 		return 0;
 
@@ -559,6 +634,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 	helper = get_helper(transport);
 
+	if (process_connect(transport, for_push)) {
+		transport_take_over(transport);
+		return transport->get_refs_list(transport, for_push);
+	}
+
 	if (data->push && for_push)
 		write_str_in_full(helper->in, "list for-push\n");
 	else
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 3/8] Pass unknown protocols to external protocol handlers
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Change URL handling to allow external protocol handlers to implement
new protocols without the '::' syntax if helper name does not conflict
with any built-in protocol.

foo:// now invokes git-remote-foo with foo:// URL.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   22 ++++++++++++-
 transport.c        |   87 +++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 93 insertions(+), 16 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index f5c585d..95aaa02 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -63,6 +63,26 @@ static void write_constant(int fd, const char *str)
 		die_errno("Full write to remote helper failed");
 }
 
+const char* remove_ext_force(const char* url)
+{
+	const char* url2 = url;
+	const char* first_colon = NULL;
+
+	if (!url)
+		return NULL;
+
+	while (*url2 && !first_colon)
+		if (*url2 == ':')
+			first_colon = url2;
+		else
+			url2++;
+
+	if (first_colon && first_colon[1] == ':')
+		return first_colon + 2;
+	else
+		return url;
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -83,7 +103,7 @@ static struct child_process *get_helper(struct transport *transport)
 	strbuf_addf(&buf, "remote-%s", data->name);
 	helper->argv[0] = strbuf_detach(&buf, NULL);
 	helper->argv[1] = transport->remote->name;
-	helper->argv[2] = transport->url;
+	helper->argv[2] = remove_ext_force(transport->url);
 	helper->git_cmd = 1;
 	if (start_command(helper))
 		die("Unable to run helper: git %s", helper->argv[0]);
diff --git a/transport.c b/transport.c
index 3eea836..eadd553 100644
--- a/transport.c
+++ b/transport.c
@@ -780,6 +780,55 @@ static int is_file(const char *url)
 	return S_ISREG(buf.st_mode);
 }
 
+static const char* strchrc(const char* str, int c)
+{
+	while (*str)
+		if (*str == c)
+			return str;
+		else
+			str++;
+	return NULL;
+}
+
+static int is_url(const char* url)
+{
+	if (!url)
+		return 0;
+
+	const char* url2 = url;
+	const char* first_slash = strchrc(url, '/');
+
+	/* Input with no slash at all or slash first can't be URL. */
+	if (!first_slash || first_slash == url)
+		return 0;
+	/* Character before must be : and next must be /. */
+	if (first_slash[-1] != ':' || first_slash[1] != '/')
+		return 0;
+	/* There must be something before the :// */
+	if (first_slash == url + 1)
+		return 0;
+	/* Check all characters up to first slash. Only alpha, num and
+	   : are allowed. : must be followed by : or / */
+	url2 = url;
+	while (url2 < first_slash) {
+		if (*url2 != ':' && !isalnum((unsigned char)*url2))
+			return 0;
+		if (*url2 == ':' && url2[1] != ':' && url2[1] != '/')
+			return 0;
+		if (*url2 == ':')
+			url2++;		/* Skip second : */
+		url2++;
+	}
+
+	/* Valid enough. */
+	return 1;
+}
+
+static int external_specification_len(const char* url)
+{
+	return strchrc(url, ':') - url;
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
 	struct transport *ret = xcalloc(1, sizeof(*ret));
@@ -805,30 +854,23 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 	if (remote && remote->foreign_vcs) {
 		transport_helper_init(ret, remote->foreign_vcs);
-		return ret;
-	}
-
-	if (!prefixcmp(url, "rsync:")) {
+	} else if (!prefixcmp(url, "rsync:")) {
 		ret->get_refs_list = get_refs_via_rsync;
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
-
-	} else if (!prefixcmp(url, "http://")
-	        || !prefixcmp(url, "https://")
-	        || !prefixcmp(url, "ftp://")) {
-		transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-		error("git was compiled without libcurl support.");
-#endif
-
 	} else if (is_local(url) && is_file(url)) {
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->get_refs_list = get_refs_from_bundle;
 		ret->fetch = fetch_refs_from_bundle;
 		ret->disconnect = close_bundle;
-
-	} else {
+	} else if (!is_url(url)
+		|| !prefixcmp(url, "file://")
+		|| !prefixcmp(url, "git://")
+		|| !prefixcmp(url, "ssh://")
+		|| !prefixcmp(url, "git+ssh://")
+		|| !prefixcmp(url, "ssh+git://")) {
+		/* These are builtin smart transports. */
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->set_option = set_git_option;
@@ -845,6 +887,21 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		data->receivepack = "git-receive-pack";
 		if (remote->receivepack)
 			data->receivepack = remote->receivepack;
+	} else if (!prefixcmp(url, "http://")
+		|| !prefixcmp(url, "https://")
+		|| !prefixcmp(url, "ftp://")) {
+		/* These three are just plain special. */
+		transport_helper_init(ret, "curl");
+#ifdef NO_CURL
+		error("git was compiled without libcurl support.");
+#endif
+	} else {
+		/* Unknown protocol in URL. Pass to external handler. */
+		int len = external_specification_len(url);
+		char* handler = xmalloc(len + 1);
+		handler[len] = 0;
+		strncpy(handler, url, len);
+		transport_helper_init(ret, handler);
 	}
 
 	return ret;
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 7/8] Support remote archive from external protocol helpers
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Helpers which support invoke/connect also should support remote archive
snapshot (or at least there's only one way to attempt it). So support
remote snapshotting for protocol helpers.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 builtin-archive.c  |   17 ++++++++++-------
 transport-helper.c |   19 +++++++++++++++++++
 transport.c        |   21 +++++++++++++++++++++
 transport.h        |    5 +++++
 4 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/builtin-archive.c b/builtin-archive.c
index 12351e9..d34b3fd 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -5,6 +5,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "transport.h"
 #include "parse-options.h"
 #include "pkt-line.h"
 #include "sideband.h"
@@ -25,12 +26,16 @@ static void create_output_file(const char *output_file)
 static int run_remote_archiver(int argc, const char **argv,
 			       const char *remote, const char *exec)
 {
-	char *url, buf[LARGE_PACKET_MAX];
+	char buf[LARGE_PACKET_MAX];
 	int fd[2], i, len, rv;
-	struct child_process *conn;
+	struct transport *transport;
+	struct remote *_remote;
 
-	url = xstrdup(remote);
-	conn = git_connect(fd, url, exec, 0);
+	_remote = remote_get(remote);
+	if (!_remote->url[0])
+		die("git archive: Remote with no URL");
+	transport = transport_get(_remote, _remote->url[0]);
+	transport_connect(transport, "git-upload-archive", exec, fd);
 
 	for (i = 1; i < argc; i++)
 		packet_write(fd[1], "argument %s\n", argv[i]);
@@ -53,9 +58,7 @@ static int run_remote_archiver(int argc, const char **argv,
 
 	/* Now, start reading from fd[0] and spit it out to stdout */
 	rv = recv_sideband("archive", fd[0], 1);
-	close(fd[0]);
-	close(fd[1]);
-	rv |= finish_connect(conn);
+	rv |= transport_disconnect(transport);
 
 	return !!rv;
 }
diff --git a/transport-helper.c b/transport-helper.c
index e0254bc..a49be55 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -451,6 +451,24 @@ static int process_connect(struct transport* transport,
 	return _process_connect(transport, name, exec);
 }
 
+static int connect_helper(struct transport *transport, const char *name,
+		   const char *exec, int fd[2])
+{
+	struct helper_data *data = transport->data;
+
+	/* Get_helper so connect is inited. */
+	get_helper(transport);
+	if (!data->connect)
+		die("Operation not supported by protocol.");
+
+	if (!_process_connect(transport, name, exec))
+		die("Can't connect to subservice %s.", name);
+
+	fd[0] = data->helper->out;
+	fd[1] = data->helper->in;
+	return 0;
+}
+
 static int fetch(struct transport *transport,
 		 int nr_heads, struct ref **to_fetch)
 {
@@ -694,6 +712,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 	transport->fetch = fetch;
 	transport->push_refs = push_refs;
 	transport->disconnect = release_helper;
+	transport->connect = connect_helper;
 	transport->disown = helper_disown;
 	transport->smart_options = &(data->gitoptions);
 	return 0;
diff --git a/transport.c b/transport.c
index e9802e3..b418889 100644
--- a/transport.c
+++ b/transport.c
@@ -756,6 +756,17 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	return ret;
 }
 
+static int connect_git(struct transport *transport, const char* name,
+		       const char* executable, int fd[2])
+{
+	struct git_transport_data *data = transport->data;
+	data->conn = git_connect(data->fd, transport->url,
+				 executable, 0);
+	fd[0] = data->fd[0];
+	fd[1] = data->fd[1];
+	return 0;
+}
+
 static int disconnect_git(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
@@ -917,6 +928,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->get_refs_list = get_refs_via_connect;
 		ret->fetch = fetch_refs_via_pack;
 		ret->push_refs = git_transport_push;
+		ret->connect = connect_git;
 		ret->disconnect = disconnect_git;
 		ret->smart_options = &(data->options);
 		ret->disown = NULL;
@@ -1078,6 +1090,15 @@ void transport_unlock_pack(struct transport *transport)
 	}
 }
 
+int transport_connect(struct transport *transport, const char *name,
+		      const char* exec, int fd[2])
+{
+	if (transport->connect)
+		return transport->connect(transport, name, exec, fd);
+	else
+		die("Operation not supported by protocol");
+}
+
 int transport_disconnect(struct transport *transport)
 {
 	int ret = 0;
diff --git a/transport.h b/transport.h
index 499962d..d702e31 100644
--- a/transport.h
+++ b/transport.h
@@ -64,6 +64,8 @@ struct transport {
 	 **/
 	int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
+	int (*connect)(struct transport *connection, const char* name,
+		       const char* executable, int fd[2]);
 
 	/**
 	 * Disown the transport helper. Releases all resources used
@@ -139,6 +141,9 @@ int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
 void transport_take_over(struct transport *transport);
 
+int transport_connect(struct transport *transport, const char *name,
+		      const char* exec, int fd[2]);
+
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
 
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 5/8] Support taking over transports
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

Add support for taking over transports that turn out to be smart.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   28 ++++++++++++++++++++++++-
 transport.c        |   57 +++++++++++++++++++++++++++++++++++++++++++++++----
 transport.h        |   10 +++++++++
 3 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/transport-helper.c b/transport-helper.c
index 95aaa02..3d99fe1 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -22,6 +22,7 @@ struct helper_data
 	/* These go from remote name (as in "list") to private name */
 	struct refspec *refspecs;
 	int refspec_nr;
+	struct git_transport_options gitoptions;
 };
 
 static void sendline(struct helper_data *helper, struct strbuf *buffer)
@@ -63,6 +64,15 @@ static void write_constant(int fd, const char *str)
 		die_errno("Full write to remote helper failed");
 }
 
+static struct child_process* helper_disown(struct transport *transport)
+{
+	struct helper_data *data = transport->data;
+	struct child_process *child = data->helper;
+	fclose(data->out);
+	free(data);
+	return child;
+}
+
 const char* remove_ext_force(const char* url)
 {
 	const char* url2 = url;
@@ -91,6 +101,7 @@ static struct child_process *get_helper(struct transport *transport)
 	const char **refspecs = NULL;
 	int refspec_nr = 0;
 	int refspec_alloc = 0;
+	int duped;
 
 	if (data->helper)
 		return data->helper;
@@ -109,9 +120,21 @@ static struct child_process *get_helper(struct transport *transport)
 		die("Unable to run helper: git %s", helper->argv[0]);
 	data->helper = helper;
 
+	/* Open the output as FILE* so strbuf_getline() can be used.
+	   Do this with duped fd because fclose() will close the fd,
+	   and stuff like disowning will require the fd to remain.
+
+	   Set the stream to unbuffered because some reads are critical
+	   in sense that any overreading will cause deadlocks.
+	*/
+        duped = dup(helper->out);
+	if (duped < 0)
+		die_errno("Can't dup helper output fd");
+	data->out = xfdopen(duped, "r");
+	setvbuf(data->out, NULL, _IONBF, 0);
+
 	write_constant(helper->in, "capabilities\n");
 
-	data->out = xfdopen(helper->out, "r");
 	while (1) {
 		const char* capname;
 		int mandatory = 0;
@@ -171,6 +194,7 @@ static int disconnect_helper(struct transport *transport)
 		strbuf_addf(&buf, "\n");
 		sendline(data, &buf);
 		close(data->helper->in);
+		close(data->helper->out);
 		fclose(data->out);
 		finish_command(data->helper);
 		free((char *)data->helper->argv[0]);
@@ -590,5 +614,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 	transport->fetch = fetch;
 	transport->push_refs = push_refs;
 	transport->disconnect = release_helper;
+	transport->disown = helper_disown;
+	transport->smart_options = &(data->gitoptions);
 	return 0;
 }
diff --git a/transport.c b/transport.c
index deaf983..e9802e3 100644
--- a/transport.c
+++ b/transport.c
@@ -398,6 +398,7 @@ struct git_transport_data {
 	struct git_transport_options options;
 	struct child_process *conn;
 	int fd[2];
+	unsigned got_remote_heads : 1;
 	struct extra_have_objects extra_have;
 };
 
@@ -432,10 +433,15 @@ static int set_git_option(struct git_transport_options *opts,
 static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
 	struct git_transport_data *data = transport->data;
+
+	if (data->conn)
+		return 0;
+
 	data->conn = git_connect(data->fd, transport->url,
 				 for_push ? data->options.receivepack :
 				 data->options.uploadpack,
 				 verbose ? CONNECT_VERBOSE : 0);
+
 	return 0;
 }
 
@@ -447,6 +453,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
 	connect_setup(transport, for_push, 0);
 	get_remote_heads(data->fd[0], &refs, 0, NULL,
 			 for_push ? REF_NORMAL : 0, &data->extra_have);
+	data->got_remote_heads = 1;
 
 	return refs;
 }
@@ -477,9 +484,10 @@ static int fetch_refs_via_pack(struct transport *transport,
 	for (i = 0; i < nr_heads; i++)
 		origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 
-	if (!data->conn) {
+	if (!data->got_remote_heads) {
 		connect_setup(transport, 0, 0);
 		get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
+		data->got_remote_heads = 1;
 	}
 
 	refs = fetch_pack(&args, data->fd, data->conn,
@@ -490,6 +498,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 	if (finish_connect(data->conn))
 		refs = NULL;
 	data->conn = NULL;
+	data->got_remote_heads = 0;
 
 	free_refs(refs_tmp);
 
@@ -718,12 +727,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	struct send_pack_args args;
 	int ret;
 
-	if (!data->conn) {
+	if (!data->got_remote_heads) {
 		struct ref *tmp_refs;
 		connect_setup(transport, 1, 0);
 
 		get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
 				 NULL);
+		data->got_remote_heads = 1;
 	}
 
 	memset(&args, 0, sizeof(args));
@@ -741,6 +751,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 	close(data->fd[0]);
 	ret |= finish_connect(data->conn);
 	data->conn = NULL;
+	data->got_remote_heads = 0;
 
 	return ret;
 }
@@ -749,7 +760,8 @@ static int disconnect_git(struct transport *transport)
 {
 	struct git_transport_data *data = transport->data;
 	if (data->conn) {
-		packet_flush(data->fd[1]);
+		if (data->got_remote_heads)
+			packet_flush(data->fd[1]);
 		close(data->fd[0]);
 		close(data->fd[1]);
 		finish_connect(data->conn);
@@ -759,6 +771,35 @@ static int disconnect_git(struct transport *transport)
 	return 0;
 }
 
+void transport_take_over(struct transport *transport)
+{
+	struct git_transport_data *data;
+
+	if (!transport->disown)
+		die("Bug detected: Taking over transport requires non-NULL "
+		    "disown method.");
+	if (!transport->smart_options)
+		die("Bug detected: Taking over transport requires non-NULL "
+		    "smart_options field.");
+
+	data = xcalloc(1, sizeof(*data));
+	data->options = *transport->smart_options;
+	data->conn = transport->disown(transport);
+	data->fd[0] = data->conn->out;
+	data->fd[1] = data->conn->in;
+	data->got_remote_heads = 0;
+	transport->data = data;
+
+	transport->set_option = NULL;
+	transport->get_refs_list = get_refs_via_connect;
+	transport->fetch = fetch_refs_via_pack;
+	transport->push = NULL;
+	transport->push_refs = git_transport_push;
+	transport->disconnect = disconnect_git;
+	transport->smart_options = &(data->options);
+	transport->disown = NULL;
+}
+
 static int is_local(const char *url)
 {
 	const char *colon = strchr(url, ':');
@@ -854,6 +895,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
 		ret->smart_options = NULL;
+		ret->disown = NULL;
 	} else if (is_local(url) && is_file(url)) {
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
@@ -861,6 +903,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->fetch = fetch_refs_from_bundle;
 		ret->disconnect = close_bundle;
 		ret->smart_options = NULL;
+		ret->disown = NULL;
 	} else if (!is_url(url)
 		|| !prefixcmp(url, "file://")
 		|| !prefixcmp(url, "git://")
@@ -876,8 +919,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->push_refs = git_transport_push;
 		ret->disconnect = disconnect_git;
 		ret->smart_options = &(data->options);
+		ret->disown = NULL;
 
 		data->conn = NULL;
+		data->got_remote_heads = 0;
 	} else if (!prefixcmp(url, "http://")
 		|| !prefixcmp(url, "https://")
 		|| !prefixcmp(url, "ftp://")) {
@@ -938,9 +983,9 @@ int transport_push(struct transport *transport,
 	*nonfastforward = 0;
 	verify_remote_names(refspec_nr, refspec);
 
-	if (transport->push)
+	if (transport->push) {
 		return transport->push(transport, refspec_nr, refspec, flags);
-	if (transport->push_refs) {
+	} else if (transport->push_refs) {
 		struct ref *remote_refs =
 			transport->get_refs_list(transport, 1);
 		struct ref *local_refs = get_local_heads();
@@ -984,6 +1029,7 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
 {
 	if (!transport->remote_refs)
 		transport->remote_refs = transport->get_refs_list(transport, 0);
+
 	return transport->remote_refs;
 }
 
@@ -1018,6 +1064,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
 	}
 
 	rc = transport->fetch(transport, nr_heads, heads);
+
 	free(heads);
 	return rc;
 }
diff --git a/transport.h b/transport.h
index d0192f9..499962d 100644
--- a/transport.h
+++ b/transport.h
@@ -65,6 +65,15 @@ struct transport {
 	int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 
+	/**
+	 * Disown the transport helper. Releases all resources used
+	 * by field pointed by member data, except that the child
+	 * process is not released but returned and whatever is pointed
+	 * by smart transport options structure is not freed (but the
+	 * smart transport options structure itself is).
+	 **/
+	struct child_process* (*disown)(struct transport* connection);
+
 	/** get_refs_list(), fetch(), and push_refs() can keep
 	 * resources (such as a connection) reserved for futher
 	 * use. disconnect() releases these resources.
@@ -128,6 +137,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
+void transport_take_over(struct transport *transport);
 
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* [RFC PATCH v3 8/8] Remove special casing of http, https and ftp
From: Ilari Liusvaara @ 2009-12-06 16:28 UTC (permalink / raw)
  To: git
In-Reply-To: <1260116931-16549-1-git-send-email-ilari.liusvaara@elisanet.fi>

HTTP, HTTPS and FTP are no longer special to transport code. Also
add support for FTPS (curl supports it so it is easy).

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 .gitignore  |    4 ++++
 Makefile    |   24 ++++++++++++++++++++++--
 transport.c |    8 --------
 3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7cc54b4..7c79f97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,6 +107,10 @@
 /git-relink
 /git-remote
 /git-remote-curl
+/git-remote-http
+/git-remote-https
+/git-remote-ftp
+/git-remote-ftps
 /git-repack
 /git-replace
 /git-repo-config
diff --git a/Makefile b/Makefile
index 42744a4..1332225 100644
--- a/Makefile
+++ b/Makefile
@@ -424,6 +424,13 @@ BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
 BUILT_INS += git-whatchanged$X
 
+#ifdef NO_CURL
+REMOTE_CURL_NAMES =
+#else
+# Yes, this is missing git-remote-http intentionally!
+REMOTE_CURL_NAMES = git-remote-https git-remote-ftp git-remote-ftps
+#endif
+
 # what 'all' will build and 'install' will install in gitexecdir,
 # excluding programs for built-in commands
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -1097,7 +1104,7 @@ else
 	else
 		CURL_LIBCURL = -lcurl
 	endif
-	PROGRAMS += git-remote-curl$X git-http-fetch$X
+	PROGRAMS += git-remote-http$X git-remote-https$X git-remote-ftp$X git-remote-ftps$X git-http-fetch$X
 	curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
 	ifeq "$(curl_check)" "070908"
 		ifndef NO_EXPAT
@@ -1676,7 +1683,13 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
+$(REMOTE_CURL_NAMES): git-remote-http$X
+	$(QUIET_LNCP)$(RM) $@ && \
+	ln $< $@ 2>/dev/null || \
+	ln -s $< $@ 2>/dev/null || \
+	cp $< $@
+
+git-remote-http$X: remote-curl.o http.o http-walker.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
@@ -1853,6 +1866,7 @@ endif
 ifneq (,$X)
 	$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
 endif
+
 	bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
 	execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
 	{ test "$$bindir/" = "$$execdir/" || \
@@ -1866,6 +1880,12 @@ endif
 		ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
 		cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
 	  done; } && \
+	{ for p in $(REMOTE_CURL_NAMES); do \
+		$(RM) "$$execdir/$$p" && \
+		ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+		ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+		cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
+	  done; } && \
 	./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
diff --git a/transport.c b/transport.c
index b418889..c6684ac 100644
--- a/transport.c
+++ b/transport.c
@@ -935,14 +935,6 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		data->conn = NULL;
 		data->got_remote_heads = 0;
-	} else if (!prefixcmp(url, "http://")
-		|| !prefixcmp(url, "https://")
-		|| !prefixcmp(url, "ftp://")) {
-		/* These three are just plain special. */
-		transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-		error("git was compiled without libcurl support.");
-#endif
 	} else {
 		/* Unknown protocol in URL. Pass to external handler. */
 		int len = external_specification_len(url);
-- 
1.6.6.rc1.300.gfbc27

^ permalink raw reply related

* Re: git-svn breakage on repository rename
From: Guido Stevens @ 2009-12-06 16:24 UTC (permalink / raw)
  To: Eric Wong; +Cc: git, George Kuk, clark.alex, wichert, admins
In-Reply-To: <20091205222251.GA2120@dcvr.yhbt.net>

Eric, thanks!

Plone admins: I can now confirm it was not a svn corruption. Adding the 
"--no-follow-parent" switch to git svn clone makes it possible to check 
out the Plone svn into git -- at least the commits since the rename.

That one might be relevant for anybody who wants to contribute to the 
Plone code base from a git-centered workflow.

Thanks everybody for helping to solve this problem! I can now integrate 
the analysis of Products.CMFPlone into the overall Plone ecosystem 
analysis, and it would have been very awkward not to be able to do that 
because of this renaming issue. Again: thanks.

:*CU#

Eric Wong wrote:
> Guido Stevens <guido.stevens@cosent.net> wrote:
>> ----------------------------------------------------
>> To reconstruct this error:
>> ----------------------------------------------------
>>
>> $ git svn init https://svn-mirror.plone.org/svn/plone/Plone/trunk Plone
>> $ cd Plone
>> $ git svn fetch
> 
> Since Plone appears to use a standard trunk/branches/tags layout
> recommended by SVN developers, can you try this instead?:
> 
>   git svn clone -s http://svn-mirror.plone.org/svn/plone/Plone

That one breaks completely, silently.

> 
> If you don't care about branches/tags at all, you can also try
> 
>   git svn clone --no-follow-parent \
>     https://svn-mirror.plone.org/svn/plone/Plone/trunk Plone

Yep, that one does the trick.

It does what it says though: it stops at the rename boundary 
22715->22716. Luckily I can do other checkouts on the other names that 
this package has had and complete my analysis anyway.

:*CU#
-- 
***   Guido A.J. Stevens        ***   tel: +31.43.3618933    ***
***   guido.stevens@cosent.nl   ***   Postbus 619            ***
***   http://www.cosent.nl      ***   6200 AP  Maastricht    ***

             s h a r i n g    m a k e s    s e n s e

	

^ permalink raw reply

* Approach for collaborative branches
From: Gary Pickrell @ 2009-12-06 16:57 UTC (permalink / raw)
  To: git


My apologies if this is the wrong place to ask my question.

I've setup a git remote repository and placed the source for a windows 
program there.   I'm working on getting it to compile on Ubuntu.  The 
compilers are different so the code needs to be tweeked to make sure it 
compiles fine in Ubuntu without breaking anything in Windows.  Great!  
This is a situation that calls for git branches, I thought.  Did the 
following:

1) Clone the repository on Ubuntu
2) Made an Ubuntu branch
3) Made my changes to the code.  Added files...ect
4) Used git push origin Ubuntu to push the changes to the repository

I'm unable to see my Ubuntu changes on my windows machine.  How should I 
proceed?

Thanks in advance.

    -Gary

^ permalink raw reply

* Re: Approach for collaborative branches
From: Matthieu Moy @ 2009-12-06 17:15 UTC (permalink / raw)
  To: Gary Pickrell; +Cc: git
In-Reply-To: <4B1BE266.2000208@pickrell.com>

Gary Pickrell <gary@pickrell.com> writes:

> 1) Clone the repository on Ubuntu
> 2) Made an Ubuntu branch
> 3) Made my changes to the code.  Added files...ect
> 4) Used git push origin Ubuntu to push the changes to the repository
>
> I'm unable to see my Ubuntu changes on my windows machine.  How should
> I proceed?

On the windows machine, you should do first

  git fetch

this will tell you about the new branch if you didn't fetch it
already. If it's called Ubuntu remotely, it's probably called
origin/Ubuntu on your local repository after doing a fetch. Therefore,
you can now do

  git merge origin/Ubuntu


Now, you probably also want your local branch to be named the same way
on both machines. One way to do that is to create a branch "foo" on
the repository, then on both sides, fetch it and do

  git checkout --track origin/foo

this will create a local branch foo, and tell git that further "pull"
should take their changes from origin/foo.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply

* Re: svn svn returning 'fatal: Not a valid object name' on sourceforge svn repo
From: Stephen Bannasch @ 2009-12-06 17:49 UTC (permalink / raw)
  To: Eric Wong; +Cc: git
In-Reply-To: <20091205224950.GC2120@dcvr.yhbt.net>

At 2:49 PM -0800 12/5/09, Eric Wong wrote:
>Stephen Bannasch <stephen.bannasch@deanbrook.org> wrote:
>> I use git svn often and normally it works fine.
>>
>> I getting a fatal error trying to clone the asciimathm svn repo at sourceforge:
>>
>> $ git svn clone --trunk=trunk --branches=branches http://asciimathml.svn.sourceforge.net/svnroot/asciimathml asciimathml-svn-git
>> fatal: Not a valid object name
> > ls-tree -z  ./: command returned error: 128
>
>Passing the "-r4:HEAD" parameter to "git svn clone" should work.  It
>looks like the repository was initially miscreated and "trunk" was a
>symlink (and not a directory) in r1.

Thanks Eric, your suggestion worked.

How could you tell the first svn commit was a symlink?

^ permalink raw reply

* [PATCH 1/2] Documentation: 'git add --all' can remove files
From: Björn Gustavsson @ 2009-12-06 18:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Document that 'git add --all' can remove files no longer present
in the working tree from the index. Also mention why it is useful,
and insert a cross-reference in the 'git rm' documentation to
'git add --all'.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 Documentation/git-add.txt |   13 +++++++++----
 Documentation/git-rm.txt  |    4 ++++
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index e93e606..b7e8aa2 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -16,6 +16,8 @@ DESCRIPTION
 -----------
 This command adds the current content of new or modified files to the
 index, thus staging that content for inclusion in the next commit.
+(Given the `--all` option, the `git add` command will also remove files
+from the index that are no longer present in the working tree.)
 
 The "index" holds a snapshot of the content of the working tree, and it
 is this snapshot that is taken as the contents of the next commit.  Thus
@@ -102,10 +104,13 @@ apply.
 
 -A::
 --all::
-	Update files that git already knows about (same as '\--update')
-	and add all untracked files that are not ignored by '.gitignore'
-	mechanism.
-
+	Update files that git already knows about (same as '\--update'),
+	add all untracked files that are not ignored by '.gitignore'
+	mechanism and remove files from the index that are no longer
+	present in the working tree.
++
+This option is especially useful after running the `rsync` command with
+the working tree as the target to sync the index with the working tree.
 
 -N::
 --intent-to-add::
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 5afb1e7..b4dff5b 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -81,6 +81,10 @@ two directories `d` and `d2`, there is a difference between
 using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
 also remove all of directory `d2`.
 
+To automatically remove all files from the index that are no longer
+present in the working tree, see the `--all` option for
+linkgit:git-add[1].
+
 EXAMPLES
 --------
 git rm Documentation/\\*.txt::
-- 
1.6.6.rc1.31.g1a56b

^ permalink raw reply related

* [PATCH 2/2] git-add/rm doc: Consistently back-quote
From: Björn Gustavsson @ 2009-12-06 18:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Consistently back-quote commands, options and file names.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 Documentation/git-add.txt |   30 +++++++++++++++---------------
 Documentation/git-rm.txt  |    6 +++---
 2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index b7e8aa2..cf75738 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -22,22 +22,22 @@ from the index that are no longer present in the working tree.)
 The "index" holds a snapshot of the content of the working tree, and it
 is this snapshot that is taken as the contents of the next commit.  Thus
 after making any changes to the working directory, and before running
-the commit command, you must use the 'add' command to add any new or
+the commit command, you must use the `add` command to add any new or
 modified files to the index.
 
 This command can be performed multiple times before a commit.  It only
 adds the content of the specified file(s) at the time the add command is
 run; if you want subsequent changes included in the next commit, then
-you must run 'git add' again to add the new content to the index.
+you must run `git add` again to add the new content to the index.
 
-The 'git status' command can be used to obtain a summary of which
+The `git status` command can be used to obtain a summary of which
 files have changes that are staged for the next commit.
 
-The 'git add' command will not add ignored files by default.  If any
-ignored files were explicitly specified on the command line, 'git add'
+The `git add` command will not add ignored files by default.  If any
+ignored files were explicitly specified on the command line, `git add`
 will fail with a list of ignored files.  Ignored files reached by
 directory recursion or filename globbing performed by Git (quote your
-globs before the shell) will be silently ignored.  The 'add' command can
+globs before the shell) will be silently ignored.  The `add` command can
 be used to add ignored files with the `-f` (force) option.
 
 Please see linkgit:git-commit[1] for alternative ways to add content to a
@@ -97,15 +97,15 @@ apply.
 	Update only files that git already knows about, staging modified
 	content for commit and marking deleted files for removal. This
 	is similar
-	to what "git commit -a" does in preparation for making a commit,
+	to what `git commit -a` does in preparation for making a commit,
 	except that the update is limited to paths specified on the
 	command line. If no paths are specified, all tracked files in the
 	current directory and its subdirectories are updated.
 
 -A::
 --all::
-	Update files that git already knows about (same as '\--update'),
-	add all untracked files that are not ignored by '.gitignore'
+	Update files that git already knows about (same as `\--update`),
+	add all untracked files that are not ignored by `.gitignore`
 	mechanism and remove files from the index that are no longer
 	present in the working tree.
 +
@@ -117,8 +117,8 @@ the working tree as the target to sync the index with the working tree.
 	Record only the fact that the path will be added later. An entry
 	for the path is placed in the index with no content. This is
 	useful for, among other things, showing the unstaged content of
-	such files with 'git diff' and committing them with 'git commit
-	-a'.
+	such files with `git diff` and committing them with `git commit
+	-a`.
 
 --refresh::
 	Don't add the file(s), but only refresh their stat()
@@ -138,7 +138,7 @@ the working tree as the target to sync the index with the working tree.
 Configuration
 -------------
 
-The optional configuration variable 'core.excludesfile' indicates a path to a
+The optional configuration variable `core.excludesfile` indicates a path to a
 file containing patterns of file names to exclude from git-add, similar to
 $GIT_DIR/info/exclude.  Patterns in the exclude file are used in addition to
 those in info/exclude.  See linkgit:gitrepository-layout[5].
@@ -186,7 +186,7 @@ and type return, like this:
     What now> 1
 ------------
 
-You also could say "s" or "sta" or "status" above as long as the
+You also could say `s` or `sta` or `status` above as long as the
 choice is unique.
 
 The main command loop has 6 subcommands (plus help and quit).
@@ -194,9 +194,9 @@ The main command loop has 6 subcommands (plus help and quit).
 status::
 
    This shows the change between HEAD and index (i.e. what will be
-   committed if you say "git commit"), and between index and
+   committed if you say `git commit`), and between index and
    working tree files (i.e. what you could stage further before
-   "git commit" using "git-add") for each path.  A sample output
+   `git commit` using `git add`) for each path.  A sample output
    looks like this:
 +
 ------------
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index b4dff5b..2803266 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -12,13 +12,13 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Remove files from the index, or from the working tree and the index.
-'git-rm' will not remove a file from just your working directory.
+`git rm` will not remove a file from just your working directory.
 (There is no option to remove a file only from the work tree
 and yet keep it in the index; use `/bin/rm` if you want to do that.)
 The files being removed have to be identical to the tip of the branch,
 and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
-When '--cached' is given, the staged content has to
+When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
 allowing the file to be removed from just the index.
 
@@ -64,7 +64,7 @@ OPTIONS
 
 -q::
 --quiet::
-	'git-rm' normally outputs one line (in the form of an "rm" command)
+	`git rm` normally outputs one line (in the form of an `rm` command)
 	for each file removed. This option suppresses that output.
 
 
-- 
1.6.6.rc1.31.g1a56b

^ permalink raw reply related

* Re: Speedlimit at "git clone"
From: Daniel Stenberg @ 2009-12-06 18:13 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20091206144322.GB26440@coredump.intra.peff.net>

On Sun, 6 Dec 2009, Jeff King wrote:

>>> how can i limit the download speed at "git clone"?
>>>
>> no one any idea?
>
> Git has no internal support for doing this.

libcurl provides such a feature so if the clone is done over HTTP it would be 
easy to offer, but I figure it would also be weird to only provide it over a 
single particular transfer method...

-- 

  / daniel.haxx.se

^ permalink raw reply

* Re: [PATCH v2] Detailed diagnosis when parsing an object name fails.
From: Matthieu Moy @ 2009-12-06 18:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vljhj4wv0.fsf@alter.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

> y@imag.fr writes:

(sorry for the bad email, my fingers tried to anwser "yes" when
git-send-email asked me to confirm my address)

>> diff --git a/cache.h b/cache.h
>> index 0e69384..5c8cb5f 100644
>> --- a/cache.h
>> +++ b/cache.h
>> @@ -708,7 +708,11 @@ static inline unsigned int hexval(unsigned char c)
>>  #define DEFAULT_ABBREV 7
>>  
>>  extern int get_sha1(const char *str, unsigned char *sha1);
>> -extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
>> +static inline get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
>> +{
>> +	return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
>> +}
>> +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int fatal, const char *prefix);
>
> Do I understand correctly that "fatal" here is the same as "!gently"
> elsewhere in the API?

It seems it is. I renamed it.

>> +	if (errno == ENOENT || errno == ENOTDIR) {
>> +		char *fullname = malloc(strlen(filename)
>> +					     + strlen(prefix) + 1);
>> +		strcpy(fullname, prefix);
>> +		strcat(fullname, filename);
>
> What if malloc fails here (and elsewhere in your patch)?

Should have been an xmalloc. Fixed.

>> +static void diagnose_invalid_index_path(int stage,
>> +					const char *prefix,
>> +					const char *filename)
>> +{
>> +	struct stat st;
>> +
>> +	if (!prefix)
>> +		prefix = "";
>> +
>> +	if (!lstat(filename, &st))
>> +		die("Path '%s' exists on disk, but not in the index.", filename);
>> +	if (errno == ENOENT || errno == ENOTDIR) {
>> +		struct cache_entry *ce;
>> +		int pos;
>> +		int namelen = strlen(filename) + strlen(prefix);
>> +		char *fullname = malloc(namelen + 1);
>> +		strcpy(fullname, prefix);
>> +		strcat(fullname, filename);
>> +		pos = cache_name_pos(fullname, namelen);
>> +		if (pos < 0)
>> +			pos = -pos - 1;
>> +		ce = active_cache[pos];
>> +		if (ce_namelen(ce) == namelen &&
>> +		    !memcmp(ce->name, fullname, namelen))
>> +			die("Path '%s' is in the index, but not '%s'.\n"
>> +			    "Did you mean ':%d:%s'?",
>> +			    fullname, filename,
>> +			    stage, fullname);
                            ^^^^^
This one should have been ce_stage(ce), otherwise, we
suggest :0:t/Makefile as you probably guessed:

> What happens if the user asked for ":2:Makefile" while in directory "t/",
> and there is ":1:t/Makefile" but not ":2:t/Makefile" in the index?
>
> What should happen if the user asked for ":2:t/Makefile" in such a
> case?

I originally didn't handle the case "it's in the index, but not at the
stage you requested" (since most users asking :N:blah will be fluent
enough in Git not to need a special case of error messages). But I
finally implemented this test.

>> @@ -862,9 +934,24 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
>>  	}
>>  	if (*cp == ':') {
>>  		unsigned char tree_sha1[20];
>> -		if (!get_sha1_1(name, cp-name, tree_sha1))
>> -			return get_tree_entry(tree_sha1, cp+1, sha1,
>> -					      mode);
>> +		char *object_name;
>> +		if (fatal) {
>> +			object_name = malloc(cp-name+1);
>
> Where is this freed?

Nowhere. In the present state, it's not serious since all codepath
going through this malloc reach a die() right after. But a free()
doesn't harm, I've added it.

> Instead of doing a leaky allocation, it may make sense to pass the tree
> object name as <const char *, size_t> pair, and print it with "%.*s" in
> the error reporting codepath.  After all, object_name is used only for
> that purpose in diagnose_invalid_sha1_path(), no?

matter of taste, but I prefer doing one "complicated" thing (copying
the string) once, and avoid having to deal with two variables instead
of one later.

>> +test_expect_success 'correct file objects' '
>> +	HASH_file=$(git rev-parse HEAD:file.txt) &&
>> +	git rev-parse HEAD:subdir/file.txt &&
>> +	git rev-parse :index-only.txt &&
>> +	cd subdir &&
>> +	git rev-parse HEAD:file.txt &&
>> +	git rev-parse HEAD:subdir/file2.txt &&
>> +	test $HASH_file = $(git rev-parse HEAD:file.txt) &&
>> +	test $HASH_file = $(git rev-parse :file.txt) &&
>> +	test $HASH_file = $(git rev-parse :0:file.txt) &&
>> +	cd ..
>> +'
>
> Please make it a habit of not doing "cd" without forcing a subprocess
> using ().

Fixed.

>> +	test_must_fail git rev-parse foobar:file.txt 2>&1 |
>> +		grep "Invalid object name '"'"'foobar'"'"'." &&
>
> It always is better to write this in separate steps, because exit status
> of the upstream of pipe is discarded by the shell.

Fixed.

I'll resubmit soon.

--
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply

* Re: git-blame.el: what is format-spec?
From: David Kågedal @ 2009-12-06 18:43 UTC (permalink / raw)
  To: Sergei Organov; +Cc: Andreas Schwab, git
In-Reply-To: <87einafojx.fsf@osv.gnss.ru>

Sergei Organov <osv@javad.com> writes:

> Then there should be (require 'format-spec) in git-blame.el, right? Due
> to:

Of course. I must have missed that since I already had it loaded.

> Now, I've evaluated (require 'format-spec) in my Emacs 22 (yes, 22, not
> 23), and now git-blame almost works there. The problem I see is that it
> doesn't output anything in the echo area. It color-codes the buffer, it
> does show correct pop-up when mouse is over a region, but it doesn't
> print anything in the echo area when I move cursor through the regions.
> Any idea how to debug/fix this?

Well, it appears I removed the output to the echo area. I didn't think
it worked very well, and the new output format mostly replaces it by
showing the hash.

There are also technical reasons for removing it (it couldn't be
implemented very cleanly).

It would of course be possible to restore the old way, but I think it
would be good to ask ourselves what we really would like to see? Some
ideas:

* A keybinding to show the commit introducing the current line,
  including diff and all.

* A keybinding to show the commit message in the echo areia.

* The old "log --oneline in echo area when point moves in to a new
  region" behavior.

* The old behvaior with a delay.

* Inline display of even more information. For just the current region?

* A constantly updating separate window

-- 
David Kågedal

^ permalink raw reply

* [PATCH v2] Detailed diagnosis when parsing an object name fails.
From: Matthieu Moy @ 2009-12-06 18:45 UTC (permalink / raw)
  To: git, gitster; +Cc: Matthieu Moy
In-Reply-To: <vpqhbs4dkjr.fsf@bauges.imag.fr>

The previous error message was the same in many situations (unknown
revision or path not in the working tree). We try to help the user as
much as possible to understand the error, especially with the
sha1:filename notation. In this case, we say whether the sha1 or the
filename is problematic, and diagnose the confusion between
relative-to-root and relative-to-$PWD confusion precisely.

The 7 new error messages are tested.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
This should address Junio's comments (BTW, I forgot to say "thanks for
the review" :-) ).


 cache.h                        |    6 ++-
 setup.c                        |   15 +++++-
 sha1_name.c                    |  115 ++++++++++++++++++++++++++++++++++++++--
 t/t1506-rev-parse-diagnosis.sh |   69 ++++++++++++++++++++++++
 4 files changed, 198 insertions(+), 7 deletions(-)
 create mode 100755 t/t1506-rev-parse-diagnosis.sh

diff --git a/cache.h b/cache.h
index 0e69384..563862c 100644
--- a/cache.h
+++ b/cache.h
@@ -708,7 +708,11 @@ static inline unsigned int hexval(unsigned char c)
 #define DEFAULT_ABBREV 7
 
 extern int get_sha1(const char *str, unsigned char *sha1);
-extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
+static inline get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
+{
+	return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
+}
+extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 extern char *sha1_to_hex(const unsigned char *sha1);	/* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
diff --git a/setup.c b/setup.c
index f67250b..5792eb7 100644
--- a/setup.c
+++ b/setup.c
@@ -74,6 +74,18 @@ int check_filename(const char *prefix, const char *arg)
 	die_errno("failed to stat '%s'", arg);
 }
 
+static void NORETURN die_verify_filename(const char *prefix, const char *arg)
+{
+	unsigned char sha1[20];
+	unsigned mode;
+	/* try a detailed diagnostic ... */
+	get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
+	/* ... or fall back the most general message. */
+	die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+	    "Use '--' to separate paths from revisions", arg);
+
+}
+
 /*
  * Verify a filename that we got as an argument for a pathspec
  * entry. Note that a filename that begins with "-" never verifies
@@ -87,8 +99,7 @@ void verify_filename(const char *prefix, const char *arg)
 		die("bad flag '%s' used after filename", arg);
 	if (check_filename(prefix, arg))
 		return;
-	die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
-	    "Use '--' to separate paths from revisions", arg);
+	die_verify_filename(prefix, arg);
 }
 
 /*
diff --git a/sha1_name.c b/sha1_name.c
index 44bb62d..1bc09ac 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -804,7 +804,96 @@ int get_sha1(const char *name, unsigned char *sha1)
 	return get_sha1_with_mode(name, sha1, &unused);
 }
 
-int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
+/* Must be called only when object_name:filename doesn't exist. */
+static void diagnose_invalid_sha1_path(const char *prefix,
+				       const char *filename,
+				       const char *tree_sha1,
+				       const char *object_name)
+{
+	struct stat st;
+	unsigned char sha1[20];
+	unsigned mode;
+
+	if (!prefix)
+		prefix = "";
+
+	if (!lstat(filename, &st))
+		die("Path '%s' exists on disk, but not in '%s'.",
+		    filename, object_name);
+	if (errno == ENOENT || errno == ENOTDIR) {
+		char *fullname = xmalloc(strlen(filename)
+					     + strlen(prefix) + 1);
+		strcpy(fullname, prefix);
+		strcat(fullname, filename);
+
+		if (!get_tree_entry(tree_sha1, fullname,
+				    sha1, &mode)) {
+			die("Path '%s' exists, but not '%s'.\n"
+			    "Did you mean '%s:%s'?",
+			    fullname,
+			    filename,
+			    object_name,
+			    fullname);
+		}
+		die("Path '%s' does not exist in '%s'",
+		    filename, object_name);
+	}
+}
+
+/* Must be called only when :stage:filename doesn't exist. */
+static void diagnose_invalid_index_path(int stage,
+					const char *prefix,
+					const char *filename)
+{
+	struct stat st;
+	struct cache_entry *ce;
+	int pos;
+	int namelen = strlen(filename);
+	int fullnamelen;
+	char *fullname;
+
+	if (!prefix)
+		prefix = "";
+
+	/* Wrong stage number? */
+	pos = cache_name_pos(filename, namelen);
+	if (pos < 0)
+		pos = -pos - 1;
+	ce = active_cache[pos];
+	if (ce_namelen(ce) == namelen &&
+	    !memcmp(ce->name, filename, namelen))
+		die("Path '%s' is in the index, but not at stage %d.\n"
+		    "Did you mean ':%d:%s'?",
+		    filename, stage,
+		    ce_stage(ce), filename);
+
+	/* Confusion between relative and absolute filenames? */
+	fullnamelen = namelen + strlen(prefix);
+	fullname = xmalloc(fullnamelen + 1);
+	strcpy(fullname, prefix);
+	strcat(fullname, filename);
+	pos = cache_name_pos(fullname, fullnamelen);
+	if (pos < 0)
+		pos = -pos - 1;
+	ce = active_cache[pos];
+	if (ce_namelen(ce) == fullnamelen &&
+	    !memcmp(ce->name, fullname, fullnamelen))
+		die("Path '%s' is in the index, but not '%s'.\n"
+		    "Did you mean ':%d:%s'?",
+		    fullname, filename,
+		    ce_stage(ce), fullname);
+
+	if (!lstat(filename, &st))
+		die("Path '%s' exists on disk, but not in the index.", filename);
+	if (errno == ENOENT || errno == ENOTDIR)
+		die("Path '%s' does not exist (neither on disk nor in the index).",
+		    filename);
+
+	free(fullname);
+}
+
+
+int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
 {
 	int ret, bracket_depth;
 	int namelen = strlen(name);
@@ -850,6 +939,8 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
 			}
 			pos++;
 		}
+		if (!gently)
+			diagnose_invalid_index_path(stage, prefix, cp);
 		return -1;
 	}
 	for (cp = name, bracket_depth = 0; *cp; cp++) {
@@ -862,9 +953,25 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
 	}
 	if (*cp == ':') {
 		unsigned char tree_sha1[20];
-		if (!get_sha1_1(name, cp-name, tree_sha1))
-			return get_tree_entry(tree_sha1, cp+1, sha1,
-					      mode);
+		char *object_name;
+		if (!gently) {
+			object_name = xmalloc(cp-name+1);
+			strncpy(object_name, name, cp-name);
+			object_name[cp-name] = '\0';
+		}
+		if (!get_sha1_1(name, cp-name, tree_sha1)) {
+			const char *filename = cp+1;
+			ret = get_tree_entry(tree_sha1, filename, sha1, mode);
+			if (!gently) {
+				diagnose_invalid_sha1_path(prefix, filename,
+							   tree_sha1, object_name);
+				free(object_name);
+			}
+			return ret;
+		} else {
+			if (!gently)
+				die("Invalid object name '%s'.", object_name);
+		}
 	}
 	return ret;
 }
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
new file mode 100755
index 0000000..af721f9
--- /dev/null
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='test git rev-parse diagnosis for invalid argument'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+HASH_file=
+
+test_expect_success 'set up basic repo' '
+	echo one > file.txt &&
+	mkdir subdir &&
+	echo two > subdir/file.txt &&
+	echo three > subdir/file2.txt &&
+	git add . &&
+	git commit -m init &&
+	echo four > index-only.txt &&
+	git add index-only.txt &&
+	echo five > disk-only.txt
+'
+
+test_expect_success 'correct file objects' '
+	HASH_file=$(git rev-parse HEAD:file.txt) &&
+	git rev-parse HEAD:subdir/file.txt &&
+	git rev-parse :index-only.txt &&
+	(cd subdir &&
+	 git rev-parse HEAD:subdir/file2.txt &&
+	 test $HASH_file = $(git rev-parse HEAD:file.txt) &&
+	 test $HASH_file = $(git rev-parse :file.txt) &&
+	 test $HASH_file = $(git rev-parse :0:file.txt) )
+'
+
+test_expect_success 'incorrect revision id' '
+	test_must_fail git rev-parse foobar:file.txt 2>error &&
+	grep "Invalid object name '"'"'foobar'"'"'." error &&
+	test_must_fail git rev-parse foobar 2> error &&
+	grep "unknown revision or path not in the working tree." error
+'
+
+test_expect_success 'incorrect file in sha1:path' '
+	test_must_fail git rev-parse HEAD:nothing.txt 2> error &&
+	grep "fatal: Path '"'"'nothing.txt'"'"' does not exist in '"'"'HEAD'"'"'" error &&
+	test_must_fail git rev-parse HEAD:index-only.txt 2> error &&
+	grep "fatal: Path '"'"'index-only.txt'"'"' exists on disk, but not in '"'"'HEAD'"'"'." error &&
+	(cd subdir &&
+	 test_must_fail git rev-parse HEAD:file2.txt 2> error &&
+	 grep "Did you mean '"'"'HEAD:subdir/file2.txt'"'"'?" error )
+'
+
+test_expect_success 'incorrect file in :path and :N:path' '
+	test_must_fail git rev-parse :nothing.txt 2> error &&
+	grep "fatal: Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+	test_must_fail git rev-parse :1:nothing.txt 2> error &&
+	grep "Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+	test_must_fail git rev-parse :1:file.txt 2> error &&
+	grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+	(cd subdir &&
+	 test_must_fail git rev-parse :1:file.txt 2> error &&
+	 grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+	 test_must_fail git rev-parse :file2.txt 2> error &&
+	 grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error &&
+	 test_must_fail git rev-parse :2:file2.txt 2> error &&
+	 grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error) &&
+	test_must_fail git rev-parse :disk-only.txt 2> error &&
+	grep "fatal: Path '"'"'disk-only.txt'"'"' exists on disk, but not in the index." error
+'
+
+test_done
-- 
1.6.6.rc0.355.gcb53a.dirty

^ permalink raw reply related

* Re: Approach for collaborative branches
From: Gary Pickrell @ 2009-12-06 18:56 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git
In-Reply-To: <vpqhbs4f2gz.fsf@bauges.imag.fr>


Thank you Matthieu,

It did exactly what I wanted and I wouldn't have figured it out by myself.

    -Gary


Matthieu Moy wrote:
> Gary Pickrell <gary@pickrell.com> writes:
>
>   
>> 1) Clone the repository on Ubuntu
>> 2) Made an Ubuntu branch
>> 3) Made my changes to the code.  Added files...ect
>> 4) Used git push origin Ubuntu to push the changes to the repository
>>
>> I'm unable to see my Ubuntu changes on my windows machine.  How should
>> I proceed?
>>     
>
> On the windows machine, you should do first
>
>   git fetch
>
> this will tell you about the new branch if you didn't fetch it
> already. If it's called Ubuntu remotely, it's probably called
> origin/Ubuntu on your local repository after doing a fetch. Therefore,
> you can now do
>
>   git merge origin/Ubuntu
>
>
> Now, you probably also want your local branch to be named the same way
> on both machines. One way to do that is to create a branch "foo" on
> the repository, then on both sides, fetch it and do
>
>   git checkout --track origin/foo
>
> this will create a local branch foo, and tell git that further "pull"
> should take their changes from origin/foo.
>
>   

^ permalink raw reply

* Re: [PATCH 1/2] Documentation: 'git add --all' can remove files
From: Junio C Hamano @ 2009-12-06 21:14 UTC (permalink / raw)
  To: Björn Gustavsson; +Cc: git
In-Reply-To: <4B1BF2D2.8000506@gmail.com>

Björn Gustavsson <bgustavsson@gmail.com> writes:

> diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
> index e93e606..b7e8aa2 100644
> --- a/Documentation/git-add.txt
> +++ b/Documentation/git-add.txt
> @@ -16,6 +16,8 @@ DESCRIPTION
>  -----------
>  This command adds the current content of new or modified files to the
>  index, thus staging that content for inclusion in the next commit.
> +(Given the `--all` option, the `git add` command will also remove files
> +from the index that are no longer present in the working tree.)

The first paragraph says what "git add" does in general (does "something"
to the index) and continues to the second paragraph that describes what an
index is.

It is a good point that you noticed that the existing description of 'does
"something" to the index' covers only the "add as an entirety" and not the
whole range of what "git add" can be used for.  One thing that is missing
is a removal, and "-A" is one of the two options that lets you do so.

But "-u" is to match the index with the work tree contents (it does not
add untracked-so-far paths).  At least, "Given the `--all` option," part
needs to be rethought.

More importantly, 'does "something" to the index' is not limited to the
"add whole" and "remove path".  Perhaps we would want to update the first
paragraph like this to cover partial add while we are at it.

	This command updates the index using the current content found in
        the work tree, to prepare the content staged for the next commit.
        It typically adds the current content of existing paths as a whole
        but with some options, it can also be used to add a content with
        only part of the changes made to the work tree files applied, or
        remove paths that do not exist in the work tree anymore.

That way we also cover what "add -p" lets you do.

> ++
> +This option is especially useful after running the `rsync` command with
> +the working tree as the target to sync the index with the working tree.

I do not think mentioning `rsync` like this is helpful; I actually think
this clutteres the document and nudges people in a wrong direction at the
same time.  You usually don't overwrite your source tree you already have
under control with rsync.  Use cases that do not use "git" as a SCM but as
something else do have a valid reason to use rsync, but "git add -A" being
useful for that use case is a side effect, and I do not think we should
hint people who use "git" as a SCM into that direction, by casually
mentioning the use of `rsync` like the above as if it is a common and
encouraged thing to update your work tree from sideways.

> diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
> index 5afb1e7..b4dff5b 100644
> --- a/Documentation/git-rm.txt
> +++ b/Documentation/git-rm.txt
> @@ -81,6 +81,10 @@ two directories `d` and `d2`, there is a difference between
>  using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
>  also remove all of directory `d2`.
>  
> +To automatically remove all files from the index that are no longer
> +present in the working tree, see the `--all` option for
> +linkgit:git-add[1].

I think this change is a lot more harmful than being helpful.  Many lazy
readers would _not_ read "git add --help" but would think "git add -A"
would "automatically remove all files from the index that are no longer
present".  But "add -A" does a lot more than:

    git diff --name-only --diff-filter=D -z | xargs -0 rm -f

So overall I am happy with the issue you identified, but not very happy
with the proposed solution.

^ permalink raw reply

* Re: [StGit PATCH v2 0/6] add support for git send-email
From: Catalin Marinas @ 2009-12-06 22:16 UTC (permalink / raw)
  To: Alex Chiang; +Cc: git, Karl Wiberg
In-Reply-To: <20091202003503.7737.51579.stgit@bob.kio>

2009/12/2 Alex Chiang <achiang@hp.com>:
> This is v2 of the series that starts teaching stg mail how to
> call git send-email.

I merged these patches into the "proposed" branch for now and test
them for a bit more. It would be nice for some of the stgit options to
be translated into git send-email options (I don't mind renaming the
stgit options to match the git ones):

--refid -> --in-reply-to
--noreply -> --no-thread

Thanks.

-- 
Catalin

^ permalink raw reply

* [PATCH 1/2] Documentation: 'git add -A' can remove files
From: Björn Gustavsson @ 2009-12-06 23:03 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Document that 'git add -A/--all' can remove files no longer
present in the working tree from the index, and also document the
behavior with and without path arguments on the command line.

Also update the intro paragraph (as suggested by Junio, with
some minor edits) to make it clear that 'git add' is able to
delete and to also cover the -p option.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
Thanks, Junio, for reviewing my patch, and for suggesting a
much better intro paragraph.

I have cut out the mention of rsync and the reference from
git-rm.

I also realized that the documentation was not clear on whether
any path arguments were allowed, so I have clarified that too.

 Documentation/git-add.txt |   20 ++++++++++++++------
 1 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index e93e606..6d3e76f 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -14,8 +14,12 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This command adds the current content of new or modified files to the
-index, thus staging that content for inclusion in the next commit.
+This command updates the index using the current content found in
+the working tree, to prepare the content staged for the next commit.
+It typically adds the current content of existing paths as a whole,
+but with some options it can also be used to add content with
+only part of the changes made to the working tree files applied, or
+remove paths that do not exist in the work tree anymore.
 
 The "index" holds a snapshot of the content of the working tree, and it
 is this snapshot that is taken as the contents of the next commit.  Thus
@@ -102,10 +106,14 @@ apply.
 
 -A::
 --all::
-	Update files that git already knows about (same as '\--update')
-	and add all untracked files that are not ignored by '.gitignore'
-	mechanism.
-
+	Update files that git already knows about (same as '\--update'),
+	add all untracked files that are not ignored by the '.gitignore'
+	mechanism and remove files from the index that are no longer
+	present in the working tree.
++
+If no paths are given on the command line, `git add -A` will operate
+on the current directory and its subdirectories. If paths are given,
+it will operate on those paths and their subdirectories.
 
 -N::
 --intent-to-add::
-- 
1.6.6.rc1.31.g1a56b

^ permalink raw reply related

* [PATCH 2/2] git-add/rm doc: Consistently back-quote
From: Björn Gustavsson @ 2009-12-06 23:04 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Consistently back-quote commands, options and file names.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 Documentation/git-add.txt |   30 +++++++++++++++---------------
 Documentation/git-rm.txt  |    6 +++---
 2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 6d3e76f..4e33b30 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -24,22 +24,22 @@ remove paths that do not exist in the work tree anymore.
 The "index" holds a snapshot of the content of the working tree, and it
 is this snapshot that is taken as the contents of the next commit.  Thus
 after making any changes to the working directory, and before running
-the commit command, you must use the 'add' command to add any new or
+the commit command, you must use the `add` command to add any new or
 modified files to the index.
 
 This command can be performed multiple times before a commit.  It only
 adds the content of the specified file(s) at the time the add command is
 run; if you want subsequent changes included in the next commit, then
-you must run 'git add' again to add the new content to the index.
+you must run `git add` again to add the new content to the index.
 
-The 'git status' command can be used to obtain a summary of which
+The `git status` command can be used to obtain a summary of which
 files have changes that are staged for the next commit.
 
-The 'git add' command will not add ignored files by default.  If any
-ignored files were explicitly specified on the command line, 'git add'
+The `git add` command will not add ignored files by default.  If any
+ignored files were explicitly specified on the command line, `git add`
 will fail with a list of ignored files.  Ignored files reached by
 directory recursion or filename globbing performed by Git (quote your
-globs before the shell) will be silently ignored.  The 'add' command can
+globs before the shell) will be silently ignored.  The `add` command can
 be used to add ignored files with the `-f` (force) option.
 
 Please see linkgit:git-commit[1] for alternative ways to add content to a
@@ -99,15 +99,15 @@ apply.
 	Update only files that git already knows about, staging modified
 	content for commit and marking deleted files for removal. This
 	is similar
-	to what "git commit -a" does in preparation for making a commit,
+	to what `git commit -a` does in preparation for making a commit,
 	except that the update is limited to paths specified on the
 	command line. If no paths are specified, all tracked files in the
 	current directory and its subdirectories are updated.
 
 -A::
 --all::
-	Update files that git already knows about (same as '\--update'),
-	add all untracked files that are not ignored by the '.gitignore'
+	Update files that git already knows about (same as `\--update`),
+	add all untracked files that are not ignored by the `.gitignore`
 	mechanism and remove files from the index that are no longer
 	present in the working tree.
 +
@@ -120,8 +120,8 @@ it will operate on those paths and their subdirectories.
 	Record only the fact that the path will be added later. An entry
 	for the path is placed in the index with no content. This is
 	useful for, among other things, showing the unstaged content of
-	such files with 'git diff' and committing them with 'git commit
-	-a'.
+	such files with `git diff` and committing them with `git commit
+	-a`.
 
 --refresh::
 	Don't add the file(s), but only refresh their stat()
@@ -141,7 +141,7 @@ it will operate on those paths and their subdirectories.
 Configuration
 -------------
 
-The optional configuration variable 'core.excludesfile' indicates a path to a
+The optional configuration variable `core.excludesfile` indicates a path to a
 file containing patterns of file names to exclude from git-add, similar to
 $GIT_DIR/info/exclude.  Patterns in the exclude file are used in addition to
 those in info/exclude.  See linkgit:gitrepository-layout[5].
@@ -189,7 +189,7 @@ and type return, like this:
     What now> 1
 ------------
 
-You also could say "s" or "sta" or "status" above as long as the
+You also could say `s` or `sta` or `status` above as long as the
 choice is unique.
 
 The main command loop has 6 subcommands (plus help and quit).
@@ -197,9 +197,9 @@ The main command loop has 6 subcommands (plus help and quit).
 status::
 
    This shows the change between HEAD and index (i.e. what will be
-   committed if you say "git commit"), and between index and
+   committed if you say `git commit`), and between index and
    working tree files (i.e. what you could stage further before
-   "git commit" using "git-add") for each path.  A sample output
+   `git commit` using `git add`) for each path.  A sample output
    looks like this:
 +
 ------------
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 5afb1e7..d4162f6 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -12,13 +12,13 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Remove files from the index, or from the working tree and the index.
-'git-rm' will not remove a file from just your working directory.
+`git rm` will not remove a file from just your working directory.
 (There is no option to remove a file only from the work tree
 and yet keep it in the index; use `/bin/rm` if you want to do that.)
 The files being removed have to be identical to the tip of the branch,
 and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
-When '--cached' is given, the staged content has to
+When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
 allowing the file to be removed from just the index.
 
@@ -64,7 +64,7 @@ OPTIONS
 
 -q::
 --quiet::
-	'git-rm' normally outputs one line (in the form of an "rm" command)
+	`git rm` normally outputs one line (in the form of an `rm` command)
 	for each file removed. This option suppresses that output.
 
 
-- 
1.6.6.rc1.31.g1a56b

^ permalink raw reply related

* Re: [PATCH 1/2] Documentation: 'git add -A' can remove files
From: Junio C Hamano @ 2009-12-06 23:31 UTC (permalink / raw)
  To: Björn Gustavsson; +Cc: git
In-Reply-To: <4B1C384A.8000106@gmail.com>

Björn Gustavsson <bgustavsson@gmail.com> writes:

> Document that 'git add -A/--all' can remove files no longer
> present in the working tree from the index, and also document the
> behavior with and without path arguments on the command line.

Thanks.

> +This command updates the index using the current content found in
> +the working tree, to prepare the content staged for the next commit.
> +It typically adds the current content of existing paths as a whole,
> +but with some options it can also be used to add content with
> +only part of the changes made to the working tree files applied, or
> +remove paths that do not exist in the work tree anymore.

You probably want to change the last one also to "working tree"?

I often write this as "work tree" without thinking too much about "work"
vs "working", but if anybody asks me, I prefer the former because it is
shorter and because it is more consistent with the way how names of the
environment variable GIT_WORK_TREE and the configuration variable
core.worktree are spelled.  I personally am OK with either word used in
the descriptive text, as there is no room for confusion.

But it would be better to be consistent in a single paragraph.

>  -A::
>  --all::
> -	Update files that git already knows about (same as '\--update')
> -	and add all untracked files that are not ignored by '.gitignore'
> -	mechanism.
> -
> +	Update files that git already knows about (same as '\--update'),
> +	add all untracked files that are not ignored by the '.gitignore'
> +	mechanism and remove files from the index that are no longer
> +	present in the working tree.
> ++
> +If no paths are given on the command line, `git add -A` will operate
> +on the current directory and its subdirectories. If paths are given,
> +it will operate on those paths and their subdirectories.

The first line of the existing description for "--all", by saying "same as
--update", refers to the first sentence of the corresponding entry for
"update", which says:

    -u::
    --update::
            Update only files that git already knows about, staging modified
            content for commit and marking deleted files for removal. This
            is similar
            to what "git commit -a" does in preparation for making a commit,
            except that the update is limited to paths specified on the
            command line. If no paths are specified, all tracked files in the
            current directory and its subdirectories are updated.

In fact, "-A" is "do everything -u does, including removal of lost paths,
honoring the pathspecs exactly the same way (e.g. no pathspec is to work
in the current directory). but unlike -u, also add any new files that are
not excluded by the ignore mechanism."  There is something wrong if we
have to spend more lines to describe "-A" than we describe "-u", if
description of "-A" says "it does the same for -u, and in addition...".

I wonder if we can restructure the description of "-u" to make it easier
to read, to simplify the description of "-A".

^ permalink raw reply

* Re: Continued work on sr/vcs-helper and sr/gfi-options
From: Junio C Hamano @ 2009-12-06 23:36 UTC (permalink / raw)
  To: Sverre Rabbelier; +Cc: Git List
In-Reply-To: <fabb9a1e0912060223h148a67b0q589b8461dae6330e@mail.gmail.com>

Sverre Rabbelier <srabbelier@gmail.com> writes:

>>  - Merge today's 'master' to sr/remote-hg (optional);
>
> I'm not sure why this is though? (no objections against it, I just
> don't understand the motivation)

It would make sense if you use 1.6.6 features in your new series (as the
forkpoint of sr/vcs-helper is beginning to look a tad stale), but
otherwise unnecessary; that is the reason why I said it is "(optional)"
and it is up to what is in the remote-hg patch.

>>  - Create an unstable sr/pu-remote-hg branch that:
>>
>>   - is reset to the tip of sr/remote-hg at the beginning of the day;
>>   - merges the day's sr/gfi-options on top;
>>   - re-applies patches to implement Hg interoperation on top of the
>>     result.
>
> Ok, that does make sense, how would I send out patches for review from
> this unstable branch though? (since others would not have the required
> merges etc) I reckon it would be necessary to publish sr/pu-remote-hg
> somewhere?

I was thinking about carrying it myself (perhaps with help from you in
conflict resolution as necessary) when I wrote it, but if you want me to
pull from your repository somewhere e.g. repo.or.cz, that would also be
fine.

^ 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