| /* |
| * libgit2 "describe" example - shows how to describe commits |
| * |
| * Written by the libgit2 contributors |
| * |
| * To the extent possible under law, the author(s) have dedicated all copyright |
| * and related and neighboring rights to this software to the public domain |
| * worldwide. This software is distributed without any warranty. |
| * |
| * You should have received a copy of the CC0 Public Domain Dedication along |
| * with this software. If not, see |
| * <http://creativecommons.org/publicdomain/zero/1.0/>. |
| */ |
| |
| #include "common.h" |
| #include <assert.h> |
| |
| /** |
| * The following example partially reimplements the `git describe` command |
| * and some of its options. |
| * |
| * These commands should work: |
| |
| * - Describe HEAD with default options (`describe`) |
| * - Describe specified revision (`describe master~2`) |
| * - Describe specified revisions (`describe master~2 HEAD~3`) |
| * - Describe HEAD with dirty state suffix (`describe --dirty=*`) |
| * - Describe consider all refs (`describe --all master`) |
| * - Describe consider lightweight tags (`describe --tags temp-tag`) |
| * - Describe show non-default abbreviated size (`describe --abbrev=10`) |
| * - Describe always output the long format if matches a tag (`describe --long v1.0`) |
| * - Describe consider only tags of specified pattern (`describe --match v*-release`) |
| * - Describe show the fallback result (`describe --always`) |
| * - Describe follow only the first parent commit (`describe --first-parent`) |
| * |
| * The command line parsing logic is simplified and doesn't handle |
| * all of the use cases. |
| */ |
| |
| /** describe_options represents the parsed command line options */ |
| typedef struct { |
| const char **commits; |
| size_t commit_count; |
| git_describe_options describe_options; |
| git_describe_format_options format_options; |
| } describe_options; |
| |
| typedef struct args_info args_info; |
| |
| static void *xrealloc(void *oldp, size_t newsz) |
| { |
| void *p = realloc(oldp, newsz); |
| if (p == NULL) { |
| fprintf(stderr, "Cannot allocate memory, exiting.\n"); |
| exit(1); |
| } |
| return p; |
| } |
| |
| static void opts_add_commit(describe_options *opts, const char *commit) |
| { |
| size_t sz; |
| |
| assert(opts != NULL); |
| |
| sz = ++opts->commit_count * sizeof(opts->commits[0]); |
| opts->commits = xrealloc(opts->commits, sz); |
| opts->commits[opts->commit_count - 1] = commit; |
| } |
| |
| static void do_describe_single(git_repository *repo, describe_options *opts, const char *rev) |
| { |
| git_object *commit; |
| git_describe_result *describe_result; |
| git_buf buf = { 0 }; |
| |
| if (rev) { |
| check_lg2(git_revparse_single(&commit, repo, rev), |
| "Failed to lookup rev", rev); |
| |
| check_lg2(git_describe_commit(&describe_result, commit, &opts->describe_options), |
| "Failed to describe rev", rev); |
| } |
| else |
| check_lg2(git_describe_workdir(&describe_result, repo, &opts->describe_options), |
| "Failed to describe workdir", NULL); |
| |
| check_lg2(git_describe_format(&buf, describe_result, &opts->format_options), |
| "Failed to format describe rev", rev); |
| |
| printf("%s\n", buf.ptr); |
| } |
| |
| static void do_describe(git_repository *repo, describe_options *opts) |
| { |
| if (opts->commit_count == 0) |
| do_describe_single(repo, opts, NULL); |
| else |
| { |
| size_t i; |
| for (i = 0; i < opts->commit_count; i++) |
| do_describe_single(repo, opts, opts->commits[i]); |
| } |
| } |
| |
| static void print_usage(void) |
| { |
| fprintf(stderr, "usage: see `git help describe`\n"); |
| exit(1); |
| } |
| |
| /** Parse command line arguments */ |
| static void parse_options(describe_options *opts, int argc, char **argv) |
| { |
| args_info args = ARGS_INFO_INIT; |
| |
| for (args.pos = 1; args.pos < argc; ++args.pos) { |
| const char *curr = argv[args.pos]; |
| |
| if (curr[0] != '-') { |
| opts_add_commit(opts, curr); |
| } else if (!strcmp(curr, "--all")) { |
| opts->describe_options.describe_strategy = GIT_DESCRIBE_ALL; |
| } else if (!strcmp(curr, "--tags")) { |
| opts->describe_options.describe_strategy = GIT_DESCRIBE_TAGS; |
| } else if (!strcmp(curr, "--exact-match")) { |
| opts->describe_options.max_candidates_tags = 0; |
| } else if (!strcmp(curr, "--long")) { |
| opts->format_options.always_use_long_format = 1; |
| } else if (!strcmp(curr, "--always")) { |
| opts->describe_options.show_commit_oid_as_fallback = 1; |
| } else if (!strcmp(curr, "--first-parent")) { |
| opts->describe_options.only_follow_first_parent = 1; |
| } else if (optional_str_arg(&opts->format_options.dirty_suffix, &args, "--dirty", "-dirty")) { |
| } else if (match_int_arg((int *)&opts->format_options.abbreviated_size, &args, "--abbrev", 0)) { |
| } else if (match_int_arg((int *)&opts->describe_options.max_candidates_tags, &args, "--candidates", 0)) { |
| } else if (match_str_arg(&opts->describe_options.pattern, &args, "--match")) { |
| } else { |
| print_usage(); |
| } |
| } |
| |
| if (opts->commit_count > 0) { |
| if (opts->format_options.dirty_suffix) |
| fatal("--dirty is incompatible with commit-ishes", NULL); |
| } |
| else { |
| if (!opts->format_options.dirty_suffix || !opts->format_options.dirty_suffix[0]) { |
| opts_add_commit(opts, "HEAD"); |
| } |
| } |
| } |
| |
| /** Initialize describe_options struct */ |
| static void describe_options_init(describe_options *opts) |
| { |
| memset(opts, 0, sizeof(*opts)); |
| |
| opts->commits = NULL; |
| opts->commit_count = 0; |
| git_describe_init_options(&opts->describe_options, GIT_DESCRIBE_OPTIONS_VERSION); |
| git_describe_init_format_options(&opts->format_options, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| git_repository *repo; |
| describe_options opts; |
| |
| git_libgit2_init(); |
| |
| check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), |
| "Could not open repository", NULL); |
| |
| describe_options_init(&opts); |
| parse_options(&opts, argc, argv); |
| |
| do_describe(repo, &opts); |
| |
| git_repository_free(repo); |
| git_libgit2_shutdown(); |
| |
| return 0; |
| } |