]> git.proxmox.com Git - libgit2.git/blob - examples/tag.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / examples / tag.c
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 */
34 struct tag_options {
35 const char *message;
36 const char *pattern;
37 const char *tag_name;
38 const char *target;
39 int num_lines;
40 int force;
41 };
42
43 /** tag_state represents the current program state for dragging around */
44 typedef struct {
45 git_repository *repo;
46 struct tag_options *opts;
47 } tag_state;
48
49 /** An action to execute based on the command line arguments */
50 typedef void (*tag_action)(tag_state *state);
51 typedef struct args_info args_info;
52
53 static void check(int result, const char *message)
54 {
55 if (result) fatal(message, NULL);
56 }
57
58 /** Tag listing: Print individual message lines */
59 static 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 */
96 static 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) */
109 static 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 */
123 static 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 */
129 static 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)) {
138 case GIT_OBJECT_TAG:
139 print_tag((git_tag *) obj, state);
140 break;
141 case GIT_OBJECT_COMMIT:
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
152 static 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
165 git_strarray_dispose(&tag_names);
166 }
167
168 static void action_delete_tag(tag_state *state)
169 {
170 struct tag_options *opts = state->opts;
171 git_object *obj;
172 git_buf abbrev_oid = {0};
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
179 check_lg2(git_object_short_id(&abbrev_oid, obj),
180 "Unable to get abbreviated OID", opts->tag_name);
181
182 check_lg2(git_tag_delete(state->repo, opts->tag_name),
183 "Unable to delete tag", opts->tag_name);
184
185 printf("Deleted tag '%s' (was %s)\n", opts->tag_name, abbrev_oid.ptr);
186
187 git_buf_dispose(&abbrev_oid);
188 git_object_free(obj);
189 }
190
191 static void action_create_lighweight_tag(tag_state *state)
192 {
193 git_repository *repo = state->repo;
194 struct tag_options *opts = state->opts;
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
213 static void action_create_tag(tag_state *state)
214 {
215 git_repository *repo = state->repo;
216 struct tag_options *opts = state->opts;
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
239 static void print_usage(void)
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 */
246 static void parse_options(tag_action *action, struct tag_options *opts, int argc, char **argv)
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 */
284 static void tag_options_init(struct tag_options *opts)
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
296 int lg2_tag(git_repository *repo, int argc, char **argv)
297 {
298 struct tag_options opts;
299 tag_action action;
300 tag_state state;
301
302 tag_options_init(&opts);
303 parse_options(&action, &opts, argc, argv);
304
305 state.repo = repo;
306 state.opts = &opts;
307 action(&state);
308
309 return 0;
310 }