Merge branch 'tg/tag-points-at'
authorJunio C Hamano <gitster@pobox.com>
Mon, 20 Feb 2012 08:15:28 +0000 (00:15 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 20 Feb 2012 08:15:28 +0000 (00:15 -0800)
* tg/tag-points-at:
builtin/tag.c: Fix a sparse warning
tag: add --points-at list option

1  2 
builtin/tag.c
t/t7004-tag.sh
diff --combined builtin/tag.c
index 03df16ac6e0e492483bf3695e0f6b2aa0a95978b,e377706e6d362792055b5e2a67efc12edd972e52..fe7e5e5b3d64dc8168ebe687ac5c6c2d1cbbdba1
  #include "diff.h"
  #include "revision.h"
  #include "gpg-interface.h"
+ #include "sha1-array.h"
  
  static const char * const git_tag_usage[] = {
        "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
        "git tag -d <tagname>...",
-       "git tag -l [-n[<num>]] [<pattern>...]",
+       "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] "
+               "\n\t\t[<pattern>...]",
        "git tag -v <tagname>...",
        NULL
  };
@@@ -30,6 -32,8 +32,8 @@@ struct tag_filter 
        struct commit_list *with_commit;
  };
  
+ static struct sha1_array points_at;
  static int match_pattern(const char **patterns, const char *ref)
  {
        /* no pattern means match everything */
        return 0;
  }
  
+ static const unsigned char *match_points_at(const char *refname,
+                                           const unsigned char *sha1)
+ {
+       const unsigned char *tagged_sha1 = NULL;
+       struct object *obj;
+       if (sha1_array_lookup(&points_at, sha1) >= 0)
+               return sha1;
+       obj = parse_object(sha1);
+       if (!obj)
+               die(_("malformed object at '%s'"), refname);
+       if (obj->type == OBJ_TAG)
+               tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
+       if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)
+               return tagged_sha1;
+       return NULL;
+ }
  static int in_commit_list(const struct commit_list *want, struct commit *c)
  {
        for (; want; want = want->next)
@@@ -83,51 -105,18 +105,51 @@@ static int contains(struct commit *cand
        return contains_recurse(candidate, want);
  }
  
 +static void show_tag_lines(const unsigned char *sha1, int lines)
 +{
 +      int i;
 +      unsigned long size;
 +      enum object_type type;
 +      char *buf, *sp, *eol;
 +      size_t len;
 +
 +      buf = read_sha1_file(sha1, &type, &size);
 +      if (!buf)
 +              die_errno("unable to read object %s", sha1_to_hex(sha1));
 +      if (type != OBJ_COMMIT && type != OBJ_TAG)
 +              goto free_return;
 +      if (!size)
 +              die("an empty %s object %s?",
 +                  typename(type), sha1_to_hex(sha1));
 +
 +      /* skip header */
 +      sp = strstr(buf, "\n\n");
 +      if (!sp)
 +              goto free_return;
 +
 +      /* only take up to "lines" lines, and strip the signature from a tag */
 +      if (type == OBJ_TAG)
 +              size = parse_signature(buf, size);
 +      for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
 +              if (i)
 +                      printf("\n    ");
 +              eol = memchr(sp, '\n', size - (sp - buf));
 +              len = eol ? eol - sp : size - (sp - buf);
 +              fwrite(sp, len, 1, stdout);
 +              if (!eol)
 +                      break;
 +              sp = eol + 1;
 +      }
 +free_return:
 +      free(buf);
 +}
 +
  static int show_reference(const char *refname, const unsigned char *sha1,
                          int flag, void *cb_data)
  {
        struct tag_filter *filter = cb_data;
  
        if (match_pattern(filter->patterns, refname)) {
 -              int i;
 -              unsigned long size;
 -              enum object_type type;
 -              char *buf, *sp, *eol;
 -              size_t len;
 -
                if (filter->with_commit) {
                        struct commit *commit;
  
                                return 0;
                }
  
+               if (points_at.nr && !match_points_at(refname, sha1))
+                       return 0;
                if (!filter->lines) {
                        printf("%s\n", refname);
                        return 0;
                }
                printf("%-15s ", refname);
 -
 -              buf = read_sha1_file(sha1, &type, &size);
 -              if (!buf || !size)
 -                      return 0;
 -
 -              /* skip header */
 -              sp = strstr(buf, "\n\n");
 -              if (!sp) {
 -                      free(buf);
 -                      return 0;
 -              }
 -              /* only take up to "lines" lines, and strip the signature */
 -              size = parse_signature(buf, size);
 -              for (i = 0, sp += 2;
 -                              i < filter->lines && sp < buf + size;
 -                              i++) {
 -                      if (i)
 -                              printf("\n    ");
 -                      eol = memchr(sp, '\n', size - (sp - buf));
 -                      len = eol ? eol - sp : size - (sp - buf);
 -                      fwrite(sp, len, 1, stdout);
 -                      if (!eol)
 -                              break;
 -                      sp = eol + 1;
 -              }
 +              show_tag_lines(sha1, filter->lines);
                putchar('\n');
 -              free(buf);
        }
  
        return 0;
@@@ -383,6 -400,23 +408,23 @@@ static int strbuf_check_tag_ref(struct 
        return check_refname_format(sb->buf, 0);
  }
  
+ static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
+                       const char *arg, int unset)
+ {
+       unsigned char sha1[20];
+       if (unset) {
+               sha1_array_clear(&points_at);
+               return 0;
+       }
+       if (!arg)
+               return error(_("switch 'points-at' requires an object"));
+       if (get_sha1(arg, sha1))
+               return error(_("malformed object name '%s'"), arg);
+       sha1_array_append(&points_at, sha1);
+       return 0;
+ }
  int cmd_tag(int argc, const char **argv, const char *prefix)
  {
        struct strbuf buf = STRBUF_INIT;
                        PARSE_OPT_LASTARG_DEFAULT,
                        parse_opt_with_commit, (intptr_t)"HEAD",
                },
+               {
+                       OPTION_CALLBACK, 0, "points-at", NULL, "object",
+                       "print only tags of the object", 0, parse_opt_points_at
+               },
                OPT_END()
        };
  
                die(_("-n option is only allowed with -l."));
        if (with_commit)
                die(_("--contains option is only allowed with -l."));
+       if (points_at.nr)
+               die(_("--points-at option is only allowed with -l."));
        if (delete)
                return for_each_tag_name(argv, delete_tag);
        if (verify)
diff --combined t/t7004-tag.sh
index 4ef79aabc47a4ef2e9def65253edf6bcd8ce91a3,f61e3987ee678563d12f548fc93ec3e0c2fbbef8..f8c247a7500d723e46796e7b9b76b9812e35db9b
@@@ -586,19 -586,6 +586,19 @@@ test_expect_success 
        test_cmp expect actual
  '
  
 +test_expect_success 'annotations for blobs are empty' '
 +      blob=$(git hash-object -w --stdin <<-\EOF
 +      Blob paragraph 1.
 +
 +      Blob paragraph 2.
 +      EOF
 +      ) &&
 +      git tag tag-blob $blob &&
 +      echo "tag-blob        " >expect &&
 +      git tag -n1 -l tag-blob >actual &&
 +      test_cmp expect actual
 +'
 +
  # trying to verify annotated non-signed tags:
  
  test_expect_success GPG \
@@@ -1282,4 -1269,43 +1282,43 @@@ test_expect_success 'mixing incompatibl
        test_must_fail git tag -v -s
  '
  
+ # check points-at
+ test_expect_success '--points-at cannot be used in non-list mode' '
+       test_must_fail git tag --points-at=v4.0 foo
+ '
+ test_expect_success '--points-at finds lightweight tags' '
+       echo v4.0 >expect &&
+       git tag --points-at v4.0 >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success '--points-at finds annotated tags of commits' '
+       git tag -m "v4.0, annotated" annotated-v4.0 v4.0 &&
+       echo annotated-v4.0 >expect &&
+       git tag -l --points-at v4.0 "annotated*" >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success '--points-at finds annotated tags of tags' '
+       git tag -m "describing the v4.0 tag object" \
+               annotated-again-v4.0 annotated-v4.0 &&
+       cat >expect <<-\EOF &&
+       annotated-again-v4.0
+       annotated-v4.0
+       EOF
+       git tag --points-at=annotated-v4.0 >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success 'multiple --points-at are OR-ed together' '
+       cat >expect <<-\EOF &&
+       v2.0
+       v3.0
+       EOF
+       git tag --points-at=v2.0 --points-at=v3.0 >actual &&
+       test_cmp expect actual
+ '
  test_done