@matheusbsbs / matheus-bagliano / commits / 30b1c7ad9d
Fork 0

describe: don't abort too early when searching tags

When searching the commit graph for tag candidates, `git-describe` will stop as soon as there is only one active branch left and it already found an annotated tag as a candidate. This works well as long as all branches eventually connect back to a common root, but if the tags are found across branches with no common ancestor B o----. \ o-----o---o----x A it can happen that the search on one branch terminates prematurely because a tag was found on another, independent branch. This scenario isn't quite as obscure as it sounds, since cloning with a limited depth often introduces many independent "dead ends" into the commit graph. The help text of `git-describe` states pretty clearly that when describing a commit D, the number appended to the emitted tag X should correspond to the number of commits found by `git log X..D`. Thus, this commit modifies the stopping condition to only abort the search when only one branch is left to search *and* all current best candidates are descendants from that branch. For repositories with a single root, this condition is always true: When the search is reduced to a single active branch, the current commit must be an ancestor of *all* tag candidates. This means that in the common case, this change will have no negative performance impact since the same number of commits as before will be traversed. Signed-off-by: Benno Evers <benno@bmevers.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Benno Evers committed Feb 26, 2020 at 18:48 UTC 30b1c7ad9d64695a65c23f922e6ffd2fd35660da
2 files changed +69 -4
builtin/describe.c
+18 -4
@@ -376,11 +376,25 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
376 if (!(c->object.flags & t->flag_within))
377 t->depth++;
378 }
379 + /* Stop if last remaining path already covered by best candidate(s) */
380 if (annotated_cnt && !list) {
380 - if (debug)
381 - fprintf(stderr, _("finished search at %s\n"),
382 - oid_to_hex(&c->object.oid));
383 - break;
381 + int best_depth = INT_MAX;
382 + unsigned best_within = 0;
383 + for (cur_match = 0; cur_match < match_cnt; cur_match++) {
384 + struct possible_tag *t = &all_matches[cur_match];
385 + if (t->depth < best_depth) {
386 + best_depth = t->depth;
387 + best_within = t->flag_within;
388 + } else if (t->depth == best_depth) {
389 + best_within |= t->flag_within;
390 + }
391 + }
392 + if ((c->object.flags & best_within) == best_within) {
393 + if (debug)
394 + fprintf(stderr, _("finished search at %s\n"),
395 + oid_to_hex(&c->object.oid));
396 + break;
397 + }
398 }
399 while (parents) {
400 struct commit *p = parents->item;
t/t6120-describe.sh
+51
@@ -479,4 +479,55 @@ test_expect_success 'name-rev covers all conditions while looking at parents' '
479 )
480 '
481
482 +# B
483 +# o
484 +# \
485 +# o-----o---o----x
486 +# A
487 +#
488 +test_expect_success 'describe commits with disjoint bases' '
489 + git init disjoint1 &&
490 + (
491 + cd disjoint1 &&
492 +
493 + echo o >> file && git add file && git commit -m o &&
494 + echo A >> file && git add file && git commit -m A &&
495 + git tag A -a -m A &&
496 + echo o >> file && git add file && git commit -m o &&
497 +
498 + git checkout --orphan branch && rm file &&
499 + echo B > file2 && git add file2 && git commit -m B &&
500 + git tag B -a -m B &&
501 + git merge --no-ff --allow-unrelated-histories master -m x &&
502 +
503 + check_describe "A-3-*" HEAD
504 + )
505 +'
506 +
507 +# B
508 +# o---o---o------------.
509 +# \
510 +# o---o---x
511 +# A
512 +#
513 +test_expect_success 'describe commits with disjoint bases 2' '
514 + git init disjoint2 &&
515 + (
516 + cd disjoint2 &&
517 +
518 + echo A >> file && git add file && GIT_COMMITTER_DATE="2020-01-01 18:00" git commit -m A &&
519 + git tag A -a -m A &&
520 + echo o >> file && git add file && GIT_COMMITTER_DATE="2020-01-01 18:01" git commit -m o &&
521 +
522 + git checkout --orphan branch &&
523 + echo o >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:00" git commit -m o &&
524 + echo o >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:01" git commit -m o &&
525 + echo B >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:02" git commit -m B &&
526 + git tag B -a -m B &&
527 + git merge --no-ff --allow-unrelated-histories master -m x &&
528 +
529 + check_describe "B-3-*" HEAD
530 + )
531 +'
532 +
533 test_done