Skip to content

Commit c8dd491

Browse files
anderskgitster
authored andcommitted
worktree: simplify find_shared_symref() memory ownership model
Storing the worktrees list in a static variable meant that find_shared_symref() had to rebuild the list on each call (which is inefficient when the call site is in a loop), and also that each call invalidated the pointer returned by the previous call (which is confusing). Instead, make it the caller’s responsibility to pass in the worktrees list and manage its lifetime. Signed-off-by: Anders Kaseorg <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7435e7e commit c8dd491

File tree

6 files changed

+65
-38
lines changed

6 files changed

+65
-38
lines changed

branch.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,16 @@ void remove_branch_state(struct repository *r, int verbose)
357357

358358
void die_if_checked_out(const char *branch, int ignore_current_worktree)
359359
{
360+
struct worktree **worktrees = get_worktrees();
360361
const struct worktree *wt;
361362

362-
wt = find_shared_symref("HEAD", branch);
363-
if (!wt || (ignore_current_worktree && wt->is_current))
364-
return;
365-
skip_prefix(branch, "refs/heads/", &branch);
366-
die(_("'%s' is already checked out at '%s'"),
367-
branch, wt->path);
363+
wt = find_shared_symref(worktrees, "HEAD", branch);
364+
if (wt && (!ignore_current_worktree || !wt->is_current)) {
365+
skip_prefix(branch, "refs/heads/", &branch);
366+
die(_("'%s' is already checked out at '%s'"), branch, wt->path);
367+
}
368+
369+
free_worktrees(worktrees);
368370
}
369371

370372
int replace_each_worktree_head_symref(const char *oldref, const char *newref,

builtin/branch.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ static void delete_branch_config(const char *branchname)
192192
static int delete_branches(int argc, const char **argv, int force, int kinds,
193193
int quiet)
194194
{
195+
struct worktree **worktrees;
195196
struct commit *head_rev = NULL;
196197
struct object_id oid;
197198
char *name = NULL;
@@ -228,6 +229,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
228229
if (!head_rev)
229230
die(_("Couldn't look up commit object for HEAD"));
230231
}
232+
233+
worktrees = get_worktrees();
234+
231235
for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
232236
char *target = NULL;
233237
int flags = 0;
@@ -238,7 +242,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
238242

239243
if (kinds == FILTER_REFS_BRANCHES) {
240244
const struct worktree *wt =
241-
find_shared_symref("HEAD", name);
245+
find_shared_symref(worktrees, "HEAD", name);
242246
if (wt) {
243247
error(_("Cannot delete branch '%s' "
244248
"checked out at '%s'"),
@@ -299,6 +303,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
299303

300304
free(name);
301305
strbuf_release(&bname);
306+
free_worktrees(worktrees);
302307

303308
return ret;
304309
}

builtin/notes.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,15 +861,19 @@ static int merge(int argc, const char **argv, const char *prefix)
861861
update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
862862
UPDATE_REFS_DIE_ON_ERR);
863863
else { /* Merge has unresolved conflicts */
864+
struct worktree **worktrees;
864865
const struct worktree *wt;
865866
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
866867
update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
867868
0, UPDATE_REFS_DIE_ON_ERR);
868869
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
869-
wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
870+
worktrees = get_worktrees();
871+
wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
872+
default_notes_ref());
870873
if (wt)
871874
die(_("a notes merge into %s is already in-progress at %s"),
872875
default_notes_ref(), wt->path);
876+
free_worktrees(worktrees);
873877
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
874878
die(_("failed to store link to current notes ref (%s)"),
875879
default_notes_ref());

builtin/receive-pack.c

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,12 +1486,17 @@ static const char *update(struct command *cmd, struct shallow_info *si)
14861486
struct object_id *old_oid = &cmd->old_oid;
14871487
struct object_id *new_oid = &cmd->new_oid;
14881488
int do_update_worktree = 0;
1489-
const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
1489+
struct worktree **worktrees = get_worktrees();
1490+
const struct worktree *worktree =
1491+
is_bare_repository() ?
1492+
NULL :
1493+
find_shared_symref(worktrees, "HEAD", name);
14901494

14911495
/* only refs/... are allowed */
14921496
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
14931497
rp_error("refusing to create funny ref '%s' remotely", name);
1494-
return "funny refname";
1498+
ret = "funny refname";
1499+
goto out;
14951500
}
14961501

14971502
strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
@@ -1510,7 +1515,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
15101515
rp_error("refusing to update checked out branch: %s", name);
15111516
if (deny_current_branch == DENY_UNCONFIGURED)
15121517
refuse_unconfigured_deny();
1513-
return "branch is currently checked out";
1518+
ret = "branch is currently checked out";
1519+
goto out;
15141520
case DENY_UPDATE_INSTEAD:
15151521
/* pass -- let other checks intervene first */
15161522
do_update_worktree = 1;
@@ -1521,13 +1527,15 @@ static const char *update(struct command *cmd, struct shallow_info *si)
15211527
if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
15221528
error("unpack should have generated %s, "
15231529
"but I can't find it!", oid_to_hex(new_oid));
1524-
return "bad pack";
1530+
ret = "bad pack";
1531+
goto out;
15251532
}
15261533

15271534
if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
15281535
if (deny_deletes && starts_with(name, "refs/heads/")) {
15291536
rp_error("denying ref deletion for %s", name);
1530-
return "deletion prohibited";
1537+
ret = "deletion prohibited";
1538+
goto out;
15311539
}
15321540

15331541
if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
@@ -1543,9 +1551,11 @@ static const char *update(struct command *cmd, struct shallow_info *si)
15431551
if (deny_delete_current == DENY_UNCONFIGURED)
15441552
refuse_unconfigured_deny_delete_current();
15451553
rp_error("refusing to delete the current branch: %s", name);
1546-
return "deletion of the current branch prohibited";
1554+
ret = "deletion of the current branch prohibited";
1555+
goto out;
15471556
default:
1548-
return "Invalid denyDeleteCurrent setting";
1557+
ret = "Invalid denyDeleteCurrent setting";
1558+
goto out;
15491559
}
15501560
}
15511561
}
@@ -1563,25 +1573,30 @@ static const char *update(struct command *cmd, struct shallow_info *si)
15631573
old_object->type != OBJ_COMMIT ||
15641574
new_object->type != OBJ_COMMIT) {
15651575
error("bad sha1 objects for %s", name);
1566-
return "bad ref";
1576+
ret = "bad ref";
1577+
goto out;
15671578
}
15681579
old_commit = (struct commit *)old_object;
15691580
new_commit = (struct commit *)new_object;
15701581
if (!in_merge_bases(old_commit, new_commit)) {
15711582
rp_error("denying non-fast-forward %s"
15721583
" (you should pull first)", name);
1573-
return "non-fast-forward";
1584+
ret = "non-fast-forward";
1585+
goto out;
15741586
}
15751587
}
15761588
if (run_update_hook(cmd)) {
15771589
rp_error("hook declined to update %s", name);
1578-
return "hook declined";
1590+
ret = "hook declined";
1591+
goto out;
15791592
}
15801593

15811594
if (do_update_worktree) {
1582-
ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
1595+
ret = update_worktree(new_oid->hash,
1596+
find_shared_symref(worktrees, "HEAD",
1597+
name));
15831598
if (ret)
1584-
return ret;
1599+
goto out;
15851600
}
15861601

15871602
if (is_null_oid(new_oid)) {
@@ -1600,32 +1615,36 @@ static const char *update(struct command *cmd, struct shallow_info *si)
16001615
old_oid,
16011616
0, "push", &err)) {
16021617
rp_error("%s", err.buf);
1603-
strbuf_release(&err);
1604-
return "failed to delete";
1618+
ret = "failed to delete";
1619+
} else {
1620+
ret = NULL; /* good */
16051621
}
16061622
strbuf_release(&err);
1607-
return NULL; /* good */
16081623
}
16091624
else {
16101625
struct strbuf err = STRBUF_INIT;
16111626
if (shallow_update && si->shallow_ref[cmd->index] &&
1612-
update_shallow_ref(cmd, si))
1613-
return "shallow error";
1627+
update_shallow_ref(cmd, si)) {
1628+
ret = "shallow error";
1629+
goto out;
1630+
}
16141631

16151632
if (ref_transaction_update(transaction,
16161633
namespaced_name,
16171634
new_oid, old_oid,
16181635
0, "push",
16191636
&err)) {
16201637
rp_error("%s", err.buf);
1621-
strbuf_release(&err);
1622-
1623-
return "failed to update ref";
1638+
ret = "failed to update ref";
1639+
} else {
1640+
ret = NULL; /* good */
16241641
}
16251642
strbuf_release(&err);
1626-
1627-
return NULL; /* good */
16281643
}
1644+
1645+
out:
1646+
free_worktrees(worktrees);
1647+
return ret;
16291648
}
16301649

16311650
static void run_update_post_hook(struct command *commands)

worktree.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -404,17 +404,13 @@ int is_worktree_being_bisected(const struct worktree *wt,
404404
* bisect). New commands that do similar things should update this
405405
* function as well.
406406
*/
407-
const struct worktree *find_shared_symref(const char *symref,
407+
const struct worktree *find_shared_symref(struct worktree **worktrees,
408+
const char *symref,
408409
const char *target)
409410
{
410411
const struct worktree *existing = NULL;
411-
static struct worktree **worktrees;
412412
int i = 0;
413413

414-
if (worktrees)
415-
free_worktrees(worktrees);
416-
worktrees = get_worktrees();
417-
418414
for (i = 0; worktrees[i]; i++) {
419415
struct worktree *wt = worktrees[i];
420416
const char *symref_target;

worktree.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,10 @@ void free_worktrees(struct worktree **);
143143
/*
144144
* Check if a per-worktree symref points to a ref in the main worktree
145145
* or any linked worktree, and return the worktree that holds the ref,
146-
* or NULL otherwise. The result may be destroyed by the next call.
146+
* or NULL otherwise.
147147
*/
148-
const struct worktree *find_shared_symref(const char *symref,
148+
const struct worktree *find_shared_symref(struct worktree **worktrees,
149+
const char *symref,
149150
const char *target);
150151

151152
/*

0 commit comments

Comments
 (0)