From: "tsukasa inoue via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Harald Nordgren <haraldnordgren@gmail.com>,
tsukasa-ino <inoue.tsukasa.4@gmail.com>,
tsukasa inoue <inoue.tsukasa.4@gmail.com>
Subject: [PATCH v2 3/3] checkout: finish switching when the tracking config cannot be written
Date: Wed, 21 Oct 2026 21:35:18 +0000 [thread overview]
Message-ID: <be94ac75bfbcb4ea6af982f5a5cffd20344445b5.1792618518.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2268.v2.git.1792618518.gitgitgadget@gmail.com>
From: tsukasa inoue <inoue.tsukasa.4@gmail.com>
When git switch -c <new> <remote>/<branch> (or checkout -b) cannot write
the tracking config, for example because .git/config.lock exists, the
worktree, the index and the new branch ref have already been updated.
Git then exits with status 1 while HEAD still points to the original
branch. The changes of the switch remain staged, and the post-checkout
hook does not run.
create_branch() now returns an error instead of exiting. Make checkout
ignore that error so that the switch completes and the command exits
with status 0. The error and the hint are still printed.
Match push -u, clone and fetch, which also print the error and continue.
The upstream can be set later with the command shown in the hint.
Keep git branch <new> <start> failing when the tracking config cannot be
written, so its behavior does not change; t3200 covers this. Making the
switch fully atomic, undoing the worktree, the index, the ref and the
config when the tracking config fails, is a separate topic.
Signed-off-by: tsukasa inoue <inoue.tsukasa.4@gmail.com>
---
builtin/checkout.c | 23 ++++++++++++++---------
t/t2027-checkout-track.sh | 20 ++++++++++++++++++++
t/t2060-switch.sh | 20 ++++++++++++++++++++
t/t3200-branch.sh | 13 +++++++++++++
4 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 87e067b45b..a203f9e2ca 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -979,15 +979,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
free(refname);
}
else
- if (create_branch(the_repository,
- opts->new_branch, new_branch_info->name,
- opts->new_branch_force ? 1 : 0,
- opts->new_branch_force ? 1 : 0,
- opts->new_branch_log,
- opts->quiet,
- opts->track,
- 0))
- exit(1);
+ /*
+ * 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,
+ opts->new_branch_force ? 1 : 0,
+ opts->new_branch_log,
+ opts->quiet,
+ opts->track,
+ 0);
free(new_branch_info->name);
free(new_branch_info->refname);
new_branch_info->name = xstrdup(opts->new_branch);
diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
index c01f1cd617..c3be183bfc 100755
--- a/t/t2027-checkout-track.sh
+++ b/t/t2027-checkout-track.sh
@@ -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 &&
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index cec2fe6876..b4f137acc3 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -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 ||:
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ccfb53efa3..b9c169a91a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1105,6 +1105,19 @@ test_expect_success '--set-upstream-to hint uses short remote-tracking branch na
test_cmp_config refs/heads/hintbranch branch.hint-local.merge
'
+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 &&
--
gitgitgadget
next prev parent reply other threads:[~2026-10-21 21:35 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 1:15 [PATCH] branch: strip refs/heads/ from the --set-upstream-to hint tsukasa-ino via GitGitGadget
2026-10-20 19:51 ` Junio C Hamano
2026-10-21 7:05 ` Harald Nordgren
2026-10-21 21:35 ` [PATCH v2 0/3] checkout: finish switch when tracking config cannot be written tsukasa-ino via GitGitGadget
2026-10-21 21:35 ` [PATCH v2 1/3] branch: strip refs/heads/ from the --set-upstream-to hint tsukasa inoue via GitGitGadget
2026-10-21 21:35 ` [PATCH v2 2/3] branch: return an error from setup_tracking() tsukasa inoue via GitGitGadget
2026-10-21 21:35 ` tsukasa inoue via GitGitGadget [this message]
2026-10-21 22:20 ` [PATCH v2 0/3] checkout: finish switch when tracking config cannot be written Junio C Hamano
2026-10-22 2:17 ` 井上司
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=be94ac75bfbcb4ea6af982f5a5cffd20344445b5.1792618518.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=haraldnordgren@gmail.com \
--cc=inoue.tsukasa.4@gmail.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.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox