]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'gc/branch-recurse-submodules'
authorJunio C Hamano <gitster@pobox.com>
Fri, 18 Feb 2022 21:53:29 +0000 (13:53 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 18 Feb 2022 21:53:29 +0000 (13:53 -0800)
"git branch" learned the "--recurse-submodules" option.

* gc/branch-recurse-submodules:
  branch.c: use 'goto cleanup' in setup_tracking() to fix memory leaks
  branch: add --recurse-submodules option for branch creation
  builtin/branch: consolidate action-picking logic in cmd_branch()
  branch: add a dry_run parameter to create_branch()
  branch: make create_branch() always create a branch
  branch: move --set-upstream-to behavior to dwim_and_setup_tracking()

1  2 
branch.c
builtin/branch.c
builtin/checkout.c
builtin/submodule--helper.c
t/t3200-branch.sh
t/t3207-branch-submodule.sh

diff --cc branch.c
index 5d20a2e8484b22f321a60e01d63cb4c4bd8e88fe,47251669e1414e99d5b49debe18e3fe6a21cd902..6b31df539a5f72e07bc5efb4592f703ddd70d28c
+++ b/branch.c
@@@ -406,8 -412,44 +417,44 @@@ static void dwim_branch_start(struct re
        }
  
        if ((commit = lookup_commit_reference(r, &oid)) == NULL)
 -              die(_("Not a valid branch point: '%s'."), start_name);
 +              die(_("not a valid branch point: '%s'"), start_name);
-       oidcpy(&oid, &commit->object.oid);
+       if (out_real_ref) {
+               *out_real_ref = real_ref;
+               real_ref = NULL;
+       }
+       if (out_oid)
+               oidcpy(out_oid, &commit->object.oid);
+       FREE_AND_NULL(real_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 dry_run)
+ {
+       struct object_id oid;
+       char *real_ref;
+       struct strbuf ref = STRBUF_INIT;
+       int forcing = 0;
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
+       char *msg;
+       if (track == BRANCH_TRACK_OVERRIDE)
+               BUG("'track' cannot be BRANCH_TRACK_OVERRIDE. Did you mean to call dwim_and_setup_tracking()?");
+       if (clobber_head_ok && !force)
+               BUG("'clobber_head_ok' can only be used with 'force'");
+       if (clobber_head_ok ?
+                         validate_branchname(name, &ref) :
+                         validate_new_branchname(name, &ref, force)) {
+               forcing = 1;
+       }
+       dwim_branch_start(r, start_name, track, &real_ref, &oid);
+       if (dry_run)
+               goto cleanup;
  
        if (reflog)
                log_all_ref_updates = LOG_REFS_NORMAL;
Simple merge
index f6e65fedfa2bbbc3cf7d180dc26e9881c6c0c2d6,86008606298ef039844de39eca088a5032da02bb..2b33960e2167a8269beba1cc1d065ecab27aafe9
@@@ -909,10 -893,9 +909,11 @@@ static void update_refs_for_switch(cons
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
                                      opts->quiet,
-                                     opts->track);
+                                     opts->track,
+                                     0);
 -              new_branch_info->name = opts->new_branch;
 +              free(new_branch_info->name);
 +              free(new_branch_info->refname);
 +              new_branch_info->name = xstrdup(opts->new_branch);
                setup_branch_path(new_branch_info);
        }
  
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,6ef2733396627bd8d30b021a0d329e209f7ca3eb..0d93f7516c9af298181de179e37f2d7cf6e99e53
mode 000000,100755..100755
--- /dev/null
@@@ -1,0 -1,292 +1,292 @@@
 -              grep "submodule .sub.: fatal: A branch named .branch-a. already exists" actual
+ #!/bin/sh
+ test_description='git branch submodule tests'
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+ . ./test-lib.sh
+ . "$TEST_DIRECTORY"/lib-rebase.sh
+ pwd=$(pwd)
+ # Creates a clean test environment in "pwd" by copying the repo setup
+ # from test_dirs.
+ reset_test () {
+       rm -fr super &&
+       rm -fr sub-sub-upstream &&
+       rm -fr sub-upstream &&
+       cp -r test_dirs/* .
+ }
+ # Tests that the expected branch does not exist
+ test_no_branch () {
+       DIR=$1 &&
+       BRANCH_NAME=$2 &&
+       test_must_fail git -C "$DIR" rev-parse "$BRANCH_NAME" 2>err &&
+       grep "ambiguous argument .$BRANCH_NAME." err
+ }
+ test_expect_success 'setup superproject and submodule' '
+       mkdir test_dirs &&
+       (
+               cd test_dirs &&
+               git init super &&
+               test_commit -C super foo &&
+               git init sub-sub-upstream &&
+               test_commit -C sub-sub-upstream foo &&
+               git init sub-upstream &&
+               # Submodule in a submodule
+               git -C sub-upstream submodule add "${pwd}/test_dirs/sub-sub-upstream" sub-sub &&
+               git -C sub-upstream commit -m "add submodule" &&
+               # Regular submodule
+               git -C super submodule add "${pwd}/test_dirs/sub-upstream" sub &&
+               # Submodule in a subdirectory
+               git -C super submodule add "${pwd}/test_dirs/sub-sub-upstream" second/sub &&
+               git -C super commit -m "add submodule" &&
+               git -C super config submodule.propagateBranches true &&
+               git -C super/sub submodule update --init
+       ) &&
+       reset_test
+ '
+ # Test the argument parsing
+ test_expect_success '--recurse-submodules should create branches' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git branch --recurse-submodules branch-a &&
+               git rev-parse branch-a &&
+               git -C sub rev-parse branch-a &&
+               git -C sub/sub-sub rev-parse branch-a &&
+               git -C second/sub rev-parse branch-a
+       )
+ '
+ test_expect_success '--recurse-submodules should die if submodule.propagateBranches is false' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               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
+       )
+ '
+ test_expect_success '--recurse-submodules should fail when not creating branches' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git branch --recurse-submodules branch-a &&
+               echo "fatal: --recurse-submodules can only be used to create branches" >expected &&
+               test_must_fail git branch --recurse-submodules -D branch-a 2>actual &&
+               test_cmp expected actual &&
+               # Assert that the branches were not deleted
+               git rev-parse branch-a &&
+               git -C sub rev-parse branch-a
+       )
+ '
+ test_expect_success 'should respect submodule.recurse when creating branches' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git -c submodule.recurse=true branch branch-a &&
+               git rev-parse branch-a &&
+               git -C sub rev-parse branch-a
+       )
+ '
+ test_expect_success 'should ignore submodule.recurse when not creating branches' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git branch --recurse-submodules branch-a &&
+               git -c submodule.recurse=true branch -D branch-a &&
+               test_no_branch . branch-a &&
+               git -C sub rev-parse branch-a
+       )
+ '
+ # Test branch creation behavior
+ test_expect_success 'should create branches based off commit id in superproject' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git branch --recurse-submodules branch-a &&
+               git checkout --recurse-submodules branch-a &&
+               git -C sub rev-parse HEAD >expected &&
+               # Move the tip of sub:branch-a so that it no longer matches the commit in super:branch-a
+               git -C sub checkout branch-a &&
+               test_commit -C sub bar &&
+               # Create a new branch-b branch with start-point=branch-a
+               git branch --recurse-submodules branch-b branch-a &&
+               git rev-parse branch-b &&
+               git -C sub rev-parse branch-b >actual &&
+               # Assert that the commit id of sub:second-branch matches super:branch-a and not sub:branch-a
+               test_cmp expected actual
+       )
+ '
+ test_expect_success 'should not create any branches if branch is not valid for all repos' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git -C sub branch branch-a &&
+               test_must_fail git branch --recurse-submodules branch-a 2>actual &&
+               test_no_branch . branch-a &&
++              grep "submodule .sub.: fatal: a branch named .branch-a. already exists" actual
+       )
+ '
+ test_expect_success 'should create branches if branch exists and --force is given' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git -C sub rev-parse HEAD >expected &&
+               test_commit -C sub baz &&
+               # branch-a in sub now points to a newer commit.
+               git -C sub branch branch-a HEAD &&
+               git -C sub rev-parse branch-a >actual-old-branch-a &&
+               git branch --recurse-submodules --force branch-a &&
+               git rev-parse branch-a &&
+               git -C sub rev-parse branch-a >actual-new-branch-a &&
+               test_cmp expected actual-new-branch-a &&
+               # assert that branch --force actually moved the sub
+               # branch
+               ! test_cmp expected actual-old-branch-a
+       )
+ '
+ test_expect_success 'should create branch when submodule is not in HEAD:.gitmodules' '
+       test_when_finished "reset_test" &&
+       (
+               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 &&
+               git branch --recurse-submodules branch-c branch-b &&
+               git checkout --recurse-submodules branch-c &&
+               git -C sub2 rev-parse branch-c &&
+               git -C sub2/sub-sub rev-parse branch-c
+       )
+ '
+ test_expect_success 'should not create branches in inactive submodules' '
+       test_when_finished "reset_test" &&
+       test_config -C super submodule.sub.active false &&
+       (
+               cd super &&
+               git branch --recurse-submodules branch-a &&
+               git rev-parse branch-a &&
+               test_no_branch sub branch-a
+       )
+ '
+ test_expect_success 'should set up tracking of local branches with track=always' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git -c branch.autoSetupMerge=always branch --recurse-submodules branch-a main &&
+               git -C sub rev-parse main &&
+               test_cmp_config -C sub . branch.branch-a.remote &&
+               test_cmp_config -C sub refs/heads/main branch.branch-a.merge
+       )
+ '
+ test_expect_success 'should set up tracking of local branches with explicit track' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git branch --track --recurse-submodules branch-a main &&
+               git -C sub rev-parse main &&
+               test_cmp_config -C sub . branch.branch-a.remote &&
+               test_cmp_config -C sub refs/heads/main branch.branch-a.merge
+       )
+ '
+ test_expect_success 'should not set up unnecessary tracking of local branches' '
+       test_when_finished "reset_test" &&
+       (
+               cd super &&
+               git branch --recurse-submodules branch-a main &&
+               git -C sub rev-parse main &&
+               test_cmp_config -C sub "" --default "" branch.branch-a.remote &&
+               test_cmp_config -C sub "" --default "" branch.branch-a.merge
+       )
+ '
+ reset_remote_test () {
+       rm -fr super-clone &&
+       reset_test
+ }
+ test_expect_success 'setup tests with remotes' '
+       (
+               cd test_dirs &&
+               (
+                       cd super &&
+                       git branch branch-a &&
+                       git checkout -b branch-b &&
+                       git submodule add ../sub-upstream sub2 &&
+                       # branch-b now has a committed submodule not in branch-a
+                       git commit -m "add second submodule"
+               ) &&
+               git clone --branch main --recurse-submodules super super-clone &&
+               git -C super-clone config submodule.propagateBranches true
+       ) &&
+       reset_remote_test
+ '
+ test_expect_success 'should get fatal error upon branch creation when submodule is not in .git/modules' '
+       test_when_finished "reset_remote_test" &&
+       (
+               cd super-clone &&
+               # This should succeed because super-clone has sub in .git/modules
+               git branch --recurse-submodules branch-a origin/branch-a &&
+               # This should fail because super-clone does not have sub2 .git/modules
+               test_must_fail git branch --recurse-submodules branch-b origin/branch-b 2>actual &&
+               grep "fatal: submodule .sub2.: unable to find submodule" actual &&
+               test_no_branch . branch-b &&
+               test_no_branch sub branch-b &&
+               # User can fix themselves by initializing the submodule
+               git checkout origin/branch-b &&
+               git submodule update --init --recursive &&
+               git branch --recurse-submodules branch-b origin/branch-b
+       )
+ '
+ test_expect_success 'should set up tracking of remote-tracking branches' '
+       test_when_finished "reset_remote_test" &&
+       (
+               cd super-clone &&
+               git branch --recurse-submodules branch-a origin/branch-a &&
+               test_cmp_config origin branch.branch-a.remote &&
+               test_cmp_config refs/heads/branch-a branch.branch-a.merge &&
+               # "origin/branch-a" does not exist for "sub", but it matches the refspec
+               # so tracking should be set up
+               test_cmp_config -C sub origin branch.branch-a.remote &&
+               test_cmp_config -C sub refs/heads/branch-a branch.branch-a.merge &&
+               test_cmp_config -C sub/sub-sub origin branch.branch-a.remote &&
+               test_cmp_config -C sub/sub-sub refs/heads/branch-a branch.branch-a.merge
+       )
+ '
+ test_expect_success 'should not fail when unable to set up tracking in submodule' '
+       test_when_finished "reset_remote_test" &&
+       (
+               cd super-clone &&
+               git remote rename origin ex-origin &&
+               git branch --recurse-submodules branch-a ex-origin/branch-a &&
+               test_cmp_config ex-origin branch.branch-a.remote &&
+               test_cmp_config refs/heads/branch-a branch.branch-a.merge &&
+               test_cmp_config -C sub "" --default "" branch.branch-a.remote &&
+               test_cmp_config -C sub "" --default "" branch.branch-a.merge
+       )
+ '
+ test_done