From a264f229cb01a5f5215ccf04a7a41cbebaeb65c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 10 May 2018 12:42:58 +0000 Subject: [PATCH 1/5] sha1-name.c: remove stray newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This stray newline was accidentally introduced in d2b7d9c7ed ("sha1_name: convert disambiguate_hint_fn to take object_id", 2017-03-26). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- sha1-name.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sha1-name.c b/sha1-name.c index 5b93bf8da3..cd3b133aae 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -346,7 +346,6 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) struct strbuf desc = STRBUF_INIT; int type; - if (ds->fn && !ds->fn(oid, ds->cb_data)) return 0; From 7248672947c8b88605f12f6a53b510bb9d9d4c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 10 May 2018 12:42:59 +0000 Subject: [PATCH 2/5] sha1-array.h: align function arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arguments weren't lined up with the opening parenthesis, after 910650d2 ("Rename sha1_array to oid_array", 2017-03-31) renamed the function. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- sha1-array.c | 4 ++-- sha1-array.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sha1-array.c b/sha1-array.c index 838b3bf847..466a926aa3 100644 --- a/sha1-array.c +++ b/sha1-array.c @@ -42,8 +42,8 @@ void oid_array_clear(struct oid_array *array) } int oid_array_for_each_unique(struct oid_array *array, - for_each_oid_fn fn, - void *data) + for_each_oid_fn fn, + void *data) { int i; diff --git a/sha1-array.h b/sha1-array.h index 04b0756334..1e1d24b009 100644 --- a/sha1-array.h +++ b/sha1-array.h @@ -17,7 +17,7 @@ void oid_array_clear(struct oid_array *array); typedef int (*for_each_oid_fn)(const struct object_id *oid, void *data); int oid_array_for_each_unique(struct oid_array *array, - for_each_oid_fn fn, - void *data); + for_each_oid_fn fn, + void *data); #endif /* SHA1_ARRAY_H */ From 89f32a92b4f04ab93fc52ef16fa628cbb2bddd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 10 May 2018 12:43:00 +0000 Subject: [PATCH 3/5] git-p4: change "commitish" typo to "committish" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was the only occurrence of "commitish" in the tree, but as the log will reveal we've had others in the past. Fixes up code added in 00ad6e3182 ("git-p4: work with a detached head", 2015-11-21). Signed-off-by: Ævar Arnfjörð Bjarmason Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-p4.py b/git-p4.py index 7bb9cadc69..1afa87cd9d 100755 --- a/git-p4.py +++ b/git-p4.py @@ -2099,11 +2099,11 @@ class P4Submit(Command, P4UserMap): commits = [] if self.master: - commitish = self.master + committish = self.master else: - commitish = 'HEAD' + committish = 'HEAD' - for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, commitish)]): + for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, committish)]): commits.append(line.strip()) commits.reverse() From a885c93b5c2975352d7c61b330be04a270c9062f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 10 May 2018 12:43:01 +0000 Subject: [PATCH 4/5] sha1-name.c: move around the collect_ambiguous() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A subsequent change will make use of this static function in the get_short_oid() function, which is defined above where the collect_ambiguous() function is now. Without this we'd then have a compilation error due to a forward declaration. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- sha1-name.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sha1-name.c b/sha1-name.c index cd3b133aae..9d7bbd3e96 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -372,6 +372,12 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) return 0; } +static int collect_ambiguous(const struct object_id *oid, void *data) +{ + oid_array_append(data, oid); + return 0; +} + static int get_short_oid(const char *name, int len, struct object_id *oid, unsigned flags) { @@ -421,12 +427,6 @@ static int get_short_oid(const char *name, int len, struct object_id *oid, return status; } -static int collect_ambiguous(const struct object_id *oid, void *data) -{ - oid_array_append(data, oid); - return 0; -} - int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) { struct oid_array collect = OID_ARRAY_INIT; From 5cc044e02571e93048160d94e64fe6d8dd96597e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 10 May 2018 12:43:02 +0000 Subject: [PATCH 5/5] get_short_oid: sort ambiguous objects by type, then SHA-1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: e8f2093055 tree hint: e8f21caf94 commit 2013-06-24 - bash prompt: print unique detached HEAD abbreviated object name hint: e8f21d02f7 blob hint: e8f21d577c blob hint: e8f25a3a50 tree hint: e8f26250fa commit 2017-02-03 - Merge pull request #996 from jeffhostetler/jeffhostetler/register_rename_src hint: e8f2650052 tag v2.17.0 hint: e8f2867228 blob hint: e8f28d537c tree hint: e8f2a35526 blob hint: e8f2bc0c06 commit 2015-05-10 - Documentation: note behavior for multiple remote.url entries hint: e8f2cf6ec0 tree Now we'll instead show: hint: e8f2650052 tag v2.17.0 hint: e8f21caf94 commit 2013-06-24 - bash prompt: print unique detached HEAD abbreviated object name hint: e8f26250fa commit 2017-02-03 - Merge pull request #996 from jeffhostetler/jeffhostetler/register_rename_src hint: e8f2bc0c06 commit 2015-05-10 - Documentation: note behavior for multiple remote.url entries hint: e8f2093055 tree hint: e8f25a3a50 tree hint: e8f28d537c tree hint: e8f2cf6ec0 tree hint: e8f21d02f7 blob hint: e8f21d577c blob hint: e8f2867228 blob hint: e8f2a35526 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/20180501130318.58251-1-dstolee@microsoft.com/ 2. https://public-inbox.org/git/876047ze9v.fsf@evledraar.gmail.com/ 3. https://public-inbox.org/git/874ljrzctc.fsf@evledraar.gmail.com/ Helped-by: Derrick Stolee Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/technical/api-oid-array.txt | 17 +++++++---- sha1-array.c | 17 +++++++++++ sha1-array.h | 3 ++ sha1-name.c | 37 ++++++++++++++++++++++- t/t1512-rev-parse-disambiguation.sh | 21 +++++++++++++ 5 files changed, 88 insertions(+), 7 deletions(-) diff --git a/Documentation/technical/api-oid-array.txt b/Documentation/technical/api-oid-array.txt index b0c11f868d..9febfb1d52 100644 --- a/Documentation/technical/api-oid-array.txt +++ b/Documentation/technical/api-oid-array.txt @@ -35,13 +35,18 @@ Functions Free all memory associated with the array and return it to the initial, empty state. +`oid_array_for_each`:: + Iterate over each element of the list, executing the callback + function for each one. Does not sort the list, so any custom + hash order is retained. If the callback returns a non-zero + value, the iteration ends immediately and the callback's + return is propagated; otherwise, 0 is returned. + `oid_array_for_each_unique`:: - Efficiently iterate over each unique element of the list, - executing the callback function for each one. If the array is - not sorted, this function has the side effect of sorting it. If - the callback returns a non-zero value, the iteration ends - immediately and the callback's return is propagated; otherwise, - 0 is returned. + Iterate over each unique element of the list in sorted order, + but otherwise behave like `oid_array_for_each`. If the array + is not sorted, this function has the side effect of sorting + it. Examples -------- diff --git a/sha1-array.c b/sha1-array.c index 466a926aa3..265941fbf4 100644 --- a/sha1-array.c +++ b/sha1-array.c @@ -41,6 +41,23 @@ void oid_array_clear(struct oid_array *array) array->sorted = 0; } + +int oid_array_for_each(struct oid_array *array, + for_each_oid_fn fn, + void *data) +{ + int i; + + /* No oid_array_sort() here! See the api-oid-array.txt docs! */ + + for (i = 0; i < array->nr; i++) { + int ret = fn(array->oid + i, data); + if (ret) + return ret; + } + return 0; +} + int oid_array_for_each_unique(struct oid_array *array, for_each_oid_fn fn, void *data) diff --git a/sha1-array.h b/sha1-array.h index 1e1d24b009..232bf95017 100644 --- a/sha1-array.h +++ b/sha1-array.h @@ -16,6 +16,9 @@ void oid_array_clear(struct oid_array *array); typedef int (*for_each_oid_fn)(const struct object_id *oid, void *data); +int oid_array_for_each(struct oid_array *array, + for_each_oid_fn fn, + void *data); int oid_array_for_each_unique(struct oid_array *array, for_each_oid_fn fn, void *data); diff --git a/sha1-name.c b/sha1-name.c index 9d7bbd3e96..46d8b1afa6 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -378,6 +378,34 @@ static int collect_ambiguous(const struct object_id *oid, void *data) return 0; } +static int sort_ambiguous(const void *a, const void *b) +{ + int a_type = oid_object_info(a, NULL); + int b_type = oid_object_info(b, NULL); + int a_type_sort; + int b_type_sort; + + /* + * Sorts by hash within the same object type, just as + * oid_array_for_each_unique() would do. + */ + if (a_type == b_type) + return oidcmp(a, b); + + /* + * Between object types show tags, then commits, and finally + * trees and blobs. + * + * The object_type enum is commit, tree, blob, tag, but we + * want tag, commit, tree blob. Cleverly (perhaps too + * cleverly) do that with modulus, since the enum assigns 1 to + * commit, so tag becomes 0. + */ + a_type_sort = a_type % 4; + b_type_sort = b_type % 4; + return a_type_sort > b_type_sort ? 1 : -1; +} + static int get_short_oid(const char *name, int len, struct object_id *oid, unsigned flags) { @@ -409,6 +437,8 @@ static int get_short_oid(const char *name, int len, struct object_id *oid, status = finish_object_disambiguation(&ds, oid); if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) { + struct oid_array collect = OID_ARRAY_INIT; + error(_("short SHA1 %s is ambiguous"), ds.hex_pfx); /* @@ -421,7 +451,12 @@ static int get_short_oid(const char *name, int len, struct object_id *oid, ds.fn = NULL; advise(_("The candidates are:")); - for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds); + for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect); + QSORT(collect.oid, collect.nr, sort_ambiguous); + + if (oid_array_for_each(&collect, show_ambiguous_object, &ds)) + BUG("show_ambiguous_object shouldn't return non-zero"); + oid_array_clear(&collect); } return status; diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh index 711704ba5a..2701462041 100755 --- a/t/t1512-rev-parse-disambiguation.sh +++ b/t/t1512-rev-parse-disambiguation.sh @@ -361,4 +361,25 @@ test_expect_success 'core.disambiguate does not override context' ' git -c core.disambiguate=committish rev-parse $sha1^{tree} ' +test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' ' + test_must_fail git rev-parse 0000 2>stderr && + grep ^hint: stderr >hints && + grep 0000 hints >objects && + cat >expected <<-\EOF && + tag + commit + tree + blob + EOF + awk "{print \$3}" objects.types && + uniq objects.types.uniq && + test_cmp expected objects.types.uniq && + for type in tag commit tree blob + do + grep $type objects >$type.objects && + sort $type.objects >$type.objects.sorted && + test_cmp $type.objects.sorted $type.objects + done +' + test_done