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 * <http://creativecommons.org/publicdomain/zero/1.0/>. 13 */ 14 module libgit2_d.example.describe; 15 16 17 private static import core.stdc.stdio; 18 private static import core.stdc.stdlib; 19 private static import core.stdc..string; 20 private static import libgit2_d.buffer; 21 private static import libgit2_d.describe; 22 private static import libgit2_d.example.args; 23 private static import libgit2_d.example.common; 24 private static import libgit2_d.revparse; 25 private static import libgit2_d.types; 26 27 package: 28 29 /** 30 * The following example partially reimplements the `git describe` command 31 * and some of its options. 32 * 33 * These commands should work: 34 * 35 * - Describe HEAD with default options (`describe`) 36 * - Describe specified revision (`describe master~2`) 37 * - Describe specified revisions (`describe master~2 HEAD~3`) 38 * - Describe HEAD with dirty state suffix (`describe --dirty=*`) 39 * - Describe consider all refs (`describe --all master`) 40 * - Describe consider lightweight tags (`describe --tags temp-tag`) 41 * - Describe show non-default abbreviated size (`describe --abbrev=10`) 42 * - Describe always output the long format if matches a tag (`describe --long v1.0`) 43 * - Describe consider only tags of specified pattern (`describe --match v*-release`) 44 * - Describe show the fallback result (`describe --always`) 45 * - Describe follow only the first parent commit (`describe --first-parent`) 46 * 47 * The command line parsing logic is simplified and doesn't handle 48 * all of the use cases. 49 */ 50 51 /** 52 * describe_options represents the parsed command line options 53 */ 54 public struct describe_options 55 { 56 const (char)** commits; 57 size_t commit_count; 58 libgit2_d.describe.git_describe_options describe_options; 59 libgit2_d.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_d.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_d.types.git_repository* repo, .describe_options* opts, const (char)* rev) 79 80 in 81 { 82 } 83 84 do 85 { 86 libgit2_d.types.git_object* commit; 87 libgit2_d.describe.git_describe_result* describe_result; 88 89 if (rev != null) { 90 libgit2_d.example.common.check_lg2(libgit2_d.revparse.git_revparse_single(&commit, repo, rev), "Failed to lookup rev", rev); 91 92 libgit2_d.example.common.check_lg2(libgit2_d.describe.git_describe_commit(&describe_result, commit, &opts.describe_options), "Failed to describe rev", rev); 93 } else { 94 libgit2_d.example.common.check_lg2(libgit2_d.describe.git_describe_workdir(&describe_result, repo, &opts.describe_options), "Failed to describe workdir", null); 95 } 96 97 libgit2_d.buffer.git_buf buf = libgit2_d.buffer.git_buf.init; 98 libgit2_d.example.common.check_lg2(libgit2_d.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_d.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_d.example.args.args_info args = libgit2_d.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_d.describe.git_describe_strategy_t.GIT_DESCRIBE_ALL; 155 } else if (!core.stdc..string.strcmp(curr, "--tags")) { 156 opts.describe_options.describe_strategy = libgit2_d.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_d.example.args.optional_str_arg(&opts.format_options.dirty_suffix, &args, "--dirty", "-dirty")) { 166 } else if (libgit2_d.example.args.match_int_arg(cast(int*)(&opts.format_options.abbreviated_size), &args, "--abbrev", 0)) { 167 } else if (libgit2_d.example.args.match_int_arg(cast(int*)(&opts.describe_options.max_candidates_tags), &args, "--candidates", 0)) { 168 } else if (libgit2_d.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_d.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_d.describe.git_describe_options_init(&opts.describe_options, libgit2_d.describe.GIT_DESCRIBE_OPTIONS_VERSION); 202 libgit2_d.describe.git_describe_format_options_init(&opts.format_options, libgit2_d.describe.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); 203 } 204 205 extern (C) 206 nothrow @nogc 207 public int lg2_describe(libgit2_d.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 }