Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Builtin "git tag" |
| 3 | * |
| 4 | * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>, |
| 5 | * Carlos Rica <jasampler@gmail.com> |
| 6 | * Based on git-tag.sh and mktag.c by Linus Torvalds. |
| 7 | */ |
| 8 | |
| 9 | #include "cache.h" |
| 10 | #include "builtin.h" |
| 11 | #include "refs.h" |
| 12 | #include "tag.h" |
| 13 | #include "run-command.h" |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 14 | #include "parse-options.h" |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 15 | #include "diff.h" |
| 16 | #include "revision.h" |
Junio C Hamano | 2f47eae | 2011-09-07 21:19:47 -0700 | [diff] [blame] | 17 | #include "gpg-interface.h" |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 18 | #include "sha1-array.h" |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 19 | #include "column.h" |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 20 | |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 21 | static const char * const git_tag_usage[] = { |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 22 | N_("git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"), |
| 23 | N_("git tag -d <tagname>..."), |
| 24 | N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] " |
| 25 | "\n\t\t[<pattern>...]"), |
| 26 | N_("git tag -v <tagname>..."), |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 27 | NULL |
| 28 | }; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 29 | |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 30 | #define STRCMP_SORT 0 /* must be zero */ |
| 31 | #define VERCMP_SORT 1 |
| 32 | #define SORT_MASK 0x7fff |
| 33 | #define REVERSE_SORT 0x8000 |
| 34 | |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 35 | static int tag_sort; |
| 36 | |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 37 | struct tag_filter { |
Jeff King | 588d0e8 | 2011-06-20 12:59:28 -0400 | [diff] [blame] | 38 | const char **patterns; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 39 | int lines; |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 40 | int sort; |
| 41 | struct string_list tags; |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 42 | struct commit_list *with_commit; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 43 | }; |
| 44 | |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 45 | static struct sha1_array points_at; |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 46 | static unsigned int colopts; |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 47 | |
Jeff King | 588d0e8 | 2011-06-20 12:59:28 -0400 | [diff] [blame] | 48 | static int match_pattern(const char **patterns, const char *ref) |
| 49 | { |
| 50 | /* no pattern means match everything */ |
| 51 | if (!*patterns) |
| 52 | return 1; |
| 53 | for (; *patterns; patterns++) |
Nguyễn Thái Ngọc Duy | eb07894 | 2014-02-15 09:01:46 +0700 | [diff] [blame] | 54 | if (!wildmatch(*patterns, ref, 0, NULL)) |
Jeff King | 588d0e8 | 2011-06-20 12:59:28 -0400 | [diff] [blame] | 55 | return 1; |
| 56 | return 0; |
| 57 | } |
| 58 | |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 59 | static const unsigned char *match_points_at(const char *refname, |
| 60 | const unsigned char *sha1) |
| 61 | { |
| 62 | const unsigned char *tagged_sha1 = NULL; |
| 63 | struct object *obj; |
| 64 | |
| 65 | if (sha1_array_lookup(&points_at, sha1) >= 0) |
| 66 | return sha1; |
| 67 | obj = parse_object(sha1); |
| 68 | if (!obj) |
| 69 | die(_("malformed object at '%s'"), refname); |
| 70 | if (obj->type == OBJ_TAG) |
| 71 | tagged_sha1 = ((struct tag *)obj)->tagged->sha1; |
| 72 | if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0) |
| 73 | return tagged_sha1; |
| 74 | return NULL; |
| 75 | } |
| 76 | |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 77 | static int in_commit_list(const struct commit_list *want, struct commit *c) |
| 78 | { |
| 79 | for (; want; want = want->next) |
| 80 | if (!hashcmp(want->item->object.sha1, c->object.sha1)) |
| 81 | return 1; |
| 82 | return 0; |
| 83 | } |
| 84 | |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 85 | enum contains_result { |
| 86 | CONTAINS_UNKNOWN = -1, |
| 87 | CONTAINS_NO = 0, |
Ronnie Sahlberg | 95acfc2 | 2014-07-02 11:24:05 -0700 | [diff] [blame] | 88 | CONTAINS_YES = 1 |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 89 | }; |
| 90 | |
| 91 | /* |
| 92 | * Test whether the candidate or one of its parents is contained in the list. |
| 93 | * Do not recurse to find out, though, but return -1 if inconclusive. |
| 94 | */ |
| 95 | static enum contains_result contains_test(struct commit *candidate, |
Junio C Hamano | c6d72c4 | 2011-07-14 11:02:06 -0700 | [diff] [blame] | 96 | const struct commit_list *want) |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 97 | { |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 98 | /* was it previously marked as containing a want commit? */ |
| 99 | if (candidate->object.flags & TMP_MARK) |
| 100 | return 1; |
| 101 | /* or marked as not possibly containing a want commit? */ |
| 102 | if (candidate->object.flags & UNINTERESTING) |
| 103 | return 0; |
| 104 | /* or are we it? */ |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 105 | if (in_commit_list(want, candidate)) { |
| 106 | candidate->object.flags |= TMP_MARK; |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 107 | return 1; |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 108 | } |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 109 | |
| 110 | if (parse_commit(candidate) < 0) |
| 111 | return 0; |
| 112 | |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 113 | return -1; |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 114 | } |
| 115 | |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 116 | /* |
| 117 | * Mimicking the real stack, this stack lives on the heap, avoiding stack |
| 118 | * overflows. |
| 119 | * |
| 120 | * At each recursion step, the stack items points to the commits whose |
| 121 | * ancestors are to be inspected. |
| 122 | */ |
| 123 | struct stack { |
| 124 | int nr, alloc; |
| 125 | struct stack_entry { |
| 126 | struct commit *commit; |
| 127 | struct commit_list *parents; |
| 128 | } *stack; |
| 129 | }; |
| 130 | |
| 131 | static void push_to_stack(struct commit *candidate, struct stack *stack) |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 132 | { |
Jean-Jacques Lafay | cbc60b6 | 2014-04-24 14:24:39 +0200 | [diff] [blame] | 133 | int index = stack->nr++; |
| 134 | ALLOC_GROW(stack->stack, stack->nr, stack->alloc); |
| 135 | stack->stack[index].commit = candidate; |
| 136 | stack->stack[index].parents = candidate->parents; |
| 137 | } |
| 138 | |
| 139 | static enum contains_result contains(struct commit *candidate, |
| 140 | const struct commit_list *want) |
| 141 | { |
| 142 | struct stack stack = { 0, 0, NULL }; |
| 143 | int result = contains_test(candidate, want); |
| 144 | |
| 145 | if (result != CONTAINS_UNKNOWN) |
| 146 | return result; |
| 147 | |
| 148 | push_to_stack(candidate, &stack); |
| 149 | while (stack.nr) { |
| 150 | struct stack_entry *entry = &stack.stack[stack.nr - 1]; |
| 151 | struct commit *commit = entry->commit; |
| 152 | struct commit_list *parents = entry->parents; |
| 153 | |
| 154 | if (!parents) { |
| 155 | commit->object.flags |= UNINTERESTING; |
| 156 | stack.nr--; |
| 157 | } |
| 158 | /* |
| 159 | * If we just popped the stack, parents->item has been marked, |
| 160 | * therefore contains_test will return a meaningful 0 or 1. |
| 161 | */ |
| 162 | else switch (contains_test(parents->item, want)) { |
| 163 | case CONTAINS_YES: |
| 164 | commit->object.flags |= TMP_MARK; |
| 165 | stack.nr--; |
| 166 | break; |
| 167 | case CONTAINS_NO: |
| 168 | entry->parents = parents->next; |
| 169 | break; |
| 170 | case CONTAINS_UNKNOWN: |
| 171 | push_to_stack(parents->item, &stack); |
| 172 | break; |
| 173 | } |
| 174 | } |
| 175 | free(stack.stack); |
| 176 | return contains_test(candidate, want); |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 177 | } |
| 178 | |
Jeff King | ca51699 | 2012-02-06 03:13:12 -0500 | [diff] [blame] | 179 | static void show_tag_lines(const unsigned char *sha1, int lines) |
| 180 | { |
| 181 | int i; |
| 182 | unsigned long size; |
| 183 | enum object_type type; |
| 184 | char *buf, *sp, *eol; |
| 185 | size_t len; |
| 186 | |
| 187 | buf = read_sha1_file(sha1, &type, &size); |
Jeff King | fb630e0 | 2012-02-06 03:13:42 -0500 | [diff] [blame] | 188 | if (!buf) |
| 189 | die_errno("unable to read object %s", sha1_to_hex(sha1)); |
Junio C Hamano | 31fd8d7 | 2012-02-06 10:13:27 -0800 | [diff] [blame] | 190 | if (type != OBJ_COMMIT && type != OBJ_TAG) |
| 191 | goto free_return; |
| 192 | if (!size) |
| 193 | die("an empty %s object %s?", |
| 194 | typename(type), sha1_to_hex(sha1)); |
Jeff King | ca51699 | 2012-02-06 03:13:12 -0500 | [diff] [blame] | 195 | |
| 196 | /* skip header */ |
| 197 | sp = strstr(buf, "\n\n"); |
Junio C Hamano | 31fd8d7 | 2012-02-06 10:13:27 -0800 | [diff] [blame] | 198 | if (!sp) |
| 199 | goto free_return; |
| 200 | |
| 201 | /* only take up to "lines" lines, and strip the signature from a tag */ |
| 202 | if (type == OBJ_TAG) |
| 203 | size = parse_signature(buf, size); |
Jeff King | ca51699 | 2012-02-06 03:13:12 -0500 | [diff] [blame] | 204 | for (i = 0, sp += 2; i < lines && sp < buf + size; i++) { |
| 205 | if (i) |
| 206 | printf("\n "); |
| 207 | eol = memchr(sp, '\n', size - (sp - buf)); |
| 208 | len = eol ? eol - sp : size - (sp - buf); |
| 209 | fwrite(sp, len, 1, stdout); |
| 210 | if (!eol) |
| 211 | break; |
| 212 | sp = eol + 1; |
| 213 | } |
Junio C Hamano | 31fd8d7 | 2012-02-06 10:13:27 -0800 | [diff] [blame] | 214 | free_return: |
Jeff King | ca51699 | 2012-02-06 03:13:12 -0500 | [diff] [blame] | 215 | free(buf); |
| 216 | } |
| 217 | |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 218 | static int show_reference(const char *refname, const unsigned char *sha1, |
| 219 | int flag, void *cb_data) |
| 220 | { |
| 221 | struct tag_filter *filter = cb_data; |
| 222 | |
Jeff King | 588d0e8 | 2011-06-20 12:59:28 -0400 | [diff] [blame] | 223 | if (match_pattern(filter->patterns, refname)) { |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 224 | if (filter->with_commit) { |
| 225 | struct commit *commit; |
| 226 | |
| 227 | commit = lookup_commit_reference_gently(sha1, 1); |
| 228 | if (!commit) |
| 229 | return 0; |
Jeff King | ffc4b80 | 2011-06-11 19:04:08 +0000 | [diff] [blame] | 230 | if (!contains(commit, filter->with_commit)) |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 231 | return 0; |
| 232 | } |
| 233 | |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 234 | if (points_at.nr && !match_points_at(refname, sha1)) |
| 235 | return 0; |
| 236 | |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 237 | if (!filter->lines) { |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 238 | if (filter->sort) |
| 239 | string_list_append(&filter->tags, refname); |
| 240 | else |
| 241 | printf("%s\n", refname); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 242 | return 0; |
| 243 | } |
| 244 | printf("%-15s ", refname); |
Jeff King | ca51699 | 2012-02-06 03:13:12 -0500 | [diff] [blame] | 245 | show_tag_lines(sha1, filter->lines); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 246 | putchar('\n'); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 247 | } |
| 248 | |
| 249 | return 0; |
| 250 | } |
| 251 | |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 252 | static int sort_by_version(const void *a_, const void *b_) |
| 253 | { |
| 254 | const struct string_list_item *a = a_; |
| 255 | const struct string_list_item *b = b_; |
| 256 | return versioncmp(a->string, b->string); |
| 257 | } |
| 258 | |
Jeff King | 588d0e8 | 2011-06-20 12:59:28 -0400 | [diff] [blame] | 259 | static int list_tags(const char **patterns, int lines, |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 260 | struct commit_list *with_commit, int sort) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 261 | { |
| 262 | struct tag_filter filter; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 263 | |
Jeff King | 588d0e8 | 2011-06-20 12:59:28 -0400 | [diff] [blame] | 264 | filter.patterns = patterns; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 265 | filter.lines = lines; |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 266 | filter.sort = sort; |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 267 | filter.with_commit = with_commit; |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 268 | memset(&filter.tags, 0, sizeof(filter.tags)); |
| 269 | filter.tags.strdup_strings = 1; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 270 | |
| 271 | for_each_tag_ref(show_reference, (void *) &filter); |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 272 | if (sort) { |
| 273 | int i; |
| 274 | if ((sort & SORT_MASK) == VERCMP_SORT) |
| 275 | qsort(filter.tags.items, filter.tags.nr, |
| 276 | sizeof(struct string_list_item), sort_by_version); |
| 277 | if (sort & REVERSE_SORT) |
| 278 | for (i = filter.tags.nr - 1; i >= 0; i--) |
| 279 | printf("%s\n", filter.tags.items[i].string); |
| 280 | else |
| 281 | for (i = 0; i < filter.tags.nr; i++) |
| 282 | printf("%s\n", filter.tags.items[i].string); |
| 283 | string_list_clear(&filter.tags, 0); |
| 284 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 285 | return 0; |
| 286 | } |
| 287 | |
Carlos Rica | e317cfa | 2007-07-21 14:13:12 +0200 | [diff] [blame] | 288 | typedef int (*each_tag_name_fn)(const char *name, const char *ref, |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 289 | const unsigned char *sha1); |
| 290 | |
Carlos Rica | e317cfa | 2007-07-21 14:13:12 +0200 | [diff] [blame] | 291 | static int for_each_tag_name(const char **argv, each_tag_name_fn fn) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 292 | { |
| 293 | const char **p; |
| 294 | char ref[PATH_MAX]; |
| 295 | int had_error = 0; |
| 296 | unsigned char sha1[20]; |
| 297 | |
| 298 | for (p = argv; *p; p++) { |
| 299 | if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p) |
| 300 | >= sizeof(ref)) { |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 301 | error(_("tag name too long: %.*s..."), 50, *p); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 302 | had_error = 1; |
| 303 | continue; |
| 304 | } |
Nguyễn Thái Ngọc Duy | c689332 | 2011-11-13 17:22:14 +0700 | [diff] [blame] | 305 | if (read_ref(ref, sha1)) { |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 306 | error(_("tag '%s' not found."), *p); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 307 | had_error = 1; |
| 308 | continue; |
| 309 | } |
| 310 | if (fn(*p, ref, sha1)) |
| 311 | had_error = 1; |
| 312 | } |
| 313 | return had_error; |
| 314 | } |
| 315 | |
| 316 | static int delete_tag(const char *name, const char *ref, |
| 317 | const unsigned char *sha1) |
| 318 | { |
Miklos Vajna | eca35a2 | 2008-10-26 03:33:56 +0100 | [diff] [blame] | 319 | if (delete_ref(ref, sha1, 0)) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 320 | return 1; |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 321 | printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV)); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 322 | return 0; |
| 323 | } |
| 324 | |
| 325 | static int verify_tag(const char *name, const char *ref, |
| 326 | const unsigned char *sha1) |
| 327 | { |
Jonathan Nieder | a6ccbbd | 2010-04-15 04:36:25 -0500 | [diff] [blame] | 328 | const char *argv_verify_tag[] = {"verify-tag", |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 329 | "-v", "SHA1_HEX", NULL}; |
| 330 | argv_verify_tag[2] = sha1_to_hex(sha1); |
| 331 | |
Jonathan Nieder | a6ccbbd | 2010-04-15 04:36:25 -0500 | [diff] [blame] | 332 | if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD)) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 333 | return error(_("could not verify the tag '%s'"), name); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 334 | return 0; |
| 335 | } |
| 336 | |
Pierre Habouzit | fd17f5b | 2007-09-10 12:35:09 +0200 | [diff] [blame] | 337 | static int do_sign(struct strbuf *buffer) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 338 | { |
Junio C Hamano | 2f47eae | 2011-09-07 21:19:47 -0700 | [diff] [blame] | 339 | return sign_buffer(buffer, buffer, get_signing_key()); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 340 | } |
| 341 | |
| 342 | static const char tag_template[] = |
Thorsten Glaser | d78f340 | 2013-08-30 00:03:10 +0200 | [diff] [blame] | 343 | N_("\nWrite a message for tag:\n %s\n" |
Junio C Hamano | eff80a9 | 2013-01-16 20:18:48 +0100 | [diff] [blame] | 344 | "Lines starting with '%c' will be ignored.\n"); |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 345 | |
| 346 | static const char tag_template_nocleanup[] = |
Thorsten Glaser | d78f340 | 2013-08-30 00:03:10 +0200 | [diff] [blame] | 347 | N_("\nWrite a message for tag:\n %s\n" |
Junio C Hamano | eff80a9 | 2013-01-16 20:18:48 +0100 | [diff] [blame] | 348 | "Lines starting with '%c' will be kept; you may remove them" |
| 349 | " yourself if you want to.\n"); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 350 | |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 351 | /* |
| 352 | * Parse a sort string, and return 0 if parsed successfully. Will return |
| 353 | * non-zero when the sort string does not parse into a known type. If var is |
| 354 | * given, the error message becomes a warning and includes information about |
| 355 | * the configuration value. |
| 356 | */ |
| 357 | static int parse_sort_string(const char *var, const char *arg, int *sort) |
| 358 | { |
| 359 | int type = 0, flags = 0; |
| 360 | |
| 361 | if (skip_prefix(arg, "-", &arg)) |
| 362 | flags |= REVERSE_SORT; |
| 363 | |
| 364 | if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg)) |
| 365 | type = VERCMP_SORT; |
| 366 | else |
| 367 | type = STRCMP_SORT; |
| 368 | |
| 369 | if (strcmp(arg, "refname")) { |
| 370 | if (!var) |
| 371 | return error(_("unsupported sort specification '%s'"), arg); |
| 372 | else { |
| 373 | warning(_("unsupported sort specification '%s' in variable '%s'"), |
| 374 | var, arg); |
| 375 | return -1; |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | *sort = (type | flags); |
| 380 | |
| 381 | return 0; |
| 382 | } |
| 383 | |
Johannes Schindelin | ef90d6d | 2008-05-14 18:46:53 +0100 | [diff] [blame] | 384 | static int git_tag_config(const char *var, const char *value, void *cb) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 385 | { |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 386 | int status; |
| 387 | |
| 388 | if (!strcmp(var, "tag.sort")) { |
| 389 | if (!value) |
| 390 | return config_error_nonbool(var); |
| 391 | parse_sort_string(var, value, &tag_sort); |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | status = git_gpg_config(var, value, cb); |
Junio C Hamano | 2f47eae | 2011-09-07 21:19:47 -0700 | [diff] [blame] | 396 | if (status) |
| 397 | return status; |
Christian Couder | 5955654 | 2013-11-30 21:55:40 +0100 | [diff] [blame] | 398 | if (starts_with(var, "column.")) |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 399 | return git_column_config(var, value, "tag", &colopts); |
Johannes Schindelin | ef90d6d | 2008-05-14 18:46:53 +0100 | [diff] [blame] | 400 | return git_default_config(var, value, cb); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 401 | } |
| 402 | |
Mike Hommey | bab8118 | 2007-11-04 01:11:14 +0100 | [diff] [blame] | 403 | static void write_tag_body(int fd, const unsigned char *sha1) |
| 404 | { |
| 405 | unsigned long size; |
| 406 | enum object_type type; |
Michael J Gruber | e10dfb6 | 2010-11-10 12:17:28 +0100 | [diff] [blame] | 407 | char *buf, *sp; |
Mike Hommey | bab8118 | 2007-11-04 01:11:14 +0100 | [diff] [blame] | 408 | |
| 409 | buf = read_sha1_file(sha1, &type, &size); |
| 410 | if (!buf) |
| 411 | return; |
| 412 | /* skip header */ |
| 413 | sp = strstr(buf, "\n\n"); |
| 414 | |
| 415 | if (!sp || !size || type != OBJ_TAG) { |
| 416 | free(buf); |
| 417 | return; |
| 418 | } |
| 419 | sp += 2; /* skip the 2 LFs */ |
Michael J Gruber | e10dfb6 | 2010-11-10 12:17:28 +0100 | [diff] [blame] | 420 | write_or_die(fd, sp, parse_signature(sp, buf + size - sp)); |
Mike Hommey | bab8118 | 2007-11-04 01:11:14 +0100 | [diff] [blame] | 421 | |
| 422 | free(buf); |
| 423 | } |
| 424 | |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 425 | static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result) |
| 426 | { |
| 427 | if (sign && do_sign(buf) < 0) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 428 | return error(_("unable to sign the tag")); |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 429 | if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 430 | return error(_("unable to write tag file")); |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 431 | return 0; |
| 432 | } |
| 433 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 434 | struct create_tag_options { |
| 435 | unsigned int message_given:1; |
| 436 | unsigned int sign; |
| 437 | enum { |
| 438 | CLEANUP_NONE, |
| 439 | CLEANUP_SPACE, |
| 440 | CLEANUP_ALL |
| 441 | } cleanup_mode; |
| 442 | }; |
| 443 | |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 444 | static void create_tag(const unsigned char *object, const char *tag, |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 445 | struct strbuf *buf, struct create_tag_options *opt, |
Junio C Hamano | bd46c9a | 2007-11-22 23:16:51 -0800 | [diff] [blame] | 446 | unsigned char *prev, unsigned char *result) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 447 | { |
| 448 | enum object_type type; |
Pierre Habouzit | fd17f5b | 2007-09-10 12:35:09 +0200 | [diff] [blame] | 449 | char header_buf[1024]; |
| 450 | int header_len; |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 451 | char *path = NULL; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 452 | |
| 453 | type = sha1_object_info(object, NULL); |
Carlos Rica | e317cfa | 2007-07-21 14:13:12 +0200 | [diff] [blame] | 454 | if (type <= OBJ_NONE) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 455 | die(_("bad object type.")); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 456 | |
| 457 | header_len = snprintf(header_buf, sizeof(header_buf), |
| 458 | "object %s\n" |
| 459 | "type %s\n" |
| 460 | "tag %s\n" |
| 461 | "tagger %s\n\n", |
| 462 | sha1_to_hex(object), |
| 463 | typename(type), |
| 464 | tag, |
Jeff King | f9bc573 | 2012-05-24 19:28:40 -0400 | [diff] [blame] | 465 | git_committer_info(IDENT_STRICT)); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 466 | |
Carlos Rica | e317cfa | 2007-07-21 14:13:12 +0200 | [diff] [blame] | 467 | if (header_len > sizeof(header_buf) - 1) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 468 | die(_("tag header too big.")); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 469 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 470 | if (!opt->message_given) { |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 471 | int fd; |
| 472 | |
| 473 | /* write the template message before editing: */ |
Alex Riesen | a4f34cb | 2008-10-27 11:22:09 +0100 | [diff] [blame] | 474 | path = git_pathdup("TAG_EDITMSG"); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 475 | fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); |
| 476 | if (fd < 0) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 477 | die_errno(_("could not create file '%s'"), path); |
Mike Hommey | bab8118 | 2007-11-04 01:11:14 +0100 | [diff] [blame] | 478 | |
Junio C Hamano | eff80a9 | 2013-01-16 20:18:48 +0100 | [diff] [blame] | 479 | if (!is_null_sha1(prev)) { |
Mike Hommey | bab8118 | 2007-11-04 01:11:14 +0100 | [diff] [blame] | 480 | write_tag_body(fd, prev); |
Junio C Hamano | eff80a9 | 2013-01-16 20:18:48 +0100 | [diff] [blame] | 481 | } else { |
| 482 | struct strbuf buf = STRBUF_INIT; |
| 483 | strbuf_addch(&buf, '\n'); |
| 484 | if (opt->cleanup_mode == CLEANUP_ALL) |
Thorsten Glaser | d78f340 | 2013-08-30 00:03:10 +0200 | [diff] [blame] | 485 | strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char); |
Junio C Hamano | eff80a9 | 2013-01-16 20:18:48 +0100 | [diff] [blame] | 486 | else |
Thorsten Glaser | d78f340 | 2013-08-30 00:03:10 +0200 | [diff] [blame] | 487 | strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char); |
Junio C Hamano | eff80a9 | 2013-01-16 20:18:48 +0100 | [diff] [blame] | 488 | write_or_die(fd, buf.buf, buf.len); |
| 489 | strbuf_release(&buf); |
| 490 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 491 | close(fd); |
| 492 | |
Stephan Beyer | 7198203 | 2008-07-25 18:28:42 +0200 | [diff] [blame] | 493 | if (launch_editor(path, buf, NULL)) { |
| 494 | fprintf(stderr, |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 495 | _("Please supply the message using either -m or -F option.\n")); |
Stephan Beyer | 7198203 | 2008-07-25 18:28:42 +0200 | [diff] [blame] | 496 | exit(1); |
| 497 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 498 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 499 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 500 | if (opt->cleanup_mode != CLEANUP_NONE) |
| 501 | stripspace(buf, opt->cleanup_mode == CLEANUP_ALL); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 502 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 503 | if (!opt->message_given && !buf->len) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 504 | die(_("no tag message?")); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 505 | |
Pierre Habouzit | fd17f5b | 2007-09-10 12:35:09 +0200 | [diff] [blame] | 506 | strbuf_insert(buf, 0, header_buf, header_len); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 507 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 508 | if (build_tag_object(buf, opt->sign, result) < 0) { |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 509 | if (path) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 510 | fprintf(stderr, _("The tag message has been left in %s\n"), |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 511 | path); |
| 512 | exit(128); |
| 513 | } |
| 514 | if (path) { |
Alex Riesen | 691f1a2 | 2009-04-29 23:22:56 +0200 | [diff] [blame] | 515 | unlink_or_warn(path); |
Jeff King | 3927bbe | 2008-12-06 14:40:34 -0500 | [diff] [blame] | 516 | free(path); |
| 517 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 518 | } |
| 519 | |
Junio C Hamano | bd46c9a | 2007-11-22 23:16:51 -0800 | [diff] [blame] | 520 | struct msg_arg { |
| 521 | int given; |
| 522 | struct strbuf buf; |
| 523 | }; |
| 524 | |
| 525 | static int parse_msg_arg(const struct option *opt, const char *arg, int unset) |
| 526 | { |
| 527 | struct msg_arg *msg = opt->value; |
| 528 | |
| 529 | if (!arg) |
| 530 | return -1; |
| 531 | if (msg->buf.len) |
| 532 | strbuf_addstr(&(msg->buf), "\n\n"); |
| 533 | strbuf_addstr(&(msg->buf), arg); |
| 534 | msg->given = 1; |
| 535 | return 0; |
| 536 | } |
| 537 | |
Michael Schubert | 4f0accd | 2011-05-10 01:36:36 +0200 | [diff] [blame] | 538 | static int strbuf_check_tag_ref(struct strbuf *sb, const char *name) |
| 539 | { |
| 540 | if (name[0] == '-') |
Michael Haggerty | 8d9c501 | 2011-09-15 23:10:25 +0200 | [diff] [blame] | 541 | return -1; |
Michael Schubert | 4f0accd | 2011-05-10 01:36:36 +0200 | [diff] [blame] | 542 | |
| 543 | strbuf_reset(sb); |
| 544 | strbuf_addf(sb, "refs/tags/%s", name); |
| 545 | |
Michael Haggerty | 8d9c501 | 2011-09-15 23:10:25 +0200 | [diff] [blame] | 546 | return check_refname_format(sb->buf, 0); |
Michael Schubert | 4f0accd | 2011-05-10 01:36:36 +0200 | [diff] [blame] | 547 | } |
| 548 | |
Ramsay Jones | 0975a50 | 2012-02-13 18:24:41 +0000 | [diff] [blame] | 549 | static int parse_opt_points_at(const struct option *opt __attribute__((unused)), |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 550 | const char *arg, int unset) |
| 551 | { |
| 552 | unsigned char sha1[20]; |
| 553 | |
| 554 | if (unset) { |
| 555 | sha1_array_clear(&points_at); |
| 556 | return 0; |
| 557 | } |
| 558 | if (!arg) |
| 559 | return error(_("switch 'points-at' requires an object")); |
| 560 | if (get_sha1(arg, sha1)) |
| 561 | return error(_("malformed object name '%s'"), arg); |
| 562 | sha1_array_append(&points_at, sha1); |
| 563 | return 0; |
| 564 | } |
| 565 | |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 566 | static int parse_opt_sort(const struct option *opt, const char *arg, int unset) |
| 567 | { |
| 568 | int *sort = opt->value; |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 569 | |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 570 | return parse_sort_string(NULL, arg, sort); |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 571 | } |
| 572 | |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 573 | int cmd_tag(int argc, const char **argv, const char *prefix) |
| 574 | { |
Brandon Casey | f285a2d | 2008-10-09 14:12:12 -0500 | [diff] [blame] | 575 | struct strbuf buf = STRBUF_INIT; |
Michael Schubert | 4f0accd | 2011-05-10 01:36:36 +0200 | [diff] [blame] | 576 | struct strbuf ref = STRBUF_INIT; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 577 | unsigned char object[20], prev[20]; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 578 | const char *object_ref, *tag; |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 579 | struct create_tag_options opt; |
| 580 | char *cleanup_arg = NULL; |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 581 | int annotate = 0, force = 0, lines = -1; |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 582 | int cmdmode = 0; |
Junio C Hamano | dbd0f5c | 2008-08-06 11:43:47 -0700 | [diff] [blame] | 583 | const char *msgfile = NULL, *keyid = NULL; |
Junio C Hamano | bd46c9a | 2007-11-22 23:16:51 -0800 | [diff] [blame] | 584 | struct msg_arg msg = { 0, STRBUF_INIT }; |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 585 | struct commit_list *with_commit = NULL; |
Ronnie Sahlberg | e5074bf | 2014-04-16 15:30:41 -0700 | [diff] [blame] | 586 | struct ref_transaction *transaction; |
| 587 | struct strbuf err = STRBUF_INIT; |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 588 | struct option options[] = { |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 589 | OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 590 | { OPTION_INTEGER, 'n', NULL, &lines, N_("n"), |
| 591 | N_("print <n> lines of each tag message"), |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 592 | PARSE_OPT_OPTARG, NULL, 1 }, |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 593 | OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'), |
| 594 | OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'), |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 595 | |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 596 | OPT_GROUP(N_("Tag creation options")), |
Stefan Beller | d5d09d4 | 2013-08-03 13:51:19 +0200 | [diff] [blame] | 597 | OPT_BOOL('a', "annotate", &annotate, |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 598 | N_("annotated tag, needs a message")), |
| 599 | OPT_CALLBACK('m', "message", &msg, N_("message"), |
| 600 | N_("tag message"), parse_msg_arg), |
| 601 | OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), |
Stefan Beller | d5d09d4 | 2013-08-03 13:51:19 +0200 | [diff] [blame] | 602 | OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 603 | OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"), |
| 604 | N_("how to strip spaces and #comments from message")), |
Junio C Hamano | e703d71 | 2014-03-23 15:58:12 -0700 | [diff] [blame] | 605 | OPT_STRING('u', "local-user", &keyid, N_("key-id"), |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 606 | N_("use another key to sign the tag")), |
| 607 | OPT__FORCE(&force, N_("replace the tag if exists")), |
| 608 | OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 609 | { |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 610 | OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"), |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 611 | PARSE_OPT_NONEG, parse_opt_sort |
| 612 | }, |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 613 | |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 614 | OPT_GROUP(N_("Tag listing options")), |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 615 | { |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 616 | OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"), |
| 617 | N_("print only tags that contain the commit"), |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 618 | PARSE_OPT_LASTARG_DEFAULT, |
| 619 | parse_opt_with_commit, (intptr_t)"HEAD", |
| 620 | }, |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 621 | { |
Junio C Hamano | b0bc136 | 2014-03-07 12:07:35 -0800 | [diff] [blame] | 622 | OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"), |
| 623 | N_("print only tags that contain the commit"), |
| 624 | PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT, |
| 625 | parse_opt_with_commit, (intptr_t)"HEAD", |
| 626 | }, |
| 627 | { |
Nguyễn Thái Ngọc Duy | c88bba1 | 2012-08-20 19:32:47 +0700 | [diff] [blame] | 628 | OPTION_CALLBACK, 0, "points-at", NULL, N_("object"), |
| 629 | N_("print only tags of the object"), 0, parse_opt_points_at |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 630 | }, |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 631 | OPT_END() |
| 632 | }; |
| 633 | |
Johannes Schindelin | ef90d6d | 2008-05-14 18:46:53 +0100 | [diff] [blame] | 634 | git_config(git_tag_config, NULL); |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 635 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 636 | memset(&opt, 0, sizeof(opt)); |
| 637 | |
Stephen Boyd | 3778292 | 2009-05-23 11:53:12 -0700 | [diff] [blame] | 638 | argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0); |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 639 | |
Linus Torvalds | be15f50 | 2007-12-10 20:08:06 -0800 | [diff] [blame] | 640 | if (keyid) { |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 641 | opt.sign = 1; |
Junio C Hamano | 2f47eae | 2011-09-07 21:19:47 -0700 | [diff] [blame] | 642 | set_signing_key(keyid); |
Linus Torvalds | be15f50 | 2007-12-10 20:08:06 -0800 | [diff] [blame] | 643 | } |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 644 | if (opt.sign) |
Junio C Hamano | c1a41b9 | 2007-11-25 15:21:42 -0800 | [diff] [blame] | 645 | annotate = 1; |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 646 | if (argc == 0 && !cmdmode) |
| 647 | cmdmode = 'l'; |
Junio C Hamano | c1a41b9 | 2007-11-25 15:21:42 -0800 | [diff] [blame] | 648 | |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 649 | if ((annotate || msg.given || msgfile || force) && (cmdmode != 0)) |
Samuel Tardieu | 6fa8342 | 2008-11-05 00:20:31 +0100 | [diff] [blame] | 650 | usage_with_options(git_tag_usage, options); |
| 651 | |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 652 | finalize_colopts(&colopts, -1); |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 653 | if (cmdmode == 'l' && lines != -1) { |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 654 | if (explicitly_enable_column(colopts)) |
| 655 | die(_("--column and -n are incompatible")); |
| 656 | colopts = 0; |
| 657 | } |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 658 | if (cmdmode == 'l') { |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 659 | int ret; |
| 660 | if (column_active(colopts)) { |
| 661 | struct column_options copts; |
| 662 | memset(&copts, 0, sizeof(copts)); |
| 663 | copts.padding = 2; |
| 664 | run_column_filter(colopts, &copts); |
| 665 | } |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 666 | if (lines != -1 && tag_sort) |
Nguyễn Thái Ngọc Duy | 9ef176b | 2014-02-27 19:56:52 +0700 | [diff] [blame] | 667 | die(_("--sort and -n are incompatible")); |
Jacob Keller | b150794 | 2014-07-16 14:48:02 -0700 | [diff] [blame] | 668 | ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort); |
Nguyễn Thái Ngọc Duy | d96e3c1 | 2012-04-13 17:54:41 +0700 | [diff] [blame] | 669 | if (column_active(colopts)) |
| 670 | stop_column_filter(); |
| 671 | return ret; |
| 672 | } |
Samuel Tardieu | 6fa8342 | 2008-11-05 00:20:31 +0100 | [diff] [blame] | 673 | if (lines != -1) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 674 | die(_("-n option is only allowed with -l.")); |
Jake Goulding | 32c35cf | 2009-01-26 09:13:25 -0500 | [diff] [blame] | 675 | if (with_commit) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 676 | die(_("--contains option is only allowed with -l.")); |
Tom Grennan | ae7706b | 2012-02-08 15:03:43 -0800 | [diff] [blame] | 677 | if (points_at.nr) |
| 678 | die(_("--points-at option is only allowed with -l.")); |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 679 | if (cmdmode == 'd') |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 680 | return for_each_tag_name(argv, delete_tag); |
Junio C Hamano | e6b722d | 2013-07-30 12:31:27 -0700 | [diff] [blame] | 681 | if (cmdmode == 'v') |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 682 | return for_each_tag_name(argv, verify_tag); |
| 683 | |
Junio C Hamano | bd46c9a | 2007-11-22 23:16:51 -0800 | [diff] [blame] | 684 | if (msg.given || msgfile) { |
| 685 | if (msg.given && msgfile) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 686 | die(_("only one -F or -m option is allowed.")); |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 687 | annotate = 1; |
Junio C Hamano | bd46c9a | 2007-11-22 23:16:51 -0800 | [diff] [blame] | 688 | if (msg.given) |
| 689 | strbuf_addbuf(&buf, &(msg.buf)); |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 690 | else { |
| 691 | if (!strcmp(msgfile, "-")) { |
Pierre Habouzit | 387e7e1 | 2007-09-27 15:25:55 +0200 | [diff] [blame] | 692 | if (strbuf_read(&buf, 0, 1024) < 0) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 693 | die_errno(_("cannot read '%s'"), msgfile); |
Pierre Habouzit | 387e7e1 | 2007-09-27 15:25:55 +0200 | [diff] [blame] | 694 | } else { |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 695 | if (strbuf_read_file(&buf, msgfile, 1024) < 0) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 696 | die_errno(_("could not open or read '%s'"), |
Thomas Rast | d824cbb | 2009-06-27 17:58:46 +0200 | [diff] [blame] | 697 | msgfile); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 698 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 699 | } |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 700 | } |
| 701 | |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 702 | tag = argv[0]; |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 703 | |
Carlos Rica | 3968658 | 2007-11-09 14:42:56 +0100 | [diff] [blame] | 704 | object_ref = argc == 2 ? argv[1] : "HEAD"; |
| 705 | if (argc > 2) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 706 | die(_("too many params")); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 707 | |
| 708 | if (get_sha1(object_ref, object)) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 709 | die(_("Failed to resolve '%s' as a valid ref."), object_ref); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 710 | |
Michael Schubert | 4f0accd | 2011-05-10 01:36:36 +0200 | [diff] [blame] | 711 | if (strbuf_check_tag_ref(&ref, tag)) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 712 | die(_("'%s' is not a valid tag name."), tag); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 713 | |
Nguyễn Thái Ngọc Duy | c689332 | 2011-11-13 17:22:14 +0700 | [diff] [blame] | 714 | if (read_ref(ref.buf, prev)) |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 715 | hashclr(prev); |
| 716 | else if (!force) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 717 | die(_("tag '%s' already exists"), tag); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 718 | |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 719 | opt.message_given = msg.given || msgfile; |
| 720 | |
| 721 | if (!cleanup_arg || !strcmp(cleanup_arg, "strip")) |
| 722 | opt.cleanup_mode = CLEANUP_ALL; |
| 723 | else if (!strcmp(cleanup_arg, "verbatim")) |
| 724 | opt.cleanup_mode = CLEANUP_NONE; |
| 725 | else if (!strcmp(cleanup_arg, "whitespace")) |
| 726 | opt.cleanup_mode = CLEANUP_SPACE; |
| 727 | else |
| 728 | die(_("Invalid cleanup mode %s"), cleanup_arg); |
| 729 | |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 730 | if (annotate) |
Kirill A. Shutemov | d3e0598 | 2011-12-07 05:01:45 +0200 | [diff] [blame] | 731 | create_tag(object, tag, &buf, &opt, prev, object); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 732 | |
Ronnie Sahlberg | e5074bf | 2014-04-16 15:30:41 -0700 | [diff] [blame] | 733 | transaction = ref_transaction_begin(&err); |
| 734 | if (!transaction || |
| 735 | ref_transaction_update(transaction, ref.buf, object, prev, |
Ronnie Sahlberg | db7516a | 2014-04-30 12:22:42 -0700 | [diff] [blame^] | 736 | 0, 1, NULL, &err) || |
| 737 | ref_transaction_commit(transaction, &err)) |
Ronnie Sahlberg | e5074bf | 2014-04-16 15:30:41 -0700 | [diff] [blame] | 738 | die("%s", err.buf); |
| 739 | ref_transaction_free(transaction); |
Phil Hord | 3ae851e | 2013-03-12 19:13:41 -0400 | [diff] [blame] | 740 | if (force && !is_null_sha1(prev) && hashcmp(prev, object)) |
Ævar Arnfjörð Bjarmason | d08ebf9 | 2011-02-22 23:42:09 +0000 | [diff] [blame] | 741 | printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV)); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 742 | |
Ronnie Sahlberg | e5074bf | 2014-04-16 15:30:41 -0700 | [diff] [blame] | 743 | strbuf_release(&err); |
Pierre Habouzit | fd17f5b | 2007-09-10 12:35:09 +0200 | [diff] [blame] | 744 | strbuf_release(&buf); |
Michael Schubert | 4f0accd | 2011-05-10 01:36:36 +0200 | [diff] [blame] | 745 | strbuf_release(&ref); |
Carlos Rica | 62e09ce | 2007-07-20 01:42:28 +0200 | [diff] [blame] | 746 | return 0; |
| 747 | } |