1 /* 2 * libgit2 "describe" example - shows how to describe commits 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 * <https://creativecommons.org/publicdomain/zero/1.0/>. 13 */ 14 /** 15 * The following example partially reimplements the `git describe` command 16 * and some of its options. 17 * 18 * These commands should work: 19 * 20 * - Describe HEAD with default options (`describe`) 21 * - Describe specified revision (`describe master~2`) 22 * - Describe specified revisions (`describe master~2 HEAD~3`) 23 * - Describe HEAD with dirty state suffix (`describe --dirty=*`) 24 * - Describe consider all refs (`describe --all master`) 25 * - Describe consider lightweight tags (`describe --tags temp-tag`) 26 * - Describe show non-default abbreviated size (`describe --abbrev=10`) 27 * - Describe always output the long format if matches a tag (`describe --long v1.0`) 28 * - Describe consider only tags of specified pattern (`describe --match v*-release`) 29 * - Describe show the fallback result (`describe --always`) 30 * - Describe follow only the first parent commit (`describe --first-parent`) 31 * 32 * The command line parsing logic is simplified and doesn't handle 33 * all of the use cases. 34 * 35 * License: $(LINK2 https://creativecommons.org/publicdomain/zero/1.0/, CC0 1.0 Universal) 36 */ 37 module libgit2.example.describe; 38 39 40 private static import core.stdc.stdio; 41 private static import core.stdc.stdlib; 42 private static import core.stdc.string; 43 private static import libgit2.buffer; 44 private static import libgit2.describe; 45 private static import libgit2.example.args; 46 private static import libgit2.example.common; 47 private static import libgit2.revparse; 48 private static import libgit2.types; 49 50 /** 51 * describe_options represents the parsed command line options 52 */ 53 extern (C) 54 public struct describe_options 55 { 56 const (char)** commits; 57 size_t commit_count; 58 libgit2.describe.git_describe_options describe_options; 59 libgit2.describe.git_describe_format_options format_options; 60 } 61 62 nothrow @nogc 63 private void opts_add_commit(.describe_options* opts, const (char)* commit) 64 65 in 66 { 67 assert(opts != null); 68 } 69 70 do 71 { 72 size_t sz = ++opts.commit_count * opts.commits[0].sizeof; 73 opts.commits = cast(const (char)**)(libgit2.example.common.xrealloc(cast(void*)(opts.commits), sz)); 74 opts.commits[opts.commit_count - 1] = commit; 75 } 76 77 nothrow @nogc 78 private void do_describe_single(libgit2.types.git_repository* repo, .describe_options* opts, const (char)* rev) 79 80 in 81 { 82 } 83 84 do 85 { 86 libgit2.types.git_object* commit; 87 libgit2.describe.git_describe_result* describe_result; 88 89 if (rev != null) { 90 libgit2.example.common.check_lg2(libgit2.revparse.git_revparse_single(&commit, repo, rev), "Failed to lookup rev", rev); 91 92 libgit2.example.common.check_lg2(libgit2.describe.git_describe_commit(&describe_result, commit, &opts.describe_options), "Failed to describe rev", rev); 93 } else { 94 libgit2.example.common.check_lg2(libgit2.describe.git_describe_workdir(&describe_result, repo, &opts.describe_options), "Failed to describe workdir", null); 95 } 96 97 libgit2.buffer.git_buf buf = libgit2.buffer.git_buf.init; 98 libgit2.example.common.check_lg2(libgit2.describe.git_describe_format(&buf, describe_result, &opts.format_options), "Failed to format describe rev", rev); 99 100 core.stdc.stdio.printf("%s\n", buf.ptr_); 101 } 102 103 nothrow @nogc 104 private void do_describe(libgit2.types.git_repository* repo, .describe_options* opts) 105 106 in 107 { 108 } 109 110 do 111 { 112 if (opts.commit_count == 0) { 113 .do_describe_single(repo, opts, null); 114 } else { 115 for (size_t i = 0; i < opts.commit_count; i++) { 116 .do_describe_single(repo, opts, opts.commits[i]); 117 } 118 } 119 } 120 121 nothrow @nogc 122 private void print_usage() 123 124 in 125 { 126 } 127 128 do 129 { 130 core.stdc.stdio.fprintf(core.stdc.stdio.stderr, "usage: see `git help describe`\n"); 131 core.stdc.stdlib.exit(1); 132 } 133 134 /** 135 * Parse command line arguments 136 */ 137 nothrow @nogc 138 private void parse_options(.describe_options* opts, int argc, char** argv) 139 140 in 141 { 142 } 143 144 do 145 { 146 libgit2.example.args.args_info args = libgit2.example.args.ARGS_INFO_INIT(argc, argv); 147 148 for (args.pos = 1; args.pos < argc; ++args.pos) { 149 const (char)* curr = argv[args.pos]; 150 151 if (curr[0] != '-') { 152 .opts_add_commit(opts, curr); 153 } else if (!core.stdc..string.strcmp(curr, "--all")) { 154 opts.describe_options.describe_strategy = libgit2.describe.git_describe_strategy_t.GIT_DESCRIBE_ALL; 155 } else if (!core.stdc..string.strcmp(curr, "--tags")) { 156 opts.describe_options.describe_strategy = libgit2.describe.git_describe_strategy_t.GIT_DESCRIBE_TAGS; 157 } else if (!core.stdc..string.strcmp(curr, "--exact-match")) { 158 opts.describe_options.max_candidates_tags = 0; 159 } else if (!core.stdc..string.strcmp(curr, "--long")) { 160 opts.format_options.always_use_long_format = 1; 161 } else if (!core.stdc..string.strcmp(curr, "--always")) { 162 opts.describe_options.show_commit_oid_as_fallback = 1; 163 } else if (!core.stdc..string.strcmp(curr, "--first-parent")) { 164 opts.describe_options.only_follow_first_parent = 1; 165 } else if (libgit2.example.args.optional_str_arg(&opts.format_options.dirty_suffix, &args, "--dirty", "-dirty")) { 166 } else if (libgit2.example.args.match_int_arg(cast(int*)(&opts.format_options.abbreviated_size), &args, "--abbrev", 0)) { 167 } else if (libgit2.example.args.match_int_arg(cast(int*)(&opts.describe_options.max_candidates_tags), &args, "--candidates", 0)) { 168 } else if (libgit2.example.args.match_str_arg(&opts.describe_options.pattern, &args, "--match")) { 169 } else { 170 .print_usage(); 171 } 172 } 173 174 if (opts.commit_count > 0) { 175 if (opts.format_options.dirty_suffix) { 176 libgit2.example.common.fatal("--dirty is incompatible with commit-ishes", null); 177 } 178 } else { 179 if ((!opts.format_options.dirty_suffix) || (!opts.format_options.dirty_suffix[0])) { 180 .opts_add_commit(opts, "HEAD"); 181 } 182 } 183 } 184 185 /** 186 * Initialize describe_options struct 187 */ 188 nothrow @nogc 189 private void describe_options_init(.describe_options* opts) 190 191 in 192 { 193 } 194 195 do 196 { 197 core.stdc..string.memset(opts, 0, (*opts).sizeof); 198 199 opts.commits = null; 200 opts.commit_count = 0; 201 libgit2.describe.git_describe_options_init(&opts.describe_options, libgit2.describe.GIT_DESCRIBE_OPTIONS_VERSION); 202 libgit2.describe.git_describe_format_options_init(&opts.format_options, libgit2.describe.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); 203 } 204 205 extern (C) 206 nothrow @nogc 207 public int lg2_describe(libgit2.types.git_repository* repo, int argc, char** argv) 208 209 in 210 { 211 } 212 213 do 214 { 215 .describe_options opts; 216 217 .describe_options_init(&opts); 218 .parse_options(&opts, argc, argv); 219 220 .do_describe(repo, &opts); 221 222 return 0; 223 }