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

[PATCH 6/6] clone, submodule update: check out branches

From
Glen Choo via GitGitGadget <gitgitgadget@gmail.com>
Date
Aug 29, 2022, 20:54 UTC
Message-ID
<6f7f2f9a3f19b6d874d644b7fb7feb3a72fc6227.1661806456.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1321.git.git.1661806456.gitgitgadget@gmail.com>
From: Glen Choo <chooglen@google.com>

Teach "git submodule update" to update submodules by creating and checking out the current superproject branch when "submodule.propagateBranches=true". "git clone --recurse-submodules" also learns this trick because it is implemented with "git submodule update --recursive".

With "submodule.propagateBranches=true", submodules are cloned with "--detach" so that they do not contain branches from their upstream. This prevents conflicts between branch names from the superproject and the branch names from the submodule's upstream. Arguably, "--detach" should also be the default for "submodule.propagateBranches=false" since it doesn't make sense to create a submodule branch when the submodule is always expected to be in detached HEAD. But, to be conservative, this commit does not change the behavior of "submodule.propagateBranches=false".

"git submodule update" tries to create the branch as long as it is not currently checked out, thus it will fail if the submodule has the branch, but it is not checked out. This is fine because the main purpose of "git submodule update" is to clone new submodules (which have no branches, and will never have this problem). "git checkout" with "submodule.propagateBranches" will cover the use case of recursively checking out an existing branch.

Signed-off-by: Glen Choo <chooglen@google.com>
---
 builtin/submodule--helper.c | 28 ++++++++++++++++++++++++++--
 t/t5617-clone-submodules.sh | 34 ++++++++++++++++++++++++++++++++++
 t/t7406-submodule-update.sh | 22 ++++++++++++++++++++++
 3 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cbf6bda4850..7eb2c45900e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1695,6 +1695,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
 			strvec_push(&cp.args, clone_data->single_branch ?
 				    "--single-branch" :
 				    "--no-single-branch");
+		if (the_repository->settings.submodule_propagate_branches)
+			strvec_push(&cp.args, "--detach");
+
 
 		strvec_push(&cp.args, "--");
 		strvec_push(&cp.args, clone_data->url);
@@ -1733,6 +1736,9 @@ static int clone_submodule(struct module_clone_data *clone_data)
 	if (error_strategy)
 		git_config_set_in_file(p, "submodule.alternateErrorStrategy",
 				       error_strategy);
+	if (the_repository->settings.submodule_propagate_branches)
+		git_config_set_in_file(p, "submodule.propagateBranches",
+				       "true");
 
 	free(sm_alternate);
 	free(error_strategy);
@@ -1792,6 +1798,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_clone_options,
 			     git_submodule_helper_usage, 0);
+	prepare_repo_settings(the_repository);
 
 	clone_data.dissociate = !!dissociate;
 	clone_data.quiet = !!quiet;
@@ -1872,6 +1879,7 @@ struct submodule_update_clone {
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
+	const char *super_branch;
 	enum submodule_update_type update_default;
 	struct object_id suboid;
 	struct string_list references;
@@ -2206,6 +2214,8 @@ static int run_update_command(struct update_data *ud, int subforce)
 		strvec_pushl(&cp.args, "checkout", "-q", NULL);
 		if (subforce)
 			strvec_push(&cp.args, "-f");
+		if (ud->super_branch)
+			strvec_pushl(&cp.args, "-b", ud->super_branch, NULL);
 		break;
 	case SM_UPDATE_REBASE:
 		cp.git_cmd = 1;
@@ -2456,6 +2466,7 @@ static void update_data_to_args(struct update_data *update_data, struct strvec *
 static int update_submodule(struct update_data *update_data)
 {
 	int submodule_up_to_date;
+	const char *submodule_head = NULL;
 
 	ensure_core_worktree(update_data->sm_path);
 
@@ -2469,7 +2480,7 @@ static int update_submodule(struct update_data *update_data)
 	if (update_data->just_cloned)
 		oidcpy(&update_data->suboid, null_oid());
 	else if (resolve_gitlink_ref(update_data->sm_path, "HEAD",
-				     &update_data->suboid, NULL))
+				     &update_data->suboid, &submodule_head))
 		die(_("Unable to find current revision in submodule path '%s'"),
 			update_data->displaypath);
 
@@ -2493,7 +2504,13 @@ static int update_submodule(struct update_data *update_data)
 		free(remote_ref);
 	}
 
-	submodule_up_to_date = oideq(&update_data->oid, &update_data->suboid);
+	if (update_data->super_branch &&
+	    submodule_head &&
+	    !skip_prefix(submodule_head, "refs/heads/", &submodule_head))
+		submodule_up_to_date = !strcmp(update_data->super_branch, submodule_head);
+	else
+		submodule_up_to_date = oideq(&update_data->oid, &update_data->suboid);
+
 	if (!submodule_up_to_date || update_data->force)
 		if (run_update_procedure(update_data))
 			return 1;
@@ -2551,6 +2568,12 @@ static int update_submodules(struct update_data *update_data)
 		goto cleanup;
 	}
 
+	if (the_repository->settings.submodule_propagate_branches) {
+		struct branch *current_branch = branch_get(NULL);
+		if (current_branch)
+			update_data->super_branch = current_branch->name;
+	}
+
 	for (i = 0; i < suc.update_clone_nr; i++) {
 		struct update_clone_data ucd = suc.update_clone[i];
 
@@ -2634,6 +2657,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
 	memset(&filter_options, 0, sizeof(filter_options));
 	argc = parse_options(argc, argv, prefix, module_update_options,
 			     git_submodule_helper_usage, 0);
+	prepare_repo_settings(the_repository);
 
 	if (opt.require_init)
 		opt.init = 1;
diff --git a/t/t5617-clone-submodules.sh b/t/t5617-clone-submodules.sh
index b5c66cb18cb..215fb02e9fb 100755
--- a/t/t5617-clone-submodules.sh
+++ b/t/t5617-clone-submodules.sh
@@ -12,10 +12,17 @@ pwd=$(pwd)
 test_expect_success 'setup' '
 	git checkout -b main &&
 	test_commit commit1 &&
+	mkdir subsub &&
+	(
+		cd subsub &&
+		git init &&
+		test_commit subsubcommit1
+	) &&
 	mkdir sub &&
 	(
 		cd sub &&
 		git init &&
+		git submodule add "file://$pwd/subsub" subsub &&
 		test_commit subcommit1 &&
 		git tag sub_when_added_to_super &&
 		git branch other
@@ -106,4 +113,31 @@ test_expect_success '--no-also-filter-submodules overrides clone.filterSubmodule
 	test_cmp_config -C super_clone3/sub false --default false remote.origin.promisor
 '
 
+test_expect_success 'submodule.propagateBranches checks out branches at correct commits' '
+	git -C sub checkout -b not-main &&
+	git -C subsub checkout -b not-main &&
+	git clone --recurse-submodules \
+		-c submodule.propagateBranches=true \
+		"file://$pwd/." super_clone4 &&
+
+	# Assert that each repo is pointing to "main"
+	for REPO in "super_clone4" "super_clone4/sub" "super_clone4/sub/subsub"
+	do
+	    HEAD_BRANCH=$(git -C $REPO symbolic-ref HEAD) &&
+	    test $HEAD_BRANCH = "refs/heads/main" || return 1
+	done &&
+
+	# Assert that the submodule branches are pointing to the right revs
+	EXPECT_SUB_OID="$(git -C super_clone4 rev-parse :sub)" &&
+	ACTUAL_SUB_OID="$(git -C super_clone4/sub rev-parse refs/heads/main)" &&
+	test $EXPECT_SUB_OID = $ACTUAL_SUB_OID &&
+	EXPECT_SUBSUB_OID="$(git -C super_clone4/sub rev-parse :subsub)" &&
+	ACTUAL_SUBSUB_OID="$(git -C super_clone4/sub/subsub rev-parse refs/heads/main)" &&
+	test $EXPECT_SUBSUB_OID = $ACTUAL_SUBSUB_OID &&
+
+	# Assert that the submodules do not have branches from their upstream
+	test_must_fail git -C super_clone4/sub rev-parse not-main &&
+	test_must_fail git -C super_clone4/sub/subsub rev-parse not-main
+'
+
 test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 6cc07460dd2..00a6fec8912 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -1178,4 +1178,26 @@ test_expect_success 'submodule update --recursive skip submodules with strategy=
 	test_cmp expect.err actual.err
 '
 
+test_expect_success 'submodule update with submodule.propagateBranches checks out branches' '
+	test_when_finished "rm -fr top-cloned" &&
+	cp -r top-clean top-cloned &&
+
+	# Create a new upstream submodule
+	git init middle2 &&
+	test_commit -C middle2 "middle2" &&
+	git -C top submodule add ../middle2 middle2 &&
+	git -C top commit -m "add middle2" &&
+
+	git -C top-cloned checkout -b "new-branch" &&
+	git -C top-cloned pull origin main &&
+	test_config -C top-cloned submodule.propagateBranches true &&
+	git -C top-cloned submodule update --recursive &&
+
+	for REPO in "top-cloned/middle2" "top-cloned/middle" "top-cloned/middle/bottom"
+	do
+	    HEAD_BRANCH=$(git -C $REPO symbolic-ref HEAD) &&
+	    test $HEAD_BRANCH = "refs/heads/new-branch" || return 1
+	done
+'
+
 test_done
-- 
gitgitgadget
Previous: Glen ChooNext: Philippe Blain
Message 11 of 56 in “clone, submodule update: check out submodule branches”
  1. 0/6 clone, submodule update: check out submodule branchesGlen Choo via GitGitGadget, Aug 29, 2022
  2. 1/6 clone: teach --detach optionGlen Choo via GitGitGadget, Aug 29, 2022
  3. Philippe BlainAug 30, 2022
  4. 2/6 repo-settings: add submodule_propagate_branchesGlen Choo via GitGitGadget, Aug 29, 2022
  5. Philippe BlainAug 30, 2022
  6. 3/6 t5617: drop references to remote-tracking branchesGlen Choo via GitGitGadget, Aug 29, 2022
  7. Philippe BlainAug 30, 2022
  8. 4/6 submodule: return target of submodule symrefGlen Choo via GitGitGadget, Aug 29, 2022
  9. Jonathan TanSep 1, 2022
  10. Glen ChooSep 1, 2022
  11. 6/6 clone, submodule update: check out branchesGlen Choo via GitGitGadget, Aug 29, 2022
  12. Philippe BlainAug 30, 2022
  13. Glen ChooAug 30, 2022
  14. Jonathan TanSep 1, 2022
  15. 5/6 submodule--helper: refactor up-to-date criterionGlen Choo via GitGitGadget, Aug 29, 2022
  16. 0/7 clone, submodule update: check out submodule branchesGlen Choo via GitGitGadget, Oct 20, 2022
  17. 1/7 clone: teach --detach optionGlen Choo via GitGitGadget, Oct 20, 2022
  18. 2/7 repo-settings: add submodule_propagate_branchesGlen Choo via GitGitGadget, Oct 20, 2022
  19. Jonathan TanOct 25, 2022
  20. 3/7 submodule--helper clone: create named branchGlen Choo via GitGitGadget, Oct 20, 2022
  21. Jonathan TanOct 25, 2022
  22. 4/7 t5617: drop references to remote-tracking branchesGlen Choo via GitGitGadget, Oct 20, 2022
  23. 5/7 submodule: return target of submodule symrefGlen Choo via GitGitGadget, Oct 20, 2022
  24. 6/7 submodule update: refactor update targetsGlen Choo via GitGitGadget, Oct 20, 2022
  25. 7/7 clone, submodule update: create and check out branchesGlen Choo via GitGitGadget, Oct 20, 2022
  26. Jonathan TanOct 25, 2022
  27. Glen ChooOct 25, 2022
  28. Junio C HamanoOct 20, 2022
  29. Glen ChooOct 20, 2022
  30. Junio C HamanoOct 21, 2022
  31. 0/8 clone, submodule update: check out submodule branchesGlen Choo via GitGitGadget, Oct 28, 2022
  32. 1/8 clone: teach --detach optionGlen Choo via GitGitGadget, Oct 28, 2022
  33. Junio C HamanoOct 28, 2022
  34. Junio C HamanoOct 28, 2022
  35. Glen ChooOct 28, 2022
  36. Taylor BlauOct 30, 2022
  37. Glen ChooOct 31, 2022
  38. Philippe BlainNov 8, 2022
  39. 2/8 repo-settings: add submodule_propagate_branchesGlen Choo via GitGitGadget, Oct 28, 2022
  40. 3/8 submodule--helper clone: create named branchGlen Choo via GitGitGadget, Oct 28, 2022
  41. 5/8 submodule: return target of submodule symrefGlen Choo via GitGitGadget, Oct 28, 2022
  42. Junio C HamanoOct 28, 2022
  43. Glen ChooOct 28, 2022
  44. 4/8 t5617: drop references to remote-tracking branchesGlen Choo via GitGitGadget, Oct 28, 2022
  45. 8/8 clone, submodule update: create and check out branchesGlen Choo via GitGitGadget, Oct 28, 2022
  46. Philippe BlainNov 8, 2022
  47. Jonathan TanNov 15, 2022
  48. Glen ChooNov 22, 2022
  49. Jonathan TanNov 23, 2022
  50. Junio C HamanoNov 23, 2022
  51. 6/8 submodule update: refactor update targetsGlen Choo via GitGitGadget, Oct 28, 2022
  52. 7/8 submodule--helper: remove update_data.suboidGlen Choo via GitGitGadget, Oct 28, 2022
  53. Jonathan TanNov 14, 2022
  54. Taylor BlauOct 30, 2022
  55. Philippe BlainNov 8, 2022
  56. Glen ChooNov 8, 2022

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.