]> git.proxmox.com Git - libgit2.git/blame - examples/tag.c
Fix autopkgtest regression with CMake 3.19+
[libgit2.git] / examples / tag.c
CommitLineData
a53b8584
JP
1/*
2 * libgit2 "tag" example - shows how to list, create and delete tags
3 *
4 * Written by the libgit2 contributors
5 *
6 * To the extent possible under law, the author(s) have dedicated all copyright
7 * and related and neighboring rights to this software to the public domain
8 * worldwide. This software is distributed without any warranty.
9 *
10 * You should have received a copy of the CC0 Public Domain Dedication along
11 * with this software. If not, see
12 * <http://creativecommons.org/publicdomain/zero/1.0/>.
13 */
14
15#include "common.h"
16
17/**
18 * The following example partially reimplements the `git tag` command
19 * and some of its options.
20 *
21 * These commands should work:
22
23 * - Tag name listing (`tag`)
24 * - Filtered tag listing with messages (`tag -n3 -l "v0.1*"`)
25 * - Lightweight tag creation (`tag test v0.18.0`)
26 * - Tag creation (`tag -a -m "Test message" test v0.18.0`)
27 * - Tag deletion (`tag -d test`)
28 *
29 * The command line parsing logic is simplified and doesn't handle
30 * all of the use cases.
31 */
32
33/** tag_options represents the parsed command line options */
22a2d3d5 34struct tag_options {
a53b8584
JP
35 const char *message;
36 const char *pattern;
37 const char *tag_name;
38 const char *target;
39 int num_lines;
40 int force;
22a2d3d5 41};
a53b8584
JP
42
43/** tag_state represents the current program state for dragging around */
44typedef struct {
45 git_repository *repo;
22a2d3d5 46 struct tag_options *opts;
a53b8584
JP
47} tag_state;
48
49/** An action to execute based on the command line arguments */
50typedef void (*tag_action)(tag_state *state);
51typedef struct args_info args_info;
52
53static void check(int result, const char *message)
54{
55 if (result) fatal(message, NULL);
56}
57
58/** Tag listing: Print individual message lines */
59static void print_list_lines(const char *message, const tag_state *state)
60{
61 const char *msg = message;
62 int num = state->opts->num_lines - 1;
63
64 if (!msg) return;
65
66 /** first line - headline */
67 while(*msg && *msg != '\n') printf("%c", *msg++);
68
69 /** skip over new lines */
70 while(*msg && *msg == '\n') msg++;
71
72 printf("\n");
73
74 /** print just headline? */
75 if (num == 0) return;
76 if (*msg && msg[1]) printf("\n");
77
78 /** print individual commit/tag lines */
79 while (*msg && num-- >= 2) {
80 printf(" ");
81
82 while (*msg && *msg != '\n') printf("%c", *msg++);
83
84 /** handle consecutive new lines */
85 if (*msg && *msg == '\n' && msg[1] == '\n') {
86 num--;
87 printf("\n");
88 }
89 while(*msg && *msg == '\n') msg++;
90
91 printf("\n");
92 }
93}
94
95/** Tag listing: Print an actual tag object */
96static void print_tag(git_tag *tag, const tag_state *state)
97{
98 printf("%-16s", git_tag_name(tag));
99
100 if (state->opts->num_lines) {
101 const char *msg = git_tag_message(tag);
102 print_list_lines(msg, state);
103 } else {
104 printf("\n");
105 }
106}
107
108/** Tag listing: Print a commit (target of a lightweight tag) */
109static void print_commit(git_commit *commit, const char *name,
110 const tag_state *state)
111{
112 printf("%-16s", name);
113
114 if (state->opts->num_lines) {
115 const char *msg = git_commit_message(commit);
116 print_list_lines(msg, state);
117 } else {
118 printf("\n");
119 }
120}
121
122/** Tag listing: Fallback, should not happen */
123static void print_name(const char *name)
124{
125 printf("%s\n", name);
126}
127
128/** Tag listing: Lookup tags based on ref name and dispatch to print */
129static int each_tag(const char *name, tag_state *state)
130{
131 git_repository *repo = state->repo;
132 git_object *obj;
133
134 check_lg2(git_revparse_single(&obj, repo, name),
135 "Failed to lookup rev", name);
136
137 switch (git_object_type(obj)) {
ac3d33df 138 case GIT_OBJECT_TAG:
a53b8584
JP
139 print_tag((git_tag *) obj, state);
140 break;
ac3d33df 141 case GIT_OBJECT_COMMIT:
a53b8584
JP
142 print_commit((git_commit *) obj, name, state);
143 break;
144 default:
145 print_name(name);
146 }
147
148 git_object_free(obj);
149 return 0;
150}
151
152static void action_list_tags(tag_state *state)
153{
154 const char *pattern = state->opts->pattern;
155 git_strarray tag_names = {0};
156 size_t i;
157
158 check_lg2(git_tag_list_match(&tag_names, pattern ? pattern : "*", state->repo),
159 "Unable to get list of tags", NULL);
160
161 for(i = 0; i < tag_names.count; i++) {
162 each_tag(tag_names.strings[i], state);
163 }
164
22a2d3d5 165 git_strarray_dispose(&tag_names);
a53b8584
JP
166}
167
168static void action_delete_tag(tag_state *state)
169{
22a2d3d5 170 struct tag_options *opts = state->opts;
a53b8584 171 git_object *obj;
6ad59ef1 172 git_buf abbrev_oid = {0};
a53b8584
JP
173
174 check(!opts->tag_name, "Name required");
175
176 check_lg2(git_revparse_single(&obj, state->repo, opts->tag_name),
177 "Failed to lookup rev", opts->tag_name);
178
6ad59ef1
JP
179 check_lg2(git_object_short_id(&abbrev_oid, obj),
180 "Unable to get abbreviated OID", opts->tag_name);
181
a53b8584
JP
182 check_lg2(git_tag_delete(state->repo, opts->tag_name),
183 "Unable to delete tag", opts->tag_name);
184
6ad59ef1 185 printf("Deleted tag '%s' (was %s)\n", opts->tag_name, abbrev_oid.ptr);
a53b8584 186
ac3d33df 187 git_buf_dispose(&abbrev_oid);
a53b8584
JP
188 git_object_free(obj);
189}
190
191static void action_create_lighweight_tag(tag_state *state)
192{
193 git_repository *repo = state->repo;
22a2d3d5 194 struct tag_options *opts = state->opts;
a53b8584
JP
195 git_oid oid;
196 git_object *target;
197
198 check(!opts->tag_name, "Name required");
199
200 if (!opts->target) opts->target = "HEAD";
201
202 check(!opts->target, "Target required");
203
204 check_lg2(git_revparse_single(&target, repo, opts->target),
205 "Unable to resolve spec", opts->target);
206
207 check_lg2(git_tag_create_lightweight(&oid, repo, opts->tag_name,
208 target, opts->force), "Unable to create tag", NULL);
209
210 git_object_free(target);
211}
212
213static void action_create_tag(tag_state *state)
214{
215 git_repository *repo = state->repo;
22a2d3d5 216 struct tag_options *opts = state->opts;
a53b8584
JP
217 git_signature *tagger;
218 git_oid oid;
219 git_object *target;
220
221 check(!opts->tag_name, "Name required");
222 check(!opts->message, "Message required");
223
224 if (!opts->target) opts->target = "HEAD";
225
226 check_lg2(git_revparse_single(&target, repo, opts->target),
227 "Unable to resolve spec", opts->target);
228
229 check_lg2(git_signature_default(&tagger, repo),
230 "Unable to create signature", NULL);
231
232 check_lg2(git_tag_create(&oid, repo, opts->tag_name,
233 target, tagger, opts->message, opts->force), "Unable to create tag", NULL);
234
235 git_object_free(target);
236 git_signature_free(tagger);
237}
238
bd465f9c 239static void print_usage(void)
a53b8584
JP
240{
241 fprintf(stderr, "usage: see `git help tag`\n");
242 exit(1);
243}
244
245/** Parse command line arguments and choose action to run when done */
22a2d3d5 246static void parse_options(tag_action *action, struct tag_options *opts, int argc, char **argv)
a53b8584
JP
247{
248 args_info args = ARGS_INFO_INIT;
249 *action = &action_list_tags;
250
251 for (args.pos = 1; args.pos < argc; ++args.pos) {
252 const char *curr = argv[args.pos];
253
254 if (curr[0] != '-') {
255 if (!opts->tag_name)
256 opts->tag_name = curr;
257 else if (!opts->target)
258 opts->target = curr;
259 else
260 print_usage();
261
262 if (*action != &action_create_tag)
263 *action = &action_create_lighweight_tag;
264 } else if (!strcmp(curr, "-n")) {
265 opts->num_lines = 1;
266 *action = &action_list_tags;
267 } else if (!strcmp(curr, "-a")) {
268 *action = &action_create_tag;
269 } else if (!strcmp(curr, "-f")) {
270 opts->force = 1;
271 } else if (match_int_arg(&opts->num_lines, &args, "-n", 0)) {
272 *action = &action_list_tags;
273 } else if (match_str_arg(&opts->pattern, &args, "-l")) {
274 *action = &action_list_tags;
275 } else if (match_str_arg(&opts->tag_name, &args, "-d")) {
276 *action = &action_delete_tag;
277 } else if (match_str_arg(&opts->message, &args, "-m")) {
278 *action = &action_create_tag;
279 }
280 }
281}
282
283/** Initialize tag_options struct */
22a2d3d5 284static void tag_options_init(struct tag_options *opts)
a53b8584
JP
285{
286 memset(opts, 0, sizeof(*opts));
287
288 opts->message = NULL;
289 opts->pattern = NULL;
290 opts->tag_name = NULL;
291 opts->target = NULL;
292 opts->num_lines = 0;
293 opts->force = 0;
294}
295
22a2d3d5 296int lg2_tag(git_repository *repo, int argc, char **argv)
a53b8584 297{
22a2d3d5 298 struct tag_options opts;
feebe615
JP
299 tag_action action;
300 tag_state state;
301
a53b8584 302 tag_options_init(&opts);
a53b8584
JP
303 parse_options(&action, &opts, argc, argv);
304
feebe615
JP
305 state.repo = repo;
306 state.opts = &opts;
a53b8584
JP
307 action(&state);
308
a53b8584
JP
309 return 0;
310}