From: Glen Choo <chooglen@google.com>
To: git@vger.kernel.org
Cc: "Glen Choo" <chooglen@google.com>,
"Jonathan Tan" <jonathantanmy@google.com>,
"Josh Steadmon" <steadmon@google.com>,
"Emily Shaffer" <emilyshaffer@google.com>,
"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
"Philippe Blain" <levraiphilippeblain@gmail.com>
Subject: [PATCH v2 0/3] implement branch --recurse-submodules
Date: Mon, 6 Dec 2021 13:55:25 -0800 [thread overview]
Message-ID: <20211206215528.97050-1-chooglen@google.com> (raw)
In-Reply-To: <20211122223252.19922-1-chooglen@google.com>
Submodule branching RFC:
https://lore.kernel.org/git/kl6lv912uvjv.fsf@chooglen-macbookpro.roam.corp.google.com/
Original Submodule UX RFC/Discussion:
https://lore.kernel.org/git/YHofmWcIAidkvJiD@google.com/
Contributor Summit submodules Notes:
https://lore.kernel.org/git/nycvar.QRO.7.76.6.2110211148060.56@tvgsbejvaqbjf.bet/
Submodule UX overhaul updates:
https://lore.kernel.org/git/?q=Submodule+UX+overhaul+update
This series implements branch --recurse-submodules as laid out in the
Submodule branching RFC (linked above). If there are concerns about the
UX/behavior, I would appreciate feedback on the RFC thread as well :)
Thanks for the feedback, everyone. I really really appreciate it.
The biggest difference in V2 is that I took Jonathan's advice to remove
"git branch --dry-run" in favor of adding "--dry-run" to "git
submodule--helper create-branch" instead [1]. The benefit of having "git
branch --dry-run" is pretty small, and we'd have to explain to users why
"--dry-run" doesn't work in more situations [2].
Unfortunately patch 3 (formerly patch 4) is now bigger than I would
prefer. This is due to the combined effect of removing "--dry-run" and
squashing the former patch 1. I'd appreciate any feedback on how I can
structure things differently :)
Changes since v1:
* Move the functionality of "git branch --dry-run" into "git
submodule-helper create-branch --dry-run"
* Add more fields to the submodules_of_tree() struct to reduce the
number of allocations made by the caller [3]. Move this functionality
to patch 3 (formerly patch 4) and drop patch 1.
* Make submodules_of_tree() ignore inactive submodules [4]
* Structure the output of the submodules a bit better by adding prefixes
to the child process' output (instead of inconsistently indenting the
output).
** I wasn't able to find a good way to interleave stdout/stderr
correctly, so a less-than-desirable workaround was to route the child
process output to stdout/stderr depending on the exit code.
** Eventually, I would like to structure the output of submodules in a
report, as Ævar suggested [5]. But at this stage, I think that it's
better to spend time getting user feedback on the submodules
branching UX and it'll be easier to standardize the output when we've
implemented more of the UX :)
[1] https://lore.kernel.org/git/20211129210140.937875-1-jonathantanmy@google.com
[2] https://lore.kernel.org/git/211123.86zgpvup6m.gmgdl@evledraar.gmail.com
[3] https://lore.kernel.org/git/211123.86r1b7uoil.gmgdl@evledraar.gmail.com
[4] https://lore.kernel.org/git/3ad3941c-de18-41bf-2e44-4238ae868d79@gmail.com
[5] https://lore.kernel.org/git/211123.86v90juovj.gmgdl@evledraar.gmail.com
Glen Choo (3):
branch: move --set-upstream-to behavior to setup_tracking()
builtin/branch: clean up action-picking logic in cmd_branch()
branch: add --recurse-submodules option for branch creation
Documentation/config/advice.txt | 3 +
Documentation/config/submodule.txt | 8 +
advice.c | 1 +
advice.h | 1 +
branch.c | 322 +++++++++++++++++++++--------
branch.h | 44 +++-
builtin/branch.c | 66 ++++--
builtin/checkout.c | 3 +-
builtin/submodule--helper.c | 38 ++++
submodule-config.c | 35 ++++
submodule-config.h | 35 ++++
submodule.c | 11 +-
submodule.h | 3 +
t/t3200-branch.sh | 17 ++
t/t3207-branch-submodule.sh | 284 +++++++++++++++++++++++++
15 files changed, 758 insertions(+), 113 deletions(-)
create mode 100755 t/t3207-branch-submodule.sh
Range-diff against v1:
1: 1551dd683f < -: ---------- submodule-config: add submodules_of_tree() helper
2: a4984f6eef ! 1: cc212dcd39 branch: refactor out branch validation from create_branch()
@@ Metadata
Author: Glen Choo <chooglen@google.com>
## Commit message ##
- branch: refactor out branch validation from create_branch()
+ branch: move --set-upstream-to behavior to setup_tracking()
- In a subsequent commit, we would like to be able to validate whether or
- not a branch name is valid before we create it (--dry-run). This is
- useful for `git branch --recurse-submodules topic` because it allows Git
- to determine if the branch 'topic' can be created in all submodules
- without creating the branch 'topic'.
+ This refactor is motivated by a desire to add a "dry_run" parameter to
+ create_branch() that will validate whether or not a branch can be
+ created without actually creating it - this behavior be used in a
+ subsequent commit that adds `git branch --recurse-submodules topic`.
- A good starting point would be to refactor out the start point
- validation and dwim logic in create_branch() in a
- validate_branch_start() helper function. Once we do so, it becomes
- clear that create_branch() is more complex than it needs to be -
- create_branch() is also used to set tracking information when performing
- `git branch --set-upstream-to`. This made more sense when
- (the now unsupported) --set-upstream was first introduced in
- 4fc5006676 (Add branch --set-upstream, 2010-01-18), because
- it would sometimes create a branch and sometimes update tracking
- information without creating a branch.
+ Adding "dry_run" is not obvious because create_branch() is also used to
+ set tracking information without creating a branch, i.e. when using
+ --set-upstream-to. This appears to be a leftover from 4fc5006676 (Add
+ branch --set-upstream, 2010-01-18), when --set-upstream would sometimes
+ create a branch and sometimes update tracking information without
+ creating a branch. However, we no longer support --set-upstream, so it
+ makes more sense to set tracking information with another function, like
+ setup_tracking(), and use create_branch() only to create branches. When
+ this is done, it will be trivial to add "dry_run".
- Refactor out the branch validation and dwim logic from create_branch()
- into validate_branch_start(), make it so that create_branch() always
- tries to create a branch, and replace the now-incorrect create_branch()
- call with setup_tracking(). Since there were none, add tests for
- creating a branch with `--force`.
+ Do this refactor by moving the branch validation and dwim logic from
+ create_branch() into a new function, validate_branch_start(), and call
+ it from setup_tracking(). Now that setup_tracking() can perform dwim and
+ tracking setup without creating a branch, use it in `git branch
+ --set-upstream-to` and remove unnecessary behavior from create_branch().
+
+ Since there were none, add tests for creating a branch with `--force`.
Signed-off-by: Glen Choo <chooglen@google.com>
@@ branch.c: N_("\n"
- const char *name, const char *start_name,
- int force, int clobber_head_ok, int reflog,
- int quiet, enum branch_track track)
++/**
++ * Validates whether a ref is a valid starting point for a branch, where:
++ *
++ * - r is the repository to validate the branch for
++ *
++ * - start_name is the ref that we would like to test. This is
++ * expanded with DWIM and assigned to real_ref.
++ *
++ * - track is the tracking mode of the new branch. If tracking is
++ * explicitly requested, start_name must be a branch (because
++ * otherwise start_name cannot be tracked)
++ *
++ * - oid is an out parameter containing the object_id of start_name
++ *
++ * - real_ref is an out parameter containing the full, 'real' form of
++ * start_name e.g. refs/heads/main instead of main
++ *
++ */
+static void validate_branch_start(struct repository *r, const char *start_name,
+ enum branch_track track,
-+ struct object_id *oid, char **full_ref)
++ struct object_id *oid, char **real_ref)
{
struct commit *commit;
- struct object_id oid;
@@ branch.c: void create_branch(struct repository *r,
}
- switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
-+ switch (repo_dwim_ref(r, start_name, strlen(start_name), oid, full_ref,
++ switch (repo_dwim_ref(r, start_name, strlen(start_name), oid, real_ref,
+ 0)) {
case 0:
/* Not branching from any existing branch */
@@ branch.c: void create_branch(struct repository *r,
/* Unique completion -- good, only if it is a real branch */
- if (!starts_with(real_ref, "refs/heads/") &&
- validate_remote_tracking_branch(real_ref)) {
-+ if (!starts_with(*full_ref, "refs/heads/") &&
-+ validate_remote_tracking_branch(*full_ref)) {
++ if (!starts_with(*real_ref, "refs/heads/") &&
++ validate_remote_tracking_branch(*real_ref)) {
if (explicit_tracking)
die(_(upstream_not_branch), start_name);
else
- FREE_AND_NULL(real_ref);
-+ FREE_AND_NULL(*full_ref);
++ FREE_AND_NULL(*real_ref);
}
break;
default:
3: cbcbc4f49e < -: ---------- branch: add --dry-run option to branch
-: ---------- > 2: 320749cc82 builtin/branch: clean up action-picking logic in cmd_branch()
4: 416a114fa9 ! 3: c0441c6691 branch: add --recurse-submodules option for branch creation
@@ Metadata
## Commit message ##
branch: add --recurse-submodules option for branch creation
- Teach cmd_branch to accept the --recurse-submodules option when creating
- branches so that `git branch --recurse-submodules topic` will create the
- "topic" branch in the superproject and all submodules. Guard this (and
- future submodule branching) behavior behind a new configuration value
- 'submodule.propagateBranches'.
+ 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 <chooglen@google.com>
@@ Documentation/config/submodule.txt: submodule.recurse::
configuration value by using `git -c submodule.recurse=0`.
+submodule.propagateBranches::
-+ [EXPERIMENTAL] A boolean that enables branching support with
-+ submodules. This allows certain commands to accept
-+ `--recurse-submodules` (`git branch --recurse-submodules` will
-+ create branches recursively), and certain commands that already
-+ accept `--recurse-submodules` will now consider branches (`git
-+ switch --recurse-submodules` will switch to the correct branch
-+ in all submodules).
++ [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.
+
submodule.fetchJobs::
Specifies how many submodules are fetched/cloned at the same time.
@@ branch.c
struct tracking {
struct refspec_item spec;
+@@ branch.c: void setup_tracking(const char *new_ref, const char *orig_ref,
+
+ void create_branch(struct repository *r, const char *name,
+ const char *start_name, int force, int clobber_head_ok,
+- int reflog, int quiet, enum branch_track track)
++ int reflog, int quiet, enum branch_track track, int dry_run)
+ {
+ struct object_id oid;
+ char *real_ref;
@@ branch.c: void create_branch(struct repository *r, const char *name,
+ }
+
+ validate_branch_start(r, start_name, track, &oid, &real_ref);
++ if (dry_run)
++ goto cleanup;
+
+ if (reflog)
+ log_all_ref_updates = LOG_REFS_NORMAL;
+@@ branch.c: void create_branch(struct repository *r, const char *name,
+ if (real_ref && track)
+ setup_tracking(ref.buf + 11, real_ref, track, quiet, 0);
+
++cleanup:
+ strbuf_release(&ref);
free(real_ref);
}
-+static int submodule_validate_branchname(struct repository *r, const char *name,
-+ const char *start_name, int force,
-+ int quiet, char **err_msg)
-+{
-+ int ret = 0;
-+ struct child_process child = CHILD_PROCESS_INIT;
-+ struct strbuf child_err = STRBUF_INIT;
-+ child.git_cmd = 1;
-+ child.err = -1;
-+
-+ prepare_other_repo_env(&child.env_array, r->gitdir);
-+ strvec_pushl(&child.args, "branch", "--dry-run", NULL);
-+ if (force)
-+ strvec_push(&child.args, "--force");
-+ if (quiet)
-+ strvec_push(&child.args, "--quiet");
-+ strvec_pushl(&child.args, name, start_name, NULL);
-+
-+ if ((ret = start_command(&child)))
-+ return ret;
-+ ret = finish_command(&child);
-+ strbuf_read(&child_err, child.err, 0);
-+ *err_msg = strbuf_detach(&child_err, NULL);
-+ return ret;
-+}
-+
-+static int submodule_create_branch(struct repository *r, const char *name,
-+ const char *start_oid,
++static int submodule_create_branch(struct repository *r,
++ const struct submodule *submodule,
++ const char *name, const char *start_oid,
+ const char *start_name, int force,
+ int reflog, int quiet,
-+ enum branch_track track, char **err_msg)
++ enum branch_track track, int dry_run)
+{
+ int ret = 0;
+ struct child_process child = CHILD_PROCESS_INIT;
+ struct strbuf child_err = STRBUF_INIT;
++ struct strbuf out_buf = STRBUF_INIT;
++ char *out_prefix = xstrfmt("submodule '%s': ", submodule->name);
+ child.git_cmd = 1;
+ child.err = -1;
++ child.stdout_to_stderr = 1;
+
+ prepare_other_repo_env(&child.env_array, r->gitdir);
+ strvec_pushl(&child.args, "submodule--helper", "create-branch", NULL);
++ if (dry_run)
++ strvec_push(&child.args, "--dry-run");
+ if (force)
+ strvec_push(&child.args, "--force");
+ if (quiet)
@@ branch.c: void create_branch(struct repository *r, const char *name,
+ return ret;
+ ret = finish_command(&child);
+ strbuf_read(&child_err, child.err, 0);
-+ *err_msg = strbuf_detach(&child_err, NULL);
++ strbuf_add_lines(&out_buf, out_prefix, child_err.buf, child_err.len);
++
++ if (ret)
++ fprintf(stderr, "%s", out_buf.buf);
++ else
++ printf("%s", out_buf.buf);
++
++ strbuf_release(&child_err);
++ strbuf_release(&out_buf);
+ return ret;
+}
+
-+void create_submodule_branches(struct repository *r, const char *name,
-+ const char *start_name, int force, int reflog,
-+ int quiet, enum branch_track track)
++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 repository *subrepos;
-+ struct submodule *submodules;
+ struct object_id super_oid;
-+ struct submodule_entry_list *submodule_entry_list;
-+ char *err_msg = NULL;
-+
-+ validate_branch_start(r, start_name, track, &super_oid, &branch_point);
-+
-+ submodule_entry_list = submodules_of_tree(r, &super_oid);
-+ CALLOC_ARRAY(subrepos, submodule_entry_list->entry_nr);
-+ CALLOC_ARRAY(submodules, submodule_entry_list->entry_nr);
-+
-+ for (i = 0; i < submodule_entry_list->entry_nr; i++) {
-+ submodules[i] = *submodule_from_path(
-+ r, &super_oid,
-+ submodule_entry_list->name_entries[i].path);
-+
-+ if (repo_submodule_init(
-+ &subrepos[i], r,
-+ submodule_entry_list->name_entries[i].path,
-+ &super_oid)) {
-+ die(_("submodule %s: unable to find submodule"),
-+ submodules[i].name);
++ 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 initializing the submodules using 'git checkout %s && git submodule update'"),
++ advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"),
+ start_name);
++ die(_("submodule '%s': unable to find submodule"),
++ submodule_entry_list.entries[i].submodule->name);
+ }
+
-+ if (submodule_validate_branchname(
-+ &subrepos[i], name,
-+ oid_to_hex(
-+ &submodule_entry_list->name_entries[i].oid),
-+ force, quiet, &err_msg))
-+ die(_("submodule %s: could not create branch '%s'\n\t%s"),
-+ submodules[i].name, name, err_msg);
++ 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))
++ 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,
-+ track);
-+
-+ for (i = 0; i < submodule_entry_list->entry_nr; i++) {
-+ printf_ln(_("submodule %s: creating branch '%s'"),
-+ submodules[i].name, name);
++ BRANCH_TRACK_NEVER, dry_run);
++ 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(
-+ &subrepos[i], name,
-+ oid_to_hex(
-+ &submodule_entry_list->name_entries[i].oid),
-+ branch_point, force, reflog, quiet, track,
-+ &err_msg))
-+ die(_("submodule %s: could not create branch '%s'\n\t%s"),
-+ submodules[i].name, name, err_msg);
-+
-+ repo_clear(&subrepos[i]);
++ 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))
++ die(_("submodule '%s': cannot create branch '%s'"),
++ submodule_entry_list.entries[i].submodule->name,
++ name);
++ repo_clear(submodule_entry_list.entries[i].repo);
+ }
+}
+
@@ branch.c: void create_branch(struct repository *r, const char *name,
unlink(git_path_merge_head(r));
## branch.h ##
-@@ branch.h: void create_branch(struct repository *r,
- int force, int clobber_head_ok,
- int reflog, int quiet, enum branch_track track);
+@@ branch.h: void setup_tracking(const char *new_ref, const char *orig_ref,
+ * - track causes the new branch to be configured to merge the remote branch
+ * that start_name is a tracking branch for (if any).
+ *
++ * - dry_run causes the branch to be validated but not created.
++ *
+ */
+-void create_branch(struct repository *r,
+- const char *name, const char *start_name,
+- int force, int clobber_head_ok,
+- int reflog, int quiet, enum branch_track track);
++void create_branch(struct repository *r, const char *name,
++ const char *start_name, int force, int clobber_head_ok,
++ int reflog, int quiet, enum branch_track track, int dry_run);
+/*
-+ * Creates a new branch in repository and its submodules.
++ * Creates a new branch in repository and its submodules (and its
++ * submodules, recursively). Besides these exceptions, the parameters
++ * function identically to create_branch():
++ *
++ * - start_name is the name of the ref, in repository r, that the new
++ * branch should start from. In submodules, branches will start from
++ * the respective gitlink commit ids in start_name's tree.
++ *
++ * - tracking_name is the name used of the ref that will be used to set
++ * up tracking, e.g. origin/main. This is propagated to submodules so
++ * that tracking information will appear as if the branch branched off
++ * tracking_name instead of start_name (which is a plain commit id for
++ * submodules). If omitted, start_name is used for tracking (just like
++ * create_branch()).
++ *
+ */
-+void create_submodule_branches(struct repository *r, const char *name,
-+ const char *start_name, int force, int reflog,
-+ int quiet, enum branch_track track);
++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);
/*
* Check if 'name' can be a valid name for a branch; die otherwise.
* Return 1 if the named branch already exists; return 0 otherwise.
@@ builtin/branch.c: static int git_branch_config(const char *var, const char *valu
}
@@ builtin/branch.c: int cmd_branch(int argc, const char **argv, const char *prefix)
- int delete = 0, rename = 0, copy = 0, force = 0, list = 0, create = 0,
unset_upstream = 0, show_current = 0, edit_description = 0;
+ int noncreate_actions = 0;
/* possible options */
-- int reflog = 0, quiet = 0, dry_run = 0, icase = 0;
-+ int reflog = 0, quiet = 0, dry_run = 0, icase = 0,
-+ recurse_submodules_explicit = 0;
+- int reflog = 0, quiet = 0, icase = 0;
++ int reflog = 0, quiet = 0, icase = 0, recurse_submodules_explicit = 0;
const char *new_upstream = NULL;
enum branch_track track;
struct ref_filter filter;
@@ builtin/branch.c: int cmd_branch(int argc, const char **argv, const char *prefix
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+ OPT_BOOL(0, "recurse-submodules", &recurse_submodules_explicit, N_("recurse through submodules")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
- OPT__DRY_RUN(&dry_run, N_("show whether the branch would be created")),
OPT_END(),
+ };
@@ builtin/branch.c: int cmd_branch(int argc, const char **argv, const char *prefix)
- if (create < 0)
+ if (noncreate_actions > 1)
usage_with_options(builtin_branch_usage, options);
-+ if (recurse_submodules_explicit && submodule_propagate_branches &&
-+ !create)
-+ die(_("--recurse-submodules can only be used to create branches"));
- if (dry_run && !create)
- die(_("--dry-run can only be used when creating branches"));
-
++ if (recurse_submodules_explicit) {
++ if (!submodule_propagate_branches)
++ die(_("branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled"));
++ if (noncreate_actions)
++ die(_("--recurse-submodules can only be used to create branches"));
++ }
++
+ recurse_submodules =
+ (recurse_submodules || recurse_submodules_explicit) &&
+ submodule_propagate_branches;
@@ builtin/branch.c: int cmd_branch(int argc, const char **argv, const char *prefix
filter.abbrev = DEFAULT_ABBREV;
filter.ignore_case = icase;
@@ builtin/branch.c: int cmd_branch(int argc, const char **argv, const char *prefix)
- FREE_AND_NULL(unused_full_ref);
- return 0;
- }
+ 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>?"));
+@@ builtin/branch.c: 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_submodule_branches(the_repository, branch_name,
-+ start_name, force, reflog,
-+ quiet, track);
++ create_branches_recursively(the_repository, branch_name,
++ start_name, NULL, force,
++ reflog, quiet, track, 0);
+ return 0;
+ }
- create_branch(the_repository, branch_name, start_name, force, 0,
- reflog, quiet, track);
++ create_branch(the_repository, branch_name, start_name, force, 0,
++ reflog, quiet, track, 0);
} else
+ usage_with_options(builtin_branch_usage, options);
+
+
+ ## builtin/checkout.c ##
+@@ builtin/checkout.c: static void update_refs_for_switch(const struct checkout_opts *opts,
+ opts->new_branch_force ? 1 : 0,
+ opts->new_branch_log,
+ opts->quiet,
+- opts->track);
++ opts->track,
++ 0);
+ new_branch_info->name = opts->new_branch;
+ setup_branch_path(new_branch_info);
+ }
## builtin/submodule--helper.c ##
@@
@@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
+static int module_create_branch(int argc, const char **argv, const char *prefix)
+{
+ enum branch_track track;
-+ int quiet = 0, force = 0, reflog = 0;
++ int quiet = 0, force = 0, reflog = 0, dry_run = 0;
+
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("print only error messages")),
@@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
+ OPT_SET_INT('t', "track", &track,
+ N_("set up tracking mode (see git-pull(1))"),
+ BRANCH_TRACK_EXPLICIT),
++ OPT__DRY_RUN(&dry_run,
++ N_("show whether the branch would be created")),
+ OPT_END()
+ };
+ const char *const usage[] = {
-+ N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] <name> <start_oid> <start_name>"),
++ N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start_oid> <start_name>"),
+ NULL
+ };
+
++ git_config(git_default_config, NULL);
++ track = git_branch_track;
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (argc != 3)
+ usage_with_options(usage, options);
+
-+ create_branch(the_repository, argv[0], argv[1], force, 0, reflog, quiet,
-+ BRANCH_TRACK_NEVER);
-+ setup_tracking(argv[0], argv[2], track, quiet, 0);
++ if (!quiet && !dry_run)
++ printf_ln(_("creating branch '%s'"), argv[0]);
+
++ create_branches_recursively(the_repository, argv[0], argv[1], argv[2],
++ force, reflog, quiet, track, dry_run);
+ return 0;
+}
struct add_data {
@@ builtin/submodule--helper.c: static struct cmd_struct commands[] = {
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
+ ## submodule-config.c ##
+@@
+ #include "strbuf.h"
+ #include "object-store.h"
+ #include "parse-options.h"
++#include "tree-walk.h"
+
+ /*
+ * submodule cache lookup structure
+@@ submodule-config.c: const struct submodule *submodule_from_path(struct repository *r,
+ return config_from(r->submodule_cache, treeish_name, path, lookup_path);
+ }
+
++void submodules_of_tree(struct repository *r,
++ const struct object_id *treeish_name,
++ struct submodule_entry_list *out)
++{
++ struct tree_desc tree;
++ struct submodule_tree_entry *st_entry;
++ struct name_entry *name_entry;
++
++ name_entry = xmalloc(sizeof(*name_entry));
++
++ CALLOC_ARRAY(out->entries, 0);
++ out->entry_nr = 0;
++ out->entry_alloc = 0;
++
++ fill_tree_descriptor(r, &tree, treeish_name);
++ while (tree_entry(&tree, name_entry)) {
++ if (!S_ISGITLINK(name_entry->mode) || !is_tree_submodule_active(r, treeish_name, name_entry->path)) {
++ continue;
++ }
++
++ st_entry = xmalloc(sizeof(*st_entry));
++ st_entry->name_entry = name_entry;
++ st_entry->submodule =
++ submodule_from_path(r, treeish_name, name_entry->path);
++ st_entry->repo = xmalloc(sizeof(*st_entry->repo));
++ if (repo_submodule_init(st_entry->repo, r, name_entry->path,
++ treeish_name))
++ FREE_AND_NULL(st_entry->repo);
++
++ ALLOC_GROW(out->entries, out->entry_nr + 1, out->entry_alloc);
++ out->entries[out->entry_nr++] = *st_entry;
++ }
++}
++
+ void submodule_free(struct repository *r)
+ {
+ if (r->submodule_cache)
+
+ ## submodule-config.h ##
+@@
+ #include "hashmap.h"
+ #include "submodule.h"
+ #include "strbuf.h"
++#include "tree-walk.h"
+
+ /**
+ * The submodule config cache API allows to read submodule
+@@ submodule-config.h: int check_submodule_name(const char *name);
+ void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);
+ void update_clone_config_from_gitmodules(int *max_jobs);
+
++/*
++ * Submodule entry that contains relevant information about a
++ * submodule in a tree.
++ */
++struct submodule_tree_entry {
++ /* The submodule's tree entry. */
++ struct name_entry *name_entry;
++ /*
++ * A struct repository corresponding to the submodule. May be
++ * NULL if the submodule has not been updated.
++ */
++ struct repository *repo;
++ /*
++ * A struct submodule containing the submodule config in the
++ * tree's .gitmodules.
++ */
++ const struct submodule *submodule;
++};
++
++struct submodule_entry_list {
++ struct submodule_tree_entry *entries;
++ int entry_nr;
++ int entry_alloc;
++};
++
++/**
++ * Given a treeish, return all submodules in the tree. This only reads
++ * one level of the tree, so it will not return nested submodules;
++ * callers that require nested submodules are expected to handle the
++ * recursion themselves.
++ */
++void submodules_of_tree(struct repository *r,
++ const struct object_id *treeish_name,
++ struct submodule_entry_list *ret);
+ #endif /* SUBMODULE_CONFIG_H */
+
+ ## submodule.c ##
+@@ submodule.c: int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
+ * ie, the config looks like: "[submodule] active\n".
+ * Since that is an invalid pathspec, we should inform the user.
+ */
+-int is_submodule_active(struct repository *repo, const char *path)
++int is_tree_submodule_active(struct repository *repo,
++ const struct object_id *treeish_name,
++ const char *path)
+ {
+ int ret = 0;
+ char *key = NULL;
+@@ submodule.c: int is_submodule_active(struct repository *repo, const char *path)
+ const struct string_list *sl;
+ const struct submodule *module;
+
+- module = submodule_from_path(repo, null_oid(), path);
++ module = submodule_from_path(repo, treeish_name, path);
+
+ /* early return if there isn't a path->module mapping */
+ if (!module)
+@@ submodule.c: int is_submodule_active(struct repository *repo, const char *path)
+ return ret;
+ }
+
++int is_submodule_active(struct repository *repo, const char *path)
++{
++ return is_tree_submodule_active(repo, null_oid(), path);
++}
++
+ int is_submodule_populated_gently(const char *path, int *return_error_code)
+ {
+ int ret = 0;
+
+ ## submodule.h ##
+@@ submodule.h: int git_default_submodule_config(const char *var, const char *value, void *cb);
+ struct option;
+ int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
+ const char *arg, int unset);
++int is_tree_submodule_active(struct repository *repo,
++ const struct object_id *treeish_name,
++ const char *path);
+ int is_submodule_active(struct repository *repo, const char *path);
+ /*
+ * Determine if a submodule has been populated at a given 'path' by checking if
+
## t/t3207-branch-submodule.sh (new) ##
@@
+#!/bin/sh
@@ t/t3207-branch-submodule.sh (new)
+test_expect_success 'setup superproject and submodule' '
+ git init super &&
+ test_commit foo &&
++ git init sub-sub-upstream &&
++ test_commit -C sub-sub-upstream foo &&
+ git init sub-upstream &&
-+ test_commit -C sub-upstream foo &&
-+ git -C super submodule add ../sub-upstream sub &&
++ git -C sub-upstream submodule add "$TRASH_DIRECTORY/sub-sub-upstream" sub-sub &&
++ git -C sub-upstream commit -m "add submodule" &&
++ git -C super submodule add "$TRASH_DIRECTORY/sub-upstream" sub &&
+ git -C super commit -m "add submodule" &&
-+ git -C super config submodule.propagateBranches true
++ git -C super config submodule.propagateBranches true &&
++ git -C super/sub submodule update --init
+'
+
-+cleanup_branches() {
++CLEANUP_SCRIPT_PATH="$TRASH_DIRECTORY/cleanup_branches.sh"
++
++cat >"$CLEANUP_SCRIPT_PATH" <<'EOF'
++ #!/bin/sh
++
+ super_dir="$1"
+ shift
+ (
@@ t/t3207-branch-submodule.sh (new)
+ git checkout main &&
+ for branch_name in "$@"; do
+ git branch -D "$branch_name"
-+ git submodule foreach "(git checkout main && git branch -D $branch_name) || true"
++ git submodule foreach "$TRASH_DIRECTORY/cleanup_branches.sh . $branch_name || true"
+ done
+ )
++EOF
++chmod +x "$CLEANUP_SCRIPT_PATH"
++
++cleanup_branches() {
++ TRASH_DIRECTORY="\"$TRASH_DIRECTORY\"" "$CLEANUP_SCRIPT_PATH" "$@"
+} >/dev/null 2>/dev/null
+
+# Test the argument parsing
@@ t/t3207-branch-submodule.sh (new)
+ (
+ cd super &&
+ git branch --recurse-submodules branch-a &&
-+ git rev-parse --abbrev-ref branch-a &&
-+ git -C sub rev-parse --abbrev-ref branch-a
++ git rev-parse branch-a &&
++ git -C sub rev-parse branch-a &&
++ git -C sub/sub-sub rev-parse branch-a
+ )
+'
+
-+test_expect_success '--recurse-submodules should be ignored if submodule.propagateBranches is false' '
++test_expect_success '--recurse-submodules should die if submodule.propagateBranches is false' '
+ test_when_finished "cleanup_branches super branch-a" &&
+ (
+ cd super &&
-+ git -c submodule.propagateBranches=false branch --recurse-submodules branch-a &&
-+ git rev-parse branch-a &&
-+ test_must_fail git -C sub rev-parse branch-a
++ echo "fatal: branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled" >expected &&
++ test_must_fail git -c submodule.propagateBranches=false branch --recurse-submodules branch-a 2>actual &&
++ test_cmp expected actual
+ )
+'
+
@@ t/t3207-branch-submodule.sh (new)
+ test_must_fail git branch --recurse-submodules branch-a 2>actual &&
+ test_must_fail git rev-parse branch-a &&
+
-+ cat >expected <<EOF &&
-+fatal: submodule sub: could not create branch ${SQ}branch-a${SQ}
-+ fatal: A branch named ${SQ}branch-a${SQ} already exists.
-+
-+EOF
++ cat >expected <<-EOF &&
++ submodule ${SQ}sub${SQ}: fatal: A branch named ${SQ}branch-a${SQ} already exists.
++ fatal: submodule ${SQ}sub${SQ}: cannot create branch ${SQ}branch-a${SQ}
++ EOF
+ test_cmp expected actual
+ )
+'
@@ t/t3207-branch-submodule.sh (new)
+ )
+'
+
-+test_expect_success 'should create branch when submodule is in .git/modules but not .gitmodules' '
++test_expect_success 'should create branch when submodule is not in HEAD .gitmodules' '
+ test_when_finished "cleanup_branches super branch-a branch-b branch-c" &&
+ (
+ cd super &&
+ git branch branch-a &&
+ git checkout -b branch-b &&
+ git submodule add ../sub-upstream sub2 &&
++ git -C sub2 submodule update --init &&
+ # branch-b now has a committed submodule not in branch-a
+ git commit -m "add second submodule" &&
+ git checkout branch-a &&
@@ t/t3207-branch-submodule.sh (new)
+ )
+'
+
++test_expect_success 'should not create branches in inactive submodules' '
++ test_when_finished "cleanup_branches super branch-a" &&
++ test_config -C super submodule.sub.active false &&
++ (
++ cd super &&
++ git branch --recurse-submodules branch-a &&
++ git rev-parse branch-a &&
++ test_must_fail git -C sub branch-a
++ )
++'
++
+test_expect_success 'setup remote-tracking tests' '
+ (
+ cd super &&
@@ t/t3207-branch-submodule.sh (new)
+ # branch-b now has a committed submodule not in branch-a
+ git commit -m "add second submodule"
+ ) &&
-+ (
-+ cd sub-upstream &&
-+ git branch branch-a
-+ ) &&
+ git clone --branch main --recurse-submodules super super-clone &&
+ git -C super-clone config submodule.propagateBranches true
+'
+
+test_expect_success 'should not create branch when submodule is not in .git/modules' '
-+ # The cleanup needs to delete sub2:branch-b in particular because main does not have sub2
++ # The cleanup needs to delete sub2 separately because main does not have sub2
+ test_when_finished "git -C super-clone/sub2 branch -D branch-b && \
++ git -C super-clone/sub2/sub-sub branch -D branch-b && \
+ cleanup_branches super-clone branch-a branch-b" &&
+ (
+ cd super-clone &&
@@ t/t3207-branch-submodule.sh (new)
+ # This should fail because super-clone does not have sub2.
+ test_must_fail git branch --recurse-submodules branch-b origin/branch-b 2>actual &&
+ cat >expected <<-EOF &&
-+ fatal: submodule sub: unable to find submodule
-+ You may reinitialize the submodules using ${SQ}git checkout origin/branch-b && git submodule update${SQ}
++ hint: You may try updating the submodules using ${SQ}git checkout origin/branch-b && git submodule update --init${SQ}
++ fatal: submodule ${SQ}sub2${SQ}: unable to find submodule
+ EOF
++ test_cmp expected actual &&
+ test_must_fail git rev-parse branch-b &&
+ test_must_fail git -C sub rev-parse branch-b &&
+ # User can fix themselves by initializing the submodule
+ git checkout origin/branch-b &&
-+ git submodule update &&
++ git submodule update --init --recursive &&
+ git branch --recurse-submodules branch-b origin/branch-b
+ )
+'
@@ t/t3207-branch-submodule.sh (new)
+ (
+ cd super-clone &&
+ git branch --recurse-submodules branch-a origin/branch-a &&
++ test "$(git config branch.branch-a.remote)" = origin &&
++ test "$(git config branch.branch-a.merge)" = refs/heads/branch-a &&
++ # "origin/branch-a" does not exist for "sub", but it matches the refspec
++ # so tracking should be set up
+ test "$(git -C sub config branch.branch-a.remote)" = origin &&
-+ test "$(git -C sub config branch.branch-a.merge)" = refs/heads/branch-a
++ test "$(git -C sub config branch.branch-a.merge)" = refs/heads/branch-a &&
++ test "$(git -C sub/sub-sub config branch.branch-a.remote)" = origin &&
++ test "$(git -C sub/sub-sub config branch.branch-a.merge)" = refs/heads/branch-a
+ )
+'
+
+test_expect_success 'should not fail when unable to set up tracking in submodule' '
-+ test_when_finished "cleanup_branches super-clone branch-b" &&
++ test_when_finished "cleanup_branches super-clone branch-a && \
++ git -C super-clone remote rename ex-origin origin" &&
+ (
+ cd super-clone &&
-+ git branch --recurse-submodules branch-b origin/branch-b
++ git remote rename origin ex-origin &&
++ git branch --recurse-submodules branch-a ex-origin/branch-a &&
++ test "$(git config branch.branch-a.remote)" = ex-origin &&
++ test "$(git config branch.branch-a.merge)" = refs/heads/branch-a &&
++ test "$(git -C sub config branch.branch-a.remote)" = "" &&
++ test "$(git -C sub config branch.branch-a.merge)" = ""
+ )
+'
+
--
2.33.GIT
next prev parent reply other threads:[~2021-12-06 21:56 UTC|newest]
Thread overview: 110+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-22 22:32 [PATCH 0/4] implement branch --recurse-submodules Glen Choo
2021-11-22 22:32 ` [PATCH 1/4] submodule-config: add submodules_of_tree() helper Glen Choo
2021-11-23 2:12 ` Jonathan Tan
2021-11-23 19:48 ` Glen Choo
2021-11-23 10:53 ` Ævar Arnfjörð Bjarmason
2021-11-23 18:35 ` Glen Choo
2021-11-23 22:46 ` Junio C Hamano
2021-11-22 22:32 ` [PATCH 2/4] branch: refactor out branch validation from create_branch() Glen Choo
2021-11-22 22:32 ` [PATCH 3/4] branch: add --dry-run option to branch Glen Choo
2021-11-23 10:42 ` Ævar Arnfjörð Bjarmason
2021-11-23 18:42 ` Glen Choo
2021-11-23 23:10 ` Jonathan Tan
2021-11-24 0:52 ` Glen Choo
2021-11-22 22:32 ` [PATCH 4/4] branch: add --recurse-submodules option for branch creation Glen Choo
2021-11-23 10:45 ` Ævar Arnfjörð Bjarmason
2021-11-23 18:56 ` Glen Choo
2021-11-23 19:41 ` Philippe Blain
2021-11-23 23:43 ` Glen Choo
2021-11-24 1:31 ` Jonathan Tan
2021-11-24 18:18 ` Glen Choo
2021-11-29 21:01 ` Jonathan Tan
2021-12-06 21:55 ` Glen Choo [this message]
2021-12-06 21:55 ` [PATCH v2 1/3] branch: move --set-upstream-to behavior to setup_tracking() Glen Choo
2021-12-06 22:48 ` Junio C Hamano
2021-12-08 18:48 ` Glen Choo
2021-12-06 23:28 ` Junio C Hamano
2021-12-08 17:09 ` Glen Choo
2021-12-06 21:55 ` [PATCH v2 2/3] builtin/branch: clean up action-picking logic in cmd_branch() Glen Choo
2021-12-06 21:55 ` [PATCH v2 3/3] branch: add --recurse-submodules option for branch creation Glen Choo
2021-12-09 18:49 ` [PATCH v3 0/5] implement branch --recurse-submodules Glen Choo
2021-12-09 18:49 ` [PATCH v3 1/5] branch: move --set-upstream-to behavior to setup_tracking() Glen Choo
2021-12-09 21:19 ` Jonathan Tan
2021-12-09 22:16 ` Glen Choo
2021-12-09 18:49 ` [PATCH v3 2/5] branch: remove forward declaration of validate_branch_start() Glen Choo
2021-12-09 18:49 ` [PATCH v3 3/5] builtin/branch: clean up action-picking logic in cmd_branch() Glen Choo
2021-12-09 21:23 ` Jonathan Tan
2021-12-09 21:57 ` Glen Choo
2021-12-09 18:49 ` [PATCH v3 4/5] branch: add --recurse-submodules option for branch creation Glen Choo
2021-12-11 18:08 ` Philippe Blain
2021-12-14 20:08 ` Glen Choo
2021-12-09 18:49 ` [PATCH v3 5/5] branch.c: replace questionable exit() codes Glen Choo
2021-12-10 2:21 ` Ævar Arnfjörð Bjarmason
2021-12-10 17:43 ` Glen Choo
2021-12-13 9:02 ` Junio C Hamano
2021-12-13 9:19 ` Ævar Arnfjörð Bjarmason
2021-12-13 19:26 ` Junio C Hamano
2021-12-09 21:59 ` [PATCH v3 0/5] implement branch --recurse-submodules Jonathan Tan
2021-12-09 22:21 ` Glen Choo
2021-12-13 23:20 ` Jonathan Tan
2021-12-14 18:47 ` Glen Choo
2021-12-16 0:32 ` [PATCH v4 " Glen Choo
2021-12-16 0:32 ` [PATCH v4 1/5] branch: move --set-upstream-to behavior to dwim_and_setup_tracking() Glen Choo
2021-12-16 0:32 ` [PATCH v4 2/5] branch: make create_branch() always create a branch Glen Choo
2021-12-16 0:32 ` [PATCH v4 3/5] branch: add a dry_run parameter to create_branch() Glen Choo
2021-12-16 0:32 ` [PATCH v4 4/5] builtin/branch: clean up action-picking logic in cmd_branch() Glen Choo
2021-12-16 0:32 ` [PATCH v4 5/5] branch: add --recurse-submodules option for branch creation Glen Choo
2021-12-16 23:33 ` [PATCH v5 0/5] implement branch --recurse-submodules Glen Choo
2021-12-16 23:33 ` [PATCH v5 1/5] branch: move --set-upstream-to behavior to dwim_and_setup_tracking() Glen Choo
2021-12-16 23:33 ` [PATCH v5 2/5] branch: make create_branch() always create a branch Glen Choo
2021-12-16 23:33 ` [PATCH v5 3/5] branch: add a dry_run parameter to create_branch() Glen Choo
2021-12-16 23:33 ` [PATCH v5 4/5] builtin/branch: clean up action-picking logic in cmd_branch() Glen Choo
2021-12-16 23:33 ` [PATCH v5 5/5] branch: add --recurse-submodules option for branch creation Glen Choo
2021-12-17 0:34 ` [PATCH v5 0/5] implement branch --recurse-submodules Junio C Hamano
2021-12-17 0:45 ` Junio C Hamano
2021-12-20 19:09 ` Glen Choo
2021-12-20 19:50 ` Junio C Hamano
2021-12-20 20:25 ` Glen Choo
2021-12-20 23:34 ` [PATCH v6 " Glen Choo
2021-12-20 23:34 ` [PATCH v6 1/5] branch: move --set-upstream-to behavior to dwim_and_setup_tracking() Glen Choo
2022-01-11 2:09 ` Jonathan Tan
2022-01-11 17:29 ` Glen Choo
2022-01-11 20:03 ` Jonathan Tan
2021-12-20 23:34 ` [PATCH v6 2/5] branch: make create_branch() always create a branch Glen Choo
2022-01-11 2:19 ` Jonathan Tan
2022-01-11 17:51 ` Glen Choo
2021-12-20 23:34 ` [PATCH v6 3/5] branch: add a dry_run parameter to create_branch() Glen Choo
2021-12-20 23:34 ` [PATCH v6 4/5] builtin/branch: clean up action-picking logic in cmd_branch() Glen Choo
2021-12-20 23:34 ` [PATCH v6 5/5] branch: add --recurse-submodules option for branch creation Glen Choo
2021-12-26 4:09 ` Junio C Hamano
2022-01-11 3:28 ` Jonathan Tan
2022-01-11 18:11 ` Glen Choo
2022-01-11 20:15 ` Jonathan Tan
2022-01-11 23:22 ` Glen Choo
2021-12-20 23:36 ` [PATCH v6 0/5] implement branch --recurse-submodules Glen Choo
2021-12-21 1:07 ` Junio C Hamano
2021-12-21 17:51 ` Glen Choo
2022-01-24 20:44 ` [PATCH v7 0/6] " Glen Choo
2022-01-24 20:44 ` [PATCH v7 1/6] branch: move --set-upstream-to behavior to dwim_and_setup_tracking() Glen Choo
2022-01-24 20:44 ` [PATCH v7 2/6] branch: make create_branch() always create a branch Glen Choo
2022-01-24 20:44 ` [PATCH v7 3/6] branch: add a dry_run parameter to create_branch() Glen Choo
2022-01-24 20:44 ` [PATCH v7 4/6] builtin/branch: consolidate action-picking logic in cmd_branch() Glen Choo
2022-01-24 20:44 ` [PATCH v7 5/6] branch: add --recurse-submodules option for branch creation Glen Choo
2022-01-27 20:29 ` Jonathan Tan
2022-01-27 21:32 ` Glen Choo
2022-01-27 22:42 ` Glen Choo
2022-01-24 20:44 ` [PATCH v7 6/6] branch.c: use 'goto cleanup' in setup_tracking() to fix memory leaks Glen Choo
2022-01-27 22:15 ` Junio C Hamano
2022-01-28 19:44 ` Glen Choo
2022-01-29 0:04 ` [PATCH v8 0/6] implement branch --recurse-submodules Glen Choo
2022-01-29 0:04 ` [PATCH v8 1/6] branch: move --set-upstream-to behavior to dwim_and_setup_tracking() Glen Choo
2022-01-29 0:04 ` [PATCH v8 2/6] branch: make create_branch() always create a branch Glen Choo
2022-02-01 22:20 ` Junio C Hamano
2022-01-29 0:04 ` [PATCH v8 3/6] branch: add a dry_run parameter to create_branch() Glen Choo
2022-01-29 0:04 ` [PATCH v8 4/6] builtin/branch: consolidate action-picking logic in cmd_branch() Glen Choo
2022-01-29 0:04 ` [PATCH v8 5/6] branch: add --recurse-submodules option for branch creation Glen Choo
2022-02-04 1:10 ` Glen Choo
2022-02-04 16:15 ` Junio C Hamano
2022-02-04 18:10 ` Glen Choo
2022-01-29 0:04 ` [PATCH v8 6/6] branch.c: use 'goto cleanup' in setup_tracking() to fix memory leaks Glen Choo
2022-02-01 17:43 ` [PATCH v8 0/6] implement branch --recurse-submodules Jonathan Tan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211206215528.97050-1-chooglen@google.com \
--to=chooglen@google.com \
--cc=avarab@gmail.com \
--cc=emilyshaffer@google.com \
--cc=git@vger.kernel.org \
--cc=jonathantanmy@google.com \
--cc=levraiphilippeblain@gmail.com \
--cc=steadmon@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).