summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinquize <linquize@yahoo.com.hk>2014-10-12 15:52:53 +0800
committerLinquize <linquize@yahoo.com.hk>2014-10-12 19:25:20 +0800
commitd6bbcefce3a6daf6ee2ece329e9708ca44409252 (patch)
tree65ee4d2273924a1d8e1e3561fb71077de4c4f816
parent9e49cb7a4ba4f6961e3681cb59421cec04a50893 (diff)
downloadlibgit2-d6bbcefce3a6daf6ee2ece329e9708ca44409252.tar.gz
describe: add example
-rw-r--r--examples/common.c27
-rw-r--r--examples/common.h9
-rw-r--r--examples/describe.c162
3 files changed, 198 insertions, 0 deletions
diff --git a/examples/common.c b/examples/common.c
index a066c153c..0f25f3787 100644
--- a/examples/common.c
+++ b/examples/common.c
@@ -53,6 +53,33 @@ size_t is_prefixed(const char *str, const char *pfx)
return strncmp(str, pfx, len) ? 0 : len;
}
+int optional_str_arg(
+ const char **out, struct args_info *args, const char *opt, const char *def)
+{
+ const char *found = args->argv[args->pos];
+ size_t len = is_prefixed(found, opt);
+
+ if (!len)
+ return 0;
+
+ if (!found[len]) {
+ if (args->pos + 1 == args->argc) {
+ *out = def;
+ return 1;
+ }
+ args->pos += 1;
+ *out = args->argv[args->pos];
+ return 1;
+ }
+
+ if (found[len] == '=') {
+ *out = found + len + 1;
+ return 1;
+ }
+
+ return 0;
+}
+
int match_str_arg(
const char **out, struct args_info *args, const char *opt)
{
diff --git a/examples/common.h b/examples/common.h
index 2fd7d579f..b9fa37ce9 100644
--- a/examples/common.h
+++ b/examples/common.h
@@ -49,6 +49,15 @@ struct args_info {
/**
* Check current `args` entry against `opt` string. If it matches
* exactly, take the next arg as a string; if it matches as a prefix with
+ * an equal sign, take the remainder as a string; if value not supplied,
+ * default value `def` will be given. otherwise return 0.
+ */
+extern int optional_str_arg(
+ const char **out, struct args_info *args, const char *opt, const char *def);
+
+/**
+ * Check current `args` entry against `opt` string. If it matches
+ * exactly, take the next arg as a string; if it matches as a prefix with
* an equal sign, take the remainder as a string; otherwise return 0.
*/
extern int match_str_arg(
diff --git a/examples/describe.c b/examples/describe.c
new file mode 100644
index 000000000..cbf73b012
--- /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 */
+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 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->commits = (const char **)realloc((void *)opts->commits, ++opts->commit_count);
+ opts->commits[opts->commit_count - 1] = 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")) {
+ }
+ }
+
+ 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->commits = (const char **)malloc(++opts->commit_count);
+ opts->commits[0] = "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_threads_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_threads_shutdown();
+
+ return 0;
+}