2 * libgit2 "tag" example - shows how to list, create and delete tags
4 * Written by the libgit2 contributors
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.
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/>.
18 * The following example partially reimplements the `git tag` command
19 * and some of its options.
21 * These commands should work:
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`)
29 * The command line parsing logic is simplified and doesn't handle
30 * all of the use cases.
33 /** tag_options represents the parsed command line options */
43 /** tag_state represents the current program state for dragging around */
46 struct tag_options
*opts
;
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
;
53 static void check(int result
, const char *message
)
55 if (result
) fatal(message
, NULL
);
58 /** Tag listing: Print individual message lines */
59 static void print_list_lines(const char *message
, const tag_state
*state
)
61 const char *msg
= message
;
62 int num
= state
->opts
->num_lines
- 1;
66 /** first line - headline */
67 while(*msg
&& *msg
!= '\n') printf("%c", *msg
++);
69 /** skip over new lines */
70 while(*msg
&& *msg
== '\n') msg
++;
74 /** print just headline? */
76 if (*msg
&& msg
[1]) printf("\n");
78 /** print individual commit/tag lines */
79 while (*msg
&& num
-- >= 2) {
82 while (*msg
&& *msg
!= '\n') printf("%c", *msg
++);
84 /** handle consecutive new lines */
85 if (*msg
&& *msg
== '\n' && msg
[1] == '\n') {
89 while(*msg
&& *msg
== '\n') msg
++;
95 /** Tag listing: Print an actual tag object */
96 static void print_tag(git_tag
*tag
, const tag_state
*state
)
98 printf("%-16s", git_tag_name(tag
));
100 if (state
->opts
->num_lines
) {
101 const char *msg
= git_tag_message(tag
);
102 print_list_lines(msg
, state
);
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
)
112 printf("%-16s", name
);
114 if (state
->opts
->num_lines
) {
115 const char *msg
= git_commit_message(commit
);
116 print_list_lines(msg
, state
);
122 /** Tag listing: Fallback, should not happen */
123 static void print_name(const char *name
)
125 printf("%s\n", name
);
128 /** Tag listing: Lookup tags based on ref name and dispatch to print */
129 static int each_tag(const char *name
, tag_state
*state
)
131 git_repository
*repo
= state
->repo
;
134 check_lg2(git_revparse_single(&obj
, repo
, name
),
135 "Failed to lookup rev", name
);
137 switch (git_object_type(obj
)) {
139 print_tag((git_tag
*) obj
, state
);
141 case GIT_OBJECT_COMMIT
:
142 print_commit((git_commit
*) obj
, name
, state
);
148 git_object_free(obj
);
152 static void action_list_tags(tag_state
*state
)
154 const char *pattern
= state
->opts
->pattern
;
155 git_strarray tag_names
= {0};
158 check_lg2(git_tag_list_match(&tag_names
, pattern
? pattern
: "*", state
->repo
),
159 "Unable to get list of tags", NULL
);
161 for(i
= 0; i
< tag_names
.count
; i
++) {
162 each_tag(tag_names
.strings
[i
], state
);
165 git_strarray_dispose(&tag_names
);
168 static void action_delete_tag(tag_state
*state
)
170 struct tag_options
*opts
= state
->opts
;
172 git_buf abbrev_oid
= {0};
174 check(!opts
->tag_name
, "Name required");
176 check_lg2(git_revparse_single(&obj
, state
->repo
, opts
->tag_name
),
177 "Failed to lookup rev", opts
->tag_name
);
179 check_lg2(git_object_short_id(&abbrev_oid
, obj
),
180 "Unable to get abbreviated OID", opts
->tag_name
);
182 check_lg2(git_tag_delete(state
->repo
, opts
->tag_name
),
183 "Unable to delete tag", opts
->tag_name
);
185 printf("Deleted tag '%s' (was %s)\n", opts
->tag_name
, abbrev_oid
.ptr
);
187 git_buf_dispose(&abbrev_oid
);
188 git_object_free(obj
);
191 static void action_create_lighweight_tag(tag_state
*state
)
193 git_repository
*repo
= state
->repo
;
194 struct tag_options
*opts
= state
->opts
;
198 check(!opts
->tag_name
, "Name required");
200 if (!opts
->target
) opts
->target
= "HEAD";
202 check(!opts
->target
, "Target required");
204 check_lg2(git_revparse_single(&target
, repo
, opts
->target
),
205 "Unable to resolve spec", opts
->target
);
207 check_lg2(git_tag_create_lightweight(&oid
, repo
, opts
->tag_name
,
208 target
, opts
->force
), "Unable to create tag", NULL
);
210 git_object_free(target
);
213 static void action_create_tag(tag_state
*state
)
215 git_repository
*repo
= state
->repo
;
216 struct tag_options
*opts
= state
->opts
;
217 git_signature
*tagger
;
221 check(!opts
->tag_name
, "Name required");
222 check(!opts
->message
, "Message required");
224 if (!opts
->target
) opts
->target
= "HEAD";
226 check_lg2(git_revparse_single(&target
, repo
, opts
->target
),
227 "Unable to resolve spec", opts
->target
);
229 check_lg2(git_signature_default(&tagger
, repo
),
230 "Unable to create signature", NULL
);
232 check_lg2(git_tag_create(&oid
, repo
, opts
->tag_name
,
233 target
, tagger
, opts
->message
, opts
->force
), "Unable to create tag", NULL
);
235 git_object_free(target
);
236 git_signature_free(tagger
);
239 static void print_usage(void)
241 fprintf(stderr
, "usage: see `git help tag`\n");
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
)
248 args_info args
= ARGS_INFO_INIT
;
249 *action
= &action_list_tags
;
251 for (args
.pos
= 1; args
.pos
< argc
; ++args
.pos
) {
252 const char *curr
= argv
[args
.pos
];
254 if (curr
[0] != '-') {
256 opts
->tag_name
= curr
;
257 else if (!opts
->target
)
262 if (*action
!= &action_create_tag
)
263 *action
= &action_create_lighweight_tag
;
264 } else if (!strcmp(curr
, "-n")) {
266 *action
= &action_list_tags
;
267 } else if (!strcmp(curr
, "-a")) {
268 *action
= &action_create_tag
;
269 } else if (!strcmp(curr
, "-f")) {
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
;
283 /** Initialize tag_options struct */
284 static void tag_options_init(struct tag_options
*opts
)
286 memset(opts
, 0, sizeof(*opts
));
288 opts
->message
= NULL
;
289 opts
->pattern
= NULL
;
290 opts
->tag_name
= NULL
;
296 int lg2_tag(git_repository
*repo
, int argc
, char **argv
)
298 struct tag_options opts
;
302 tag_options_init(&opts
);
303 parse_options(&action
, &opts
, argc
, argv
);