Merge branch 'dk/describe-all-output-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 23 Jan 2018 21:16:28 +0000 (13:16 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 23 Jan 2018 21:16:28 +0000 (13:16 -0800)
An old regression in "git describe --all $annotated_tag^0" has been
fixed.

* dk/describe-all-output-fix:
describe: prepend "tags/" when describing tags with embedded name

1  2 
builtin/describe.c
t/t6120-describe.sh
diff --combined builtin/describe.c
index 3b0b204b1e495626f3490d62f281291ee039ba42,2004a1a86eb381221f643becb7862a5690ed4880..6fe1c51281bf1bb6a92f00d837fecf60fe38ca31
@@@ -3,19 -3,16 +3,19 @@@
  #include "lockfile.h"
  #include "commit.h"
  #include "tag.h"
 +#include "blob.h"
  #include "refs.h"
  #include "builtin.h"
  #include "exec_cmd.h"
  #include "parse-options.h"
 +#include "revision.h"
  #include "diff.h"
  #include "hashmap.h"
  #include "argv-array.h"
  #include "run-command.h"
 +#include "revision.h"
 +#include "list-objects.h"
  
 -#define SEEN          (1u << 0)
  #define MAX_TAGS      (FLAG_BITS - 1)
  
  static const char * const describe_usage[] = {
@@@ -184,7 -181,7 +184,7 @@@ static int get_name(const char *path, c
        }
  
        /* Is it annotated? */
 -      if (!peel_ref(path, peeled.hash)) {
 +      if (!peel_ref(path, &peeled)) {
                is_annotated = !!oidcmp(oid, &peeled);
        } else {
                oidcpy(&peeled, oid);
@@@ -259,7 -256,7 +259,7 @@@ static unsigned long finish_depth_compu
        return seen_commits;
  }
  
 -static void display_name(struct commit_name *n)
 +static void append_name(struct commit_name *n, struct strbuf *dst)
  {
        if (n->prio == 2 && !n->tag) {
                n->tag = lookup_tag(&n->oid);
                n->name_checked = 1;
        }
  
-       if (n->tag)
+       if (n->tag) {
+               if (all)
 -                      printf("tags/");
 -              printf("%s", n->tag->tag);
++                      strbuf_addstr(dst, "tags/");
 +              strbuf_addstr(dst, n->tag->tag);
-       else
+       } else {
 -              printf("%s", n->path);
 +              strbuf_addstr(dst, n->path);
+       }
  }
  
 -static void show_suffix(int depth, const struct object_id *oid)
 +static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
  {
 -      printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
 +      strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
  }
  
 -static void describe(const char *arg, int last_one)
 +static void describe_commit(struct object_id *oid, struct strbuf *dst)
  {
 -      struct object_id oid;
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
        struct commit_name *n;
        unsigned long seen_commits = 0;
        unsigned int unannotated_cnt = 0;
  
 -      if (get_oid(arg, &oid))
 -              die(_("Not a valid object name %s"), arg);
 -      cmit = lookup_commit_reference(&oid);
 -      if (!cmit)
 -              die(_("%s is not a valid '%s' object"), arg, commit_type);
 +      cmit = lookup_commit_reference(oid);
  
        n = find_commit_name(&cmit->object.oid);
        if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
 -              display_name(n);
 +              append_name(n, dst);
                if (longformat)
 -                      show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
 +                      append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
                if (suffix)
 -                      printf("%s", suffix);
 -              printf("\n");
 +                      strbuf_addstr(dst, suffix);
                return;
        }
  
        if (!max_candidates)
                die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
        if (debug)
 -              fprintf(stderr, _("searching to describe %s\n"), arg);
 +              fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
  
        if (!have_util) {
                struct hashmap_iter iter;
        }
  
        if (!match_cnt) {
 -              struct object_id *oid = &cmit->object.oid;
 +              struct object_id *cmit_oid = &cmit->object.oid;
                if (always) {
 -                      printf("%s", find_unique_abbrev(oid->hash, abbrev));
 +                      strbuf_addstr(dst, find_unique_abbrev(cmit_oid->hash, abbrev));
                        if (suffix)
 -                              printf("%s", suffix);
 -                      printf("\n");
 +                              strbuf_addstr(dst, suffix);
                        return;
                }
                if (unannotated_cnt)
                        die(_("No annotated tags can describe '%s'.\n"
                            "However, there were unannotated tags: try --tags."),
 -                          oid_to_hex(oid));
 +                          oid_to_hex(cmit_oid));
                else
                        die(_("No tags can describe '%s'.\n"
                            "Try --always, or create some tags."),
 -                          oid_to_hex(oid));
 +                          oid_to_hex(cmit_oid));
        }
  
        QSORT(all_matches, match_cnt, compare_pt);
                }
        }
  
 -      display_name(all_matches[0].name);
 +      append_name(all_matches[0].name, dst);
        if (abbrev)
 -              show_suffix(all_matches[0].depth, &cmit->object.oid);
 +              append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
        if (suffix)
 -              printf("%s", suffix);
 -      printf("\n");
 +              strbuf_addstr(dst, suffix);
 +}
 +
 +struct process_commit_data {
 +      struct object_id current_commit;
 +      struct object_id looking_for;
 +      struct strbuf *dst;
 +      struct rev_info *revs;
 +};
 +
 +static void process_commit(struct commit *commit, void *data)
 +{
 +      struct process_commit_data *pcd = data;
 +      pcd->current_commit = commit->object.oid;
 +}
 +
 +static void process_object(struct object *obj, const char *path, void *data)
 +{
 +      struct process_commit_data *pcd = data;
 +
 +      if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
 +              reset_revision_walk();
 +              describe_commit(&pcd->current_commit, pcd->dst);
 +              strbuf_addf(pcd->dst, ":%s", path);
 +              free_commit_list(pcd->revs->commits);
 +              pcd->revs->commits = NULL;
 +      }
 +}
 +
 +static void describe_blob(struct object_id oid, struct strbuf *dst)
 +{
 +      struct rev_info revs;
 +      struct argv_array args = ARGV_ARRAY_INIT;
 +      struct process_commit_data pcd = { null_oid, oid, dst, &revs};
 +
 +      argv_array_pushl(&args, "internal: The first arg is not parsed",
 +              "--objects", "--in-commit-order", "--reverse", "HEAD",
 +              NULL);
 +
 +      init_revisions(&revs, NULL);
 +      if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
 +              BUG("setup_revisions could not handle all args?");
 +
 +      if (prepare_revision_walk(&revs))
 +              die("revision walk setup failed");
 +
 +      traverse_commit_list(&revs, process_commit, process_object, &pcd);
 +      reset_revision_walk();
 +}
 +
 +static void describe(const char *arg, int last_one)
 +{
 +      struct object_id oid;
 +      struct commit *cmit;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      if (debug)
 +              fprintf(stderr, _("describe %s\n"), arg);
 +
 +      if (get_oid(arg, &oid))
 +              die(_("Not a valid object name %s"), arg);
 +      cmit = lookup_commit_reference_gently(&oid, 1);
 +
 +      if (cmit)
 +              describe_commit(&oid, &sb);
 +      else if (lookup_blob(&oid))
 +              describe_blob(oid, &sb);
 +      else
 +              die(_("%s is neither a commit nor blob"), arg);
 +
 +      puts(sb.buf);
  
        if (!last_one)
                clear_commit_marks(cmit, -1);
 +
 +      strbuf_release(&sb);
  }
  
  int cmd_describe(int argc, const char **argv, const char *prefix)
                        }
                } else if (dirty) {
                        static struct lock_file index_lock;
 -                      int fd;
 +                      struct rev_info revs;
 +                      struct argv_array args = ARGV_ARRAY_INIT;
 +                      int fd, result;
  
                        read_cache_preload(NULL);
                        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
                        if (0 <= fd)
                                update_index_if_able(&the_index, &index_lock);
  
 -                      if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
 -                                          diff_index_args, prefix))
 +                      init_revisions(&revs, prefix);
 +                      argv_array_pushv(&args, diff_index_args);
 +                      if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
 +                              BUG("malformed internal diff-index command line");
 +                      result = run_diff_index(&revs, 0);
 +
 +                      if (!diff_result_code(&revs.diffopt, result))
                                suffix = NULL;
                        else
                                suffix = dirty;
diff --combined t/t6120-describe.sh
index 3e3fb462a098c01c72270acbb276abd2df225a66,15612b3bbe95fc58150f392b4150f6ee5b62c15a..a5d901502414f25616a474152ee0f5816465bd37
@@@ -122,7 -122,7 +122,7 @@@ test_expect_success 'describe --contain
  '
  
  : >err.expect
- check_describe A --all A^0
+ check_describe tags/A --all A^0
  test_expect_success 'no warning was displayed for A' '
        test_cmp err.expect err.actual
  '
@@@ -304,46 -304,12 +304,46 @@@ test_expect_success 'describe chokes o
        mv .git/modules/sub1/ .git/modules/sub_moved &&
        test_must_fail git describe --dirty
  '
 -test_expect_success 'describe ignoring a borken submodule' '
 +test_expect_success 'describe ignoring a broken submodule' '
        git describe --broken >out &&
        test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
        grep broken out
  '
  
 +test_expect_success 'describe a blob at a directly tagged commit' '
 +      echo "make it a unique blob" >file &&
 +      git add file && git commit -m "content in file" &&
 +      git tag -a -m "latest annotated tag" unique-file &&
 +      git describe HEAD:file >actual &&
 +      echo "unique-file:file" >expect &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'describe a blob with its first introduction' '
 +      git commit --allow-empty -m "empty commit" &&
 +      git rm file &&
 +      git commit -m "delete blob" &&
 +      git revert HEAD &&
 +      git commit --allow-empty -m "empty commit" &&
 +      git describe HEAD:file >actual &&
 +      echo "unique-file:file" >expect &&
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'describe directly tagged blob' '
 +      git tag test-blob unique-file:file &&
 +      git describe test-blob >actual &&
 +      echo "unique-file:file" >expect &&
 +      # suboptimal: we rather want to see "test-blob"
 +      test_cmp expect actual
 +'
 +
 +test_expect_success 'describe tag object' '
 +      git tag test-blob-1 -a -m msg unique-file:file &&
 +      test_must_fail git describe test-blob-1 2>actual &&
 +      test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
 +'
 +
  test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
        i=1 &&
        while test $i -lt 8000
@@@ -374,4 -340,8 +374,8 @@@ test_expect_success ULIMIT_STACK_SIZE '
        test_cmp expect actual
  '
  
+ check_describe tags/A --all A
+ check_describe tags/c --all c
+ check_describe heads/branch_A --all --match='branch_*' branch_A
  test_done