diff options
Diffstat (limited to 'examples/describe.c')
-rw-r--r-- | examples/describe.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/examples/describe.c b/examples/describe.c new file mode 100644 index 0000000..1236272 --- /dev/null +++ b/examples/describe.c @@ -0,0 +1,162 @@ +/* + * 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" + +/** + * 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 */ +struct describe_options { + const char **commits; + size_t commit_count; + git_describe_options describe_options; + git_describe_format_options format_options; +}; + +static void opts_add_commit(struct describe_options *opts, const char *commit) +{ + size_t sz; + + assert(opts != NULL); + + sz = ++opts->commit_count * sizeof(opts->commits[0]); + opts->commits = xrealloc((void *) opts->commits, sz); + opts->commits[opts->commit_count - 1] = commit; +} + +static void do_describe_single(git_repository *repo, struct 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, struct 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(struct describe_options *opts, int argc, char **argv) +{ + struct 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(struct describe_options *opts) +{ + memset(opts, 0, sizeof(*opts)); + + opts->commits = NULL; + opts->commit_count = 0; + git_describe_options_init(&opts->describe_options, GIT_DESCRIBE_OPTIONS_VERSION); + git_describe_format_options_init(&opts->format_options, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); +} + +int lg2_describe(git_repository *repo, int argc, char **argv) +{ + struct describe_options opts; + + describe_options_init(&opts); + parse_options(&opts, argc, argv); + + do_describe(repo, &opts); + + return 0; +} |