rev-parse --disambiguate=<prefix>

The new option allows you to feed an ambiguous prefix and enumerate all the objects that share it as a prefix of their object names. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 3, 2012 at 14:21 UTC 957d74062c1f0e92368aeda335e27d2d61f584f6
5 files changed +78 -9
Documentation/git-rev-parse.txt
+6
@@ -101,6 +101,12 @@ OPTIONS
101 The option core.warnAmbiguousRefs is used to select the strict
102 abbreviation mode.
103
104 +--disambiguate=<prefix>::
105 + Show every object whose name begins with the given prefix.
106 + The <prefix> must be at least 4 hexadecimal digits long to
107 + avoid listing each and every object in the repository by
108 + mistake.
109 +
110 --all::
111 Show all refs found in `refs/`.
112
builtin/rev-parse.c
+10
@@ -195,6 +195,12 @@ static int anti_reference(const char *refname, const unsigned char *sha1, int fl
195 return 0;
196 }
197
198 +static int show_abbrev(const unsigned char *sha1, void *cb_data)
199 +{
200 + show_rev(NORMAL, sha1, NULL);
201 + return 0;
202 +}
203 +
204 static void show_datestring(const char *flag, const char *datestr)
205 {
206 static char buffer[100];
@@ -589,6 +595,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
595 for_each_ref(show_reference, NULL);
596 continue;
597 }
598 + if (!prefixcmp(arg, "--disambiguate=")) {
599 + for_each_abbrev(arg + 15, show_abbrev, NULL);
600 + continue;
601 + }
602 if (!strcmp(arg, "--bisect")) {
603 for_each_ref_in("refs/bisect/bad", show_reference, NULL);
604 for_each_ref_in("refs/bisect/good", anti_reference, NULL);
cache.h
+3
@@ -828,6 +828,9 @@ extern int get_sha1_blob(const char *str, unsigned char *sha1);
828 extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
829 extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
830
831 +typedef int each_abbrev_fn(const unsigned char *sha1, void *);
832 +extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
833 +
834 /*
835 * Try to read a SHA1 in hexadecimal format from the 40 characters
836 * starting at hex. Write the 20-byte result to sha1 in binary form.
sha1_name.c
+50 -9
@@ -20,10 +20,15 @@ struct disambiguate_state {
20 unsigned candidate_ok:1;
21 unsigned disambiguate_fn_used:1;
22 unsigned ambiguous:1;
23 + unsigned always_call_fn:1;
24 };
25
26 static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
27 {
28 + if (ds->always_call_fn) {
29 + ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
30 + return;
31 + }
32 if (!ds->candidate_exists) {
33 /* this is the first candidate */
34 hashcpy(ds->candidate, current);
@@ -272,17 +277,12 @@ static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unuse
277 return kind == OBJ_BLOB;
278 }
279
275 -static int get_short_sha1(const char *name, int len, unsigned char *sha1,
276 - unsigned flags)
280 +static int prepare_prefixes(const char *name, int len,
281 + unsigned char *bin_pfx,
282 + char *hex_pfx)
283 {
278 - int i, status;
279 - char hex_pfx[40];
280 - unsigned char bin_pfx[20];
281 - struct disambiguate_state ds;
282 - int quietly = !!(flags & GET_SHA1_QUIETLY);
284 + int i;
285
284 - if (len < MINIMUM_ABBREV || len > 40)
285 - return -1;
286 hashclr(bin_pfx);
287 memset(hex_pfx, 'x', 40);
288 for (i = 0; i < len ;i++) {
@@ -303,6 +303,22 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
303 val <<= 4;
304 bin_pfx[i >> 1] |= val;
305 }
306 + return 0;
307 +}
308 +
309 +static int get_short_sha1(const char *name, int len, unsigned char *sha1,
310 + unsigned flags)
311 +{
312 + int status;
313 + char hex_pfx[40];
314 + unsigned char bin_pfx[20];
315 + struct disambiguate_state ds;
316 + int quietly = !!(flags & GET_SHA1_QUIETLY);
317 +
318 + if (len < MINIMUM_ABBREV || len > 40)
319 + return -1;
320 + if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
321 + return -1;
322
323 prepare_alt_odb();
324
@@ -327,6 +343,31 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
343 return status;
344 }
345
346 +
347 +int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
348 +{
349 + char hex_pfx[40];
350 + unsigned char bin_pfx[20];
351 + struct disambiguate_state ds;
352 + int len = strlen(prefix);
353 +
354 + if (len < MINIMUM_ABBREV || len > 40)
355 + return -1;
356 + if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
357 + return -1;
358 +
359 + prepare_alt_odb();
360 +
361 + memset(&ds, 0, sizeof(ds));
362 + ds.always_call_fn = 1;
363 + ds.cb_data = cb_data;
364 + ds.fn = fn;
365 +
366 + find_short_object_filename(len, hex_pfx, &ds);
367 + find_short_packed_object(len, bin_pfx, &ds);
368 + return ds.ambiguous;
369 +}
370 +
371 const char *find_unique_abbrev(const unsigned char *sha1, int len)
372 {
373 int status, exists;
t/t1512-rev-parse-disambiguation.sh
+9
@@ -252,4 +252,13 @@ test_expect_success 'ambiguous commit-ish' '
252 test_must_fail git log 000000000...
253 '
254
255 +test_expect_success 'rev-parse --disambiguate' '
256 + # The test creates 16 objects that share the prefix and two
257 + # commits created by commit-tree in earlier tests do not share
258 + # the prefix.
259 + git rev-parse --disambiguate=000000000 >actual &&
260 + test "$(wc -l <actual)" = 16 &&
261 + test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
262 +'
263 +
264 test_done