Re: [PATCH v3 4/5] branch: add --recurse-submodules option for branch creation
flat view
From: Philippe Blain <hidden>
Date: 2021-12-11 18:08:20
Hi Glen, Le 2021-12-09 à 13:49, Glen Choo a écrit :
To improve the submodules UX, we would like to teach Git to handle branches in submodules. Start this process by teaching `git branch` the --recurse-submodules option so that `git branch --recurse-submodules topic` will create the "topic" branch in the superproject and its submodules. Although this commit does not introduce breaking changes, it is incompatible with existing --recurse-submodules semantics e.g. `git checkout` does not recursively checkout the expected branches created by `git branch` yet. To ensure that the correct set of semantics is used, this commit introduces a new configuration value, `submodule.propagateBranches`, which enables submodule branching when true (defaults to false). This commit includes changes that allow Git to work with submodules that are in trees (and not just the index): * add a submodules_of_tree() helper that gives the relevant information of an in-tree submodule (e.g. path and oid) and initializes the repository * add is_tree_submodule_active() by adding a treeish_name parameter to is_submodule_active() * add the "submoduleNotUpdated" advice to advise users to update the submodules in their trees Other changes * add a "dry_run" parameter to create_branch() in order to support `git submodule--helper create-branch --dry-run` Signed-off-by: Glen Choo <redacted> --- Documentation/config/advice.txt | 3 + Documentation/config/submodule.txt | 8 +
Same comment as I remarked in v1 [1]: We would need to add the new flag to Documentation/git-branch.txt, and also probably update the documentation of 'submodule.recurse' in 'Documentation/config/submodule.txt'. [1] https://lore.kernel.org/git/3ad3941c-de18-41bf-2e44-4238ae868d79@gmail.com/ (local)
quoted hunk ↗ jump to hunk
advice.c | 1 + advice.h | 1 + branch.c | 129 ++++++++++++- branch.h | 31 +++- builtin/branch.c | 40 +++- builtin/checkout.c | 3 +- builtin/submodule--helper.c | 38 ++++ submodule-config.c | 35 ++++ submodule-config.h | 35 ++++ submodule.c | 11 +- submodule.h | 3 + t/t3207-branch-submodule.sh | 284 +++++++++++++++++++++++++++++ 14 files changed, 609 insertions(+), 13 deletions(-) create mode 100755 t/t3207-branch-submodule.shdiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt index 063eec2511..e52262dc69 100644 --- a/Documentation/config/advice.txt +++ b/Documentation/config/advice.txt@@ -116,6 +116,9 @@ advice.*:: submoduleAlternateErrorStrategyDie:: Advice shown when a submodule.alternateErrorStrategy option configured to "die" causes a fatal error. + submodulesNotUpdated:: + Advice shown when a user runs a submodule command that fails + because `git submodule update` was not run.
I see you added '--init' in the actual message below, but maybe it would be more accurate to also add it here ?
quoted hunk ↗ jump to hunk
addIgnoredFile:: Advice shown if a user attempts to add an ignored file to the index.diff --git a/Documentation/config/submodule.txt b/Documentation/config/submodule.txt index ee454f8126..52b35964c0 100644 --- a/Documentation/config/submodule.txt +++ b/Documentation/config/submodule.txt@@ -72,6 +72,14 @@ submodule.recurse:: For these commands a workaround is to temporarily change the configuration value by using `git -c submodule.recurse=0`. +submodule.propagateBranches:: + [EXPERIMENTAL] A boolean that enables branching support when + using `--recurse-submodules` or `submodule.recurse=true`. + Enabling this will allow certain commands to accept + `--recurse-submodules` and certain commands that already accept + `--recurse-submodules` will now consider branches. + Defaults to false. +
Thanks, that updated description seems simpler and more accurate with what this series is doing.
submodule.fetchJobs:: Specifies how many submodules are fetched/cloned at the same time. A positive integer allows up to that number of submodules fetched
quoted hunk ↗ jump to hunk
diff --git a/branch.c b/branch.c index 6b9d64cdf9..305154de0b 100644 --- a/branch.c +++ b/branch.c@@ -8,6 +8,8 @@
quoted hunk ↗ jump to hunk
+void create_branches_recursively(struct repository *r, const char *name, + const char *start_name, + const char *tracking_name, int force, + int reflog, int quiet, enum branch_track track, + int dry_run) +{ + int i = 0; + char *branch_point = NULL; + struct object_id super_oid; + struct submodule_entry_list submodule_entry_list; + + /* Perform dwim on start_name to get super_oid and branch_point. */ + validate_branch_start(r, start_name, BRANCH_TRACK_NEVER, &super_oid, + &branch_point); + + /* + * If we were not given an explicit name to track, then assume we are at + * the top level and, just like the non-recursive case, the tracking + * name is the branch point. + */ + if (!tracking_name) + tracking_name = branch_point; + + submodules_of_tree(r, &super_oid, &submodule_entry_list); + /* + * Before creating any branches, first check that the branch can + * be created in every submodule. + */ + for (i = 0; i < submodule_entry_list.entry_nr; i++) { + if (submodule_entry_list.entries[i].repo == NULL) { + if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED)) + advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"), + start_name); + die(_("submodule '%s': unable to find submodule"),
small nit, maybe write: "unable to find submodule repository" ?
quoted hunk ↗ jump to hunk
+ submodule_entry_list.entries[i].submodule->name); + } + + if (submodule_create_branch( + submodule_entry_list.entries[i].repo, + submodule_entry_list.entries[i].submodule, name, + oid_to_hex(&submodule_entry_list.entries[i] + .name_entry->oid), + tracking_name, force, reflog, quiet, track, 1))
Here, we do not actually use the dry_run argument, since we *always* want to do a dry run for the submodules...
quoted hunk ↗ jump to hunk
+ die(_("submodule '%s': cannot create branch '%s'"), + submodule_entry_list.entries[i].submodule->name, + name); + } + + create_branch(the_repository, name, start_name, force, 0, reflog, quiet, + BRANCH_TRACK_NEVER, dry_run);
... whereas for the superproject branch, we use the given dry_run argument...
quoted hunk ↗ jump to hunk
+ if (dry_run) + return; + /* + * NEEDSWORK If tracking was set up in the superproject but not the + * submodule, users might expect "git branch --recurse-submodules" to + * fail or give a warning, but this is not yet implemented because it is + * tedious to determine whether or not tracking was set up in the + * superproject. + */ + setup_tracking(name, tracking_name, track, quiet, 0); + + for (i = 0; i < submodule_entry_list.entry_nr; i++) { + if (submodule_create_branch( + submodule_entry_list.entries[i].repo, + submodule_entry_list.entries[i].submodule, name, + oid_to_hex(&submodule_entry_list.entries[i] + .name_entry->oid), + tracking_name, force, reflog, quiet, track, 0))
... and if !dry_run, then we do create the branches in the submodules. That is a little bit hard to follow if you are not careful. Maybe it's just me, but as I was first reading it I wondered why '0' and '1' were hard-coded as the dry-run arguments to submodule_create_branch... It would maybe be clearer to use a named variable ?
quoted hunk ↗ jump to hunk
@@ -851,6 +874,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_release(&buf); } else if (!noncreate_actions && argc > 0 && argc <= 2) { + const char *branch_name = argv[0]; + const char *start_name = argc == 2 ? argv[1] : head; + if (filter.kind != FILTER_REFS_BRANCHES) die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n" "Did you mean to use: -a|-r --list <pattern>?"));@@ -858,10 +884,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (track == BRANCH_TRACK_OVERRIDE) die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead.")); - create_branch(the_repository, - argv[0], (argc == 2) ? argv[1] : head, - force, 0, reflog, quiet, track); - + if (recurse_submodules) { + create_branches_recursively(the_repository, branch_name, + start_name, NULL, force, + reflog, quiet, track, 0);
Here again, maybe it would be clearer to use a named variable instead of hard-coding '0' ?
quoted hunk ↗ jump to hunk
+ return 0; + } + create_branch(the_repository, branch_name, start_name, force, 0, + reflog, quiet, track, 0);
Same here.
} else usage_with_options(builtin_branch_usage, options);
quoted hunk ↗ jump to hunk
diff --git a/t/t3207-branch-submodule.sh b/t/t3207-branch-submodule.sh new file mode 100755 index 0000000000..2dd0e2b01f --- /dev/null +++ b/t/t3207-branch-submodule.sh
The tests look pretty thourough.
+ +test_expect_success 'should create branch when submodule is not in HEAD .gitmodules' '
micro-nit: maybe write: HEAD:.gitmodules as this is revision synatx. Cheers, Philippe.