Re* [PATCH/RFC] Documentation/git-gc.txt: add reference to githooks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



David Brown <davidb@xxxxxxxxxxxxxx> writes:

> Suppose I want to publish some changes to a tree.  I have a server
> available where I can run a git daemon, but for one reason or another
> I want to force people to use the another git repo as a reference.
> The reason could be one of bandwidth, or someone who isn't comfortable
> making all of the other source available.  Ideally, someone who
> already has the other git repo cloned, and just adds mine as a remote
> wouldn't notice the difference.
>
> Is there a way to do this?  I've tried various ways of using
> alternates to keep the blobs out of the repository I want to export,
> but the daemon just follows the alternates.  If I remove the
> alternates, I then seem to have a broken repository.  Most things I
> try, at least carry objects for all of the files in the HEAD tree,
> which most of the time is a large portion of the data.

I've seen people ask for something like this in the past for a few times.
Once might have been during GitTogether'08 by you.

I don't think there is a way to do that, though.

Perhaps something like this totally untested patch?  The code may be
utterly wrong but you should be able to get the idea from the test
scripts.  Add configuration to the repository for you to declare which
objects are prerequiste for pulling from you, with a message to suggest
where to grab them from.

I am a bit reluctant to suggest upload-pack to start reading from the
repository configuration, though.

 t/t5532-upload-limit.sh |  107 +++++++++++++++++++++++++++++++++++++++++
 upload-pack.c           |  120 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 227 insertions(+), 0 deletions(-)

diff --git a/t/t5532-upload-limit.sh b/t/t5532-upload-limit.sh
new file mode 100755
index 0000000..070594e
--- /dev/null
+++ b/t/t5532-upload-limit.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Google Inc.
+#
+
+test_description='git upload-pack honoring clonelimit configuration'
+
+. ./test-lib.sh
+
+grow () {
+	for a
+	do
+		echo "$a" >file &&
+		git add file &&
+		git commit -m "$a" || exit
+	done
+}
+
+setup_clone () {
+	git clone "file://$(pwd)/src" "$1" &&
+	(
+		cd "$1" &&
+		git rev-parse --verify "$2" >actual
+	) &&
+	test_cmp src/expect "$1/actual"
+}
+
+test_expect_success 'setup' '
+	mkdir src &&
+	(
+		cd src &&
+		git init &&
+		grow a b c &&
+		git rev-parse --verify master >expect
+	) &&
+	setup_clone dst-0 master &&
+	setup_clone dst-1 master &&
+	(
+		cd src &&
+		grow d e &&
+		git tag -a -m mark mark &&
+		git rev-parse --verify mark >expect
+	) &&
+	setup_clone dst-2 mark &&
+	setup_clone dst-3 mark &&
+	(
+		cd src &&
+		grow f g h &&
+		git tag -a -m tip tip &&
+		git rev-parse --verify tip >expect
+	)
+'
+
+test_expect_success 'baseline - no limitation in fetch' '
+	(
+		cd dst-0 &&
+		git fetch &&
+		git rev-parse --verify tip >actual
+	) &&
+	test_cmp src/expect dst-0/actual
+'
+
+test_expect_success 'clone refused when limitation is set' '
+	(
+		cd src &&
+		git config clonelimit.mark.message "go away"
+	) &&
+	test_must_fail git clone "file://$(pwd)/src" dst-4 &&
+	! test -d dst-4
+'
+
+test_expect_success 'fetch refused when limitation is unmet' '
+	(
+		cd dst-1 &&
+		test_must_fail git fetch &&
+		test_must_fail git rev-parse --verify tip
+	)
+'
+
+test_expect_success 'fetch works when limitation is met' '
+	(
+		cd src &&
+		git config clonelimit.mark.message "go away"
+	) &&
+	(
+		cd dst-2 &&
+		git fetch &&
+		git rev-parse --verify tip >actual
+	) &&
+	test_cmp src/expect dst-2/actual
+'
+
+test_expect_success 'missing tag is not a limitation violation' '
+	(
+		cd src &&
+		git config clonelimit.mark.message "go away"
+	) &&
+	(
+		cd dst-3 &&
+		git tag -d mark &&
+		git fetch &&
+		git rev-parse --verify tip >actual
+	) &&
+	test_cmp src/expect dst-3/actual
+'
+
+test_done
diff --git a/upload-pack.c b/upload-pack.c
index 92f9530..9d4a367 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -655,6 +655,124 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag
 	return 0;
 }
 
+/*
+ * Some repositories may not want to allow a full clone, and
+ * want users to first fetch from other repositories with
+ * better connection.  By having a configuration variable like
+ * this:
+ *
+ * [clonelimit "v0.99"]
+ *   message = go to git://git.kernel.org/pub/scm/git/...
+ *
+ * a fetch request by a fetcher that does not have the named commit
+ * is denied with the given message.
+ */
+struct clone_limit {
+	struct clone_limit *next;
+	struct commit *commit;
+	char *ref; /* points at the tail of msg to store symbolic ref */
+	char msg[FLEX_ARRAY]; /* message and more */
+};
+
+static int check_clone_limit(const char *var, const char *value, void *cb_)
+{
+	int msglen, reflen;
+	const char *lastdot;
+	struct clone_limit **clp = cb_;
+	struct clone_limit *limit;
+	unsigned char sha1[20];
+
+	if (prefixcmp(var, "clonelimit."))
+		return 0;
+
+	if (debug_fd) {
+		write_str_in_full(debug_fd, "config: ");
+		write_str_in_full(debug_fd, var);
+		write_str_in_full(debug_fd, " = <");
+		write_str_in_full(debug_fd, value);
+		write_str_in_full(debug_fd, ">\n");
+	}
+
+	var += 11; /* skip "clonelimit." */
+	lastdot = strrchr(var, '.');
+	reflen = lastdot - var;
+	if (reflen < 0 || memcmp(var + reflen, ".message", 9))
+		return 0; /* not our variable */
+
+	/*
+	 * The remainder will ignore a malformed entry; we might
+	 * want to abort the whole operation.  I dunno.
+	 */
+	if (!value)
+		return 0;
+	msglen = strlen(value) + 1;
+	limit = xmalloc(sizeof(*limit) + msglen + reflen + 1);
+	memcpy(limit->msg, value, msglen);
+	limit->ref = limit->msg + msglen;
+	memcpy(limit->ref, var, reflen);
+	limit->ref[reflen] = '\0';
+	if (get_sha1(limit->ref, sha1)) {
+		free(limit);
+		return 0;
+	}
+	limit->commit = lookup_commit_reference_gently(sha1, 0);
+	if (!limit->commit) {
+		free(limit);
+		return 0;
+	}
+	limit->next = *clp;
+	*clp = limit;
+	return 0;
+}
+
+static int limit_served_history(void)
+{
+	int has_missing, i, n;
+	struct clone_limit *clone_limit = NULL;
+	struct clone_limit *cl;
+	struct commit **twos = NULL;
+	struct commit_list *l;
+
+	git_config(check_clone_limit, &clone_limit);
+	if (!clone_limit)
+		return 0;
+
+	twos = xcalloc(have_obj.nr, sizeof(*twos));
+	for (i = n = 0; i < have_obj.nr; i++) {
+		struct object *o = have_obj.objects[i].item;
+		struct commit *have = lookup_commit_reference_gently(o->sha1, 0);
+		if (!have)
+			continue;
+		twos[n++] = have;
+	}
+
+	has_missing = 0;
+	for (cl = clone_limit; cl; cl = cl->next) {
+		int seen = 0;
+		l = get_merge_bases_many(cl->commit, n, twos, 1);
+		while (l) {
+			struct commit_list *next = l->next;
+			if (!hashcmp(l->item->object.sha1, cl->commit->object.sha1))
+				seen = 1;
+			free(l);
+			l = next;
+		}
+		if (!seen) {
+			has_missing = 1;
+			error("you do not have object '%s'; %s",
+			      cl->ref, cl->msg);
+		}
+	}
+
+	while (clone_limit) {
+		struct clone_limit *next = clone_limit->next;
+		free(clone_limit);
+		clone_limit = next;
+	}
+	free(twos);
+	return has_missing;
+}
+
 static void upload_pack(void)
 {
 	if (advertise_refs || !stateless_rpc) {
@@ -672,6 +790,8 @@ static void upload_pack(void)
 	receive_needs();
 	if (want_obj.nr) {
 		get_common_commits();
+		if (limit_served_history())
+			die("git upload-pack: missing prerequisites");
 		create_pack_file();
 	}
 }
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]