fetch: protect branches checked out in all worktrees
Refuse to fetch into the currently checked out branch of any working tree, not just the current one. Fixes this previously reported bug: https://lore.kernel.org/git/cb957174-5e9a-5603-ea9e-ac9b58a2eaad@mathema.de/ As a side effect of using find_shared_symref, we’ll also refuse the fetch when we’re on a detached HEAD because we’re rebasing or bisecting on the branch in question. This seems like a sensible change. Signed-off-by: Anders Kaseorg <andersk@mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Anders Kaseorg committed
Dec 1, 2021 at 14:15 UTC
8bc1f39f411e06b940230dc84da5568dfafec922
2 files changed
+58
-35
builtin/fetch.c
+40
-35
@@ -28,6 +28,7 @@
28
#include "promisor-remote.h"
29
#include "commit-graph.h"
30
#include "shallow.h"
31
+#include "worktree.h"
32
33
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
34
@@ -848,13 +849,12 @@ static void format_display(struct strbuf *display, char code,
849
850
static int update_local_ref(struct ref *ref,
851
struct ref_transaction *transaction,
851
- const char *remote,
852
- const struct ref *remote_ref,
853
- struct strbuf *display,
854
- int summary_width)
852
+ const char *remote, const struct ref *remote_ref,
853
+ struct strbuf *display, int summary_width,
854
+ struct worktree **worktrees)
855
{
856
struct commit *current = NULL, *updated;
857
- struct branch *current_branch = branch_get(NULL);
857
+ const struct worktree *wt;
858
const char *pretty_ref = prettify_refname(ref->name);
859
int fast_forward = 0;
860
@@ -868,16 +868,17 @@ static int update_local_ref(struct ref *ref,
868
return 0;
869
}
870
871
- if (current_branch &&
872
- !strcmp(ref->name, current_branch->name) &&
873
- !(update_head_ok || is_bare_repository()) &&
874
- !is_null_oid(&ref->old_oid)) {
871
+ if (!update_head_ok &&
872
+ (wt = find_shared_symref(worktrees, "HEAD", ref->name)) &&
873
+ !wt->is_bare && !is_null_oid(&ref->old_oid)) {
874
/*
875
* If this is the head, and it's not okay to update
876
* the head, and the old value of the head isn't empty...
877
*/
878
format_display(display, '!', _("[rejected]"),
880
- _("can't fetch in current branch"),
879
+ wt->is_current ?
880
+ _("can't fetch in current branch") :
881
+ _("checked out in another worktree"),
882
remote, pretty_ref, summary_width);
883
return 1;
884
}
@@ -1076,7 +1077,8 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
1077
"to avoid this check\n");
1078
1079
static int store_updated_refs(const char *raw_url, const char *remote_name,
1079
- int connectivity_checked, struct ref *ref_map)
1080
+ int connectivity_checked, struct ref *ref_map,
1081
+ struct worktree **worktrees)
1082
{
1083
struct fetch_head fetch_head;
1084
int url_len, i, rc = 0;
@@ -1205,7 +1207,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
1207
strbuf_reset(¬e);
1208
if (ref) {
1209
rc |= update_local_ref(ref, transaction, what,
1208
- rm, ¬e, summary_width);
1210
+ rm, ¬e, summary_width,
1211
+ worktrees);
1212
free(ref);
1213
} else if (write_fetch_head || dry_run) {
1214
/*
@@ -1298,7 +1301,9 @@ static int check_exist_and_connected(struct ref *ref_map)
1301
return check_connected(iterate_ref_map, &rm, &opt);
1302
}
1303
1301
-static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_map)
1304
+static int fetch_and_consume_refs(struct transport *transport,
1305
+ struct ref *ref_map,
1306
+ struct worktree **worktrees)
1307
{
1308
int connectivity_checked = 1;
1309
int ret;
@@ -1319,10 +1324,8 @@ static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_m
1324
}
1325
1326
trace2_region_enter("fetch", "consume_refs", the_repository);
1322
- ret = store_updated_refs(transport->url,
1323
- transport->remote->name,
1324
- connectivity_checked,
1325
- ref_map);
1327
+ ret = store_updated_refs(transport->url, transport->remote->name,
1328
+ connectivity_checked, ref_map, worktrees);
1329
trace2_region_leave("fetch", "consume_refs", the_repository);
1330
1331
out:
@@ -1385,19 +1388,18 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
1388
return result;
1389
}
1390
1388
-static void check_not_current_branch(struct ref *ref_map)
1391
+static void check_not_current_branch(struct ref *ref_map,
1392
+ struct worktree **worktrees)
1393
{
1390
- struct branch *current_branch = branch_get(NULL);
1391
-
1392
- if (is_bare_repository() || !current_branch)
1393
- return;
1394
-
1394
+ const struct worktree *wt;
1395
for (; ref_map; ref_map = ref_map->next)
1396
- if (ref_map->peer_ref && !strcmp(current_branch->refname,
1397
- ref_map->peer_ref->name))
1398
- die(_("refusing to fetch into current branch %s "
1399
- "of non-bare repository"),
1400
- current_branch->refname);
1396
+ if (ref_map->peer_ref &&
1397
+ (wt = find_shared_symref(worktrees, "HEAD",
1398
+ ref_map->peer_ref->name)) &&
1399
+ !wt->is_bare)
1400
+ die(_("refusing to fetch into branch '%s' "
1401
+ "checked out at '%s'"),
1402
+ ref_map->peer_ref->name, wt->path);
1403
}
1404
1405
static int truncate_fetch_head(void)
@@ -1495,7 +1497,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
1497
return transport;
1498
}
1499
1498
-static void backfill_tags(struct transport *transport, struct ref *ref_map)
1500
+static void backfill_tags(struct transport *transport, struct ref *ref_map,
1501
+ struct worktree **worktrees)
1502
{
1503
int cannot_reuse;
1504
@@ -1516,7 +1519,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
1519
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
1520
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
1521
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
1519
- fetch_and_consume_refs(transport, ref_map);
1522
+ fetch_and_consume_refs(transport, ref_map, worktrees);
1523
1524
if (gsecondary) {
1525
transport_disconnect(gsecondary);
@@ -1534,6 +1537,7 @@ static int do_fetch(struct transport *transport,
1537
struct transport_ls_refs_options transport_ls_refs_options =
1538
TRANSPORT_LS_REFS_OPTIONS_INIT;
1539
int must_list_refs = 1;
1540
+ struct worktree **worktrees = get_worktrees();
1541
1542
if (tags == TAGS_DEFAULT) {
1543
if (transport->remote->fetch_tags == 2)
@@ -1589,7 +1593,7 @@ static int do_fetch(struct transport *transport,
1593
ref_map = get_ref_map(transport->remote, remote_refs, rs,
1594
tags, &autotags);
1595
if (!update_head_ok)
1592
- check_not_current_branch(ref_map);
1596
+ check_not_current_branch(ref_map, worktrees);
1597
1598
if (tags == TAGS_DEFAULT && autotags)
1599
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
@@ -1607,7 +1611,7 @@ static int do_fetch(struct transport *transport,
1611
transport->url);
1612
}
1613
}
1610
- if (fetch_and_consume_refs(transport, ref_map)) {
1614
+ if (fetch_and_consume_refs(transport, ref_map, worktrees)) {
1615
free_refs(ref_map);
1616
retcode = 1;
1617
goto cleanup;
@@ -1656,7 +1660,7 @@ static int do_fetch(struct transport *transport,
1660
"you need to specify exactly one branch with the --set-upstream option"));
1661
}
1662
}
1659
- skip:
1663
+skip:
1664
free_refs(ref_map);
1665
1666
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -1666,11 +1670,12 @@ static int do_fetch(struct transport *transport,
1670
ref_map = NULL;
1671
find_non_local_tags(remote_refs, &ref_map, &tail);
1672
if (ref_map)
1669
- backfill_tags(transport, ref_map);
1673
+ backfill_tags(transport, ref_map, worktrees);
1674
free_refs(ref_map);
1675
}
1676
1673
- cleanup:
1677
+cleanup:
1678
+ free_worktrees(worktrees);
1679
return retcode;
1680
}
1681
t/t5516-fetch-push.sh
+18
@@ -1771,4 +1771,22 @@ test_expect_success 'denyCurrentBranch and worktrees' '
1771
git -C cloned push origin HEAD:new-wt &&
1772
test_must_fail git -C cloned push --delete origin new-wt
1773
'
1774
+
1775
+test_expect_success 'refuse fetch to current branch of worktree' '
1776
+ test_when_finished "git worktree remove --force wt && git branch -D wt" &&
1777
+ git worktree add wt &&
1778
+ test_commit apple &&
1779
+ test_must_fail git fetch . HEAD:wt &&
1780
+ git fetch -u . HEAD:wt
1781
+'
1782
+
1783
+test_expect_success 'refuse fetch to current branch of bare repository worktree' '
1784
+ test_when_finished "rm -fr bare.git" &&
1785
+ git clone --bare . bare.git &&
1786
+ git -C bare.git worktree add wt &&
1787
+ test_commit banana &&
1788
+ test_must_fail git -C bare.git fetch .. HEAD:wt &&
1789
+ git -C bare.git fetch -u .. HEAD:wt
1790
+'
1791
+
1792
test_done