* [RFC PATCH 1/2] refs: pass struct repository *r through to write_ref_to_lockfile()
2021-09-21 23:25 [RFC PATCH 0/2] branch: implement in-process --recurse-submodules Glen Choo
@ 2021-09-21 23:25 ` Glen Choo
2021-09-21 23:25 ` [RFC PATCH 2/2] branch: add --recurse-submodules option for branch creation Glen Choo
1 sibling, 0 replies; 7+ messages in thread
From: Glen Choo @ 2021-09-21 23:25 UTC (permalink / raw)
To: git; +Cc: Glen Choo
In refs/files-backend.c, write_ref_to_lockfile() uses the_repository to
validate the oid of the ref being written. Thus, any function that
includes write_ref_to_lockfile() in its call chain has an implicit
dependence on the odb of the_repository. This is undesirable in the case
where we are using oids that are not in the_repository, e.g. when we are
updating refs in a submodule.
Let's fix this by passing struct repository * as a parameter through to
write_ref_to_lockfile().
To avoid breaking existing call sites, the existing function
signatures in refs.h are preserved. struct repository * is only passed
to new functions; these new functions have names prefixed with repo_.
Signed-off-by: Glen Choo <chooglen@google.com>
---
refs.c | 21 +++++++++++----------
refs.h | 18 ++++++++++++++----
refs/debug.c | 14 ++++++++------
refs/files-backend.c | 30 +++++++++++++++---------------
refs/packed-backend.c | 7 ++++---
refs/refs-internal.h | 7 ++++---
t/helper/test-ref-store.c | 2 +-
7 files changed, 57 insertions(+), 42 deletions(-)
diff --git a/refs.c b/refs.c
index 8b9f7c3a80..c217512cd3 100644
--- a/refs.c
+++ b/refs.c
@@ -2105,7 +2105,7 @@ static int run_transaction_hook(struct ref_transaction *transaction,
return ret;
}
-int ref_transaction_prepare(struct ref_transaction *transaction,
+int repo_ref_transaction_prepare(struct repository *r, struct ref_transaction *transaction,
struct strbuf *err)
{
struct ref_store *refs = transaction->ref_store;
@@ -2132,7 +2132,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
return -1;
}
- ret = refs->be->transaction_prepare(refs, transaction, err);
+ ret = refs->be->transaction_prepare(r, refs, transaction, err);
if (ret)
return ret;
@@ -2172,7 +2172,8 @@ int ref_transaction_abort(struct ref_transaction *transaction,
return ret;
}
-int ref_transaction_commit(struct ref_transaction *transaction,
+int repo_ref_transaction_commit(struct repository *r,
+ struct ref_transaction *transaction,
struct strbuf *err)
{
struct ref_store *refs = transaction->ref_store;
@@ -2181,7 +2182,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
switch (transaction->state) {
case REF_TRANSACTION_OPEN:
/* Need to prepare first. */
- ret = ref_transaction_prepare(transaction, err);
+ ret = repo_ref_transaction_prepare(r, transaction, err);
if (ret)
return ret;
break;
@@ -2421,36 +2422,36 @@ int delete_refs(const char *msg, struct string_list *refnames,
return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
}
-int refs_rename_ref(struct ref_store *refs, const char *oldref,
+int refs_rename_ref(struct repository *r, struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
char *msg;
int retval;
msg = normalize_reflog_message(logmsg);
- retval = refs->be->rename_ref(refs, oldref, newref, msg);
+ retval = refs->be->rename_ref(r, refs, oldref, newref, msg);
free(msg);
return retval;
}
int rename_ref(const char *oldref, const char *newref, const char *logmsg)
{
- return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+ return refs_rename_ref(the_repository, get_main_ref_store(the_repository), oldref, newref, logmsg);
}
-int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
+int refs_copy_existing_ref(struct repository *r, struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
char *msg;
int retval;
msg = normalize_reflog_message(logmsg);
- retval = refs->be->copy_ref(refs, oldref, newref, msg);
+ retval = refs->be->copy_ref(r, refs, oldref, newref, msg);
free(msg);
return retval;
}
int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
{
- return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+ return refs_copy_existing_ref(the_repository, get_main_ref_store(the_repository), oldref, newref, logmsg);
}
diff --git a/refs.h b/refs.h
index 48970dfc7e..157d14d7a3 100644
--- a/refs.h
+++ b/refs.h
@@ -524,13 +524,13 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
char *shorten_unambiguous_ref(const char *refname, int strict);
/** rename ref, return 0 on success **/
-int refs_rename_ref(struct ref_store *refs, const char *oldref,
+int refs_rename_ref(struct repository *r, struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg);
int rename_ref(const char *oldref, const char *newref,
const char *logmsg);
/** copy ref, return 0 on success **/
-int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
+int refs_copy_existing_ref(struct repository *r, struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg);
int copy_existing_ref(const char *oldref, const char *newref,
const char *logmsg);
@@ -706,8 +706,13 @@ int ref_transaction_verify(struct ref_transaction *transaction,
* Callers who don't need such fine-grained control over committing
* reference transactions should just call `ref_transaction_commit()`.
*/
-int ref_transaction_prepare(struct ref_transaction *transaction,
+int repo_ref_transaction_prepare(struct repository *r, struct ref_transaction *transaction,
struct strbuf *err);
+static inline int ref_transaction_prepare(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ return repo_ref_transaction_prepare(the_repository, transaction, err);
+}
/*
* Commit all of the changes that have been queued in transaction, as
@@ -716,8 +721,13 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
* transaction, write an error message to `err`, and return one of the
* `TRANSACTION_*` constants
*/
-int ref_transaction_commit(struct ref_transaction *transaction,
+int repo_ref_transaction_commit(struct repository *r, struct ref_transaction *transaction,
struct strbuf *err);
+static inline int ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ return repo_ref_transaction_commit(the_repository, transaction, err);
+}
/*
* Abort `transaction`, which has been begun and possibly prepared,
diff --git a/refs/debug.c b/refs/debug.c
index 1a7a9e11cf..224ee76563 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -38,14 +38,15 @@ static int debug_init_db(struct ref_store *refs, struct strbuf *err)
return res;
}
-static int debug_transaction_prepare(struct ref_store *refs,
+static int debug_transaction_prepare(struct repository *r,
+ struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
int res;
transaction->ref_store = drefs->refs;
- res = drefs->refs->be->transaction_prepare(drefs->refs, transaction,
+ res = drefs->refs->be->transaction_prepare(r, drefs->refs, transaction,
err);
trace_printf_key(&trace_refs, "transaction_prepare: %d\n", res);
return res;
@@ -153,23 +154,24 @@ static int debug_delete_refs(struct ref_store *ref_store, const char *msg,
return res;
}
-static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
+static int debug_rename_ref(struct repository *r,
+ struct ref_store *ref_store, const char *oldref,
const char *newref, const char *logmsg)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
- int res = drefs->refs->be->rename_ref(drefs->refs, oldref, newref,
+ int res = drefs->refs->be->rename_ref(r, drefs->refs, oldref, newref,
logmsg);
trace_printf_key(&trace_refs, "rename_ref: %s -> %s \"%s\": %d\n", oldref, newref,
logmsg, res);
return res;
}
-static int debug_copy_ref(struct ref_store *ref_store, const char *oldref,
+static int debug_copy_ref(struct repository *r, struct ref_store *ref_store, const char *oldref,
const char *newref, const char *logmsg)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
int res =
- drefs->refs->be->copy_ref(drefs->refs, oldref, newref, logmsg);
+ drefs->refs->be->copy_ref(r, drefs->refs, oldref, newref, logmsg);
trace_printf_key(&trace_refs, "copy_ref: %s -> %s \"%s\": %d\n", oldref, newref,
logmsg, res);
return res;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 74c0385873..2bd6115484 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1308,14 +1308,14 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
return ret;
}
-static int write_ref_to_lockfile(struct ref_lock *lock,
+static int write_ref_to_lockfile(struct repository *r, struct ref_lock *lock,
const struct object_id *oid, struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
const struct object_id *oid, const char *logmsg,
struct strbuf *err);
-static int files_copy_or_rename_ref(struct ref_store *ref_store,
+static int files_copy_or_rename_ref(struct repository *r, struct ref_store *ref_store,
const char *oldrefname, const char *newrefname,
const char *logmsg, int copy)
{
@@ -1427,7 +1427,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
}
oidcpy(&lock->old_oid, &orig_oid);
- if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+ if (write_ref_to_lockfile(r, lock, &orig_oid, &err) ||
commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
strbuf_release(&err);
@@ -1448,7 +1448,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
flag = log_all_ref_updates;
log_all_ref_updates = LOG_REFS_NONE;
- if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+ if (write_ref_to_lockfile(r, lock, &orig_oid, &err) ||
commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -1472,19 +1472,19 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
return ret;
}
-static int files_rename_ref(struct ref_store *ref_store,
+static int files_rename_ref(struct repository *r, struct ref_store *ref_store,
const char *oldrefname, const char *newrefname,
const char *logmsg)
{
- return files_copy_or_rename_ref(ref_store, oldrefname,
+ return files_copy_or_rename_ref(r, ref_store, oldrefname,
newrefname, logmsg, 0);
}
-static int files_copy_ref(struct ref_store *ref_store,
+static int files_copy_ref(struct repository *r, struct ref_store *ref_store,
const char *oldrefname, const char *newrefname,
const char *logmsg)
{
- return files_copy_or_rename_ref(ref_store, oldrefname,
+ return files_copy_or_rename_ref(r, ref_store, oldrefname,
newrefname, logmsg, 1);
}
@@ -1683,14 +1683,14 @@ static int files_log_ref_write(struct files_ref_store *refs,
* Write oid into the open lockfile, then close the lockfile. On
* errors, rollback the lockfile, fill in *err and return -1.
*/
-static int write_ref_to_lockfile(struct ref_lock *lock,
+static int write_ref_to_lockfile(struct repository *r, struct ref_lock *lock,
const struct object_id *oid, struct strbuf *err)
{
static char term = '\n';
struct object *o;
int fd;
- o = parse_object(the_repository, oid);
+ o = parse_object(r, oid);
if (!o) {
strbuf_addf(err,
"trying to write ref '%s' with nonexistent object %s",
@@ -2390,7 +2390,7 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
* - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
* update of HEAD.
*/
-static int lock_ref_for_update(struct files_ref_store *refs,
+static int lock_ref_for_update(struct repository *r, struct files_ref_store *refs,
struct ref_update *update,
struct ref_transaction *transaction,
const char *head_ref,
@@ -2496,7 +2496,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* The reference already has the desired
* value, so we don't need to write it.
*/
- } else if (write_ref_to_lockfile(lock, &update->new_oid,
+ } else if (write_ref_to_lockfile(r, lock, &update->new_oid,
err)) {
char *write_err = strbuf_detach(err, NULL);
@@ -2577,7 +2577,7 @@ static void files_transaction_cleanup(struct files_ref_store *refs,
transaction->state = REF_TRANSACTION_CLOSED;
}
-static int files_transaction_prepare(struct ref_store *ref_store,
+static int files_transaction_prepare(struct repository *r, struct ref_store *ref_store,
struct ref_transaction *transaction,
struct strbuf *err)
{
@@ -2667,7 +2667,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
- ret = lock_ref_for_update(refs, update, transaction,
+ ret = lock_ref_for_update(r, refs, update, transaction,
head_ref, &affected_refnames, err);
if (ret)
goto cleanup;
@@ -2708,7 +2708,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
if (is_packed_transaction_needed(refs->packed_ref_store,
packed_transaction)) {
- ret = ref_transaction_prepare(packed_transaction, err);
+ ret = repo_ref_transaction_prepare(r, packed_transaction, err);
/*
* A failure during the prepare step will abort
* itself, but not free. Do that now, and disconnect
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index f8aa97d799..e6aa1841ba 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1403,7 +1403,8 @@ static void packed_transaction_cleanup(struct packed_ref_store *refs,
transaction->state = REF_TRANSACTION_CLOSED;
}
-static int packed_transaction_prepare(struct ref_store *ref_store,
+static int packed_transaction_prepare(struct repository *r,
+ struct ref_store *ref_store,
struct ref_transaction *transaction,
struct strbuf *err)
{
@@ -1577,14 +1578,14 @@ static int packed_create_symref(struct ref_store *ref_store,
BUG("packed reference store does not support symrefs");
}
-static int packed_rename_ref(struct ref_store *ref_store,
+static int packed_rename_ref(struct repository *r, struct ref_store *ref_store,
const char *oldrefname, const char *newrefname,
const char *logmsg)
{
BUG("packed reference store does not support renaming references");
}
-static int packed_copy_ref(struct ref_store *ref_store,
+static int packed_copy_ref(struct repository *r, struct ref_store *ref_store,
const char *oldrefname, const char *newrefname,
const char *logmsg)
{
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 3155708345..1b7a68db8c 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -530,7 +530,8 @@ typedef struct ref_store *ref_store_init_fn(const char *gitdir,
typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
-typedef int ref_transaction_prepare_fn(struct ref_store *refs,
+typedef int ref_transaction_prepare_fn(struct repository *r,
+ struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
@@ -553,10 +554,10 @@ typedef int create_symref_fn(struct ref_store *ref_store,
const char *logmsg);
typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
struct string_list *refnames, unsigned int flags);
-typedef int rename_ref_fn(struct ref_store *ref_store,
+typedef int rename_ref_fn(struct repository *r, struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
-typedef int copy_ref_fn(struct ref_store *ref_store,
+typedef int copy_ref_fn(struct repository *r, struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index b314b81a45..ad47488e5b 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -99,7 +99,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv)
const char *newref = notnull(*argv++, "newref");
const char *logmsg = *argv++;
- return refs_rename_ref(refs, oldref, newref, logmsg);
+ return refs_rename_ref(the_repository, refs, oldref, newref, logmsg);
}
static int each_ref(const char *refname, const struct object_id *oid,
--
2.33.0.464.g1972c5931b-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH 2/2] branch: add --recurse-submodules option for branch creation
2021-09-21 23:25 [RFC PATCH 0/2] branch: implement in-process --recurse-submodules Glen Choo
2021-09-21 23:25 ` [RFC PATCH 1/2] refs: pass struct repository *r through to write_ref_to_lockfile() Glen Choo
@ 2021-09-21 23:25 ` Glen Choo
2021-09-22 11:10 ` Ævar Arnfjörð Bjarmason
2021-09-22 12:28 ` Philippe Blain
1 sibling, 2 replies; 7+ messages in thread
From: Glen Choo @ 2021-09-21 23:25 UTC (permalink / raw)
To: git; +Cc: Glen Choo
When working with a superproject and submodules, it can be helpful to
create topic branches to coordinate the work between each repository.
Teach cmd_branch to accept the --recurse-submodules option when creating
branches. When specified, like
git branch --recurse-submodules topic
git-branch will create the "topic" branch in the superproject and all
submodules.
recurse_submodules is only supported for creating a branch. git-branch
will fail if the user attempts to use --recurse-submodules with anything
other than creating a branch. If a user has submodule.recurse in their
config, git-branch will use --recurse-submodules for creating a branch,
but will treat it as unset for any other operation.
There is no easy way to get the remotes of a submodule because remotes.c
stores state as static variables. As such, branch tracking is disabled
when using --recurse-submodules.
Signed-off-by: Glen Choo <chooglen@google.com>
---
branch.c | 26 ++++++++++-------
branch.h | 4 +--
builtin/branch.c | 69 ++++++++++++++++++++++++++++++++++++++++++----
builtin/checkout.c | 4 +--
t/t3200-branch.sh | 58 ++++++++++++++++++++++++++++++++++++++
5 files changed, 142 insertions(+), 19 deletions(-)
diff --git a/branch.c b/branch.c
index 07a46430b3..1acf4aa407 100644
--- a/branch.c
+++ b/branch.c
@@ -137,6 +137,9 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
struct tracking tracking;
int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
+ if (track == BRANCH_TRACK_NEVER)
+ return;
+
memset(&tracking, 0, sizeof(tracking));
tracking.spec.dst = (char *)orig_ref;
if (for_each_remote(find_tracked_branch, &tracking))
@@ -183,12 +186,12 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
* Return 1 if the named branch already exists; return 0 otherwise.
* Fill ref with the full refname for the branch.
*/
-int validate_branchname(const char *name, struct strbuf *ref)
+int validate_branchname(struct repository *r, const char *name, struct strbuf *ref)
{
if (strbuf_check_branch_ref(ref, name))
die(_("'%s' is not a valid branch name."), name);
- return ref_exists(ref->buf);
+ return refs_ref_exists(get_main_ref_store(r), ref->buf);
}
/*
@@ -197,11 +200,11 @@ int validate_branchname(const char *name, struct strbuf *ref)
* Return 1 if the named branch already exists; return 0 otherwise.
* Fill ref with the full refname for the branch.
*/
-int validate_new_branchname(const char *name, struct strbuf *ref, int force)
+int validate_new_branchname(struct repository *r, const char *name, struct strbuf *ref, int force)
{
const char *head;
- if (!validate_branchname(name, ref))
+ if (!validate_branchname(r, name, ref))
return 0;
if (!force)
@@ -209,6 +212,9 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force)
ref->buf + strlen("refs/heads/"));
head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+ /*
+ * NEEDSWORK is_bare_repository() references the_repository.
+ */
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die(_("Cannot force update the current branch."));
@@ -260,8 +266,8 @@ void create_branch(struct repository *r,
explicit_tracking = 1;
if ((track == BRANCH_TRACK_OVERRIDE || clobber_head_ok)
- ? validate_branchname(name, &ref)
- : validate_new_branchname(name, &ref, force)) {
+ ? validate_branchname(r, name, &ref)
+ : validate_new_branchname(r, name, &ref, force)) {
if (!force)
dont_change_ref = 1;
else
@@ -269,7 +275,7 @@ void create_branch(struct repository *r,
}
real_ref = NULL;
- if (get_oid_mb(start_name, &oid)) {
+ if (repo_get_oid_mb(r, start_name, &oid)) {
if (explicit_tracking) {
if (advice_enabled(ADVICE_SET_UPSTREAM_FAILURE)) {
error(_(upstream_missing), start_name);
@@ -281,7 +287,7 @@ void create_branch(struct repository *r,
die(_("Not a valid object name: '%s'."), start_name);
}
- switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
+ switch (repo_dwim_ref(r, start_name, strlen(start_name), &oid, &real_ref, 0)) {
case 0:
/* Not branching from any existing branch */
if (explicit_tracking)
@@ -319,12 +325,12 @@ void create_branch(struct repository *r,
else
msg = xstrfmt("branch: Created from %s", start_name);
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(r), &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf,
&oid, forcing ? NULL : null_oid(),
0, msg, &err) ||
- ref_transaction_commit(transaction, &err))
+ repo_ref_transaction_commit(r, transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
diff --git a/branch.h b/branch.h
index df0be61506..5fb9ed7e03 100644
--- a/branch.h
+++ b/branch.h
@@ -50,7 +50,7 @@ void create_branch(struct repository *r,
* Return 1 if the named branch already exists; return 0 otherwise.
* Fill ref with the full refname for the branch.
*/
-int validate_branchname(const char *name, struct strbuf *ref);
+int validate_branchname(struct repository *r, const char *name, struct strbuf *ref);
/*
* Check if a branch 'name' can be created as a new branch; die otherwise.
@@ -58,7 +58,7 @@ int validate_branchname(const char *name, struct strbuf *ref);
* Return 1 if the named branch already exists; return 0 otherwise.
* Fill ref with the full refname for the branch.
*/
-int validate_new_branchname(const char *name, struct strbuf *ref, int force);
+int validate_new_branchname(struct repository *r, const char *name, struct strbuf *ref, int force);
/*
* Remove information about the merge state on the current
diff --git a/builtin/branch.c b/builtin/branch.c
index 03c7b7253a..58a730b275 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -24,6 +24,7 @@
#include "worktree.h"
#include "help.h"
#include "commit-reach.h"
+#include "submodule-config.h"
static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"),
@@ -38,6 +39,7 @@ static const char * const builtin_branch_usage[] = {
static const char *head;
static struct object_id head_oid;
+static int recurse_submodules = 0;
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
@@ -100,6 +102,11 @@ static int git_branch_config(const char *var, const char *value, void *cb)
return config_error_nonbool(var);
return color_parse(value, branch_colors[slot]);
}
+ if (!strcmp(var, "submodule.recurse")) {
+ recurse_submodules = git_config_bool(var, value);
+ return 0;
+ }
+
return git_color_default_config(var, value, cb);
}
@@ -190,6 +197,50 @@ static void delete_branch_config(const char *branchname)
strbuf_release(&buf);
}
+static void create_branches(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 i = 0;
+ struct repository subrepo;
+ const char *r_start_name;
+ const struct submodule *sub;
+
+ r_start_name = strcmp(start_name, "HEAD") ? start_name :
+ refs_resolve_refdup(get_main_ref_store(r), start_name, 0, NULL, NULL);
+ if (strcmp(r_start_name, "HEAD"))
+ skip_prefix(r_start_name, "refs/heads/", &r_start_name);
+ create_branch(r, name, r_start_name, force, clobber_head_ok, reflog,
+ quiet, track);
+
+ if (!recurse_submodules) {
+ return;
+ }
+
+ if (repo_read_index(r) < 0)
+ die(_("index file corrupt"));
+
+ for (i = 0; i < r->index->cache_nr; i++) {
+ const struct cache_entry *ce = r->index->cache[i];
+ if (!S_ISGITLINK(ce->ce_mode))
+ continue;
+ sub = submodule_from_path(r, null_oid(), ce->name);
+ if (repo_submodule_init(&subrepo, r, sub) < 0) {
+ warning(_("Unable to initialize subrepo %s, skipping."), ce->name);
+ continue;
+ }
+ /*
+ * NEEDSWORK: branch tracking is not supported for
+ * submodules because it is not possible to get the
+ * remotes of a submodule.
+ */
+ create_branches(&subrepo, name, start_name, force,
+ clobber_head_ok, reflog, quiet, BRANCH_TRACK_NEVER);
+ repo_clear(&subrepo);
+ }
+}
+
static int delete_branches(int argc, const char **argv, int force, int kinds,
int quiet)
{
@@ -531,9 +582,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
* cause the worktree to become inconsistent with HEAD, so allow it.
*/
if (!strcmp(oldname, newname))
- validate_branchname(newname, &newref);
+ validate_branchname(the_repository, newname, &newref);
else
- validate_new_branchname(newname, &newref, force);
+ validate_new_branchname(the_repository, newname, &newref, force);
reject_rebase_or_bisect_branch(oldref.buf);
@@ -626,6 +677,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int icase = 0;
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
struct ref_format format = REF_FORMAT_INIT;
+ int recurse_submodules_explicit = 0;
struct option options[] = {
OPT_GROUP(N_("Generic options")),
@@ -669,6 +721,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
N_("print only branches of the object"), parse_opt_object_name),
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_END(),
};
@@ -708,6 +761,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
list + edit_description + unset_upstream > 1)
usage_with_options(builtin_branch_usage, options);
+ else if (recurse_submodules_explicit &&
+ (delete || rename || copy || new_upstream || show_current || list
+ || edit_description || unset_upstream))
+ die(_("--recurse-submodules can only be used to create branches"));
+
+ recurse_submodules |= !!recurse_submodules_explicit;
if (filter.abbrev == -1)
filter.abbrev = DEFAULT_ABBREV;
@@ -857,9 +916,9 @@ 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);
+ create_branches(the_repository,
+ argv[0], (argc == 2) ? argv[1] : "HEAD",
+ force, 0, reflog, quiet, track);
} else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 8c69dcdf72..8859076a14 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1761,10 +1761,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
struct strbuf buf = STRBUF_INIT;
if (opts->new_branch_force)
- opts->branch_exists = validate_branchname(opts->new_branch, &buf);
+ opts->branch_exists = validate_branchname(the_repository, opts->new_branch, &buf);
else
opts->branch_exists =
- validate_new_branchname(opts->new_branch, &buf, 0);
+ validate_new_branchname(the_repository, opts->new_branch, &buf, 0);
strbuf_release(&buf);
}
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index e575ffb4ff..858831d4cf 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1421,5 +1421,63 @@ test_expect_success 'invalid sort parameter in configuration' '
test_must_fail git branch
)
'
+# Submodule tests
+
+test_expect_success 'setup superproject and submodule' '
+ git init parent &&
+ test_commit -C parent bar &&
+ git init child &&
+ test_commit -C child bar &&
+ git -C parent submodule add ../child sub &&
+ git -C parent commit -m "add submodule"
+'
+
+test_expect_success '--recurse-submodules should create branches' '
+ (
+ cd parent &&
+ git branch --recurse-submodules abc &&
+ test_path_is_file .git/refs/heads/abc &&
+ test_path_is_file .git/modules/sub/refs/heads/abc
+ )
+'
+
+test_expect_success '--recurse-submodules should fail when not creating branches' '
+ (
+ cd parent &&
+ test_must_fail git branch --recurse-submodules -D abc &&
+ test_path_is_file .git/refs/heads/abc &&
+ test_path_is_file .git/modules/sub/refs/heads/abc
+ )
+'
+
+test_expect_success 'should respect submodule.recurse when creating branches' '
+ (
+ cd parent &&
+ git -c submodule.recurse=true branch def &&
+ test_path_is_file .git/refs/heads/def &&
+ test_path_is_file .git/modules/sub/refs/heads/def
+ )
+'
+
+test_expect_success 'should ignore submodule.recurse when not creating branches' '
+ (
+ cd parent &&
+ git -c submodule.recurse=true branch -D def &&
+ test_path_is_missing .git/refs/heads/def &&
+ test_path_is_file .git/modules/sub/refs/heads/def
+ )
+'
+
+test_expect_success 'should not set up tracking for new submodule branches' '
+ (
+ cd parent &&
+ git -c branch.autoSetupMerge=always branch --recurse-submodules ghi main &&
+ test_path_is_file .git/modules/sub/refs/heads/ghi &&
+ test "$(git config branch.ghi.remote)" = . &&
+ test "$(git config branch.ghi.merge)" = refs/heads/main &&
+ test "z$(git -C ../child config branch.ghi.remote)" = z &&
+ test "z$(git -C ../child config branch.ghi.merge)" = z
+ )
+'
test_done
--
2.33.0.464.g1972c5931b-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread