git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 12/28] clone: support remote shallow repository

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Dec 5, 2013, 13:02 UTC
Message-ID
<1386248575-10206-13-git-send-email-pclouds@gmail.com>
In-Reply-To
<1386248575-10206-1-git-send-email-pclouds@gmail.com>

Cloning from a shallow repository does not follow the "8 steps for new .git/shallow" because if it does we need to get through step 6 for all refs. That means commit walking down to the bottom.

Instead the rule to create .git/shallow is simpler and, more importantly, cheap: if a shallow commit is found in the pack, it's probably used (i.e. reachable from some refs), so we add it. Others are dropped.

One may notice this method seems flawed by the word "probably". A shallow commit may not be reachable from any refs at all if it's attached to an object island (a group of objects that are not reachable by any refs).

If that object island is not complete, a new fetch request may send more objects to connect it to some ref. At that time, because we incorrectly installed the shallow commit in this island, the user will not see anything after that commit (fsck is still ok). This is not desired.

Given that object islands are rare (C Git never sends such islands for security reasons) and do not really harm the repository integrity, a tradeoff is made to surprise the user occasionally but work faster everyday.

A new option --strict could be added later that follows exactly the 8 steps. "git prune" can also learn to remove dangling objects _and_ the shallow commits that are attached to them from .git/shallow.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c      |  1 +
 builtin/fetch-pack.c |  2 +-
 fetch-pack.c         | 54 +++++++++++++++++++++++++++++++++++++++++++++++++---
 fetch-pack.h         |  4 ++++
 transport.c          | 11 ++++++++---
 transport.h          |  6 ++++++
 6 files changed, 71 insertions(+), 7 deletions(-)
diff --git a/builtin/clone.c b/builtin/clone.c
index 900f564..0b182ce 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -889,6 +889,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 	remote = remote_get(option_origin);
 	transport = transport_get(remote, remote->url[0]);
+	transport->cloning = 1;
 
 	if (!transport->get_refs_list || (!is_local && !transport->fetch))
 		die(_("Don't know how to clone %s"), transport->url);
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index c1d918f..927424b 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -153,7 +153,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 	get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, NULL);
 
 	ref = fetch_pack(&args, fd, conn, ref, dest,
-			 sought, nr_sought, pack_lockfile_ptr);
+			 sought, nr_sought, NULL, pack_lockfile_ptr);
 	if (pack_lockfile) {
 		printf("lock %s\n", pack_lockfile);
 		fflush(stdout);
diff --git a/fetch-pack.c b/fetch-pack.c
index 35d097e..e18a9fd 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -13,6 +13,7 @@
 #include "transport.h"
 #include "version.h"
 #include "prio-queue.h"
+#include "sha1-array.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -774,6 +775,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 int fd[2],
 				 const struct ref *orig_ref,
 				 struct ref **sought, int nr_sought,
+				 struct shallow_info *si,
 				 char **pack_lockfile)
 {
 	struct ref *ref = copy_ref_list(orig_ref);
@@ -852,6 +854,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
 					NULL);
+	else if (args->cloning && si->shallow && si->shallow->nr)
+		alternate_shallow_file = setup_temporary_shallow(si->shallow);
 	else
 		alternate_shallow_file = NULL;
 	if (get_pack(args, fd, pack_lockfile))
@@ -925,8 +929,11 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
 	return dst;
 }
 
-static void update_shallow(struct fetch_pack_args *args)
+static void update_shallow(struct fetch_pack_args *args,
+			   struct shallow_info *si)
 {
+	int i;
+
 	if (args->depth > 0 && alternate_shallow_file) {
 		if (*alternate_shallow_file == '\0') { /* --unshallow */
 			unlink_or_warn(git_path("shallow"));
@@ -935,6 +942,42 @@ static void update_shallow(struct fetch_pack_args *args)
 			commit_lock_file(&shallow_lock);
 		return;
 	}
+
+	if (!si->shallow || !si->shallow->nr)
+		return;
+
+	if (alternate_shallow_file) {
+		/*
+		 * The temporary shallow file is only useful for
+		 * index-pack and unpack-objects because it may
+		 * contain more roots than we want. Delete it.
+		 */
+		if (*alternate_shallow_file)
+			unlink(alternate_shallow_file);
+		free((char*)alternate_shallow_file);
+	}
+
+	if (args->cloning) {
+		/*
+		 * remote is shallow, but this is a clone, there are
+		 * no objects in repo to worry about. Accept any
+		 * shallow points that exist in the pack (iow in repo
+		 * after get_pack() and reprepare_packed_git())
+		 */
+		struct sha1_array extra = SHA1_ARRAY_INIT;
+		unsigned char (*sha1)[20] = si->shallow->sha1;
+		for (i = 0; i < si->shallow->nr; i++)
+			if (has_sha1_file(sha1[i]))
+				sha1_array_append(&extra, sha1[i]);
+		if (extra.nr) {
+			setup_alternate_shallow(&shallow_lock,
+						&alternate_shallow_file,
+						&extra);
+			commit_lock_file(&shallow_lock);
+		}
+		sha1_array_clear(&extra);
+		return;
+	}
 }
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
@@ -942,9 +985,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 		       const struct ref *ref,
 		       const char *dest,
 		       struct ref **sought, int nr_sought,
+		       struct sha1_array *shallow,
 		       char **pack_lockfile)
 {
 	struct ref *ref_cpy;
+	struct shallow_info si;
 
 	fetch_pack_setup();
 	if (nr_sought)
@@ -954,8 +999,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 		packet_flush(fd[1]);
 		die("no matching remote head");
 	}
-	ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
-	update_shallow(args);
+	prepare_shallow_info(&si, shallow);
+	ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
+				&si, pack_lockfile);
 	reprepare_packed_git();
+	update_shallow(args, &si);
+	clear_shallow_info(&si);
 	return ref_cpy;
 }
diff --git a/fetch-pack.h b/fetch-pack.h
index 9b08388..ce59537 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -4,6 +4,8 @@
 #include "string-list.h"
 #include "run-command.h"
 
+struct sha1_array;
+
 struct fetch_pack_args {
 	const char *uploadpack;
 	int unpacklimit;
@@ -20,6 +22,7 @@ struct fetch_pack_args {
 	unsigned stateless_rpc:1;
 	unsigned check_self_contained_and_connected:1;
 	unsigned self_contained_and_connected:1;
+	unsigned cloning:1;
 };
 
 /*
@@ -33,6 +36,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 		       const char *dest,
 		       struct ref **sought,
 		       int nr_sought,
+		       struct sha1_array *shallow,
 		       char **pack_lockfile);
 
 #endif
diff --git a/transport.c b/transport.c
index 90453df..91c4667 100644
--- a/transport.c
+++ b/transport.c
@@ -456,6 +456,7 @@ struct git_transport_data {
 	int fd[2];
 	unsigned got_remote_heads : 1;
 	struct sha1_array extra_have;
+	struct sha1_array shallow;
 };
 
 static int set_git_option(struct git_transport_options *opts,
@@ -512,7 +513,9 @@ 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], NULL, 0, &refs,
-			 for_push ? REF_NORMAL : 0, &data->extra_have, NULL);
+			 for_push ? REF_NORMAL : 0,
+			 &data->extra_have,
+			 transport->cloning ? &data->shallow : NULL);
 	data->got_remote_heads = 1;
 
 	return refs;
@@ -539,17 +542,19 @@ static int fetch_refs_via_pack(struct transport *transport,
 	args.depth = data->options.depth;
 	args.check_self_contained_and_connected =
 		data->options.check_self_contained_and_connected;
+	args.cloning = transport->cloning;
 
 	if (!data->got_remote_heads) {
 		connect_setup(transport, 0, 0);
 		get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
-				 NULL, NULL);
+				 NULL,
+				 transport->cloning ? &data->shallow : NULL);
 		data->got_remote_heads = 1;
 	}
 
 	refs = fetch_pack(&args, data->fd, data->conn,
 			  refs_tmp ? refs_tmp : transport->remote_refs,
-			  dest, to_fetch, nr_heads,
+			  dest, to_fetch, nr_heads, &data->shallow,
 			  &transport->pack_lockfile);
 	close(data->fd[0]);
 	close(data->fd[1]);
diff --git a/transport.h b/transport.h
index b3679bb..59842d4 100644
--- a/transport.h
+++ b/transport.h
@@ -35,6 +35,12 @@ struct transport {
 	 */
 	unsigned cannot_reuse : 1;
 
+	/*
+	 * A hint from caller that it will be performing a clone, not
+	 * normal fetch. IOW the repository is guaranteed empty.
+	 */
+	unsigned cloning : 1;
+
 	/**
 	 * Returns 0 if successful, positive if the option is not
 	 * recognized or is inapplicable, and negative if the option
-- 
1.8.5.1.25.g8667982
Previous: Nguyễn Thái Ngọc DuyNext: Nguyễn Thái Ngọc Duy
Message 60 of 79 in “First class shallow clone”
  1. 00/28 First class shallow cloneNguyễn Thái Ngọc Duy, Nov 25, 2013
  2. 01/28 transport.h: remove send_pack prototype, already defined in send-pack.hNguyễn Thái Ngọc Duy, Nov 25, 2013
  3. 02/28 send-pack: forbid pushing from a shallow repositoryNguyễn Thái Ngọc Duy, Nov 25, 2013
  4. 03/28 clone: prevent --reference to a shallow repositoryNguyễn Thái Ngọc Duy, Nov 25, 2013
  5. Eric SunshineNov 26, 2013
  6. 04/28 update-server-info: do not publish shallow clonesNguyễn Thái Ngọc Duy, Nov 25, 2013
  7. Junio C HamanoNov 25, 2013
  8. Duy NguyenNov 26, 2013
  9. 05/28 Advertise shallow graft information on the server endNguyễn Thái Ngọc Duy, Nov 25, 2013
  10. 06/28 connect.c: teach get_remote_heads to parse "shallow" linesNguyễn Thái Ngọc Duy, Nov 25, 2013
  11. Junio C HamanoNov 25, 2013
  12. Junio C HamanoNov 25, 2013
  13. Duy NguyenNov 27, 2013
  14. 07/28 shallow.c: add remove_reachable_shallow_points()Nguyễn Thái Ngọc Duy, Nov 25, 2013
  15. Junio C HamanoNov 25, 2013
  16. 08/28 shallow.c: add mark_new_shallow_refs()Nguyễn Thái Ngọc Duy, Nov 25, 2013
  17. Junio C HamanoNov 25, 2013
  18. Duy NguyenNov 26, 2013
  19. Junio C HamanoNov 26, 2013
  20. 09/28 shallow.c: extend setup_*_shallow() to accept extra shallow pointsNguyễn Thái Ngọc Duy, Nov 25, 2013
  21. Junio C HamanoNov 25, 2013
  22. 10/28 fetch-pack.c: move shallow update code out of fetch_pack()Nguyễn Thái Ngọc Duy, Nov 25, 2013
  23. 11/28 fetch-pack.h: one statement per bitfield declarationNguyễn Thái Ngọc Duy, Nov 25, 2013
  24. 12/28 clone: support remote shallow repositoryNguyễn Thái Ngọc Duy, Nov 25, 2013
  25. 13/28 fetch: support fetching from a shallow repositoryNguyễn Thái Ngọc Duy, Nov 25, 2013
  26. Eric SunshineNov 27, 2013
  27. 14/28 upload-pack: make sure deepening preserves shallow rootsNguyễn Thái Ngọc Duy, Nov 25, 2013
  28. 15/28 fetch: add --update-shallow to get refs that require updating .git/shallowNguyễn Thái Ngọc Duy, Nov 25, 2013
  29. Eric SunshineNov 27, 2013
  30. Duy NguyenNov 27, 2013
  31. Junio C HamanoNov 27, 2013
  32. 16/28 receive-pack: reorder some code in unpack()Nguyễn Thái Ngọc Duy, Nov 25, 2013
  33. Junio C HamanoDec 2, 2013
  34. 17/28 Support pushing from a shallow cloneNguyễn Thái Ngọc Duy, Nov 25, 2013
  35. Eric SunshineNov 26, 2013
  36. 18/28 New var GIT_SHALLOW_FILE to propagate --shallow-file to subprocessesNguyễn Thái Ngọc Duy, Nov 25, 2013
  37. 19/28 connected.c: add new variant that runs with --shallow-fileNguyễn Thái Ngọc Duy, Nov 25, 2013
  38. 20/28 receive-pack: allow pushing with new shallow rootsNguyễn Thái Ngọc Duy, Nov 25, 2013
  39. 21/28 send-pack: support pushing to a shallow cloneNguyễn Thái Ngọc Duy, Nov 25, 2013
  40. 22/28 remote-curl: pass ref SHA-1 to fetch-pack as wellNguyễn Thái Ngọc Duy, Nov 25, 2013
  41. 23/28 Support fetch/clone over httpNguyễn Thái Ngọc Duy, Nov 25, 2013
  42. 24/28 receive-pack: support pushing to a shallow clone via httpNguyễn Thái Ngọc Duy, Nov 25, 2013
  43. 25/28 send-pack: support pushing from a shallow clone via httpNguyễn Thái Ngọc Duy, Nov 25, 2013
  44. 26/28 git-clone.txt: remove shallow clone limitationsNguyễn Thái Ngọc Duy, Nov 25, 2013
  45. 27/28 clone: use git protocol for cloning shallow repo locallyNguyễn Thái Ngọc Duy, Nov 25, 2013
  46. Eric SunshineNov 27, 2013
  47. 28/28 prune: clean .git/shallow after pruning objectsNguyễn Thái Ngọc Duy, Nov 25, 2013
  48. 00/28 First class shallow cloneNguyễn Thái Ngọc Duy, Dec 5, 2013
  49. 01/28 transport.h: remove send_pack prototype, already defined in send-pack.hNguyễn Thái Ngọc Duy, Dec 5, 2013
  50. 02/28 Replace struct extra_have_objects with struct sha1_arrayNguyễn Thái Ngọc Duy, Dec 5, 2013
  51. 03/28 send-pack: forbid pushing from a shallow repositoryNguyễn Thái Ngọc Duy, Dec 5, 2013
  52. 04/28 clone: prevent --reference to a shallow repositoryNguyễn Thái Ngọc Duy, Dec 5, 2013
  53. 05/28 Make the sender advertise shallow commits to the receiverNguyễn Thái Ngọc Duy, Dec 5, 2013
  54. 06/28 connect.c: teach get_remote_heads to parse "shallow" linesNguyễn Thái Ngọc Duy, Dec 5, 2013
  55. 07/28 shallow.c: extend setup_*_shallow() to accept extra shallow commitsNguyễn Thái Ngọc Duy, Dec 5, 2013
  56. 08/28 shallow.c: the 8 steps to select new commits for .git/shallowNguyễn Thái Ngọc Duy, Dec 5, 2013
  57. 09/28 shallow.c: steps 6 and 7 to select new commits for .git/shallowNguyễn Thái Ngọc Duy, Dec 5, 2013
  58. 10/28 fetch-pack.c: move shallow update code out of fetch_pack()Nguyễn Thái Ngọc Duy, Dec 5, 2013
  59. 11/28 fetch-pack.h: one statement per bitfield declarationNguyễn Thái Ngọc Duy, Dec 5, 2013
  60. 12/28 clone: support remote shallow repositoryNguyễn Thái Ngọc Duy, Dec 5, 2013
  61. 13/28 fetch: support fetching from a shallow repositoryNguyễn Thái Ngọc Duy, Dec 5, 2013
  62. 14/28 upload-pack: make sure deepening preserves shallow rootsNguyễn Thái Ngọc Duy, Dec 5, 2013
  63. 15/28 fetch: add --update-shallow to accept refs that update .git/shallowNguyễn Thái Ngọc Duy, Dec 5, 2013
  64. 16/28 receive-pack: reorder some code in unpack()Nguyễn Thái Ngọc Duy, Dec 5, 2013
  65. 17/28 receive/send-pack: support pushing from a shallow cloneNguyễn Thái Ngọc Duy, Dec 5, 2013
  66. 18/28 New var GIT_SHALLOW_FILE to propagate --shallow-file to subprocessesNguyễn Thái Ngọc Duy, Dec 5, 2013
  67. 19/28 connected.c: add new variant that runs with --shallow-fileNguyễn Thái Ngọc Duy, Dec 5, 2013
  68. 20/28 receive-pack: allow pushes that update .git/shallowNguyễn Thái Ngọc Duy, Dec 5, 2013
  69. 21/28 send-pack: support pushing to a shallow cloneNguyễn Thái Ngọc Duy, Dec 5, 2013
  70. 22/28 remote-curl: pass ref SHA-1 to fetch-pack as wellNguyễn Thái Ngọc Duy, Dec 5, 2013
  71. 23/28 Support shallow fetch/clone over smart-httpNguyễn Thái Ngọc Duy, Dec 5, 2013
  72. Jeff KingJan 8, 2014
  73. t5537: fix incorrect expectation in test case 10Nguyễn Thái Ngọc Duy, Jan 8, 2014
  74. Jeff KingJan 9, 2014
  75. 24/28 receive-pack: support pushing to a shallow clone via httpNguyễn Thái Ngọc Duy, Dec 5, 2013
  76. 25/28 send-pack: support pushing from a shallow clone via httpNguyễn Thái Ngọc Duy, Dec 5, 2013
  77. 26/28 clone: use git protocol for cloning shallow repo locallyNguyễn Thái Ngọc Duy, Dec 5, 2013
  78. 27/28 prune: clean .git/shallow after pruning objectsNguyễn Thái Ngọc Duy, Dec 5, 2013
  79. 28/28 git-clone.txt: remove shallow clone limitationsNguyễn Thái Ngọc Duy, Dec 5, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.