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

[PATCH 16/22] unpack-trees.c: optionally clone submodules for later checkout

From
Sskimo@liacs.nl <skimo@liacs.nl>
Date
May 23, 2007, 22:23 UTC
Message-ID
<11799589931789-git-send-email-skimo@liacs.nl>
In-Reply-To
<11799589913153-git-send-email-skimo@liacs.nl>
From: Sven Verdoolaege <skimo@kotnet.org>

When the --submodules option is specified and a submodule to be checked out is not available locally, git-checkout will search for submodule.<submodule>.url options in the remote configuration and clone each submodule using the first url that it can use from the local site.

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
---
 Documentation/config.txt |    3 +
 Makefile                 |    4 +-
 submodules.c             |  211 ++++++++++++++++++++++++++++++++++++++++++++++
 submodules.h             |    7 ++
 unpack-trees.c           |   50 +++++++++++
 5 files changed, 273 insertions(+), 2 deletions(-)
 create mode 100644 submodules.c
 create mode 100644 submodules.h
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5045443..2a2e142 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -615,6 +615,9 @@ showbranch.default::
 	The default set of branches for gitlink:git-show-branch[1].
 	See gitlink:git-show-branch[1].
 
+submodule.<submodule>.url
+	The URL of a submodule.  See gitlink:git-clone[1].
+
 tar.umask::
 	By default, gitlink:git-tar-tree[1] sets file and directories modes
 	to 0666 or 0777. While this is both useful and acceptable for projects
diff --git a/Makefile b/Makefile
index 1fa1896..39bf2d4 100644
--- a/Makefile
+++ b/Makefile
@@ -298,7 +298,7 @@ LIB_H = \
 	run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
 	tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
 	utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
-	mailmap.h remote.h
+	mailmap.h remote.h submodules.h
 
 DIFF_OBJS = \
 	diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -321,7 +321,7 @@ LIB_OBJS = \
 	alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
 	color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
 	convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
-	$(HTTP_CONFIG_OBJ)
+	$(HTTP_CONFIG_OBJ) submodules.o
 
 BUILTIN_OBJS = \
 	builtin-add.o \
diff --git a/submodules.c b/submodules.c
new file mode 100644
index 0000000..44c0f2c
--- /dev/null
+++ b/submodules.c
@@ -0,0 +1,211 @@
+#include "cache.h"
+#include "refs.h"
+#include "submodules.h"
+#include "run-command.h"
+
+int is_checkedout_submodule(const char *path)
+{
+	unsigned char sha1[20];
+	return resolve_gitlink_ref(path, "HEAD", sha1) == 0;
+}
+
+struct key_val_list {
+	struct key_val_list *next;
+	char *key;
+	char *val;
+};
+
+static void free_key_val_list(struct key_val_list *list)
+{
+	struct key_val_list *next;
+	for (; list; list = next) {
+		next = list->next;
+		free(list->key);
+		free(list->val);
+		free(list);
+	}
+}
+
+static struct key_val_list *find_key_val_list(struct key_val_list *list,
+						const char *key)
+{
+	while (list && strcmp(list->key, key))
+		list = list->next;
+	return list;
+}
+
+struct collect_urls_data {
+	struct key_val_list **next;
+
+	const char *type;
+};
+
+static int collect_urls(const char *var, const char *value, void *cb_data)
+{
+	struct collect_urls_data *cb = (struct collect_urls_data*)cb_data;
+	int typelen = strlen(cb->type);
+	int len;
+	char *doturl;
+	struct key_val_list *item;
+
+	if (prefixcmp(var, cb->type))
+		return 0;
+
+	if (var[typelen] != '.')
+		return 0;
+
+	var += typelen+1;
+
+	doturl = strrchr(var, '.');
+	if (!doturl || strcmp(doturl, ".url"))
+		return 0;
+
+	len = doturl-var;
+	if (len <= 0)
+		return 0;
+
+	item = xmalloc(sizeof(struct key_val_list));
+	item->key = xmalloc(len+1);
+	memcpy(item->key, var, len);
+	item->key[len] = 0;
+	item->val = xstrdup(value);
+	item->next = NULL;
+	*cb->next = item;
+	cb->next = &item->next;
+
+	return 0;
+}
+
+static const char *local_URL(const char *remote, const char *url)
+{
+	static char local_url[PATH_MAX];
+
+	if (!prefixcmp(url, "https://"))
+		return url;
+
+	if (!prefixcmp(url, "http://"))
+		return url;
+
+	if (!prefixcmp(url, "ftp://"))
+		return url;
+
+	if (!prefixcmp(remote, "/"))
+		return url;
+
+	if (!prefixcmp(remote, "ssh://") && !prefixcmp(url, "/")) {
+		char *slash;
+		int len = strlen(url);
+
+		slash = strchr(remote+6, '/');
+		if (!slash || (slash-remote)+len+1 > sizeof(local_url))
+			return NULL;
+		memcpy(local_url, remote, slash-remote);
+		memcpy(local_url+(slash-remote), url, len+1);
+		return local_url;
+	}
+
+	return NULL;
+}
+
+static int fetch_submodule_urls(struct key_val_list **next_url)
+{
+	struct key_val_list *remotes = NULL;
+	struct collect_urls_data remotes_data = { &remotes, "remote" };
+	struct key_val_list *remote;
+	static char key[1024];
+
+	git_config(collect_urls, &remotes_data);
+	for (remote = remotes; remote; remote = remote->next) {
+		struct key_val_list *submodules = NULL;
+		struct collect_urls_data submodules_data =
+			{ &submodules, "submodule" };
+		struct key_val_list *submodule;
+		char *dest;
+
+		dest = xstrdup(remote->val);
+		git_config_from_remote(collect_urls, dest, &submodules_data);
+		free(dest);
+		for (submodule = submodules; submodule; submodule = submodule->next) {
+			const char *local_url;
+			struct key_val_list *item;
+
+			local_url = local_URL(remote->val, submodule->val);
+			if (!local_url)
+				continue;
+
+			if (snprintf(key, sizeof(key),
+			    "submodule.%s.url", submodule->key) > sizeof(key))
+				return error("submodule name too long");
+
+			git_config_set(key, local_url);
+
+			item = xmalloc(sizeof(struct key_val_list));
+			item->key = xstrdup(submodule->key);
+			item->val = xstrdup(local_url);
+			item->next = NULL;
+			*next_url = item;
+			next_url = &item->next;
+		}
+
+		free_key_val_list(submodules);
+	}
+
+	free_key_val_list(remotes);
+
+	return 0;
+}
+
+int clone_submodule(const char *submodule)
+{
+	struct key_val_list *submodules = NULL;
+	struct collect_urls_data submodules_data = { &submodules, "submodule" };
+	struct key_val_list *item;
+	char *path;
+	int err;
+	const char *args[10];
+	int argc;
+
+	git_config(collect_urls, &submodules_data);
+	item = find_key_val_list(submodules, submodule);
+	if (!item) {
+		err = fetch_submodule_urls(submodules_data.next);
+		if (err)
+			return err;
+		item = find_key_val_list(*submodules_data.next, submodule);
+		if (!item)
+			return error("don't know where to get submodule '%s'",
+					submodule);
+	}
+
+	path = git_path("submodules/%s", submodule);
+
+	argc = 0;
+	args[argc++] = "clone";
+	args[argc++] = "--submodules";
+	args[argc++] = "-n";
+	args[argc++] = item->val;
+	args[argc++] = path;
+	args[argc] = NULL;
+
+	err = run_command_v_opt(args, RUN_GIT_CMD|RUN_COMMAND_CLEAR_GIT_ENV);
+
+	path = git_path("submodules/%s/.git", submodule);
+
+	argc = 0;
+	args[argc++] = "update-ref";
+	args[argc++] = "--no-deref";
+	args[argc++] = "HEAD";
+	args[argc++] = "0000000000000000000000000000000000000000";
+	args[argc] = NULL;
+
+	if (!err)
+		err = run_command_v_opt_cd(args,
+				RUN_GIT_CMD|RUN_COMMAND_CLEAR_GIT_ENV, path);
+
+	if (err)
+		return error("failed to clone submodule '%s'", submodule);
+
+	free_key_val_list(submodules);
+
+	return 0;
+}
diff --git a/submodules.h b/submodules.h
new file mode 100644
index 0000000..bf3f118
--- /dev/null
+++ b/submodules.h
@@ -0,0 +1,7 @@
+#ifndef SUBMODULES_H
+#define SUBMODULES_H
+
+int is_checkedout_submodule(const char *path);
+int clone_submodule(const char *submodule);
+
+#endif
diff --git a/unpack-trees.c b/unpack-trees.c
index d5e458d..ddefb51 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -6,6 +6,7 @@
 #include "unpack-trees.h"
 #include "progress.h"
 #include "refs.h"
+#include "submodules.h"
 
 #define DBRT_DEBUG 1
 
@@ -804,6 +805,35 @@ int threeway_merge(struct cache_entry **stages,
 	return count;
 }
 
+static int ensure_submodule(struct cache_entry *ce,
+			    struct unpack_trees_options *o)
+{
+	struct stat st;
+	char *path;
+
+	if (!ce)
+		return 0;
+
+	if (!S_ISGITLINK(ntohl(ce->ce_mode)))
+		return 0;
+
+	if (!is_checkedout_submodule(ce->name) && !o->submodules)
+		return 0;
+
+	path = mkpath("%s/.git", ce->name);
+	if (lstat(path, &st)) {
+		path = git_path("submodules/%s/.git", ce->name);
+		if (lstat(path, &st)) {
+			if (clone_submodule(ce->name))
+				return -1;
+		}
+	}
+
+	/* Now check that the commit is available and fetch if needed */
+
+	return 0;
+}
+
 /*
  * Two-way merge.
  *
@@ -829,6 +859,17 @@ int twoway_merge(struct cache_entry **src,
 	if (newtree == o->df_conflict_entry)
 		newtree = NULL;
 
+	if (o->update) {
+		int err;
+		err = ensure_submodule(current, o);
+		if (!err)
+			err = ensure_submodule(oldtree, o);
+		if (!err)
+			err = ensure_submodule(newtree, o);
+		if (err)
+			return err;
+	}
+
 	if (current) {
 		if ((!oldtree && !newtree) || /* 4 and 5 */
 		    (!oldtree && newtree &&
@@ -905,6 +946,15 @@ int oneway_merge(struct cache_entry **src,
 		return error("Cannot do a oneway merge of %d trees",
 			     o->merge_size);
 
+	if (o->update) {
+		int err;
+		err = ensure_submodule(old, o);
+		if (!err)
+			err = ensure_submodule(a, o);
+		if (err)
+			return err;
+	}
+
 	if (!a)
 		return deleted_entry(old, old, o);
 	if (old && same(old, a)) {
-- 
1.5.2.784.g5532e
Previous: skimo@liacs.nlNext: skimo@liacs.nl
Message 26 of 94 in “[RFC] Fourth round of support for cloning submodules”
  1. skimo@liacs.nlMay 23, 2007
  2. 01/22 git_connect: unset CONFIG_ENVIRONMENT in childskimo@liacs.nl, May 23, 2007
  3. 02/22 Add dump-configskimo@liacs.nl, May 23, 2007
  4. 03/22 git-config: add --remote option for reading config from remote reposkimo@liacs.nl, May 23, 2007
  5. 04/22 http.h: make fill_active_slots a function pointerskimo@liacs.nl, May 23, 2007
  6. 05/22 git-config: read remote config files over HTTPskimo@liacs.nl, May 23, 2007
  7. 06/22 unpack-trees.c: pass cache_entry * to verify_absent rather than just the nameskimo@liacs.nl, May 23, 2007
  8. 07/22 git-read-tree: take --submodules optionskimo@liacs.nl, May 23, 2007
  9. 08/22 unpack-trees.c: assume submodules are cleanskimo@liacs.nl, May 23, 2007
  10. 09/22 Add run_command_v_opt_cd: chdir into a directory before execskimo@liacs.nl, May 23, 2007
  11. 10/22 run-command: optionally clear git environmentskimo@liacs.nl, May 23, 2007
  12. Alex RiesenMay 24, 2007
  13. Shawn O. PearceMay 24, 2007
  14. Alex RiesenMay 24, 2007
  15. 11/22 entry.c: optionally checkout submodulesskimo@liacs.nl, May 23, 2007
  16. Alex RiesenMay 24, 2007
  17. Shawn O. PearceMay 24, 2007
  18. Sven VerdoolaegeMay 24, 2007
  19. Alex RiesenMay 24, 2007
  20. Martin WaitzMay 24, 2007
  21. Shawn O. PearceMay 25, 2007
  22. 12/22 git-checkout: pass --submodules option to git-read-treeskimo@liacs.nl, May 23, 2007
  23. 13/22 git-read-tree: treat null commit as empty treeskimo@liacs.nl, May 23, 2007
  24. 14/22 git_config: add void * for callback dataskimo@liacs.nl, May 23, 2007
  25. 15/22 make redirecting stdout to /dev/null available via run_command_v_optskimo@liacs.nl, May 23, 2007
  26. 16/22 unpack-trees.c: optionally clone submodules for later checkoutskimo@liacs.nl, May 23, 2007
  27. 17/22 entry.c: optionally checkout newly cloned submodulesskimo@liacs.nl, May 23, 2007
  28. Johannes SixtMay 24, 2007
  29. 18/22 git-clone: add --submodules for cloning submodulesskimo@liacs.nl, May 23, 2007
  30. 19/22 test for simple submodule checkout supportskimo@liacs.nl, May 23, 2007
  31. 20/22 checkout_submodule: checkout submodule on forced checkout of submodule dirskimo@liacs.nl, May 23, 2007
  32. 21/22 run-command: optionally redirect stderr to /dev/nullskimo@liacs.nl, May 23, 2007
  33. 22/22 ensure_submodule: fetch missing revisionsskimo@liacs.nl, May 23, 2007
  34. Johannes SchindelinMay 23, 2007
  35. Junio C HamanoMay 24, 2007
  36. Sven VerdoolaegeMay 24, 2007
  37. Shawn O. PearceMay 24, 2007
  38. Sven VerdoolaegeMay 24, 2007
  39. Johannes SchindelinMay 24, 2007
  40. Sven VerdoolaegeMay 24, 2007
  41. Johannes SchindelinMay 24, 2007
  42. Sven VerdoolaegeMay 24, 2007
  43. Johannes SchindelinMay 24, 2007
  44. Sven VerdoolaegeMay 24, 2007
  45. Johannes SchindelinMay 24, 2007
  46. Johannes SixtMay 24, 2007
  47. Johannes SchindelinMay 24, 2007
  48. Sven VerdoolaegeMay 24, 2007
  49. Johannes SchindelinMay 24, 2007
  50. Sven VerdoolaegeMay 24, 2007
  51. Johannes SchindelinMay 24, 2007
  52. Sven VerdoolaegeMay 24, 2007
  53. Johannes SchindelinMay 24, 2007
  54. Lars HjemliMay 24, 2007
  55. Junio C HamanoMay 24, 2007
  56. Lars HjemliMay 24, 2007
  57. Sven VerdoolaegeMay 24, 2007
  58. Linus TorvaldsMay 24, 2007
  59. Sven VerdoolaegeMay 24, 2007
  60. Linus TorvaldsMay 24, 2007
  61. Junio C HamanoMay 24, 2007
  62. Lars HjemliMay 24, 2007
  63. Johannes SchindelinMay 24, 2007
  64. Johannes SchindelinMay 24, 2007
  65. Sven VerdoolaegeMay 25, 2007
  66. Junio C HamanoMay 25, 2007
  67. Sven VerdoolaegeMay 25, 2007
  68. Johannes SchindelinMay 25, 2007
  69. Junio C HamanoMay 24, 2007
  70. Josef WeidendorferMay 25, 2007
  71. Johannes SchindelinMay 25, 2007
  72. Josef WeidendorferMay 25, 2007
  73. Johannes SchindelinMay 25, 2007
  74. Sven VerdoolaegeMay 25, 2007
  75. Johannes SchindelinMay 25, 2007
  76. Josef WeidendorferMay 25, 2007
  77. Johannes SchindelinMay 25, 2007
  78. Linus TorvaldsMay 25, 2007
  79. Josef WeidendorferMay 25, 2007
  80. Johannes SchindelinMay 25, 2007
  81. Josef WeidendorferMay 25, 2007
  82. Jakub NarebskiMay 25, 2007
  83. Johannes SchindelinMay 25, 2007
  84. Santi BéjarMay 24, 2007
  85. Sven VerdoolaegeMay 24, 2007
  86. Lars HjemliMay 24, 2007
  87. Martin WaitzMay 27, 2007
  88. Sven VerdoolaegeMay 27, 2007
  89. Martin WaitzMay 24, 2007
  90. Sven VerdoolaegeMay 24, 2007
  91. Johannes SchindelinMay 24, 2007
  92. Sven VerdoolaegeMay 24, 2007
  93. Petr BaudisMay 24, 2007
  94. Johannes SchindelinMay 24, 2007

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.