blob: 31e6b155c04084fc5a61065f6bf5d8360bb542a6 [file] [log] [blame]
Carlos Rica62e09ce2007-07-20 01:42:28 +02001/*
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"
Brandon Williamsb2141fc2017-06-14 11:07:36 -070010#include "config.h"
Carlos Rica62e09ce2007-07-20 01:42:28 +020011#include "builtin.h"
12#include "refs.h"
Stefan Bellercbd53a22018-05-15 16:42:15 -070013#include "object-store.h"
Carlos Rica62e09ce2007-07-20 01:42:28 +020014#include "tag.h"
15#include "run-command.h"
Carlos Rica39686582007-11-09 14:42:56 +010016#include "parse-options.h"
Jeff Kingffc4b802011-06-11 19:04:08 +000017#include "diff.h"
18#include "revision.h"
Junio C Hamano2f47eae2011-09-07 21:19:47 -070019#include "gpg-interface.h"
Jeff Kingfe299ec2020-03-30 10:03:46 -040020#include "oid-array.h"
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +070021#include "column.h"
Karthik Nayakac4cc862015-09-10 21:18:27 +053022#include "ref-filter.h"
Carlos Rica62e09ce2007-07-20 01:42:28 +020023
Carlos Rica39686582007-11-09 14:42:56 +010024static const char * const git_tag_usage[] = {
Denton Liu01dc8012019-04-04 11:25:13 -070025 N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
Ævar Arnfjörð Bjarmason5d701982021-09-13 02:13:19 +020026 " <tagname> [<head>]"),
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +070027 N_("git tag -d <tagname>..."),
Denton Liu01dc8012019-04-04 11:25:13 -070028 N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
Ævar Arnfjörð Bjarmason5d701982021-09-13 02:13:19 +020029 " [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
Lukas Puehringer07d347c2017-01-17 18:37:21 -050030 N_("git tag -v [--format=<format>] <tagname>..."),
Carlos Rica39686582007-11-09 14:42:56 +010031 NULL
32};
Carlos Rica62e09ce2007-07-20 01:42:28 +020033
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +070034static unsigned int colopts;
Laurent Arnoud61c2fe02016-03-22 21:41:26 +010035static int force_sign_annotate;
Tigran Mkrtchyan1c6b5652019-06-05 23:33:21 +020036static int config_sign_tag = -1; /* unspecified */
Tom Grennanae7706b2012-02-08 15:03:43 -080037
Jeff King4a68e362017-07-13 11:01:18 -040038static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
39 struct ref_format *format)
Jeff King588d0e82011-06-20 12:59:28 -040040{
Karthik Nayakac4cc862015-09-10 21:18:27 +053041 struct ref_array array;
ZheNing Hu844c3f02021-04-20 16:52:11 +000042 struct strbuf output = STRBUF_INIT;
43 struct strbuf err = STRBUF_INIT;
Karthik Nayakdf094742015-09-11 20:36:46 +053044 char *to_free = NULL;
Jeff Kingca516992012-02-06 03:13:12 -050045 int i;
Jeff Kingca516992012-02-06 03:13:12 -050046
Karthik Nayakac4cc862015-09-10 21:18:27 +053047 memset(&array, 0, sizeof(array));
Jeff Kingca516992012-02-06 03:13:12 -050048
Karthik Nayakac4cc862015-09-10 21:18:27 +053049 if (filter->lines == -1)
50 filter->lines = 0;
Junio C Hamano31fd8d72012-02-06 10:13:27 -080051
Jeff King4a68e362017-07-13 11:01:18 -040052 if (!format->format) {
Karthik Nayakdf094742015-09-11 20:36:46 +053053 if (filter->lines) {
54 to_free = xstrfmt("%s %%(contents:lines=%d)",
Karthik Nayak17938f12017-01-10 14:19:46 +053055 "%(align:15)%(refname:lstrip=2)%(end)",
Karthik Nayakdf094742015-09-11 20:36:46 +053056 filter->lines);
Jeff King4a68e362017-07-13 11:01:18 -040057 format->format = to_free;
Karthik Nayakdf094742015-09-11 20:36:46 +053058 } else
Jeff King4a68e362017-07-13 11:01:18 -040059 format->format = "%(refname:lstrip=2)";
Carlos Rica62e09ce2007-07-20 01:42:28 +020060 }
61
Jeff King2eda0102017-07-13 10:56:10 -040062 if (verify_ref_format(format))
63 die(_("unable to parse format string"));
Karthik Nayak008ed7d2015-10-18 15:55:04 +053064 filter->with_commit_tag_algo = 1;
Karthik Nayakb7cc53e2015-09-11 20:36:16 +053065 filter_refs(&array, filter, FILTER_REFS_TAGS);
66 ref_array_sort(sorting, &array);
Carlos Rica62e09ce2007-07-20 01:42:28 +020067
ZheNing Hu22f69a82021-04-19 11:28:44 +000068 for (i = 0; i < array.nr; i++) {
ZheNing Hu844c3f02021-04-20 16:52:11 +000069 strbuf_reset(&output);
70 strbuf_reset(&err);
ZheNing Hu22f69a82021-04-19 11:28:44 +000071 if (format_ref_array_item(array.items[i], format, &output, &err))
72 die("%s", err.buf);
73 fwrite(output.buf, 1, output.len, stdout);
74 putchar('\n');
ZheNing Hu22f69a82021-04-19 11:28:44 +000075 }
ZheNing Hu844c3f02021-04-20 16:52:11 +000076
77 strbuf_release(&err);
78 strbuf_release(&output);
Karthik Nayakb7cc53e2015-09-11 20:36:16 +053079 ref_array_clear(&array);
80 free(to_free);
Nguyễn Thái Ngọc Duy9ef176b2014-02-27 19:56:52 +070081
Carlos Rica62e09ce2007-07-20 01:42:28 +020082 return 0;
83}
84
Carlos Ricae317cfa2007-07-21 14:13:12 +020085typedef int (*each_tag_name_fn)(const char *name, const char *ref,
Phil Hord81989072021-01-20 19:23:32 -080086 const struct object_id *oid, void *cb_data);
Carlos Rica62e09ce2007-07-20 01:42:28 +020087
Lukas Puehringer07d347c2017-01-17 18:37:21 -050088static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
Phil Hord81989072021-01-20 19:23:32 -080089 void *cb_data)
Carlos Rica62e09ce2007-07-20 01:42:28 +020090{
91 const char **p;
Jeff King7f897b62017-03-28 15:46:30 -040092 struct strbuf ref = STRBUF_INIT;
Carlos Rica62e09ce2007-07-20 01:42:28 +020093 int had_error = 0;
brian m. carlson7422ab52017-05-06 22:10:08 +000094 struct object_id oid;
Carlos Rica62e09ce2007-07-20 01:42:28 +020095
96 for (p = argv; *p; p++) {
Jeff King7f897b62017-03-28 15:46:30 -040097 strbuf_reset(&ref);
98 strbuf_addf(&ref, "refs/tags/%s", *p);
brian m. carlson34c290a2017-10-15 22:06:56 +000099 if (read_ref(ref.buf, &oid)) {
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000100 error(_("tag '%s' not found."), *p);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200101 had_error = 1;
102 continue;
103 }
brian m. carlson7422ab52017-05-06 22:10:08 +0000104 if (fn(*p, ref.buf, &oid, cb_data))
Carlos Rica62e09ce2007-07-20 01:42:28 +0200105 had_error = 1;
106 }
Jeff King7f897b62017-03-28 15:46:30 -0400107 strbuf_release(&ref);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200108 return had_error;
109}
110
Phil Hord81989072021-01-20 19:23:32 -0800111static int collect_tags(const char *name, const char *ref,
112 const struct object_id *oid, void *cb_data)
Carlos Rica62e09ce2007-07-20 01:42:28 +0200113{
Phil Hord81989072021-01-20 19:23:32 -0800114 struct string_list *ref_list = cb_data;
115
116 string_list_append(ref_list, ref);
117 ref_list->items[ref_list->nr - 1].util = oiddup(oid);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200118 return 0;
119}
120
Phil Hord81989072021-01-20 19:23:32 -0800121static int delete_tags(const char **argv)
122{
123 int result;
124 struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
125 struct string_list_item *item;
126
127 result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
128 if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
129 result = 1;
130
131 for_each_string_list_item(item, &refs_to_delete) {
132 const char *name = item->string;
133 struct object_id *oid = item->util;
134 if (!ref_exists(name))
135 printf(_("Deleted tag '%s' (was %s)\n"),
136 item->string + 10,
137 find_unique_abbrev(oid, DEFAULT_ABBREV));
138
139 free(oid);
140 }
141 string_list_clear(&refs_to_delete, 0);
142 return result;
143}
144
Carlos Rica62e09ce2007-07-20 01:42:28 +0200145static int verify_tag(const char *name, const char *ref,
Phil Hord81989072021-01-20 19:23:32 -0800146 const struct object_id *oid, void *cb_data)
Carlos Rica62e09ce2007-07-20 01:42:28 +0200147{
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500148 int flags;
ZheNing Hue85fcb32021-07-26 03:26:49 +0000149 struct ref_format *format = cb_data;
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500150 flags = GPG_VERIFY_VERBOSE;
151
Jeff King4a68e362017-07-13 11:01:18 -0400152 if (format->format)
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500153 flags = GPG_VERIFY_OMIT_STATUS;
154
Stefan Beller84571762017-07-12 17:44:15 -0700155 if (gpg_verify_tag(oid, name, flags))
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500156 return -1;
157
Jeff King4a68e362017-07-13 11:01:18 -0400158 if (format->format)
Jeff King53df97a2018-04-06 14:58:32 -0400159 pretty_print_ref(name, oid, format);
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500160
161 return 0;
Carlos Rica62e09ce2007-07-20 01:42:28 +0200162}
163
Pierre Habouzitfd17f5b2007-09-10 12:35:09 +0200164static int do_sign(struct strbuf *buffer)
Carlos Rica62e09ce2007-07-20 01:42:28 +0200165{
Junio C Hamano2f47eae2011-09-07 21:19:47 -0700166 return sign_buffer(buffer, buffer, get_signing_key());
Carlos Rica62e09ce2007-07-20 01:42:28 +0200167}
168
169static const char tag_template[] =
Thorsten Glaserd78f3402013-08-30 00:03:10 +0200170 N_("\nWrite a message for tag:\n %s\n"
Junio C Hamanoeff80a92013-01-16 20:18:48 +0100171 "Lines starting with '%c' will be ignored.\n");
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200172
173static const char tag_template_nocleanup[] =
Thorsten Glaserd78f3402013-08-30 00:03:10 +0200174 N_("\nWrite a message for tag:\n %s\n"
Junio C Hamanoeff80a92013-01-16 20:18:48 +0100175 "Lines starting with '%c' will be kept; you may remove them"
176 " yourself if you want to.\n");
Carlos Rica62e09ce2007-07-20 01:42:28 +0200177
Johannes Schindelinef90d6d2008-05-14 18:46:53 +0100178static int git_tag_config(const char *var, const char *value, void *cb)
Carlos Rica62e09ce2007-07-20 01:42:28 +0200179{
Jacob Kellerb1507942014-07-16 14:48:02 -0700180 int status;
181
Tigran Mkrtchyan1c6b5652019-06-05 23:33:21 +0200182 if (!strcmp(var, "tag.gpgsign")) {
183 config_sign_tag = git_config_bool(var, value);
184 return 0;
185 }
186
Jacob Kellerb1507942014-07-16 14:48:02 -0700187 if (!strcmp(var, "tag.sort")) {
188 if (!value)
189 return config_error_nonbool(var);
Junio C Hamano98e7ab62021-10-20 12:23:53 -0700190 string_list_append(cb, value);
Jacob Kellerb1507942014-07-16 14:48:02 -0700191 return 0;
192 }
193
194 status = git_gpg_config(var, value, cb);
Junio C Hamano2f47eae2011-09-07 21:19:47 -0700195 if (status)
196 return status;
Laurent Arnoud61c2fe02016-03-22 21:41:26 +0100197 if (!strcmp(var, "tag.forcesignannotated")) {
198 force_sign_annotate = git_config_bool(var, value);
199 return 0;
200 }
201
Christian Couder59556542013-11-30 21:55:40 +0100202 if (starts_with(var, "column."))
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700203 return git_column_config(var, value, "tag", &colopts);
Jeff Kingb521fd12017-10-13 13:26:02 -0400204 return git_color_default_config(var, value, cb);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200205}
206
brian m. carlson7422ab52017-05-06 22:10:08 +0000207static void write_tag_body(int fd, const struct object_id *oid)
Mike Hommeybab81182007-11-04 01:11:14 +0100208{
209 unsigned long size;
210 enum object_type type;
brian m. carlson482c1192021-02-11 02:08:03 +0000211 char *buf, *sp, *orig;
212 struct strbuf payload = STRBUF_INIT;
213 struct strbuf signature = STRBUF_INIT;
Mike Hommeybab81182007-11-04 01:11:14 +0100214
brian m. carlson482c1192021-02-11 02:08:03 +0000215 orig = buf = read_object_file(oid, &type, &size);
Mike Hommeybab81182007-11-04 01:11:14 +0100216 if (!buf)
217 return;
brian m. carlson482c1192021-02-11 02:08:03 +0000218 if (parse_signature(buf, size, &payload, &signature)) {
219 buf = payload.buf;
220 size = payload.len;
221 }
Mike Hommeybab81182007-11-04 01:11:14 +0100222 /* skip header */
223 sp = strstr(buf, "\n\n");
224
225 if (!sp || !size || type != OBJ_TAG) {
226 free(buf);
227 return;
228 }
229 sp += 2; /* skip the 2 LFs */
brian m. carlson482c1192021-02-11 02:08:03 +0000230 write_or_die(fd, sp, buf + size - sp);
Mike Hommeybab81182007-11-04 01:11:14 +0100231
brian m. carlson482c1192021-02-11 02:08:03 +0000232 free(orig);
233 strbuf_release(&payload);
234 strbuf_release(&signature);
Mike Hommeybab81182007-11-04 01:11:14 +0100235}
236
brian m. carlson7422ab52017-05-06 22:10:08 +0000237static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
Jeff King3927bbe2008-12-06 14:40:34 -0500238{
239 if (sign && do_sign(buf) < 0)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000240 return error(_("unable to sign the tag"));
Ævar Arnfjörð Bjarmasonc80d2262022-02-05 00:48:26 +0100241 if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000242 return error(_("unable to write tag file"));
Jeff King3927bbe2008-12-06 14:40:34 -0500243 return 0;
244}
245
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200246struct create_tag_options {
247 unsigned int message_given:1;
Nicolas Morey-Chaisemartin9eed6e42018-02-06 09:36:24 +0100248 unsigned int use_editor:1;
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200249 unsigned int sign;
250 enum {
251 CLEANUP_NONE,
252 CLEANUP_SPACE,
253 CLEANUP_ALL
254 } cleanup_mode;
255};
256
Denton Liueea9c1e2019-04-04 11:25:15 -0700257static const char message_advice_nested_tag[] =
Denton Liua54b2ab2019-05-08 15:16:41 -0400258 N_("You have created a nested tag. The object referred to by your new tag is\n"
Denton Liueea9c1e2019-04-04 11:25:15 -0700259 "already a tag. If you meant to tag the object that it points to, use:\n"
260 "\n"
261 "\tgit tag -f %s %s^{}");
262
263static void create_tag(const struct object_id *object, const char *object_ref,
264 const char *tag,
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200265 struct strbuf *buf, struct create_tag_options *opt,
brian m. carlson7422ab52017-05-06 22:10:08 +0000266 struct object_id *prev, struct object_id *result)
Carlos Rica62e09ce2007-07-20 01:42:28 +0200267{
268 enum object_type type;
Jeff Kingb0ceab92017-03-28 15:46:23 -0400269 struct strbuf header = STRBUF_INIT;
Jeff King3927bbe2008-12-06 14:40:34 -0500270 char *path = NULL;
Carlos Rica62e09ce2007-07-20 01:42:28 +0200271
Stefan Beller0df8e962018-04-25 11:20:59 -0700272 type = oid_object_info(the_repository, object, NULL);
Carlos Ricae317cfa2007-07-21 14:13:12 +0200273 if (type <= OBJ_NONE)
Denton Liu01dc8012019-04-04 11:25:13 -0700274 die(_("bad object type."));
Carlos Rica62e09ce2007-07-20 01:42:28 +0200275
Heba Walyf665d632020-03-02 20:02:00 +0000276 if (type == OBJ_TAG)
277 advise_if_enabled(ADVICE_NESTED_TAG, _(message_advice_nested_tag),
278 tag, object_ref);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200279
Jeff Kingb0ceab92017-03-28 15:46:23 -0400280 strbuf_addf(&header,
281 "object %s\n"
282 "type %s\n"
283 "tag %s\n"
284 "tagger %s\n\n",
brian m. carlson7422ab52017-05-06 22:10:08 +0000285 oid_to_hex(object),
Brandon Williamsdebca9d2018-02-14 10:59:24 -0800286 type_name(type),
Jeff Kingb0ceab92017-03-28 15:46:23 -0400287 tag,
288 git_committer_info(IDENT_STRICT));
Carlos Rica62e09ce2007-07-20 01:42:28 +0200289
Nicolas Morey-Chaisemartin9eed6e42018-02-06 09:36:24 +0100290 if (!opt->message_given || opt->use_editor) {
Carlos Rica62e09ce2007-07-20 01:42:28 +0200291 int fd;
292
293 /* write the template message before editing: */
Alex Riesena4f34cb2008-10-27 11:22:09 +0100294 path = git_pathdup("TAG_EDITMSG");
René Scharfe66e905b2021-08-25 22:16:46 +0200295 fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
Mike Hommeybab81182007-11-04 01:11:14 +0100296
Nicolas Morey-Chaisemartin9eed6e42018-02-06 09:36:24 +0100297 if (opt->message_given) {
298 write_or_die(fd, buf->buf, buf->len);
299 strbuf_reset(buf);
300 } else if (!is_null_oid(prev)) {
Mike Hommeybab81182007-11-04 01:11:14 +0100301 write_tag_body(fd, prev);
Junio C Hamanoeff80a92013-01-16 20:18:48 +0100302 } else {
303 struct strbuf buf = STRBUF_INIT;
304 strbuf_addch(&buf, '\n');
305 if (opt->cleanup_mode == CLEANUP_ALL)
Thorsten Glaserd78f3402013-08-30 00:03:10 +0200306 strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
Junio C Hamanoeff80a92013-01-16 20:18:48 +0100307 else
Thorsten Glaserd78f3402013-08-30 00:03:10 +0200308 strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
Junio C Hamanoeff80a92013-01-16 20:18:48 +0100309 write_or_die(fd, buf.buf, buf.len);
310 strbuf_release(&buf);
311 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200312 close(fd);
313
Stephan Beyer71982032008-07-25 18:28:42 +0200314 if (launch_editor(path, buf, NULL)) {
315 fprintf(stderr,
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000316 _("Please supply the message using either -m or -F option.\n"));
Stephan Beyer71982032008-07-25 18:28:42 +0200317 exit(1);
318 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200319 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200320
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200321 if (opt->cleanup_mode != CLEANUP_NONE)
Tobias Klauser63af4a82015-10-16 17:16:42 +0200322 strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200323
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200324 if (!opt->message_given && !buf->len)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000325 die(_("no tag message?"));
Carlos Rica62e09ce2007-07-20 01:42:28 +0200326
Jeff Kingb0ceab92017-03-28 15:46:23 -0400327 strbuf_insert(buf, 0, header.buf, header.len);
328 strbuf_release(&header);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200329
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200330 if (build_tag_object(buf, opt->sign, result) < 0) {
Jeff King3927bbe2008-12-06 14:40:34 -0500331 if (path)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000332 fprintf(stderr, _("The tag message has been left in %s\n"),
Jeff King3927bbe2008-12-06 14:40:34 -0500333 path);
334 exit(128);
335 }
336 if (path) {
Alex Riesen691f1a22009-04-29 23:22:56 +0200337 unlink_or_warn(path);
Jeff King3927bbe2008-12-06 14:40:34 -0500338 free(path);
339 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200340}
341
brian m. carlson7422ab52017-05-06 22:10:08 +0000342static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100343{
344 enum object_type type;
345 struct commit *c;
346 char *buf;
347 unsigned long size;
348 int subject_len = 0;
349 const char *subject_start;
350
351 char *rla = getenv("GIT_REFLOG_ACTION");
352 if (rla) {
353 strbuf_addstr(sb, rla);
354 } else {
Jean-Noel Avilac3027be2017-04-30 23:32:47 +0200355 strbuf_addstr(sb, "tag: tagging ");
brian m. carlson30e677e2018-03-12 02:27:28 +0000356 strbuf_add_unique_abbrev(sb, oid, DEFAULT_ABBREV);
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100357 }
358
359 strbuf_addstr(sb, " (");
Stefan Beller0df8e962018-04-25 11:20:59 -0700360 type = oid_object_info(the_repository, oid, NULL);
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100361 switch (type) {
362 default:
Jean-Noel Avilac3027be2017-04-30 23:32:47 +0200363 strbuf_addstr(sb, "object of unknown type");
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100364 break;
365 case OBJ_COMMIT:
brian m. carlsonb4f5aca2018-03-12 02:27:53 +0000366 if ((buf = read_object_file(oid, &type, &size)) != NULL) {
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100367 subject_len = find_commit_subject(buf, &subject_start);
368 strbuf_insert(sb, sb->len, subject_start, subject_len);
369 } else {
Jean-Noel Avilac3027be2017-04-30 23:32:47 +0200370 strbuf_addstr(sb, "commit object");
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100371 }
372 free(buf);
373
Stefan Beller2122f672018-06-28 18:21:58 -0700374 if ((c = lookup_commit_reference(the_repository, oid)) != NULL)
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100375 strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
376 break;
377 case OBJ_TREE:
Jean-Noel Avilac3027be2017-04-30 23:32:47 +0200378 strbuf_addstr(sb, "tree object");
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100379 break;
380 case OBJ_BLOB:
Jean-Noel Avilac3027be2017-04-30 23:32:47 +0200381 strbuf_addstr(sb, "blob object");
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100382 break;
383 case OBJ_TAG:
Jean-Noel Avilac3027be2017-04-30 23:32:47 +0200384 strbuf_addstr(sb, "other tag object");
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100385 break;
386 }
387 strbuf_addch(sb, ')');
388}
389
Junio C Hamanobd46c9a2007-11-22 23:16:51 -0800390struct msg_arg {
391 int given;
392 struct strbuf buf;
393};
394
395static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
396{
397 struct msg_arg *msg = opt->value;
398
Jeff King517fe802018-11-05 01:45:42 -0500399 BUG_ON_OPT_NEG(unset);
400
Junio C Hamanobd46c9a2007-11-22 23:16:51 -0800401 if (!arg)
402 return -1;
403 if (msg->buf.len)
404 strbuf_addstr(&(msg->buf), "\n\n");
405 strbuf_addstr(&(msg->buf), arg);
406 msg->given = 1;
407 return 0;
408}
409
Michael Schubert4f0accd2011-05-10 01:36:36 +0200410static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
411{
412 if (name[0] == '-')
Michael Haggerty8d9c5012011-09-15 23:10:25 +0200413 return -1;
Michael Schubert4f0accd2011-05-10 01:36:36 +0200414
415 strbuf_reset(sb);
416 strbuf_addf(sb, "refs/tags/%s", name);
417
Michael Haggerty8d9c5012011-09-15 23:10:25 +0200418 return check_refname_format(sb->buf, 0);
Michael Schubert4f0accd2011-05-10 01:36:36 +0200419}
420
Carlos Rica62e09ce2007-07-20 01:42:28 +0200421int cmd_tag(int argc, const char **argv, const char *prefix)
422{
Brandon Caseyf285a2d2008-10-09 14:12:12 -0500423 struct strbuf buf = STRBUF_INIT;
Michael Schubert4f0accd2011-05-10 01:36:36 +0200424 struct strbuf ref = STRBUF_INIT;
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100425 struct strbuf reflog_msg = STRBUF_INIT;
brian m. carlson7422ab52017-05-06 22:10:08 +0000426 struct object_id object, prev;
Carlos Rica62e09ce2007-07-20 01:42:28 +0200427 const char *object_ref, *tag;
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200428 struct create_tag_options opt;
429 char *cleanup_arg = NULL;
David Turner144c76f2015-07-21 17:04:55 -0400430 int create_reflog = 0;
Karthik Nayakac4cc862015-09-10 21:18:27 +0530431 int annotate = 0, force = 0;
Laurent Arnoud61c2fe02016-03-22 21:41:26 +0100432 int cmdmode = 0, create_tag_object = 0;
Junio C Hamanodbd0f5c2008-08-06 11:43:47 -0700433 const char *msgfile = NULL, *keyid = NULL;
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200434 struct msg_arg msg = { .buf = STRBUF_INIT };
Ronnie Sahlberge5074bf2014-04-16 15:30:41 -0700435 struct ref_transaction *transaction;
436 struct strbuf err = STRBUF_INIT;
Karthik Nayakac4cc862015-09-10 21:18:27 +0530437 struct ref_filter filter;
Junio C Hamano98e7ab62021-10-20 12:23:53 -0700438 struct ref_sorting *sorting;
439 struct string_list sorting_options = STRING_LIST_INIT_DUP;
Jeff King4a68e362017-07-13 11:01:18 -0400440 struct ref_format format = REF_FORMAT_INIT;
Nguyễn Thái Ngọc Duy3bb16a82016-12-04 09:52:25 +0700441 int icase = 0;
Nicolas Morey-Chaisemartin9eed6e42018-02-06 09:36:24 +0100442 int edit_flag = 0;
Carlos Rica39686582007-11-09 14:42:56 +0100443 struct option options[] = {
Junio C Hamanoe6b722d2013-07-30 12:31:27 -0700444 OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
Karthik Nayakac4cc862015-09-10 21:18:27 +0530445 { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +0700446 N_("print <n> lines of each tag message"),
Carlos Rica39686582007-11-09 14:42:56 +0100447 PARSE_OPT_OPTARG, NULL, 1 },
Junio C Hamanoe6b722d2013-07-30 12:31:27 -0700448 OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
449 OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'),
Carlos Rica39686582007-11-09 14:42:56 +0100450
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +0700451 OPT_GROUP(N_("Tag creation options")),
Stefan Bellerd5d09d42013-08-03 13:51:19 +0200452 OPT_BOOL('a', "annotate", &annotate,
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +0700453 N_("annotated tag, needs a message")),
Denton Liu203c8532020-04-28 04:36:28 -0400454 OPT_CALLBACK_F('m', "message", &msg, N_("message"),
455 N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +0700456 OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
Nicolas Morey-Chaisemartin9eed6e42018-02-06 09:36:24 +0100457 OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
Stefan Bellerd5d09d42013-08-03 13:51:19 +0200458 OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
Denton Liuca04dc92019-04-17 11:23:26 +0100459 OPT_CLEANUP(&cleanup_arg),
Junio C Hamanoe703d712014-03-23 15:58:12 -0700460 OPT_STRING('u', "local-user", &keyid, N_("key-id"),
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +0700461 N_("use another key to sign the tag")),
Nguyễn Thái Ngọc Duy12247812018-02-09 18:01:42 +0700462 OPT__FORCE(&force, N_("replace the tag if exists"), 0),
Ralf Thielow98c32bd2015-09-11 18:04:13 +0200463 OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")),
Jeff Kingdd059c62015-03-12 14:15:09 -0400464
465 OPT_GROUP(N_("Tag listing options")),
Nguyễn Thái Ngọc Duyc88bba12012-08-20 19:32:47 +0700466 OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
Karthik Nayakac4cc862015-09-10 21:18:27 +0530467 OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
Ævar Arnfjörð Bjarmasonac3f5a32017-03-24 18:40:57 +0000468 OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
Karthik Nayakac4cc862015-09-10 21:18:27 +0530469 OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
Ævar Arnfjörð Bjarmasonac3f5a32017-03-24 18:40:57 +0000470 OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
Karthik Nayak52428602015-09-10 21:52:49 +0530471 OPT_MERGED(&filter, N_("print only tags that are merged")),
472 OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
Junio C Hamano98e7ab62021-10-20 12:23:53 -0700473 OPT_REF_SORT(&sorting_options),
Nguyễn Thái Ngọc Duy9ef176b2014-02-27 19:56:52 +0700474 {
Karthik Nayakac4cc862015-09-10 21:18:27 +0530475 OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
Ævar Arnfjörð Bjarmason1e0c3b62017-03-24 18:40:56 +0000476 N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
477 parse_opt_object_name, (intptr_t) "HEAD"
Tom Grennanae7706b2012-02-08 15:03:43 -0800478 },
Jeff King4a68e362017-07-13 11:01:18 -0400479 OPT_STRING( 0 , "format", &format.format, N_("format"),
480 N_("format to use for the output")),
Jeff King0c88bf52017-10-03 09:45:47 -0400481 OPT__COLOR(&format.use_color, N_("respect format colors")),
Nguyễn Thái Ngọc Duy3bb16a82016-12-04 09:52:25 +0700482 OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
Carlos Rica39686582007-11-09 14:42:56 +0100483 OPT_END()
484 };
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200485 int ret = 0;
Jean-Noël Avila408c5c52022-01-05 20:02:18 +0000486 const char *only_in_list = NULL;
Carlos Rica39686582007-11-09 14:42:56 +0100487
Karthik Nayak56b43602017-01-10 14:19:51 +0530488 setup_ref_filter_porcelain_msg();
489
Junio C Hamano98e7ab62021-10-20 12:23:53 -0700490 git_config(git_tag_config, &sorting_options);
Carlos Rica39686582007-11-09 14:42:56 +0100491
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200492 memset(&opt, 0, sizeof(opt));
Karthik Nayakac4cc862015-09-10 21:18:27 +0530493 memset(&filter, 0, sizeof(filter));
494 filter.lines = -1;
Tigran Mkrtchyan1c6b5652019-06-05 23:33:21 +0200495 opt.sign = -1;
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200496
Stephen Boyd37782922009-05-23 11:53:12 -0700497 argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
Carlos Rica39686582007-11-09 14:42:56 +0100498
Ævar Arnfjörð Bjarmason6a338142017-03-24 18:40:55 +0000499 if (!cmdmode) {
500 if (argc == 0)
501 cmdmode = 'l';
Ævar Arnfjörð Bjarmasonac3f5a32017-03-24 18:40:57 +0000502 else if (filter.with_commit || filter.no_commit ||
Aaron Lipman21bf9332020-09-15 22:08:40 -0400503 filter.reachable_from || filter.unreachable_from ||
504 filter.points_at.nr || filter.lines != -1)
Ævar Arnfjörð Bjarmason6a338142017-03-24 18:40:55 +0000505 cmdmode = 'l';
506 }
Junio C Hamanoc1a41b92007-11-25 15:21:42 -0800507
Martin Ågrende121ff2017-08-02 21:40:53 +0200508 if (cmdmode == 'l')
Martin Ågrenff1e7242017-08-02 21:40:54 +0200509 setup_auto_pager("tag", 1);
Martin Ågrende121ff2017-08-02 21:40:53 +0200510
Tigran Mkrtchyan1c6b5652019-06-05 23:33:21 +0200511 if (opt.sign == -1)
512 opt.sign = cmdmode ? 0 : config_sign_tag > 0;
513
514 if (keyid) {
515 opt.sign = 1;
516 set_signing_key(keyid);
517 }
518 create_tag_object = (opt.sign || annotate || msg.given || msgfile);
519
Laurent Arnoud61c2fe02016-03-22 21:41:26 +0100520 if ((create_tag_object || force) && (cmdmode != 0))
Samuel Tardieu6fa83422008-11-05 00:20:31 +0100521 usage_with_options(git_tag_usage, options);
522
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700523 finalize_colopts(&colopts, -1);
Karthik Nayakac4cc862015-09-10 21:18:27 +0530524 if (cmdmode == 'l' && filter.lines != -1) {
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700525 if (explicitly_enable_column(colopts))
Jean-Noël Avila12909b62022-01-05 20:02:16 +0000526 die(_("options '%s' and '%s' cannot be used together"), "--column", "-n");
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700527 colopts = 0;
528 }
Junio C Hamano98e7ab62021-10-20 12:23:53 -0700529 sorting = ref_sorting_options(&sorting_options);
Ævar Arnfjörð Bjarmason7c269a72021-01-07 10:51:51 +0100530 ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
Nguyễn Thái Ngọc Duy3bb16a82016-12-04 09:52:25 +0700531 filter.ignore_case = icase;
Junio C Hamanoe6b722d2013-07-30 12:31:27 -0700532 if (cmdmode == 'l') {
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700533 if (column_active(colopts)) {
534 struct column_options copts;
535 memset(&copts, 0, sizeof(copts));
536 copts.padding = 2;
537 run_column_filter(colopts, &copts);
538 }
Karthik Nayakac4cc862015-09-10 21:18:27 +0530539 filter.name_patterns = argv;
Jeff King4a68e362017-07-13 11:01:18 -0400540 ret = list_tags(&filter, sorting, &format);
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700541 if (column_active(colopts))
542 stop_column_filter();
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200543 goto cleanup;
Nguyễn Thái Ngọc Duyd96e3c12012-04-13 17:54:41 +0700544 }
Karthik Nayakac4cc862015-09-10 21:18:27 +0530545 if (filter.lines != -1)
Jean-Noël Avila408c5c52022-01-05 20:02:18 +0000546 only_in_list = "-n";
547 else if (filter.with_commit)
548 only_in_list = "--contains";
549 else if (filter.no_commit)
550 only_in_list = "--no-contains";
551 else if (filter.points_at.nr)
552 only_in_list = "--points-at";
553 else if (filter.reachable_from)
554 only_in_list = "--merged";
555 else if (filter.unreachable_from)
556 only_in_list = "--no-merged";
557 if (only_in_list)
558 die(_("the '%s' option is only allowed in list mode"), only_in_list);
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200559 if (cmdmode == 'd') {
560 ret = delete_tags(argv);
561 goto cleanup;
562 }
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500563 if (cmdmode == 'v') {
Jeff King4a68e362017-07-13 11:01:18 -0400564 if (format.format && verify_ref_format(&format))
Jeff King2eda0102017-07-13 10:56:10 -0400565 usage_with_options(git_tag_usage, options);
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200566 ret = for_each_tag_name(argv, verify_tag, &format);
567 goto cleanup;
Lukas Puehringer07d347c2017-01-17 18:37:21 -0500568 }
Carlos Rica39686582007-11-09 14:42:56 +0100569
Junio C Hamanobd46c9a2007-11-22 23:16:51 -0800570 if (msg.given || msgfile) {
571 if (msg.given && msgfile)
Jean-Noël Avila12909b62022-01-05 20:02:16 +0000572 die(_("options '%s' and '%s' cannot be used together"), "-F", "-m");
Junio C Hamanobd46c9a2007-11-22 23:16:51 -0800573 if (msg.given)
574 strbuf_addbuf(&buf, &(msg.buf));
Carlos Rica39686582007-11-09 14:42:56 +0100575 else {
576 if (!strcmp(msgfile, "-")) {
Pierre Habouzit387e7e12007-09-27 15:25:55 +0200577 if (strbuf_read(&buf, 0, 1024) < 0)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000578 die_errno(_("cannot read '%s'"), msgfile);
Pierre Habouzit387e7e12007-09-27 15:25:55 +0200579 } else {
Carlos Rica39686582007-11-09 14:42:56 +0100580 if (strbuf_read_file(&buf, msgfile, 1024) < 0)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000581 die_errno(_("could not open or read '%s'"),
Thomas Rastd824cbb2009-06-27 17:58:46 +0200582 msgfile);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200583 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200584 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200585 }
586
Carlos Rica39686582007-11-09 14:42:56 +0100587 tag = argv[0];
Carlos Rica62e09ce2007-07-20 01:42:28 +0200588
Carlos Rica39686582007-11-09 14:42:56 +0100589 object_ref = argc == 2 ? argv[1] : "HEAD";
590 if (argc > 2)
Johannes Sixtb8657342021-02-23 22:11:32 +0100591 die(_("too many arguments"));
Carlos Rica62e09ce2007-07-20 01:42:28 +0200592
brian m. carlson7422ab52017-05-06 22:10:08 +0000593 if (get_oid(object_ref, &object))
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000594 die(_("Failed to resolve '%s' as a valid ref."), object_ref);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200595
Michael Schubert4f0accd2011-05-10 01:36:36 +0200596 if (strbuf_check_tag_ref(&ref, tag))
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000597 die(_("'%s' is not a valid tag name."), tag);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200598
brian m. carlson34c290a2017-10-15 22:06:56 +0000599 if (read_ref(ref.buf, &prev))
brian m. carlson7422ab52017-05-06 22:10:08 +0000600 oidclr(&prev);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200601 else if (!force)
Ævar Arnfjörð Bjarmasond08ebf92011-02-22 23:42:09 +0000602 die(_("tag '%s' already exists"), tag);
Carlos Rica62e09ce2007-07-20 01:42:28 +0200603
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200604 opt.message_given = msg.given || msgfile;
Nicolas Morey-Chaisemartin9eed6e42018-02-06 09:36:24 +0100605 opt.use_editor = edit_flag;
Kirill A. Shutemovd3e05982011-12-07 05:01:45 +0200606
607 if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
608 opt.cleanup_mode = CLEANUP_ALL;
609 else if (!strcmp(cleanup_arg, "verbatim"))
610 opt.cleanup_mode = CLEANUP_NONE;
611 else if (!strcmp(cleanup_arg, "whitespace"))
612 opt.cleanup_mode = CLEANUP_SPACE;
613 else
614 die(_("Invalid cleanup mode %s"), cleanup_arg);
615
brian m. carlson7422ab52017-05-06 22:10:08 +0000616 create_reflog_msg(&object, &reflog_msg);
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100617
Laurent Arnoud61c2fe02016-03-22 21:41:26 +0100618 if (create_tag_object) {
619 if (force_sign_annotate && !annotate)
620 opt.sign = 1;
Denton Liueea9c1e2019-04-04 11:25:15 -0700621 create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
Laurent Arnoud61c2fe02016-03-22 21:41:26 +0100622 }
Carlos Rica62e09ce2007-07-20 01:42:28 +0200623
Ronnie Sahlberge5074bf2014-04-16 15:30:41 -0700624 transaction = ref_transaction_begin(&err);
625 if (!transaction ||
brian m. carlson89f3bbd2017-10-15 22:06:53 +0000626 ref_transaction_update(transaction, ref.buf, &object, &prev,
David Turner144c76f2015-07-21 17:04:55 -0400627 create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
Cornelius Weigdf8512e2017-02-08 23:41:18 +0100628 reflog_msg.buf, &err) ||
Ronnie Sahlbergdb7516a2014-04-30 12:22:42 -0700629 ref_transaction_commit(transaction, &err))
Ronnie Sahlberge5074bf2014-04-16 15:30:41 -0700630 die("%s", err.buf);
631 ref_transaction_free(transaction);
Jeff King9001dc22018-08-28 17:22:48 -0400632 if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
brian m. carlsonaab95832018-03-12 02:27:30 +0000633 printf(_("Updated tag '%s' (was %s)\n"), tag,
634 find_unique_abbrev(&prev, DEFAULT_ABBREV));
Carlos Rica62e09ce2007-07-20 01:42:28 +0200635
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200636cleanup:
Ævar Arnfjörð Bjarmasone5fb0282021-10-20 20:27:20 +0200637 ref_sorting_release(sorting);
Ævar Arnfjörð Bjarmason37766b62021-10-20 20:27:19 +0200638 strbuf_release(&buf);
639 strbuf_release(&ref);
640 strbuf_release(&reflog_msg);
641 strbuf_release(&msg.buf);
642 strbuf_release(&err);
643 return ret;
Carlos Rica62e09ce2007-07-20 01:42:28 +0200644}