Skip to content

Commit a78fafe

Browse files
committed
sha1_name.c: restructure disambiguation of short names
We try to find zero, one or more matches from loose objects and packed objects independently and then decide if the given short object name is unique across them. Instead, introduce a "struct disambiguate_state" that keeps track of what we have found so far, that can be one of: - We have seen one object that _could_ be what we are looking for; - We have also checked that object for additional constraints (if any), and found that the object satisfies it; - We have also checked that object for additional constraints (if any), and found that the object does not satisfy it; or - We have seen more than one objects that satisfy the constraints. and pass it to the enumeration functions for loose and packed objects. The disambiguation state can optionally take a callback function that takes a candidate object name and reports if the object satisifies additional criteria (e.g. when the caller knows that the short name must refer to a commit, this mechanism can be used to check the type of the given object). Compared to the earlier attempt, this round avoids the optional check if there is only one candidate that matches the short name in the first place. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1703f9a commit a78fafe

File tree

1 file changed

+112
-60
lines changed

1 file changed

+112
-60
lines changed

sha1_name.c

Lines changed: 112 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,67 @@
99

1010
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
1111

12-
static int find_short_object_filename(int len, const char *hex_pfx, unsigned char *sha1)
12+
typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
13+
14+
struct disambiguate_state {
15+
disambiguate_hint_fn fn;
16+
void *cb_data;
17+
unsigned char candidate[20];
18+
unsigned candidate_exists:1;
19+
unsigned candidate_checked:1;
20+
unsigned candidate_ok:1;
21+
unsigned disambiguate_fn_used:1;
22+
unsigned ambiguous:1;
23+
};
24+
25+
static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
26+
{
27+
if (!ds->candidate_exists) {
28+
/* this is the first candidate */
29+
hashcpy(ds->candidate, current);
30+
ds->candidate_exists = 1;
31+
return;
32+
} else if (!hashcmp(ds->candidate, current)) {
33+
/* the same as what we already have seen */
34+
return;
35+
}
36+
37+
if (!ds->fn) {
38+
/* cannot disambiguate between ds->candidate and current */
39+
ds->ambiguous = 1;
40+
return;
41+
}
42+
43+
if (!ds->candidate_checked) {
44+
ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
45+
ds->disambiguate_fn_used = 1;
46+
ds->candidate_checked = 1;
47+
}
48+
49+
if (!ds->candidate_ok) {
50+
/* discard the candidate; we know it does not satisify fn */
51+
hashcpy(ds->candidate, current);
52+
ds->candidate_checked = 0;
53+
return;
54+
}
55+
56+
/* if we reach this point, we know ds->candidate satisfies fn */
57+
if (ds->fn(current, ds->cb_data)) {
58+
/*
59+
* if both current and candidate satisfy fn, we cannot
60+
* disambiguate.
61+
*/
62+
ds->candidate_ok = 0;
63+
ds->ambiguous = 1;
64+
}
65+
66+
/* otherwise, current can be discarded and candidate is still good */
67+
}
68+
69+
static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
1370
{
1471
struct alternate_object_database *alt;
1572
char hex[40];
16-
int found = 0;
1773
static struct alternate_object_database *fakeent;
1874

1975
if (!fakeent) {
@@ -35,32 +91,27 @@ static int find_short_object_filename(int len, const char *hex_pfx, unsigned cha
3591
fakeent->next = alt_odb_list;
3692

3793
sprintf(hex, "%.2s", hex_pfx);
38-
for (alt = fakeent; alt && found < 2; alt = alt->next) {
94+
for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
3995
struct dirent *de;
4096
DIR *dir;
4197
sprintf(alt->name, "%.2s/", hex_pfx);
4298
dir = opendir(alt->base);
4399
if (!dir)
44100
continue;
45-
while ((de = readdir(dir)) != NULL) {
101+
102+
while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
103+
unsigned char sha1[20];
104+
46105
if (strlen(de->d_name) != 38)
47106
continue;
48107
if (memcmp(de->d_name, hex_pfx + 2, len - 2))
49108
continue;
50-
if (!found) {
51-
memcpy(hex + 2, de->d_name, 38);
52-
found++;
53-
}
54-
else if (memcmp(hex + 2, de->d_name, 38)) {
55-
found = 2;
56-
break;
57-
}
109+
memcpy(hex + 2, de->d_name, 38);
110+
if (!get_sha1_hex(hex, sha1))
111+
update_candidates(ds, sha1);
58112
}
59113
closedir(dir);
60114
}
61-
if (found == 1)
62-
return get_sha1_hex(hex, sha1) == 0;
63-
return found;
64115
}
65116

66117
static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
@@ -78,11 +129,10 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char *
78129
return 1;
79130
}
80131

81-
static int unique_in_pack(int len,
132+
static void unique_in_pack(int len,
82133
const unsigned char *bin_pfx,
83-
struct packed_git *p,
84-
const unsigned char **found_sha1,
85-
int seen_so_far)
134+
struct packed_git *p,
135+
struct disambiguate_state *ds)
86136
{
87137
uint32_t num, last, i, first = 0;
88138
const unsigned char *current = NULL;
@@ -113,63 +163,58 @@ static int unique_in_pack(int len,
113163
* with an object name that could match "bin_pfx". See if we have
114164
* 0, 1 or more objects that actually match(es).
115165
*/
116-
for (i = first; i < num; i++) {
117-
current = nth_packed_object_sha1(p, first);
166+
for (i = first; i < num && !ds->ambiguous; i++) {
167+
current = nth_packed_object_sha1(p, i);
118168
if (!match_sha(len, bin_pfx, current))
119169
break;
120-
121-
/* current matches */
122-
if (!seen_so_far) {
123-
*found_sha1 = current;
124-
seen_so_far++;
125-
} else if (seen_so_far) {
126-
/* is it the same as the one previously found elsewhere? */
127-
if (hashcmp(*found_sha1, current))
128-
return 2; /* definitely not unique */
129-
}
170+
update_candidates(ds, current);
130171
}
131-
return seen_so_far;
132172
}
133173

134-
static int find_short_packed_object(int len, const unsigned char *bin_pfx, unsigned char *sha1)
174+
static void find_short_packed_object(int len, const unsigned char *bin_pfx,
175+
struct disambiguate_state *ds)
135176
{
136177
struct packed_git *p;
137-
const unsigned char *found_sha1 = NULL;
138-
int found = 0;
139178

140179
prepare_packed_git();
141-
for (p = packed_git; p && found < 2; p = p->next)
142-
found = unique_in_pack(len, bin_pfx, p, &found_sha1, found);
143-
144-
if (found == 1)
145-
hashcpy(sha1, found_sha1);
146-
return found;
180+
for (p = packed_git; p && !ds->ambiguous; p = p->next)
181+
unique_in_pack(len, bin_pfx, p, ds);
147182
}
148183

149184
#define SHORT_NAME_NOT_FOUND (-1)
150185
#define SHORT_NAME_AMBIGUOUS (-2)
151186

152-
static int find_unique_short_object(int len, char *hex_pfx,
153-
unsigned char *bin_pfx, unsigned char *sha1)
187+
static int finish_object_disambiguation(struct disambiguate_state *ds,
188+
unsigned char *sha1)
154189
{
155-
int has_unpacked, has_packed;
156-
unsigned char unpacked_sha1[20], packed_sha1[20];
190+
if (ds->ambiguous)
191+
return SHORT_NAME_AMBIGUOUS;
157192

158-
prepare_alt_odb();
159-
has_unpacked = find_short_object_filename(len, hex_pfx, unpacked_sha1);
160-
has_packed = find_short_packed_object(len, bin_pfx, packed_sha1);
161-
if (!has_unpacked && !has_packed)
193+
if (!ds->candidate_exists)
162194
return SHORT_NAME_NOT_FOUND;
163-
if (1 < has_unpacked || 1 < has_packed)
164-
return SHORT_NAME_AMBIGUOUS;
165-
if (has_unpacked != has_packed) {
166-
hashcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1));
167-
return 0;
168-
}
169-
/* Both have unique ones -- do they match? */
170-
if (hashcmp(packed_sha1, unpacked_sha1))
171-
return SHORT_NAME_AMBIGUOUS;
172-
hashcpy(sha1, packed_sha1);
195+
196+
if (!ds->candidate_checked)
197+
/*
198+
* If this is the only candidate, there is no point
199+
* calling the disambiguation hint callback.
200+
*
201+
* On the other hand, if the current candidate
202+
* replaced an earlier candidate that did _not_ pass
203+
* the disambiguation hint callback, then we do have
204+
* more than one objects that match the short name
205+
* given, so we should make sure this one matches;
206+
* otherwise, if we discovered this one and the one
207+
* that we previously discarded in the reverse order,
208+
* we would end up showing different results in the
209+
* same repository!
210+
*/
211+
ds->candidate_ok = (!ds->disambiguate_fn_used ||
212+
ds->fn(ds->candidate, ds->cb_data));
213+
214+
if (!ds->candidate_ok)
215+
return SHORT_NAME_NOT_FOUND;
216+
217+
hashcpy(sha1, ds->candidate);
173218
return 0;
174219
}
175220

@@ -179,6 +224,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
179224
int i, status;
180225
char hex_pfx[40];
181226
unsigned char bin_pfx[20];
227+
struct disambiguate_state ds;
182228

183229
if (len < MINIMUM_ABBREV || len > 40)
184230
return -1;
@@ -203,7 +249,13 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
203249
bin_pfx[i >> 1] |= val;
204250
}
205251

206-
status = find_unique_short_object(i, hex_pfx, bin_pfx, sha1);
252+
prepare_alt_odb();
253+
254+
memset(&ds, 0, sizeof(ds));
255+
find_short_object_filename(len, hex_pfx, &ds);
256+
find_short_packed_object(len, bin_pfx, &ds);
257+
status = finish_object_disambiguation(&ds, sha1);
258+
207259
if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
208260
return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
209261
return status;

0 commit comments

Comments
 (0)