Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,13 @@ static int inherit_tracking(struct tracking *tracking, const char *orig_ref)
* will not be expanded to "refs/remotes/origin/main", so it is not safe
* for 'orig_ref' to be raw user input.
*/
static void setup_tracking(const char *new_ref, const char *orig_ref,
enum branch_track track, int quiet)
static int setup_tracking(const char *new_ref, const char *orig_ref,
enum branch_track track, int quiet)
{
struct tracking tracking;
struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
int ret = 0;
struct find_tracked_branch_cb ftb_cb = {
.tracking = &tracking,
.ambiguous_remotes = STRING_LIST_INIT_DUP,
Expand Down Expand Up @@ -343,11 +344,12 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
string_list_append(tracking.srcs, orig_ref);
if (install_branch_config_multiple_remotes(config_flags, new_ref,
tracking.remote, tracking.srcs) < 0)
exit(1);
ret = -1;

cleanup:
string_list_clear(&tracking_srcs, 0);
string_list_clear(&ftb_cb.ambiguous_remotes, 0);
return ret;
}

int read_branch_desc(struct strbuf *buf, const char *branch_name)
Expand Down Expand Up @@ -627,10 +629,10 @@ static void dwim_branch_start(struct repository *r, const char *start_name,
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)
int 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;
Expand All @@ -639,6 +641,7 @@ void create_branch(struct repository *r,
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
int flags = 0;
int ret = 0;
char *msg;

if (track == BRANCH_TRACK_OVERRIDE)
Expand Down Expand Up @@ -676,21 +679,25 @@ void create_branch(struct repository *r,
free(msg);

if (real_ref && track)
setup_tracking(ref.buf + 11, real_ref, track, quiet);
ret = setup_tracking(ref.buf + 11, real_ref, track, quiet);

cleanup:
strbuf_release(&ref);
free(real_ref);
return ret;
}

void dwim_and_setup_tracking(struct repository *r, const char *new_ref,
const char *orig_ref, enum branch_track track,
int quiet)
int dwim_and_setup_tracking(struct repository *r, const char *new_ref,
const char *orig_ref, enum branch_track track,
int quiet)
{
char *real_orig_ref = NULL;
int ret;

dwim_branch_start(r, orig_ref, track, &real_orig_ref, NULL);
setup_tracking(new_ref, real_orig_ref, track, quiet);
ret = setup_tracking(new_ref, real_orig_ref, track, quiet);
free(real_orig_ref);
return ret;
}

/**
Expand Down Expand Up @@ -840,8 +847,8 @@ void create_branches_recursively(struct repository *r, const char *name,
* tedious to determine whether or not tracking was set up in the
* superproject.
*/
if (track)
setup_tracking(name, tracking_name, track, quiet);
if (track && setup_tracking(name, tracking_name, track, quiet))
exit(1);

for (i = 0; i < submodule_entry_list.entry_nr; i++) {
if (submodule_create_branch(
Expand Down
21 changes: 13 additions & 8 deletions branch.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ enum branch_track {
* causes new_ref to track whatever orig_ref tracks.
*
* - quiet suppresses tracking information.
*
* Returns 0 on success, or -1 if the tracking configuration could not be
* written.
*/
void dwim_and_setup_tracking(struct repository *r, const char *new_ref,
const char *orig_ref, enum branch_track track,
int quiet);
int dwim_and_setup_tracking(struct repository *r, const char *new_ref,
const char *orig_ref, enum branch_track track,
int quiet);

/*
* Creates a new branch, where:
Expand All @@ -63,12 +66,14 @@ void dwim_and_setup_tracking(struct repository *r, const char *new_ref,
*
* - dry_run causes the branch to be validated but not created.
*
* Returns 0 on success, or -1 if the tracking configuration could not be
* written.
*/
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);
int 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 a repository and its submodules (and its
Expand Down
17 changes: 12 additions & 5 deletions builtin/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -1253,9 +1253,13 @@ int cmd_branch(int argc,
die(_("branch '%s' does not exist"), branch->name);
}

dwim_and_setup_tracking(the_repository, branch->name,
new_upstream, BRANCH_TRACK_OVERRIDE,
quiet);
if (dwim_and_setup_tracking(the_repository, branch->name,
new_upstream, BRANCH_TRACK_OVERRIDE,
quiet)) {
strbuf_release(&buf);
ret = 1;
goto out;
}
strbuf_release(&buf);
} else if (unset_upstream) {
struct branch *branch;
Expand Down Expand Up @@ -1305,8 +1309,11 @@ int cmd_branch(int argc,
ret = 0;
goto out;
}
create_branch(the_repository, branch_name, start_name, force, 0,
reflog, quiet, track, 0);
if (create_branch(the_repository, branch_name, start_name,
force, 0, reflog, quiet, track, 0)) {
ret = 1;
goto out;
}
} else
usage_with_options(builtin_branch_usage, options);

Expand Down
6 changes: 6 additions & 0 deletions builtin/checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,12 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
free(refname);
}
else
/*
* Not fatal: the error and hint have already been
* shown, and like "push -u" and "clone" we finish
* the switch instead of leaving HEAD on the old
* branch with the worktree already updated.
*/
create_branch(the_repository,
opts->new_branch, new_branch_info->name,
opts->new_branch_force ? 1 : 0,
Expand Down
20 changes: 20 additions & 0 deletions t/t2027-checkout-track.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
test_cmp_config refs/heads/main branch.b4.merge
'

test_expect_success 'checkout -b completes when tracking config cannot be written' '
test_when_finished "git update-ref -d refs/remotes/lockremote/lockbranch" &&
test_when_finished "git branch -D lock-new" &&
test_when_finished "git checkout main" &&
test_config remote.lockremote.url . &&
test_config remote.lockremote.fetch "+refs/heads/*:refs/remotes/lockremote/*" &&
git update-ref refs/remotes/lockremote/lockbranch one &&
test_when_finished "rm -f .git/config.lock" &&
>.git/config.lock &&
git -c core.configLockTimeout=0 \
checkout -b lock-new lockremote/lockbranch 2>err &&
test_grep "unable to write upstream branch configuration" err &&
echo refs/heads/lock-new >expect &&
git symbolic-ref HEAD >actual &&
test_cmp expect actual &&
git diff --cached --exit-code &&
rm -f .git/config.lock &&
test_must_fail git config branch.lock-new.remote
'

test_expect_success 'ambiguous tracking info' '
# Set up a few remote repositories
git init --bare --initial-branch=trunk src1 &&
Expand Down
20 changes: 20 additions & 0 deletions t/t2060-switch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,26 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
test_cmp_config "" --default "" branch.main2.merge
'

test_expect_success 'switch -c completes when tracking config cannot be written' '
test_when_finished "git update-ref -d refs/remotes/lockremote/lockbranch" &&
test_when_finished "git branch -D lock-new" &&
test_when_finished "git switch main" &&
test_config remote.lockremote.url . &&
test_config remote.lockremote.fetch "+refs/heads/*:refs/remotes/lockremote/*" &&
git update-ref refs/remotes/lockremote/lockbranch first-branch &&
test_when_finished "rm -f .git/config.lock" &&
>.git/config.lock &&
git -c core.configLockTimeout=0 \
switch -c lock-new lockremote/lockbranch 2>err &&
test_grep "unable to write upstream branch configuration" err &&
echo refs/heads/lock-new >expect &&
git symbolic-ref HEAD >actual &&
test_cmp expect actual &&
git diff --cached --exit-code &&
rm -f .git/config.lock &&
test_must_fail git config branch.lock-new.remote
'

test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '
test_when_finished "
git worktree remove wt1 ||:
Expand Down
13 changes: 13 additions & 0 deletions t/t3200-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,19 @@ test_expect_success '--set-upstream-to fails on locked config' '
test_grep "could not lock config file .git/config" err
'

test_expect_success 'branch with start point fails when tracking config cannot be written' '
test_when_finished "git branch -D lock-new || :" &&
test_when_finished "git update-ref -d refs/remotes/lockremote/lockbranch" &&
test_config remote.lockremote.url . &&
test_config remote.lockremote.fetch "+refs/heads/*:refs/remotes/lockremote/*" &&
git update-ref refs/remotes/lockremote/lockbranch HEAD &&
test_when_finished "rm -f .git/config.lock" &&
>.git/config.lock &&
test_must_fail git -c core.configLockTimeout=0 \
branch lock-new lockremote/lockbranch 2>err &&
test_grep "unable to write upstream branch configuration" err
'

test_expect_success 'use --set-upstream-to modify HEAD' '
test_config branch.main.remote foo &&
test_config branch.main.merge foo &&
Expand Down
Loading