Skip to content

Commit 5cc044e

Browse files
avargitster
authored andcommitted
get_short_oid: sort ambiguous objects by type, then SHA-1
Change the output emitted when an ambiguous object is encountered so that we show tags first, then commits, followed by trees, and finally blobs. Within each type we show objects in hashcmp() order. Before this change the objects were only ordered by hashcmp(). The reason for doing this is that the output looks better as a result, e.g. the v2.17.0 tag before this change on "git show e8f2" would display: hint: The candidates are: hint: e8f2093 tree hint: e8f21ca commit 2013-06-24 - bash prompt: print unique detached HEAD abbreviated object name hint: e8f21d0 blob hint: e8f21d5 blob hint: e8f25a3 tree hint: e8f2625 commit 2017-02-03 - Merge pull request #996 from jeffhostetler/jeffhostetler/register_rename_src hint: e8f2650 tag v2.17.0 hint: e8f2867 blob hint: e8f28d5 tree hint: e8f2a35 blob hint: e8f2bc0 commit 2015-05-10 - Documentation: note behavior for multiple remote.url entries hint: e8f2cf6 tree Now we'll instead show: hint: e8f2650 tag v2.17.0 hint: e8f21ca commit 2013-06-24 - bash prompt: print unique detached HEAD abbreviated object name hint: e8f2625 commit 2017-02-03 - Merge pull request #996 from jeffhostetler/jeffhostetler/register_rename_src hint: e8f2bc0 commit 2015-05-10 - Documentation: note behavior for multiple remote.url entries hint: e8f2093 tree hint: e8f25a3 tree hint: e8f28d5 tree hint: e8f2cf6 tree hint: e8f21d0 blob hint: e8f21d5 blob hint: e8f2867 blob hint: e8f2a35 blob Since we show the commit data in the output that's nicely aligned once we sort by object type. The decision to show tags before commits is pretty arbitrary. I don't want to order by object_type since there tags come last after blobs, which doesn't make sense if we want to show the most important things first. I could display them after commits, but it's much less likely that we'll display a tag, so if there is one it makes sense to show it prominently at the top. A note on the implementation: Derrick rightly pointed out[1] that we're bending over backwards here in get_short_oid() to first de-duplicate the list, and then emit it, but could simply do it in one step. The reason for that is that oid_array_for_each_unique() doesn't actually require that the array be sorted by oid_array_sort(), it just needs to be sorted in some order that guarantees that all objects with the same ID are adjacent to one another, which (barring a hash collision, which'll be someone else's problem) the sort_ambiguous() function does. I agree that would be simpler for this code, and had forgotten why I initially wrote it like this[2]. But on further reflection I think it's better to do more work here just so we're not underhandedly using the oid-array API where we lie about the list being sorted. That would break any subsequent use of oid_array_lookup() in subtle ways. I could get around that by hacking the API itself to support this use-case and documenting it, which I did as a WIP patch in [3], but I think it's too much code smell just for this one call site. It's simpler for the API to just introduce a oid_array_for_each() function to eagerly spew out the list without sorting or de-duplication, and then do the de-duplication and sorting in two passes. 1. https://public-inbox.org/git/[email protected]/ 2. https://public-inbox.org/git/[email protected]/ 3. https://public-inbox.org/git/[email protected]/ Helped-by: Derrick Stolee <[email protected]> Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a885c93 commit 5cc044e

File tree

5 files changed

+88
-7
lines changed

5 files changed

+88
-7
lines changed

Documentation/technical/api-oid-array.txt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,18 @@ Functions
3535
Free all memory associated with the array and return it to the
3636
initial, empty state.
3737

38+
`oid_array_for_each`::
39+
Iterate over each element of the list, executing the callback
40+
function for each one. Does not sort the list, so any custom
41+
hash order is retained. If the callback returns a non-zero
42+
value, the iteration ends immediately and the callback's
43+
return is propagated; otherwise, 0 is returned.
44+
3845
`oid_array_for_each_unique`::
39-
Efficiently iterate over each unique element of the list,
40-
executing the callback function for each one. If the array is
41-
not sorted, this function has the side effect of sorting it. If
42-
the callback returns a non-zero value, the iteration ends
43-
immediately and the callback's return is propagated; otherwise,
44-
0 is returned.
46+
Iterate over each unique element of the list in sorted order,
47+
but otherwise behave like `oid_array_for_each`. If the array
48+
is not sorted, this function has the side effect of sorting
49+
it.
4550

4651
Examples
4752
--------

sha1-array.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ void oid_array_clear(struct oid_array *array)
4141
array->sorted = 0;
4242
}
4343

44+
45+
int oid_array_for_each(struct oid_array *array,
46+
for_each_oid_fn fn,
47+
void *data)
48+
{
49+
int i;
50+
51+
/* No oid_array_sort() here! See the api-oid-array.txt docs! */
52+
53+
for (i = 0; i < array->nr; i++) {
54+
int ret = fn(array->oid + i, data);
55+
if (ret)
56+
return ret;
57+
}
58+
return 0;
59+
}
60+
4461
int oid_array_for_each_unique(struct oid_array *array,
4562
for_each_oid_fn fn,
4663
void *data)

sha1-array.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ void oid_array_clear(struct oid_array *array);
1616

1717
typedef int (*for_each_oid_fn)(const struct object_id *oid,
1818
void *data);
19+
int oid_array_for_each(struct oid_array *array,
20+
for_each_oid_fn fn,
21+
void *data);
1922
int oid_array_for_each_unique(struct oid_array *array,
2023
for_each_oid_fn fn,
2124
void *data);

sha1-name.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,34 @@ static int collect_ambiguous(const struct object_id *oid, void *data)
378378
return 0;
379379
}
380380

381+
static int sort_ambiguous(const void *a, const void *b)
382+
{
383+
int a_type = oid_object_info(a, NULL);
384+
int b_type = oid_object_info(b, NULL);
385+
int a_type_sort;
386+
int b_type_sort;
387+
388+
/*
389+
* Sorts by hash within the same object type, just as
390+
* oid_array_for_each_unique() would do.
391+
*/
392+
if (a_type == b_type)
393+
return oidcmp(a, b);
394+
395+
/*
396+
* Between object types show tags, then commits, and finally
397+
* trees and blobs.
398+
*
399+
* The object_type enum is commit, tree, blob, tag, but we
400+
* want tag, commit, tree blob. Cleverly (perhaps too
401+
* cleverly) do that with modulus, since the enum assigns 1 to
402+
* commit, so tag becomes 0.
403+
*/
404+
a_type_sort = a_type % 4;
405+
b_type_sort = b_type % 4;
406+
return a_type_sort > b_type_sort ? 1 : -1;
407+
}
408+
381409
static int get_short_oid(const char *name, int len, struct object_id *oid,
382410
unsigned flags)
383411
{
@@ -409,6 +437,8 @@ static int get_short_oid(const char *name, int len, struct object_id *oid,
409437
status = finish_object_disambiguation(&ds, oid);
410438

411439
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
440+
struct oid_array collect = OID_ARRAY_INIT;
441+
412442
error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
413443

414444
/*
@@ -421,7 +451,12 @@ static int get_short_oid(const char *name, int len, struct object_id *oid,
421451
ds.fn = NULL;
422452

423453
advise(_("The candidates are:"));
424-
for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds);
454+
for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect);
455+
QSORT(collect.oid, collect.nr, sort_ambiguous);
456+
457+
if (oid_array_for_each(&collect, show_ambiguous_object, &ds))
458+
BUG("show_ambiguous_object shouldn't return non-zero");
459+
oid_array_clear(&collect);
425460
}
426461

427462
return status;

t/t1512-rev-parse-disambiguation.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,25 @@ test_expect_success 'core.disambiguate does not override context' '
361361
git -c core.disambiguate=committish rev-parse $sha1^{tree}
362362
'
363363

364+
test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' '
365+
test_must_fail git rev-parse 0000 2>stderr &&
366+
grep ^hint: stderr >hints &&
367+
grep 0000 hints >objects &&
368+
cat >expected <<-\EOF &&
369+
tag
370+
commit
371+
tree
372+
blob
373+
EOF
374+
awk "{print \$3}" <objects >objects.types &&
375+
uniq <objects.types >objects.types.uniq &&
376+
test_cmp expected objects.types.uniq &&
377+
for type in tag commit tree blob
378+
do
379+
grep $type objects >$type.objects &&
380+
sort $type.objects >$type.objects.sorted &&
381+
test_cmp $type.objects.sorted $type.objects
382+
done
383+
'
384+
364385
test_done

0 commit comments

Comments
 (0)