Skip to content

Commit 624cac3

Browse files
mhaggergitster
authored andcommitted
refs: change the internal reference-iteration API
Establish an internal API for iterating over references, which gives the callback functions direct access to the ref_entry structure describing the reference. (Do not change the iteration API that is exposed outside of the module.) Define a new internal callback signature int each_ref_entry_fn(struct ref_entry *entry, void *cb_data) Change do_for_each_ref_in_dir() and do_for_each_ref_in_dirs() to accept each_ref_entry_fn callbacks, and rename them to do_for_each_entry_in_dir() and do_for_each_entry_in_dirs(), respectively. Adapt their callers accordingly. Add a new function do_for_each_entry() analogous to do_for_each_ref() but using the new callback style. Change do_one_ref() into an each_ref_entry_fn that does some bookkeeping and then calls a wrapped each_ref_fn. Reimplement do_for_each_ref() in terms of do_for_each_entry(), using do_one_ref() as an adapter. Please note that the responsibility for setting current_ref remains in do_one_ref(), which means that current_ref is *not* set when iterating over references via the new internal API. This is not a disadvantage, because current_ref is not needed by callers of the internal API (they receive a pointer to the current ref_entry anyway). But more importantly, this change prevents peel_ref() from returning invalid results in the following scenario: When iterating via the external API, the iteration always includes both packed and loose references, and in particular never presents a packed ref if there is a loose ref with the same name. The internal API, on the other hand, gives the option to iterate over only the packed references. During such an iteration, there is no check whether the packed ref might be hidden by a loose ref of the same name. But until now the packed ref was recorded in current_ref during the iteration. So if peel_ref() were called with the reference name corresponding to current ref, it would return the peeled version of the packed ref even though there might be a loose ref that peels to a different value. This scenario doesn't currently occur in the code, but fix it to prevent things from breaking in a very confusing way in the future. Signed-off-by: Michael Haggerty <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9a489f3 commit 624cac3

File tree

1 file changed

+83
-61
lines changed

1 file changed

+83
-61
lines changed

refs.c

Lines changed: 83 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -557,22 +557,34 @@ static int ref_resolves_to_object(struct ref_entry *entry)
557557
*/
558558
static struct ref_entry *current_ref;
559559

560+
typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
561+
562+
struct ref_entry_cb {
563+
const char *base;
564+
int trim;
565+
int flags;
566+
each_ref_fn *fn;
567+
void *cb_data;
568+
};
569+
560570
/*
561-
* Handle one reference in a do_for_each_ref*()-style iteration.
571+
* Handle one reference in a do_for_each_ref*()-style iteration,
572+
* calling an each_ref_fn for each entry.
562573
*/
563-
static int do_one_ref(const char *base, each_ref_fn fn, int trim,
564-
int flags, void *cb_data, struct ref_entry *entry)
574+
static int do_one_ref(struct ref_entry *entry, void *cb_data)
565575
{
576+
struct ref_entry_cb *data = cb_data;
566577
int retval;
567-
if (prefixcmp(entry->name, base))
578+
if (prefixcmp(entry->name, data->base))
568579
return 0;
569580

570-
if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
581+
if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
571582
!ref_resolves_to_object(entry))
572583
return 0;
573584

574585
current_ref = entry;
575-
retval = fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);
586+
retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
587+
entry->flag, data->cb_data);
576588
current_ref = NULL;
577589
return retval;
578590
}
@@ -581,11 +593,11 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
581593
* Call fn for each reference in dir that has index in the range
582594
* offset <= index < dir->nr. Recurse into subdirectories that are in
583595
* that index range, sorting them before iterating. This function
584-
* does not sort dir itself; it should be sorted beforehand.
596+
* does not sort dir itself; it should be sorted beforehand. fn is
597+
* called for all references, including broken ones.
585598
*/
586-
static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
587-
const char *base,
588-
each_ref_fn fn, int trim, int flags, void *cb_data)
599+
static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
600+
each_ref_entry_fn fn, void *cb_data)
589601
{
590602
int i;
591603
assert(dir->sorted == dir->nr);
@@ -595,10 +607,9 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
595607
if (entry->flag & REF_DIR) {
596608
struct ref_dir *subdir = get_ref_dir(entry);
597609
sort_ref_dir(subdir);
598-
retval = do_for_each_ref_in_dir(subdir, 0,
599-
base, fn, trim, flags, cb_data);
610+
retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
600611
} else {
601-
retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
612+
retval = fn(entry, cb_data);
602613
}
603614
if (retval)
604615
return retval;
@@ -611,12 +622,12 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
611622
* by refname. Recurse into subdirectories. If a value entry appears
612623
* in both dir1 and dir2, then only process the version that is in
613624
* dir2. The input dirs must already be sorted, but subdirs will be
614-
* sorted as needed.
625+
* sorted as needed. fn is called for all references, including
626+
* broken ones.
615627
*/
616-
static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
617-
struct ref_dir *dir2,
618-
const char *base, each_ref_fn fn, int trim,
619-
int flags, void *cb_data)
628+
static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
629+
struct ref_dir *dir2,
630+
each_ref_entry_fn fn, void *cb_data)
620631
{
621632
int retval;
622633
int i1 = 0, i2 = 0;
@@ -627,12 +638,10 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
627638
struct ref_entry *e1, *e2;
628639
int cmp;
629640
if (i1 == dir1->nr) {
630-
return do_for_each_ref_in_dir(dir2, i2,
631-
base, fn, trim, flags, cb_data);
641+
return do_for_each_entry_in_dir(dir2, i2, fn, cb_data);
632642
}
633643
if (i2 == dir2->nr) {
634-
return do_for_each_ref_in_dir(dir1, i1,
635-
base, fn, trim, flags, cb_data);
644+
return do_for_each_entry_in_dir(dir1, i1, fn, cb_data);
636645
}
637646
e1 = dir1->entries[i1];
638647
e2 = dir2->entries[i2];
@@ -644,14 +653,13 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
644653
struct ref_dir *subdir2 = get_ref_dir(e2);
645654
sort_ref_dir(subdir1);
646655
sort_ref_dir(subdir2);
647-
retval = do_for_each_ref_in_dirs(
648-
subdir1, subdir2,
649-
base, fn, trim, flags, cb_data);
656+
retval = do_for_each_entry_in_dirs(
657+
subdir1, subdir2, fn, cb_data);
650658
i1++;
651659
i2++;
652660
} else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
653661
/* Both are references; ignore the one from dir1. */
654-
retval = do_one_ref(base, fn, trim, flags, cb_data, e2);
662+
retval = fn(e2, cb_data);
655663
i1++;
656664
i2++;
657665
} else {
@@ -670,11 +678,10 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
670678
if (e->flag & REF_DIR) {
671679
struct ref_dir *subdir = get_ref_dir(e);
672680
sort_ref_dir(subdir);
673-
retval = do_for_each_ref_in_dir(
674-
subdir, 0,
675-
base, fn, trim, flags, cb_data);
681+
retval = do_for_each_entry_in_dir(
682+
subdir, 0, fn, cb_data);
676683
} else {
677-
retval = do_one_ref(base, fn, trim, flags, cb_data, e);
684+
retval = fn(e, cb_data);
678685
}
679686
}
680687
if (retval)
@@ -703,22 +710,21 @@ struct name_conflict_cb {
703710
const char *conflicting_refname;
704711
};
705712

706-
static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
707-
int flags, void *cb_data)
713+
static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
708714
{
709715
struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
710-
if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
716+
if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
711717
return 0;
712-
if (names_conflict(data->refname, existingrefname)) {
713-
data->conflicting_refname = existingrefname;
718+
if (names_conflict(data->refname, entry->name)) {
719+
data->conflicting_refname = entry->name;
714720
return 1;
715721
}
716722
return 0;
717723
}
718724

719725
/*
720726
* Return true iff a reference named refname could be created without
721-
* conflicting with the name of an existing reference in array. If
727+
* conflicting with the name of an existing reference in dir. If
722728
* oldrefname is non-NULL, ignore potential conflicts with oldrefname
723729
* (e.g., because oldrefname is scheduled for deletion in the same
724730
* operation).
@@ -732,9 +738,7 @@ static int is_refname_available(const char *refname, const char *oldrefname,
732738
data.conflicting_refname = NULL;
733739

734740
sort_ref_dir(dir);
735-
if (do_for_each_ref_in_dir(dir, 0, "", name_conflict_fn,
736-
0, DO_FOR_EACH_INCLUDE_BROKEN,
737-
&data)) {
741+
if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
738742
error("'%s' exists; cannot create '%s'",
739743
data.conflicting_refname, refname);
740744
return 0;
@@ -1422,16 +1426,14 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
14221426
}
14231427

14241428
/*
1425-
* Call fn for each reference in the specified submodule for which the
1426-
* refname begins with base. If trim is non-zero, then trim that many
1427-
* characters off the beginning of each refname before passing the
1428-
* refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to include
1429-
* broken references in the iteration. If fn ever returns a non-zero
1429+
* Call fn for each reference in the specified submodule, omitting
1430+
* references not in the containing_dir of base. fn is called for all
1431+
* references, including broken ones. If fn ever returns a non-zero
14301432
* value, stop the iteration and return that value; otherwise, return
14311433
* 0.
14321434
*/
1433-
static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
1434-
int trim, int flags, void *cb_data)
1435+
static int do_for_each_entry(const char *submodule, const char *base,
1436+
each_ref_entry_fn fn, void *cb_data)
14351437
{
14361438
struct ref_cache *refs = get_ref_cache(submodule);
14371439
struct ref_dir *packed_dir = get_packed_refs(refs);
@@ -1446,24 +1448,43 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn
14461448
if (packed_dir && loose_dir) {
14471449
sort_ref_dir(packed_dir);
14481450
sort_ref_dir(loose_dir);
1449-
retval = do_for_each_ref_in_dirs(
1450-
packed_dir, loose_dir,
1451-
base, fn, trim, flags, cb_data);
1451+
retval = do_for_each_entry_in_dirs(
1452+
packed_dir, loose_dir, fn, cb_data);
14521453
} else if (packed_dir) {
14531454
sort_ref_dir(packed_dir);
1454-
retval = do_for_each_ref_in_dir(
1455-
packed_dir, 0,
1456-
base, fn, trim, flags, cb_data);
1455+
retval = do_for_each_entry_in_dir(
1456+
packed_dir, 0, fn, cb_data);
14571457
} else if (loose_dir) {
14581458
sort_ref_dir(loose_dir);
1459-
retval = do_for_each_ref_in_dir(
1460-
loose_dir, 0,
1461-
base, fn, trim, flags, cb_data);
1459+
retval = do_for_each_entry_in_dir(
1460+
loose_dir, 0, fn, cb_data);
14621461
}
14631462

14641463
return retval;
14651464
}
14661465

1466+
/*
1467+
* Call fn for each reference in the specified submodule for which the
1468+
* refname begins with base. If trim is non-zero, then trim that many
1469+
* characters off the beginning of each refname before passing the
1470+
* refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to include
1471+
* broken references in the iteration. If fn ever returns a non-zero
1472+
* value, stop the iteration and return that value; otherwise, return
1473+
* 0.
1474+
*/
1475+
static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
1476+
int trim, int flags, void *cb_data)
1477+
{
1478+
struct ref_entry_cb data;
1479+
data.base = base;
1480+
data.trim = trim;
1481+
data.flags = flags;
1482+
data.fn = fn;
1483+
data.cb_data = cb_data;
1484+
1485+
return do_for_each_entry(submodule, base, do_one_ref, &data);
1486+
}
1487+
14671488
static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
14681489
{
14691490
unsigned char sha1[20];
@@ -1873,20 +1894,21 @@ struct repack_without_ref_sb {
18731894
int fd;
18741895
};
18751896

1876-
static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,
1877-
int flags, void *cb_data)
1897+
static int repack_without_ref_fn(struct ref_entry *entry, void *cb_data)
18781898
{
18791899
struct repack_without_ref_sb *data = cb_data;
18801900
char line[PATH_MAX + 100];
18811901
int len;
18821902

1883-
if (!strcmp(data->refname, refname))
1903+
if (!strcmp(data->refname, entry->name))
18841904
return 0;
1905+
if (!ref_resolves_to_object(entry))
1906+
return 0; /* Skip broken refs */
18851907
len = snprintf(line, sizeof(line), "%s %s\n",
1886-
sha1_to_hex(sha1), refname);
1908+
sha1_to_hex(entry->u.value.sha1), entry->name);
18871909
/* this should not happen but just being defensive */
18881910
if (len > sizeof(line))
1889-
die("too long a refname '%s'", refname);
1911+
die("too long a refname '%s'", entry->name);
18901912
write_or_die(data->fd, line, len);
18911913
return 0;
18921914
}
@@ -1910,7 +1932,7 @@ static int repack_without_ref(const char *refname)
19101932
}
19111933
clear_packed_ref_cache(refs);
19121934
packed = get_packed_refs(refs);
1913-
do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
1935+
do_for_each_entry_in_dir(packed, 0, repack_without_ref_fn, &data);
19141936
return commit_lock_file(&packlock);
19151937
}
19161938

0 commit comments

Comments
 (0)