for_each_abbrev: drop duplicate objects

If an object appears multiple times in the object database (e.g., in both loose and packed form, or in two separate packs), the disambiguation machinery may see it more than once. The get_short_sha1() function handles this already, but for_each_abbrev() blindly fires the callback for each instance it finds. We can fix this by collecting the output in a sha1 array and de-duplicating it. As a bonus, the sort done for the de-duplication means that our output will be stable, regardless of the order in which the objects are found. Note that the old code normalized the callback's output to 0/1 to store in the 1-bit ds->ambiguous flag (which both halted the iteration and was returned from the for_each_abbrev function). Now that we are using sha1_array, we can return the real value. In practice, it doesn't matter as the sole caller only ever returns 0. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Sep 26, 2016 at 08:00 UTC fad6b9e5905f00654f394cac4093a052b7a3cfb6
2 files changed +22 -4
sha1_name.c
+15 -4
@@ -7,6 +7,7 @@
7 #include "refs.h"
8 #include "remote.h"
9 #include "dir.h"
10 +#include "sha1-array.h"
11
12 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
13
@@ -350,20 +351,30 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
351 return status;
352 }
353
354 +static int collect_ambiguous(const unsigned char *sha1, void *data)
355 +{
356 + sha1_array_append(data, sha1);
357 + return 0;
358 +}
359 +
360 int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
361 {
362 + struct sha1_array collect = SHA1_ARRAY_INIT;
363 struct disambiguate_state ds;
364 + int ret;
365
366 if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
367 return -1;
368
369 ds.always_call_fn = 1;
361 - ds.cb_data = cb_data;
362 - ds.fn = fn;
363 -
370 + ds.fn = collect_ambiguous;
371 + ds.cb_data = &collect;
372 find_short_object_filename(&ds);
373 find_short_packed_object(&ds);
366 - return ds.ambiguous;
374 +
375 + ret = sha1_array_for_each_unique(&collect, fn, cb_data);
376 + sha1_array_clear(&collect);
377 + return ret;
378 }
379
380 int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
t/t1512-rev-parse-disambiguation.sh
+7
@@ -280,6 +280,13 @@ test_expect_success 'rev-parse --disambiguate' '
280 test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
281 '
282
283 +test_expect_success 'rev-parse --disambiguate drops duplicates' '
284 + git rev-parse --disambiguate=000000000 >expect &&
285 + git pack-objects .git/objects/pack/pack <expect &&
286 + git rev-parse --disambiguate=000000000 >actual &&
287 + test_cmp expect actual
288 +'
289 +
290 test_expect_success 'ambiguous 40-hex ref' '
291 TREE=$(git mktree </dev/null) &&
292 REF=$(git rev-parse HEAD) &&