1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-06 12:16:14 +02:00

submodule--helper: teach push-check to handle HEAD

In 06bf4ad1d (push: propagate remote and refspec with
--recurse-submodules) push was taught how to propagate a refspec down to
submodules when the '--recurse-submodules' flag is given.  The only refspecs
that are allowed to be propagated are ones which name a ref which exists
in both the superproject and the submodule, with the caveat that 'HEAD'
was disallowed.

This patch teaches push-check (the submodule helper which determines if
a refspec can be propagated to a submodule) to permit propagating 'HEAD'
if and only if the superproject and the submodule both have the same
named branch checked out and the submodule is not in a detached head
state.

Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Brandon Williams 2017-07-20 10:40:37 -07:00 committed by Junio C Hamano
parent 06bf4ad1db
commit c7be7201a7
3 changed files with 79 additions and 13 deletions

View File

@ -1108,9 +1108,28 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
static int push_check(int argc, const char **argv, const char *prefix)
{
struct remote *remote;
const char *superproject_head;
char *head;
int detached_head = 0;
struct object_id head_oid;
if (argc < 2)
die("submodule--helper push-check requires at least 1 argument");
if (argc < 3)
die("submodule--helper push-check requires at least 2 arguments");
/*
* superproject's resolved head ref.
* if HEAD then the superproject is in a detached head state, otherwise
* it will be the resolved head ref.
*/
superproject_head = argv[1];
argv++;
argc--;
/* Get the submodule's head ref and determine if it is detached */
head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
detached_head = 1;
/*
* The remote must be configured.
@ -1133,18 +1152,30 @@ static int push_check(int argc, const char **argv, const char *prefix)
if (rs->pattern || rs->matching)
continue;
/*
* LHS must match a single ref
* NEEDSWORK: add logic to special case 'HEAD' once
* working with submodules in a detached head state
* ceases to be the norm.
*/
if (count_refspec_match(rs->src, local_refs, NULL) != 1)
/* LHS must match a single ref */
switch (count_refspec_match(rs->src, local_refs, NULL)) {
case 1:
break;
case 0:
/*
* If LHS matches 'HEAD' then we need to ensure
* that it matches the same named branch
* checked out in the superproject.
*/
if (!strcmp(rs->src, "HEAD")) {
if (!detached_head &&
!strcmp(head, superproject_head))
break;
die("HEAD does not match the named branch in the superproject");
}
default:
die("src refspec '%s' must name a ref",
rs->src);
}
}
free_refspec(refspec_nr, refspec);
}
free(head);
return 0;
}

View File

@ -828,7 +828,8 @@ static int push_submodule(const char *path,
* Perform a check in the submodule to see if the remote and refspec work.
* Die if the submodule can't be pushed.
*/
static void submodule_push_check(const char *path, const struct remote *remote,
static void submodule_push_check(const char *path, const char *head,
const struct remote *remote,
const char **refspec, int refspec_nr)
{
struct child_process cp = CHILD_PROCESS_INIT;
@ -836,6 +837,7 @@ static void submodule_push_check(const char *path, const struct remote *remote,
argv_array_push(&cp.args, "submodule--helper");
argv_array_push(&cp.args, "push-check");
argv_array_push(&cp.args, head);
argv_array_push(&cp.args, remote->name);
for (i = 0; i < refspec_nr; i++)
@ -874,10 +876,20 @@ int push_unpushed_submodules(struct sha1_array *commits,
* won't be propagated due to the remote being unconfigured (e.g. a URL
* instead of a remote name).
*/
if (remote->origin != REMOTE_UNCONFIGURED)
if (remote->origin != REMOTE_UNCONFIGURED) {
char *head;
struct object_id head_oid;
head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
for (i = 0; i < needs_pushing.nr; i++)
submodule_push_check(needs_pushing.items[i].string,
remote, refspec, refspec_nr);
head, remote,
refspec, refspec_nr);
free(head);
}
/* Actually push the submodules */
for (i = 0; i < needs_pushing.nr; i++) {

View File

@ -512,7 +512,8 @@ test_expect_success 'push propagating refspec to a submodule' '
# Fails when refspec includes an object id
test_must_fail git -C work push --recurse-submodules=on-demand origin \
"$(git -C work rev-parse branch2):refs/heads/branch2" &&
# Fails when refspec includes 'HEAD' as it is unsupported at this time
# Fails when refspec includes HEAD and parent and submodule do not
# have the same named branch checked out
test_must_fail git -C work push --recurse-submodules=on-demand origin \
HEAD:refs/heads/branch2 &&
@ -527,4 +528,26 @@ test_expect_success 'push propagating refspec to a submodule' '
test_cmp expected_pub actual_pub
'
test_expect_success 'push propagating HEAD refspec to a submodule' '
git -C work/gar/bage checkout branch2 &&
> work/gar/bage/junk12 &&
git -C work/gar/bage add junk12 &&
git -C work/gar/bage commit -m "Twelfth junk" &&
git -C work checkout branch2 &&
git -C work add gar/bage &&
git -C work commit -m "updating gar/bage in branch2" &&
# Passes since the superproject and submodules HEAD are both on branch2
git -C work push --recurse-submodules=on-demand origin \
HEAD:refs/heads/branch2 &&
git -C submodule.git rev-parse branch2 >actual_submodule &&
git -C pub.git rev-parse branch2 >actual_pub &&
git -C work/gar/bage rev-parse branch2 >expected_submodule &&
git -C work rev-parse branch2 >expected_pub &&
test_cmp expected_submodule actual_submodule &&
test_cmp expected_pub actual_pub
'
test_done